expand.c revision 104672
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. 351556Srgrimes */ 361556Srgrimes 371556Srgrimes#ifndef lint 3836150Scharnier#if 0 3936150Scharnierstatic char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; 4036150Scharnier#endif 411556Srgrimes#endif /* not lint */ 4299110Sobrien#include <sys/cdefs.h> 4399110Sobrien__FBSDID("$FreeBSD: head/bin/sh/expand.c 104672 2002-10-08 11:22:49Z tjr $"); 441556Srgrimes 4517987Speter#include <sys/types.h> 4617987Speter#include <sys/time.h> 4717987Speter#include <sys/stat.h> 4817987Speter#include <errno.h> 4917987Speter#include <dirent.h> 5017987Speter#include <unistd.h> 5117987Speter#include <pwd.h> 5217987Speter#include <stdlib.h> 5319281Sache#include <limits.h> 5438887Stegge#include <stdio.h> 5517987Speter 561556Srgrimes/* 571556Srgrimes * Routines to expand arguments to commands. We have to deal with 581556Srgrimes * backquotes, shell variables, and file metacharacters. 591556Srgrimes */ 601556Srgrimes 611556Srgrimes#include "shell.h" 621556Srgrimes#include "main.h" 631556Srgrimes#include "nodes.h" 641556Srgrimes#include "eval.h" 651556Srgrimes#include "expand.h" 661556Srgrimes#include "syntax.h" 671556Srgrimes#include "parser.h" 681556Srgrimes#include "jobs.h" 691556Srgrimes#include "options.h" 701556Srgrimes#include "var.h" 711556Srgrimes#include "input.h" 721556Srgrimes#include "output.h" 731556Srgrimes#include "memalloc.h" 741556Srgrimes#include "error.h" 751556Srgrimes#include "mystring.h" 7617987Speter#include "arith.h" 7717987Speter#include "show.h" 781556Srgrimes 791556Srgrimes/* 801556Srgrimes * Structure specifying which parts of the string should be searched 811556Srgrimes * for IFS characters. 821556Srgrimes */ 831556Srgrimes 841556Srgrimesstruct ifsregion { 851556Srgrimes struct ifsregion *next; /* next region in list */ 861556Srgrimes int begoff; /* offset of start of region */ 871556Srgrimes int endoff; /* offset of end of region */ 881556Srgrimes int nulonly; /* search for nul bytes only */ 891556Srgrimes}; 901556Srgrimes 911556Srgrimes 921556Srgrimeschar *expdest; /* output of current string */ 931556Srgrimesstruct nodelist *argbackq; /* list of back quote expressions */ 941556Srgrimesstruct ifsregion ifsfirst; /* first struct in list of ifs regions */ 951556Srgrimesstruct ifsregion *ifslastp; /* last struct in list */ 961556Srgrimesstruct arglist exparg; /* holds expanded arg list */ 971556Srgrimes 9890111SimpSTATIC void argstr(char *, int); 9990111SimpSTATIC char *exptilde(char *, int); 10090111SimpSTATIC void expbackq(union node *, int, int); 10190111SimpSTATIC int subevalvar(char *, char *, int, int, int, int); 10290111SimpSTATIC char *evalvar(char *, int); 10390111SimpSTATIC int varisset(char *, int); 10490111SimpSTATIC void varvalue(char *, int, int); 10590111SimpSTATIC void recordregion(int, int, int); 10690111SimpSTATIC void removerecordregions(int); 10790111SimpSTATIC void ifsbreakup(char *, struct arglist *); 10890111SimpSTATIC void expandmeta(struct strlist *, int); 10990111SimpSTATIC void expmeta(char *, char *); 11090111SimpSTATIC void addfname(char *); 11190111SimpSTATIC struct strlist *expsort(struct strlist *); 11290111SimpSTATIC struct strlist *msort(struct strlist *, int); 11390111SimpSTATIC int pmatch(char *, char *, int); 11490111SimpSTATIC char *cvtnum(int, char *); 11590111SimpSTATIC int collate_range_cmp(int, int); 1161556Srgrimes 11790111SimpSTATIC int 11890111Simpcollate_range_cmp (int c1, int c2) 11919281Sache{ 12019281Sache static char s1[2], s2[2]; 12119281Sache int ret; 12219281Sache 12319281Sache c1 &= UCHAR_MAX; 12419281Sache c2 &= UCHAR_MAX; 12519281Sache if (c1 == c2) 12619281Sache return (0); 12719281Sache s1[0] = c1; 12819281Sache s2[0] = c2; 12919281Sache if ((ret = strcoll(s1, s2)) != 0) 13019281Sache return (ret); 13119281Sache return (c1 - c2); 13219281Sache} 13319281Sache 1341556Srgrimes/* 1351556Srgrimes * Expand shell variables and backquotes inside a here document. 13690111Simp * union node *arg the document 13790111Simp * int fd; where to write the expanded version 1381556Srgrimes */ 1391556Srgrimes 1401556Srgrimesvoid 14190111Simpexpandhere(union node *arg, int fd) 14290111Simp{ 1431556Srgrimes herefd = fd; 1441556Srgrimes expandarg(arg, (struct arglist *)NULL, 0); 14539137Stegge xwrite(fd, stackblock(), expdest - stackblock()); 1461556Srgrimes} 1471556Srgrimes 1481556Srgrimes 1491556Srgrimes/* 1501556Srgrimes * Perform variable substitution and command substitution on an argument, 1511556Srgrimes * placing the resulting list of arguments in arglist. If EXP_FULL is true, 1521556Srgrimes * perform splitting and file name expansion. When arglist is NULL, perform 1531556Srgrimes * here document expansion. 1541556Srgrimes */ 1551556Srgrimes 1561556Srgrimesvoid 15790111Simpexpandarg(union node *arg, struct arglist *arglist, int flag) 15817987Speter{ 1591556Srgrimes struct strlist *sp; 1601556Srgrimes char *p; 1611556Srgrimes 1621556Srgrimes argbackq = arg->narg.backquote; 1631556Srgrimes STARTSTACKSTR(expdest); 1641556Srgrimes ifsfirst.next = NULL; 1651556Srgrimes ifslastp = NULL; 1661556Srgrimes argstr(arg->narg.text, flag); 1671556Srgrimes if (arglist == NULL) { 1681556Srgrimes return; /* here document expanded */ 1691556Srgrimes } 1701556Srgrimes STPUTC('\0', expdest); 1711556Srgrimes p = grabstackstr(expdest); 1721556Srgrimes exparg.lastp = &exparg.list; 1731556Srgrimes /* 1741556Srgrimes * TODO - EXP_REDIR 1751556Srgrimes */ 1761556Srgrimes if (flag & EXP_FULL) { 1771556Srgrimes ifsbreakup(p, &exparg); 1781556Srgrimes *exparg.lastp = NULL; 1791556Srgrimes exparg.lastp = &exparg.list; 1801556Srgrimes expandmeta(exparg.list, flag); 1811556Srgrimes } else { 1821556Srgrimes if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 1831556Srgrimes rmescapes(p); 1841556Srgrimes sp = (struct strlist *)stalloc(sizeof (struct strlist)); 1851556Srgrimes sp->text = p; 1861556Srgrimes *exparg.lastp = sp; 1871556Srgrimes exparg.lastp = &sp->next; 1881556Srgrimes } 1891556Srgrimes while (ifsfirst.next != NULL) { 1901556Srgrimes struct ifsregion *ifsp; 1911556Srgrimes INTOFF; 1921556Srgrimes ifsp = ifsfirst.next->next; 1931556Srgrimes ckfree(ifsfirst.next); 1941556Srgrimes ifsfirst.next = ifsp; 1951556Srgrimes INTON; 1961556Srgrimes } 1971556Srgrimes *exparg.lastp = NULL; 1981556Srgrimes if (exparg.list) { 1991556Srgrimes *arglist->lastp = exparg.list; 2001556Srgrimes arglist->lastp = exparg.lastp; 2011556Srgrimes } 2021556Srgrimes} 2031556Srgrimes 2041556Srgrimes 2051556Srgrimes 2061556Srgrimes/* 2071556Srgrimes * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 2081556Srgrimes * characters to allow for further processing. Otherwise treat 2091556Srgrimes * $@ like $* since no splitting will be performed. 2101556Srgrimes */ 2111556Srgrimes 2121556SrgrimesSTATIC void 21390111Simpargstr(char *p, int flag) 21417987Speter{ 21525233Ssteve char c; 216104672Stjr int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ 2171556Srgrimes int firsteq = 1; 2181556Srgrimes 2191556Srgrimes if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 2201556Srgrimes p = exptilde(p, flag); 2211556Srgrimes for (;;) { 2221556Srgrimes switch (c = *p++) { 2231556Srgrimes case '\0': 2241556Srgrimes case CTLENDVAR: /* ??? */ 2251556Srgrimes goto breakloop; 22638887Stegge case CTLQUOTEMARK: 22738887Stegge /* "$@" syntax adherence hack */ 22838887Stegge if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') 22938887Stegge break; 23039137Stegge if ((flag & EXP_FULL) != 0) 23139137Stegge STPUTC(c, expdest); 23238887Stegge break; 2331556Srgrimes case CTLESC: 2341556Srgrimes if (quotes) 2351556Srgrimes STPUTC(c, expdest); 2361556Srgrimes c = *p++; 2371556Srgrimes STPUTC(c, expdest); 2381556Srgrimes break; 2391556Srgrimes case CTLVAR: 2401556Srgrimes p = evalvar(p, flag); 2411556Srgrimes break; 2421556Srgrimes case CTLBACKQ: 2431556Srgrimes case CTLBACKQ|CTLQUOTE: 2441556Srgrimes expbackq(argbackq->n, c & CTLQUOTE, flag); 2451556Srgrimes argbackq = argbackq->next; 2461556Srgrimes break; 2471556Srgrimes case CTLENDARI: 2481556Srgrimes expari(flag); 2491556Srgrimes break; 2501556Srgrimes case ':': 2511556Srgrimes case '=': 2521556Srgrimes /* 2531556Srgrimes * sort of a hack - expand tildes in variable 2541556Srgrimes * assignments (after the first '=' and after ':'s). 2551556Srgrimes */ 2561556Srgrimes STPUTC(c, expdest); 2571556Srgrimes if (flag & EXP_VARTILDE && *p == '~') { 2581556Srgrimes if (c == '=') { 2591556Srgrimes if (firsteq) 2601556Srgrimes firsteq = 0; 2611556Srgrimes else 2621556Srgrimes break; 2631556Srgrimes } 2641556Srgrimes p = exptilde(p, flag); 2651556Srgrimes } 2661556Srgrimes break; 2671556Srgrimes default: 2681556Srgrimes STPUTC(c, expdest); 2691556Srgrimes } 2701556Srgrimes } 2711556Srgrimesbreakloop:; 2721556Srgrimes} 2731556Srgrimes 2741556SrgrimesSTATIC char * 27590111Simpexptilde(char *p, int flag) 27617987Speter{ 2771556Srgrimes char c, *startp = p; 2781556Srgrimes struct passwd *pw; 2791556Srgrimes char *home; 2801556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 2811556Srgrimes 28217987Speter while ((c = *p) != '\0') { 2831556Srgrimes switch(c) { 2841556Srgrimes case CTLESC: 2851556Srgrimes return (startp); 28639137Stegge case CTLQUOTEMARK: 28739137Stegge return (startp); 2881556Srgrimes case ':': 2891556Srgrimes if (flag & EXP_VARTILDE) 2901556Srgrimes goto done; 2911556Srgrimes break; 2921556Srgrimes case '/': 2931556Srgrimes goto done; 2941556Srgrimes } 2951556Srgrimes p++; 2961556Srgrimes } 2971556Srgrimesdone: 2981556Srgrimes *p = '\0'; 2991556Srgrimes if (*(startp+1) == '\0') { 3001556Srgrimes if ((home = lookupvar("HOME")) == NULL) 3011556Srgrimes goto lose; 3021556Srgrimes } else { 3031556Srgrimes if ((pw = getpwnam(startp+1)) == NULL) 3041556Srgrimes goto lose; 3051556Srgrimes home = pw->pw_dir; 3061556Srgrimes } 3071556Srgrimes if (*home == '\0') 3081556Srgrimes goto lose; 3091556Srgrimes *p = c; 31017987Speter while ((c = *home++) != '\0') { 31183675Stegge if (quotes && SQSYNTAX[(int)c] == CCTL) 3121556Srgrimes STPUTC(CTLESC, expdest); 3131556Srgrimes STPUTC(c, expdest); 3141556Srgrimes } 3151556Srgrimes return (p); 3161556Srgrimeslose: 3171556Srgrimes *p = c; 3181556Srgrimes return (startp); 3191556Srgrimes} 3201556Srgrimes 3211556Srgrimes 32238887SteggeSTATIC void 32390111Simpremoverecordregions(int endoff) 32438887Stegge{ 32538887Stegge if (ifslastp == NULL) 32638887Stegge return; 32738887Stegge 32838887Stegge if (ifsfirst.endoff > endoff) { 32938887Stegge while (ifsfirst.next != NULL) { 33038887Stegge struct ifsregion *ifsp; 33138887Stegge INTOFF; 33238887Stegge ifsp = ifsfirst.next->next; 33338887Stegge ckfree(ifsfirst.next); 33438887Stegge ifsfirst.next = ifsp; 33538887Stegge INTON; 33638887Stegge } 33738887Stegge if (ifsfirst.begoff > endoff) 33838887Stegge ifslastp = NULL; 33938887Stegge else { 34038887Stegge ifslastp = &ifsfirst; 34138887Stegge ifsfirst.endoff = endoff; 34238887Stegge } 34338887Stegge return; 34438887Stegge } 34538887Stegge 34638887Stegge ifslastp = &ifsfirst; 34738887Stegge while (ifslastp->next && ifslastp->next->begoff < endoff) 34838887Stegge ifslastp=ifslastp->next; 34938887Stegge while (ifslastp->next != NULL) { 35038887Stegge struct ifsregion *ifsp; 35138887Stegge INTOFF; 35238887Stegge ifsp = ifslastp->next->next; 35338887Stegge ckfree(ifslastp->next); 35438887Stegge ifslastp->next = ifsp; 35538887Stegge INTON; 35638887Stegge } 35738887Stegge if (ifslastp->endoff > endoff) 35838887Stegge ifslastp->endoff = endoff; 35938887Stegge} 36038887Stegge 3611556Srgrimes/* 3621556Srgrimes * Expand arithmetic expression. Backup to start of expression, 3631556Srgrimes * evaluate, place result in (backed up) result, adjust string position. 3641556Srgrimes */ 3651556Srgrimesvoid 36690111Simpexpari(int flag) 36717987Speter{ 3681556Srgrimes char *p, *start; 3691556Srgrimes int result; 37038887Stegge int begoff; 3711556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 37238887Stegge int quoted; 3731556Srgrimes 37425233Ssteve 3751556Srgrimes /* 37646684Skris * This routine is slightly over-complicated for 3771556Srgrimes * efficiency. First we make sure there is 3781556Srgrimes * enough space for the result, which may be bigger 37946684Skris * than the expression if we add exponentiation. Next we 3801556Srgrimes * scan backwards looking for the start of arithmetic. If the 3811556Srgrimes * next previous character is a CTLESC character, then we 3821556Srgrimes * have to rescan starting from the beginning since CTLESC 3838855Srgrimes * characters have to be processed left to right. 3841556Srgrimes */ 38522777Ssteve#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10 38622777Ssteve#error "integers with more than 10 digits are not supported" 38722777Ssteve#endif 38822777Ssteve CHECKSTRSPACE(12 - 2, expdest); 3898855Srgrimes USTPUTC('\0', expdest); 3901556Srgrimes start = stackblock(); 39183676Stegge p = expdest - 2; 39283676Stegge while (p >= start && *p != CTLARI) 3931556Srgrimes --p; 39483676Stegge if (p < start || *p != CTLARI) 3951556Srgrimes error("missing CTLARI (shouldn't happen)"); 39683676Stegge if (p > start && *(p - 1) == CTLESC) 3971556Srgrimes for (p = start; *p != CTLARI; p++) 3981556Srgrimes if (*p == CTLESC) 3991556Srgrimes p++; 40038887Stegge 40138887Stegge if (p[1] == '"') 40238887Stegge quoted=1; 40338887Stegge else 40438887Stegge quoted=0; 40538887Stegge begoff = p - start; 40638887Stegge removerecordregions(begoff); 4071556Srgrimes if (quotes) 40838887Stegge rmescapes(p+2); 40938887Stegge result = arith(p+2); 41022777Ssteve fmtstr(p, 12, "%d", result); 4111556Srgrimes while (*p++) 4121556Srgrimes ; 41338887Stegge if (quoted == 0) 41438887Stegge recordregion(begoff, p - 1 - start, 0); 4151556Srgrimes result = expdest - p + 1; 4161556Srgrimes STADJUST(-result, expdest); 4171556Srgrimes} 4181556Srgrimes 4191556Srgrimes 4201556Srgrimes/* 4211556Srgrimes * Expand stuff in backwards quotes. 4221556Srgrimes */ 4231556Srgrimes 4241556SrgrimesSTATIC void 42590111Simpexpbackq(union node *cmd, int quoted, int flag) 42617987Speter{ 4271556Srgrimes struct backcmd in; 4281556Srgrimes int i; 4291556Srgrimes char buf[128]; 4301556Srgrimes char *p; 4311556Srgrimes char *dest = expdest; 4321556Srgrimes struct ifsregion saveifs, *savelastp; 4331556Srgrimes struct nodelist *saveargbackq; 4341556Srgrimes char lastc; 4351556Srgrimes int startloc = dest - stackblock(); 4361556Srgrimes char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 4371556Srgrimes int saveherefd; 4381556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 4391556Srgrimes 4401556Srgrimes INTOFF; 4411556Srgrimes saveifs = ifsfirst; 4421556Srgrimes savelastp = ifslastp; 4431556Srgrimes saveargbackq = argbackq; 4448855Srgrimes saveherefd = herefd; 4451556Srgrimes herefd = -1; 4461556Srgrimes p = grabstackstr(dest); 4471556Srgrimes evalbackcmd(cmd, &in); 4481556Srgrimes ungrabstackstr(p, dest); 4491556Srgrimes ifsfirst = saveifs; 4501556Srgrimes ifslastp = savelastp; 4511556Srgrimes argbackq = saveargbackq; 4521556Srgrimes herefd = saveherefd; 4531556Srgrimes 4541556Srgrimes p = in.buf; 4551556Srgrimes lastc = '\0'; 4561556Srgrimes for (;;) { 4571556Srgrimes if (--in.nleft < 0) { 4581556Srgrimes if (in.fd < 0) 4591556Srgrimes break; 4601556Srgrimes while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 4611556Srgrimes TRACE(("expbackq: read returns %d\n", i)); 4621556Srgrimes if (i <= 0) 4631556Srgrimes break; 4641556Srgrimes p = buf; 4651556Srgrimes in.nleft = i - 1; 4661556Srgrimes } 4671556Srgrimes lastc = *p++; 4681556Srgrimes if (lastc != '\0') { 46983675Stegge if (quotes && syntax[(int)lastc] == CCTL) 4701556Srgrimes STPUTC(CTLESC, dest); 4711556Srgrimes STPUTC(lastc, dest); 4721556Srgrimes } 4731556Srgrimes } 47417987Speter 47517987Speter /* Eat all trailing newlines */ 47617987Speter for (p--; lastc == '\n'; lastc = *--p) 4771556Srgrimes STUNPUTC(dest); 47817987Speter 4791556Srgrimes if (in.fd >= 0) 4801556Srgrimes close(in.fd); 4811556Srgrimes if (in.buf) 4821556Srgrimes ckfree(in.buf); 4831556Srgrimes if (in.jp) 48445916Scracauer exitstatus = waitforjob(in.jp, (int *)NULL); 4851556Srgrimes if (quoted == 0) 4861556Srgrimes recordregion(startloc, dest - stackblock(), 0); 4871556Srgrimes TRACE(("evalbackq: size=%d: \"%.*s\"\n", 4881556Srgrimes (dest - stackblock()) - startloc, 4891556Srgrimes (dest - stackblock()) - startloc, 4901556Srgrimes stackblock() + startloc)); 4911556Srgrimes expdest = dest; 4921556Srgrimes INTON; 4931556Srgrimes} 4941556Srgrimes 4951556Srgrimes 4961556Srgrimes 49717987SpeterSTATIC int 49890111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc, 49990111Simp int varflags) 50017987Speter{ 50117987Speter char *startp; 50217987Speter char *loc = NULL; 50345514Stegge char *q; 50417987Speter int c = 0; 50517987Speter int saveherefd = herefd; 50617987Speter struct nodelist *saveargbackq = argbackq; 50720425Ssteve int amount; 50820425Ssteve 50917987Speter herefd = -1; 51017987Speter argstr(p, 0); 51117987Speter STACKSTRNUL(expdest); 51217987Speter herefd = saveherefd; 51317987Speter argbackq = saveargbackq; 51417987Speter startp = stackblock() + startloc; 51520425Ssteve if (str == NULL) 51620425Ssteve str = stackblock() + strloc; 51717987Speter 51817987Speter switch (subtype) { 51917987Speter case VSASSIGN: 52017987Speter setvar(str, startp, 0); 52120425Ssteve amount = startp - expdest; 52220425Ssteve STADJUST(amount, expdest); 52317987Speter varflags &= ~VSNUL; 52417987Speter if (c != 0) 52517987Speter *loc = c; 52617987Speter return 1; 52717987Speter 52817987Speter case VSQUESTION: 52917987Speter if (*p != CTLENDVAR) { 53017987Speter outfmt(&errout, "%s\n", startp); 53117987Speter error((char *)NULL); 53217987Speter } 53317987Speter error("%.*s: parameter %snot set", p - str - 1, 53417987Speter str, (varflags & VSNUL) ? "null or " 53517987Speter : nullstr); 53617987Speter return 0; 53717987Speter 53817987Speter case VSTRIMLEFT: 53925233Ssteve for (loc = startp; loc < str; loc++) { 54017987Speter c = *loc; 54117987Speter *loc = '\0'; 54245514Stegge if (patmatch(str, startp, varflags & VSQUOTE)) { 54317987Speter *loc = c; 54417987Speter goto recordleft; 54517987Speter } 54617987Speter *loc = c; 54745514Stegge if ((varflags & VSQUOTE) && *loc == CTLESC) 54845514Stegge loc++; 54917987Speter } 55017987Speter return 0; 55117987Speter 55217987Speter case VSTRIMLEFTMAX: 55345514Stegge for (loc = str - 1; loc >= startp;) { 55417987Speter c = *loc; 55517987Speter *loc = '\0'; 55645514Stegge if (patmatch(str, startp, varflags & VSQUOTE)) { 55717987Speter *loc = c; 55817987Speter goto recordleft; 55917987Speter } 56017987Speter *loc = c; 56145514Stegge loc--; 56245514Stegge if ((varflags & VSQUOTE) && loc > startp && 56345514Stegge *(loc - 1) == CTLESC) { 56445514Stegge for (q = startp; q < loc; q++) 56545514Stegge if (*q == CTLESC) 56645514Stegge q++; 56745514Stegge if (q > loc) 56845514Stegge loc--; 56945514Stegge } 57017987Speter } 57117987Speter return 0; 57217987Speter 57317987Speter case VSTRIMRIGHT: 57445514Stegge for (loc = str - 1; loc >= startp;) { 57545514Stegge if (patmatch(str, loc, varflags & VSQUOTE)) { 57620425Ssteve amount = loc - expdest; 57720425Ssteve STADJUST(amount, expdest); 57817987Speter return 1; 57917987Speter } 58045514Stegge loc--; 58145514Stegge if ((varflags & VSQUOTE) && loc > startp && 58245514Stegge *(loc - 1) == CTLESC) { 58345514Stegge for (q = startp; q < loc; q++) 58445514Stegge if (*q == CTLESC) 58545514Stegge q++; 58645514Stegge if (q > loc) 58745514Stegge loc--; 58845514Stegge } 58917987Speter } 59017987Speter return 0; 59117987Speter 59217987Speter case VSTRIMRIGHTMAX: 59317987Speter for (loc = startp; loc < str - 1; loc++) { 59445514Stegge if (patmatch(str, loc, varflags & VSQUOTE)) { 59520425Ssteve amount = loc - expdest; 59620425Ssteve STADJUST(amount, expdest); 59717987Speter return 1; 59817987Speter } 59945514Stegge if ((varflags & VSQUOTE) && *loc == CTLESC) 60045514Stegge loc++; 60117987Speter } 60217987Speter return 0; 60317987Speter 60417987Speter 60517987Speter default: 60617987Speter abort(); 60717987Speter } 60817987Speter 60917987Speterrecordleft: 61020425Ssteve amount = ((str - 1) - (loc - startp)) - expdest; 61120425Ssteve STADJUST(amount, expdest); 61217987Speter while (loc != str - 1) 61317987Speter *startp++ = *loc++; 61417987Speter return 1; 61517987Speter} 61617987Speter 61717987Speter 6181556Srgrimes/* 6191556Srgrimes * Expand a variable, and return a pointer to the next character in the 6201556Srgrimes * input string. 6211556Srgrimes */ 6221556Srgrimes 6231556SrgrimesSTATIC char * 62490111Simpevalvar(char *p, int flag) 62517987Speter{ 6261556Srgrimes int subtype; 6271556Srgrimes int varflags; 6281556Srgrimes char *var; 6291556Srgrimes char *val; 63045644Stegge int patloc; 6311556Srgrimes int c; 6321556Srgrimes int set; 6331556Srgrimes int special; 6341556Srgrimes int startloc; 63517987Speter int varlen; 63617987Speter int easy; 6371556Srgrimes int quotes = flag & (EXP_FULL | EXP_CASE); 6381556Srgrimes 6391556Srgrimes varflags = *p++; 6401556Srgrimes subtype = varflags & VSTYPE; 6411556Srgrimes var = p; 6421556Srgrimes special = 0; 6431556Srgrimes if (! is_name(*p)) 6441556Srgrimes special = 1; 6451556Srgrimes p = strchr(p, '=') + 1; 6461556Srgrimesagain: /* jump here after setting a variable with ${var=text} */ 6471556Srgrimes if (special) { 64825233Ssteve set = varisset(var, varflags & VSNUL); 6491556Srgrimes val = NULL; 6501556Srgrimes } else { 65160592Scracauer val = bltinlookup(var, 1); 65217987Speter if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 6531556Srgrimes val = NULL; 6541556Srgrimes set = 0; 6551556Srgrimes } else 6561556Srgrimes set = 1; 6571556Srgrimes } 65817987Speter varlen = 0; 6591556Srgrimes startloc = expdest - stackblock(); 66096939Stjr if (!set && uflag) { 66196939Stjr switch (subtype) { 66296939Stjr case VSNORMAL: 66396939Stjr case VSTRIMLEFT: 66496939Stjr case VSTRIMLEFTMAX: 66596939Stjr case VSTRIMRIGHT: 66696939Stjr case VSTRIMRIGHTMAX: 66796939Stjr case VSLENGTH: 66896939Stjr error("%.*s: parameter not set", p - var - 1, var); 66996939Stjr } 67096939Stjr } 6711556Srgrimes if (set && subtype != VSPLUS) { 6721556Srgrimes /* insert the value of the variable */ 6731556Srgrimes if (special) { 67418202Speter varvalue(var, varflags & VSQUOTE, flag & EXP_FULL); 67517987Speter if (subtype == VSLENGTH) { 67625233Ssteve varlen = expdest - stackblock() - startloc; 67725233Ssteve STADJUST(-varlen, expdest); 67817987Speter } 6791556Srgrimes } else { 68020425Ssteve char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 68117987Speter : BASESYNTAX; 6821556Srgrimes 68317987Speter if (subtype == VSLENGTH) { 68417987Speter for (;*val; val++) 68517987Speter varlen++; 6861556Srgrimes } 68717987Speter else { 68817987Speter while (*val) { 68983675Stegge if (quotes && 69054132Scracauer syntax[(int)*val] == CCTL) 69117987Speter STPUTC(CTLESC, expdest); 69217987Speter STPUTC(*val++, expdest); 69317987Speter } 69417987Speter 69517987Speter } 6961556Srgrimes } 6971556Srgrimes } 69820425Ssteve 6991556Srgrimes if (subtype == VSPLUS) 7001556Srgrimes set = ! set; 70117987Speter 70220425Ssteve easy = ((varflags & VSQUOTE) == 0 || 70317987Speter (*var == '@' && shellparam.nparam != 1)); 70417987Speter 70517987Speter 70617987Speter switch (subtype) { 70717987Speter case VSLENGTH: 70817987Speter expdest = cvtnum(varlen, expdest); 70917987Speter goto record; 71017987Speter 71117987Speter case VSNORMAL: 71217987Speter if (!easy) 71317987Speter break; 71417987Speterrecord: 71520425Ssteve recordregion(startloc, expdest - stackblock(), 71617987Speter varflags & VSQUOTE); 71717987Speter break; 71817987Speter 71917987Speter case VSPLUS: 72017987Speter case VSMINUS: 72117987Speter if (!set) { 7221556Srgrimes argstr(p, flag); 72317987Speter break; 72417987Speter } 72517987Speter if (easy) 72617987Speter goto record; 72717987Speter break; 72817987Speter 72917987Speter case VSTRIMLEFT: 73017987Speter case VSTRIMLEFTMAX: 73117987Speter case VSTRIMRIGHT: 73217987Speter case VSTRIMRIGHTMAX: 73317987Speter if (!set) 73417987Speter break; 73517987Speter /* 73617987Speter * Terminate the string and start recording the pattern 73717987Speter * right after it 73817987Speter */ 73917987Speter STPUTC('\0', expdest); 74045644Stegge patloc = expdest - stackblock(); 74145644Stegge if (subevalvar(p, NULL, patloc, subtype, 74238887Stegge startloc, varflags) == 0) { 74345644Stegge int amount = (expdest - stackblock() - patloc) + 1; 74425233Ssteve STADJUST(-amount, expdest); 74525233Ssteve } 74638887Stegge /* Remove any recorded regions beyond start of variable */ 74738887Stegge removerecordregions(startloc); 74838887Stegge goto record; 74917987Speter 75017987Speter case VSASSIGN: 75117987Speter case VSQUESTION: 75217987Speter if (!set) { 75320425Ssteve if (subevalvar(p, var, 0, subtype, startloc, varflags)) { 75420425Ssteve varflags &= ~VSNUL; 75538887Stegge /* 75638887Stegge * Remove any recorded regions beyond 75738887Stegge * start of variable 75838887Stegge */ 75938887Stegge removerecordregions(startloc); 7601556Srgrimes goto again; 76120425Ssteve } 76217987Speter break; 7631556Srgrimes } 76417987Speter if (easy) 76517987Speter goto record; 76617987Speter break; 76717987Speter 76817987Speter default: 76917987Speter abort(); 7701556Srgrimes } 77117987Speter 7721556Srgrimes if (subtype != VSNORMAL) { /* skip to end of alternative */ 7731556Srgrimes int nesting = 1; 7741556Srgrimes for (;;) { 7751556Srgrimes if ((c = *p++) == CTLESC) 7761556Srgrimes p++; 7771556Srgrimes else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 7781556Srgrimes if (set) 7791556Srgrimes argbackq = argbackq->next; 7801556Srgrimes } else if (c == CTLVAR) { 7811556Srgrimes if ((*p++ & VSTYPE) != VSNORMAL) 7821556Srgrimes nesting++; 7831556Srgrimes } else if (c == CTLENDVAR) { 7841556Srgrimes if (--nesting == 0) 7851556Srgrimes break; 7861556Srgrimes } 7871556Srgrimes } 7881556Srgrimes } 7891556Srgrimes return p; 7901556Srgrimes} 7911556Srgrimes 7921556Srgrimes 7931556Srgrimes 7941556Srgrimes/* 7951556Srgrimes * Test whether a specialized variable is set. 7961556Srgrimes */ 7971556Srgrimes 7981556SrgrimesSTATIC int 79990111Simpvarisset(char *name, int nulok) 80025233Ssteve{ 8011556Srgrimes 80225233Ssteve if (*name == '!') 80325233Ssteve return backgndpid != -1; 80425233Ssteve else if (*name == '@' || *name == '*') { 8051556Srgrimes if (*shellparam.p == NULL) 8061556Srgrimes return 0; 80725233Ssteve 80825233Ssteve if (nulok) { 80925233Ssteve char **av; 81025233Ssteve 81125233Ssteve for (av = shellparam.p; *av; av++) 81225233Ssteve if (**av != '\0') 81325233Ssteve return 1; 81425233Ssteve return 0; 81525233Ssteve } 81618202Speter } else if (is_digit(*name)) { 81725233Ssteve char *ap; 81820425Ssteve int num = atoi(name); 81925233Ssteve 82025233Ssteve if (num > shellparam.nparam) 82125233Ssteve return 0; 82225233Ssteve 82325233Ssteve if (num == 0) 82425233Ssteve ap = arg0; 82525233Ssteve else 82625233Ssteve ap = shellparam.p[num - 1]; 82725233Ssteve 82825233Ssteve if (nulok && (ap == NULL || *ap == '\0')) 82925233Ssteve return 0; 8301556Srgrimes } 8311556Srgrimes return 1; 8321556Srgrimes} 8331556Srgrimes 8341556Srgrimes 8351556Srgrimes 8361556Srgrimes/* 8371556Srgrimes * Add the value of a specialized variable to the stack string. 8381556Srgrimes */ 8391556Srgrimes 8401556SrgrimesSTATIC void 84190111Simpvarvalue(char *name, int quoted, int allow_split) 84217987Speter{ 8431556Srgrimes int num; 8441556Srgrimes char *p; 8451556Srgrimes int i; 84617987Speter extern int oexitstatus; 8471556Srgrimes char sep; 8481556Srgrimes char **ap; 8491556Srgrimes char const *syntax; 8501556Srgrimes 8511556Srgrimes#define STRTODEST(p) \ 8521556Srgrimes do {\ 8531556Srgrimes if (allow_split) { \ 8541556Srgrimes syntax = quoted? DQSYNTAX : BASESYNTAX; \ 8551556Srgrimes while (*p) { \ 85683675Stegge if (syntax[(int)*p] == CCTL) \ 8571556Srgrimes STPUTC(CTLESC, expdest); \ 8581556Srgrimes STPUTC(*p++, expdest); \ 8591556Srgrimes } \ 8601556Srgrimes } else \ 8611556Srgrimes while (*p) \ 8621556Srgrimes STPUTC(*p++, expdest); \ 8631556Srgrimes } while (0) 8641556Srgrimes 8651556Srgrimes 86618202Speter switch (*name) { 8671556Srgrimes case '$': 8681556Srgrimes num = rootpid; 8691556Srgrimes goto numvar; 8701556Srgrimes case '?': 87117987Speter num = oexitstatus; 8721556Srgrimes goto numvar; 8731556Srgrimes case '#': 8741556Srgrimes num = shellparam.nparam; 8751556Srgrimes goto numvar; 8761556Srgrimes case '!': 8771556Srgrimes num = backgndpid; 8781556Srgrimesnumvar: 87917987Speter expdest = cvtnum(num, expdest); 8801556Srgrimes break; 8811556Srgrimes case '-': 8821556Srgrimes for (i = 0 ; i < NOPTS ; i++) { 8831556Srgrimes if (optlist[i].val) 8841556Srgrimes STPUTC(optlist[i].letter, expdest); 8851556Srgrimes } 8861556Srgrimes break; 8871556Srgrimes case '@': 88838887Stegge if (allow_split && quoted) { 88938887Stegge for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 89038887Stegge STRTODEST(p); 89138887Stegge if (*ap) 89238887Stegge STPUTC('\0', expdest); 89338887Stegge } 89438887Stegge break; 8951556Srgrimes } 896102410Scharnier /* FALLTHROUGH */ 8971556Srgrimes case '*': 89838887Stegge if (ifsset() != 0) 89938887Stegge sep = ifsval()[0]; 90038887Stegge else 90138887Stegge sep = ' '; 9021556Srgrimes for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 9031556Srgrimes STRTODEST(p); 90438887Stegge if (*ap && sep) 9051556Srgrimes STPUTC(sep, expdest); 9061556Srgrimes } 9071556Srgrimes break; 9081556Srgrimes case '0': 9091556Srgrimes p = arg0; 9101556Srgrimes STRTODEST(p); 9111556Srgrimes break; 9121556Srgrimes default: 91318202Speter if (is_digit(*name)) { 91418202Speter num = atoi(name); 91518202Speter if (num > 0 && num <= shellparam.nparam) { 91618202Speter p = shellparam.p[num - 1]; 91718202Speter STRTODEST(p); 91818202Speter } 9191556Srgrimes } 9201556Srgrimes break; 9211556Srgrimes } 9221556Srgrimes} 9231556Srgrimes 9241556Srgrimes 9251556Srgrimes 9261556Srgrimes/* 9271556Srgrimes * Record the the fact that we have to scan this region of the 9281556Srgrimes * string for IFS characters. 9291556Srgrimes */ 9301556Srgrimes 9311556SrgrimesSTATIC void 93290111Simprecordregion(int start, int end, int nulonly) 93317987Speter{ 93425233Ssteve struct ifsregion *ifsp; 9351556Srgrimes 9361556Srgrimes if (ifslastp == NULL) { 9371556Srgrimes ifsp = &ifsfirst; 9381556Srgrimes } else { 9391556Srgrimes ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 9401556Srgrimes ifslastp->next = ifsp; 9411556Srgrimes } 9421556Srgrimes ifslastp = ifsp; 9431556Srgrimes ifslastp->next = NULL; 9441556Srgrimes ifslastp->begoff = start; 9451556Srgrimes ifslastp->endoff = end; 9461556Srgrimes ifslastp->nulonly = nulonly; 9471556Srgrimes} 9481556Srgrimes 9491556Srgrimes 9501556Srgrimes 9511556Srgrimes/* 9521556Srgrimes * Break the argument string into pieces based upon IFS and add the 9531556Srgrimes * strings to the argument list. The regions of the string to be 9541556Srgrimes * searched for IFS characters have been stored by recordregion. 9551556Srgrimes */ 9561556SrgrimesSTATIC void 95790111Simpifsbreakup(char *string, struct arglist *arglist) 95890111Simp{ 9591556Srgrimes struct ifsregion *ifsp; 9601556Srgrimes struct strlist *sp; 9611556Srgrimes char *start; 96225233Ssteve char *p; 9631556Srgrimes char *q; 9641556Srgrimes char *ifs; 96517987Speter int ifsspc; 96638887Stegge int nulonly; 9671556Srgrimes 96817987Speter 9691556Srgrimes start = string; 97038887Stegge ifsspc = 0; 97138887Stegge nulonly = 0; 9721556Srgrimes if (ifslastp != NULL) { 9731556Srgrimes ifsp = &ifsfirst; 9741556Srgrimes do { 9751556Srgrimes p = string + ifsp->begoff; 97638887Stegge nulonly = ifsp->nulonly; 97738887Stegge ifs = nulonly ? nullstr : 97838887Stegge ( ifsset() ? ifsval() : " \t\n" ); 97938887Stegge ifsspc = 0; 9801556Srgrimes while (p < string + ifsp->endoff) { 9811556Srgrimes q = p; 9821556Srgrimes if (*p == CTLESC) 9831556Srgrimes p++; 98438887Stegge if (strchr(ifs, *p)) { 98538887Stegge if (!nulonly) 98638887Stegge ifsspc = (strchr(" \t\n", *p) != NULL); 98738887Stegge /* Ignore IFS whitespace at start */ 98838887Stegge if (q == start && ifsspc) { 98938887Stegge p++; 99038887Stegge start = p; 99138887Stegge continue; 9921556Srgrimes } 99338887Stegge *q = '\0'; 99438887Stegge sp = (struct strlist *)stalloc(sizeof *sp); 99538887Stegge sp->text = start; 99638887Stegge *arglist->lastp = sp; 99738887Stegge arglist->lastp = &sp->next; 99838887Stegge p++; 99938887Stegge if (!nulonly) { 10001556Srgrimes for (;;) { 100138887Stegge if (p >= string + ifsp->endoff) { 10021556Srgrimes break; 100338887Stegge } 10041556Srgrimes q = p; 10051556Srgrimes if (*p == CTLESC) 10061556Srgrimes p++; 100738887Stegge if (strchr(ifs, *p) == NULL ) { 10081556Srgrimes p = q; 10091556Srgrimes break; 101038887Stegge } else if (strchr(" \t\n",*p) == NULL) { 101138887Stegge if (ifsspc) { 101238887Stegge p++; 101338887Stegge ifsspc = 0; 101438887Stegge } else { 101538887Stegge p = q; 101638887Stegge break; 101738887Stegge } 101838887Stegge } else 101938887Stegge p++; 10201556Srgrimes } 10211556Srgrimes } 10221556Srgrimes start = p; 102338887Stegge } else 102438887Stegge p++; 10251556Srgrimes } 10261556Srgrimes } while ((ifsp = ifsp->next) != NULL); 102738887Stegge if (*start || (!ifsspc && start > string && 102838887Stegge (nulonly || 1))) { 10291556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 10301556Srgrimes sp->text = start; 10311556Srgrimes *arglist->lastp = sp; 10321556Srgrimes arglist->lastp = &sp->next; 10331556Srgrimes } 10341556Srgrimes } else { 10351556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 10361556Srgrimes sp->text = start; 10371556Srgrimes *arglist->lastp = sp; 10381556Srgrimes arglist->lastp = &sp->next; 10391556Srgrimes } 10401556Srgrimes} 10411556Srgrimes 10421556Srgrimes 10431556Srgrimes 10441556Srgrimes/* 10451556Srgrimes * Expand shell metacharacters. At this point, the only control characters 10461556Srgrimes * should be escapes. The results are stored in the list exparg. 10471556Srgrimes */ 10481556Srgrimes 10491556Srgrimeschar *expdir; 10501556Srgrimes 10511556Srgrimes 10521556SrgrimesSTATIC void 105390111Simpexpandmeta(struct strlist *str, int flag __unused) 105417987Speter{ 10551556Srgrimes char *p; 10561556Srgrimes struct strlist **savelastp; 10571556Srgrimes struct strlist *sp; 10581556Srgrimes char c; 10591556Srgrimes /* TODO - EXP_REDIR */ 10601556Srgrimes 10611556Srgrimes while (str) { 10621556Srgrimes if (fflag) 10631556Srgrimes goto nometa; 10641556Srgrimes p = str->text; 10651556Srgrimes for (;;) { /* fast check for meta chars */ 10661556Srgrimes if ((c = *p++) == '\0') 10671556Srgrimes goto nometa; 10681556Srgrimes if (c == '*' || c == '?' || c == '[' || c == '!') 10691556Srgrimes break; 10701556Srgrimes } 10711556Srgrimes savelastp = exparg.lastp; 10721556Srgrimes INTOFF; 10731556Srgrimes if (expdir == NULL) { 10741556Srgrimes int i = strlen(str->text); 10751556Srgrimes expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 10761556Srgrimes } 10771556Srgrimes 10781556Srgrimes expmeta(expdir, str->text); 10791556Srgrimes ckfree(expdir); 10801556Srgrimes expdir = NULL; 10811556Srgrimes INTON; 10821556Srgrimes if (exparg.lastp == savelastp) { 10838855Srgrimes /* 10848855Srgrimes * no matches 10851556Srgrimes */ 10861556Srgrimesnometa: 10871556Srgrimes *exparg.lastp = str; 10881556Srgrimes rmescapes(str->text); 10891556Srgrimes exparg.lastp = &str->next; 10901556Srgrimes } else { 10911556Srgrimes *exparg.lastp = NULL; 10921556Srgrimes *savelastp = sp = expsort(*savelastp); 10931556Srgrimes while (sp->next != NULL) 10941556Srgrimes sp = sp->next; 10951556Srgrimes exparg.lastp = &sp->next; 10961556Srgrimes } 10971556Srgrimes str = str->next; 10981556Srgrimes } 10991556Srgrimes} 11001556Srgrimes 11011556Srgrimes 11021556Srgrimes/* 11031556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion. 11041556Srgrimes */ 11051556Srgrimes 11061556SrgrimesSTATIC void 110790111Simpexpmeta(char *enddir, char *name) 110890111Simp{ 110925233Ssteve char *p; 11101556Srgrimes char *q; 11111556Srgrimes char *start; 11121556Srgrimes char *endname; 11131556Srgrimes int metaflag; 11141556Srgrimes struct stat statb; 11151556Srgrimes DIR *dirp; 11161556Srgrimes struct dirent *dp; 11171556Srgrimes int atend; 11181556Srgrimes int matchdot; 11191556Srgrimes 11201556Srgrimes metaflag = 0; 11211556Srgrimes start = name; 11221556Srgrimes for (p = name ; ; p++) { 11231556Srgrimes if (*p == '*' || *p == '?') 11241556Srgrimes metaflag = 1; 11251556Srgrimes else if (*p == '[') { 11261556Srgrimes q = p + 1; 112726488Sache if (*q == '!' || *q == '^') 11281556Srgrimes q++; 11291556Srgrimes for (;;) { 113038887Stegge while (*q == CTLQUOTEMARK) 113138887Stegge q++; 11321556Srgrimes if (*q == CTLESC) 11331556Srgrimes q++; 11341556Srgrimes if (*q == '/' || *q == '\0') 11351556Srgrimes break; 11361556Srgrimes if (*++q == ']') { 11371556Srgrimes metaflag = 1; 11381556Srgrimes break; 11391556Srgrimes } 11401556Srgrimes } 11411556Srgrimes } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 11421556Srgrimes metaflag = 1; 11431556Srgrimes } else if (*p == '\0') 11441556Srgrimes break; 114538887Stegge else if (*p == CTLQUOTEMARK) 114638887Stegge continue; 11471556Srgrimes else if (*p == CTLESC) 11481556Srgrimes p++; 11491556Srgrimes if (*p == '/') { 11501556Srgrimes if (metaflag) 11511556Srgrimes break; 11521556Srgrimes start = p + 1; 11531556Srgrimes } 11541556Srgrimes } 11551556Srgrimes if (metaflag == 0) { /* we've reached the end of the file name */ 11561556Srgrimes if (enddir != expdir) 11571556Srgrimes metaflag++; 11581556Srgrimes for (p = name ; ; p++) { 115938887Stegge if (*p == CTLQUOTEMARK) 116038887Stegge continue; 11611556Srgrimes if (*p == CTLESC) 11621556Srgrimes p++; 11631556Srgrimes *enddir++ = *p; 11641556Srgrimes if (*p == '\0') 11651556Srgrimes break; 11661556Srgrimes } 11671556Srgrimes if (metaflag == 0 || stat(expdir, &statb) >= 0) 11681556Srgrimes addfname(expdir); 11691556Srgrimes return; 11701556Srgrimes } 11711556Srgrimes endname = p; 11721556Srgrimes if (start != name) { 11731556Srgrimes p = name; 11741556Srgrimes while (p < start) { 117538887Stegge while (*p == CTLQUOTEMARK) 117638887Stegge p++; 11771556Srgrimes if (*p == CTLESC) 11781556Srgrimes p++; 11791556Srgrimes *enddir++ = *p++; 11801556Srgrimes } 11811556Srgrimes } 11821556Srgrimes if (enddir == expdir) { 11831556Srgrimes p = "."; 11841556Srgrimes } else if (enddir == expdir + 1 && *expdir == '/') { 11851556Srgrimes p = "/"; 11861556Srgrimes } else { 11871556Srgrimes p = expdir; 11881556Srgrimes enddir[-1] = '\0'; 11891556Srgrimes } 11901556Srgrimes if ((dirp = opendir(p)) == NULL) 11911556Srgrimes return; 11921556Srgrimes if (enddir != expdir) 11931556Srgrimes enddir[-1] = '/'; 11941556Srgrimes if (*endname == 0) { 11951556Srgrimes atend = 1; 11961556Srgrimes } else { 11971556Srgrimes atend = 0; 11981556Srgrimes *endname++ = '\0'; 11991556Srgrimes } 12001556Srgrimes matchdot = 0; 120138887Stegge p = start; 120238887Stegge while (*p == CTLQUOTEMARK) 120338887Stegge p++; 120438887Stegge if (*p == CTLESC) 120538887Stegge p++; 120638887Stegge if (*p == '.') 12071556Srgrimes matchdot++; 12081556Srgrimes while (! int_pending() && (dp = readdir(dirp)) != NULL) { 12091556Srgrimes if (dp->d_name[0] == '.' && ! matchdot) 12101556Srgrimes continue; 121145514Stegge if (patmatch(start, dp->d_name, 0)) { 12121556Srgrimes if (atend) { 12131556Srgrimes scopy(dp->d_name, enddir); 12141556Srgrimes addfname(expdir); 12151556Srgrimes } else { 12161556Srgrimes char *q; 121717987Speter for (p = enddir, q = dp->d_name; 121817987Speter (*p++ = *q++) != '\0';) 121917987Speter continue; 12201556Srgrimes p[-1] = '/'; 12211556Srgrimes expmeta(p, endname); 12221556Srgrimes } 12231556Srgrimes } 12241556Srgrimes } 12251556Srgrimes closedir(dirp); 12261556Srgrimes if (! atend) 12271556Srgrimes endname[-1] = '/'; 12281556Srgrimes} 12291556Srgrimes 12301556Srgrimes 12311556Srgrimes/* 12321556Srgrimes * Add a file name to the list. 12331556Srgrimes */ 12341556Srgrimes 12351556SrgrimesSTATIC void 123690111Simpaddfname(char *name) 123790111Simp{ 12381556Srgrimes char *p; 12391556Srgrimes struct strlist *sp; 12401556Srgrimes 12411556Srgrimes p = stalloc(strlen(name) + 1); 12421556Srgrimes scopy(name, p); 12431556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 12441556Srgrimes sp->text = p; 12451556Srgrimes *exparg.lastp = sp; 12461556Srgrimes exparg.lastp = &sp->next; 12471556Srgrimes} 12481556Srgrimes 12491556Srgrimes 12501556Srgrimes/* 12511556Srgrimes * Sort the results of file name expansion. It calculates the number of 12521556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the 12531556Srgrimes * work. 12541556Srgrimes */ 12551556Srgrimes 12561556SrgrimesSTATIC struct strlist * 125790111Simpexpsort(struct strlist *str) 125890111Simp{ 12591556Srgrimes int len; 12601556Srgrimes struct strlist *sp; 12611556Srgrimes 12621556Srgrimes len = 0; 12631556Srgrimes for (sp = str ; sp ; sp = sp->next) 12641556Srgrimes len++; 12651556Srgrimes return msort(str, len); 12661556Srgrimes} 12671556Srgrimes 12681556Srgrimes 12691556SrgrimesSTATIC struct strlist * 127090111Simpmsort(struct strlist *list, int len) 127117987Speter{ 127217987Speter struct strlist *p, *q = NULL; 12731556Srgrimes struct strlist **lpp; 12741556Srgrimes int half; 12751556Srgrimes int n; 12761556Srgrimes 12771556Srgrimes if (len <= 1) 12781556Srgrimes return list; 12798855Srgrimes half = len >> 1; 12801556Srgrimes p = list; 12811556Srgrimes for (n = half ; --n >= 0 ; ) { 12821556Srgrimes q = p; 12831556Srgrimes p = p->next; 12841556Srgrimes } 12851556Srgrimes q->next = NULL; /* terminate first half of list */ 12861556Srgrimes q = msort(list, half); /* sort first half of list */ 12871556Srgrimes p = msort(p, len - half); /* sort second half */ 12881556Srgrimes lpp = &list; 12891556Srgrimes for (;;) { 12901556Srgrimes if (strcmp(p->text, q->text) < 0) { 12911556Srgrimes *lpp = p; 12921556Srgrimes lpp = &p->next; 12931556Srgrimes if ((p = *lpp) == NULL) { 12941556Srgrimes *lpp = q; 12951556Srgrimes break; 12961556Srgrimes } 12971556Srgrimes } else { 12981556Srgrimes *lpp = q; 12991556Srgrimes lpp = &q->next; 13001556Srgrimes if ((q = *lpp) == NULL) { 13011556Srgrimes *lpp = p; 13021556Srgrimes break; 13031556Srgrimes } 13041556Srgrimes } 13051556Srgrimes } 13061556Srgrimes return list; 13071556Srgrimes} 13081556Srgrimes 13091556Srgrimes 13101556Srgrimes 13111556Srgrimes/* 13121556Srgrimes * Returns true if the pattern matches the string. 13131556Srgrimes */ 13141556Srgrimes 13151556Srgrimesint 131690111Simppatmatch(char *pattern, char *string, int squoted) 131790111Simp{ 13181556Srgrimes#ifdef notdef 13191556Srgrimes if (pattern[0] == '!' && pattern[1] == '!') 13201556Srgrimes return 1 - pmatch(pattern + 2, string); 13211556Srgrimes else 13221556Srgrimes#endif 132345514Stegge return pmatch(pattern, string, squoted); 13241556Srgrimes} 13251556Srgrimes 132617987Speter 132717525SacheSTATIC int 132890111Simppmatch(char *pattern, char *string, int squoted) 132990111Simp{ 133025233Ssteve char *p, *q; 133125233Ssteve char c; 13321556Srgrimes 13331556Srgrimes p = pattern; 13341556Srgrimes q = string; 13351556Srgrimes for (;;) { 13361556Srgrimes switch (c = *p++) { 13371556Srgrimes case '\0': 13381556Srgrimes goto breakloop; 13391556Srgrimes case CTLESC: 134045514Stegge if (squoted && *q == CTLESC) 134145514Stegge q++; 13421556Srgrimes if (*q++ != *p++) 13431556Srgrimes return 0; 13441556Srgrimes break; 134538887Stegge case CTLQUOTEMARK: 134638887Stegge continue; 13471556Srgrimes case '?': 134845514Stegge if (squoted && *q == CTLESC) 134945514Stegge q++; 13501556Srgrimes if (*q++ == '\0') 13511556Srgrimes return 0; 13521556Srgrimes break; 13531556Srgrimes case '*': 13541556Srgrimes c = *p; 135538887Stegge while (c == CTLQUOTEMARK || c == '*') 135638887Stegge c = *++p; 135738887Stegge if (c != CTLESC && c != CTLQUOTEMARK && 135838887Stegge c != '?' && c != '*' && c != '[') { 13591556Srgrimes while (*q != c) { 136045514Stegge if (squoted && *q == CTLESC && 136145514Stegge q[1] == c) 136245514Stegge break; 13631556Srgrimes if (*q == '\0') 13641556Srgrimes return 0; 136545514Stegge if (squoted && *q == CTLESC) 136645514Stegge q++; 13671556Srgrimes q++; 13681556Srgrimes } 13691556Srgrimes } 13701556Srgrimes do { 137145514Stegge if (pmatch(p, q, squoted)) 13721556Srgrimes return 1; 137345514Stegge if (squoted && *q == CTLESC) 137445514Stegge q++; 13751556Srgrimes } while (*q++ != '\0'); 13761556Srgrimes return 0; 13771556Srgrimes case '[': { 13781556Srgrimes char *endp; 13791556Srgrimes int invert, found; 13801556Srgrimes char chr; 13811556Srgrimes 13821556Srgrimes endp = p; 138326488Sache if (*endp == '!' || *endp == '^') 13841556Srgrimes endp++; 13851556Srgrimes for (;;) { 138638887Stegge while (*endp == CTLQUOTEMARK) 138738887Stegge endp++; 13881556Srgrimes if (*endp == '\0') 13891556Srgrimes goto dft; /* no matching ] */ 13901556Srgrimes if (*endp == CTLESC) 13911556Srgrimes endp++; 13921556Srgrimes if (*++endp == ']') 13931556Srgrimes break; 13941556Srgrimes } 13951556Srgrimes invert = 0; 139626488Sache if (*p == '!' || *p == '^') { 13971556Srgrimes invert++; 13981556Srgrimes p++; 13991556Srgrimes } 14001556Srgrimes found = 0; 14011556Srgrimes chr = *q++; 140245514Stegge if (squoted && chr == CTLESC) 140345514Stegge chr = *q++; 140417987Speter if (chr == '\0') 140517987Speter return 0; 14061556Srgrimes c = *p++; 14071556Srgrimes do { 140838887Stegge if (c == CTLQUOTEMARK) 140938887Stegge continue; 14101556Srgrimes if (c == CTLESC) 14111556Srgrimes c = *p++; 14121556Srgrimes if (*p == '-' && p[1] != ']') { 14131556Srgrimes p++; 141438887Stegge while (*p == CTLQUOTEMARK) 141538887Stegge p++; 14161556Srgrimes if (*p == CTLESC) 14171556Srgrimes p++; 141817557Sache if ( collate_range_cmp(chr, c) >= 0 141917557Sache && collate_range_cmp(chr, *p) <= 0 142017525Sache ) 14211556Srgrimes found = 1; 14221556Srgrimes p++; 14231556Srgrimes } else { 14241556Srgrimes if (chr == c) 14251556Srgrimes found = 1; 14261556Srgrimes } 14271556Srgrimes } while ((c = *p++) != ']'); 14281556Srgrimes if (found == invert) 14291556Srgrimes return 0; 14301556Srgrimes break; 14311556Srgrimes } 14321556Srgrimesdft: default: 143345514Stegge if (squoted && *q == CTLESC) 143445514Stegge q++; 14351556Srgrimes if (*q++ != c) 14361556Srgrimes return 0; 14371556Srgrimes break; 14381556Srgrimes } 14391556Srgrimes } 14401556Srgrimesbreakloop: 14411556Srgrimes if (*q != '\0') 14421556Srgrimes return 0; 14431556Srgrimes return 1; 14441556Srgrimes} 14451556Srgrimes 14461556Srgrimes 14471556Srgrimes 14481556Srgrimes/* 14491556Srgrimes * Remove any CTLESC characters from a string. 14501556Srgrimes */ 14511556Srgrimes 14521556Srgrimesvoid 145390111Simprmescapes(char *str) 145438887Stegge{ 145525233Ssteve char *p, *q; 14561556Srgrimes 14571556Srgrimes p = str; 145838887Stegge while (*p != CTLESC && *p != CTLQUOTEMARK) { 14591556Srgrimes if (*p++ == '\0') 14601556Srgrimes return; 14611556Srgrimes } 14621556Srgrimes q = p; 14631556Srgrimes while (*p) { 146438887Stegge if (*p == CTLQUOTEMARK) { 146538887Stegge p++; 146638887Stegge continue; 146738887Stegge } 14681556Srgrimes if (*p == CTLESC) 14691556Srgrimes p++; 14701556Srgrimes *q++ = *p++; 14711556Srgrimes } 14721556Srgrimes *q = '\0'; 14731556Srgrimes} 14741556Srgrimes 14751556Srgrimes 14761556Srgrimes 14771556Srgrimes/* 14781556Srgrimes * See if a pattern matches in a case statement. 14791556Srgrimes */ 14801556Srgrimes 14811556Srgrimesint 148290111Simpcasematch(union node *pattern, char *val) 148390111Simp{ 14841556Srgrimes struct stackmark smark; 14851556Srgrimes int result; 14861556Srgrimes char *p; 14871556Srgrimes 14881556Srgrimes setstackmark(&smark); 14891556Srgrimes argbackq = pattern->narg.backquote; 14901556Srgrimes STARTSTACKSTR(expdest); 14911556Srgrimes ifslastp = NULL; 14921556Srgrimes argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 14931556Srgrimes STPUTC('\0', expdest); 14941556Srgrimes p = grabstackstr(expdest); 149545514Stegge result = patmatch(p, val, 0); 14961556Srgrimes popstackmark(&smark); 14971556Srgrimes return result; 14981556Srgrimes} 149917987Speter 150017987Speter/* 150117987Speter * Our own itoa(). 150217987Speter */ 150317987Speter 150417987SpeterSTATIC char * 150590111Simpcvtnum(int num, char *buf) 150690111Simp{ 150717987Speter char temp[32]; 150817987Speter int neg = num < 0; 150917987Speter char *p = temp + 31; 151017987Speter 151117987Speter temp[31] = '\0'; 151217987Speter 151317987Speter do { 151417987Speter *--p = num % 10 + '0'; 151517987Speter } while ((num /= 10) != 0); 151617987Speter 151717987Speter if (neg) 151817987Speter *--p = '-'; 151917987Speter 152017987Speter while (*p) 152117987Speter STPUTC(*p++, buf); 152217987Speter return buf; 152317987Speter} 1524