1*a7e8b4a5Skre /* $NetBSD: expand.c,v 1.146 2024/10/21 15:57:45 kre 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*a7e8b4a5Skre __RCSID("$NetBSD: expand.c,v 1.146 2024/10/21 15:57:45 kre 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 1294cb87529Srillig #define NULLTERM_4_TRACE(p) do { /* nothing */ } while (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 14616d85571Skre char * 14716d85571Skre expandhere(union node *arg) 14861f28255Scgd { 149c69ada4cSkre int len; 1501d1484aaSchristos 15116d85571Skre VTRACE(DBG_EXPAND|DBG_REDIR, ("expandhere(%p)\n", arg)); 1529f61b804Splunky expandarg(arg, NULL, 0); 153c69ada4cSkre len = rmescapes(stackblock()); 15416d85571Skre VTRACE(DBG_EXPAND|DBG_REDIR, ("expandhere() -> %d\n", len)); 15516d85571Skre return stalloc(len + 1); /* include the \0 */ 15661f28255Scgd } 15761f28255Scgd 15861f28255Scgd 1598e4a570fSchristos static int 1608e4a570fSchristos collate_range_cmp(wchar_t c1, wchar_t c2) 1618e4a570fSchristos { 1628e4a570fSchristos wchar_t s1[2], s2[2]; 1638e4a570fSchristos 1648e4a570fSchristos s1[0] = c1; 1658e4a570fSchristos s1[1] = L'\0'; 1668e4a570fSchristos s2[0] = c2; 1678e4a570fSchristos s2[1] = L'\0'; 1688e4a570fSchristos return (wcscoll(s1, s2)); 1698e4a570fSchristos } 1708e4a570fSchristos 17161f28255Scgd /* 17261f28255Scgd * Perform variable substitution and command substitution on an argument, 17337ed7877Sjtc * placing the resulting list of arguments in arglist. If EXP_FULL is true, 17461f28255Scgd * perform splitting and file name expansion. When arglist is NULL, perform 17561f28255Scgd * here document expansion. 17661f28255Scgd */ 17761f28255Scgd 17861f28255Scgd void 179c02b3bbdSchristos expandarg(union node *arg, struct arglist *arglist, int flag) 18061f28255Scgd { 18161f28255Scgd struct strlist *sp; 18261f28255Scgd char *p; 18361f28255Scgd 18458810bf0Skre CTRACE(DBG_EXPAND, ("expandarg(fl=%#x)\n", flag)); 185dd6b6414Skre if (fflag) /* no filename expandsion */ 186dd6b6414Skre flag &= ~EXP_GLOB; 187dd6b6414Skre 188265b0617Skre empty_dollar_at = 0; 18961f28255Scgd argbackq = arg->narg.backquote; 19061f28255Scgd STARTSTACKSTR(expdest); 19161f28255Scgd ifsfirst.next = NULL; 19261f28255Scgd ifslastp = NULL; 193727a69dcSkre line_number = arg->narg.lineno; 19469c48e32Skre argstr(arg->narg.text, flag); 19537ed7877Sjtc if (arglist == NULL) { 1961fca9bbfSkre STACKSTRNUL(expdest); 197b8bee70dSkre CTRACE(DBG_EXPAND, 198b8bee70dSkre ("expandarg: no arglist, done[%d] (len %d) \"%s\"\n", 199b8bee70dSkre back_exitstatus, expdest - stackblock(), stackblock())); 20061f28255Scgd return; /* here document expanded */ 20137ed7877Sjtc } 20261f28255Scgd STPUTC('\0', expdest); 20358810bf0Skre CTRACE(DBG_EXPAND, ("expandarg: arglist got (%d) \"%s\"\n", 20458810bf0Skre expdest - stackblock() - 1, stackblock())); 20561f28255Scgd p = grabstackstr(expdest); 20661f28255Scgd exparg.lastp = &exparg.list; 20737ed7877Sjtc /* 20837ed7877Sjtc * TODO - EXP_REDIR 20937ed7877Sjtc */ 210dd6b6414Skre if (flag & EXP_SPLIT) { 21161f28255Scgd ifsbreakup(p, &exparg); 21261f28255Scgd *exparg.lastp = NULL; 21361f28255Scgd exparg.lastp = &exparg.list; 214dd6b6414Skre if (flag & EXP_GLOB) 21537ed7877Sjtc expandmeta(exparg.list, flag); 216dd6b6414Skre else 217dd6b6414Skre add_args(exparg.list); 218256d645dSkre #if 0 219256d645dSkre } else if (flag & EXP_REDIR) { 220256d645dSkre /* if EXP_REDIR ever happens, it happens here */ 221256d645dSkre /* for now just (below) remove escapes, and leave it alone */ 222256d645dSkre #endif 22361f28255Scgd } else { 224256d645dSkre rmescapes(p); /* we might have escaped CTL bytes to remove */ 22578204bbfSchristos sp = stalloc(sizeof(*sp)); 22661f28255Scgd sp->text = p; 22761f28255Scgd *exparg.lastp = sp; 22861f28255Scgd exparg.lastp = &sp->next; 22961f28255Scgd } 230f7c8df6dSchristos ifsfree(); 23161f28255Scgd *exparg.lastp = NULL; 23261f28255Scgd if (exparg.list) { 23361f28255Scgd *arglist->lastp = exparg.list; 23461f28255Scgd arglist->lastp = exparg.lastp; 23561f28255Scgd } 23661f28255Scgd } 23761f28255Scgd 23861f28255Scgd 23961f28255Scgd 24061f28255Scgd /* 241c6cbc16dSdsl * Perform variable and command substitution. 242dd6b6414Skre * If EXP_GLOB is set, output CTLESC characters to allow for further processing. 243dd6b6414Skre * If EXP_SPLIT is set, remember location of result for later, 244c6cbc16dSdsl * Otherwise treat $@ like $* since no splitting will be performed. 24561f28255Scgd */ 24661f28255Scgd 247dd6b6414Skre STATIC const char * 248dd6b6414Skre argstr(const char *p, int flag) 24961f28255Scgd { 25048250187Stls char c; 251df073671Skre const int quotes = flag & EXP_QNEEDED; /* do CTLESC */ 25237ed7877Sjtc int firsteq = 1; 253265b0617Skre int had_dol_at = 0; 254265b0617Skre int startoff; 2555166671bSlukem const char *ifs = NULL; 256c6cbc16dSdsl int ifs_split = EXP_IFS_SPLIT; 257c6cbc16dSdsl 258c6cbc16dSdsl if (flag & EXP_IFS_SPLIT) 259ca12a0b8Schristos ifs = ifsval(); 26061f28255Scgd 261ea89b130Skre CTRACE(DBG_EXPAND, ("argstr(\"%s\", %#x) quotes=%#x\n", p,flag,quotes)); 262dd6b6414Skre 263265b0617Skre startoff = expdest - stackblock(); 26437ed7877Sjtc if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 26537ed7877Sjtc p = exptilde(p, flag); 26661f28255Scgd for (;;) { 26761f28255Scgd switch (c = *p++) { 26861f28255Scgd case '\0': 269701ac132Skre NULLTERM_4_TRACE(expdest); 270dd6b6414Skre VTRACE(DBG_EXPAND, ("argstr returning at \"\" " 271ee3b307fSkre "added \"%s\" to expdest\n", stackblock())); 272dd6b6414Skre return p - 1; 273e4a2a056Sdsl case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */ 274dd6b6414Skre case CTLENDARI: /* end of a $(( )) string */ 275d08d589dSchristos if (had_dol_at && *p == CTLQUOTEEND) 276265b0617Skre p++; 277701ac132Skre NULLTERM_4_TRACE(expdest); 278dd6b6414Skre VTRACE(DBG_EXPAND, ("argstr returning at \"%.6s\"..." 279ee3b307fSkre " after %2.2X; added \"%s\" to expdest\n", 280ee3b307fSkre p, (c & 0xff), stackblock())); 281dd6b6414Skre return p; 2821fbf0781Smycroft case CTLQUOTEMARK: 2831fbf0781Smycroft /* "$@" syntax adherence hack */ 284dec2ca90Schristos if (p[0] == CTLVAR && p[1] & VSQUOTE && 285265b0617Skre p[2] == '@' && p[3] == '=') { 286265b0617Skre had_dol_at = 1; 2871fbf0781Smycroft break; 288265b0617Skre } 289265b0617Skre had_dol_at = 0; 290265b0617Skre empty_dollar_at = 0; 291dd6b6414Skre if ((flag & EXP_SPLIT) != 0) 2921fbf0781Smycroft STPUTC(c, expdest); 293c6cbc16dSdsl ifs_split = 0; 294c6cbc16dSdsl break; 295727a69dcSkre case CTLNONL: 296727a69dcSkre if (flag & EXP_NL) 297727a69dcSkre STPUTC(c, expdest); 298727a69dcSkre line_number++; 299727a69dcSkre break; 3005f92382cSkre case CTLCNL: 3015f92382cSkre STPUTC('\n', expdest); /* no line_number++ */ 3025f92382cSkre break; 303c6cbc16dSdsl case CTLQUOTEEND: 304265b0617Skre if (empty_dollar_at && 305265b0617Skre expdest - stackblock() > startoff && 306265b0617Skre expdest[-1] == CTLQUOTEMARK) 307265b0617Skre expdest--; 308265b0617Skre else if (!had_dol_at && (flag & EXP_SPLIT) != 0) 309c9f333adSkre STPUTC(c, expdest); 310c6cbc16dSdsl ifs_split = EXP_IFS_SPLIT; 311265b0617Skre had_dol_at = 0; 3121fbf0781Smycroft break; 31361f28255Scgd case CTLESC: 314b95d46c2Skre if (quotes || ISCTL(*p)) 31561f28255Scgd STPUTC(c, expdest); 31661f28255Scgd c = *p++; 31761f28255Scgd STPUTC(c, expdest); 318ee3b307fSkre if (c == '\n') /* should not happen, but ... */ 319ee3b307fSkre line_number++; 32061f28255Scgd break; 321ee3b307fSkre case CTLVAR: { 322ee3b307fSkre #ifdef DEBUG 323ee3b307fSkre unsigned int pos = expdest - stackblock(); 324c9f333adSkre NULLTERM_4_TRACE(expdest); 325ee3b307fSkre #endif 326c6cbc16dSdsl p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split)); 327701ac132Skre NULLTERM_4_TRACE(expdest); 328dd6b6414Skre VTRACE(DBG_EXPAND, ("argstr evalvar " 329c9f333adSkre "added %zd \"%s\" to expdest\n", 330c9f333adSkre (size_t)(expdest - (stackblock() + pos)), 331ee3b307fSkre stackblock() + pos)); 33261f28255Scgd break; 333ee3b307fSkre } 33461f28255Scgd case CTLBACKQ: 335ee3b307fSkre case CTLBACKQ|CTLQUOTE: { 336ee3b307fSkre #ifdef DEBUG 337ee3b307fSkre unsigned int pos = expdest - stackblock(); 338ee3b307fSkre #endif 33937ed7877Sjtc expbackq(argbackq->n, c & CTLQUOTE, flag); 34061f28255Scgd argbackq = argbackq->next; 341701ac132Skre NULLTERM_4_TRACE(expdest); 342ee3b307fSkre VTRACE(DBG_EXPAND, ("argstr expbackq added \"%s\" " 343ee3b307fSkre "to expdest\n", stackblock() + pos)); 34461f28255Scgd break; 345ee3b307fSkre } 346ee3b307fSkre case CTLARI: { 347ee3b307fSkre #ifdef DEBUG 348ee3b307fSkre unsigned int pos = expdest - stackblock(); 349ee3b307fSkre #endif 35051791eabSkre p = expari(p); 351701ac132Skre NULLTERM_4_TRACE(expdest); 352dd6b6414Skre VTRACE(DBG_EXPAND, ("argstr expari " 353ee3b307fSkre "+ \"%s\" to expdest p=\"%.5s...\"\n", 354ee3b307fSkre stackblock() + pos, p)); 35537ed7877Sjtc break; 356ee3b307fSkre } 35737ed7877Sjtc case ':': 35837ed7877Sjtc case '=': 35937ed7877Sjtc /* 36037ed7877Sjtc * sort of a hack - expand tildes in variable 36137ed7877Sjtc * assignments (after the first '=' and after ':'s). 36237ed7877Sjtc */ 36337ed7877Sjtc STPUTC(c, expdest); 36437ed7877Sjtc if (flag & EXP_VARTILDE && *p == '~') { 36537ed7877Sjtc if (c == '=') { 36637ed7877Sjtc if (firsteq) 36737ed7877Sjtc firsteq = 0; 36837ed7877Sjtc else 36937ed7877Sjtc break; 37037ed7877Sjtc } 37137ed7877Sjtc p = exptilde(p, flag); 37237ed7877Sjtc } 37337ed7877Sjtc break; 37461f28255Scgd default: 375727a69dcSkre if (c == '\n') 376727a69dcSkre line_number++; 37761f28255Scgd STPUTC(c, expdest); 3787d60739aSdsl if (flag & ifs_split && strchr(ifs, c) != NULL) { 379c6cbc16dSdsl /* We need to get the output split here... */ 380c6cbc16dSdsl recordregion(expdest - stackblock() - 1, 381c6cbc16dSdsl expdest - stackblock(), 0); 382c6cbc16dSdsl } 383e4a2a056Sdsl break; 38461f28255Scgd } 38561f28255Scgd } 38661f28255Scgd } 38761f28255Scgd 388dd6b6414Skre STATIC const char * 389dd6b6414Skre exptilde(const char *p, int flag) 39037ed7877Sjtc { 391726d188aSkre char c, last; 392dd6b6414Skre const char *startp = p; 39337ed7877Sjtc struct passwd *pw; 3945dd823abSmycroft const char *home; 395df073671Skre const int quotes = flag & EXP_QNEEDED; 396dd6b6414Skre char *user; 397ee3b307fSkre struct stackmark smark; 398ee3b307fSkre #ifdef DEBUG 399ee3b307fSkre unsigned int offs = expdest - stackblock(); 400ee3b307fSkre #endif 40137ed7877Sjtc 402ee3b307fSkre setstackmark(&smark); 403bd208c69Skre (void) grabstackstr(expdest); 404ee3b307fSkre user = stackblock(); /* we will just borrow top of stack */ 405ee3b307fSkre 406aa681addSkre while ((c = *++p) != '\0') { 40737ed7877Sjtc switch(c) { 408ee3b307fSkre case CTLESC: /* any of these occurring */ 409ee3b307fSkre case CTLVAR: /* means ~ expansion */ 410ee3b307fSkre case CTLBACKQ: /* does not happen at all */ 411dd6b6414Skre case CTLBACKQ | CTLQUOTE: 412ee3b307fSkre case CTLARI: /* just leave original unchanged */ 413dd6b6414Skre case CTLENDARI: 4141fbf0781Smycroft case CTLQUOTEMARK: 415ee3b307fSkre case '\n': 416ee3b307fSkre popstackmark(&smark); 4171fbf0781Smycroft return (startp); 418727a69dcSkre case CTLNONL: 419ee3b307fSkre continue; 42037ed7877Sjtc case ':': 421bd208c69Skre if (!posix || flag & EXP_VARTILDE) 42237ed7877Sjtc goto done; 42337ed7877Sjtc break; 424bd208c69Skre case CTLENDVAR: 42537ed7877Sjtc case '/': 42637ed7877Sjtc goto done; 42737ed7877Sjtc } 428dd6b6414Skre STPUTC(c, user); 42937ed7877Sjtc } 43037ed7877Sjtc done: 431dd6b6414Skre STACKSTRNUL(user); 432ee3b307fSkre user = stackblock(); /* to start of collected username */ 433ea89b130Skre 434ee3b307fSkre CTRACE(DBG_EXPAND, ("exptilde, found \"~%s\"", user)); 435ee3b307fSkre if (*user == '\0') { 436dd6b6414Skre home = lookupvar("HOME"); 437ee3b307fSkre /* 438ee3b307fSkre * if HOME is unset, results are unspecified... 439ee3b307fSkre * we used to just leave the ~ unchanged, but 440ee3b307fSkre * (some) other shells do ... and this seems more useful. 441ee3b307fSkre */ 442ee3b307fSkre if (home == NULL && (pw = getpwuid(getuid())) != NULL) 44337ed7877Sjtc home = pw->pw_dir; 444ee3b307fSkre } else if ((pw = getpwnam(user)) == NULL) { 445ee3b307fSkre /* 446ee3b307fSkre * If user does not exist, results are undefined. 447ee3b307fSkre * so we can abort() here if we want, but let's not! 448ee3b307fSkre */ 449ee3b307fSkre home = NULL; 450ee3b307fSkre } else 451ee3b307fSkre home = pw->pw_dir; 452dd6b6414Skre 453ee3b307fSkre VTRACE(DBG_EXPAND, (" ->\"%s\"", home ? home : "<<NULL>>")); 454ee3b307fSkre popstackmark(&smark); /* now expdest is valid again */ 455ee3b307fSkre 456ee3b307fSkre /* 457ee3b307fSkre * Posix XCU 2.6.1: The value of $HOME (for ~) or the initial 458ee3b307fSkre * working directory from getpwnam() for ~user 459ee3b307fSkre * Nothing there about "except if a null string". So do what it wants. 460726d188aSkre * In later drafts (to become Issue 8), it is even required that in 461726d188aSkre * this case, (where HOME='') a bare ~ expands to "" (which must not 462726d188aSkre * be reduced to nothing). 463ee3b307fSkre */ 464726d188aSkre last = '\0'; /* just in case *home == '\0' (already) */ 465726d188aSkre if (home == NULL) { 466ee3b307fSkre CTRACE(DBG_EXPAND, (": returning unused \"%s\"\n", startp)); 467dd6b6414Skre return startp; 468391d4540Skre } 469391d4540Skre while ((c = *home++) != '\0') { 470256d645dSkre if ((quotes && NEEDESC(c)) || ISCTL(c)) 47137ed7877Sjtc STPUTC(CTLESC, expdest); 47237ed7877Sjtc STPUTC(c, expdest); 473726d188aSkre last = c; 47437ed7877Sjtc } 475726d188aSkre 476726d188aSkre /* 477726d188aSkre * If HOME (or whatver) ended in a '/' (last == '/'), and 478726d188aSkre * the ~prefix was terminated by a '/', then only keep one 479726d188aSkre * of them - since we already took the one from HOME, just 480726d188aSkre * skip over the one that ended the tilde prefix. 481726d188aSkre * 482726d188aSkre * Current (Issue 8) drafts say this is permitted, and recommend 483726d188aSkre * it - a later version of the standard will probably require it. 484726d188aSkre * This is to prevent ~/foo generating //foo when HOME=/ (and 485726d188aSkre * other cases like it, but that's the important one). 486726d188aSkre */ 487726d188aSkre if (last == '/' && *p == '/') 488726d188aSkre p++; 489726d188aSkre 490ee3b307fSkre CTRACE(DBG_EXPAND, (": added %d \"%.*s\" returning \"%s\"\n", 491ee3b307fSkre expdest - stackblock() - offs, expdest - stackblock() - offs, 492ee3b307fSkre stackblock() + offs, p)); 493ea89b130Skre 49437ed7877Sjtc return (p); 49537ed7877Sjtc } 49637ed7877Sjtc 49737ed7877Sjtc 4981fbf0781Smycroft STATIC void 499c02b3bbdSchristos removerecordregions(int endoff) 5001fbf0781Smycroft { 50158810bf0Skre 50258810bf0Skre VTRACE(DBG_EXPAND, ("removerecordregions(%d):", endoff)); 50358810bf0Skre if (ifslastp == NULL) { 50458810bf0Skre VTRACE(DBG_EXPAND, (" none\n", endoff)); 5051fbf0781Smycroft return; 50658810bf0Skre } 5071fbf0781Smycroft 5081fbf0781Smycroft if (ifsfirst.endoff > endoff) { 50958810bf0Skre VTRACE(DBG_EXPAND, (" first(%d)", ifsfirst.endoff)); 5101fbf0781Smycroft while (ifsfirst.next != NULL) { 5111fbf0781Smycroft struct ifsregion *ifsp; 5121fbf0781Smycroft INTOFF; 5131fbf0781Smycroft ifsp = ifsfirst.next->next; 5141fbf0781Smycroft ckfree(ifsfirst.next); 5151fbf0781Smycroft ifsfirst.next = ifsp; 5161fbf0781Smycroft INTON; 5171fbf0781Smycroft } 5181fbf0781Smycroft if (ifsfirst.begoff > endoff) 5191fbf0781Smycroft ifslastp = NULL; 5201fbf0781Smycroft else { 52158810bf0Skre VTRACE(DBG_EXPAND,("->(%d,%d)",ifsfirst.begoff,endoff)); 5221fbf0781Smycroft ifslastp = &ifsfirst; 5231fbf0781Smycroft ifsfirst.endoff = endoff; 5241fbf0781Smycroft } 52558810bf0Skre VTRACE(DBG_EXPAND, ("\n")); 5261fbf0781Smycroft return; 5271fbf0781Smycroft } 5281fbf0781Smycroft 5291fbf0781Smycroft ifslastp = &ifsfirst; 5301fbf0781Smycroft while (ifslastp->next && ifslastp->next->begoff < endoff) 5311fbf0781Smycroft ifslastp=ifslastp->next; 53258810bf0Skre VTRACE(DBG_EXPAND, (" found(%d,%d)", ifslastp->begoff,ifslastp->endoff)); 5331fbf0781Smycroft while (ifslastp->next != NULL) { 5341fbf0781Smycroft struct ifsregion *ifsp; 5351fbf0781Smycroft INTOFF; 5361fbf0781Smycroft ifsp = ifslastp->next->next; 5371fbf0781Smycroft ckfree(ifslastp->next); 5381fbf0781Smycroft ifslastp->next = ifsp; 5391fbf0781Smycroft INTON; 5401fbf0781Smycroft } 5411fbf0781Smycroft if (ifslastp->endoff > endoff) 5421fbf0781Smycroft ifslastp->endoff = endoff; 54358810bf0Skre VTRACE(DBG_EXPAND, ("->(%d,%d)", ifslastp->begoff,ifslastp->endoff)); 5441fbf0781Smycroft } 5451fbf0781Smycroft 5461fbf0781Smycroft 54737ed7877Sjtc /* 548ee3b307fSkre * Expand arithmetic expression. 549ee3b307fSkre * 550ee3b307fSkre * In this incarnation, we start at the beginning (yes, "Let's start at the 551ee3b307fSkre * very beginning. A very good place to start.") and collect the expression 552ee3b307fSkre * until the end - which means expanding anything contained within. 553ee3b307fSkre * 554ee3b307fSkre * Fortunately, argstr() just happens to do that for us... 55537ed7877Sjtc */ 556dd6b6414Skre STATIC const char * 55751791eabSkre expari(const char *p) 55837ed7877Sjtc { 559dd6b6414Skre char *q, *start; 56091ce988bSapb intmax_t result; 56191ce988bSapb int adjustment; 5621fbf0781Smycroft int begoff; 5631fbf0781Smycroft int quoted; 564ee3b307fSkre struct stackmark smark; 56537ed7877Sjtc 5661fbf0781Smycroft /* ifsfree(); */ 567633ceb6dSchristos 56837ed7877Sjtc /* 569cc8e58edSkre * SPACE_NEEDED is enough for all possible digits (rounded up) 570cc8e58edSkre * plus possible "-", and the terminating '\0', hence, plus 2 571cc8e58edSkre * 572cc8e58edSkre * The calculation produces the number of bytes needed to 573cc8e58edSkre * represent the biggest possible value, in octal. We only 574cc8e58edSkre * generate decimal, which takes (often) less digits (never more) 575cc8e58edSkre * so this is safe, if occasionally slightly wasteful. 576cc8e58edSkre */ 577dd6b6414Skre #define SPACE_NEEDED ((int)((sizeof(intmax_t) * CHAR_BIT + 2) / 3 + 2)) 578ee3b307fSkre 579dd6b6414Skre quoted = *p++ == '"'; 580dd6b6414Skre begoff = expdest - stackblock(); 581ee3b307fSkre VTRACE(DBG_EXPAND, ("expari%s: \"%s\" begoff %d\n", 582ee3b307fSkre quoted ? "(quoted)" : "", p, begoff)); 583ee3b307fSkre 584727a69dcSkre p = argstr(p, EXP_NL); /* expand $(( )) string */ 585dd6b6414Skre STPUTC('\0', expdest); 586dd6b6414Skre start = stackblock() + begoff; 587ee3b307fSkre 588ee3b307fSkre removerecordregions(begoff); /* nothing there is kept */ 589727a69dcSkre rmescapes_nl(start); /* convert CRTNONL back into \n's */ 590ee3b307fSkre 591ee3b307fSkre setstackmark(&smark); 592ee3b307fSkre q = grabstackstr(expdest); /* keep the expression while eval'ing */ 593727a69dcSkre result = arith(start, line_number); 594ee3b307fSkre popstackmark(&smark); /* return the stack to before grab */ 595633ceb6dSchristos 596ee3b307fSkre start = stackblock() + begoff; /* block may have moved */ 597dd6b6414Skre adjustment = expdest - start; 598ee3b307fSkre STADJUST(-adjustment, expdest); /* remove the argstr() result */ 599dd6b6414Skre 600ee3b307fSkre CHECKSTRSPACE(SPACE_NEEDED, expdest); /* nb: stack block might move */ 601dd6b6414Skre fmtstr(expdest, SPACE_NEEDED, "%"PRIdMAX, result); 602dd6b6414Skre 603ee3b307fSkre for (q = expdest; *q++ != '\0'; ) /* find end of what we added */ 60437ed7877Sjtc ; 605633ceb6dSchristos 606dd6b6414Skre if (quoted == 0) /* allow weird splitting */ 60758810bf0Skre recordregion(begoff, begoff + q - 1 - expdest, 0); 608d3573c91Skre adjustment = q - expdest - 1; 609ee3b307fSkre STADJUST(adjustment, expdest); /* move expdest to end */ 610f72bc19eSkre VTRACE(DBG_EXPAND, ("expari: adding %d \"%s\", returning \"%.5s...\"\n", 611f72bc19eSkre adjustment, stackblock() + begoff, p)); 612dd6b6414Skre 613dd6b6414Skre return p; 61437ed7877Sjtc } 61537ed7877Sjtc 61661f28255Scgd 61761f28255Scgd /* 618ee3b307fSkre * Expand stuff in backwards quotes (these days, any command substitution). 61961f28255Scgd */ 62061f28255Scgd 62161f28255Scgd STATIC void 622c02b3bbdSchristos expbackq(union node *cmd, int quoted, int flag) 62361f28255Scgd { 62461f28255Scgd struct backcmd in; 62561f28255Scgd int i; 62661f28255Scgd char buf[128]; 62761f28255Scgd char *p; 628ee3b307fSkre char *dest = expdest; /* expdest may be reused by eval, use an alt */ 62961f28255Scgd struct ifsregion saveifs, *savelastp; 63061f28255Scgd struct nodelist *saveargbackq; 63161f28255Scgd char lastc; 63261f28255Scgd int startloc = dest - stackblock(); 63361f28255Scgd int saveherefd; 634df073671Skre const int quotes = flag & EXP_QNEEDED; 63569a4e2eeSchristos int nnl; 636ee3b307fSkre struct stackmark smark; 63761f28255Scgd 638ee3b307fSkre VTRACE(DBG_EXPAND, ("expbackq( ..., q=%d flag=%#x) have %d\n", 639ee3b307fSkre quoted, flag, startloc)); 64061f28255Scgd INTOFF; 64161f28255Scgd saveifs = ifsfirst; 64261f28255Scgd savelastp = ifslastp; 64361f28255Scgd saveargbackq = argbackq; 64461f28255Scgd saveherefd = herefd; 64561f28255Scgd herefd = -1; 646ee3b307fSkre 647ee3b307fSkre setstackmark(&smark); /* preserve the stack */ 648ee3b307fSkre p = grabstackstr(dest); /* save what we have there currently */ 649ee3b307fSkre evalbackcmd(cmd, &in); /* evaluate the $( ) tree (using stack) */ 650ee3b307fSkre popstackmark(&smark); /* and return stack to when we entered */ 651ee3b307fSkre 65261f28255Scgd ifsfirst = saveifs; 65361f28255Scgd ifslastp = savelastp; 65461f28255Scgd argbackq = saveargbackq; 65561f28255Scgd herefd = saveherefd; 65661f28255Scgd 657ee3b307fSkre p = in.buf; /* now extract the results */ 658ee3b307fSkre nnl = 0; /* dropping trailing \n's */ 65961f28255Scgd for (;;) { 66061f28255Scgd if (--in.nleft < 0) { 66161f28255Scgd if (in.fd < 0) 66261f28255Scgd break; 6631fca9bbfSkre INTON; 6643eb04a36Sjoerg while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR) 6653eb04a36Sjoerg continue; 6661fca9bbfSkre INTOFF; 667ea89b130Skre VTRACE(DBG_EXPAND, ("expbackq: read returns %d\n", i)); 66861f28255Scgd if (i <= 0) 66961f28255Scgd break; 67061f28255Scgd p = buf; 67161f28255Scgd in.nleft = i - 1; 67261f28255Scgd } 67361f28255Scgd lastc = *p++; 67461f28255Scgd if (lastc != '\0') { 675ee3b307fSkre if (lastc == '\n') /* don't save \n yet */ 676ee3b307fSkre nnl++; /* it might be trailing */ 67769a4e2eeSchristos else { 678ee3b307fSkre /* 679ee3b307fSkre * We have something other than \n 680ee3b307fSkre * 681ee3b307fSkre * Before saving it, we need to insert 682ee3b307fSkre * any \n's that we have just skipped. 683ee3b307fSkre */ 684ee3b307fSkre 685ee3b307fSkre /* XXX 686ee3b307fSkre * this hack is just because our 687ee3b307fSkre * CHECKSTRSPACE() is lazy, and only 688ee3b307fSkre * ever grows the stack once, even 689ee3b307fSkre * if that does not allocate the space 690ee3b307fSkre * we requested. ie: safe for small 691ee3b307fSkre * requests, but not large ones. 692ee3b307fSkre * FIXME someday... 693ee3b307fSkre */ 694ee3b307fSkre if (nnl < 20) { 69569a4e2eeSchristos CHECKSTRSPACE(nnl + 2, dest); 69669a4e2eeSchristos while (nnl > 0) { 69769a4e2eeSchristos nnl--; 69869a4e2eeSchristos USTPUTC('\n', dest); 69969a4e2eeSchristos } 700ee3b307fSkre } else { 701ee3b307fSkre /* The slower, safer, way */ 702ee3b307fSkre while (nnl > 0) { 703ee3b307fSkre nnl--; 704ee3b307fSkre STPUTC('\n', dest); 705ee3b307fSkre } 706ee3b307fSkre CHECKSTRSPACE(2, dest); 707ee3b307fSkre } 708256d645dSkre if ((quotes && quoted && NEEDESC(lastc)) || 709256d645dSkre ISCTL(lastc)) 71069a4e2eeSchristos USTPUTC(CTLESC, dest); 71169a4e2eeSchristos USTPUTC(lastc, dest); 71261f28255Scgd } 71361f28255Scgd } 71469a4e2eeSchristos } 7156ab19545Schristos 71661f28255Scgd if (in.fd >= 0) 71761f28255Scgd close(in.fd); 71861f28255Scgd if (in.buf) 71961f28255Scgd ckfree(in.buf); 72061f28255Scgd if (in.jp) 721c02b3bbdSchristos back_exitstatus = waitforjob(in.jp); 72261f28255Scgd if (quoted == 0) 72361f28255Scgd recordregion(startloc, dest - stackblock(), 0); 724b8bee70dSkre CTRACE(DBG_EXPAND, ("evalbackq: [%d] size=%d: \"%.*s\"\n", 725b8bee70dSkre back_exitstatus, 7267fb5a8c6Sdholland (int)((dest - stackblock()) - startloc), 7277fb5a8c6Sdholland (int)((dest - stackblock()) - startloc), 72861f28255Scgd stackblock() + startloc)); 729ee3b307fSkre 730ee3b307fSkre expdest = dest; /* all done, expdest is all ours again */ 73161f28255Scgd INTON; 73261f28255Scgd } 73361f28255Scgd 73461f28255Scgd 735ad8d5369Schristos STATIC int 736dd6b6414Skre subevalvar(const char *p, const char *str, int subtype, int startloc, 737dd6b6414Skre int varflags) 738ad8d5369Schristos { 739ad8d5369Schristos char *startp; 740ad8d5369Schristos int saveherefd = herefd; 741ad8d5369Schristos struct nodelist *saveargbackq = argbackq; 742dd6b6414Skre int amount; 743d3aec206Schristos 744ad8d5369Schristos herefd = -1; 745ee3b307fSkre VTRACE(DBG_EXPAND, ("subevalvar(%d) \"%.20s\" ${%.*s} sloc=%d vf=%x\n", 746ee3b307fSkre subtype, p, p-str, str, startloc, varflags)); 747bd208c69Skre argstr(p, subtype == VSASSIGN ? EXP_VARTILDE : EXP_TILDE); 748ad8d5369Schristos STACKSTRNUL(expdest); 749ad8d5369Schristos herefd = saveherefd; 750ad8d5369Schristos argbackq = saveargbackq; 751ad8d5369Schristos startp = stackblock() + startloc; 752ad8d5369Schristos 753ad8d5369Schristos switch (subtype) { 754ad8d5369Schristos case VSASSIGN: 755ad8d5369Schristos setvar(str, startp, 0); 756ee3b307fSkre amount = startp - expdest; /* remove what argstr added */ 757d3aec206Schristos STADJUST(amount, expdest); 758ee3b307fSkre varflags &= ~VSNUL; /*XXX Huh? What's that achieve? */ 759ee3b307fSkre return 1; /* go back and eval var again */ 760ad8d5369Schristos 761ad8d5369Schristos case VSQUESTION: 762ad8d5369Schristos if (*p != CTLENDVAR) { 763ad8d5369Schristos outfmt(&errout, "%s\n", startp); 7649f61b804Splunky error(NULL); 765ad8d5369Schristos } 7667fb5a8c6Sdholland error("%.*s: parameter %snot set", 7677fb5a8c6Sdholland (int)(p - str - 1), 768ad8d5369Schristos str, (varflags & VSNUL) ? "null or " 769ad8d5369Schristos : nullstr); 770ee9e50eaSmycroft /* NOTREACHED */ 771ad8d5369Schristos 772dd6b6414Skre default: 773dd6b6414Skre abort(); 774dd6b6414Skre } 775dd6b6414Skre } 776dd6b6414Skre 777dd6b6414Skre STATIC int 778dd6b6414Skre subevalvar_trim(const char *p, int strloc, int subtype, int startloc, 779dd6b6414Skre int varflags, int quotes) 780dd6b6414Skre { 781dd6b6414Skre char *startp; 782dd6b6414Skre char *str; 783dd6b6414Skre char *loc = NULL; 784dd6b6414Skre char *q; 785dd6b6414Skre int c = 0; 786dd6b6414Skre int saveherefd = herefd; 787dd6b6414Skre struct nodelist *saveargbackq = argbackq; 788dd6b6414Skre int amount; 789dd6b6414Skre 790dd6b6414Skre herefd = -1; 791dd6b6414Skre switch (subtype) { 792dd6b6414Skre case VSTRIMLEFT: 793dd6b6414Skre case VSTRIMLEFTMAX: 794dd6b6414Skre case VSTRIMRIGHT: 795dd6b6414Skre case VSTRIMRIGHTMAX: 796dd6b6414Skre break; 797dd6b6414Skre default: 798dd6b6414Skre abort(); 799dd6b6414Skre break; 800dd6b6414Skre } 801ea89b130Skre 802ea89b130Skre VTRACE(DBG_EXPAND, 803ea89b130Skre ("subevalvar_trim(\"%.9s\", STR@%d, SUBT=%d, start@%d, vf=%x, q=%x)\n", 804ea89b130Skre p, strloc, subtype, startloc, varflags, quotes)); 805ea89b130Skre 806dd6b6414Skre argstr(p, (varflags & (VSQUOTE|VSPATQ)) == VSQUOTE ? 0 : EXP_CASE); 807dd6b6414Skre STACKSTRNUL(expdest); 808dd6b6414Skre herefd = saveherefd; 809dd6b6414Skre argbackq = saveargbackq; 810dd6b6414Skre startp = stackblock() + startloc; 811dd6b6414Skre str = stackblock() + strloc; 812dd6b6414Skre 813dd6b6414Skre switch (subtype) { 814dd6b6414Skre 815ad8d5369Schristos case VSTRIMLEFT: 816633ceb6dSchristos for (loc = startp; loc < str; loc++) { 817ad8d5369Schristos c = *loc; 818ad8d5369Schristos *loc = '\0'; 81919d6b839Schristos if (patmatch(str, startp, quotes)) 820ad8d5369Schristos goto recordleft; 821ad8d5369Schristos *loc = c; 82219d6b839Schristos if (quotes && *loc == CTLESC) 82328607542Schristos loc++; 824ad8d5369Schristos } 825ad8d5369Schristos return 0; 826ad8d5369Schristos 827ad8d5369Schristos case VSTRIMLEFTMAX: 82828607542Schristos for (loc = str - 1; loc >= startp;) { 829ad8d5369Schristos c = *loc; 830ad8d5369Schristos *loc = '\0'; 83119d6b839Schristos if (patmatch(str, startp, quotes)) 832ad8d5369Schristos goto recordleft; 833ad8d5369Schristos *loc = c; 83428607542Schristos loc--; 83519d6b839Schristos if (quotes && loc > startp && 83628607542Schristos *(loc - 1) == CTLESC) { 83728607542Schristos for (q = startp; q < loc; q++) 83828607542Schristos if (*q == CTLESC) 83928607542Schristos q++; 84028607542Schristos if (q > loc) 84128607542Schristos loc--; 84228607542Schristos } 843ad8d5369Schristos } 844ad8d5369Schristos return 0; 845ad8d5369Schristos 846ad8d5369Schristos case VSTRIMRIGHT: 84728607542Schristos for (loc = str - 1; loc >= startp;) { 84819d6b839Schristos if (patmatch(str, loc, quotes)) 84904784d87Schristos goto recordright; 85028607542Schristos loc--; 85119d6b839Schristos if (quotes && loc > startp && 85228607542Schristos *(loc - 1) == CTLESC) { 85328607542Schristos for (q = startp; q < loc; q++) 85428607542Schristos if (*q == CTLESC) 85528607542Schristos q++; 85628607542Schristos if (q > loc) 85728607542Schristos loc--; 85828607542Schristos } 85928607542Schristos } 860ad8d5369Schristos return 0; 861ad8d5369Schristos 862ad8d5369Schristos case VSTRIMRIGHTMAX: 86328607542Schristos for (loc = startp; loc < str - 1; loc++) { 86419d6b839Schristos if (patmatch(str, loc, quotes)) 86504784d87Schristos goto recordright; 86619d6b839Schristos if (quotes && *loc == CTLESC) 86728607542Schristos loc++; 86828607542Schristos } 869ad8d5369Schristos return 0; 870ad8d5369Schristos 871ad8d5369Schristos default: 872ad8d5369Schristos abort(); 873ad8d5369Schristos } 874ad8d5369Schristos 875ad8d5369Schristos recordleft: 87604784d87Schristos *loc = c; 877d3aec206Schristos amount = ((str - 1) - (loc - startp)) - expdest; 878d3aec206Schristos STADJUST(amount, expdest); 879ad8d5369Schristos while (loc != str - 1) 880ad8d5369Schristos *startp++ = *loc++; 881ad8d5369Schristos return 1; 88204784d87Schristos 88304784d87Schristos recordright: 88404784d87Schristos amount = loc - expdest; 88504784d87Schristos STADJUST(amount, expdest); 88604784d87Schristos STPUTC('\0', expdest); 88704784d87Schristos STADJUST(-1, expdest); 88804784d87Schristos return 1; 889ad8d5369Schristos } 890ad8d5369Schristos 891ad8d5369Schristos 89261f28255Scgd /* 89361f28255Scgd * Expand a variable, and return a pointer to the next character in the 89461f28255Scgd * input string. 89561f28255Scgd */ 89661f28255Scgd 897dd6b6414Skre STATIC const char * 898dd6b6414Skre evalvar(const char *p, int flag) 89961f28255Scgd { 90061f28255Scgd int subtype; 90137ed7877Sjtc int varflags; 902dd6b6414Skre const char *var; 90361f28255Scgd char *val; 904e97b0193She int patloc; 90561f28255Scgd int c; 90661f28255Scgd int set; 90761f28255Scgd int special; 90861f28255Scgd int startloc; 909ad8d5369Schristos int varlen; 910e4a2a056Sdsl int apply_ifs; 911df073671Skre const int quotes = flag & EXP_QNEEDED; 91261f28255Scgd 9134dcb41aeSchristos varflags = (unsigned char)*p++; 91437ed7877Sjtc subtype = varflags & VSTYPE; 91561f28255Scgd var = p; 916*a7e8b4a5Skre special = subtype != VSUNKNOWN && !is_name(*p); 91761f28255Scgd p = strchr(p, '=') + 1; 918e4a2a056Sdsl 919ea89b130Skre CTRACE(DBG_EXPAND, 920ea89b130Skre ("evalvar \"%.*s\", flag=%#X quotes=%#X vf=%#X subtype=%X\n", 921*a7e8b4a5Skre (int)(p - var - 1), var, flag, quotes, varflags, subtype)); 922ea89b130Skre 92361f28255Scgd again: /* jump here after setting a variable with ${var=text} */ 924018a6f78Schristos if (varflags & VSLINENO) { 925727a69dcSkre if (line_num.flags & VUNSET) { 926727a69dcSkre set = 0; 927727a69dcSkre val = NULL; 928727a69dcSkre } else { 929018a6f78Schristos set = 1; 930dd6b6414Skre special = p - var; 931dd6b6414Skre val = NULL; 932727a69dcSkre } 933018a6f78Schristos } else if (special) { 934633ceb6dSchristos set = varisset(var, varflags & VSNUL); 93561f28255Scgd val = NULL; 936265b0617Skre if (!set && *var == '@') 937265b0617Skre empty_dollar_at = 1; 93861f28255Scgd } else { 93961f28255Scgd val = lookupvar(var); 94007bae7edSchristos if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 94161f28255Scgd val = NULL; 94261f28255Scgd set = 0; 94361f28255Scgd } else 94461f28255Scgd set = 1; 94561f28255Scgd } 946e4a2a056Sdsl 947ad8d5369Schristos varlen = 0; 94861f28255Scgd startloc = expdest - stackblock(); 949e4a2a056Sdsl 95083d9b545Sast if (!set && uflag && *var != '@' && *var != '*') { 951eac95517Schristos switch (subtype) { 952eac95517Schristos case VSNORMAL: 953eac95517Schristos case VSTRIMLEFT: 954eac95517Schristos case VSTRIMLEFTMAX: 955eac95517Schristos case VSTRIMRIGHT: 956eac95517Schristos case VSTRIMRIGHTMAX: 957eac95517Schristos case VSLENGTH: 9587fb5a8c6Sdholland error("%.*s: parameter not set", 9597fb5a8c6Sdholland (int)(p - var - 1), var); 960eac95517Schristos /* NOTREACHED */ 961eac95517Schristos } 962e4a2a056Sdsl } 963e4a2a056Sdsl 964265b0617Skre #if 0 /* no longer need this $@ evil ... */ 965c9f333adSkre if (!set && subtype != VSPLUS && special && *var == '@') 966c9f333adSkre if (startloc > 0 && expdest[-1] == CTLQUOTEMARK) 967c9f333adSkre expdest--, startloc--; 968265b0617Skre #endif 969c9f333adSkre 97061f28255Scgd if (set && subtype != VSPLUS) { 97161f28255Scgd /* insert the value of the variable */ 97261f28255Scgd if (special) { 973dd6b6414Skre if (varflags & VSLINENO) { 974dd6b6414Skre /* 975ee3b307fSkre * The LINENO hack (expansion part) 976dd6b6414Skre */ 977dd6b6414Skre while (--special > 0) { 978dd6b6414Skre /* not needed, it is a number... 979df073671Skre if (quotes && NEEDESC(*var)) 980dd6b6414Skre STPUTC(CTLESC, expdest); 981dd6b6414Skre */ 982dd6b6414Skre STPUTC(*var++, expdest); 983dd6b6414Skre } 984dd6b6414Skre } else 985b17e1b5cSdsl varvalue(var, varflags&VSQUOTE, subtype, flag); 986ad8d5369Schristos if (subtype == VSLENGTH) { 987a0fa692dSchristos varlen = expdest - stackblock() - startloc; 988a0fa692dSchristos STADJUST(-varlen, expdest); 989ad8d5369Schristos } 99061f28255Scgd } else { 99161f28255Scgd 992ad8d5369Schristos if (subtype == VSLENGTH) { 993ad8d5369Schristos for (; *val; val++) 994ad8d5369Schristos varlen++; 995df073671Skre } else if (quotes && varflags & VSQUOTE) { 99658e34de6Skre /* 99758e34de6Skre * If we are going to look for magic in the 99858e34de6Skre * value (quotes is set) and the expansion 99958e34de6Skre * occurs inside "" (VSQUOTE) then any char 100058e34de6Skre * that has any potential special meaning 100158e34de6Skre * needs to have that meaning suppressed, 100258e34de6Skre * so supply a CTLESC prefix for it. 100358e34de6Skre */ 1004df073671Skre for (; (c = *val) != '\0'; val++) { 1005df073671Skre if (NEEDESC(c)) 100661f28255Scgd STPUTC(CTLESC, expdest); 1007df073671Skre STPUTC(c, expdest); 100861f28255Scgd } 1009df073671Skre } else { 101058e34de6Skre /* 101158e34de6Skre * We are going to rmescapes() later, 101258e34de6Skre * so make sure that any data char that 101358e34de6Skre * might be mistaken for one of our CTLxxx 101458e34de6Skre * magic chars is protected ... always. 101558e34de6Skre */ 101658e34de6Skre for (; (c = *val) != '\0'; val++) { 1017256d645dSkre if (ISCTL(c)) 101858e34de6Skre STPUTC(CTLESC, expdest); 101958e34de6Skre STPUTC(c, expdest); 102058e34de6Skre } 102161f28255Scgd } 102261f28255Scgd } 1023ad8d5369Schristos } 1024ad8d5369Schristos 1025ad8d5369Schristos 1026c9f333adSkre if (varflags & VSQUOTE) { 10277d60739aSdsl if (*var == '@' && shellparam.nparam != 1) 10287d60739aSdsl apply_ifs = 1; 10297d60739aSdsl else { 10307d60739aSdsl /* 10317d60739aSdsl * Mark so that we don't apply IFS if we recurse through 10327d60739aSdsl * here expanding $bar from "${foo-$bar}". 10337d60739aSdsl */ 10347d60739aSdsl flag |= EXP_IN_QUOTES; 10357d60739aSdsl apply_ifs = 0; 10367d60739aSdsl } 1037c9f333adSkre } else if (flag & EXP_IN_QUOTES) { 1038c9f333adSkre apply_ifs = 0; 10397d60739aSdsl } else 10407d60739aSdsl apply_ifs = 1; 1041ad8d5369Schristos 1042ad8d5369Schristos switch (subtype) { 1043ad8d5369Schristos case VSLENGTH: 1044ad8d5369Schristos expdest = cvtnum(varlen, expdest); 1045e4a2a056Sdsl break; 1046ad8d5369Schristos 1047ad8d5369Schristos case VSNORMAL: 1048ad8d5369Schristos break; 1049ad8d5369Schristos 1050ad8d5369Schristos case VSPLUS: 1051e4a2a056Sdsl set = !set; 1052e4a2a056Sdsl /* FALLTHROUGH */ 1053ad8d5369Schristos case VSMINUS: 1054cf199667Schristos if (!set) { 1055c6cbc16dSdsl argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0)); 1056e4a2a056Sdsl /* 1057e4a2a056Sdsl * ${x-a b c} doesn't get split, but removing the 10585c056f67Swiz * 'apply_ifs = 0' apparently breaks ${1+"$@"}.. 1059e4a2a056Sdsl * ${x-'a b' c} should generate 2 args. 1060e4a2a056Sdsl */ 1061dec2ca90Schristos if (*p != CTLENDVAR) 1062e4a2a056Sdsl /* We should have marked stuff already */ 1063e4a2a056Sdsl apply_ifs = 0; 1064cf199667Schristos } 1065ad8d5369Schristos break; 1066ad8d5369Schristos 1067ad8d5369Schristos case VSTRIMLEFT: 1068ad8d5369Schristos case VSTRIMLEFTMAX: 1069ad8d5369Schristos case VSTRIMRIGHT: 1070ad8d5369Schristos case VSTRIMRIGHTMAX: 1071df029fccSkre if (!set) { 1072df029fccSkre set = 1; /* allow argbackq to be advanced if needed */ 1073ad8d5369Schristos break; 1074df029fccSkre } 1075ad8d5369Schristos /* 1076ad8d5369Schristos * Terminate the string and start recording the pattern 1077ad8d5369Schristos * right after it 1078ad8d5369Schristos */ 1079ad8d5369Schristos STPUTC('\0', expdest); 1080e97b0193She patloc = expdest - stackblock(); 1081dd6b6414Skre if (subevalvar_trim(p, patloc, subtype, startloc, varflags, 1082dd6b6414Skre quotes) == 0) { 1083e97b0193She int amount = (expdest - stackblock() - patloc) + 1; 1084633ceb6dSchristos STADJUST(-amount, expdest); 1085633ceb6dSchristos } 10861fbf0781Smycroft /* Remove any recorded regions beyond start of variable */ 10871fbf0781Smycroft removerecordregions(startloc); 1088e4a2a056Sdsl apply_ifs = 1; 1089e4a2a056Sdsl break; 1090ad8d5369Schristos 1091ad8d5369Schristos case VSASSIGN: 1092ad8d5369Schristos case VSQUESTION: 1093e4a2a056Sdsl if (set) 1094e4a2a056Sdsl break; 1095dd6b6414Skre if (subevalvar(p, var, subtype, startloc, varflags)) { 1096dd6b6414Skre /* if subevalvar() returns, it always returns 1 */ 1097dd6b6414Skre 109829cad877Schristos varflags &= ~VSNUL; 10991fbf0781Smycroft /* 11001fbf0781Smycroft * Remove any recorded regions beyond 11011fbf0781Smycroft * start of variable 11021fbf0781Smycroft */ 11031fbf0781Smycroft removerecordregions(startloc); 110461f28255Scgd goto again; 110529cad877Schristos } 1106ee3b307fSkre apply_ifs = 0; /* never executed */ 1107ad8d5369Schristos break; 1108ad8d5369Schristos 1109*a7e8b4a5Skre case VSUNKNOWN: 1110*a7e8b4a5Skre VTRACE(DBG_EXPAND, 1111*a7e8b4a5Skre ("evalvar \"%.*s\", unknown [%p %p] \"%.3s\" (%#2x %#2x)\n", 1112*a7e8b4a5Skre (int)(p - var - 1), var, var, p, p, p[0] & 0xFF, p[1] & 0xFF)); 1113*a7e8b4a5Skre 1114*a7e8b4a5Skre if ((p - var) <= 1) 1115*a7e8b4a5Skre error("%d: unknown expansion type", line_number); 1116*a7e8b4a5Skre else { 1117*a7e8b4a5Skre if (*p == '#') /* only VSUNKNOWN as a ${#var:...} */ 1118*a7e8b4a5Skre error("%d: ${#%.*s%c..}: unknown modifier", 1119*a7e8b4a5Skre line_number, (int)(p - var - 1), 1120*a7e8b4a5Skre var, p[1]&0xFF); 1121*a7e8b4a5Skre 1122*a7e8b4a5Skre if (*p == CTLESC) 1123*a7e8b4a5Skre p++; 1124*a7e8b4a5Skre error("%d: ${%.*s%c..}: unknown modifier", 1125*a7e8b4a5Skre line_number, (int)(p - var - 1), var, (*p & 0xFF)); 1126*a7e8b4a5Skre } 1127*a7e8b4a5Skre /* NOTREACHED */ 1128*a7e8b4a5Skre 1129ad8d5369Schristos default: 1130ad8d5369Schristos abort(); 113161f28255Scgd } 1132ad8d5369Schristos 1133e4a2a056Sdsl if (apply_ifs) 1134e4a2a056Sdsl recordregion(startloc, expdest - stackblock(), 1135e4a2a056Sdsl varflags & VSQUOTE); 1136e4a2a056Sdsl 113761f28255Scgd if (subtype != VSNORMAL) { /* skip to end of alternative */ 113861f28255Scgd int nesting = 1; 113961f28255Scgd for (;;) { 114061f28255Scgd if ((c = *p++) == CTLESC) 114161f28255Scgd p++; 1142727a69dcSkre else if (c == CTLNONL) 1143727a69dcSkre ; 114461f28255Scgd else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 1145c823b55cSmycroft if (set) 114661f28255Scgd argbackq = argbackq->next; 114761f28255Scgd } else if (c == CTLVAR) { 114861f28255Scgd if ((*p++ & VSTYPE) != VSNORMAL) 114961f28255Scgd nesting++; 115061f28255Scgd } else if (c == CTLENDVAR) { 115161f28255Scgd if (--nesting == 0) 115261f28255Scgd break; 115361f28255Scgd } 115461f28255Scgd } 115561f28255Scgd } 115661f28255Scgd return p; 115761f28255Scgd } 115861f28255Scgd 115961f28255Scgd 116061f28255Scgd 116161f28255Scgd /* 1162ee3b307fSkre * Test whether a special parameter is set. 116361f28255Scgd */ 116461f28255Scgd 116561f28255Scgd STATIC int 1166dd6b6414Skre varisset(const char *name, int nulok) 116761f28255Scgd { 1168633ceb6dSchristos if (*name == '!') 1169633ceb6dSchristos return backgndpid != -1; 1170633ceb6dSchristos else if (*name == '@' || *name == '*') { 117161f28255Scgd if (*shellparam.p == NULL) 117261f28255Scgd return 0; 1173633ceb6dSchristos 1174633ceb6dSchristos if (nulok) { 11754a521d35Schristos char **av; 11764a521d35Schristos 11774a521d35Schristos for (av = shellparam.p; *av; av++) 11784a521d35Schristos if (**av != '\0') 1179633ceb6dSchristos return 1; 1180633ceb6dSchristos return 0; 1181633ceb6dSchristos } 11824b99c4d7Schristos } else if (is_digit(*name)) { 11834a521d35Schristos char *ap; 1184ee3b307fSkre long num; 1185ee3b307fSkre 1186ee3b307fSkre /* 1187ee3b307fSkre * handle overflow sensibly (the *ap tests should never fail) 1188ee3b307fSkre */ 1189ee3b307fSkre errno = 0; 1190ee3b307fSkre num = strtol(name, &ap, 10); 1191ee3b307fSkre if (errno != 0 || (*ap != '\0' && *ap != '=')) 1192ee3b307fSkre return 0; 1193633ceb6dSchristos 11944a521d35Schristos if (num == 0) 11954a521d35Schristos ap = arg0; 1196dd6b6414Skre else if (num > shellparam.nparam) 1197dd6b6414Skre return 0; 11984a521d35Schristos else 11994a521d35Schristos ap = shellparam.p[num - 1]; 12004a521d35Schristos 12014a521d35Schristos if (nulok && (ap == NULL || *ap == '\0')) 120261f28255Scgd return 0; 120361f28255Scgd } 120461f28255Scgd return 1; 120561f28255Scgd } 120661f28255Scgd 120761f28255Scgd 120861f28255Scgd 120961f28255Scgd /* 121061f28255Scgd * Add the value of a specialized variable to the stack string. 121161f28255Scgd */ 121261f28255Scgd 121361f28255Scgd STATIC void 1214dd6b6414Skre varvalue(const char *name, int quoted, int subtype, int flag) 121561f28255Scgd { 121661f28255Scgd int num; 121761f28255Scgd char *p; 121861f28255Scgd int i; 1219c83568a7Skre int sep; 122061f28255Scgd char **ap; 1221c9f333adSkre #ifdef DEBUG 1222c9f333adSkre char *start = expdest; 1223c9f333adSkre #endif 1224c9f333adSkre 1225c9f333adSkre VTRACE(DBG_EXPAND, ("varvalue(%c%s, sub=%d, fl=%#x)", *name, 1226c9f333adSkre quoted ? ", quoted" : "", subtype, flag)); 122761f28255Scgd 1228dd6b6414Skre if (subtype == VSLENGTH) /* no magic required ... */ 12296654ff1cSkre flag &= ~(EXP_FULL | EXP_QNEEDED); 1230dd6b6414Skre 123137ed7877Sjtc #define STRTODEST(p) \ 123237ed7877Sjtc do {\ 1233df073671Skre if ((flag & EXP_QNEEDED) && quoted) { \ 123437ed7877Sjtc while (*p) { \ 1235df073671Skre if (NEEDESC(*p)) \ 123637ed7877Sjtc STPUTC(CTLESC, expdest); \ 123737ed7877Sjtc STPUTC(*p++, expdest); \ 123837ed7877Sjtc } \ 123937ed7877Sjtc } else \ 1240256d645dSkre while (*p) { \ 12416654ff1cSkre if ((flag & EXP_QNEEDED) && ISCTL(*p)) \ 1242256d645dSkre STPUTC(CTLESC, expdest); \ 124337ed7877Sjtc STPUTC(*p++, expdest); \ 1244256d645dSkre } \ 124537ed7877Sjtc } while (0) 124637ed7877Sjtc 124737ed7877Sjtc 12484b99c4d7Schristos switch (*name) { 124961f28255Scgd case '$': 125061f28255Scgd num = rootpid; 1251dd6b6414Skre break; 125261f28255Scgd case '?': 1253c02b3bbdSchristos num = exitstatus; 1254dd6b6414Skre break; 125561f28255Scgd case '#': 125661f28255Scgd num = shellparam.nparam; 1257dd6b6414Skre break; 125861f28255Scgd case '!': 125961f28255Scgd num = backgndpid; 126061f28255Scgd break; 126161f28255Scgd case '-': 1262f359a311Skre for (i = 0; i < option_flags; i++) { 1263f359a311Skre if (optlist[optorder[i]].val) 1264f359a311Skre STPUTC(optlist[optorder[i]].letter, expdest); 126561f28255Scgd } 1266c9f333adSkre VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); 1267dd6b6414Skre return; 126861f28255Scgd case '@': 1269dd6b6414Skre if (flag & EXP_SPLIT && quoted) { 1270c9f333adSkre VTRACE(DBG_EXPAND, (": $@ split (%d)\n", 1271c9f333adSkre shellparam.nparam)); 1272265b0617Skre #if 0 1273c9f333adSkre /* GROSS HACK */ 1274c9f333adSkre if (shellparam.nparam == 0 && 1275c9f333adSkre expdest[-1] == CTLQUOTEMARK) 1276c9f333adSkre expdest--; 1277c9f333adSkre /* KCAH SSORG */ 1278265b0617Skre #endif 1279265b0617Skre if (shellparam.nparam == 0) 1280265b0617Skre empty_dollar_at = 1; 1281265b0617Skre 128261f28255Scgd for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 1283265b0617Skre if (*p == '\0') { 1284265b0617Skre /* retain an explicit null string */ 1285265b0617Skre STPUTC(CTLQUOTEMARK, expdest); 1286265b0617Skre STPUTC(CTLQUOTEEND, expdest); 1287265b0617Skre } else 128837ed7877Sjtc STRTODEST(p); 1289413b7762Sdsl if (*ap) 1290413b7762Sdsl /* A NUL separates args inside "" */ 12911fbf0781Smycroft STPUTC('\0', expdest); 12921fbf0781Smycroft } 1293dd6b6414Skre return; 12941fbf0781Smycroft } 12951fbf0781Smycroft /* fall through */ 12961fbf0781Smycroft case '*': 12971fbf0781Smycroft sep = ifsval()[0]; 12981fbf0781Smycroft for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 12991fbf0781Smycroft STRTODEST(p); 1300bda1f28dSkre if (!*ap) 1301bda1f28dSkre break; 1302c83568a7Skre if (sep) { 1303df073671Skre if (quoted && (flag & EXP_QNEEDED) && 1304df073671Skre NEEDESC(sep)) 1305c83568a7Skre STPUTC(CTLESC, expdest); 130661f28255Scgd STPUTC(sep, expdest); 1307c83568a7Skre } else 1308c83568a7Skre if ((flag & (EXP_SPLIT|EXP_IN_QUOTES)) == EXP_SPLIT 1309bda1f28dSkre && !quoted && **ap != '\0') 1310bda1f28dSkre STPUTC('\0', expdest); 131161f28255Scgd } 1312c9f333adSkre VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); 1313dd6b6414Skre return; 131461f28255Scgd default: 13154b99c4d7Schristos if (is_digit(*name)) { 1316dd6b6414Skre long lnum; 1317dd6b6414Skre 1318dd6b6414Skre errno = 0; 1319dd6b6414Skre lnum = strtol(name, &p, 10); 1320dd6b6414Skre if (errno != 0 || (*p != '\0' && *p != '=')) 1321dd6b6414Skre return; 1322dd6b6414Skre 1323dd6b6414Skre if (lnum == 0) 1324dd6b6414Skre p = arg0; 1325dd6b6414Skre else if (lnum > 0 && lnum <= shellparam.nparam) 1326dd6b6414Skre p = shellparam.p[lnum - 1]; 1327dd6b6414Skre else 1328dd6b6414Skre return; 132937ed7877Sjtc STRTODEST(p); 133061f28255Scgd } 1331c9f333adSkre VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); 1332dd6b6414Skre return; 13334b99c4d7Schristos } 1334dd6b6414Skre /* 1335dd6b6414Skre * only the specials with an int value arrive here 1336dd6b6414Skre */ 1337c9f333adSkre VTRACE(DBG_EXPAND, ("(%d)", num)); 1338dd6b6414Skre expdest = cvtnum(num, expdest); 1339c9f333adSkre VTRACE(DBG_EXPAND, (": %.*s\n", expdest-start, start)); 134061f28255Scgd } 134161f28255Scgd 134261f28255Scgd 134361f28255Scgd 134461f28255Scgd /* 13451594850fSsoren * Record the fact that we have to scan this region of the 134661f28255Scgd * string for IFS characters. 134761f28255Scgd */ 134861f28255Scgd 134961f28255Scgd STATIC void 1350e4a2a056Sdsl recordregion(int start, int end, int inquotes) 13515dad1439Scgd { 135248250187Stls struct ifsregion *ifsp; 135361f28255Scgd 135458810bf0Skre VTRACE(DBG_EXPAND, ("recordregion(%d,%d,%d)\n", start, end, inquotes)); 135561f28255Scgd if (ifslastp == NULL) { 135661f28255Scgd ifsp = &ifsfirst; 135761f28255Scgd } else { 13582078d2c0Sdsl if (ifslastp->endoff == start 13592078d2c0Sdsl && ifslastp->inquotes == inquotes) { 1360c6cbc16dSdsl /* extend previous area */ 1361c6cbc16dSdsl ifslastp->endoff = end; 1362c6cbc16dSdsl return; 1363c6cbc16dSdsl } 136461f28255Scgd ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 136561f28255Scgd ifslastp->next = ifsp; 136661f28255Scgd } 136761f28255Scgd ifslastp = ifsp; 136861f28255Scgd ifslastp->next = NULL; 136961f28255Scgd ifslastp->begoff = start; 137061f28255Scgd ifslastp->endoff = end; 1371e4a2a056Sdsl ifslastp->inquotes = inquotes; 137261f28255Scgd } 137361f28255Scgd 137461f28255Scgd 137561f28255Scgd 137661f28255Scgd /* 137761f28255Scgd * Break the argument string into pieces based upon IFS and add the 137861f28255Scgd * strings to the argument list. The regions of the string to be 137961f28255Scgd * searched for IFS characters have been stored by recordregion. 138061f28255Scgd */ 138161f28255Scgd STATIC void 1382c02b3bbdSchristos ifsbreakup(char *string, struct arglist *arglist) 138361f28255Scgd { 138461f28255Scgd struct ifsregion *ifsp; 138561f28255Scgd struct strlist *sp; 138661f28255Scgd char *start; 138748250187Stls char *p; 138861f28255Scgd char *q; 13893d424690Schristos const char *ifs; 1390e4a2a056Sdsl const char *ifsspc; 1391413b7762Sdsl int had_param_ch = 0; 139261f28255Scgd 139361f28255Scgd start = string; 1394e4a2a056Sdsl 1395ea89b130Skre VTRACE(DBG_EXPAND, ("ifsbreakup(\"%s\")", string)); /* misses \0's */ 1396e4a2a056Sdsl if (ifslastp == NULL) { 1397e4a2a056Sdsl /* Return entire argument, IFS doesn't apply to any of it */ 1398ea89b130Skre VTRACE(DBG_EXPAND, ("no regions\n", string)); 139978204bbfSchristos sp = stalloc(sizeof(*sp)); 1400e4a2a056Sdsl sp->text = start; 1401e4a2a056Sdsl *arglist->lastp = sp; 1402e4a2a056Sdsl arglist->lastp = &sp->next; 1403e4a2a056Sdsl return; 1404e4a2a056Sdsl } 1405e4a2a056Sdsl 1406ca12a0b8Schristos ifs = ifsval(); 1407e4a2a056Sdsl 1408e4a2a056Sdsl for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { 140961f28255Scgd p = string + ifsp->begoff; 1410ea89b130Skre VTRACE(DBG_EXPAND, (" !%.*s!(%d)", ifsp->endoff-ifsp->begoff, 1411ea89b130Skre p, ifsp->endoff-ifsp->begoff)); 141261f28255Scgd while (p < string + ifsp->endoff) { 1413413b7762Sdsl had_param_ch = 1; 141461f28255Scgd q = p; 1415c9f333adSkre if (IS_BORING(*p)) { 1416727a69dcSkre p++; 1417727a69dcSkre continue; 1418727a69dcSkre } 141961f28255Scgd if (*p == CTLESC) 142061f28255Scgd p++; 1421413b7762Sdsl if (ifsp->inquotes) { 1422413b7762Sdsl /* Only NULs (should be from "$@") end args */ 1423e4a2a056Sdsl if (*p != 0) { 1424e4a2a056Sdsl p++; 1425e4a2a056Sdsl continue; 1426e4a2a056Sdsl } 1427413b7762Sdsl ifsspc = NULL; 1428ea89b130Skre VTRACE(DBG_EXPAND, (" \\0 nxt:\"%s\" ", p)); 1429e4a2a056Sdsl } else { 1430e4a2a056Sdsl if (!strchr(ifs, *p)) { 1431e4a2a056Sdsl p++; 1432e4a2a056Sdsl continue; 1433e4a2a056Sdsl } 1434c58b829dSdsl had_param_ch = 0; 1435e4a2a056Sdsl ifsspc = strchr(" \t\n", *p); 1436e4a2a056Sdsl 14371fbf0781Smycroft /* Ignore IFS whitespace at start */ 1438e4a2a056Sdsl if (q == start && ifsspc != NULL) { 14391fbf0781Smycroft p++; 14401fbf0781Smycroft start = p; 14411fbf0781Smycroft continue; 14421fbf0781Smycroft } 1443e4a2a056Sdsl } 1444e4a2a056Sdsl 1445e4a2a056Sdsl /* Save this argument... */ 144661f28255Scgd *q = '\0'; 1447ea89b130Skre VTRACE(DBG_EXPAND, ("<%s>", start)); 144878204bbfSchristos sp = stalloc(sizeof(*sp)); 144961f28255Scgd sp->text = start; 145061f28255Scgd *arglist->lastp = sp; 145161f28255Scgd arglist->lastp = &sp->next; 14521fbf0781Smycroft p++; 1453e4a2a056Sdsl 1454328c4365Sdsl if (ifsspc != NULL) { 1455328c4365Sdsl /* Ignore further trailing IFS whitespace */ 1456e4a2a056Sdsl for (; p < string + ifsp->endoff; p++) { 145761f28255Scgd q = p; 1458727a69dcSkre if (*p == CTLNONL) 1459727a69dcSkre continue; 146061f28255Scgd if (*p == CTLESC) 146161f28255Scgd p++; 14621fbf0781Smycroft if (strchr(ifs, *p) == NULL) { 14631fbf0781Smycroft p = q; 14641fbf0781Smycroft break; 1465e4a2a056Sdsl } 1466e4a2a056Sdsl if (strchr(" \t\n", *p) == NULL) { 1467328c4365Sdsl p++; 146861f28255Scgd break; 146961f28255Scgd } 147061f28255Scgd } 147161f28255Scgd } 147261f28255Scgd start = p; 147361f28255Scgd } 147461f28255Scgd } 1475e4a2a056Sdsl 1476265b0617Skre /* 1477c9f333adSkre while (*start == CTLQUOTEEND) 1478c9f333adSkre start++; 1479265b0617Skre */ 1480c9f333adSkre 1481328c4365Sdsl /* 1482328c4365Sdsl * Save anything left as an argument. 1483328c4365Sdsl * Traditionally we have treated 'IFS=':'; set -- x$IFS' as 1484328c4365Sdsl * generating 2 arguments, the second of which is empty. 1485328c4365Sdsl * Some recent clarification of the Posix spec say that it 1486328c4365Sdsl * should only generate one.... 1487328c4365Sdsl */ 1488413b7762Sdsl if (had_param_ch || *start != 0) { 1489ea89b130Skre VTRACE(DBG_EXPAND, (" T<%s>", start)); 149078204bbfSchristos sp = stalloc(sizeof(*sp)); 149161f28255Scgd sp->text = start; 149261f28255Scgd *arglist->lastp = sp; 149361f28255Scgd arglist->lastp = &sp->next; 149461f28255Scgd } 1495ea89b130Skre VTRACE(DBG_EXPAND, ("\n")); 149661f28255Scgd } 149761f28255Scgd 1498f7c8df6dSchristos STATIC void 1499c02b3bbdSchristos ifsfree(void) 1500f7c8df6dSchristos { 1501f7c8df6dSchristos while (ifsfirst.next != NULL) { 1502f7c8df6dSchristos struct ifsregion *ifsp; 1503f7c8df6dSchristos INTOFF; 1504f7c8df6dSchristos ifsp = ifsfirst.next->next; 1505f7c8df6dSchristos ckfree(ifsfirst.next); 1506f7c8df6dSchristos ifsfirst.next = ifsp; 1507f7c8df6dSchristos INTON; 1508f7c8df6dSchristos } 1509f7c8df6dSchristos ifslastp = NULL; 1510f7c8df6dSchristos ifsfirst.next = NULL; 1511f7c8df6dSchristos } 1512f7c8df6dSchristos 151361f28255Scgd 151461f28255Scgd 151561f28255Scgd /* 151661f28255Scgd * Expand shell metacharacters. At this point, the only control characters 151761f28255Scgd * should be escapes. The results are stored in the list exparg. 151861f28255Scgd */ 151961f28255Scgd 152061f28255Scgd char *expdir; 152161f28255Scgd 152261f28255Scgd 152361f28255Scgd STATIC void 1524c02b3bbdSchristos expandmeta(struct strlist *str, int flag) 152561f28255Scgd { 152661f28255Scgd char *p; 152761f28255Scgd struct strlist **savelastp; 152861f28255Scgd struct strlist *sp; 152961f28255Scgd char c; 153037ed7877Sjtc /* TODO - EXP_REDIR */ 153161f28255Scgd 153261f28255Scgd while (str) { 153361f28255Scgd p = str->text; 153461f28255Scgd for (;;) { /* fast check for meta chars */ 153561f28255Scgd if ((c = *p++) == '\0') 153661f28255Scgd goto nometa; 1537c9f333adSkre if (c == '*' || c == '?' || c == '[' /* || c == '!' */) 153861f28255Scgd break; 153961f28255Scgd } 154061f28255Scgd savelastp = exparg.lastp; 154161f28255Scgd INTOFF; 154237ed7877Sjtc if (expdir == NULL) { 154337ed7877Sjtc int i = strlen(str->text); 154437ed7877Sjtc expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 154537ed7877Sjtc } 154637ed7877Sjtc 154761f28255Scgd expmeta(expdir, str->text); 154861f28255Scgd ckfree(expdir); 154961f28255Scgd expdir = NULL; 155061f28255Scgd INTON; 155161f28255Scgd if (exparg.lastp == savelastp) { 155237ed7877Sjtc /* 155337ed7877Sjtc * no matches 155437ed7877Sjtc */ 155561f28255Scgd nometa: 155661f28255Scgd *exparg.lastp = str; 155761f28255Scgd rmescapes(str->text); 155861f28255Scgd exparg.lastp = &str->next; 155961f28255Scgd } else { 156061f28255Scgd *exparg.lastp = NULL; 156161f28255Scgd *savelastp = sp = expsort(*savelastp); 156261f28255Scgd while (sp->next != NULL) 156361f28255Scgd sp = sp->next; 156461f28255Scgd exparg.lastp = &sp->next; 156561f28255Scgd } 156661f28255Scgd str = str->next; 156761f28255Scgd } 156861f28255Scgd } 156961f28255Scgd 1570dd6b6414Skre STATIC void 1571dd6b6414Skre add_args(struct strlist *str) 1572dd6b6414Skre { 1573dd6b6414Skre while (str) { 1574dd6b6414Skre *exparg.lastp = str; 1575dd6b6414Skre rmescapes(str->text); 1576dd6b6414Skre exparg.lastp = &str->next; 1577dd6b6414Skre str = str->next; 1578dd6b6414Skre } 1579dd6b6414Skre } 1580dd6b6414Skre 158161f28255Scgd 158261f28255Scgd /* 158361f28255Scgd * Do metacharacter (i.e. *, ?, [...]) expansion. 158461f28255Scgd */ 158561f28255Scgd 158661f28255Scgd STATIC void 1587c02b3bbdSchristos expmeta(char *enddir, char *name) 158861f28255Scgd { 158948250187Stls char *p; 15903d424690Schristos const char *cp; 159161f28255Scgd char *q; 159261f28255Scgd char *start; 159361f28255Scgd char *endname; 159461f28255Scgd int metaflag; 159561f28255Scgd struct stat statb; 159661f28255Scgd DIR *dirp; 159761f28255Scgd struct dirent *dp; 159861f28255Scgd int atend; 159961f28255Scgd int matchdot; 160061f28255Scgd 1601bcacfd9aSkre CTRACE(DBG_EXPAND|DBG_MATCH, ("expmeta(\"%s\")\n", name)); 160261f28255Scgd metaflag = 0; 160361f28255Scgd start = name; 160461f28255Scgd for (p = name ; ; p++) { 160561f28255Scgd if (*p == '*' || *p == '?') 160661f28255Scgd metaflag = 1; 160761f28255Scgd else if (*p == '[') { 160861f28255Scgd q = p + 1; 160914482abcSkre if (*q == '!' || *q == '^') 161061f28255Scgd q++; 161161f28255Scgd for (;;) { 1612c9f333adSkre while (IS_BORING(*q)) 16131fbf0781Smycroft q++; 161414482abcSkre if (*q == ']') { 161514482abcSkre q++; 161614482abcSkre metaflag = 1; 161714482abcSkre break; 161814482abcSkre } 161914482abcSkre if (*q == '[' && q[1] == ':') { 162014482abcSkre /* 162114482abcSkre * character class, look for :] ending 162214482abcSkre * also stop on ']' (end bracket expr) 162314482abcSkre * or '\0' or '/' (end pattern) 162414482abcSkre */ 162514482abcSkre while (*++q != '\0' && *q != ']' && 162614482abcSkre *q != '/') { 162714482abcSkre if (*q == CTLESC) { 162814482abcSkre if (*++q == '\0') 162914482abcSkre break; 163014482abcSkre if (*q == '/') 163114482abcSkre break; 163214482abcSkre } else if (*q == ':' && 163314482abcSkre q[1] == ']') 163414482abcSkre break; 163514482abcSkre } 163614482abcSkre if (*q == ':') { 163714482abcSkre /* 163814482abcSkre * stopped at ':]' 163914482abcSkre * still in [...] 164014482abcSkre * skip ":]" and continue; 164114482abcSkre */ 164214482abcSkre q += 2; 164314482abcSkre continue; 164414482abcSkre } 164514482abcSkre 164614482abcSkre /* done at end of pattern, not [...] */ 164714482abcSkre if (*q == '\0' || *q == '/') 164814482abcSkre break; 164914482abcSkre 165014482abcSkre /* found the ']', we have a [...] */ 165114482abcSkre metaflag = 1; 165214482abcSkre q++; /* skip ']' */ 165314482abcSkre break; 165414482abcSkre } 165561f28255Scgd if (*q == CTLESC) 165661f28255Scgd q++; 165714482abcSkre /* end of pattern cannot be escaped */ 165861f28255Scgd if (*q == '/' || *q == '\0') 165961f28255Scgd break; 166014482abcSkre q++; 166161f28255Scgd } 166261f28255Scgd } else if (*p == '\0') 166361f28255Scgd break; 1664c9f333adSkre else if (IS_BORING(*p)) 16651fbf0781Smycroft continue; 166661f28255Scgd else if (*p == CTLESC) 166761f28255Scgd p++; 166861f28255Scgd if (*p == '/') { 166961f28255Scgd if (metaflag) 167061f28255Scgd break; 167161f28255Scgd start = p + 1; 167261f28255Scgd } 167361f28255Scgd } 167461f28255Scgd if (metaflag == 0) { /* we've reached the end of the file name */ 167561f28255Scgd if (enddir != expdir) 167661f28255Scgd metaflag++; 167761f28255Scgd for (p = name ; ; p++) { 1678c9f333adSkre if (IS_BORING(*p)) 16791fbf0781Smycroft continue; 168061f28255Scgd if (*p == CTLESC) 168161f28255Scgd p++; 168261f28255Scgd *enddir++ = *p; 168361f28255Scgd if (*p == '\0') 168461f28255Scgd break; 168561f28255Scgd } 168659771e30Smycroft if (metaflag == 0 || lstat(expdir, &statb) >= 0) 168761f28255Scgd addfname(expdir); 168861f28255Scgd return; 168961f28255Scgd } 169061f28255Scgd endname = p; 169161f28255Scgd if (start != name) { 169261f28255Scgd p = name; 169361f28255Scgd while (p < start) { 1694c9f333adSkre while (IS_BORING(*p)) 16951fbf0781Smycroft p++; 169661f28255Scgd if (*p == CTLESC) 169761f28255Scgd p++; 169861f28255Scgd *enddir++ = *p++; 169961f28255Scgd } 170061f28255Scgd } 170161f28255Scgd if (enddir == expdir) { 17023d424690Schristos cp = "."; 170361f28255Scgd } else if (enddir == expdir + 1 && *expdir == '/') { 17043d424690Schristos cp = "/"; 170561f28255Scgd } else { 17063d424690Schristos cp = expdir; 170761f28255Scgd enddir[-1] = '\0'; 170861f28255Scgd } 17093d424690Schristos if ((dirp = opendir(cp)) == NULL) 171061f28255Scgd return; 171161f28255Scgd if (enddir != expdir) 171261f28255Scgd enddir[-1] = '/'; 171361f28255Scgd if (*endname == 0) { 171461f28255Scgd atend = 1; 171561f28255Scgd } else { 171661f28255Scgd atend = 0; 171761f28255Scgd *endname++ = '\0'; 171861f28255Scgd } 171961f28255Scgd matchdot = 0; 17201fbf0781Smycroft p = start; 1721c9f333adSkre while (IS_BORING(*p)) 17221fbf0781Smycroft p++; 17231fbf0781Smycroft if (*p == CTLESC) 17241fbf0781Smycroft p++; 17251fbf0781Smycroft if (*p == '.') 172661f28255Scgd matchdot++; 172761f28255Scgd while (! int_pending() && (dp = readdir(dirp)) != NULL) { 172861f28255Scgd if (dp->d_name[0] == '.' && ! matchdot) 172961f28255Scgd continue; 173028607542Schristos if (patmatch(start, dp->d_name, 0)) { 173161f28255Scgd if (atend) { 173261f28255Scgd scopy(dp->d_name, enddir); 173361f28255Scgd addfname(expdir); 173461f28255Scgd } else { 17353d424690Schristos for (p = enddir, cp = dp->d_name; 17363d424690Schristos (*p++ = *cp++) != '\0';) 173707bae7edSchristos continue; 173861f28255Scgd p[-1] = '/'; 173961f28255Scgd expmeta(p, endname); 174061f28255Scgd } 174161f28255Scgd } 174261f28255Scgd } 174361f28255Scgd closedir(dirp); 174461f28255Scgd if (! atend) 174561f28255Scgd endname[-1] = '/'; 174661f28255Scgd } 174761f28255Scgd 174861f28255Scgd 174961f28255Scgd /* 175061f28255Scgd * Add a file name to the list. 175161f28255Scgd */ 175261f28255Scgd 175361f28255Scgd STATIC void 1754c02b3bbdSchristos addfname(char *name) 175561f28255Scgd { 175661f28255Scgd char *p; 175761f28255Scgd struct strlist *sp; 175861f28255Scgd 175961f28255Scgd p = stalloc(strlen(name) + 1); 176061f28255Scgd scopy(name, p); 176178204bbfSchristos sp = stalloc(sizeof(*sp)); 176261f28255Scgd sp->text = p; 176361f28255Scgd *exparg.lastp = sp; 176461f28255Scgd exparg.lastp = &sp->next; 176561f28255Scgd } 176661f28255Scgd 176761f28255Scgd 176861f28255Scgd /* 176961f28255Scgd * Sort the results of file name expansion. It calculates the number of 177061f28255Scgd * strings to sort and then calls msort (short for merge sort) to do the 177161f28255Scgd * work. 177261f28255Scgd */ 177361f28255Scgd 177461f28255Scgd STATIC struct strlist * 1775c02b3bbdSchristos expsort(struct strlist *str) 177661f28255Scgd { 177761f28255Scgd int len; 177861f28255Scgd struct strlist *sp; 177961f28255Scgd 178061f28255Scgd len = 0; 178161f28255Scgd for (sp = str ; sp ; sp = sp->next) 178261f28255Scgd len++; 178361f28255Scgd return msort(str, len); 178461f28255Scgd } 178561f28255Scgd 178661f28255Scgd 178761f28255Scgd STATIC struct strlist * 1788c02b3bbdSchristos msort(struct strlist *list, int len) 178961f28255Scgd { 17904b99c4d7Schristos struct strlist *p, *q = NULL; 179161f28255Scgd struct strlist **lpp; 179261f28255Scgd int half; 179361f28255Scgd int n; 179461f28255Scgd 179561f28255Scgd if (len <= 1) 179661f28255Scgd return list; 179761f28255Scgd half = len >> 1; 179861f28255Scgd p = list; 179961f28255Scgd for (n = half ; --n >= 0 ; ) { 180061f28255Scgd q = p; 180161f28255Scgd p = p->next; 180261f28255Scgd } 180361f28255Scgd q->next = NULL; /* terminate first half of list */ 180461f28255Scgd q = msort(list, half); /* sort first half of list */ 180561f28255Scgd p = msort(p, len - half); /* sort second half */ 180661f28255Scgd lpp = &list; 180761f28255Scgd for (;;) { 180861f28255Scgd if (strcmp(p->text, q->text) < 0) { 180961f28255Scgd *lpp = p; 181061f28255Scgd lpp = &p->next; 181161f28255Scgd if ((p = *lpp) == NULL) { 181261f28255Scgd *lpp = q; 181361f28255Scgd break; 181461f28255Scgd } 181561f28255Scgd } else { 181661f28255Scgd *lpp = q; 181761f28255Scgd lpp = &q->next; 181861f28255Scgd if ((q = *lpp) == NULL) { 181961f28255Scgd *lpp = p; 182061f28255Scgd break; 182161f28255Scgd } 182261f28255Scgd } 182361f28255Scgd } 182461f28255Scgd return list; 182561f28255Scgd } 182661f28255Scgd 182761f28255Scgd 18287969ec4dSroy /* 18297969ec4dSroy * See if a character matches a character class, starting at the first colon 18307969ec4dSroy * of "[:class:]". 18317969ec4dSroy * If a valid character class is recognized, a pointer to the next character 18327969ec4dSroy * after the final closing bracket is stored into *end, otherwise a null 18337969ec4dSroy * pointer is stored into *end. 18347969ec4dSroy */ 18357969ec4dSroy static int 18368e4a570fSchristos match_charclass(const char *p, wchar_t chr, const char **end) 18377969ec4dSroy { 18387969ec4dSroy char name[20]; 1839d47295ccSrillig const char *nameend; 18407969ec4dSroy wctype_t cclass; 18417dca2b7eSkre char *q; 18427969ec4dSroy 18437969ec4dSroy *end = NULL; 18447969ec4dSroy p++; 18457dca2b7eSkre q = &name[0]; 18467969ec4dSroy nameend = strstr(p, ":]"); 1847b81009ceSkre if (nameend == NULL || nameend == p) /* not a valid class */ 18487969ec4dSroy return 0; 1849b81009ceSkre 18507dca2b7eSkre if (*p == CTLESC) { 18517dca2b7eSkre if (*++p == CTLESC) 1852b81009ceSkre return 0; 18537dca2b7eSkre if (p == nameend) 18547dca2b7eSkre return 0; 18557dca2b7eSkre } 18567dca2b7eSkre if (!is_alpha(*p)) 18577dca2b7eSkre return 0; 18587dca2b7eSkre while (p < nameend) { 18597dca2b7eSkre if (*p == CTLESC) { 18607dca2b7eSkre p++; 18617dca2b7eSkre if (p == nameend) 18627dca2b7eSkre return 0; 18637dca2b7eSkre } 18647dca2b7eSkre if (!is_in_name(*p)) /* '_' is a local extension */ 18657dca2b7eSkre return 0; 18667dca2b7eSkre if (q < &name[sizeof name]) 18677dca2b7eSkre *q++ = *p++; 18687dca2b7eSkre else 18697dca2b7eSkre p++; 18707dca2b7eSkre } 1871b81009ceSkre 1872b81009ceSkre *end = nameend + 2; /* committed to it being a char class */ 18737dca2b7eSkre 18747dca2b7eSkre if (q < &name[sizeof name]) /* a usable name found */ 18757dca2b7eSkre *q++ = '\0'; 18767dca2b7eSkre else /* too long, valid, but no match */ 18777dca2b7eSkre return 0; 18787dca2b7eSkre 18797969ec4dSroy cclass = wctype(name); 18807969ec4dSroy /* An unknown class matches nothing but is valid nevertheless. */ 18817969ec4dSroy if (cclass == 0) 18827969ec4dSroy return 0; 18837969ec4dSroy return iswctype(chr, cclass); 18847969ec4dSroy } 18857969ec4dSroy 18867969ec4dSroy 188761f28255Scgd /* 188861f28255Scgd * Returns true if the pattern matches the string. 188961f28255Scgd */ 189061f28255Scgd 189161f28255Scgd STATIC int 18928e4a570fSchristos patmatch(const char *pattern, const char *string, int squoted) 189361f28255Scgd { 18948e4a570fSchristos const char *p, *q, *end; 18958e4a570fSchristos const char *bt_p, *bt_q; 189648250187Stls char c; 18978e4a570fSchristos wchar_t wc, wc2; 189861f28255Scgd 1899bcacfd9aSkre VTRACE(DBG_MATCH, ("patmatch(P=\"%s\", W=\"%s\"%s): ", 1900bcacfd9aSkre pattern, string, squoted ? ", SQ" : "")); 190161f28255Scgd p = pattern; 190261f28255Scgd q = string; 19038e4a570fSchristos bt_p = NULL; 19048e4a570fSchristos bt_q = NULL; 190561f28255Scgd for (;;) { 190661f28255Scgd switch (c = *p++) { 190761f28255Scgd case '\0': 190814482abcSkre if (squoted && *q == CTLESC) { 190914482abcSkre if (q[1] == '\0') 191014482abcSkre q++; 191114482abcSkre } 19128e4a570fSchristos if (*q != '\0') 19138e4a570fSchristos goto backtrack; 1914bcacfd9aSkre VTRACE(DBG_MATCH, ("match\n")); 19158e4a570fSchristos return 1; 191661f28255Scgd case CTLESC: 191728607542Schristos if (squoted && *q == CTLESC) 191828607542Schristos q++; 191914482abcSkre if (*p == '\0' && *q == '\0') { 192014482abcSkre VTRACE(DBG_MATCH, ("match-\\\n")); 192114482abcSkre return 1; 192214482abcSkre } 192314482abcSkre if (*q++ != *p++) 192414482abcSkre goto backtrack; 192514482abcSkre break; 192614482abcSkre case '\\': 192714482abcSkre if (squoted && *q == CTLESC) 192814482abcSkre q++; 192961f28255Scgd if (*q++ != *p++) 19308e4a570fSchristos goto backtrack; 193161f28255Scgd break; 19321fbf0781Smycroft case CTLQUOTEMARK: 1933c9f333adSkre case CTLQUOTEEND: 1934727a69dcSkre case CTLNONL: 19351fbf0781Smycroft continue; 193661f28255Scgd case '?': 193728607542Schristos if (squoted && *q == CTLESC) 193828607542Schristos q++; 1939bcacfd9aSkre if (*q++ == '\0') { 1940bcacfd9aSkre VTRACE(DBG_MATCH, ("?fail\n")); 194161f28255Scgd return 0; 1942bcacfd9aSkre } 194361f28255Scgd break; 194461f28255Scgd case '*': 194561f28255Scgd c = *p; 19461fbf0781Smycroft while (c == CTLQUOTEMARK || c == '*') 19471fbf0781Smycroft c = *++p; 1948c9f333adSkre if (c != CTLESC && !IS_BORING(c) && 19491fbf0781Smycroft c != '?' && c != '*' && c != '[') { 195061f28255Scgd while (*q != c) { 195128607542Schristos if (squoted && *q == CTLESC && 195228607542Schristos q[1] == c) 195328607542Schristos break; 1954bcacfd9aSkre if (*q == '\0') { 1955bcacfd9aSkre VTRACE(DBG_MATCH, ("*fail\n")); 195661f28255Scgd return 0; 1957bcacfd9aSkre } 195828607542Schristos if (squoted && *q == CTLESC) 195928607542Schristos q++; 196061f28255Scgd q++; 196161f28255Scgd } 196261f28255Scgd } 196314482abcSkre if (c == CTLESC && p[1] == '\0') { 196414482abcSkre VTRACE(DBG_MATCH, ("match+\\\n")); 196514482abcSkre return 1; 196614482abcSkre } 19678e4a570fSchristos /* 19688e4a570fSchristos * First try the shortest match for the '*' that 19698e4a570fSchristos * could work. We can forget any earlier '*' since 19708e4a570fSchristos * there is no way having it match more characters 19718e4a570fSchristos * can help us, given that we are already here. 19728e4a570fSchristos */ 19738e4a570fSchristos bt_p = p; 19748e4a570fSchristos bt_q = q; 19758e4a570fSchristos break; 197661f28255Scgd case '[': { 19778e4a570fSchristos const char *savep, *saveq, *endp; 197861f28255Scgd int invert, found; 19798e4a570fSchristos unsigned char chr; 198061f28255Scgd 1981c83568a7Skre /* 1982c83568a7Skre * First quick check to see if there is a 1983c83568a7Skre * possible matching ']' - if not, then this 1984c83568a7Skre * is not a char class, and the '[' is just 1985c83568a7Skre * a literal '['. 1986c83568a7Skre * 1987c83568a7Skre * This check will not detect all non classes, but 1988c83568a7Skre * that's OK - It just means that we execute the 1989c83568a7Skre * harder code sometimes when it it cannot succeed. 1990c83568a7Skre */ 199161f28255Scgd endp = p; 1992c83568a7Skre if (*endp == '!' || *endp == '^') 199361f28255Scgd endp++; 199461f28255Scgd for (;;) { 1995c9f333adSkre while (IS_BORING(*endp)) 19961fbf0781Smycroft endp++; 199761f28255Scgd if (*endp == '\0') 199861f28255Scgd goto dft; /* no matching ] */ 1999c9f333adSkre if (*endp++ == ']') 200061f28255Scgd break; 200161f28255Scgd } 2002c83568a7Skre /* end shortcut */ 2003c83568a7Skre 20048e4a570fSchristos savep = p, saveq = q; 20058e4a570fSchristos invert = 0; 20068e4a570fSchristos if (*p == '!' || *p == '^') { 200761f28255Scgd invert++; 200861f28255Scgd p++; 200961f28255Scgd } 201061f28255Scgd found = 0; 2011bcacfd9aSkre if (*q == '\0') { 2012bcacfd9aSkre VTRACE(DBG_MATCH, ("[]fail\n")); 2013d22a5147Smycroft return 0; 2014bcacfd9aSkre } 201514482abcSkre if (squoted && *q == CTLESC) 201614482abcSkre q++; 20178e4a570fSchristos chr = (unsigned char)*q++; 201861f28255Scgd c = *p++; 201961f28255Scgd do { 2020c9f333adSkre if (IS_BORING(c)) 20211fbf0781Smycroft continue; 20228e4a570fSchristos if (c == '\0') { 20238e4a570fSchristos p = savep, q = saveq; 20248e4a570fSchristos c = '['; 20258e4a570fSchristos goto dft; 20268e4a570fSchristos } 20277969ec4dSroy if (c == '[' && *p == ':') { 20287969ec4dSroy found |= match_charclass(p, chr, &end); 2029829cc62aSkre if (end != NULL) { 20307969ec4dSroy p = end; 2031829cc62aSkre continue; 2032829cc62aSkre } 20337969ec4dSroy } 2034c83568a7Skre if (c == CTLESC || c == '\\') 203561f28255Scgd c = *p++; 20368e4a570fSchristos wc = (unsigned char)c; 203761f28255Scgd if (*p == '-' && p[1] != ']') { 203861f28255Scgd p++; 2039c83568a7Skre if (*p == CTLESC || *p == '\\') 204061f28255Scgd p++; 20418e4a570fSchristos wc2 = (unsigned char)*p++; 20428e4a570fSchristos if ( collate_range_cmp(chr, wc) >= 0 20438e4a570fSchristos && collate_range_cmp(chr, wc2) <= 0 20448e4a570fSchristos ) 204561f28255Scgd found = 1; 204661f28255Scgd } else { 20478e4a570fSchristos if (chr == wc) 204861f28255Scgd found = 1; 204961f28255Scgd } 205061f28255Scgd } while ((c = *p++) != ']'); 205161f28255Scgd if (found == invert) 20528e4a570fSchristos goto backtrack; 205361f28255Scgd break; 205461f28255Scgd } 205561f28255Scgd dft: default: 205628607542Schristos if (squoted && *q == CTLESC) 205728607542Schristos q++; 20588e4a570fSchristos if (*q++ == c) 20598e4a570fSchristos break; 20608e4a570fSchristos backtrack: 20618e4a570fSchristos /* 20628e4a570fSchristos * If we have a mismatch (other than hitting the end 20638e4a570fSchristos * of the string), go back to the last '*' seen and 20648e4a570fSchristos * have it match one additional character. 20658e4a570fSchristos */ 2066bcacfd9aSkre if (bt_p == NULL) { 2067bcacfd9aSkre VTRACE(DBG_MATCH, ("BTP fail\n")); 206861f28255Scgd return 0; 2069bcacfd9aSkre } 2070bcacfd9aSkre if (*bt_q == '\0') { 2071bcacfd9aSkre VTRACE(DBG_MATCH, ("BTQ fail\n")); 20728e4a570fSchristos return 0; 2073bcacfd9aSkre } 20748e4a570fSchristos bt_q++; 20758e4a570fSchristos p = bt_p; 20768e4a570fSchristos q = bt_q; 207761f28255Scgd break; 207861f28255Scgd } 207961f28255Scgd } 208061f28255Scgd } 208161f28255Scgd 208261f28255Scgd 208361f28255Scgd 208461f28255Scgd /* 2085727a69dcSkre * Remove any CTLESC or CTLNONL characters from a string. 2086c69ada4cSkre * 2087c69ada4cSkre * String is modified in place, and we return the length of the result 208861f28255Scgd */ 208961f28255Scgd 2090c69ada4cSkre int 2091c02b3bbdSchristos rmescapes(char *str) 209261f28255Scgd { 209348250187Stls char *p, *q; 209461f28255Scgd 209561f28255Scgd p = str; 2096256d645dSkre while (!ISCTL(*p)) { 209761f28255Scgd if (*p++ == '\0') 2098c69ada4cSkre return ((int)(p - str) - 1); 209961f28255Scgd } 210061f28255Scgd q = p; 210161f28255Scgd while (*p) { 2102c9f333adSkre if (IS_BORING(*p)) { 21031fbf0781Smycroft p++; 21041fbf0781Smycroft continue; 21051fbf0781Smycroft } 21065f92382cSkre if (*p == CTLCNL) { 21075f92382cSkre p++; 21085f92382cSkre *q++ = '\n'; 21095f92382cSkre continue; 21105f92382cSkre } 211161f28255Scgd if (*p == CTLESC) 211261f28255Scgd p++; 211358e34de6Skre #ifdef DEBUG 2114256d645dSkre else if (ISCTL(*p)) 211558e34de6Skre abort(); 211658e34de6Skre #endif 211761f28255Scgd *q++ = *p++; 211861f28255Scgd } 211961f28255Scgd *q = '\0'; 2120c69ada4cSkre 2121c69ada4cSkre return ((int)(q - str)); 212261f28255Scgd } 212361f28255Scgd 2124727a69dcSkre /* 2125727a69dcSkre * and a special version for dealing with expressions to be parsed 2126727a69dcSkre * by the arithmetic evaluator. That needs to be able to count \n's 2127727a69dcSkre * even ones that were \newline elided \n's, so we have to put the 2128727a69dcSkre * latter back into the string - just being careful to put them only 2129727a69dcSkre * at a place where white space can reasonably occur in the string 2130727a69dcSkre * -- then the \n we insert will just be white space, and ignored 2131727a69dcSkre * for all purposes except line counting. 2132727a69dcSkre */ 2133727a69dcSkre 2134727a69dcSkre void 2135727a69dcSkre rmescapes_nl(char *str) 2136727a69dcSkre { 2137727a69dcSkre char *p, *q; 2138727a69dcSkre int nls = 0, holdnl = 0, holdlast; 2139727a69dcSkre 2140727a69dcSkre p = str; 2141256d645dSkre while (!ISCTL(*p)) { 2142727a69dcSkre if (*p++ == '\0') 2143727a69dcSkre return; 2144727a69dcSkre } 2145727a69dcSkre if (p > str) /* must reprocess char before stopper (if any) */ 2146727a69dcSkre --p; /* so we do not place a \n badly */ 2147727a69dcSkre q = p; 2148727a69dcSkre while (*p) { 2149c9f333adSkre if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { 2150727a69dcSkre p++; 2151727a69dcSkre continue; 2152727a69dcSkre } 2153727a69dcSkre if (*p == CTLNONL) { 2154727a69dcSkre p++; 2155727a69dcSkre nls++; 2156727a69dcSkre continue; 2157727a69dcSkre } 21585f92382cSkre if (*p == CTLCNL) { 21595f92382cSkre p++; 21605f92382cSkre *q++ = '\n'; 21615f92382cSkre continue; 21625f92382cSkre } 2163727a69dcSkre if (*p == CTLESC) 2164727a69dcSkre p++; 216558e34de6Skre #ifdef DEBUG 2166256d645dSkre else if (ISCTL(*p)) 216758e34de6Skre abort(); 216858e34de6Skre #endif 2169727a69dcSkre 2170727a69dcSkre holdlast = holdnl; 2171727a69dcSkre holdnl = is_in_name(*p); /* letters, digits, _ */ 2172727a69dcSkre if (q == str || is_space(q[-1]) || (*p != '=' && q[-1] != *p)) { 2173727a69dcSkre if (nls > 0 && holdnl != holdlast) { 2174727a69dcSkre while (nls > 0) 2175727a69dcSkre *q++ = '\n', nls--; 2176727a69dcSkre } 2177727a69dcSkre } 2178727a69dcSkre *q++ = *p++; 2179727a69dcSkre } 2180727a69dcSkre while (--nls >= 0) 2181727a69dcSkre *q++ = '\n'; 2182727a69dcSkre *q = '\0'; 2183727a69dcSkre } 2184727a69dcSkre 218561f28255Scgd 218661f28255Scgd 218761f28255Scgd /* 218861f28255Scgd * See if a pattern matches in a case statement. 218961f28255Scgd */ 219061f28255Scgd 219161f28255Scgd int 2192c02b3bbdSchristos casematch(union node *pattern, char *val) 219361f28255Scgd { 219461f28255Scgd struct stackmark smark; 219561f28255Scgd int result; 219661f28255Scgd char *p; 219761f28255Scgd 2198bcacfd9aSkre CTRACE(DBG_MATCH, ("casematch(P=\"%s\", W=\"%s\")\n", 2199bcacfd9aSkre pattern->narg.text, val)); 220061f28255Scgd setstackmark(&smark); 220161f28255Scgd argbackq = pattern->narg.backquote; 220261f28255Scgd STARTSTACKSTR(expdest); 220361f28255Scgd ifslastp = NULL; 22041fbf0781Smycroft argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 220561f28255Scgd STPUTC('\0', expdest); 220661f28255Scgd p = grabstackstr(expdest); 220728607542Schristos result = patmatch(p, val, 0); 220861f28255Scgd popstackmark(&smark); 220961f28255Scgd return result; 221061f28255Scgd } 2211ad8d5369Schristos 2212ad8d5369Schristos /* 2213dd6b6414Skre * Our own itoa(). Assumes result buffer is on the stack 2214ad8d5369Schristos */ 2215ad8d5369Schristos 2216ad8d5369Schristos STATIC char * 2217c02b3bbdSchristos cvtnum(int num, char *buf) 2218ad8d5369Schristos { 2219ad8d5369Schristos char temp[32]; 2220ad8d5369Schristos int neg = num < 0; 2221dd6b6414Skre char *p = temp + sizeof temp - 1; 2222ad8d5369Schristos 2223dd6b6414Skre if (neg) 2224dd6b6414Skre num = -num; 2225ad8d5369Schristos 2226dd6b6414Skre *p = '\0'; 2227ad8d5369Schristos do { 2228ad8d5369Schristos *--p = num % 10 + '0'; 2229dd6b6414Skre } while ((num /= 10) != 0 && p > temp + 1); 2230ad8d5369Schristos 2231ad8d5369Schristos if (neg) 2232ad8d5369Schristos *--p = '-'; 2233ad8d5369Schristos 2234ad8d5369Schristos while (*p) 2235ad8d5369Schristos STPUTC(*p++, buf); 2236ad8d5369Schristos return buf; 2237ad8d5369Schristos } 2238cf788c31Sseb 2239cf788c31Sseb /* 2240cf788c31Sseb * Do most of the work for wordexp(3). 2241cf788c31Sseb */ 2242cf788c31Sseb 2243cf788c31Sseb int 2244cf788c31Sseb wordexpcmd(int argc, char **argv) 2245cf788c31Sseb { 2246cf788c31Sseb size_t len; 2247cf788c31Sseb int i; 2248cf788c31Sseb 2249cf788c31Sseb out1fmt("%d", argc - 1); 2250cf788c31Sseb out1c('\0'); 2251cf788c31Sseb for (i = 1, len = 0; i < argc; i++) 2252cf788c31Sseb len += strlen(argv[i]); 225349ee47d0Stsutsui out1fmt("%zu", len); 2254cf788c31Sseb out1c('\0'); 2255cf788c31Sseb for (i = 1; i < argc; i++) { 2256cf788c31Sseb out1str(argv[i]); 2257cf788c31Sseb out1c('\0'); 2258cf788c31Sseb } 2259cf788c31Sseb return (0); 2260cf788c31Sseb } 2261