3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
9 static char sccsid
[] = "$Id: ex_argv.c,v 8.26 1994/01/02 17:55:15 bostic Exp $ (Berkeley) $Date: 1994/01/02 17:55:15 $";
12 #include <sys/types.h>
23 static int argv_alloc
__P((SCR
*, size_t));
24 static int argv_fexp
__P((SCR
*, EXCMDARG
*,
25 char *, size_t, char *, size_t *, char **, size_t *, int));
26 static int argv_sexp
__P((SCR
*, char **, size_t *, size_t *));
30 * Build a prototype arguments list.
33 argv_init(sp
, ep
, excp
)
44 excp
->argv
= exp
->args
;
45 excp
->argc
= exp
->argsoff
;
51 * Put a string into an argv.
54 argv_exp0(sp
, ep
, excp
, cmd
, cmdlen
)
64 argv_alloc(sp
, cmdlen
);
65 memmove(exp
->args
[exp
->argsoff
]->bp
, cmd
, cmdlen
);
66 exp
->args
[exp
->argsoff
]->bp
[cmdlen
] = '\0';
67 exp
->args
[exp
->argsoff
]->len
= cmdlen
;
69 excp
->argv
= exp
->args
;
70 excp
->argc
= exp
->argsoff
;
76 * Do file name expansion on a string, and leave it in a string.
79 argv_exp1(sp
, ep
, excp
, cmd
, cmdlen
, is_bang
)
91 GET_SPACE_RET(sp
, bp
, blen
, 512);
95 if (argv_fexp(sp
, excp
, cmd
, cmdlen
, bp
, &len
, &bp
, &blen
, is_bang
)) {
96 FREE_SPACE(sp
, bp
, blen
);
101 memmove(exp
->args
[exp
->argsoff
]->bp
, bp
, len
);
102 exp
->args
[exp
->argsoff
]->bp
[len
] = '\0';
103 exp
->args
[exp
->argsoff
]->len
= len
;
105 excp
->argv
= exp
->args
;
106 excp
->argc
= exp
->argsoff
;
108 FREE_SPACE(sp
, bp
, blen
);
114 * Do file name and shell expansion on a string, and break
115 * it up into an argv.
118 argv_exp2(sp
, ep
, excp
, cmd
, cmdlen
, is_bang
)
130 GET_SPACE_RET(sp
, bp
, blen
, 512);
132 #define SHELLECHO "echo "
133 #define SHELLOFFSET (sizeof(SHELLECHO) - 1)
134 memmove(bp
, SHELLECHO
, SHELLOFFSET
);
135 p
= bp
+ SHELLOFFSET
;
138 #if defined(DEBUG) && 0
139 TRACE(sp
, "file_argv: {%.*s}\n", (int)cmdlen
, cmd
);
142 if (argv_fexp(sp
, excp
, cmd
, cmdlen
, p
, &len
, &bp
, &blen
, is_bang
)) {
147 #if defined(DEBUG) && 0
148 TRACE(sp
, "before shell: %d: {%s}\n", len
, bp
);
152 * Do shell word expansion -- it's very, very hard to figure out
153 * what magic characters the user's shell expects. If it's not
154 * pure vanilla, don't even try.
156 for (p
= bp
, n
= len
; n
> 0; --n
, ++p
)
157 if (!isalnum(*p
) && !isblank(*p
) && *p
!= '/' && *p
!= '.')
160 if (argv_sexp(sp
, &bp
, &blen
, &len
)) {
166 p
= bp
+ SHELLOFFSET
;
170 #if defined(DEBUG) && 0
171 TRACE(sp
, "after shell: %d: {%s}\n", len
, bp
);
174 rval
= argv_exp3(sp
, ep
, excp
, p
, len
);
176 err
: FREE_SPACE(sp
, bp
, blen
);
182 * Take a string and break it up into an argv.
185 argv_exp3(sp
, ep
, excp
, cmd
, cmdlen
)
198 (void)term_key_ch(sp
, K_VLNEXT
, &vlit
);
199 for (exp
= EXP(sp
); cmdlen
> 0; ++exp
->argsoff
) {
200 /* Skip any leading whitespace. */
201 for (; cmdlen
> 0; --cmdlen
, ++cmd
) {
210 * Determine the length of this whitespace delimited
215 * Skip any character preceded by the user's quoting
218 for (ap
= cmd
, len
= 0; cmdlen
> 0; ++cmd
, --cmdlen
, ++len
)
219 if ((ch
= *cmd
) == vlit
&& cmdlen
> 1) {
222 } else if (isblank(ch
))
226 * Copy the argument into place.
234 exp
->args
[off
]->len
= len
;
235 for (p
= exp
->args
[off
]->bp
; len
> 0; --len
, *p
++ = *ap
++)
238 --exp
->args
[off
]->len
;
242 excp
->argv
= exp
->args
;
243 excp
->argc
= exp
->argsoff
;
245 #if defined(DEBUG) && 0
246 for (cnt
= 0; cnt
< exp
->argsoff
; ++cnt
)
247 TRACE(sp
, "arg %d: {%s}\n", cnt
, exp
->argv
[cnt
]);
254 * Do file name and bang command expansion.
257 argv_fexp(sp
, excp
, cmd
, cmdlen
, p
, lenp
, bpp
, blenp
, is_bang
)
260 char *cmd
, *p
, **bpp
;
261 size_t cmdlen
, *lenp
, *blenp
;
266 size_t blen
, len
, tlen
;
268 /* Replace file name characters. */
269 for (bp
= *bpp
, blen
= *blenp
, len
= *lenp
; cmdlen
> 0; --cmdlen
, ++cmd
)
275 if (exp
->lastbcomm
== NULL
) {
277 "No previous command to replace \"!\".");
280 len
+= tlen
= strlen(exp
->lastbcomm
);
281 ADD_SPACE_RET(sp
, bp
, blen
, len
);
282 memmove(p
, exp
->lastbcomm
, tlen
);
284 F_SET(excp
, E_MODIFY
);
287 if (sp
->frp
->cname
== NULL
&& sp
->frp
->name
== NULL
) {
289 "No filename to substitute for %%.");
292 tlen
= strlen(t
= FILENAME(sp
->frp
));
294 ADD_SPACE_RET(sp
, bp
, blen
, len
);
297 F_SET(excp
, E_MODIFY
);
301 * Try the alternate file name first, then the
302 * previously edited file.
304 if (sp
->alt_name
== NULL
&& (sp
->p_frp
== NULL
||
305 sp
->frp
->cname
== NULL
&& sp
->frp
->name
== NULL
)) {
307 "No filename to substitute for #.");
310 if (sp
->alt_name
!= NULL
)
313 t
= FILENAME(sp
->frp
);
314 len
+= tlen
= strlen(t
);
315 ADD_SPACE_RET(sp
, bp
, blen
, len
);
318 F_SET(excp
, E_MODIFY
);
324 * Strip any backslashes that protected the file
325 * expansion characters.
327 if (cmdlen
> 1 && cmd
[1] == '%' || cmd
[1] == '#')
332 ADD_SPACE_RET(sp
, bp
, blen
, len
);
336 /* Nul termination. */
338 ADD_SPACE_RET(sp
, bp
, blen
, len
);
341 /* Return the new string length, buffer, buffer length. */
350 * Make more space for arguments.
362 * Allocate room for another argument, always leaving
363 * enough room for an ARGS structure with a length of 0.
368 if (exp
->argscnt
== 0 || off
+ 2 >= exp
->argscnt
- 1) {
369 cnt
= exp
->argscnt
+ INCREMENT
;
370 REALLOC(sp
, exp
->args
, ARGS
**, cnt
* sizeof(ARGS
*));
371 if (exp
->args
== NULL
) {
375 memset(&exp
->args
[off
], 0, INCREMENT
* sizeof(ARGS
*));
379 /* First argument. */
380 if (exp
->args
[off
] == NULL
) {
381 CALLOC(sp
, exp
->args
[off
], ARGS
*, 1, sizeof(ARGS
));
382 if (exp
->args
[off
] == NULL
)
386 /* First argument buffer. */
389 if (ap
->blen
< len
+ 1) {
391 REALLOC(sp
, ap
->bp
, CHAR_T
*, ap
->blen
* sizeof(CHAR_T
));
392 if (ap
->bp
== NULL
) {
395 F_CLR(ap
, A_ALLOCATED
);
396 mem
: msgq(sp
, M_SYSERR
, NULL
);
399 F_SET(ap
, A_ALLOCATED
);
402 /* Second argument. */
403 if (exp
->args
[++off
] == NULL
) {
404 CALLOC(sp
, exp
->args
[off
], ARGS
*, 1, sizeof(ARGS
));
405 if (exp
->args
[off
] == NULL
)
408 /* 0 length serves as end-of-argument marker. */
409 exp
->args
[off
]->len
= 0;
415 * Free up argument structures.
425 if (exp
->args
!= NULL
) {
426 for (off
= 0; off
< exp
->argscnt
; ++off
) {
427 if (exp
->args
[off
] == NULL
)
429 if (F_ISSET(exp
->args
[off
], A_ALLOCATED
))
430 free(exp
->args
[off
]->bp
);
431 FREE(exp
->args
[off
], sizeof(ARGS
));
433 FREE(exp
->args
, exp
->argscnt
* sizeof(ARGS
*));
443 * Fork a shell, pipe a command through it, and read the output into
447 argv_sexp(sp
, bpp
, blenp
, lenp
)
450 size_t *blenp
, *lenp
;
455 int ch
, rval
, output
[2];
456 char *bp
, *p
, *sh
, *sh_path
;
461 sh_path
= O_STR(sp
, O_SHELL
);
462 if ((sh
= strrchr(sh_path
, '/')) == NULL
)
468 * There are two different processes running through this code.
469 * They are named the utility and the parent. The utility reads
470 * from standard input and writes to the parent. The parent reads
471 * from the utility and writes into the buffer. The parent reads
472 * from output[0], and the utility writes to output[1].
474 if (pipe(output
) < 0) {
475 msgq(sp
, M_SYSERR
, "pipe");
478 if ((ifp
= fdopen(output
[0], "r")) == NULL
) {
479 msgq(sp
, M_SYSERR
, "fdopen");
484 * Do the minimal amount of work possible, the shell is going
485 * to run briefly and then exit. Hopefully.
487 switch (pid
= vfork()) {
488 case -1: /* Error. */
489 msgq(sp
, M_SYSERR
, "vfork");
490 err
: (void)close(output
[0]);
491 (void)close(output
[1]);
493 case 0: /* Utility. */
494 /* Redirect stdout/stderr to the write end of the pipe. */
495 (void)dup2(output
[1], STDOUT_FILENO
);
496 (void)dup2(output
[1], STDERR_FILENO
);
498 /* Close the utility's file descriptors. */
499 (void)close(output
[0]);
500 (void)close(output
[1]);
502 /* Assumes that all shells have -c. */
503 execl(sh_path
, sh
, "-c", bp
, NULL
);
505 "Error: execl: %s: %s", sh_path
, strerror(errno
));
508 /* Close the pipe end the parent won't use. */
509 (void)close(output
[1]);
516 * Copy process output into a buffer.
519 * Historic vi apparently discarded leading \n and \r's from
520 * the shell output stream. We don't on the grounds that any
521 * shell that does that is broken.
523 for (p
= bp
, len
= 0, ch
= EOF
;
524 (ch
= getc(ifp
)) != EOF
; *p
++ = ch
, --blen
, ++len
)
526 ADD_SPACE_GOTO(sp
, bp
, blen
, *blenp
* 2);
531 /* Delete the final newline, nul terminate the string. */
532 if (p
> bp
&& p
[-1] == '\n' || p
[-1] == '\r') {
540 msgq(sp
, M_ERR
, "I/O error: %s", sh
);
545 *bpp
= bp
; /* *blenp is already updated. */
547 /* Wait for the process. */
548 return (proc_wait(sp
, (long)pid
, sh
, 0) | rval
);