expand.c revision 212243
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1991, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 4207944Sjilles * Copyright (c) 1997-2005 5207944Sjilles * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. 61556Srgrimes * 71556Srgrimes * This code is derived from software contributed to Berkeley by 81556Srgrimes * Kenneth Almquist. 91556Srgrimes * 101556Srgrimes * Redistribution and use in source and binary forms, with or without 111556Srgrimes * modification, are permitted provided that the following conditions 121556Srgrimes * are met: 131556Srgrimes * 1. Redistributions of source code must retain the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer. 151556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 161556Srgrimes * notice, this list of conditions and the following disclaimer in the 171556Srgrimes * documentation and/or other materials provided with the distribution. 181556Srgrimes * 4. Neither the name of the University nor the names of its contributors 191556Srgrimes * may be used to endorse or promote products derived from this software 201556Srgrimes * without specific prior written permission. 211556Srgrimes * 221556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 231556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 261556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 271556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321556Srgrimes * SUCH DAMAGE. 331556Srgrimes */ 341556Srgrimes 351556Srgrimes#ifndef lint 3636150Scharnier#if 0 3736150Scharnierstatic char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; 3836150Scharnier#endif 391556Srgrimes#endif /* not lint */ 4099110Sobrien#include <sys/cdefs.h> 4199110Sobrien__FBSDID("$FreeBSD: head/bin/sh/expand.c 212243 2010-09-05 21:12:48Z jilles $"); 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> 5238887Stegge#include <stdio.h> 53108286Stjr#include <string.h> 5417987Speter 551556Srgrimes/* 561556Srgrimes * Routines to expand arguments to commands. We have to deal with 571556Srgrimes * backquotes, shell variables, and file metacharacters. 581556Srgrimes */ 591556Srgrimes 601556Srgrimes#include "shell.h" 611556Srgrimes#include "main.h" 621556Srgrimes#include "nodes.h" 631556Srgrimes#include "eval.h" 641556Srgrimes#include "expand.h" 651556Srgrimes#include "syntax.h" 661556Srgrimes#include "parser.h" 671556Srgrimes#include "jobs.h" 681556Srgrimes#include "options.h" 691556Srgrimes#include "var.h" 701556Srgrimes#include "input.h" 711556Srgrimes#include "output.h" 721556Srgrimes#include "memalloc.h" 731556Srgrimes#include "error.h" 741556Srgrimes#include "mystring.h" 7517987Speter#include "arith.h" 7617987Speter#include "show.h" 771556Srgrimes 781556Srgrimes/* 791556Srgrimes * Structure specifying which parts of the string should be searched 801556Srgrimes * for IFS characters. 811556Srgrimes */ 821556Srgrimes 831556Srgrimesstruct ifsregion { 841556Srgrimes struct ifsregion *next; /* next region in list */ 851556Srgrimes int begoff; /* offset of start of region */ 861556Srgrimes int endoff; /* offset of end of region */ 87194975Sjilles int inquotes; /* search for nul bytes only */ 881556Srgrimes}; 891556Srgrimes 901556Srgrimes 91117261SddsSTATIC char *expdest; /* output of current string */ 92117261SddsSTATIC struct nodelist *argbackq; /* list of back quote expressions */ 93117261SddsSTATIC struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 94117261SddsSTATIC struct ifsregion *ifslastp; /* last struct in list */ 95117261SddsSTATIC struct arglist exparg; /* holds expanded arg list */ 961556Srgrimes 9790111SimpSTATIC void argstr(char *, int); 9890111SimpSTATIC char *exptilde(char *, int); 9990111SimpSTATIC void expbackq(union node *, int, int); 10090111SimpSTATIC int subevalvar(char *, char *, int, int, int, int); 10190111SimpSTATIC char *evalvar(char *, int); 10290111SimpSTATIC int varisset(char *, int); 103164081SstefanfSTATIC void varvalue(char *, int, int, int); 10490111SimpSTATIC void recordregion(int, int, int); 105155301SschweikhSTATIC void removerecordregions(int); 10690111SimpSTATIC void ifsbreakup(char *, struct arglist *); 10790111SimpSTATIC void expandmeta(struct strlist *, int); 10890111SimpSTATIC void expmeta(char *, char *); 10990111SimpSTATIC void addfname(char *); 11090111SimpSTATIC struct strlist *expsort(struct strlist *); 11190111SimpSTATIC struct strlist *msort(struct strlist *, int); 11290111SimpSTATIC char *cvtnum(int, char *); 11390111SimpSTATIC int collate_range_cmp(int, int); 1141556Srgrimes 11590111SimpSTATIC int 116118374Sachecollate_range_cmp(int c1, int c2) 11719281Sache{ 11819281Sache static char s1[2], s2[2]; 11919281Sache 12019281Sache s1[0] = c1; 12119281Sache s2[0] = c2; 122118374Sache return (strcoll(s1, s2)); 12319281Sache} 12419281Sache 1251556Srgrimes/* 1261556Srgrimes * Expand shell variables and backquotes inside a here document. 12790111Simp * union node *arg the document 12890111Simp * int fd; where to write the expanded version 1291556Srgrimes */ 1301556Srgrimes 1311556Srgrimesvoid 13290111Simpexpandhere(union node *arg, int fd) 13390111Simp{ 1341556Srgrimes herefd = fd; 1351556Srgrimes expandarg(arg, (struct arglist *)NULL, 0); 13639137Stegge xwrite(fd, stackblock(), expdest - stackblock()); 1371556Srgrimes} 1381556Srgrimes 1391556Srgrimes 1401556Srgrimes/* 141212243Sjilles * Perform expansions on an argument, placing the resulting list of arguments 142212243Sjilles * in arglist. Parameter expansion, command substitution and arithmetic 143212243Sjilles * expansion are always performed; additional expansions can be requested 144212243Sjilles * via flag (EXP_*). 145212243Sjilles * The result is left in the stack string. 146212243Sjilles * When arglist is NULL, perform here document expansion. A partial result 147212243Sjilles * may be written to herefd, which is then not included in the stack string. 148212243Sjilles * 149212243Sjilles * Caution: this function uses global state and is not reentrant. 150212243Sjilles * However, a new invocation after an interrupted invocation is safe 151212243Sjilles * and will reset the global state for the new call. 1521556Srgrimes */ 1531556Srgrimesvoid 15490111Simpexpandarg(union node *arg, struct arglist *arglist, int flag) 15517987Speter{ 1561556Srgrimes struct strlist *sp; 1571556Srgrimes char *p; 1581556Srgrimes 1591556Srgrimes argbackq = arg->narg.backquote; 1601556Srgrimes STARTSTACKSTR(expdest); 1611556Srgrimes ifsfirst.next = NULL; 1621556Srgrimes ifslastp = NULL; 1631556Srgrimes argstr(arg->narg.text, flag); 1641556Srgrimes if (arglist == NULL) { 1651556Srgrimes return; /* here document expanded */ 1661556Srgrimes } 1671556Srgrimes STPUTC('\0', expdest); 1681556Srgrimes p = grabstackstr(expdest); 1691556Srgrimes exparg.lastp = &exparg.list; 1701556Srgrimes /* 1711556Srgrimes * TODO - EXP_REDIR 1721556Srgrimes */ 1731556Srgrimes if (flag & EXP_FULL) { 1741556Srgrimes ifsbreakup(p, &exparg); 1751556Srgrimes *exparg.lastp = NULL; 1761556Srgrimes exparg.lastp = &exparg.list; 1771556Srgrimes expandmeta(exparg.list, flag); 1781556Srgrimes } else { 1791556Srgrimes if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 1801556Srgrimes rmescapes(p); 1811556Srgrimes sp = (struct strlist *)stalloc(sizeof (struct strlist)); 1821556Srgrimes sp->text = p; 1831556Srgrimes *exparg.lastp = sp; 1841556Srgrimes exparg.lastp = &sp->next; 1851556Srgrimes } 1861556Srgrimes while (ifsfirst.next != NULL) { 1871556Srgrimes struct ifsregion *ifsp; 1881556Srgrimes INTOFF; 1891556Srgrimes ifsp = ifsfirst.next->next; 1901556Srgrimes ckfree(ifsfirst.next); 1911556Srgrimes ifsfirst.next = ifsp; 1921556Srgrimes INTON; 1931556Srgrimes } 1941556Srgrimes *exparg.lastp = NULL; 1951556Srgrimes if (exparg.list) { 1961556Srgrimes *arglist->lastp = exparg.list; 1971556Srgrimes arglist->lastp = exparg.lastp; 1981556Srgrimes } 1991556Srgrimes} 2001556Srgrimes 2011556Srgrimes 2021556Srgrimes 2031556Srgrimes/* 204212243Sjilles * Perform parameter expansion, command substitution and arithmetic 205212243Sjilles * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. 206212243Sjilles * Processing ends at a CTLENDVAR character as well as '\0'. 207212243Sjilles * This is used to expand word in ${var+word} etc. 208212243Sjilles * If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC 209212243Sjilles * characters to allow for further processing. 210212243Sjilles * If EXP_FULL is set, also preserve CTLQUOTEMARK characters. 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': 224212243Sjilles 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 274212243Sjilles/* 275212243Sjilles * Perform tilde expansion, placing the result in the stack string and 276212243Sjilles * returning the next position in the input string to process. 277212243Sjilles */ 2781556SrgrimesSTATIC char * 27990111Simpexptilde(char *p, int flag) 28017987Speter{ 2811556Srgrimes char c, *startp = p; 2821556Srgrimes struct passwd *pw; 2831556Srgrimes char *home; 284108935Stjr int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 2851556Srgrimes 28617987Speter while ((c = *p) != '\0') { 2871556Srgrimes switch(c) { 288200988Sjilles case CTLESC: /* This means CTL* are always considered quoted. */ 289200988Sjilles case CTLVAR: 290200988Sjilles case CTLBACKQ: 291200988Sjilles case CTLBACKQ | CTLQUOTE: 292200988Sjilles case CTLARI: 293200988Sjilles case CTLENDARI: 29439137Stegge case CTLQUOTEMARK: 29539137Stegge return (startp); 2961556Srgrimes case ':': 2971556Srgrimes if (flag & EXP_VARTILDE) 2981556Srgrimes goto done; 2991556Srgrimes break; 3001556Srgrimes case '/': 301206150Sjilles case CTLENDVAR: 3021556Srgrimes goto done; 3031556Srgrimes } 3041556Srgrimes p++; 3051556Srgrimes } 3061556Srgrimesdone: 3071556Srgrimes *p = '\0'; 3081556Srgrimes if (*(startp+1) == '\0') { 3091556Srgrimes if ((home = lookupvar("HOME")) == NULL) 3101556Srgrimes goto lose; 3111556Srgrimes } else { 3121556Srgrimes if ((pw = getpwnam(startp+1)) == NULL) 3131556Srgrimes goto lose; 3141556Srgrimes home = pw->pw_dir; 3151556Srgrimes } 3161556Srgrimes if (*home == '\0') 3171556Srgrimes goto lose; 3181556Srgrimes *p = c; 31917987Speter while ((c = *home++) != '\0') { 32083675Stegge if (quotes && SQSYNTAX[(int)c] == CCTL) 3211556Srgrimes STPUTC(CTLESC, expdest); 3221556Srgrimes STPUTC(c, expdest); 3231556Srgrimes } 3241556Srgrimes return (p); 3251556Srgrimeslose: 3261556Srgrimes *p = c; 3271556Srgrimes return (startp); 3281556Srgrimes} 3291556Srgrimes 3301556Srgrimes 331155301SschweikhSTATIC void 33290111Simpremoverecordregions(int endoff) 33338887Stegge{ 33438887Stegge if (ifslastp == NULL) 33538887Stegge return; 33638887Stegge 33738887Stegge if (ifsfirst.endoff > endoff) { 33838887Stegge while (ifsfirst.next != NULL) { 33938887Stegge struct ifsregion *ifsp; 34038887Stegge INTOFF; 34138887Stegge ifsp = ifsfirst.next->next; 34238887Stegge ckfree(ifsfirst.next); 34338887Stegge ifsfirst.next = ifsp; 34438887Stegge INTON; 34538887Stegge } 34638887Stegge if (ifsfirst.begoff > endoff) 34738887Stegge ifslastp = NULL; 34838887Stegge else { 34938887Stegge ifslastp = &ifsfirst; 35038887Stegge ifsfirst.endoff = endoff; 35138887Stegge } 35238887Stegge return; 35338887Stegge } 354155301Sschweikh 35538887Stegge ifslastp = &ifsfirst; 35638887Stegge while (ifslastp->next && ifslastp->next->begoff < endoff) 35738887Stegge ifslastp=ifslastp->next; 35838887Stegge while (ifslastp->next != NULL) { 35938887Stegge struct ifsregion *ifsp; 36038887Stegge INTOFF; 36138887Stegge ifsp = ifslastp->next->next; 36238887Stegge ckfree(ifslastp->next); 36338887Stegge ifslastp->next = ifsp; 36438887Stegge INTON; 36538887Stegge } 36638887Stegge if (ifslastp->endoff > endoff) 36738887Stegge ifslastp->endoff = endoff; 36838887Stegge} 36938887Stegge 3701556Srgrimes/* 3711556Srgrimes * Expand arithmetic expression. Backup to start of expression, 3721556Srgrimes * evaluate, place result in (backed up) result, adjust string position. 3731556Srgrimes */ 3741556Srgrimesvoid 37590111Simpexpari(int flag) 37617987Speter{ 377207206Sjilles char *p, *q, *start; 378178631Sstefanf arith_t result; 37938887Stegge int begoff; 380108935Stjr int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 38138887Stegge int quoted; 3821556Srgrimes 3831556Srgrimes /* 38446684Skris * This routine is slightly over-complicated for 3851556Srgrimes * efficiency. First we make sure there is 3861556Srgrimes * enough space for the result, which may be bigger 387212243Sjilles * than the expression. Next we 3881556Srgrimes * scan backwards looking for the start of arithmetic. If the 3891556Srgrimes * next previous character is a CTLESC character, then we 3901556Srgrimes * have to rescan starting from the beginning since CTLESC 3918855Srgrimes * characters have to be processed left to right. 3921556Srgrimes */ 393178631Sstefanf CHECKSTRSPACE(DIGITS(result) - 2, expdest); 3948855Srgrimes USTPUTC('\0', expdest); 3951556Srgrimes start = stackblock(); 39683676Stegge p = expdest - 2; 39783676Stegge while (p >= start && *p != CTLARI) 3981556Srgrimes --p; 39983676Stegge if (p < start || *p != CTLARI) 4001556Srgrimes error("missing CTLARI (shouldn't happen)"); 40183676Stegge if (p > start && *(p - 1) == CTLESC) 4021556Srgrimes for (p = start; *p != CTLARI; p++) 4031556Srgrimes if (*p == CTLESC) 4041556Srgrimes p++; 40538887Stegge 40638887Stegge if (p[1] == '"') 40738887Stegge quoted=1; 40838887Stegge else 40938887Stegge quoted=0; 41038887Stegge begoff = p - start; 41138887Stegge removerecordregions(begoff); 4121556Srgrimes if (quotes) 41338887Stegge rmescapes(p+2); 414207206Sjilles q = grabstackstr(expdest); 41538887Stegge result = arith(p+2); 416207206Sjilles ungrabstackstr(q, expdest); 417178631Sstefanf fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result); 4181556Srgrimes while (*p++) 4191556Srgrimes ; 42038887Stegge if (quoted == 0) 42138887Stegge recordregion(begoff, p - 1 - start, 0); 4221556Srgrimes result = expdest - p + 1; 4231556Srgrimes STADJUST(-result, expdest); 4241556Srgrimes} 4251556Srgrimes 4261556Srgrimes 4271556Srgrimes/* 428212243Sjilles * Perform command substitution. 4291556Srgrimes */ 4301556SrgrimesSTATIC void 43190111Simpexpbackq(union node *cmd, int quoted, int flag) 43217987Speter{ 4331556Srgrimes struct backcmd in; 4341556Srgrimes int i; 4351556Srgrimes char buf[128]; 4361556Srgrimes char *p; 4371556Srgrimes char *dest = expdest; 4381556Srgrimes struct ifsregion saveifs, *savelastp; 4391556Srgrimes struct nodelist *saveargbackq; 4401556Srgrimes char lastc; 4411556Srgrimes int startloc = dest - stackblock(); 4421556Srgrimes char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 4431556Srgrimes int saveherefd; 444108935Stjr int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 445115424Sfenner int nnl; 4461556Srgrimes 4471556Srgrimes INTOFF; 4481556Srgrimes saveifs = ifsfirst; 4491556Srgrimes savelastp = ifslastp; 4501556Srgrimes saveargbackq = argbackq; 4518855Srgrimes saveherefd = herefd; 4521556Srgrimes herefd = -1; 4531556Srgrimes p = grabstackstr(dest); 4541556Srgrimes evalbackcmd(cmd, &in); 4551556Srgrimes ungrabstackstr(p, dest); 4561556Srgrimes ifsfirst = saveifs; 4571556Srgrimes ifslastp = savelastp; 4581556Srgrimes argbackq = saveargbackq; 4591556Srgrimes herefd = saveherefd; 4601556Srgrimes 4611556Srgrimes p = in.buf; 4621556Srgrimes lastc = '\0'; 463115424Sfenner nnl = 0; 464115424Sfenner /* Don't copy trailing newlines */ 4651556Srgrimes for (;;) { 4661556Srgrimes if (--in.nleft < 0) { 4671556Srgrimes if (in.fd < 0) 4681556Srgrimes break; 4691556Srgrimes while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 4701556Srgrimes TRACE(("expbackq: read returns %d\n", i)); 4711556Srgrimes if (i <= 0) 4721556Srgrimes break; 4731556Srgrimes p = buf; 4741556Srgrimes in.nleft = i - 1; 4751556Srgrimes } 4761556Srgrimes lastc = *p++; 4771556Srgrimes if (lastc != '\0') { 47883675Stegge if (quotes && syntax[(int)lastc] == CCTL) 4791556Srgrimes STPUTC(CTLESC, dest); 480115424Sfenner if (lastc == '\n') { 481115424Sfenner nnl++; 482115424Sfenner } else { 483115424Sfenner while (nnl > 0) { 484115424Sfenner nnl--; 485115424Sfenner STPUTC('\n', dest); 486115424Sfenner } 487115424Sfenner STPUTC(lastc, dest); 488115424Sfenner } 4891556Srgrimes } 4901556Srgrimes } 49117987Speter 4921556Srgrimes if (in.fd >= 0) 4931556Srgrimes close(in.fd); 4941556Srgrimes if (in.buf) 4951556Srgrimes ckfree(in.buf); 4961556Srgrimes if (in.jp) 49745916Scracauer exitstatus = waitforjob(in.jp, (int *)NULL); 4981556Srgrimes if (quoted == 0) 4991556Srgrimes recordregion(startloc, dest - stackblock(), 0); 5001556Srgrimes TRACE(("evalbackq: size=%d: \"%.*s\"\n", 5011556Srgrimes (dest - stackblock()) - startloc, 5021556Srgrimes (dest - stackblock()) - startloc, 5031556Srgrimes stackblock() + startloc)); 5041556Srgrimes expdest = dest; 5051556Srgrimes INTON; 5061556Srgrimes} 5071556Srgrimes 5081556Srgrimes 5091556Srgrimes 51017987SpeterSTATIC int 51190111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc, 51290111Simp int varflags) 51317987Speter{ 51417987Speter char *startp; 51517987Speter char *loc = NULL; 51645514Stegge char *q; 51717987Speter int c = 0; 51817987Speter int saveherefd = herefd; 51917987Speter struct nodelist *saveargbackq = argbackq; 52020425Ssteve int amount; 52120425Ssteve 52217987Speter herefd = -1; 523206150Sjilles argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || 524206147Sjilles subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ? 525206150Sjilles EXP_CASE : 0) | EXP_TILDE); 52617987Speter STACKSTRNUL(expdest); 52717987Speter herefd = saveherefd; 52817987Speter argbackq = saveargbackq; 52917987Speter startp = stackblock() + startloc; 53020425Ssteve if (str == NULL) 53120425Ssteve str = stackblock() + strloc; 53217987Speter 53317987Speter switch (subtype) { 53417987Speter case VSASSIGN: 53517987Speter setvar(str, startp, 0); 53620425Ssteve amount = startp - expdest; 53720425Ssteve STADJUST(amount, expdest); 53817987Speter varflags &= ~VSNUL; 53917987Speter if (c != 0) 54017987Speter *loc = c; 54117987Speter return 1; 54217987Speter 54317987Speter case VSQUESTION: 54417987Speter if (*p != CTLENDVAR) { 545201366Sjilles outfmt(out2, "%s\n", startp); 54617987Speter error((char *)NULL); 54717987Speter } 548112254Sru error("%.*s: parameter %snot set", (int)(p - str - 1), 54917987Speter str, (varflags & VSNUL) ? "null or " 55017987Speter : nullstr); 55117987Speter return 0; 55217987Speter 55317987Speter case VSTRIMLEFT: 55425233Ssteve for (loc = startp; loc < str; loc++) { 55517987Speter c = *loc; 55617987Speter *loc = '\0'; 55745514Stegge if (patmatch(str, startp, varflags & VSQUOTE)) { 55817987Speter *loc = c; 55917987Speter goto recordleft; 56017987Speter } 56117987Speter *loc = c; 56245514Stegge if ((varflags & VSQUOTE) && *loc == CTLESC) 56345514Stegge loc++; 56417987Speter } 56517987Speter return 0; 56617987Speter 56717987Speter case VSTRIMLEFTMAX: 56845514Stegge for (loc = str - 1; loc >= startp;) { 56917987Speter c = *loc; 57017987Speter *loc = '\0'; 57145514Stegge if (patmatch(str, startp, varflags & VSQUOTE)) { 57217987Speter *loc = c; 57317987Speter goto recordleft; 57417987Speter } 57517987Speter *loc = c; 57645514Stegge loc--; 57745514Stegge if ((varflags & VSQUOTE) && loc > startp && 57845514Stegge *(loc - 1) == CTLESC) { 57945514Stegge for (q = startp; q < loc; q++) 58045514Stegge if (*q == CTLESC) 58145514Stegge q++; 58245514Stegge if (q > loc) 58345514Stegge loc--; 58445514Stegge } 58517987Speter } 58617987Speter return 0; 58717987Speter 58817987Speter case VSTRIMRIGHT: 58945514Stegge for (loc = str - 1; loc >= startp;) { 59045514Stegge if (patmatch(str, loc, varflags & VSQUOTE)) { 59120425Ssteve amount = loc - expdest; 59220425Ssteve STADJUST(amount, expdest); 59317987Speter return 1; 59417987Speter } 59545514Stegge loc--; 59645514Stegge if ((varflags & VSQUOTE) && loc > startp && 597155301Sschweikh *(loc - 1) == CTLESC) { 59845514Stegge for (q = startp; q < loc; q++) 59945514Stegge if (*q == CTLESC) 60045514Stegge q++; 60145514Stegge if (q > loc) 60245514Stegge loc--; 60345514Stegge } 60417987Speter } 60517987Speter return 0; 60617987Speter 60717987Speter case VSTRIMRIGHTMAX: 60817987Speter for (loc = startp; loc < str - 1; loc++) { 60945514Stegge if (patmatch(str, loc, varflags & VSQUOTE)) { 61020425Ssteve amount = loc - expdest; 61120425Ssteve STADJUST(amount, expdest); 61217987Speter return 1; 61317987Speter } 61445514Stegge if ((varflags & VSQUOTE) && *loc == CTLESC) 61545514Stegge loc++; 61617987Speter } 61717987Speter return 0; 61817987Speter 61917987Speter 62017987Speter default: 62117987Speter abort(); 62217987Speter } 62317987Speter 62417987Speterrecordleft: 62520425Ssteve amount = ((str - 1) - (loc - startp)) - expdest; 62620425Ssteve STADJUST(amount, expdest); 62717987Speter while (loc != str - 1) 62817987Speter *startp++ = *loc++; 62917987Speter return 1; 63017987Speter} 63117987Speter 63217987Speter 6331556Srgrimes/* 6341556Srgrimes * Expand a variable, and return a pointer to the next character in the 6351556Srgrimes * input string. 6361556Srgrimes */ 6371556Srgrimes 6381556SrgrimesSTATIC char * 63990111Simpevalvar(char *p, int flag) 64017987Speter{ 6411556Srgrimes int subtype; 6421556Srgrimes int varflags; 6431556Srgrimes char *var; 6441556Srgrimes char *val; 64545644Stegge int patloc; 6461556Srgrimes int c; 6471556Srgrimes int set; 6481556Srgrimes int special; 6491556Srgrimes int startloc; 65017987Speter int varlen; 65117987Speter int easy; 652108935Stjr int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 6531556Srgrimes 654164081Sstefanf varflags = (unsigned char)*p++; 6551556Srgrimes subtype = varflags & VSTYPE; 6561556Srgrimes var = p; 6571556Srgrimes special = 0; 6581556Srgrimes if (! is_name(*p)) 6591556Srgrimes special = 1; 6601556Srgrimes p = strchr(p, '=') + 1; 6611556Srgrimesagain: /* jump here after setting a variable with ${var=text} */ 662179022Sstefanf if (varflags & VSLINENO) { 663179022Sstefanf set = 1; 664179022Sstefanf special = 0; 665179022Sstefanf val = var; 666179022Sstefanf p[-1] = '\0'; /* temporarily overwrite '=' to have \0 667179022Sstefanf terminated string */ 668179022Sstefanf } else if (special) { 66925233Ssteve set = varisset(var, varflags & VSNUL); 6701556Srgrimes val = NULL; 6711556Srgrimes } else { 67260592Scracauer val = bltinlookup(var, 1); 67317987Speter if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 6741556Srgrimes val = NULL; 6751556Srgrimes set = 0; 6761556Srgrimes } else 6771556Srgrimes set = 1; 6781556Srgrimes } 67917987Speter varlen = 0; 6801556Srgrimes startloc = expdest - stackblock(); 681198454Sjilles if (!set && uflag && *var != '@' && *var != '*') { 68296939Stjr switch (subtype) { 68396939Stjr case VSNORMAL: 68496939Stjr case VSTRIMLEFT: 68596939Stjr case VSTRIMLEFTMAX: 68696939Stjr case VSTRIMRIGHT: 68796939Stjr case VSTRIMRIGHTMAX: 68896939Stjr case VSLENGTH: 689112254Sru error("%.*s: parameter not set", (int)(p - var - 1), 690112254Sru var); 69196939Stjr } 69296939Stjr } 6931556Srgrimes if (set && subtype != VSPLUS) { 6941556Srgrimes /* insert the value of the variable */ 6951556Srgrimes if (special) { 696164081Sstefanf varvalue(var, varflags & VSQUOTE, subtype, flag); 69717987Speter if (subtype == VSLENGTH) { 69825233Ssteve varlen = expdest - stackblock() - startloc; 69925233Ssteve STADJUST(-varlen, expdest); 70017987Speter } 7011556Srgrimes } else { 70220425Ssteve char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 70317987Speter : BASESYNTAX; 7041556Srgrimes 70517987Speter if (subtype == VSLENGTH) { 70617987Speter for (;*val; val++) 70717987Speter varlen++; 7081556Srgrimes } 70917987Speter else { 71017987Speter while (*val) { 71183675Stegge if (quotes && 71254132Scracauer syntax[(int)*val] == CCTL) 71317987Speter STPUTC(CTLESC, expdest); 71417987Speter STPUTC(*val++, expdest); 71517987Speter } 71617987Speter 71717987Speter } 7181556Srgrimes } 7191556Srgrimes } 72020425Ssteve 7211556Srgrimes if (subtype == VSPLUS) 7221556Srgrimes set = ! set; 72317987Speter 72420425Ssteve easy = ((varflags & VSQUOTE) == 0 || 72517987Speter (*var == '@' && shellparam.nparam != 1)); 72617987Speter 72717987Speter 72817987Speter switch (subtype) { 72917987Speter case VSLENGTH: 73017987Speter expdest = cvtnum(varlen, expdest); 73117987Speter goto record; 73217987Speter 73317987Speter case VSNORMAL: 73417987Speter if (!easy) 73517987Speter break; 73617987Speterrecord: 73720425Ssteve recordregion(startloc, expdest - stackblock(), 73817987Speter varflags & VSQUOTE); 73917987Speter break; 74017987Speter 74117987Speter case VSPLUS: 74217987Speter case VSMINUS: 74317987Speter if (!set) { 7441556Srgrimes argstr(p, flag); 74517987Speter break; 74617987Speter } 74717987Speter if (easy) 74817987Speter goto record; 74917987Speter break; 75017987Speter 75117987Speter case VSTRIMLEFT: 75217987Speter case VSTRIMLEFTMAX: 75317987Speter case VSTRIMRIGHT: 75417987Speter case VSTRIMRIGHTMAX: 75517987Speter if (!set) 75617987Speter break; 75717987Speter /* 75817987Speter * Terminate the string and start recording the pattern 75917987Speter * right after it 76017987Speter */ 76117987Speter STPUTC('\0', expdest); 76245644Stegge patloc = expdest - stackblock(); 76345644Stegge if (subevalvar(p, NULL, patloc, subtype, 76438887Stegge startloc, varflags) == 0) { 76545644Stegge int amount = (expdest - stackblock() - patloc) + 1; 76625233Ssteve STADJUST(-amount, expdest); 76725233Ssteve } 76838887Stegge /* Remove any recorded regions beyond start of variable */ 76938887Stegge removerecordregions(startloc); 77038887Stegge goto record; 77117987Speter 77217987Speter case VSASSIGN: 77317987Speter case VSQUESTION: 77417987Speter if (!set) { 77520425Ssteve if (subevalvar(p, var, 0, subtype, startloc, varflags)) { 77620425Ssteve varflags &= ~VSNUL; 777155301Sschweikh /* 778155301Sschweikh * Remove any recorded regions beyond 779155301Sschweikh * start of variable 78038887Stegge */ 78138887Stegge removerecordregions(startloc); 7821556Srgrimes goto again; 78320425Ssteve } 78417987Speter break; 7851556Srgrimes } 78617987Speter if (easy) 78717987Speter goto record; 78817987Speter break; 78917987Speter 790164003Sstefanf case VSERROR: 791164003Sstefanf c = p - var - 1; 792164003Sstefanf error("${%.*s%s}: Bad substitution", c, var, 793164003Sstefanf (c > 0 && *p != CTLENDVAR) ? "..." : ""); 794164003Sstefanf 79517987Speter default: 79617987Speter abort(); 7971556Srgrimes } 798179022Sstefanf p[-1] = '='; /* recover overwritten '=' */ 79917987Speter 8001556Srgrimes if (subtype != VSNORMAL) { /* skip to end of alternative */ 8011556Srgrimes int nesting = 1; 8021556Srgrimes for (;;) { 8031556Srgrimes if ((c = *p++) == CTLESC) 8041556Srgrimes p++; 8051556Srgrimes else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 8061556Srgrimes if (set) 8071556Srgrimes argbackq = argbackq->next; 8081556Srgrimes } else if (c == CTLVAR) { 8091556Srgrimes if ((*p++ & VSTYPE) != VSNORMAL) 8101556Srgrimes nesting++; 8111556Srgrimes } else if (c == CTLENDVAR) { 8121556Srgrimes if (--nesting == 0) 8131556Srgrimes break; 8141556Srgrimes } 8151556Srgrimes } 8161556Srgrimes } 8171556Srgrimes return p; 8181556Srgrimes} 8191556Srgrimes 8201556Srgrimes 8211556Srgrimes 8221556Srgrimes/* 8231556Srgrimes * Test whether a specialized variable is set. 8241556Srgrimes */ 8251556Srgrimes 8261556SrgrimesSTATIC int 82790111Simpvarisset(char *name, int nulok) 82825233Ssteve{ 8291556Srgrimes 83025233Ssteve if (*name == '!') 831209600Sjilles return backgndpidset(); 83225233Ssteve else if (*name == '@' || *name == '*') { 8331556Srgrimes if (*shellparam.p == NULL) 8341556Srgrimes return 0; 83525233Ssteve 83625233Ssteve if (nulok) { 83725233Ssteve char **av; 83825233Ssteve 83925233Ssteve for (av = shellparam.p; *av; av++) 84025233Ssteve if (**av != '\0') 84125233Ssteve return 1; 84225233Ssteve return 0; 84325233Ssteve } 84418202Speter } else if (is_digit(*name)) { 84525233Ssteve char *ap; 84620425Ssteve int num = atoi(name); 84725233Ssteve 84825233Ssteve if (num > shellparam.nparam) 84925233Ssteve return 0; 85025233Ssteve 85125233Ssteve if (num == 0) 85225233Ssteve ap = arg0; 85325233Ssteve else 85425233Ssteve ap = shellparam.p[num - 1]; 85525233Ssteve 85625233Ssteve if (nulok && (ap == NULL || *ap == '\0')) 85725233Ssteve return 0; 8581556Srgrimes } 8591556Srgrimes return 1; 8601556Srgrimes} 8611556Srgrimes 8621556Srgrimes 8631556Srgrimes 8641556Srgrimes/* 8651556Srgrimes * Add the value of a specialized variable to the stack string. 8661556Srgrimes */ 8671556Srgrimes 8681556SrgrimesSTATIC void 869164081Sstefanfvarvalue(char *name, int quoted, int subtype, int flag) 87017987Speter{ 8711556Srgrimes int num; 8721556Srgrimes char *p; 8731556Srgrimes int i; 8741556Srgrimes char sep; 8751556Srgrimes char **ap; 8761556Srgrimes char const *syntax; 8771556Srgrimes 8781556Srgrimes#define STRTODEST(p) \ 8791556Srgrimes do {\ 880164081Sstefanf if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \ 8811556Srgrimes syntax = quoted? DQSYNTAX : BASESYNTAX; \ 8821556Srgrimes while (*p) { \ 88383675Stegge if (syntax[(int)*p] == CCTL) \ 8841556Srgrimes STPUTC(CTLESC, expdest); \ 8851556Srgrimes STPUTC(*p++, expdest); \ 8861556Srgrimes } \ 8871556Srgrimes } else \ 8881556Srgrimes while (*p) \ 8891556Srgrimes STPUTC(*p++, expdest); \ 8901556Srgrimes } while (0) 8911556Srgrimes 8921556Srgrimes 89318202Speter switch (*name) { 8941556Srgrimes case '$': 8951556Srgrimes num = rootpid; 8961556Srgrimes goto numvar; 8971556Srgrimes case '?': 89817987Speter num = oexitstatus; 8991556Srgrimes goto numvar; 9001556Srgrimes case '#': 9011556Srgrimes num = shellparam.nparam; 9021556Srgrimes goto numvar; 9031556Srgrimes case '!': 904209600Sjilles num = backgndpidval(); 9051556Srgrimesnumvar: 90617987Speter expdest = cvtnum(num, expdest); 9071556Srgrimes break; 9081556Srgrimes case '-': 9091556Srgrimes for (i = 0 ; i < NOPTS ; i++) { 9101556Srgrimes if (optlist[i].val) 9111556Srgrimes STPUTC(optlist[i].letter, expdest); 9121556Srgrimes } 9131556Srgrimes break; 9141556Srgrimes case '@': 915164081Sstefanf if (flag & EXP_FULL && quoted) { 91638887Stegge for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 91738887Stegge STRTODEST(p); 91838887Stegge if (*ap) 91938887Stegge STPUTC('\0', expdest); 92038887Stegge } 92138887Stegge break; 9221556Srgrimes } 923102410Scharnier /* FALLTHROUGH */ 9241556Srgrimes case '*': 925149825Srse if (ifsset()) 92638887Stegge sep = ifsval()[0]; 92738887Stegge else 92838887Stegge sep = ' '; 9291556Srgrimes for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 9301556Srgrimes STRTODEST(p); 93138887Stegge if (*ap && sep) 9321556Srgrimes STPUTC(sep, expdest); 9331556Srgrimes } 9341556Srgrimes break; 9351556Srgrimes case '0': 9361556Srgrimes p = arg0; 9371556Srgrimes STRTODEST(p); 9381556Srgrimes break; 9391556Srgrimes default: 94018202Speter if (is_digit(*name)) { 94118202Speter num = atoi(name); 94218202Speter if (num > 0 && num <= shellparam.nparam) { 94318202Speter p = shellparam.p[num - 1]; 94418202Speter STRTODEST(p); 94518202Speter } 9461556Srgrimes } 9471556Srgrimes break; 9481556Srgrimes } 9491556Srgrimes} 9501556Srgrimes 9511556Srgrimes 9521556Srgrimes 9531556Srgrimes/* 9541556Srgrimes * Record the the fact that we have to scan this region of the 9551556Srgrimes * string for IFS characters. 9561556Srgrimes */ 9571556Srgrimes 9581556SrgrimesSTATIC void 959194975Sjillesrecordregion(int start, int end, int inquotes) 96017987Speter{ 96125233Ssteve struct ifsregion *ifsp; 9621556Srgrimes 9631556Srgrimes if (ifslastp == NULL) { 9641556Srgrimes ifsp = &ifsfirst; 9651556Srgrimes } else { 966194975Sjilles if (ifslastp->endoff == start 967194975Sjilles && ifslastp->inquotes == inquotes) { 968194975Sjilles /* extend previous area */ 969194975Sjilles ifslastp->endoff = end; 970194975Sjilles return; 971194975Sjilles } 9721556Srgrimes ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 9731556Srgrimes ifslastp->next = ifsp; 9741556Srgrimes } 9751556Srgrimes ifslastp = ifsp; 9761556Srgrimes ifslastp->next = NULL; 9771556Srgrimes ifslastp->begoff = start; 9781556Srgrimes ifslastp->endoff = end; 979194975Sjilles ifslastp->inquotes = inquotes; 9801556Srgrimes} 9811556Srgrimes 9821556Srgrimes 9831556Srgrimes 9841556Srgrimes/* 9851556Srgrimes * Break the argument string into pieces based upon IFS and add the 9861556Srgrimes * strings to the argument list. The regions of the string to be 9871556Srgrimes * searched for IFS characters have been stored by recordregion. 988212243Sjilles * CTLESC characters are preserved but have little effect in this pass 989212243Sjilles * other than escaping CTL* characters. In particular, they do not escape 990212243Sjilles * IFS characters: that should be done with the ifsregion mechanism. 991212243Sjilles * CTLQUOTEMARK characters are used to preserve empty quoted strings. 992212243Sjilles * This pass treats them as a regular character, making the string non-empty. 993212243Sjilles * Later, they are removed along with the other CTL* characters. 9941556Srgrimes */ 9951556SrgrimesSTATIC void 99690111Simpifsbreakup(char *string, struct arglist *arglist) 99790111Simp{ 9981556Srgrimes struct ifsregion *ifsp; 9991556Srgrimes struct strlist *sp; 10001556Srgrimes char *start; 100125233Ssteve char *p; 10021556Srgrimes char *q; 1003201053Sjilles const char *ifs; 1004194975Sjilles const char *ifsspc; 1005194975Sjilles int had_param_ch = 0; 10061556Srgrimes 1007194975Sjilles start = string; 100817987Speter 1009194975Sjilles if (ifslastp == NULL) { 1010194975Sjilles /* Return entire argument, IFS doesn't apply to any of it */ 1011194975Sjilles sp = (struct strlist *)stalloc(sizeof *sp); 1012194975Sjilles sp->text = start; 1013194975Sjilles *arglist->lastp = sp; 1014194975Sjilles arglist->lastp = &sp->next; 1015194975Sjilles return; 1016194975Sjilles } 1017194975Sjilles 1018194975Sjilles ifs = ifsset() ? ifsval() : " \t\n"; 1019194975Sjilles 1020194975Sjilles for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { 1021194975Sjilles p = string + ifsp->begoff; 1022194975Sjilles while (p < string + ifsp->endoff) { 1023194975Sjilles q = p; 1024194975Sjilles if (*p == CTLESC) 1025194975Sjilles p++; 1026194975Sjilles if (ifsp->inquotes) { 1027194975Sjilles /* Only NULs (should be from "$@") end args */ 1028194977Sjilles had_param_ch = 1; 1029194975Sjilles if (*p != 0) { 10301556Srgrimes p++; 1031194975Sjilles continue; 1032194975Sjilles } 1033194975Sjilles ifsspc = NULL; 1034194975Sjilles } else { 1035194975Sjilles if (!strchr(ifs, *p)) { 1036194977Sjilles had_param_ch = 1; 103738887Stegge p++; 1038194975Sjilles continue; 1039194975Sjilles } 1040194975Sjilles ifsspc = strchr(" \t\n", *p); 1041194975Sjilles 1042194975Sjilles /* Ignore IFS whitespace at start */ 1043194975Sjilles if (q == start && ifsspc != NULL) { 1044194975Sjilles p++; 10451556Srgrimes start = p; 1046194975Sjilles continue; 1047194975Sjilles } 1048194977Sjilles had_param_ch = 0; 10491556Srgrimes } 1050194975Sjilles 1051194975Sjilles /* Save this argument... */ 1052194975Sjilles *q = '\0'; 10531556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 10541556Srgrimes sp->text = start; 10551556Srgrimes *arglist->lastp = sp; 10561556Srgrimes arglist->lastp = &sp->next; 1057194975Sjilles p++; 1058194975Sjilles 1059194975Sjilles if (ifsspc != NULL) { 1060194975Sjilles /* Ignore further trailing IFS whitespace */ 1061194975Sjilles for (; p < string + ifsp->endoff; p++) { 1062194975Sjilles q = p; 1063194975Sjilles if (*p == CTLESC) 1064194975Sjilles p++; 1065194975Sjilles if (strchr(ifs, *p) == NULL) { 1066194975Sjilles p = q; 1067194975Sjilles break; 1068194975Sjilles } 1069194975Sjilles if (strchr(" \t\n", *p) == NULL) { 1070194975Sjilles p++; 1071194975Sjilles break; 1072194975Sjilles } 1073194975Sjilles } 1074194975Sjilles } 1075194975Sjilles start = p; 10761556Srgrimes } 1077194975Sjilles } 1078194975Sjilles 1079194975Sjilles /* 1080194975Sjilles * Save anything left as an argument. 1081194975Sjilles * Traditionally we have treated 'IFS=':'; set -- x$IFS' as 1082194975Sjilles * generating 2 arguments, the second of which is empty. 1083194975Sjilles * Some recent clarification of the Posix spec say that it 1084194975Sjilles * should only generate one.... 1085194975Sjilles */ 1086194975Sjilles if (had_param_ch || *start != 0) { 10871556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 10881556Srgrimes sp->text = start; 10891556Srgrimes *arglist->lastp = sp; 10901556Srgrimes arglist->lastp = &sp->next; 10911556Srgrimes } 10921556Srgrimes} 10931556Srgrimes 10941556Srgrimes 1095212243SjillesSTATIC char expdir[PATH_MAX]; 1096212243Sjilles#define expdir_end (expdir + sizeof(expdir)) 10971556Srgrimes 10981556Srgrimes/* 1099212243Sjilles * Perform pathname generation and remove control characters. 1100212243Sjilles * At this point, the only control characters should be CTLESC and CTLQUOTEMARK. 1101212243Sjilles * The results are stored in the list exparg. 11021556Srgrimes */ 11031556SrgrimesSTATIC void 110490111Simpexpandmeta(struct strlist *str, int flag __unused) 110517987Speter{ 11061556Srgrimes char *p; 11071556Srgrimes struct strlist **savelastp; 11081556Srgrimes struct strlist *sp; 11091556Srgrimes char c; 11101556Srgrimes /* TODO - EXP_REDIR */ 11111556Srgrimes 11121556Srgrimes while (str) { 11131556Srgrimes if (fflag) 11141556Srgrimes goto nometa; 11151556Srgrimes p = str->text; 11161556Srgrimes for (;;) { /* fast check for meta chars */ 11171556Srgrimes if ((c = *p++) == '\0') 11181556Srgrimes goto nometa; 1119211646Sjilles if (c == '*' || c == '?' || c == '[') 11201556Srgrimes break; 11211556Srgrimes } 11221556Srgrimes savelastp = exparg.lastp; 11231556Srgrimes INTOFF; 11241556Srgrimes expmeta(expdir, str->text); 11251556Srgrimes INTON; 11261556Srgrimes if (exparg.lastp == savelastp) { 11278855Srgrimes /* 11288855Srgrimes * no matches 11291556Srgrimes */ 11301556Srgrimesnometa: 11311556Srgrimes *exparg.lastp = str; 11321556Srgrimes rmescapes(str->text); 11331556Srgrimes exparg.lastp = &str->next; 11341556Srgrimes } else { 11351556Srgrimes *exparg.lastp = NULL; 11361556Srgrimes *savelastp = sp = expsort(*savelastp); 11371556Srgrimes while (sp->next != NULL) 11381556Srgrimes sp = sp->next; 11391556Srgrimes exparg.lastp = &sp->next; 11401556Srgrimes } 11411556Srgrimes str = str->next; 11421556Srgrimes } 11431556Srgrimes} 11441556Srgrimes 11451556Srgrimes 11461556Srgrimes/* 11471556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion. 11481556Srgrimes */ 11491556Srgrimes 11501556SrgrimesSTATIC void 115190111Simpexpmeta(char *enddir, char *name) 115290111Simp{ 115325233Ssteve char *p; 11541556Srgrimes char *q; 11551556Srgrimes char *start; 11561556Srgrimes char *endname; 11571556Srgrimes int metaflag; 11581556Srgrimes struct stat statb; 11591556Srgrimes DIR *dirp; 11601556Srgrimes struct dirent *dp; 11611556Srgrimes int atend; 11621556Srgrimes int matchdot; 1163207944Sjilles int esc; 11641556Srgrimes 11651556Srgrimes metaflag = 0; 11661556Srgrimes start = name; 1167207944Sjilles for (p = name; esc = 0, *p; p += esc + 1) { 11681556Srgrimes if (*p == '*' || *p == '?') 11691556Srgrimes metaflag = 1; 11701556Srgrimes else if (*p == '[') { 11711556Srgrimes q = p + 1; 117226488Sache if (*q == '!' || *q == '^') 11731556Srgrimes q++; 11741556Srgrimes for (;;) { 117538887Stegge while (*q == CTLQUOTEMARK) 117638887Stegge q++; 11771556Srgrimes if (*q == CTLESC) 11781556Srgrimes q++; 11791556Srgrimes if (*q == '/' || *q == '\0') 11801556Srgrimes break; 11811556Srgrimes if (*++q == ']') { 11821556Srgrimes metaflag = 1; 11831556Srgrimes break; 11841556Srgrimes } 11851556Srgrimes } 11861556Srgrimes } else if (*p == '\0') 11871556Srgrimes break; 118838887Stegge else if (*p == CTLQUOTEMARK) 118938887Stegge continue; 1190207944Sjilles else { 1191207944Sjilles if (*p == CTLESC) 1192207944Sjilles esc++; 1193207944Sjilles if (p[esc] == '/') { 1194207944Sjilles if (metaflag) 1195207944Sjilles break; 1196207944Sjilles start = p + esc + 1; 1197207944Sjilles } 11981556Srgrimes } 11991556Srgrimes } 12001556Srgrimes if (metaflag == 0) { /* we've reached the end of the file name */ 12011556Srgrimes if (enddir != expdir) 12021556Srgrimes metaflag++; 12031556Srgrimes for (p = name ; ; p++) { 120438887Stegge if (*p == CTLQUOTEMARK) 120538887Stegge continue; 12061556Srgrimes if (*p == CTLESC) 12071556Srgrimes p++; 12081556Srgrimes *enddir++ = *p; 12091556Srgrimes if (*p == '\0') 12101556Srgrimes break; 1211211155Sjilles if (enddir == expdir_end) 1212211155Sjilles return; 12131556Srgrimes } 1214147812Sdelphij if (metaflag == 0 || lstat(expdir, &statb) >= 0) 12151556Srgrimes addfname(expdir); 12161556Srgrimes return; 12171556Srgrimes } 12181556Srgrimes endname = p; 12191556Srgrimes if (start != name) { 12201556Srgrimes p = name; 12211556Srgrimes while (p < start) { 122238887Stegge while (*p == CTLQUOTEMARK) 122338887Stegge p++; 12241556Srgrimes if (*p == CTLESC) 12251556Srgrimes p++; 12261556Srgrimes *enddir++ = *p++; 1227211155Sjilles if (enddir == expdir_end) 1228211155Sjilles return; 12291556Srgrimes } 12301556Srgrimes } 12311556Srgrimes if (enddir == expdir) { 12321556Srgrimes p = "."; 12331556Srgrimes } else if (enddir == expdir + 1 && *expdir == '/') { 12341556Srgrimes p = "/"; 12351556Srgrimes } else { 12361556Srgrimes p = expdir; 12371556Srgrimes enddir[-1] = '\0'; 12381556Srgrimes } 12391556Srgrimes if ((dirp = opendir(p)) == NULL) 12401556Srgrimes return; 12411556Srgrimes if (enddir != expdir) 12421556Srgrimes enddir[-1] = '/'; 12431556Srgrimes if (*endname == 0) { 12441556Srgrimes atend = 1; 12451556Srgrimes } else { 12461556Srgrimes atend = 0; 1247207944Sjilles *endname = '\0'; 1248207944Sjilles endname += esc + 1; 12491556Srgrimes } 12501556Srgrimes matchdot = 0; 125138887Stegge p = start; 125238887Stegge while (*p == CTLQUOTEMARK) 125338887Stegge p++; 125438887Stegge if (*p == CTLESC) 125538887Stegge p++; 125638887Stegge if (*p == '.') 12571556Srgrimes matchdot++; 12581556Srgrimes while (! int_pending() && (dp = readdir(dirp)) != NULL) { 12591556Srgrimes if (dp->d_name[0] == '.' && ! matchdot) 12601556Srgrimes continue; 126145514Stegge if (patmatch(start, dp->d_name, 0)) { 1262211155Sjilles if (enddir + dp->d_namlen + 1 > expdir_end) 1263211155Sjilles continue; 1264211155Sjilles memcpy(enddir, dp->d_name, dp->d_namlen + 1); 1265211155Sjilles if (atend) 12661556Srgrimes addfname(expdir); 1267211155Sjilles else { 1268211155Sjilles if (enddir + dp->d_namlen + 2 > expdir_end) 126917987Speter continue; 1270211155Sjilles enddir[dp->d_namlen] = '/'; 1271211155Sjilles enddir[dp->d_namlen + 1] = '\0'; 1272211155Sjilles expmeta(enddir + dp->d_namlen + 1, endname); 12731556Srgrimes } 12741556Srgrimes } 12751556Srgrimes } 12761556Srgrimes closedir(dirp); 12771556Srgrimes if (! atend) 1278207944Sjilles endname[-esc - 1] = esc ? CTLESC : '/'; 12791556Srgrimes} 12801556Srgrimes 12811556Srgrimes 12821556Srgrimes/* 12831556Srgrimes * Add a file name to the list. 12841556Srgrimes */ 12851556Srgrimes 12861556SrgrimesSTATIC void 128790111Simpaddfname(char *name) 128890111Simp{ 12891556Srgrimes char *p; 12901556Srgrimes struct strlist *sp; 12911556Srgrimes 12921556Srgrimes p = stalloc(strlen(name) + 1); 12931556Srgrimes scopy(name, p); 12941556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 12951556Srgrimes sp->text = p; 12961556Srgrimes *exparg.lastp = sp; 12971556Srgrimes exparg.lastp = &sp->next; 12981556Srgrimes} 12991556Srgrimes 13001556Srgrimes 13011556Srgrimes/* 13021556Srgrimes * Sort the results of file name expansion. It calculates the number of 13031556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the 13041556Srgrimes * work. 13051556Srgrimes */ 13061556Srgrimes 13071556SrgrimesSTATIC struct strlist * 130890111Simpexpsort(struct strlist *str) 130990111Simp{ 13101556Srgrimes int len; 13111556Srgrimes struct strlist *sp; 13121556Srgrimes 13131556Srgrimes len = 0; 13141556Srgrimes for (sp = str ; sp ; sp = sp->next) 13151556Srgrimes len++; 13161556Srgrimes return msort(str, len); 13171556Srgrimes} 13181556Srgrimes 13191556Srgrimes 13201556SrgrimesSTATIC struct strlist * 132190111Simpmsort(struct strlist *list, int len) 132217987Speter{ 132317987Speter struct strlist *p, *q = NULL; 13241556Srgrimes struct strlist **lpp; 13251556Srgrimes int half; 13261556Srgrimes int n; 13271556Srgrimes 13281556Srgrimes if (len <= 1) 13291556Srgrimes return list; 13308855Srgrimes half = len >> 1; 13311556Srgrimes p = list; 13321556Srgrimes for (n = half ; --n >= 0 ; ) { 13331556Srgrimes q = p; 13341556Srgrimes p = p->next; 13351556Srgrimes } 13361556Srgrimes q->next = NULL; /* terminate first half of list */ 13371556Srgrimes q = msort(list, half); /* sort first half of list */ 13381556Srgrimes p = msort(p, len - half); /* sort second half */ 13391556Srgrimes lpp = &list; 13401556Srgrimes for (;;) { 13411556Srgrimes if (strcmp(p->text, q->text) < 0) { 13421556Srgrimes *lpp = p; 13431556Srgrimes lpp = &p->next; 13441556Srgrimes if ((p = *lpp) == NULL) { 13451556Srgrimes *lpp = q; 13461556Srgrimes break; 13471556Srgrimes } 13481556Srgrimes } else { 13491556Srgrimes *lpp = q; 13501556Srgrimes lpp = &q->next; 13511556Srgrimes if ((q = *lpp) == NULL) { 13521556Srgrimes *lpp = p; 13531556Srgrimes break; 13541556Srgrimes } 13551556Srgrimes } 13561556Srgrimes } 13571556Srgrimes return list; 13581556Srgrimes} 13591556Srgrimes 13601556Srgrimes 13611556Srgrimes 13621556Srgrimes/* 13631556Srgrimes * Returns true if the pattern matches the string. 13641556Srgrimes */ 13651556Srgrimes 13661556Srgrimesint 1367200956Sjillespatmatch(const char *pattern, const char *string, int squoted) 136890111Simp{ 1369200956Sjilles const char *p, *q; 137025233Ssteve char c; 13711556Srgrimes 13721556Srgrimes p = pattern; 13731556Srgrimes q = string; 13741556Srgrimes for (;;) { 13751556Srgrimes switch (c = *p++) { 13761556Srgrimes case '\0': 13771556Srgrimes goto breakloop; 13781556Srgrimes case CTLESC: 137945514Stegge if (squoted && *q == CTLESC) 138045514Stegge q++; 13811556Srgrimes if (*q++ != *p++) 13821556Srgrimes return 0; 13831556Srgrimes break; 138438887Stegge case CTLQUOTEMARK: 138538887Stegge continue; 13861556Srgrimes case '?': 138745514Stegge if (squoted && *q == CTLESC) 138845514Stegge q++; 13891556Srgrimes if (*q++ == '\0') 13901556Srgrimes return 0; 13911556Srgrimes break; 13921556Srgrimes case '*': 13931556Srgrimes c = *p; 139438887Stegge while (c == CTLQUOTEMARK || c == '*') 139538887Stegge c = *++p; 139638887Stegge if (c != CTLESC && c != CTLQUOTEMARK && 139738887Stegge c != '?' && c != '*' && c != '[') { 13981556Srgrimes while (*q != c) { 139945514Stegge if (squoted && *q == CTLESC && 140045514Stegge q[1] == c) 140145514Stegge break; 14021556Srgrimes if (*q == '\0') 14031556Srgrimes return 0; 140445514Stegge if (squoted && *q == CTLESC) 140545514Stegge q++; 14061556Srgrimes q++; 14071556Srgrimes } 14081556Srgrimes } 14091556Srgrimes do { 1410211646Sjilles if (patmatch(p, q, squoted)) 14111556Srgrimes return 1; 141245514Stegge if (squoted && *q == CTLESC) 141345514Stegge q++; 14141556Srgrimes } while (*q++ != '\0'); 14151556Srgrimes return 0; 14161556Srgrimes case '[': { 1417200956Sjilles const char *endp; 14181556Srgrimes int invert, found; 14191556Srgrimes char chr; 14201556Srgrimes 14211556Srgrimes endp = p; 142226488Sache if (*endp == '!' || *endp == '^') 14231556Srgrimes endp++; 14241556Srgrimes for (;;) { 142538887Stegge while (*endp == CTLQUOTEMARK) 142638887Stegge endp++; 14271556Srgrimes if (*endp == '\0') 14281556Srgrimes goto dft; /* no matching ] */ 14291556Srgrimes if (*endp == CTLESC) 14301556Srgrimes endp++; 14311556Srgrimes if (*++endp == ']') 14321556Srgrimes break; 14331556Srgrimes } 14341556Srgrimes invert = 0; 143526488Sache if (*p == '!' || *p == '^') { 14361556Srgrimes invert++; 14371556Srgrimes p++; 14381556Srgrimes } 14391556Srgrimes found = 0; 14401556Srgrimes chr = *q++; 144145514Stegge if (squoted && chr == CTLESC) 144245514Stegge chr = *q++; 144317987Speter if (chr == '\0') 144417987Speter return 0; 14451556Srgrimes c = *p++; 14461556Srgrimes do { 144738887Stegge if (c == CTLQUOTEMARK) 144838887Stegge continue; 14491556Srgrimes if (c == CTLESC) 14501556Srgrimes c = *p++; 14511556Srgrimes if (*p == '-' && p[1] != ']') { 14521556Srgrimes p++; 145338887Stegge while (*p == CTLQUOTEMARK) 145438887Stegge p++; 14551556Srgrimes if (*p == CTLESC) 14561556Srgrimes p++; 145717557Sache if ( collate_range_cmp(chr, c) >= 0 145817557Sache && collate_range_cmp(chr, *p) <= 0 145917525Sache ) 14601556Srgrimes found = 1; 14611556Srgrimes p++; 14621556Srgrimes } else { 14631556Srgrimes if (chr == c) 14641556Srgrimes found = 1; 14651556Srgrimes } 14661556Srgrimes } while ((c = *p++) != ']'); 14671556Srgrimes if (found == invert) 14681556Srgrimes return 0; 14691556Srgrimes break; 14701556Srgrimes } 14711556Srgrimesdft: default: 147245514Stegge if (squoted && *q == CTLESC) 147345514Stegge q++; 14741556Srgrimes if (*q++ != c) 14751556Srgrimes return 0; 14761556Srgrimes break; 14771556Srgrimes } 14781556Srgrimes } 14791556Srgrimesbreakloop: 14801556Srgrimes if (*q != '\0') 14811556Srgrimes return 0; 14821556Srgrimes return 1; 14831556Srgrimes} 14841556Srgrimes 14851556Srgrimes 14861556Srgrimes 14871556Srgrimes/* 1488212243Sjilles * Remove any CTLESC and CTLQUOTEMARK characters from a string. 14891556Srgrimes */ 14901556Srgrimes 14911556Srgrimesvoid 149290111Simprmescapes(char *str) 149338887Stegge{ 149425233Ssteve char *p, *q; 14951556Srgrimes 14961556Srgrimes p = str; 149738887Stegge while (*p != CTLESC && *p != CTLQUOTEMARK) { 14981556Srgrimes if (*p++ == '\0') 14991556Srgrimes return; 15001556Srgrimes } 15011556Srgrimes q = p; 15021556Srgrimes while (*p) { 150338887Stegge if (*p == CTLQUOTEMARK) { 150438887Stegge p++; 150538887Stegge continue; 150638887Stegge } 15071556Srgrimes if (*p == CTLESC) 15081556Srgrimes p++; 15091556Srgrimes *q++ = *p++; 15101556Srgrimes } 15111556Srgrimes *q = '\0'; 15121556Srgrimes} 15131556Srgrimes 15141556Srgrimes 15151556Srgrimes 15161556Srgrimes/* 15171556Srgrimes * See if a pattern matches in a case statement. 15181556Srgrimes */ 15191556Srgrimes 15201556Srgrimesint 1521200956Sjillescasematch(union node *pattern, const char *val) 152290111Simp{ 15231556Srgrimes struct stackmark smark; 15241556Srgrimes int result; 15251556Srgrimes char *p; 15261556Srgrimes 15271556Srgrimes setstackmark(&smark); 15281556Srgrimes argbackq = pattern->narg.backquote; 15291556Srgrimes STARTSTACKSTR(expdest); 15301556Srgrimes ifslastp = NULL; 15311556Srgrimes argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 15321556Srgrimes STPUTC('\0', expdest); 15331556Srgrimes p = grabstackstr(expdest); 153445514Stegge result = patmatch(p, val, 0); 15351556Srgrimes popstackmark(&smark); 15361556Srgrimes return result; 15371556Srgrimes} 153817987Speter 153917987Speter/* 154017987Speter * Our own itoa(). 154117987Speter */ 154217987Speter 154317987SpeterSTATIC char * 154490111Simpcvtnum(int num, char *buf) 154590111Simp{ 154617987Speter char temp[32]; 154717987Speter int neg = num < 0; 154817987Speter char *p = temp + 31; 154917987Speter 155017987Speter temp[31] = '\0'; 155117987Speter 155217987Speter do { 155317987Speter *--p = num % 10 + '0'; 155417987Speter } while ((num /= 10) != 0); 155517987Speter 155617987Speter if (neg) 155717987Speter *--p = '-'; 155817987Speter 155917987Speter while (*p) 156017987Speter STPUTC(*p++, buf); 156117987Speter return buf; 156217987Speter} 1563108286Stjr 1564108286Stjr/* 1565108286Stjr * Do most of the work for wordexp(3). 1566108286Stjr */ 1567108286Stjr 1568108286Stjrint 1569108286Stjrwordexpcmd(int argc, char **argv) 1570108286Stjr{ 1571108286Stjr size_t len; 1572108286Stjr int i; 1573108286Stjr 1574108286Stjr out1fmt("%08x", argc - 1); 1575108286Stjr for (i = 1, len = 0; i < argc; i++) 1576108286Stjr len += strlen(argv[i]); 1577108286Stjr out1fmt("%08x", (int)len); 1578108286Stjr for (i = 1; i < argc; i++) { 1579108286Stjr out1str(argv[i]); 1580108286Stjr out1c('\0'); 1581108286Stjr } 1582108286Stjr return (0); 1583108286Stjr} 1584