expand.c revision 18202
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1991, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Kenneth Almquist. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 3. All advertising materials mentioning features or use of this software 171556Srgrimes * must display the following acknowledgement: 181556Srgrimes * This product includes software developed by the University of 191556Srgrimes * California, Berkeley and its contributors. 201556Srgrimes * 4. Neither the name of the University nor the names of its contributors 211556Srgrimes * may be used to endorse or promote products derived from this software 221556Srgrimes * without specific prior written permission. 231556Srgrimes * 241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341556Srgrimes * SUCH DAMAGE. 353044Sdg * 3618202Speter * $Id$ 371556Srgrimes */ 381556Srgrimes 391556Srgrimes#ifndef lint 4017987Speterstatic char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; 411556Srgrimes#endif /* not lint */ 421556Srgrimes 4317987Speter#include <sys/types.h> 4417987Speter#include <sys/time.h> 4517987Speter#include <sys/stat.h> 4617987Speter#include <errno.h> 4717987Speter#include <dirent.h> 4817987Speter#include <unistd.h> 4917987Speter#include <pwd.h> 5017987Speter#include <stdlib.h> 5117987Speter#include <locale.h> 5217987Speter 531556Srgrimes/* 541556Srgrimes * Routines to expand arguments to commands. We have to deal with 551556Srgrimes * backquotes, shell variables, and file metacharacters. 561556Srgrimes */ 571556Srgrimes 581556Srgrimes#include "shell.h" 591556Srgrimes#include "main.h" 601556Srgrimes#include "nodes.h" 611556Srgrimes#include "eval.h" 621556Srgrimes#include "expand.h" 631556Srgrimes#include "syntax.h" 641556Srgrimes#include "parser.h" 651556Srgrimes#include "jobs.h" 661556Srgrimes#include "options.h" 671556Srgrimes#include "var.h" 681556Srgrimes#include "input.h" 691556Srgrimes#include "output.h" 701556Srgrimes#include "memalloc.h" 711556Srgrimes#include "error.h" 721556Srgrimes#include "mystring.h" 7317987Speter#include "arith.h" 7417987Speter#include "show.h" 751556Srgrimes 761556Srgrimes/* 771556Srgrimes * Structure specifying which parts of the string should be searched 781556Srgrimes * for IFS characters. 791556Srgrimes */ 801556Srgrimes 811556Srgrimesstruct ifsregion { 821556Srgrimes struct ifsregion *next; /* next region in list */ 831556Srgrimes int begoff; /* offset of start of region */ 841556Srgrimes int endoff; /* offset of end of region */ 851556Srgrimes int nulonly; /* search for nul bytes only */ 861556Srgrimes}; 871556Srgrimes 881556Srgrimes 891556Srgrimeschar *expdest; /* output of current string */ 901556Srgrimesstruct nodelist *argbackq; /* list of back quote expressions */ 911556Srgrimesstruct ifsregion ifsfirst; /* first struct in list of ifs regions */ 921556Srgrimesstruct ifsregion *ifslastp; /* last struct in list */ 931556Srgrimesstruct arglist exparg; /* holds expanded arg list */ 941556Srgrimes 9517987SpeterSTATIC void argstr __P((char *, int)); 9617987SpeterSTATIC char *exptilde __P((char *, int)); 9717987SpeterSTATIC void expbackq __P((union node *, int, int)); 9817987SpeterSTATIC int subevalvar __P((char *, char *, int, int, int)); 9917987SpeterSTATIC char *evalvar __P((char *, int)); 10018202SpeterSTATIC int varisset __P((char *)); 10118202SpeterSTATIC void varvalue __P((char *, int, int)); 10217987SpeterSTATIC void recordregion __P((int, int, int)); 10317987SpeterSTATIC void ifsbreakup __P((char *, struct arglist *)); 10417987SpeterSTATIC void expandmeta __P((struct strlist *, int)); 10517987SpeterSTATIC void expmeta __P((char *, char *)); 10617987SpeterSTATIC void addfname __P((char *)); 10717987SpeterSTATIC struct strlist *expsort __P((struct strlist *)); 10817987SpeterSTATIC struct strlist *msort __P((struct strlist *, int)); 10917987SpeterSTATIC int pmatch __P((char *, char *)); 11017987SpeterSTATIC char *cvtnum __P((int, char *)); 1111556Srgrimes 1121556Srgrimes/* 1131556Srgrimes * Expand shell variables and backquotes inside a here document. 1141556Srgrimes */ 1151556Srgrimes 1161556Srgrimesvoid 1171556Srgrimesexpandhere(arg, fd) 1181556Srgrimes union node *arg; /* the document */ 1191556Srgrimes int fd; /* where to write the expanded version */ 1201556Srgrimes { 1211556Srgrimes herefd = fd; 1221556Srgrimes expandarg(arg, (struct arglist *)NULL, 0); 1231556Srgrimes xwrite(fd, stackblock(), expdest - stackblock()); 1241556Srgrimes} 1251556Srgrimes 1261556Srgrimes 1271556Srgrimes/* 1281556Srgrimes * Perform variable substitution and command substitution on an argument, 1291556Srgrimes * placing the resulting list of arguments in arglist. If EXP_FULL is true, 1301556Srgrimes * perform splitting and file name expansion. When arglist is NULL, perform 1311556Srgrimes * here document expansion. 1321556Srgrimes */ 1331556Srgrimes 1341556Srgrimesvoid 1351556Srgrimesexpandarg(arg, arglist, flag) 1361556Srgrimes union node *arg; 1371556Srgrimes struct arglist *arglist; 13817987Speter int flag; 13917987Speter{ 1401556Srgrimes struct strlist *sp; 1411556Srgrimes char *p; 1421556Srgrimes 1431556Srgrimes argbackq = arg->narg.backquote; 1441556Srgrimes STARTSTACKSTR(expdest); 1451556Srgrimes ifsfirst.next = NULL; 1461556Srgrimes ifslastp = NULL; 1471556Srgrimes argstr(arg->narg.text, flag); 1481556Srgrimes if (arglist == NULL) { 1491556Srgrimes return; /* here document expanded */ 1501556Srgrimes } 1511556Srgrimes STPUTC('\0', expdest); 1521556Srgrimes p = grabstackstr(expdest); 1531556Srgrimes exparg.lastp = &exparg.list; 1541556Srgrimes /* 1551556Srgrimes * TODO - EXP_REDIR 1561556Srgrimes */ 1571556Srgrimes if (flag & EXP_FULL) { 1581556Srgrimes ifsbreakup(p, &exparg); 1591556Srgrimes *exparg.lastp = NULL; 1601556Srgrimes exparg.lastp = &exparg.list; 1611556Srgrimes expandmeta(exparg.list, flag); 1621556Srgrimes } else { 1631556Srgrimes if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 1641556Srgrimes rmescapes(p); 1651556Srgrimes sp = (struct strlist *)stalloc(sizeof (struct strlist)); 1661556Srgrimes sp->text = p; 1671556Srgrimes *exparg.lastp = sp; 1681556Srgrimes exparg.lastp = &sp->next; 1691556Srgrimes } 1701556Srgrimes while (ifsfirst.next != NULL) { 1711556Srgrimes struct ifsregion *ifsp; 1721556Srgrimes INTOFF; 1731556Srgrimes ifsp = ifsfirst.next->next; 1741556Srgrimes ckfree(ifsfirst.next); 1751556Srgrimes ifsfirst.next = ifsp; 1761556Srgrimes INTON; 1771556Srgrimes } 1781556Srgrimes *exparg.lastp = NULL; 1791556Srgrimes if (exparg.list) { 1801556Srgrimes *arglist->lastp = exparg.list; 1811556Srgrimes arglist->lastp = exparg.lastp; 1821556Srgrimes } 1831556Srgrimes} 1841556Srgrimes 1851556Srgrimes 1861556Srgrimes 1871556Srgrimes/* 1881556Srgrimes * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 1891556Srgrimes * characters to allow for further processing. Otherwise treat 1901556Srgrimes * $@ like $* since no splitting will be performed. 1911556Srgrimes */ 1921556Srgrimes 1931556SrgrimesSTATIC void 1941556Srgrimesargstr(p, flag) 1951556Srgrimes register char *p; 19617987Speter int flag; 19717987Speter{ 1981556Srgrimes register char c; 1991556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ 2001556Srgrimes int firsteq = 1; 2011556Srgrimes 2021556Srgrimes if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 2031556Srgrimes p = exptilde(p, flag); 2041556Srgrimes for (;;) { 2051556Srgrimes switch (c = *p++) { 2061556Srgrimes case '\0': 2071556Srgrimes case CTLENDVAR: /* ??? */ 2081556Srgrimes goto breakloop; 2091556Srgrimes case CTLESC: 2101556Srgrimes if (quotes) 2111556Srgrimes STPUTC(c, expdest); 2121556Srgrimes c = *p++; 2131556Srgrimes STPUTC(c, expdest); 2141556Srgrimes break; 2151556Srgrimes case CTLVAR: 2161556Srgrimes p = evalvar(p, flag); 2171556Srgrimes break; 2181556Srgrimes case CTLBACKQ: 2191556Srgrimes case CTLBACKQ|CTLQUOTE: 2201556Srgrimes expbackq(argbackq->n, c & CTLQUOTE, flag); 2211556Srgrimes argbackq = argbackq->next; 2221556Srgrimes break; 2231556Srgrimes case CTLENDARI: 2241556Srgrimes expari(flag); 2251556Srgrimes break; 2261556Srgrimes case ':': 2271556Srgrimes case '=': 2281556Srgrimes /* 2291556Srgrimes * sort of a hack - expand tildes in variable 2301556Srgrimes * assignments (after the first '=' and after ':'s). 2311556Srgrimes */ 2321556Srgrimes STPUTC(c, expdest); 2331556Srgrimes if (flag & EXP_VARTILDE && *p == '~') { 2341556Srgrimes if (c == '=') { 2351556Srgrimes if (firsteq) 2361556Srgrimes firsteq = 0; 2371556Srgrimes else 2381556Srgrimes break; 2391556Srgrimes } 2401556Srgrimes p = exptilde(p, flag); 2411556Srgrimes } 2421556Srgrimes break; 2431556Srgrimes default: 2441556Srgrimes STPUTC(c, expdest); 2451556Srgrimes } 2461556Srgrimes } 2471556Srgrimesbreakloop:; 2481556Srgrimes} 2491556Srgrimes 2501556SrgrimesSTATIC char * 2511556Srgrimesexptilde(p, flag) 2521556Srgrimes char *p; 25317987Speter int flag; 25417987Speter{ 2551556Srgrimes char c, *startp = p; 2561556Srgrimes struct passwd *pw; 2571556Srgrimes char *home; 2581556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 2591556Srgrimes 26017987Speter while ((c = *p) != '\0') { 2611556Srgrimes switch(c) { 2621556Srgrimes case CTLESC: 2631556Srgrimes return (startp); 2641556Srgrimes case ':': 2651556Srgrimes if (flag & EXP_VARTILDE) 2661556Srgrimes goto done; 2671556Srgrimes break; 2681556Srgrimes case '/': 2691556Srgrimes goto done; 2701556Srgrimes } 2711556Srgrimes p++; 2721556Srgrimes } 2731556Srgrimesdone: 2741556Srgrimes *p = '\0'; 2751556Srgrimes if (*(startp+1) == '\0') { 2761556Srgrimes if ((home = lookupvar("HOME")) == NULL) 2771556Srgrimes goto lose; 2781556Srgrimes } else { 2791556Srgrimes if ((pw = getpwnam(startp+1)) == NULL) 2801556Srgrimes goto lose; 2811556Srgrimes home = pw->pw_dir; 2821556Srgrimes } 2831556Srgrimes if (*home == '\0') 2841556Srgrimes goto lose; 2851556Srgrimes *p = c; 28617987Speter while ((c = *home++) != '\0') { 2871556Srgrimes if (quotes && SQSYNTAX[c] == CCTL) 2881556Srgrimes STPUTC(CTLESC, expdest); 2891556Srgrimes STPUTC(c, expdest); 2901556Srgrimes } 2911556Srgrimes return (p); 2921556Srgrimeslose: 2931556Srgrimes *p = c; 2941556Srgrimes return (startp); 2951556Srgrimes} 2961556Srgrimes 2971556Srgrimes 2981556Srgrimes/* 2991556Srgrimes * Expand arithmetic expression. Backup to start of expression, 3001556Srgrimes * evaluate, place result in (backed up) result, adjust string position. 3011556Srgrimes */ 3021556Srgrimesvoid 3031556Srgrimesexpari(flag) 30417987Speter int flag; 30517987Speter{ 3061556Srgrimes char *p, *start; 3071556Srgrimes int result; 3081556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 3091556Srgrimes 3101556Srgrimes /* 3111556Srgrimes * This routine is slightly over-compilcated for 3121556Srgrimes * efficiency. First we make sure there is 3131556Srgrimes * enough space for the result, which may be bigger 3141556Srgrimes * than the expression if we add exponentation. Next we 3151556Srgrimes * scan backwards looking for the start of arithmetic. If the 3161556Srgrimes * next previous character is a CTLESC character, then we 3171556Srgrimes * have to rescan starting from the beginning since CTLESC 3188855Srgrimes * characters have to be processed left to right. 3191556Srgrimes */ 3201556Srgrimes CHECKSTRSPACE(8, expdest); 3218855Srgrimes USTPUTC('\0', expdest); 3221556Srgrimes start = stackblock(); 3231556Srgrimes p = expdest; 3241556Srgrimes while (*p != CTLARI && p >= start) 3251556Srgrimes --p; 3261556Srgrimes if (*p != CTLARI) 3271556Srgrimes error("missing CTLARI (shouldn't happen)"); 3281556Srgrimes if (p > start && *(p-1) == CTLESC) 3291556Srgrimes for (p = start; *p != CTLARI; p++) 3301556Srgrimes if (*p == CTLESC) 3311556Srgrimes p++; 3321556Srgrimes if (quotes) 3331556Srgrimes rmescapes(p+1); 3341556Srgrimes result = arith(p+1); 3351556Srgrimes fmtstr(p, 10, "%d", result); 3361556Srgrimes while (*p++) 3371556Srgrimes ; 3381556Srgrimes result = expdest - p + 1; 3391556Srgrimes STADJUST(-result, expdest); 3401556Srgrimes} 3411556Srgrimes 3421556Srgrimes 3431556Srgrimes/* 3441556Srgrimes * Expand stuff in backwards quotes. 3451556Srgrimes */ 3461556Srgrimes 3471556SrgrimesSTATIC void 3481556Srgrimesexpbackq(cmd, quoted, flag) 3491556Srgrimes union node *cmd; 35017987Speter int quoted; 35117987Speter int flag; 35217987Speter{ 3531556Srgrimes struct backcmd in; 3541556Srgrimes int i; 3551556Srgrimes char buf[128]; 3561556Srgrimes char *p; 3571556Srgrimes char *dest = expdest; 3581556Srgrimes struct ifsregion saveifs, *savelastp; 3591556Srgrimes struct nodelist *saveargbackq; 3601556Srgrimes char lastc; 3611556Srgrimes int startloc = dest - stackblock(); 3621556Srgrimes char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 3631556Srgrimes int saveherefd; 3641556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 3651556Srgrimes 3661556Srgrimes INTOFF; 3671556Srgrimes saveifs = ifsfirst; 3681556Srgrimes savelastp = ifslastp; 3691556Srgrimes saveargbackq = argbackq; 3708855Srgrimes saveherefd = herefd; 3711556Srgrimes herefd = -1; 3721556Srgrimes p = grabstackstr(dest); 3731556Srgrimes evalbackcmd(cmd, &in); 3741556Srgrimes ungrabstackstr(p, dest); 3751556Srgrimes ifsfirst = saveifs; 3761556Srgrimes ifslastp = savelastp; 3771556Srgrimes argbackq = saveargbackq; 3781556Srgrimes herefd = saveherefd; 3791556Srgrimes 3801556Srgrimes p = in.buf; 3811556Srgrimes lastc = '\0'; 3821556Srgrimes for (;;) { 3831556Srgrimes if (--in.nleft < 0) { 3841556Srgrimes if (in.fd < 0) 3851556Srgrimes break; 3861556Srgrimes while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 3871556Srgrimes TRACE(("expbackq: read returns %d\n", i)); 3881556Srgrimes if (i <= 0) 3891556Srgrimes break; 3901556Srgrimes p = buf; 3911556Srgrimes in.nleft = i - 1; 3921556Srgrimes } 3931556Srgrimes lastc = *p++; 3941556Srgrimes if (lastc != '\0') { 3951556Srgrimes if (quotes && syntax[lastc] == CCTL) 3961556Srgrimes STPUTC(CTLESC, dest); 3971556Srgrimes STPUTC(lastc, dest); 3981556Srgrimes } 3991556Srgrimes } 40017987Speter 40117987Speter /* Eat all trailing newlines */ 40217987Speter for (p--; lastc == '\n'; lastc = *--p) 4031556Srgrimes STUNPUTC(dest); 40417987Speter 4051556Srgrimes if (in.fd >= 0) 4061556Srgrimes close(in.fd); 4071556Srgrimes if (in.buf) 4081556Srgrimes ckfree(in.buf); 4091556Srgrimes if (in.jp) 4101556Srgrimes exitstatus = waitforjob(in.jp); 4111556Srgrimes if (quoted == 0) 4121556Srgrimes recordregion(startloc, dest - stackblock(), 0); 4131556Srgrimes TRACE(("evalbackq: size=%d: \"%.*s\"\n", 4141556Srgrimes (dest - stackblock()) - startloc, 4151556Srgrimes (dest - stackblock()) - startloc, 4161556Srgrimes stackblock() + startloc)); 4171556Srgrimes expdest = dest; 4181556Srgrimes INTON; 4191556Srgrimes} 4201556Srgrimes 4211556Srgrimes 4221556Srgrimes 42317987SpeterSTATIC int 42417987Spetersubevalvar(p, str, subtype, startloc, varflags) 42517987Speter char *p; 42617987Speter char *str; 42717987Speter int subtype; 42817987Speter int startloc; 42917987Speter int varflags; 43017987Speter{ 43117987Speter 43217987Speter char *startp; 43317987Speter char *loc = NULL; 43417987Speter int c = 0; 43517987Speter int saveherefd = herefd; 43617987Speter struct nodelist *saveargbackq = argbackq; 43717987Speter herefd = -1; 43817987Speter argstr(p, 0); 43917987Speter STACKSTRNUL(expdest); 44017987Speter herefd = saveherefd; 44117987Speter argbackq = saveargbackq; 44217987Speter startp = stackblock() + startloc; 44317987Speter 44417987Speter switch (subtype) { 44517987Speter case VSASSIGN: 44617987Speter setvar(str, startp, 0); 44717987Speter STADJUST(startp - expdest, expdest); 44817987Speter varflags &= ~VSNUL; 44917987Speter if (c != 0) 45017987Speter *loc = c; 45117987Speter return 1; 45217987Speter 45317987Speter case VSQUESTION: 45417987Speter if (*p != CTLENDVAR) { 45517987Speter outfmt(&errout, "%s\n", startp); 45617987Speter error((char *)NULL); 45717987Speter } 45817987Speter error("%.*s: parameter %snot set", p - str - 1, 45917987Speter str, (varflags & VSNUL) ? "null or " 46017987Speter : nullstr); 46117987Speter return 0; 46217987Speter 46317987Speter case VSTRIMLEFT: 46417987Speter for (loc = startp; loc < str - 1; loc++) { 46517987Speter c = *loc; 46617987Speter *loc = '\0'; 46717987Speter if (patmatch(str, startp)) { 46817987Speter *loc = c; 46917987Speter goto recordleft; 47017987Speter } 47117987Speter *loc = c; 47217987Speter } 47317987Speter return 0; 47417987Speter 47517987Speter case VSTRIMLEFTMAX: 47617987Speter for (loc = str - 1; loc >= startp; loc--) { 47717987Speter c = *loc; 47817987Speter *loc = '\0'; 47917987Speter if (patmatch(str, startp)) { 48017987Speter *loc = c; 48117987Speter goto recordleft; 48217987Speter } 48317987Speter *loc = c; 48417987Speter } 48517987Speter return 0; 48617987Speter 48717987Speter case VSTRIMRIGHT: 48817987Speter for (loc = str - 1; loc >= startp; loc--) { 48917987Speter if (patmatch(str, loc)) { 49017987Speter expdest = loc; 49117987Speter return 1; 49217987Speter } 49317987Speter } 49417987Speter return 0; 49517987Speter 49617987Speter case VSTRIMRIGHTMAX: 49717987Speter for (loc = startp; loc < str - 1; loc++) { 49817987Speter if (patmatch(str, loc)) { 49917987Speter expdest = loc; 50017987Speter return 1; 50117987Speter } 50217987Speter } 50317987Speter return 0; 50417987Speter 50517987Speter 50617987Speter default: 50717987Speter abort(); 50817987Speter } 50917987Speter 51017987Speterrecordleft: 51117987Speter expdest = (str - 1) - (loc - startp); 51217987Speter while (loc != str - 1) 51317987Speter *startp++ = *loc++; 51417987Speter return 1; 51517987Speter} 51617987Speter 51717987Speter 5181556Srgrimes/* 5191556Srgrimes * Expand a variable, and return a pointer to the next character in the 5201556Srgrimes * input string. 5211556Srgrimes */ 5221556Srgrimes 5231556SrgrimesSTATIC char * 5241556Srgrimesevalvar(p, flag) 5251556Srgrimes char *p; 52617987Speter int flag; 52717987Speter{ 5281556Srgrimes int subtype; 5291556Srgrimes int varflags; 5301556Srgrimes char *var; 5311556Srgrimes char *val; 53217987Speter char *pat; 5331556Srgrimes int c; 5341556Srgrimes int set; 5351556Srgrimes int special; 5361556Srgrimes int startloc; 53717987Speter int varlen; 53817987Speter int easy; 5391556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 5401556Srgrimes 5411556Srgrimes varflags = *p++; 5421556Srgrimes subtype = varflags & VSTYPE; 5431556Srgrimes var = p; 5441556Srgrimes special = 0; 5451556Srgrimes if (! is_name(*p)) 5461556Srgrimes special = 1; 5471556Srgrimes p = strchr(p, '=') + 1; 5481556Srgrimesagain: /* jump here after setting a variable with ${var=text} */ 5491556Srgrimes if (special) { 55018202Speter set = varisset(var); 5511556Srgrimes val = NULL; 5521556Srgrimes } else { 5531556Srgrimes val = lookupvar(var); 55417987Speter if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 5551556Srgrimes val = NULL; 5561556Srgrimes set = 0; 5571556Srgrimes } else 5581556Srgrimes set = 1; 5591556Srgrimes } 56017987Speter varlen = 0; 5611556Srgrimes startloc = expdest - stackblock(); 5621556Srgrimes if (set && subtype != VSPLUS) { 5631556Srgrimes /* insert the value of the variable */ 5641556Srgrimes if (special) { 56517987Speter char *exp, *oexpdest = expdest; 56618202Speter varvalue(var, varflags & VSQUOTE, flag & EXP_FULL); 56717987Speter if (subtype == VSLENGTH) { 56817987Speter for (exp = oexpdest;exp != expdest; exp++) 56917987Speter varlen++; 57017987Speter expdest = oexpdest; 57117987Speter } 5721556Srgrimes } else { 57317987Speter char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 57417987Speter : BASESYNTAX; 5751556Srgrimes 57617987Speter if (subtype == VSLENGTH) { 57717987Speter for (;*val; val++) 57817987Speter varlen++; 5791556Srgrimes } 58017987Speter else { 58117987Speter while (*val) { 58217987Speter if (quotes && syntax[*val] == CCTL) 58317987Speter STPUTC(CTLESC, expdest); 58417987Speter STPUTC(*val++, expdest); 58517987Speter } 58617987Speter 58717987Speter } 5881556Srgrimes } 5891556Srgrimes } 59017987Speter 5911556Srgrimes if (subtype == VSPLUS) 5921556Srgrimes set = ! set; 59317987Speter 59417987Speter easy = ((varflags & VSQUOTE) == 0 || 59517987Speter (*var == '@' && shellparam.nparam != 1)); 59617987Speter 59717987Speter 59817987Speter switch (subtype) { 59917987Speter case VSLENGTH: 60017987Speter expdest = cvtnum(varlen, expdest); 60117987Speter goto record; 60217987Speter 60317987Speter case VSNORMAL: 60417987Speter if (!easy) 60517987Speter break; 60617987Speterrecord: 60717987Speter recordregion(startloc, expdest - stackblock(), 60817987Speter varflags & VSQUOTE); 60917987Speter break; 61017987Speter 61117987Speter case VSPLUS: 61217987Speter case VSMINUS: 61317987Speter if (!set) { 6141556Srgrimes argstr(p, flag); 61517987Speter break; 61617987Speter } 61717987Speter if (easy) 61817987Speter goto record; 61917987Speter break; 62017987Speter 62117987Speter case VSTRIMLEFT: 62217987Speter case VSTRIMLEFTMAX: 62317987Speter case VSTRIMRIGHT: 62417987Speter case VSTRIMRIGHTMAX: 62517987Speter if (!set) 62617987Speter break; 62717987Speter /* 62817987Speter * Terminate the string and start recording the pattern 62917987Speter * right after it 63017987Speter */ 63117987Speter STPUTC('\0', expdest); 63217987Speter pat = expdest; 63317987Speter if (subevalvar(p, pat, subtype, startloc, varflags)) 63417987Speter goto record; 63517987Speter break; 63617987Speter 63717987Speter case VSASSIGN: 63817987Speter case VSQUESTION: 63917987Speter if (!set) { 64017987Speter if (subevalvar(p, var, subtype, startloc, varflags)) 6411556Srgrimes goto again; 64217987Speter break; 6431556Srgrimes } 64417987Speter if (easy) 64517987Speter goto record; 64617987Speter break; 64717987Speter 64817987Speter default: 64917987Speter abort(); 6501556Srgrimes } 65117987Speter 6521556Srgrimes if (subtype != VSNORMAL) { /* skip to end of alternative */ 6531556Srgrimes int nesting = 1; 6541556Srgrimes for (;;) { 6551556Srgrimes if ((c = *p++) == CTLESC) 6561556Srgrimes p++; 6571556Srgrimes else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 6581556Srgrimes if (set) 6591556Srgrimes argbackq = argbackq->next; 6601556Srgrimes } else if (c == CTLVAR) { 6611556Srgrimes if ((*p++ & VSTYPE) != VSNORMAL) 6621556Srgrimes nesting++; 6631556Srgrimes } else if (c == CTLENDVAR) { 6641556Srgrimes if (--nesting == 0) 6651556Srgrimes break; 6661556Srgrimes } 6671556Srgrimes } 6681556Srgrimes } 6691556Srgrimes return p; 6701556Srgrimes} 6711556Srgrimes 6721556Srgrimes 6731556Srgrimes 6741556Srgrimes/* 6751556Srgrimes * Test whether a specialized variable is set. 6761556Srgrimes */ 6771556Srgrimes 6781556SrgrimesSTATIC int 6791556Srgrimesvarisset(name) 68018202Speter char *name; 6811556Srgrimes { 6821556Srgrimes char **ap; 68318202Speter int num; 6841556Srgrimes 68518202Speter if (*name == '!') { 6861556Srgrimes if (backgndpid == -1) 6871556Srgrimes return 0; 68818202Speter } else if (*name == '@' || *name == '*') { 6891556Srgrimes if (*shellparam.p == NULL) 6901556Srgrimes return 0; 69118202Speter } else if (is_digit(*name)) { 69218202Speter num = atoi(name); 6931556Srgrimes ap = shellparam.p; 69418202Speter while (num-- > 0) 6951556Srgrimes if (*ap++ == NULL) 6961556Srgrimes return 0; 6971556Srgrimes } 6981556Srgrimes return 1; 6991556Srgrimes} 7001556Srgrimes 7011556Srgrimes 7021556Srgrimes 7031556Srgrimes/* 7041556Srgrimes * Add the value of a specialized variable to the stack string. 7051556Srgrimes */ 7061556Srgrimes 7071556SrgrimesSTATIC void 7081556Srgrimesvarvalue(name, quoted, allow_split) 70918202Speter char *name; 71017987Speter int quoted; 71117987Speter int allow_split; 71217987Speter{ 7131556Srgrimes int num; 7141556Srgrimes char *p; 7151556Srgrimes int i; 71617987Speter extern int oexitstatus; 7171556Srgrimes char sep; 7181556Srgrimes char **ap; 7191556Srgrimes char const *syntax; 7201556Srgrimes 7211556Srgrimes#define STRTODEST(p) \ 7221556Srgrimes do {\ 7231556Srgrimes if (allow_split) { \ 7241556Srgrimes syntax = quoted? DQSYNTAX : BASESYNTAX; \ 7251556Srgrimes while (*p) { \ 7261556Srgrimes if (syntax[*p] == CCTL) \ 7271556Srgrimes STPUTC(CTLESC, expdest); \ 7281556Srgrimes STPUTC(*p++, expdest); \ 7291556Srgrimes } \ 7301556Srgrimes } else \ 7311556Srgrimes while (*p) \ 7321556Srgrimes STPUTC(*p++, expdest); \ 7331556Srgrimes } while (0) 7341556Srgrimes 7351556Srgrimes 73618202Speter switch (*name) { 7371556Srgrimes case '$': 7381556Srgrimes num = rootpid; 7391556Srgrimes goto numvar; 7401556Srgrimes case '?': 74117987Speter num = oexitstatus; 7421556Srgrimes goto numvar; 7431556Srgrimes case '#': 7441556Srgrimes num = shellparam.nparam; 7451556Srgrimes goto numvar; 7461556Srgrimes case '!': 7471556Srgrimes num = backgndpid; 7481556Srgrimesnumvar: 74917987Speter expdest = cvtnum(num, expdest); 7501556Srgrimes break; 7511556Srgrimes case '-': 7521556Srgrimes for (i = 0 ; i < NOPTS ; i++) { 7531556Srgrimes if (optlist[i].val) 7541556Srgrimes STPUTC(optlist[i].letter, expdest); 7551556Srgrimes } 7561556Srgrimes break; 7571556Srgrimes case '@': 7581556Srgrimes if (allow_split) { 7591556Srgrimes sep = '\0'; 7601556Srgrimes goto allargs; 7611556Srgrimes } 7628855Srgrimes /* fall through */ 7631556Srgrimes case '*': 7641556Srgrimes sep = ' '; 7651556Srgrimesallargs: 7661556Srgrimes for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 7671556Srgrimes STRTODEST(p); 7681556Srgrimes if (*ap) 7691556Srgrimes STPUTC(sep, expdest); 7701556Srgrimes } 7711556Srgrimes break; 7721556Srgrimes case '0': 7731556Srgrimes p = arg0; 7741556Srgrimes STRTODEST(p); 7751556Srgrimes break; 7761556Srgrimes default: 77718202Speter if (is_digit(*name)) { 77818202Speter num = atoi(name); 77918202Speter if (num > 0 && num <= shellparam.nparam) { 78018202Speter p = shellparam.p[num - 1]; 78118202Speter STRTODEST(p); 78218202Speter } 7831556Srgrimes } 7841556Srgrimes break; 7851556Srgrimes } 7861556Srgrimes} 7871556Srgrimes 7881556Srgrimes 7891556Srgrimes 7901556Srgrimes/* 7911556Srgrimes * Record the the fact that we have to scan this region of the 7921556Srgrimes * string for IFS characters. 7931556Srgrimes */ 7941556Srgrimes 7951556SrgrimesSTATIC void 79617987Speterrecordregion(start, end, nulonly) 79717987Speter int start; 79817987Speter int end; 79917987Speter int nulonly; 80017987Speter{ 8011556Srgrimes register struct ifsregion *ifsp; 8021556Srgrimes 8031556Srgrimes if (ifslastp == NULL) { 8041556Srgrimes ifsp = &ifsfirst; 8051556Srgrimes } else { 8061556Srgrimes ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 8071556Srgrimes ifslastp->next = ifsp; 8081556Srgrimes } 8091556Srgrimes ifslastp = ifsp; 8101556Srgrimes ifslastp->next = NULL; 8111556Srgrimes ifslastp->begoff = start; 8121556Srgrimes ifslastp->endoff = end; 8131556Srgrimes ifslastp->nulonly = nulonly; 8141556Srgrimes} 8151556Srgrimes 8161556Srgrimes 8171556Srgrimes 8181556Srgrimes/* 8191556Srgrimes * Break the argument string into pieces based upon IFS and add the 8201556Srgrimes * strings to the argument list. The regions of the string to be 8211556Srgrimes * searched for IFS characters have been stored by recordregion. 8221556Srgrimes */ 8231556SrgrimesSTATIC void 8241556Srgrimesifsbreakup(string, arglist) 8251556Srgrimes char *string; 8261556Srgrimes struct arglist *arglist; 8271556Srgrimes { 8281556Srgrimes struct ifsregion *ifsp; 8291556Srgrimes struct strlist *sp; 8301556Srgrimes char *start; 8311556Srgrimes register char *p; 8321556Srgrimes char *q; 8331556Srgrimes char *ifs; 83417987Speter int ifsspc; 8351556Srgrimes 83617987Speter 8371556Srgrimes start = string; 8381556Srgrimes if (ifslastp != NULL) { 8391556Srgrimes ifsp = &ifsfirst; 8401556Srgrimes do { 8411556Srgrimes p = string + ifsp->begoff; 8421556Srgrimes ifs = ifsp->nulonly? nullstr : ifsval(); 84317987Speter ifsspc = strchr(ifs, ' ') != NULL; 8441556Srgrimes while (p < string + ifsp->endoff) { 8451556Srgrimes q = p; 8461556Srgrimes if (*p == CTLESC) 8471556Srgrimes p++; 8481556Srgrimes if (strchr(ifs, *p++)) { 84917987Speter if (q > start || !ifsspc) { 8501556Srgrimes *q = '\0'; 8511556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 8521556Srgrimes sp->text = start; 8531556Srgrimes *arglist->lastp = sp; 8541556Srgrimes arglist->lastp = &sp->next; 8551556Srgrimes } 85617987Speter if (ifsspc) { 8571556Srgrimes for (;;) { 8581556Srgrimes if (p >= string + ifsp->endoff) 8591556Srgrimes break; 8601556Srgrimes q = p; 8611556Srgrimes if (*p == CTLESC) 8621556Srgrimes p++; 8631556Srgrimes if (strchr(ifs, *p++) == NULL) { 8641556Srgrimes p = q; 8651556Srgrimes break; 8661556Srgrimes } 8671556Srgrimes } 8681556Srgrimes } 8691556Srgrimes start = p; 8701556Srgrimes } 8711556Srgrimes } 8721556Srgrimes } while ((ifsp = ifsp->next) != NULL); 87317987Speter if (*start || (!ifsspc && start > string)) { 8741556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 8751556Srgrimes sp->text = start; 8761556Srgrimes *arglist->lastp = sp; 8771556Srgrimes arglist->lastp = &sp->next; 8781556Srgrimes } 8791556Srgrimes } else { 8801556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 8811556Srgrimes sp->text = start; 8821556Srgrimes *arglist->lastp = sp; 8831556Srgrimes arglist->lastp = &sp->next; 8841556Srgrimes } 8851556Srgrimes} 8861556Srgrimes 8871556Srgrimes 8881556Srgrimes 8891556Srgrimes/* 8901556Srgrimes * Expand shell metacharacters. At this point, the only control characters 8911556Srgrimes * should be escapes. The results are stored in the list exparg. 8921556Srgrimes */ 8931556Srgrimes 8941556Srgrimeschar *expdir; 8951556Srgrimes 8961556Srgrimes 8971556SrgrimesSTATIC void 8981556Srgrimesexpandmeta(str, flag) 8991556Srgrimes struct strlist *str; 90017987Speter int flag; 90117987Speter{ 9021556Srgrimes char *p; 9031556Srgrimes struct strlist **savelastp; 9041556Srgrimes struct strlist *sp; 9051556Srgrimes char c; 9061556Srgrimes /* TODO - EXP_REDIR */ 9071556Srgrimes 9081556Srgrimes while (str) { 9091556Srgrimes if (fflag) 9101556Srgrimes goto nometa; 9111556Srgrimes p = str->text; 9121556Srgrimes for (;;) { /* fast check for meta chars */ 9131556Srgrimes if ((c = *p++) == '\0') 9141556Srgrimes goto nometa; 9151556Srgrimes if (c == '*' || c == '?' || c == '[' || c == '!') 9161556Srgrimes break; 9171556Srgrimes } 9181556Srgrimes savelastp = exparg.lastp; 9191556Srgrimes INTOFF; 9201556Srgrimes if (expdir == NULL) { 9211556Srgrimes int i = strlen(str->text); 9221556Srgrimes expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 9231556Srgrimes } 9241556Srgrimes 9251556Srgrimes expmeta(expdir, str->text); 9261556Srgrimes ckfree(expdir); 9271556Srgrimes expdir = NULL; 9281556Srgrimes INTON; 9291556Srgrimes if (exparg.lastp == savelastp) { 9308855Srgrimes /* 9318855Srgrimes * no matches 9321556Srgrimes */ 9331556Srgrimesnometa: 9341556Srgrimes *exparg.lastp = str; 9351556Srgrimes rmescapes(str->text); 9361556Srgrimes exparg.lastp = &str->next; 9371556Srgrimes } else { 9381556Srgrimes *exparg.lastp = NULL; 9391556Srgrimes *savelastp = sp = expsort(*savelastp); 9401556Srgrimes while (sp->next != NULL) 9411556Srgrimes sp = sp->next; 9421556Srgrimes exparg.lastp = &sp->next; 9431556Srgrimes } 9441556Srgrimes str = str->next; 9451556Srgrimes } 9461556Srgrimes} 9471556Srgrimes 9481556Srgrimes 9491556Srgrimes/* 9501556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion. 9511556Srgrimes */ 9521556Srgrimes 9531556SrgrimesSTATIC void 9541556Srgrimesexpmeta(enddir, name) 9551556Srgrimes char *enddir; 9561556Srgrimes char *name; 9571556Srgrimes { 9581556Srgrimes register char *p; 9591556Srgrimes char *q; 9601556Srgrimes char *start; 9611556Srgrimes char *endname; 9621556Srgrimes int metaflag; 9631556Srgrimes struct stat statb; 9641556Srgrimes DIR *dirp; 9651556Srgrimes struct dirent *dp; 9661556Srgrimes int atend; 9671556Srgrimes int matchdot; 9681556Srgrimes 9691556Srgrimes metaflag = 0; 9701556Srgrimes start = name; 9711556Srgrimes for (p = name ; ; p++) { 9721556Srgrimes if (*p == '*' || *p == '?') 9731556Srgrimes metaflag = 1; 9741556Srgrimes else if (*p == '[') { 9751556Srgrimes q = p + 1; 9761556Srgrimes if (*q == '!') 9771556Srgrimes q++; 9781556Srgrimes for (;;) { 9791556Srgrimes if (*q == CTLESC) 9801556Srgrimes q++; 9811556Srgrimes if (*q == '/' || *q == '\0') 9821556Srgrimes break; 9831556Srgrimes if (*++q == ']') { 9841556Srgrimes metaflag = 1; 9851556Srgrimes break; 9861556Srgrimes } 9871556Srgrimes } 9881556Srgrimes } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 9891556Srgrimes metaflag = 1; 9901556Srgrimes } else if (*p == '\0') 9911556Srgrimes break; 9921556Srgrimes else if (*p == CTLESC) 9931556Srgrimes p++; 9941556Srgrimes if (*p == '/') { 9951556Srgrimes if (metaflag) 9961556Srgrimes break; 9971556Srgrimes start = p + 1; 9981556Srgrimes } 9991556Srgrimes } 10001556Srgrimes if (metaflag == 0) { /* we've reached the end of the file name */ 10011556Srgrimes if (enddir != expdir) 10021556Srgrimes metaflag++; 10031556Srgrimes for (p = name ; ; p++) { 10041556Srgrimes if (*p == CTLESC) 10051556Srgrimes p++; 10061556Srgrimes *enddir++ = *p; 10071556Srgrimes if (*p == '\0') 10081556Srgrimes break; 10091556Srgrimes } 10101556Srgrimes if (metaflag == 0 || stat(expdir, &statb) >= 0) 10111556Srgrimes addfname(expdir); 10121556Srgrimes return; 10131556Srgrimes } 10141556Srgrimes endname = p; 10151556Srgrimes if (start != name) { 10161556Srgrimes p = name; 10171556Srgrimes while (p < start) { 10181556Srgrimes if (*p == CTLESC) 10191556Srgrimes p++; 10201556Srgrimes *enddir++ = *p++; 10211556Srgrimes } 10221556Srgrimes } 10231556Srgrimes if (enddir == expdir) { 10241556Srgrimes p = "."; 10251556Srgrimes } else if (enddir == expdir + 1 && *expdir == '/') { 10261556Srgrimes p = "/"; 10271556Srgrimes } else { 10281556Srgrimes p = expdir; 10291556Srgrimes enddir[-1] = '\0'; 10301556Srgrimes } 10311556Srgrimes if ((dirp = opendir(p)) == NULL) 10321556Srgrimes return; 10331556Srgrimes if (enddir != expdir) 10341556Srgrimes enddir[-1] = '/'; 10351556Srgrimes if (*endname == 0) { 10361556Srgrimes atend = 1; 10371556Srgrimes } else { 10381556Srgrimes atend = 0; 10391556Srgrimes *endname++ = '\0'; 10401556Srgrimes } 10411556Srgrimes matchdot = 0; 104217987Speter if (start[0] == '.' || (start[0] == CTLESC && start[1] == '.')) 10431556Srgrimes matchdot++; 10441556Srgrimes while (! int_pending() && (dp = readdir(dirp)) != NULL) { 10451556Srgrimes if (dp->d_name[0] == '.' && ! matchdot) 10461556Srgrimes continue; 10471556Srgrimes if (patmatch(start, dp->d_name)) { 10481556Srgrimes if (atend) { 10491556Srgrimes scopy(dp->d_name, enddir); 10501556Srgrimes addfname(expdir); 10511556Srgrimes } else { 10521556Srgrimes char *q; 105317987Speter for (p = enddir, q = dp->d_name; 105417987Speter (*p++ = *q++) != '\0';) 105517987Speter continue; 10561556Srgrimes p[-1] = '/'; 10571556Srgrimes expmeta(p, endname); 10581556Srgrimes } 10591556Srgrimes } 10601556Srgrimes } 10611556Srgrimes closedir(dirp); 10621556Srgrimes if (! atend) 10631556Srgrimes endname[-1] = '/'; 10641556Srgrimes} 10651556Srgrimes 10661556Srgrimes 10671556Srgrimes/* 10681556Srgrimes * Add a file name to the list. 10691556Srgrimes */ 10701556Srgrimes 10711556SrgrimesSTATIC void 10721556Srgrimesaddfname(name) 10731556Srgrimes char *name; 10741556Srgrimes { 10751556Srgrimes char *p; 10761556Srgrimes struct strlist *sp; 10771556Srgrimes 10781556Srgrimes p = stalloc(strlen(name) + 1); 10791556Srgrimes scopy(name, p); 10801556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 10811556Srgrimes sp->text = p; 10821556Srgrimes *exparg.lastp = sp; 10831556Srgrimes exparg.lastp = &sp->next; 10841556Srgrimes} 10851556Srgrimes 10861556Srgrimes 10871556Srgrimes/* 10881556Srgrimes * Sort the results of file name expansion. It calculates the number of 10891556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the 10901556Srgrimes * work. 10911556Srgrimes */ 10921556Srgrimes 10931556SrgrimesSTATIC struct strlist * 10941556Srgrimesexpsort(str) 10951556Srgrimes struct strlist *str; 10961556Srgrimes { 10971556Srgrimes int len; 10981556Srgrimes struct strlist *sp; 10991556Srgrimes 11001556Srgrimes len = 0; 11011556Srgrimes for (sp = str ; sp ; sp = sp->next) 11021556Srgrimes len++; 11031556Srgrimes return msort(str, len); 11041556Srgrimes} 11051556Srgrimes 11061556Srgrimes 11071556SrgrimesSTATIC struct strlist * 11081556Srgrimesmsort(list, len) 11091556Srgrimes struct strlist *list; 111017987Speter int len; 111117987Speter{ 111217987Speter struct strlist *p, *q = NULL; 11131556Srgrimes struct strlist **lpp; 11141556Srgrimes int half; 11151556Srgrimes int n; 11161556Srgrimes 11171556Srgrimes if (len <= 1) 11181556Srgrimes return list; 11198855Srgrimes half = len >> 1; 11201556Srgrimes p = list; 11211556Srgrimes for (n = half ; --n >= 0 ; ) { 11221556Srgrimes q = p; 11231556Srgrimes p = p->next; 11241556Srgrimes } 11251556Srgrimes q->next = NULL; /* terminate first half of list */ 11261556Srgrimes q = msort(list, half); /* sort first half of list */ 11271556Srgrimes p = msort(p, len - half); /* sort second half */ 11281556Srgrimes lpp = &list; 11291556Srgrimes for (;;) { 11301556Srgrimes if (strcmp(p->text, q->text) < 0) { 11311556Srgrimes *lpp = p; 11321556Srgrimes lpp = &p->next; 11331556Srgrimes if ((p = *lpp) == NULL) { 11341556Srgrimes *lpp = q; 11351556Srgrimes break; 11361556Srgrimes } 11371556Srgrimes } else { 11381556Srgrimes *lpp = q; 11391556Srgrimes lpp = &q->next; 11401556Srgrimes if ((q = *lpp) == NULL) { 11411556Srgrimes *lpp = p; 11421556Srgrimes break; 11431556Srgrimes } 11441556Srgrimes } 11451556Srgrimes } 11461556Srgrimes return list; 11471556Srgrimes} 11481556Srgrimes 11491556Srgrimes 11501556Srgrimes 11511556Srgrimes/* 11521556Srgrimes * Returns true if the pattern matches the string. 11531556Srgrimes */ 11541556Srgrimes 11551556Srgrimesint 11561556Srgrimespatmatch(pattern, string) 11571556Srgrimes char *pattern; 11581556Srgrimes char *string; 11591556Srgrimes { 11601556Srgrimes#ifdef notdef 11611556Srgrimes if (pattern[0] == '!' && pattern[1] == '!') 11621556Srgrimes return 1 - pmatch(pattern + 2, string); 11631556Srgrimes else 11641556Srgrimes#endif 11651556Srgrimes return pmatch(pattern, string); 11661556Srgrimes} 11671556Srgrimes 116817987Speter 116917525SacheSTATIC int 11701556Srgrimespmatch(pattern, string) 11711556Srgrimes char *pattern; 11721556Srgrimes char *string; 11731556Srgrimes { 11741556Srgrimes register char *p, *q; 11751556Srgrimes register char c; 11761556Srgrimes 11771556Srgrimes p = pattern; 11781556Srgrimes q = string; 11791556Srgrimes for (;;) { 11801556Srgrimes switch (c = *p++) { 11811556Srgrimes case '\0': 11821556Srgrimes goto breakloop; 11831556Srgrimes case CTLESC: 11841556Srgrimes if (*q++ != *p++) 11851556Srgrimes return 0; 11861556Srgrimes break; 11871556Srgrimes case '?': 11881556Srgrimes if (*q++ == '\0') 11891556Srgrimes return 0; 11901556Srgrimes break; 11911556Srgrimes case '*': 11921556Srgrimes c = *p; 11931556Srgrimes if (c != CTLESC && c != '?' && c != '*' && c != '[') { 11941556Srgrimes while (*q != c) { 11951556Srgrimes if (*q == '\0') 11961556Srgrimes return 0; 11971556Srgrimes q++; 11981556Srgrimes } 11991556Srgrimes } 12001556Srgrimes do { 12011556Srgrimes if (pmatch(p, q)) 12021556Srgrimes return 1; 12031556Srgrimes } while (*q++ != '\0'); 12041556Srgrimes return 0; 12051556Srgrimes case '[': { 12061556Srgrimes char *endp; 12071556Srgrimes int invert, found; 12081556Srgrimes char chr; 12091556Srgrimes 12101556Srgrimes endp = p; 12111556Srgrimes if (*endp == '!') 12121556Srgrimes endp++; 12131556Srgrimes for (;;) { 12141556Srgrimes if (*endp == '\0') 12151556Srgrimes goto dft; /* no matching ] */ 12161556Srgrimes if (*endp == CTLESC) 12171556Srgrimes endp++; 12181556Srgrimes if (*++endp == ']') 12191556Srgrimes break; 12201556Srgrimes } 12211556Srgrimes invert = 0; 12221556Srgrimes if (*p == '!') { 12231556Srgrimes invert++; 12241556Srgrimes p++; 12251556Srgrimes } 12261556Srgrimes found = 0; 12271556Srgrimes chr = *q++; 122817987Speter if (chr == '\0') 122917987Speter return 0; 12301556Srgrimes c = *p++; 12311556Srgrimes do { 12321556Srgrimes if (c == CTLESC) 12331556Srgrimes c = *p++; 12341556Srgrimes if (*p == '-' && p[1] != ']') { 12351556Srgrimes p++; 12361556Srgrimes if (*p == CTLESC) 12371556Srgrimes p++; 123817557Sache if ( collate_range_cmp(chr, c) >= 0 123917557Sache && collate_range_cmp(chr, *p) <= 0 124017525Sache ) 12411556Srgrimes found = 1; 12421556Srgrimes p++; 12431556Srgrimes } else { 12441556Srgrimes if (chr == c) 12451556Srgrimes found = 1; 12461556Srgrimes } 12471556Srgrimes } while ((c = *p++) != ']'); 12481556Srgrimes if (found == invert) 12491556Srgrimes return 0; 12501556Srgrimes break; 12511556Srgrimes } 12521556Srgrimesdft: default: 12531556Srgrimes if (*q++ != c) 12541556Srgrimes return 0; 12551556Srgrimes break; 12561556Srgrimes } 12571556Srgrimes } 12581556Srgrimesbreakloop: 12591556Srgrimes if (*q != '\0') 12601556Srgrimes return 0; 12611556Srgrimes return 1; 12621556Srgrimes} 12631556Srgrimes 12641556Srgrimes 12651556Srgrimes 12661556Srgrimes/* 12671556Srgrimes * Remove any CTLESC characters from a string. 12681556Srgrimes */ 12691556Srgrimes 12701556Srgrimesvoid 12711556Srgrimesrmescapes(str) 12721556Srgrimes char *str; 12731556Srgrimes { 12741556Srgrimes register char *p, *q; 12751556Srgrimes 12761556Srgrimes p = str; 12771556Srgrimes while (*p != CTLESC) { 12781556Srgrimes if (*p++ == '\0') 12791556Srgrimes return; 12801556Srgrimes } 12811556Srgrimes q = p; 12821556Srgrimes while (*p) { 12831556Srgrimes if (*p == CTLESC) 12841556Srgrimes p++; 12851556Srgrimes *q++ = *p++; 12861556Srgrimes } 12871556Srgrimes *q = '\0'; 12881556Srgrimes} 12891556Srgrimes 12901556Srgrimes 12911556Srgrimes 12921556Srgrimes/* 12931556Srgrimes * See if a pattern matches in a case statement. 12941556Srgrimes */ 12951556Srgrimes 12961556Srgrimesint 12971556Srgrimescasematch(pattern, val) 12981556Srgrimes union node *pattern; 12991556Srgrimes char *val; 13001556Srgrimes { 13011556Srgrimes struct stackmark smark; 13021556Srgrimes int result; 13031556Srgrimes char *p; 13041556Srgrimes 13051556Srgrimes setstackmark(&smark); 13061556Srgrimes argbackq = pattern->narg.backquote; 13071556Srgrimes STARTSTACKSTR(expdest); 13081556Srgrimes ifslastp = NULL; 13091556Srgrimes argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 13101556Srgrimes STPUTC('\0', expdest); 13111556Srgrimes p = grabstackstr(expdest); 13121556Srgrimes result = patmatch(p, val); 13131556Srgrimes popstackmark(&smark); 13141556Srgrimes return result; 13151556Srgrimes} 131617987Speter 131717987Speter/* 131817987Speter * Our own itoa(). 131917987Speter */ 132017987Speter 132117987SpeterSTATIC char * 132217987Spetercvtnum(num, buf) 132317987Speter int num; 132417987Speter char *buf; 132517987Speter { 132617987Speter char temp[32]; 132717987Speter int neg = num < 0; 132817987Speter char *p = temp + 31; 132917987Speter 133017987Speter temp[31] = '\0'; 133117987Speter 133217987Speter do { 133317987Speter *--p = num % 10 + '0'; 133417987Speter } while ((num /= 10) != 0); 133517987Speter 133617987Speter if (neg) 133717987Speter *--p = '-'; 133817987Speter 133917987Speter while (*p) 134017987Speter STPUTC(*p++, buf); 134117987Speter return buf; 134217987Speter} 1343