📄 eval.c
字号:
/* * Expansion - quoting, separation, substitution, globbing */#include "sh.h"#include <pwd.h>#include "ksh_dir.h"#include "ksh_stat.h"/* * string expansion * * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution. * second pass: alternation ({,}), filename expansion (*?[]). *//* expansion generator state */typedef struct Expand { /* int type; */ /* see expand() */ const char *str; /* string */ union { const char **strv;/* string[] */ struct shf *shf;/* file */ } u; /* source */ struct tbl *var; /* variable in ${var..} */ short split; /* split "$@" / call waitlast $() */} Expand;#define XBASE 0 /* scanning original */#define XSUB 1 /* expanding ${} string */#define XARGSEP 2 /* ifs0 between "$*" */#define XARG 3 /* expanding $*, $@ */#define XCOM 4 /* expanding $() */#define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) *//* States used for field splitting */#define IFS_WORD 0 /* word has chars (or quotes) */#define IFS_WS 1 /* have seen IFS white-space */#define IFS_NWS 2 /* have seen IFS non-white-space */static int varsub ARGS((Expand *xp, char *sp, char *word, int *stypep, int *slenp));static int comsub ARGS((Expand *xp, char *cp));static char *trimsub ARGS((char *str, char *pat, int how));static void glob ARGS((char *cp, XPtrV *wp, int markdirs));static void globit ARGS((XString *xs, char **xpp, char *sp, XPtrV *wp, int check));static char *maybe_expand_tilde ARGS((char *p, XString *dsp, char **dpp, int isassign));static char *tilde ARGS((char *acp));static char *homedir ARGS((char *name));#ifdef BRACE_EXPANDstatic void alt_expand ARGS((XPtrV *wp, char *start, char *exp_start, char *end, int fdo));#endif/* compile and expand word */char *substitute(cp, f) const char *cp; int f;{ struct source *s, *sold; sold = source; s = pushs(SWSTR, ATEMP); s->start = s->str = cp; source = s; if (yylex(ONEWORD) != LWORD) internal_errorf(1, "substitute"); source = sold; afree(s, ATEMP); return evalstr(yylval.cp, f);}/* * expand arg-list */char **eval(ap, f) register char **ap; int f;{ XPtrV w; if (*ap == NULL) return ap; XPinit(w, 32); XPput(w, NULL); /* space for shell name */#ifdef SHARPBANG XPput(w, NULL); /* and space for one arg */#endif while (*ap != NULL) expand(*ap++, &w, f); XPput(w, NULL);#ifdef SHARPBANG return (char **) XPclose(w) + 2;#else return (char **) XPclose(w) + 1;#endif}/* * expand string */char *evalstr(cp, f) char *cp; int f;{ XPtrV w; XPinit(w, 1); expand(cp, &w, f); cp = (XPsize(w) == 0) ? null : (char*) *XPptrv(w); XPfree(w); return cp;}/* * expand string - return only one component * used from iosetup to expand redirection files */char *evalonestr(cp, f) register char *cp; int f;{ XPtrV w; XPinit(w, 1); expand(cp, &w, f); switch (XPsize(w)) { case 0: cp = null; break; case 1: cp = (char*) *XPptrv(w); break; default: cp = evalstr(cp, f&~DOGLOB); break; } XPfree(w); return cp;}/* for nested substitution: ${var:=$var2} */typedef struct SubType { short stype; /* [=+-?%#] action after expanded word */ short base; /* begin position of expanded word */ short f; /* saved value of f (DOPAT, etc) */ struct tbl *var; /* variable for ${var..} */ short quote; /* saved value of quote (for ${..[%#]..}) */ struct SubType *prev; /* old type */ struct SubType *next; /* poped type (to avoid re-allocating) */} SubType;voidexpand(cp, wp, f) char *cp; /* input word */ register XPtrV *wp; /* output words */ int f; /* DO* flags */{ register int UNINITIALIZED(c); register int type; /* expansion type */ register int quote = 0; /* quoted */ XString ds; /* destination string */ register char *dp, *sp; /* dest., source */ int fdo, word; /* second pass flags; have word */ int doblank; /* field spliting of parameter/command subst */ Expand x; /* expansion variables */ SubType st_head, *st; int UNINITIALIZED(newlines); /* For trailing newlines in COMSUB */ int saw_eq, tilde_ok; int make_magic; if (cp == NULL) internal_errorf(1, "expand(NULL)"); /* for alias, readonly, set, typeset commands */ if ((f & DOVACHECK) && is_wdvarassign(cp)) { f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE); f |= DOASNTILDE; } if (Flag(FNOGLOB)) f &= ~DOGLOB; if (Flag(FMARKDIRS)) f |= DOMARKDIRS;#ifdef BRACE_EXPAND if (Flag(FBRACEEXPAND) && (f & DOGLOB)) f |= DOBRACE_;#endif /* BRACE_EXPAND */ Xinit(ds, dp, 128, ATEMP); /* init dest. string */ type = XBASE; sp = cp; fdo = 0; saw_eq = 0; tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0; /* must be 1/0 */ doblank = 0; make_magic = 0; word = (f&DOBLANK) ? IFS_WS : IFS_WORD; st_head.next = (SubType *) 0; st = &st_head; while (1) { Xcheck(ds, dp); switch (type) { case XBASE: /* original prefixed string */ c = *sp++; switch (c) { case EOS: c = 0; break; case CHAR: c = *sp++; break; case QCHAR: quote |= 2; /* temporary quote */ c = *sp++; break; case OQUOTE: word = IFS_WORD; tilde_ok = 0; quote = 1; continue; case CQUOTE: quote = 0; continue; case COMSUB: tilde_ok = 0; if (f & DONTRUNCOMMAND) { word = IFS_WORD; *dp++ = '$'; *dp++ = '('; while (*sp != '\0') { Xcheck(ds, dp); *dp++ = *sp++; } *dp++ = ')'; } else { type = comsub(&x, sp); if (type == XCOM && (f&DOBLANK)) doblank++; sp = strchr(sp, 0) + 1; newlines = 0; } continue; case EXPRSUB: word = IFS_WORD; tilde_ok = 0; if (f & DONTRUNCOMMAND) { *dp++ = '$'; *dp++ = '('; *dp++ = '('; while (*sp != '\0') { Xcheck(ds, dp); *dp++ = *sp++; } *dp++ = ')'; *dp++ = ')'; } else { struct tbl v; char *p; v.flag = DEFINED|ISSET|INTEGER; v.type = 10; /* not default */ v.name[0] = '\0'; v_evaluate(&v, substitute(sp, 0), KSH_UNWIND_ERROR); sp = strchr(sp, 0) + 1; for (p = str_val(&v); *p; ) { Xcheck(ds, dp); *dp++ = *p++; } } continue; case OSUBST: /* ${{#}var{:}[=+-?#%]word} */ /* format is: * OSUBST [{x] plain-variable-part \0 * compiled-word-part CSUBST [}x] * This is were all syntax checking gets done... */ { char *varname = ++sp; /* skip the { or x (}) */ int stype; int slen; sp = strchr(sp, '\0') + 1; /* skip variable */ type = varsub(&x, varname, sp, &stype, &slen); if (type < 0) { char endc; char *str, *end; end = (char *) wdscan(sp, CSUBST); /* ({) the } or x is already skipped */ endc = *end; *end = EOS; str = snptreef((char *) 0, 64, "%S", varname - 1); *end = endc; errorf("%s: bad substitution", str); } if (f&DOBLANK) doblank++; tilde_ok = 0; if (type == XBASE) { /* expand? */ if (!st->next) { SubType *newst; newst = (SubType *) alloc( sizeof(SubType), ATEMP); newst->next = (SubType *) 0; newst->prev = st; st->next = newst; } st = st->next; st->stype = stype; st->base = Xsavepos(ds, dp); st->f = f; st->var = x.var; st->quote = quote; /* skip qualifier(s) */ if (stype) sp += slen; switch (stype & 0x7f) { case '#': case '%': /* ! DOBLANK,DOBRACE_,DOTILDE */ f = DOPAT | (f&DONTRUNCOMMAND) | DOTEMP_; quote = 0; /* Prepend open pattern (so | * in a trim will work as * expected) */ *dp++ = MAGIC; *dp++ = '@' + 0x80; break; case '=': /* Enabling tilde expansion * after :'s here is * non-standard ksh, but is * consistent with rules for * other assignments. Not * sure what POSIX thinks of * this. * Not doing tilde expansion * for integer variables is a * non-POSIX thing - makes * sense though, since ~ is * a arithmetic operator. */ if (!(x.var->flag & INTEGER)) f |= DOASNTILDE|DOTILDE; f |= DOTEMP_; /* These will be done after the * value has been assigned. */ f &= ~(DOBLANK|DOGLOB|DOBRACE_); tilde_ok = 1; break; case '?': f &= ~DOBLANK; f |= DOTEMP_; /* fall through */ default: /* Enable tilde expansion */ tilde_ok = 1; f |= DOTILDE; } } else /* skip word */ sp = (char *) wdscan(sp, CSUBST); continue; } case CSUBST: /* only get here if expanding word */ sp++; /* ({) skip the } or x */ tilde_ok = 0; /* in case of ${unset:-} */ *dp = '\0'; quote = st->quote; f = st->f; if (f&DOBLANK) doblank--; switch (st->stype&0x7f) { case '#': case '%': /* Append end-pattern */ *dp++ = MAGIC; *dp++ = ')'; *dp = '\0'; dp = Xrestpos(ds, dp, st->base); /* Must use st->var since calling * global would break things * like x[i+=1]. */ x.str = trimsub(str_val(st->var), dp, st->stype); type = XSUB; if (f&DOBLANK) doblank++; st = st->prev; continue; case '=': /* Restore our position and substitute * the value of st->var (may not be * the assigned value in the presence * of integer/right-adj/etc attributes). */ dp = Xrestpos(ds, dp, st->base); /* Must use st->var since calling * global would cause with things * like x[i+=1] to be evaluated twice. */ /* Note: not exported by FEXPORT * in at&t ksh. */ /* XXX POSIX says readonly is only * fatal for special builtins (setstr * does readonly check). */ setstr(st->var, debunk( (char *) alloc(strlen(dp) + 1, ATEMP), dp), KSH_UNWIND_ERROR); x.str = str_val(st->var); type = XSUB; if (f&DOBLANK) doblank++; st = st->prev; continue; case '?': { char *s = Xrestpos(ds, dp, st->base); errorf("%s: %s", st->var->name, dp == s ? "parameter null or not set" : (debunk(s, s), s)); } } st = st->prev; type = XBASE; continue; case OPAT: /* open pattern: *(foo|bar) */ /* Next char is the type of pattern */ make_magic = 1; c = *sp++ + 0x80; break; case SPAT: /* pattern seperator (|) */ make_magic = 1; c = '|'; break; case CPAT: /* close pattern */ make_magic = 1; c = /*(*/ ')'; break; } break; case XNULLSUB: /* Special case for "$@" (and "${foo[@]}") - no
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -