147116Sbostic /*- 260698Sbostic * Copyright (c) 1991, 1993 360698Sbostic * The Regents of the University of California. All rights reserved. 447116Sbostic * 547116Sbostic * This code is derived from software contributed to Berkeley by 647116Sbostic * Kenneth Almquist. 747116Sbostic * 847116Sbostic * %sccs.include.redist.c% 947116Sbostic */ 1047116Sbostic 1147116Sbostic #ifndef lint 12*69090Sbostic static char sccsid[] = "@(#)expand.c 8.3 (Berkeley) 04/28/95"; 1347116Sbostic #endif /* not lint */ 1447116Sbostic 1547116Sbostic /* 1647116Sbostic * Routines to expand arguments to commands. We have to deal with 1747116Sbostic * backquotes, shell variables, and file metacharacters. 1847116Sbostic */ 1947116Sbostic 2047116Sbostic #include "shell.h" 2147116Sbostic #include "main.h" 2247116Sbostic #include "nodes.h" 2347116Sbostic #include "eval.h" 2447116Sbostic #include "expand.h" 2547116Sbostic #include "syntax.h" 2647116Sbostic #include "parser.h" 2747116Sbostic #include "jobs.h" 2847116Sbostic #include "options.h" 2947116Sbostic #include "var.h" 3047116Sbostic #include "input.h" 3147116Sbostic #include "output.h" 3247116Sbostic #include "memalloc.h" 3347116Sbostic #include "error.h" 3447116Sbostic #include "mystring.h" 3547116Sbostic #include <sys/types.h> 3653276Smarc #include <sys/time.h> 3747116Sbostic #include <sys/stat.h> 3847116Sbostic #include <errno.h> 3947116Sbostic #include <dirent.h> 4053299Smarc #include <pwd.h> 4147116Sbostic 4247116Sbostic /* 4347116Sbostic * Structure specifying which parts of the string should be searched 4447116Sbostic * for IFS characters. 4547116Sbostic */ 4647116Sbostic 4747116Sbostic struct ifsregion { 4847116Sbostic struct ifsregion *next; /* next region in list */ 4947116Sbostic int begoff; /* offset of start of region */ 5047116Sbostic int endoff; /* offset of end of region */ 5147116Sbostic int nulonly; /* search for nul bytes only */ 5247116Sbostic }; 5347116Sbostic 5447116Sbostic 5547116Sbostic char *expdest; /* output of current string */ 5647116Sbostic struct nodelist *argbackq; /* list of back quote expressions */ 5747116Sbostic struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 5847116Sbostic struct ifsregion *ifslastp; /* last struct in list */ 5947116Sbostic struct arglist exparg; /* holds expanded arg list */ 6047116Sbostic 6147116Sbostic #ifdef __STDC__ 6247116Sbostic STATIC void argstr(char *, int); 6347116Sbostic STATIC void expbackq(union node *, int, int); 64*69090Sbostic STATIC int subevalvar(char *, char *, int, int, int); 6547116Sbostic STATIC char *evalvar(char *, int); 6647116Sbostic STATIC int varisset(int); 6747116Sbostic STATIC void varvalue(int, int, int); 6847116Sbostic STATIC void recordregion(int, int, int); 6947116Sbostic STATIC void ifsbreakup(char *, struct arglist *); 7053299Smarc STATIC void expandmeta(struct strlist *, int); 7147116Sbostic STATIC void expmeta(char *, char *); 7255787Smarc STATIC void expari(int); 7347116Sbostic STATIC void addfname(char *); 7447116Sbostic STATIC struct strlist *expsort(struct strlist *); 7547116Sbostic STATIC struct strlist *msort(struct strlist *, int); 7647116Sbostic STATIC int pmatch(char *, char *); 7753299Smarc STATIC char *exptilde(char *, int); 78*69090Sbostic STATIC char *cvtnum(int, char *); 7947116Sbostic #else 8047116Sbostic STATIC void argstr(); 8147116Sbostic STATIC void expbackq(); 82*69090Sbostic STATIC int subevalvar(); 8347116Sbostic STATIC char *evalvar(); 8447116Sbostic STATIC int varisset(); 8547116Sbostic STATIC void varvalue(); 8647116Sbostic STATIC void recordregion(); 8747116Sbostic STATIC void ifsbreakup(); 8847116Sbostic STATIC void expandmeta(); 8947116Sbostic STATIC void expmeta(); 9055787Smarc STATIC void expari(); 9147116Sbostic STATIC void addfname(); 9247116Sbostic STATIC struct strlist *expsort(); 9347116Sbostic STATIC struct strlist *msort(); 9447116Sbostic STATIC int pmatch(); 9553299Smarc STATIC char *exptilde(); 96*69090Sbostic STATIC char *cvtnum(); 9747116Sbostic #endif 9847116Sbostic 9947116Sbostic /* 10047116Sbostic * Expand shell variables and backquotes inside a here document. 10147116Sbostic */ 10247116Sbostic 10347116Sbostic void 10447116Sbostic expandhere(arg, fd) 10547116Sbostic union node *arg; /* the document */ 10647116Sbostic int fd; /* where to write the expanded version */ 10747116Sbostic { 10847116Sbostic herefd = fd; 10947116Sbostic expandarg(arg, (struct arglist *)NULL, 0); 11047116Sbostic xwrite(fd, stackblock(), expdest - stackblock()); 11147116Sbostic } 11247116Sbostic 11347116Sbostic 11447116Sbostic /* 11547116Sbostic * Perform variable substitution and command substitution on an argument, 11655787Smarc * placing the resulting list of arguments in arglist. If EXP_FULL is true, 11747116Sbostic * perform splitting and file name expansion. When arglist is NULL, perform 11847116Sbostic * here document expansion. 11947116Sbostic */ 12047116Sbostic 12147116Sbostic void 12253299Smarc expandarg(arg, arglist, flag) 12347116Sbostic union node *arg; 12447116Sbostic struct arglist *arglist; 12547116Sbostic { 12647116Sbostic struct strlist *sp; 12747116Sbostic char *p; 12847116Sbostic 12947116Sbostic argbackq = arg->narg.backquote; 13047116Sbostic STARTSTACKSTR(expdest); 13147116Sbostic ifsfirst.next = NULL; 13247116Sbostic ifslastp = NULL; 13353299Smarc argstr(arg->narg.text, flag); 13454315Smarc if (arglist == NULL) { 13547116Sbostic return; /* here document expanded */ 13654315Smarc } 13747116Sbostic STPUTC('\0', expdest); 13847116Sbostic p = grabstackstr(expdest); 13947116Sbostic exparg.lastp = &exparg.list; 14053299Smarc /* 14153299Smarc * TODO - EXP_REDIR 14253299Smarc */ 14353299Smarc if (flag & EXP_FULL) { 14447116Sbostic ifsbreakup(p, &exparg); 14547116Sbostic *exparg.lastp = NULL; 14647116Sbostic exparg.lastp = &exparg.list; 14753299Smarc expandmeta(exparg.list, flag); 14847116Sbostic } else { 14954315Smarc if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 15054315Smarc rmescapes(p); 15147116Sbostic sp = (struct strlist *)stalloc(sizeof (struct strlist)); 15247116Sbostic sp->text = p; 15347116Sbostic *exparg.lastp = sp; 15447116Sbostic exparg.lastp = &sp->next; 15547116Sbostic } 15647116Sbostic while (ifsfirst.next != NULL) { 15747116Sbostic struct ifsregion *ifsp; 15847116Sbostic INTOFF; 15947116Sbostic ifsp = ifsfirst.next->next; 16047116Sbostic ckfree(ifsfirst.next); 16147116Sbostic ifsfirst.next = ifsp; 16247116Sbostic INTON; 16347116Sbostic } 16447116Sbostic *exparg.lastp = NULL; 16547116Sbostic if (exparg.list) { 16647116Sbostic *arglist->lastp = exparg.list; 16747116Sbostic arglist->lastp = exparg.lastp; 16847116Sbostic } 16947116Sbostic } 17047116Sbostic 17147116Sbostic 17247116Sbostic 17347116Sbostic /* 17455787Smarc * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 17555787Smarc * characters to allow for further processing. Otherwise treat 17647116Sbostic * $@ like $* since no splitting will be performed. 17747116Sbostic */ 17847116Sbostic 17947116Sbostic STATIC void 18053299Smarc argstr(p, flag) 18147116Sbostic register char *p; 18247116Sbostic { 18355787Smarc register char c; 18455787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ 18555799Smarc int firsteq = 1; 18647116Sbostic 18753299Smarc if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 18855787Smarc p = exptilde(p, flag); 18947116Sbostic for (;;) { 19047116Sbostic switch (c = *p++) { 19147116Sbostic case '\0': 19253276Smarc case CTLENDVAR: /* ??? */ 19347116Sbostic goto breakloop; 19447116Sbostic case CTLESC: 19555787Smarc if (quotes) 19647116Sbostic STPUTC(c, expdest); 19747116Sbostic c = *p++; 19847116Sbostic STPUTC(c, expdest); 19947116Sbostic break; 20047116Sbostic case CTLVAR: 20153299Smarc p = evalvar(p, flag); 20247116Sbostic break; 20347116Sbostic case CTLBACKQ: 20447116Sbostic case CTLBACKQ|CTLQUOTE: 20555787Smarc expbackq(argbackq->n, c & CTLQUOTE, flag); 20647116Sbostic argbackq = argbackq->next; 20747116Sbostic break; 20853276Smarc case CTLENDARI: 20955787Smarc expari(flag); 21053276Smarc break; 21153299Smarc case ':': 21255799Smarc case '=': 21355799Smarc /* 21455799Smarc * sort of a hack - expand tildes in variable 21555799Smarc * assignments (after the first '=' and after ':'s). 21655799Smarc */ 21754315Smarc STPUTC(c, expdest); 21855799Smarc if (flag & EXP_VARTILDE && *p == '~') { 21955799Smarc if (c == '=') { 22055799Smarc if (firsteq) 22155799Smarc firsteq = 0; 22255799Smarc else 22355799Smarc break; 22455799Smarc } 22555787Smarc p = exptilde(p, flag); 22655799Smarc } 22754315Smarc break; 22847116Sbostic default: 22947116Sbostic STPUTC(c, expdest); 23047116Sbostic } 23147116Sbostic } 23247116Sbostic breakloop:; 23347116Sbostic } 23447116Sbostic 23553299Smarc STATIC char * 23655787Smarc exptilde(p, flag) 23753299Smarc char *p; 23853299Smarc { 23953299Smarc char c, *startp = p; 24053299Smarc struct passwd *pw; 24153299Smarc char *home; 24255787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 24353299Smarc 24453299Smarc while (c = *p) { 24553299Smarc switch(c) { 24653299Smarc case CTLESC: 24753299Smarc return (startp); 24853299Smarc case ':': 24955787Smarc if (flag & EXP_VARTILDE) 25053299Smarc goto done; 25153299Smarc break; 25253299Smarc case '/': 25353299Smarc goto done; 25453299Smarc } 25553299Smarc p++; 25653299Smarc } 25753299Smarc done: 25853299Smarc *p = '\0'; 25953299Smarc if (*(startp+1) == '\0') { 26053299Smarc if ((home = lookupvar("HOME")) == NULL) 26153299Smarc goto lose; 26253299Smarc } else { 26353299Smarc if ((pw = getpwnam(startp+1)) == NULL) 26453299Smarc goto lose; 26553299Smarc home = pw->pw_dir; 26653299Smarc } 26753299Smarc if (*home == '\0') 26853299Smarc goto lose; 26953299Smarc *p = c; 27053299Smarc while (c = *home++) { 27155787Smarc if (quotes && SQSYNTAX[c] == CCTL) 27253299Smarc STPUTC(CTLESC, expdest); 27353299Smarc STPUTC(c, expdest); 27453299Smarc } 27553299Smarc return (p); 27653299Smarc lose: 27753299Smarc *p = c; 27853299Smarc return (startp); 27953299Smarc } 28053299Smarc 28153299Smarc 28253276Smarc /* 28353276Smarc * Expand arithmetic expression. Backup to start of expression, 28453276Smarc * evaluate, place result in (backed up) result, adjust string position. 28553276Smarc */ 28655787Smarc void 28755787Smarc expari(flag) 28853299Smarc { 28955243Smarc char *p, *start; 29053276Smarc int result; 29155787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 29247116Sbostic 29353276Smarc /* 29453276Smarc * This routine is slightly over-compilcated for 29553276Smarc * efficiency. First we make sure there is 29653276Smarc * enough space for the result, which may be bigger 29753276Smarc * than the expression if we add exponentation. Next we 29853276Smarc * scan backwards looking for the start of arithmetic. If the 29953276Smarc * next previous character is a CTLESC character, then we 30053276Smarc * have to rescan starting from the beginning since CTLESC 30153276Smarc * characters have to be processed left to right. 30253276Smarc */ 30353276Smarc CHECKSTRSPACE(8, expdest); 30455243Smarc USTPUTC('\0', expdest); 30555243Smarc start = stackblock(); 30653276Smarc p = expdest; 30753276Smarc while (*p != CTLARI && p >= start) 30853276Smarc --p; 30953276Smarc if (*p != CTLARI) 31053276Smarc error("missing CTLARI (shouldn't happen)"); 31153276Smarc if (p > start && *(p-1) == CTLESC) 31253276Smarc for (p = start; *p != CTLARI; p++) 31353276Smarc if (*p == CTLESC) 31453276Smarc p++; 31555787Smarc if (quotes) 31655787Smarc rmescapes(p+1); 31753276Smarc result = arith(p+1); 31853276Smarc fmtstr(p, 10, "%d", result); 31953276Smarc while (*p++) 32053276Smarc ; 32153276Smarc result = expdest - p + 1; 32253276Smarc STADJUST(-result, expdest); 32353276Smarc } 32453276Smarc 32553276Smarc 32647116Sbostic /* 32747116Sbostic * Expand stuff in backwards quotes. 32847116Sbostic */ 32947116Sbostic 33047116Sbostic STATIC void 33155787Smarc expbackq(cmd, quoted, flag) 33247116Sbostic union node *cmd; 33347116Sbostic { 33447116Sbostic struct backcmd in; 33547116Sbostic int i; 33647116Sbostic char buf[128]; 33747116Sbostic char *p; 33847116Sbostic char *dest = expdest; 33947116Sbostic struct ifsregion saveifs, *savelastp; 34047116Sbostic struct nodelist *saveargbackq; 34147116Sbostic char lastc; 34247116Sbostic int startloc = dest - stackblock(); 34347116Sbostic char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 34447116Sbostic int saveherefd; 34555787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 34647116Sbostic 34747116Sbostic INTOFF; 34847116Sbostic saveifs = ifsfirst; 34947116Sbostic savelastp = ifslastp; 35047116Sbostic saveargbackq = argbackq; 35147116Sbostic saveherefd = herefd; 35247116Sbostic herefd = -1; 35347116Sbostic p = grabstackstr(dest); 35447116Sbostic evalbackcmd(cmd, &in); 35547116Sbostic ungrabstackstr(p, dest); 35647116Sbostic ifsfirst = saveifs; 35747116Sbostic ifslastp = savelastp; 35847116Sbostic argbackq = saveargbackq; 35947116Sbostic herefd = saveherefd; 36047116Sbostic 36147116Sbostic p = in.buf; 36247116Sbostic lastc = '\0'; 36347116Sbostic for (;;) { 36447116Sbostic if (--in.nleft < 0) { 36547116Sbostic if (in.fd < 0) 36647116Sbostic break; 36747116Sbostic while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 36847116Sbostic TRACE(("expbackq: read returns %d\n", i)); 36947116Sbostic if (i <= 0) 37047116Sbostic break; 37147116Sbostic p = buf; 37247116Sbostic in.nleft = i - 1; 37347116Sbostic } 37447116Sbostic lastc = *p++; 37547116Sbostic if (lastc != '\0') { 37655787Smarc if (quotes && syntax[lastc] == CCTL) 37747116Sbostic STPUTC(CTLESC, dest); 37847116Sbostic STPUTC(lastc, dest); 37947116Sbostic } 38047116Sbostic } 38147116Sbostic if (lastc == '\n') { 38247116Sbostic STUNPUTC(dest); 38347116Sbostic } 38447116Sbostic if (in.fd >= 0) 38547116Sbostic close(in.fd); 38647116Sbostic if (in.buf) 38747116Sbostic ckfree(in.buf); 38847116Sbostic if (in.jp) 38959176Storek exitstatus = waitforjob(in.jp); 39047116Sbostic if (quoted == 0) 39147116Sbostic recordregion(startloc, dest - stackblock(), 0); 39247116Sbostic TRACE(("evalbackq: size=%d: \"%.*s\"\n", 39347116Sbostic (dest - stackblock()) - startloc, 39447116Sbostic (dest - stackblock()) - startloc, 39547116Sbostic stackblock() + startloc)); 39647116Sbostic expdest = dest; 39747116Sbostic INTON; 39847116Sbostic } 39947116Sbostic 40047116Sbostic 40147116Sbostic 402*69090Sbostic STATIC int 403*69090Sbostic subevalvar(p, str, subtype, startloc, varflags) 404*69090Sbostic char *p; 405*69090Sbostic char *str; 406*69090Sbostic int subtype; 407*69090Sbostic int startloc; 408*69090Sbostic int varflags; 409*69090Sbostic { 410*69090Sbostic 411*69090Sbostic char *startp; 412*69090Sbostic char *loc; 413*69090Sbostic int c = 0; 414*69090Sbostic int saveherefd = herefd; 415*69090Sbostic struct nodelist *saveargbackq = argbackq; 416*69090Sbostic herefd = -1; 417*69090Sbostic argstr(p, 0); 418*69090Sbostic STACKSTRNUL(expdest); 419*69090Sbostic herefd = saveherefd; 420*69090Sbostic argbackq = saveargbackq; 421*69090Sbostic startp = stackblock() + startloc; 422*69090Sbostic 423*69090Sbostic switch (subtype) { 424*69090Sbostic case VSASSIGN: 425*69090Sbostic setvar(str, startp, 0); 426*69090Sbostic STADJUST(startp - expdest, expdest); 427*69090Sbostic varflags &= ~VSNUL; 428*69090Sbostic if (c != 0) 429*69090Sbostic *loc = c; 430*69090Sbostic return 1; 431*69090Sbostic 432*69090Sbostic case VSQUESTION: 433*69090Sbostic if (*p != CTLENDVAR) { 434*69090Sbostic outfmt(&errout, "%s\n", startp); 435*69090Sbostic error((char *)NULL); 436*69090Sbostic } 437*69090Sbostic error("%.*s: parameter %snot set", p - str - 1, 438*69090Sbostic str, (varflags & VSNUL) ? "null or " 439*69090Sbostic : nullstr); 440*69090Sbostic return 0; 441*69090Sbostic 442*69090Sbostic case VSTRIMLEFT: 443*69090Sbostic for (loc = startp; loc < str - 1; loc++) { 444*69090Sbostic c = *loc; 445*69090Sbostic *loc = '\0'; 446*69090Sbostic if (patmatch(str, startp)) { 447*69090Sbostic *loc = c; 448*69090Sbostic goto recordleft; 449*69090Sbostic } 450*69090Sbostic *loc = c; 451*69090Sbostic } 452*69090Sbostic return 0; 453*69090Sbostic 454*69090Sbostic case VSTRIMLEFTMAX: 455*69090Sbostic for (loc = str - 1; loc >= startp; loc--) { 456*69090Sbostic c = *loc; 457*69090Sbostic *loc = '\0'; 458*69090Sbostic if (patmatch(str, startp)) { 459*69090Sbostic *loc = c; 460*69090Sbostic goto recordleft; 461*69090Sbostic } 462*69090Sbostic *loc = c; 463*69090Sbostic } 464*69090Sbostic return 0; 465*69090Sbostic 466*69090Sbostic case VSTRIMRIGHT: 467*69090Sbostic for (loc = str - 1; loc >= startp; loc--) { 468*69090Sbostic if (patmatch(str, loc)) { 469*69090Sbostic expdest = loc; 470*69090Sbostic return 1; 471*69090Sbostic } 472*69090Sbostic } 473*69090Sbostic return 0; 474*69090Sbostic 475*69090Sbostic case VSTRIMRIGHTMAX: 476*69090Sbostic for (loc = startp; loc < str - 1; loc++) { 477*69090Sbostic if (patmatch(str, loc)) { 478*69090Sbostic expdest = loc; 479*69090Sbostic return 1; 480*69090Sbostic } 481*69090Sbostic } 482*69090Sbostic return 0; 483*69090Sbostic 484*69090Sbostic 485*69090Sbostic default: 486*69090Sbostic abort(); 487*69090Sbostic } 488*69090Sbostic 489*69090Sbostic recordleft: 490*69090Sbostic expdest = (str - 1) - (loc - startp); 491*69090Sbostic while (loc != str - 1) 492*69090Sbostic *startp++ = *loc++; 493*69090Sbostic return 1; 494*69090Sbostic } 495*69090Sbostic 496*69090Sbostic 49747116Sbostic /* 49847116Sbostic * Expand a variable, and return a pointer to the next character in the 49947116Sbostic * input string. 50047116Sbostic */ 50147116Sbostic 50247116Sbostic STATIC char * 50353299Smarc evalvar(p, flag) 50447116Sbostic char *p; 50547116Sbostic { 50647116Sbostic int subtype; 50753299Smarc int varflags; 50847116Sbostic char *var; 50947116Sbostic char *val; 510*69090Sbostic char *pat; 51147116Sbostic int c; 51247116Sbostic int set; 51347116Sbostic int special; 51447116Sbostic int startloc; 515*69090Sbostic int varlen; 516*69090Sbostic int easy; 51755787Smarc int quotes = flag & (EXP_FULL | EXP_CASE); 51847116Sbostic 51953299Smarc varflags = *p++; 52053299Smarc subtype = varflags & VSTYPE; 52147116Sbostic var = p; 52247116Sbostic special = 0; 52347116Sbostic if (! is_name(*p)) 52447116Sbostic special = 1; 52547116Sbostic p = strchr(p, '=') + 1; 52647116Sbostic again: /* jump here after setting a variable with ${var=text} */ 52747116Sbostic if (special) { 52847116Sbostic set = varisset(*var); 52947116Sbostic val = NULL; 53047116Sbostic } else { 53147116Sbostic val = lookupvar(var); 53253299Smarc if (val == NULL || (varflags & VSNUL) && val[0] == '\0') { 53347116Sbostic val = NULL; 53447116Sbostic set = 0; 53547116Sbostic } else 53647116Sbostic set = 1; 53747116Sbostic } 538*69090Sbostic varlen = 0; 53947116Sbostic startloc = expdest - stackblock(); 54047116Sbostic if (set && subtype != VSPLUS) { 54147116Sbostic /* insert the value of the variable */ 54247116Sbostic if (special) { 543*69090Sbostic char *exp, *oexpdest = expdest; 54453299Smarc varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL); 545*69090Sbostic if (subtype == VSLENGTH) { 546*69090Sbostic for (exp = oexpdest;exp != expdest; exp++) 547*69090Sbostic varlen++; 548*69090Sbostic expdest = oexpdest; 549*69090Sbostic } 55047116Sbostic } else { 551*69090Sbostic char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 552*69090Sbostic : BASESYNTAX; 55347116Sbostic 554*69090Sbostic if (subtype == VSLENGTH) { 555*69090Sbostic for (;*val; val++) 556*69090Sbostic varlen++; 55747116Sbostic } 558*69090Sbostic else { 559*69090Sbostic while (*val) { 560*69090Sbostic if (quotes && syntax[*val] == CCTL) 561*69090Sbostic STPUTC(CTLESC, expdest); 562*69090Sbostic STPUTC(*val++, expdest); 563*69090Sbostic } 564*69090Sbostic 565*69090Sbostic } 56647116Sbostic } 56747116Sbostic } 568*69090Sbostic 56947116Sbostic if (subtype == VSPLUS) 57047116Sbostic set = ! set; 571*69090Sbostic 572*69090Sbostic easy = ((varflags & VSQUOTE) == 0 || 573*69090Sbostic (*var == '@' && shellparam.nparam != 1)); 574*69090Sbostic 575*69090Sbostic 576*69090Sbostic switch (subtype) { 577*69090Sbostic case VSLENGTH: 578*69090Sbostic expdest = cvtnum(varlen, expdest); 579*69090Sbostic goto record; 580*69090Sbostic 581*69090Sbostic case VSNORMAL: 582*69090Sbostic if (!easy) 583*69090Sbostic break; 584*69090Sbostic record: 585*69090Sbostic recordregion(startloc, expdest - stackblock(), 586*69090Sbostic varflags & VSQUOTE); 587*69090Sbostic break; 588*69090Sbostic 589*69090Sbostic case VSPLUS: 590*69090Sbostic case VSMINUS: 591*69090Sbostic if (!set) { 59253299Smarc argstr(p, flag); 593*69090Sbostic break; 594*69090Sbostic } 595*69090Sbostic if (easy) 596*69090Sbostic goto record; 597*69090Sbostic break; 598*69090Sbostic 599*69090Sbostic case VSTRIMLEFT: 600*69090Sbostic case VSTRIMLEFTMAX: 601*69090Sbostic case VSTRIMRIGHT: 602*69090Sbostic case VSTRIMRIGHTMAX: 603*69090Sbostic if (!set) 604*69090Sbostic break; 605*69090Sbostic /* 606*69090Sbostic * Terminate the string and start recording the pattern 607*69090Sbostic * right after it 608*69090Sbostic */ 609*69090Sbostic STPUTC('\0', expdest); 610*69090Sbostic pat = expdest; 611*69090Sbostic if (subevalvar(p, pat, subtype, startloc, varflags)) 612*69090Sbostic goto record; 613*69090Sbostic break; 614*69090Sbostic 615*69090Sbostic case VSASSIGN: 616*69090Sbostic case VSQUESTION: 617*69090Sbostic if (!set) { 618*69090Sbostic if (subevalvar(p, var, subtype, startloc, varflags)) 61947116Sbostic goto again; 620*69090Sbostic break; 62147116Sbostic } 622*69090Sbostic if (easy) 623*69090Sbostic goto record; 624*69090Sbostic break; 625*69090Sbostic 626*69090Sbostic default: 627*69090Sbostic abort(); 62847116Sbostic } 629*69090Sbostic 63047116Sbostic if (subtype != VSNORMAL) { /* skip to end of alternative */ 63147116Sbostic int nesting = 1; 63247116Sbostic for (;;) { 63347116Sbostic if ((c = *p++) == CTLESC) 63447116Sbostic p++; 63547116Sbostic else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 63647116Sbostic if (set) 63747116Sbostic argbackq = argbackq->next; 63847116Sbostic } else if (c == CTLVAR) { 63947116Sbostic if ((*p++ & VSTYPE) != VSNORMAL) 64047116Sbostic nesting++; 64147116Sbostic } else if (c == CTLENDVAR) { 64247116Sbostic if (--nesting == 0) 64347116Sbostic break; 64447116Sbostic } 64547116Sbostic } 64647116Sbostic } 64747116Sbostic return p; 64847116Sbostic } 64947116Sbostic 65047116Sbostic 65147116Sbostic 65247116Sbostic /* 65347116Sbostic * Test whether a specialized variable is set. 65447116Sbostic */ 65547116Sbostic 65647116Sbostic STATIC int 65747116Sbostic varisset(name) 65847116Sbostic char name; 65947116Sbostic { 66047116Sbostic char **ap; 66147116Sbostic 66247116Sbostic if (name == '!') { 66347116Sbostic if (backgndpid == -1) 66447116Sbostic return 0; 66547116Sbostic } else if (name == '@' || name == '*') { 66647116Sbostic if (*shellparam.p == NULL) 66747116Sbostic return 0; 66847116Sbostic } else if ((unsigned)(name -= '1') <= '9' - '1') { 66947116Sbostic ap = shellparam.p; 67047116Sbostic do { 67147116Sbostic if (*ap++ == NULL) 67247116Sbostic return 0; 67347116Sbostic } while (--name >= 0); 67447116Sbostic } 67547116Sbostic return 1; 67647116Sbostic } 67747116Sbostic 67847116Sbostic 67947116Sbostic 68047116Sbostic /* 68147116Sbostic * Add the value of a specialized variable to the stack string. 68247116Sbostic */ 68347116Sbostic 68447116Sbostic STATIC void 68547116Sbostic varvalue(name, quoted, allow_split) 68647116Sbostic char name; 68747116Sbostic { 68847116Sbostic int num; 68947116Sbostic char *p; 69047116Sbostic int i; 69147116Sbostic extern int exitstatus; 69247116Sbostic char sep; 69347116Sbostic char **ap; 69447116Sbostic char const *syntax; 69547116Sbostic 69654315Smarc #define STRTODEST(p) \ 69754315Smarc do {\ 69854315Smarc if (allow_split) { \ 69954315Smarc syntax = quoted? DQSYNTAX : BASESYNTAX; \ 70054315Smarc while (*p) { \ 70154315Smarc if (syntax[*p] == CCTL) \ 70254315Smarc STPUTC(CTLESC, expdest); \ 70354315Smarc STPUTC(*p++, expdest); \ 70454315Smarc } \ 70554315Smarc } else \ 70654315Smarc while (*p) \ 70754315Smarc STPUTC(*p++, expdest); \ 70854315Smarc } while (0) 70954315Smarc 71054315Smarc 71147116Sbostic switch (name) { 71247116Sbostic case '$': 71347116Sbostic num = rootpid; 71447116Sbostic goto numvar; 71547116Sbostic case '?': 71647116Sbostic num = exitstatus; 71747116Sbostic goto numvar; 71847116Sbostic case '#': 71947116Sbostic num = shellparam.nparam; 72047116Sbostic goto numvar; 72147116Sbostic case '!': 72247116Sbostic num = backgndpid; 72347116Sbostic numvar: 724*69090Sbostic expdest = cvtnum(num, expdest); 72547116Sbostic break; 72647116Sbostic case '-': 72755243Smarc for (i = 0 ; i < NOPTS ; i++) { 72855243Smarc if (optlist[i].val) 72955243Smarc STPUTC(optlist[i].letter, expdest); 73047116Sbostic } 73147116Sbostic break; 73247116Sbostic case '@': 73347116Sbostic if (allow_split) { 73447116Sbostic sep = '\0'; 73547116Sbostic goto allargs; 73647116Sbostic } 73747116Sbostic /* fall through */ 73847116Sbostic case '*': 73947116Sbostic sep = ' '; 74047116Sbostic allargs: 74147116Sbostic for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 74254315Smarc STRTODEST(p); 74347116Sbostic if (*ap) 74447116Sbostic STPUTC(sep, expdest); 74547116Sbostic } 74647116Sbostic break; 74747116Sbostic case '0': 74847116Sbostic p = arg0; 74954315Smarc STRTODEST(p); 75047116Sbostic break; 75147116Sbostic default: 75247116Sbostic if ((unsigned)(name -= '1') <= '9' - '1') { 75347116Sbostic p = shellparam.p[name]; 75454315Smarc STRTODEST(p); 75547116Sbostic } 75647116Sbostic break; 75747116Sbostic } 75847116Sbostic } 75947116Sbostic 76047116Sbostic 76147116Sbostic 76247116Sbostic /* 76347116Sbostic * Record the the fact that we have to scan this region of the 76447116Sbostic * string for IFS characters. 76547116Sbostic */ 76647116Sbostic 76747116Sbostic STATIC void 76847116Sbostic recordregion(start, end, nulonly) { 76947116Sbostic register struct ifsregion *ifsp; 77047116Sbostic 77147116Sbostic if (ifslastp == NULL) { 77247116Sbostic ifsp = &ifsfirst; 77347116Sbostic } else { 77447116Sbostic ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 77547116Sbostic ifslastp->next = ifsp; 77647116Sbostic } 77747116Sbostic ifslastp = ifsp; 77847116Sbostic ifslastp->next = NULL; 77947116Sbostic ifslastp->begoff = start; 78047116Sbostic ifslastp->endoff = end; 78147116Sbostic ifslastp->nulonly = nulonly; 78247116Sbostic } 78347116Sbostic 78447116Sbostic 78547116Sbostic 78647116Sbostic /* 78747116Sbostic * Break the argument string into pieces based upon IFS and add the 78847116Sbostic * strings to the argument list. The regions of the string to be 78947116Sbostic * searched for IFS characters have been stored by recordregion. 79047116Sbostic */ 79147116Sbostic 79247116Sbostic STATIC void 79347116Sbostic ifsbreakup(string, arglist) 79447116Sbostic char *string; 79547116Sbostic struct arglist *arglist; 79647116Sbostic { 79747116Sbostic struct ifsregion *ifsp; 79847116Sbostic struct strlist *sp; 79947116Sbostic char *start; 80047116Sbostic register char *p; 80147116Sbostic char *q; 80247116Sbostic char *ifs; 80347116Sbostic 80447116Sbostic start = string; 80547116Sbostic if (ifslastp != NULL) { 80647116Sbostic ifsp = &ifsfirst; 80747116Sbostic do { 80847116Sbostic p = string + ifsp->begoff; 80947116Sbostic ifs = ifsp->nulonly? nullstr : ifsval(); 81047116Sbostic while (p < string + ifsp->endoff) { 81147116Sbostic q = p; 81247116Sbostic if (*p == CTLESC) 81347116Sbostic p++; 81447116Sbostic if (strchr(ifs, *p++)) { 81547116Sbostic if (q > start || *ifs != ' ') { 81647116Sbostic *q = '\0'; 81747116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 81847116Sbostic sp->text = start; 81947116Sbostic *arglist->lastp = sp; 82047116Sbostic arglist->lastp = &sp->next; 82147116Sbostic } 82247116Sbostic if (*ifs == ' ') { 82347116Sbostic for (;;) { 82447116Sbostic if (p >= string + ifsp->endoff) 82547116Sbostic break; 82647116Sbostic q = p; 82747116Sbostic if (*p == CTLESC) 82847116Sbostic p++; 82947116Sbostic if (strchr(ifs, *p++) == NULL) { 83047116Sbostic p = q; 83147116Sbostic break; 83247116Sbostic } 83347116Sbostic } 83447116Sbostic } 83547116Sbostic start = p; 83647116Sbostic } 83747116Sbostic } 83847116Sbostic } while ((ifsp = ifsp->next) != NULL); 83947116Sbostic if (*start || (*ifs != ' ' && start > string)) { 84047116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 84147116Sbostic sp->text = start; 84247116Sbostic *arglist->lastp = sp; 84347116Sbostic arglist->lastp = &sp->next; 84447116Sbostic } 84547116Sbostic } else { 84647116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 84747116Sbostic sp->text = start; 84847116Sbostic *arglist->lastp = sp; 84947116Sbostic arglist->lastp = &sp->next; 85047116Sbostic } 85147116Sbostic } 85247116Sbostic 85347116Sbostic 85447116Sbostic 85547116Sbostic /* 85647116Sbostic * Expand shell metacharacters. At this point, the only control characters 85747116Sbostic * should be escapes. The results are stored in the list exparg. 85847116Sbostic */ 85947116Sbostic 86047116Sbostic char *expdir; 86147116Sbostic 86247116Sbostic 86347116Sbostic STATIC void 86453299Smarc expandmeta(str, flag) 86547116Sbostic struct strlist *str; 86647116Sbostic { 86747116Sbostic char *p; 86847116Sbostic struct strlist **savelastp; 86947116Sbostic struct strlist *sp; 87047116Sbostic char c; 87153299Smarc /* TODO - EXP_REDIR */ 87247116Sbostic 87347116Sbostic while (str) { 87447116Sbostic if (fflag) 87547116Sbostic goto nometa; 87647116Sbostic p = str->text; 87747116Sbostic for (;;) { /* fast check for meta chars */ 87847116Sbostic if ((c = *p++) == '\0') 87947116Sbostic goto nometa; 88047116Sbostic if (c == '*' || c == '?' || c == '[' || c == '!') 88147116Sbostic break; 88247116Sbostic } 88347116Sbostic savelastp = exparg.lastp; 88447116Sbostic INTOFF; 88555272Smarc if (expdir == NULL) { 88655272Smarc int i = strlen(str->text); 88755272Smarc expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 88855272Smarc } 88955272Smarc 89047116Sbostic expmeta(expdir, str->text); 89147116Sbostic ckfree(expdir); 89247116Sbostic expdir = NULL; 89347116Sbostic INTON; 89447116Sbostic if (exparg.lastp == savelastp) { 89553299Smarc /* 89653299Smarc * no matches 89753299Smarc */ 89847116Sbostic nometa: 89955243Smarc *exparg.lastp = str; 90055243Smarc rmescapes(str->text); 90155243Smarc exparg.lastp = &str->next; 90247116Sbostic } else { 90347116Sbostic *exparg.lastp = NULL; 90447116Sbostic *savelastp = sp = expsort(*savelastp); 90547116Sbostic while (sp->next != NULL) 90647116Sbostic sp = sp->next; 90747116Sbostic exparg.lastp = &sp->next; 90847116Sbostic } 90947116Sbostic str = str->next; 91047116Sbostic } 91147116Sbostic } 91247116Sbostic 91347116Sbostic 91447116Sbostic /* 91547116Sbostic * Do metacharacter (i.e. *, ?, [...]) expansion. 91647116Sbostic */ 91747116Sbostic 91847116Sbostic STATIC void 91947116Sbostic expmeta(enddir, name) 92047116Sbostic char *enddir; 92147116Sbostic char *name; 92247116Sbostic { 92347116Sbostic register char *p; 92447116Sbostic char *q; 92547116Sbostic char *start; 92647116Sbostic char *endname; 92747116Sbostic int metaflag; 92847116Sbostic struct stat statb; 92947116Sbostic DIR *dirp; 93047116Sbostic struct dirent *dp; 93147116Sbostic int atend; 93247116Sbostic int matchdot; 93347116Sbostic 93447116Sbostic metaflag = 0; 93547116Sbostic start = name; 93647116Sbostic for (p = name ; ; p++) { 93747116Sbostic if (*p == '*' || *p == '?') 93847116Sbostic metaflag = 1; 93947116Sbostic else if (*p == '[') { 94047116Sbostic q = p + 1; 94147116Sbostic if (*q == '!') 94247116Sbostic q++; 94347116Sbostic for (;;) { 94447116Sbostic if (*q == CTLESC) 94547116Sbostic q++; 94647116Sbostic if (*q == '/' || *q == '\0') 94747116Sbostic break; 94847116Sbostic if (*++q == ']') { 94947116Sbostic metaflag = 1; 95047116Sbostic break; 95147116Sbostic } 95247116Sbostic } 95347116Sbostic } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 95447116Sbostic metaflag = 1; 95547116Sbostic } else if (*p == '\0') 95647116Sbostic break; 95747116Sbostic else if (*p == CTLESC) 95847116Sbostic p++; 95947116Sbostic if (*p == '/') { 96047116Sbostic if (metaflag) 96147116Sbostic break; 96247116Sbostic start = p + 1; 96347116Sbostic } 96447116Sbostic } 96547116Sbostic if (metaflag == 0) { /* we've reached the end of the file name */ 96647116Sbostic if (enddir != expdir) 96747116Sbostic metaflag++; 96847116Sbostic for (p = name ; ; p++) { 96947116Sbostic if (*p == CTLESC) 97047116Sbostic p++; 97147116Sbostic *enddir++ = *p; 97247116Sbostic if (*p == '\0') 97347116Sbostic break; 97447116Sbostic } 97547116Sbostic if (metaflag == 0 || stat(expdir, &statb) >= 0) 97647116Sbostic addfname(expdir); 97747116Sbostic return; 97847116Sbostic } 97947116Sbostic endname = p; 98047116Sbostic if (start != name) { 98147116Sbostic p = name; 98247116Sbostic while (p < start) { 98347116Sbostic if (*p == CTLESC) 98447116Sbostic p++; 98547116Sbostic *enddir++ = *p++; 98647116Sbostic } 98747116Sbostic } 98847116Sbostic if (enddir == expdir) { 98947116Sbostic p = "."; 99047116Sbostic } else if (enddir == expdir + 1 && *expdir == '/') { 99147116Sbostic p = "/"; 99247116Sbostic } else { 99347116Sbostic p = expdir; 99447116Sbostic enddir[-1] = '\0'; 99547116Sbostic } 99647116Sbostic if ((dirp = opendir(p)) == NULL) 99747116Sbostic return; 99847116Sbostic if (enddir != expdir) 99947116Sbostic enddir[-1] = '/'; 100047116Sbostic if (*endname == 0) { 100147116Sbostic atend = 1; 100247116Sbostic } else { 100347116Sbostic atend = 0; 100447116Sbostic *endname++ = '\0'; 100547116Sbostic } 100647116Sbostic matchdot = 0; 100747116Sbostic if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') 100847116Sbostic matchdot++; 100947116Sbostic while (! int_pending() && (dp = readdir(dirp)) != NULL) { 101047116Sbostic if (dp->d_name[0] == '.' && ! matchdot) 101147116Sbostic continue; 101247116Sbostic if (patmatch(start, dp->d_name)) { 101347116Sbostic if (atend) { 101447116Sbostic scopy(dp->d_name, enddir); 101547116Sbostic addfname(expdir); 101647116Sbostic } else { 101747116Sbostic char *q; 101847116Sbostic for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); 101947116Sbostic p[-1] = '/'; 102047116Sbostic expmeta(p, endname); 102147116Sbostic } 102247116Sbostic } 102347116Sbostic } 102447116Sbostic closedir(dirp); 102547116Sbostic if (! atend) 102647116Sbostic endname[-1] = '/'; 102747116Sbostic } 102847116Sbostic 102947116Sbostic 103047116Sbostic /* 103147116Sbostic * Add a file name to the list. 103247116Sbostic */ 103347116Sbostic 103447116Sbostic STATIC void 103547116Sbostic addfname(name) 103647116Sbostic char *name; 103747116Sbostic { 103847116Sbostic char *p; 103947116Sbostic struct strlist *sp; 104047116Sbostic 104147116Sbostic p = stalloc(strlen(name) + 1); 104247116Sbostic scopy(name, p); 104347116Sbostic sp = (struct strlist *)stalloc(sizeof *sp); 104447116Sbostic sp->text = p; 104547116Sbostic *exparg.lastp = sp; 104647116Sbostic exparg.lastp = &sp->next; 104747116Sbostic } 104847116Sbostic 104947116Sbostic 105047116Sbostic /* 105147116Sbostic * Sort the results of file name expansion. It calculates the number of 105247116Sbostic * strings to sort and then calls msort (short for merge sort) to do the 105347116Sbostic * work. 105447116Sbostic */ 105547116Sbostic 105647116Sbostic STATIC struct strlist * 105747116Sbostic expsort(str) 105847116Sbostic struct strlist *str; 105947116Sbostic { 106047116Sbostic int len; 106147116Sbostic struct strlist *sp; 106247116Sbostic 106347116Sbostic len = 0; 106447116Sbostic for (sp = str ; sp ; sp = sp->next) 106547116Sbostic len++; 106647116Sbostic return msort(str, len); 106747116Sbostic } 106847116Sbostic 106947116Sbostic 107047116Sbostic STATIC struct strlist * 107147116Sbostic msort(list, len) 107247116Sbostic struct strlist *list; 107347116Sbostic { 107447116Sbostic struct strlist *p, *q; 107547116Sbostic struct strlist **lpp; 107647116Sbostic int half; 107747116Sbostic int n; 107847116Sbostic 107947116Sbostic if (len <= 1) 108047116Sbostic return list; 108147116Sbostic half = len >> 1; 108247116Sbostic p = list; 108347116Sbostic for (n = half ; --n >= 0 ; ) { 108447116Sbostic q = p; 108547116Sbostic p = p->next; 108647116Sbostic } 108747116Sbostic q->next = NULL; /* terminate first half of list */ 108847116Sbostic q = msort(list, half); /* sort first half of list */ 108947116Sbostic p = msort(p, len - half); /* sort second half */ 109047116Sbostic lpp = &list; 109147116Sbostic for (;;) { 109247116Sbostic if (strcmp(p->text, q->text) < 0) { 109347116Sbostic *lpp = p; 109447116Sbostic lpp = &p->next; 109547116Sbostic if ((p = *lpp) == NULL) { 109647116Sbostic *lpp = q; 109747116Sbostic break; 109847116Sbostic } 109947116Sbostic } else { 110047116Sbostic *lpp = q; 110147116Sbostic lpp = &q->next; 110247116Sbostic if ((q = *lpp) == NULL) { 110347116Sbostic *lpp = p; 110447116Sbostic break; 110547116Sbostic } 110647116Sbostic } 110747116Sbostic } 110847116Sbostic return list; 110947116Sbostic } 111047116Sbostic 111147116Sbostic 111247116Sbostic 111347116Sbostic /* 111447116Sbostic * Returns true if the pattern matches the string. 111547116Sbostic */ 111647116Sbostic 111747116Sbostic int 111847116Sbostic patmatch(pattern, string) 111947116Sbostic char *pattern; 112047116Sbostic char *string; 112147116Sbostic { 112255787Smarc #ifdef notdef 112347116Sbostic if (pattern[0] == '!' && pattern[1] == '!') 112447116Sbostic return 1 - pmatch(pattern + 2, string); 112547116Sbostic else 112655787Smarc #endif 112747116Sbostic return pmatch(pattern, string); 112847116Sbostic } 112947116Sbostic 113047116Sbostic 113147116Sbostic STATIC int 113247116Sbostic pmatch(pattern, string) 113347116Sbostic char *pattern; 113447116Sbostic char *string; 113547116Sbostic { 113647116Sbostic register char *p, *q; 113747116Sbostic register char c; 113847116Sbostic 113947116Sbostic p = pattern; 114047116Sbostic q = string; 114147116Sbostic for (;;) { 114247116Sbostic switch (c = *p++) { 114347116Sbostic case '\0': 114447116Sbostic goto breakloop; 114547116Sbostic case CTLESC: 114647116Sbostic if (*q++ != *p++) 114747116Sbostic return 0; 114847116Sbostic break; 114947116Sbostic case '?': 115047116Sbostic if (*q++ == '\0') 115147116Sbostic return 0; 115247116Sbostic break; 115347116Sbostic case '*': 115447116Sbostic c = *p; 115547116Sbostic if (c != CTLESC && c != '?' && c != '*' && c != '[') { 115647116Sbostic while (*q != c) { 115747116Sbostic if (*q == '\0') 115847116Sbostic return 0; 115947116Sbostic q++; 116047116Sbostic } 116147116Sbostic } 116247116Sbostic do { 116347116Sbostic if (pmatch(p, q)) 116447116Sbostic return 1; 116547116Sbostic } while (*q++ != '\0'); 116647116Sbostic return 0; 116747116Sbostic case '[': { 116847116Sbostic char *endp; 116947116Sbostic int invert, found; 117047116Sbostic char chr; 117147116Sbostic 117247116Sbostic endp = p; 117347116Sbostic if (*endp == '!') 117447116Sbostic endp++; 117547116Sbostic for (;;) { 117647116Sbostic if (*endp == '\0') 117747116Sbostic goto dft; /* no matching ] */ 117847116Sbostic if (*endp == CTLESC) 117947116Sbostic endp++; 118047116Sbostic if (*++endp == ']') 118147116Sbostic break; 118247116Sbostic } 118347116Sbostic invert = 0; 118447116Sbostic if (*p == '!') { 118547116Sbostic invert++; 118647116Sbostic p++; 118747116Sbostic } 118847116Sbostic found = 0; 118947116Sbostic chr = *q++; 119047116Sbostic c = *p++; 119147116Sbostic do { 119247116Sbostic if (c == CTLESC) 119347116Sbostic c = *p++; 119447116Sbostic if (*p == '-' && p[1] != ']') { 119547116Sbostic p++; 119647116Sbostic if (*p == CTLESC) 119747116Sbostic p++; 119847116Sbostic if (chr >= c && chr <= *p) 119947116Sbostic found = 1; 120047116Sbostic p++; 120147116Sbostic } else { 120247116Sbostic if (chr == c) 120347116Sbostic found = 1; 120447116Sbostic } 120547116Sbostic } while ((c = *p++) != ']'); 120647116Sbostic if (found == invert) 120747116Sbostic return 0; 120847116Sbostic break; 120947116Sbostic } 121055787Smarc dft: default: 121147116Sbostic if (*q++ != c) 121247116Sbostic return 0; 121347116Sbostic break; 121447116Sbostic } 121547116Sbostic } 121647116Sbostic breakloop: 121747116Sbostic if (*q != '\0') 121847116Sbostic return 0; 121947116Sbostic return 1; 122047116Sbostic } 122147116Sbostic 122247116Sbostic 122347116Sbostic 122447116Sbostic /* 122547116Sbostic * Remove any CTLESC characters from a string. 122647116Sbostic */ 122747116Sbostic 122847116Sbostic void 122947116Sbostic rmescapes(str) 123047116Sbostic char *str; 123147116Sbostic { 123247116Sbostic register char *p, *q; 123347116Sbostic 123447116Sbostic p = str; 123547116Sbostic while (*p != CTLESC) { 123647116Sbostic if (*p++ == '\0') 123747116Sbostic return; 123847116Sbostic } 123947116Sbostic q = p; 124047116Sbostic while (*p) { 124147116Sbostic if (*p == CTLESC) 124247116Sbostic p++; 124347116Sbostic *q++ = *p++; 124447116Sbostic } 124547116Sbostic *q = '\0'; 124647116Sbostic } 124747116Sbostic 124847116Sbostic 124947116Sbostic 125047116Sbostic /* 125147116Sbostic * See if a pattern matches in a case statement. 125247116Sbostic */ 125347116Sbostic 125447116Sbostic int 125547116Sbostic casematch(pattern, val) 125647116Sbostic union node *pattern; 125747116Sbostic char *val; 125847116Sbostic { 125947116Sbostic struct stackmark smark; 126047116Sbostic int result; 126147116Sbostic char *p; 126247116Sbostic 126347116Sbostic setstackmark(&smark); 126447116Sbostic argbackq = pattern->narg.backquote; 126547116Sbostic STARTSTACKSTR(expdest); 126647116Sbostic ifslastp = NULL; 126755787Smarc argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 126847116Sbostic STPUTC('\0', expdest); 126947116Sbostic p = grabstackstr(expdest); 127047116Sbostic result = patmatch(p, val); 127147116Sbostic popstackmark(&smark); 127247116Sbostic return result; 127347116Sbostic } 1274*69090Sbostic 1275*69090Sbostic /* 1276*69090Sbostic * Our own itoa(). 1277*69090Sbostic */ 1278*69090Sbostic 1279*69090Sbostic STATIC char * 1280*69090Sbostic cvtnum(num, buf) 1281*69090Sbostic int num; 1282*69090Sbostic char *buf; 1283*69090Sbostic { 1284*69090Sbostic char temp[32]; 1285*69090Sbostic int neg = num < 0; 1286*69090Sbostic char *p = temp + 31; 1287*69090Sbostic 1288*69090Sbostic temp[31] = '\0'; 1289*69090Sbostic 1290*69090Sbostic do { 1291*69090Sbostic *--p = num % 10 + '0'; 1292*69090Sbostic } while ((num /= 10) != 0); 1293*69090Sbostic 1294*69090Sbostic if (neg) 1295*69090Sbostic *--p = '-'; 1296*69090Sbostic 1297*69090Sbostic while (*p) 1298*69090Sbostic STPUTC(*p++, buf); 1299*69090Sbostic return buf; 1300*69090Sbostic } 1301