| 1 | /* $NetBSD: expand.c,v 1.71 2005/06/01 15:41:19 lukem Exp $ */
|
|---|
| 2 |
|
|---|
| 3 | /*-
|
|---|
| 4 | * Copyright (c) 1991, 1993
|
|---|
| 5 | * The Regents of the University of California. All rights reserved.
|
|---|
| 6 | *
|
|---|
| 7 | * This code is derived from software contributed to Berkeley by
|
|---|
| 8 | * Kenneth Almquist.
|
|---|
| 9 | *
|
|---|
| 10 | * Redistribution and use in source and binary forms, with or without
|
|---|
| 11 | * modification, are permitted provided that the following conditions
|
|---|
| 12 | * are met:
|
|---|
| 13 | * 1. Redistributions of source code must retain the above copyright
|
|---|
| 14 | * notice, this list of conditions and the following disclaimer.
|
|---|
| 15 | * 2. Redistributions in binary form must reproduce the above copyright
|
|---|
| 16 | * notice, this list of conditions and the following disclaimer in the
|
|---|
| 17 | * documentation and/or other materials provided with the distribution.
|
|---|
| 18 | * 3. Neither the name of the University nor the names of its contributors
|
|---|
| 19 | * may be used to endorse or promote products derived from this software
|
|---|
| 20 | * without specific prior written permission.
|
|---|
| 21 | *
|
|---|
| 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|---|
| 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|---|
| 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|---|
| 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|---|
| 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|---|
| 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|---|
| 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|---|
| 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|---|
| 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|---|
| 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|---|
| 32 | * SUCH DAMAGE.
|
|---|
| 33 | */
|
|---|
| 34 |
|
|---|
| 35 | #include <sys/cdefs.h>
|
|---|
| 36 | #ifndef lint
|
|---|
| 37 | #if 0
|
|---|
| 38 | static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95";
|
|---|
| 39 | #else
|
|---|
| 40 | __RCSID("$NetBSD: expand.c,v 1.71 2005/06/01 15:41:19 lukem Exp $");
|
|---|
| 41 | #endif
|
|---|
| 42 | #endif /* not lint */
|
|---|
| 43 |
|
|---|
| 44 | #include <sys/types.h>
|
|---|
| 45 | #include <sys/time.h>
|
|---|
| 46 | #include <sys/stat.h>
|
|---|
| 47 | #include <errno.h>
|
|---|
| 48 | #include <dirent.h>
|
|---|
| 49 | #include <unistd.h>
|
|---|
| 50 | #include <pwd.h>
|
|---|
| 51 | #include <stdlib.h>
|
|---|
| 52 | #include <stdio.h>
|
|---|
| 53 |
|
|---|
| 54 | /*
|
|---|
| 55 | * Routines to expand arguments to commands. We have to deal with
|
|---|
| 56 | * backquotes, shell variables, and file metacharacters.
|
|---|
| 57 | */
|
|---|
| 58 |
|
|---|
| 59 | #include "shell.h"
|
|---|
| 60 | #include "main.h"
|
|---|
| 61 | #include "nodes.h"
|
|---|
| 62 | #include "eval.h"
|
|---|
| 63 | #include "expand.h"
|
|---|
| 64 | #include "syntax.h"
|
|---|
| 65 | #include "parser.h"
|
|---|
| 66 | #include "jobs.h"
|
|---|
| 67 | #include "options.h"
|
|---|
| 68 | #include "var.h"
|
|---|
| 69 | #include "input.h"
|
|---|
| 70 | #include "output.h"
|
|---|
| 71 | #include "memalloc.h"
|
|---|
| 72 | #include "error.h"
|
|---|
| 73 | #include "mystring.h"
|
|---|
| 74 | #include "show.h"
|
|---|
| 75 |
|
|---|
| 76 | /*
|
|---|
| 77 | * Structure specifying which parts of the string should be searched
|
|---|
| 78 | * for IFS characters.
|
|---|
| 79 | */
|
|---|
| 80 |
|
|---|
| 81 | struct ifsregion {
|
|---|
| 82 | struct ifsregion *next; /* next region in list */
|
|---|
| 83 | int begoff; /* offset of start of region */
|
|---|
| 84 | int endoff; /* offset of end of region */
|
|---|
| 85 | int inquotes; /* search for nul bytes only */
|
|---|
| 86 | };
|
|---|
| 87 |
|
|---|
| 88 |
|
|---|
| 89 | char *expdest; /* output of current string */
|
|---|
| 90 | struct nodelist *argbackq; /* list of back quote expressions */
|
|---|
| 91 | struct ifsregion ifsfirst; /* first struct in list of ifs regions */
|
|---|
| 92 | struct ifsregion *ifslastp; /* last struct in list */
|
|---|
| 93 | struct arglist exparg; /* holds expanded arg list */
|
|---|
| 94 |
|
|---|
| 95 | STATIC void argstr(char *, int);
|
|---|
| 96 | STATIC char *exptilde(char *, int);
|
|---|
| 97 | STATIC void expbackq(union node *, int, int);
|
|---|
| 98 | STATIC int subevalvar(char *, char *, int, int, int, int);
|
|---|
| 99 | STATIC char *evalvar(char *, int);
|
|---|
| 100 | STATIC int varisset(char *, int);
|
|---|
| 101 | STATIC void varvalue(char *, int, int, int);
|
|---|
| 102 | STATIC void recordregion(int, int, int);
|
|---|
| 103 | STATIC void removerecordregions(int);
|
|---|
| 104 | STATIC void ifsbreakup(char *, struct arglist *);
|
|---|
| 105 | STATIC void ifsfree(void);
|
|---|
| 106 | STATIC void expandmeta(struct strlist *, int);
|
|---|
| 107 | STATIC void expmeta(char *, char *);
|
|---|
| 108 | STATIC void addfname(char *);
|
|---|
| 109 | STATIC struct strlist *expsort(struct strlist *);
|
|---|
| 110 | STATIC struct strlist *msort(struct strlist *, int);
|
|---|
| 111 | STATIC int pmatch(char *, char *, int);
|
|---|
| 112 | STATIC char *cvtnum(int, char *);
|
|---|
| 113 |
|
|---|
| 114 | /*
|
|---|
| 115 | * Expand shell variables and backquotes inside a here document.
|
|---|
| 116 | */
|
|---|
| 117 |
|
|---|
| 118 | void
|
|---|
| 119 | expandhere(union node *arg, int fd)
|
|---|
| 120 | {
|
|---|
| 121 | herefd = fd;
|
|---|
| 122 | expandarg(arg, (struct arglist *)NULL, 0);
|
|---|
| 123 | xwrite(fd, stackblock(), expdest - stackblock());
|
|---|
| 124 | }
|
|---|
| 125 |
|
|---|
| 126 |
|
|---|
| 127 | /*
|
|---|
| 128 | * Perform variable substitution and command substitution on an argument,
|
|---|
| 129 | * placing the resulting list of arguments in arglist. If EXP_FULL is true,
|
|---|
| 130 | * perform splitting and file name expansion. When arglist is NULL, perform
|
|---|
| 131 | * here document expansion.
|
|---|
| 132 | */
|
|---|
| 133 |
|
|---|
| 134 | void
|
|---|
| 135 | expandarg(union node *arg, struct arglist *arglist, int flag)
|
|---|
| 136 | {
|
|---|
| 137 | struct strlist *sp;
|
|---|
| 138 | char *p;
|
|---|
| 139 |
|
|---|
| 140 | argbackq = arg->narg.backquote;
|
|---|
| 141 | STARTSTACKSTR(expdest);
|
|---|
| 142 | ifsfirst.next = NULL;
|
|---|
| 143 | ifslastp = NULL;
|
|---|
| 144 | argstr(arg->narg.text, flag);
|
|---|
| 145 | if (arglist == NULL) {
|
|---|
| 146 | return; /* here document expanded */
|
|---|
| 147 | }
|
|---|
| 148 | STPUTC('\0', expdest);
|
|---|
| 149 | p = grabstackstr(expdest);
|
|---|
| 150 | exparg.lastp = &exparg.list;
|
|---|
| 151 | /*
|
|---|
| 152 | * TODO - EXP_REDIR
|
|---|
| 153 | */
|
|---|
| 154 | if (flag & EXP_FULL) {
|
|---|
| 155 | ifsbreakup(p, &exparg);
|
|---|
| 156 | *exparg.lastp = NULL;
|
|---|
| 157 | exparg.lastp = &exparg.list;
|
|---|
| 158 | expandmeta(exparg.list, flag);
|
|---|
| 159 | } else {
|
|---|
| 160 | if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
|
|---|
| 161 | rmescapes(p);
|
|---|
| 162 | sp = (struct strlist *)stalloc(sizeof (struct strlist));
|
|---|
| 163 | sp->text = p;
|
|---|
| 164 | *exparg.lastp = sp;
|
|---|
| 165 | exparg.lastp = &sp->next;
|
|---|
| 166 | }
|
|---|
| 167 | ifsfree();
|
|---|
| 168 | *exparg.lastp = NULL;
|
|---|
| 169 | if (exparg.list) {
|
|---|
| 170 | *arglist->lastp = exparg.list;
|
|---|
| 171 | arglist->lastp = exparg.lastp;
|
|---|
| 172 | }
|
|---|
| 173 | }
|
|---|
| 174 |
|
|---|
| 175 |
|
|---|
| 176 |
|
|---|
| 177 | /*
|
|---|
| 178 | * Perform variable and command substitution.
|
|---|
| 179 | * If EXP_FULL is set, output CTLESC characters to allow for further processing.
|
|---|
| 180 | * Otherwise treat $@ like $* since no splitting will be performed.
|
|---|
| 181 | */
|
|---|
| 182 |
|
|---|
| 183 | STATIC void
|
|---|
| 184 | argstr(char *p, int flag)
|
|---|
| 185 | {
|
|---|
| 186 | char c;
|
|---|
| 187 | int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
|
|---|
| 188 | int firsteq = 1;
|
|---|
| 189 | const char *ifs = NULL;
|
|---|
| 190 | int ifs_split = EXP_IFS_SPLIT;
|
|---|
| 191 |
|
|---|
| 192 | if (flag & EXP_IFS_SPLIT)
|
|---|
| 193 | ifs = ifsset() ? ifsval() : " \t\n";
|
|---|
| 194 |
|
|---|
| 195 | if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
|
|---|
| 196 | p = exptilde(p, flag);
|
|---|
| 197 | for (;;) {
|
|---|
| 198 | switch (c = *p++) {
|
|---|
| 199 | case '\0':
|
|---|
| 200 | case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */
|
|---|
| 201 | return;
|
|---|
| 202 | case CTLQUOTEMARK:
|
|---|
| 203 | /* "$@" syntax adherence hack */
|
|---|
| 204 | if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
|
|---|
| 205 | break;
|
|---|
| 206 | if ((flag & EXP_FULL) != 0)
|
|---|
| 207 | STPUTC(c, expdest);
|
|---|
| 208 | ifs_split = 0;
|
|---|
| 209 | break;
|
|---|
| 210 | case CTLQUOTEEND:
|
|---|
| 211 | ifs_split = EXP_IFS_SPLIT;
|
|---|
| 212 | break;
|
|---|
| 213 | case CTLESC:
|
|---|
| 214 | if (quotes)
|
|---|
| 215 | STPUTC(c, expdest);
|
|---|
| 216 | c = *p++;
|
|---|
| 217 | STPUTC(c, expdest);
|
|---|
| 218 | break;
|
|---|
| 219 | case CTLVAR:
|
|---|
| 220 | p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split));
|
|---|
| 221 | break;
|
|---|
| 222 | case CTLBACKQ:
|
|---|
| 223 | case CTLBACKQ|CTLQUOTE:
|
|---|
| 224 | expbackq(argbackq->n, c & CTLQUOTE, flag);
|
|---|
| 225 | argbackq = argbackq->next;
|
|---|
| 226 | break;
|
|---|
| 227 | case CTLENDARI:
|
|---|
| 228 | expari(flag);
|
|---|
| 229 | break;
|
|---|
| 230 | case ':':
|
|---|
| 231 | case '=':
|
|---|
| 232 | /*
|
|---|
| 233 | * sort of a hack - expand tildes in variable
|
|---|
| 234 | * assignments (after the first '=' and after ':'s).
|
|---|
| 235 | */
|
|---|
| 236 | STPUTC(c, expdest);
|
|---|
| 237 | if (flag & EXP_VARTILDE && *p == '~') {
|
|---|
| 238 | if (c == '=') {
|
|---|
| 239 | if (firsteq)
|
|---|
| 240 | firsteq = 0;
|
|---|
| 241 | else
|
|---|
| 242 | break;
|
|---|
| 243 | }
|
|---|
| 244 | p = exptilde(p, flag);
|
|---|
| 245 | }
|
|---|
| 246 | break;
|
|---|
| 247 | default:
|
|---|
| 248 | STPUTC(c, expdest);
|
|---|
| 249 | if (flag & EXP_IFS_SPLIT & ifs_split && strchr(ifs, c) != NULL) {
|
|---|
| 250 | /* We need to get the output split here... */
|
|---|
| 251 | recordregion(expdest - stackblock() - 1,
|
|---|
| 252 | expdest - stackblock(), 0);
|
|---|
| 253 | }
|
|---|
| 254 | break;
|
|---|
| 255 | }
|
|---|
| 256 | }
|
|---|
| 257 | }
|
|---|
| 258 |
|
|---|
| 259 | STATIC char *
|
|---|
| 260 | exptilde(char *p, int flag)
|
|---|
| 261 | {
|
|---|
| 262 | char c, *startp = p;
|
|---|
| 263 | struct passwd *pw;
|
|---|
| 264 | const char *home;
|
|---|
| 265 | int quotes = flag & (EXP_FULL | EXP_CASE);
|
|---|
| 266 |
|
|---|
| 267 | while ((c = *p) != '\0') {
|
|---|
| 268 | switch(c) {
|
|---|
| 269 | case CTLESC:
|
|---|
| 270 | return (startp);
|
|---|
| 271 | case CTLQUOTEMARK:
|
|---|
| 272 | return (startp);
|
|---|
| 273 | case ':':
|
|---|
| 274 | if (flag & EXP_VARTILDE)
|
|---|
| 275 | goto done;
|
|---|
| 276 | break;
|
|---|
| 277 | case '/':
|
|---|
| 278 | goto done;
|
|---|
| 279 | }
|
|---|
| 280 | p++;
|
|---|
| 281 | }
|
|---|
| 282 | done:
|
|---|
| 283 | *p = '\0';
|
|---|
| 284 | if (*(startp+1) == '\0') {
|
|---|
| 285 | if ((home = lookupvar("HOME")) == NULL)
|
|---|
| 286 | goto lose;
|
|---|
| 287 | } else {
|
|---|
| 288 | if ((pw = getpwnam(startp+1)) == NULL)
|
|---|
| 289 | goto lose;
|
|---|
| 290 | home = pw->pw_dir;
|
|---|
| 291 | }
|
|---|
| 292 | if (*home == '\0')
|
|---|
| 293 | goto lose;
|
|---|
| 294 | *p = c;
|
|---|
| 295 | while ((c = *home++) != '\0') {
|
|---|
| 296 | if (quotes && SQSYNTAX[(int)c] == CCTL)
|
|---|
| 297 | STPUTC(CTLESC, expdest);
|
|---|
| 298 | STPUTC(c, expdest);
|
|---|
| 299 | }
|
|---|
| 300 | return (p);
|
|---|
| 301 | lose:
|
|---|
| 302 | *p = c;
|
|---|
| 303 | return (startp);
|
|---|
| 304 | }
|
|---|
| 305 |
|
|---|
| 306 |
|
|---|
| 307 | STATIC void
|
|---|
| 308 | removerecordregions(int endoff)
|
|---|
| 309 | {
|
|---|
| 310 | if (ifslastp == NULL)
|
|---|
| 311 | return;
|
|---|
| 312 |
|
|---|
| 313 | if (ifsfirst.endoff > endoff) {
|
|---|
| 314 | while (ifsfirst.next != NULL) {
|
|---|
| 315 | struct ifsregion *ifsp;
|
|---|
| 316 | INTOFF;
|
|---|
| 317 | ifsp = ifsfirst.next->next;
|
|---|
| 318 | ckfree(ifsfirst.next);
|
|---|
| 319 | ifsfirst.next = ifsp;
|
|---|
| 320 | INTON;
|
|---|
| 321 | }
|
|---|
| 322 | if (ifsfirst.begoff > endoff)
|
|---|
| 323 | ifslastp = NULL;
|
|---|
| 324 | else {
|
|---|
| 325 | ifslastp = &ifsfirst;
|
|---|
| 326 | ifsfirst.endoff = endoff;
|
|---|
| 327 | }
|
|---|
| 328 | return;
|
|---|
| 329 | }
|
|---|
| 330 |
|
|---|
| 331 | ifslastp = &ifsfirst;
|
|---|
| 332 | while (ifslastp->next && ifslastp->next->begoff < endoff)
|
|---|
| 333 | ifslastp=ifslastp->next;
|
|---|
| 334 | while (ifslastp->next != NULL) {
|
|---|
| 335 | struct ifsregion *ifsp;
|
|---|
| 336 | INTOFF;
|
|---|
| 337 | ifsp = ifslastp->next->next;
|
|---|
| 338 | ckfree(ifslastp->next);
|
|---|
| 339 | ifslastp->next = ifsp;
|
|---|
| 340 | INTON;
|
|---|
| 341 | }
|
|---|
| 342 | if (ifslastp->endoff > endoff)
|
|---|
| 343 | ifslastp->endoff = endoff;
|
|---|
| 344 | }
|
|---|
| 345 |
|
|---|
| 346 |
|
|---|
| 347 | /*
|
|---|
| 348 | * Expand arithmetic expression. Backup to start of expression,
|
|---|
| 349 | * evaluate, place result in (backed up) result, adjust string position.
|
|---|
| 350 | */
|
|---|
| 351 | void
|
|---|
| 352 | expari(int flag)
|
|---|
| 353 | {
|
|---|
| 354 | char *p, *start;
|
|---|
| 355 | int result;
|
|---|
| 356 | int begoff;
|
|---|
| 357 | int quotes = flag & (EXP_FULL | EXP_CASE);
|
|---|
| 358 | int quoted;
|
|---|
| 359 |
|
|---|
| 360 | /* ifsfree(); */
|
|---|
| 361 |
|
|---|
| 362 | /*
|
|---|
| 363 | * This routine is slightly over-complicated for
|
|---|
| 364 | * efficiency. First we make sure there is
|
|---|
| 365 | * enough space for the result, which may be bigger
|
|---|
| 366 | * than the expression if we add exponentation. Next we
|
|---|
| 367 | * scan backwards looking for the start of arithmetic. If the
|
|---|
| 368 | * next previous character is a CTLESC character, then we
|
|---|
| 369 | * have to rescan starting from the beginning since CTLESC
|
|---|
| 370 | * characters have to be processed left to right.
|
|---|
| 371 | */
|
|---|
| 372 | #if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
|
|---|
| 373 | #error "integers with more than 10 digits are not supported"
|
|---|
| 374 | #endif
|
|---|
| 375 | CHECKSTRSPACE(12 - 2, expdest);
|
|---|
| 376 | USTPUTC('\0', expdest);
|
|---|
| 377 | start = stackblock();
|
|---|
| 378 | p = expdest - 1;
|
|---|
| 379 | while (*p != CTLARI && p >= start)
|
|---|
| 380 | --p;
|
|---|
| 381 | if (*p != CTLARI)
|
|---|
| 382 | error("missing CTLARI (shouldn't happen)");
|
|---|
| 383 | if (p > start && *(p-1) == CTLESC)
|
|---|
| 384 | for (p = start; *p != CTLARI; p++)
|
|---|
| 385 | if (*p == CTLESC)
|
|---|
| 386 | p++;
|
|---|
| 387 |
|
|---|
| 388 | if (p[1] == '"')
|
|---|
| 389 | quoted=1;
|
|---|
| 390 | else
|
|---|
| 391 | quoted=0;
|
|---|
| 392 | begoff = p - start;
|
|---|
| 393 | removerecordregions(begoff);
|
|---|
| 394 | if (quotes)
|
|---|
| 395 | rmescapes(p+2);
|
|---|
| 396 | result = arith(p+2);
|
|---|
| 397 | fmtstr(p, 12, "%d", result);
|
|---|
| 398 |
|
|---|
| 399 | while (*p++)
|
|---|
| 400 | ;
|
|---|
| 401 |
|
|---|
| 402 | if (quoted == 0)
|
|---|
| 403 | recordregion(begoff, p - 1 - start, 0);
|
|---|
| 404 | result = expdest - p + 1;
|
|---|
| 405 | STADJUST(-result, expdest);
|
|---|
| 406 | }
|
|---|
| 407 |
|
|---|
| 408 |
|
|---|
|
|---|