expand.c revision 26488
1141586Simp/*- 2141586Simp * Copyright (c) 1991, 1993 3141586Simp * The Regents of the University of California. All rights reserved. 4141586Simp * 5141586Simp * This code is derived from software contributed to Berkeley by 6141586Simp * Kenneth Almquist. 7141586Simp * 8141586Simp * Redistribution and use in source and binary forms, with or without 9141586Simp * modification, are permitted provided that the following conditions 10141586Simp * are met: 11141586Simp * 1. Redistributions of source code must retain the above copyright 12141586Simp * notice, this list of conditions and the following disclaimer. 13141586Simp * 2. Redistributions in binary form must reproduce the above copyright 14141586Simp * notice, this list of conditions and the following disclaimer in the 15141586Simp * documentation and/or other materials provided with the distribution. 16141586Simp * 3. All advertising materials mentioning features or use of this software 17141586Simp * must display the following acknowledgement: 18141586Simp * This product includes software developed by the University of 19141586Simp * California, Berkeley and its contributors. 20141586Simp * 4. Neither the name of the University nor the names of its contributors 21141586Simp * may be used to endorse or promote products derived from this software 22141586Simp * without specific prior written permission. 23141586Simp * 24141586Simp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25141586Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26141586Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27141586Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28141586Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29141586Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30141586Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31141586Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32141586Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33141586Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34141586Simp * SUCH DAMAGE. 35141586Simp * 36141586Simp * $Id: expand.c,v 1.18 1997/05/19 00:18:40 steve Exp $ 37141586Simp */ 38141586Simp 39141586Simp#ifndef lint 40141586Simpstatic char const sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; 41141586Simp#endif /* not lint */ 42141586Simp 43141586Simp#include <sys/types.h> 44141586Simp#include <sys/time.h> 45141586Simp#include <sys/stat.h> 46141586Simp#include <errno.h> 47141586Simp#include <dirent.h> 48141586Simp#include <unistd.h> 49141586Simp#include <pwd.h> 50141586Simp#include <stdlib.h> 51141586Simp#include <limits.h> 52141586Simp 53141586Simp/* 54141586Simp * Routines to expand arguments to commands. We have to deal with 55141586Simp * backquotes, shell variables, and file metacharacters. 56141586Simp */ 57141586Simp 58141586Simp#include "shell.h" 59141586Simp#include "main.h" 60141586Simp#include "nodes.h" 61141586Simp#include "eval.h" 62141586Simp#include "expand.h" 63154924Simp#include "syntax.h" 64154924Simp#include "parser.h" 65141586Simp#include "jobs.h" 66141586Simp#include "options.h" 67154895Simp#include "var.h" 68154924Simp#include "input.h" 69154924Simp#include "output.h" 70141586Simp#include "memalloc.h" 71141586Simp#include "error.h" 72141586Simp#include "mystring.h" 73141586Simp#include "arith.h" 74141586Simp#include "show.h" 75141586Simp 76141586Simp/* 77141586Simp * Structure specifying which parts of the string should be searched 78141586Simp * for IFS characters. 79141586Simp */ 80141586Simp 81141586Simpstruct ifsregion { 82141586Simp struct ifsregion *next; /* next region in list */ 83141586Simp int begoff; /* offset of start of region */ 84141586Simp int endoff; /* offset of end of region */ 85141586Simp int nulonly; /* search for nul bytes only */ 86141586Simp}; 87141586Simp 88141586Simp 89141586Simpchar *expdest; /* output of current string */ 90141586Simpstruct nodelist *argbackq; /* list of back quote expressions */ 91141586Simpstruct ifsregion ifsfirst; /* first struct in list of ifs regions */ 92141586Simpstruct ifsregion *ifslastp; /* last struct in list */ 93141586Simpstruct arglist exparg; /* holds expanded arg list */ 94141586Simp 95141586SimpSTATIC void argstr __P((char *, int)); 96141586SimpSTATIC char *exptilde __P((char *, int)); 97141586SimpSTATIC void expbackq __P((union node *, int, int)); 98141586SimpSTATIC int subevalvar __P((char *, char *, int, int, int, int)); 99141586SimpSTATIC char *evalvar __P((char *, int)); 100141586SimpSTATIC int varisset __P((char *, int)); 101141586SimpSTATIC void varvalue __P((char *, int, int)); 102141586SimpSTATIC void recordregion __P((int, int, int)); 103141586SimpSTATIC void ifsbreakup __P((char *, struct arglist *)); 104141586SimpSTATIC void expandmeta __P((struct strlist *, int)); 105141586SimpSTATIC void expmeta __P((char *, char *)); 106141586SimpSTATIC void addfname __P((char *)); 107141586SimpSTATIC struct strlist *expsort __P((struct strlist *)); 108141586SimpSTATIC struct strlist *msort __P((struct strlist *, int)); 109141586SimpSTATIC int pmatch __P((char *, char *)); 110141586SimpSTATIC char *cvtnum __P((int, char *)); 111141586SimpSTATIC int collate_range_cmp __P((int, int)); 112141586Simp 113141586SimpSTATIC int collate_range_cmp (c1, c2) 114141586Simp int c1, c2; 115141586Simp{ 116141586Simp static char s1[2], s2[2]; 117141586Simp int ret; 118141586Simp 119141586Simp c1 &= UCHAR_MAX; 120141586Simp c2 &= UCHAR_MAX; 121141586Simp if (c1 == c2) 122141586Simp return (0); 123141586Simp s1[0] = c1; 124141586Simp s2[0] = c2; 125141586Simp if ((ret = strcoll(s1, s2)) != 0) 126141586Simp return (ret); 127141586Simp return (c1 - c2); 128141586Simp} 129141586Simp 130141586Simp/* 131141586Simp * Expand shell variables and backquotes inside a here document. 132141586Simp */ 133141586Simp 134141586Simpvoid 135141586Simpexpandhere(arg, fd) 136141586Simp union node *arg; /* the document */ 137141586Simp int fd; /* where to write the expanded version */ 138141586Simp { 139141586Simp herefd = fd; 140141586Simp expandarg(arg, (struct arglist *)NULL, 0); 141141586Simp xwrite(fd, stackblock(), expdest - stackblock()); 142141586Simp} 143141586Simp 144141586Simp 145141586Simp/* 146141586Simp * Perform variable substitution and command substitution on an argument, 147141586Simp * placing the resulting list of arguments in arglist. If EXP_FULL is true, 148141586Simp * perform splitting and file name expansion. When arglist is NULL, perform 149154894Simp * here document expansion. 150141586Simp */ 151141586Simp 152141586Simpvoid 153141586Simpexpandarg(arg, arglist, flag) 154141586Simp union node *arg; 155141586Simp struct arglist *arglist; 156141586Simp int flag; 157147256Sbrooks{ 158141586Simp struct strlist *sp; 159141586Simp char *p; 160141586Simp 161141586Simp argbackq = arg->narg.backquote; 162141586Simp STARTSTACKSTR(expdest); 163154894Simp ifsfirst.next = NULL; 164141586Simp ifslastp = NULL; 165141586Simp argstr(arg->narg.text, flag); 166141586Simp if (arglist == NULL) { 167141586Simp return; /* here document expanded */ 168141586Simp } 169141586Simp STPUTC('\0', expdest); 170141586Simp p = grabstackstr(expdest); 171141586Simp exparg.lastp = &exparg.list; 172154894Simp /* 173141586Simp * TODO - EXP_REDIR 174141586Simp */ 175141586Simp if (flag & EXP_FULL) { 176141586Simp ifsbreakup(p, &exparg); 177141586Simp *exparg.lastp = NULL; 178141586Simp exparg.lastp = &exparg.list; 179141586Simp expandmeta(exparg.list, flag); 180141586Simp } else { 181141586Simp if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 182141586Simp rmescapes(p); 183141586Simp sp = (struct strlist *)stalloc(sizeof (struct strlist)); 184141586Simp sp->text = p; 185141586Simp *exparg.lastp = sp; 186141586Simp exparg.lastp = &sp->next; 187141586Simp } 188141586Simp while (ifsfirst.next != NULL) { 189141586Simp struct ifsregion *ifsp; 190141586Simp INTOFF; 191141586Simp ifsp = ifsfirst.next->next; 192141586Simp ckfree(ifsfirst.next); 193141586Simp ifsfirst.next = ifsp; 194141586Simp INTON; 195141586Simp } 196141586Simp *exparg.lastp = NULL; 197141586Simp if (exparg.list) { 198141586Simp *arglist->lastp = exparg.list; 199141586Simp arglist->lastp = exparg.lastp; 200141586Simp } 201141586Simp} 202154894Simp 203141586Simp 204141586Simp 205141586Simp/* 206141586Simp * Perform variable and command substitution. If EXP_FULL is set, output CTLESC 207141586Simp * characters to allow for further processing. Otherwise treat 208141586Simp * $@ like $* since no splitting will be performed. 209141586Simp */ 210141586Simp 211141586SimpSTATIC void 212141586Simpargstr(p, flag) 213141586Simp char *p; 214141586Simp int flag; 215141586Simp{ 216141586Simp char c; 217154894Simp int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ 218141586Simp int firsteq = 1; 219141586Simp 220141586Simp if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 221141586Simp p = exptilde(p, flag); 222141586Simp for (;;) { 223141586Simp switch (c = *p++) { 224141586Simp case '\0': 225141586Simp case CTLENDVAR: /* ??? */ 226141586Simp goto breakloop; 227141586Simp case CTLESC: 228141586Simp if (quotes) 229141586Simp STPUTC(c, expdest); 230141586Simp c = *p++; 231141586Simp STPUTC(c, expdest); 232141586Simp break; 233141586Simp case CTLVAR: 234141586Simp p = evalvar(p, flag); 235141586Simp break; 236141586Simp case CTLBACKQ: 237141586Simp case CTLBACKQ|CTLQUOTE: 238141586Simp expbackq(argbackq->n, c & CTLQUOTE, flag); 239141586Simp argbackq = argbackq->next; 240141586Simp break; 241141586Simp case CTLENDARI: 242141586Simp expari(flag); 243141586Simp break; 244141586Simp case ':': 245141586Simp case '=': 246141586Simp /* 247141586Simp * sort of a hack - expand tildes in variable 248141586Simp * assignments (after the first '=' and after ':'s). 249141586Simp */ 250141586Simp STPUTC(c, expdest); 251141586Simp if (flag & EXP_VARTILDE && *p == '~') { 252141586Simp if (c == '=') { 253141586Simp if (firsteq) 254141586Simp firsteq = 0; 255141586Simp else 256141586Simp break; 257141586Simp } 258141586Simp p = exptilde(p, flag); 259141586Simp } 260141586Simp break; 261141586Simp default: 262141586Simp STPUTC(c, expdest); 263141586Simp } 264141586Simp } 265141586Simpbreakloop:; 266141586Simp} 267141586Simp 268141586SimpSTATIC char * 269141586Simpexptilde(p, flag) 270141586Simp char *p; 271141586Simp int flag; 272141586Simp{ 273141586Simp char c, *startp = p; 274141586Simp struct passwd *pw; 275154894Simp char *home; 276141586Simp int quotes = flag & (EXP_FULL | EXP_CASE); 277141586Simp 278141586Simp while ((c = *p) != '\0') { 279141586Simp switch(c) { 280141586Simp case CTLESC: 281141586Simp return (startp); 282141586Simp case ':': 283141586Simp if (flag & EXP_VARTILDE) 284141586Simp goto done; 285141586Simp break; 286141586Simp case '/': 287141586Simp goto done; 288141586Simp } 289141586Simp p++; 290141586Simp } 291141586Simpdone: 292141586Simp *p = '\0'; 293141586Simp if (*(startp+1) == '\0') { 294141586Simp if ((home = lookupvar("HOME")) == NULL) 295141586Simp goto lose; 296141586Simp } else { 297141586Simp if ((pw = getpwnam(startp+1)) == NULL) 298141586Simp goto lose; 299141586Simp home = pw->pw_dir; 300141586Simp } 301141586Simp if (*home == '\0') 302141586Simp goto lose; 303141586Simp *p = c; 304141586Simp while ((c = *home++) != '\0') { 305141586Simp if (quotes && SQSYNTAX[c] == CCTL) 306141586Simp STPUTC(CTLESC, expdest); 307141586Simp STPUTC(c, expdest); 308141586Simp } 309141586Simp return (p); 310141586Simplose: 311141586Simp *p = c; 312141586Simp return (startp); 313141586Simp} 314141586Simp 315141586Simp 316141586Simp/* 317141586Simp * Expand arithmetic expression. Backup to start of expression, 318141586Simp * evaluate, place result in (backed up) result, adjust string position. 319141586Simp */ 320141586Simpvoid 321141586Simpexpari(flag) 322141586Simp int flag; 323141586Simp{ 324141586Simp char *p, *start; 325141586Simp int result; 326141586Simp int quotes = flag & (EXP_FULL | EXP_CASE); 327141586Simp 328141586Simp while (ifsfirst.next != NULL) { 329141586Simp struct ifsregion *ifsp; 330141586Simp INTOFF; 331141586Simp ifsp = ifsfirst.next->next; 332141586Simp ckfree(ifsfirst.next); 333141586Simp ifsfirst.next = ifsp; 334141586Simp INTON; 335141586Simp } 336141586Simp ifslastp = NULL; 337141586Simp 338141586Simp /* 339141586Simp * This routine is slightly over-compilcated for 340141586Simp * efficiency. First we make sure there is 341141586Simp * enough space for the result, which may be bigger 342141586Simp * than the expression if we add exponentation. Next we 343141586Simp * scan backwards looking for the start of arithmetic. If the 344141586Simp * next previous character is a CTLESC character, then we 345141586Simp * have to rescan starting from the beginning since CTLESC 346141586Simp * characters have to be processed left to right. 347141586Simp */ 348141586Simp#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10 349141586Simp#error "integers with more than 10 digits are not supported" 350141586Simp#endif 351141586Simp CHECKSTRSPACE(12 - 2, expdest); 352141586Simp USTPUTC('\0', expdest); 353141586Simp start = stackblock(); 354141586Simp p = expdest; 355141586Simp while (*p != CTLARI && p >= start) 356141586Simp --p; 357141586Simp if (*p != CTLARI) 358141586Simp error("missing CTLARI (shouldn't happen)"); 359141586Simp if (p > start && *(p-1) == CTLESC) 360154894Simp for (p = start; *p != CTLARI; p++) 361141586Simp if (*p == CTLESC) 362141586Simp p++; 363154895Simp if (quotes) 364154924Simp rmescapes(p+1); 365154924Simp result = arith(p+1); 366141586Simp fmtstr(p, 12, "%d", result); 367141586Simp while (*p++) 368141586Simp ; 369141586Simp result = expdest - p + 1; 370141586Simp STADJUST(-result, expdest); 371141586Simp} 372141586Simp 373154895Simp 374141586Simp/* 375141586Simp * Expand stuff in backwards quotes. 376147256Sbrooks */ 377141586Simp 378141586SimpSTATIC void 379141586Simpexpbackq(cmd, quoted, flag) 380141586Simp union node *cmd; 381141586Simp int quoted; 382154892Simp int flag; 383141586Simp{ 384141586Simp struct backcmd in; 385141586Simp int i; 386141586Simp char buf[128]; 387141586Simp char *p; 388141586Simp char *dest = expdest; 389141586Simp struct ifsregion saveifs, *savelastp; 390141586Simp struct nodelist *saveargbackq; 391141586Simp char lastc; 392141586Simp int startloc = dest - stackblock(); 393141586Simp char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 394141586Simp int saveherefd; 395141586Simp int quotes = flag & (EXP_FULL | EXP_CASE); 396141586Simp 397141586Simp INTOFF; 398141586Simp saveifs = ifsfirst; 399141586Simp savelastp = ifslastp; 400141586Simp saveargbackq = argbackq; 401141586Simp saveherefd = herefd; 402141586Simp herefd = -1; 403141586Simp p = grabstackstr(dest); 404141586Simp evalbackcmd(cmd, &in); 405141586Simp ungrabstackstr(p, dest); 406141586Simp ifsfirst = saveifs; 407141586Simp ifslastp = savelastp; 408141586Simp argbackq = saveargbackq; 409141586Simp herefd = saveherefd; 410141586Simp 411141586Simp p = in.buf; 412141586Simp lastc = '\0'; 413141586Simp for (;;) { 414141586Simp if (--in.nleft < 0) { 415141586Simp if (in.fd < 0) 416141586Simp break; 417141586Simp while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 418141586Simp TRACE(("expbackq: read returns %d\n", i)); 419141586Simp if (i <= 0) 420154924Simp break; 421149558Simp p = buf; 422149558Simp in.nleft = i - 1; 423141586Simp } 424141586Simp lastc = *p++; 425141586Simp if (lastc != '\0') { 426141586Simp if (quotes && syntax[lastc] == CCTL) 427141586Simp STPUTC(CTLESC, dest); 428141586Simp STPUTC(lastc, dest); 429141586Simp } 430141586Simp } 431141586Simp 432141586Simp /* Eat all trailing newlines */ 433141586Simp for (p--; lastc == '\n'; lastc = *--p) 434141586Simp STUNPUTC(dest); 435141586Simp 436141586Simp if (in.fd >= 0) 437141586Simp close(in.fd); 438141586Simp if (in.buf) 439141586Simp ckfree(in.buf); 440141586Simp if (in.jp) 441141586Simp exitstatus = waitforjob(in.jp); 442141586Simp if (quoted == 0) 443141586Simp recordregion(startloc, dest - stackblock(), 0); 444141586Simp TRACE(("evalbackq: size=%d: \"%.*s\"\n", 445141586Simp (dest - stackblock()) - startloc, 446149558Simp (dest - stackblock()) - startloc, 447149558Simp stackblock() + startloc)); 448149558Simp expdest = dest; 449149558Simp INTON; 450141586Simp} 451141586Simp 452141586Simp 453141586Simp 454141586SimpSTATIC int 455141586Simpsubevalvar(p, str, strloc, subtype, startloc, varflags) 456141586Simp char *p; 457141586Simp char *str; 458141586Simp int strloc; 459141586Simp int subtype; 460141586Simp int startloc; 461141586Simp int varflags; 462141586Simp{ 463141586Simp char *startp; 464141586Simp char *loc = NULL; 465141586Simp int c = 0; 466141586Simp int saveherefd = herefd; 467141586Simp struct nodelist *saveargbackq = argbackq; 468141586Simp int amount; 469141586Simp 470141586Simp herefd = -1; 471141586Simp argstr(p, 0); 472141586Simp STACKSTRNUL(expdest); 473141586Simp herefd = saveherefd; 474141586Simp argbackq = saveargbackq; 475141586Simp startp = stackblock() + startloc; 476141586Simp if (str == NULL) 477141586Simp str = stackblock() + strloc; 478141586Simp 479141586Simp switch (subtype) { 480141586Simp case VSASSIGN: 481141586Simp setvar(str, startp, 0); 482141586Simp amount = startp - expdest; 483141586Simp STADJUST(amount, expdest); 484141586Simp varflags &= ~VSNUL; 485141586Simp if (c != 0) 486141586Simp *loc = c; 487141586Simp return 1; 488141586Simp 489141586Simp case VSQUESTION: 490141586Simp if (*p != CTLENDVAR) { 491141586Simp outfmt(&errout, "%s\n", startp); 492141586Simp error((char *)NULL); 493141586Simp } 494141586Simp error("%.*s: parameter %snot set", p - str - 1, 495141586Simp str, (varflags & VSNUL) ? "null or " 496141586Simp : nullstr); 497141586Simp return 0; 498141586Simp 499141586Simp case VSTRIMLEFT: 500141586Simp for (loc = startp; loc < str; loc++) { 501141586Simp c = *loc; 502141586Simp *loc = '\0'; 503141586Simp if (patmatch(str, startp)) { 504141586Simp *loc = c; 505141586Simp goto recordleft; 506141586Simp } 507141586Simp *loc = c; 508141586Simp } 509141586Simp return 0; 510141586Simp 511141586Simp case VSTRIMLEFTMAX: 512141586Simp for (loc = str - 1; loc >= startp; loc--) { 513141586Simp c = *loc; 514141586Simp *loc = '\0'; 515141586Simp if (patmatch(str, startp)) { 516141586Simp *loc = c; 517141586Simp goto recordleft; 518141586Simp } 519141586Simp *loc = c; 520141586Simp } 521141586Simp return 0; 522141586Simp 523141586Simp case VSTRIMRIGHT: 524141586Simp for (loc = str - 1; loc >= startp; loc--) { 525141586Simp if (patmatch(str, loc)) { 526141586Simp amount = loc - expdest; 527141586Simp STADJUST(amount, expdest); 528141586Simp return 1; 529141586Simp } 530141586Simp } 531141586Simp return 0; 532141586Simp 533141586Simp case VSTRIMRIGHTMAX: 534141586Simp for (loc = startp; loc < str - 1; loc++) { 535141586Simp if (patmatch(str, loc)) { 536141586Simp amount = loc - expdest; 537141586Simp STADJUST(amount, expdest); 538141586Simp return 1; 539141586Simp } 540141586Simp } 541141586Simp return 0; 542141586Simp 543141586Simp 544141586Simp default: 545141586Simp abort(); 546141586Simp } 547141586Simp 548141586Simprecordleft: 549141586Simp amount = ((str - 1) - (loc - startp)) - expdest; 550141586Simp STADJUST(amount, expdest); 551141586Simp while (loc != str - 1) 552141586Simp *startp++ = *loc++; 553141586Simp return 1; 554141586Simp} 555141586Simp 556154924Simp 557154924Simp/* 558141586Simp * Expand a variable, and return a pointer to the next character in the 559141586Simp * input string. 560141586Simp */ 561141586Simp 562141586SimpSTATIC char * 563141586Simpevalvar(p, flag) 564141586Simp char *p; 565141586Simp int flag; 566141586Simp{ 567264942Smarius int subtype; 568264942Smarius int varflags; 569141586Simp char *var; 570264942Smarius char *val; 571264942Smarius char *pat; 572141586Simp int c; 573141586Simp int set; 574141586Simp int special; 575141586Simp int startloc; 576141586Simp int varlen; 577141586Simp int easy; 578141586Simp int quotes = flag & (EXP_FULL | EXP_CASE); 579141586Simp 580141586Simp varflags = *p++; 581141586Simp subtype = varflags & VSTYPE; 582141586Simp var = p; 583141586Simp special = 0; 584141586Simp if (! is_name(*p)) 585141586Simp special = 1; 586141586Simp p = strchr(p, '=') + 1; 587141586Simpagain: /* jump here after setting a variable with ${var=text} */ 588141586Simp if (special) { 589141586Simp set = varisset(var, varflags & VSNUL); 590141586Simp val = NULL; 591141586Simp } else { 592141586Simp val = lookupvar(var); 593141586Simp if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 594141586Simp val = NULL; 595141586Simp set = 0; 596141586Simp } else 597141586Simp set = 1; 598141586Simp } 599141586Simp varlen = 0; 600141586Simp startloc = expdest - stackblock(); 601141586Simp if (set && subtype != VSPLUS) { 602141586Simp /* insert the value of the variable */ 603141586Simp if (special) { 604141586Simp varvalue(var, varflags & VSQUOTE, flag & EXP_FULL); 605141586Simp if (subtype == VSLENGTH) { 606141586Simp varlen = expdest - stackblock() - startloc; 607141586Simp STADJUST(-varlen, expdest); 608141586Simp } 609141586Simp } else { 610141586Simp char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 611141586Simp : BASESYNTAX; 612141586Simp 613141586Simp if (subtype == VSLENGTH) { 614141586Simp for (;*val; val++) 615141586Simp varlen++; 616141586Simp } 617141586Simp else { 618141586Simp while (*val) { 619141586Simp if (quotes && syntax[*val] == CCTL) 620141586Simp STPUTC(CTLESC, expdest); 621141586Simp STPUTC(*val++, expdest); 622141586Simp } 623141586Simp 624141586Simp } 625141586Simp } 626141586Simp } 627141586Simp 628141586Simp if (subtype == VSPLUS) 629141586Simp set = ! set; 630141586Simp 631141586Simp easy = ((varflags & VSQUOTE) == 0 || 632141586Simp (*var == '@' && shellparam.nparam != 1)); 633141586Simp 634141586Simp 635141586Simp switch (subtype) { 636141586Simp case VSLENGTH: 637141586Simp expdest = cvtnum(varlen, expdest); 638141586Simp goto record; 639141586Simp 640141586Simp case VSNORMAL: 641141586Simp if (!easy) 642141586Simp break; 643141586Simprecord: 644141586Simp recordregion(startloc, expdest - stackblock(), 645141586Simp varflags & VSQUOTE); 646141586Simp break; 647141586Simp 648141586Simp case VSPLUS: 649141586Simp case VSMINUS: 650141586Simp if (!set) { 651141586Simp argstr(p, flag); 652141586Simp break; 653141586Simp } 654141586Simp if (easy) 655141586Simp goto record; 656141586Simp break; 657141586Simp 658141586Simp case VSTRIMLEFT: 659141586Simp case VSTRIMLEFTMAX: 660141586Simp case VSTRIMRIGHT: 661141586Simp case VSTRIMRIGHTMAX: 662141586Simp if (!set) 663141586Simp break; 664141586Simp /* 665141586Simp * Terminate the string and start recording the pattern 666141586Simp * right after it 667141586Simp */ 668141586Simp STPUTC('\0', expdest); 669141586Simp pat = expdest; 670141586Simp if (subevalvar(p, NULL, expdest - stackblock(), subtype, 671141586Simp startloc, varflags)) 672141586Simp goto record; 673141586Simp else { 674141586Simp int amount = (expdest - pat) + 1; 675141586Simp STADJUST(-amount, expdest); 676141586Simp } 677 break; 678 679 case VSASSIGN: 680 case VSQUESTION: 681 if (!set) { 682 if (subevalvar(p, var, 0, subtype, startloc, varflags)) { 683 varflags &= ~VSNUL; 684 goto again; 685 } 686 break; 687 } 688 if (easy) 689 goto record; 690 break; 691 692 default: 693 abort(); 694 } 695 696 if (subtype != VSNORMAL) { /* skip to end of alternative */ 697 int nesting = 1; 698 for (;;) { 699 if ((c = *p++) == CTLESC) 700 p++; 701 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 702 if (set) 703 argbackq = argbackq->next; 704 } else if (c == CTLVAR) { 705 if ((*p++ & VSTYPE) != VSNORMAL) 706 nesting++; 707 } else if (c == CTLENDVAR) { 708 if (--nesting == 0) 709 break; 710 } 711 } 712 } 713 return p; 714} 715 716 717 718/* 719 * Test whether a specialized variable is set. 720 */ 721 722STATIC int 723varisset(name, nulok) 724 char *name; 725 int nulok; 726{ 727 728 if (*name == '!') 729 return backgndpid != -1; 730 else if (*name == '@' || *name == '*') { 731 if (*shellparam.p == NULL) 732 return 0; 733 734 if (nulok) { 735 char **av; 736 737 for (av = shellparam.p; *av; av++) 738 if (**av != '\0') 739 return 1; 740 return 0; 741 } 742 } else if (is_digit(*name)) { 743 char *ap; 744 int num = atoi(name); 745 746 if (num > shellparam.nparam) 747 return 0; 748 749 if (num == 0) 750 ap = arg0; 751 else 752 ap = shellparam.p[num - 1]; 753 754 if (nulok && (ap == NULL || *ap == '\0')) 755 return 0; 756 } 757 return 1; 758} 759 760 761 762/* 763 * Add the value of a specialized variable to the stack string. 764 */ 765 766STATIC void 767varvalue(name, quoted, allow_split) 768 char *name; 769 int quoted; 770 int allow_split; 771{ 772 int num; 773 char *p; 774 int i; 775 extern int oexitstatus; 776 char sep; 777 char **ap; 778 char const *syntax; 779 780#define STRTODEST(p) \ 781 do {\ 782 if (allow_split) { \ 783 syntax = quoted? DQSYNTAX : BASESYNTAX; \ 784 while (*p) { \ 785 if (syntax[*p] == CCTL) \ 786 STPUTC(CTLESC, expdest); \ 787 STPUTC(*p++, expdest); \ 788 } \ 789 } else \ 790 while (*p) \ 791 STPUTC(*p++, expdest); \ 792 } while (0) 793 794 795 switch (*name) { 796 case '$': 797 num = rootpid; 798 goto numvar; 799 case '?': 800 num = oexitstatus; 801 goto numvar; 802 case '#': 803 num = shellparam.nparam; 804 goto numvar; 805 case '!': 806 num = backgndpid; 807numvar: 808 expdest = cvtnum(num, expdest); 809 break; 810 case '-': 811 for (i = 0 ; i < NOPTS ; i++) { 812 if (optlist[i].val) 813 STPUTC(optlist[i].letter, expdest); 814 } 815 break; 816 case '@': 817 if (allow_split) { 818 sep = '\0'; 819 goto allargs; 820 } 821 /* fall through */ 822 case '*': 823 sep = ' '; 824allargs: 825 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 826 STRTODEST(p); 827 if (*ap) 828 STPUTC(sep, expdest); 829 } 830 break; 831 case '0': 832 p = arg0; 833 STRTODEST(p); 834 break; 835 default: 836 if (is_digit(*name)) { 837 num = atoi(name); 838 if (num > 0 && num <= shellparam.nparam) { 839 p = shellparam.p[num - 1]; 840 STRTODEST(p); 841 } 842 } 843 break; 844 } 845} 846 847 848 849/* 850 * Record the the fact that we have to scan this region of the 851 * string for IFS characters. 852 */ 853 854STATIC void 855recordregion(start, end, nulonly) 856 int start; 857 int end; 858 int nulonly; 859{ 860 struct ifsregion *ifsp; 861 862 if (ifslastp == NULL) { 863 ifsp = &ifsfirst; 864 } else { 865 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 866 ifslastp->next = ifsp; 867 } 868 ifslastp = ifsp; 869 ifslastp->next = NULL; 870 ifslastp->begoff = start; 871 ifslastp->endoff = end; 872 ifslastp->nulonly = nulonly; 873} 874 875 876 877/* 878 * Break the argument string into pieces based upon IFS and add the 879 * strings to the argument list. The regions of the string to be 880 * searched for IFS characters have been stored by recordregion. 881 */ 882STATIC void 883ifsbreakup(string, arglist) 884 char *string; 885 struct arglist *arglist; 886 { 887 struct ifsregion *ifsp; 888 struct strlist *sp; 889 char *start; 890 char *p; 891 char *q; 892 char *ifs; 893 int ifsspc; 894 895 896 start = string; 897 if (ifslastp != NULL) { 898 ifsp = &ifsfirst; 899 do { 900 p = string + ifsp->begoff; 901 ifs = ifsp->nulonly? nullstr : ifsval(); 902 ifsspc = strchr(ifs, ' ') != NULL; 903 while (p < string + ifsp->endoff) { 904 q = p; 905 if (*p == CTLESC) 906 p++; 907 if (strchr(ifs, *p++)) { 908 if (q > start || !ifsspc) { 909 *q = '\0'; 910 sp = (struct strlist *)stalloc(sizeof *sp); 911 sp->text = start; 912 *arglist->lastp = sp; 913 arglist->lastp = &sp->next; 914 } 915 if (ifsspc) { 916 for (;;) { 917 if (p >= string + ifsp->endoff) 918 break; 919 q = p; 920 if (*p == CTLESC) 921 p++; 922 if (strchr(ifs, *p++) == NULL) { 923 p = q; 924 break; 925 } 926 } 927 } 928 start = p; 929 } 930 } 931 } while ((ifsp = ifsp->next) != NULL); 932 if (*start || (!ifsspc && start > string)) { 933 sp = (struct strlist *)stalloc(sizeof *sp); 934 sp->text = start; 935 *arglist->lastp = sp; 936 arglist->lastp = &sp->next; 937 } 938 } else { 939 sp = (struct strlist *)stalloc(sizeof *sp); 940 sp->text = start; 941 *arglist->lastp = sp; 942 arglist->lastp = &sp->next; 943 } 944} 945 946 947 948/* 949 * Expand shell metacharacters. At this point, the only control characters 950 * should be escapes. The results are stored in the list exparg. 951 */ 952 953char *expdir; 954 955 956STATIC void 957expandmeta(str, flag) 958 struct strlist *str; 959 int flag __unused; 960{ 961 char *p; 962 struct strlist **savelastp; 963 struct strlist *sp; 964 char c; 965 /* TODO - EXP_REDIR */ 966 967 while (str) { 968 if (fflag) 969 goto nometa; 970 p = str->text; 971 for (;;) { /* fast check for meta chars */ 972 if ((c = *p++) == '\0') 973 goto nometa; 974 if (c == '*' || c == '?' || c == '[' || c == '!') 975 break; 976 } 977 savelastp = exparg.lastp; 978 INTOFF; 979 if (expdir == NULL) { 980 int i = strlen(str->text); 981 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ 982 } 983 984 expmeta(expdir, str->text); 985 ckfree(expdir); 986 expdir = NULL; 987 INTON; 988 if (exparg.lastp == savelastp) { 989 /* 990 * no matches 991 */ 992nometa: 993 *exparg.lastp = str; 994 rmescapes(str->text); 995 exparg.lastp = &str->next; 996 } else { 997 *exparg.lastp = NULL; 998 *savelastp = sp = expsort(*savelastp); 999 while (sp->next != NULL) 1000 sp = sp->next; 1001 exparg.lastp = &sp->next; 1002 } 1003 str = str->next; 1004 } 1005} 1006 1007 1008/* 1009 * Do metacharacter (i.e. *, ?, [...]) expansion. 1010 */ 1011 1012STATIC void 1013expmeta(enddir, name) 1014 char *enddir; 1015 char *name; 1016 { 1017 char *p; 1018 char *q; 1019 char *start; 1020 char *endname; 1021 int metaflag; 1022 struct stat statb; 1023 DIR *dirp; 1024 struct dirent *dp; 1025 int atend; 1026 int matchdot; 1027 1028 metaflag = 0; 1029 start = name; 1030 for (p = name ; ; p++) { 1031 if (*p == '*' || *p == '?') 1032 metaflag = 1; 1033 else if (*p == '[') { 1034 q = p + 1; 1035 if (*q == '!' || *q == '^') 1036 q++; 1037 for (;;) { 1038 if (*q == CTLESC) 1039 q++; 1040 if (*q == '/' || *q == '\0') 1041 break; 1042 if (*++q == ']') { 1043 metaflag = 1; 1044 break; 1045 } 1046 } 1047 } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { 1048 metaflag = 1; 1049 } else if (*p == '\0') 1050 break; 1051 else if (*p == CTLESC) 1052 p++; 1053 if (*p == '/') { 1054 if (metaflag) 1055 break; 1056 start = p + 1; 1057 } 1058 } 1059 if (metaflag == 0) { /* we've reached the end of the file name */ 1060 if (enddir != expdir) 1061 metaflag++; 1062 for (p = name ; ; p++) { 1063 if (*p == CTLESC) 1064 p++; 1065 *enddir++ = *p; 1066 if (*p == '\0') 1067 break; 1068 } 1069 if (metaflag == 0 || stat(expdir, &statb) >= 0) 1070 addfname(expdir); 1071 return; 1072 } 1073 endname = p; 1074 if (start != name) { 1075 p = name; 1076 while (p < start) { 1077 if (*p == CTLESC) 1078 p++; 1079 *enddir++ = *p++; 1080 } 1081 } 1082 if (enddir == expdir) { 1083 p = "."; 1084 } else if (enddir == expdir + 1 && *expdir == '/') { 1085 p = "/"; 1086 } else { 1087 p = expdir; 1088 enddir[-1] = '\0'; 1089 } 1090 if ((dirp = opendir(p)) == NULL) 1091 return; 1092 if (enddir != expdir) 1093 enddir[-1] = '/'; 1094 if (*endname == 0) { 1095 atend = 1; 1096 } else { 1097 atend = 0; 1098 *endname++ = '\0'; 1099 } 1100 matchdot = 0; 1101 if (start[0] == '.' || (start[0] == CTLESC && start[1] == '.')) 1102 matchdot++; 1103 while (! int_pending() && (dp = readdir(dirp)) != NULL) { 1104 if (dp->d_name[0] == '.' && ! matchdot) 1105 continue; 1106 if (patmatch(start, dp->d_name)) { 1107 if (atend) { 1108 scopy(dp->d_name, enddir); 1109 addfname(expdir); 1110 } else { 1111 char *q; 1112 for (p = enddir, q = dp->d_name; 1113 (*p++ = *q++) != '\0';) 1114 continue; 1115 p[-1] = '/'; 1116 expmeta(p, endname); 1117 } 1118 } 1119 } 1120 closedir(dirp); 1121 if (! atend) 1122 endname[-1] = '/'; 1123} 1124 1125 1126/* 1127 * Add a file name to the list. 1128 */ 1129 1130STATIC void 1131addfname(name) 1132 char *name; 1133 { 1134 char *p; 1135 struct strlist *sp; 1136 1137 p = stalloc(strlen(name) + 1); 1138 scopy(name, p); 1139 sp = (struct strlist *)stalloc(sizeof *sp); 1140 sp->text = p; 1141 *exparg.lastp = sp; 1142 exparg.lastp = &sp->next; 1143} 1144 1145 1146/* 1147 * Sort the results of file name expansion. It calculates the number of 1148 * strings to sort and then calls msort (short for merge sort) to do the 1149 * work. 1150 */ 1151 1152STATIC struct strlist * 1153expsort(str) 1154 struct strlist *str; 1155 { 1156 int len; 1157 struct strlist *sp; 1158 1159 len = 0; 1160 for (sp = str ; sp ; sp = sp->next) 1161 len++; 1162 return msort(str, len); 1163} 1164 1165 1166STATIC struct strlist * 1167msort(list, len) 1168 struct strlist *list; 1169 int len; 1170{ 1171 struct strlist *p, *q = NULL; 1172 struct strlist **lpp; 1173 int half; 1174 int n; 1175 1176 if (len <= 1) 1177 return list; 1178 half = len >> 1; 1179 p = list; 1180 for (n = half ; --n >= 0 ; ) { 1181 q = p; 1182 p = p->next; 1183 } 1184 q->next = NULL; /* terminate first half of list */ 1185 q = msort(list, half); /* sort first half of list */ 1186 p = msort(p, len - half); /* sort second half */ 1187 lpp = &list; 1188 for (;;) { 1189 if (strcmp(p->text, q->text) < 0) { 1190 *lpp = p; 1191 lpp = &p->next; 1192 if ((p = *lpp) == NULL) { 1193 *lpp = q; 1194 break; 1195 } 1196 } else { 1197 *lpp = q; 1198 lpp = &q->next; 1199 if ((q = *lpp) == NULL) { 1200 *lpp = p; 1201 break; 1202 } 1203 } 1204 } 1205 return list; 1206} 1207 1208 1209 1210/* 1211 * Returns true if the pattern matches the string. 1212 */ 1213 1214int 1215patmatch(pattern, string) 1216 char *pattern; 1217 char *string; 1218 { 1219#ifdef notdef 1220 if (pattern[0] == '!' && pattern[1] == '!') 1221 return 1 - pmatch(pattern + 2, string); 1222 else 1223#endif 1224 return pmatch(pattern, string); 1225} 1226 1227 1228STATIC int 1229pmatch(pattern, string) 1230 char *pattern; 1231 char *string; 1232 { 1233 char *p, *q; 1234 char c; 1235 1236 p = pattern; 1237 q = string; 1238 for (;;) { 1239 switch (c = *p++) { 1240 case '\0': 1241 goto breakloop; 1242 case CTLESC: 1243 if (*q++ != *p++) 1244 return 0; 1245 break; 1246 case '?': 1247 if (*q++ == '\0') 1248 return 0; 1249 break; 1250 case '*': 1251 c = *p; 1252 if (c != CTLESC && c != '?' && c != '*' && c != '[') { 1253 while (*q != c) { 1254 if (*q == '\0') 1255 return 0; 1256 q++; 1257 } 1258 } 1259 do { 1260 if (pmatch(p, q)) 1261 return 1; 1262 } while (*q++ != '\0'); 1263 return 0; 1264 case '[': { 1265 char *endp; 1266 int invert, found; 1267 char chr; 1268 1269 endp = p; 1270 if (*endp == '!' || *endp == '^') 1271 endp++; 1272 for (;;) { 1273 if (*endp == '\0') 1274 goto dft; /* no matching ] */ 1275 if (*endp == CTLESC) 1276 endp++; 1277 if (*++endp == ']') 1278 break; 1279 } 1280 invert = 0; 1281 if (*p == '!' || *p == '^') { 1282 invert++; 1283 p++; 1284 } 1285 found = 0; 1286 chr = *q++; 1287 if (chr == '\0') 1288 return 0; 1289 c = *p++; 1290 do { 1291 if (c == CTLESC) 1292 c = *p++; 1293 if (*p == '-' && p[1] != ']') { 1294 p++; 1295 if (*p == CTLESC) 1296 p++; 1297 if ( collate_range_cmp(chr, c) >= 0 1298 && collate_range_cmp(chr, *p) <= 0 1299 ) 1300 found = 1; 1301 p++; 1302 } else { 1303 if (chr == c) 1304 found = 1; 1305 } 1306 } while ((c = *p++) != ']'); 1307 if (found == invert) 1308 return 0; 1309 break; 1310 } 1311dft: default: 1312 if (*q++ != c) 1313 return 0; 1314 break; 1315 } 1316 } 1317breakloop: 1318 if (*q != '\0') 1319 return 0; 1320 return 1; 1321} 1322 1323 1324 1325/* 1326 * Remove any CTLESC characters from a string. 1327 */ 1328 1329void 1330rmescapes(str) 1331 char *str; 1332 { 1333 char *p, *q; 1334 1335 p = str; 1336 while (*p != CTLESC) { 1337 if (*p++ == '\0') 1338 return; 1339 } 1340 q = p; 1341 while (*p) { 1342 if (*p == CTLESC) 1343 p++; 1344 *q++ = *p++; 1345 } 1346 *q = '\0'; 1347} 1348 1349 1350 1351/* 1352 * See if a pattern matches in a case statement. 1353 */ 1354 1355int 1356casematch(pattern, val) 1357 union node *pattern; 1358 char *val; 1359 { 1360 struct stackmark smark; 1361 int result; 1362 char *p; 1363 1364 setstackmark(&smark); 1365 argbackq = pattern->narg.backquote; 1366 STARTSTACKSTR(expdest); 1367 ifslastp = NULL; 1368 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 1369 STPUTC('\0', expdest); 1370 p = grabstackstr(expdest); 1371 result = patmatch(p, val); 1372 popstackmark(&smark); 1373 return result; 1374} 1375 1376/* 1377 * Our own itoa(). 1378 */ 1379 1380STATIC char * 1381cvtnum(num, buf) 1382 int num; 1383 char *buf; 1384 { 1385 char temp[32]; 1386 int neg = num < 0; 1387 char *p = temp + 31; 1388 1389 temp[31] = '\0'; 1390 1391 do { 1392 *--p = num % 10 + '0'; 1393 } while ((num /= 10) != 0); 1394 1395 if (neg) 1396 *--p = '-'; 1397 1398 while (*p) 1399 STPUTC(*p++, buf); 1400 return buf; 1401} 1402