159243Sobrien/* 259243Sobrien * sh.dol.c: Variable substitutions 359243Sobrien */ 459243Sobrien/*- 559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California. 659243Sobrien * All rights reserved. 759243Sobrien * 859243Sobrien * Redistribution and use in source and binary forms, with or without 959243Sobrien * modification, are permitted provided that the following conditions 1059243Sobrien * are met: 1159243Sobrien * 1. Redistributions of source code must retain the above copyright 1259243Sobrien * notice, this list of conditions and the following disclaimer. 1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1459243Sobrien * notice, this list of conditions and the following disclaimer in the 1559243Sobrien * documentation and/or other materials provided with the distribution. 16100616Smp * 3. Neither the name of the University nor the names of its contributors 1759243Sobrien * may be used to endorse or promote products derived from this software 1859243Sobrien * without specific prior written permission. 1959243Sobrien * 2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2359243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3059243Sobrien * SUCH DAMAGE. 3159243Sobrien */ 3259243Sobrien#include "sh.h" 3359243Sobrien 3459243Sobrien/* 3559243Sobrien * C shell 3659243Sobrien */ 3759243Sobrien 3859243Sobrien/* 3959243Sobrien * These routines perform variable substitution and quoting via ' and ". 4059243Sobrien * To this point these constructs have been preserved in the divided 4159243Sobrien * input words. Here we expand variables and turn quoting via ' and " into 4259243Sobrien * QUOTE bits on characters (which prevent further interpretation). 4359243Sobrien * If the `:q' modifier was applied during history expansion, then 4459243Sobrien * some QUOTEing may have occurred already, so we dont "trim()" here. 4559243Sobrien */ 4659243Sobrien 47231990Smpstatic eChar Dpeekc; /* Peek for DgetC */ 48145479Smpstatic eChar Dpeekrd; /* Peek for Dreadc */ 49167465Smpstatic Char *Dcp, *const *Dvp; /* Input vector for Dreadc */ 5059243Sobrien 51145479Smp#define DEOF CHAR_ERR 5259243Sobrien 5359243Sobrien#define unDgetC(c) Dpeekc = c 5459243Sobrien 5559243Sobrien#define QUOTES (_QF|_QB|_ESC) /* \ ' " ` */ 5659243Sobrien 5759243Sobrien/* 5859243Sobrien * The following variables give the information about the current 5959243Sobrien * $ expansion, recording the current word position, the remaining 6059243Sobrien * words within this expansion, the count of remaining words, and the 6159243Sobrien * information about any : modifier which is being applied. 6259243Sobrien */ 6359243Sobrienstatic Char *dolp; /* Remaining chars from this word */ 6459243Sobrienstatic Char **dolnxt; /* Further words */ 6559243Sobrienstatic int dolcnt; /* Count of further words */ 66167465Smpstatic struct Strbuf dolmod; /* = Strbuf_INIT; : modifier characters */ 67167465Smpstatic int dolmcnt; /* :gx -> INT_MAX, else 1 */ 68167465Smpstatic int dol_flag_a; /* :ax -> 1, else 0 */ 6959243Sobrien 70167465Smpstatic Char **Dfix2 (Char *const *); 71167465Smpstatic int Dpack (struct Strbuf *); 72167465Smpstatic int Dword (struct blk_buf *); 73167465Smpstatic void dolerror (Char *); 74167465Smpstatic eChar DgetC (int); 75167465Smpstatic void Dgetdol (void); 76167465Smpstatic void fixDolMod (void); 77167465Smpstatic void setDolp (Char *); 78167465Smpstatic void unDredc (eChar); 79167465Smpstatic eChar Dredc (void); 80167465Smpstatic void Dtestq (Char); 8159243Sobrien 8259243Sobrien/* 8359243Sobrien * Fix up the $ expansions and quotations in the 8459243Sobrien * argument list to command t. 8559243Sobrien */ 8659243Sobrienvoid 87167465SmpDfix(struct command *t) 8859243Sobrien{ 89145479Smp Char **pp; 90145479Smp Char *p; 9159243Sobrien 9259243Sobrien if (noexec) 9359243Sobrien return; 9459243Sobrien /* Note that t_dcom isn't trimmed thus !...:q's aren't lost */ 9559243Sobrien for (pp = t->t_dcom; (p = *pp++) != NULL;) { 9659243Sobrien for (; *p; p++) { 9759243Sobrien if (cmap(*p, _DOL | QUOTES)) { /* $, \, ', ", ` */ 98167465Smp Char **expanded; 99167465Smp 100167465Smp expanded = Dfix2(t->t_dcom); /* found one */ 10159243Sobrien blkfree(t->t_dcom); 102167465Smp t->t_dcom = expanded; 10359243Sobrien return; 10459243Sobrien } 10559243Sobrien } 10659243Sobrien } 10759243Sobrien} 10859243Sobrien 10959243Sobrien/* 11059243Sobrien * $ substitute one word, for i/o redirection 11159243Sobrien */ 11259243SobrienChar * 113167465SmpDfix1(Char *cp) 11459243Sobrien{ 115167465Smp Char *Dv[2], **expanded; 11659243Sobrien 11759243Sobrien if (noexec) 11859243Sobrien return (0); 11959243Sobrien Dv[0] = cp; 12059243Sobrien Dv[1] = NULL; 121167465Smp expanded = Dfix2(Dv); 122167465Smp if (expanded[0] == NULL || expanded[1] != NULL) { 123167465Smp blkfree(expanded); 12459243Sobrien setname(short2str(cp)); 12559243Sobrien stderror(ERR_NAME | ERR_AMBIG); 12659243Sobrien } 127167465Smp cp = Strsave(expanded[0]); 128167465Smp blkfree(expanded); 12959243Sobrien return (cp); 13059243Sobrien} 13159243Sobrien 13259243Sobrien/* 13359243Sobrien * Subroutine to do actual fixing after state initialization. 13459243Sobrien */ 135167465Smpstatic Char ** 136167465SmpDfix2(Char *const *v) 13759243Sobrien{ 138195609Smp struct blk_buf *bb = bb_alloc(); 139195609Smp Char **vec; 140167465Smp 14159243Sobrien Dvp = v; 14259243Sobrien Dcp = STRNULL; /* Setup input vector for Dreadc */ 14359243Sobrien unDgetC(0); 14459243Sobrien unDredc(0); /* Clear out any old peeks (at error) */ 14559243Sobrien dolp = 0; 14659243Sobrien dolcnt = 0; /* Clear out residual $ expands (...) */ 147195609Smp cleanup_push(bb, bb_free); 148195609Smp while (Dword(bb)) 14959243Sobrien continue; 150195609Smp cleanup_ignore(bb); 151195609Smp cleanup_until(bb); 152195609Smp vec = bb_finish(bb); 153195609Smp xfree(bb); 154195609Smp return vec; 15559243Sobrien} 15659243Sobrien 15759243Sobrien/* 15859243Sobrien * Pack up more characters in this word 15959243Sobrien */ 160167465Smpstatic int 161167465SmpDpack(struct Strbuf *wbuf) 16259243Sobrien{ 163145479Smp eChar c; 16459243Sobrien 16559243Sobrien for (;;) { 16659243Sobrien c = DgetC(DODOL); 16759243Sobrien if (c == '\\') { 16859243Sobrien c = DgetC(0); 16959243Sobrien if (c == DEOF) { 17059243Sobrien unDredc(c); 171167465Smp return 1; 17259243Sobrien } 17359243Sobrien if (c == '\n') 17459243Sobrien c = ' '; 17559243Sobrien else 17659243Sobrien c |= QUOTE; 17759243Sobrien } 17859243Sobrien if (c == DEOF) { 17959243Sobrien unDredc(c); 180167465Smp return 1; 18159243Sobrien } 18259243Sobrien if (cmap(c, _SP | _NL | _QF | _QB)) { /* sp \t\n'"` */ 18359243Sobrien unDgetC(c); 18459243Sobrien if (cmap(c, QUOTES)) 185167465Smp return 0; 186167465Smp return 1; 18759243Sobrien } 188167465Smp Strbuf_append1(wbuf, (Char) c); 18959243Sobrien } 19059243Sobrien} 19159243Sobrien 19259243Sobrien/* 19359243Sobrien * Get a word. This routine is analogous to the routine 19459243Sobrien * word() in sh.lex.c for the main lexical input. One difference 19559243Sobrien * here is that we don't get a newline to terminate our expansion. 19659243Sobrien * Rather, DgetC will return a DEOF when we hit the end-of-input. 19759243Sobrien */ 19859243Sobrienstatic int 199167465SmpDword(struct blk_buf *bb) 20059243Sobrien{ 201145479Smp eChar c, c1; 202195609Smp struct Strbuf *wbuf = Strbuf_alloc(); 203145479Smp int dolflg; 204167465Smp int sofar = 0; 205195609Smp Char *str; 20659243Sobrien 207195609Smp cleanup_push(wbuf, Strbuf_free); 208167465Smp for (;;) { 20959243Sobrien c = DgetC(DODOL); 21059243Sobrien switch (c) { 21159243Sobrien 21259243Sobrien case DEOF: 213167465Smp if (sofar == 0) { 214195609Smp cleanup_until(wbuf); 21559243Sobrien return (0); 216167465Smp } 21759243Sobrien /* finish this word and catch the code above the next time */ 21859243Sobrien unDredc(c); 21959243Sobrien /*FALLTHROUGH*/ 22059243Sobrien 22159243Sobrien case '\n': 222167465Smp goto end; 22359243Sobrien 22459243Sobrien case ' ': 22559243Sobrien case '\t': 226167465Smp continue; 22759243Sobrien 22859243Sobrien case '`': 22959243Sobrien /* We preserve ` quotations which are done yet later */ 230195609Smp Strbuf_append1(wbuf, (Char) c); 23159243Sobrien /*FALLTHROUGH*/ 23259243Sobrien case '\'': 23359243Sobrien case '"': 23459243Sobrien /* 23559243Sobrien * Note that DgetC never returns a QUOTES character from an 23659243Sobrien * expansion, so only true input quotes will get us here or out. 23759243Sobrien */ 23859243Sobrien c1 = c; 23959243Sobrien dolflg = c1 == '"' ? DODOL : 0; 24059243Sobrien for (;;) { 24159243Sobrien c = DgetC(dolflg); 24259243Sobrien if (c == c1) 24359243Sobrien break; 244195609Smp if (c == '\n' || c == DEOF) { 245195609Smp cleanup_until(bb); 246145479Smp stderror(ERR_UNMATCHED, (int)c1); 247195609Smp } 24859243Sobrien if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) { 249195609Smp if (wbuf->len != 0 && (wbuf->s[wbuf->len - 1] & TRIM) == '\\') 250195609Smp wbuf->len--; 25159243Sobrien } 25259243Sobrien switch (c1) { 25359243Sobrien 25459243Sobrien case '"': 25559243Sobrien /* 25659243Sobrien * Leave any `s alone for later. Other chars are all 25759243Sobrien * quoted, thus `...` can tell it was within "...". 25859243Sobrien */ 259195609Smp Strbuf_append1(wbuf, c == '`' ? '`' : c | QUOTE); 26059243Sobrien break; 26159243Sobrien 26259243Sobrien case '\'': 26359243Sobrien /* Prevent all further interpretation */ 264195609Smp Strbuf_append1(wbuf, c | QUOTE); 26559243Sobrien break; 26659243Sobrien 26759243Sobrien case '`': 26859243Sobrien /* Leave all text alone for later */ 269195609Smp Strbuf_append1(wbuf, (Char) c); 27059243Sobrien break; 27159243Sobrien 27259243Sobrien default: 27359243Sobrien break; 27459243Sobrien } 27559243Sobrien } 27659243Sobrien if (c1 == '`') 277195609Smp Strbuf_append1(wbuf, '`'); 27859243Sobrien sofar = 1; 279195609Smp if (Dpack(wbuf) != 0) 280167465Smp goto end; 281167465Smp continue; 28259243Sobrien 28359243Sobrien case '\\': 28459243Sobrien c = DgetC(0); /* No $ subst! */ 285167465Smp if (c == '\n' || c == DEOF) 286167465Smp continue; 28759243Sobrien c |= QUOTE; 28859243Sobrien break; 28959243Sobrien 29059243Sobrien default: 29159243Sobrien break; 29259243Sobrien } 293167465Smp unDgetC(c); 294167465Smp sofar = 1; 295195609Smp if (Dpack(wbuf) != 0) 296167465Smp goto end; 29759243Sobrien } 298167465Smp 299167465Smp end: 300195609Smp cleanup_ignore(wbuf); 301195609Smp cleanup_until(wbuf); 302195609Smp str = Strbuf_finish(wbuf); 303195609Smp bb_append(bb, str); 304195609Smp xfree(wbuf); 305167465Smp return 1; 30659243Sobrien} 30759243Sobrien 30859243Sobrien 30959243Sobrien/* 31059243Sobrien * Get a character, performing $ substitution unless flag is 0. 31159243Sobrien * Any QUOTES character which is returned from a $ expansion is 31259243Sobrien * QUOTEd so that it will not be recognized above. 31359243Sobrien */ 314145479Smpstatic eChar 315167465SmpDgetC(int flag) 31659243Sobrien{ 317231990Smp eChar c; 31859243Sobrien 31959243Sobrientop: 32059243Sobrien if ((c = Dpeekc) != 0) { 32159243Sobrien Dpeekc = 0; 32259243Sobrien return (c); 32359243Sobrien } 324167465Smp if (lap < labuf.len) { 325167465Smp c = labuf.s[lap++] & (QUOTE | TRIM); 32659243Sobrienquotspec: 32759243Sobrien if (cmap(c, QUOTES)) 32859243Sobrien return (c | QUOTE); 32959243Sobrien return (c); 33059243Sobrien } 33159243Sobrien if (dolp) { 33259243Sobrien if ((c = *dolp++ & (QUOTE | TRIM)) != 0) 33359243Sobrien goto quotspec; 33459243Sobrien if (dolcnt > 0) { 33559243Sobrien setDolp(*dolnxt++); 33659243Sobrien --dolcnt; 33759243Sobrien return (' '); 33859243Sobrien } 33959243Sobrien dolp = 0; 34059243Sobrien } 34159243Sobrien if (dolcnt > 0) { 34259243Sobrien setDolp(*dolnxt++); 34359243Sobrien --dolcnt; 34459243Sobrien goto top; 34559243Sobrien } 34659243Sobrien c = Dredc(); 34759243Sobrien if (c == '$' && flag) { 34859243Sobrien Dgetdol(); 34959243Sobrien goto top; 35059243Sobrien } 35159243Sobrien return (c); 35259243Sobrien} 35359243Sobrien 35459243Sobrienstatic Char *nulvec[] = { NULL }; 35559243Sobrienstatic struct varent nulargv = {nulvec, STRargv, VAR_READWRITE, 35659243Sobrien { NULL, NULL, NULL }, 0 }; 35759243Sobrien 35859243Sobrienstatic void 359167465Smpdolerror(Char *s) 36059243Sobrien{ 36159243Sobrien setname(short2str(s)); 36259243Sobrien stderror(ERR_NAME | ERR_RANGE); 36359243Sobrien} 36459243Sobrien 36559243Sobrien/* 36659243Sobrien * Handle the multitudinous $ expansion forms. 36759243Sobrien * Ugh. 36859243Sobrien */ 36959243Sobrienstatic void 370167465SmpDgetdol(void) 37159243Sobrien{ 372145479Smp Char *np; 373145479Smp struct varent *vp = NULL; 374195609Smp struct Strbuf *name = Strbuf_alloc(); 375145479Smp eChar c, sc; 37659243Sobrien int subscr = 0, lwb = 1, upb = 0; 377145479Smp int dimen = 0, bitset = 0, length = 0; 37859243Sobrien static Char *dolbang = NULL; 37959243Sobrien 380195609Smp cleanup_push(name, Strbuf_free); 381167465Smp dolmod.len = dolmcnt = dol_flag_a = 0; 38259243Sobrien c = sc = DgetC(0); 383167465Smp if (c == DEOF) { 384167465Smp stderror(ERR_SYNTAX); 385167465Smp return; 386167465Smp } 38759243Sobrien if (c == '{') 38859243Sobrien c = DgetC(0); /* sc is { to take } later */ 38959243Sobrien if ((c & TRIM) == '#') 39059243Sobrien dimen++, c = DgetC(0); /* $# takes dimension */ 39159243Sobrien else if (c == '?') 39259243Sobrien bitset++, c = DgetC(0); /* $? tests existence */ 39359243Sobrien else if (c == '%') 39459243Sobrien length++, c = DgetC(0); /* $% returns length in chars */ 39559243Sobrien switch (c) { 39659243Sobrien 39759243Sobrien case '!': 39859243Sobrien if (dimen || bitset || length) 39959243Sobrien stderror(ERR_SYNTAX); 40059243Sobrien if (backpid != 0) { 401167465Smp xfree(dolbang); 402231990Smp setDolp(dolbang = putn((tcsh_number_t)backpid)); 40359243Sobrien } 404195609Smp cleanup_until(name); 40559243Sobrien goto eatbrac; 40659243Sobrien 40759243Sobrien case '$': 40859243Sobrien if (dimen || bitset || length) 40959243Sobrien stderror(ERR_SYNTAX); 41059243Sobrien setDolp(doldol); 411195609Smp cleanup_until(name); 41259243Sobrien goto eatbrac; 41359243Sobrien 414167465Smp case '<'|QUOTE: { 415167465Smp static struct Strbuf wbuf; /* = Strbuf_INIT; */ 416167465Smp 41759243Sobrien if (bitset) 41859243Sobrien stderror(ERR_NOTALLOWED, "$?<"); 41959243Sobrien if (dimen) 42059243Sobrien stderror(ERR_NOTALLOWED, "$#<"); 42159243Sobrien if (length) 42259243Sobrien stderror(ERR_NOTALLOWED, "$%<"); 423167465Smp wbuf.len = 0; 42459243Sobrien { 425145479Smp char cbuf[MB_LEN_MAX]; 426145479Smp size_t cbp = 0; 427167465Smp int old_pintr_disabled; 428145479Smp 429167465Smp for (;;) { 430145479Smp int len; 431167465Smp ssize_t res; 432167465Smp Char wc; 433145479Smp 434167465Smp pintr_push_enable(&old_pintr_disabled); 435167465Smp res = force_read(OLDSTD, cbuf + cbp, 1); 436167465Smp cleanup_until(&old_pintr_disabled); 437167465Smp if (res != 1) 438167465Smp break; 439167465Smp cbp++; 440167465Smp len = normal_mbtowc(&wc, cbuf, cbp); 441145479Smp if (len == -1) { 442145479Smp reset_mbtowc(); 443145479Smp if (cbp < MB_LEN_MAX) 444145479Smp continue; /* Maybe a partial character */ 445167465Smp wc = (unsigned char)*cbuf | INVALID_BYTE; 446145479Smp } 447145479Smp if (len <= 0) 448145479Smp len = 1; 449145479Smp if (cbp != (size_t)len) 450145479Smp memmove(cbuf, cbuf + len, cbp - len); 451145479Smp cbp -= len; 452167465Smp if (wc == '\n') 45359243Sobrien break; 454167465Smp Strbuf_append1(&wbuf, wc); 45559243Sobrien } 456145479Smp while (cbp != 0) { 457167465Smp int len; 458167465Smp Char wc; 459167465Smp 460167465Smp len = normal_mbtowc(&wc, cbuf, cbp); 461167465Smp if (len == -1) { 462167465Smp reset_mbtowc(); 463167465Smp wc = (unsigned char)*cbuf | INVALID_BYTE; 464167465Smp } 465167465Smp if (len <= 0) 466167465Smp len = 1; 467167465Smp if (cbp != (size_t)len) 468167465Smp memmove(cbuf, cbuf + len, cbp - len); 469167465Smp cbp -= len; 470167465Smp if (wc == '\n') 471145479Smp break; 472167465Smp Strbuf_append1(&wbuf, wc); 473145479Smp } 474167465Smp Strbuf_terminate(&wbuf); 47559243Sobrien } 47659243Sobrien 47759243Sobrien fixDolMod(); 478167465Smp setDolp(wbuf.s); /* Kept allocated until next $< expansion */ 479195609Smp cleanup_until(name); 48059243Sobrien goto eatbrac; 481167465Smp } 48259243Sobrien 48359243Sobrien case '*': 484195609Smp Strbuf_append(name, STRargv); 485195609Smp Strbuf_terminate(name); 48659243Sobrien vp = adrof(STRargv); 48759243Sobrien subscr = -1; /* Prevent eating [...] */ 48859243Sobrien break; 48959243Sobrien 49059243Sobrien case DEOF: 49159243Sobrien case '\n': 49259243Sobrien np = dimen ? STRargv : (bitset ? STRstatus : NULL); 49359243Sobrien if (np) { 49459243Sobrien bitset = 0; 495195609Smp Strbuf_append(name, np); 496195609Smp Strbuf_terminate(name); 49759243Sobrien vp = adrof(np); 49859243Sobrien subscr = -1; /* Prevent eating [...] */ 49959243Sobrien unDredc(c); 50059243Sobrien break; 50159243Sobrien } 50259243Sobrien else 50359243Sobrien stderror(ERR_SYNTAX); 50459243Sobrien /*NOTREACHED*/ 50559243Sobrien 50659243Sobrien default: 50759243Sobrien if (Isdigit(c)) { 50859243Sobrien if (dimen) 50959243Sobrien stderror(ERR_NOTALLOWED, "$#<num>"); 51059243Sobrien subscr = 0; 51159243Sobrien do { 51259243Sobrien subscr = subscr * 10 + c - '0'; 51359243Sobrien c = DgetC(0); 514167465Smp } while (c != DEOF && Isdigit(c)); 51559243Sobrien unDredc(c); 516100616Smp if (subscr < 0) 517100616Smp stderror(ERR_RANGE); 51859243Sobrien if (subscr == 0) { 51959243Sobrien if (bitset) { 52059243Sobrien dolp = dolzero ? STR1 : STR0; 521195609Smp cleanup_until(name); 52259243Sobrien goto eatbrac; 52359243Sobrien } 52459243Sobrien if (ffile == 0) 52559243Sobrien stderror(ERR_DOLZERO); 52659243Sobrien if (length) { 527167465Smp length = Strlen(ffile); 528231990Smp addla(putn((tcsh_number_t)length)); 52959243Sobrien } 53059243Sobrien else { 53159243Sobrien fixDolMod(); 53259243Sobrien setDolp(ffile); 53359243Sobrien } 534195609Smp cleanup_until(name); 53559243Sobrien goto eatbrac; 53659243Sobrien } 53759243Sobrien#if 0 53859243Sobrien if (bitset) 53959243Sobrien stderror(ERR_NOTALLOWED, "$?<num>"); 54059243Sobrien if (length) 54159243Sobrien stderror(ERR_NOTALLOWED, "$%<num>"); 54259243Sobrien#endif 54359243Sobrien vp = adrof(STRargv); 54459243Sobrien if (vp == 0) { 54559243Sobrien vp = &nulargv; 546195609Smp cleanup_until(name); 54759243Sobrien goto eatmod; 54859243Sobrien } 54959243Sobrien break; 55059243Sobrien } 551167465Smp if (c == DEOF || !alnum(c)) { 55259243Sobrien np = dimen ? STRargv : (bitset ? STRstatus : NULL); 55359243Sobrien if (np) { 55459243Sobrien bitset = 0; 555195609Smp Strbuf_append(name, np); 556195609Smp Strbuf_terminate(name); 55759243Sobrien vp = adrof(np); 55859243Sobrien subscr = -1; /* Prevent eating [...] */ 55959243Sobrien unDredc(c); 56059243Sobrien break; 56159243Sobrien } 56259243Sobrien else 56359243Sobrien stderror(ERR_VARALNUM); 56459243Sobrien } 56559243Sobrien for (;;) { 566195609Smp Strbuf_append1(name, (Char) c); 56759243Sobrien c = DgetC(0); 568167465Smp if (c == DEOF || !alnum(c)) 56959243Sobrien break; 57059243Sobrien } 571195609Smp Strbuf_terminate(name); 57259243Sobrien unDredc(c); 573195609Smp vp = adrof(name->s); 57459243Sobrien } 57559243Sobrien if (bitset) { 576195609Smp dolp = (vp || getenv(short2str(name->s))) ? STR1 : STR0; 577195609Smp cleanup_until(name); 57859243Sobrien goto eatbrac; 57959243Sobrien } 580100616Smp if (vp == NULL || vp->vec == NULL) { 581195609Smp np = str2short(getenv(short2str(name->s))); 58259243Sobrien if (np) { 583167465Smp static Char *env_val; /* = NULL; */ 584167465Smp 585195609Smp cleanup_until(name); 58659243Sobrien fixDolMod(); 587195609Smp if (length) { 588231990Smp addla(putn((tcsh_number_t)Strlen(np))); 589195609Smp } else { 590195609Smp xfree(env_val); 591195609Smp env_val = Strsave(np); 592195609Smp setDolp(env_val); 593195609Smp } 59459243Sobrien goto eatbrac; 59559243Sobrien } 596195609Smp udvar(name->s); 59759243Sobrien /* NOTREACHED */ 59859243Sobrien } 599195609Smp cleanup_until(name); 60059243Sobrien c = DgetC(0); 60159243Sobrien upb = blklen(vp->vec); 60259243Sobrien if (dimen == 0 && subscr == 0 && c == '[') { 603195609Smp name = Strbuf_alloc(); 604195609Smp cleanup_push(name, Strbuf_free); 605195609Smp np = name->s; 60659243Sobrien for (;;) { 60759243Sobrien c = DgetC(DODOL); /* Allow $ expand within [ ] */ 60859243Sobrien if (c == ']') 60959243Sobrien break; 61059243Sobrien if (c == '\n' || c == DEOF) 61159243Sobrien stderror(ERR_INCBR); 612195609Smp Strbuf_append1(name, (Char) c); 61359243Sobrien } 614195609Smp Strbuf_terminate(name); 615195609Smp np = name->s; 61659243Sobrien if (dolp || dolcnt) /* $ exp must end before ] */ 61759243Sobrien stderror(ERR_EXPORD); 61859243Sobrien if (!*np) 61959243Sobrien stderror(ERR_SYNTAX); 62059243Sobrien if (Isdigit(*np)) { 62159243Sobrien int i; 62259243Sobrien 62359243Sobrien for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0') 62459243Sobrien continue; 625231990Smp if (i < 0 || (i > upb && !any("-*", *np))) { 626195609Smp cleanup_until(name); 62759243Sobrien dolerror(vp->v_name); 62859243Sobrien return; 62959243Sobrien } 63059243Sobrien lwb = i; 63159243Sobrien if (!*np) 63259243Sobrien upb = lwb, np = STRstar; 63359243Sobrien } 63459243Sobrien if (*np == '*') 63559243Sobrien np++; 63659243Sobrien else if (*np != '-') 63759243Sobrien stderror(ERR_MISSING, '-'); 63859243Sobrien else { 639145479Smp int i = upb; 64059243Sobrien 64159243Sobrien np++; 64259243Sobrien if (Isdigit(*np)) { 64359243Sobrien i = 0; 64459243Sobrien while (Isdigit(*np)) 64559243Sobrien i = i * 10 + *np++ - '0'; 64659243Sobrien if (i < 0 || i > upb) { 647195609Smp cleanup_until(name); 64859243Sobrien dolerror(vp->v_name); 64959243Sobrien return; 65059243Sobrien } 65159243Sobrien } 65259243Sobrien if (i < lwb) 65359243Sobrien upb = lwb - 1; 65459243Sobrien else 65559243Sobrien upb = i; 65659243Sobrien } 65759243Sobrien if (lwb == 0) { 65859243Sobrien if (upb != 0) { 659195609Smp cleanup_until(name); 66059243Sobrien dolerror(vp->v_name); 66159243Sobrien return; 66259243Sobrien } 66359243Sobrien upb = -1; 66459243Sobrien } 66559243Sobrien if (*np) 66659243Sobrien stderror(ERR_SYNTAX); 667195609Smp cleanup_until(name); 66859243Sobrien } 66959243Sobrien else { 67059243Sobrien if (subscr > 0) { 67159243Sobrien if (subscr > upb) 67259243Sobrien lwb = 1, upb = 0; 67359243Sobrien else 67459243Sobrien lwb = upb = subscr; 67559243Sobrien } 67659243Sobrien unDredc(c); 67759243Sobrien } 67859243Sobrien if (dimen) { 67959243Sobrien /* this is a kludge. It prevents Dgetdol() from */ 68059243Sobrien /* pushing erroneous ${#<error> values into the labuf. */ 68159243Sobrien if (sc == '{') { 68259243Sobrien c = Dredc(); 68359243Sobrien if (c != '}') 68459243Sobrien stderror(ERR_MISSING, '}'); 68559243Sobrien unDredc(c); 68659243Sobrien } 687231990Smp addla(putn((tcsh_number_t)(upb - lwb + 1))); 68859243Sobrien } 68959243Sobrien else if (length) { 69059243Sobrien int i; 691167465Smp 69259243Sobrien for (i = lwb - 1, length = 0; i < upb; i++) 693167465Smp length += Strlen(vp->vec[i]); 69459243Sobrien#ifdef notdef 69559243Sobrien /* We don't want that, since we can always compute it by adding $#xxx */ 69659243Sobrien length += i - 1; /* Add the number of spaces in */ 69759243Sobrien#endif 698231990Smp addla(putn((tcsh_number_t)length)); 69959243Sobrien } 70059243Sobrien else { 70159243Sobrieneatmod: 70259243Sobrien fixDolMod(); 70359243Sobrien dolnxt = &vp->vec[lwb - 1]; 70459243Sobrien dolcnt = upb - lwb + 1; 70559243Sobrien } 70659243Sobrieneatbrac: 70759243Sobrien if (sc == '{') { 70859243Sobrien c = Dredc(); 70959243Sobrien if (c != '}') 71059243Sobrien stderror(ERR_MISSING, '}'); 71159243Sobrien } 71259243Sobrien} 71359243Sobrien 71459243Sobrienstatic void 715167465SmpfixDolMod(void) 71659243Sobrien{ 717145479Smp eChar c; 71859243Sobrien 71959243Sobrien c = DgetC(0); 72059243Sobrien if (c == ':') { 72159243Sobrien do { 722167465Smp c = DgetC(0), dolmcnt = 1, dol_flag_a = 0; 72359243Sobrien if (c == 'g' || c == 'a') { 72459243Sobrien if (c == 'g') 725167465Smp dolmcnt = INT_MAX; 72659243Sobrien else 727167465Smp dol_flag_a = 1; 72859243Sobrien c = DgetC(0); 72959243Sobrien } 730167465Smp if ((c == 'g' && dolmcnt != INT_MAX) || 731167465Smp (c == 'a' && dol_flag_a == 0)) { 73259243Sobrien if (c == 'g') 733167465Smp dolmcnt = INT_MAX; 73459243Sobrien else 735167465Smp dol_flag_a = 1; 736167465Smp c = DgetC(0); 73759243Sobrien } 73859243Sobrien 73959243Sobrien if (c == 's') { /* [eichin:19910926.0755EST] */ 74059243Sobrien int delimcnt = 2; 741145479Smp eChar delim = DgetC(0); 742167465Smp Strbuf_append1(&dolmod, (Char) c); 743167465Smp Strbuf_append1(&dolmod, (Char) delim); 744167465Smp 745167465Smp if (delim == DEOF || !delim || letter(delim) 74659243Sobrien || Isdigit(delim) || any(" \t\n", delim)) { 74759243Sobrien seterror(ERR_BADSUBST); 74859243Sobrien break; 74959243Sobrien } 750145479Smp while ((c = DgetC(0)) != DEOF) { 751167465Smp Strbuf_append1(&dolmod, (Char) c); 75259243Sobrien if(c == delim) delimcnt--; 75359243Sobrien if(!delimcnt) break; 75459243Sobrien } 75559243Sobrien if(delimcnt) { 75659243Sobrien seterror(ERR_BADSUBST); 75759243Sobrien break; 75859243Sobrien } 75959243Sobrien continue; 76059243Sobrien } 76159243Sobrien if (!any("luhtrqxes", c)) 762145479Smp stderror(ERR_BADMOD, (int)c); 763167465Smp Strbuf_append1(&dolmod, (Char) c); 76459243Sobrien if (c == 'q') 765167465Smp dolmcnt = INT_MAX; 76659243Sobrien } 76759243Sobrien while ((c = DgetC(0)) == ':'); 76859243Sobrien unDredc(c); 76959243Sobrien } 77059243Sobrien else 77159243Sobrien unDredc(c); 77259243Sobrien} 77359243Sobrien 77459243Sobrienstatic void 775167465SmpsetDolp(Char *cp) 77659243Sobrien{ 777145479Smp Char *dp; 778167465Smp size_t i; 77959243Sobrien 780167465Smp if (dolmod.len == 0 || dolmcnt == 0) { 78159243Sobrien dolp = cp; 78259243Sobrien return; 78359243Sobrien } 784167465Smp cp = Strsave(cp); 785167465Smp for (i = 0; i < dolmod.len; i++) { 786167465Smp int didmod = 0; 787167465Smp 78859243Sobrien /* handle s// [eichin:19910926.0510EST] */ 789167465Smp if(dolmod.s[i] == 's') { 790145479Smp Char delim; 79159243Sobrien Char *lhsub, *rhsub, *np; 79259243Sobrien size_t lhlen = 0, rhlen = 0; 793167465Smp 794167465Smp delim = dolmod.s[++i]; 79559243Sobrien if (!delim || letter(delim) 79659243Sobrien || Isdigit(delim) || any(" \t\n", delim)) { 79759243Sobrien seterror(ERR_BADSUBST); 79859243Sobrien break; 79959243Sobrien } 800167465Smp lhsub = &dolmod.s[++i]; 801167465Smp while(dolmod.s[i] != delim && dolmod.s[++i]) { 80259243Sobrien lhlen++; 80359243Sobrien } 804167465Smp dolmod.s[i] = 0; 805167465Smp rhsub = &dolmod.s[++i]; 806167465Smp while(dolmod.s[i] != delim && dolmod.s[++i]) { 80759243Sobrien rhlen++; 80859243Sobrien } 809167465Smp dolmod.s[i] = 0; 81059243Sobrien 811167465Smp strip(lhsub); 812195609Smp strip(rhsub); 813167465Smp strip(cp); 814167465Smp dp = cp; 81559243Sobrien do { 816167465Smp dp = Strstr(dp, lhsub); 81759243Sobrien if (dp) { 818167465Smp ptrdiff_t diff = dp - cp; 819195609Smp size_t len = (Strlen(cp) + 1 - lhlen + rhlen); 820195609Smp np = xmalloc(len * sizeof(Char)); 821167465Smp (void) Strncpy(np, cp, diff); 822167465Smp (void) Strcpy(np + diff, rhsub); 823167465Smp (void) Strcpy(np + diff + rhlen, dp + lhlen); 82459243Sobrien 825167465Smp xfree(cp); 826316957Sdchagin dp = cp = np; 827195609Smp cp[--len] = '\0'; 82859243Sobrien didmod = 1; 829231990Smp if (diff >= (ssize_t)len) 830195609Smp break; 83159243Sobrien } else { 83259243Sobrien /* should this do a seterror? */ 83359243Sobrien break; 83459243Sobrien } 83559243Sobrien } 836167465Smp while (dol_flag_a != 0); 83759243Sobrien /* 83859243Sobrien * restore dolmod for additional words 83959243Sobrien */ 840167465Smp dolmod.s[i] = rhsub[-1] = (Char) delim; 84159243Sobrien } else { 84259243Sobrien 84359243Sobrien do { 844167465Smp if ((dp = domod(cp, dolmod.s[i])) != NULL) { 84559243Sobrien didmod = 1; 84659243Sobrien if (Strcmp(cp, dp) == 0) { 847167465Smp xfree(cp); 84859243Sobrien cp = dp; 84959243Sobrien break; 85059243Sobrien } 85159243Sobrien else { 852167465Smp xfree(cp); 85359243Sobrien cp = dp; 85459243Sobrien } 85559243Sobrien } 85659243Sobrien else 85759243Sobrien break; 85859243Sobrien } 859167465Smp while (dol_flag_a != 0); 860167465Smp } 861167465Smp if (didmod && dolmcnt != INT_MAX) 862167465Smp dolmcnt--; 86359243Sobrien#ifdef notdef 864167465Smp else 865167465Smp break; 86659243Sobrien#endif 86759243Sobrien } 86859243Sobrien 869167465Smp addla(cp); 87059243Sobrien 87159243Sobrien dolp = STRNULL; 87259243Sobrien if (seterr) 87359243Sobrien stderror(ERR_OLD); 87459243Sobrien} 87559243Sobrien 87659243Sobrienstatic void 877167465SmpunDredc(eChar c) 87859243Sobrien{ 87959243Sobrien 88059243Sobrien Dpeekrd = c; 88159243Sobrien} 88259243Sobrien 883145479Smpstatic eChar 884167465SmpDredc(void) 88559243Sobrien{ 886231990Smp eChar c; 88759243Sobrien 88859243Sobrien if ((c = Dpeekrd) != 0) { 88959243Sobrien Dpeekrd = 0; 89059243Sobrien return (c); 89159243Sobrien } 89259243Sobrien if (Dcp && (c = *Dcp++)) 89359243Sobrien return (c & (QUOTE | TRIM)); 89459243Sobrien if (*Dvp == 0) { 89559243Sobrien Dcp = 0; 89659243Sobrien return (DEOF); 89759243Sobrien } 89859243Sobrien Dcp = *Dvp++; 89959243Sobrien return (' '); 90059243Sobrien} 90159243Sobrien 902167465Smpstatic int gflag; 903167465Smp 90459243Sobrienstatic void 905167465SmpDtestq(Char c) 90659243Sobrien{ 90759243Sobrien 90859243Sobrien if (cmap(c, QUOTES)) 90959243Sobrien gflag = 1; 91059243Sobrien} 91159243Sobrien 912167465Smpstatic void 913167465Smpinheredoc_cleanup(void *dummy) 914167465Smp{ 915167465Smp USE(dummy); 916167465Smp inheredoc = 0; 917167465Smp} 918167465Smp 919316957SdchaginChar * 920316957Sdchaginrandsuf(void) { 921316957Sdchagin#ifndef WINNT_NATIVE 922316957Sdchagin struct timeval tv; 923316957Sdchagin (void) gettimeofday(&tv, NULL); 924316957Sdchagin return putn((((tcsh_number_t)tv.tv_sec) ^ 925316957Sdchagin ((tcsh_number_t)tv.tv_usec) ^ 926316957Sdchagin ((tcsh_number_t)getpid())) & 0x00ffffff); 927316957Sdchagin#else 928316957Sdchagin return putn(getpid()); 929316957Sdchagin#endif 930316957Sdchagin} 931316957Sdchagin 93259243Sobrien/* 93359243Sobrien * Form a shell temporary file (in unit 0) from the words 93459243Sobrien * of the shell input up to EOF or a line the same as "term". 93559243Sobrien * Unit 0 should have been closed before this call. 93659243Sobrien */ 93759243Sobrienvoid 938167465Smpheredoc(Char *term) 93959243Sobrien{ 940145479Smp eChar c; 94159243Sobrien Char *Dv[2]; 942167465Smp struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT; 943167465Smp Char obuf[BUFSIZE + 1]; 944167465Smp#define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1) 945145479Smp Char *lbp, *obp, *mbp; 94659243Sobrien Char **vp; 947145479Smp int quoted; 948231990Smp#ifdef HAVE_MKSTEMP 949231990Smp char *tmp = short2str(shtemp); 950231990Smp char *dot = strrchr(tmp, '.'); 951231990Smp 952231990Smp if (!dot) 953231990Smp stderror(ERR_NAME | ERR_NOMATCH); 954231990Smp strcpy(dot, TMP_TEMPLATE); 955231990Smp 956231990Smp xclose(0); 957231990Smp if (mkstemp(tmp) == -1) 958231990Smp stderror(ERR_SYSTEM, tmp, strerror(errno)); 959231990Smp#else /* !HAVE_MKSTEMP */ 96059243Sobrien char *tmp; 961231990Smp# ifndef WINNT_NATIVE 96259243Sobrien 96368332Skrisagain: 964231990Smp# endif /* WINNT_NATIVE */ 96559243Sobrien tmp = short2str(shtemp); 966231990Smp# if O_CREAT == 0 967167465Smp if (xcreat(tmp, 0600) < 0) 96859243Sobrien stderror(ERR_SYSTEM, tmp, strerror(errno)); 969231990Smp# endif 970167465Smp xclose(0); 971167465Smp if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) == 972167465Smp -1) { 97368332Skris int oerrno = errno; 974231990Smp# ifndef WINNT_NATIVE 97568332Skris if (errno == EEXIST) { 97668332Skris if (unlink(tmp) == -1) { 977167465Smp xfree(shtemp); 978316957Sdchagin mbp = randsuf(); 979167465Smp shtemp = Strspl(STRtmpsh, mbp); 980167465Smp xfree(mbp); 98168332Skris } 98268332Skris goto again; 98368332Skris } 984231990Smp# endif /* WINNT_NATIVE */ 98559243Sobrien (void) unlink(tmp); 98659243Sobrien errno = oerrno; 98768332Skris stderror(ERR_SYSTEM, tmp, strerror(errno)); 98859243Sobrien } 989231990Smp#endif /* HAVE_MKSTEMP */ 99059243Sobrien (void) unlink(tmp); /* 0 0 inode! */ 99159243Sobrien Dv[0] = term; 99259243Sobrien Dv[1] = NULL; 99359243Sobrien gflag = 0; 99459243Sobrien trim(Dv); 99559243Sobrien rscan(Dv, Dtestq); 99659243Sobrien quoted = gflag; 99759243Sobrien obp = obuf; 998145479Smp obuf[BUFSIZE] = 0; 99959243Sobrien inheredoc = 1; 1000167465Smp cleanup_push(&inheredoc, inheredoc_cleanup); 100169408Sache#ifdef WINNT_NATIVE 100259243Sobrien __dup_stdin = 1; 100369408Sache#endif /* WINNT_NATIVE */ 1004167465Smp cleanup_push(&lbuf, Strbuf_cleanup); 1005167465Smp cleanup_push(&mbuf, Strbuf_cleanup); 100659243Sobrien for (;;) { 1007167465Smp Char **words; 1008167465Smp 100959243Sobrien /* 101059243Sobrien * Read up a line 101159243Sobrien */ 1012167465Smp lbuf.len = 0; 101359243Sobrien for (;;) { 101459243Sobrien c = readc(1); /* 1 -> Want EOF returns */ 1015145479Smp if (c == CHAR_ERR || c == '\n') 101659243Sobrien break; 1017167465Smp if ((c &= TRIM) != 0) 1018167465Smp Strbuf_append1(&lbuf, (Char) c); 101959243Sobrien } 1020167465Smp Strbuf_terminate(&lbuf); 102159243Sobrien 1022231990Smp /* Catch EOF in the middle of a line. */ 1023231990Smp if (c == CHAR_ERR && lbuf.len != 0) 1024231990Smp c = '\n'; 1025231990Smp 102659243Sobrien /* 102759243Sobrien * Check for EOF or compare to terminator -- before expansion 102859243Sobrien */ 1029167465Smp if (c == CHAR_ERR || eq(lbuf.s, term)) 1030167465Smp break; 103159243Sobrien 103259243Sobrien /* 103359243Sobrien * If term was quoted or -n just pass it on 103459243Sobrien */ 103559243Sobrien if (quoted || noexec) { 1036167465Smp Strbuf_append1(&lbuf, '\n'); 1037167465Smp Strbuf_terminate(&lbuf); 1038167465Smp for (lbp = lbuf.s; (c = *lbp++) != 0;) { 103959243Sobrien *obp++ = (Char) c; 1040167465Smp if (obp == OBUF_END) { 1041145479Smp tmp = short2str(obuf); 1042167465Smp (void) xwrite(0, tmp, strlen (tmp)); 104359243Sobrien obp = obuf; 104459243Sobrien } 104559243Sobrien } 104659243Sobrien continue; 104759243Sobrien } 104859243Sobrien 104959243Sobrien /* 105059243Sobrien * Term wasn't quoted so variable and then command expand the input 105159243Sobrien * line 105259243Sobrien */ 1053167465Smp Dcp = lbuf.s; 105459243Sobrien Dvp = Dv + 1; 1055167465Smp mbuf.len = 0; 105659243Sobrien for (;;) { 105759243Sobrien c = DgetC(DODOL); 105859243Sobrien if (c == DEOF) 105959243Sobrien break; 106059243Sobrien if ((c &= TRIM) == 0) 106159243Sobrien continue; 106259243Sobrien /* \ quotes \ $ ` here */ 106359243Sobrien if (c == '\\') { 106459243Sobrien c = DgetC(0); 106559243Sobrien if (!any("$\\`", c)) 106659243Sobrien unDgetC(c | QUOTE), c = '\\'; 106759243Sobrien else 106859243Sobrien c |= QUOTE; 106959243Sobrien } 1070167465Smp Strbuf_append1(&mbuf, (Char) c); 107159243Sobrien } 1072167465Smp Strbuf_terminate(&mbuf); 107359243Sobrien 107459243Sobrien /* 107559243Sobrien * If any ` in line do command substitution 107659243Sobrien */ 1077167465Smp mbp = mbuf.s; 107859243Sobrien if (Strchr(mbp, '`') != NULL) { 107959243Sobrien /* 108059243Sobrien * 1 arg to dobackp causes substitution to be literal. Words are 108159243Sobrien * broken only at newlines so that all blanks and tabs are 108259243Sobrien * preserved. Blank lines (null words) are not discarded. 108359243Sobrien */ 1084167465Smp words = dobackp(mbp, 1); 108559243Sobrien } 108659243Sobrien else 108759243Sobrien /* Setup trivial vector similar to return of dobackp */ 1088167465Smp Dv[0] = mbp, Dv[1] = NULL, words = Dv; 108959243Sobrien 109059243Sobrien /* 109159243Sobrien * Resurrect the words from the command substitution each separated by 109259243Sobrien * a newline. Note that the last newline of a command substitution 109359243Sobrien * will have been discarded, but we put a newline after the last word 109459243Sobrien * because this represents the newline after the last input line! 109559243Sobrien */ 1096167465Smp for (vp= words; *vp; vp++) { 109759243Sobrien for (mbp = *vp; *mbp; mbp++) { 109859243Sobrien *obp++ = *mbp & TRIM; 1099167465Smp if (obp == OBUF_END) { 1100145479Smp tmp = short2str(obuf); 1101167465Smp (void) xwrite(0, tmp, strlen (tmp)); 110259243Sobrien obp = obuf; 110359243Sobrien } 110459243Sobrien } 110559243Sobrien *obp++ = '\n'; 1106167465Smp if (obp == OBUF_END) { 1107145479Smp tmp = short2str(obuf); 1108167465Smp (void) xwrite(0, tmp, strlen (tmp)); 110959243Sobrien obp = obuf; 111059243Sobrien } 111159243Sobrien } 1112167465Smp if (words != Dv) 1113167465Smp blkfree(words); 111459243Sobrien } 1115167465Smp *obp = 0; 1116167465Smp tmp = short2str(obuf); 1117167465Smp (void) xwrite(0, tmp, strlen (tmp)); 1118167465Smp (void) lseek(0, (off_t) 0, L_SET); 1119167465Smp cleanup_until(&inheredoc); 112059243Sobrien} 1121