/* * Expand arithmetic expression. * Note that flag is not required as digits never require CTLESC characters. */ static char * expari(char *p) { char *q, *start; arith_t result; int begoff; int quoted; int adj; quoted = *p++ == '"'; begoff = expdest - stackblock(); p = argstr(p, 0); removerecordregions(begoff); STPUTC('\0', expdest); start = stackblock() + begoff; q = grabstackstr(expdest); result = arith(start); ungrabstackstr(q, expdest); start = stackblock() + begoff; adj = start - expdest; STADJUST(adj, expdest); CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest); fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result); adj = strlen(expdest); STADJUST(adj, expdest); if (!quoted) recordregion(begoff, expdest - stackblock(), 0); return p; }
/* * Expand arithmetic expression. Backup to start of expression, * evaluate, place result in (backed up) result, adjust string position. */ void expari(int flag) { char *p, *start; intmax_t result; int adjustment; int begoff; int quotes = flag & (EXP_FULL | EXP_CASE); int quoted; /* ifsfree(); */ /* * This routine is slightly over-complicated for * efficiency. First we make sure there is * enough space for the result, which may be bigger * than the expression if we add exponentation. Next we * scan backwards looking for the start of arithmetic. If the * next previous character is a CTLESC character, then we * have to rescan starting from the beginning since CTLESC * characters have to be processed left to right. */ /* SPACE_NEEDED is enough for all digits, plus possible "-", plus 2 (why?) */ #define SPACE_NEEDED ((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 1 + 2) CHECKSTRSPACE((int)(SPACE_NEEDED - 2), expdest); USTPUTC('\0', expdest); start = stackblock(); p = expdest - 1; while (*p != CTLARI && p >= start) --p; if (*p != CTLARI) error("missing CTLARI (shouldn't happen)"); if (p > start && *(p-1) == CTLESC) for (p = start; *p != CTLARI; p++) if (*p == CTLESC) p++; if (p[1] == '"') quoted=1; else quoted=0; begoff = p - start; removerecordregions(begoff); if (quotes) rmescapes(p+2); result = arith(p+2); fmtstr(p, SPACE_NEEDED, "%"PRIdMAX, result); while (*p++) ; if (quoted == 0) recordregion(begoff, p - 1 - start, 0); adjustment = expdest - p + 1; STADJUST(-adjustment, expdest); }
/* * Expand arithmetic expression. Backup to start of expression, * evaluate, place result in (backed up) result, adjust string position. */ void expari(shinstance *psh, int flag) { char *p, *start; int result; int begoff; int quotes = flag & (EXP_FULL | EXP_CASE); int quoted; /* ifsfree(); */ /* * This routine is slightly over-complicated for * efficiency. First we make sure there is * enough space for the result, which may be bigger * than the expression if we add exponentation. Next we * scan backwards looking for the start of arithmetic. If the * next previous character is a CTLESC character, then we * have to rescan starting from the beginning since CTLESC * characters have to be processed left to right. */ #if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10 #error "integers with more than 10 digits are not supported" #endif CHECKSTRSPACE(psh, 12 - 2, psh->expdest); USTPUTC(psh, '\0', psh->expdest); start = stackblock(psh); p = psh->expdest - 1; while (*p != CTLARI && p >= start) --p; if (*p != CTLARI) error(psh, "missing CTLARI (shouldn't happen)"); if (p > start && *(p-1) == CTLESC) for (p = start; *p != CTLARI; p++) if (*p == CTLESC) p++; if (p[1] == '"') quoted=1; else quoted=0; begoff = (int)(p - start); removerecordregions(psh, begoff); if (quotes) rmescapes(psh, p+2); result = arith(psh, p+2); fmtstr(p, 12, "%d", result); while (*p++) ; if (quoted == 0) recordregion(psh, begoff, (int)(p - 1 - start), 0); result = (int)(psh->expdest - p + 1); STADJUST(psh, -result, psh->expdest); }
/* * Expand arithmetic expression. Backup to start of expression, * evaluate, place result in (backed up) result, adjust string position. */ void expari(int flag) { char *p, *q, *start; arith_t result; int begoff; int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); int quoted; /* * This routine is slightly over-complicated for * efficiency. First we make sure there is * enough space for the result, which may be bigger * than the expression. Next we * scan backwards looking for the start of arithmetic. If the * next previous character is a CTLESC character, then we * have to rescan starting from the beginning since CTLESC * characters have to be processed left to right. */ CHECKSTRSPACE(DIGITS(result) - 2, expdest); USTPUTC('\0', expdest); start = stackblock(); p = expdest - 2; while (p >= start && *p != CTLARI) --p; if (p < start || *p != CTLARI) error("missing CTLARI (shouldn't happen)"); if (p > start && *(p - 1) == CTLESC) for (p = start; *p != CTLARI; p++) if (*p == CTLESC) p++; if (p[1] == '"') quoted=1; else quoted=0; begoff = p - start; removerecordregions(begoff); if (quotes) rmescapes(p+2); q = grabstackstr(expdest); result = arith(p+2); ungrabstackstr(q, expdest); fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result); while (*p++) ; if (quoted == 0) recordregion(begoff, p - 1 - start, 0); result = expdest - p + 1; STADJUST(-result, expdest); }
/* * Expand arithmetic expression. * Note that flag is not required as digits never require CTLESC characters. */ static char * expari(char *p) { char *q, *start; arith_t result; int begoff; int quoted; int c; int nesting; int adj; quoted = *p++ == '"'; begoff = expdest - stackblock(); argstr(p, 0); removerecordregions(begoff); STPUTC('\0', expdest); start = stackblock() + begoff; q = grabstackstr(expdest); result = arith(start); ungrabstackstr(q, expdest); start = stackblock() + begoff; adj = start - expdest; STADJUST(adj, expdest); CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest); fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result); adj = strlen(expdest); STADJUST(adj, expdest); if (!quoted) recordregion(begoff, expdest - stackblock(), 0); nesting = 1; while (nesting > 0) { c = *p++; if (c == CTLESC) p++; else if (c == CTLARI) nesting++; else if (c == CTLENDARI) nesting--; else if (c == CTLVAR) p++; /* ignore variable substitution byte */ else if (c == '\0') return p - 1; } return p; }
/** handle one line of the read command. * more fields than variables -> remainder shall be part of last variable. * less fields than variables -> remaining variables unset. * * @param line complete line of input * @param ap argument (variable) list * @param len length of line including trailing '\0' */ static void readcmd_handle_line(char *line, char **ap, size_t len) { struct arglist arglist; struct strlist *sl; char *s, *backup; /* ifsbreakup will fiddle with stack region... */ s = grabstackstr(line + len); /* need a copy, so that delimiters aren't lost * in case there are more fields than variables */ backup = sstrdup(line); arglist.lastp = &arglist.list; recordregion(0, len - 1, 0); ifsbreakup(s, &arglist); *arglist.lastp = NULL; removerecordregions(0); for (sl = arglist.list; sl; sl = sl->next) { /* remaining fields present, but no variables left. */ if (!ap[1]) { size_t offset; char *remainder; /* FIXME little bit hacky, assuming that ifsbreakup * will not modify the length of the string */ offset = sl->text - s; remainder = backup + offset; rmescapes(remainder); setvar(*ap, remainder, 0); return; } /* set variable to field */ rmescapes(sl->text); setvar(*ap, sl->text, 0); ap++; } /* nullify remaining arguments */ do { setvar(*ap, nullstr, 0); } while (*++ap); }
static char * evalvar(char *p, int flag) { int subtype; int varflags; char *var; const char *val; int patloc; int c; int set; int special; int startloc; int varlen; int varlenb; int easy; int quotes = flag & (EXP_FULL | EXP_CASE); int record = 0; varflags = (unsigned char)*p++; subtype = varflags & VSTYPE; var = p; special = 0; if (! is_name(*p)) special = 1; p = strchr(p, '=') + 1; again: /* jump here after setting a variable with ${var=text} */ if (varflags & VSLINENO) { set = 1; special = 1; val = NULL; } else if (special) { set = varisset(var, varflags & VSNUL); val = NULL; } else { val = bltinlookup(var, 1); if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { val = NULL; set = 0; } else set = 1; } varlen = 0; startloc = expdest - stackblock(); if (!set && uflag && *var != '@' && *var != '*') { switch (subtype) { case VSNORMAL: case VSTRIMLEFT: case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: case VSLENGTH: error("%.*s: parameter not set", (int)(p - var - 1), var); } } if (set && subtype != VSPLUS) { /* insert the value of the variable */ if (special) { if (varflags & VSLINENO) STPUTBIN(var, p - var - 1, expdest); else varvalue(var, varflags & VSQUOTE, subtype, flag); if (subtype == VSLENGTH) { varlenb = expdest - stackblock() - startloc; varlen = varlenb; if (localeisutf8) { val = stackblock() + startloc; for (;val != expdest; val++) if ((*val & 0xC0) == 0x80) varlen--; } STADJUST(-varlenb, expdest); } } else { if (subtype == VSLENGTH) { for (;*val; val++) if (!localeisutf8 || (*val & 0xC0) != 0x80) varlen++; } else strtodest(val, flag, subtype, varflags & VSQUOTE); } } if (subtype == VSPLUS) set = ! set; easy = ((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)); switch (subtype) { case VSLENGTH: expdest = cvtnum(varlen, expdest); record = 1; break; case VSNORMAL: record = easy; break; case VSPLUS: case VSMINUS: if (!set) { argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) | (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0)); break; } record = easy; break; case VSTRIMLEFT: case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: if (!set) break; /* * Terminate the string and start recording the pattern * right after it */ STPUTC('\0', expdest); patloc = expdest - stackblock(); if (subevalvar(p, NULL, patloc, subtype, startloc, varflags, quotes) == 0) { int amount = (expdest - stackblock() - patloc) + 1; STADJUST(-amount, expdest); } /* Remove any recorded regions beyond start of variable */ removerecordregions(startloc); record = 1; break; case VSASSIGN: case VSQUESTION: if (!set) { if (subevalvar(p, var, 0, subtype, startloc, varflags, quotes)) { varflags &= ~VSNUL; /* * Remove any recorded regions beyond * start of variable */ removerecordregions(startloc); goto again; } break; } record = easy; break; case VSERROR: c = p - var - 1; error("${%.*s%s}: Bad substitution", c, var, (c > 0 && *p != CTLENDVAR) ? "..." : ""); default: abort(); } if (record) recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' && (*var == '@' || *var == '*'))); if (subtype != VSNORMAL) { /* skip to end of alternative */ int nesting = 1; for (;;) { if ((c = *p++) == CTLESC) p++; else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { if (set) argbackq = argbackq->next; } else if (c == CTLVAR) { if ((*p++ & VSTYPE) != VSNORMAL) nesting++; } else if (c == CTLENDVAR) { if (--nesting == 0) break; } } } return p; }
/* * Perform command substitution. */ static void expbackq(union node *cmd, int quoted, int flag) { struct backcmd in; int i; char buf[128]; char *p; char *dest = expdest; struct ifsregion saveifs, *savelastp; struct nodelist *saveargbackq; char lastc; int startloc = dest - stackblock(); char const *syntax = quoted? DQSYNTAX : BASESYNTAX; int quotes = flag & (EXP_FULL | EXP_CASE); size_t nnl; INTOFF; saveifs = ifsfirst; savelastp = ifslastp; saveargbackq = argbackq; p = grabstackstr(dest); evalbackcmd(cmd, &in); ungrabstackstr(p, dest); ifsfirst = saveifs; ifslastp = savelastp; argbackq = saveargbackq; p = in.buf; lastc = '\0'; nnl = 0; /* Don't copy trailing newlines */ for (;;) { if (--in.nleft < 0) { if (in.fd < 0) break; while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; p = buf; in.nleft = i - 1; } lastc = *p++; if (lastc != '\0') { if (lastc == '\n') { nnl++; } else { CHECKSTRSPACE(nnl + 2, dest); while (nnl > 0) { nnl--; USTPUTC('\n', dest); } if (quotes && syntax[(int)lastc] == CCTL) USTPUTC(CTLESC, dest); USTPUTC(lastc, dest); } } } if (in.fd >= 0) close(in.fd); if (in.buf) ckfree(in.buf); if (in.jp) exitstatus = waitforjob(in.jp, (int *)NULL); if (quoted == 0) recordregion(startloc, dest - stackblock(), 0); TRACE(("expbackq: size=%td: \"%.*s\"\n", ((dest - stackblock()) - startloc), (int)((dest - stackblock()) - startloc), stackblock() + startloc)); expdest = dest; INTON; }
/* * Perform parameter expansion, command substitution and arithmetic * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'. * This is used to expand word in ${var+word} etc. * If EXP_FULL or EXP_CASE are set, keep and/or generate CTLESC * characters to allow for further processing. * If EXP_FULL is set, also preserve CTLQUOTEMARK characters. */ static char * argstr(char *p, int flag) { char c; int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ int firsteq = 1; int split_lit; int lit_quoted; split_lit = flag & EXP_SPLIT_LIT; lit_quoted = flag & EXP_LIT_QUOTED; flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(p, flag); for (;;) { CHECKSTRSPACE(2, expdest); switch (c = *p++) { case '\0': return (p - 1); case CTLENDVAR: case CTLENDARI: return (p); case CTLQUOTEMARK: lit_quoted = 1; /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') break; if ((flag & EXP_FULL) != 0) USTPUTC(c, expdest); break; case CTLQUOTEEND: lit_quoted = 0; break; case CTLESC: if (quotes) USTPUTC(c, expdest); c = *p++; USTPUTC(c, expdest); if (split_lit && !lit_quoted) recordregion(expdest - stackblock() - (quotes ? 2 : 1), expdest - stackblock(), 0); break; case CTLVAR: p = evalvar(p, flag); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: expbackq(argbackq->n, c & CTLQUOTE, flag); argbackq = argbackq->next; break; case CTLARI: p = expari(p); break; case ':': case '=': /* * sort of a hack - expand tildes in variable * assignments (after the first '=' and after ':'s). */ USTPUTC(c, expdest); if (split_lit && !lit_quoted) recordregion(expdest - stackblock() - 1, expdest - stackblock(), 0); if (flag & EXP_VARTILDE && *p == '~' && (c != '=' || firsteq)) { if (c == '=') firsteq = 0; p = exptilde(p, flag); } break; default: USTPUTC(c, expdest); if (split_lit && !lit_quoted) recordregion(expdest - stackblock() - 1, expdest - stackblock(), 0); } } }
int readcmd(int argc, char **argv) { char **ap; char c; int rflag; char *prompt; char *p; int startloc; int newloc; int status; int i; rflag = 0; prompt = NULL; while ((i = nextopt("p:r")) != '\0') { if (i == 'p') prompt = optionarg; else rflag = 1; } if (prompt && isatty(0)) { out2str(prompt); #ifdef FLUSHERR flushall(); #endif } if (*(ap = argptr) == NULL) sh_error("arg count"); status = 0; STARTSTACKSTR(p); goto start; for (;;) { switch (read(0, &c, 1)) { case 1: break; default: if (errno == EINTR && !pendingsigs) continue; /* fall through */ case 0: status = 1; goto out; } if (c == '\0') continue; if (newloc >= startloc) { if (c == '\n') goto resetbs; goto put; } if (!rflag && c == '\\') { newloc = p - (char *)stackblock(); continue; } if (c == '\n') break; put: CHECKSTRSPACE(2, p); if (strchr(qchars, c)) USTPUTC(CTLESC, p); USTPUTC(c, p); if (newloc >= startloc) { resetbs: recordregion(startloc, newloc, 0); start: startloc = p - (char *)stackblock(); newloc = startloc - 1; } } out: recordregion(startloc, p - (char *)stackblock(), 0); STACKSTRNUL(p); readcmd_handle_line(p + 1, ap); return status; }
STATIC char * evalvar(shinstance *psh, char *p, int flag) { int subtype; int varflags; char *var; char *val; int patloc; int c; int set; int special; int startloc; int varlen; int apply_ifs; int quotes = flag & (EXP_FULL | EXP_CASE); varflags = (unsigned char)*p++; subtype = varflags & VSTYPE; var = p; special = !is_name(*p); p = strchr(p, '=') + 1; again: /* jump here after setting a variable with ${var=text} */ if (special) { set = varisset(psh, var, varflags & VSNUL); val = NULL; } else { val = lookupvar(psh, var); if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { val = NULL; set = 0; } else set = 1; } varlen = 0; startloc = (int)(psh->expdest - stackblock(psh)); if (!set && uflag(psh)) { switch (subtype) { case VSNORMAL: case VSTRIMLEFT: case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: case VSLENGTH: error(psh, "%.*s: parameter not set", p - var - 1, var); /* NOTREACHED */ } } if (set && subtype != VSPLUS) { /* insert the value of the variable */ if (special) { varvalue(psh, var, varflags & VSQUOTE, subtype, flag); if (subtype == VSLENGTH) { varlen = (int)(psh->expdest - stackblock(psh) - startloc); STADJUST(psh, -varlen, psh->expdest); } } else { char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX : BASESYNTAX; if (subtype == VSLENGTH) { for (;*val; val++) varlen++; } else { while (*val) { if (quotes && syntax[(int)*val] == CCTL) STPUTC(psh, CTLESC, psh->expdest); STPUTC(psh, *val++, psh->expdest); } } } } apply_ifs = ((varflags & VSQUOTE) == 0 || (*var == '@' && psh->shellparam.nparam != 1)); switch (subtype) { case VSLENGTH: psh->expdest = cvtnum(psh, varlen, psh->expdest); break; case VSNORMAL: break; case VSPLUS: set = !set; /* FALLTHROUGH */ case VSMINUS: if (!set) { argstr(psh, p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0)); /* * ${x-a b c} doesn't get split, but removing the * 'apply_ifs = 0' apparantly breaks ${1+"$@"}.. * ${x-'a b' c} should generate 2 args. */ /* We should have marked stuff already */ apply_ifs = 0; } break; case VSTRIMLEFT: case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: if (!set) break; /* * Terminate the string and start recording the pattern * right after it */ STPUTC(psh, '\0', psh->expdest); patloc = (int)(psh->expdest - stackblock(psh)); if (subevalvar(psh, p, NULL, patloc, subtype, startloc, varflags) == 0) { int amount = (int)(psh->expdest - stackblock(psh) - patloc) + 1; STADJUST(psh, -amount, psh->expdest); } /* Remove any recorded regions beyond start of variable */ removerecordregions(psh, startloc); apply_ifs = 1; break; case VSASSIGN: case VSQUESTION: if (set) break; if (subevalvar(psh, p, var, 0, subtype, startloc, varflags)) { varflags &= ~VSNUL; /* * Remove any recorded regions beyond * start of variable */ removerecordregions(psh, startloc); goto again; } apply_ifs = 0; break; default: sh_abort(psh); } if (apply_ifs) recordregion(psh, startloc, (int)(psh->expdest - stackblock(psh)), varflags & VSQUOTE); if (subtype != VSNORMAL) { /* skip to end of alternative */ int nesting = 1; for (;;) { if ((c = *p++) == CTLESC) p++; else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { if (set) psh->argbackq = psh->argbackq->next; } else if (c == CTLVAR) { if ((*p++ & VSTYPE) != VSNORMAL) nesting++; } else if (c == CTLENDVAR) { if (--nesting == 0) break; } } } return p; }
STATIC void expbackq(shinstance *psh, union node *cmd, int quoted, int flag) { struct backcmd in; int i; char buf[128]; char *p; char *dest = psh->expdest; struct ifsregion saveifs, *savelastp; struct nodelist *saveargbackq; char lastc; int startloc = (int)(dest - stackblock(psh)); char const *syntax = quoted? DQSYNTAX : BASESYNTAX; int saveherefd; int quotes = flag & (EXP_FULL | EXP_CASE); #ifdef SH_DEAL_WITH_CRLF int pending_cr = 0; #endif INTOFF; saveifs = psh->ifsfirst; savelastp = psh->ifslastp; saveargbackq = psh->argbackq; saveherefd = psh->herefd; psh->herefd = -1; p = grabstackstr(psh, dest); evalbackcmd(psh, cmd, &in); ungrabstackstr(psh, p, dest); psh->ifsfirst = saveifs; psh->ifslastp = savelastp; psh->argbackq = saveargbackq; psh->herefd = saveherefd; p = in.buf; lastc = '\0'; for (;;) { if (--in.nleft < 0) { if (in.fd < 0) break; while ((i = shfile_read(&psh->fdtab, in.fd, buf, sizeof buf)) < 0 && errno == EINTR); TRACE((psh, "expbackq: read returns %d\n", i)); if (i <= 0) break; p = buf; in.nleft = i - 1; } lastc = *p++; #ifdef SH_DEAL_WITH_CRLF if (pending_cr) { pending_cr = 0; if (lastc != '\n') { if (quotes && syntax[(int)'\r'] == CCTL) STPUTC(psh, CTLESC, dest); STPUTC(psh, '\r', dest); } } if (lastc == '\r') pending_cr = '\r'; else #endif if (lastc != '\0') { if (quotes && syntax[(int)lastc] == CCTL) STPUTC(psh, CTLESC, dest); STPUTC(psh, lastc, dest); } } #ifdef SH_DEAL_WITH_CRLF if (pending_cr) { if (quotes && syntax[(int)'\r'] == CCTL) STPUTC(psh, CTLESC, dest); STPUTC(psh, '\r', dest); } #endif /* Eat all trailing newlines */ p = stackblock(psh) + startloc; while (dest > p && dest[-1] == '\n') STUNPUTC(psh, dest); if (in.fd >= 0) shfile_close(&psh->fdtab, in.fd); if (in.buf) ckfree(psh, in.buf); if (in.jp) psh->back_exitstatus = waitforjob(psh, in.jp); if (quoted == 0) recordregion(psh, startloc, (int)(dest - stackblock(psh)), 0); TRACE((psh, "evalbackq: size=%d: \"%.*s\"\n", (dest - stackblock(psh)) - startloc, (dest - stackblock(psh)) - startloc, stackblock(psh) + startloc)); psh->expdest = dest; INTON; }
STATIC void argstr(shinstance *psh, char *p, int flag) { char c; int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ int firsteq = 1; const char *ifs = NULL; int ifs_split = EXP_IFS_SPLIT; if (flag & EXP_IFS_SPLIT) ifs = ifsset(psh) ? ifsval(psh) : " \t\n"; if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(psh, p, flag); for (;;) { switch (c = *p++) { case '\0': case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */ return; case CTLQUOTEMARK: /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') break; if ((flag & EXP_FULL) != 0) STPUTC(psh, c, psh->expdest); ifs_split = 0; break; case CTLQUOTEEND: ifs_split = EXP_IFS_SPLIT; break; case CTLESC: if (quotes) STPUTC(psh, c, psh->expdest); c = *p++; STPUTC(psh, c, psh->expdest); break; case CTLVAR: p = evalvar(psh, p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split)); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: expbackq(psh, psh->argbackq->n, c & CTLQUOTE, flag); psh->argbackq = psh->argbackq->next; break; case CTLENDARI: expari(psh, flag); break; case ':': case '=': /* * sort of a hack - expand tildes in variable * assignments (after the first '=' and after ':'s). */ STPUTC(psh, c, psh->expdest); if (flag & EXP_VARTILDE && *p == '~') { if (c == '=') { if (firsteq) firsteq = 0; else break; } p = exptilde(psh, p, flag); } break; default: STPUTC(psh, c, psh->expdest); if (flag & EXP_IFS_SPLIT & ifs_split && strchr(ifs, c) != NULL) { /* We need to get the output split here... */ recordregion(psh, (int)(psh->expdest - stackblock(psh) - 1), (int)(psh->expdest - stackblock(psh)), 0); } break; } } }
STATIC void expbackq(union node *cmd, int quoted, int flag) { struct backcmd in; int i; char buf[128]; char *p; char *dest = expdest; struct ifsregion saveifs, *savelastp; struct nodelist *saveargbackq; char lastc; int startloc = dest - stackblock(); char const *syntax = quoted? DQSYNTAX : BASESYNTAX; int saveherefd; int quotes = flag & (EXP_FULL | EXP_CASE); INTOFF; saveifs = ifsfirst; savelastp = ifslastp; saveargbackq = argbackq; saveherefd = herefd; herefd = -1; p = grabstackstr(dest); evalbackcmd(cmd, &in); ungrabstackstr(p, dest); ifsfirst = saveifs; ifslastp = savelastp; argbackq = saveargbackq; herefd = saveherefd; p = in.buf; lastc = '\0'; for (;;) { if (--in.nleft < 0) { if (in.fd < 0) break; while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; p = buf; in.nleft = i - 1; } lastc = *p++; if (lastc != '\0') { if (quotes && syntax[(int)lastc] == CCTL) STPUTC(CTLESC, dest); STPUTC(lastc, dest); } } /* Eat all trailing newlines */ p = stackblock() + startloc; while (dest > p && dest[-1] == '\n') STUNPUTC(dest); if (in.fd >= 0) close(in.fd); if (in.buf) ckfree(in.buf); if (in.jp) back_exitstatus = waitforjob(in.jp); if (quoted == 0) recordregion(startloc, dest - stackblock(), 0); TRACE(("evalbackq: size=%d: \"%.*s\"\n", (dest - stackblock()) - startloc, (dest - stackblock()) - startloc, stackblock() + startloc)); expdest = dest; INTON; }
int readcmd(int argc, char **argv) { char **ap; char c; int rflag; char *prompt; char *p; int startloc; int newloc; int status; int timeout; int i; fd_set set; struct timeval ts, t0, t1, to; ts.tv_sec = ts.tv_usec = 0; rflag = 0; timeout = 0; prompt = NULL; while ((i = nextopt("p:rt:")) != '\0') { switch(i) { case 'p': prompt = optionarg; break; case 't': p = strtotimeval(optionarg, &ts); if (*p || (!ts.tv_sec && !ts.tv_usec)) sh_error("invalid timeout"); timeout = 1; break; case 'r': rflag = 1; break; default: break; } } if (prompt && isatty(0)) { out2str(prompt); #ifdef FLUSHERR flushall(); #endif } if (*(ap = argptr) == NULL) sh_error("arg count"); status = 0; if (timeout) { gettimeofday(&t0, NULL); /* ts += t0; */ ts.tv_usec += t0.tv_usec; while (ts.tv_usec >= 1000000) { ts.tv_sec++; ts.tv_usec -= 1000000; } ts.tv_sec += t0.tv_sec; } STARTSTACKSTR(p); goto start; for (;;) { if (timeout) { gettimeofday(&t1, NULL); if (t1.tv_sec > ts.tv_sec || (t1.tv_sec == ts.tv_sec && t1.tv_usec >= ts.tv_usec)) { status = 1; break; /* Timeout! */ } /* to = ts - t1; */ if (ts.tv_usec >= t1.tv_usec) { to.tv_usec = ts.tv_usec - t1.tv_usec; to.tv_sec = ts.tv_sec - t1.tv_sec; } else { to.tv_usec = ts.tv_usec - t1.tv_usec + 1000000; to.tv_sec = ts.tv_sec - t1.tv_sec - 1; } FD_ZERO(&set); FD_SET(0, &set); if (select(1, &set, NULL, NULL, &to) != 1) { status = 1; break; /* Timeout! */ } } switch (read(0, &c, 1)) { case 1: break; default: if (errno == EINTR && !pendingsigs) continue; /* fall through */ case 0: status = 1; goto out; } if (c == '\0') continue; if (newloc >= startloc) { if (c == '\n') goto resetbs; goto put; } if (!rflag && c == '\\') { newloc = p - (char *)stackblock(); continue; } if (c == '\n') break; put: CHECKSTRSPACE(2, p); if (strchr(qchars, c)) USTPUTC(CTLESC, p); USTPUTC(c, p); if (newloc >= startloc) { resetbs: recordregion(startloc, newloc, 0); start: startloc = p - (char *)stackblock(); newloc = startloc - 1; } } out: recordregion(startloc, p - (char *)stackblock(), 0); STACKSTRNUL(p); readcmd_handle_line(p + 1, ap); return status; }
STATIC char * evalvar(char *p, int flag) { int subtype; int varflags; char *var; char *val; int patloc; int c; int set; int special; int startloc; int varlen; int easy; int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); varflags = (unsigned char)*p++; subtype = varflags & VSTYPE; var = p; special = 0; if (! is_name(*p)) special = 1; p = strchr(p, '=') + 1; again: /* jump here after setting a variable with ${var=text} */ if (varflags & VSLINENO) { set = 1; special = 0; val = var; p[-1] = '\0'; /* temporarily overwrite '=' to have \0 terminated string */ } else if (special) { set = varisset(var, varflags & VSNUL); val = NULL; } else { val = bltinlookup(var, 1); if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { val = NULL; set = 0; } else set = 1; } varlen = 0; startloc = expdest - stackblock(); if (!set && uflag) { switch (subtype) { case VSNORMAL: case VSTRIMLEFT: case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: case VSLENGTH: error("%.*s: parameter not set", (int)(p - var - 1), var); } } if (set && subtype != VSPLUS) { /* insert the value of the variable */ if (special) { varvalue(var, varflags & VSQUOTE, subtype, flag); if (subtype == VSLENGTH) { varlen = expdest - stackblock() - startloc; STADJUST(-varlen, expdest); } } else { char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX : BASESYNTAX; if (subtype == VSLENGTH) { for (;*val; val++) varlen++; } else { while (*val) { if (quotes && syntax[(int)*val] == CCTL) STPUTC(CTLESC, expdest); STPUTC(*val++, expdest); } } } } if (subtype == VSPLUS) set = ! set; easy = ((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)); switch (subtype) { case VSLENGTH: expdest = cvtnum(varlen, expdest); goto record; case VSNORMAL: if (!easy) break; record: recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE); break; case VSPLUS: case VSMINUS: if (!set) { argstr(p, flag); break; } if (easy) goto record; break; case VSTRIMLEFT: case VSTRIMLEFTMAX: case VSTRIMRIGHT: case VSTRIMRIGHTMAX: if (!set) break; /* * Terminate the string and start recording the pattern * right after it */ STPUTC('\0', expdest); patloc = expdest - stackblock(); if (subevalvar(p, NULL, patloc, subtype, startloc, varflags) == 0) { int amount = (expdest - stackblock() - patloc) + 1; STADJUST(-amount, expdest); } /* Remove any recorded regions beyond start of variable */ removerecordregions(startloc); goto record; case VSASSIGN: case VSQUESTION: if (!set) { if (subevalvar(p, var, 0, subtype, startloc, varflags)) { varflags &= ~VSNUL; /* * Remove any recorded regions beyond * start of variable */ removerecordregions(startloc); goto again; } break; } if (easy) goto record; break; case VSERROR: c = p - var - 1; error("${%.*s%s}: Bad substitution", c, var, (c > 0 && *p != CTLENDVAR) ? "..." : ""); default: abort(); } p[-1] = '='; /* recover overwritten '=' */ if (subtype != VSNORMAL) { /* skip to end of alternative */ int nesting = 1; for (;;) { if ((c = *p++) == CTLESC) p++; else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { if (set) argbackq = argbackq->next; } else if (c == CTLVAR) { if ((*p++ & VSTYPE) != VSNORMAL) nesting++; } else if (c == CTLENDVAR) { if (--nesting == 0) break; } } } return p; }