/* * 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(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, *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(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; }
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 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; }