expand.c revision 26488
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 * 3626488Sache * $Id: expand.c,v 1.18 1997/05/19 00:18:40 steve Exp $ 371556Srgrimes */ 381556Srgrimes 391556Srgrimes#ifndef lint 4020425Sstevestatic char const 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> 5119281Sache#include <limits.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)); 9820425SsteveSTATIC int subevalvar __P((char *, char *, int, int, int, int)); 9917987SpeterSTATIC char *evalvar __P((char *, int)); 10025233SsteveSTATIC int varisset __P((char *, int)); 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 *)); 11119281SacheSTATIC int collate_range_cmp __P((int, int)); 1121556Srgrimes 11319281SacheSTATIC int collate_range_cmp (c1, c2) 11419281Sache int c1, c2; 11519281Sache{ 11619281Sache static char s1[2], s2[2]; 11719281Sache int ret; 11819281Sache 11919281Sache c1 &= UCHAR_MAX; 12019281Sache c2 &= UCHAR_MAX; 12119281Sache if (c1 == c2) 12219281Sache return (0); 12319281Sache s1[0] = c1; 12419281Sache s2[0] = c2; 12519281Sache if ((ret = strcoll(s1, s2)) != 0) 12619281Sache return (ret); 12719281Sache return (c1 - c2); 12819281Sache} 12919281Sache 1301556Srgrimes/* 1311556Srgrimes * Expand shell variables and backquotes inside a here document. 1321556Srgrimes */ 1331556Srgrimes 1341556Srgrimesvoid 1351556Srgrimesexpandhere(arg, fd) 1361556Srgrimes union node *arg; /* the document */ 1371556Srgrimes int fd; /* where to write the expanded version */ 1381556Srgrimes { 1391556Srgrimes herefd = fd; 1401556Srgrimes expandarg(arg, (struct arglist *)NULL, 0); 1411556Srgrimes xwrite(fd, stackblock(), expdest - stackblock()); 1421556Srgrimes} 1431556Srgrimes 1441556Srgrimes 1451556Srgrimes/* 1461556Srgrimes * Perform variable substitution and command substitution on an argument, 1471556Srgrimes * placing the resulting list of arguments in arglist. If EXP_FULL is true, 1481556Srgrimes * perform splitting and file name expansion. When arglist is NULL, perform 1491556Srgrimes * here document expansion. 1501556Srgrimes */ 1511556Srgrimes 1521556Srgrimesvoid 1531556Srgrimesexpandarg(arg, arglist, flag) 1541556Srgrimes union node *arg; 1551556Srgrimes struct arglist *arglist; 15617987Speter int flag; 15717987Speter{ 1581556Srgrimes struct strlist *sp; 1591556Srgrimes char *p; 1601556Srgrimes 1611556Srgrimes argbackq = arg->narg.backquote; 1621556Srgrimes STARTSTACKSTR(expdest); 1631556Srgrimes ifsfirst.next = NULL; 1641556Srgrimes ifslastp = NULL; 1651556Srgrimes argstr(arg->narg.text, flag); 1661556Srgrimes if (arglist == NULL) { 1671556Srgrimes return; /* here document expanded */ 1681556Srgrimes } 1691556Srgrimes STPUTC('\0', expdest); 1701556Srgrimes p = grabstackstr(expdest); 1711556Srgrimes exparg.lastp = &exparg.list; 1721556Srgrimes /* 1731556Srgrimes * TODO - EXP_REDIR 1741556Srgrimes */ 1751556Srgrimes if (flag & EXP_FULL) { 1761556Srgrimes ifsbreakup(p, &exparg); 1771556Srgrimes *exparg.lastp = NULL; 1781556Srgrimes exparg.lastp = &exparg.list; 1791556Srgrimes expandmeta(exparg.list, flag); 1801556Srgrimes } else { 1811556Srgrimes if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 1821556Srgrimes rmescapes(p); 1831556Srgrimes sp = (struct strlist *)stalloc(sizeof (struct strlist)); 1841556Srgrimes sp->text = p; 1851556Srgrimes *exparg.lastp = sp; 1861556Srgrimes exparg.lastp = &sp->next; 1871556Srgrimes } 1881556Srgrimes while (ifsfirst.next != NULL) { 1891556Srgrimes struct ifsregion *ifsp; 1901556Srgrimes INTOFF; 1911556Srgrimes ifsp = ifsfirst.next->next; 1921556Srgrimes ckfree(ifsfirst.next); 1931556Srgrimes ifsfirst.next = ifsp; 1941556Srgrimes INTON; 1951556Srgrimes } 1961556Srgrimes *exparg.lastp = NULL; 1971556Srgrimes if (exparg.list) { 1981556Srgrimes *arglist->lastp = exparg.list; 1991556Srgrimes arglist->lastp = exparg.lastp; 2001556Srgrimes } 2011556Srgrimes} 2021556Srgrimes 2031556Srgrimes 2041556Srgrimes 2051556Srgrimes/* 2061556Srgrimes * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 2071556Srgrimes * characters to allow for further processing. Otherwise treat 2081556Srgrimes * $@ like $* since no splitting will be performed. 2091556Srgrimes */ 2101556Srgrimes 2111556SrgrimesSTATIC void 2121556Srgrimesargstr(p, flag) 21325233Ssteve char *p; 21417987Speter int flag; 21517987Speter{ 21625233Ssteve char c; 2171556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ 2181556Srgrimes int firsteq = 1; 2191556Srgrimes 2201556Srgrimes if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 2211556Srgrimes p = exptilde(p, flag); 2221556Srgrimes for (;;) { 2231556Srgrimes switch (c = *p++) { 2241556Srgrimes case '\0': 2251556Srgrimes case CTLENDVAR: /* ??? */ 2261556Srgrimes goto breakloop; 2271556Srgrimes case CTLESC: 2281556Srgrimes if (quotes) 2291556Srgrimes STPUTC(c, expdest); 2301556Srgrimes c = *p++; 2311556Srgrimes STPUTC(c, expdest); 2321556Srgrimes break; 2331556Srgrimes case CTLVAR: 2341556Srgrimes p = evalvar(p, flag); 2351556Srgrimes break; 2361556Srgrimes case CTLBACKQ: 2371556Srgrimes case CTLBACKQ|CTLQUOTE: 2381556Srgrimes expbackq(argbackq->n, c & CTLQUOTE, flag); 2391556Srgrimes argbackq = argbackq->next; 2401556Srgrimes break; 2411556Srgrimes case CTLENDARI: 2421556Srgrimes expari(flag); 2431556Srgrimes break; 2441556Srgrimes case ':': 2451556Srgrimes case '=': 2461556Srgrimes /* 2471556Srgrimes * sort of a hack - expand tildes in variable 2481556Srgrimes * assignments (after the first '=' and after ':'s). 2491556Srgrimes */ 2501556Srgrimes STPUTC(c, expdest); 2511556Srgrimes if (flag & EXP_VARTILDE && *p == '~') { 2521556Srgrimes if (c == '=') { 2531556Srgrimes if (firsteq) 2541556Srgrimes firsteq = 0; 2551556Srgrimes else 2561556Srgrimes break; 2571556Srgrimes } 2581556Srgrimes p = exptilde(p, flag); 2591556Srgrimes } 2601556Srgrimes break; 2611556Srgrimes default: 2621556Srgrimes STPUTC(c, expdest); 2631556Srgrimes } 2641556Srgrimes } 2651556Srgrimesbreakloop:; 2661556Srgrimes} 2671556Srgrimes 2681556SrgrimesSTATIC char * 2691556Srgrimesexptilde(p, flag) 2701556Srgrimes char *p; 27117987Speter int flag; 27217987Speter{ 2731556Srgrimes char c, *startp = p; 2741556Srgrimes struct passwd *pw; 2751556Srgrimes char *home; 2761556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 2771556Srgrimes 27817987Speter while ((c = *p) != '\0') { 2791556Srgrimes switch(c) { 2801556Srgrimes case CTLESC: 2811556Srgrimes return (startp); 2821556Srgrimes case ':': 2831556Srgrimes if (flag & EXP_VARTILDE) 2841556Srgrimes goto done; 2851556Srgrimes break; 2861556Srgrimes case '/': 2871556Srgrimes goto done; 2881556Srgrimes } 2891556Srgrimes p++; 2901556Srgrimes } 2911556Srgrimesdone: 2921556Srgrimes *p = '\0'; 2931556Srgrimes if (*(startp+1) == '\0') { 2941556Srgrimes if ((home = lookupvar("HOME")) == NULL) 2951556Srgrimes goto lose; 2961556Srgrimes } else { 2971556Srgrimes if ((pw = getpwnam(startp+1)) == NULL) 2981556Srgrimes goto lose; 2991556Srgrimes home = pw->pw_dir; 3001556Srgrimes } 3011556Srgrimes if (*home == '\0') 3021556Srgrimes goto lose; 3031556Srgrimes *p = c; 30417987Speter while ((c = *home++) != '\0') { 3051556Srgrimes if (quotes && SQSYNTAX[c] == CCTL) 3061556Srgrimes STPUTC(CTLESC, expdest); 3071556Srgrimes STPUTC(c, expdest); 3081556Srgrimes } 3091556Srgrimes return (p); 3101556Srgrimeslose: 3111556Srgrimes *p = c; 3121556Srgrimes return (startp); 3131556Srgrimes} 3141556Srgrimes 3151556Srgrimes 3161556Srgrimes/* 3171556Srgrimes * Expand arithmetic expression. Backup to start of expression, 3181556Srgrimes * evaluate, place result in (backed up) result, adjust string position. 3191556Srgrimes */ 3201556Srgrimesvoid 3211556Srgrimesexpari(flag) 32217987Speter int flag; 32317987Speter{ 3241556Srgrimes char *p, *start; 3251556Srgrimes int result; 3261556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 3271556Srgrimes 32825233Ssteve while (ifsfirst.next != NULL) { 32925233Ssteve struct ifsregion *ifsp; 33025233Ssteve INTOFF; 33125233Ssteve ifsp = ifsfirst.next->next; 33225233Ssteve ckfree(ifsfirst.next); 33325233Ssteve ifsfirst.next = ifsp; 33425233Ssteve INTON; 33525233Ssteve } 33625233Ssteve ifslastp = NULL; 33725233Ssteve 3381556Srgrimes /* 3391556Srgrimes * This routine is slightly over-compilcated for 3401556Srgrimes * efficiency. First we make sure there is 3411556Srgrimes * enough space for the result, which may be bigger 3421556Srgrimes * than the expression if we add exponentation. Next we 3431556Srgrimes * scan backwards looking for the start of arithmetic. If the 3441556Srgrimes * next previous character is a CTLESC character, then we 3451556Srgrimes * have to rescan starting from the beginning since CTLESC 3468855Srgrimes * characters have to be processed left to right. 3471556Srgrimes */ 34822777Ssteve#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10 34922777Ssteve#error "integers with more than 10 digits are not supported" 35022777Ssteve#endif 35122777Ssteve CHECKSTRSPACE(12 - 2, expdest); 3528855Srgrimes USTPUTC('\0', expdest); 3531556Srgrimes start = stackblock(); 3541556Srgrimes p = expdest; 3551556Srgrimes while (*p != CTLARI && p >= start) 3561556Srgrimes --p; 3571556Srgrimes if (*p != CTLARI) 3581556Srgrimes error("missing CTLARI (shouldn't happen)"); 3591556Srgrimes if (p > start && *(p-1) == CTLESC) 3601556Srgrimes for (p = start; *p != CTLARI; p++) 3611556Srgrimes if (*p == CTLESC) 3621556Srgrimes p++; 3631556Srgrimes if (quotes) 3641556Srgrimes rmescapes(p+1); 3651556Srgrimes result = arith(p+1); 36622777Ssteve fmtstr(p, 12, "%d", result); 3671556Srgrimes while (*p++) 3681556Srgrimes ; 3691556Srgrimes result = expdest - p + 1; 3701556Srgrimes STADJUST(-result, expdest); 3711556Srgrimes} 3721556Srgrimes 3731556Srgrimes 3741556Srgrimes/* 3751556Srgrimes * Expand stuff in backwards quotes. 3761556Srgrimes */ 3771556Srgrimes 3781556SrgrimesSTATIC void 3791556Srgrimesexpbackq(cmd, quoted, flag) 3801556Srgrimes union node *cmd; 38117987Speter int quoted; 38217987Speter int flag; 38317987Speter{ 3841556Srgrimes struct backcmd in; 3851556Srgrimes int i; 3861556Srgrimes char buf[128]; 3871556Srgrimes char *p; 3881556Srgrimes char *dest = expdest; 3891556Srgrimes struct ifsregion saveifs, *savelastp; 3901556Srgrimes struct nodelist *saveargbackq; 3911556Srgrimes char lastc; 3921556Srgrimes int startloc = dest - stackblock(); 3931556Srgrimes char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 3941556Srgrimes int saveherefd; 3951556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 3961556Srgrimes 3971556Srgrimes INTOFF; 3981556Srgrimes saveifs = ifsfirst; 3991556Srgrimes savelastp = ifslastp; 4001556Srgrimes saveargbackq = argbackq; 4018855Srgrimes saveherefd = herefd; 4021556Srgrimes herefd = -1; 4031556Srgrimes p = grabstackstr(dest); 4041556Srgrimes evalbackcmd(cmd, &in); 4051556Srgrimes ungrabstackstr(p, dest); 4061556Srgrimes ifsfirst = saveifs; 4071556Srgrimes ifslastp = savelastp; 4081556Srgrimes argbackq = saveargbackq; 4091556Srgrimes herefd = saveherefd; 4101556Srgrimes 4111556Srgrimes p = in.buf; 4121556Srgrimes lastc = '\0'; 4131556Srgrimes for (;;) { 4141556Srgrimes if (--in.nleft < 0) { 4151556Srgrimes if (in.fd < 0) 4161556Srgrimes break; 4171556Srgrimes while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 4181556Srgrimes TRACE(("expbackq: read returns %d\n", i)); 4191556Srgrimes if (i <= 0) 4201556Srgrimes break; 4211556Srgrimes p = buf; 4221556Srgrimes in.nleft = i - 1; 4231556Srgrimes } 4241556Srgrimes lastc = *p++; 4251556Srgrimes if (lastc != '\0') { 4261556Srgrimes if (quotes && syntax[lastc] == CCTL) 4271556Srgrimes STPUTC(CTLESC, dest); 4281556Srgrimes STPUTC(lastc, dest); 4291556Srgrimes } 4301556Srgrimes } 43117987Speter 43217987Speter /* Eat all trailing newlines */ 43317987Speter for (p--; lastc == '\n'; lastc = *--p) 4341556Srgrimes STUNPUTC(dest); 43517987Speter 4361556Srgrimes if (in.fd >= 0) 4371556Srgrimes close(in.fd); 4381556Srgrimes if (in.buf) 4391556Srgrimes ckfree(in.buf); 4401556Srgrimes if (in.jp) 4411556Srgrimes exitstatus = waitforjob(in.jp); 4421556Srgrimes if (quoted == 0) 4431556Srgrimes recordregion(startloc, dest - stackblock(), 0); 4441556Srgrimes TRACE(("evalbackq: size=%d: \"%.*s\"\n", 4451556Srgrimes (dest - stackblock()) - startloc, 4461556Srgrimes (dest - stackblock()) - startloc, 4471556Srgrimes stackblock() + startloc)); 4481556Srgrimes expdest = dest; 4491556Srgrimes INTON; 4501556Srgrimes} 4511556Srgrimes 4521556Srgrimes 4531556Srgrimes 45417987SpeterSTATIC int 45520425Sstevesubevalvar(p, str, strloc, subtype, startloc, varflags) 45617987Speter char *p; 45717987Speter char *str; 45820425Ssteve int strloc; 45917987Speter int subtype; 46017987Speter int startloc; 46117987Speter int varflags; 46217987Speter{ 46317987Speter char *startp; 46417987Speter char *loc = NULL; 46517987Speter int c = 0; 46617987Speter int saveherefd = herefd; 46717987Speter struct nodelist *saveargbackq = argbackq; 46820425Ssteve int amount; 46920425Ssteve 47017987Speter herefd = -1; 47117987Speter argstr(p, 0); 47217987Speter STACKSTRNUL(expdest); 47317987Speter herefd = saveherefd; 47417987Speter argbackq = saveargbackq; 47517987Speter startp = stackblock() + startloc; 47620425Ssteve if (str == NULL) 47720425Ssteve str = stackblock() + strloc; 47817987Speter 47917987Speter switch (subtype) { 48017987Speter case VSASSIGN: 48117987Speter setvar(str, startp, 0); 48220425Ssteve amount = startp - expdest; 48320425Ssteve STADJUST(amount, expdest); 48417987Speter varflags &= ~VSNUL; 48517987Speter if (c != 0) 48617987Speter *loc = c; 48717987Speter return 1; 48817987Speter 48917987Speter case VSQUESTION: 49017987Speter if (*p != CTLENDVAR) { 49117987Speter outfmt(&errout, "%s\n", startp); 49217987Speter error((char *)NULL); 49317987Speter } 49417987Speter error("%.*s: parameter %snot set", p - str - 1, 49517987Speter str, (varflags & VSNUL) ? "null or " 49617987Speter : nullstr); 49717987Speter return 0; 49817987Speter 49917987Speter case VSTRIMLEFT: 50025233Ssteve for (loc = startp; loc < str; loc++) { 50117987Speter c = *loc; 50217987Speter *loc = '\0'; 50317987Speter if (patmatch(str, startp)) { 50417987Speter *loc = c; 50517987Speter goto recordleft; 50617987Speter } 50717987Speter *loc = c; 50817987Speter } 50917987Speter return 0; 51017987Speter 51117987Speter case VSTRIMLEFTMAX: 51217987Speter for (loc = str - 1; loc >= startp; loc--) { 51317987Speter c = *loc; 51417987Speter *loc = '\0'; 51517987Speter if (patmatch(str, startp)) { 51617987Speter *loc = c; 51717987Speter goto recordleft; 51817987Speter } 51917987Speter *loc = c; 52017987Speter } 52117987Speter return 0; 52217987Speter 52317987Speter case VSTRIMRIGHT: 52417987Speter for (loc = str - 1; loc >= startp; loc--) { 52517987Speter if (patmatch(str, loc)) { 52620425Ssteve amount = loc - expdest; 52720425Ssteve STADJUST(amount, expdest); 52817987Speter return 1; 52917987Speter } 53017987Speter } 53117987Speter return 0; 53217987Speter 53317987Speter case VSTRIMRIGHTMAX: 53417987Speter for (loc = startp; loc < str - 1; loc++) { 53517987Speter if (patmatch(str, loc)) { 53620425Ssteve amount = loc - expdest; 53720425Ssteve STADJUST(amount, expdest); 53817987Speter return 1; 53917987Speter } 54017987Speter } 54117987Speter return 0; 54217987Speter 54317987Speter 54417987Speter default: 54517987Speter abort(); 54617987Speter } 54717987Speter 54817987Speterrecordleft: 54920425Ssteve amount = ((str - 1) - (loc - startp)) - expdest; 55020425Ssteve STADJUST(amount, expdest); 55117987Speter while (loc != str - 1) 55217987Speter *startp++ = *loc++; 55317987Speter return 1; 55417987Speter} 55517987Speter 55617987Speter 5571556Srgrimes/* 5581556Srgrimes * Expand a variable, and return a pointer to the next character in the 5591556Srgrimes * input string. 5601556Srgrimes */ 5611556Srgrimes 5621556SrgrimesSTATIC char * 5631556Srgrimesevalvar(p, flag) 5641556Srgrimes char *p; 56517987Speter int flag; 56617987Speter{ 5671556Srgrimes int subtype; 5681556Srgrimes int varflags; 5691556Srgrimes char *var; 5701556Srgrimes char *val; 57117987Speter char *pat; 5721556Srgrimes int c; 5731556Srgrimes int set; 5741556Srgrimes int special; 5751556Srgrimes int startloc; 57617987Speter int varlen; 57717987Speter int easy; 5781556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 5791556Srgrimes 5801556Srgrimes varflags = *p++; 5811556Srgrimes subtype = varflags & VSTYPE; 5821556Srgrimes var = p; 5831556Srgrimes special = 0; 5841556Srgrimes if (! is_name(*p)) 5851556Srgrimes special = 1; 5861556Srgrimes p = strchr(p, '=') + 1; 5871556Srgrimesagain: /* jump here after setting a variable with ${var=text} */ 5881556Srgrimes if (special) { 58925233Ssteve set = varisset(var, varflags & VSNUL); 5901556Srgrimes val = NULL; 5911556Srgrimes } else { 5921556Srgrimes val = lookupvar(var); 59317987Speter if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 5941556Srgrimes val = NULL; 5951556Srgrimes set = 0; 5961556Srgrimes } else 5971556Srgrimes set = 1; 5981556Srgrimes } 59917987Speter varlen = 0; 6001556Srgrimes startloc = expdest - stackblock(); 6011556Srgrimes if (set && subtype != VSPLUS) { 6021556Srgrimes /* insert the value of the variable */ 6031556Srgrimes if (special) { 60418202Speter varvalue(var, varflags & VSQUOTE, flag & EXP_FULL); 60517987Speter if (subtype == VSLENGTH) { 60625233Ssteve varlen = expdest - stackblock() - startloc; 60725233Ssteve STADJUST(-varlen, expdest); 60817987Speter } 6091556Srgrimes } else { 61020425Ssteve char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 61117987Speter : BASESYNTAX; 6121556Srgrimes 61317987Speter if (subtype == VSLENGTH) { 61417987Speter for (;*val; val++) 61517987Speter varlen++; 6161556Srgrimes } 61717987Speter else { 61817987Speter while (*val) { 61917987Speter if (quotes && syntax[*val] == CCTL) 62017987Speter STPUTC(CTLESC, expdest); 62117987Speter STPUTC(*val++, expdest); 62217987Speter } 62317987Speter 62417987Speter } 6251556Srgrimes } 6261556Srgrimes } 62720425Ssteve 6281556Srgrimes if (subtype == VSPLUS) 6291556Srgrimes set = ! set; 63017987Speter 63120425Ssteve easy = ((varflags & VSQUOTE) == 0 || 63217987Speter (*var == '@' && shellparam.nparam != 1)); 63317987Speter 63417987Speter 63517987Speter switch (subtype) { 63617987Speter case VSLENGTH: 63717987Speter expdest = cvtnum(varlen, expdest); 63817987Speter goto record; 63917987Speter 64017987Speter case VSNORMAL: 64117987Speter if (!easy) 64217987Speter break; 64317987Speterrecord: 64420425Ssteve recordregion(startloc, expdest - stackblock(), 64517987Speter varflags & VSQUOTE); 64617987Speter break; 64717987Speter 64817987Speter case VSPLUS: 64917987Speter case VSMINUS: 65017987Speter if (!set) { 6511556Srgrimes argstr(p, flag); 65217987Speter break; 65317987Speter } 65417987Speter if (easy) 65517987Speter goto record; 65617987Speter break; 65717987Speter 65817987Speter case VSTRIMLEFT: 65917987Speter case VSTRIMLEFTMAX: 66017987Speter case VSTRIMRIGHT: 66117987Speter case VSTRIMRIGHTMAX: 66217987Speter if (!set) 66317987Speter break; 66417987Speter /* 66517987Speter * Terminate the string and start recording the pattern 66617987Speter * right after it 66717987Speter */ 66817987Speter STPUTC('\0', expdest); 66917987Speter pat = expdest; 67020425Ssteve if (subevalvar(p, NULL, expdest - stackblock(), subtype, 67120425Ssteve startloc, varflags)) 67217987Speter goto record; 67325233Ssteve else { 67425233Ssteve int amount = (expdest - pat) + 1; 67525233Ssteve STADJUST(-amount, expdest); 67625233Ssteve } 67717987Speter break; 67817987Speter 67917987Speter case VSASSIGN: 68017987Speter case VSQUESTION: 68117987Speter if (!set) { 68220425Ssteve if (subevalvar(p, var, 0, subtype, startloc, varflags)) { 68320425Ssteve varflags &= ~VSNUL; 6841556Srgrimes goto again; 68520425Ssteve } 68617987Speter break; 6871556Srgrimes } 68817987Speter if (easy) 68917987Speter goto record; 69017987Speter break; 69117987Speter 69217987Speter default: 69317987Speter abort(); 6941556Srgrimes } 69517987Speter 6961556Srgrimes if (subtype != VSNORMAL) { /* skip to end of alternative */ 6971556Srgrimes int nesting = 1; 6981556Srgrimes for (;;) { 6991556Srgrimes if ((c = *p++) == CTLESC) 7001556Srgrimes p++; 7011556Srgrimes else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 7021556Srgrimes if (set) 7031556Srgrimes argbackq = argbackq->next; 7041556Srgrimes } else if (c == CTLVAR) { 7051556Srgrimes if ((*p++ & VSTYPE) != VSNORMAL) 7061556Srgrimes nesting++; 7071556Srgrimes } else if (c == CTLENDVAR) { 7081556Srgrimes if (--nesting == 0) 7091556Srgrimes break; 7101556Srgrimes } 7111556Srgrimes } 7121556Srgrimes } 7131556Srgrimes return p; 7141556Srgrimes} 7151556Srgrimes 7161556Srgrimes 7171556Srgrimes 7181556Srgrimes/* 7191556Srgrimes * Test whether a specialized variable is set. 7201556Srgrimes */ 7211556Srgrimes 7221556SrgrimesSTATIC int 72325233Sstevevarisset(name, nulok) 72418202Speter char *name; 72525233Ssteve int nulok; 72625233Ssteve{ 7271556Srgrimes 72825233Ssteve if (*name == '!') 72925233Ssteve return backgndpid != -1; 73025233Ssteve else if (*name == '@' || *name == '*') { 7311556Srgrimes if (*shellparam.p == NULL) 7321556Srgrimes return 0; 73325233Ssteve 73425233Ssteve if (nulok) { 73525233Ssteve char **av; 73625233Ssteve 73725233Ssteve for (av = shellparam.p; *av; av++) 73825233Ssteve if (**av != '\0') 73925233Ssteve return 1; 74025233Ssteve return 0; 74125233Ssteve } 74218202Speter } else if (is_digit(*name)) { 74325233Ssteve char *ap; 74420425Ssteve int num = atoi(name); 74525233Ssteve 74625233Ssteve if (num > shellparam.nparam) 74725233Ssteve return 0; 74825233Ssteve 74925233Ssteve if (num == 0) 75025233Ssteve ap = arg0; 75125233Ssteve else 75225233Ssteve ap = shellparam.p[num - 1]; 75325233Ssteve 75425233Ssteve if (nulok && (ap == NULL || *ap == '\0')) 75525233Ssteve return 0; 7561556Srgrimes } 7571556Srgrimes return 1; 7581556Srgrimes} 7591556Srgrimes 7601556Srgrimes 7611556Srgrimes 7621556Srgrimes/* 7631556Srgrimes * Add the value of a specialized variable to the stack string. 7641556Srgrimes */ 7651556Srgrimes 7661556SrgrimesSTATIC void 7671556Srgrimesvarvalue(name, quoted, allow_split) 76818202Speter char *name; 76917987Speter int quoted; 77017987Speter int allow_split; 77117987Speter{ 7721556Srgrimes int num; 7731556Srgrimes char *p; 7741556Srgrimes int i; 77517987Speter extern int oexitstatus; 7761556Srgrimes char sep; 7771556Srgrimes char **ap; 7781556Srgrimes char const *syntax; 7791556Srgrimes 7801556Srgrimes#define STRTODEST(p) \ 7811556Srgrimes do {\ 7821556Srgrimes if (allow_split) { \ 7831556Srgrimes syntax = quoted? DQSYNTAX : BASESYNTAX; \ 7841556Srgrimes while (*p) { \ 7851556Srgrimes if (syntax[*p] == CCTL) \ 7861556Srgrimes STPUTC(CTLESC, expdest); \ 7871556Srgrimes STPUTC(*p++, expdest); \ 7881556Srgrimes } \ 7891556Srgrimes } else \ 7901556Srgrimes while (*p) \ 7911556Srgrimes STPUTC(*p++, expdest); \ 7921556Srgrimes } while (0) 7931556Srgrimes 7941556Srgrimes 79518202Speter switch (*name) { 7961556Srgrimes case '$': 7971556Srgrimes num = rootpid; 7981556Srgrimes goto numvar; 7991556Srgrimes case '?': 80017987Speter num = oexitstatus; 8011556Srgrimes goto numvar; 8021556Srgrimes case '#': 8031556Srgrimes num = shellparam.nparam; 8041556Srgrimes goto numvar; 8051556Srgrimes case '!': 8061556Srgrimes num = backgndpid; 8071556Srgrimesnumvar: 80817987Speter expdest = cvtnum(num, expdest); 8091556Srgrimes break; 8101556Srgrimes case '-': 8111556Srgrimes for (i = 0 ; i < NOPTS ; i++) { 8121556Srgrimes if (optlist[i].val) 8131556Srgrimes STPUTC(optlist[i].letter, expdest); 8141556Srgrimes } 8151556Srgrimes break; 8161556Srgrimes case '@': 8171556Srgrimes if (allow_split) { 8181556Srgrimes sep = '\0'; 8191556Srgrimes goto allargs; 8201556Srgrimes } 8218855Srgrimes /* fall through */ 8221556Srgrimes case '*': 8231556Srgrimes sep = ' '; 8241556Srgrimesallargs: 8251556Srgrimes for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 8261556Srgrimes STRTODEST(p); 8271556Srgrimes if (*ap) 8281556Srgrimes STPUTC(sep, expdest); 8291556Srgrimes } 8301556Srgrimes break; 8311556Srgrimes case '0': 8321556Srgrimes p = arg0; 8331556Srgrimes STRTODEST(p); 8341556Srgrimes break; 8351556Srgrimes default: 83618202Speter if (is_digit(*name)) { 83718202Speter num = atoi(name); 83818202Speter if (num > 0 && num <= shellparam.nparam) { 83918202Speter p = shellparam.p[num - 1]; 84018202Speter STRTODEST(p); 84118202Speter } 8421556Srgrimes } 8431556Srgrimes break; 8441556Srgrimes } 8451556Srgrimes} 8461556Srgrimes 8471556Srgrimes 8481556Srgrimes 8491556Srgrimes/* 8501556Srgrimes * Record the the fact that we have to scan this region of the 8511556Srgrimes * string for IFS characters. 8521556Srgrimes */ 8531556Srgrimes 8541556SrgrimesSTATIC void 85520425Ssteverecordregion(start, end, nulonly) 85617987Speter int start; 85717987Speter int end; 85817987Speter int nulonly; 85917987Speter{ 86025233Ssteve struct ifsregion *ifsp; 8611556Srgrimes 8621556Srgrimes if (ifslastp == NULL) { 8631556Srgrimes ifsp = &ifsfirst; 8641556Srgrimes } else { 8651556Srgrimes ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 8661556Srgrimes ifslastp->next = ifsp; 8671556Srgrimes } 8681556Srgrimes ifslastp = ifsp; 8691556Srgrimes ifslastp->next = NULL; 8701556Srgrimes ifslastp->begoff = start; 8711556Srgrimes ifslastp->endoff = end; 8721556Srgrimes ifslastp->nulonly = nulonly; 8731556Srgrimes} 8741556Srgrimes 8751556Srgrimes 8761556Srgrimes 8771556Srgrimes/* 8781556Srgrimes * Break the argument string into pieces based upon IFS and add the 8791556Srgrimes * strings to the argument list. The regions of the string to be 8801556Srgrimes * searched for IFS characters have been stored by recordregion. 8811556Srgrimes */ 8821556SrgrimesSTATIC void 8831556Srgrimesifsbreakup(string, arglist) 8841556Srgrimes char *string; 8851556Srgrimes struct arglist *arglist; 8861556Srgrimes { 8871556Srgrimes struct ifsregion *ifsp; 8881556Srgrimes struct strlist *sp; 8891556Srgrimes char *start; 89025233Ssteve char *p; 8911556Srgrimes char *q; 8921556Srgrimes char *ifs; 89317987Speter int ifsspc; 8941556Srgrimes 89517987Speter 8961556Srgrimes start = string; 8971556Srgrimes if (ifslastp != NULL) { 8981556Srgrimes ifsp = &ifsfirst; 8991556Srgrimes do { 9001556Srgrimes p = string + ifsp->begoff; 9011556Srgrimes ifs = ifsp->nulonly? nullstr : ifsval(); 90217987Speter ifsspc = strchr(ifs, ' ') != NULL; 9031556Srgrimes while (p < string + ifsp->endoff) { 9041556Srgrimes q = p; 9051556Srgrimes if (*p == CTLESC) 9061556Srgrimes p++; 9071556Srgrimes if (strchr(ifs, *p++)) { 90817987Speter if (q > start || !ifsspc) { 9091556Srgrimes *q = '\0'; 9101556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 9111556Srgrimes sp->text = start; 9121556Srgrimes *arglist->lastp = sp; 9131556Srgrimes arglist->lastp = &sp->next; 9141556Srgrimes } 91517987Speter if (ifsspc) { 9161556Srgrimes for (;;) { 9171556Srgrimes if (p >= string + ifsp->endoff) 9181556Srgrimes break; 9191556Srgrimes q = p; 9201556Srgrimes if (*p == CTLESC) 9211556Srgrimes p++; 9221556Srgrimes if (strchr(ifs, *p++) == NULL) { 9231556Srgrimes p = q; 9241556Srgrimes break; 9251556Srgrimes } 9261556Srgrimes } 9271556Srgrimes } 9281556Srgrimes start = p; 9291556Srgrimes } 9301556Srgrimes } 9311556Srgrimes } while ((ifsp = ifsp->next) != NULL); 93217987Speter if (*start || (!ifsspc && start > string)) { 9331556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 9341556Srgrimes sp->text = start; 9351556Srgrimes *arglist->lastp = sp; 9361556Srgrimes arglist->lastp = &sp->next; 9371556Srgrimes } 9381556Srgrimes } else { 9391556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 9401556Srgrimes sp->text = start; 9411556Srgrimes *arglist->lastp = sp; 9421556Srgrimes arglist->lastp = &sp->next; 9431556Srgrimes } 9441556Srgrimes} 9451556Srgrimes 9461556Srgrimes 9471556Srgrimes 9481556Srgrimes/* 9491556Srgrimes * Expand shell metacharacters. At this point, the only control characters 9501556Srgrimes * should be escapes. The results are stored in the list exparg. 9511556Srgrimes */ 9521556Srgrimes 9531556Srgrimeschar *expdir; 9541556Srgrimes 9551556Srgrimes 9561556SrgrimesSTATIC void 9571556Srgrimesexpandmeta(str, flag) 9581556Srgrimes struct strlist *str; 95925905Ssteve int flag __unused; 96017987Speter{ 9611556Srgrimes char *p; 9621556Srgrimes struct strlist **savelastp; 9631556Srgrimes struct strlist *sp; 9641556Srgrimes char c; 9651556Srgrimes /* TODO - EXP_REDIR */ 9661556Srgrimes 9671556Srgrimes while (str) { 9681556Srgrimes if (fflag) 9691556Srgrimes goto nometa; 9701556Srgrimes p = str->text; 9711556Srgrimes for (;;) { /* fast check for meta chars */ 9721556Srgrimes if ((c = *p++) == '\0') 9731556Srgrimes goto nometa; 9741556Srgrimes if (c == '*' || c == '?' || c == '[' || c == '!') 9751556Srgrimes break; 9761556Srgrimes } 9771556Srgrimes savelastp = exparg.lastp; 9781556Srgrimes INTOFF; 9791556Srgrimes if (expdir == NULL) { 9801556Srgrimes int i = strlen(str->text); 9811556Srgrimes expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 9821556Srgrimes } 9831556Srgrimes 9841556Srgrimes expmeta(expdir, str->text); 9851556Srgrimes ckfree(expdir); 9861556Srgrimes expdir = NULL; 9871556Srgrimes INTON; 9881556Srgrimes if (exparg.lastp == savelastp) { 9898855Srgrimes /* 9908855Srgrimes * no matches 9911556Srgrimes */ 9921556Srgrimesnometa: 9931556Srgrimes *exparg.lastp = str; 9941556Srgrimes rmescapes(str->text); 9951556Srgrimes exparg.lastp = &str->next; 9961556Srgrimes } else { 9971556Srgrimes *exparg.lastp = NULL; 9981556Srgrimes *savelastp = sp = expsort(*savelastp); 9991556Srgrimes while (sp->next != NULL) 10001556Srgrimes sp = sp->next; 10011556Srgrimes exparg.lastp = &sp->next; 10021556Srgrimes } 10031556Srgrimes str = str->next; 10041556Srgrimes } 10051556Srgrimes} 10061556Srgrimes 10071556Srgrimes 10081556Srgrimes/* 10091556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion. 10101556Srgrimes */ 10111556Srgrimes 10121556SrgrimesSTATIC void 10131556Srgrimesexpmeta(enddir, name) 10141556Srgrimes char *enddir; 10151556Srgrimes char *name; 10161556Srgrimes { 101725233Ssteve char *p; 10181556Srgrimes char *q; 10191556Srgrimes char *start; 10201556Srgrimes char *endname; 10211556Srgrimes int metaflag; 10221556Srgrimes struct stat statb; 10231556Srgrimes DIR *dirp; 10241556Srgrimes struct dirent *dp; 10251556Srgrimes int atend; 10261556Srgrimes int matchdot; 10271556Srgrimes 10281556Srgrimes metaflag = 0; 10291556Srgrimes start = name; 10301556Srgrimes for (p = name ; ; p++) { 10311556Srgrimes if (*p == '*' || *p == '?') 10321556Srgrimes metaflag = 1; 10331556Srgrimes else if (*p == '[') { 10341556Srgrimes q = p + 1; 103526488Sache if (*q == '!' || *q == '^') 10361556Srgrimes q++; 10371556Srgrimes for (;;) { 10381556Srgrimes if (*q == CTLESC) 10391556Srgrimes q++; 10401556Srgrimes if (*q == '/' || *q == '\0') 10411556Srgrimes break; 10421556Srgrimes if (*++q == ']') { 10431556Srgrimes metaflag = 1; 10441556Srgrimes break; 10451556Srgrimes } 10461556Srgrimes } 10471556Srgrimes } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 10481556Srgrimes metaflag = 1; 10491556Srgrimes } else if (*p == '\0') 10501556Srgrimes break; 10511556Srgrimes else if (*p == CTLESC) 10521556Srgrimes p++; 10531556Srgrimes if (*p == '/') { 10541556Srgrimes if (metaflag) 10551556Srgrimes break; 10561556Srgrimes start = p + 1; 10571556Srgrimes } 10581556Srgrimes } 10591556Srgrimes if (metaflag == 0) { /* we've reached the end of the file name */ 10601556Srgrimes if (enddir != expdir) 10611556Srgrimes metaflag++; 10621556Srgrimes for (p = name ; ; p++) { 10631556Srgrimes if (*p == CTLESC) 10641556Srgrimes p++; 10651556Srgrimes *enddir++ = *p; 10661556Srgrimes if (*p == '\0') 10671556Srgrimes break; 10681556Srgrimes } 10691556Srgrimes if (metaflag == 0 || stat(expdir, &statb) >= 0) 10701556Srgrimes addfname(expdir); 10711556Srgrimes return; 10721556Srgrimes } 10731556Srgrimes endname = p; 10741556Srgrimes if (start != name) { 10751556Srgrimes p = name; 10761556Srgrimes while (p < start) { 10771556Srgrimes if (*p == CTLESC) 10781556Srgrimes p++; 10791556Srgrimes *enddir++ = *p++; 10801556Srgrimes } 10811556Srgrimes } 10821556Srgrimes if (enddir == expdir) { 10831556Srgrimes p = "."; 10841556Srgrimes } else if (enddir == expdir + 1 && *expdir == '/') { 10851556Srgrimes p = "/"; 10861556Srgrimes } else { 10871556Srgrimes p = expdir; 10881556Srgrimes enddir[-1] = '\0'; 10891556Srgrimes } 10901556Srgrimes if ((dirp = opendir(p)) == NULL) 10911556Srgrimes return; 10921556Srgrimes if (enddir != expdir) 10931556Srgrimes enddir[-1] = '/'; 10941556Srgrimes if (*endname == 0) { 10951556Srgrimes atend = 1; 10961556Srgrimes } else { 10971556Srgrimes atend = 0; 10981556Srgrimes *endname++ = '\0'; 10991556Srgrimes } 11001556Srgrimes matchdot = 0; 110117987Speter if (start[0] == '.' || (start[0] == CTLESC && start[1] == '.')) 11021556Srgrimes matchdot++; 11031556Srgrimes while (! int_pending() && (dp = readdir(dirp)) != NULL) { 11041556Srgrimes if (dp->d_name[0] == '.' && ! matchdot) 11051556Srgrimes continue; 11061556Srgrimes if (patmatch(start, dp->d_name)) { 11071556Srgrimes if (atend) { 11081556Srgrimes scopy(dp->d_name, enddir); 11091556Srgrimes addfname(expdir); 11101556Srgrimes } else { 11111556Srgrimes char *q; 111217987Speter for (p = enddir, q = dp->d_name; 111317987Speter (*p++ = *q++) != '\0';) 111417987Speter continue; 11151556Srgrimes p[-1] = '/'; 11161556Srgrimes expmeta(p, endname); 11171556Srgrimes } 11181556Srgrimes } 11191556Srgrimes } 11201556Srgrimes closedir(dirp); 11211556Srgrimes if (! atend) 11221556Srgrimes endname[-1] = '/'; 11231556Srgrimes} 11241556Srgrimes 11251556Srgrimes 11261556Srgrimes/* 11271556Srgrimes * Add a file name to the list. 11281556Srgrimes */ 11291556Srgrimes 11301556SrgrimesSTATIC void 11311556Srgrimesaddfname(name) 11321556Srgrimes char *name; 11331556Srgrimes { 11341556Srgrimes char *p; 11351556Srgrimes struct strlist *sp; 11361556Srgrimes 11371556Srgrimes p = stalloc(strlen(name) + 1); 11381556Srgrimes scopy(name, p); 11391556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 11401556Srgrimes sp->text = p; 11411556Srgrimes *exparg.lastp = sp; 11421556Srgrimes exparg.lastp = &sp->next; 11431556Srgrimes} 11441556Srgrimes 11451556Srgrimes 11461556Srgrimes/* 11471556Srgrimes * Sort the results of file name expansion. It calculates the number of 11481556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the 11491556Srgrimes * work. 11501556Srgrimes */ 11511556Srgrimes 11521556SrgrimesSTATIC struct strlist * 11531556Srgrimesexpsort(str) 11541556Srgrimes struct strlist *str; 11551556Srgrimes { 11561556Srgrimes int len; 11571556Srgrimes struct strlist *sp; 11581556Srgrimes 11591556Srgrimes len = 0; 11601556Srgrimes for (sp = str ; sp ; sp = sp->next) 11611556Srgrimes len++; 11621556Srgrimes return msort(str, len); 11631556Srgrimes} 11641556Srgrimes 11651556Srgrimes 11661556SrgrimesSTATIC struct strlist * 11671556Srgrimesmsort(list, len) 11681556Srgrimes struct strlist *list; 116917987Speter int len; 117017987Speter{ 117117987Speter struct strlist *p, *q = NULL; 11721556Srgrimes struct strlist **lpp; 11731556Srgrimes int half; 11741556Srgrimes int n; 11751556Srgrimes 11761556Srgrimes if (len <= 1) 11771556Srgrimes return list; 11788855Srgrimes half = len >> 1; 11791556Srgrimes p = list; 11801556Srgrimes for (n = half ; --n >= 0 ; ) { 11811556Srgrimes q = p; 11821556Srgrimes p = p->next; 11831556Srgrimes } 11841556Srgrimes q->next = NULL; /* terminate first half of list */ 11851556Srgrimes q = msort(list, half); /* sort first half of list */ 11861556Srgrimes p = msort(p, len - half); /* sort second half */ 11871556Srgrimes lpp = &list; 11881556Srgrimes for (;;) { 11891556Srgrimes if (strcmp(p->text, q->text) < 0) { 11901556Srgrimes *lpp = p; 11911556Srgrimes lpp = &p->next; 11921556Srgrimes if ((p = *lpp) == NULL) { 11931556Srgrimes *lpp = q; 11941556Srgrimes break; 11951556Srgrimes } 11961556Srgrimes } else { 11971556Srgrimes *lpp = q; 11981556Srgrimes lpp = &q->next; 11991556Srgrimes if ((q = *lpp) == NULL) { 12001556Srgrimes *lpp = p; 12011556Srgrimes break; 12021556Srgrimes } 12031556Srgrimes } 12041556Srgrimes } 12051556Srgrimes return list; 12061556Srgrimes} 12071556Srgrimes 12081556Srgrimes 12091556Srgrimes 12101556Srgrimes/* 12111556Srgrimes * Returns true if the pattern matches the string. 12121556Srgrimes */ 12131556Srgrimes 12141556Srgrimesint 12151556Srgrimespatmatch(pattern, string) 12161556Srgrimes char *pattern; 12171556Srgrimes char *string; 12181556Srgrimes { 12191556Srgrimes#ifdef notdef 12201556Srgrimes if (pattern[0] == '!' && pattern[1] == '!') 12211556Srgrimes return 1 - pmatch(pattern + 2, string); 12221556Srgrimes else 12231556Srgrimes#endif 12241556Srgrimes return pmatch(pattern, string); 12251556Srgrimes} 12261556Srgrimes 122717987Speter 122817525SacheSTATIC int 12291556Srgrimespmatch(pattern, string) 12301556Srgrimes char *pattern; 12311556Srgrimes char *string; 12321556Srgrimes { 123325233Ssteve char *p, *q; 123425233Ssteve char c; 12351556Srgrimes 12361556Srgrimes p = pattern; 12371556Srgrimes q = string; 12381556Srgrimes for (;;) { 12391556Srgrimes switch (c = *p++) { 12401556Srgrimes case '\0': 12411556Srgrimes goto breakloop; 12421556Srgrimes case CTLESC: 12431556Srgrimes if (*q++ != *p++) 12441556Srgrimes return 0; 12451556Srgrimes break; 12461556Srgrimes case '?': 12471556Srgrimes if (*q++ == '\0') 12481556Srgrimes return 0; 12491556Srgrimes break; 12501556Srgrimes case '*': 12511556Srgrimes c = *p; 12521556Srgrimes if (c != CTLESC && c != '?' && c != '*' && c != '[') { 12531556Srgrimes while (*q != c) { 12541556Srgrimes if (*q == '\0') 12551556Srgrimes return 0; 12561556Srgrimes q++; 12571556Srgrimes } 12581556Srgrimes } 12591556Srgrimes do { 12601556Srgrimes if (pmatch(p, q)) 12611556Srgrimes return 1; 12621556Srgrimes } while (*q++ != '\0'); 12631556Srgrimes return 0; 12641556Srgrimes case '[': { 12651556Srgrimes char *endp; 12661556Srgrimes int invert, found; 12671556Srgrimes char chr; 12681556Srgrimes 12691556Srgrimes endp = p; 127026488Sache if (*endp == '!' || *endp == '^') 12711556Srgrimes endp++; 12721556Srgrimes for (;;) { 12731556Srgrimes if (*endp == '\0') 12741556Srgrimes goto dft; /* no matching ] */ 12751556Srgrimes if (*endp == CTLESC) 12761556Srgrimes endp++; 12771556Srgrimes if (*++endp == ']') 12781556Srgrimes break; 12791556Srgrimes } 12801556Srgrimes invert = 0; 128126488Sache if (*p == '!' || *p == '^') { 12821556Srgrimes invert++; 12831556Srgrimes p++; 12841556Srgrimes } 12851556Srgrimes found = 0; 12861556Srgrimes chr = *q++; 128717987Speter if (chr == '\0') 128817987Speter return 0; 12891556Srgrimes c = *p++; 12901556Srgrimes do { 12911556Srgrimes if (c == CTLESC) 12921556Srgrimes c = *p++; 12931556Srgrimes if (*p == '-' && p[1] != ']') { 12941556Srgrimes p++; 12951556Srgrimes if (*p == CTLESC) 12961556Srgrimes p++; 129717557Sache if ( collate_range_cmp(chr, c) >= 0 129817557Sache && collate_range_cmp(chr, *p) <= 0 129917525Sache ) 13001556Srgrimes found = 1; 13011556Srgrimes p++; 13021556Srgrimes } else { 13031556Srgrimes if (chr == c) 13041556Srgrimes found = 1; 13051556Srgrimes } 13061556Srgrimes } while ((c = *p++) != ']'); 13071556Srgrimes if (found == invert) 13081556Srgrimes return 0; 13091556Srgrimes break; 13101556Srgrimes } 13111556Srgrimesdft: default: 13121556Srgrimes if (*q++ != c) 13131556Srgrimes return 0; 13141556Srgrimes break; 13151556Srgrimes } 13161556Srgrimes } 13171556Srgrimesbreakloop: 13181556Srgrimes if (*q != '\0') 13191556Srgrimes return 0; 13201556Srgrimes return 1; 13211556Srgrimes} 13221556Srgrimes 13231556Srgrimes 13241556Srgrimes 13251556Srgrimes/* 13261556Srgrimes * Remove any CTLESC characters from a string. 13271556Srgrimes */ 13281556Srgrimes 13291556Srgrimesvoid 13301556Srgrimesrmescapes(str) 13311556Srgrimes char *str; 13321556Srgrimes { 133325233Ssteve char *p, *q; 13341556Srgrimes 13351556Srgrimes p = str; 13361556Srgrimes while (*p != CTLESC) { 13371556Srgrimes if (*p++ == '\0') 13381556Srgrimes return; 13391556Srgrimes } 13401556Srgrimes q = p; 13411556Srgrimes while (*p) { 13421556Srgrimes if (*p == CTLESC) 13431556Srgrimes p++; 13441556Srgrimes *q++ = *p++; 13451556Srgrimes } 13461556Srgrimes *q = '\0'; 13471556Srgrimes} 13481556Srgrimes 13491556Srgrimes 13501556Srgrimes 13511556Srgrimes/* 13521556Srgrimes * See if a pattern matches in a case statement. 13531556Srgrimes */ 13541556Srgrimes 13551556Srgrimesint 13561556Srgrimescasematch(pattern, val) 13571556Srgrimes union node *pattern; 13581556Srgrimes char *val; 13591556Srgrimes { 13601556Srgrimes struct stackmark smark; 13611556Srgrimes int result; 13621556Srgrimes char *p; 13631556Srgrimes 13641556Srgrimes setstackmark(&smark); 13651556Srgrimes argbackq = pattern->narg.backquote; 13661556Srgrimes STARTSTACKSTR(expdest); 13671556Srgrimes ifslastp = NULL; 13681556Srgrimes argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 13691556Srgrimes STPUTC('\0', expdest); 13701556Srgrimes p = grabstackstr(expdest); 13711556Srgrimes result = patmatch(p, val); 13721556Srgrimes popstackmark(&smark); 13731556Srgrimes return result; 13741556Srgrimes} 137517987Speter 137617987Speter/* 137717987Speter * Our own itoa(). 137817987Speter */ 137917987Speter 138017987SpeterSTATIC char * 138117987Spetercvtnum(num, buf) 138217987Speter int num; 138317987Speter char *buf; 138417987Speter { 138517987Speter char temp[32]; 138617987Speter int neg = num < 0; 138717987Speter char *p = temp + 31; 138817987Speter 138917987Speter temp[31] = '\0'; 139017987Speter 139117987Speter do { 139217987Speter *--p = num % 10 + '0'; 139317987Speter } while ((num /= 10) != 0); 139417987Speter 139517987Speter if (neg) 139617987Speter *--p = '-'; 139717987Speter 139817987Speter while (*p) 139917987Speter STPUTC(*p++, buf); 140017987Speter return buf; 140117987Speter} 1402