dol.c revision 1.2 1 /*-
2 * Copyright (c) 1980, 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 static char sccsid[] = "@(#)dol.c 5.13 (Berkeley) 6/8/91";
36 static char rcsid[] = "$Id: dol.c,v 1.2 1993/03/22 08:04:00 cgd Exp $";
37 #endif /* not lint */
38
39 #include <sys/types.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #if __STDC__
46 # include <stdarg.h>
47 #else
48 # include <varargs.h>
49 #endif
50
51 #include "csh.h"
52 #include "extern.h"
53
54 /*
55 * These routines perform variable substitution and quoting via ' and ".
56 * To this point these constructs have been preserved in the divided
57 * input words. Here we expand variables and turn quoting via ' and " into
58 * QUOTE bits on characters (which prevent further interpretation).
59 * If the `:q' modifier was applied during history expansion, then
60 * some QUOTEing may have occurred already, so we dont "trim()" here.
61 */
62
63 static int Dpeekc, Dpeekrd; /* Peeks for DgetC and Dreadc */
64 static Char *Dcp, **Dvp; /* Input vector for Dreadc */
65
66 #define DEOF -1
67
68 #define unDgetC(c) Dpeekc = c
69
70 #define QUOTES (_Q|_Q1|_ESC) /* \ ' " ` */
71
72 /*
73 * The following variables give the information about the current
74 * $ expansion, recording the current word position, the remaining
75 * words within this expansion, the count of remaining words, and the
76 * information about any : modifier which is being applied.
77 */
78 static Char *dolp; /* Remaining chars from this word */
79 static Char **dolnxt; /* Further words */
80 static int dolcnt; /* Count of further words */
81 static Char dolmod; /* : modifier character */
82 static int dolmcnt; /* :gx -> 10000, else 1 */
83
84 static void Dfix2 __P((Char **));
85 static Char *Dpack __P((Char *, Char *));
86 static int Dword __P((void));
87 static void dolerror __P((Char *));
88 static int DgetC __P((int));
89 static void Dgetdol __P((void));
90 static void fixDolMod __P((void));
91 static void setDolp __P((Char *));
92 static void unDredc __P((int));
93 static int Dredc __P((void));
94 static void Dtestq __P((int));
95
96
97 /*
98 * Fix up the $ expansions and quotations in the
99 * argument list to command t.
100 */
101 void
102 Dfix(t)
103 register struct command *t;
104 {
105 register Char **pp;
106 register Char *p;
107
108 if (noexec)
109 return;
110 /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */
111 for (pp = t->t_dcom; p = *pp++;)
112 for (; *p; p++) {
113 if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */
114 Dfix2(t->t_dcom); /* found one */
115 blkfree(t->t_dcom);
116 t->t_dcom = gargv;
117 gargv = 0;
118 return;
119 }
120 }
121 }
122
123 /*
124 * $ substitute one word, for i/o redirection
125 */
126 Char *
127 Dfix1(cp)
128 register Char *cp;
129 {
130 Char *Dv[2];
131
132 if (noexec)
133 return (0);
134 Dv[0] = cp;
135 Dv[1] = NULL;
136 Dfix2(Dv);
137 if (gargc != 1) {
138 setname(short2str(cp));
139 stderror(ERR_NAME | ERR_AMBIG);
140 }
141 cp = Strsave(gargv[0]);
142 blkfree(gargv), gargv = 0;
143 return (cp);
144 }
145
146 /*
147 * Subroutine to do actual fixing after state initialization.
148 */
149 static void
150 Dfix2(v)
151 Char **v;
152 {
153 ginit(); /* Initialize glob's area pointers */
154 Dvp = v;
155 Dcp = STRNULL; /* Setup input vector for Dreadc */
156 unDgetC(0);
157 unDredc(0); /* Clear out any old peeks (at error) */
158 dolp = 0;
159 dolcnt = 0; /* Clear out residual $ expands (...) */
160 while (Dword())
161 continue;
162 }
163
164 #define MAXWLEN (BUFSIZ - 4)
165 /*
166 * Pack up more characters in this word
167 */
168 static Char *
169 Dpack(wbuf, wp)
170 Char *wbuf, *wp;
171 {
172 register int c;
173 register int i = MAXWLEN - (wp - wbuf);
174
175 for (;;) {
176 c = DgetC(DODOL);
177 if (c == '\\') {
178 c = DgetC(0);
179 if (c == DEOF) {
180 unDredc(c);
181 *wp = 0;
182 Gcat(STRNULL, wbuf);
183 return (NULL);
184 }
185 if (c == '\n')
186 c = ' ';
187 else
188 c |= QUOTE;
189 }
190 if (c == DEOF) {
191 unDredc(c);
192 *wp = 0;
193 Gcat(STRNULL, wbuf);
194 return (NULL);
195 }
196 if (cmap(c, _SP | _NL | _Q | _Q1)) { /* sp \t\n'"` */
197 unDgetC(c);
198 if (cmap(c, QUOTES))
199 return (wp);
200 *wp++ = 0;
201 Gcat(STRNULL, wbuf);
202 return (NULL);
203 }
204 if (--i <= 0)
205 stderror(ERR_WTOOLONG);
206 *wp++ = c;
207 }
208 }
209
210 /*
211 * Get a word. This routine is analogous to the routine
212 * word() in sh.lex.c for the main lexical input. One difference
213 * here is that we don't get a newline to terminate our expansion.
214 * Rather, DgetC will return a DEOF when we hit the end-of-input.
215 */
216 static int
217 Dword()
218 {
219 register int c, c1;
220 Char wbuf[BUFSIZ];
221 register Char *wp = wbuf;
222 register int i = MAXWLEN;
223 register bool dolflg;
224 bool sofar = 0, done = 0;
225
226 while (!done) {
227 done = 1;
228 c = DgetC(DODOL);
229 switch (c) {
230
231 case DEOF:
232 if (sofar == 0)
233 return (0);
234 /* finish this word and catch the code above the next time */
235 unDredc(c);
236 /* fall into ... */
237
238 case '\n':
239 *wp = 0;
240 Gcat(STRNULL, wbuf);
241 return (1);
242
243 case ' ':
244 case '\t':
245 done = 0;
246 break;
247
248 case '`':
249 /* We preserve ` quotations which are done yet later */
250 *wp++ = c, --i;
251 case '\'':
252 case '"':
253 /*
254 * Note that DgetC never returns a QUOTES character from an
255 * expansion, so only true input quotes will get us here or out.
256 */
257 c1 = c;
258 dolflg = c1 == '"' ? DODOL : 0;
259 for (;;) {
260 c = DgetC(dolflg);
261 if (c == c1)
262 break;
263 if (c == '\n' || c == DEOF)
264 stderror(ERR_UNMATCHED, c1);
265 if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE))
266 --wp, ++i;
267 if (--i <= 0)
268 stderror(ERR_WTOOLONG);
269 switch (c1) {
270
271 case '"':
272 /*
273 * Leave any `s alone for later. Other chars are all
274 * quoted, thus `...` can tell it was within "...".
275 */
276 *wp++ = c == '`' ? '`' : c | QUOTE;
277 break;
278
279 case '\'':
280 /* Prevent all further interpretation */
281 *wp++ = c | QUOTE;
282 break;
283
284 case '`':
285 /* Leave all text alone for later */
286 *wp++ = c;
287 break;
288 }
289 }
290 if (c1 == '`')
291 *wp++ = '`', --i;
292 sofar = 1;
293 if ((wp = Dpack(wbuf, wp)) == NULL)
294 return (1);
295 else {
296 i = MAXWLEN - (wp - wbuf);
297 done = 0;
298 }
299 break;
300
301 case '\\':
302 c = DgetC(0); /* No $ subst! */
303 if (c == '\n' || c == DEOF) {
304 done = 0;
305 break;
306 }
307 c |= QUOTE;
308 break;
309 }
310 if (done) {
311 unDgetC(c);
312 sofar = 1;
313 if ((wp = Dpack(wbuf, wp)) == NULL)
314 return (1);
315 else {
316 i = MAXWLEN - (wp - wbuf);
317 done = 0;
318 }
319 }
320 }
321 /* Really NOTREACHED */
322 return (0);
323 }
324
325
326 /*
327 * Get a character, performing $ substitution unless flag is 0.
328 * Any QUOTES character which is returned from a $ expansion is
329 * QUOTEd so that it will not be recognized above.
330 */
331 static int
332 DgetC(flag)
333 register int flag;
334 {
335 register int c;
336
337 top:
338 if (c = Dpeekc) {
339 Dpeekc = 0;
340 return (c);
341 }
342 if (lap) {
343 c = *lap++ & (QUOTE | TRIM);
344 if (c == 0) {
345 lap = 0;
346 goto top;
347 }
348 quotspec:
349 if (cmap(c, QUOTES))
350 return (c | QUOTE);
351 return (c);
352 }
353 if (dolp) {
354 if (c = *dolp++ & (QUOTE | TRIM))
355 goto quotspec;
356 if (dolcnt > 0) {
357 setDolp(*dolnxt++);
358 --dolcnt;
359 return (' ');
360 }
361 dolp = 0;
362 }
363 if (dolcnt > 0) {
364 setDolp(*dolnxt++);
365 --dolcnt;
366 goto top;
367 }
368 c = Dredc();
369 if (c == '$' && flag) {
370 Dgetdol();
371 goto top;
372 }
373 return (c);
374 }
375
376 static Char *nulvec[] = {0};
377 static struct varent nulargv = {nulvec, STRargv, 0};
378
379 static void
380 dolerror(s)
381 Char *s;
382 {
383 setname(short2str(s));
384 stderror(ERR_NAME | ERR_RANGE);
385 }
386
387 /*
388 * Handle the multitudinous $ expansion forms.
389 * Ugh.
390 */
391 static void
392 Dgetdol()
393 {
394 register Char *np;
395 register struct varent *vp = NULL;
396 Char name[4 * MAXVARLEN + 1];
397 int c, sc;
398 int subscr = 0, lwb = 1, upb = 0;
399 bool dimen = 0, bitset = 0;
400 char tnp;
401 Char wbuf[BUFSIZ];
402
403 dolmod = dolmcnt = 0;
404 c = sc = DgetC(0);
405 if (c == '{')
406 c = DgetC(0); /* sc is { to take } later */
407 if ((c & TRIM) == '#')
408 dimen++, c = DgetC(0); /* $# takes dimension */
409 else if (c == '?')
410 bitset++, c = DgetC(0); /* $? tests existence */
411 switch (c) {
412
413 case '$':
414 if (dimen || bitset)
415 stderror(ERR_SYNTAX);
416 setDolp(doldol);
417 goto eatbrac;
418
419 case '<' | QUOTE:
420 if (bitset)
421 stderror(ERR_NOTALLOWED, "$?<");
422 if (dimen)
423 stderror(ERR_NOTALLOWED, "$?#");
424 for (np = wbuf; read(OLDSTD, &tnp, 1) == 1; np++) {
425 *np = tnp;
426 if (np >= &wbuf[BUFSIZ - 1])
427 stderror(ERR_LTOOLONG);
428 if (SIGN_EXTEND_CHAR(tnp) <= 0 || tnp == '\n')
429 break;
430 }
431 *np = 0;
432 /*
433 * KLUDGE: dolmod is set here because it will cause setDolp to call
434 * domod and thus to copy wbuf. Otherwise setDolp would use it
435 * directly. If we saved it ourselves, no one would know when to free
436 * it. The actual function of the 'q' causes filename expansion not to
437 * be done on the interpolated value.
438 */
439 dolmod = 'q';
440 dolmcnt = 10000;
441 setDolp(wbuf);
442 goto eatbrac;
443
444 case DEOF:
445 case '\n':
446 stderror(ERR_SYNTAX);
447 /* NOTREACHED */
448 break;
449
450 case '*':
451 (void) Strcpy(name, STRargv);
452 vp = adrof(STRargv);
453 subscr = -1; /* Prevent eating [...] */
454 break;
455
456 default:
457 np = name;
458 if (Isdigit(c)) {
459 if (dimen)
460 stderror(ERR_NOTALLOWED, "$#<num>");
461 subscr = 0;
462 do {
463 subscr = subscr * 10 + c - '0';
464 c = DgetC(0);
465 } while (Isdigit(c));
466 unDredc(c);
467 if (subscr < 0) {
468 dolerror(vp->v_name);
469 return;
470 }
471 if (subscr == 0) {
472 if (bitset) {
473 dolp = ffile ? STR1 : STR0;
474 goto eatbrac;
475 }
476 if (ffile == 0)
477 stderror(ERR_DOLZERO);
478 fixDolMod();
479 setDolp(ffile);
480 goto eatbrac;
481 }
482 if (bitset)
483 stderror(ERR_DOLQUEST);
484 vp = adrof(STRargv);
485 if (vp == 0) {
486 vp = &nulargv;
487 goto eatmod;
488 }
489 break;
490 }
491 if (!alnum(c))
492 stderror(ERR_VARALNUM);
493 for (;;) {
494 *np++ = c;
495 c = DgetC(0);
496 if (!alnum(c))
497 break;
498 if (np >= &name[MAXVARLEN])
499 stderror(ERR_VARTOOLONG);
500 }
501 *np++ = 0;
502 unDredc(c);
503 vp = adrof(name);
504 }
505 if (bitset) {
506 dolp = (vp || getenv(short2str(name))) ? STR1 : STR0;
507 goto eatbrac;
508 }
509 if (vp == 0) {
510 np = str2short(getenv(short2str(name)));
511 if (np) {
512 fixDolMod();
513 setDolp(np);
514 goto eatbrac;
515 }
516 udvar(name);
517 /* NOTREACHED */
518 }
519 c = DgetC(0);
520 upb = blklen(vp->vec);
521 if (dimen == 0 && subscr == 0 && c == '[') {
522 np = name;
523 for (;;) {
524 c = DgetC(DODOL); /* Allow $ expand within [ ] */
525 if (c == ']')
526 break;
527 if (c == '\n' || c == DEOF)
528 stderror(ERR_INCBR);
529 if (np >= &name[sizeof(name) / sizeof(Char) - 2])
530 stderror(ERR_VARTOOLONG);
531 *np++ = c;
532 }
533 *np = 0, np = name;
534 if (dolp || dolcnt) /* $ exp must end before ] */
535 stderror(ERR_EXPORD);
536 if (!*np)
537 stderror(ERR_SYNTAX);
538 if (Isdigit(*np)) {
539 int i;
540
541 for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0');
542 if ((i < 0 || i > upb) && !any("-*", *np)) {
543 dolerror(vp->v_name);
544 return;
545 }
546 lwb = i;
547 if (!*np)
548 upb = lwb, np = STRstar;
549 }
550 if (*np == '*')
551 np++;
552 else if (*np != '-')
553 stderror(ERR_MISSING, '-');
554 else {
555 register int i = upb;
556
557 np++;
558 if (Isdigit(*np)) {
559 i = 0;
560 while (Isdigit(*np))
561 i = i * 10 + *np++ - '0';
562 if (i < 0 || i > upb) {
563 dolerror(vp->v_name);
564 return;
565 }
566 }
567 if (i < lwb)
568 upb = lwb - 1;
569 else
570 upb = i;
571 }
572 if (lwb == 0) {
573 if (upb != 0) {
574 dolerror(vp->v_name);
575 return;
576 }
577 upb = -1;
578 }
579 if (*np)
580 stderror(ERR_SYNTAX);
581 }
582 else {
583 if (subscr > 0)
584 if (subscr > upb)
585 lwb = 1, upb = 0;
586 else
587 lwb = upb = subscr;
588 unDredc(c);
589 }
590 if (dimen) {
591 Char *cp = putn(upb - lwb + 1);
592
593 addla(cp);
594 xfree((ptr_t) cp);
595 }
596 else {
597 eatmod:
598 fixDolMod();
599 dolnxt = &vp->vec[lwb - 1];
600 dolcnt = upb - lwb + 1;
601 }
602 eatbrac:
603 if (sc == '{') {
604 c = Dredc();
605 if (c != '}')
606 stderror(ERR_MISSING, '}');
607 }
608 }
609
610 static void
611 fixDolMod()
612 {
613 register int c;
614
615 c = DgetC(0);
616 if (c == ':') {
617 c = DgetC(0), dolmcnt = 1;
618 if (c == 'g')
619 c = DgetC(0), dolmcnt = 10000;
620 if (!any("htrqxe", c))
621 stderror(ERR_BADMOD, c);
622 dolmod = c;
623 if (c == 'q')
624 dolmcnt = 10000;
625 }
626 else
627 unDredc(c);
628 }
629
630 static void
631 setDolp(cp)
632 register Char *cp;
633 {
634 register Char *dp;
635
636 if (dolmod == 0 || dolmcnt == 0) {
637 dolp = cp;
638 return;
639 }
640 dp = domod(cp, dolmod);
641 if (dp) {
642 dolmcnt--;
643 addla(dp);
644 xfree((ptr_t) dp);
645 }
646 else
647 addla(cp);
648 dolp = STRNULL;
649 if (seterr)
650 stderror(ERR_OLD);
651 }
652
653 static void
654 unDredc(c)
655 int c;
656 {
657
658 Dpeekrd = c;
659 }
660
661 static int
662 Dredc()
663 {
664 register int c;
665
666 if (c = Dpeekrd) {
667 Dpeekrd = 0;
668 return (c);
669 }
670 if (Dcp && (c = *Dcp++))
671 return (c & (QUOTE | TRIM));
672 if (*Dvp == 0) {
673 Dcp = 0;
674 return (DEOF);
675 }
676 Dcp = *Dvp++;
677 return (' ');
678 }
679
680 static void
681 Dtestq(c)
682 register int c;
683 {
684
685 if (cmap(c, QUOTES))
686 gflag = 1;
687 }
688
689 /*
690 * Form a shell temporary file (in unit 0) from the words
691 * of the shell input up to EOF or a line the same as "term".
692 * Unit 0 should have been closed before this call.
693 */
694 void
695 heredoc(term)
696 Char *term;
697 {
698 register int c;
699 Char *Dv[2];
700 Char obuf[BUFSIZ], lbuf[BUFSIZ], mbuf[BUFSIZ];
701 int ocnt, lcnt, mcnt;
702 register Char *lbp, *obp, *mbp;
703 Char **vp;
704 bool quoted;
705 char *tmp;
706
707 if (creat(tmp = short2str(shtemp), 0600) < 0)
708 stderror(ERR_SYSTEM, tmp, strerror(errno));
709 (void) close(0);
710 if (open(tmp, O_RDWR) < 0) {
711 int oerrno = errno;
712
713 (void) unlink(tmp);
714 errno = oerrno;
715 stderror(ERR_SYSTEM, tmp, strerror(errno));
716 }
717 (void) unlink(tmp); /* 0 0 inode! */
718 Dv[0] = term;
719 Dv[1] = NULL;
720 gflag = 0;
721 trim(Dv);
722 rscan(Dv, Dtestq);
723 quoted = gflag;
724 ocnt = BUFSIZ;
725 obp = obuf;
726 for (;;) {
727 /*
728 * Read up a line
729 */
730 lbp = lbuf;
731 lcnt = BUFSIZ - 4;
732 for (;;) {
733 c = readc(1); /* 1 -> Want EOF returns */
734 if (c < 0 || c == '\n')
735 break;
736 if (c &= TRIM) {
737 *lbp++ = c;
738 if (--lcnt < 0) {
739 setname("<<");
740 stderror(ERR_NAME | ERR_OVERFLOW);
741 }
742 }
743 }
744 *lbp = 0;
745
746 /*
747 * Check for EOF or compare to terminator -- before expansion
748 */
749 if (c < 0 || eq(lbuf, term)) {
750 (void) write(0, short2str(obuf), (size_t) (BUFSIZ - ocnt));
751 (void) lseek(0, 0l, L_SET);
752 return;
753 }
754
755 /*
756 * If term was quoted or -n just pass it on
757 */
758 if (quoted || noexec) {
759 *lbp++ = '\n';
760 *lbp = 0;
761 for (lbp = lbuf; c = *lbp++;) {
762 *obp++ = c;
763 if (--ocnt == 0) {
764 (void) write(0, short2str(obuf), BUFSIZ);
765 obp = obuf;
766 ocnt = BUFSIZ;
767 }
768 }
769 continue;
770 }
771
772 /*
773 * Term wasn't quoted so variable and then command expand the input
774 * line
775 */
776 Dcp = lbuf;
777 Dvp = Dv + 1;
778 mbp = mbuf;
779 mcnt = BUFSIZ - 4;
780 for (;;) {
781 c = DgetC(DODOL);
782 if (c == DEOF)
783 break;
784 if ((c &= TRIM) == 0)
785 continue;
786 /* \ quotes \ $ ` here */
787 if (c == '\\') {
788 c = DgetC(0);
789 if (!any("$\\`", c))
790 unDgetC(c | QUOTE), c = '\\';
791 else
792 c |= QUOTE;
793 }
794 *mbp++ = c;
795 if (--mcnt == 0) {
796 setname("<<");
797 stderror(ERR_NAME | ERR_OVERFLOW);
798 }
799 }
800 *mbp++ = 0;
801
802 /*
803 * If any ` in line do command substitution
804 */
805 mbp = mbuf;
806 if (any(short2str(mbp), '`')) {
807 /*
808 * 1 arg to dobackp causes substitution to be literal. Words are
809 * broken only at newlines so that all blanks and tabs are
810 * preserved. Blank lines (null words) are not discarded.
811 */
812 vp = dobackp(mbuf, 1);
813 }
814 else
815 /* Setup trivial vector similar to return of dobackp */
816 Dv[0] = mbp, Dv[1] = NULL, vp = Dv;
817
818 /*
819 * Resurrect the words from the command substitution each separated by
820 * a newline. Note that the last newline of a command substitution
821 * will have been discarded, but we put a newline after the last word
822 * because this represents the newline after the last input line!
823 */
824 for (; *vp; vp++) {
825 for (mbp = *vp; *mbp; mbp++) {
826 *obp++ = *mbp & TRIM;
827 if (--ocnt == 0) {
828 (void) write(0, short2str(obuf), BUFSIZ);
829 obp = obuf;
830 ocnt = BUFSIZ;
831 }
832 }
833 *obp++ = '\n';
834 if (--ocnt == 0) {
835 (void) write(0, short2str(obuf), BUFSIZ);
836 obp = obuf;
837 ocnt = BUFSIZ;
838 }
839 }
840 if (pargv)
841 blkfree(pargv), pargv = 0;
842 }
843 }
844