expand.c revision 206150
1195609Smp/*- 2195609Smp * Copyright (c) 1991, 1993 3195609Smp * The Regents of the University of California. All rights reserved. 4195609Smp * 5195609Smp * This code is derived from software contributed to Berkeley by 6195609Smp * Kenneth Almquist. 7195609Smp * 8195609Smp * Redistribution and use in source and binary forms, with or without 9195609Smp * modification, are permitted provided that the following conditions 10195609Smp * are met: 11195609Smp * 1. Redistributions of source code must retain the above copyright 12195609Smp * notice, this list of conditions and the following disclaimer. 13195609Smp * 2. Redistributions in binary form must reproduce the above copyright 14195609Smp * notice, this list of conditions and the following disclaimer in the 15195609Smp * documentation and/or other materials provided with the distribution. 16195609Smp * 4. Neither the name of the University nor the names of its contributors 17195609Smp * may be used to endorse or promote products derived from this software 18195609Smp * without specific prior written permission. 19195609Smp * 20195609Smp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21195609Smp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22195609Smp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23195609Smp * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24195609Smp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25195609Smp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26195609Smp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27195609Smp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28195609Smp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29195609Smp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30195609Smp * SUCH DAMAGE. 31195609Smp */ 32195609Smp 33195609Smp#ifndef lint 34195609Smp#if 0 35195609Smpstatic char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; 36195609Smp#endif 37195609Smp#endif /* not lint */ 38195609Smp#include <sys/cdefs.h> 39195609Smp__FBSDID("$FreeBSD: head/bin/sh/expand.c 206150 2010-04-03 22:04:44Z jilles $"); 40195609Smp 41195609Smp#include <sys/types.h> 42195609Smp#include <sys/time.h> 43195609Smp#include <sys/stat.h> 44195609Smp#include <errno.h> 45195609Smp#include <dirent.h> 46195609Smp#include <unistd.h> 47195609Smp#include <pwd.h> 48195609Smp#include <stdlib.h> 49195609Smp#include <limits.h> 50195609Smp#include <stdio.h> 51195609Smp#include <string.h> 52195609Smp 53195609Smp/* 54195609Smp * Routines to expand arguments to commands. We have to deal with 55195609Smp * backquotes, shell variables, and file metacharacters. 56195609Smp */ 57195609Smp 58195609Smp#include "shell.h" 59195609Smp#include "main.h" 60195609Smp#include "nodes.h" 61195609Smp#include "eval.h" 62195609Smp#include "expand.h" 63195609Smp#include "syntax.h" 64195609Smp#include "parser.h" 65195609Smp#include "jobs.h" 66195609Smp#include "options.h" 67195609Smp#include "var.h" 68195609Smp#include "input.h" 69195609Smp#include "output.h" 70195609Smp#include "memalloc.h" 71195609Smp#include "error.h" 72167465Smp#include "mystring.h" 73167465Smp#include "arith.h" 74167465Smp#include "show.h" 75167465Smp 76167465Smp/* 77167465Smp * Structure specifying which parts of the string should be searched 78167465Smp * for IFS characters. 79167465Smp */ 80167465Smp 81167465Smpstruct ifsregion { 82167465Smp struct ifsregion *next; /* next region in list */ 83167465Smp int begoff; /* offset of start of region */ 84167465Smp int endoff; /* offset of end of region */ 85167465Smp int inquotes; /* search for nul bytes only */ 86167465Smp}; 87167465Smp 88167465Smp 89167465SmpSTATIC char *expdest; /* output of current string */ 90167465SmpSTATIC struct nodelist *argbackq; /* list of back quote expressions */ 91167465SmpSTATIC struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 92167465SmpSTATIC struct ifsregion *ifslastp; /* last struct in list */ 93167465SmpSTATIC struct arglist exparg; /* holds expanded arg list */ 94167465Smp 95167465SmpSTATIC void argstr(char *, int); 96167465SmpSTATIC char *exptilde(char *, int); 97167465SmpSTATIC void expbackq(union node *, int, int); 98167465SmpSTATIC int subevalvar(char *, char *, int, int, int, int); 99167465SmpSTATIC char *evalvar(char *, int); 100167465SmpSTATIC int varisset(char *, int); 101167465SmpSTATIC void varvalue(char *, int, int, int); 102167465SmpSTATIC void recordregion(int, int, int); 103167465SmpSTATIC void removerecordregions(int); 104167465SmpSTATIC void ifsbreakup(char *, struct arglist *); 105167465SmpSTATIC void expandmeta(struct strlist *, int); 106167465SmpSTATIC void expmeta(char *, char *); 107167465SmpSTATIC void addfname(char *); 108167465SmpSTATIC struct strlist *expsort(struct strlist *); 109167465SmpSTATIC struct strlist *msort(struct strlist *, int); 110167465SmpSTATIC int pmatch(const char *, const char *, int); 111167465SmpSTATIC char *cvtnum(int, char *); 112167465SmpSTATIC int collate_range_cmp(int, int); 113167465Smp 114167465SmpSTATIC int 115167465Smpcollate_range_cmp(int c1, int c2) 116167465Smp{ 117167465Smp static char s1[2], s2[2]; 118167465Smp 119167465Smp s1[0] = c1; 120167465Smp s2[0] = c2; 121167465Smp return (strcoll(s1, s2)); 122167465Smp} 123167465Smp 124167465Smp/* 125167465Smp * Expand shell variables and backquotes inside a here document. 126167465Smp * union node *arg the document 127167465Smp * int fd; where to write the expanded version 128167465Smp */ 129167465Smp 130167465Smpvoid 131167465Smpexpandhere(union node *arg, int fd) 132167465Smp{ 133167465Smp herefd = fd; 134167465Smp expandarg(arg, (struct arglist *)NULL, 0); 135167465Smp xwrite(fd, stackblock(), expdest - stackblock()); 136167465Smp} 137167465Smp 138167465Smp 139167465Smp/* 140167465Smp * Perform variable substitution and command substitution on an argument, 141167465Smp * placing the resulting list of arguments in arglist. If EXP_FULL is true, 142167465Smp * perform splitting and file name expansion. When arglist is NULL, perform 143167465Smp * here document expansion. 144167465Smp */ 145167465Smp 146167465Smpvoid 147167465Smpexpandarg(union node *arg, struct arglist *arglist, int flag) 148167465Smp{ 149167465Smp struct strlist *sp; 150167465Smp char *p; 151167465Smp 152167465Smp argbackq = arg->narg.backquote; 153167465Smp STARTSTACKSTR(expdest); 154167465Smp ifsfirst.next = NULL; 155167465Smp ifslastp = NULL; 156167465Smp argstr(arg->narg.text, flag); 157167465Smp if (arglist == NULL) { 158167465Smp return; /* here document expanded */ 159167465Smp } 160167465Smp STPUTC('\0', expdest); 161167465Smp p = grabstackstr(expdest); 162167465Smp exparg.lastp = &exparg.list; 163167465Smp /* 164167465Smp * TODO - EXP_REDIR 165167465Smp */ 166167465Smp if (flag & EXP_FULL) { 167167465Smp ifsbreakup(p, &exparg); 168167465Smp *exparg.lastp = NULL; 169167465Smp exparg.lastp = &exparg.list; 170167465Smp expandmeta(exparg.list, flag); 171145479Smp } else { 172145479Smp if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 173145479Smp rmescapes(p); 174145479Smp sp = (struct strlist *)stalloc(sizeof (struct strlist)); 175145479Smp sp->text = p; 176145479Smp *exparg.lastp = sp; 177145479Smp exparg.lastp = &sp->next; 178145479Smp } 179145479Smp while (ifsfirst.next != NULL) { 180145479Smp struct ifsregion *ifsp; 181145479Smp INTOFF; 182145479Smp ifsp = ifsfirst.next->next; 183145479Smp ckfree(ifsfirst.next); 184145479Smp ifsfirst.next = ifsp; 185145479Smp INTON; 186145479Smp } 187145479Smp *exparg.lastp = NULL; 188145479Smp if (exparg.list) { 189145479Smp *arglist->lastp = exparg.list; 190145479Smp arglist->lastp = exparg.lastp; 191145479Smp } 192145479Smp} 193145479Smp 194145479Smp 195145479Smp 196145479Smp/* 197145479Smp * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 198145479Smp * characters to allow for further processing. Otherwise treat 199145479Smp * $@ like $* since no splitting will be performed. 200145479Smp */ 201145479Smp 202145479SmpSTATIC void 203145479Smpargstr(char *p, int flag) 204145479Smp{ 205145479Smp char c; 206145479Smp int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ 207145479Smp int firsteq = 1; 208145479Smp 209145479Smp if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 210145479Smp p = exptilde(p, flag); 211145479Smp for (;;) { 212145479Smp switch (c = *p++) { 213145479Smp case '\0': 214145479Smp case CTLENDVAR: /* ??? */ 215145479Smp goto breakloop; 216145479Smp case CTLQUOTEMARK: 217145479Smp /* "$@" syntax adherence hack */ 218145479Smp if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') 219145479Smp break; 220145479Smp if ((flag & EXP_FULL) != 0) 221145479Smp STPUTC(c, expdest); 222145479Smp break; 223145479Smp case CTLESC: 224145479Smp if (quotes) 225145479Smp STPUTC(c, expdest); 226145479Smp c = *p++; 227145479Smp STPUTC(c, expdest); 228145479Smp break; 229145479Smp case CTLVAR: 230145479Smp p = evalvar(p, flag); 231145479Smp break; 232145479Smp case CTLBACKQ: 233145479Smp case CTLBACKQ|CTLQUOTE: 234145479Smp expbackq(argbackq->n, c & CTLQUOTE, flag); 235145479Smp argbackq = argbackq->next; 236145479Smp break; 237145479Smp case CTLENDARI: 238145479Smp expari(flag); 239145479Smp break; 240145479Smp case ':': 241145479Smp case '=': 242145479Smp /* 243145479Smp * sort of a hack - expand tildes in variable 244145479Smp * assignments (after the first '=' and after ':'s). 245145479Smp */ 246145479Smp STPUTC(c, expdest); 247145479Smp if (flag & EXP_VARTILDE && *p == '~') { 248145479Smp if (c == '=') { 249145479Smp if (firsteq) 250145479Smp firsteq = 0; 251145479Smp else 252145479Smp break; 253145479Smp } 254145479Smp p = exptilde(p, flag); 255145479Smp } 256145479Smp break; 257145479Smp default: 258145479Smp STPUTC(c, expdest); 259145479Smp } 260145479Smp } 261131962Smpbreakloop:; 262131962Smp} 263131962Smp 264131962SmpSTATIC char * 265131962Smpexptilde(char *p, int flag) 266131962Smp{ 267131962Smp char c, *startp = p; 268131962Smp struct passwd *pw; 269131962Smp char *home; 270131962Smp int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 271131962Smp 272131962Smp while ((c = *p) != '\0') { 273131962Smp switch(c) { 274131962Smp case CTLESC: /* This means CTL* are always considered quoted. */ 275131962Smp case CTLVAR: 276131962Smp case CTLBACKQ: 277131962Smp case CTLBACKQ | CTLQUOTE: 278131962Smp case CTLARI: 279131962Smp case CTLENDARI: 280131962Smp case CTLQUOTEMARK: 281131962Smp return (startp); 282131962Smp case ':': 283131962Smp if (flag & EXP_VARTILDE) 284131962Smp goto done; 285131962Smp break; 286131962Smp case '/': 287131962Smp case CTLENDVAR: 288131962Smp goto done; 289131962Smp } 290131962Smp p++; 291131962Smp } 292131962Smpdone: 293131962Smp *p = '\0'; 294131962Smp if (*(startp+1) == '\0') { 295131962Smp if ((home = lookupvar("HOME")) == NULL) 296131962Smp goto lose; 297131962Smp } else { 298131962Smp if ((pw = getpwnam(startp+1)) == NULL) 299131962Smp goto lose; 300131962Smp home = pw->pw_dir; 301131962Smp } 302131962Smp if (*home == '\0') 303131962Smp goto lose; 304100616Smp *p = c; 305100616Smp while ((c = *home++) != '\0') { 306100616Smp if (quotes && SQSYNTAX[(int)c] == CCTL) 307100616Smp STPUTC(CTLESC, expdest); 308100616Smp STPUTC(c, expdest); 309100616Smp } 310100616Smp return (p); 311100616Smplose: 312100616Smp *p = c; 313100616Smp return (startp); 314100616Smp} 315100616Smp 316100616Smp 317100616SmpSTATIC void 318100616Smpremoverecordregions(int endoff) 319100616Smp{ 320100616Smp if (ifslastp == NULL) 321100616Smp return; 322100616Smp 323100616Smp if (ifsfirst.endoff > endoff) { 324100616Smp while (ifsfirst.next != NULL) { 325100616Smp struct ifsregion *ifsp; 326100616Smp INTOFF; 327100616Smp ifsp = ifsfirst.next->next; 328100616Smp ckfree(ifsfirst.next); 329100616Smp ifsfirst.next = ifsp; 330100616Smp INTON; 331100616Smp } 332100616Smp if (ifsfirst.begoff > endoff) 333100616Smp ifslastp = NULL; 334100616Smp else { 335100616Smp ifslastp = &ifsfirst; 336100616Smp ifsfirst.endoff = endoff; 337100616Smp } 338100616Smp return; 339100616Smp } 340100616Smp 341100616Smp ifslastp = &ifsfirst; 342100616Smp while (ifslastp->next && ifslastp->next->begoff < endoff) 343100616Smp ifslastp=ifslastp->next; 344100616Smp while (ifslastp->next != NULL) { 345100616Smp struct ifsregion *ifsp; 346100616Smp INTOFF; 347100616Smp ifsp = ifslastp->next->next; 348100616Smp ckfree(ifslastp->next); 349100616Smp ifslastp->next = ifsp; 350100616Smp INTON; 351100616Smp } 352100616Smp if (ifslastp->endoff > endoff) 353100616Smp ifslastp->endoff = endoff; 354100616Smp} 355100616Smp 356100616Smp/* 357100616Smp * Expand arithmetic expression. Backup to start of expression, 358100616Smp * evaluate, place result in (backed up) result, adjust string position. 359100616Smp */ 360100616Smpvoid 361100616Smpexpari(int flag) 362100616Smp{ 363100616Smp char *p, *start; 364100616Smp arith_t result; 365100616Smp int begoff; 366100616Smp int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 367100616Smp int quoted; 368100616Smp 369100616Smp 370100616Smp /* 37183098Smp * This routine is slightly over-complicated for 37283098Smp * efficiency. First we make sure there is 37383098Smp * enough space for the result, which may be bigger 37483098Smp * than the expression if we add exponentiation. Next we 37583098Smp * scan backwards looking for the start of arithmetic. If the 376100616Smp * next previous character is a CTLESC character, then we 37783098Smp * have to rescan starting from the beginning since CTLESC 37883098Smp * characters have to be processed left to right. 37983098Smp */ 38083098Smp CHECKSTRSPACE(DIGITS(result) - 2, expdest); 38183098Smp USTPUTC('\0', expdest); 38283098Smp start = stackblock(); 38383098Smp p = expdest - 2; 38483098Smp while (p >= start && *p != CTLARI) 38583098Smp --p; 38683098Smp if (p < start || *p != CTLARI) 38783098Smp error("missing CTLARI (shouldn't happen)"); 38883098Smp if (p > start && *(p - 1) == CTLESC) 38983098Smp for (p = start; *p != CTLARI; p++) 39083098Smp if (*p == CTLESC) 39183098Smp p++; 39283098Smp 39383098Smp if (p[1] == '"') 39483098Smp quoted=1; 39583098Smp else 39683098Smp quoted=0; 39783098Smp begoff = p - start; 39883098Smp removerecordregions(begoff); 39983098Smp if (quotes) 40083098Smp rmescapes(p+2); 40183098Smp result = arith(p+2); 40283098Smp fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result); 40383098Smp while (*p++) 40483098Smp ; 40583098Smp if (quoted == 0) 40683098Smp recordregion(begoff, p - 1 - start, 0); 40783098Smp result = expdest - p + 1; 40883098Smp STADJUST(-result, expdest); 40983098Smp} 41083098Smp 41183098Smp 41283098Smp/* 41383098Smp * Expand stuff in backwards quotes. 41483098Smp */ 41569408Sache 41669408SacheSTATIC void 41769408Sacheexpbackq(union node *cmd, int quoted, int flag) 41869408Sache{ 41969408Sache struct backcmd in; 42069408Sache int i; 42169408Sache char buf[128]; 42269408Sache char *p; 42369408Sache char *dest = expdest; 42469408Sache struct ifsregion saveifs, *savelastp; 42569408Sache struct nodelist *saveargbackq; 42669408Sache char lastc; 42769408Sache int startloc = dest - stackblock(); 42869408Sache char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 42969408Sache int saveherefd; 43069408Sache int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 43169408Sache int nnl; 43269408Sache 43369408Sache INTOFF; 43469408Sache saveifs = ifsfirst; 43569408Sache savelastp = ifslastp; 43669408Sache saveargbackq = argbackq; 43769408Sache saveherefd = herefd; 43869408Sache herefd = -1; 43969408Sache p = grabstackstr(dest); 44069408Sache evalbackcmd(cmd, &in); 44169408Sache ungrabstackstr(p, dest); 44269408Sache ifsfirst = saveifs; 44369408Sache ifslastp = savelastp; 44469408Sache argbackq = saveargbackq; 44569408Sache herefd = saveherefd; 44669408Sache 44769408Sache p = in.buf; 44869408Sache lastc = '\0'; 44969408Sache nnl = 0; 45069408Sache /* Don't copy trailing newlines */ 45169408Sache for (;;) { 45269408Sache if (--in.nleft < 0) { 45369408Sache if (in.fd < 0) 45469408Sache break; 45569408Sache while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 45669408Sache TRACE(("expbackq: read returns %d\n", i)); 45769408Sache if (i <= 0) 45869408Sache break; 45969408Sache p = buf; 46069408Sache in.nleft = i - 1; 46169408Sache } 46269408Sache lastc = *p++; 46369408Sache if (lastc != '\0') { 46469408Sache if (quotes && syntax[(int)lastc] == CCTL) 46569408Sache STPUTC(CTLESC, dest); 46669408Sache if (lastc == '\n') { 46769408Sache nnl++; 46869408Sache } else { 46969408Sache while (nnl > 0) { 47069408Sache nnl--; 47169408Sache STPUTC('\n', dest); 47269408Sache } 47359415Sobrien STPUTC(lastc, dest); 47459415Sobrien } 47569408Sache } 47669408Sache } 47769408Sache 47869408Sache if (in.fd >= 0) 47969408Sache close(in.fd); 48069408Sache if (in.buf) 48159243Sobrien ckfree(in.buf); 48259243Sobrien if (in.jp) 48359243Sobrien exitstatus = waitforjob(in.jp, (int *)NULL); 48459243Sobrien if (quoted == 0) 48559243Sobrien recordregion(startloc, dest - stackblock(), 0); 48659243Sobrien TRACE(("evalbackq: size=%d: \"%.*s\"\n", 48759243Sobrien (dest - stackblock()) - startloc, 48859243Sobrien (dest - stackblock()) - startloc, 48959243Sobrien stackblock() + startloc)); 49059243Sobrien expdest = dest; 49159243Sobrien INTON; 49259243Sobrien} 49359243Sobrien 49459243Sobrien 49559243Sobrien 49659243SobrienSTATIC int 49759243Sobriensubevalvar(char *p, char *str, int strloc, int subtype, int startloc, 49859243Sobrien int varflags) 49959243Sobrien{ 50059243Sobrien char *startp; 50159243Sobrien char *loc = NULL; 50259243Sobrien char *q; 50359243Sobrien int c = 0; 50459243Sobrien int saveherefd = herefd; 50559243Sobrien struct nodelist *saveargbackq = argbackq; 50659243Sobrien int amount; 50759243Sobrien 50859243Sobrien herefd = -1; 50959243Sobrien argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || 51059243Sobrien subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ? 51159243Sobrien EXP_CASE : 0) | EXP_TILDE); 51259243Sobrien STACKSTRNUL(expdest); 51359243Sobrien herefd = saveherefd; 51459243Sobrien argbackq = saveargbackq; 51559243Sobrien startp = stackblock() + startloc; 51659243Sobrien if (str == NULL) 51759243Sobrien str = stackblock() + strloc; 51859243Sobrien 51959243Sobrien switch (subtype) { 52059243Sobrien case VSASSIGN: 52159243Sobrien setvar(str, startp, 0); 52259243Sobrien amount = startp - expdest; 52359243Sobrien STADJUST(amount, expdest); 52459243Sobrien varflags &= ~VSNUL; 52559243Sobrien if (c != 0) 52659243Sobrien *loc = c; 52759243Sobrien return 1; 52859243Sobrien 52959243Sobrien case VSQUESTION: 53059243Sobrien if (*p != CTLENDVAR) { 53159243Sobrien outfmt(out2, "%s\n", startp); 53259243Sobrien error((char *)NULL); 53359243Sobrien } 53459243Sobrien error("%.*s: parameter %snot set", (int)(p - str - 1), 53559243Sobrien str, (varflags & VSNUL) ? "null or " 53659243Sobrien : nullstr); 53759243Sobrien return 0; 53859243Sobrien 53959243Sobrien case VSTRIMLEFT: 54059243Sobrien for (loc = startp; loc < str; loc++) { 54159243Sobrien c = *loc; 54259243Sobrien *loc = '\0'; 54359243Sobrien if (patmatch(str, startp, varflags & VSQUOTE)) { 54459243Sobrien *loc = c; 54559243Sobrien goto recordleft; 54659243Sobrien } 54759243Sobrien *loc = c; 54859243Sobrien if ((varflags & VSQUOTE) && *loc == CTLESC) 54959243Sobrien loc++; 55059243Sobrien } 55159243Sobrien return 0; 55259243Sobrien 55359243Sobrien case VSTRIMLEFTMAX: 55459243Sobrien for (loc = str - 1; loc >= startp;) { 55559243Sobrien c = *loc; 55659243Sobrien *loc = '\0'; 55759243Sobrien if (patmatch(str, startp, varflags & VSQUOTE)) { 55859243Sobrien *loc = c; 55959243Sobrien goto recordleft; 56059243Sobrien } 56159243Sobrien *loc = c; 56259243Sobrien loc--; 56359243Sobrien if ((varflags & VSQUOTE) && loc > startp && 56459243Sobrien *(loc - 1) == CTLESC) { 56559243Sobrien for (q = startp; q < loc; q++) 56659243Sobrien if (*q == CTLESC) 56759243Sobrien q++; 56859243Sobrien if (q > loc) 56959243Sobrien loc--; 57059243Sobrien } 57159243Sobrien } 57259243Sobrien return 0; 57359243Sobrien 57459243Sobrien case VSTRIMRIGHT: 57559243Sobrien for (loc = str - 1; loc >= startp;) { 57659243Sobrien if (patmatch(str, loc, varflags & VSQUOTE)) { 57759243Sobrien amount = loc - expdest; 57859243Sobrien STADJUST(amount, expdest); 57959243Sobrien return 1; 58059243Sobrien } 58159243Sobrien loc--; 58259243Sobrien if ((varflags & VSQUOTE) && loc > startp && 58359243Sobrien *(loc - 1) == CTLESC) { 58459243Sobrien for (q = startp; q < loc; q++) 58559243Sobrien if (*q == CTLESC) 58659243Sobrien q++; 58759243Sobrien if (q > loc) 58859243Sobrien loc--; 58959243Sobrien } 59059243Sobrien } 59159243Sobrien return 0; 59259243Sobrien 59359243Sobrien case VSTRIMRIGHTMAX: 59459243Sobrien for (loc = startp; loc < str - 1; loc++) { 59559243Sobrien if (patmatch(str, loc, varflags & VSQUOTE)) { 59659243Sobrien amount = loc - expdest; 59759243Sobrien STADJUST(amount, expdest); 59859243Sobrien return 1; 59959243Sobrien } 60059243Sobrien if ((varflags & VSQUOTE) && *loc == CTLESC) 60159243Sobrien loc++; 60259243Sobrien } 60359243Sobrien return 0; 60459243Sobrien 60559243Sobrien 60659243Sobrien default: 60759243Sobrien abort(); 60859243Sobrien } 60959243Sobrien 61059243Sobrienrecordleft: 61159243Sobrien amount = ((str - 1) - (loc - startp)) - expdest; 61259243Sobrien STADJUST(amount, expdest); 61359243Sobrien while (loc != str - 1) 61459243Sobrien *startp++ = *loc++; 61559243Sobrien return 1; 61659243Sobrien} 61759243Sobrien 61859243Sobrien 61959243Sobrien/* 62059243Sobrien * Expand a variable, and return a pointer to the next character in the 62159243Sobrien * input string. 62259243Sobrien */ 62359243Sobrien 62459243SobrienSTATIC char * 62559243Sobrienevalvar(char *p, int flag) 62659243Sobrien{ 62759243Sobrien int subtype; 62859243Sobrien int varflags; 62959243Sobrien char *var; 63059243Sobrien char *val; 63159243Sobrien int patloc; 63259243Sobrien int c; 63359243Sobrien int set; 63459243Sobrien int special; 63559243Sobrien int startloc; 63659243Sobrien int varlen; 63759243Sobrien int easy; 63859243Sobrien int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 63959243Sobrien 64059243Sobrien varflags = (unsigned char)*p++; 64159243Sobrien subtype = varflags & VSTYPE; 64259243Sobrien var = p; 64359243Sobrien special = 0; 64459243Sobrien if (! is_name(*p)) 64559243Sobrien special = 1; 64659243Sobrien p = strchr(p, '=') + 1; 64759243Sobrienagain: /* jump here after setting a variable with ${var=text} */ 64859243Sobrien if (varflags & VSLINENO) { 64959243Sobrien set = 1; 65059243Sobrien special = 0; 65159243Sobrien val = var; 65259243Sobrien p[-1] = '\0'; /* temporarily overwrite '=' to have \0 65359243Sobrien terminated string */ 65459243Sobrien } else if (special) { 65559243Sobrien set = varisset(var, varflags & VSNUL); 65659243Sobrien val = NULL; 65759243Sobrien } else { 65859243Sobrien val = bltinlookup(var, 1); 65959243Sobrien if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 66059243Sobrien val = NULL; 66159243Sobrien set = 0; 66259243Sobrien } else 66359243Sobrien set = 1; 66459243Sobrien } 66559243Sobrien varlen = 0; 66659243Sobrien startloc = expdest - stackblock(); 66759243Sobrien if (!set && uflag && *var != '@' && *var != '*') { 66859243Sobrien switch (subtype) { 66959243Sobrien case VSNORMAL: 67059243Sobrien case VSTRIMLEFT: 67159243Sobrien case VSTRIMLEFTMAX: 67259243Sobrien case VSTRIMRIGHT: 67359243Sobrien case VSTRIMRIGHTMAX: 67459243Sobrien case VSLENGTH: 67559243Sobrien error("%.*s: parameter not set", (int)(p - var - 1), 67659243Sobrien var); 67759243Sobrien } 67859243Sobrien } 67959243Sobrien if (set && subtype != VSPLUS) { 68059243Sobrien /* insert the value of the variable */ 68159243Sobrien if (special) { 68259243Sobrien varvalue(var, varflags & VSQUOTE, subtype, flag); 68359243Sobrien if (subtype == VSLENGTH) { 68459243Sobrien varlen = expdest - stackblock() - startloc; 68559243Sobrien STADJUST(-varlen, expdest); 68659243Sobrien } 68759243Sobrien } else { 68859243Sobrien char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 68959243Sobrien : BASESYNTAX; 69059243Sobrien 69159243Sobrien if (subtype == VSLENGTH) { 69259243Sobrien for (;*val; val++) 69359243Sobrien varlen++; 69459243Sobrien } 69559243Sobrien else { 69659243Sobrien while (*val) { 69759243Sobrien if (quotes && 69859243Sobrien syntax[(int)*val] == CCTL) 69959243Sobrien STPUTC(CTLESC, expdest); 70059243Sobrien STPUTC(*val++, expdest); 70159243Sobrien } 70259243Sobrien 70359243Sobrien } 70459243Sobrien } 70559243Sobrien } 70659243Sobrien 70759243Sobrien if (subtype == VSPLUS) 70859243Sobrien set = ! set; 70959243Sobrien 71059243Sobrien easy = ((varflags & VSQUOTE) == 0 || 71159243Sobrien (*var == '@' && shellparam.nparam != 1)); 71259243Sobrien 71359243Sobrien 71459243Sobrien switch (subtype) { 71559243Sobrien case VSLENGTH: 71659243Sobrien expdest = cvtnum(varlen, expdest); 71759243Sobrien goto record; 71859243Sobrien 71959243Sobrien case VSNORMAL: 72059243Sobrien if (!easy) 72159243Sobrien break; 72259243Sobrienrecord: 72359243Sobrien recordregion(startloc, expdest - stackblock(), 72459243Sobrien varflags & VSQUOTE); 72559243Sobrien break; 72659243Sobrien 72759243Sobrien case VSPLUS: 72859243Sobrien case VSMINUS: 72959243Sobrien if (!set) { 73059243Sobrien argstr(p, flag); 73159243Sobrien break; 73259243Sobrien } 73359243Sobrien if (easy) 73459243Sobrien goto record; 73559243Sobrien break; 73659243Sobrien 73759243Sobrien case VSTRIMLEFT: 73859243Sobrien case VSTRIMLEFTMAX: 73959243Sobrien case VSTRIMRIGHT: 74059243Sobrien case VSTRIMRIGHTMAX: 74159243Sobrien if (!set) 74259243Sobrien break; 74359243Sobrien /* 74459243Sobrien * Terminate the string and start recording the pattern 74559243Sobrien * right after it 74659243Sobrien */ 74759243Sobrien STPUTC('\0', expdest); 74859243Sobrien patloc = expdest - stackblock(); 74959243Sobrien if (subevalvar(p, NULL, patloc, subtype, 75059243Sobrien startloc, varflags) == 0) { 75159243Sobrien int amount = (expdest - stackblock() - patloc) + 1; 75259243Sobrien STADJUST(-amount, expdest); 75359243Sobrien } 75459243Sobrien /* Remove any recorded regions beyond start of variable */ 75559243Sobrien removerecordregions(startloc); 75659243Sobrien goto record; 75759243Sobrien 75859243Sobrien case VSASSIGN: 75959243Sobrien case VSQUESTION: 76059243Sobrien if (!set) { 76159243Sobrien if (subevalvar(p, var, 0, subtype, startloc, varflags)) { 76259243Sobrien varflags &= ~VSNUL; 76359243Sobrien /* 76459243Sobrien * Remove any recorded regions beyond 76559243Sobrien * start of variable 76659243Sobrien */ 76759243Sobrien removerecordregions(startloc); 76859243Sobrien goto again; 76959243Sobrien } 77059243Sobrien break; 77159243Sobrien } 77259243Sobrien if (easy) 77359243Sobrien goto record; 77459243Sobrien break; 77559243Sobrien 77659243Sobrien case VSERROR: 77759243Sobrien c = p - var - 1; 77859243Sobrien error("${%.*s%s}: Bad substitution", c, var, 77959243Sobrien (c > 0 && *p != CTLENDVAR) ? "..." : ""); 78059243Sobrien 78159243Sobrien default: 78259243Sobrien abort(); 78359243Sobrien } 78459243Sobrien p[-1] = '='; /* recover overwritten '=' */ 78559243Sobrien 78659243Sobrien if (subtype != VSNORMAL) { /* skip to end of alternative */ 78759243Sobrien int nesting = 1; 78859243Sobrien for (;;) { 78959243Sobrien if ((c = *p++) == CTLESC) 79059243Sobrien p++; 79159243Sobrien else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 79259243Sobrien if (set) 79359243Sobrien argbackq = argbackq->next; 79459243Sobrien } else if (c == CTLVAR) { 79559243Sobrien if ((*p++ & VSTYPE) != VSNORMAL) 79659243Sobrien nesting++; 79759243Sobrien } else if (c == CTLENDVAR) { 79859243Sobrien if (--nesting == 0) 79959243Sobrien break; 80059243Sobrien } 80159243Sobrien } 80259243Sobrien } 80359243Sobrien return p; 80459243Sobrien} 80559243Sobrien 80659243Sobrien 80759243Sobrien 80859243Sobrien/* 80959243Sobrien * Test whether a specialized variable is set. 81059243Sobrien */ 81159243Sobrien 81259243SobrienSTATIC int 81359243Sobrienvarisset(char *name, int nulok) 81459243Sobrien{ 81559243Sobrien 81659243Sobrien if (*name == '!') 81759243Sobrien return backgndpid != -1; 81859243Sobrien else if (*name == '@' || *name == '*') { 81959243Sobrien if (*shellparam.p == NULL) 82059243Sobrien return 0; 82159243Sobrien 82259243Sobrien if (nulok) { 82359243Sobrien char **av; 82459243Sobrien 82559243Sobrien for (av = shellparam.p; *av; av++) 82659243Sobrien if (**av != '\0') 82759243Sobrien return 1; 82859243Sobrien return 0; 82959243Sobrien } 83059243Sobrien } else if (is_digit(*name)) { 83159243Sobrien char *ap; 83259243Sobrien int num = atoi(name); 83359243Sobrien 83459243Sobrien if (num > shellparam.nparam) 83559243Sobrien return 0; 83659243Sobrien 83759243Sobrien if (num == 0) 83859243Sobrien ap = arg0; 83959243Sobrien else 84059243Sobrien ap = shellparam.p[num - 1]; 84159243Sobrien 84259243Sobrien if (nulok && (ap == NULL || *ap == '\0')) 84359243Sobrien return 0; 84459243Sobrien } 84559243Sobrien return 1; 84659243Sobrien} 84759243Sobrien 84859243Sobrien 84959243Sobrien 85059243Sobrien/* 85159243Sobrien * Add the value of a specialized variable to the stack string. 85259243Sobrien */ 85359243Sobrien 85459243SobrienSTATIC void 85559243Sobrienvarvalue(char *name, int quoted, int subtype, int flag) 85659243Sobrien{ 85759243Sobrien int num; 85859243Sobrien char *p; 85959243Sobrien int i; 86059243Sobrien char sep; 86159243Sobrien char **ap; 86259243Sobrien char const *syntax; 86359243Sobrien 86459243Sobrien#define STRTODEST(p) \ 86559243Sobrien do {\ 86659243Sobrien if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \ 86759243Sobrien syntax = quoted? DQSYNTAX : BASESYNTAX; \ 86859243Sobrien while (*p) { \ 86959243Sobrien if (syntax[(int)*p] == CCTL) \ 87059243Sobrien STPUTC(CTLESC, expdest); \ 87159243Sobrien STPUTC(*p++, expdest); \ 87259243Sobrien } \ 87359243Sobrien } else \ 87459243Sobrien while (*p) \ 87559243Sobrien STPUTC(*p++, expdest); \ 87659243Sobrien } while (0) 87759243Sobrien 87859243Sobrien 87959243Sobrien switch (*name) { 88059243Sobrien case '$': 88159243Sobrien num = rootpid; 88259243Sobrien goto numvar; 88359243Sobrien case '?': 88459243Sobrien num = oexitstatus; 88559243Sobrien goto numvar; 88659243Sobrien case '#': 88759243Sobrien num = shellparam.nparam; 88859243Sobrien goto numvar; 88959243Sobrien case '!': 89059243Sobrien num = backgndpid; 89159243Sobriennumvar: 89259243Sobrien expdest = cvtnum(num, expdest); 89359243Sobrien break; 89459243Sobrien case '-': 89559243Sobrien for (i = 0 ; i < NOPTS ; i++) { 89659243Sobrien if (optlist[i].val) 89759243Sobrien STPUTC(optlist[i].letter, expdest); 89859243Sobrien } 89959243Sobrien break; 90059243Sobrien case '@': 90159243Sobrien if (flag & EXP_FULL && quoted) { 90259243Sobrien for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 90359243Sobrien STRTODEST(p); 90459243Sobrien if (*ap) 90559243Sobrien STPUTC('\0', expdest); 90659243Sobrien } 90759243Sobrien break; 90859243Sobrien } 90959243Sobrien /* FALLTHROUGH */ 91059243Sobrien case '*': 91159243Sobrien if (ifsset()) 91259243Sobrien sep = ifsval()[0]; 91359243Sobrien else 91459243Sobrien sep = ' '; 91559243Sobrien for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 91659243Sobrien STRTODEST(p); 91759243Sobrien if (*ap && sep) 91859243Sobrien STPUTC(sep, expdest); 91959243Sobrien } 92059243Sobrien break; 92159243Sobrien case '0': 92259243Sobrien p = arg0; 92359243Sobrien STRTODEST(p); 92459243Sobrien break; 92559243Sobrien default: 92659243Sobrien if (is_digit(*name)) { 92759243Sobrien num = atoi(name); 92859243Sobrien if (num > 0 && num <= shellparam.nparam) { 92959243Sobrien p = shellparam.p[num - 1]; 93059243Sobrien STRTODEST(p); 93159243Sobrien } 93259243Sobrien } 93359243Sobrien break; 93459243Sobrien } 93559243Sobrien} 93659243Sobrien 93759243Sobrien 93859243Sobrien 93959243Sobrien/* 94059243Sobrien * Record the the fact that we have to scan this region of the 94159243Sobrien * string for IFS characters. 94259243Sobrien */ 94359243Sobrien 94459243SobrienSTATIC void 94559243Sobrienrecordregion(int start, int end, int inquotes) 94659243Sobrien{ 94759243Sobrien struct ifsregion *ifsp; 94859243Sobrien 94959243Sobrien if (ifslastp == NULL) { 95059243Sobrien ifsp = &ifsfirst; 95159243Sobrien } else { 95259243Sobrien if (ifslastp->endoff == start 95359243Sobrien && ifslastp->inquotes == inquotes) { 95459243Sobrien /* extend previous area */ 95559243Sobrien ifslastp->endoff = end; 95659243Sobrien return; 95759243Sobrien } 95859243Sobrien ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 95959243Sobrien ifslastp->next = ifsp; 96059243Sobrien } 96159243Sobrien ifslastp = ifsp; 96259243Sobrien ifslastp->next = NULL; 96359243Sobrien ifslastp->begoff = start; 96459243Sobrien ifslastp->endoff = end; 96559243Sobrien ifslastp->inquotes = inquotes; 96659243Sobrien} 96759243Sobrien 96859243Sobrien 96959243Sobrien 97059243Sobrien/* 97159243Sobrien * Break the argument string into pieces based upon IFS and add the 97259243Sobrien * strings to the argument list. The regions of the string to be 97359243Sobrien * searched for IFS characters have been stored by recordregion. 97459243Sobrien */ 97559243SobrienSTATIC void 97659243Sobrienifsbreakup(char *string, struct arglist *arglist) 97759243Sobrien{ 97859243Sobrien struct ifsregion *ifsp; 97959243Sobrien struct strlist *sp; 98059243Sobrien char *start; 98159243Sobrien char *p; 98259243Sobrien char *q; 98359243Sobrien const char *ifs; 98459243Sobrien const char *ifsspc; 98559243Sobrien int had_param_ch = 0; 98659243Sobrien 98759243Sobrien start = string; 98859243Sobrien 98959243Sobrien if (ifslastp == NULL) { 99059243Sobrien /* Return entire argument, IFS doesn't apply to any of it */ 99159243Sobrien sp = (struct strlist *)stalloc(sizeof *sp); 99259243Sobrien sp->text = start; 99359243Sobrien *arglist->lastp = sp; 99459243Sobrien arglist->lastp = &sp->next; 99559243Sobrien return; 99659243Sobrien } 99759243Sobrien 99859243Sobrien ifs = ifsset() ? ifsval() : " \t\n"; 99959243Sobrien 100059243Sobrien for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { 100159243Sobrien p = string + ifsp->begoff; 100259243Sobrien while (p < string + ifsp->endoff) { 100359243Sobrien q = p; 100459243Sobrien if (*p == CTLESC) 100559243Sobrien p++; 100659243Sobrien if (ifsp->inquotes) { 100759243Sobrien /* Only NULs (should be from "$@") end args */ 100859243Sobrien had_param_ch = 1; 100959243Sobrien if (*p != 0) { 101059243Sobrien p++; 101159243Sobrien continue; 101259243Sobrien } 101359243Sobrien ifsspc = NULL; 101459243Sobrien } else { 101559243Sobrien if (!strchr(ifs, *p)) { 101659243Sobrien had_param_ch = 1; 101759243Sobrien p++; 101859243Sobrien continue; 101959243Sobrien } 102059243Sobrien ifsspc = strchr(" \t\n", *p); 102159243Sobrien 102259243Sobrien /* Ignore IFS whitespace at start */ 102359243Sobrien if (q == start && ifsspc != NULL) { 102459243Sobrien p++; 102559243Sobrien start = p; 102659243Sobrien continue; 102759243Sobrien } 102859243Sobrien had_param_ch = 0; 102959243Sobrien } 103059243Sobrien 103159243Sobrien /* Save this argument... */ 103259243Sobrien *q = '\0'; 103359243Sobrien sp = (struct strlist *)stalloc(sizeof *sp); 103459243Sobrien sp->text = start; 103559243Sobrien *arglist->lastp = sp; 103659243Sobrien arglist->lastp = &sp->next; 103759243Sobrien p++; 103859243Sobrien 103959243Sobrien if (ifsspc != NULL) { 104059243Sobrien /* Ignore further trailing IFS whitespace */ 104159243Sobrien for (; p < string + ifsp->endoff; p++) { 104259243Sobrien q = p; 104359243Sobrien if (*p == CTLESC) 104459243Sobrien p++; 104559243Sobrien if (strchr(ifs, *p) == NULL) { 104659243Sobrien p = q; 104759243Sobrien break; 104859243Sobrien } 104959243Sobrien if (strchr(" \t\n", *p) == NULL) { 105059243Sobrien p++; 105159243Sobrien break; 105259243Sobrien } 105359243Sobrien } 105459243Sobrien } 105559243Sobrien start = p; 105659243Sobrien } 105759243Sobrien } 105859243Sobrien 105959243Sobrien /* 106059243Sobrien * Save anything left as an argument. 106159243Sobrien * Traditionally we have treated 'IFS=':'; set -- x$IFS' as 106259243Sobrien * generating 2 arguments, the second of which is empty. 106359243Sobrien * Some recent clarification of the Posix spec say that it 106459243Sobrien * should only generate one.... 106559243Sobrien */ 106659243Sobrien if (had_param_ch || *start != 0) { 106759243Sobrien sp = (struct strlist *)stalloc(sizeof *sp); 106859243Sobrien sp->text = start; 106959243Sobrien *arglist->lastp = sp; 107059243Sobrien arglist->lastp = &sp->next; 107159243Sobrien } 107259243Sobrien} 107359243Sobrien 107459243Sobrien 107559243Sobrien 107659243Sobrien/* 107759243Sobrien * Expand shell metacharacters. At this point, the only control characters 107859243Sobrien * should be escapes. The results are stored in the list exparg. 107959243Sobrien */ 108059243Sobrien 108159243SobrienSTATIC char *expdir; 108259243Sobrien 108359243Sobrien 108459243SobrienSTATIC void 108559243Sobrienexpandmeta(struct strlist *str, int flag __unused) 108659243Sobrien{ 108759243Sobrien char *p; 108859243Sobrien struct strlist **savelastp; 108959243Sobrien struct strlist *sp; 109059243Sobrien char c; 109159243Sobrien /* TODO - EXP_REDIR */ 109259243Sobrien 109359243Sobrien while (str) { 109459243Sobrien if (fflag) 109559243Sobrien goto nometa; 109659243Sobrien p = str->text; 109759243Sobrien for (;;) { /* fast check for meta chars */ 109859243Sobrien if ((c = *p++) == '\0') 109959243Sobrien goto nometa; 110059243Sobrien if (c == '*' || c == '?' || c == '[' || c == '!') 110159243Sobrien break; 110259243Sobrien } 110359243Sobrien savelastp = exparg.lastp; 110459243Sobrien INTOFF; 110559243Sobrien if (expdir == NULL) { 110659243Sobrien int i = strlen(str->text); 110759243Sobrien expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 110859243Sobrien } 110959243Sobrien 111059243Sobrien expmeta(expdir, str->text); 111159243Sobrien ckfree(expdir); 111259243Sobrien expdir = NULL; 111359243Sobrien INTON; 111459243Sobrien if (exparg.lastp == savelastp) { 111559243Sobrien /* 111659243Sobrien * no matches 111759243Sobrien */ 111859243Sobriennometa: 111959243Sobrien *exparg.lastp = str; 112059243Sobrien rmescapes(str->text); 112159243Sobrien exparg.lastp = &str->next; 112259243Sobrien } else { 112359243Sobrien *exparg.lastp = NULL; 112459243Sobrien *savelastp = sp = expsort(*savelastp); 112559243Sobrien while (sp->next != NULL) 112659243Sobrien sp = sp->next; 112759243Sobrien exparg.lastp = &sp->next; 112859243Sobrien } 112959243Sobrien str = str->next; 113059243Sobrien } 113159243Sobrien} 113259243Sobrien 113359243Sobrien 113459243Sobrien/* 113559243Sobrien * Do metacharacter (i.e. *, ?, [...]) expansion. 113659243Sobrien */ 113759243Sobrien 113859243SobrienSTATIC void 113959243Sobrienexpmeta(char *enddir, char *name) 114059243Sobrien{ 114159243Sobrien char *p; 114259243Sobrien char *q; 114359243Sobrien char *start; 114459243Sobrien char *endname; 114559243Sobrien int metaflag; 114659243Sobrien struct stat statb; 114759243Sobrien DIR *dirp; 114859243Sobrien struct dirent *dp; 114959243Sobrien int atend; 115059243Sobrien int matchdot; 115159243Sobrien 115259243Sobrien metaflag = 0; 115359243Sobrien start = name; 115459243Sobrien for (p = name ; ; p++) { 115559243Sobrien if (*p == '*' || *p == '?') 115659243Sobrien metaflag = 1; 115759243Sobrien else if (*p == '[') { 115859243Sobrien q = p + 1; 115959243Sobrien if (*q == '!' || *q == '^') 116059243Sobrien q++; 116159243Sobrien for (;;) { 116259243Sobrien while (*q == CTLQUOTEMARK) 116359243Sobrien q++; 116459243Sobrien if (*q == CTLESC) 116559243Sobrien q++; 116659243Sobrien if (*q == '/' || *q == '\0') 116759243Sobrien break; 116859243Sobrien if (*++q == ']') { 116959243Sobrien metaflag = 1; 117059243Sobrien break; 117159243Sobrien } 117259243Sobrien } 117359243Sobrien } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 117459243Sobrien metaflag = 1; 117559243Sobrien } else if (*p == '\0') 117659243Sobrien break; 117759243Sobrien else if (*p == CTLQUOTEMARK) 117859243Sobrien continue; 117959243Sobrien else if (*p == CTLESC) 118059243Sobrien p++; 118159243Sobrien if (*p == '/') { 118259243Sobrien if (metaflag) 118359243Sobrien break; 118459243Sobrien start = p + 1; 118559243Sobrien } 118659243Sobrien } 118759243Sobrien if (metaflag == 0) { /* we've reached the end of the file name */ 118859243Sobrien if (enddir != expdir) 118959243Sobrien metaflag++; 119059243Sobrien for (p = name ; ; p++) { 119159243Sobrien if (*p == CTLQUOTEMARK) 119259243Sobrien continue; 119359243Sobrien if (*p == CTLESC) 119459243Sobrien p++; 119559243Sobrien *enddir++ = *p; 119659243Sobrien if (*p == '\0') 119759243Sobrien break; 119859243Sobrien } 119959243Sobrien if (metaflag == 0 || lstat(expdir, &statb) >= 0) 120059243Sobrien addfname(expdir); 120159243Sobrien return; 120259243Sobrien } 120359243Sobrien endname = p; 120459243Sobrien if (start != name) { 120559243Sobrien p = name; 120659243Sobrien while (p < start) { 120759243Sobrien while (*p == CTLQUOTEMARK) 120859243Sobrien p++; 120959243Sobrien if (*p == CTLESC) 121059243Sobrien p++; 121159243Sobrien *enddir++ = *p++; 121259243Sobrien } 121359243Sobrien } 121459243Sobrien if (enddir == expdir) { 121559243Sobrien p = "."; 121659243Sobrien } else if (enddir == expdir + 1 && *expdir == '/') { 121759243Sobrien p = "/"; 121859243Sobrien } else { 121959243Sobrien p = expdir; 122059243Sobrien enddir[-1] = '\0'; 122159243Sobrien } 122259243Sobrien if ((dirp = opendir(p)) == NULL) 122359243Sobrien return; 122459243Sobrien if (enddir != expdir) 122559243Sobrien enddir[-1] = '/'; 122659243Sobrien if (*endname == 0) { 122759243Sobrien atend = 1; 122859243Sobrien } else { 122959243Sobrien atend = 0; 123059243Sobrien *endname++ = '\0'; 123159243Sobrien } 123259243Sobrien matchdot = 0; 123359243Sobrien p = start; 123459243Sobrien while (*p == CTLQUOTEMARK) 123559243Sobrien p++; 123659243Sobrien if (*p == CTLESC) 123759243Sobrien p++; 123859243Sobrien if (*p == '.') 123959243Sobrien matchdot++; 124059243Sobrien while (! int_pending() && (dp = readdir(dirp)) != NULL) { 124159243Sobrien if (dp->d_name[0] == '.' && ! matchdot) 124259243Sobrien continue; 124359243Sobrien if (patmatch(start, dp->d_name, 0)) { 124459243Sobrien if (atend) { 124559243Sobrien scopy(dp->d_name, enddir); 124659243Sobrien addfname(expdir); 124759243Sobrien } else { 124859243Sobrien for (p = enddir, q = dp->d_name; 124959243Sobrien (*p++ = *q++) != '\0';) 125059243Sobrien continue; 125159243Sobrien p[-1] = '/'; 125259243Sobrien expmeta(p, endname); 125359243Sobrien } 125459243Sobrien } 125559243Sobrien } 125659243Sobrien closedir(dirp); 125759243Sobrien if (! atend) 125859243Sobrien endname[-1] = '/'; 125959243Sobrien} 126059243Sobrien 126159243Sobrien 126259243Sobrien/* 126359243Sobrien * Add a file name to the list. 126459243Sobrien */ 126559243Sobrien 126659243SobrienSTATIC void 126759243Sobrienaddfname(char *name) 126859243Sobrien{ 126959243Sobrien char *p; 127059243Sobrien struct strlist *sp; 127159243Sobrien 127259243Sobrien p = stalloc(strlen(name) + 1); 127359243Sobrien scopy(name, p); 127459243Sobrien sp = (struct strlist *)stalloc(sizeof *sp); 127559243Sobrien sp->text = p; 127659243Sobrien *exparg.lastp = sp; 127759243Sobrien exparg.lastp = &sp->next; 127859243Sobrien} 127959243Sobrien 128059243Sobrien 128159243Sobrien/* 128259243Sobrien * Sort the results of file name expansion. It calculates the number of 128359243Sobrien * strings to sort and then calls msort (short for merge sort) to do the 128459243Sobrien * work. 128559243Sobrien */ 128659243Sobrien 128759243SobrienSTATIC struct strlist * 128859243Sobrienexpsort(struct strlist *str) 128959243Sobrien{ 129059243Sobrien int len; 129159243Sobrien struct strlist *sp; 129259243Sobrien 129359243Sobrien len = 0; 129459243Sobrien for (sp = str ; sp ; sp = sp->next) 129559243Sobrien len++; 129659243Sobrien return msort(str, len); 129759243Sobrien} 129859243Sobrien 129959243Sobrien 130059243SobrienSTATIC struct strlist * 130159243Sobrienmsort(struct strlist *list, int len) 130259243Sobrien{ 130359243Sobrien struct strlist *p, *q = NULL; 130459243Sobrien struct strlist **lpp; 130559243Sobrien int half; 130659243Sobrien int n; 130759243Sobrien 130859243Sobrien if (len <= 1) 130959243Sobrien return list; 131059243Sobrien half = len >> 1; 131159243Sobrien p = list; 131259243Sobrien for (n = half ; --n >= 0 ; ) { 131359243Sobrien q = p; 131459243Sobrien p = p->next; 131559243Sobrien } 131659243Sobrien q->next = NULL; /* terminate first half of list */ 131759243Sobrien q = msort(list, half); /* sort first half of list */ 131859243Sobrien p = msort(p, len - half); /* sort second half */ 131959243Sobrien lpp = &list; 132059243Sobrien for (;;) { 132159243Sobrien if (strcmp(p->text, q->text) < 0) { 132259243Sobrien *lpp = p; 132359243Sobrien lpp = &p->next; 132459243Sobrien if ((p = *lpp) == NULL) { 132559243Sobrien *lpp = q; 132659243Sobrien break; 132759243Sobrien } 132859243Sobrien } else { 132959243Sobrien *lpp = q; 133059243Sobrien lpp = &q->next; 133159243Sobrien if ((q = *lpp) == NULL) { 133259243Sobrien *lpp = p; 133359243Sobrien break; 133459243Sobrien } 133559243Sobrien } 133659243Sobrien } 133759243Sobrien return list; 133859243Sobrien} 133959243Sobrien 134059243Sobrien 134159243Sobrien 134259243Sobrien/* 134359243Sobrien * Returns true if the pattern matches the string. 134459243Sobrien */ 134559243Sobrien 134659243Sobrienint 134759243Sobrienpatmatch(const char *pattern, const char *string, int squoted) 134859243Sobrien{ 134959243Sobrien#ifdef notdef 135059243Sobrien if (pattern[0] == '!' && pattern[1] == '!') 135159243Sobrien return 1 - pmatch(pattern + 2, string); 135259243Sobrien else 135359243Sobrien#endif 135459243Sobrien return pmatch(pattern, string, squoted); 135559243Sobrien} 135659243Sobrien 135759243Sobrien 135859243SobrienSTATIC int 135959243Sobrienpmatch(const char *pattern, const char *string, int squoted) 136059243Sobrien{ 136159243Sobrien const char *p, *q; 136259243Sobrien char c; 136359243Sobrien 136459243Sobrien p = pattern; 136559243Sobrien q = string; 136659243Sobrien for (;;) { 136759243Sobrien switch (c = *p++) { 136859243Sobrien case '\0': 136959243Sobrien goto breakloop; 137059243Sobrien case CTLESC: 137159243Sobrien if (squoted && *q == CTLESC) 137259243Sobrien q++; 137359243Sobrien if (*q++ != *p++) 137459243Sobrien return 0; 137559243Sobrien break; 137659243Sobrien case CTLQUOTEMARK: 137759243Sobrien continue; 137859243Sobrien case '?': 137959243Sobrien if (squoted && *q == CTLESC) 138059243Sobrien q++; 138159243Sobrien if (*q++ == '\0') 138259243Sobrien return 0; 138359243Sobrien break; 138459243Sobrien case '*': 138559243Sobrien c = *p; 138659243Sobrien while (c == CTLQUOTEMARK || c == '*') 138759243Sobrien c = *++p; 138859243Sobrien if (c != CTLESC && c != CTLQUOTEMARK && 138959243Sobrien c != '?' && c != '*' && c != '[') { 139059243Sobrien while (*q != c) { 139159243Sobrien if (squoted && *q == CTLESC && 139259243Sobrien q[1] == c) 139359243Sobrien break; 139459243Sobrien if (*q == '\0') 139559243Sobrien return 0; 139659243Sobrien if (squoted && *q == CTLESC) 139759243Sobrien q++; 139859243Sobrien q++; 139959243Sobrien } 140059243Sobrien } 140159243Sobrien do { 140259243Sobrien if (pmatch(p, q, squoted)) 140359243Sobrien return 1; 140459243Sobrien if (squoted && *q == CTLESC) 140559243Sobrien q++; 140659243Sobrien } while (*q++ != '\0'); 140759243Sobrien return 0; 140859243Sobrien case '[': { 140959243Sobrien const char *endp; 141059243Sobrien int invert, found; 141159243Sobrien char chr; 141259243Sobrien 141359243Sobrien endp = p; 141459243Sobrien if (*endp == '!' || *endp == '^') 141559243Sobrien endp++; 141659243Sobrien for (;;) { 141759243Sobrien while (*endp == CTLQUOTEMARK) 141859243Sobrien endp++; 141959243Sobrien if (*endp == '\0') 142059243Sobrien goto dft; /* no matching ] */ 142159243Sobrien if (*endp == CTLESC) 142259243Sobrien endp++; 142359243Sobrien if (*++endp == ']') 142459243Sobrien break; 142559243Sobrien } 142659243Sobrien invert = 0; 142759243Sobrien if (*p == '!' || *p == '^') { 142859243Sobrien invert++; 142959243Sobrien p++; 143059243Sobrien } 143159243Sobrien found = 0; 143259243Sobrien chr = *q++; 143359243Sobrien if (squoted && chr == CTLESC) 143459243Sobrien chr = *q++; 143559243Sobrien if (chr == '\0') 143659243Sobrien return 0; 143759243Sobrien c = *p++; 143859243Sobrien do { 143959243Sobrien if (c == CTLQUOTEMARK) 144059243Sobrien continue; 144159243Sobrien if (c == CTLESC) 144259243Sobrien c = *p++; 144359243Sobrien if (*p == '-' && p[1] != ']') { 144459243Sobrien p++; 144559243Sobrien while (*p == CTLQUOTEMARK) 144659243Sobrien p++; 144759243Sobrien if (*p == CTLESC) 144859243Sobrien p++; 144959243Sobrien if ( collate_range_cmp(chr, c) >= 0 145059243Sobrien && collate_range_cmp(chr, *p) <= 0 145159243Sobrien ) 145259243Sobrien found = 1; 145359243Sobrien p++; 145459243Sobrien } else { 145559243Sobrien if (chr == c) 145659243Sobrien found = 1; 145759243Sobrien } 145859243Sobrien } while ((c = *p++) != ']'); 145959243Sobrien if (found == invert) 146059243Sobrien return 0; 146159243Sobrien break; 146259243Sobrien } 146359243Sobriendft: default: 146459243Sobrien if (squoted && *q == CTLESC) 146559243Sobrien q++; 146659243Sobrien if (*q++ != c) 146759243Sobrien return 0; 146859243Sobrien break; 146959243Sobrien } 147059243Sobrien } 147159243Sobrienbreakloop: 147259243Sobrien if (*q != '\0') 147359243Sobrien return 0; 147459243Sobrien return 1; 147559243Sobrien} 147659243Sobrien 147759243Sobrien 147859243Sobrien 147959243Sobrien/* 148059243Sobrien * Remove any CTLESC characters from a string. 148159243Sobrien */ 148259243Sobrien 148359243Sobrienvoid 148459243Sobrienrmescapes(char *str) 148559243Sobrien{ 148659243Sobrien char *p, *q; 148759243Sobrien 148859243Sobrien p = str; 148959243Sobrien while (*p != CTLESC && *p != CTLQUOTEMARK) { 149059243Sobrien if (*p++ == '\0') 149159243Sobrien return; 149259243Sobrien } 149359243Sobrien q = p; 149459243Sobrien while (*p) { 149559243Sobrien if (*p == CTLQUOTEMARK) { 149659243Sobrien p++; 149759243Sobrien continue; 149859243Sobrien } 149959243Sobrien if (*p == CTLESC) 150059243Sobrien p++; 150159243Sobrien *q++ = *p++; 150259243Sobrien } 150359243Sobrien *q = '\0'; 150459243Sobrien} 150559243Sobrien 150659243Sobrien 150759243Sobrien 150859243Sobrien/* 150959243Sobrien * See if a pattern matches in a case statement. 151059243Sobrien */ 151159243Sobrien 151259243Sobrienint 151359243Sobriencasematch(union node *pattern, const char *val) 151459243Sobrien{ 151559243Sobrien struct stackmark smark; 151659243Sobrien int result; 151759243Sobrien char *p; 151859243Sobrien 151959243Sobrien setstackmark(&smark); 152059243Sobrien argbackq = pattern->narg.backquote; 152159243Sobrien STARTSTACKSTR(expdest); 152259243Sobrien ifslastp = NULL; 152359243Sobrien argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 152459243Sobrien STPUTC('\0', expdest); 152559243Sobrien p = grabstackstr(expdest); 152659243Sobrien result = patmatch(p, val, 0); 152759243Sobrien popstackmark(&smark); 152859243Sobrien return result; 152959243Sobrien} 153059243Sobrien 153159243Sobrien/* 153259243Sobrien * Our own itoa(). 153359243Sobrien */ 153459243Sobrien 153559243SobrienSTATIC char * 153659243Sobriencvtnum(int num, char *buf) 153759243Sobrien{ 153859243Sobrien char temp[32]; 153959243Sobrien int neg = num < 0; 154059243Sobrien char *p = temp + 31; 154159243Sobrien 154259243Sobrien temp[31] = '\0'; 154359243Sobrien 154459243Sobrien do { 154559243Sobrien *--p = num % 10 + '0'; 154659243Sobrien } while ((num /= 10) != 0); 154759243Sobrien 154859243Sobrien if (neg) 154959243Sobrien *--p = '-'; 155059243Sobrien 155159243Sobrien while (*p) 155259243Sobrien STPUTC(*p++, buf); 155359243Sobrien return buf; 155459243Sobrien} 155559243Sobrien 155659243Sobrien/* 155759243Sobrien * Do most of the work for wordexp(3). 155859243Sobrien */ 155959243Sobrien 156059243Sobrienint 156159243Sobrienwordexpcmd(int argc, char **argv) 156259243Sobrien{ 156359243Sobrien size_t len; 156459243Sobrien int i; 156559243Sobrien 156659243Sobrien out1fmt("%08x", argc - 1); 156759243Sobrien for (i = 1, len = 0; i < argc; i++) 156859243Sobrien len += strlen(argv[i]); 156959243Sobrien out1fmt("%08x", (int)len); 157059243Sobrien for (i = 1; i < argc; i++) { 157159243Sobrien out1str(argv[i]); 157259243Sobrien out1c('\0'); 157359243Sobrien } 157459243Sobrien return (0); 157559243Sobrien} 157659243Sobrien