expand.c revision 194975
1235723Sbapt/*- 2235723Sbapt * Copyright (c) 1991, 1993 3235723Sbapt * The Regents of the University of California. All rights reserved. 4235723Sbapt * 5235723Sbapt * This code is derived from software contributed to Berkeley by 6235723Sbapt * Kenneth Almquist. 7235723Sbapt * 8262960Sjmmv * Redistribution and use in source and binary forms, with or without 9235723Sbapt * modification, are permitted provided that the following conditions 10235723Sbapt * are met: 11235723Sbapt * 1. Redistributions of source code must retain the above copyright 12235723Sbapt * notice, this list of conditions and the following disclaimer. 13235723Sbapt * 2. Redistributions in binary form must reproduce the above copyright 14235723Sbapt * notice, this list of conditions and the following disclaimer in the 15235723Sbapt * documentation and/or other materials provided with the distribution. 16235723Sbapt * 4. Neither the name of the University nor the names of its contributors 17235723Sbapt * may be used to endorse or promote products derived from this software 18235723Sbapt * without specific prior written permission. 19235723Sbapt * 20235723Sbapt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21235723Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22235723Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23235723Sbapt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24235723Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25235723Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26235723Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27235723Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28235723Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29235723Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30235723Sbapt * SUCH DAMAGE. 31235723Sbapt */ 32235723Sbapt 33235723Sbapt#ifndef lint 34235723Sbapt#if 0 35235723Sbaptstatic char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; 36235723Sbapt#endif 37235723Sbapt#endif /* not lint */ 38235723Sbapt#include <sys/cdefs.h> 39235723Sbapt__FBSDID("$FreeBSD: head/bin/sh/expand.c 194975 2009-06-25 17:10:51Z jilles $"); 40235723Sbapt 41235723Sbapt#include <sys/types.h> 42235723Sbapt#include <sys/time.h> 43235723Sbapt#include <sys/stat.h> 44235723Sbapt#include <errno.h> 45235723Sbapt#include <dirent.h> 46235723Sbapt#include <unistd.h> 47235723Sbapt#include <pwd.h> 48235723Sbapt#include <stdlib.h> 49235723Sbapt#include <limits.h> 50235723Sbapt#include <stdio.h> 51235723Sbapt#include <string.h> 52235723Sbapt 53235723Sbapt/* 54235723Sbapt * Routines to expand arguments to commands. We have to deal with 55235723Sbapt * backquotes, shell variables, and file metacharacters. 56235723Sbapt */ 57235723Sbapt 58235723Sbapt#include "shell.h" 59235723Sbapt#include "main.h" 60235723Sbapt#include "nodes.h" 61235723Sbapt#include "eval.h" 62235723Sbapt#include "expand.h" 63235723Sbapt#include "syntax.h" 64235723Sbapt#include "parser.h" 65235723Sbapt#include "jobs.h" 66235723Sbapt#include "options.h" 67235723Sbapt#include "var.h" 68235723Sbapt#include "input.h" 69235723Sbapt#include "output.h" 70235723Sbapt#include "memalloc.h" 71235723Sbapt#include "error.h" 72235723Sbapt#include "mystring.h" 73235723Sbapt#include "arith.h" 74235723Sbapt#include "show.h" 75235723Sbapt 76235723Sbapt/* 77235723Sbapt * Structure specifying which parts of the string should be searched 78235723Sbapt * for IFS characters. 79235723Sbapt */ 80235723Sbapt 81235723Sbaptstruct ifsregion { 82235723Sbapt struct ifsregion *next; /* next region in list */ 83235723Sbapt int begoff; /* offset of start of region */ 84235723Sbapt int endoff; /* offset of end of region */ 85235723Sbapt int inquotes; /* search for nul bytes only */ 86235723Sbapt}; 87235723Sbapt 88235723Sbapt 89235723SbaptSTATIC char *expdest; /* output of current string */ 90235723SbaptSTATIC struct nodelist *argbackq; /* list of back quote expressions */ 91235723SbaptSTATIC struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 92235723SbaptSTATIC struct ifsregion *ifslastp; /* last struct in list */ 93235723SbaptSTATIC struct arglist exparg; /* holds expanded arg list */ 94235723Sbapt 95235723SbaptSTATIC void argstr(char *, int); 96235723SbaptSTATIC char *exptilde(char *, int); 97235723SbaptSTATIC void expbackq(union node *, int, int); 98235723SbaptSTATIC int subevalvar(char *, char *, int, int, int, int); 99235723SbaptSTATIC char *evalvar(char *, int); 100235723SbaptSTATIC int varisset(char *, int); 101235723SbaptSTATIC void varvalue(char *, int, int, int); 102235723SbaptSTATIC void recordregion(int, int, int); 103235723SbaptSTATIC void removerecordregions(int); 104235723SbaptSTATIC void ifsbreakup(char *, struct arglist *); 105235723SbaptSTATIC void expandmeta(struct strlist *, int); 106235723SbaptSTATIC void expmeta(char *, char *); 107235723SbaptSTATIC void addfname(char *); 108235723SbaptSTATIC struct strlist *expsort(struct strlist *); 109235723SbaptSTATIC struct strlist *msort(struct strlist *, int); 110235723SbaptSTATIC int pmatch(char *, char *, int); 111235723SbaptSTATIC char *cvtnum(int, char *); 112235723SbaptSTATIC int collate_range_cmp(int, int); 113235723Sbapt 114235723SbaptSTATIC int 115235723Sbaptcollate_range_cmp(int c1, int c2) 116235723Sbapt{ 117235723Sbapt static char s1[2], s2[2]; 118235723Sbapt 119235723Sbapt s1[0] = c1; 120235723Sbapt s2[0] = c2; 121235723Sbapt return (strcoll(s1, s2)); 122235723Sbapt} 123235723Sbapt 124235723Sbapt/* 125235723Sbapt * Expand shell variables and backquotes inside a here document. 126235723Sbapt * union node *arg the document 127235723Sbapt * int fd; where to write the expanded version 128235723Sbapt */ 129235723Sbapt 130235723Sbaptvoid 131235723Sbaptexpandhere(union node *arg, int fd) 132235723Sbapt{ 133235723Sbapt herefd = fd; 134235723Sbapt expandarg(arg, (struct arglist *)NULL, 0); 135235723Sbapt xwrite(fd, stackblock(), expdest - stackblock()); 136235723Sbapt} 137235723Sbapt 138235723Sbapt 139235723Sbapt/* 140235723Sbapt * Perform variable substitution and command substitution on an argument, 141235723Sbapt * placing the resulting list of arguments in arglist. If EXP_FULL is true, 142235723Sbapt * perform splitting and file name expansion. When arglist is NULL, perform 143235723Sbapt * here document expansion. 144235723Sbapt */ 145235723Sbapt 146235723Sbaptvoid 147235723Sbaptexpandarg(union node *arg, struct arglist *arglist, int flag) 148235723Sbapt{ 149235723Sbapt struct strlist *sp; 150235723Sbapt char *p; 151235723Sbapt 152235723Sbapt argbackq = arg->narg.backquote; 153235723Sbapt STARTSTACKSTR(expdest); 154235723Sbapt ifsfirst.next = NULL; 155235723Sbapt ifslastp = NULL; 156235723Sbapt argstr(arg->narg.text, flag); 157235723Sbapt if (arglist == NULL) { 158235723Sbapt return; /* here document expanded */ 159235723Sbapt } 160235723Sbapt STPUTC('\0', expdest); 161235723Sbapt p = grabstackstr(expdest); 162235723Sbapt exparg.lastp = &exparg.list; 163235723Sbapt /* 164235723Sbapt * TODO - EXP_REDIR 165235723Sbapt */ 166235723Sbapt if (flag & EXP_FULL) { 167235723Sbapt ifsbreakup(p, &exparg); 168235723Sbapt *exparg.lastp = NULL; 169235723Sbapt exparg.lastp = &exparg.list; 170235723Sbapt expandmeta(exparg.list, flag); 171235723Sbapt } else { 172235723Sbapt if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 173235723Sbapt rmescapes(p); 174235723Sbapt sp = (struct strlist *)stalloc(sizeof (struct strlist)); 175235723Sbapt sp->text = p; 176235723Sbapt *exparg.lastp = sp; 177235723Sbapt exparg.lastp = &sp->next; 178235723Sbapt } 179235723Sbapt while (ifsfirst.next != NULL) { 180235723Sbapt struct ifsregion *ifsp; 181235723Sbapt INTOFF; 182235723Sbapt ifsp = ifsfirst.next->next; 183235723Sbapt ckfree(ifsfirst.next); 184235723Sbapt ifsfirst.next = ifsp; 185235723Sbapt INTON; 186235723Sbapt } 187235723Sbapt *exparg.lastp = NULL; 188235723Sbapt if (exparg.list) { 189235723Sbapt *arglist->lastp = exparg.list; 190235723Sbapt arglist->lastp = exparg.lastp; 191235723Sbapt } 192235723Sbapt} 193235723Sbapt 194235723Sbapt 195235723Sbapt 196235723Sbapt/* 197235723Sbapt * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 198235723Sbapt * characters to allow for further processing. Otherwise treat 199235723Sbapt * $@ like $* since no splitting will be performed. 200235723Sbapt */ 201235723Sbapt 202235723SbaptSTATIC void 203235723Sbaptargstr(char *p, int flag) 204235723Sbapt{ 205235723Sbapt char c; 206235723Sbapt int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ 207235723Sbapt int firsteq = 1; 208235723Sbapt 209235723Sbapt if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 210235723Sbapt p = exptilde(p, flag); 211235723Sbapt for (;;) { 212235723Sbapt switch (c = *p++) { 213235723Sbapt case '\0': 214235723Sbapt case CTLENDVAR: /* ??? */ 215235723Sbapt goto breakloop; 216235723Sbapt case CTLQUOTEMARK: 217235723Sbapt /* "$@" syntax adherence hack */ 218235723Sbapt if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') 219235723Sbapt break; 220235723Sbapt if ((flag & EXP_FULL) != 0) 221235723Sbapt STPUTC(c, expdest); 222235723Sbapt break; 223235723Sbapt case CTLESC: 224235723Sbapt if (quotes) 225235723Sbapt STPUTC(c, expdest); 226235723Sbapt c = *p++; 227235723Sbapt STPUTC(c, expdest); 228235723Sbapt break; 229235723Sbapt case CTLVAR: 230235723Sbapt p = evalvar(p, flag); 231235723Sbapt break; 232235723Sbapt case CTLBACKQ: 233235723Sbapt case CTLBACKQ|CTLQUOTE: 234235723Sbapt expbackq(argbackq->n, c & CTLQUOTE, flag); 235235723Sbapt argbackq = argbackq->next; 236235723Sbapt break; 237235723Sbapt case CTLENDARI: 238235723Sbapt expari(flag); 239235723Sbapt break; 240235723Sbapt case ':': 241235723Sbapt case '=': 242235723Sbapt /* 243235723Sbapt * sort of a hack - expand tildes in variable 244235723Sbapt * assignments (after the first '=' and after ':'s). 245235723Sbapt */ 246235723Sbapt STPUTC(c, expdest); 247235723Sbapt if (flag & EXP_VARTILDE && *p == '~') { 248235723Sbapt if (c == '=') { 249235723Sbapt if (firsteq) 250235723Sbapt firsteq = 0; 251235723Sbapt else 252235723Sbapt break; 253235723Sbapt } 254235723Sbapt p = exptilde(p, flag); 255235723Sbapt } 256235723Sbapt break; 257235723Sbapt default: 258235723Sbapt STPUTC(c, expdest); 259235723Sbapt } 260235723Sbapt } 261235723Sbaptbreakloop:; 262235723Sbapt} 263235723Sbapt 264235723SbaptSTATIC char * 265235723Sbaptexptilde(char *p, int flag) 266235723Sbapt{ 267235723Sbapt char c, *startp = p; 268235723Sbapt struct passwd *pw; 269235723Sbapt char *home; 270235723Sbapt int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 271235723Sbapt 272235723Sbapt while ((c = *p) != '\0') { 273235723Sbapt switch(c) { 274235723Sbapt case CTLESC: 275235723Sbapt return (startp); 276235723Sbapt case CTLQUOTEMARK: 277235723Sbapt return (startp); 278235723Sbapt case ':': 279235723Sbapt if (flag & EXP_VARTILDE) 280235723Sbapt goto done; 281235723Sbapt break; 282235723Sbapt case '/': 283235723Sbapt goto done; 284235723Sbapt } 285235723Sbapt p++; 286235723Sbapt } 287235723Sbaptdone: 288235723Sbapt *p = '\0'; 289235723Sbapt if (*(startp+1) == '\0') { 290235723Sbapt if ((home = lookupvar("HOME")) == NULL) 291235723Sbapt goto lose; 292235723Sbapt } else { 293235723Sbapt if ((pw = getpwnam(startp+1)) == NULL) 294235723Sbapt goto lose; 295235723Sbapt home = pw->pw_dir; 296235723Sbapt } 297235723Sbapt if (*home == '\0') 298235723Sbapt goto lose; 299235723Sbapt *p = c; 300235723Sbapt while ((c = *home++) != '\0') { 301235723Sbapt if (quotes && SQSYNTAX[(int)c] == CCTL) 302235723Sbapt STPUTC(CTLESC, expdest); 303235723Sbapt STPUTC(c, expdest); 304235723Sbapt } 305235723Sbapt return (p); 306262960Sjmmvlose: 307235723Sbapt *p = c; 308235723Sbapt return (startp); 309235723Sbapt} 310235723Sbapt 311235723Sbapt 312235723SbaptSTATIC void 313235723Sbaptremoverecordregions(int endoff) 314235723Sbapt{ 315235723Sbapt if (ifslastp == NULL) 316235723Sbapt return; 317235723Sbapt 318235723Sbapt if (ifsfirst.endoff > endoff) { 319235723Sbapt while (ifsfirst.next != NULL) { 320235723Sbapt struct ifsregion *ifsp; 321235723Sbapt INTOFF; 322235723Sbapt ifsp = ifsfirst.next->next; 323235723Sbapt ckfree(ifsfirst.next); 324235723Sbapt ifsfirst.next = ifsp; 325235723Sbapt INTON; 326235723Sbapt } 327235723Sbapt if (ifsfirst.begoff > endoff) 328235723Sbapt ifslastp = NULL; 329235723Sbapt else { 330235723Sbapt ifslastp = &ifsfirst; 331235723Sbapt ifsfirst.endoff = endoff; 332235723Sbapt } 333235723Sbapt return; 334235723Sbapt } 335235723Sbapt 336235723Sbapt ifslastp = &ifsfirst; 337235723Sbapt while (ifslastp->next && ifslastp->next->begoff < endoff) 338235723Sbapt ifslastp=ifslastp->next; 339235723Sbapt while (ifslastp->next != NULL) { 340235723Sbapt struct ifsregion *ifsp; 341235723Sbapt INTOFF; 342235723Sbapt ifsp = ifslastp->next->next; 343235723Sbapt ckfree(ifslastp->next); 344235723Sbapt ifslastp->next = ifsp; 345235723Sbapt INTON; 346235723Sbapt } 347235723Sbapt if (ifslastp->endoff > endoff) 348235723Sbapt ifslastp->endoff = endoff; 349235723Sbapt} 350235723Sbapt 351235723Sbapt/* 352235723Sbapt * Expand arithmetic expression. Backup to start of expression, 353235723Sbapt * evaluate, place result in (backed up) result, adjust string position. 354235723Sbapt */ 355235723Sbaptvoid 356235723Sbaptexpari(int flag) 357235723Sbapt{ 358235723Sbapt char *p, *start; 359235723Sbapt arith_t result; 360235723Sbapt int begoff; 361235723Sbapt int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 362235723Sbapt int quoted; 363235723Sbapt 364235723Sbapt 365235723Sbapt /* 366235723Sbapt * This routine is slightly over-complicated for 367235723Sbapt * efficiency. First we make sure there is 368235723Sbapt * enough space for the result, which may be bigger 369235723Sbapt * than the expression if we add exponentiation. Next we 370235723Sbapt * scan backwards looking for the start of arithmetic. If the 371235723Sbapt * next previous character is a CTLESC character, then we 372235723Sbapt * have to rescan starting from the beginning since CTLESC 373235723Sbapt * characters have to be processed left to right. 374235723Sbapt */ 375235723Sbapt CHECKSTRSPACE(DIGITS(result) - 2, expdest); 376235723Sbapt USTPUTC('\0', expdest); 377235723Sbapt start = stackblock(); 378235723Sbapt p = expdest - 2; 379235723Sbapt while (p >= start && *p != CTLARI) 380235723Sbapt --p; 381235723Sbapt if (p < start || *p != CTLARI) 382235723Sbapt error("missing CTLARI (shouldn't happen)"); 383235723Sbapt if (p > start && *(p - 1) == CTLESC) 384235723Sbapt for (p = start; *p != CTLARI; p++) 385235723Sbapt if (*p == CTLESC) 386235723Sbapt p++; 387235723Sbapt 388235723Sbapt if (p[1] == '"') 389235723Sbapt quoted=1; 390235723Sbapt else 391235723Sbapt quoted=0; 392235723Sbapt begoff = p - start; 393235723Sbapt removerecordregions(begoff); 394235723Sbapt if (quotes) 395235723Sbapt rmescapes(p+2); 396235723Sbapt result = arith(p+2); 397235723Sbapt fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result); 398235723Sbapt while (*p++) 399235723Sbapt ; 400235723Sbapt if (quoted == 0) 401235723Sbapt recordregion(begoff, p - 1 - start, 0); 402235723Sbapt result = expdest - p + 1; 403235723Sbapt STADJUST(-result, expdest); 404235723Sbapt} 405235723Sbapt 406235723Sbapt 407235723Sbapt/* 408235723Sbapt * Expand stuff in backwards quotes. 409235723Sbapt */ 410235723Sbapt 411235723SbaptSTATIC void 412235723Sbaptexpbackq(union node *cmd, int quoted, int flag) 413235723Sbapt{ 414235723Sbapt struct backcmd in; 415235723Sbapt int i; 416235723Sbapt char buf[128]; 417235723Sbapt char *p; 418235723Sbapt char *dest = expdest; 419235723Sbapt struct ifsregion saveifs, *savelastp; 420235723Sbapt struct nodelist *saveargbackq; 421235723Sbapt char lastc; 422235723Sbapt int startloc = dest - stackblock(); 423235723Sbapt char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 424235723Sbapt int saveherefd; 425235723Sbapt int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 426235723Sbapt int nnl; 427235723Sbapt 428235723Sbapt INTOFF; 429235723Sbapt saveifs = ifsfirst; 430235723Sbapt savelastp = ifslastp; 431235723Sbapt saveargbackq = argbackq; 432235723Sbapt saveherefd = herefd; 433235723Sbapt herefd = -1; 434235723Sbapt p = grabstackstr(dest); 435235723Sbapt evalbackcmd(cmd, &in); 436235723Sbapt ungrabstackstr(p, dest); 437235723Sbapt ifsfirst = saveifs; 438235723Sbapt ifslastp = savelastp; 439235723Sbapt argbackq = saveargbackq; 440235723Sbapt herefd = saveherefd; 441235723Sbapt 442235723Sbapt p = in.buf; 443235723Sbapt lastc = '\0'; 444235723Sbapt nnl = 0; 445235723Sbapt /* Don't copy trailing newlines */ 446235723Sbapt for (;;) { 447235723Sbapt if (--in.nleft < 0) { 448235723Sbapt if (in.fd < 0) 449235723Sbapt break; 450235723Sbapt while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 451235723Sbapt TRACE(("expbackq: read returns %d\n", i)); 452235723Sbapt if (i <= 0) 453235723Sbapt break; 454235723Sbapt p = buf; 455235723Sbapt in.nleft = i - 1; 456235723Sbapt } 457235723Sbapt lastc = *p++; 458235723Sbapt if (lastc != '\0') { 459235723Sbapt if (quotes && syntax[(int)lastc] == CCTL) 460235723Sbapt STPUTC(CTLESC, dest); 461235723Sbapt if (lastc == '\n') { 462235723Sbapt nnl++; 463235723Sbapt } else { 464235723Sbapt while (nnl > 0) { 465235723Sbapt nnl--; 466235723Sbapt STPUTC('\n', dest); 467235723Sbapt } 468235723Sbapt STPUTC(lastc, dest); 469235723Sbapt } 470235723Sbapt } 471235723Sbapt } 472235723Sbapt 473235723Sbapt if (in.fd >= 0) 474235723Sbapt close(in.fd); 475235723Sbapt if (in.buf) 476235723Sbapt ckfree(in.buf); 477235723Sbapt if (in.jp) 478235723Sbapt exitstatus = waitforjob(in.jp, (int *)NULL); 479235723Sbapt if (quoted == 0) 480235723Sbapt recordregion(startloc, dest - stackblock(), 0); 481235723Sbapt TRACE(("evalbackq: size=%d: \"%.*s\"\n", 482235723Sbapt (dest - stackblock()) - startloc, 483235723Sbapt (dest - stackblock()) - startloc, 484235723Sbapt stackblock() + startloc)); 485235723Sbapt expdest = dest; 486235723Sbapt INTON; 487235723Sbapt} 488235723Sbapt 489235723Sbapt 490235723Sbapt 491235723SbaptSTATIC int 492235723Sbaptsubevalvar(char *p, char *str, int strloc, int subtype, int startloc, 493235723Sbapt int varflags) 494235723Sbapt{ 495235723Sbapt char *startp; 496235723Sbapt char *loc = NULL; 497235723Sbapt char *q; 498235723Sbapt int c = 0; 499235723Sbapt int saveherefd = herefd; 500235723Sbapt struct nodelist *saveargbackq = argbackq; 501235723Sbapt int amount; 502235723Sbapt 503235723Sbapt herefd = -1; 504235723Sbapt argstr(p, 0); 505235723Sbapt STACKSTRNUL(expdest); 506235723Sbapt herefd = saveherefd; 507235723Sbapt argbackq = saveargbackq; 508235723Sbapt startp = stackblock() + startloc; 509235723Sbapt if (str == NULL) 510235723Sbapt str = stackblock() + strloc; 511235723Sbapt 512235723Sbapt switch (subtype) { 513235723Sbapt case VSASSIGN: 514235723Sbapt setvar(str, startp, 0); 515235723Sbapt amount = startp - expdest; 516235723Sbapt STADJUST(amount, expdest); 517235723Sbapt varflags &= ~VSNUL; 518235723Sbapt if (c != 0) 519235723Sbapt *loc = c; 520235723Sbapt return 1; 521235723Sbapt 522235723Sbapt case VSQUESTION: 523235723Sbapt if (*p != CTLENDVAR) { 524235723Sbapt outfmt(&errout, "%s\n", startp); 525235723Sbapt error((char *)NULL); 526235723Sbapt } 527235723Sbapt error("%.*s: parameter %snot set", (int)(p - str - 1), 528235723Sbapt str, (varflags & VSNUL) ? "null or " 529235723Sbapt : nullstr); 530235723Sbapt return 0; 531235723Sbapt 532235723Sbapt case VSTRIMLEFT: 533235723Sbapt for (loc = startp; loc < str; loc++) { 534235723Sbapt c = *loc; 535235723Sbapt *loc = '\0'; 536235723Sbapt if (patmatch(str, startp, varflags & VSQUOTE)) { 537235723Sbapt *loc = c; 538235723Sbapt goto recordleft; 539235723Sbapt } 540235723Sbapt *loc = c; 541235723Sbapt if ((varflags & VSQUOTE) && *loc == CTLESC) 542235723Sbapt loc++; 543235723Sbapt } 544235723Sbapt return 0; 545235723Sbapt 546235723Sbapt case VSTRIMLEFTMAX: 547235723Sbapt for (loc = str - 1; loc >= startp;) { 548235723Sbapt c = *loc; 549235723Sbapt *loc = '\0'; 550235723Sbapt if (patmatch(str, startp, varflags & VSQUOTE)) { 551235723Sbapt *loc = c; 552235723Sbapt goto recordleft; 553235723Sbapt } 554235723Sbapt *loc = c; 555235723Sbapt loc--; 556235723Sbapt if ((varflags & VSQUOTE) && loc > startp && 557235723Sbapt *(loc - 1) == CTLESC) { 558235723Sbapt for (q = startp; q < loc; q++) 559235723Sbapt if (*q == CTLESC) 560235723Sbapt q++; 561235723Sbapt if (q > loc) 562235723Sbapt loc--; 563235723Sbapt } 564235723Sbapt } 565235723Sbapt return 0; 566235723Sbapt 567235723Sbapt case VSTRIMRIGHT: 568235723Sbapt for (loc = str - 1; loc >= startp;) { 569235723Sbapt if (patmatch(str, loc, varflags & VSQUOTE)) { 570235723Sbapt amount = loc - expdest; 571235723Sbapt STADJUST(amount, expdest); 572235723Sbapt return 1; 573235723Sbapt } 574235723Sbapt loc--; 575235723Sbapt if ((varflags & VSQUOTE) && loc > startp && 576235723Sbapt *(loc - 1) == CTLESC) { 577235723Sbapt for (q = startp; q < loc; q++) 578235723Sbapt if (*q == CTLESC) 579235723Sbapt q++; 580235723Sbapt if (q > loc) 581235723Sbapt loc--; 582235723Sbapt } 583235723Sbapt } 584235723Sbapt return 0; 585235723Sbapt 586235723Sbapt case VSTRIMRIGHTMAX: 587235723Sbapt for (loc = startp; loc < str - 1; loc++) { 588235723Sbapt if (patmatch(str, loc, varflags & VSQUOTE)) { 589235723Sbapt amount = loc - expdest; 590235723Sbapt STADJUST(amount, expdest); 591235723Sbapt return 1; 592235723Sbapt } 593235723Sbapt if ((varflags & VSQUOTE) && *loc == CTLESC) 594235723Sbapt loc++; 595235723Sbapt } 596235723Sbapt return 0; 597235723Sbapt 598235723Sbapt 599235723Sbapt default: 600235723Sbapt abort(); 601235723Sbapt } 602235723Sbapt 603235723Sbaptrecordleft: 604235723Sbapt amount = ((str - 1) - (loc - startp)) - expdest; 605235723Sbapt STADJUST(amount, expdest); 606235723Sbapt while (loc != str - 1) 607235723Sbapt *startp++ = *loc++; 608235723Sbapt return 1; 609235723Sbapt} 610235723Sbapt 611235723Sbapt 612235723Sbapt/* 613235723Sbapt * Expand a variable, and return a pointer to the next character in the 614235723Sbapt * input string. 615235723Sbapt */ 616235723Sbapt 617235723SbaptSTATIC char * 618235723Sbaptevalvar(char *p, int flag) 619235723Sbapt{ 620235723Sbapt int subtype; 621235723Sbapt int varflags; 622235723Sbapt char *var; 623235723Sbapt char *val; 624235723Sbapt int patloc; 625235723Sbapt int c; 626235723Sbapt int set; 627235723Sbapt int special; 628235723Sbapt int startloc; 629235723Sbapt int varlen; 630235723Sbapt int easy; 631235723Sbapt int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 632235723Sbapt 633235723Sbapt varflags = (unsigned char)*p++; 634235723Sbapt subtype = varflags & VSTYPE; 635235723Sbapt var = p; 636235723Sbapt special = 0; 637235723Sbapt if (! is_name(*p)) 638235723Sbapt special = 1; 639235723Sbapt p = strchr(p, '=') + 1; 640235723Sbaptagain: /* jump here after setting a variable with ${var=text} */ 641235723Sbapt if (varflags & VSLINENO) { 642235723Sbapt set = 1; 643235723Sbapt special = 0; 644235723Sbapt val = var; 645235723Sbapt p[-1] = '\0'; /* temporarily overwrite '=' to have \0 646235723Sbapt terminated string */ 647235723Sbapt } else if (special) { 648235723Sbapt set = varisset(var, varflags & VSNUL); 649235723Sbapt val = NULL; 650235723Sbapt } else { 651235723Sbapt val = bltinlookup(var, 1); 652235723Sbapt if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 653235723Sbapt val = NULL; 654235723Sbapt set = 0; 655235723Sbapt } else 656235723Sbapt set = 1; 657235723Sbapt } 658235723Sbapt varlen = 0; 659235723Sbapt startloc = expdest - stackblock(); 660235723Sbapt if (!set && uflag) { 661235723Sbapt switch (subtype) { 662235723Sbapt case VSNORMAL: 663235723Sbapt case VSTRIMLEFT: 664262960Sjmmv case VSTRIMLEFTMAX: 665235723Sbapt case VSTRIMRIGHT: 666235723Sbapt case VSTRIMRIGHTMAX: 667235723Sbapt case VSLENGTH: 668235723Sbapt error("%.*s: parameter not set", (int)(p - var - 1), 669235723Sbapt var); 670235723Sbapt } 671235723Sbapt } 672235723Sbapt if (set && subtype != VSPLUS) { 673235723Sbapt /* insert the value of the variable */ 674235723Sbapt if (special) { 675235723Sbapt varvalue(var, varflags & VSQUOTE, subtype, flag); 676235723Sbapt if (subtype == VSLENGTH) { 677235723Sbapt varlen = expdest - stackblock() - startloc; 678235723Sbapt STADJUST(-varlen, expdest); 679235723Sbapt } 680235723Sbapt } else { 681262960Sjmmv char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 682235723Sbapt : BASESYNTAX; 683235723Sbapt 684235723Sbapt if (subtype == VSLENGTH) { 685235723Sbapt for (;*val; val++) 686235723Sbapt varlen++; 687235723Sbapt } 688235723Sbapt else { 689235723Sbapt while (*val) { 690235723Sbapt if (quotes && 691235723Sbapt syntax[(int)*val] == CCTL) 692235723Sbapt STPUTC(CTLESC, expdest); 693235723Sbapt STPUTC(*val++, expdest); 694235723Sbapt } 695235723Sbapt 696235723Sbapt } 697235723Sbapt } 698235723Sbapt } 699235723Sbapt 700235723Sbapt if (subtype == VSPLUS) 701235723Sbapt set = ! set; 702235723Sbapt 703235723Sbapt easy = ((varflags & VSQUOTE) == 0 || 704235723Sbapt (*var == '@' && shellparam.nparam != 1)); 705235723Sbapt 706235723Sbapt 707235723Sbapt switch (subtype) { 708235723Sbapt case VSLENGTH: 709235723Sbapt expdest = cvtnum(varlen, expdest); 710235723Sbapt goto record; 711235723Sbapt 712235723Sbapt case VSNORMAL: 713235723Sbapt if (!easy) 714235723Sbapt break; 715235723Sbaptrecord: 716235723Sbapt recordregion(startloc, expdest - stackblock(), 717235723Sbapt varflags & VSQUOTE); 718235723Sbapt break; 719235723Sbapt 720235723Sbapt case VSPLUS: 721235723Sbapt case VSMINUS: 722235723Sbapt if (!set) { 723235723Sbapt argstr(p, flag); 724235723Sbapt break; 725235723Sbapt } 726235723Sbapt if (easy) 727235723Sbapt goto record; 728235723Sbapt break; 729235723Sbapt 730235723Sbapt case VSTRIMLEFT: 731235723Sbapt case VSTRIMLEFTMAX: 732235723Sbapt case VSTRIMRIGHT: 733235723Sbapt case VSTRIMRIGHTMAX: 734235723Sbapt if (!set) 735235723Sbapt break; 736235723Sbapt /* 737235723Sbapt * Terminate the string and start recording the pattern 738235723Sbapt * right after it 739235723Sbapt */ 740235723Sbapt STPUTC('\0', expdest); 741235723Sbapt patloc = expdest - stackblock(); 742235723Sbapt if (subevalvar(p, NULL, patloc, subtype, 743235723Sbapt startloc, varflags) == 0) { 744235723Sbapt int amount = (expdest - stackblock() - patloc) + 1; 745235723Sbapt STADJUST(-amount, expdest); 746235723Sbapt } 747235723Sbapt /* Remove any recorded regions beyond start of variable */ 748235723Sbapt removerecordregions(startloc); 749235723Sbapt goto record; 750235723Sbapt 751235723Sbapt case VSASSIGN: 752235723Sbapt case VSQUESTION: 753235723Sbapt if (!set) { 754235723Sbapt if (subevalvar(p, var, 0, subtype, startloc, varflags)) { 755235723Sbapt varflags &= ~VSNUL; 756235723Sbapt /* 757235723Sbapt * Remove any recorded regions beyond 758235723Sbapt * start of variable 759235723Sbapt */ 760235723Sbapt removerecordregions(startloc); 761235723Sbapt goto again; 762235723Sbapt } 763235723Sbapt break; 764235723Sbapt } 765235723Sbapt if (easy) 766235723Sbapt goto record; 767235723Sbapt break; 768235723Sbapt 769235723Sbapt case VSERROR: 770235723Sbapt c = p - var - 1; 771235723Sbapt error("${%.*s%s}: Bad substitution", c, var, 772235723Sbapt (c > 0 && *p != CTLENDVAR) ? "..." : ""); 773235723Sbapt 774235723Sbapt default: 775235723Sbapt abort(); 776235723Sbapt } 777235723Sbapt p[-1] = '='; /* recover overwritten '=' */ 778235723Sbapt 779235723Sbapt if (subtype != VSNORMAL) { /* skip to end of alternative */ 780235723Sbapt int nesting = 1; 781235723Sbapt for (;;) { 782235723Sbapt if ((c = *p++) == CTLESC) 783235723Sbapt p++; 784235723Sbapt else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 785235723Sbapt if (set) 786235723Sbapt argbackq = argbackq->next; 787235723Sbapt } else if (c == CTLVAR) { 788235723Sbapt if ((*p++ & VSTYPE) != VSNORMAL) 789235723Sbapt nesting++; 790235723Sbapt } else if (c == CTLENDVAR) { 791235723Sbapt if (--nesting == 0) 792235723Sbapt break; 793235723Sbapt } 794235723Sbapt } 795235723Sbapt } 796235723Sbapt return p; 797235723Sbapt} 798235723Sbapt 799235723Sbapt 800235723Sbapt 801235723Sbapt/* 802235723Sbapt * Test whether a specialized variable is set. 803235723Sbapt */ 804235723Sbapt 805235723SbaptSTATIC int 806235723Sbaptvarisset(char *name, int nulok) 807235723Sbapt{ 808235723Sbapt 809235723Sbapt if (*name == '!') 810235723Sbapt return backgndpid != -1; 811235723Sbapt else if (*name == '@' || *name == '*') { 812235723Sbapt if (*shellparam.p == NULL) 813235723Sbapt return 0; 814235723Sbapt 815235723Sbapt if (nulok) { 816235723Sbapt char **av; 817235723Sbapt 818235723Sbapt for (av = shellparam.p; *av; av++) 819262960Sjmmv if (**av != '\0') 820262960Sjmmv return 1; 821235723Sbapt return 0; 822235723Sbapt } 823235723Sbapt } else if (is_digit(*name)) { 824262960Sjmmv char *ap; 825235723Sbapt int num = atoi(name); 826235723Sbapt 827235723Sbapt if (num > shellparam.nparam) 828235723Sbapt return 0; 829235723Sbapt 830235723Sbapt if (num == 0) 831235723Sbapt ap = arg0; 832235723Sbapt else 833235723Sbapt ap = shellparam.p[num - 1]; 834235723Sbapt 835235723Sbapt if (nulok && (ap == NULL || *ap == '\0')) 836235723Sbapt return 0; 837235723Sbapt } 838235723Sbapt return 1; 839235723Sbapt} 840235723Sbapt 841235723Sbapt 842235723Sbapt 843235723Sbapt/* 844235723Sbapt * Add the value of a specialized variable to the stack string. 845235723Sbapt */ 846235723Sbapt 847235723SbaptSTATIC void 848235723Sbaptvarvalue(char *name, int quoted, int subtype, int flag) 849235723Sbapt{ 850235723Sbapt int num; 851235723Sbapt char *p; 852235723Sbapt int i; 853249582Sgabor extern int oexitstatus; 854235723Sbapt char sep; 855235723Sbapt char **ap; 856235723Sbapt char const *syntax; 857235723Sbapt 858235723Sbapt#define STRTODEST(p) \ 859235723Sbapt do {\ 860235723Sbapt if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \ 861235723Sbapt syntax = quoted? DQSYNTAX : BASESYNTAX; \ 862235723Sbapt while (*p) { \ 863235723Sbapt if (syntax[(int)*p] == CCTL) \ 864235723Sbapt STPUTC(CTLESC, expdest); \ 865235723Sbapt STPUTC(*p++, expdest); \ 866235723Sbapt } \ 867235723Sbapt } else \ 868235723Sbapt while (*p) \ 869235723Sbapt STPUTC(*p++, expdest); \ 870235723Sbapt } while (0) 871235723Sbapt 872235723Sbapt 873235723Sbapt switch (*name) { 874235723Sbapt case '$': 875235723Sbapt num = rootpid; 876235723Sbapt goto numvar; 877235723Sbapt case '?': 878235723Sbapt num = oexitstatus; 879235723Sbapt goto numvar; 880235723Sbapt case '#': 881235723Sbapt num = shellparam.nparam; 882235723Sbapt goto numvar; 883235723Sbapt case '!': 884235723Sbapt num = backgndpid; 885235723Sbaptnumvar: 886235723Sbapt expdest = cvtnum(num, expdest); 887235723Sbapt break; 888235723Sbapt case '-': 889235723Sbapt for (i = 0 ; i < NOPTS ; i++) { 890235723Sbapt if (optlist[i].val) 891235723Sbapt STPUTC(optlist[i].letter, expdest); 892235723Sbapt } 893235723Sbapt break; 894235723Sbapt case '@': 895235723Sbapt if (flag & EXP_FULL && quoted) { 896235723Sbapt for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 897235723Sbapt STRTODEST(p); 898235723Sbapt if (*ap) 899235723Sbapt STPUTC('\0', expdest); 900235723Sbapt } 901235723Sbapt break; 902235723Sbapt } 903235723Sbapt /* FALLTHROUGH */ 904235723Sbapt case '*': 905235723Sbapt if (ifsset()) 906235723Sbapt sep = ifsval()[0]; 907235723Sbapt else 908235723Sbapt sep = ' '; 909235723Sbapt for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 910235723Sbapt STRTODEST(p); 911235723Sbapt if (*ap && sep) 912235723Sbapt STPUTC(sep, expdest); 913235723Sbapt } 914235723Sbapt break; 915235723Sbapt case '0': 916235723Sbapt p = arg0; 917235723Sbapt STRTODEST(p); 918235723Sbapt break; 919235723Sbapt default: 920235723Sbapt if (is_digit(*name)) { 921235723Sbapt num = atoi(name); 922235723Sbapt if (num > 0 && num <= shellparam.nparam) { 923235723Sbapt p = shellparam.p[num - 1]; 924235723Sbapt STRTODEST(p); 925235723Sbapt } 926235723Sbapt } 927235723Sbapt break; 928235723Sbapt } 929235723Sbapt} 930235723Sbapt 931235723Sbapt 932235723Sbapt 933235723Sbapt/* 934235723Sbapt * Record the the fact that we have to scan this region of the 935235723Sbapt * string for IFS characters. 936235723Sbapt */ 937235723Sbapt 938235723SbaptSTATIC void 939235723Sbaptrecordregion(int start, int end, int inquotes) 940235723Sbapt{ 941235723Sbapt struct ifsregion *ifsp; 942235723Sbapt 943235723Sbapt if (ifslastp == NULL) { 944235723Sbapt ifsp = &ifsfirst; 945235723Sbapt } else { 946235723Sbapt if (ifslastp->endoff == start 947235723Sbapt && ifslastp->inquotes == inquotes) { 948235723Sbapt /* extend previous area */ 949235723Sbapt ifslastp->endoff = end; 950235723Sbapt return; 951235723Sbapt } 952235723Sbapt ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 953235723Sbapt ifslastp->next = ifsp; 954235723Sbapt } 955235723Sbapt ifslastp = ifsp; 956235723Sbapt ifslastp->next = NULL; 957235723Sbapt ifslastp->begoff = start; 958235723Sbapt ifslastp->endoff = end; 959235723Sbapt ifslastp->inquotes = inquotes; 960235723Sbapt} 961235723Sbapt 962235723Sbapt 963235723Sbapt 964235723Sbapt/* 965235723Sbapt * Break the argument string into pieces based upon IFS and add the 966235723Sbapt * strings to the argument list. The regions of the string to be 967235723Sbapt * searched for IFS characters have been stored by recordregion. 968235723Sbapt */ 969235723SbaptSTATIC void 970235723Sbaptifsbreakup(char *string, struct arglist *arglist) 971235723Sbapt{ 972235723Sbapt struct ifsregion *ifsp; 973235723Sbapt struct strlist *sp; 974235723Sbapt char *start; 975235723Sbapt char *p; 976235723Sbapt char *q; 977235723Sbapt char *ifs; 978235723Sbapt const char *ifsspc; 979235723Sbapt int had_param_ch = 0; 980235723Sbapt 981235723Sbapt start = string; 982235723Sbapt 983235723Sbapt if (ifslastp == NULL) { 984235723Sbapt /* Return entire argument, IFS doesn't apply to any of it */ 985235723Sbapt sp = (struct strlist *)stalloc(sizeof *sp); 986235723Sbapt sp->text = start; 987235723Sbapt *arglist->lastp = sp; 988235723Sbapt arglist->lastp = &sp->next; 989235723Sbapt return; 990235723Sbapt } 991235723Sbapt 992235723Sbapt ifs = ifsset() ? ifsval() : " \t\n"; 993235723Sbapt 994235723Sbapt for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { 995235723Sbapt p = string + ifsp->begoff; 996235723Sbapt while (p < string + ifsp->endoff) { 997235723Sbapt had_param_ch = 1; 998235723Sbapt q = p; 999235723Sbapt if (*p == CTLESC) 1000235723Sbapt p++; 1001235723Sbapt if (ifsp->inquotes) { 1002235723Sbapt /* Only NULs (should be from "$@") end args */ 1003235723Sbapt if (*p != 0) { 1004235723Sbapt p++; 1005235723Sbapt continue; 1006235723Sbapt } 1007262960Sjmmv ifsspc = NULL; 1008235723Sbapt } else { 1009235723Sbapt if (!strchr(ifs, *p)) { 1010235723Sbapt p++; 1011235723Sbapt continue; 1012235723Sbapt } 1013235723Sbapt had_param_ch = 0; 1014235723Sbapt ifsspc = strchr(" \t\n", *p); 1015235723Sbapt 1016235723Sbapt /* Ignore IFS whitespace at start */ 1017235723Sbapt if (q == start && ifsspc != NULL) { 1018235723Sbapt p++; 1019235723Sbapt start = p; 1020235723Sbapt continue; 1021235723Sbapt } 1022235723Sbapt } 1023235723Sbapt 1024235723Sbapt /* Save this argument... */ 1025235723Sbapt *q = '\0'; 1026235723Sbapt sp = (struct strlist *)stalloc(sizeof *sp); 1027235723Sbapt sp->text = start; 1028235723Sbapt *arglist->lastp = sp; 1029235723Sbapt arglist->lastp = &sp->next; 1030235723Sbapt p++; 1031262960Sjmmv 1032235723Sbapt if (ifsspc != NULL) { 1033235723Sbapt /* Ignore further trailing IFS whitespace */ 1034235723Sbapt for (; p < string + ifsp->endoff; p++) { 1035235723Sbapt q = p; 1036235723Sbapt if (*p == CTLESC) 1037235723Sbapt p++; 1038235723Sbapt if (strchr(ifs, *p) == NULL) { 1039235723Sbapt p = q; 1040235723Sbapt break; 1041235723Sbapt } 1042235723Sbapt if (strchr(" \t\n", *p) == NULL) { 1043235723Sbapt p++; 1044235723Sbapt break; 1045235723Sbapt } 1046235723Sbapt } 1047235723Sbapt } 1048235723Sbapt start = p; 1049235723Sbapt } 1050235723Sbapt } 1051235723Sbapt 1052235723Sbapt /* 1053235723Sbapt * Save anything left as an argument. 1054235723Sbapt * Traditionally we have treated 'IFS=':'; set -- x$IFS' as 1055235723Sbapt * generating 2 arguments, the second of which is empty. 1056235723Sbapt * Some recent clarification of the Posix spec say that it 1057235723Sbapt * should only generate one.... 1058235723Sbapt */ 1059235723Sbapt if (had_param_ch || *start != 0) { 1060235723Sbapt sp = (struct strlist *)stalloc(sizeof *sp); 1061235723Sbapt sp->text = start; 1062235723Sbapt *arglist->lastp = sp; 1063235723Sbapt arglist->lastp = &sp->next; 1064235723Sbapt } 1065235723Sbapt} 1066235723Sbapt 1067235723Sbapt 1068235723Sbapt 1069235723Sbapt/* 1070235723Sbapt * Expand shell metacharacters. At this point, the only control characters 1071235723Sbapt * should be escapes. The results are stored in the list exparg. 1072235723Sbapt */ 1073235723Sbapt 1074235723SbaptSTATIC char *expdir; 1075235723Sbapt 1076235723Sbapt 1077235723SbaptSTATIC void 1078235723Sbaptexpandmeta(struct strlist *str, int flag __unused) 1079235723Sbapt{ 1080235723Sbapt char *p; 1081235723Sbapt struct strlist **savelastp; 1082235723Sbapt struct strlist *sp; 1083235723Sbapt char c; 1084235723Sbapt /* TODO - EXP_REDIR */ 1085235723Sbapt 1086235723Sbapt while (str) { 1087235723Sbapt if (fflag) 1088235723Sbapt goto nometa; 1089235723Sbapt p = str->text; 1090235723Sbapt for (;;) { /* fast check for meta chars */ 1091235723Sbapt if ((c = *p++) == '\0') 1092235723Sbapt goto nometa; 1093235723Sbapt if (c == '*' || c == '?' || c == '[' || c == '!') 1094235723Sbapt break; 1095235723Sbapt } 1096235723Sbapt savelastp = exparg.lastp; 1097235723Sbapt INTOFF; 1098235723Sbapt if (expdir == NULL) { 1099235723Sbapt int i = strlen(str->text); 1100235723Sbapt expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 1101235723Sbapt } 1102235723Sbapt 1103235723Sbapt expmeta(expdir, str->text); 1104235723Sbapt ckfree(expdir); 1105262960Sjmmv expdir = NULL; 1106235723Sbapt INTON; 1107235723Sbapt if (exparg.lastp == savelastp) { 1108235723Sbapt /* 1109235723Sbapt * no matches 1110235723Sbapt */ 1111235723Sbaptnometa: 1112235723Sbapt *exparg.lastp = str; 1113235723Sbapt rmescapes(str->text); 1114235723Sbapt exparg.lastp = &str->next; 1115235723Sbapt } else { 1116235723Sbapt *exparg.lastp = NULL; 1117235723Sbapt *savelastp = sp = expsort(*savelastp); 1118235723Sbapt while (sp->next != NULL) 1119235723Sbapt sp = sp->next; 1120235723Sbapt exparg.lastp = &sp->next; 1121235723Sbapt } 1122235723Sbapt str = str->next; 1123235723Sbapt } 1124235723Sbapt} 1125235723Sbapt 1126235723Sbapt 1127235723Sbapt/* 1128235723Sbapt * Do metacharacter (i.e. *, ?, [...]) expansion. 1129235723Sbapt */ 1130235723Sbapt 1131235723SbaptSTATIC void 1132235723Sbaptexpmeta(char *enddir, char *name) 1133235723Sbapt{ 1134235723Sbapt char *p; 1135235723Sbapt char *q; 1136235723Sbapt char *start; 1137235723Sbapt char *endname; 1138235723Sbapt int metaflag; 1139235723Sbapt struct stat statb; 1140235723Sbapt DIR *dirp; 1141235723Sbapt struct dirent *dp; 1142235723Sbapt int atend; 1143235723Sbapt int matchdot; 1144235723Sbapt 1145235723Sbapt metaflag = 0; 1146235723Sbapt start = name; 1147235723Sbapt for (p = name ; ; p++) { 1148235723Sbapt if (*p == '*' || *p == '?') 1149235723Sbapt metaflag = 1; 1150235723Sbapt else if (*p == '[') { 1151235723Sbapt q = p + 1; 1152235723Sbapt if (*q == '!' || *q == '^') 1153235723Sbapt q++; 1154235723Sbapt for (;;) { 1155235723Sbapt while (*q == CTLQUOTEMARK) 1156235723Sbapt q++; 1157235723Sbapt if (*q == CTLESC) 1158235723Sbapt q++; 1159235723Sbapt if (*q == '/' || *q == '\0') 1160235723Sbapt break; 1161235723Sbapt if (*++q == ']') { 1162235723Sbapt metaflag = 1; 1163235723Sbapt break; 1164235723Sbapt } 1165235723Sbapt } 1166235723Sbapt } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 1167235723Sbapt metaflag = 1; 1168235723Sbapt } else if (*p == '\0') 1169235723Sbapt break; 1170235723Sbapt else if (*p == CTLQUOTEMARK) 1171235723Sbapt continue; 1172235723Sbapt else if (*p == CTLESC) 1173235723Sbapt p++; 1174235723Sbapt if (*p == '/') { 1175235723Sbapt if (metaflag) 1176235723Sbapt break; 1177235723Sbapt start = p + 1; 1178235723Sbapt } 1179235723Sbapt } 1180235723Sbapt if (metaflag == 0) { /* we've reached the end of the file name */ 1181235723Sbapt if (enddir != expdir) 1182235723Sbapt metaflag++; 1183235723Sbapt for (p = name ; ; p++) { 1184235723Sbapt if (*p == CTLQUOTEMARK) 1185235723Sbapt continue; 1186235723Sbapt if (*p == CTLESC) 1187262960Sjmmv p++; 1188235723Sbapt *enddir++ = *p; 1189235723Sbapt if (*p == '\0') 1190235723Sbapt break; 1191235723Sbapt } 1192235723Sbapt if (metaflag == 0 || lstat(expdir, &statb) >= 0) 1193235723Sbapt addfname(expdir); 1194235723Sbapt return; 1195235723Sbapt } 1196235723Sbapt endname = p; 1197235723Sbapt if (start != name) { 1198235723Sbapt p = name; 1199235723Sbapt while (p < start) { 1200235723Sbapt while (*p == CTLQUOTEMARK) 1201235723Sbapt p++; 1202235723Sbapt if (*p == CTLESC) 1203235723Sbapt p++; 1204235723Sbapt *enddir++ = *p++; 1205235723Sbapt } 1206235723Sbapt } 1207235723Sbapt if (enddir == expdir) { 1208235723Sbapt p = "."; 1209235723Sbapt } else if (enddir == expdir + 1 && *expdir == '/') { 1210235723Sbapt p = "/"; 1211235723Sbapt } else { 1212235723Sbapt p = expdir; 1213235723Sbapt enddir[-1] = '\0'; 1214235723Sbapt } 1215235723Sbapt if ((dirp = opendir(p)) == NULL) 1216235723Sbapt return; 1217235723Sbapt if (enddir != expdir) 1218235723Sbapt enddir[-1] = '/'; 1219235723Sbapt if (*endname == 0) { 1220235723Sbapt atend = 1; 1221235723Sbapt } else { 1222235723Sbapt atend = 0; 1223235723Sbapt *endname++ = '\0'; 1224235723Sbapt } 1225235723Sbapt matchdot = 0; 1226235723Sbapt p = start; 1227235723Sbapt while (*p == CTLQUOTEMARK) 1228235723Sbapt p++; 1229235723Sbapt if (*p == CTLESC) 1230235723Sbapt p++; 1231235723Sbapt if (*p == '.') 1232235723Sbapt matchdot++; 1233235723Sbapt while (! int_pending() && (dp = readdir(dirp)) != NULL) { 1234235723Sbapt if (dp->d_name[0] == '.' && ! matchdot) 1235235723Sbapt continue; 1236235723Sbapt if (patmatch(start, dp->d_name, 0)) { 1237235723Sbapt if (atend) { 1238235723Sbapt scopy(dp->d_name, enddir); 1239235723Sbapt addfname(expdir); 1240235723Sbapt } else { 1241235723Sbapt for (p = enddir, q = dp->d_name; 1242235723Sbapt (*p++ = *q++) != '\0';) 1243235723Sbapt continue; 1244235723Sbapt p[-1] = '/'; 1245235723Sbapt expmeta(p, endname); 1246235723Sbapt } 1247235723Sbapt } 1248235723Sbapt } 1249235723Sbapt closedir(dirp); 1250235723Sbapt if (! atend) 1251235723Sbapt endname[-1] = '/'; 1252235723Sbapt} 1253235723Sbapt 1254235723Sbapt 1255235723Sbapt/* 1256235723Sbapt * Add a file name to the list. 1257235723Sbapt */ 1258235723Sbapt 1259235723SbaptSTATIC void 1260235723Sbaptaddfname(char *name) 1261235723Sbapt{ 1262235723Sbapt char *p; 1263235723Sbapt struct strlist *sp; 1264235723Sbapt 1265235723Sbapt p = stalloc(strlen(name) + 1); 1266235723Sbapt scopy(name, p); 1267235723Sbapt sp = (struct strlist *)stalloc(sizeof *sp); 1268235723Sbapt sp->text = p; 1269235723Sbapt *exparg.lastp = sp; 1270235723Sbapt exparg.lastp = &sp->next; 1271235723Sbapt} 1272235723Sbapt 1273235723Sbapt 1274235723Sbapt/* 1275235723Sbapt * Sort the results of file name expansion. It calculates the number of 1276235723Sbapt * strings to sort and then calls msort (short for merge sort) to do the 1277235723Sbapt * work. 1278235723Sbapt */ 1279235723Sbapt 1280235723SbaptSTATIC struct strlist * 1281235723Sbaptexpsort(struct strlist *str) 1282235723Sbapt{ 1283235723Sbapt int len; 1284235723Sbapt struct strlist *sp; 1285235723Sbapt 1286235723Sbapt len = 0; 1287235723Sbapt for (sp = str ; sp ; sp = sp->next) 1288235723Sbapt len++; 1289235723Sbapt return msort(str, len); 1290235723Sbapt} 1291235723Sbapt 1292235723Sbapt 1293235723SbaptSTATIC struct strlist * 1294235723Sbaptmsort(struct strlist *list, int len) 1295235723Sbapt{ 1296235723Sbapt struct strlist *p, *q = NULL; 1297235723Sbapt struct strlist **lpp; 1298235723Sbapt int half; 1299235723Sbapt int n; 1300235723Sbapt 1301235723Sbapt if (len <= 1) 1302235723Sbapt return list; 1303235723Sbapt half = len >> 1; 1304235723Sbapt p = list; 1305235723Sbapt for (n = half ; --n >= 0 ; ) { 1306235723Sbapt q = p; 1307235723Sbapt p = p->next; 1308235723Sbapt } 1309235723Sbapt q->next = NULL; /* terminate first half of list */ 1310235723Sbapt q = msort(list, half); /* sort first half of list */ 1311235723Sbapt p = msort(p, len - half); /* sort second half */ 1312235723Sbapt lpp = &list; 1313235723Sbapt for (;;) { 1314235723Sbapt if (strcmp(p->text, q->text) < 0) { 1315235723Sbapt *lpp = p; 1316235723Sbapt lpp = &p->next; 1317235723Sbapt if ((p = *lpp) == NULL) { 1318235723Sbapt *lpp = q; 1319235723Sbapt break; 1320235723Sbapt } 1321235723Sbapt } else { 1322235723Sbapt *lpp = q; 1323235723Sbapt lpp = &q->next; 1324235723Sbapt if ((q = *lpp) == NULL) { 1325235723Sbapt *lpp = p; 1326235723Sbapt break; 1327235723Sbapt } 1328235723Sbapt } 1329235723Sbapt } 1330235723Sbapt return list; 1331235723Sbapt} 1332235723Sbapt 1333235723Sbapt 1334235723Sbapt 1335235723Sbapt/* 1336235723Sbapt * Returns true if the pattern matches the string. 1337235723Sbapt */ 1338235723Sbapt 1339235723Sbaptint 1340235723Sbaptpatmatch(char *pattern, char *string, int squoted) 1341235723Sbapt{ 1342235723Sbapt#ifdef notdef 1343235723Sbapt if (pattern[0] == '!' && pattern[1] == '!') 1344235723Sbapt return 1 - pmatch(pattern + 2, string); 1345235723Sbapt else 1346235723Sbapt#endif 1347235723Sbapt return pmatch(pattern, string, squoted); 1348235723Sbapt} 1349235723Sbapt 1350235723Sbapt 1351235723SbaptSTATIC int 1352235723Sbaptpmatch(char *pattern, char *string, int squoted) 1353235723Sbapt{ 1354235723Sbapt char *p, *q; 1355235723Sbapt char c; 1356235723Sbapt 1357235723Sbapt p = pattern; 1358235723Sbapt q = string; 1359235723Sbapt for (;;) { 1360235723Sbapt switch (c = *p++) { 1361235723Sbapt case '\0': 1362235723Sbapt goto breakloop; 1363235723Sbapt case CTLESC: 1364235723Sbapt if (squoted && *q == CTLESC) 1365235723Sbapt q++; 1366235723Sbapt if (*q++ != *p++) 1367235723Sbapt return 0; 1368235723Sbapt break; 1369235723Sbapt case CTLQUOTEMARK: 1370235723Sbapt continue; 1371235723Sbapt case '?': 1372235723Sbapt if (squoted && *q == CTLESC) 1373235723Sbapt q++; 1374235723Sbapt if (*q++ == '\0') 1375235723Sbapt return 0; 1376235723Sbapt break; 1377235723Sbapt case '*': 1378235723Sbapt c = *p; 1379235723Sbapt while (c == CTLQUOTEMARK || c == '*') 1380235723Sbapt c = *++p; 1381235723Sbapt if (c != CTLESC && c != CTLQUOTEMARK && 1382235723Sbapt c != '?' && c != '*' && c != '[') { 1383235723Sbapt while (*q != c) { 1384235723Sbapt if (squoted && *q == CTLESC && 1385235723Sbapt q[1] == c) 1386235723Sbapt break; 1387235723Sbapt if (*q == '\0') 1388235723Sbapt return 0; 1389235723Sbapt if (squoted && *q == CTLESC) 1390235723Sbapt q++; 1391235723Sbapt q++; 1392235723Sbapt } 1393235723Sbapt } 1394235723Sbapt do { 1395235723Sbapt if (pmatch(p, q, squoted)) 1396235723Sbapt return 1; 1397235723Sbapt if (squoted && *q == CTLESC) 1398235723Sbapt q++; 1399235723Sbapt } while (*q++ != '\0'); 1400235723Sbapt return 0; 1401235723Sbapt case '[': { 1402235723Sbapt char *endp; 1403235723Sbapt int invert, found; 1404235723Sbapt char chr; 1405235723Sbapt 1406235723Sbapt endp = p; 1407235723Sbapt if (*endp == '!' || *endp == '^') 1408235723Sbapt endp++; 1409235723Sbapt for (;;) { 1410235723Sbapt while (*endp == CTLQUOTEMARK) 1411235723Sbapt endp++; 1412235723Sbapt if (*endp == '\0') 1413235723Sbapt goto dft; /* no matching ] */ 1414235723Sbapt if (*endp == CTLESC) 1415235723Sbapt endp++; 1416235723Sbapt if (*++endp == ']') 1417235723Sbapt break; 1418235723Sbapt } 1419235723Sbapt invert = 0; 1420235723Sbapt if (*p == '!' || *p == '^') { 1421235723Sbapt invert++; 1422235723Sbapt p++; 1423235723Sbapt } 1424235723Sbapt found = 0; 1425235723Sbapt chr = *q++; 1426235723Sbapt if (squoted && chr == CTLESC) 1427235723Sbapt chr = *q++; 1428235723Sbapt if (chr == '\0') 1429235723Sbapt return 0; 1430235723Sbapt c = *p++; 1431235723Sbapt do { 1432235723Sbapt if (c == CTLQUOTEMARK) 1433235723Sbapt continue; 1434235723Sbapt if (c == CTLESC) 1435235723Sbapt c = *p++; 1436235723Sbapt if (*p == '-' && p[1] != ']') { 1437235723Sbapt p++; 1438235723Sbapt while (*p == CTLQUOTEMARK) 1439235723Sbapt p++; 1440235723Sbapt if (*p == CTLESC) 1441235723Sbapt p++; 1442235723Sbapt if ( collate_range_cmp(chr, c) >= 0 1443235723Sbapt && collate_range_cmp(chr, *p) <= 0 1444235723Sbapt ) 1445235723Sbapt found = 1; 1446235723Sbapt p++; 1447235723Sbapt } else { 1448235723Sbapt if (chr == c) 1449235723Sbapt found = 1; 1450235723Sbapt } 1451235723Sbapt } while ((c = *p++) != ']'); 1452235723Sbapt if (found == invert) 1453235723Sbapt return 0; 1454235723Sbapt break; 1455235723Sbapt } 1456235723Sbaptdft: default: 1457235723Sbapt if (squoted && *q == CTLESC) 1458235723Sbapt q++; 1459235723Sbapt if (*q++ != c) 1460235723Sbapt return 0; 1461235723Sbapt break; 1462235723Sbapt } 1463235723Sbapt } 1464235723Sbaptbreakloop: 1465235723Sbapt if (*q != '\0') 1466235723Sbapt return 0; 1467235723Sbapt return 1; 1468235723Sbapt} 1469235723Sbapt 1470235723Sbapt 1471235723Sbapt 1472235723Sbapt/* 1473235723Sbapt * Remove any CTLESC characters from a string. 1474235723Sbapt */ 1475235723Sbapt 1476235723Sbaptvoid 1477235723Sbaptrmescapes(char *str) 1478235723Sbapt{ 1479235723Sbapt char *p, *q; 1480235723Sbapt 1481235723Sbapt p = str; 1482235723Sbapt while (*p != CTLESC && *p != CTLQUOTEMARK) { 1483235723Sbapt if (*p++ == '\0') 1484235723Sbapt return; 1485235723Sbapt } 1486235723Sbapt q = p; 1487235723Sbapt while (*p) { 1488235723Sbapt if (*p == CTLQUOTEMARK) { 1489235723Sbapt p++; 1490235723Sbapt continue; 1491235723Sbapt } 1492235723Sbapt if (*p == CTLESC) 1493235723Sbapt p++; 1494235723Sbapt *q++ = *p++; 1495235723Sbapt } 1496235723Sbapt *q = '\0'; 1497235723Sbapt} 1498235723Sbapt 1499235723Sbapt 1500235723Sbapt 1501235723Sbapt/* 1502235723Sbapt * See if a pattern matches in a case statement. 1503235723Sbapt */ 1504235723Sbapt 1505235723Sbaptint 1506235723Sbaptcasematch(union node *pattern, char *val) 1507235723Sbapt{ 1508235723Sbapt struct stackmark smark; 1509235723Sbapt int result; 1510235723Sbapt char *p; 1511235723Sbapt 1512235723Sbapt setstackmark(&smark); 1513235723Sbapt argbackq = pattern->narg.backquote; 1514235723Sbapt STARTSTACKSTR(expdest); 1515235723Sbapt ifslastp = NULL; 1516235723Sbapt argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 1517235723Sbapt STPUTC('\0', expdest); 1518235723Sbapt p = grabstackstr(expdest); 1519235723Sbapt result = patmatch(p, val, 0); 1520235723Sbapt popstackmark(&smark); 1521235723Sbapt return result; 1522235723Sbapt} 1523235723Sbapt 1524235723Sbapt/* 1525235723Sbapt * Our own itoa(). 1526235723Sbapt */ 1527235723Sbapt 1528235723SbaptSTATIC char * 1529235723Sbaptcvtnum(int num, char *buf) 1530235723Sbapt{ 1531235723Sbapt char temp[32]; 1532235723Sbapt int neg = num < 0; 1533235723Sbapt char *p = temp + 31; 1534235723Sbapt 1535235723Sbapt temp[31] = '\0'; 1536235723Sbapt 1537235723Sbapt do { 1538235723Sbapt *--p = num % 10 + '0'; 1539235723Sbapt } while ((num /= 10) != 0); 1540235723Sbapt 1541235723Sbapt if (neg) 1542235723Sbapt *--p = '-'; 1543235723Sbapt 1544235723Sbapt while (*p) 1545235723Sbapt STPUTC(*p++, buf); 1546235723Sbapt return buf; 1547235723Sbapt} 1548235723Sbapt 1549235723Sbapt/* 1550235723Sbapt * Do most of the work for wordexp(3). 1551235723Sbapt */ 1552235723Sbapt 1553235723Sbaptint 1554235723Sbaptwordexpcmd(int argc, char **argv) 1555235723Sbapt{ 1556235723Sbapt size_t len; 1557235723Sbapt int i; 1558235723Sbapt 1559235723Sbapt out1fmt("%08x", argc - 1); 1560235723Sbapt for (i = 1, len = 0; i < argc; i++) 1561235723Sbapt len += strlen(argv[i]); 1562235723Sbapt out1fmt("%08x", (int)len); 1563235723Sbapt for (i = 1; i < argc; i++) { 1564235723Sbapt out1str(argv[i]); 1565235723Sbapt out1c('\0'); 1566235723Sbapt } 1567235723Sbapt return (0); 1568235723Sbapt} 1569235723Sbapt