📄 expand.c
字号:
case VSTRIMLEFT: for (loc = startp; loc < str; loc++) { c = *loc; *loc = '\0'; if (patmatch(str, startp, varflags & VSQUOTE)) goto recordleft; *loc = c; if ((varflags & VSQUOTE) && *loc == CTLESC) loc++; } return 0; case VSTRIMLEFTMAX: for (loc = str - 1; loc >= startp;) { c = *loc; *loc = '\0'; if (patmatch(str, startp, varflags & VSQUOTE)) goto recordleft; *loc = c; loc--; if ((varflags & VSQUOTE) && loc > startp && *(loc - 1) == CTLESC) { for (q = startp; q < loc; q++) if (*q == CTLESC) q++; if (q > loc) loc--; } } return 0; case VSTRIMRIGHT: for (loc = str - 1; loc >= startp;) { if (patmatch(str, loc, varflags & VSQUOTE)) goto recordright; loc--; if ((varflags & VSQUOTE) && loc > startp && *(loc - 1) == CTLESC) { for (q = startp; q < loc; q++) if (*q == CTLESC) q++; if (q > loc) loc--; } } return 0; case VSTRIMRIGHTMAX: for (loc = startp; loc < str - 1; loc++) { if (patmatch(str, loc, varflags & VSQUOTE)) goto recordright; if ((varflags & VSQUOTE) && *loc == CTLESC) loc++; } return 0; default: abort(); }recordleft: *loc = c; amount = ((str - 1) - (loc - startp)) - expdest; STADJUST(amount, expdest); while (loc != str - 1) *startp++ = *loc++; return 1;recordright: amount = loc - expdest; STADJUST(amount, expdest); STPUTC('\0', expdest); STADJUST(-1, expdest); return 1;}/* * Expand a variable, and return a pointer to the next character in the * input string. */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 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(var, varflags & VSNUL); val = NULL; } else { val = lookupvar(var); 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", p - var - 1, var); /* NOTREACHED */ } } 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); } } } } apply_ifs = ((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)); switch (subtype) { case VSLENGTH: expdest = cvtnum(varlen, expdest); break; case VSNORMAL: break; case VSPLUS: set = !set; /* FALLTHROUGH */ case VSMINUS: if (!set) { argstr(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('\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); apply_ifs = 1; break; case VSASSIGN: case VSQUESTION: if (set) break; if (subevalvar(p, var, 0, subtype, startloc, varflags)) { varflags &= ~VSNUL; /* * Remove any recorded regions beyond * start of variable */ removerecordregions(startloc); goto again; } apply_ifs = 0; break; default: abort(); } if (apply_ifs) recordregion(startloc, expdest - stackblock(), 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) argbackq = argbackq->next; } else if (c == CTLVAR) { if ((*p++ & VSTYPE) != VSNORMAL) nesting++; } else if (c == CTLENDVAR) { if (--nesting == 0) break; } } } return p;}/* * Test whether a specialized variable is set. */STATIC intvarisset(char *name, int nulok){ if (*name == '!') return backgndpid != -1; else if (*name == '@' || *name == '*') { if (*shellparam.p == NULL) return 0; if (nulok) { char **av; for (av = shellparam.p; *av; av++) if (**av != '\0') return 1; return 0; } } else if (is_digit(*name)) { char *ap; int num = atoi(name); if (num > shellparam.nparam) return 0; if (num == 0) ap = arg0; else ap = shellparam.p[num - 1]; if (nulok && (ap == NULL || *ap == '\0')) return 0; } return 1;}/* * Add the value of a specialized variable to the stack string. */STATIC voidvarvalue(char *name, int quoted, int subtype, int flag){ int num; char *p; int i; char sep; char **ap; char const *syntax;#define STRTODEST(p) \ do {\ if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \ syntax = quoted? DQSYNTAX : BASESYNTAX; \ while (*p) { \ if (syntax[(int)*p] == CCTL) \ STPUTC(CTLESC, expdest); \ STPUTC(*p++, expdest); \ } \ } else \ while (*p) \ STPUTC(*p++, expdest); \ } while (0) switch (*name) { case '$': num = rootpid; goto numvar; case '?': num = exitstatus; goto numvar; case '#': num = shellparam.nparam; goto numvar; case '!': num = backgndpid;numvar: expdest = cvtnum(num, expdest); break; case '-': for (i = 0; optlist[i].name; i++) { if (optlist[i].val) STPUTC(optlist[i].letter, expdest); } break; case '@': if (flag & EXP_FULL && quoted) { for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { STRTODEST(p); if (*ap) STPUTC('\0', expdest); } break; } /* fall through */ case '*': if (ifsset() != 0) sep = ifsval()[0]; else sep = ' '; for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { STRTODEST(p); if (*ap && sep) STPUTC(sep, expdest); } break; case '0': p = arg0; STRTODEST(p); break; default: if (is_digit(*name)) { num = atoi(name); if (num > 0 && num <= shellparam.nparam) { p = shellparam.p[num - 1]; STRTODEST(p); } } break; }}/* * Record the fact that we have to scan this region of the * string for IFS characters. */STATIC voidrecordregion(int start, int end, int inquotes){ struct ifsregion *ifsp; if (ifslastp == NULL) { ifsp = &ifsfirst; } else { if (ifslastp->endoff == start && ifslastp->inquotes == inquotes) { /* extend previous area */ ifslastp->endoff = end; return; } ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); ifslastp->next = ifsp; } ifslastp = ifsp; ifslastp->next = NULL; ifslastp->begoff = start; ifslastp->endoff = end; ifslastp->inquotes = inquotes;}/* * Break the argument string into pieces based upon IFS and add the * strings to the argument list. The regions of the string to be * searched for IFS characters have been stored by recordregion. */STATIC voidifsbreakup(char *string, struct arglist *arglist){ struct ifsregion *ifsp; struct strlist *sp; char *start; char *p; char *q; const char *ifs; const char *ifsspc; int inquotes; start = string; ifsspc = NULL; inquotes = 0; if (ifslastp == NULL) { /* Return entire argument, IFS doesn't apply to any of it */ sp = (struct strlist *)stalloc(sizeof *sp); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; return; } ifs = ifsset() ? ifsval() : " \t\n"; for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { p = string + ifsp->begoff; inquotes = ifsp->inquotes; ifsspc = NULL; while (p < string + ifsp->endoff) { q = p; if (*p == CTLESC) p++; if (inquotes) { /* Only NULs (probably from "$@") end args */ if (*p != 0) { p++; continue; } } else { if (!strchr(ifs, *p)) { p++; continue; } ifsspc = strchr(" \t\n", *p); /* Ignore IFS whitespace at start */ if (q == start && ifsspc != NULL) { p++; start = p; continue; } } /* Save this argument... */ *q = '\0'; sp = (struct strlist *)stalloc(sizeof *sp); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; p++; if (ifsspc != NULL) { /* Ignore further trailing IFS whitespace */ for (; p < string + ifsp->endoff; p++) { q = p; if (*p == CTLESC) p++; if (strchr(ifs, *p) == NULL) { p = q; break; } if (strchr(" \t\n", *p) == NULL) { p++; break; } } } start = p; } } /* * Save anything left as an argument. * Traditionally we have treated 'IFS=':'; set -- x$IFS' as * generating 2 arguments, the second of which is empty. * Some recent clarification of the Posix spec say that it * should only generate one.... */ if (*start /* || (!ifsspc && start > string) */) { sp = (struct strlist *)stalloc(sizeof *sp); sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -