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.18 1993/11/29 14:15:16 bostic Exp $ (Berkeley) $Date: 1993/11/29 14:15:16 $";
12 #include <sys/types.h>
24 static int argv_allocate
__P((SCR
*, int));
25 static int argv_fexp
__P((SCR
*, EXCMDARG
*,
26 char *, char *, size_t *, char **, size_t *, int));
27 static int argv_sexp
__P((SCR
*, char *, size_t, size_t *));
29 #define ARGALLOC(off, len) \
30 if (exp->args[off].len < len) { \
31 if ((exp->args[off].bp = \
32 realloc(exp->args[off].bp, len)) == NULL) { \
33 exp->args[off].bp = NULL; \
34 exp->args[off].len = 0; \
35 msgq(sp, M_SYSERR, NULL); \
38 exp->args[off].len = len; \
39 exp->args[off].flags |= A_ALLOCATED; \
44 * Do file name expansion on a string, and leave it in a string.
47 argv_exp1(sp
, ep
, cmdp
, s
, is_bang
)
58 GET_SPACE(sp
, bp
, blen
, 512);
62 if (argv_fexp(sp
, cmdp
, s
, bp
, &len
, &bp
, &blen
, is_bang
) ||
63 exp
->argscnt
< 2 && argv_allocate(sp
, 0)) {
64 FREE_SPACE(sp
, bp
, blen
);
69 memmove(exp
->args
[0].bp
, bp
, len
);
70 exp
->argv
[0] = exp
->args
[0].bp
;
73 FREE_SPACE(sp
, bp
, blen
);
75 cmdp
->argv
= exp
->argv
;
82 * Do file name and shell expansion on a string, and break
86 argv_exp2(sp
, ep
, cmdp
, s
, is_bang
)
97 GET_SPACE(sp
, bp
, blen
, 512);
99 #define SHELLECHO "echo "
100 #define SHELLOFFSET (sizeof(SHELLECHO) - 1)
101 memmove(bp
, SHELLECHO
, SHELLOFFSET
);
102 p
= bp
+ SHELLOFFSET
;
105 #if defined(DEBUG) && 0
106 TRACE(sp
, "file_argv: {%s}\n", s
);
109 if (argv_fexp(sp
, cmdp
, s
, p
, &len
, &bp
, &blen
, is_bang
)) {
114 #if defined(DEBUG) && 0
115 TRACE(sp
, "before shell: %d: {%s}\n", len
, bp
);
119 * Do shell word expansion -- it's very, very hard to figure out
120 * what magic characters the user's shell expects. If it's not
121 * pure vanilla, don't even try.
123 for (p
= bp
; *p
; ++p
)
124 if (!isalnum(*p
) && !isblank(*p
) && *p
!= '/' && *p
!= '.')
127 if (argv_sexp(sp
, bp
, blen
, &len
)) {
133 p
= bp
+ SHELLOFFSET
;
135 #if defined(DEBUG) && 0
136 TRACE(sp
, "after shell: %d: {%s}\n", len
, bp
);
139 rval
= argv_exp3(sp
, ep
, cmdp
, p
);
141 err
: FREE_SPACE(sp
, bp
, blen
);
147 * Take a string and break it up into an argv.
150 argv_exp3(sp
, ep
, cmdp
, p
)
162 for (done
= off
= 0;; ++p
) {
163 /* Skip any leading whitespace. */
164 for (; isblank(p
[0]); ++p
);
171 * New argument; NULL terminate, skipping anything
172 * that's preceded by the user's quoting character.
174 for (ap
= p
; p
[0] != '\0'; ++p
) {
175 if (term_key_val(sp
, p
[0]) == K_VLNEXT
&& p
[1])
186 * Allocate more pointer space if necessary, making
187 * sure there's space for a trailing NULL pointer.
189 if (off
+ 2 >= exp
->argscnt
- 1 && argv_allocate(sp
, off
))
192 /* Allocate more argument space if necessary. */
195 exp
->argv
[off
] = exp
->args
[off
].bp
;
200 * Copy the argument into place, losing quote chars.
202 for (t
= exp
->args
[off
].bp
; len
; *t
++ = *ap
++, --len
)
203 if (term_key_val(sp
, *ap
) == K_VLNEXT
) {
212 exp
->argv
[off
] = NULL
;
213 cmdp
->argv
= exp
->argv
;
216 #if defined(DEBUG) && 0
217 for (cnt
= 0; cnt
< off
; ++cnt
)
218 TRACE(sp
, "arg %d: {%s}\n", cnt
, exp
->argv
[cnt
]);
225 * Do file name and bang command expansion.
228 argv_fexp(sp
, cmdp
, s
, p
, lenp
, bpp
, blenp
, is_bang
)
232 size_t *lenp
, *blenp
;
237 size_t blen
, len
, tlen
;
239 /* Replace file name characters. */
240 for (bp
= *bpp
, blen
= *blenp
, len
= *lenp
; *s
; ++s
)
246 if (exp
->lastbcomm
== NULL
) {
248 "No previous command to replace \"!\".");
251 len
+= tlen
= strlen(exp
->lastbcomm
);
252 ADD_SPACE(sp
, bp
, blen
, len
);
253 memmove(p
, exp
->lastbcomm
, tlen
);
255 F_SET(cmdp
, E_MODIFY
);
258 if (sp
->frp
->cname
== NULL
&& sp
->frp
->name
== NULL
) {
260 "No filename to substitute for %%.");
265 * t = FILENAME(sp->frp);
267 if (sp
->frp
->cname
!= NULL
) {
269 tlen
= sp
->frp
->clen
;
272 tlen
= sp
->frp
->nlen
;
275 ADD_SPACE(sp
, bp
, blen
, len
);
278 F_SET(cmdp
, E_MODIFY
);
282 * Try the alternate file name first, then the
283 * previously edited file.
285 if (sp
->alt_name
== NULL
&& (sp
->p_frp
== NULL
||
286 sp
->frp
->cname
== NULL
&& sp
->frp
->name
== NULL
)) {
288 "No filename to substitute for #.");
293 * t = sp->alt_name == NULL ?
294 * FILENAME(sp->frp): sp->alt_name;
296 if (sp
->alt_name
!= NULL
) {
299 } else if (sp
->frp
->cname
!= NULL
) {
301 tlen
= sp
->frp
->clen
;
304 tlen
= sp
->frp
->nlen
;
307 ADD_SPACE(sp
, bp
, blen
, len
);
310 F_SET(cmdp
, E_MODIFY
);
316 * Strip any backslashes that protected the file
317 * expansion characters.
319 if (s
[1] == '%' || s
[1] == '#')
324 ADD_SPACE(sp
, bp
, blen
, len
);
328 /* Nul termination. */
330 ADD_SPACE(sp
, bp
, blen
, len
);
333 /* Return the new string length, buffer, buffer length. */
342 * Make more space for arguments.
345 argv_allocate(sp
, off
)
354 cnt
= exp
->argscnt
+ INCREMENT
;
355 if ((exp
->args
= realloc(exp
->args
, cnt
* sizeof(ARGS
))) == NULL
) {
360 realloc(exp
->argv
, cnt
* sizeof(char *))) == NULL
) {
362 mem
: msgq(sp
, M_SYSERR
, NULL
);
365 memset(&exp
->args
[off
], 0, INCREMENT
* sizeof(ARGS
));
372 * Free up argument structures.
382 if (exp
->args
!= NULL
) {
383 for (off
= 0; off
< exp
->argscnt
; ++off
)
384 if (F_ISSET(&exp
->args
[off
], A_ALLOCATED
))
385 FREE(exp
->args
[off
].bp
, exp
->args
[off
].len
);
386 FREE(exp
->args
, exp
->argscnt
* sizeof(ARGS
*));
388 if (exp
->argv
!= NULL
)
389 FREE(exp
->argv
, exp
->argscnt
* sizeof(char *));
396 * Fork a shell, pipe a command through it, and read the output into
400 argv_sexp(sp
, bp
, blen
, lenp
)
408 int ch
, rval
, output
[2];
409 char *p
, *sh
, *sh_path
;
411 sh_path
= O_STR(sp
, O_SHELL
);
412 if ((sh
= strrchr(sh_path
, '/')) == NULL
)
418 * There are two different processes running through this code.
419 * They are named the utility and the parent. The utility reads
420 * from standard input and writes to the parent. The parent reads
421 * from the utility and writes into the buffer. The parent reads
422 * from output[0], and the utility writes to output[1].
424 if (pipe(output
) < 0) {
425 msgq(sp
, M_SYSERR
, "pipe");
428 if ((ifp
= fdopen(output
[0], "r")) == NULL
) {
429 msgq(sp
, M_SYSERR
, "fdopen");
434 * Do the minimal amount of work possible, the shell is going
435 * to run briefly and then exit. Hopefully.
437 switch (pid
= vfork()) {
438 case -1: /* Error. */
439 msgq(sp
, M_SYSERR
, "vfork");
440 err
: (void)close(output
[0]);
441 (void)close(output
[1]);
443 case 0: /* Utility. */
444 /* Redirect stdout/stderr to the write end of the pipe. */
445 (void)dup2(output
[1], STDOUT_FILENO
);
446 (void)dup2(output
[1], STDERR_FILENO
);
448 /* Close the utility's file descriptors. */
449 (void)close(output
[0]);
450 (void)close(output
[1]);
452 /* Assumes that all shells have -c. */
453 execl(sh_path
, sh
, "-c", bp
, NULL
);
455 "Error: execl: %s: %s", sh_path
, strerror(errno
));
458 /* Close the pipe end the parent won't use. */
459 (void)close(output
[1]);
465 /* Copy process output into a buffer. */
466 for (p
= bp
, len
= 0, ch
= EOF
;
467 --blen
&& (ch
= getc(ifp
)) != EOF
; *p
++ = ch
, ++len
);
470 msgq(sp
, M_ERR
, "%s: output truncated", sh
);
474 msgq(sp
, M_ERR
, "I/O error: %s", sh
);
478 /* Wait for the process. */
479 rval
|= proc_wait(sp
, (long)pid
, sh
, 0);
481 /* Delete the final newline, nul terminate the string. */
482 if (p
> bp
&& p
[-1] == '\n' || p
[-1] == '\r') {