1*d08d589dSchristos /* $NetBSD: expand.c,v 1.136 2019/10/14 13:34:14 christos Exp $ */ 249f0ad86Scgd 361f28255Scgd /*- 437ed7877Sjtc * Copyright (c) 1991, 1993 537ed7877Sjtc * The Regents of the University of California. All rights reserved. 661f28255Scgd * 761f28255Scgd * This code is derived from software contributed to Berkeley by 861f28255Scgd * Kenneth Almquist. 961f28255Scgd * 1061f28255Scgd * Redistribution and use in source and binary forms, with or without 1161f28255Scgd * modification, are permitted provided that the following conditions 1261f28255Scgd * are met: 1361f28255Scgd * 1. Redistributions of source code must retain the above copyright 1461f28255Scgd * notice, this list of conditions and the following disclaimer. 1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright 1661f28255Scgd * notice, this list of conditions and the following disclaimer in the 1761f28255Scgd * documentation and/or other materials provided with the distribution. 18b5b29542Sagc * 3. Neither the name of the University nor the names of its contributors 1961f28255Scgd * may be used to endorse or promote products derived from this software 2061f28255Scgd * without specific prior written permission. 2161f28255Scgd * 2261f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2361f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2461f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2561f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2661f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2761f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2861f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2961f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3061f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3161f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3261f28255Scgd * SUCH DAMAGE. 3361f28255Scgd */ 3461f28255Scgd 35cd799663Schristos #include <sys/cdefs.h> 3661f28255Scgd #ifndef lint 3749f0ad86Scgd #if 0 38fbac2e9dSchristos static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; 3949f0ad86Scgd #else 40*d08d589dSchristos __RCSID("$NetBSD: expand.c,v 1.136 2019/10/14 13:34:14 christos Exp $"); 4149f0ad86Scgd #endif 4261f28255Scgd #endif /* not lint */ 4361f28255Scgd 4407bae7edSchristos #include <sys/types.h> 4507bae7edSchristos #include <sys/time.h> 4607bae7edSchristos #include <sys/stat.h> 4707bae7edSchristos #include <errno.h> 4807bae7edSchristos #include <dirent.h> 4907bae7edSchristos #include <unistd.h> 5007bae7edSchristos #include <pwd.h> 51a080d612Schristos #include <limits.h> 5207bae7edSchristos #include <stdlib.h> 531fbf0781Smycroft #include <stdio.h> 547969ec4dSroy #include <wctype.h> 558e4a570fSchristos #include <wchar.h> 5607bae7edSchristos 5761f28255Scgd /* 5861f28255Scgd * Routines to expand arguments to commands. We have to deal with 5961f28255Scgd * backquotes, shell variables, and file metacharacters. 6061f28255Scgd */ 6161f28255Scgd 6261f28255Scgd #include "shell.h" 6361f28255Scgd #include "main.h" 6461f28255Scgd #include "nodes.h" 6561f28255Scgd #include "eval.h" 6661f28255Scgd #include "expand.h" 6761f28255Scgd #include "syntax.h" 68cc8e58edSkre #include "arithmetic.h" 6961f28255Scgd #include "parser.h" 7061f28255Scgd #include "jobs.h" 7161f28255Scgd #include "options.h" 724fc4fe2eSchristos #include "builtins.h" 7361f28255Scgd #include "var.h" 7461f28255Scgd #include "input.h" 7561f28255Scgd #include "output.h" 7661f28255Scgd #include "memalloc.h" 7761f28255Scgd #include "error.h" 7861f28255Scgd #include "mystring.h" 7907bae7edSchristos #include "show.h" 8061f28255Scgd 8161f28255Scgd /* 8261f28255Scgd * Structure specifying which parts of the string should be searched 8361f28255Scgd * for IFS characters. 8461f28255Scgd */ 8561f28255Scgd 8661f28255Scgd struct ifsregion { 8761f28255Scgd struct ifsregion *next; /* next region in list */ 8861f28255Scgd int begoff; /* offset of start of region */ 8961f28255Scgd int endoff; /* offset of end of region */ 90e4a2a056Sdsl int inquotes; /* search for nul bytes only */ 9161f28255Scgd }; 9261f28255Scgd 9361f28255Scgd 9461f28255Scgd char *expdest; /* output of current string */ 9561f28255Scgd struct nodelist *argbackq; /* list of back quote expressions */ 9661f28255Scgd struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 9761f28255Scgd struct ifsregion *ifslastp; /* last struct in list */ 9861f28255Scgd struct arglist exparg; /* holds expanded arg list */ 9961f28255Scgd 100265b0617Skre static int empty_dollar_at; /* have expanded "$@" to nothing */ 101265b0617Skre 102dd6b6414Skre STATIC const char *argstr(const char *, int); 103dd6b6414Skre STATIC const char *exptilde(const char *, int); 104c02b3bbdSchristos STATIC void expbackq(union node *, int, int); 10551791eabSkre STATIC const char *expari(const char *); 106dd6b6414Skre STATIC int subevalvar(const char *, const char *, int, int, int); 107dd6b6414Skre STATIC int subevalvar_trim(const char *, int, int, int, int, int); 108dd6b6414Skre STATIC const char *evalvar(const char *, int); 109dd6b6414Skre STATIC int varisset(const char *, int); 110dd6b6414Skre STATIC void varvalue(const char *, int, int, int); 111c02b3bbdSchristos STATIC void recordregion(int, int, int); 112c02b3bbdSchristos STATIC void removerecordregions(int); 113c02b3bbdSchristos STATIC void ifsbreakup(char *, struct arglist *); 114c02b3bbdSchristos STATIC void ifsfree(void); 115c02b3bbdSchristos STATIC void expandmeta(struct strlist *, int); 116c02b3bbdSchristos STATIC void expmeta(char *, char *); 117c02b3bbdSchristos STATIC void addfname(char *); 118c02b3bbdSchristos STATIC struct strlist *expsort(struct strlist *); 119c02b3bbdSchristos STATIC struct strlist *msort(struct strlist *, int); 1208e4a570fSchristos STATIC int patmatch(const char *, const char *, int); 121c02b3bbdSchristos STATIC char *cvtnum(int, char *); 1228e4a570fSchristos static int collate_range_cmp(wchar_t, wchar_t); 123dd6b6414Skre STATIC void add_args(struct strlist *); 124727a69dcSkre STATIC void rmescapes_nl(char *); 12533809804Schristos 126701ac132Skre #ifdef DEBUG 127701ac132Skre #define NULLTERM_4_TRACE(p) STACKSTRNUL(p) 128701ac132Skre #else 129701ac132Skre #define NULLTERM_4_TRACE(p) do { /* nothing */ } while (/*CONSTCOND*/0) 130701ac132Skre #endif 131701ac132Skre 132c9f333adSkre #define IS_BORING(_ch) \ 133c9f333adSkre ((_ch) == CTLQUOTEMARK || (_ch) == CTLQUOTEEND || (_ch) == CTLNONL) 134c9f333adSkre #define SKIP_BORING(p) \ 135c9f333adSkre do { \ 136c9f333adSkre char _ch; \ 137c9f333adSkre \ 138c9f333adSkre while ((_ch = *(p)), IS_BORING(_ch)) \ 139c9f333adSkre (p)++; \ 140c9f333adSkre } while (0) 141c9f333adSkre 14261f28255Scgd /* 14361f28255Scgd * Expand shell variables and backquotes inside a here document. 14461f28255Scgd */ 14561f28255Scgd 14661f28255Scgd void 147c02b3bbdSchristos expandhere(union node *arg, int fd) 14861f28255Scgd { 1491d1484aaSchristos 15061f28255Scgd herefd = fd; 1519f61b804Splunky expandarg(arg, NULL, 0); 15261f28255Scgd xwrite(fd, stackblock(), expdest - stackblock()); 15361f28255Scgd } 15461f28255Scgd 15561f28255Scgd 1568e4a570fSchristos static int 1578e4a570fSchristos collate_range_cmp(wchar_t c1, wchar_t c2) 1588e4a570fSchristos { 1598e4a570fSchristos wchar_t s1[2], s2[2]; 1608e4a570fSchristos 1618e4a570fSchristos s1[0] = c1; 1628e4a570fSchristos s1[1] = L'\0'; 1638e4a570fSchristos s2[0] = c2; 1648e4a570fSchristos s2[1] = L'\0'; 1658e4a570fSchristos return (wcscoll(s1, s2)); 1668e4a570fSchristos } 1678e4a570fSchristos 16861f28255Scgd /* 16961f28255Scgd * Perform variable substitution and command substitution on an argument, 17037ed7877Sjtc * placing the resulting list of arguments in arglist. If EXP_FULL is true, 17161f28255Scgd * perform splitting and file name expansion. When arglist is NULL, perform 17261f28255Scgd * here document expansion. 17361f28255Scgd */ 17461f28255Scgd 17561f28255Scgd void 176c02b3bbdSchristos expandarg(union node *arg, struct arglist *arglist, int flag) 17761f28255Scgd { 17861f28255Scgd struct strlist *sp; 17961f28255Scgd char *p; 18061f28255Scgd 18158810bf0Skre CTRACE(DBG_EXPAND, ("expandarg(fl=%#x)\n", flag)); 182dd6b6414Skre if (fflag) /* no filename expandsion */ 183dd6b6414Skre flag &= ~EXP_GLOB; 184dd6b6414Skre 185265b0617Skre empty_dollar_at = 0; 18661f28255Scgd argbackq = arg->narg.backquote; 18761f28255Scgd STARTSTACKSTR(expdest); 18861f28255Scgd ifsfirst.next = NULL; 18961f28255Scgd ifslastp = NULL; 190727a69dcSkre line_number = arg->narg.lineno; 19169c48e32Skre argstr(arg->narg.text, flag); 19237ed7877Sjtc if (arglist == NULL) { 1931fca9bbfSkre STACKSTRNUL(expdest); 19458810bf0Skre CTRACE(DBG_EXPAND, ("expandarg: no arglist, done (%d) \"%s\"\n", 19558810bf0Skre expdest - stackblock(), stackblock())); 19661f28255Scgd return; /* here document expanded */ 19737ed7877Sjtc } 19861f28255Scgd STPUTC('\0', expdest); 19958810bf0Skre CTRACE(DBG_EXPAND, ("expandarg: arglist got (%d) \"%s\"\n", 20058810bf0Skre expdest - stackblock() - 1, stackblock())); 20161f28255Scgd p = grabstackstr(expdest); 20261f28255Scgd exparg.lastp = &exparg.list; 20337ed7877Sjtc /* 20437ed7877Sjtc * TODO - EXP_REDIR 20537ed7877Sjtc */ 206dd6b6414Skre if (flag & EXP_SPLIT) { 20761f28255Scgd ifsbreakup(p, &exparg); 20861f28255Scgd *exparg.lastp = NULL; 20961f28255Scgd exparg.lastp = &exparg.list; 210dd6b6414Skre if (flag & EXP_GLOB) 21137ed7877Sjtc expandmeta(exparg.list, flag); 212dd6b6414Skre else 213dd6b6414Skre add_args(exparg.list); 214256d645dSkre #if 0 215256d645dSkre } else if (flag & EXP_REDIR) { 216256d645dSkre /* if EXP_REDIR ever happens, it happens here */ 217256d645dSkre /* for now just (below) remove escapes, and leave it alone */ 218256d645dSkre #endif 21961f28255Scgd } else { 220256d645dSkre rmescapes(p); /* we might have escaped CTL bytes to remove */ 22178204bbfSchristos sp = stalloc(sizeof(*sp)); 22261f28255Scgd sp->text = p; 22361f28255Scgd *exparg.lastp = sp; 22461f28255Scgd exparg.lastp = &sp->next; 22561f28255Scgd } 226f7c8df6dSchristos ifsfree(); 22761f28255Scgd *exparg.lastp = NULL; 22861f28255Scgd if (exparg.list) { 22961f28255Scgd *arglist->lastp = exparg.list; 23061f28255Scgd arglist->lastp = exparg.lastp; 23161f28255Scgd } 23261f28255Scgd } 23361f28255Scgd 23461f28255Scgd 23561f28255Scgd 23661f28255Scgd /* 237c6cbc16dSdsl * Perform variable and command substitution. 238dd6b6414Skre * If EXP_GLOB is set, output CTLESC characters to allow for further processing. 239dd6b6414Skre * If EXP_SPLIT is set, remember location of result for later, 240c6cbc16dSdsl * Otherwise treat $@ like $* since no splitting will be performed. 24161f28255Scgd */ 24261f28255Scgd 243dd6b6414Skre STATIC const char * 244dd6b6414Skre argstr(const char *p, int flag) 24561f28255Scgd { 24648250187Stls char c; 247df073671Skre const int quotes = flag & EXP_QNEEDED; /* do CTLESC */ 24837ed7877Sjtc int firsteq = 1; 249265b0617Skre int had_dol_at = 0; 250265b0617Skre int startoff; 2515166671bSlukem const char *ifs = NULL; 252c6cbc16dSdsl int ifs_split = EXP_IFS_SPLIT; 253c6cbc16dSdsl 254c6cbc16dSdsl if (flag & EXP_IFS_SPLIT) 255ca12a0b8Schristos ifs = ifsval(); 25661f28255Scgd 257ea89b130Skre CTRACE(DBG_EXPAND, ("argstr(\"%s\", %#x) quotes=%#x\n", p,flag,quotes)); 258dd6b6414Skre 259265b0617Skre startoff = expdest - stackblock(); 26037ed7877Sjtc if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 26137ed7877Sjtc p = exptilde(p, flag); 26261f28255Scgd for (;;) { 26361f28255Scgd switch (c = *p++) { 26461f28255Scgd case '\0': 265701ac132Skre NULLTERM_4_TRACE(expdest); 266dd6b6414Skre VTRACE(DBG_EXPAND, ("argstr returning at \"\" " 267ee3b307fSkre "added \"%s\" to expdest\n", stackblock())); 268dd6b6414Skre return p - 1; 269e4a2a056Sdsl case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */ 270dd6b6414Skre case CTLENDARI: /* end of a $(( )) string */ 271*d08d589dSchristos if (had_dol_at && *p == CTLQUOTEEND) 272265b0617Skre p++; 273701ac132Skre NULLTERM_4_TRACE(expdest); 274dd6b6414Skre VTRACE(DBG_EXPAND, ("argstr returning at \"%.6s\"..." 275ee3b307fSkre " after %2.2X; added \"%s\" to expdest\n", 276ee3b307fSkre p, (c & 0xff), stackblock())); 277dd6b6414Skre return p; 2781fbf0781Smycroft case CTLQUOTEMARK: 2791fbf0781Smycroft /* "$@" syntax adherence hack */ 280dec2ca90Schristos if (p[0] == CTLVAR && p[1] & VSQUOTE && 281265b0617Skre p[2] == '@' && p[3] == '=') { 282265b0617Skre had_dol_at = 1; 2831fbf0781Smycroft break; 284265b0617Skre } 285265b0617Skre had_dol_at = 0; 286265b0617Skre empty_dollar_at = 0; 287dd6b6414Skre if ((flag & EXP_SPLIT) != 0) 2881fbf0781Smycroft STPUTC(c, expdest); 289c6cbc16dSdsl ifs_split = 0; 290c6cbc16dSdsl break; 291727a69dcSkre case CTLNONL: 292727a69dcSkre if (flag & EXP_NL) 293727a69dcSkre STPUTC(c, expdest); 294727a69dcSkre line_number++; 295727a69dcSkre break; 2965f92382cSkre case CTLCNL: 2975f92382cSkre STPUTC('\n', expdest); /* no line_number++ */ 2985f92382cSkre break; 299c6cbc16dSdsl case CTLQUOTEEND: 300265b0617Skre if (empty_dollar_at && 301265b0617Skre expdest - stackblock() > startoff && 302265b0617Skre expdest[-1] == CTLQUOTEMARK) 303265b0617Skre expdest--; 304265b0617Skre else if (!had_dol_at && (flag & EXP_SPLIT) != 0) 305c9f333adSkre STPUTC(c, expdest); 306c6cbc16dSdsl ifs_split = EXP_IFS_SPLIT; 307265b0617Skre had_dol_at = 0; 3081fbf0781Smycroft break; 30961f28255Scgd case CTLESC: 310256d645dSkre if (quotes || ISCTL(*p)) 31161f28255Scgd STPUTC(c, expdest); 31261f28255Scgd c = *p++; 31361f28255Scgd STPUTC(c, expdest); 314ee3b307fSkre if (c == '\n') /* should not happen, but ... */ 315ee3b307fSkre line_number++; 31661f28255Scgd break; 317ee3b307fSkre case CTLVAR: { 318ee3b307fSkre #ifdef DEBUG 319ee3b307fSkre unsigned int pos = expdest - stackblock(); 320c9f333adSkre NULLTERM_4_TRACE(expdest); 321ee3b307fSkre #endif 322c6cbc16dSdsl p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split)); 323701ac132Skre NULLTERM_4_TRACE(expdest); 324dd6b6414Skre VTRACE(DBG_EXPAND, ("argstr evalvar " 325c9f333adSkre "added %zd \"%s\" to expdest\n", 326c9f333adSkre (size_t)(expdest - (stackblock() + pos)), 327ee3b307fSkre stackblock() + pos)); 32861f28255Scgd break; 329ee3b307fSkre } 33061f28255Scgd case CTLBACKQ: 331ee3b307fSkre case CTLBACKQ|CTLQUOTE: { 332ee3b307fSkre #ifdef DEBUG 333ee3b307fSkre unsigned int pos = expdest - stackblock(); 334ee3b307fSkre #endif 33537ed7877Sjtc expbackq(argbackq->n, c & CTLQUOTE, flag); 33661f28255Scgd argbackq = argbackq->next; 337701ac132Skre NULLTERM_4_TRACE(expdest); 338ee3b307fSkre VTRACE(DBG_EXPAND, ("argstr expbackq added \"%s\" " 339ee3b307fSkre "to expdest\n", stackblock() + pos)); 34061f28255Scgd break; 341ee3b307fSkre } 342ee3b307fSkre case CTLARI: { 343ee3b307fSkre #ifdef DEBUG 344ee3b307fSkre unsigned int pos = expdest - stackblock(); 345ee3b307fSkre #endif 34651791eabSkre p = expari(p); 347701ac132Skre NULLTERM_4_TRACE(expdest); 348dd6b6414Skre VTRACE(DBG_EXPAND, ("argstr expari " 349ee3b307fSkre "+ \"%s\" to expdest p=\"%.5s...\"\n", 350ee3b307fSkre stackblock() + pos, p)); 35137ed7877Sjtc break; 352ee3b307fSkre } 35337ed7877Sjtc case ':': 35437ed7877Sjtc case '=': 35537ed7877Sjtc /* 35637ed7877Sjtc * sort of a hack - expand tildes in variable 35737ed7877Sjtc * assignments (after the first '=' and after ':'s). 35837ed7877Sjtc */ 35937ed7877Sjtc STPUTC(c, expdest); 36037ed7877Sjtc if (flag & EXP_VARTILDE && *p == '~') { 36137ed7877Sjtc if (c == '=') { 36237ed7877Sjtc if (firsteq) 36337ed7877Sjtc firsteq = 0; 36437ed7877Sjtc else 36537ed7877Sjtc break; 36637ed7877Sjtc } 36737ed7877Sjtc p = exptilde(p, flag); 36837ed7877Sjtc } 36937ed7877Sjtc break; 37061f28255Scgd default: 371727a69dcSkre if (c == '\n') 372727a69dcSkre line_number++; 37361f28255Scgd STPUTC(c, expdest); 3747d60739aSdsl if (flag & ifs_split && strchr(ifs, c) != NULL) { 375c6cbc16dSdsl /* We need to get the output split here... */ 376c6cbc16dSdsl recordregion(expdest - stackblock() - 1, 377c6cbc16dSdsl expdest - stackblock(), 0); 378c6cbc16dSdsl } 379e4a2a056Sdsl break; 38061f28255Scgd } 38161f28255Scgd } 38261f28255Scgd } 38361f28255Scgd 384dd6b6414Skre STATIC const char * 385dd6b6414Skre exptilde(const char *p, int flag) 38637ed7877Sjtc { 387dd6b6414Skre char c; 388dd6b6414Skre const char *startp = p; 38937ed7877Sjtc struct passwd *pw; 3905dd823abSmycroft const char *home; 391df073671Skre const int quotes = flag & EXP_QNEEDED; 392dd6b6414Skre char *user; 393ee3b307fSkre struct stackmark smark; 394ee3b307fSkre #ifdef DEBUG 395ee3b307fSkre unsigned int offs = expdest - stackblock(); 396ee3b307fSkre #endif 39737ed7877Sjtc 398ee3b307fSkre setstackmark(&smark); 399bd208c69Skre (void) grabstackstr(expdest); 400ee3b307fSkre user = stackblock(); /* we will just borrow top of stack */ 401ee3b307fSkre 402aa681addSkre while ((c = *++p) != '\0') { 40337ed7877Sjtc switch(c) { 404ee3b307fSkre case CTLESC: /* any of these occurring */ 405ee3b307fSkre case CTLVAR: /* means ~ expansion */ 406ee3b307fSkre case CTLBACKQ: /* does not happen at all */ 407dd6b6414Skre case CTLBACKQ | CTLQUOTE: 408ee3b307fSkre case CTLARI: /* just leave original unchanged */ 409dd6b6414Skre case CTLENDARI: 4101fbf0781Smycroft case CTLQUOTEMARK: 411ee3b307fSkre case '\n': 412ee3b307fSkre popstackmark(&smark); 4131fbf0781Smycroft return (startp); 414727a69dcSkre case CTLNONL: 415ee3b307fSkre continue; 41637ed7877Sjtc case ':': 417bd208c69Skre if (!posix || flag & EXP_VARTILDE) 41837ed7877Sjtc goto done; 41937ed7877Sjtc break; 420bd208c69Skre case CTLENDVAR: 42137ed7877Sjtc case '/': 42237ed7877Sjtc goto done; 42337ed7877Sjtc } 424dd6b6414Skre STPUTC(c, user); 42537ed7877Sjtc } 42637ed7877Sjtc done: 427dd6b6414Skre STACKSTRNUL(user); 428ee3b307fSkre user = stackblock(); /* to start of collected username */ 429ea89b130Skre 430ee3b307fSkre CTRACE(DBG_EXPAND, ("exptilde, found \"~%s\"", user)); 431ee3b307fSkre if (*user == '\0') { 432dd6b6414Skre home = lookupvar("HOME"); 433ee3b307fSkre /* 434ee3b307fSkre * if HOME is unset, results are unspecified... 435ee3b307fSkre * we used to just leave the ~ unchanged, but 436ee3b307fSkre * (some) other shells do ... and this seems more useful. 437ee3b307fSkre */ 438ee3b307fSkre if (home == NULL && (pw = getpwuid(getuid())) != NULL) 43937ed7877Sjtc home = pw->pw_dir; 440ee3b307fSkre } else if ((pw = getpwnam(user)) == NULL) { 441ee3b307fSkre /* 442ee3b307fSkre * If user does not exist, results are undefined. 443ee3b307fSkre * so we can abort() here if we want, but let's not! 444ee3b307fSkre */ 445ee3b307fSkre home = NULL; 446ee3b307fSkre } else 447ee3b307fSkre home = pw->pw_dir; 448dd6b6414Skre 449ee3b307fSkre VTRACE(DBG_EXPAND, (" ->\"%s\"", home ? home : "<<NULL>>")); 450ee3b307fSkre popstackmark(&smark); /* now expdest is valid again */ 451ee3b307fSkre 452ee3b307fSkre /* 453ee3b307fSkre * Posix XCU 2.6.1: The value of $HOME (for ~) or the initial 454ee3b307fSkre * working directory from getpwnam() for ~user 455ee3b307fSkre * Nothing there about "except if a null string". So do what it wants. 456ee3b307fSkre */ 457ee3b307fSkre if (home == NULL /* || *home == '\0' */) { 458ee3b307fSkre CTRACE(DBG_EXPAND, (": returning unused \"%s\"\n", startp)); 459dd6b6414Skre return startp; 460ee3b307fSkre } while ((c = *home++) != '\0') { 461256d645dSkre if ((quotes && NEEDESC(c)) || ISCTL(c)) 46237ed7877Sjtc STPUTC(CTLESC, expdest); 46337ed7877Sjtc STPUTC(c, expdest); 46437ed7877Sjtc } 465ee3b307fSkre CTRACE(DBG_EXPAND, (": added %d \"%.*s\" returning \"%s\"\n", 466ee3b307fSkre expdest - stackblock() - offs, expdest - stackblock() - offs, 467ee3b307fSkre stackblock() + offs, p)); 468ea89b130Skre 46937ed7877Sjtc return (p); 47037ed7877Sjtc } 47137ed7877Sjtc 47237ed7877Sjtc 4731fbf0781Smycroft STATIC void 474c02b3bbdSchristos removerecordregions(int endoff) 4751fbf0781Smycroft { 47658810bf0Skre 47758810bf0Skre VTRACE(DBG_EXPAND, ("removerecordregions(%d):", endoff)); 47858810bf0Skre if (ifslastp == NULL) { 47958810bf0Skre VTRACE(DBG_EXPAND, (" none\n", endoff)); 4801fbf0781Smycroft return; 48158810bf0Skre } 4821fbf0781Smycroft 4831fbf0781Smycroft if (ifsfirst.endoff > endoff) { 48458810bf0Skre VTRACE(DBG_EXPAND, (" first(%d)", ifsfirst.endoff)); 4851fbf0781Smycroft while (ifsfirst.next != NULL) { 4861fbf0781Smycroft struct ifsregion *ifsp; 4871fbf0781Smycroft INTOFF; 4881fbf0781Smycroft ifsp = ifsfirst.next->next; 4891fbf0781Smycroft ckfree(ifsfirst.next); 4901fbf0781Smycroft ifsfirst.next = ifsp; 4911fbf0781Smycroft INTON; 4921fbf0781Smycroft } 4931fbf0781Smycroft if (ifsfirst.begoff > endoff) 4941fbf0781Smycroft ifslastp = NULL; 4951fbf0781Smycroft else { 49658810bf0Skre VTRACE(DBG_EXPAND,("->(%d,%d)",ifsfirst.begoff,endoff)); 4971fbf0781Smycroft ifslastp = &ifsfirst; 4981fbf0781Smycroft ifsfirst.endoff = endoff; 4991fbf0781Smycroft } 50058810bf0Skre VTRACE(DBG_EXPAND, ("\n")); 5011fbf0781Smycroft return; 5021fbf0781Smycroft } 5031fbf0781Smycroft 5041fbf0781Smycroft ifslastp = &ifsfirst; 5051fbf0781Smycroft while (ifslastp->next && ifslastp->next->begoff < endoff) 5061fbf0781Smycroft ifslastp=ifslastp->next; 50758810bf0Skre VTRACE(DBG_EXPAND, (" found(%d,%d)", ifslastp->begoff,ifslastp->endoff)); 5081fbf0781Smycroft while (ifslastp->next != NULL) { 5091fbf0781Smycroft struct ifsregion *ifsp; 5101fbf0781Smycroft INTOFF; 5111fbf0781Smycroft ifsp = ifslastp->next->next; 5121fbf0781Smycroft ckfree(ifslastp->next); 5131fbf0781Smycroft ifslastp->next = ifsp; 5141fbf0781Smycroft INTON; 5151fbf0781Smycroft } 5161fbf0781Smycroft if (ifslastp->endoff > endoff) 5171fbf0781Smycroft ifslastp->endoff = endoff; 51858810bf0Skre VTRACE(DBG_EXPAND, ("->(%d,%d)", ifslastp->begoff,ifslastp->endoff)); 5191fbf0781Smycroft } 5201fbf0781Smycroft 5211fbf0781Smycroft 52237ed7877Sjtc /* 523ee3b307fSkre * Expand arithmetic expression. 524ee3b307fSkre * 525ee3b307fSkre * In this incarnation, we start at the beginning (yes, "Let's start at the 526ee3b307fSkre * very beginning. A very good place to start.") and collect the expression 527ee3b307fSkre * until the end - which means expanding anything contained within. 528ee3b307fSkre * 529ee3b307fSkre * Fortunately, argstr() just happens to do that for us... 53037ed7877Sjtc */ 531dd6b6414Skre STATIC const char * 53251791eabSkre expari(const char *p) 53337ed7877Sjtc { 534dd6b6414Skre char *q, *start; 53591ce988bSapb intmax_t result; 53691ce988bSapb int adjustment; 5371fbf0781Smycroft int begoff; 5381fbf0781Smycroft int quoted; 539ee3b307fSkre struct stackmark smark; 54037ed7877Sjtc 5411fbf0781Smycroft /* ifsfree(); */ 542633ceb6dSchristos 54337ed7877Sjtc /* 544cc8e58edSkre * SPACE_NEEDED is enough for all possible digits (rounded up) 545cc8e58edSkre * plus possible "-", and the terminating '\0', hence, plus 2 546cc8e58edSkre * 547cc8e58edSkre * The calculation produces the number of bytes needed to 548cc8e58edSkre * represent the biggest possible value, in octal. We only 549cc8e58edSkre * generate decimal, which takes (often) less digits (never more) 550cc8e58edSkre * so this is safe, if occasionally slightly wasteful. 551cc8e58edSkre */ 552dd6b6414Skre #define SPACE_NEEDED ((int)((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 2)) 553ee3b307fSkre 554dd6b6414Skre quoted = *p++ == '"'; 555dd6b6414Skre begoff = expdest - stackblock(); 556ee3b307fSkre VTRACE(DBG_EXPAND, ("expari%s: \"%s\" begoff %d\n", 557ee3b307fSkre quoted ? "(quoted)" : "", p, begoff)); 558ee3b307fSkre 559727a69dcSkre p = argstr(p, EXP_NL); /* expand $(( )) string */ 560dd6b6414Skre STPUTC('\0', expdest); 561dd6b6414Skre start = stackblock() + begoff; 562ee3b307fSkre 563ee3b307fSkre removerecordregions(begoff); /* nothing there is kept */ 564727a69dcSkre rmescapes_nl(start); /* convert CRTNONL back into \n's */ 565ee3b307fSkre 566ee3b307fSkre setstackmark(&smark); 567ee3b307fSkre q = grabstackstr(expdest); /* keep the expression while eval'ing */ 568727a69dcSkre result = arith(start, line_number); 569ee3b307fSkre popstackmark(&smark); /* return the stack to before grab */ 570633ceb6dSchristos 571ee3b307fSkre start = stackblock() + begoff; /* block may have moved */ 572dd6b6414Skre adjustment = expdest - start; 573ee3b307fSkre STADJUST(-adjustment, expdest); /* remove the argstr() result */ 574dd6b6414Skre 575ee3b307fSkre CHECKSTRSPACE(SPACE_NEEDED, expdest); /* nb: stack block might move */ 576dd6b6414Skre fmtstr(expdest, SPACE_NEEDED, "%"PRIdMAX, result); 577dd6b6414Skre 578ee3b307fSkre for (q = expdest; *q++ != '\0'; ) /* find end of what we added */ 57937ed7877Sjtc ; 580633ceb6dSchristos 581dd6b6414Skre if (quoted == 0) /* allow weird splitting */ 58258810bf0Skre recordregion(begoff, begoff + q - 1 - expdest, 0); 583d3573c91Skre adjustment = q - expdest - 1; 584ee3b307fSkre STADJUST(adjustment, expdest); /* move expdest to end */ 585f72bc19eSkre VTRACE(DBG_EXPAND, ("expari: adding %d \"%s\", returning \"%.5s...\"\n", 586f72bc19eSkre adjustment, stackblock() + begoff, p)); 587dd6b6414Skre 588dd6b6414Skre return p; 58937ed7877Sjtc } 59037ed7877Sjtc 59161f28255Scgd 59261f28255Scgd /* 593ee3b307fSkre * Expand stuff in backwards quotes (these days, any command substitution). 59461f28255Scgd */ 59561f28255Scgd 59661f28255Scgd STATIC void 597c02b3bbdSchristos expbackq(union node *cmd, int quoted, int flag) 59861f28255Scgd { 59961f28255Scgd struct backcmd in; 60061f28255Scgd int i; 60161f28255Scgd char buf[128]; 60261f28255Scgd char *p; 603ee3b307fSkre char *dest = expdest; /* expdest may be reused by eval, use an alt */ 60461f28255Scgd struct ifsregion saveifs, *savelastp; 60561f28255Scgd struct nodelist *saveargbackq; 60661f28255Scgd char lastc; 60761f28255Scgd int startloc = dest - stackblock(); 60861f28255Scgd int saveherefd; 609df073671Skre const int quotes = flag & EXP_QNEEDED; 61069a4e2eeSchristos int nnl; 611ee3b307fSkre struct stackmark smark; 61261f28255Scgd 613ee3b307fSkre VTRACE(DBG_EXPAND, ("expbackq( ..., q=%d flag=%#x) have %d\n", 614ee3b307fSkre quoted, flag, startloc)); 61561f28255Scgd INTOFF; 61661f28255Scgd saveifs = ifsfirst; 61761f28255Scgd savelastp = ifslastp; 61861f28255Scgd saveargbackq = argbackq; 61961f28255Scgd saveherefd = herefd; 62061f28255Scgd herefd = -1; 621ee3b307fSkre 622ee3b307fSkre setstackmark(&smark); /* preserve the stack */ 623ee3b307fSkre p = grabstackstr(dest); /* save what we have there currently */ 624ee3b307fSkre evalbackcmd(cmd, &in); /* evaluate the $( ) tree (using stack) */ 625ee3b307fSkre popstackmark(&smark); /* and return stack to when we entered */ 626ee3b307fSkre 62761f28255Scgd ifsfirst = saveifs; 62861f28255Scgd ifslastp = savelastp; 62961f28255Scgd argbackq = saveargbackq; 63061f28255Scgd herefd = saveherefd; 63161f28255Scgd 632ee3b307fSkre p = in.buf; /* now extract the results */ 633ee3b307fSkre nnl = 0; /* dropping trailing \n's */ 63461f28255Scgd for (;;) { 63561f28255Scgd if (--in.nleft < 0) { 63661f28255Scgd if (in.fd < 0) 63761f28255Scgd break; 6381fca9bbfSkre INTON; 6393eb04a36Sjoerg while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR) 6403eb04a36Sjoerg continue; 6411fca9bbfSkre INTOFF; 642ea89b130Skre VTRACE(DBG_EXPAND, ("expbackq: read returns %d\n", i)); 64361f28255Scgd if (i <= 0) 64461f28255Scgd break; 64561f28255Scgd p = buf; 64661f28255Scgd in.nleft = i - 1; 64761f28255Scgd } 64861f28255Scgd lastc = *p++; 64961f28255Scgd if (lastc != '\0') { 650ee3b307fSkre if (lastc == '\n') /* don't save \n yet */ 651ee3b307fSkre nnl++; /* it might be trailing */ 65269a4e2eeSchristos else { 653ee3b307fSkre /* 654ee3b307fSkre * We have something other than \n 655ee3b307fSkre * 656ee3b307fSkre * Before saving it, we need to insert 657ee3b307fSkre * any \n's that we have just skipped. 658ee3b307fSkre */ 659ee3b307fSkre 660ee3b307fSkre /* XXX 661ee3b307fSkre * this hack is just because our 662ee3b307fSkre * CHECKSTRSPACE() is lazy, and only 663ee3b307fSkre * ever grows the stack once, even 664ee3b307fSkre * if that does not allocate the space 665ee3b307fSkre * we requested. ie: safe for small 666ee3b307fSkre * requests, but not large ones. 667ee3b307fSkre * FIXME someday... 668ee3b307fSkre */ 669ee3b307fSkre if (nnl < 20) { 67069a4e2eeSchristos CHECKSTRSPACE(nnl + 2, dest); 67169a4e2eeSchristos while (nnl > 0) { 67269a4e2eeSchristos nnl--; 67369a4e2eeSchristos USTPUTC('\n', dest); 67469a4e2eeSchristos } 675ee3b307fSkre } else { 676ee3b307fSkre /* The slower, safer, way */ 677ee3b307fSkre while (nnl > 0) { 678ee3b307fSkre nnl--; 679ee3b307fSkre STPUTC('\n', dest); 680ee3b307fSkre } 681ee3b307fSkre CHECKSTRSPACE(2, dest); 682ee3b307fSkre } 683256d645dSkre if ((quotes && quoted && NEEDESC(lastc)) || 684256d645dSkre ISCTL(lastc)) 68569a4e2eeSchristos USTPUTC(CTLESC, dest); 68669a4e2eeSchristos USTPUTC(lastc, dest); 68761f28255Scgd } 68861f28255Scgd } 68969a4e2eeSchristos } 6906ab19545Schristos 69161f28255Scgd if (in.fd >= 0) 69261f28255Scgd close(in.fd); 69361f28255Scgd if (in.buf) 69461f28255Scgd ckfree(in.buf); 69561f28255Scgd if (in.jp) 696c02b3bbdSchristos back_exitstatus = waitforjob(in.jp); 69761f28255Scgd if (quoted == 0) 69861f28255Scgd recordregion(startloc, dest - stackblock(), 0); 699ea89b130Skre CTRACE(DBG_EXPAND, ("evalbackq: size=%d: \"%.*s\"\n", 7007fb5a8c6Sdholland (int)((dest - stackblock()) - startloc), 7017fb5a8c6Sdholland (int)((dest - stackblock()) - startloc), 70261f28255Scgd stackblock() + startloc)); 703ee3b307fSkre 704ee3b307fSkre expdest = dest; /* all done, expdest is all ours again */ 70561f28255Scgd INTON; 70661f28255Scgd } 70761f28255Scgd 70861f28255Scgd 709ad8d5369Schristos STATIC int 710dd6b6414Skre subevalvar(const char *p, const char *str, int subtype, int startloc, 711dd6b6414Skre int varflags) 712ad8d5369Schristos { 713ad8d5369Schristos char *startp; 714ad8d5369Schristos int saveherefd = herefd; 715ad8d5369Schristos struct nodelist *saveargbackq = argbackq; 716dd6b6414Skre int amount; 717d3aec206Schristos 718ad8d5369Schristos herefd = -1; 719ee3b307fSkre VTRACE(DBG_EXPAND, ("subevalvar(%d) \"%.20s\" ${%.*s} sloc=%d vf=%x\n", 720ee3b307fSkre subtype, p, p-str, str, startloc, varflags)); 721bd208c69Skre argstr(p, subtype == VSASSIGN ? EXP_VARTILDE : EXP_TILDE); 722ad8d5369Schristos STACKSTRNUL(expdest); 723ad8d5369Schristos herefd = saveherefd; 724ad8d5369Schristos argbackq = saveargbackq; 725ad8d5369Schristos startp = stackblock() + startloc; 726ad8d5369Schristos 727ad8d5369Schristos switch (subtype) { 728ad8d5369Schristos case VSASSIGN: 729ad8d5369Schristos setvar(str, startp, 0); 730ee3b307fSkre amount = startp - expdest; /* remove what argstr added */ 731d3aec206Schristos STADJUST(amount, expdest); 732ee3b307fSkre varflags &= ~VSNUL; /*XXX Huh? What's that achieve? */ 733ee3b307fSkre return 1; /* go back and eval var again */ 734ad8d5369Schristos 735ad8d5369Schristos case VSQUESTION: 736ad8d5369Schristos if (*p != CTLENDVAR) { 737ad8d5369Schristos outfmt(&errout, "%s\n", startp); 7389f61b804Splunky error(NULL); 739ad8d5369Schristos } 7407fb5a8c6Sdholland error("%.*s: parameter %snot set", 7417fb5a8c6Sdholland (int)(p - str - 1), 742ad8d5369Schristos str, (varflags & VSNUL) ? "null or " 743ad8d5369Schristos : nullstr); 744ee9e50eaSmycroft /* NOTREACHED */ 745ad8d5369Schristos 746dd6b6414Skre default: 747dd6b6414Skre abort(); 748dd6b6414Skre } 749dd6b6414Skre } 750dd6b6414Skre 751dd6b6414Skre STATIC int 752dd6b6414Skre subevalvar_trim(const char *p, int strloc, int subtype, int startloc, 753dd6b6414Skre int varflags, int quotes) 754dd6b6414Skre { 755dd6b6414Skre char *startp; 756dd6b6414Skre char *str; 757dd6b6414Skre char *loc = NULL; 758dd6b6414Skre char *q; 759dd6b6414Skre int c = 0; 760dd6b6414Skre int saveherefd = herefd; 761dd6b6414Skre struct nodelist *saveargbackq = argbackq; 762dd6b6414Skre int amount; 763dd6b6414Skre 764dd6b6414Skre herefd = -1; 765dd6b6414Skre switch (subtype) { 766dd6b6414Skre case VSTRIMLEFT: 767dd6b6414Skre case VSTRIMLEFTMAX: 768dd6b6414Skre case VSTRIMRIGHT: 769dd6b6414Skre case VSTRIMRIGHTMAX: 770dd6b6414Skre break; 771dd6b6414Skre default: 772dd6b6414Skre abort(); 773dd6b6414Skre break; 774dd6b6414Skre } 775ea89b130Skre 776ea89b130Skre VTRACE(DBG_EXPAND, 777ea89b130Skre ("subevalvar_trim(\"%.9s\", STR@%d, SUBT=%d, start@%d, vf=%x, q=%x)\n", 778ea89b130Skre p, strloc, subtype, startloc, varflags, quotes)); 779ea89b130Skre 780dd6b6414Skre argstr(p, (varflags & (VSQUOTE|VSPATQ)) == VSQUOTE ? 0 : EXP_CASE); 781dd6b6414Skre STACKSTRNUL(expdest); 782dd6b6414Skre herefd = saveherefd; 783dd6b6414Skre argbackq = saveargbackq; 784dd6b6414Skre startp = stackblock() + startloc; 785dd6b6414Skre str = stackblock() + strloc; 786dd6b6414Skre 787dd6b6414Skre switch (subtype) { 788dd6b6414Skre 789ad8d5369Schristos case VSTRIMLEFT: 790633ceb6dSchristos for (loc = startp; loc < str; loc++) { 791ad8d5369Schristos c = *loc; 792ad8d5369Schristos *loc = '\0'; 79319d6b839Schristos if (patmatch(str, startp, quotes)) 794ad8d5369Schristos goto recordleft; 795ad8d5369Schristos *loc = c; 79619d6b839Schristos if (quotes && *loc == CTLESC) 79728607542Schristos loc++; 798ad8d5369Schristos } 799ad8d5369Schristos return 0; 800ad8d5369Schristos 801ad8d5369Schristos case VSTRIMLEFTMAX: 80228607542Schristos for (loc = str - 1; loc >= startp;) { 803ad8d5369Schristos c = *loc; 804ad8d5369Schristos *loc = '\0'; 80519d6b839Schristos if (patmatch(str, startp, quotes)) 806ad8d5369Schristos goto recordleft; 807ad8d5369Schristos *loc = c; 80828607542Schristos loc--; 80919d6b839Schristos if (quotes && loc > startp && 81028607542Schristos *(loc - 1) == CTLESC) { 81128607542Schristos for (q = startp; q < loc; q++) 81228607542Schristos if (*q == CTLESC) 81328607542Schristos q++; 81428607542Schristos if (q > loc) 81528607542Schristos loc--; 81628607542Schristos } 817ad8d5369Schristos } 818ad8d5369Schristos return 0; 819ad8d5369Schristos 820ad8d5369Schristos case VSTRIMRIGHT: 82128607542Schristos for (loc = str - 1; loc >= startp;) { 82219d6b839Schristos if (patmatch(str, loc, quotes)) 82304784d87Schristos goto recordright; 82428607542Schristos loc--; 82519d6b839Schristos if (quotes && loc > startp && 82628607542Schristos *(loc - 1) == CTLESC) { 82728607542Schristos for (q = startp; q < loc; q++) 82828607542Schristos if (*q == CTLESC) 82928607542Schristos q++; 83028607542Schristos if (q > loc) 83128607542Schristos loc--; 83228607542Schristos } 83328607542Schristos } 834ad8d5369Schristos return 0; 835ad8d5369Schristos 836ad8d5369Schristos case VSTRIMRIGHTMAX: 83728607542Schristos for (loc = startp; loc < str - 1; loc++) { 83819d6b839Schristos if (patmatch(str, loc, quotes)) 83904784d87Schristos goto recordright; 84019d6b839Schristos if (quotes && *loc == CTLESC) 84128607542Schristos loc++; 84228607542Schristos } 843ad8d5369Schristos return 0; 844ad8d5369Schristos 845ad8d5369Schristos default: 846ad8d5369Schristos abort(); 847ad8d5369Schristos } 848ad8d5369Schristos 849ad8d5369Schristos recordleft: 85004784d87Schristos *loc = c; 851d3aec206Schristos amount = ((str - 1) - (loc - startp)) - expdest; 852d3aec206Schristos STADJUST(amount, expdest); 853ad8d5369Schristos while (loc != str - 1) 854ad8d5369Schristos *startp++ = *loc++; 855ad8d5369Schristos return 1; 85604784d87Schristos 85704784d87Schristos recordright: 85804784d87Schristos amount = loc - expdest; 85904784d87Schristos STADJUST(amount, expdest); 86004784d87Schristos STPUTC('\0', expdest); 86104784d87Schristos STADJUST(-1, expdest); 86204784d87Schristos return 1; 863ad8d5369Schristos } 864ad8d5369Schristos 865ad8d5369Schristos 86661f28255Scgd /* 86761f28255Scgd * Expand a variable, and return a pointer to the next character in the 86861f28255Scgd * input string. 86961f28255Scgd */ 87061f28255Scgd 871dd6b6414Skre STATIC const char * 872dd6b6414Skre evalvar(const char *p, int flag) 87361f28255Scgd { 87461f28255Scgd int subtype; 87537ed7877Sjtc int varflags; 876dd6b6414Skre const char *var; 87761f28255Scgd char *val; 878e97b0193She int patloc; 87961f28255Scgd int c; 88061f28255Scgd int set; 88161f28255Scgd int special; 88261f28255Scgd int startloc; 883ad8d5369Schristos int varlen; 884e4a2a056Sdsl int apply_ifs; 885df073671Skre const int quotes = flag & EXP_QNEEDED; 88661f28255Scgd 8874dcb41aeSchristos varflags = (unsigned char)*p++; 88837ed7877Sjtc subtype = varflags & VSTYPE; 88961f28255Scgd var = p; 89030651d6bSjdolecek special = !is_name(*p); 89161f28255Scgd p = strchr(p, '=') + 1; 892e4a2a056Sdsl 893ea89b130Skre CTRACE(DBG_EXPAND, 894ea89b130Skre ("evalvar \"%.*s\", flag=%#X quotes=%#X vf=%#X subtype=%X\n", 895ea89b130Skre p - var - 1, var, flag, quotes, varflags, subtype)); 896ea89b130Skre 89761f28255Scgd again: /* jump here after setting a variable with ${var=text} */ 898018a6f78Schristos if (varflags & VSLINENO) { 899727a69dcSkre if (line_num.flags & VUNSET) { 900727a69dcSkre set = 0; 901727a69dcSkre val = NULL; 902727a69dcSkre } else { 903018a6f78Schristos set = 1; 904dd6b6414Skre special = p - var; 905dd6b6414Skre val = NULL; 906727a69dcSkre } 907018a6f78Schristos } else if (special) { 908633ceb6dSchristos set = varisset(var, varflags & VSNUL); 90961f28255Scgd val = NULL; 910265b0617Skre if (!set && *var == '@') 911265b0617Skre empty_dollar_at = 1; 91261f28255Scgd } else { 91361f28255Scgd val = lookupvar(var); 91407bae7edSchristos if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 91561f28255Scgd val = NULL; 91661f28255Scgd set = 0; 91761f28255Scgd } else 91861f28255Scgd set = 1; 91961f28255Scgd } 920e4a2a056Sdsl 921ad8d5369Schristos varlen = 0; 92261f28255Scgd startloc = expdest - stackblock(); 923e4a2a056Sdsl 92483d9b545Sast if (!set && uflag && *var != '@' && *var != '*') { 925eac95517Schristos switch (subtype) { 926eac95517Schristos case VSNORMAL: 927eac95517Schristos case VSTRIMLEFT: 928eac95517Schristos case VSTRIMLEFTMAX: 929eac95517Schristos case VSTRIMRIGHT: 930eac95517Schristos case VSTRIMRIGHTMAX: 931eac95517Schristos case VSLENGTH: 9327fb5a8c6Sdholland error("%.*s: parameter not set", 9337fb5a8c6Sdholland (int)(p - var - 1), var); 934eac95517Schristos /* NOTREACHED */ 935eac95517Schristos } 936e4a2a056Sdsl } 937e4a2a056Sdsl 938265b0617Skre #if 0 /* no longer need this $@ evil ... */ 939c9f333adSkre if (!set && subtype != VSPLUS && special && *var == '@') 940c9f333adSkre if (startloc > 0 && expdest[-1] == CTLQUOTEMARK) 941c9f333adSkre expdest--, startloc--; 942265b0617Skre #endif 943c9f333adSkre 94461f28255Scgd if (set && subtype != VSPLUS) { 94561f28255Scgd /* insert the value of the variable */ 94661f28255Scgd if (special) { 947dd6b6414Skre if (varflags & VSLINENO) { 948dd6b6414Skre /* 949ee3b307fSkre * The LINENO hack (expansion part) 950dd6b6414Skre */ 951dd6b6414Skre while (--special > 0) { 952dd6b6414Skre /* not needed, it is a number... 953df073671Skre if (quotes && NEEDESC(*var)) 954dd6b6414Skre STPUTC(CTLESC, expdest); 955dd6b6414Skre */ 956dd6b6414Skre STPUTC(*var++, expdest); 957dd6b6414Skre } 958dd6b6414Skre } else 959b17e1b5cSdsl varvalue(var, varflags&VSQUOTE, subtype, flag); 960ad8d5369Schristos if (subtype == VSLENGTH) { 961a0fa692dSchristos varlen = expdest - stackblock() - startloc; 962a0fa692dSchristos STADJUST(-varlen, expdest); 963ad8d5369Schristos } 96461f28255Scgd } else { 96561f28255Scgd 966ad8d5369Schristos if (subtype == VSLENGTH) { 967ad8d5369Schristos for (; *val; val++) 968ad8d5369Schristos varlen++; 969df073671Skre } else if (quotes && varflags & VSQUOTE) { 97058e34de6Skre /* 97158e34de6Skre * If we are going to look for magic in the 97258e34de6Skre * value (quotes is set) and the expansion 97358e34de6Skre * occurs inside "" (VSQUOTE) then any char 97458e34de6Skre * that has any potential special meaning 97558e34de6Skre * needs to have that meaning suppressed, 97658e34de6Skre * so supply a CTLESC prefix for it. 97758e34de6Skre */ 978df073671Skre for (; (c = *val) != '\0'; val++) { 979df073671Skre if (NEEDESC(c)) 98061f28255Scgd STPUTC(CTLESC, expdest); 981df073671Skre STPUTC(c, expdest); 98261f28255Scgd } 983df073671Skre } else { 98458e34de6Skre /* 98558e34de6Skre * We are going to rmescapes() later, 98658e34de6Skre * so make sure that any data char that 98758e34de6Skre * might be mistaken for one of our CTLxxx 98858e34de6Skre * magic chars is protected ... always. 98958e34de6Skre */ 99058e34de6Skre for (; (c = *val) != '\0'; val++) { 991256d645dSkre if (ISCTL(c)) 99258e34de6Skre STPUTC(CTLESC, expdest); 99358e34de6Skre STPUTC(c, expdest); 99458e34de6Skre } 99561f28255Scgd } 99661f28255Scgd } 997ad8d5369Schristos } 998ad8d5369Schristos 999ad8d5369Schristos 1000c9f333adSkre if (varflags & VSQUOTE) { 10017d60739aSdsl if (*var == '@' && shellparam.nparam != 1) 10027d60739aSdsl apply_ifs = 1; 10037d60739aSdsl else { 10047d60739aSdsl /* 10057d60739aSdsl * Mark so that we don't apply IFS if we recurse through 10067d60739aSdsl * here expanding $bar from "${foo-$bar}". 10077d60739aSdsl */ 10087d60739aSdsl flag |= EXP_IN_QUOTES; 10097d60739aSdsl apply_ifs = 0; 10107d60739aSdsl } 1011c9f333adSkre } else if (flag & EXP_IN_QUOTES) { 1012c9f333adSkre apply_ifs = 0; 10137d60739aSdsl } else 10147d60739aSdsl apply_ifs = 1; 1015ad8d5369Schristos 1016ad8d5369Schristos switch (subtype) { 1017ad8d5369Schristos case VSLENGTH: 1018ad8d5369Schristos expdest = cvtnum(varlen, expdest); 1019e4a2a056Sdsl break; 1020ad8d5369Schristos 1021ad8d5369Schristos case VSNORMAL: 1022ad8d5369Schristos break; 1023ad8d5369Schristos 1024ad8d5369Schristos case VSPLUS: 1025e4a2a056Sdsl set = !set; 1026e4a2a056Sdsl /* FALLTHROUGH */ 1027ad8d5369Schristos case VSMINUS: 1028cf199667Schristos if (!set) { 1029c6cbc16dSdsl argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0)); 1030e4a2a056Sdsl /* 1031e4a2a056Sdsl * ${x-a b c} doesn't get split, but removing the 10325c056f67Swiz * 'apply_ifs = 0' apparently breaks ${1+"$@"}.. 1033e4a2a056Sdsl * ${x-'a b' c} should generate 2 args. 1034e4a2a056Sdsl */ 1035dec2ca90Schristos if (*p != CTLENDVAR) 1036e4a2a056Sdsl /* We should have marked stuff already */ 1037e4a2a056Sdsl apply_ifs = 0; 1038cf199667Schristos } 1039ad8d5369Schristos break; 1040ad8d5369Schristos 1041ad8d5369Schristos case VSTRIMLEFT: 1042ad8d5369Schristos case VSTRIMLEFTMAX: 1043ad8d5369Schristos case VSTRIMRIGHT: 1044ad8d5369Schristos case VSTRIMRIGHTMAX: 1045df029fccSkre if (!set) { 1046df029fccSkre set = 1; /* allow argbackq to be advanced if needed */ 1047ad8d5369Schristos break; 1048df029fccSkre } 1049ad8d5369Schristos /* 1050ad8d5369Schristos * Terminate the string and start recording the pattern 1051ad8d5369Schristos * right after it 1052ad8d5369Schristos */ 1053ad8d5369Schristos STPUTC('\0', expdest); 1054e97b0193She patloc = expdest - stackblock(); 1055dd6b6414Skre if (subevalvar_trim(p, patloc, subtype, startloc, varflags, 1056dd6b6414Skre quotes) == 0) { 1057e97b0193She int amount = (expdest - stackblock() - patloc) + 1; 1058633ceb6dSchristos STADJUST(-amount, expdest); 1059633ceb6dSchristos } 10601fbf0781Smycroft /* Remove any recorded regions beyond start of variable */ 10611fbf0781Smycroft removerecordregions(startloc); 1062e4a2a056Sdsl apply_ifs = 1; 1063e4a2a056Sdsl break; 1064ad8d5369Schristos 1065ad8d5369Schristos case VSASSIGN: 1066ad8d5369Schristos case VSQUESTION: 1067e4a2a056Sdsl if (set) 1068e4a2a056Sdsl break; 1069dd6b6414Skre if (subevalvar(p, var, subtype, startloc, varflags)) { 1070dd6b6414Skre /* if subevalvar() returns, it always returns 1 */ 1071dd6b6414Skre 107229cad877Schristos varflags &= ~VSNUL; 10731fbf0781Smycroft /* 10741fbf0781Smycroft * Remove any recorded regions beyond 10751fbf0781Smycroft * start of variable 10761fbf0781Smycroft */ 10771fbf0781Smycroft removerecordregions(startloc); 107861f28255Scgd goto again; 107929cad877Schristos } 1080ee3b307fSkre apply_ifs = 0; /* never executed */ 1081ad8d5369Schristos break; 1082ad8d5369Schristos 1083ad8d5369Schristos default: 1084ad8d5369Schristos abort(); 108561f28255Scgd } 1086ad8d5369Schristos 1087e4a2a056Sdsl if (apply_ifs) 1088e4a2a056Sdsl recordregion(startloc, expdest - stackblock(), 1089e4a2a056Sdsl varflags & VSQUOTE); 1090e4a2a056Sdsl 109161f28255Scgd if (subtype != VSNORMAL) { /* skip to end of alternative */ 109261f28255Scgd int nesting = 1; 109361f28255Scgd for (;;) { 109461f28255Scgd if ((c = *p++) == CTLESC) 109561f28255Scgd p++; 1096727a69dcSkre else if (c == CTLNONL) 1097727a69dcSkre ; 109861f28255Scgd else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 1099c823b55cSmycroft if (set) 110061f28255Scgd argbackq = argbackq->next; 110161f28255Scgd } else if (c == CTLVAR) { 110261f28255Scgd if ((*p++ & VSTYPE) != VSNORMAL) 110361f28255Scgd nesting++; 110461f28255Scgd } else if (c == CTLENDVAR) { 110561f28255Scgd if (--nesting == 0) 110661f28255Scgd break; 110761f28255Scgd } 110861f28255Scgd } 110961f28255Scgd } 111061f28255Scgd return p; 111161f28255Scgd } 111261f28255Scgd 111361f28255Scgd 111461f28255Scgd 111561f28255Scgd /* 1116ee3b307fSkre * Test whether a special parameter is set. 111761f28255Scgd */ 111861f28255Scgd 111961f28255Scgd STATIC int 1120dd6b6414Skre varisset(const char *name, int nulok) 112161f28255Scgd { 1122633ceb6dSchristos if (*name == '!') 1123633ceb6dSchristos return backgndpid != -1; 1124633ceb6dSchristos else if (*name == '@' || *name == '*') { 112561f28255Scgd if (*shellparam.p == NULL) 112661f28255Scgd return 0; 1127633ceb6dSchristos 1128633ceb6dSchristos if (nulok) { 11294a521d35Schristos char **av; 11304a521d35Schristos 11314a521d35Schristos for (av = shellparam.p; *av; av++) 11324a521d35Schristos if (**av != '\0') 1133633ceb6dSchristos return 1; 1134633ceb6dSchristos return 0; 1135633ceb6dSchristos } 11364b99c4d7Schristos } else if (is_digit(*name)) { 11374a521d35Schristos char *ap; 1138ee3b307fSkre long num; 1139ee3b307fSkre 1140ee3b307fSkre /* 1141ee3b307fSkre * handle overflow sensibly (the *ap tests should never fail) 1142ee3b307fSkre */ 1143ee3b307fSkre errno = 0; 1144ee3b307fSkre num = strtol(name, &ap, 10); 1145ee3b307fSkre if (errno != 0 || (*ap != '\0' && *ap != '=')) 1146ee3b307fSkre return 0; 1147633ceb6dSchristos 11484a521d35Schristos if (num == 0) 11494a521d35Schristos ap = arg0; 1150dd6b6414Skre else if (num > shellparam.nparam) 1151dd6b6414Skre return 0; 11524a521d35Schristos else 11534a521d35Schristos ap = shellparam.p[num - 1]; 11544a521d35Schristos 11554a521d35Schristos if (nulok && (ap == NULL || *ap == '\0')) 115661f28255Scgd return 0; 115761f28255Scgd } 115861f28255Scgd return 1; 115961f28255Scgd } 116061f28255Scgd 116161f28255Scgd 116261f28255Scgd 116361f28255Scgd /* 116461f28255Scgd * Add the value of a specialized variable to the stack string. 116561f28255Scgd */ 116661f28255Scgd 116761f28255Scgd STATIC void 1168dd6b6414Skre varvalue(const char *name, int quoted, int subtype, int flag) 116961f28255Scgd { 117061f28255Scgd int num; 117161f28255Scgd char *p; 117261f28255Scgd int i; 1173c83568a7Skre int sep; 117461f28255Scgd char **ap; 1175c9f333adSkre #ifdef DEBUG 1176c9f333adSkre char *start = expdest; 1177c9f333adSkre #endif 1178c9f333adSkre 1179c9f333adSkre VTRACE(DBG_EXPAND, ("varvalue(%c%s, sub=%d, fl=%#x)", *name, 1180c9f333adSkre quoted ? ", quoted" : "", subtype, flag)); 118161f28255Scgd 1182dd6b6414Skre if (subtype == VSLENGTH) /* no magic required ... */ 1183dd6b6414Skre flag &= ~EXP_FULL; 1184dd6b6414Skre 118537ed7877Sjtc #define STRTODEST(p) \ 118637ed7877Sjtc do {\ 1187df073671Skre if ((flag & EXP_QNEEDED) && quoted) { \ 118837ed7877Sjtc while (*p) { \ 1189df073671Skre if (NEEDESC(*p)) \ 119037ed7877Sjtc STPUTC(CTLESC, expdest); \ 119137ed7877Sjtc STPUTC(*p++, expdest); \ 119237ed7877Sjtc } \ 119337ed7877Sjtc } else \ 1194256d645dSkre while (*p) { \ 1195256d645dSkre if (ISCTL(*p)) \ 1196256d645dSkre STPUTC(CTLESC, expdest); \ 119737ed7877Sjtc STPUTC(*p++, expdest); \ 1198256d645dSkre } \ 119937ed7877Sjtc } while (0) 120037ed7877Sjtc 120137ed7877Sjtc 12024b99c4d7Schristos switch (*name) { 120361f28255Scgd case '$': 120461f28255Scgd num = rootpid; 1205dd6b6414Skre break; 120661f28255Scgd case '?': 1207c02b3bbdSchristos num = exitstatus; 1208dd6b6414Skre break; 120961f28255Scgd case '#': 121061f28255Scgd num = shellparam.nparam; 1211dd6b6414Skre break; 121261f28255Scgd case '!': 121361f28255Scgd num = backgndpid; 121461f28255Scgd break; 121561f28255Scgd case '-': 1216f359a311Skre for (i = 0; i < option_flags; i++) { 1217f359a311Skre if (optlist[optorder[i]].val) 1218f359a311Skre STPUTC(optlist[optorder[i]].letter, expdest); 121961f28255Scgd } 1220c9f333adSkre VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); 1221dd6b6414Skre return; 122261f28255Scgd case '@': 1223dd6b6414Skre if (flag & EXP_SPLIT && quoted) { 1224c9f333adSkre VTRACE(DBG_EXPAND, (": $@ split (%d)\n", 1225c9f333adSkre shellparam.nparam)); 1226265b0617Skre #if 0 1227c9f333adSkre /* GROSS HACK */ 1228c9f333adSkre if (shellparam.nparam == 0 && 1229c9f333adSkre expdest[-1] == CTLQUOTEMARK) 1230c9f333adSkre expdest--; 1231c9f333adSkre /* KCAH SSORG */ 1232265b0617Skre #endif 1233265b0617Skre if (shellparam.nparam == 0) 1234265b0617Skre empty_dollar_at = 1; 1235265b0617Skre 123661f28255Scgd for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 1237265b0617Skre if (*p == '\0') { 1238265b0617Skre /* retain an explicit null string */ 1239265b0617Skre STPUTC(CTLQUOTEMARK, expdest); 1240265b0617Skre STPUTC(CTLQUOTEEND, expdest); 1241265b0617Skre } else 124237ed7877Sjtc STRTODEST(p); 1243413b7762Sdsl if (*ap) 1244413b7762Sdsl /* A NUL separates args inside "" */ 12451fbf0781Smycroft STPUTC('\0', expdest); 12461fbf0781Smycroft } 1247dd6b6414Skre return; 12481fbf0781Smycroft } 12491fbf0781Smycroft /* fall through */ 12501fbf0781Smycroft case '*': 12511fbf0781Smycroft sep = ifsval()[0]; 12521fbf0781Smycroft for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 12531fbf0781Smycroft STRTODEST(p); 1254bda1f28dSkre if (!*ap) 1255bda1f28dSkre break; 1256c83568a7Skre if (sep) { 1257df073671Skre if (quoted && (flag & EXP_QNEEDED) && 1258df073671Skre NEEDESC(sep)) 1259c83568a7Skre STPUTC(CTLESC, expdest); 126061f28255Scgd STPUTC(sep, expdest); 1261c83568a7Skre } else 1262c83568a7Skre if ((flag & (EXP_SPLIT|EXP_IN_QUOTES)) == EXP_SPLIT 1263bda1f28dSkre && !quoted && **ap != '\0') 1264bda1f28dSkre STPUTC('\0', expdest); 126561f28255Scgd } 1266c9f333adSkre VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); 1267dd6b6414Skre return; 126861f28255Scgd default: 12694b99c4d7Schristos if (is_digit(*name)) { 1270dd6b6414Skre long lnum; 1271dd6b6414Skre 1272dd6b6414Skre errno = 0; 1273dd6b6414Skre lnum = strtol(name, &p, 10); 1274dd6b6414Skre if (errno != 0 || (*p != '\0' && *p != '=')) 1275dd6b6414Skre return; 1276dd6b6414Skre 1277dd6b6414Skre if (lnum == 0) 1278dd6b6414Skre p = arg0; 1279dd6b6414Skre else if (lnum > 0 && lnum <= shellparam.nparam) 1280dd6b6414Skre p = shellparam.p[lnum - 1]; 1281dd6b6414Skre else 1282dd6b6414Skre return; 128337ed7877Sjtc STRTODEST(p); 128461f28255Scgd } 1285c9f333adSkre VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); 1286dd6b6414Skre return; 12874b99c4d7Schristos } 1288dd6b6414Skre /* 1289dd6b6414Skre * only the specials with an int value arrive here 1290dd6b6414Skre */ 1291c9f333adSkre VTRACE(DBG_EXPAND, ("(%d)", num)); 1292dd6b6414Skre expdest = cvtnum(num, expdest); 1293c9f333adSkre VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); 129461f28255Scgd } 129561f28255Scgd 129661f28255Scgd 129761f28255Scgd 129861f28255Scgd /* 12991594850fSsoren * Record the fact that we have to scan this region of the 130061f28255Scgd * string for IFS characters. 130161f28255Scgd */ 130261f28255Scgd 130361f28255Scgd STATIC void 1304e4a2a056Sdsl recordregion(int start, int end, int inquotes) 13055dad1439Scgd { 130648250187Stls struct ifsregion *ifsp; 130761f28255Scgd 130858810bf0Skre VTRACE(DBG_EXPAND, ("recordregion(%d,%d,%d)\n", start, end, inquotes)); 130961f28255Scgd if (ifslastp == NULL) { 131061f28255Scgd ifsp = &ifsfirst; 131161f28255Scgd } else { 13122078d2c0Sdsl if (ifslastp->endoff == start 13132078d2c0Sdsl && ifslastp->inquotes == inquotes) { 1314c6cbc16dSdsl /* extend previous area */ 1315c6cbc16dSdsl ifslastp->endoff = end; 1316c6cbc16dSdsl return; 1317c6cbc16dSdsl } 131861f28255Scgd ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 131961f28255Scgd ifslastp->next = ifsp; 132061f28255Scgd } 132161f28255Scgd ifslastp = ifsp; 132261f28255Scgd ifslastp->next = NULL; 132361f28255Scgd ifslastp->begoff = start; 132461f28255Scgd ifslastp->endoff = end; 1325e4a2a056Sdsl ifslastp->inquotes = inquotes; 132661f28255Scgd } 132761f28255Scgd 132861f28255Scgd 132961f28255Scgd 133061f28255Scgd /* 133161f28255Scgd * Break the argument string into pieces based upon IFS and add the 133261f28255Scgd * strings to the argument list. The regions of the string to be 133361f28255Scgd * searched for IFS characters have been stored by recordregion. 133461f28255Scgd */ 133561f28255Scgd STATIC void 1336c02b3bbdSchristos ifsbreakup(char *string, struct arglist *arglist) 133761f28255Scgd { 133861f28255Scgd struct ifsregion *ifsp; 133961f28255Scgd struct strlist *sp; 134061f28255Scgd char *start; 134148250187Stls char *p; 134261f28255Scgd char *q; 13433d424690Schristos const char *ifs; 1344e4a2a056Sdsl const char *ifsspc; 1345413b7762Sdsl int had_param_ch = 0; 134661f28255Scgd 134761f28255Scgd start = string; 1348e4a2a056Sdsl 1349ea89b130Skre VTRACE(DBG_EXPAND, ("ifsbreakup(\"%s\")", string)); /* misses \0's */ 1350e4a2a056Sdsl if (ifslastp == NULL) { 1351e4a2a056Sdsl /* Return entire argument, IFS doesn't apply to any of it */ 1352ea89b130Skre VTRACE(DBG_EXPAND, ("no regions\n", string)); 135378204bbfSchristos sp = stalloc(sizeof(*sp)); 1354e4a2a056Sdsl sp->text = start; 1355e4a2a056Sdsl *arglist->lastp = sp; 1356e4a2a056Sdsl arglist->lastp = &sp->next; 1357e4a2a056Sdsl return; 1358e4a2a056Sdsl } 1359e4a2a056Sdsl 1360ca12a0b8Schristos ifs = ifsval(); 1361e4a2a056Sdsl 1362e4a2a056Sdsl for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { 136361f28255Scgd p = string + ifsp->begoff; 1364ea89b130Skre VTRACE(DBG_EXPAND, (" !%.*s!(%d)", ifsp->endoff-ifsp->begoff, 1365ea89b130Skre p, ifsp->endoff-ifsp->begoff)); 136661f28255Scgd while (p < string + ifsp->endoff) { 1367413b7762Sdsl had_param_ch = 1; 136861f28255Scgd q = p; 1369c9f333adSkre if (IS_BORING(*p)) { 1370727a69dcSkre p++; 1371727a69dcSkre continue; 1372727a69dcSkre } 137361f28255Scgd if (*p == CTLESC) 137461f28255Scgd p++; 1375413b7762Sdsl if (ifsp->inquotes) { 1376413b7762Sdsl /* Only NULs (should be from "$@") end args */ 1377e4a2a056Sdsl if (*p != 0) { 1378e4a2a056Sdsl p++; 1379e4a2a056Sdsl continue; 1380e4a2a056Sdsl } 1381413b7762Sdsl ifsspc = NULL; 1382ea89b130Skre VTRACE(DBG_EXPAND, (" \\0 nxt:\"%s\" ", p)); 1383e4a2a056Sdsl } else { 1384e4a2a056Sdsl if (!strchr(ifs, *p)) { 1385e4a2a056Sdsl p++; 1386e4a2a056Sdsl continue; 1387e4a2a056Sdsl } 1388c58b829dSdsl had_param_ch = 0; 1389e4a2a056Sdsl ifsspc = strchr(" \t\n", *p); 1390e4a2a056Sdsl 13911fbf0781Smycroft /* Ignore IFS whitespace at start */ 1392e4a2a056Sdsl if (q == start && ifsspc != NULL) { 13931fbf0781Smycroft p++; 13941fbf0781Smycroft start = p; 13951fbf0781Smycroft continue; 13961fbf0781Smycroft } 1397e4a2a056Sdsl } 1398e4a2a056Sdsl 1399e4a2a056Sdsl /* Save this argument... */ 140061f28255Scgd *q = '\0'; 1401ea89b130Skre VTRACE(DBG_EXPAND, ("<%s>", start)); 140278204bbfSchristos sp = stalloc(sizeof(*sp)); 140361f28255Scgd sp->text = start; 140461f28255Scgd *arglist->lastp = sp; 140561f28255Scgd arglist->lastp = &sp->next; 14061fbf0781Smycroft p++; 1407e4a2a056Sdsl 1408328c4365Sdsl if (ifsspc != NULL) { 1409328c4365Sdsl /* Ignore further trailing IFS whitespace */ 1410e4a2a056Sdsl for (; p < string + ifsp->endoff; p++) { 141161f28255Scgd q = p; 1412727a69dcSkre if (*p == CTLNONL) 1413727a69dcSkre continue; 141461f28255Scgd if (*p == CTLESC) 141561f28255Scgd p++; 14161fbf0781Smycroft if (strchr(ifs, *p) == NULL) { 14171fbf0781Smycroft p = q; 14181fbf0781Smycroft break; 1419e4a2a056Sdsl } 1420e4a2a056Sdsl if (strchr(" \t\n", *p) == NULL) { 1421328c4365Sdsl p++; 142261f28255Scgd break; 142361f28255Scgd } 142461f28255Scgd } 142561f28255Scgd } 142661f28255Scgd start = p; 142761f28255Scgd } 142861f28255Scgd } 1429e4a2a056Sdsl 1430265b0617Skre /* 1431c9f333adSkre while (*start == CTLQUOTEEND) 1432c9f333adSkre start++; 1433265b0617Skre */ 1434c9f333adSkre 1435328c4365Sdsl /* 1436328c4365Sdsl * Save anything left as an argument. 1437328c4365Sdsl * Traditionally we have treated 'IFS=':'; set -- x$IFS' as 1438328c4365Sdsl * generating 2 arguments, the second of which is empty. 1439328c4365Sdsl * Some recent clarification of the Posix spec say that it 1440328c4365Sdsl * should only generate one.... 1441328c4365Sdsl */ 1442413b7762Sdsl if (had_param_ch || *start != 0) { 1443ea89b130Skre VTRACE(DBG_EXPAND, (" T<%s>", start)); 144478204bbfSchristos sp = stalloc(sizeof(*sp)); 144561f28255Scgd sp->text = start; 144661f28255Scgd *arglist->lastp = sp; 144761f28255Scgd arglist->lastp = &sp->next; 144861f28255Scgd } 1449ea89b130Skre VTRACE(DBG_EXPAND, ("\n")); 145061f28255Scgd } 145161f28255Scgd 1452f7c8df6dSchristos STATIC void 1453c02b3bbdSchristos ifsfree(void) 1454f7c8df6dSchristos { 1455f7c8df6dSchristos while (ifsfirst.next != NULL) { 1456f7c8df6dSchristos struct ifsregion *ifsp; 1457f7c8df6dSchristos INTOFF; 1458f7c8df6dSchristos ifsp = ifsfirst.next->next; 1459f7c8df6dSchristos ckfree(ifsfirst.next); 1460f7c8df6dSchristos ifsfirst.next = ifsp; 1461f7c8df6dSchristos INTON; 1462f7c8df6dSchristos } 1463f7c8df6dSchristos ifslastp = NULL; 1464f7c8df6dSchristos ifsfirst.next = NULL; 1465f7c8df6dSchristos } 1466f7c8df6dSchristos 146761f28255Scgd 146861f28255Scgd 146961f28255Scgd /* 147061f28255Scgd * Expand shell metacharacters. At this point, the only control characters 147161f28255Scgd * should be escapes. The results are stored in the list exparg. 147261f28255Scgd */ 147361f28255Scgd 147461f28255Scgd char *expdir; 147561f28255Scgd 147661f28255Scgd 147761f28255Scgd STATIC void 1478c02b3bbdSchristos expandmeta(struct strlist *str, int flag) 147961f28255Scgd { 148061f28255Scgd char *p; 148161f28255Scgd struct strlist **savelastp; 148261f28255Scgd struct strlist *sp; 148361f28255Scgd char c; 148437ed7877Sjtc /* TODO - EXP_REDIR */ 148561f28255Scgd 148661f28255Scgd while (str) { 148761f28255Scgd p = str->text; 148861f28255Scgd for (;;) { /* fast check for meta chars */ 148961f28255Scgd if ((c = *p++) == '\0') 149061f28255Scgd goto nometa; 1491c9f333adSkre if (c == '*' || c == '?' || c == '[' /* || c == '!' */) 149261f28255Scgd break; 149361f28255Scgd } 149461f28255Scgd savelastp = exparg.lastp; 149561f28255Scgd INTOFF; 149637ed7877Sjtc if (expdir == NULL) { 149737ed7877Sjtc int i = strlen(str->text); 149837ed7877Sjtc expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 149937ed7877Sjtc } 150037ed7877Sjtc 150161f28255Scgd expmeta(expdir, str->text); 150261f28255Scgd ckfree(expdir); 150361f28255Scgd expdir = NULL; 150461f28255Scgd INTON; 150561f28255Scgd if (exparg.lastp == savelastp) { 150637ed7877Sjtc /* 150737ed7877Sjtc * no matches 150837ed7877Sjtc */ 150961f28255Scgd nometa: 151061f28255Scgd *exparg.lastp = str; 151161f28255Scgd rmescapes(str->text); 151261f28255Scgd exparg.lastp = &str->next; 151361f28255Scgd } else { 151461f28255Scgd *exparg.lastp = NULL; 151561f28255Scgd *savelastp = sp = expsort(*savelastp); 151661f28255Scgd while (sp->next != NULL) 151761f28255Scgd sp = sp->next; 151861f28255Scgd exparg.lastp = &sp->next; 151961f28255Scgd } 152061f28255Scgd str = str->next; 152161f28255Scgd } 152261f28255Scgd } 152361f28255Scgd 1524dd6b6414Skre STATIC void 1525dd6b6414Skre add_args(struct strlist *str) 1526dd6b6414Skre { 1527dd6b6414Skre while (str) { 1528dd6b6414Skre *exparg.lastp = str; 1529dd6b6414Skre rmescapes(str->text); 1530dd6b6414Skre exparg.lastp = &str->next; 1531dd6b6414Skre str = str->next; 1532dd6b6414Skre } 1533dd6b6414Skre } 1534dd6b6414Skre 153561f28255Scgd 153661f28255Scgd /* 153761f28255Scgd * Do metacharacter (i.e. *, ?, [...]) expansion. 153861f28255Scgd */ 153961f28255Scgd 154061f28255Scgd STATIC void 1541c02b3bbdSchristos expmeta(char *enddir, char *name) 154261f28255Scgd { 154348250187Stls char *p; 15443d424690Schristos const char *cp; 154561f28255Scgd char *q; 154661f28255Scgd char *start; 154761f28255Scgd char *endname; 154861f28255Scgd int metaflag; 154961f28255Scgd struct stat statb; 155061f28255Scgd DIR *dirp; 155161f28255Scgd struct dirent *dp; 155261f28255Scgd int atend; 155361f28255Scgd int matchdot; 155461f28255Scgd 1555bcacfd9aSkre CTRACE(DBG_EXPAND|DBG_MATCH, ("expmeta(\"%s\")\n", name)); 155661f28255Scgd metaflag = 0; 155761f28255Scgd start = name; 155861f28255Scgd for (p = name ; ; p++) { 155961f28255Scgd if (*p == '*' || *p == '?') 156061f28255Scgd metaflag = 1; 156161f28255Scgd else if (*p == '[') { 156261f28255Scgd q = p + 1; 156314482abcSkre if (*q == '!' || *q == '^') 156461f28255Scgd q++; 156561f28255Scgd for (;;) { 1566c9f333adSkre while (IS_BORING(*q)) 15671fbf0781Smycroft q++; 156814482abcSkre if (*q == ']') { 156914482abcSkre q++; 157014482abcSkre metaflag = 1; 157114482abcSkre break; 157214482abcSkre } 157314482abcSkre if (*q == '[' && q[1] == ':') { 157414482abcSkre /* 157514482abcSkre * character class, look for :] ending 157614482abcSkre * also stop on ']' (end bracket expr) 157714482abcSkre * or '\0' or '/' (end pattern) 157814482abcSkre */ 157914482abcSkre while (*++q != '\0' && *q != ']' && 158014482abcSkre *q != '/') { 158114482abcSkre if (*q == CTLESC) { 158214482abcSkre if (*++q == '\0') 158314482abcSkre break; 158414482abcSkre if (*q == '/') 158514482abcSkre break; 158614482abcSkre } else if (*q == ':' && 158714482abcSkre q[1] == ']') 158814482abcSkre break; 158914482abcSkre } 159014482abcSkre if (*q == ':') { 159114482abcSkre /* 159214482abcSkre * stopped at ':]' 159314482abcSkre * still in [...] 159414482abcSkre * skip ":]" and continue; 159514482abcSkre */ 159614482abcSkre q += 2; 159714482abcSkre continue; 159814482abcSkre } 159914482abcSkre 160014482abcSkre /* done at end of pattern, not [...] */ 160114482abcSkre if (*q == '\0' || *q == '/') 160214482abcSkre break; 160314482abcSkre 160414482abcSkre /* found the ']', we have a [...] */ 160514482abcSkre metaflag = 1; 160614482abcSkre q++; /* skip ']' */ 160714482abcSkre break; 160814482abcSkre } 160961f28255Scgd if (*q == CTLESC) 161061f28255Scgd q++; 161114482abcSkre /* end of pattern cannot be escaped */ 161261f28255Scgd if (*q == '/' || *q == '\0') 161361f28255Scgd break; 161414482abcSkre q++; 161561f28255Scgd } 161661f28255Scgd } else if (*p == '\0') 161761f28255Scgd break; 1618c9f333adSkre else if (IS_BORING(*p)) 16191fbf0781Smycroft continue; 162061f28255Scgd else if (*p == CTLESC) 162161f28255Scgd p++; 162261f28255Scgd if (*p == '/') { 162361f28255Scgd if (metaflag) 162461f28255Scgd break; 162561f28255Scgd start = p + 1; 162661f28255Scgd } 162761f28255Scgd } 162861f28255Scgd if (metaflag == 0) { /* we've reached the end of the file name */ 162961f28255Scgd if (enddir != expdir) 163061f28255Scgd metaflag++; 163161f28255Scgd for (p = name ; ; p++) { 1632c9f333adSkre if (IS_BORING(*p)) 16331fbf0781Smycroft continue; 163461f28255Scgd if (*p == CTLESC) 163561f28255Scgd p++; 163661f28255Scgd *enddir++ = *p; 163761f28255Scgd if (*p == '\0') 163861f28255Scgd break; 163961f28255Scgd } 164059771e30Smycroft if (metaflag == 0 || lstat(expdir, &statb) >= 0) 164161f28255Scgd addfname(expdir); 164261f28255Scgd return; 164361f28255Scgd } 164461f28255Scgd endname = p; 164561f28255Scgd if (start != name) { 164661f28255Scgd p = name; 164761f28255Scgd while (p < start) { 1648c9f333adSkre while (IS_BORING(*p)) 16491fbf0781Smycroft p++; 165061f28255Scgd if (*p == CTLESC) 165161f28255Scgd p++; 165261f28255Scgd *enddir++ = *p++; 165361f28255Scgd } 165461f28255Scgd } 165561f28255Scgd if (enddir == expdir) { 16563d424690Schristos cp = "."; 165761f28255Scgd } else if (enddir == expdir + 1 && *expdir == '/') { 16583d424690Schristos cp = "/"; 165961f28255Scgd } else { 16603d424690Schristos cp = expdir; 166161f28255Scgd enddir[-1] = '\0'; 166261f28255Scgd } 16633d424690Schristos if ((dirp = opendir(cp)) == NULL) 166461f28255Scgd return; 166561f28255Scgd if (enddir != expdir) 166661f28255Scgd enddir[-1] = '/'; 166761f28255Scgd if (*endname == 0) { 166861f28255Scgd atend = 1; 166961f28255Scgd } else { 167061f28255Scgd atend = 0; 167161f28255Scgd *endname++ = '\0'; 167261f28255Scgd } 167361f28255Scgd matchdot = 0; 16741fbf0781Smycroft p = start; 1675c9f333adSkre while (IS_BORING(*p)) 16761fbf0781Smycroft p++; 16771fbf0781Smycroft if (*p == CTLESC) 16781fbf0781Smycroft p++; 16791fbf0781Smycroft if (*p == '.') 168061f28255Scgd matchdot++; 168161f28255Scgd while (! int_pending() && (dp = readdir(dirp)) != NULL) { 168261f28255Scgd if (dp->d_name[0] == '.' && ! matchdot) 168361f28255Scgd continue; 168428607542Schristos if (patmatch(start, dp->d_name, 0)) { 168561f28255Scgd if (atend) { 168661f28255Scgd scopy(dp->d_name, enddir); 168761f28255Scgd addfname(expdir); 168861f28255Scgd } else { 16893d424690Schristos for (p = enddir, cp = dp->d_name; 16903d424690Schristos (*p++ = *cp++) != '\0';) 169107bae7edSchristos continue; 169261f28255Scgd p[-1] = '/'; 169361f28255Scgd expmeta(p, endname); 169461f28255Scgd } 169561f28255Scgd } 169661f28255Scgd } 169761f28255Scgd closedir(dirp); 169861f28255Scgd if (! atend) 169961f28255Scgd endname[-1] = '/'; 170061f28255Scgd } 170161f28255Scgd 170261f28255Scgd 170361f28255Scgd /* 170461f28255Scgd * Add a file name to the list. 170561f28255Scgd */ 170661f28255Scgd 170761f28255Scgd STATIC void 1708c02b3bbdSchristos addfname(char *name) 170961f28255Scgd { 171061f28255Scgd char *p; 171161f28255Scgd struct strlist *sp; 171261f28255Scgd 171361f28255Scgd p = stalloc(strlen(name) + 1); 171461f28255Scgd scopy(name, p); 171578204bbfSchristos sp = stalloc(sizeof(*sp)); 171661f28255Scgd sp->text = p; 171761f28255Scgd *exparg.lastp = sp; 171861f28255Scgd exparg.lastp = &sp->next; 171961f28255Scgd } 172061f28255Scgd 172161f28255Scgd 172261f28255Scgd /* 172361f28255Scgd * Sort the results of file name expansion. It calculates the number of 172461f28255Scgd * strings to sort and then calls msort (short for merge sort) to do the 172561f28255Scgd * work. 172661f28255Scgd */ 172761f28255Scgd 172861f28255Scgd STATIC struct strlist * 1729c02b3bbdSchristos expsort(struct strlist *str) 173061f28255Scgd { 173161f28255Scgd int len; 173261f28255Scgd struct strlist *sp; 173361f28255Scgd 173461f28255Scgd len = 0; 173561f28255Scgd for (sp = str ; sp ; sp = sp->next) 173661f28255Scgd len++; 173761f28255Scgd return msort(str, len); 173861f28255Scgd } 173961f28255Scgd 174061f28255Scgd 174161f28255Scgd STATIC struct strlist * 1742c02b3bbdSchristos msort(struct strlist *list, int len) 174361f28255Scgd { 17444b99c4d7Schristos struct strlist *p, *q = NULL; 174561f28255Scgd struct strlist **lpp; 174661f28255Scgd int half; 174761f28255Scgd int n; 174861f28255Scgd 174961f28255Scgd if (len <= 1) 175061f28255Scgd return list; 175161f28255Scgd half = len >> 1; 175261f28255Scgd p = list; 175361f28255Scgd for (n = half ; --n >= 0 ; ) { 175461f28255Scgd q = p; 175561f28255Scgd p = p->next; 175661f28255Scgd } 175761f28255Scgd q->next = NULL; /* terminate first half of list */ 175861f28255Scgd q = msort(list, half); /* sort first half of list */ 175961f28255Scgd p = msort(p, len - half); /* sort second half */ 176061f28255Scgd lpp = &list; 176161f28255Scgd for (;;) { 176261f28255Scgd if (strcmp(p->text, q->text) < 0) { 176361f28255Scgd *lpp = p; 176461f28255Scgd lpp = &p->next; 176561f28255Scgd if ((p = *lpp) == NULL) { 176661f28255Scgd *lpp = q; 176761f28255Scgd break; 176861f28255Scgd } 176961f28255Scgd } else { 177061f28255Scgd *lpp = q; 177161f28255Scgd lpp = &q->next; 177261f28255Scgd if ((q = *lpp) == NULL) { 177361f28255Scgd *lpp = p; 177461f28255Scgd break; 177561f28255Scgd } 177661f28255Scgd } 177761f28255Scgd } 177861f28255Scgd return list; 177961f28255Scgd } 178061f28255Scgd 178161f28255Scgd 17827969ec4dSroy /* 17837969ec4dSroy * See if a character matches a character class, starting at the first colon 17847969ec4dSroy * of "[:class:]". 17857969ec4dSroy * If a valid character class is recognized, a pointer to the next character 17867969ec4dSroy * after the final closing bracket is stored into *end, otherwise a null 17877969ec4dSroy * pointer is stored into *end. 17887969ec4dSroy */ 17897969ec4dSroy static int 17908e4a570fSchristos match_charclass(const char *p, wchar_t chr, const char **end) 17917969ec4dSroy { 17927969ec4dSroy char name[20]; 17937969ec4dSroy char *nameend; 17947969ec4dSroy wctype_t cclass; 17957dca2b7eSkre char *q; 17967969ec4dSroy 17977969ec4dSroy *end = NULL; 17987969ec4dSroy p++; 17997dca2b7eSkre q = &name[0]; 18007969ec4dSroy nameend = strstr(p, ":]"); 1801b81009ceSkre if (nameend == NULL || nameend == p) /* not a valid class */ 18027969ec4dSroy return 0; 1803b81009ceSkre 18047dca2b7eSkre if (*p == CTLESC) { 18057dca2b7eSkre if (*++p == CTLESC) 1806b81009ceSkre return 0; 18077dca2b7eSkre if (p == nameend) 18087dca2b7eSkre return 0; 18097dca2b7eSkre } 18107dca2b7eSkre if (!is_alpha(*p)) 18117dca2b7eSkre return 0; 18127dca2b7eSkre while (p < nameend) { 18137dca2b7eSkre if (*p == CTLESC) { 18147dca2b7eSkre p++; 18157dca2b7eSkre if (p == nameend) 18167dca2b7eSkre return 0; 18177dca2b7eSkre } 18187dca2b7eSkre if (!is_in_name(*p)) /* '_' is a local extension */ 18197dca2b7eSkre return 0; 18207dca2b7eSkre if (q < &name[sizeof name]) 18217dca2b7eSkre *q++ = *p++; 18227dca2b7eSkre else 18237dca2b7eSkre p++; 18247dca2b7eSkre } 1825b81009ceSkre 1826b81009ceSkre *end = nameend + 2; /* committed to it being a char class */ 18277dca2b7eSkre 18287dca2b7eSkre if (q < &name[sizeof name]) /* a usable name found */ 18297dca2b7eSkre *q++ = '\0'; 18307dca2b7eSkre else /* too long, valid, but no match */ 18317dca2b7eSkre return 0; 18327dca2b7eSkre 18337969ec4dSroy cclass = wctype(name); 18347969ec4dSroy /* An unknown class matches nothing but is valid nevertheless. */ 18357969ec4dSroy if (cclass == 0) 18367969ec4dSroy return 0; 18377969ec4dSroy return iswctype(chr, cclass); 18387969ec4dSroy } 18397969ec4dSroy 18407969ec4dSroy 184161f28255Scgd /* 184261f28255Scgd * Returns true if the pattern matches the string. 184361f28255Scgd */ 184461f28255Scgd 184561f28255Scgd STATIC int 18468e4a570fSchristos patmatch(const char *pattern, const char *string, int squoted) 184761f28255Scgd { 18488e4a570fSchristos const char *p, *q, *end; 18498e4a570fSchristos const char *bt_p, *bt_q; 185048250187Stls char c; 18518e4a570fSchristos wchar_t wc, wc2; 185261f28255Scgd 1853bcacfd9aSkre VTRACE(DBG_MATCH, ("patmatch(P=\"%s\", W=\"%s\"%s): ", 1854bcacfd9aSkre pattern, string, squoted ? ", SQ" : "")); 185561f28255Scgd p = pattern; 185661f28255Scgd q = string; 18578e4a570fSchristos bt_p = NULL; 18588e4a570fSchristos bt_q = NULL; 185961f28255Scgd for (;;) { 186061f28255Scgd switch (c = *p++) { 186161f28255Scgd case '\0': 186214482abcSkre if (squoted && *q == CTLESC) { 186314482abcSkre if (q[1] == '\0') 186414482abcSkre q++; 186514482abcSkre } 18668e4a570fSchristos if (*q != '\0') 18678e4a570fSchristos goto backtrack; 1868bcacfd9aSkre VTRACE(DBG_MATCH, ("match\n")); 18698e4a570fSchristos return 1; 187061f28255Scgd case CTLESC: 187128607542Schristos if (squoted && *q == CTLESC) 187228607542Schristos q++; 187314482abcSkre if (*p == '\0' && *q == '\0') { 187414482abcSkre VTRACE(DBG_MATCH, ("match-\\\n")); 187514482abcSkre return 1; 187614482abcSkre } 187714482abcSkre if (*q++ != *p++) 187814482abcSkre goto backtrack; 187914482abcSkre break; 188014482abcSkre case '\\': 188114482abcSkre if (squoted && *q == CTLESC) 188214482abcSkre q++; 188361f28255Scgd if (*q++ != *p++) 18848e4a570fSchristos goto backtrack; 188561f28255Scgd break; 18861fbf0781Smycroft case CTLQUOTEMARK: 1887c9f333adSkre case CTLQUOTEEND: 1888727a69dcSkre case CTLNONL: 18891fbf0781Smycroft continue; 189061f28255Scgd case '?': 189128607542Schristos if (squoted && *q == CTLESC) 189228607542Schristos q++; 1893bcacfd9aSkre if (*q++ == '\0') { 1894bcacfd9aSkre VTRACE(DBG_MATCH, ("?fail\n")); 189561f28255Scgd return 0; 1896bcacfd9aSkre } 189761f28255Scgd break; 189861f28255Scgd case '*': 189961f28255Scgd c = *p; 19001fbf0781Smycroft while (c == CTLQUOTEMARK || c == '*') 19011fbf0781Smycroft c = *++p; 1902c9f333adSkre if (c != CTLESC && !IS_BORING(c) && 19031fbf0781Smycroft c != '?' && c != '*' && c != '[') { 190461f28255Scgd while (*q != c) { 190528607542Schristos if (squoted && *q == CTLESC && 190628607542Schristos q[1] == c) 190728607542Schristos break; 1908bcacfd9aSkre if (*q == '\0') { 1909bcacfd9aSkre VTRACE(DBG_MATCH, ("*fail\n")); 191061f28255Scgd return 0; 1911bcacfd9aSkre } 191228607542Schristos if (squoted && *q == CTLESC) 191328607542Schristos q++; 191461f28255Scgd q++; 191561f28255Scgd } 191661f28255Scgd } 191714482abcSkre if (c == CTLESC && p[1] == '\0') { 191814482abcSkre VTRACE(DBG_MATCH, ("match+\\\n")); 191914482abcSkre return 1; 192014482abcSkre } 19218e4a570fSchristos /* 19228e4a570fSchristos * First try the shortest match for the '*' that 19238e4a570fSchristos * could work. We can forget any earlier '*' since 19248e4a570fSchristos * there is no way having it match more characters 19258e4a570fSchristos * can help us, given that we are already here. 19268e4a570fSchristos */ 19278e4a570fSchristos bt_p = p; 19288e4a570fSchristos bt_q = q; 19298e4a570fSchristos break; 193061f28255Scgd case '[': { 19318e4a570fSchristos const char *savep, *saveq, *endp; 193261f28255Scgd int invert, found; 19338e4a570fSchristos unsigned char chr; 193461f28255Scgd 1935c83568a7Skre /* 1936c83568a7Skre * First quick check to see if there is a 1937c83568a7Skre * possible matching ']' - if not, then this 1938c83568a7Skre * is not a char class, and the '[' is just 1939c83568a7Skre * a literal '['. 1940c83568a7Skre * 1941c83568a7Skre * This check will not detect all non classes, but 1942c83568a7Skre * that's OK - It just means that we execute the 1943c83568a7Skre * harder code sometimes when it it cannot succeed. 1944c83568a7Skre */ 194561f28255Scgd endp = p; 1946c83568a7Skre if (*endp == '!' || *endp == '^') 194761f28255Scgd endp++; 194861f28255Scgd for (;;) { 1949c9f333adSkre while (IS_BORING(*endp)) 19501fbf0781Smycroft endp++; 195161f28255Scgd if (*endp == '\0') 195261f28255Scgd goto dft; /* no matching ] */ 1953c9f333adSkre if (*endp++ == ']') 195461f28255Scgd break; 195561f28255Scgd } 1956c83568a7Skre /* end shortcut */ 1957c83568a7Skre 19588e4a570fSchristos savep = p, saveq = q; 19598e4a570fSchristos invert = 0; 19608e4a570fSchristos if (*p == '!' || *p == '^') { 196161f28255Scgd invert++; 196261f28255Scgd p++; 196361f28255Scgd } 196461f28255Scgd found = 0; 1965bcacfd9aSkre if (*q == '\0') { 1966bcacfd9aSkre VTRACE(DBG_MATCH, ("[]fail\n")); 1967d22a5147Smycroft return 0; 1968bcacfd9aSkre } 196914482abcSkre if (squoted && *q == CTLESC) 197014482abcSkre q++; 19718e4a570fSchristos chr = (unsigned char)*q++; 197261f28255Scgd c = *p++; 197361f28255Scgd do { 1974c9f333adSkre if (IS_BORING(c)) 19751fbf0781Smycroft continue; 19768e4a570fSchristos if (c == '\0') { 19778e4a570fSchristos p = savep, q = saveq; 19788e4a570fSchristos c = '['; 19798e4a570fSchristos goto dft; 19808e4a570fSchristos } 19817969ec4dSroy if (c == '[' && *p == ':') { 19827969ec4dSroy found |= match_charclass(p, chr, &end); 1983829cc62aSkre if (end != NULL) { 19847969ec4dSroy p = end; 1985829cc62aSkre continue; 1986829cc62aSkre } 19877969ec4dSroy } 1988c83568a7Skre if (c == CTLESC || c == '\\') 198961f28255Scgd c = *p++; 19908e4a570fSchristos wc = (unsigned char)c; 199161f28255Scgd if (*p == '-' && p[1] != ']') { 199261f28255Scgd p++; 1993c83568a7Skre if (*p == CTLESC || *p == '\\') 199461f28255Scgd p++; 19958e4a570fSchristos wc2 = (unsigned char)*p++; 19968e4a570fSchristos if ( collate_range_cmp(chr, wc) >= 0 19978e4a570fSchristos && collate_range_cmp(chr, wc2) <= 0 19988e4a570fSchristos ) 199961f28255Scgd found = 1; 200061f28255Scgd } else { 20018e4a570fSchristos if (chr == wc) 200261f28255Scgd found = 1; 200361f28255Scgd } 200461f28255Scgd } while ((c = *p++) != ']'); 200561f28255Scgd if (found == invert) 20068e4a570fSchristos goto backtrack; 200761f28255Scgd break; 200861f28255Scgd } 200961f28255Scgd dft: default: 201028607542Schristos if (squoted && *q == CTLESC) 201128607542Schristos q++; 20128e4a570fSchristos if (*q++ == c) 20138e4a570fSchristos break; 20148e4a570fSchristos backtrack: 20158e4a570fSchristos /* 20168e4a570fSchristos * If we have a mismatch (other than hitting the end 20178e4a570fSchristos * of the string), go back to the last '*' seen and 20188e4a570fSchristos * have it match one additional character. 20198e4a570fSchristos */ 2020bcacfd9aSkre if (bt_p == NULL) { 2021bcacfd9aSkre VTRACE(DBG_MATCH, ("BTP fail\n")); 202261f28255Scgd return 0; 2023bcacfd9aSkre } 2024bcacfd9aSkre if (*bt_q == '\0') { 2025bcacfd9aSkre VTRACE(DBG_MATCH, ("BTQ fail\n")); 20268e4a570fSchristos return 0; 2027bcacfd9aSkre } 20288e4a570fSchristos bt_q++; 20298e4a570fSchristos p = bt_p; 20308e4a570fSchristos q = bt_q; 203161f28255Scgd break; 203261f28255Scgd } 203361f28255Scgd } 203461f28255Scgd } 203561f28255Scgd 203661f28255Scgd 203761f28255Scgd 203861f28255Scgd /* 2039727a69dcSkre * Remove any CTLESC or CTLNONL characters from a string. 204061f28255Scgd */ 204161f28255Scgd 204261f28255Scgd void 2043c02b3bbdSchristos rmescapes(char *str) 204461f28255Scgd { 204548250187Stls char *p, *q; 204661f28255Scgd 204761f28255Scgd p = str; 2048256d645dSkre while (!ISCTL(*p)) { 204961f28255Scgd if (*p++ == '\0') 205061f28255Scgd return; 205161f28255Scgd } 205261f28255Scgd q = p; 205361f28255Scgd while (*p) { 2054c9f333adSkre if (IS_BORING(*p)) { 20551fbf0781Smycroft p++; 20561fbf0781Smycroft continue; 20571fbf0781Smycroft } 20585f92382cSkre if (*p == CTLCNL) { 20595f92382cSkre p++; 20605f92382cSkre *q++ = '\n'; 20615f92382cSkre continue; 20625f92382cSkre } 206361f28255Scgd if (*p == CTLESC) 206461f28255Scgd p++; 206558e34de6Skre #ifdef DEBUG 2066256d645dSkre else if (ISCTL(*p)) 206758e34de6Skre abort(); 206858e34de6Skre #endif 206961f28255Scgd *q++ = *p++; 207061f28255Scgd } 207161f28255Scgd *q = '\0'; 207261f28255Scgd } 207361f28255Scgd 2074727a69dcSkre /* 2075727a69dcSkre * and a special version for dealing with expressions to be parsed 2076727a69dcSkre * by the arithmetic evaluator. That needs to be able to count \n's 2077727a69dcSkre * even ones that were \newline elided \n's, so we have to put the 2078727a69dcSkre * latter back into the string - just being careful to put them only 2079727a69dcSkre * at a place where white space can reasonably occur in the string 2080727a69dcSkre * -- then the \n we insert will just be white space, and ignored 2081727a69dcSkre * for all purposes except line counting. 2082727a69dcSkre */ 2083727a69dcSkre 2084727a69dcSkre void 2085727a69dcSkre rmescapes_nl(char *str) 2086727a69dcSkre { 2087727a69dcSkre char *p, *q; 2088727a69dcSkre int nls = 0, holdnl = 0, holdlast; 2089727a69dcSkre 2090727a69dcSkre p = str; 2091256d645dSkre while (!ISCTL(*p)) { 2092727a69dcSkre if (*p++ == '\0') 2093727a69dcSkre return; 2094727a69dcSkre } 2095727a69dcSkre if (p > str) /* must reprocess char before stopper (if any) */ 2096727a69dcSkre --p; /* so we do not place a \n badly */ 2097727a69dcSkre q = p; 2098727a69dcSkre while (*p) { 2099c9f333adSkre if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { 2100727a69dcSkre p++; 2101727a69dcSkre continue; 2102727a69dcSkre } 2103727a69dcSkre if (*p == CTLNONL) { 2104727a69dcSkre p++; 2105727a69dcSkre nls++; 2106727a69dcSkre continue; 2107727a69dcSkre } 21085f92382cSkre if (*p == CTLCNL) { 21095f92382cSkre p++; 21105f92382cSkre *q++ = '\n'; 21115f92382cSkre continue; 21125f92382cSkre } 2113727a69dcSkre if (*p == CTLESC) 2114727a69dcSkre p++; 211558e34de6Skre #ifdef DEBUG 2116256d645dSkre else if (ISCTL(*p)) 211758e34de6Skre abort(); 211858e34de6Skre #endif 2119727a69dcSkre 2120727a69dcSkre holdlast = holdnl; 2121727a69dcSkre holdnl = is_in_name(*p); /* letters, digits, _ */ 2122727a69dcSkre if (q == str || is_space(q[-1]) || (*p != '=' && q[-1] != *p)) { 2123727a69dcSkre if (nls > 0 && holdnl != holdlast) { 2124727a69dcSkre while (nls > 0) 2125727a69dcSkre *q++ = '\n', nls--; 2126727a69dcSkre } 2127727a69dcSkre } 2128727a69dcSkre *q++ = *p++; 2129727a69dcSkre } 2130727a69dcSkre while (--nls >= 0) 2131727a69dcSkre *q++ = '\n'; 2132727a69dcSkre *q = '\0'; 2133727a69dcSkre } 2134727a69dcSkre 213561f28255Scgd 213661f28255Scgd 213761f28255Scgd /* 213861f28255Scgd * See if a pattern matches in a case statement. 213961f28255Scgd */ 214061f28255Scgd 214161f28255Scgd int 2142c02b3bbdSchristos casematch(union node *pattern, char *val) 214361f28255Scgd { 214461f28255Scgd struct stackmark smark; 214561f28255Scgd int result; 214661f28255Scgd char *p; 214761f28255Scgd 2148bcacfd9aSkre CTRACE(DBG_MATCH, ("casematch(P=\"%s\", W=\"%s\")\n", 2149bcacfd9aSkre pattern->narg.text, val)); 215061f28255Scgd setstackmark(&smark); 215161f28255Scgd argbackq = pattern->narg.backquote; 215261f28255Scgd STARTSTACKSTR(expdest); 215361f28255Scgd ifslastp = NULL; 21541fbf0781Smycroft argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 215561f28255Scgd STPUTC('\0', expdest); 215661f28255Scgd p = grabstackstr(expdest); 215728607542Schristos result = patmatch(p, val, 0); 215861f28255Scgd popstackmark(&smark); 215961f28255Scgd return result; 216061f28255Scgd } 2161ad8d5369Schristos 2162ad8d5369Schristos /* 2163dd6b6414Skre * Our own itoa(). Assumes result buffer is on the stack 2164ad8d5369Schristos */ 2165ad8d5369Schristos 2166ad8d5369Schristos STATIC char * 2167c02b3bbdSchristos cvtnum(int num, char *buf) 2168ad8d5369Schristos { 2169ad8d5369Schristos char temp[32]; 2170ad8d5369Schristos int neg = num < 0; 2171dd6b6414Skre char *p = temp + sizeof temp - 1; 2172ad8d5369Schristos 2173dd6b6414Skre if (neg) 2174dd6b6414Skre num = -num; 2175ad8d5369Schristos 2176dd6b6414Skre *p = '\0'; 2177ad8d5369Schristos do { 2178ad8d5369Schristos *--p = num % 10 + '0'; 2179dd6b6414Skre } while ((num /= 10) != 0 && p > temp + 1); 2180ad8d5369Schristos 2181ad8d5369Schristos if (neg) 2182ad8d5369Schristos *--p = '-'; 2183ad8d5369Schristos 2184ad8d5369Schristos while (*p) 2185ad8d5369Schristos STPUTC(*p++, buf); 2186ad8d5369Schristos return buf; 2187ad8d5369Schristos } 2188cf788c31Sseb 2189cf788c31Sseb /* 2190cf788c31Sseb * Do most of the work for wordexp(3). 2191cf788c31Sseb */ 2192cf788c31Sseb 2193cf788c31Sseb int 2194cf788c31Sseb wordexpcmd(int argc, char **argv) 2195cf788c31Sseb { 2196cf788c31Sseb size_t len; 2197cf788c31Sseb int i; 2198cf788c31Sseb 2199cf788c31Sseb out1fmt("%d", argc - 1); 2200cf788c31Sseb out1c('\0'); 2201cf788c31Sseb for (i = 1, len = 0; i < argc; i++) 2202cf788c31Sseb len += strlen(argv[i]); 220349ee47d0Stsutsui out1fmt("%zu", len); 2204cf788c31Sseb out1c('\0'); 2205cf788c31Sseb for (i = 1; i < argc; i++) { 2206cf788c31Sseb out1str(argv[i]); 2207cf788c31Sseb out1c('\0'); 2208cf788c31Sseb } 2209cf788c31Sseb return (0); 2210cf788c31Sseb } 2211