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