expand.c revision 108935
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 108935 2003-01-08 10:50:08Z 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> 55108286Stjr#include <string.h> 5617987Speter 571556Srgrimes/* 581556Srgrimes * Routines to expand arguments to commands. We have to deal with 591556Srgrimes * backquotes, shell variables, and file metacharacters. 601556Srgrimes */ 611556Srgrimes 621556Srgrimes#include "shell.h" 631556Srgrimes#include "main.h" 641556Srgrimes#include "nodes.h" 651556Srgrimes#include "eval.h" 661556Srgrimes#include "expand.h" 671556Srgrimes#include "syntax.h" 681556Srgrimes#include "parser.h" 691556Srgrimes#include "jobs.h" 701556Srgrimes#include "options.h" 711556Srgrimes#include "var.h" 721556Srgrimes#include "input.h" 731556Srgrimes#include "output.h" 741556Srgrimes#include "memalloc.h" 751556Srgrimes#include "error.h" 761556Srgrimes#include "mystring.h" 7717987Speter#include "arith.h" 7817987Speter#include "show.h" 791556Srgrimes 801556Srgrimes/* 811556Srgrimes * Structure specifying which parts of the string should be searched 821556Srgrimes * for IFS characters. 831556Srgrimes */ 841556Srgrimes 851556Srgrimesstruct ifsregion { 861556Srgrimes struct ifsregion *next; /* next region in list */ 871556Srgrimes int begoff; /* offset of start of region */ 881556Srgrimes int endoff; /* offset of end of region */ 891556Srgrimes int nulonly; /* search for nul bytes only */ 901556Srgrimes}; 911556Srgrimes 921556Srgrimes 931556Srgrimeschar *expdest; /* output of current string */ 941556Srgrimesstruct nodelist *argbackq; /* list of back quote expressions */ 951556Srgrimesstruct ifsregion ifsfirst; /* first struct in list of ifs regions */ 961556Srgrimesstruct ifsregion *ifslastp; /* last struct in list */ 971556Srgrimesstruct arglist exparg; /* holds expanded arg list */ 981556Srgrimes 9990111SimpSTATIC void argstr(char *, int); 10090111SimpSTATIC char *exptilde(char *, int); 10190111SimpSTATIC void expbackq(union node *, int, int); 10290111SimpSTATIC int subevalvar(char *, char *, int, int, int, int); 10390111SimpSTATIC char *evalvar(char *, int); 10490111SimpSTATIC int varisset(char *, int); 10590111SimpSTATIC void varvalue(char *, int, int); 10690111SimpSTATIC void recordregion(int, int, int); 10790111SimpSTATIC void removerecordregions(int); 10890111SimpSTATIC void ifsbreakup(char *, struct arglist *); 10990111SimpSTATIC void expandmeta(struct strlist *, int); 11090111SimpSTATIC void expmeta(char *, char *); 11190111SimpSTATIC void addfname(char *); 11290111SimpSTATIC struct strlist *expsort(struct strlist *); 11390111SimpSTATIC struct strlist *msort(struct strlist *, int); 11490111SimpSTATIC int pmatch(char *, char *, int); 11590111SimpSTATIC char *cvtnum(int, char *); 11690111SimpSTATIC int collate_range_cmp(int, int); 1171556Srgrimes 11890111SimpSTATIC int 11990111Simpcollate_range_cmp (int c1, int c2) 12019281Sache{ 12119281Sache static char s1[2], s2[2]; 12219281Sache int ret; 12319281Sache 12419281Sache c1 &= UCHAR_MAX; 12519281Sache c2 &= UCHAR_MAX; 12619281Sache if (c1 == c2) 12719281Sache return (0); 12819281Sache s1[0] = c1; 12919281Sache s2[0] = c2; 13019281Sache if ((ret = strcoll(s1, s2)) != 0) 13119281Sache return (ret); 13219281Sache return (c1 - c2); 13319281Sache} 13419281Sache 1351556Srgrimes/* 1361556Srgrimes * Expand shell variables and backquotes inside a here document. 13790111Simp * union node *arg the document 13890111Simp * int fd; where to write the expanded version 1391556Srgrimes */ 1401556Srgrimes 1411556Srgrimesvoid 14290111Simpexpandhere(union node *arg, int fd) 14390111Simp{ 1441556Srgrimes herefd = fd; 1451556Srgrimes expandarg(arg, (struct arglist *)NULL, 0); 14639137Stegge xwrite(fd, stackblock(), expdest - stackblock()); 1471556Srgrimes} 1481556Srgrimes 1491556Srgrimes 1501556Srgrimes/* 1511556Srgrimes * Perform variable substitution and command substitution on an argument, 1521556Srgrimes * placing the resulting list of arguments in arglist. If EXP_FULL is true, 1531556Srgrimes * perform splitting and file name expansion. When arglist is NULL, perform 1541556Srgrimes * here document expansion. 1551556Srgrimes */ 1561556Srgrimes 1571556Srgrimesvoid 15890111Simpexpandarg(union node *arg, struct arglist *arglist, int flag) 15917987Speter{ 1601556Srgrimes struct strlist *sp; 1611556Srgrimes char *p; 1621556Srgrimes 1631556Srgrimes argbackq = arg->narg.backquote; 1641556Srgrimes STARTSTACKSTR(expdest); 1651556Srgrimes ifsfirst.next = NULL; 1661556Srgrimes ifslastp = NULL; 1671556Srgrimes argstr(arg->narg.text, flag); 1681556Srgrimes if (arglist == NULL) { 1691556Srgrimes return; /* here document expanded */ 1701556Srgrimes } 1711556Srgrimes STPUTC('\0', expdest); 1721556Srgrimes p = grabstackstr(expdest); 1731556Srgrimes exparg.lastp = &exparg.list; 1741556Srgrimes /* 1751556Srgrimes * TODO - EXP_REDIR 1761556Srgrimes */ 1771556Srgrimes if (flag & EXP_FULL) { 1781556Srgrimes ifsbreakup(p, &exparg); 1791556Srgrimes *exparg.lastp = NULL; 1801556Srgrimes exparg.lastp = &exparg.list; 1811556Srgrimes expandmeta(exparg.list, flag); 1821556Srgrimes } else { 1831556Srgrimes if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 1841556Srgrimes rmescapes(p); 1851556Srgrimes sp = (struct strlist *)stalloc(sizeof (struct strlist)); 1861556Srgrimes sp->text = p; 1871556Srgrimes *exparg.lastp = sp; 1881556Srgrimes exparg.lastp = &sp->next; 1891556Srgrimes } 1901556Srgrimes while (ifsfirst.next != NULL) { 1911556Srgrimes struct ifsregion *ifsp; 1921556Srgrimes INTOFF; 1931556Srgrimes ifsp = ifsfirst.next->next; 1941556Srgrimes ckfree(ifsfirst.next); 1951556Srgrimes ifsfirst.next = ifsp; 1961556Srgrimes INTON; 1971556Srgrimes } 1981556Srgrimes *exparg.lastp = NULL; 1991556Srgrimes if (exparg.list) { 2001556Srgrimes *arglist->lastp = exparg.list; 2011556Srgrimes arglist->lastp = exparg.lastp; 2021556Srgrimes } 2031556Srgrimes} 2041556Srgrimes 2051556Srgrimes 2061556Srgrimes 2071556Srgrimes/* 2081556Srgrimes * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 2091556Srgrimes * characters to allow for further processing. Otherwise treat 2101556Srgrimes * $@ like $* since no splitting will be performed. 2111556Srgrimes */ 2121556Srgrimes 2131556SrgrimesSTATIC void 21490111Simpargstr(char *p, int flag) 21517987Speter{ 21625233Ssteve char c; 217104672Stjr int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ 2181556Srgrimes int firsteq = 1; 2191556Srgrimes 2201556Srgrimes if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 2211556Srgrimes p = exptilde(p, flag); 2221556Srgrimes for (;;) { 2231556Srgrimes switch (c = *p++) { 2241556Srgrimes case '\0': 2251556Srgrimes case CTLENDVAR: /* ??? */ 2261556Srgrimes goto breakloop; 22738887Stegge case CTLQUOTEMARK: 22838887Stegge /* "$@" syntax adherence hack */ 22938887Stegge if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') 23038887Stegge break; 23139137Stegge if ((flag & EXP_FULL) != 0) 23239137Stegge STPUTC(c, expdest); 23338887Stegge break; 2341556Srgrimes case CTLESC: 2351556Srgrimes if (quotes) 2361556Srgrimes STPUTC(c, expdest); 2371556Srgrimes c = *p++; 2381556Srgrimes STPUTC(c, expdest); 2391556Srgrimes break; 2401556Srgrimes case CTLVAR: 2411556Srgrimes p = evalvar(p, flag); 2421556Srgrimes break; 2431556Srgrimes case CTLBACKQ: 2441556Srgrimes case CTLBACKQ|CTLQUOTE: 2451556Srgrimes expbackq(argbackq->n, c & CTLQUOTE, flag); 2461556Srgrimes argbackq = argbackq->next; 2471556Srgrimes break; 2481556Srgrimes case CTLENDARI: 2491556Srgrimes expari(flag); 2501556Srgrimes break; 2511556Srgrimes case ':': 2521556Srgrimes case '=': 2531556Srgrimes /* 2541556Srgrimes * sort of a hack - expand tildes in variable 2551556Srgrimes * assignments (after the first '=' and after ':'s). 2561556Srgrimes */ 2571556Srgrimes STPUTC(c, expdest); 2581556Srgrimes if (flag & EXP_VARTILDE && *p == '~') { 2591556Srgrimes if (c == '=') { 2601556Srgrimes if (firsteq) 2611556Srgrimes firsteq = 0; 2621556Srgrimes else 2631556Srgrimes break; 2641556Srgrimes } 2651556Srgrimes p = exptilde(p, flag); 2661556Srgrimes } 2671556Srgrimes break; 2681556Srgrimes default: 2691556Srgrimes STPUTC(c, expdest); 2701556Srgrimes } 2711556Srgrimes } 2721556Srgrimesbreakloop:; 2731556Srgrimes} 2741556Srgrimes 2751556SrgrimesSTATIC char * 27690111Simpexptilde(char *p, int flag) 27717987Speter{ 2781556Srgrimes char c, *startp = p; 2791556Srgrimes struct passwd *pw; 2801556Srgrimes char *home; 281108935Stjr int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 2821556Srgrimes 28317987Speter while ((c = *p) != '\0') { 2841556Srgrimes switch(c) { 2851556Srgrimes case CTLESC: 2861556Srgrimes return (startp); 28739137Stegge case CTLQUOTEMARK: 28839137Stegge return (startp); 2891556Srgrimes case ':': 2901556Srgrimes if (flag & EXP_VARTILDE) 2911556Srgrimes goto done; 2921556Srgrimes break; 2931556Srgrimes case '/': 2941556Srgrimes goto done; 2951556Srgrimes } 2961556Srgrimes p++; 2971556Srgrimes } 2981556Srgrimesdone: 2991556Srgrimes *p = '\0'; 3001556Srgrimes if (*(startp+1) == '\0') { 3011556Srgrimes if ((home = lookupvar("HOME")) == NULL) 3021556Srgrimes goto lose; 3031556Srgrimes } else { 3041556Srgrimes if ((pw = getpwnam(startp+1)) == NULL) 3051556Srgrimes goto lose; 3061556Srgrimes home = pw->pw_dir; 3071556Srgrimes } 3081556Srgrimes if (*home == '\0') 3091556Srgrimes goto lose; 3101556Srgrimes *p = c; 31117987Speter while ((c = *home++) != '\0') { 31283675Stegge if (quotes && SQSYNTAX[(int)c] == CCTL) 3131556Srgrimes STPUTC(CTLESC, expdest); 3141556Srgrimes STPUTC(c, expdest); 3151556Srgrimes } 3161556Srgrimes return (p); 3171556Srgrimeslose: 3181556Srgrimes *p = c; 3191556Srgrimes return (startp); 3201556Srgrimes} 3211556Srgrimes 3221556Srgrimes 32338887SteggeSTATIC void 32490111Simpremoverecordregions(int endoff) 32538887Stegge{ 32638887Stegge if (ifslastp == NULL) 32738887Stegge return; 32838887Stegge 32938887Stegge if (ifsfirst.endoff > endoff) { 33038887Stegge while (ifsfirst.next != NULL) { 33138887Stegge struct ifsregion *ifsp; 33238887Stegge INTOFF; 33338887Stegge ifsp = ifsfirst.next->next; 33438887Stegge ckfree(ifsfirst.next); 33538887Stegge ifsfirst.next = ifsp; 33638887Stegge INTON; 33738887Stegge } 33838887Stegge if (ifsfirst.begoff > endoff) 33938887Stegge ifslastp = NULL; 34038887Stegge else { 34138887Stegge ifslastp = &ifsfirst; 34238887Stegge ifsfirst.endoff = endoff; 34338887Stegge } 34438887Stegge return; 34538887Stegge } 34638887Stegge 34738887Stegge ifslastp = &ifsfirst; 34838887Stegge while (ifslastp->next && ifslastp->next->begoff < endoff) 34938887Stegge ifslastp=ifslastp->next; 35038887Stegge while (ifslastp->next != NULL) { 35138887Stegge struct ifsregion *ifsp; 35238887Stegge INTOFF; 35338887Stegge ifsp = ifslastp->next->next; 35438887Stegge ckfree(ifslastp->next); 35538887Stegge ifslastp->next = ifsp; 35638887Stegge INTON; 35738887Stegge } 35838887Stegge if (ifslastp->endoff > endoff) 35938887Stegge ifslastp->endoff = endoff; 36038887Stegge} 36138887Stegge 3621556Srgrimes/* 3631556Srgrimes * Expand arithmetic expression. Backup to start of expression, 3641556Srgrimes * evaluate, place result in (backed up) result, adjust string position. 3651556Srgrimes */ 3661556Srgrimesvoid 36790111Simpexpari(int flag) 36817987Speter{ 3691556Srgrimes char *p, *start; 3701556Srgrimes int result; 37138887Stegge int begoff; 372108935Stjr int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 37338887Stegge int quoted; 3741556Srgrimes 37525233Ssteve 3761556Srgrimes /* 37746684Skris * This routine is slightly over-complicated for 3781556Srgrimes * efficiency. First we make sure there is 3791556Srgrimes * enough space for the result, which may be bigger 38046684Skris * than the expression if we add exponentiation. Next we 3811556Srgrimes * scan backwards looking for the start of arithmetic. If the 3821556Srgrimes * next previous character is a CTLESC character, then we 3831556Srgrimes * have to rescan starting from the beginning since CTLESC 3848855Srgrimes * characters have to be processed left to right. 3851556Srgrimes */ 38622777Ssteve#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10 38722777Ssteve#error "integers with more than 10 digits are not supported" 38822777Ssteve#endif 38922777Ssteve CHECKSTRSPACE(12 - 2, expdest); 3908855Srgrimes USTPUTC('\0', expdest); 3911556Srgrimes start = stackblock(); 39283676Stegge p = expdest - 2; 39383676Stegge while (p >= start && *p != CTLARI) 3941556Srgrimes --p; 39583676Stegge if (p < start || *p != CTLARI) 3961556Srgrimes error("missing CTLARI (shouldn't happen)"); 39783676Stegge if (p > start && *(p - 1) == CTLESC) 3981556Srgrimes for (p = start; *p != CTLARI; p++) 3991556Srgrimes if (*p == CTLESC) 4001556Srgrimes p++; 40138887Stegge 40238887Stegge if (p[1] == '"') 40338887Stegge quoted=1; 40438887Stegge else 40538887Stegge quoted=0; 40638887Stegge begoff = p - start; 40738887Stegge removerecordregions(begoff); 4081556Srgrimes if (quotes) 40938887Stegge rmescapes(p+2); 41038887Stegge result = arith(p+2); 41122777Ssteve fmtstr(p, 12, "%d", result); 4121556Srgrimes while (*p++) 4131556Srgrimes ; 41438887Stegge if (quoted == 0) 41538887Stegge recordregion(begoff, p - 1 - start, 0); 4161556Srgrimes result = expdest - p + 1; 4171556Srgrimes STADJUST(-result, expdest); 4181556Srgrimes} 4191556Srgrimes 4201556Srgrimes 4211556Srgrimes/* 4221556Srgrimes * Expand stuff in backwards quotes. 4231556Srgrimes */ 4241556Srgrimes 4251556SrgrimesSTATIC void 42690111Simpexpbackq(union node *cmd, int quoted, int flag) 42717987Speter{ 4281556Srgrimes struct backcmd in; 4291556Srgrimes int i; 4301556Srgrimes char buf[128]; 4311556Srgrimes char *p; 4321556Srgrimes char *dest = expdest; 4331556Srgrimes struct ifsregion saveifs, *savelastp; 4341556Srgrimes struct nodelist *saveargbackq; 4351556Srgrimes char lastc; 4361556Srgrimes int startloc = dest - stackblock(); 4371556Srgrimes char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 4381556Srgrimes int saveherefd; 439108935Stjr int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 4401556Srgrimes 4411556Srgrimes INTOFF; 4421556Srgrimes saveifs = ifsfirst; 4431556Srgrimes savelastp = ifslastp; 4441556Srgrimes saveargbackq = argbackq; 4458855Srgrimes saveherefd = herefd; 4461556Srgrimes herefd = -1; 4471556Srgrimes p = grabstackstr(dest); 4481556Srgrimes evalbackcmd(cmd, &in); 4491556Srgrimes ungrabstackstr(p, dest); 4501556Srgrimes ifsfirst = saveifs; 4511556Srgrimes ifslastp = savelastp; 4521556Srgrimes argbackq = saveargbackq; 4531556Srgrimes herefd = saveherefd; 4541556Srgrimes 4551556Srgrimes p = in.buf; 4561556Srgrimes lastc = '\0'; 4571556Srgrimes for (;;) { 4581556Srgrimes if (--in.nleft < 0) { 4591556Srgrimes if (in.fd < 0) 4601556Srgrimes break; 4611556Srgrimes while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 4621556Srgrimes TRACE(("expbackq: read returns %d\n", i)); 4631556Srgrimes if (i <= 0) 4641556Srgrimes break; 4651556Srgrimes p = buf; 4661556Srgrimes in.nleft = i - 1; 4671556Srgrimes } 4681556Srgrimes lastc = *p++; 4691556Srgrimes if (lastc != '\0') { 47083675Stegge if (quotes && syntax[(int)lastc] == CCTL) 4711556Srgrimes STPUTC(CTLESC, dest); 4721556Srgrimes STPUTC(lastc, dest); 4731556Srgrimes } 4741556Srgrimes } 47517987Speter 47617987Speter /* Eat all trailing newlines */ 47717987Speter for (p--; lastc == '\n'; lastc = *--p) 4781556Srgrimes STUNPUTC(dest); 47917987Speter 4801556Srgrimes if (in.fd >= 0) 4811556Srgrimes close(in.fd); 4821556Srgrimes if (in.buf) 4831556Srgrimes ckfree(in.buf); 4841556Srgrimes if (in.jp) 48545916Scracauer exitstatus = waitforjob(in.jp, (int *)NULL); 4861556Srgrimes if (quoted == 0) 4871556Srgrimes recordregion(startloc, dest - stackblock(), 0); 4881556Srgrimes TRACE(("evalbackq: size=%d: \"%.*s\"\n", 4891556Srgrimes (dest - stackblock()) - startloc, 4901556Srgrimes (dest - stackblock()) - startloc, 4911556Srgrimes stackblock() + startloc)); 4921556Srgrimes expdest = dest; 4931556Srgrimes INTON; 4941556Srgrimes} 4951556Srgrimes 4961556Srgrimes 4971556Srgrimes 49817987SpeterSTATIC int 49990111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc, 50090111Simp int varflags) 50117987Speter{ 50217987Speter char *startp; 50317987Speter char *loc = NULL; 50445514Stegge char *q; 50517987Speter int c = 0; 50617987Speter int saveherefd = herefd; 50717987Speter struct nodelist *saveargbackq = argbackq; 50820425Ssteve int amount; 50920425Ssteve 51017987Speter herefd = -1; 51117987Speter argstr(p, 0); 51217987Speter STACKSTRNUL(expdest); 51317987Speter herefd = saveherefd; 51417987Speter argbackq = saveargbackq; 51517987Speter startp = stackblock() + startloc; 51620425Ssteve if (str == NULL) 51720425Ssteve str = stackblock() + strloc; 51817987Speter 51917987Speter switch (subtype) { 52017987Speter case VSASSIGN: 52117987Speter setvar(str, startp, 0); 52220425Ssteve amount = startp - expdest; 52320425Ssteve STADJUST(amount, expdest); 52417987Speter varflags &= ~VSNUL; 52517987Speter if (c != 0) 52617987Speter *loc = c; 52717987Speter return 1; 52817987Speter 52917987Speter case VSQUESTION: 53017987Speter if (*p != CTLENDVAR) { 53117987Speter outfmt(&errout, "%s\n", startp); 53217987Speter error((char *)NULL); 53317987Speter } 53417987Speter error("%.*s: parameter %snot set", p - str - 1, 53517987Speter str, (varflags & VSNUL) ? "null or " 53617987Speter : nullstr); 53717987Speter return 0; 53817987Speter 53917987Speter case VSTRIMLEFT: 54025233Ssteve for (loc = startp; loc < str; loc++) { 54117987Speter c = *loc; 54217987Speter *loc = '\0'; 54345514Stegge if (patmatch(str, startp, varflags & VSQUOTE)) { 54417987Speter *loc = c; 54517987Speter goto recordleft; 54617987Speter } 54717987Speter *loc = c; 54845514Stegge if ((varflags & VSQUOTE) && *loc == CTLESC) 54945514Stegge loc++; 55017987Speter } 55117987Speter return 0; 55217987Speter 55317987Speter case VSTRIMLEFTMAX: 55445514Stegge for (loc = str - 1; loc >= startp;) { 55517987Speter c = *loc; 55617987Speter *loc = '\0'; 55745514Stegge if (patmatch(str, startp, varflags & VSQUOTE)) { 55817987Speter *loc = c; 55917987Speter goto recordleft; 56017987Speter } 56117987Speter *loc = c; 56245514Stegge loc--; 56345514Stegge if ((varflags & VSQUOTE) && loc > startp && 56445514Stegge *(loc - 1) == CTLESC) { 56545514Stegge for (q = startp; q < loc; q++) 56645514Stegge if (*q == CTLESC) 56745514Stegge q++; 56845514Stegge if (q > loc) 56945514Stegge loc--; 57045514Stegge } 57117987Speter } 57217987Speter return 0; 57317987Speter 57417987Speter case VSTRIMRIGHT: 57545514Stegge for (loc = str - 1; loc >= startp;) { 57645514Stegge if (patmatch(str, loc, varflags & VSQUOTE)) { 57720425Ssteve amount = loc - expdest; 57820425Ssteve STADJUST(amount, expdest); 57917987Speter return 1; 58017987Speter } 58145514Stegge loc--; 58245514Stegge if ((varflags & VSQUOTE) && loc > startp && 58345514Stegge *(loc - 1) == CTLESC) { 58445514Stegge for (q = startp; q < loc; q++) 58545514Stegge if (*q == CTLESC) 58645514Stegge q++; 58745514Stegge if (q > loc) 58845514Stegge loc--; 58945514Stegge } 59017987Speter } 59117987Speter return 0; 59217987Speter 59317987Speter case VSTRIMRIGHTMAX: 59417987Speter for (loc = startp; loc < str - 1; loc++) { 59545514Stegge if (patmatch(str, loc, varflags & VSQUOTE)) { 59620425Ssteve amount = loc - expdest; 59720425Ssteve STADJUST(amount, expdest); 59817987Speter return 1; 59917987Speter } 60045514Stegge if ((varflags & VSQUOTE) && *loc == CTLESC) 60145514Stegge loc++; 60217987Speter } 60317987Speter return 0; 60417987Speter 60517987Speter 60617987Speter default: 60717987Speter abort(); 60817987Speter } 60917987Speter 61017987Speterrecordleft: 61120425Ssteve amount = ((str - 1) - (loc - startp)) - expdest; 61220425Ssteve STADJUST(amount, expdest); 61317987Speter while (loc != str - 1) 61417987Speter *startp++ = *loc++; 61517987Speter return 1; 61617987Speter} 61717987Speter 61817987Speter 6191556Srgrimes/* 6201556Srgrimes * Expand a variable, and return a pointer to the next character in the 6211556Srgrimes * input string. 6221556Srgrimes */ 6231556Srgrimes 6241556SrgrimesSTATIC char * 62590111Simpevalvar(char *p, int flag) 62617987Speter{ 6271556Srgrimes int subtype; 6281556Srgrimes int varflags; 6291556Srgrimes char *var; 6301556Srgrimes char *val; 63145644Stegge int patloc; 6321556Srgrimes int c; 6331556Srgrimes int set; 6341556Srgrimes int special; 6351556Srgrimes int startloc; 63617987Speter int varlen; 63717987Speter int easy; 638108935Stjr int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 6391556Srgrimes 6401556Srgrimes varflags = *p++; 6411556Srgrimes subtype = varflags & VSTYPE; 6421556Srgrimes var = p; 6431556Srgrimes special = 0; 6441556Srgrimes if (! is_name(*p)) 6451556Srgrimes special = 1; 6461556Srgrimes p = strchr(p, '=') + 1; 6471556Srgrimesagain: /* jump here after setting a variable with ${var=text} */ 6481556Srgrimes if (special) { 64925233Ssteve set = varisset(var, varflags & VSNUL); 6501556Srgrimes val = NULL; 6511556Srgrimes } else { 65260592Scracauer val = bltinlookup(var, 1); 65317987Speter if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 6541556Srgrimes val = NULL; 6551556Srgrimes set = 0; 6561556Srgrimes } else 6571556Srgrimes set = 1; 6581556Srgrimes } 65917987Speter varlen = 0; 6601556Srgrimes startloc = expdest - stackblock(); 66196939Stjr if (!set && uflag) { 66296939Stjr switch (subtype) { 66396939Stjr case VSNORMAL: 66496939Stjr case VSTRIMLEFT: 66596939Stjr case VSTRIMLEFTMAX: 66696939Stjr case VSTRIMRIGHT: 66796939Stjr case VSTRIMRIGHTMAX: 66896939Stjr case VSLENGTH: 66996939Stjr error("%.*s: parameter not set", p - var - 1, var); 67096939Stjr } 67196939Stjr } 6721556Srgrimes if (set && subtype != VSPLUS) { 6731556Srgrimes /* insert the value of the variable */ 6741556Srgrimes if (special) { 67518202Speter varvalue(var, varflags & VSQUOTE, flag & EXP_FULL); 67617987Speter if (subtype == VSLENGTH) { 67725233Ssteve varlen = expdest - stackblock() - startloc; 67825233Ssteve STADJUST(-varlen, expdest); 67917987Speter } 6801556Srgrimes } else { 68120425Ssteve char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 68217987Speter : BASESYNTAX; 6831556Srgrimes 68417987Speter if (subtype == VSLENGTH) { 68517987Speter for (;*val; val++) 68617987Speter varlen++; 6871556Srgrimes } 68817987Speter else { 68917987Speter while (*val) { 69083675Stegge if (quotes && 69154132Scracauer syntax[(int)*val] == CCTL) 69217987Speter STPUTC(CTLESC, expdest); 69317987Speter STPUTC(*val++, expdest); 69417987Speter } 69517987Speter 69617987Speter } 6971556Srgrimes } 6981556Srgrimes } 69920425Ssteve 7001556Srgrimes if (subtype == VSPLUS) 7011556Srgrimes set = ! set; 70217987Speter 70320425Ssteve easy = ((varflags & VSQUOTE) == 0 || 70417987Speter (*var == '@' && shellparam.nparam != 1)); 70517987Speter 70617987Speter 70717987Speter switch (subtype) { 70817987Speter case VSLENGTH: 70917987Speter expdest = cvtnum(varlen, expdest); 71017987Speter goto record; 71117987Speter 71217987Speter case VSNORMAL: 71317987Speter if (!easy) 71417987Speter break; 71517987Speterrecord: 71620425Ssteve recordregion(startloc, expdest - stackblock(), 71717987Speter varflags & VSQUOTE); 71817987Speter break; 71917987Speter 72017987Speter case VSPLUS: 72117987Speter case VSMINUS: 72217987Speter if (!set) { 7231556Srgrimes argstr(p, flag); 72417987Speter break; 72517987Speter } 72617987Speter if (easy) 72717987Speter goto record; 72817987Speter break; 72917987Speter 73017987Speter case VSTRIMLEFT: 73117987Speter case VSTRIMLEFTMAX: 73217987Speter case VSTRIMRIGHT: 73317987Speter case VSTRIMRIGHTMAX: 73417987Speter if (!set) 73517987Speter break; 73617987Speter /* 73717987Speter * Terminate the string and start recording the pattern 73817987Speter * right after it 73917987Speter */ 74017987Speter STPUTC('\0', expdest); 74145644Stegge patloc = expdest - stackblock(); 74245644Stegge if (subevalvar(p, NULL, patloc, subtype, 74338887Stegge startloc, varflags) == 0) { 74445644Stegge int amount = (expdest - stackblock() - patloc) + 1; 74525233Ssteve STADJUST(-amount, expdest); 74625233Ssteve } 74738887Stegge /* Remove any recorded regions beyond start of variable */ 74838887Stegge removerecordregions(startloc); 74938887Stegge goto record; 75017987Speter 75117987Speter case VSASSIGN: 75217987Speter case VSQUESTION: 75317987Speter if (!set) { 75420425Ssteve if (subevalvar(p, var, 0, subtype, startloc, varflags)) { 75520425Ssteve varflags &= ~VSNUL; 75638887Stegge /* 75738887Stegge * Remove any recorded regions beyond 75838887Stegge * start of variable 75938887Stegge */ 76038887Stegge removerecordregions(startloc); 7611556Srgrimes goto again; 76220425Ssteve } 76317987Speter break; 7641556Srgrimes } 76517987Speter if (easy) 76617987Speter goto record; 76717987Speter break; 76817987Speter 76917987Speter default: 77017987Speter abort(); 7711556Srgrimes } 77217987Speter 7731556Srgrimes if (subtype != VSNORMAL) { /* skip to end of alternative */ 7741556Srgrimes int nesting = 1; 7751556Srgrimes for (;;) { 7761556Srgrimes if ((c = *p++) == CTLESC) 7771556Srgrimes p++; 7781556Srgrimes else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 7791556Srgrimes if (set) 7801556Srgrimes argbackq = argbackq->next; 7811556Srgrimes } else if (c == CTLVAR) { 7821556Srgrimes if ((*p++ & VSTYPE) != VSNORMAL) 7831556Srgrimes nesting++; 7841556Srgrimes } else if (c == CTLENDVAR) { 7851556Srgrimes if (--nesting == 0) 7861556Srgrimes break; 7871556Srgrimes } 7881556Srgrimes } 7891556Srgrimes } 7901556Srgrimes return p; 7911556Srgrimes} 7921556Srgrimes 7931556Srgrimes 7941556Srgrimes 7951556Srgrimes/* 7961556Srgrimes * Test whether a specialized variable is set. 7971556Srgrimes */ 7981556Srgrimes 7991556SrgrimesSTATIC int 80090111Simpvarisset(char *name, int nulok) 80125233Ssteve{ 8021556Srgrimes 80325233Ssteve if (*name == '!') 80425233Ssteve return backgndpid != -1; 80525233Ssteve else if (*name == '@' || *name == '*') { 8061556Srgrimes if (*shellparam.p == NULL) 8071556Srgrimes return 0; 80825233Ssteve 80925233Ssteve if (nulok) { 81025233Ssteve char **av; 81125233Ssteve 81225233Ssteve for (av = shellparam.p; *av; av++) 81325233Ssteve if (**av != '\0') 81425233Ssteve return 1; 81525233Ssteve return 0; 81625233Ssteve } 81718202Speter } else if (is_digit(*name)) { 81825233Ssteve char *ap; 81920425Ssteve int num = atoi(name); 82025233Ssteve 82125233Ssteve if (num > shellparam.nparam) 82225233Ssteve return 0; 82325233Ssteve 82425233Ssteve if (num == 0) 82525233Ssteve ap = arg0; 82625233Ssteve else 82725233Ssteve ap = shellparam.p[num - 1]; 82825233Ssteve 82925233Ssteve if (nulok && (ap == NULL || *ap == '\0')) 83025233Ssteve return 0; 8311556Srgrimes } 8321556Srgrimes return 1; 8331556Srgrimes} 8341556Srgrimes 8351556Srgrimes 8361556Srgrimes 8371556Srgrimes/* 8381556Srgrimes * Add the value of a specialized variable to the stack string. 8391556Srgrimes */ 8401556Srgrimes 8411556SrgrimesSTATIC void 84290111Simpvarvalue(char *name, int quoted, int allow_split) 84317987Speter{ 8441556Srgrimes int num; 8451556Srgrimes char *p; 8461556Srgrimes int i; 84717987Speter extern int oexitstatus; 8481556Srgrimes char sep; 8491556Srgrimes char **ap; 8501556Srgrimes char const *syntax; 8511556Srgrimes 8521556Srgrimes#define STRTODEST(p) \ 8531556Srgrimes do {\ 8541556Srgrimes if (allow_split) { \ 8551556Srgrimes syntax = quoted? DQSYNTAX : BASESYNTAX; \ 8561556Srgrimes while (*p) { \ 85783675Stegge if (syntax[(int)*p] == CCTL) \ 8581556Srgrimes STPUTC(CTLESC, expdest); \ 8591556Srgrimes STPUTC(*p++, expdest); \ 8601556Srgrimes } \ 8611556Srgrimes } else \ 8621556Srgrimes while (*p) \ 8631556Srgrimes STPUTC(*p++, expdest); \ 8641556Srgrimes } while (0) 8651556Srgrimes 8661556Srgrimes 86718202Speter switch (*name) { 8681556Srgrimes case '$': 8691556Srgrimes num = rootpid; 8701556Srgrimes goto numvar; 8711556Srgrimes case '?': 87217987Speter num = oexitstatus; 8731556Srgrimes goto numvar; 8741556Srgrimes case '#': 8751556Srgrimes num = shellparam.nparam; 8761556Srgrimes goto numvar; 8771556Srgrimes case '!': 8781556Srgrimes num = backgndpid; 8791556Srgrimesnumvar: 88017987Speter expdest = cvtnum(num, expdest); 8811556Srgrimes break; 8821556Srgrimes case '-': 8831556Srgrimes for (i = 0 ; i < NOPTS ; i++) { 8841556Srgrimes if (optlist[i].val) 8851556Srgrimes STPUTC(optlist[i].letter, expdest); 8861556Srgrimes } 8871556Srgrimes break; 8881556Srgrimes case '@': 88938887Stegge if (allow_split && quoted) { 89038887Stegge for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 89138887Stegge STRTODEST(p); 89238887Stegge if (*ap) 89338887Stegge STPUTC('\0', expdest); 89438887Stegge } 89538887Stegge break; 8961556Srgrimes } 897102410Scharnier /* FALLTHROUGH */ 8981556Srgrimes case '*': 89938887Stegge if (ifsset() != 0) 90038887Stegge sep = ifsval()[0]; 90138887Stegge else 90238887Stegge sep = ' '; 9031556Srgrimes for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 9041556Srgrimes STRTODEST(p); 90538887Stegge if (*ap && sep) 9061556Srgrimes STPUTC(sep, expdest); 9071556Srgrimes } 9081556Srgrimes break; 9091556Srgrimes case '0': 9101556Srgrimes p = arg0; 9111556Srgrimes STRTODEST(p); 9121556Srgrimes break; 9131556Srgrimes default: 91418202Speter if (is_digit(*name)) { 91518202Speter num = atoi(name); 91618202Speter if (num > 0 && num <= shellparam.nparam) { 91718202Speter p = shellparam.p[num - 1]; 91818202Speter STRTODEST(p); 91918202Speter } 9201556Srgrimes } 9211556Srgrimes break; 9221556Srgrimes } 9231556Srgrimes} 9241556Srgrimes 9251556Srgrimes 9261556Srgrimes 9271556Srgrimes/* 9281556Srgrimes * Record the the fact that we have to scan this region of the 9291556Srgrimes * string for IFS characters. 9301556Srgrimes */ 9311556Srgrimes 9321556SrgrimesSTATIC void 93390111Simprecordregion(int start, int end, int nulonly) 93417987Speter{ 93525233Ssteve struct ifsregion *ifsp; 9361556Srgrimes 9371556Srgrimes if (ifslastp == NULL) { 9381556Srgrimes ifsp = &ifsfirst; 9391556Srgrimes } else { 9401556Srgrimes ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 9411556Srgrimes ifslastp->next = ifsp; 9421556Srgrimes } 9431556Srgrimes ifslastp = ifsp; 9441556Srgrimes ifslastp->next = NULL; 9451556Srgrimes ifslastp->begoff = start; 9461556Srgrimes ifslastp->endoff = end; 9471556Srgrimes ifslastp->nulonly = nulonly; 9481556Srgrimes} 9491556Srgrimes 9501556Srgrimes 9511556Srgrimes 9521556Srgrimes/* 9531556Srgrimes * Break the argument string into pieces based upon IFS and add the 9541556Srgrimes * strings to the argument list. The regions of the string to be 9551556Srgrimes * searched for IFS characters have been stored by recordregion. 9561556Srgrimes */ 9571556SrgrimesSTATIC void 95890111Simpifsbreakup(char *string, struct arglist *arglist) 95990111Simp{ 9601556Srgrimes struct ifsregion *ifsp; 9611556Srgrimes struct strlist *sp; 9621556Srgrimes char *start; 96325233Ssteve char *p; 9641556Srgrimes char *q; 9651556Srgrimes char *ifs; 96617987Speter int ifsspc; 96738887Stegge int nulonly; 9681556Srgrimes 96917987Speter 9701556Srgrimes start = string; 97138887Stegge ifsspc = 0; 97238887Stegge nulonly = 0; 9731556Srgrimes if (ifslastp != NULL) { 9741556Srgrimes ifsp = &ifsfirst; 9751556Srgrimes do { 9761556Srgrimes p = string + ifsp->begoff; 97738887Stegge nulonly = ifsp->nulonly; 97838887Stegge ifs = nulonly ? nullstr : 97938887Stegge ( ifsset() ? ifsval() : " \t\n" ); 98038887Stegge ifsspc = 0; 9811556Srgrimes while (p < string + ifsp->endoff) { 9821556Srgrimes q = p; 9831556Srgrimes if (*p == CTLESC) 9841556Srgrimes p++; 98538887Stegge if (strchr(ifs, *p)) { 98638887Stegge if (!nulonly) 98738887Stegge ifsspc = (strchr(" \t\n", *p) != NULL); 98838887Stegge /* Ignore IFS whitespace at start */ 98938887Stegge if (q == start && ifsspc) { 99038887Stegge p++; 99138887Stegge start = p; 99238887Stegge continue; 9931556Srgrimes } 99438887Stegge *q = '\0'; 99538887Stegge sp = (struct strlist *)stalloc(sizeof *sp); 99638887Stegge sp->text = start; 99738887Stegge *arglist->lastp = sp; 99838887Stegge arglist->lastp = &sp->next; 99938887Stegge p++; 100038887Stegge if (!nulonly) { 10011556Srgrimes for (;;) { 100238887Stegge if (p >= string + ifsp->endoff) { 10031556Srgrimes break; 100438887Stegge } 10051556Srgrimes q = p; 10061556Srgrimes if (*p == CTLESC) 10071556Srgrimes p++; 100838887Stegge if (strchr(ifs, *p) == NULL ) { 10091556Srgrimes p = q; 10101556Srgrimes break; 101138887Stegge } else if (strchr(" \t\n",*p) == NULL) { 101238887Stegge if (ifsspc) { 101338887Stegge p++; 101438887Stegge ifsspc = 0; 101538887Stegge } else { 101638887Stegge p = q; 101738887Stegge break; 101838887Stegge } 101938887Stegge } else 102038887Stegge p++; 10211556Srgrimes } 10221556Srgrimes } 10231556Srgrimes start = p; 102438887Stegge } else 102538887Stegge p++; 10261556Srgrimes } 10271556Srgrimes } while ((ifsp = ifsp->next) != NULL); 102838887Stegge if (*start || (!ifsspc && start > string && 102938887Stegge (nulonly || 1))) { 10301556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 10311556Srgrimes sp->text = start; 10321556Srgrimes *arglist->lastp = sp; 10331556Srgrimes arglist->lastp = &sp->next; 10341556Srgrimes } 10351556Srgrimes } else { 10361556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 10371556Srgrimes sp->text = start; 10381556Srgrimes *arglist->lastp = sp; 10391556Srgrimes arglist->lastp = &sp->next; 10401556Srgrimes } 10411556Srgrimes} 10421556Srgrimes 10431556Srgrimes 10441556Srgrimes 10451556Srgrimes/* 10461556Srgrimes * Expand shell metacharacters. At this point, the only control characters 10471556Srgrimes * should be escapes. The results are stored in the list exparg. 10481556Srgrimes */ 10491556Srgrimes 10501556Srgrimeschar *expdir; 10511556Srgrimes 10521556Srgrimes 10531556SrgrimesSTATIC void 105490111Simpexpandmeta(struct strlist *str, int flag __unused) 105517987Speter{ 10561556Srgrimes char *p; 10571556Srgrimes struct strlist **savelastp; 10581556Srgrimes struct strlist *sp; 10591556Srgrimes char c; 10601556Srgrimes /* TODO - EXP_REDIR */ 10611556Srgrimes 10621556Srgrimes while (str) { 10631556Srgrimes if (fflag) 10641556Srgrimes goto nometa; 10651556Srgrimes p = str->text; 10661556Srgrimes for (;;) { /* fast check for meta chars */ 10671556Srgrimes if ((c = *p++) == '\0') 10681556Srgrimes goto nometa; 10691556Srgrimes if (c == '*' || c == '?' || c == '[' || c == '!') 10701556Srgrimes break; 10711556Srgrimes } 10721556Srgrimes savelastp = exparg.lastp; 10731556Srgrimes INTOFF; 10741556Srgrimes if (expdir == NULL) { 10751556Srgrimes int i = strlen(str->text); 10761556Srgrimes expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 10771556Srgrimes } 10781556Srgrimes 10791556Srgrimes expmeta(expdir, str->text); 10801556Srgrimes ckfree(expdir); 10811556Srgrimes expdir = NULL; 10821556Srgrimes INTON; 10831556Srgrimes if (exparg.lastp == savelastp) { 10848855Srgrimes /* 10858855Srgrimes * no matches 10861556Srgrimes */ 10871556Srgrimesnometa: 10881556Srgrimes *exparg.lastp = str; 10891556Srgrimes rmescapes(str->text); 10901556Srgrimes exparg.lastp = &str->next; 10911556Srgrimes } else { 10921556Srgrimes *exparg.lastp = NULL; 10931556Srgrimes *savelastp = sp = expsort(*savelastp); 10941556Srgrimes while (sp->next != NULL) 10951556Srgrimes sp = sp->next; 10961556Srgrimes exparg.lastp = &sp->next; 10971556Srgrimes } 10981556Srgrimes str = str->next; 10991556Srgrimes } 11001556Srgrimes} 11011556Srgrimes 11021556Srgrimes 11031556Srgrimes/* 11041556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion. 11051556Srgrimes */ 11061556Srgrimes 11071556SrgrimesSTATIC void 110890111Simpexpmeta(char *enddir, char *name) 110990111Simp{ 111025233Ssteve char *p; 11111556Srgrimes char *q; 11121556Srgrimes char *start; 11131556Srgrimes char *endname; 11141556Srgrimes int metaflag; 11151556Srgrimes struct stat statb; 11161556Srgrimes DIR *dirp; 11171556Srgrimes struct dirent *dp; 11181556Srgrimes int atend; 11191556Srgrimes int matchdot; 11201556Srgrimes 11211556Srgrimes metaflag = 0; 11221556Srgrimes start = name; 11231556Srgrimes for (p = name ; ; p++) { 11241556Srgrimes if (*p == '*' || *p == '?') 11251556Srgrimes metaflag = 1; 11261556Srgrimes else if (*p == '[') { 11271556Srgrimes q = p + 1; 112826488Sache if (*q == '!' || *q == '^') 11291556Srgrimes q++; 11301556Srgrimes for (;;) { 113138887Stegge while (*q == CTLQUOTEMARK) 113238887Stegge q++; 11331556Srgrimes if (*q == CTLESC) 11341556Srgrimes q++; 11351556Srgrimes if (*q == '/' || *q == '\0') 11361556Srgrimes break; 11371556Srgrimes if (*++q == ']') { 11381556Srgrimes metaflag = 1; 11391556Srgrimes break; 11401556Srgrimes } 11411556Srgrimes } 11421556Srgrimes } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 11431556Srgrimes metaflag = 1; 11441556Srgrimes } else if (*p == '\0') 11451556Srgrimes break; 114638887Stegge else if (*p == CTLQUOTEMARK) 114738887Stegge continue; 11481556Srgrimes else if (*p == CTLESC) 11491556Srgrimes p++; 11501556Srgrimes if (*p == '/') { 11511556Srgrimes if (metaflag) 11521556Srgrimes break; 11531556Srgrimes start = p + 1; 11541556Srgrimes } 11551556Srgrimes } 11561556Srgrimes if (metaflag == 0) { /* we've reached the end of the file name */ 11571556Srgrimes if (enddir != expdir) 11581556Srgrimes metaflag++; 11591556Srgrimes for (p = name ; ; p++) { 116038887Stegge if (*p == CTLQUOTEMARK) 116138887Stegge continue; 11621556Srgrimes if (*p == CTLESC) 11631556Srgrimes p++; 11641556Srgrimes *enddir++ = *p; 11651556Srgrimes if (*p == '\0') 11661556Srgrimes break; 11671556Srgrimes } 11681556Srgrimes if (metaflag == 0 || stat(expdir, &statb) >= 0) 11691556Srgrimes addfname(expdir); 11701556Srgrimes return; 11711556Srgrimes } 11721556Srgrimes endname = p; 11731556Srgrimes if (start != name) { 11741556Srgrimes p = name; 11751556Srgrimes while (p < start) { 117638887Stegge while (*p == CTLQUOTEMARK) 117738887Stegge p++; 11781556Srgrimes if (*p == CTLESC) 11791556Srgrimes p++; 11801556Srgrimes *enddir++ = *p++; 11811556Srgrimes } 11821556Srgrimes } 11831556Srgrimes if (enddir == expdir) { 11841556Srgrimes p = "."; 11851556Srgrimes } else if (enddir == expdir + 1 && *expdir == '/') { 11861556Srgrimes p = "/"; 11871556Srgrimes } else { 11881556Srgrimes p = expdir; 11891556Srgrimes enddir[-1] = '\0'; 11901556Srgrimes } 11911556Srgrimes if ((dirp = opendir(p)) == NULL) 11921556Srgrimes return; 11931556Srgrimes if (enddir != expdir) 11941556Srgrimes enddir[-1] = '/'; 11951556Srgrimes if (*endname == 0) { 11961556Srgrimes atend = 1; 11971556Srgrimes } else { 11981556Srgrimes atend = 0; 11991556Srgrimes *endname++ = '\0'; 12001556Srgrimes } 12011556Srgrimes matchdot = 0; 120238887Stegge p = start; 120338887Stegge while (*p == CTLQUOTEMARK) 120438887Stegge p++; 120538887Stegge if (*p == CTLESC) 120638887Stegge p++; 120738887Stegge if (*p == '.') 12081556Srgrimes matchdot++; 12091556Srgrimes while (! int_pending() && (dp = readdir(dirp)) != NULL) { 12101556Srgrimes if (dp->d_name[0] == '.' && ! matchdot) 12111556Srgrimes continue; 121245514Stegge if (patmatch(start, dp->d_name, 0)) { 12131556Srgrimes if (atend) { 12141556Srgrimes scopy(dp->d_name, enddir); 12151556Srgrimes addfname(expdir); 12161556Srgrimes } else { 12171556Srgrimes char *q; 121817987Speter for (p = enddir, q = dp->d_name; 121917987Speter (*p++ = *q++) != '\0';) 122017987Speter continue; 12211556Srgrimes p[-1] = '/'; 12221556Srgrimes expmeta(p, endname); 12231556Srgrimes } 12241556Srgrimes } 12251556Srgrimes } 12261556Srgrimes closedir(dirp); 12271556Srgrimes if (! atend) 12281556Srgrimes endname[-1] = '/'; 12291556Srgrimes} 12301556Srgrimes 12311556Srgrimes 12321556Srgrimes/* 12331556Srgrimes * Add a file name to the list. 12341556Srgrimes */ 12351556Srgrimes 12361556SrgrimesSTATIC void 123790111Simpaddfname(char *name) 123890111Simp{ 12391556Srgrimes char *p; 12401556Srgrimes struct strlist *sp; 12411556Srgrimes 12421556Srgrimes p = stalloc(strlen(name) + 1); 12431556Srgrimes scopy(name, p); 12441556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 12451556Srgrimes sp->text = p; 12461556Srgrimes *exparg.lastp = sp; 12471556Srgrimes exparg.lastp = &sp->next; 12481556Srgrimes} 12491556Srgrimes 12501556Srgrimes 12511556Srgrimes/* 12521556Srgrimes * Sort the results of file name expansion. It calculates the number of 12531556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the 12541556Srgrimes * work. 12551556Srgrimes */ 12561556Srgrimes 12571556SrgrimesSTATIC struct strlist * 125890111Simpexpsort(struct strlist *str) 125990111Simp{ 12601556Srgrimes int len; 12611556Srgrimes struct strlist *sp; 12621556Srgrimes 12631556Srgrimes len = 0; 12641556Srgrimes for (sp = str ; sp ; sp = sp->next) 12651556Srgrimes len++; 12661556Srgrimes return msort(str, len); 12671556Srgrimes} 12681556Srgrimes 12691556Srgrimes 12701556SrgrimesSTATIC struct strlist * 127190111Simpmsort(struct strlist *list, int len) 127217987Speter{ 127317987Speter struct strlist *p, *q = NULL; 12741556Srgrimes struct strlist **lpp; 12751556Srgrimes int half; 12761556Srgrimes int n; 12771556Srgrimes 12781556Srgrimes if (len <= 1) 12791556Srgrimes return list; 12808855Srgrimes half = len >> 1; 12811556Srgrimes p = list; 12821556Srgrimes for (n = half ; --n >= 0 ; ) { 12831556Srgrimes q = p; 12841556Srgrimes p = p->next; 12851556Srgrimes } 12861556Srgrimes q->next = NULL; /* terminate first half of list */ 12871556Srgrimes q = msort(list, half); /* sort first half of list */ 12881556Srgrimes p = msort(p, len - half); /* sort second half */ 12891556Srgrimes lpp = &list; 12901556Srgrimes for (;;) { 12911556Srgrimes if (strcmp(p->text, q->text) < 0) { 12921556Srgrimes *lpp = p; 12931556Srgrimes lpp = &p->next; 12941556Srgrimes if ((p = *lpp) == NULL) { 12951556Srgrimes *lpp = q; 12961556Srgrimes break; 12971556Srgrimes } 12981556Srgrimes } else { 12991556Srgrimes *lpp = q; 13001556Srgrimes lpp = &q->next; 13011556Srgrimes if ((q = *lpp) == NULL) { 13021556Srgrimes *lpp = p; 13031556Srgrimes break; 13041556Srgrimes } 13051556Srgrimes } 13061556Srgrimes } 13071556Srgrimes return list; 13081556Srgrimes} 13091556Srgrimes 13101556Srgrimes 13111556Srgrimes 13121556Srgrimes/* 13131556Srgrimes * Returns true if the pattern matches the string. 13141556Srgrimes */ 13151556Srgrimes 13161556Srgrimesint 131790111Simppatmatch(char *pattern, char *string, int squoted) 131890111Simp{ 13191556Srgrimes#ifdef notdef 13201556Srgrimes if (pattern[0] == '!' && pattern[1] == '!') 13211556Srgrimes return 1 - pmatch(pattern + 2, string); 13221556Srgrimes else 13231556Srgrimes#endif 132445514Stegge return pmatch(pattern, string, squoted); 13251556Srgrimes} 13261556Srgrimes 132717987Speter 132817525SacheSTATIC int 132990111Simppmatch(char *pattern, char *string, int squoted) 133090111Simp{ 133125233Ssteve char *p, *q; 133225233Ssteve char c; 13331556Srgrimes 13341556Srgrimes p = pattern; 13351556Srgrimes q = string; 13361556Srgrimes for (;;) { 13371556Srgrimes switch (c = *p++) { 13381556Srgrimes case '\0': 13391556Srgrimes goto breakloop; 13401556Srgrimes case CTLESC: 134145514Stegge if (squoted && *q == CTLESC) 134245514Stegge q++; 13431556Srgrimes if (*q++ != *p++) 13441556Srgrimes return 0; 13451556Srgrimes break; 134638887Stegge case CTLQUOTEMARK: 134738887Stegge continue; 13481556Srgrimes case '?': 134945514Stegge if (squoted && *q == CTLESC) 135045514Stegge q++; 13511556Srgrimes if (*q++ == '\0') 13521556Srgrimes return 0; 13531556Srgrimes break; 13541556Srgrimes case '*': 13551556Srgrimes c = *p; 135638887Stegge while (c == CTLQUOTEMARK || c == '*') 135738887Stegge c = *++p; 135838887Stegge if (c != CTLESC && c != CTLQUOTEMARK && 135938887Stegge c != '?' && c != '*' && c != '[') { 13601556Srgrimes while (*q != c) { 136145514Stegge if (squoted && *q == CTLESC && 136245514Stegge q[1] == c) 136345514Stegge break; 13641556Srgrimes if (*q == '\0') 13651556Srgrimes return 0; 136645514Stegge if (squoted && *q == CTLESC) 136745514Stegge q++; 13681556Srgrimes q++; 13691556Srgrimes } 13701556Srgrimes } 13711556Srgrimes do { 137245514Stegge if (pmatch(p, q, squoted)) 13731556Srgrimes return 1; 137445514Stegge if (squoted && *q == CTLESC) 137545514Stegge q++; 13761556Srgrimes } while (*q++ != '\0'); 13771556Srgrimes return 0; 13781556Srgrimes case '[': { 13791556Srgrimes char *endp; 13801556Srgrimes int invert, found; 13811556Srgrimes char chr; 13821556Srgrimes 13831556Srgrimes endp = p; 138426488Sache if (*endp == '!' || *endp == '^') 13851556Srgrimes endp++; 13861556Srgrimes for (;;) { 138738887Stegge while (*endp == CTLQUOTEMARK) 138838887Stegge endp++; 13891556Srgrimes if (*endp == '\0') 13901556Srgrimes goto dft; /* no matching ] */ 13911556Srgrimes if (*endp == CTLESC) 13921556Srgrimes endp++; 13931556Srgrimes if (*++endp == ']') 13941556Srgrimes break; 13951556Srgrimes } 13961556Srgrimes invert = 0; 139726488Sache if (*p == '!' || *p == '^') { 13981556Srgrimes invert++; 13991556Srgrimes p++; 14001556Srgrimes } 14011556Srgrimes found = 0; 14021556Srgrimes chr = *q++; 140345514Stegge if (squoted && chr == CTLESC) 140445514Stegge chr = *q++; 140517987Speter if (chr == '\0') 140617987Speter return 0; 14071556Srgrimes c = *p++; 14081556Srgrimes do { 140938887Stegge if (c == CTLQUOTEMARK) 141038887Stegge continue; 14111556Srgrimes if (c == CTLESC) 14121556Srgrimes c = *p++; 14131556Srgrimes if (*p == '-' && p[1] != ']') { 14141556Srgrimes p++; 141538887Stegge while (*p == CTLQUOTEMARK) 141638887Stegge p++; 14171556Srgrimes if (*p == CTLESC) 14181556Srgrimes p++; 141917557Sache if ( collate_range_cmp(chr, c) >= 0 142017557Sache && collate_range_cmp(chr, *p) <= 0 142117525Sache ) 14221556Srgrimes found = 1; 14231556Srgrimes p++; 14241556Srgrimes } else { 14251556Srgrimes if (chr == c) 14261556Srgrimes found = 1; 14271556Srgrimes } 14281556Srgrimes } while ((c = *p++) != ']'); 14291556Srgrimes if (found == invert) 14301556Srgrimes return 0; 14311556Srgrimes break; 14321556Srgrimes } 14331556Srgrimesdft: default: 143445514Stegge if (squoted && *q == CTLESC) 143545514Stegge q++; 14361556Srgrimes if (*q++ != c) 14371556Srgrimes return 0; 14381556Srgrimes break; 14391556Srgrimes } 14401556Srgrimes } 14411556Srgrimesbreakloop: 14421556Srgrimes if (*q != '\0') 14431556Srgrimes return 0; 14441556Srgrimes return 1; 14451556Srgrimes} 14461556Srgrimes 14471556Srgrimes 14481556Srgrimes 14491556Srgrimes/* 14501556Srgrimes * Remove any CTLESC characters from a string. 14511556Srgrimes */ 14521556Srgrimes 14531556Srgrimesvoid 145490111Simprmescapes(char *str) 145538887Stegge{ 145625233Ssteve char *p, *q; 14571556Srgrimes 14581556Srgrimes p = str; 145938887Stegge while (*p != CTLESC && *p != CTLQUOTEMARK) { 14601556Srgrimes if (*p++ == '\0') 14611556Srgrimes return; 14621556Srgrimes } 14631556Srgrimes q = p; 14641556Srgrimes while (*p) { 146538887Stegge if (*p == CTLQUOTEMARK) { 146638887Stegge p++; 146738887Stegge continue; 146838887Stegge } 14691556Srgrimes if (*p == CTLESC) 14701556Srgrimes p++; 14711556Srgrimes *q++ = *p++; 14721556Srgrimes } 14731556Srgrimes *q = '\0'; 14741556Srgrimes} 14751556Srgrimes 14761556Srgrimes 14771556Srgrimes 14781556Srgrimes/* 14791556Srgrimes * See if a pattern matches in a case statement. 14801556Srgrimes */ 14811556Srgrimes 14821556Srgrimesint 148390111Simpcasematch(union node *pattern, char *val) 148490111Simp{ 14851556Srgrimes struct stackmark smark; 14861556Srgrimes int result; 14871556Srgrimes char *p; 14881556Srgrimes 14891556Srgrimes setstackmark(&smark); 14901556Srgrimes argbackq = pattern->narg.backquote; 14911556Srgrimes STARTSTACKSTR(expdest); 14921556Srgrimes ifslastp = NULL; 14931556Srgrimes argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 14941556Srgrimes STPUTC('\0', expdest); 14951556Srgrimes p = grabstackstr(expdest); 149645514Stegge result = patmatch(p, val, 0); 14971556Srgrimes popstackmark(&smark); 14981556Srgrimes return result; 14991556Srgrimes} 150017987Speter 150117987Speter/* 150217987Speter * Our own itoa(). 150317987Speter */ 150417987Speter 150517987SpeterSTATIC char * 150690111Simpcvtnum(int num, char *buf) 150790111Simp{ 150817987Speter char temp[32]; 150917987Speter int neg = num < 0; 151017987Speter char *p = temp + 31; 151117987Speter 151217987Speter temp[31] = '\0'; 151317987Speter 151417987Speter do { 151517987Speter *--p = num % 10 + '0'; 151617987Speter } while ((num /= 10) != 0); 151717987Speter 151817987Speter if (neg) 151917987Speter *--p = '-'; 152017987Speter 152117987Speter while (*p) 152217987Speter STPUTC(*p++, buf); 152317987Speter return buf; 152417987Speter} 1525108286Stjr 1526108286Stjr/* 1527108286Stjr * Do most of the work for wordexp(3). 1528108286Stjr */ 1529108286Stjr 1530108286Stjrint 1531108286Stjrwordexpcmd(int argc, char **argv) 1532108286Stjr{ 1533108286Stjr size_t len; 1534108286Stjr int i; 1535108286Stjr 1536108286Stjr out1fmt("%08x", argc - 1); 1537108286Stjr for (i = 1, len = 0; i < argc; i++) 1538108286Stjr len += strlen(argv[i]); 1539108286Stjr out1fmt("%08x", (int)len); 1540108286Stjr for (i = 1; i < argc; i++) { 1541108286Stjr out1str(argv[i]); 1542108286Stjr out1c('\0'); 1543108286Stjr } 1544108286Stjr return (0); 1545108286Stjr} 1546