1232633Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.dol.c,v 3.83 2011/01/25 20:10:46 christos Exp $ */ 259243Sobrien/* 359243Sobrien * sh.dol.c: Variable substitutions 459243Sobrien */ 559243Sobrien/*- 659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 759243Sobrien * All rights reserved. 859243Sobrien * 959243Sobrien * Redistribution and use in source and binary forms, with or without 1059243Sobrien * modification, are permitted provided that the following conditions 1159243Sobrien * are met: 1259243Sobrien * 1. Redistributions of source code must retain the above copyright 1359243Sobrien * notice, this list of conditions and the following disclaimer. 1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1559243Sobrien * notice, this list of conditions and the following disclaimer in the 1659243Sobrien * documentation and/or other materials provided with the distribution. 17100616Smp * 3. Neither the name of the University nor the names of its contributors 1859243Sobrien * may be used to endorse or promote products derived from this software 1959243Sobrien * without specific prior written permission. 2059243Sobrien * 2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2459243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3159243Sobrien * SUCH DAMAGE. 3259243Sobrien */ 3359243Sobrien#include "sh.h" 3459243Sobrien 35232633SmpRCSID("$tcsh: sh.dol.c,v 3.83 2011/01/25 20:10:46 christos Exp $") 3659243Sobrien 3759243Sobrien/* 3859243Sobrien * C shell 3959243Sobrien */ 4059243Sobrien 4159243Sobrien/* 4259243Sobrien * These routines perform variable substitution and quoting via ' and ". 4359243Sobrien * To this point these constructs have been preserved in the divided 4459243Sobrien * input words. Here we expand variables and turn quoting via ' and " into 4559243Sobrien * QUOTE bits on characters (which prevent further interpretation). 4659243Sobrien * If the `:q' modifier was applied during history expansion, then 4759243Sobrien * some QUOTEing may have occurred already, so we dont "trim()" here. 4859243Sobrien */ 4959243Sobrien 50232633Smpstatic eChar Dpeekc; /* Peek for DgetC */ 51145479Smpstatic eChar Dpeekrd; /* Peek for Dreadc */ 52167465Smpstatic Char *Dcp, *const *Dvp; /* Input vector for Dreadc */ 5359243Sobrien 54145479Smp#define DEOF CHAR_ERR 5559243Sobrien 5659243Sobrien#define unDgetC(c) Dpeekc = c 5759243Sobrien 5859243Sobrien#define QUOTES (_QF|_QB|_ESC) /* \ ' " ` */ 5959243Sobrien 6059243Sobrien/* 6159243Sobrien * The following variables give the information about the current 6259243Sobrien * $ expansion, recording the current word position, the remaining 6359243Sobrien * words within this expansion, the count of remaining words, and the 6459243Sobrien * information about any : modifier which is being applied. 6559243Sobrien */ 6659243Sobrienstatic Char *dolp; /* Remaining chars from this word */ 6759243Sobrienstatic Char **dolnxt; /* Further words */ 6859243Sobrienstatic int dolcnt; /* Count of further words */ 69167465Smpstatic struct Strbuf dolmod; /* = Strbuf_INIT; : modifier characters */ 70167465Smpstatic int dolmcnt; /* :gx -> INT_MAX, else 1 */ 71167465Smpstatic int dol_flag_a; /* :ax -> 1, else 0 */ 7259243Sobrien 73167465Smpstatic Char **Dfix2 (Char *const *); 74167465Smpstatic int Dpack (struct Strbuf *); 75167465Smpstatic int Dword (struct blk_buf *); 76167465Smpstatic void dolerror (Char *); 77167465Smpstatic eChar DgetC (int); 78167465Smpstatic void Dgetdol (void); 79167465Smpstatic void fixDolMod (void); 80167465Smpstatic void setDolp (Char *); 81167465Smpstatic void unDredc (eChar); 82167465Smpstatic eChar Dredc (void); 83167465Smpstatic void Dtestq (Char); 8459243Sobrien 8559243Sobrien/* 8659243Sobrien * Fix up the $ expansions and quotations in the 8759243Sobrien * argument list to command t. 8859243Sobrien */ 8959243Sobrienvoid 90167465SmpDfix(struct command *t) 9159243Sobrien{ 92145479Smp Char **pp; 93145479Smp Char *p; 9459243Sobrien 9559243Sobrien if (noexec) 9659243Sobrien return; 9759243Sobrien /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */ 9859243Sobrien for (pp = t->t_dcom; (p = *pp++) != NULL;) { 9959243Sobrien for (; *p; p++) { 10059243Sobrien if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */ 101167465Smp Char **expanded; 102167465Smp 103167465Smp expanded = Dfix2(t->t_dcom); /* found one */ 10459243Sobrien blkfree(t->t_dcom); 105167465Smp t->t_dcom = expanded; 10659243Sobrien return; 10759243Sobrien } 10859243Sobrien } 10959243Sobrien } 11059243Sobrien} 11159243Sobrien 11259243Sobrien/* 11359243Sobrien * $ substitute one word, for i/o redirection 11459243Sobrien */ 11559243SobrienChar * 116167465SmpDfix1(Char *cp) 11759243Sobrien{ 118167465Smp Char *Dv[2], **expanded; 11959243Sobrien 12059243Sobrien if (noexec) 12159243Sobrien return (0); 12259243Sobrien Dv[0] = cp; 12359243Sobrien Dv[1] = NULL; 124167465Smp expanded = Dfix2(Dv); 125167465Smp if (expanded[0] == NULL || expanded[1] != NULL) { 126167465Smp blkfree(expanded); 12759243Sobrien setname(short2str(cp)); 12859243Sobrien stderror(ERR_NAME | ERR_AMBIG); 12959243Sobrien } 130167465Smp cp = Strsave(expanded[0]); 131167465Smp blkfree(expanded); 13259243Sobrien return (cp); 13359243Sobrien} 13459243Sobrien 13559243Sobrien/* 13659243Sobrien * Subroutine to do actual fixing after state initialization. 13759243Sobrien */ 138167465Smpstatic Char ** 139167465SmpDfix2(Char *const *v) 14059243Sobrien{ 141195609Smp struct blk_buf *bb = bb_alloc(); 142195609Smp Char **vec; 143167465Smp 14459243Sobrien Dvp = v; 14559243Sobrien Dcp = STRNULL; /* Setup input vector for Dreadc */ 14659243Sobrien unDgetC(0); 14759243Sobrien unDredc(0); /* Clear out any old peeks (at error) */ 14859243Sobrien dolp = 0; 14959243Sobrien dolcnt = 0; /* Clear out residual $ expands (...) */ 150195609Smp cleanup_push(bb, bb_free); 151195609Smp while (Dword(bb)) 15259243Sobrien continue; 153195609Smp cleanup_ignore(bb); 154195609Smp cleanup_until(bb); 155195609Smp vec = bb_finish(bb); 156195609Smp xfree(bb); 157195609Smp return vec; 15859243Sobrien} 15959243Sobrien 16059243Sobrien/* 16159243Sobrien * Pack up more characters in this word 16259243Sobrien */ 163167465Smpstatic int 164167465SmpDpack(struct Strbuf *wbuf) 16559243Sobrien{ 166145479Smp eChar c; 16759243Sobrien 16859243Sobrien for (;;) { 16959243Sobrien c = DgetC(DODOL); 17059243Sobrien if (c == '\\') { 17159243Sobrien c = DgetC(0); 17259243Sobrien if (c == DEOF) { 17359243Sobrien unDredc(c); 174167465Smp return 1; 17559243Sobrien } 17659243Sobrien if (c == '\n') 17759243Sobrien c = ' '; 17859243Sobrien else 17959243Sobrien c |= QUOTE; 18059243Sobrien } 18159243Sobrien if (c == DEOF) { 18259243Sobrien unDredc(c); 183167465Smp return 1; 18459243Sobrien } 18559243Sobrien if (cmap(c, _SP | _NL | _QF | _QB)) { /* sp \t\n'"` */ 18659243Sobrien unDgetC(c); 18759243Sobrien if (cmap(c, QUOTES)) 188167465Smp return 0; 189167465Smp return 1; 19059243Sobrien } 191167465Smp Strbuf_append1(wbuf, (Char) c); 19259243Sobrien } 19359243Sobrien} 19459243Sobrien 19559243Sobrien/* 19659243Sobrien * Get a word. This routine is analogous to the routine 19759243Sobrien * word() in sh.lex.c for the main lexical input. One difference 19859243Sobrien * here is that we don't get a newline to terminate our expansion. 19959243Sobrien * Rather, DgetC will return a DEOF when we hit the end-of-input. 20059243Sobrien */ 20159243Sobrienstatic int 202167465SmpDword(struct blk_buf *bb) 20359243Sobrien{ 204145479Smp eChar c, c1; 205195609Smp struct Strbuf *wbuf = Strbuf_alloc(); 206145479Smp int dolflg; 207167465Smp int sofar = 0; 208195609Smp Char *str; 20959243Sobrien 210195609Smp cleanup_push(wbuf, Strbuf_free); 211167465Smp for (;;) { 21259243Sobrien c = DgetC(DODOL); 21359243Sobrien switch (c) { 21459243Sobrien 21559243Sobrien case DEOF: 216167465Smp if (sofar == 0) { 217195609Smp cleanup_until(wbuf); 21859243Sobrien return (0); 219167465Smp } 22059243Sobrien /* finish this word and catch the code above the next time */ 22159243Sobrien unDredc(c); 22259243Sobrien /*FALLTHROUGH*/ 22359243Sobrien 22459243Sobrien case '\n': 225167465Smp goto end; 22659243Sobrien 22759243Sobrien case ' ': 22859243Sobrien case '\t': 229167465Smp continue; 23059243Sobrien 23159243Sobrien case '`': 23259243Sobrien /* We preserve ` quotations which are done yet later */ 233195609Smp Strbuf_append1(wbuf, (Char) c); 23459243Sobrien /*FALLTHROUGH*/ 23559243Sobrien case '\'': 23659243Sobrien case '"': 23759243Sobrien /* 23859243Sobrien * Note that DgetC never returns a QUOTES character from an 23959243Sobrien * expansion, so only true input quotes will get us here or out. 24059243Sobrien */ 24159243Sobrien c1 = c; 24259243Sobrien dolflg = c1 == '"' ? DODOL : 0; 24359243Sobrien for (;;) { 24459243Sobrien c = DgetC(dolflg); 24559243Sobrien if (c == c1) 24659243Sobrien break; 247195609Smp if (c == '\n' || c == DEOF) { 248195609Smp cleanup_until(bb); 249145479Smp stderror(ERR_UNMATCHED, (int)c1); 250195609Smp } 25159243Sobrien if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) { 252195609Smp if (wbuf->len != 0 && (wbuf->s[wbuf->len - 1] & TRIM) == '\\') 253195609Smp wbuf->len--; 25459243Sobrien } 25559243Sobrien switch (c1) { 25659243Sobrien 25759243Sobrien case '"': 25859243Sobrien /* 25959243Sobrien * Leave any `s alone for later. Other chars are all 26059243Sobrien * quoted, thus `...` can tell it was within "...". 26159243Sobrien */ 262195609Smp Strbuf_append1(wbuf, c == '`' ? '`' : c | QUOTE); 26359243Sobrien break; 26459243Sobrien 26559243Sobrien case '\'': 26659243Sobrien /* Prevent all further interpretation */ 267195609Smp Strbuf_append1(wbuf, c | QUOTE); 26859243Sobrien break; 26959243Sobrien 27059243Sobrien case '`': 27159243Sobrien /* Leave all text alone for later */ 272195609Smp Strbuf_append1(wbuf, (Char) c); 27359243Sobrien break; 27459243Sobrien 27559243Sobrien default: 27659243Sobrien break; 27759243Sobrien } 27859243Sobrien } 27959243Sobrien if (c1 == '`') 280195609Smp Strbuf_append1(wbuf, '`'); 28159243Sobrien sofar = 1; 282195609Smp if (Dpack(wbuf) != 0) 283167465Smp goto end; 284167465Smp continue; 28559243Sobrien 28659243Sobrien case '\\': 28759243Sobrien c = DgetC(0); /* No $ subst! */ 288167465Smp if (c == '\n' || c == DEOF) 289167465Smp continue; 29059243Sobrien c |= QUOTE; 29159243Sobrien break; 29259243Sobrien 29359243Sobrien default: 29459243Sobrien break; 29559243Sobrien } 296167465Smp unDgetC(c); 297167465Smp sofar = 1; 298195609Smp if (Dpack(wbuf) != 0) 299167465Smp goto end; 30059243Sobrien } 301167465Smp 302167465Smp end: 303195609Smp cleanup_ignore(wbuf); 304195609Smp cleanup_until(wbuf); 305195609Smp str = Strbuf_finish(wbuf); 306195609Smp bb_append(bb, str); 307195609Smp xfree(wbuf); 308167465Smp return 1; 30959243Sobrien} 31059243Sobrien 31159243Sobrien 31259243Sobrien/* 31359243Sobrien * Get a character, performing $ substitution unless flag is 0. 31459243Sobrien * Any QUOTES character which is returned from a $ expansion is 31559243Sobrien * QUOTEd so that it will not be recognized above. 31659243Sobrien */ 317145479Smpstatic eChar 318167465SmpDgetC(int flag) 31959243Sobrien{ 320232633Smp eChar c; 32159243Sobrien 32259243Sobrientop: 32359243Sobrien if ((c = Dpeekc) != 0) { 32459243Sobrien Dpeekc = 0; 32559243Sobrien return (c); 32659243Sobrien } 327167465Smp if (lap < labuf.len) { 328167465Smp c = labuf.s[lap++] & (QUOTE | TRIM); 32959243Sobrienquotspec: 33059243Sobrien if (cmap(c, QUOTES)) 33159243Sobrien return (c | QUOTE); 33259243Sobrien return (c); 33359243Sobrien } 33459243Sobrien if (dolp) { 33559243Sobrien if ((c = *dolp++ & (QUOTE | TRIM)) != 0) 33659243Sobrien goto quotspec; 33759243Sobrien if (dolcnt > 0) { 33859243Sobrien setDolp(*dolnxt++); 33959243Sobrien --dolcnt; 34059243Sobrien return (' '); 34159243Sobrien } 34259243Sobrien dolp = 0; 34359243Sobrien } 34459243Sobrien if (dolcnt > 0) { 34559243Sobrien setDolp(*dolnxt++); 34659243Sobrien --dolcnt; 34759243Sobrien goto top; 34859243Sobrien } 34959243Sobrien c = Dredc(); 35059243Sobrien if (c == '$' && flag) { 35159243Sobrien Dgetdol(); 35259243Sobrien goto top; 35359243Sobrien } 35459243Sobrien return (c); 35559243Sobrien} 35659243Sobrien 35759243Sobrienstatic Char *nulvec[] = { NULL }; 35859243Sobrienstatic struct varent nulargv = {nulvec, STRargv, VAR_READWRITE, 35959243Sobrien { NULL, NULL, NULL }, 0 }; 36059243Sobrien 36159243Sobrienstatic void 362167465Smpdolerror(Char *s) 36359243Sobrien{ 36459243Sobrien setname(short2str(s)); 36559243Sobrien stderror(ERR_NAME | ERR_RANGE); 36659243Sobrien} 36759243Sobrien 36859243Sobrien/* 36959243Sobrien * Handle the multitudinous $ expansion forms. 37059243Sobrien * Ugh. 37159243Sobrien */ 37259243Sobrienstatic void 373167465SmpDgetdol(void) 37459243Sobrien{ 375145479Smp Char *np; 376145479Smp struct varent *vp = NULL; 377195609Smp struct Strbuf *name = Strbuf_alloc(); 378145479Smp eChar c, sc; 37959243Sobrien int subscr = 0, lwb = 1, upb = 0; 380145479Smp int dimen = 0, bitset = 0, length = 0; 38159243Sobrien static Char *dolbang = NULL; 38259243Sobrien 383195609Smp cleanup_push(name, Strbuf_free); 384167465Smp dolmod.len = dolmcnt = dol_flag_a = 0; 38559243Sobrien c = sc = DgetC(0); 386167465Smp if (c == DEOF) { 387167465Smp stderror(ERR_SYNTAX); 388167465Smp return; 389167465Smp } 39059243Sobrien if (c == '{') 39159243Sobrien c = DgetC(0); /* sc is { to take } later */ 39259243Sobrien if ((c & TRIM) == '#') 39359243Sobrien dimen++, c = DgetC(0); /* $# takes dimension */ 39459243Sobrien else if (c == '?') 39559243Sobrien bitset++, c = DgetC(0); /* $? tests existence */ 39659243Sobrien else if (c == '%') 39759243Sobrien length++, c = DgetC(0); /* $% returns length in chars */ 39859243Sobrien switch (c) { 39959243Sobrien 40059243Sobrien case '!': 40159243Sobrien if (dimen || bitset || length) 40259243Sobrien stderror(ERR_SYNTAX); 40359243Sobrien if (backpid != 0) { 404167465Smp xfree(dolbang); 405232633Smp setDolp(dolbang = putn((tcsh_number_t)backpid)); 40659243Sobrien } 407195609Smp cleanup_until(name); 40859243Sobrien goto eatbrac; 40959243Sobrien 41059243Sobrien case '$': 41159243Sobrien if (dimen || bitset || length) 41259243Sobrien stderror(ERR_SYNTAX); 41359243Sobrien setDolp(doldol); 414195609Smp cleanup_until(name); 41559243Sobrien goto eatbrac; 41659243Sobrien 417167465Smp case '<'|QUOTE: { 418167465Smp static struct Strbuf wbuf; /* = Strbuf_INIT; */ 419167465Smp 42059243Sobrien if (bitset) 42159243Sobrien stderror(ERR_NOTALLOWED, "$?<"); 42259243Sobrien if (dimen) 42359243Sobrien stderror(ERR_NOTALLOWED, "$#<"); 42459243Sobrien if (length) 42559243Sobrien stderror(ERR_NOTALLOWED, "$%<"); 426167465Smp wbuf.len = 0; 42759243Sobrien { 428145479Smp char cbuf[MB_LEN_MAX]; 429145479Smp size_t cbp = 0; 430167465Smp int old_pintr_disabled; 431145479Smp 432167465Smp for (;;) { 433145479Smp int len; 434167465Smp ssize_t res; 435167465Smp Char wc; 436145479Smp 437167465Smp pintr_push_enable(&old_pintr_disabled); 438167465Smp res = force_read(OLDSTD, cbuf + cbp, 1); 439167465Smp cleanup_until(&old_pintr_disabled); 440167465Smp if (res != 1) 441167465Smp break; 442167465Smp cbp++; 443167465Smp len = normal_mbtowc(&wc, cbuf, cbp); 444145479Smp if (len == -1) { 445145479Smp reset_mbtowc(); 446145479Smp if (cbp < MB_LEN_MAX) 447145479Smp continue; /* Maybe a partial character */ 448167465Smp wc = (unsigned char)*cbuf | INVALID_BYTE; 449145479Smp } 450145479Smp if (len <= 0) 451145479Smp len = 1; 452145479Smp if (cbp != (size_t)len) 453145479Smp memmove(cbuf, cbuf + len, cbp - len); 454145479Smp cbp -= len; 455167465Smp if (wc == '\n') 45659243Sobrien break; 457167465Smp Strbuf_append1(&wbuf, wc); 45859243Sobrien } 459145479Smp while (cbp != 0) { 460167465Smp int len; 461167465Smp Char wc; 462167465Smp 463167465Smp len = normal_mbtowc(&wc, cbuf, cbp); 464167465Smp if (len == -1) { 465167465Smp reset_mbtowc(); 466167465Smp wc = (unsigned char)*cbuf | INVALID_BYTE; 467167465Smp } 468167465Smp if (len <= 0) 469167465Smp len = 1; 470167465Smp if (cbp != (size_t)len) 471167465Smp memmove(cbuf, cbuf + len, cbp - len); 472167465Smp cbp -= len; 473167465Smp if (wc == '\n') 474145479Smp break; 475167465Smp Strbuf_append1(&wbuf, wc); 476145479Smp } 477167465Smp Strbuf_terminate(&wbuf); 47859243Sobrien } 47959243Sobrien 48059243Sobrien fixDolMod(); 481167465Smp setDolp(wbuf.s); /* Kept allocated until next $< expansion */ 482195609Smp cleanup_until(name); 48359243Sobrien goto eatbrac; 484167465Smp } 48559243Sobrien 48659243Sobrien case '*': 487195609Smp Strbuf_append(name, STRargv); 488195609Smp Strbuf_terminate(name); 48959243Sobrien vp = adrof(STRargv); 49059243Sobrien subscr = -1; /* Prevent eating [...] */ 49159243Sobrien break; 49259243Sobrien 49359243Sobrien case DEOF: 49459243Sobrien case '\n': 49559243Sobrien np = dimen ? STRargv : (bitset ? STRstatus : NULL); 49659243Sobrien if (np) { 49759243Sobrien bitset = 0; 498195609Smp Strbuf_append(name, np); 499195609Smp Strbuf_terminate(name); 50059243Sobrien vp = adrof(np); 50159243Sobrien subscr = -1; /* Prevent eating [...] */ 50259243Sobrien unDredc(c); 50359243Sobrien break; 50459243Sobrien } 50559243Sobrien else 50659243Sobrien stderror(ERR_SYNTAX); 50759243Sobrien /*NOTREACHED*/ 50859243Sobrien 50959243Sobrien default: 51059243Sobrien if (Isdigit(c)) { 51159243Sobrien if (dimen) 51259243Sobrien stderror(ERR_NOTALLOWED, "$#<num>"); 51359243Sobrien subscr = 0; 51459243Sobrien do { 51559243Sobrien subscr = subscr * 10 + c - '0'; 51659243Sobrien c = DgetC(0); 517167465Smp } while (c != DEOF && Isdigit(c)); 51859243Sobrien unDredc(c); 519100616Smp if (subscr < 0) 520100616Smp stderror(ERR_RANGE); 52159243Sobrien if (subscr == 0) { 52259243Sobrien if (bitset) { 52359243Sobrien dolp = dolzero ? STR1 : STR0; 524195609Smp cleanup_until(name); 52559243Sobrien goto eatbrac; 52659243Sobrien } 52759243Sobrien if (ffile == 0) 52859243Sobrien stderror(ERR_DOLZERO); 52959243Sobrien if (length) { 530167465Smp length = Strlen(ffile); 531232633Smp addla(putn((tcsh_number_t)length)); 53259243Sobrien } 53359243Sobrien else { 53459243Sobrien fixDolMod(); 53559243Sobrien setDolp(ffile); 53659243Sobrien } 537195609Smp cleanup_until(name); 53859243Sobrien goto eatbrac; 53959243Sobrien } 54059243Sobrien#if 0 54159243Sobrien if (bitset) 54259243Sobrien stderror(ERR_NOTALLOWED, "$?<num>"); 54359243Sobrien if (length) 54459243Sobrien stderror(ERR_NOTALLOWED, "$%<num>"); 54559243Sobrien#endif 54659243Sobrien vp = adrof(STRargv); 54759243Sobrien if (vp == 0) { 54859243Sobrien vp = &nulargv; 549195609Smp cleanup_until(name); 55059243Sobrien goto eatmod; 55159243Sobrien } 55259243Sobrien break; 55359243Sobrien } 554167465Smp if (c == DEOF || !alnum(c)) { 55559243Sobrien np = dimen ? STRargv : (bitset ? STRstatus : NULL); 55659243Sobrien if (np) { 55759243Sobrien bitset = 0; 558195609Smp Strbuf_append(name, np); 559195609Smp Strbuf_terminate(name); 56059243Sobrien vp = adrof(np); 56159243Sobrien subscr = -1; /* Prevent eating [...] */ 56259243Sobrien unDredc(c); 56359243Sobrien break; 56459243Sobrien } 56559243Sobrien else 56659243Sobrien stderror(ERR_VARALNUM); 56759243Sobrien } 56859243Sobrien for (;;) { 569195609Smp Strbuf_append1(name, (Char) c); 57059243Sobrien c = DgetC(0); 571167465Smp if (c == DEOF || !alnum(c)) 57259243Sobrien break; 57359243Sobrien } 574195609Smp Strbuf_terminate(name); 57559243Sobrien unDredc(c); 576195609Smp vp = adrof(name->s); 57759243Sobrien } 57859243Sobrien if (bitset) { 579195609Smp dolp = (vp || getenv(short2str(name->s))) ? STR1 : STR0; 580195609Smp cleanup_until(name); 58159243Sobrien goto eatbrac; 58259243Sobrien } 583100616Smp if (vp == NULL || vp->vec == NULL) { 584195609Smp np = str2short(getenv(short2str(name->s))); 58559243Sobrien if (np) { 586167465Smp static Char *env_val; /* = NULL; */ 587167465Smp 588195609Smp cleanup_until(name); 58959243Sobrien fixDolMod(); 590195609Smp if (length) { 591232633Smp addla(putn((tcsh_number_t)Strlen(np))); 592195609Smp } else { 593195609Smp xfree(env_val); 594195609Smp env_val = Strsave(np); 595195609Smp setDolp(env_val); 596195609Smp } 59759243Sobrien goto eatbrac; 59859243Sobrien } 599195609Smp udvar(name->s); 60059243Sobrien /* NOTREACHED */ 60159243Sobrien } 602195609Smp cleanup_until(name); 60359243Sobrien c = DgetC(0); 60459243Sobrien upb = blklen(vp->vec); 60559243Sobrien if (dimen == 0 && subscr == 0 && c == '[') { 606195609Smp name = Strbuf_alloc(); 607195609Smp cleanup_push(name, Strbuf_free); 608195609Smp np = name->s; 60959243Sobrien for (;;) { 61059243Sobrien c = DgetC(DODOL); /* Allow $ expand within [ ] */ 61159243Sobrien if (c == ']') 61259243Sobrien break; 61359243Sobrien if (c == '\n' || c == DEOF) 61459243Sobrien stderror(ERR_INCBR); 615195609Smp Strbuf_append1(name, (Char) c); 61659243Sobrien } 617195609Smp Strbuf_terminate(name); 618195609Smp np = name->s; 61959243Sobrien if (dolp || dolcnt) /* $ exp must end before ] */ 62059243Sobrien stderror(ERR_EXPORD); 62159243Sobrien if (!*np) 62259243Sobrien stderror(ERR_SYNTAX); 62359243Sobrien if (Isdigit(*np)) { 62459243Sobrien int i; 62559243Sobrien 62659243Sobrien for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0') 62759243Sobrien continue; 628232633Smp if (i < 0 || (i > upb && !any("-*", *np))) { 629195609Smp cleanup_until(name); 63059243Sobrien dolerror(vp->v_name); 63159243Sobrien return; 63259243Sobrien } 63359243Sobrien lwb = i; 63459243Sobrien if (!*np) 63559243Sobrien upb = lwb, np = STRstar; 63659243Sobrien } 63759243Sobrien if (*np == '*') 63859243Sobrien np++; 63959243Sobrien else if (*np != '-') 64059243Sobrien stderror(ERR_MISSING, '-'); 64159243Sobrien else { 642145479Smp int i = upb; 64359243Sobrien 64459243Sobrien np++; 64559243Sobrien if (Isdigit(*np)) { 64659243Sobrien i = 0; 64759243Sobrien while (Isdigit(*np)) 64859243Sobrien i = i * 10 + *np++ - '0'; 64959243Sobrien if (i < 0 || i > upb) { 650195609Smp cleanup_until(name); 65159243Sobrien dolerror(vp->v_name); 65259243Sobrien return; 65359243Sobrien } 65459243Sobrien } 65559243Sobrien if (i < lwb) 65659243Sobrien upb = lwb - 1; 65759243Sobrien else 65859243Sobrien upb = i; 65959243Sobrien } 66059243Sobrien if (lwb == 0) { 66159243Sobrien if (upb != 0) { 662195609Smp cleanup_until(name); 66359243Sobrien dolerror(vp->v_name); 66459243Sobrien return; 66559243Sobrien } 66659243Sobrien upb = -1; 66759243Sobrien } 66859243Sobrien if (*np) 66959243Sobrien stderror(ERR_SYNTAX); 670195609Smp cleanup_until(name); 67159243Sobrien } 67259243Sobrien else { 67359243Sobrien if (subscr > 0) { 67459243Sobrien if (subscr > upb) 67559243Sobrien lwb = 1, upb = 0; 67659243Sobrien else 67759243Sobrien lwb = upb = subscr; 67859243Sobrien } 67959243Sobrien unDredc(c); 68059243Sobrien } 68159243Sobrien if (dimen) { 68259243Sobrien /* this is a kludge. It prevents Dgetdol() from */ 68359243Sobrien /* pushing erroneous ${#<error> values into the labuf. */ 68459243Sobrien if (sc == '{') { 68559243Sobrien c = Dredc(); 68659243Sobrien if (c != '}') 68759243Sobrien stderror(ERR_MISSING, '}'); 68859243Sobrien unDredc(c); 68959243Sobrien } 690232633Smp addla(putn((tcsh_number_t)(upb - lwb + 1))); 69159243Sobrien } 69259243Sobrien else if (length) { 69359243Sobrien int i; 694167465Smp 69559243Sobrien for (i = lwb - 1, length = 0; i < upb; i++) 696167465Smp length += Strlen(vp->vec[i]); 69759243Sobrien#ifdef notdef 69859243Sobrien /* We don't want that, since we can always compute it by adding $#xxx */ 69959243Sobrien length += i - 1; /* Add the number of spaces in */ 70059243Sobrien#endif 701232633Smp addla(putn((tcsh_number_t)length)); 70259243Sobrien } 70359243Sobrien else { 70459243Sobrieneatmod: 70559243Sobrien fixDolMod(); 70659243Sobrien dolnxt = &vp->vec[lwb - 1]; 70759243Sobrien dolcnt = upb - lwb + 1; 70859243Sobrien } 70959243Sobrieneatbrac: 71059243Sobrien if (sc == '{') { 71159243Sobrien c = Dredc(); 71259243Sobrien if (c != '}') 71359243Sobrien stderror(ERR_MISSING, '}'); 71459243Sobrien } 71559243Sobrien} 71659243Sobrien 71759243Sobrienstatic void 718167465SmpfixDolMod(void) 71959243Sobrien{ 720145479Smp eChar c; 72159243Sobrien 72259243Sobrien c = DgetC(0); 72359243Sobrien if (c == ':') { 72459243Sobrien do { 725167465Smp c = DgetC(0), dolmcnt = 1, dol_flag_a = 0; 72659243Sobrien if (c == 'g' || c == 'a') { 72759243Sobrien if (c == 'g') 728167465Smp dolmcnt = INT_MAX; 72959243Sobrien else 730167465Smp dol_flag_a = 1; 73159243Sobrien c = DgetC(0); 73259243Sobrien } 733167465Smp if ((c == 'g' && dolmcnt != INT_MAX) || 734167465Smp (c == 'a' && dol_flag_a == 0)) { 73559243Sobrien if (c == 'g') 736167465Smp dolmcnt = INT_MAX; 73759243Sobrien else 738167465Smp dol_flag_a = 1; 739167465Smp c = DgetC(0); 74059243Sobrien } 74159243Sobrien 74259243Sobrien if (c == 's') { /* [eichin:19910926.0755EST] */ 74359243Sobrien int delimcnt = 2; 744145479Smp eChar delim = DgetC(0); 745167465Smp Strbuf_append1(&dolmod, (Char) c); 746167465Smp Strbuf_append1(&dolmod, (Char) delim); 747167465Smp 748167465Smp if (delim == DEOF || !delim || letter(delim) 74959243Sobrien || Isdigit(delim) || any(" \t\n", delim)) { 75059243Sobrien seterror(ERR_BADSUBST); 75159243Sobrien break; 75259243Sobrien } 753145479Smp while ((c = DgetC(0)) != DEOF) { 754167465Smp Strbuf_append1(&dolmod, (Char) c); 75559243Sobrien if(c == delim) delimcnt--; 75659243Sobrien if(!delimcnt) break; 75759243Sobrien } 75859243Sobrien if(delimcnt) { 75959243Sobrien seterror(ERR_BADSUBST); 76059243Sobrien break; 76159243Sobrien } 76259243Sobrien continue; 76359243Sobrien } 76459243Sobrien if (!any("luhtrqxes", c)) 765145479Smp stderror(ERR_BADMOD, (int)c); 766167465Smp Strbuf_append1(&dolmod, (Char) c); 76759243Sobrien if (c == 'q') 768167465Smp dolmcnt = INT_MAX; 76959243Sobrien } 77059243Sobrien while ((c = DgetC(0)) == ':'); 77159243Sobrien unDredc(c); 77259243Sobrien } 77359243Sobrien else 77459243Sobrien unDredc(c); 77559243Sobrien} 77659243Sobrien 77759243Sobrienstatic void 778167465SmpsetDolp(Char *cp) 77959243Sobrien{ 780145479Smp Char *dp; 781167465Smp size_t i; 78259243Sobrien 783167465Smp if (dolmod.len == 0 || dolmcnt == 0) { 78459243Sobrien dolp = cp; 78559243Sobrien return; 78659243Sobrien } 787167465Smp cp = Strsave(cp); 788167465Smp for (i = 0; i < dolmod.len; i++) { 789167465Smp int didmod = 0; 790167465Smp 79159243Sobrien /* handle s// [eichin:19910926.0510EST] */ 792167465Smp if(dolmod.s[i] == 's') { 793145479Smp Char delim; 79459243Sobrien Char *lhsub, *rhsub, *np; 79559243Sobrien size_t lhlen = 0, rhlen = 0; 796167465Smp 797167465Smp delim = dolmod.s[++i]; 79859243Sobrien if (!delim || letter(delim) 79959243Sobrien || Isdigit(delim) || any(" \t\n", delim)) { 80059243Sobrien seterror(ERR_BADSUBST); 80159243Sobrien break; 80259243Sobrien } 803167465Smp lhsub = &dolmod.s[++i]; 804167465Smp while(dolmod.s[i] != delim && dolmod.s[++i]) { 80559243Sobrien lhlen++; 80659243Sobrien } 807167465Smp dolmod.s[i] = 0; 808167465Smp rhsub = &dolmod.s[++i]; 809167465Smp while(dolmod.s[i] != delim && dolmod.s[++i]) { 81059243Sobrien rhlen++; 81159243Sobrien } 812167465Smp dolmod.s[i] = 0; 81359243Sobrien 814167465Smp strip(lhsub); 815195609Smp strip(rhsub); 816167465Smp strip(cp); 817167465Smp dp = cp; 81859243Sobrien do { 819167465Smp dp = Strstr(dp, lhsub); 82059243Sobrien if (dp) { 821167465Smp ptrdiff_t diff = dp - cp; 822195609Smp size_t len = (Strlen(cp) + 1 - lhlen + rhlen); 823195609Smp np = xmalloc(len * sizeof(Char)); 824167465Smp (void) Strncpy(np, cp, diff); 825167465Smp (void) Strcpy(np + diff, rhsub); 826167465Smp (void) Strcpy(np + diff + rhlen, dp + lhlen); 82759243Sobrien 828167465Smp dp = np + diff + 1; 829167465Smp xfree(cp); 830167465Smp cp = np; 831195609Smp cp[--len] = '\0'; 83259243Sobrien didmod = 1; 833232633Smp if (diff >= (ssize_t)len) 834195609Smp break; 83559243Sobrien } else { 83659243Sobrien /* should this do a seterror? */ 83759243Sobrien break; 83859243Sobrien } 83959243Sobrien } 840167465Smp while (dol_flag_a != 0); 84159243Sobrien /* 84259243Sobrien * restore dolmod for additional words 84359243Sobrien */ 844167465Smp dolmod.s[i] = rhsub[-1] = (Char) delim; 84559243Sobrien } else { 84659243Sobrien 84759243Sobrien do { 848167465Smp if ((dp = domod(cp, dolmod.s[i])) != NULL) { 84959243Sobrien didmod = 1; 85059243Sobrien if (Strcmp(cp, dp) == 0) { 851167465Smp xfree(cp); 85259243Sobrien cp = dp; 85359243Sobrien break; 85459243Sobrien } 85559243Sobrien else { 856167465Smp xfree(cp); 85759243Sobrien cp = dp; 85859243Sobrien } 85959243Sobrien } 86059243Sobrien else 86159243Sobrien break; 86259243Sobrien } 863167465Smp while (dol_flag_a != 0); 864167465Smp } 865167465Smp if (didmod && dolmcnt != INT_MAX) 866167465Smp dolmcnt--; 86759243Sobrien#ifdef notdef 868167465Smp else 869167465Smp break; 87059243Sobrien#endif 87159243Sobrien } 87259243Sobrien 873167465Smp addla(cp); 87459243Sobrien 87559243Sobrien dolp = STRNULL; 87659243Sobrien if (seterr) 87759243Sobrien stderror(ERR_OLD); 87859243Sobrien} 87959243Sobrien 88059243Sobrienstatic void 881167465SmpunDredc(eChar c) 88259243Sobrien{ 88359243Sobrien 88459243Sobrien Dpeekrd = c; 88559243Sobrien} 88659243Sobrien 887145479Smpstatic eChar 888167465SmpDredc(void) 88959243Sobrien{ 890232633Smp eChar c; 89159243Sobrien 89259243Sobrien if ((c = Dpeekrd) != 0) { 89359243Sobrien Dpeekrd = 0; 89459243Sobrien return (c); 89559243Sobrien } 89659243Sobrien if (Dcp && (c = *Dcp++)) 89759243Sobrien return (c & (QUOTE | TRIM)); 89859243Sobrien if (*Dvp == 0) { 89959243Sobrien Dcp = 0; 90059243Sobrien return (DEOF); 90159243Sobrien } 90259243Sobrien Dcp = *Dvp++; 90359243Sobrien return (' '); 90459243Sobrien} 90559243Sobrien 906167465Smpstatic int gflag; 907167465Smp 90859243Sobrienstatic void 909167465SmpDtestq(Char c) 91059243Sobrien{ 91159243Sobrien 91259243Sobrien if (cmap(c, QUOTES)) 91359243Sobrien gflag = 1; 91459243Sobrien} 91559243Sobrien 916167465Smpstatic void 917167465Smpinheredoc_cleanup(void *dummy) 918167465Smp{ 919167465Smp USE(dummy); 920167465Smp inheredoc = 0; 921167465Smp} 922167465Smp 92359243Sobrien/* 92459243Sobrien * Form a shell temporary file (in unit 0) from the words 92559243Sobrien * of the shell input up to EOF or a line the same as "term". 92659243Sobrien * Unit 0 should have been closed before this call. 92759243Sobrien */ 92859243Sobrienvoid 929167465Smpheredoc(Char *term) 93059243Sobrien{ 931145479Smp eChar c; 93259243Sobrien Char *Dv[2]; 933167465Smp struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT; 934167465Smp Char obuf[BUFSIZE + 1]; 935167465Smp#define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1) 936145479Smp Char *lbp, *obp, *mbp; 93759243Sobrien Char **vp; 938145479Smp int quoted; 939232633Smp#ifdef HAVE_MKSTEMP 940232633Smp char *tmp = short2str(shtemp); 941232633Smp char *dot = strrchr(tmp, '.'); 942232633Smp 943232633Smp if (!dot) 944232633Smp stderror(ERR_NAME | ERR_NOMATCH); 945232633Smp strcpy(dot, TMP_TEMPLATE); 946232633Smp 947232633Smp xclose(0); 948232633Smp if (mkstemp(tmp) == -1) 949232633Smp stderror(ERR_SYSTEM, tmp, strerror(errno)); 950232633Smp#else /* !HAVE_MKSTEMP */ 95159243Sobrien char *tmp; 952232633Smp# ifndef WINNT_NATIVE 95368332Skris struct timeval tv; 95459243Sobrien 95568332Skrisagain: 956232633Smp# endif /* WINNT_NATIVE */ 95759243Sobrien tmp = short2str(shtemp); 958232633Smp# if O_CREAT == 0 959167465Smp if (xcreat(tmp, 0600) < 0) 96059243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 961232633Smp# endif 962167465Smp xclose(0); 963167465Smp if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) == 964167465Smp -1) { 96568332Skris int oerrno = errno; 966232633Smp# ifndef WINNT_NATIVE 96768332Skris if (errno == EEXIST) { 96868332Skris if (unlink(tmp) == -1) { 96968332Skris (void) gettimeofday(&tv, NULL); 970167465Smp xfree(shtemp); 971232633Smp mbp = putn((((tcsh_number_t)tv.tv_sec) ^ 972232633Smp ((tcsh_number_t)tv.tv_usec) ^ 973232633Smp ((tcsh_number_t)getpid())) & 0x00ffffff); 974167465Smp shtemp = Strspl(STRtmpsh, mbp); 975167465Smp xfree(mbp); 97668332Skris } 97768332Skris goto again; 97868332Skris } 979232633Smp# endif /* WINNT_NATIVE */ 98059243Sobrien (void) unlink(tmp); 98159243Sobrien errno = oerrno; 98268332Skris stderror(ERR_SYSTEM, tmp, strerror(errno)); 98359243Sobrien } 984232633Smp#endif /* HAVE_MKSTEMP */ 98559243Sobrien (void) unlink(tmp); /* 0 0 inode! */ 98659243Sobrien Dv[0] = term; 98759243Sobrien Dv[1] = NULL; 98859243Sobrien gflag = 0; 98959243Sobrien trim(Dv); 99059243Sobrien rscan(Dv, Dtestq); 99159243Sobrien quoted = gflag; 99259243Sobrien obp = obuf; 993145479Smp obuf[BUFSIZE] = 0; 99459243Sobrien inheredoc = 1; 995167465Smp cleanup_push(&inheredoc, inheredoc_cleanup); 99669408Sache#ifdef WINNT_NATIVE 99759243Sobrien __dup_stdin = 1; 99869408Sache#endif /* WINNT_NATIVE */ 999167465Smp cleanup_push(&lbuf, Strbuf_cleanup); 1000167465Smp cleanup_push(&mbuf, Strbuf_cleanup); 100159243Sobrien for (;;) { 1002167465Smp Char **words; 1003167465Smp 100459243Sobrien /* 100559243Sobrien * Read up a line 100659243Sobrien */ 1007167465Smp lbuf.len = 0; 100859243Sobrien for (;;) { 100959243Sobrien c = readc(1); /* 1 -> Want EOF returns */ 1010145479Smp if (c == CHAR_ERR || c == '\n') 101159243Sobrien break; 1012167465Smp if ((c &= TRIM) != 0) 1013167465Smp Strbuf_append1(&lbuf, (Char) c); 101459243Sobrien } 1015167465Smp Strbuf_terminate(&lbuf); 101659243Sobrien 1017232633Smp /* Catch EOF in the middle of a line. */ 1018232633Smp if (c == CHAR_ERR && lbuf.len != 0) 1019232633Smp c = '\n'; 1020232633Smp 102159243Sobrien /* 102259243Sobrien * Check for EOF or compare to terminator -- before expansion 102359243Sobrien */ 1024167465Smp if (c == CHAR_ERR || eq(lbuf.s, term)) 1025167465Smp break; 102659243Sobrien 102759243Sobrien /* 102859243Sobrien * If term was quoted or -n just pass it on 102959243Sobrien */ 103059243Sobrien if (quoted || noexec) { 1031167465Smp Strbuf_append1(&lbuf, '\n'); 1032167465Smp Strbuf_terminate(&lbuf); 1033167465Smp for (lbp = lbuf.s; (c = *lbp++) != 0;) { 103459243Sobrien *obp++ = (Char) c; 1035167465Smp if (obp == OBUF_END) { 1036145479Smp tmp = short2str(obuf); 1037167465Smp (void) xwrite(0, tmp, strlen (tmp)); 103859243Sobrien obp = obuf; 103959243Sobrien } 104059243Sobrien } 104159243Sobrien continue; 104259243Sobrien } 104359243Sobrien 104459243Sobrien /* 104559243Sobrien * Term wasn't quoted so variable and then command expand the input 104659243Sobrien * line 104759243Sobrien */ 1048167465Smp Dcp = lbuf.s; 104959243Sobrien Dvp = Dv + 1; 1050167465Smp mbuf.len = 0; 105159243Sobrien for (;;) { 105259243Sobrien c = DgetC(DODOL); 105359243Sobrien if (c == DEOF) 105459243Sobrien break; 105559243Sobrien if ((c &= TRIM) == 0) 105659243Sobrien continue; 105759243Sobrien /* \ quotes \ $ ` here */ 105859243Sobrien if (c == '\\') { 105959243Sobrien c = DgetC(0); 106059243Sobrien if (!any("$\\`", c)) 106159243Sobrien unDgetC(c | QUOTE), c = '\\'; 106259243Sobrien else 106359243Sobrien c |= QUOTE; 106459243Sobrien } 1065167465Smp Strbuf_append1(&mbuf, (Char) c); 106659243Sobrien } 1067167465Smp Strbuf_terminate(&mbuf); 106859243Sobrien 106959243Sobrien /* 107059243Sobrien * If any ` in line do command substitution 107159243Sobrien */ 1072167465Smp mbp = mbuf.s; 107359243Sobrien if (Strchr(mbp, '`') != NULL) { 107459243Sobrien /* 107559243Sobrien * 1 arg to dobackp causes substitution to be literal. Words are 107659243Sobrien * broken only at newlines so that all blanks and tabs are 107759243Sobrien * preserved. Blank lines (null words) are not discarded. 107859243Sobrien */ 1079167465Smp words = dobackp(mbp, 1); 108059243Sobrien } 108159243Sobrien else 108259243Sobrien /* Setup trivial vector similar to return of dobackp */ 1083167465Smp Dv[0] = mbp, Dv[1] = NULL, words = Dv; 108459243Sobrien 108559243Sobrien /* 108659243Sobrien * Resurrect the words from the command substitution each separated by 108759243Sobrien * a newline. Note that the last newline of a command substitution 108859243Sobrien * will have been discarded, but we put a newline after the last word 108959243Sobrien * because this represents the newline after the last input line! 109059243Sobrien */ 1091167465Smp for (vp= words; *vp; vp++) { 109259243Sobrien for (mbp = *vp; *mbp; mbp++) { 109359243Sobrien *obp++ = *mbp & TRIM; 1094167465Smp if (obp == OBUF_END) { 1095145479Smp tmp = short2str(obuf); 1096167465Smp (void) xwrite(0, tmp, strlen (tmp)); 109759243Sobrien obp = obuf; 109859243Sobrien } 109959243Sobrien } 110059243Sobrien *obp++ = '\n'; 1101167465Smp if (obp == OBUF_END) { 1102145479Smp tmp = short2str(obuf); 1103167465Smp (void) xwrite(0, tmp, strlen (tmp)); 110459243Sobrien obp = obuf; 110559243Sobrien } 110659243Sobrien } 1107167465Smp if (words != Dv) 1108167465Smp blkfree(words); 110959243Sobrien } 1110167465Smp *obp = 0; 1111167465Smp tmp = short2str(obuf); 1112167465Smp (void) xwrite(0, tmp, strlen (tmp)); 1113167465Smp (void) lseek(0, (off_t) 0, L_SET); 1114167465Smp cleanup_until(&inheredoc); 111559243Sobrien} 1116