| File: | src/usr.bin/vi/build/../ex/ex_script.c |
| Warning: | line 292, column 3 Null pointer passed as 1st argument to memory copy function |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: ex_script.c,v 1.27 2017/04/18 01:45:35 deraadt Exp $ */ | |||
| 2 | ||||
| 3 | /*- | |||
| 4 | * Copyright (c) 1992, 1993, 1994 | |||
| 5 | * The Regents of the University of California. All rights reserved. | |||
| 6 | * Copyright (c) 1992, 1993, 1994, 1995, 1996 | |||
| 7 | * Keith Bostic. All rights reserved. | |||
| 8 | * | |||
| 9 | * This code is derived from software contributed to Berkeley by | |||
| 10 | * Brian Hirt. | |||
| 11 | * | |||
| 12 | * See the LICENSE file for redistribution information. | |||
| 13 | */ | |||
| 14 | ||||
| 15 | #include "config.h" | |||
| 16 | ||||
| 17 | #include <sys/types.h> | |||
| 18 | #include <sys/ioctl.h> | |||
| 19 | #include <sys/queue.h> | |||
| 20 | #include <sys/stat.h> | |||
| 21 | #include <sys/time.h> | |||
| 22 | #include <sys/wait.h> | |||
| 23 | ||||
| 24 | #include <bitstring.h> | |||
| 25 | #include <errno(*__errno()).h> | |||
| 26 | #include <fcntl.h> | |||
| 27 | #include <stdio.h> /* XXX: OSF/1 bug: include before <grp.h> */ | |||
| 28 | #include <grp.h> | |||
| 29 | #include <limits.h> | |||
| 30 | #include <poll.h> | |||
| 31 | #include <stdlib.h> | |||
| 32 | #include <string.h> | |||
| 33 | #include <termios.h> | |||
| 34 | #include <unistd.h> | |||
| 35 | #include <util.h> | |||
| 36 | ||||
| 37 | #include "../common/common.h" | |||
| 38 | #include "../vi/vi.h" | |||
| 39 | #include "script.h" | |||
| 40 | #include "pathnames.h" | |||
| 41 | ||||
| 42 | static void sscr_check(SCR *); | |||
| 43 | static int sscr_getprompt(SCR *); | |||
| 44 | static int sscr_init(SCR *); | |||
| 45 | static int sscr_insert(SCR *); | |||
| 46 | static int sscr_matchprompt(SCR *, char *, size_t, size_t *); | |||
| 47 | static int sscr_setprompt(SCR *, char *, size_t); | |||
| 48 | ||||
| 49 | /* | |||
| 50 | * ex_script -- : sc[ript][!] [file] | |||
| 51 | * Switch to script mode. | |||
| 52 | * | |||
| 53 | * PUBLIC: int ex_script(SCR *, EXCMD *); | |||
| 54 | */ | |||
| 55 | int | |||
| 56 | ex_script(SCR *sp, EXCMD *cmdp) | |||
| 57 | { | |||
| 58 | /* Vi only command. */ | |||
| 59 | if (!F_ISSET(sp, SC_VI)(((sp)->flags) & ((0x00000002)))) { | |||
| 60 | msgq(sp, M_ERR, | |||
| 61 | "The script command is only available in vi mode"); | |||
| 62 | return (1); | |||
| 63 | } | |||
| 64 | ||||
| 65 | /* Switch to the new file. */ | |||
| 66 | if (cmdp->argc != 0 && ex_edit(sp, cmdp)) | |||
| 67 | return (1); | |||
| 68 | ||||
| 69 | /* Create the shell, figure out the prompt. */ | |||
| 70 | if (sscr_init(sp)) | |||
| 71 | return (1); | |||
| 72 | ||||
| 73 | return (0); | |||
| 74 | } | |||
| 75 | ||||
| 76 | /* | |||
| 77 | * sscr_init -- | |||
| 78 | * Create a pty setup for a shell. | |||
| 79 | */ | |||
| 80 | static int | |||
| 81 | sscr_init(SCR *sp) | |||
| 82 | { | |||
| 83 | SCRIPT *sc; | |||
| 84 | char *sh, *sh_path; | |||
| 85 | ||||
| 86 | /* We're going to need a shell. */ | |||
| 87 | if (opts_empty(sp, O_SHELL, 0)) | |||
| 88 | return (1); | |||
| 89 | ||||
| 90 | MALLOC_RET(sp, sc, sizeof(SCRIPT)){ if (((sc) = malloc(sizeof(SCRIPT))) == ((void *)0)) { msgq( (sp), M_SYSERR, ((void *)0)); return (1); } }; | |||
| 91 | sp->script = sc; | |||
| 92 | sc->sh_prompt = NULL((void *)0); | |||
| 93 | sc->sh_prompt_len = 0; | |||
| 94 | ||||
| 95 | /* | |||
| 96 | * There are two different processes running through this code. | |||
| 97 | * They are the shell and the parent. | |||
| 98 | */ | |||
| 99 | sc->sh_master = sc->sh_slave = -1; | |||
| 100 | ||||
| 101 | if (tcgetattr(STDIN_FILENO0, &sc->sh_term) == -1) { | |||
| 102 | msgq(sp, M_SYSERR, "tcgetattr"); | |||
| 103 | goto err; | |||
| 104 | } | |||
| 105 | ||||
| 106 | /* | |||
| 107 | * Turn off output postprocessing and echo. | |||
| 108 | */ | |||
| 109 | sc->sh_term.c_oflag &= ~OPOST0x00000001; | |||
| 110 | sc->sh_term.c_cflag &= ~(ECHO0x00000008|ECHOE0x00000002|ECHONL0x00000010|ECHOK0x00000004); | |||
| 111 | ||||
| 112 | if (ioctl(STDIN_FILENO0, TIOCGWINSZ((unsigned long)0x40000000 | ((sizeof(struct winsize) & 0x1fff ) << 16) | ((('t')) << 8) | ((104))), &sc->sh_win) == -1) { | |||
| 113 | msgq(sp, M_SYSERR, "tcgetattr"); | |||
| 114 | goto err; | |||
| 115 | } | |||
| 116 | ||||
| 117 | if (openpty(&sc->sh_master, | |||
| 118 | &sc->sh_slave, sc->sh_name, &sc->sh_term, &sc->sh_win) == -1) { | |||
| 119 | msgq(sp, M_SYSERR, "pty"); | |||
| 120 | goto err; | |||
| 121 | } | |||
| 122 | ||||
| 123 | /* | |||
| 124 | * __TK__ huh? | |||
| 125 | * Don't use vfork() here, because the signal semantics differ from | |||
| 126 | * implementation to implementation. | |||
| 127 | */ | |||
| 128 | switch (sc->sh_pid = fork()) { | |||
| 129 | case -1: /* Error. */ | |||
| 130 | msgq(sp, M_SYSERR, "fork"); | |||
| 131 | err: if (sc->sh_master != -1) | |||
| 132 | (void)close(sc->sh_master); | |||
| 133 | if (sc->sh_slave != -1) | |||
| 134 | (void)close(sc->sh_slave); | |||
| 135 | return (1); | |||
| 136 | case 0: /* Utility. */ | |||
| 137 | /* | |||
| 138 | * XXX | |||
| 139 | * So that shells that do command line editing turn it off. | |||
| 140 | */ | |||
| 141 | if (setenv("TERM", "emacs", 1) == -1 || | |||
| 142 | setenv("TERMCAP", "emacs:", 1) == -1 || | |||
| 143 | setenv("EMACS", "t", 1) == -1) | |||
| 144 | _exit(126); | |||
| 145 | ||||
| 146 | (void)setsid(); | |||
| 147 | /* | |||
| 148 | * 4.4BSD allocates a controlling terminal using the TIOCSCTTY | |||
| 149 | * ioctl, not by opening a terminal device file. POSIX 1003.1 | |||
| 150 | * doesn't define a portable way to do this. | |||
| 151 | */ | |||
| 152 | (void)ioctl(sc->sh_slave, TIOCSCTTY((unsigned long)0x20000000 | ((0 & 0x1fff) << 16) | ((('t')) << 8) | ((97))), 0); | |||
| 153 | (void)close(sc->sh_master); | |||
| 154 | (void)dup2(sc->sh_slave, STDIN_FILENO0); | |||
| 155 | (void)dup2(sc->sh_slave, STDOUT_FILENO1); | |||
| 156 | (void)dup2(sc->sh_slave, STDERR_FILENO2); | |||
| 157 | (void)close(sc->sh_slave); | |||
| 158 | ||||
| 159 | /* Assumes that all shells have -i. */ | |||
| 160 | sh_path = O_STR(sp, O_SHELL)((((&((sp))->opts[((O_SHELL))])->flags) & ((0x01 ))) ? ((sp))->gp->opts[((sp))->opts[((O_SHELL))].o_cur .val].o_cur.str : ((sp))->opts[((O_SHELL))].o_cur.str); | |||
| 161 | if ((sh = strrchr(sh_path, '/')) == NULL((void *)0)) | |||
| 162 | sh = sh_path; | |||
| 163 | else | |||
| 164 | ++sh; | |||
| 165 | execl(sh_path, sh, "-i", (char *)NULL((void *)0)); | |||
| 166 | msgq_str(sp, M_SYSERR, sh_path, "execl: %s"); | |||
| 167 | _exit(127); | |||
| 168 | default: /* Parent. */ | |||
| 169 | break; | |||
| 170 | } | |||
| 171 | ||||
| 172 | if (sscr_getprompt(sp)) | |||
| 173 | return (1); | |||
| 174 | ||||
| 175 | F_SET(sp, SC_SCRIPT)(((sp)->flags) |= ((0x01000000))); | |||
| 176 | F_SET(sp->gp, G_SCRWIN)(((sp->gp)->flags) |= ((0x0020))); | |||
| 177 | return (0); | |||
| 178 | } | |||
| 179 | ||||
| 180 | /* | |||
| 181 | * sscr_getprompt -- | |||
| 182 | * Eat lines printed by the shell until a line with no trailing | |||
| 183 | * carriage return comes; set the prompt from that line. | |||
| 184 | */ | |||
| 185 | static int | |||
| 186 | sscr_getprompt(SCR *sp) | |||
| 187 | { | |||
| 188 | CHAR_T *endp, *p, *t, buf[1024]; | |||
| 189 | SCRIPT *sc; | |||
| 190 | struct pollfd pfd[1]; | |||
| 191 | recno_t lline; | |||
| 192 | size_t llen, len; | |||
| 193 | u_int value; | |||
| 194 | int nr; | |||
| 195 | ||||
| 196 | endp = buf; | |||
| 197 | len = sizeof(buf); | |||
| 198 | ||||
| 199 | /* Wait up to a second for characters to read. */ | |||
| 200 | sc = sp->script; | |||
| 201 | pfd[0].fd = sc->sh_master; | |||
| 202 | pfd[0].events = POLLIN0x0001; | |||
| 203 | switch (poll(pfd, 1, 5 * 1000)) { | |||
| 204 | case -1: /* Error or interrupt. */ | |||
| 205 | msgq(sp, M_SYSERR, "poll"); | |||
| 206 | goto prompterr; | |||
| 207 | case 0: /* Timeout */ | |||
| 208 | msgq(sp, M_ERR, "Error: timed out"); | |||
| 209 | goto prompterr; | |||
| 210 | default: /* Characters to read. */ | |||
| 211 | break; | |||
| 212 | } | |||
| 213 | ||||
| 214 | /* Read the characters. */ | |||
| 215 | more: len = sizeof(buf) - (endp - buf); | |||
| 216 | switch (nr = read(sc->sh_master, endp, len)) { | |||
| 217 | case 0: /* EOF. */ | |||
| 218 | msgq(sp, M_ERR, "Error: shell: EOF"); | |||
| 219 | goto prompterr; | |||
| 220 | case -1: /* Error or interrupt. */ | |||
| 221 | msgq(sp, M_SYSERR, "shell"); | |||
| 222 | goto prompterr; | |||
| 223 | default: | |||
| 224 | endp += nr; | |||
| 225 | break; | |||
| 226 | } | |||
| 227 | ||||
| 228 | /* If any complete lines, push them into the file. */ | |||
| 229 | for (p = t = buf; p < endp; ++p) { | |||
| 230 | value = KEY_VAL(sp, *p)((unsigned char)(*p) <= 254 ? (sp)->gp->special_key[ (unsigned char)(*p)] : (unsigned char)(*p) > (sp)->gp-> max_special ? 0 : v_key_val((sp),(*p))); | |||
| 231 | if (value == K_CR || value == K_NL) { | |||
| 232 | if (db_last(sp, &lline) || | |||
| 233 | db_append(sp, 0, lline, t, p - t)) | |||
| 234 | goto prompterr; | |||
| 235 | t = p + 1; | |||
| 236 | } | |||
| 237 | } | |||
| 238 | if (p > buf) { | |||
| 239 | memmove(buf, t, endp - t); | |||
| 240 | endp = buf + (endp - t); | |||
| 241 | } | |||
| 242 | if (endp == buf) | |||
| 243 | goto more; | |||
| 244 | ||||
| 245 | /* Wait up 1/10 of a second to make sure that we got it all. */ | |||
| 246 | switch (poll(pfd, 1, 100)) { | |||
| 247 | case -1: /* Error or interrupt. */ | |||
| 248 | msgq(sp, M_SYSERR, "poll"); | |||
| 249 | goto prompterr; | |||
| 250 | case 0: /* Timeout */ | |||
| 251 | break; | |||
| 252 | default: /* Characters to read. */ | |||
| 253 | goto more; | |||
| 254 | } | |||
| 255 | ||||
| 256 | /* Timed out, so theoretically we have a prompt. */ | |||
| 257 | llen = endp - buf; | |||
| 258 | endp = buf; | |||
| 259 | ||||
| 260 | /* Append the line into the file. */ | |||
| 261 | if (db_last(sp, &lline) || db_append(sp, 0, lline, buf, llen)) { | |||
| 262 | prompterr: sscr_end(sp); | |||
| 263 | return (1); | |||
| 264 | } | |||
| 265 | ||||
| 266 | return (sscr_setprompt(sp, buf, llen)); | |||
| 267 | } | |||
| 268 | ||||
| 269 | /* | |||
| 270 | * sscr_exec -- | |||
| 271 | * Take a line and hand it off to the shell. | |||
| 272 | * | |||
| 273 | * PUBLIC: int sscr_exec(SCR *, recno_t); | |||
| 274 | */ | |||
| 275 | int | |||
| 276 | sscr_exec(SCR *sp, recno_t lno) | |||
| 277 | { | |||
| 278 | SCRIPT *sc; | |||
| 279 | recno_t last_lno; | |||
| 280 | size_t blen, len, last_len, tlen; | |||
| 281 | int isempty, matchprompt, nw, rval; | |||
| 282 | char *bp, *p; | |||
| 283 | ||||
| 284 | /* If there's a prompt on the last line, append the command. */ | |||
| 285 | if (db_last(sp, &last_lno)) | |||
| ||||
| 286 | return (1); | |||
| 287 | if (db_get(sp, last_lno, DBG_FATAL0x001, &p, &last_len)) | |||
| 288 | return (1); | |||
| 289 | if (sscr_matchprompt(sp, p, last_len, &tlen) && tlen == 0) { | |||
| 290 | matchprompt = 1; | |||
| 291 | GET_SPACE_RET(sp, bp, blen, last_len + 128){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp == ((void *)0) || (((L__gp)->flags) & ((0x0100 )))) { (bp) = ((void *)0); (blen) = 0; { void *L__bincp; if ( ((last_len + 128)) > ((blen))) { if ((L__bincp = binc(((sp )), ((bp)), &((blen)), ((last_len + 128)))) == ((void *)0 )) return (1); ((bp)) = L__bincp; } }; } else { { void *L__bincp ; if (((last_len + 128)) > (L__gp->tmp_blen)) { if ((L__bincp = binc(((sp)), (L__gp->tmp_bp), &(L__gp->tmp_blen) , ((last_len + 128)))) == ((void *)0)) return (1); (L__gp-> tmp_bp) = L__bincp; } }; (bp) = L__gp->tmp_bp; (blen) = L__gp ->tmp_blen; (((L__gp)->flags) |= ((0x0100))); } }; | |||
| 292 | memmove(bp, p, last_len); | |||
| ||||
| 293 | } else | |||
| 294 | matchprompt = 0; | |||
| 295 | ||||
| 296 | /* Get something to execute. */ | |||
| 297 | if (db_eget(sp, lno, &p, &len, &isempty)) { | |||
| 298 | if (isempty) | |||
| 299 | goto empty; | |||
| 300 | goto err1; | |||
| 301 | } | |||
| 302 | ||||
| 303 | /* Empty lines aren't interesting. */ | |||
| 304 | if (len == 0) | |||
| 305 | goto empty; | |||
| 306 | ||||
| 307 | /* Delete any prompt. */ | |||
| 308 | if (sscr_matchprompt(sp, p, len, &tlen)) { | |||
| 309 | if (tlen == len) { | |||
| 310 | empty: msgq(sp, M_BERR, "No command to execute"); | |||
| 311 | goto err1; | |||
| 312 | } | |||
| 313 | p += (len - tlen); | |||
| 314 | len = tlen; | |||
| 315 | } | |||
| 316 | ||||
| 317 | /* Push the line to the shell. */ | |||
| 318 | sc = sp->script; | |||
| 319 | if ((nw = write(sc->sh_master, p, len)) != len) | |||
| 320 | goto err2; | |||
| 321 | rval = 0; | |||
| 322 | if (write(sc->sh_master, "\n", 1) != 1) { | |||
| 323 | err2: if (nw == 0) | |||
| 324 | errno(*__errno()) = EIO5; | |||
| 325 | msgq(sp, M_SYSERR, "shell"); | |||
| 326 | goto err1; | |||
| 327 | } | |||
| 328 | ||||
| 329 | if (matchprompt) { | |||
| 330 | ADD_SPACE_RET(sp, bp, blen, last_len + len){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) { (((L__gp)->flags) &= ~((0x0100))); { void *L__bincp ; if (((last_len + len)) > (L__gp->tmp_blen)) { if ((L__bincp = binc(((sp)), (L__gp->tmp_bp), &(L__gp->tmp_blen) , ((last_len + len)))) == ((void *)0)) return (1); (L__gp-> tmp_bp) = L__bincp; } }; (bp) = L__gp->tmp_bp; (blen) = L__gp ->tmp_blen; (((L__gp)->flags) |= ((0x0100))); } else { void *L__bincp; if (((last_len + len)) > ((blen))) { if ((L__bincp = binc(((sp)), ((bp)), &((blen)), ((last_len + len)))) == ((void *)0)) return (1); ((bp)) = L__bincp; } }; }; | |||
| 331 | memmove(bp + last_len, p, len); | |||
| 332 | if (db_set(sp, last_lno, bp, last_len + len)) | |||
| 333 | err1: rval = 1; | |||
| 334 | } | |||
| 335 | if (matchprompt) | |||
| 336 | FREE_SPACE(sp, bp, blen){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) (((L__gp)->flags) &= ~((0x0100))); else free(bp); }; | |||
| 337 | return (rval); | |||
| 338 | } | |||
| 339 | ||||
| 340 | /* | |||
| 341 | * sscr_check_input - | |||
| 342 | * Check for input from command input or scripting windows. | |||
| 343 | * | |||
| 344 | * PUBLIC: int sscr_check_input(SCR *sp); | |||
| 345 | */ | |||
| 346 | int | |||
| 347 | sscr_check_input(SCR *sp) | |||
| 348 | { | |||
| 349 | GS *gp; | |||
| 350 | SCR *tsp; | |||
| 351 | struct pollfd *pfd; | |||
| 352 | int nfds, rval; | |||
| 353 | ||||
| 354 | gp = sp->gp; | |||
| 355 | rval = 0; | |||
| 356 | ||||
| 357 | /* Allocate space for pfd. */ | |||
| 358 | nfds = 1; | |||
| 359 | TAILQ_FOREACH(tsp, &gp->dq, q)for((tsp) = ((&gp->dq)->tqh_first); (tsp) != ((void *)0); (tsp) = ((tsp)->q.tqe_next)) | |||
| 360 | if (F_ISSET(sp, SC_SCRIPT)(((sp)->flags) & ((0x01000000)))) | |||
| 361 | nfds++; | |||
| 362 | pfd = calloc(nfds, sizeof(struct pollfd)); | |||
| 363 | if (pfd == NULL((void *)0)) { | |||
| 364 | msgq(sp, M_SYSERR, "malloc"); | |||
| 365 | return (1); | |||
| 366 | } | |||
| 367 | ||||
| 368 | /* Setup events bitmasks. */ | |||
| 369 | pfd[0].fd = STDIN_FILENO0; | |||
| 370 | pfd[0].events = POLLIN0x0001; | |||
| 371 | nfds = 1; | |||
| 372 | TAILQ_FOREACH(tsp, &gp->dq, q)for((tsp) = ((&gp->dq)->tqh_first); (tsp) != ((void *)0); (tsp) = ((tsp)->q.tqe_next)) | |||
| 373 | if (F_ISSET(sp, SC_SCRIPT)(((sp)->flags) & ((0x01000000)))) { | |||
| 374 | pfd[nfds].fd = sp->script->sh_master; | |||
| 375 | pfd[nfds].events = POLLIN0x0001; | |||
| 376 | nfds++; | |||
| 377 | } | |||
| 378 | ||||
| 379 | loop: | |||
| 380 | /* Check for input. */ | |||
| 381 | switch (poll(pfd, nfds, INFTIM(-1))) { | |||
| 382 | case -1: | |||
| 383 | msgq(sp, M_SYSERR, "poll"); | |||
| 384 | rval = 1; | |||
| 385 | /* FALLTHROUGH */ | |||
| 386 | case 0: | |||
| 387 | goto done; | |||
| 388 | default: | |||
| 389 | break; | |||
| 390 | } | |||
| 391 | ||||
| 392 | /* Only insert from the scripting windows if no command input */ | |||
| 393 | if (!(pfd[0].revents & POLLIN0x0001)) { | |||
| 394 | nfds = 1; | |||
| 395 | TAILQ_FOREACH(tsp, &gp->dq, q)for((tsp) = ((&gp->dq)->tqh_first); (tsp) != ((void *)0); (tsp) = ((tsp)->q.tqe_next)) | |||
| 396 | if (F_ISSET(sp, SC_SCRIPT)(((sp)->flags) & ((0x01000000)))) { | |||
| 397 | if ((pfd[nfds].revents & POLLHUP0x0010) && sscr_end(sp)) | |||
| 398 | goto done; | |||
| 399 | if ((pfd[nfds].revents & POLLIN0x0001) && sscr_insert(sp)) | |||
| 400 | goto done; | |||
| 401 | nfds++; | |||
| 402 | } | |||
| 403 | goto loop; | |||
| 404 | } | |||
| 405 | done: | |||
| 406 | free(pfd); | |||
| 407 | return (rval); | |||
| 408 | } | |||
| 409 | ||||
| 410 | /* | |||
| 411 | * sscr_input -- | |||
| 412 | * Read any waiting shell input. | |||
| 413 | * | |||
| 414 | * PUBLIC: int sscr_input(SCR *); | |||
| 415 | */ | |||
| 416 | int | |||
| 417 | sscr_input(SCR *sp) | |||
| 418 | { | |||
| 419 | GS *gp; | |||
| 420 | struct pollfd *pfd; | |||
| 421 | int nfds, rval; | |||
| 422 | ||||
| 423 | gp = sp->gp; | |||
| 424 | rval = 0; | |||
| 425 | ||||
| 426 | /* Allocate space for pfd. */ | |||
| 427 | nfds = 0; | |||
| 428 | TAILQ_FOREACH(sp, &gp->dq, q)for((sp) = ((&gp->dq)->tqh_first); (sp) != ((void * )0); (sp) = ((sp)->q.tqe_next)) | |||
| 429 | if (F_ISSET(sp, SC_SCRIPT)(((sp)->flags) & ((0x01000000)))) | |||
| 430 | nfds++; | |||
| 431 | if (nfds == 0) | |||
| 432 | return (0); | |||
| 433 | pfd = calloc(nfds, sizeof(struct pollfd)); | |||
| 434 | if (pfd == NULL((void *)0)) { | |||
| 435 | msgq(sp, M_SYSERR, "malloc"); | |||
| 436 | return (1); | |||
| 437 | } | |||
| 438 | ||||
| 439 | /* Setup events bitmasks. */ | |||
| 440 | nfds = 0; | |||
| 441 | TAILQ_FOREACH(sp, &gp->dq, q)for((sp) = ((&gp->dq)->tqh_first); (sp) != ((void * )0); (sp) = ((sp)->q.tqe_next)) | |||
| 442 | if (F_ISSET(sp, SC_SCRIPT)(((sp)->flags) & ((0x01000000)))) { | |||
| 443 | pfd[nfds].fd = sp->script->sh_master; | |||
| 444 | pfd[nfds].events = POLLIN0x0001; | |||
| 445 | nfds++; | |||
| 446 | } | |||
| 447 | ||||
| 448 | loop: | |||
| 449 | /* Check for input. */ | |||
| 450 | switch (poll(pfd, nfds, 0)) { | |||
| 451 | case -1: | |||
| 452 | msgq(sp, M_SYSERR, "poll"); | |||
| 453 | rval = 1; | |||
| 454 | /* FALLTHROUGH */ | |||
| 455 | case 0: | |||
| 456 | goto done; | |||
| 457 | default: | |||
| 458 | break; | |||
| 459 | } | |||
| 460 | ||||
| 461 | /* Read the input. */ | |||
| 462 | nfds = 0; | |||
| 463 | TAILQ_FOREACH(sp, &gp->dq, q)for((sp) = ((&gp->dq)->tqh_first); (sp) != ((void * )0); (sp) = ((sp)->q.tqe_next)) | |||
| 464 | if (F_ISSET(sp, SC_SCRIPT)(((sp)->flags) & ((0x01000000)))) { | |||
| 465 | if ((pfd[nfds].revents & POLLHUP0x0010) && sscr_end(sp)) | |||
| 466 | goto done; | |||
| 467 | if ((pfd[nfds].revents & POLLIN0x0001) && sscr_insert(sp)) | |||
| 468 | goto done; | |||
| 469 | nfds++; | |||
| 470 | } | |||
| 471 | goto loop; | |||
| 472 | done: | |||
| 473 | free(pfd); | |||
| 474 | return (rval); | |||
| 475 | } | |||
| 476 | ||||
| 477 | /* | |||
| 478 | * sscr_insert -- | |||
| 479 | * Take a line from the shell and insert it into the file. | |||
| 480 | */ | |||
| 481 | static int | |||
| 482 | sscr_insert(SCR *sp) | |||
| 483 | { | |||
| 484 | CHAR_T *endp, *p, *t; | |||
| 485 | SCRIPT *sc; | |||
| 486 | struct pollfd pfd[1]; | |||
| 487 | recno_t lno; | |||
| 488 | size_t blen, len, tlen; | |||
| 489 | u_int value; | |||
| 490 | int nr, rval; | |||
| 491 | char *bp; | |||
| 492 | ||||
| 493 | /* Find out where the end of the file is. */ | |||
| 494 | if (db_last(sp, &lno)) | |||
| 495 | return (1); | |||
| 496 | ||||
| 497 | #define MINREAD1024 1024 | |||
| 498 | GET_SPACE_RET(sp, bp, blen, MINREAD){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp == ((void *)0) || (((L__gp)->flags) & ((0x0100 )))) { (bp) = ((void *)0); (blen) = 0; { void *L__bincp; if ( ((1024)) > ((blen))) { if ((L__bincp = binc(((sp)), ((bp)) , &((blen)), ((1024)))) == ((void *)0)) return (1); ((bp) ) = L__bincp; } }; } else { { void *L__bincp; if (((1024)) > (L__gp->tmp_blen)) { if ((L__bincp = binc(((sp)), (L__gp-> tmp_bp), &(L__gp->tmp_blen), ((1024)))) == ((void *)0) ) return (1); (L__gp->tmp_bp) = L__bincp; } }; (bp) = L__gp ->tmp_bp; (blen) = L__gp->tmp_blen; (((L__gp)->flags ) |= ((0x0100))); } }; | |||
| 499 | endp = bp; | |||
| 500 | ||||
| 501 | /* Read the characters. */ | |||
| 502 | rval = 1; | |||
| 503 | sc = sp->script; | |||
| 504 | more: switch (nr = read(sc->sh_master, endp, MINREAD1024)) { | |||
| 505 | case 0: /* EOF; shell just exited. */ | |||
| 506 | sscr_end(sp); | |||
| 507 | rval = 0; | |||
| 508 | goto ret; | |||
| 509 | case -1: /* Error or interrupt. */ | |||
| 510 | msgq(sp, M_SYSERR, "shell"); | |||
| 511 | goto ret; | |||
| 512 | default: | |||
| 513 | endp += nr; | |||
| 514 | break; | |||
| 515 | } | |||
| 516 | ||||
| 517 | /* Append the lines into the file. */ | |||
| 518 | for (p = t = bp; p < endp; ++p) { | |||
| 519 | value = KEY_VAL(sp, *p)((unsigned char)(*p) <= 254 ? (sp)->gp->special_key[ (unsigned char)(*p)] : (unsigned char)(*p) > (sp)->gp-> max_special ? 0 : v_key_val((sp),(*p))); | |||
| 520 | if (value == K_CR || value == K_NL) { | |||
| 521 | len = p - t; | |||
| 522 | if (db_append(sp, 1, lno++, t, len)) | |||
| 523 | goto ret; | |||
| 524 | t = p + 1; | |||
| 525 | } | |||
| 526 | } | |||
| 527 | if (p > t) { | |||
| 528 | len = p - t; | |||
| 529 | /* | |||
| 530 | * If the last thing from the shell isn't another prompt, wait | |||
| 531 | * up to 1/10 of a second for more stuff to show up, so that | |||
| 532 | * we don't break the output into two separate lines. Don't | |||
| 533 | * want to hang indefinitely because some program is hanging, | |||
| 534 | * confused the shell, or whatever. | |||
| 535 | */ | |||
| 536 | if (!sscr_matchprompt(sp, t, len, &tlen) || tlen != 0) { | |||
| 537 | pfd[0].fd = sc->sh_master; | |||
| 538 | pfd[0].events = POLLIN0x0001; | |||
| 539 | if (poll(pfd, 1, 100) > 0) { | |||
| 540 | memmove(bp, t, len); | |||
| 541 | endp = bp + len; | |||
| 542 | goto more; | |||
| 543 | } | |||
| 544 | } | |||
| 545 | if (sscr_setprompt(sp, t, len)) | |||
| 546 | return (1); | |||
| 547 | if (db_append(sp, 1, lno++, t, len)) | |||
| 548 | goto ret; | |||
| 549 | } | |||
| 550 | ||||
| 551 | /* The cursor moves to EOF. */ | |||
| 552 | sp->lno = lno; | |||
| 553 | sp->cno = len ? len - 1 : 0; | |||
| 554 | rval = vs_refresh(sp, 1); | |||
| 555 | ||||
| 556 | ret: FREE_SPACE(sp, bp, blen){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (bp) == L__gp->tmp_bp ) (((L__gp)->flags) &= ~((0x0100))); else free(bp); }; | |||
| 557 | return (rval); | |||
| 558 | } | |||
| 559 | ||||
| 560 | /* | |||
| 561 | * sscr_setprompt -- | |||
| 562 | * | |||
| 563 | * Set the prompt to the last line we got from the shell. | |||
| 564 | * | |||
| 565 | */ | |||
| 566 | static int | |||
| 567 | sscr_setprompt(SCR *sp, char *buf, size_t len) | |||
| 568 | { | |||
| 569 | SCRIPT *sc; | |||
| 570 | ||||
| 571 | sc = sp->script; | |||
| 572 | free(sc->sh_prompt); | |||
| 573 | MALLOC(sp, sc->sh_prompt, len + 1){ if (((sc->sh_prompt) = malloc(len + 1)) == ((void *)0)) msgq ((sp), M_SYSERR, ((void *)0)); }; | |||
| 574 | if (sc->sh_prompt == NULL((void *)0)) { | |||
| 575 | sscr_end(sp); | |||
| 576 | return (1); | |||
| 577 | } | |||
| 578 | memmove(sc->sh_prompt, buf, len); | |||
| 579 | sc->sh_prompt_len = len; | |||
| 580 | sc->sh_prompt[len] = '\0'; | |||
| 581 | return (0); | |||
| 582 | } | |||
| 583 | ||||
| 584 | /* | |||
| 585 | * sscr_matchprompt -- | |||
| 586 | * Check to see if a line matches the prompt. Nul's indicate | |||
| 587 | * parts that can change, in both content and size. | |||
| 588 | */ | |||
| 589 | static int | |||
| 590 | sscr_matchprompt(SCR *sp, char *lp, size_t line_len, size_t *lenp) | |||
| 591 | { | |||
| 592 | SCRIPT *sc; | |||
| 593 | size_t prompt_len; | |||
| 594 | char *pp; | |||
| 595 | ||||
| 596 | sc = sp->script; | |||
| 597 | if (line_len < (prompt_len = sc->sh_prompt_len)) | |||
| 598 | return (0); | |||
| 599 | ||||
| 600 | for (pp = sc->sh_prompt; | |||
| 601 | prompt_len && line_len; --prompt_len, --line_len) { | |||
| 602 | if (*pp == '\0') { | |||
| 603 | for (; prompt_len && *pp == '\0'; --prompt_len, ++pp); | |||
| 604 | if (!prompt_len) | |||
| 605 | return (0); | |||
| 606 | for (; line_len && *lp != *pp; --line_len, ++lp); | |||
| 607 | if (!line_len) | |||
| 608 | return (0); | |||
| 609 | } | |||
| 610 | if (*pp++ != *lp++) | |||
| 611 | break; | |||
| 612 | } | |||
| 613 | ||||
| 614 | if (prompt_len) | |||
| 615 | return (0); | |||
| 616 | if (lenp != NULL((void *)0)) | |||
| 617 | *lenp = line_len; | |||
| 618 | return (1); | |||
| 619 | } | |||
| 620 | ||||
| 621 | /* | |||
| 622 | * sscr_end -- | |||
| 623 | * End the pipe to a shell. | |||
| 624 | * | |||
| 625 | * PUBLIC: int sscr_end(SCR *); | |||
| 626 | */ | |||
| 627 | int | |||
| 628 | sscr_end(SCR *sp) | |||
| 629 | { | |||
| 630 | SCRIPT *sc; | |||
| 631 | ||||
| 632 | if ((sc = sp->script) == NULL((void *)0)) | |||
| 633 | return (0); | |||
| 634 | ||||
| 635 | /* Turn off the script flags. */ | |||
| 636 | F_CLR(sp, SC_SCRIPT)(((sp)->flags) &= ~((0x01000000))); | |||
| 637 | sscr_check(sp); | |||
| 638 | ||||
| 639 | /* Close down the parent's file descriptors. */ | |||
| 640 | if (sc->sh_master != -1) | |||
| 641 | (void)close(sc->sh_master); | |||
| 642 | if (sc->sh_slave != -1) | |||
| 643 | (void)close(sc->sh_slave); | |||
| 644 | ||||
| 645 | /* This should have killed the child. */ | |||
| 646 | (void)proc_wait(sp, sc->sh_pid, "script-shell", 0, 0); | |||
| 647 | ||||
| 648 | /* Free memory. */ | |||
| 649 | free(sc->sh_prompt); | |||
| 650 | free(sc); | |||
| 651 | sp->script = NULL((void *)0); | |||
| 652 | ||||
| 653 | return (0); | |||
| 654 | } | |||
| 655 | ||||
| 656 | /* | |||
| 657 | * sscr_check -- | |||
| 658 | * Set/clear the global scripting bit. | |||
| 659 | */ | |||
| 660 | static void | |||
| 661 | sscr_check(SCR *sp) | |||
| 662 | { | |||
| 663 | GS *gp; | |||
| 664 | ||||
| 665 | gp = sp->gp; | |||
| 666 | TAILQ_FOREACH(sp, &gp->dq, q)for((sp) = ((&gp->dq)->tqh_first); (sp) != ((void * )0); (sp) = ((sp)->q.tqe_next)) | |||
| 667 | if (F_ISSET(sp, SC_SCRIPT)(((sp)->flags) & ((0x01000000)))) { | |||
| 668 | F_SET(gp, G_SCRWIN)(((gp)->flags) |= ((0x0020))); | |||
| 669 | return; | |||
| 670 | } | |||
| 671 | F_CLR(gp, G_SCRWIN)(((gp)->flags) &= ~((0x0020))); | |||
| 672 | } |