| 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 | 
 | 
|---|
| 409 | /*
 | 
|---|
| 410 |  * Expand stuff in backwards quotes.
 | 
|---|
| 411 |  */
 | 
|---|
| 412 | 
 | 
|---|
| 413 | STATIC void
 | 
|---|
| 414 | expbackq(union node *cmd, int quoted, int flag)
 | 
|---|
| 415 | {
 | 
|---|
| 416 |         struct backcmd in;
 | 
|---|
| 417 |         int i;
 | 
|---|
| 418 |         char buf[128];
 | 
|---|
| 419 |         char *p;
 | 
|---|
| 420 |         char *dest = expdest;
 | 
|---|
| 421 |         struct ifsregion saveifs, *savelastp;
 | 
|---|
| 422 |         struct nodelist *saveargbackq;
 | 
|---|
| 423 |         char lastc;
 | 
|---|
| 424 |         int startloc = dest - stackblock();
 | 
|---|
| 425 |         char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
 | 
|---|
| 426 |         int saveherefd;
 | 
|---|
| 427 |         int quotes = flag & (EXP_FULL | EXP_CASE);
 | 
|---|
| 428 | 
 | 
|---|
| 429 |         INTOFF;
 | 
|---|
| 430 |         saveifs = ifsfirst;
 | 
|---|
| 431 |         savelastp = ifslastp;
 | 
|---|
| 432 |         saveargbackq = argbackq;
 | 
|---|
| 433 |         saveherefd = herefd;
 | 
|---|
| 434 |         herefd = -1;
 | 
|---|
| 435 |         p = grabstackstr(dest);
 | 
|---|
| 436 |         evalbackcmd(cmd, &in);
 | 
|---|
| 437 |         ungrabstackstr(p, dest);
 | 
|---|
| 438 |         ifsfirst = saveifs;
 | 
|---|
| 439 |         ifslastp = savelastp;
 | 
|---|
| 440 |         argbackq = saveargbackq;
 | 
|---|
| 441 |         herefd = saveherefd;
 | 
|---|
| 442 | 
 | 
|---|
| 443 |         p = in.buf;
 | 
|---|
| 444 |         lastc = '\0';
 | 
|---|
| 445 |         for (;;) {
 | 
|---|
| 446 |                 if (--in.nleft < 0) {
 | 
|---|
| 447 |                         if (in.fd < 0)
 | 
|---|
| 448 |                                 break;
 | 
|---|
| 449 |                         while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
 | 
|---|
| 450 |                         TRACE(("expbackq: read returns %d\n", i));
 | 
|---|
| 451 |                         if (i <= 0)
 | 
|---|
| 452 |                                 break;
 | 
|---|
| 453 |                         p = buf;
 | 
|---|
| 454 |                         in.nleft = i - 1;
 | 
|---|
| 455 |                 }
 | 
|---|
| 456 |                 lastc = *p++;
 | 
|---|
| 457 |                 if (lastc != '\0') {
 | 
|---|
| 458 |                         if (quotes && syntax[(int)lastc] == CCTL)
 | 
|---|
| 459 |                                 STPUTC(CTLESC, dest);
 | 
|---|
| 460 |                         STPUTC(lastc, dest);
 | 
|---|
| 461 |                 }
 | 
|---|
| 462 |         }
 | 
|---|
| 463 | 
 | 
|---|
| 464 |         /* Eat all trailing newlines */
 | 
|---|
| 465 |         p = stackblock() + startloc;
 | 
|---|
| 466 |         while (dest > p && dest[-1] == '\n')
 | 
|---|
| 467 |                 STUNPUTC(dest);
 | 
|---|
| 468 | 
 | 
|---|
| 469 |         if (in.fd >= 0)
 | 
|---|
| 470 |                 close(in.fd);
 | 
|---|
| 471 |         if (in.buf)
 | 
|---|
| 472 |                 ckfree(in.buf);
 | 
|---|
| 473 |         if (in.jp)
 | 
|---|
| 474 |                 back_exitstatus = waitforjob(in.jp);
 | 
|---|
| 475 |         if (quoted == 0)
 | 
|---|
| 476 |                 recordregion(startloc, dest - stackblock(), 0);
 | 
|---|
| 477 |         TRACE(("evalbackq: size=%d: \"%.*s\"\n",
 | 
|---|
| 478 |                 (dest - stackblock()) - startloc,
 | 
|---|
| 479 |                 (dest - stackblock()) - startloc,
 | 
|---|
| 480 |                 stackblock() + startloc));
 | 
|---|
| 481 |         expdest = dest;
 | 
|---|
| 482 |         INTON;
 | 
|---|
| 483 | }
 | 
|---|
| 484 | 
 | 
|---|
| 485 | 
 | 
|---|
| 486 | 
 | 
|---|
| 487 | STATIC int
 | 
|---|
| 488 | subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags)
 | 
|---|
| 489 | {
 | 
|---|
| 490 |         char *startp;
 | 
|---|
| 491 |         char *loc = NULL;
 | 
|---|
| 492 |         char *q;
 | 
|---|
| 493 |         int c = 0;
 | 
|---|
| 494 |         int saveherefd = herefd;
 | 
|---|
| 495 |         struct nodelist *saveargbackq = argbackq;
 | 
|---|
| 496 |         int amount;
 | 
|---|
| 497 | 
 | 
|---|
| 498 |         herefd = -1;
 | 
|---|
| 499 |         argstr(p, 0);
 | 
|---|
| 500 |         STACKSTRNUL(expdest);
 | 
|---|
| 501 |         herefd = saveherefd;
 | 
|---|
| 502 |         argbackq = saveargbackq;
 | 
|---|
| 503 |         startp = stackblock() + startloc;
 | 
|---|
| 504 |         if (str == NULL)
 | 
|---|
| 505 |             str = stackblock() + strloc;
 | 
|---|
| 506 | 
 | 
|---|
| 507 |         switch (subtype) {
 | 
|---|
| 508 |         case VSASSIGN:
 | 
|---|
| 509 |                 setvar(str, startp, 0);
 | 
|---|
| 510 |                 amount = startp - expdest;
 | 
|---|
| 511 |                 STADJUST(amount, expdest);
 | 
|---|
| 512 |                 varflags &= ~VSNUL;
 | 
|---|
| 513 |                 if (c != 0)
 | 
|---|
| 514 |                         *loc = c;
 | 
|---|
| 515 |                 return 1;
 | 
|---|
| 516 | 
 | 
|---|
| 517 |         case VSQUESTION:
 | 
|---|
| 518 |                 if (*p != CTLENDVAR) {
 | 
|---|
| 519 |                         outfmt(&errout, "%s\n", startp);
 | 
|---|
| 520 |                         error((char *)NULL);
 | 
|---|
| 521 |                 }
 | 
|---|
| 522 |                 error("%.*s: parameter %snot set", p - str - 1,
 | 
|---|
| 523 |                       str, (varflags & VSNUL) ? "null or "
 | 
|---|
| 524 |                                               : nullstr);
 | 
|---|
| 525 |                 /* NOTREACHED */
 | 
|---|
| 526 | 
 | 
|---|
| 527 |         case VSTRIMLEFT:
 | 
|---|
| 528 |                 for (loc = startp; loc < str; loc++) {
 | 
|---|
| 529 |                         c = *loc;
 | 
|---|
| 530 |                         *loc = '\0';
 | 
|---|
| 531 |                         if (patmatch(str, startp, varflags & VSQUOTE))
 | 
|---|
| 532 |                                 goto recordleft;
 | 
|---|
| 533 |                         *loc = c;
 | 
|---|
| 534 |                         if ((varflags & VSQUOTE) && *loc == CTLESC)
 | 
|---|
| 535 |                                 loc++;
 | 
|---|
| 536 |                 }
 | 
|---|
| 537 |                 return 0;
 | 
|---|
| 538 | 
 | 
|---|
| 539 |         case VSTRIMLEFTMAX:
 | 
|---|
| 540 |                 for (loc = str - 1; loc >= startp;) {
 | 
|---|
| 541 |                         c = *loc;
 | 
|---|
| 542 |                         *loc = '\0';
 | 
|---|
| 543 |                         if (patmatch(str, startp, varflags & VSQUOTE))
 | 
|---|
| 544 |                                 goto recordleft;
 | 
|---|
| 545 |                         *loc = c;
 | 
|---|
| 546 |                         loc--;
 | 
|---|
| 547 |                         if ((varflags & VSQUOTE) && loc > startp &&
 | 
|---|
| 548 |                             *(loc - 1) == CTLESC) {
 | 
|---|
| 549 |                                 for (q = startp; q < loc; q++)
 | 
|---|
| 550 |                                         if (*q == CTLESC)
 | 
|---|
| 551 |                                                 q++;
 | 
|---|
| 552 |                                 if (q > loc)
 | 
|---|
| 553 |                                         loc--;
 | 
|---|
| 554 |                         }
 | 
|---|
| 555 |                 }
 | 
|---|
| 556 |                 return 0;
 | 
|---|
| 557 | 
 | 
|---|
| 558 |         case VSTRIMRIGHT:
 | 
|---|
| 559 |                 for (loc = str - 1; loc >= startp;) {
 | 
|---|
| 560 |                         if (patmatch(str, loc, varflags & VSQUOTE))
 | 
|---|
| 561 |                                 goto recordright;
 | 
|---|
| 562 |                         loc--;
 | 
|---|
| 563 |                         if ((varflags & VSQUOTE) && loc > startp &&
 | 
|---|
| 564 |                             *(loc - 1) == CTLESC) { 
 | 
|---|
| 565 |                                 for (q = startp; q < loc; q++)
 | 
|---|
| 566 |                                         if (*q == CTLESC)
 | 
|---|
| 567 |                                                 q++;
 | 
|---|
| 568 |                                 if (q > loc)
 | 
|---|
| 569 |                                         loc--;
 | 
|---|
| 570 |                         }
 | 
|---|
| 571 |                 }
 | 
|---|
| 572 |                 return 0;
 | 
|---|
| 573 | 
 | 
|---|
| 574 |         case VSTRIMRIGHTMAX:
 | 
|---|
| 575 |                 for (loc = startp; loc < str - 1; loc++) {
 | 
|---|
| 576 |                         if (patmatch(str, loc, varflags & VSQUOTE))
 | 
|---|
| 577 |                                 goto recordright;
 | 
|---|
| 578 |                         if ((varflags & VSQUOTE) && *loc == CTLESC)
 | 
|---|
| 579 |                                 loc++;
 | 
|---|
| 580 |                 }
 | 
|---|
| 581 |                 return 0;
 | 
|---|
| 582 | 
 | 
|---|
| 583 |         default:
 | 
|---|
| 584 |                 abort();
 | 
|---|
| 585 |         }
 | 
|---|
| 586 | 
 | 
|---|
| 587 | recordleft:
 | 
|---|
| 588 |         *loc = c;
 | 
|---|
| 589 |         amount = ((str - 1) - (loc - startp)) - expdest;
 | 
|---|
| 590 |         STADJUST(amount, expdest);
 | 
|---|
| 591 |         while (loc != str - 1)
 | 
|---|
| 592 |                 *startp++ = *loc++;
 | 
|---|
| 593 |         return 1;
 | 
|---|
| 594 | 
 | 
|---|
| 595 | recordright:
 | 
|---|
| 596 |         amount = loc - expdest;
 | 
|---|
| 597 |         STADJUST(amount, expdest);
 | 
|---|
| 598 |         STPUTC('\0', expdest);
 | 
|---|
| 599 |         STADJUST(-1, expdest);
 | 
|---|
| 600 |         return 1;
 | 
|---|
| 601 | }
 | 
|---|
| 602 | 
 | 
|---|
| 603 | 
 | 
|---|
| 604 | /*
 | 
|---|
| 605 |  * Expand a variable, and return a pointer to the next character in the
 | 
|---|
| 606 |  * input string.
 | 
|---|
| 607 |  */
 | 
|---|
| 608 | 
 | 
|---|
| 609 | STATIC char *
 | 
|---|
| 610 | evalvar(char *p, int flag)
 | 
|---|
| 611 | {
 | 
|---|
| 612 |         int subtype;
 | 
|---|
| 613 |         int varflags;
 | 
|---|
| 614 |         char *var;
 | 
|---|
| 615 |         char *val;
 | 
|---|
| 616 |         int patloc;
 | 
|---|
| 617 |         int c;
 | 
|---|
| 618 |         int set;
 | 
|---|
| 619 |         int special;
 | 
|---|
| 620 |         int startloc;
 | 
|---|
| 621 |         int varlen;
 | 
|---|
| 622 |         int apply_ifs;
 | 
|---|
| 623 |         int quotes = flag & (EXP_FULL | EXP_CASE);
 | 
|---|
| 624 | 
 | 
|---|
| 625 |         varflags = (unsigned char)*p++;
 | 
|---|
| 626 |         subtype = varflags & VSTYPE;
 | 
|---|
| 627 |         var = p;
 | 
|---|
| 628 |         special = !is_name(*p);
 | 
|---|
| 629 |         p = strchr(p, '=') + 1;
 | 
|---|
| 630 | 
 | 
|---|
| 631 | again: /* jump here after setting a variable with ${var=text} */
 | 
|---|
| 632 |         if (special) {
 | 
|---|
| 633 |                 set = varisset(var, varflags & VSNUL);
 | 
|---|
| 634 |                 val = NULL;
 | 
|---|
| 635 |         } else {
 | 
|---|
| 636 |                 val = lookupvar(var);
 | 
|---|
| 637 |                 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
 | 
|---|
| 638 |                         val = NULL;
 | 
|---|
| 639 |                         set = 0;
 | 
|---|
| 640 |                 } else
 | 
|---|
| 641 |                         set = 1;
 | 
|---|
| 642 |         }
 | 
|---|
| 643 | 
 | 
|---|
| 644 |         varlen = 0;
 | 
|---|
| 645 |         startloc = expdest - stackblock();
 | 
|---|
| 646 | 
 | 
|---|
| 647 |         if (!set && uflag) {
 | 
|---|
| 648 |                 switch (subtype) {
 | 
|---|
| 649 |                 case VSNORMAL:
 | 
|---|
| 650 |                 case VSTRIMLEFT:
 | 
|---|
| 651 |                 case VSTRIMLEFTMAX:
 | 
|---|
| 652 |                 case VSTRIMRIGHT:
 | 
|---|
| 653 |                 case VSTRIMRIGHTMAX:
 | 
|---|
| 654 |                 case VSLENGTH:
 | 
|---|
| 655 |                         error("%.*s: parameter not set", p - var - 1, var);
 | 
|---|
| 656 |                         /* NOTREACHED */
 | 
|---|
| 657 |                 }
 | 
|---|
| 658 |         }
 | 
|---|
| 659 | 
 | 
|---|
| 660 |         if (set && subtype != VSPLUS) {
 | 
|---|
| 661 |                 /* insert the value of the variable */
 | 
|---|
| 662 |                 if (special) {
 | 
|---|
| 663 |                         varvalue(var, varflags & VSQUOTE, subtype, flag);
 | 
|---|
| 664 |                         if (subtype == VSLENGTH) {
 | 
|---|
| 665 |                                 varlen = expdest - stackblock() - startloc;
 | 
|---|
| 666 |                                 STADJUST(-varlen, expdest);
 | 
|---|
| 667 |                         }
 | 
|---|
| 668 |                 } else {
 | 
|---|
| 669 |                         char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
 | 
|---|
| 670 |                                                                   : BASESYNTAX;
 | 
|---|
| 671 | 
 | 
|---|
| 672 |                         if (subtype == VSLENGTH) {
 | 
|---|
| 673 |                                 for (;*val; val++)
 | 
|---|
| 674 |                                         varlen++;
 | 
|---|
| 675 |                         } else {
 | 
|---|
| 676 |                                 while (*val) {
 | 
|---|
| 677 |                                         if (quotes && syntax[(int)*val] == CCTL)
 | 
|---|
| 678 |                                                 STPUTC(CTLESC, expdest);
 | 
|---|
| 679 |                                         STPUTC(*val++, expdest);
 | 
|---|
| 680 |                                 }
 | 
|---|
| 681 | 
 | 
|---|
| 682 |                         }
 | 
|---|
| 683 |                 }
 | 
|---|
| 684 |         }
 | 
|---|
| 685 | 
 | 
|---|
| 686 | 
 | 
|---|
| 687 |         apply_ifs = ((varflags & VSQUOTE) == 0 ||
 | 
|---|
| 688 |                 (*var == '@' && shellparam.nparam != 1));
 | 
|---|
| 689 | 
 | 
|---|
| 690 |         switch (subtype) {
 | 
|---|
| 691 |         case VSLENGTH:
 | 
|---|
| 692 |                 expdest = cvtnum(varlen, expdest);
 | 
|---|
| 693 |                 break;
 | 
|---|
| 694 | 
 | 
|---|
| 695 |         case VSNORMAL:
 | 
|---|
| 696 |                 break;
 | 
|---|
| 697 | 
 | 
|---|
| 698 |         case VSPLUS:
 | 
|---|
| 699 |                 set = !set;
 | 
|---|
| 700 |                 /* FALLTHROUGH */
 | 
|---|
| 701 |         case VSMINUS:
 | 
|---|
| 702 |                 if (!set) {
 | 
|---|
| 703 |                         argstr(p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0));
 | 
|---|
| 704 |                         /*
 | 
|---|
| 705 |                          * ${x-a b c} doesn't get split, but removing the
 | 
|---|
| 706 |                          * 'apply_ifs = 0' apparantly breaks ${1+"$@"}..
 | 
|---|
| 707 |                          * ${x-'a b' c} should generate 2 args.
 | 
|---|
| 708 |                          */
 | 
|---|
| 709 |                         /* We should have marked stuff already */
 | 
|---|
| 710 |                         apply_ifs = 0;
 | 
|---|
| 711 |                 }
 | 
|---|
| 712 |                 break;
 | 
|---|
| 713 | 
 | 
|---|
| 714 |         case VSTRIMLEFT:
 | 
|---|
| 715 |         case VSTRIMLEFTMAX:
 | 
|---|
| 716 |         case VSTRIMRIGHT:
 | 
|---|
| 717 |         case VSTRIMRIGHTMAX:
 | 
|---|
| 718 |                 if (!set)
 | 
|---|
| 719 |                         break;
 | 
|---|
| 720 |                 /*
 | 
|---|
| 721 |                  * Terminate the string and start recording the pattern
 | 
|---|
| 722 |                  * right after it
 | 
|---|
| 723 |                  */
 | 
|---|
| 724 |                 STPUTC('\0', expdest);
 | 
|---|
| 725 |                 patloc = expdest - stackblock();
 | 
|---|
| 726 |                 if (subevalvar(p, NULL, patloc, subtype,
 | 
|---|
| 727 |                                startloc, varflags) == 0) {
 | 
|---|
| 728 |                         int amount = (expdest - stackblock() - patloc) + 1;
 | 
|---|
| 729 |                         STADJUST(-amount, expdest);
 | 
|---|
| 730 |                 }
 | 
|---|
| 731 |                 /* Remove any recorded regions beyond start of variable */
 | 
|---|
| 732 |                 removerecordregions(startloc);
 | 
|---|
| 733 |                 apply_ifs = 1;
 | 
|---|
| 734 |                 break;
 | 
|---|
| 735 | 
 | 
|---|
| 736 |         case VSASSIGN:
 | 
|---|
| 737 |         case VSQUESTION:
 | 
|---|
| 738 |                 if (set)
 | 
|---|
| 739 |                         break;
 | 
|---|
| 740 |                 if (subevalvar(p, var, 0, subtype, startloc, varflags)) {
 | 
|---|
| 741 |                         varflags &= ~VSNUL;
 | 
|---|
| 742 |                         /* 
 | 
|---|
| 743 |                          * Remove any recorded regions beyond 
 | 
|---|
| 744 |                          * start of variable 
 | 
|---|
| 745 |                          */
 | 
|---|
| 746 |                         removerecordregions(startloc);
 | 
|---|
| 747 |                         goto again;
 | 
|---|
| 748 |                 }
 | 
|---|
| 749 |                 apply_ifs = 0;
 | 
|---|
| 750 |                 break;
 | 
|---|
| 751 | 
 | 
|---|
| 752 |         default:
 | 
|---|
| 753 |                 abort();
 | 
|---|
| 754 |         }
 | 
|---|
| 755 | 
 | 
|---|
| 756 |         if (apply_ifs)
 | 
|---|
| 757 |                 recordregion(startloc, expdest - stackblock(),
 | 
|---|
| 758 |                              varflags & VSQUOTE);
 | 
|---|
| 759 | 
 | 
|---|
| 760 |         if (subtype != VSNORMAL) {      /* skip to end of alternative */
 | 
|---|
| 761 |                 int nesting = 1;
 | 
|---|
| 762 |                 for (;;) {
 | 
|---|
| 763 |                         if ((c = *p++) == CTLESC)
 | 
|---|
| 764 |                                 p++;
 | 
|---|
| 765 |                         else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
 | 
|---|
| 766 |                                 if (set)
 | 
|---|
| 767 |                                         argbackq = argbackq->next;
 | 
|---|
| 768 |                         } else if (c == CTLVAR) {
 | 
|---|
| 769 |                                 if ((*p++ & VSTYPE) != VSNORMAL)
 | 
|---|
| 770 |                                         nesting++;
 | 
|---|
| 771 |                         } else if (c == CTLENDVAR) {
 | 
|---|
| 772 |                                 if (--nesting == 0)
 | 
|---|
| 773 |                                         break;
 | 
|---|
| 774 |                         }
 | 
|---|
| 775 |                 }
 | 
|---|
| 776 |         }
 | 
|---|
| 777 |         return p;
 | 
|---|
| 778 | }
 | 
|---|
| 779 | 
 | 
|---|
| 780 | 
 | 
|---|
| 781 | 
 | 
|---|
| 782 | /*
 | 
|---|
| 783 |  * Test whether a specialized variable is set.
 | 
|---|
| 784 |  */
 | 
|---|
| 785 | 
 | 
|---|
| 786 | STATIC int
 | 
|---|
| 787 | varisset(char *name, int nulok)
 | 
|---|
| 788 | {
 | 
|---|
| 789 |         if (*name == '!')
 | 
|---|
| 790 |                 return backgndpid != -1;
 | 
|---|
| 791 |         else if (*name == '@' || *name == '*') {
 | 
|---|
| 792 |                 if (*shellparam.p == NULL)
 | 
|---|
| 793 |                         return 0;
 | 
|---|
| 794 | 
 | 
|---|
| 795 |                 if (nulok) {
 | 
|---|
| 796 |                         char **av;
 | 
|---|
| 797 | 
 | 
|---|
| 798 |                         for (av = shellparam.p; *av; av++)
 | 
|---|
| 799 |                                 if (**av != '\0')
 | 
|---|
| 800 |                                         return 1;
 | 
|---|
| 801 |                         return 0;
 | 
|---|
| 802 |                 }
 | 
|---|
| 803 |         } else if (is_digit(*name)) {
 | 
|---|
| 804 |                 char *ap;
 | 
|---|
| 805 |                 int num = atoi(name);
 | 
|---|
| 806 | 
 | 
|---|
| 807 |                 if (num > shellparam.nparam)
 | 
|---|
| 808 |                         return 0;
 | 
|---|
| 809 | 
 | 
|---|
| 810 |                 if (num == 0)
 | 
|---|
| 811 |                         ap = arg0;
 | 
|---|
| 812 |                 else
 | 
|---|
| 813 |                         ap = shellparam.p[num - 1];
 | 
|---|
| 814 | 
 | 
|---|
| 815 |                 if (nulok && (ap == NULL || *ap == '\0'))
 | 
|---|
| 816 |                         return 0;
 | 
|---|
| 817 |         }
 | 
|---|
| 818 |         return 1;
 | 
|---|
| 819 | }
 | 
|---|
| 820 | 
 | 
|---|
| 821 | 
 | 
|---|
| 822 | 
 | 
|---|
| 823 | /*
 | 
|---|
| 824 |  * Add the value of a specialized variable to the stack string.
 | 
|---|
| 825 |  */
 | 
|---|
| 826 | 
 | 
|---|
| 827 | STATIC void
 | 
|---|
| 828 | varvalue(char *name, int quoted, int subtype, int flag)
 | 
|---|
| 829 | {
 | 
|---|
| 830 |         int num;
 | 
|---|
| 831 |         char *p;
 | 
|---|
| 832 |         int i;
 | 
|---|
| 833 |         char sep;
 | 
|---|
| 834 |         char **ap;
 | 
|---|
| 835 |         char const *syntax;
 | 
|---|
| 836 | 
 | 
|---|
| 837 | #define STRTODEST(p) \
 | 
|---|
| 838 |         do {\
 | 
|---|
| 839 |         if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \
 | 
|---|
| 840 |                 syntax = quoted? DQSYNTAX : BASESYNTAX; \
 | 
|---|
| 841 |                 while (*p) { \
 | 
|---|
| 842 |                         if (syntax[(int)*p] == CCTL) \
 | 
|---|
| 843 |                                 STPUTC(CTLESC, expdest); \
 | 
|---|
| 844 |                         STPUTC(*p++, expdest); \
 | 
|---|
| 845 |                 } \
 | 
|---|
| 846 |         } else \
 | 
|---|
| 847 |                 while (*p) \
 | 
|---|
| 848 |                         STPUTC(*p++, expdest); \
 | 
|---|
| 849 |         } while (0)
 | 
|---|
| 850 | 
 | 
|---|
| 851 | 
 | 
|---|
| 852 |         switch (*name) {
 | 
|---|
| 853 |         case '$':
 | 
|---|
| 854 |                 num = rootpid;
 | 
|---|
| 855 |                 goto numvar;
 | 
|---|
| 856 |         case '?':
 | 
|---|
| 857 |                 num = exitstatus;
 | 
|---|
| 858 |                 goto numvar;
 | 
|---|
| 859 |         case '#':
 | 
|---|
| 860 |                 num = shellparam.nparam;
 | 
|---|
| 861 |                 goto numvar;
 | 
|---|
| 862 |         case '!':
 | 
|---|
| 863 |                 num = backgndpid;
 | 
|---|
| 864 | numvar:
 | 
|---|
| 865 |                 expdest = cvtnum(num, expdest);
 | 
|---|
| 866 |                 break;
 | 
|---|
| 867 |         case '-':
 | 
|---|
| 868 |                 for (i = 0; optlist[i].name; i++) {
 | 
|---|
| 869 |                         if (optlist[i].val)
 | 
|---|
| 870 |                                 STPUTC(optlist[i].letter, expdest);
 | 
|---|
| 871 |                 }
 | 
|---|
| 872 |                 break;
 | 
|---|
| 873 |         case '@':
 | 
|---|
| 874 |                 if (flag & EXP_FULL && quoted) {
 | 
|---|
| 875 |                         for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
 | 
|---|
| 876 |                                 STRTODEST(p);
 | 
|---|
| 877 |                                 if (*ap)
 | 
|---|
| 878 |                                         STPUTC('\0', expdest);
 | 
|---|
| 879 |                         }
 | 
|---|
| 880 |                         break;
 | 
|---|
| 881 |                 }
 | 
|---|
| 882 |                 /* fall through */
 | 
|---|
| 883 |         case '*':
 | 
|---|
| 884 |                 if (ifsset() != 0)
 | 
|---|
| 885 |                         sep = ifsval()[0];
 | 
|---|
| 886 |                 else
 | 
|---|
| 887 |                         sep = ' ';
 | 
|---|
| 888 |                 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
 | 
|---|
| 889 |                         STRTODEST(p);
 | 
|---|
| 890 |                         if (*ap && sep)
 | 
|---|
| 891 |                                 STPUTC(sep, expdest);
 | 
|---|
| 892 |                 }
 | 
|---|
| 893 |                 break;
 | 
|---|
| 894 |         case '0':
 | 
|---|
| 895 |                 p = arg0;
 | 
|---|
| 896 |                 STRTODEST(p);
 | 
|---|
| 897 |                 break;
 | 
|---|
| 898 |         default:
 | 
|---|
| 899 |                 if (is_digit(*name)) {
 | 
|---|
| 900 |                         num = atoi(name);
 | 
|---|
| 901 |                         if (num > 0 && num <= shellparam.nparam) {
 | 
|---|
| 902 |                                 p = shellparam.p[num - 1];
 | 
|---|
| 903 |                                 STRTODEST(p);
 | 
|---|
| 904 |                         }
 | 
|---|
| 905 |                 }
 | 
|---|
| 906 |                 break;
 | 
|---|
| 907 |         }
 | 
|---|
| 908 | }
 | 
|---|
| 909 | 
 | 
|---|
| 910 | 
 | 
|---|
| 911 | 
 | 
|---|
| 912 | /*
 | 
|---|
| 913 |  * Record the fact that we have to scan this region of the
 | 
|---|
| 914 |  * string for IFS characters.
 | 
|---|
| 915 |  */
 | 
|---|
| 916 | 
 | 
|---|
| 917 | STATIC void
 | 
|---|
| 918 | recordregion(int start, int end, int inquotes)
 | 
|---|
| 919 | {
 | 
|---|
| 920 |         struct ifsregion *ifsp;
 | 
|---|
| 921 | 
 | 
|---|
| 922 |         if (ifslastp == NULL) {
 | 
|---|
| 923 |                 ifsp = &ifsfirst;
 | 
|---|
| 924 |         } else {
 | 
|---|
| 925 |                 if (ifslastp->endoff == start
 | 
|---|
| 926 |                     && ifslastp->inquotes == inquotes) {
 | 
|---|
| 927 |                         /* extend previous area */
 | 
|---|
| 928 |                         ifslastp->endoff = end;
 | 
|---|
| 929 |                         return;
 | 
|---|
| 930 |                 }
 | 
|---|
| 931 |                 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
 | 
|---|
| 932 |                 ifslastp->next = ifsp;
 | 
|---|
| 933 |         }
 | 
|---|
| 934 |         ifslastp = ifsp;
 | 
|---|
| 935 |         ifslastp->next = NULL;
 | 
|---|
| 936 |         ifslastp->begoff = start;
 | 
|---|
| 937 |         ifslastp->endoff = end;
 | 
|---|
| 938 |         ifslastp->inquotes = inquotes;
 | 
|---|
| 939 | }
 | 
|---|
| 940 | 
 | 
|---|
| 941 | 
 | 
|---|
| 942 | 
 | 
|---|
| 943 | /*
 | 
|---|
| 944 |  * Break the argument string into pieces based upon IFS and add the
 | 
|---|
| 945 |  * strings to the argument list.  The regions of the string to be
 | 
|---|
| 946 |  * searched for IFS characters have been stored by recordregion.
 | 
|---|
| 947 |  */
 | 
|---|
| 948 | STATIC void
 | 
|---|
| 949 | ifsbreakup(char *string, struct arglist *arglist)
 | 
|---|
| 950 | {
 | 
|---|
| 951 |         struct ifsregion *ifsp;
 | 
|---|
| 952 |         struct strlist *sp;
 | 
|---|
| 953 |         char *start;
 | 
|---|
| 954 |         char *p;
 | 
|---|
| 955 |         char *q;
 | 
|---|
| 956 |         const char *ifs;
 | 
|---|
| 957 |         const char *ifsspc;
 | 
|---|
| 958 |         int inquotes;
 | 
|---|
| 959 | 
 | 
|---|
| 960 |         start = string;
 | 
|---|
| 961 |         ifsspc = NULL;
 | 
|---|
| 962 |         inquotes = 0;
 | 
|---|
| 963 | 
 | 
|---|
| 964 |         if (ifslastp == NULL) {
 | 
|---|
| 965 |                 /* Return entire argument, IFS doesn't apply to any of it */
 | 
|---|
| 966 |                 sp = (struct strlist *)stalloc(sizeof *sp);
 | 
|---|
| 967 |                 sp->text = start;
 | 
|---|
| 968 |                 *arglist->lastp = sp;
 | 
|---|
| 969 |                 arglist->lastp = &sp->next;
 | 
|---|
| 970 |                 return;
 | 
|---|
| 971 |         }
 | 
|---|
| 972 | 
 | 
|---|
| 973 |         ifs = ifsset() ? ifsval() : " \t\n";
 | 
|---|
| 974 | 
 | 
|---|
| 975 |         for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
 | 
|---|
| 976 |                 p = string + ifsp->begoff;
 | 
|---|
| 977 |                 inquotes = ifsp->inquotes;
 | 
|---|
| 978 |                 ifsspc = NULL;
 | 
|---|
| 979 |                 while (p < string + ifsp->endoff) {
 | 
|---|
| 980 |                         q = p;
 | 
|---|
| 981 |                         if (*p == CTLESC)
 | 
|---|
| 982 |                                 p++;
 | 
|---|
| 983 |                         if (inquotes) {
 | 
|---|
| 984 |                                 /* Only NULs (probably from "$@") end args */
 | 
|---|
| 985 |                                 if (*p != 0) {
 | 
|---|
| 986 |                                         p++;
 | 
|---|
| 987 |                                         continue;
 | 
|---|
| 988 |                                 }
 | 
|---|
| 989 |                         } else {
 | 
|---|
| 990 |                                 if (!strchr(ifs, *p)) {
 | 
|---|
| 991 |                                         p++;
 | 
|---|
| 992 |                                         continue;
 | 
|---|
| 993 |                                 }
 | 
|---|
| 994 |                                 ifsspc = strchr(" \t\n", *p);
 | 
|---|
| 995 | 
 | 
|---|
| 996 |                                 /* Ignore IFS whitespace at start */
 | 
|---|
| 997 |                                 if (q == start && ifsspc != NULL) {
 | 
|---|
| 998 |                                         p++;
 | 
|---|
| 999 |                                         start = p;
 | 
|---|
| 1000 |                                         continue;
 | 
|---|
| 1001 |                                 }
 | 
|---|
| 1002 |                         }
 | 
|---|
| 1003 | 
 | 
|---|
| 1004 |                         /* Save this argument... */
 | 
|---|
| 1005 |                         *q = '\0';
 | 
|---|
| 1006 |                         sp = (struct strlist *)stalloc(sizeof *sp);
 | 
|---|
| 1007 |                         sp->text = start;
 | 
|---|
| 1008 |                         *arglist->lastp = sp;
 | 
|---|
| 1009 |                         arglist->lastp = &sp->next;
 | 
|---|
| 1010 |                         p++;
 | 
|---|
| 1011 | 
 | 
|---|
| 1012 |                         if (ifsspc != NULL) {
 | 
|---|
| 1013 |                                 /* Ignore further trailing IFS whitespace */
 | 
|---|
| 1014 |                                 for (; p < string + ifsp->endoff; p++) {
 | 
|---|
| 1015 |                                         q = p;
 | 
|---|
| 1016 |                                         if (*p == CTLESC)
 | 
|---|
| 1017 |                                                 p++;
 | 
|---|
| 1018 |                                         if (strchr(ifs, *p) == NULL) {
 | 
|---|
| 1019 |                                                 p = q;
 | 
|---|
| 1020 |                                                 break;
 | 
|---|
| 1021 |                                         }
 | 
|---|
| 1022 |                                         if (strchr(" \t\n", *p) == NULL) {
 | 
|---|
| 1023 |                                                 p++;
 | 
|---|
| 1024 |                                                 break;
 | 
|---|
| 1025 |                                         }
 | 
|---|
| 1026 |                                 }
 | 
|---|
| 1027 |                         }
 | 
|---|
| 1028 |                         start = p;
 | 
|---|
| 1029 |                 }
 | 
|---|
| 1030 |         }
 | 
|---|
| 1031 | 
 | 
|---|
| 1032 |         /*
 | 
|---|
| 1033 |          * Save anything left as an argument.
 | 
|---|
| 1034 |          * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
 | 
|---|
| 1035 |          * generating 2 arguments, the second of which is empty.
 | 
|---|
| 1036 |          * Some recent clarification of the Posix spec say that it
 | 
|---|
| 1037 |          * should only generate one....
 | 
|---|
| 1038 |          */
 | 
|---|
| 1039 |         if (*start /* || (!ifsspc && start > string) */) {
 | 
|---|
| 1040 |                 sp = (struct strlist *)stalloc(sizeof *sp);
 | 
|---|
| 1041 |                 sp->text = start;
 | 
|---|
| 1042 |                 *arglist->lastp = sp;
 | 
|---|
| 1043 |                 arglist->lastp = &sp->next;
 | 
|---|
| 1044 |         }
 | 
|---|
| 1045 | }
 | 
|---|
| 1046 | 
 | 
|---|
| 1047 | STATIC void
 | 
|---|
| 1048 | ifsfree(void)
 | 
|---|
| 1049 | {
 | 
|---|
| 1050 |         while (ifsfirst.next != NULL) {
 | 
|---|
| 1051 |                 struct ifsregion *ifsp;
 | 
|---|
| 1052 |                 INTOFF;
 | 
|---|
| 1053 |                 ifsp = ifsfirst.next->next;
 | 
|---|
| 1054 |                 ckfree(ifsfirst.next);
 | 
|---|
| 1055 |                 ifsfirst.next = ifsp;
 | 
|---|
| 1056 |                 INTON;
 | 
|---|
| 1057 |         }
 | 
|---|
| 1058 |         ifslastp = NULL;
 | 
|---|
| 1059 |         ifsfirst.next = NULL;
 | 
|---|
| 1060 | }
 | 
|---|
| 1061 | 
 | 
|---|
| 1062 | 
 | 
|---|
| 1063 | 
 | 
|---|
| 1064 | /*
 | 
|---|
| 1065 |  * Expand shell metacharacters.  At this point, the only control characters
 | 
|---|
| 1066 |  * should be escapes.  The results are stored in the list exparg.
 | 
|---|
| 1067 |  */
 | 
|---|
| 1068 | 
 | 
|---|
| 1069 | char *expdir;
 | 
|---|
| 1070 | 
 | 
|---|
| 1071 | 
 | 
|---|
| 1072 | STATIC void
 | 
|---|
| 1073 | expandmeta(struct strlist *str, int flag)
 | 
|---|
| 1074 | {
 | 
|---|
| 1075 |         char *p;
 | 
|---|
| 1076 |         struct strlist **savelastp;
 | 
|---|
| 1077 |         struct strlist *sp;
 | 
|---|
| 1078 |         char c;
 | 
|---|
| 1079 |         /* TODO - EXP_REDIR */
 | 
|---|
| 1080 | 
 | 
|---|
| 1081 |         while (str) {
 | 
|---|
| 1082 |                 if (fflag)
 | 
|---|
| 1083 |                         goto nometa;
 | 
|---|
| 1084 |                 p = str->text;
 | 
|---|
| 1085 |                 for (;;) {                      /* fast check for meta chars */
 | 
|---|
| 1086 |                         if ((c = *p++) == '\0')
 | 
|---|
| 1087 |                                 goto nometa;
 | 
|---|
| 1088 |                         if (c == '*' || c == '?' || c == '[' || c == '!')
 | 
|---|
| 1089 |                                 break;
 | 
|---|
| 1090 |                 }
 | 
|---|
| 1091 |                 savelastp = exparg.lastp;
 | 
|---|
| 1092 |                 INTOFF;
 | 
|---|
| 1093 |                 if (expdir == NULL) {
 | 
|---|
| 1094 |                         int i = strlen(str->text);
 | 
|---|
| 1095 |                         expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
 | 
|---|
| 1096 |                 }
 | 
|---|
| 1097 | 
 | 
|---|
| 1098 |                 expmeta(expdir, str->text);
 | 
|---|
| 1099 |                 ckfree(expdir);
 | 
|---|
| 1100 |                 expdir = NULL;
 | 
|---|
| 1101 |                 INTON;
 | 
|---|
| 1102 |                 if (exparg.lastp == savelastp) {
 | 
|---|
| 1103 |                         /*
 | 
|---|
| 1104 |                          * no matches
 | 
|---|
| 1105 |                          */
 | 
|---|
| 1106 | nometa:
 | 
|---|
| 1107 |                         *exparg.lastp = str;
 | 
|---|
| 1108 |                         rmescapes(str->text);
 | 
|---|
| 1109 |                         exparg.lastp = &str->next;
 | 
|---|
| 1110 |                 } else {
 | 
|---|
| 1111 |                         *exparg.lastp = NULL;
 | 
|---|
| 1112 |                         *savelastp = sp = expsort(*savelastp);
 | 
|---|
| 1113 |                         while (sp->next != NULL)
 | 
|---|
| 1114 |                                 sp = sp->next;
 | 
|---|
| 1115 |                         exparg.lastp = &sp->next;
 | 
|---|
| 1116 |                 }
 | 
|---|
| 1117 |                 str = str->next;
 | 
|---|
| 1118 |         }
 | 
|---|
| 1119 | }
 | 
|---|
| 1120 | 
 | 
|---|
| 1121 | 
 | 
|---|
| 1122 | /*
 | 
|---|
| 1123 |  * Do metacharacter (i.e. *, ?, [...]) expansion.
 | 
|---|
| 1124 |  */
 | 
|---|
| 1125 | 
 | 
|---|
| 1126 | STATIC void
 | 
|---|
| 1127 | expmeta(char *enddir, char *name)
 | 
|---|
| 1128 | {
 | 
|---|
| 1129 |         char *p;
 | 
|---|
| 1130 |         const char *cp;
 | 
|---|
| 1131 |         char *q;
 | 
|---|
| 1132 |         char *start;
 | 
|---|
| 1133 |         char *endname;
 | 
|---|
| 1134 |         int metaflag;
 | 
|---|
| 1135 |         struct stat statb;
 | 
|---|
| 1136 |         DIR *dirp;
 | 
|---|
| 1137 |         struct dirent *dp;
 | 
|---|
| 1138 |         int atend;
 | 
|---|
| 1139 |         int matchdot;
 | 
|---|
| 1140 | 
 | 
|---|
| 1141 |         metaflag = 0;
 | 
|---|
| 1142 |         start = name;
 | 
|---|
| 1143 |         for (p = name ; ; p++) {
 | 
|---|
| 1144 |                 if (*p == '*' || *p == '?')
 | 
|---|
| 1145 |                         metaflag = 1;
 | 
|---|
| 1146 |                 else if (*p == '[') {
 | 
|---|
| 1147 |                         q = p + 1;
 | 
|---|
| 1148 |                         if (*q == '!')
 | 
|---|
| 1149 |                                 q++;
 | 
|---|
| 1150 |                         for (;;) {
 | 
|---|
| 1151 |                                 while (*q == CTLQUOTEMARK)
 | 
|---|
| 1152 |                                         q++;
 | 
|---|
| 1153 |                                 if (*q == CTLESC)
 | 
|---|
| 1154 |                                         q++;
 | 
|---|
| 1155 |                                 if (*q == '/' || *q == '\0')
 | 
|---|
| 1156 |                                         break;
 | 
|---|
| 1157 |                                 if (*++q == ']') {
 | 
|---|
| 1158 |                                         metaflag = 1;
 | 
|---|
| 1159 |                                         break;
 | 
|---|
| 1160 |                                 }
 | 
|---|
| 1161 |                         }
 | 
|---|
| 1162 |                 } else if (*p == '!' && p[1] == '!'     && (p == name || p[-1] == '/')) {
 | 
|---|
| 1163 |                         metaflag = 1;
 | 
|---|
| 1164 |                 } else if (*p == '\0')
 | 
|---|
| 1165 |                         break;
 | 
|---|
| 1166 |                 else if (*p == CTLQUOTEMARK)
 | 
|---|
| 1167 |                         continue;
 | 
|---|
| 1168 |                 else if (*p == CTLESC)
 | 
|---|
| 1169 |                         p++;
 | 
|---|
| 1170 |                 if (*p == '/') {
 | 
|---|
| 1171 |                         if (metaflag)
 | 
|---|
| 1172 |                                 break;
 | 
|---|
| 1173 |                         start = p + 1;
 | 
|---|
| 1174 |                 }
 | 
|---|
| 1175 |         }
 | 
|---|
| 1176 |         if (metaflag == 0) {    /* we've reached the end of the file name */
 | 
|---|
| 1177 |                 if (enddir != expdir)
 | 
|---|
| 1178 |                         metaflag++;
 | 
|---|
| 1179 |                 for (p = name ; ; p++) {
 | 
|---|
| 1180 |                         if (*p == CTLQUOTEMARK)
 | 
|---|
| 1181 |                                 continue;
 | 
|---|
| 1182 |                         if (*p == CTLESC)
 | 
|---|
| 1183 |                                 p++;
 | 
|---|
| 1184 |                         *enddir++ = *p;
 | 
|---|
| 1185 |                         if (*p == '\0')
 | 
|---|
| 1186 |                                 break;
 | 
|---|
| 1187 |                 }
 | 
|---|
| 1188 |                 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
 | 
|---|
| 1189 |                         addfname(expdir);
 | 
|---|
| 1190 |                 return;
 | 
|---|
| 1191 |         }
 | 
|---|
| 1192 |         endname = p;
 | 
|---|
| 1193 |         if (start != name) {
 | 
|---|
| 1194 |                 p = name;
 | 
|---|
| 1195 |                 while (p < start) {
 | 
|---|
| 1196 |                         while (*p == CTLQUOTEMARK)
 | 
|---|
| 1197 |                                 p++;
 | 
|---|
| 1198 |                         if (*p == CTLESC)
 | 
|---|
| 1199 |                                 p++;
 | 
|---|
| 1200 |                         *enddir++ = *p++;
 | 
|---|
| 1201 |                 }
 | 
|---|
| 1202 |         }
 | 
|---|
| 1203 |         if (enddir == expdir) {
 | 
|---|
| 1204 |                 cp = ".";
 | 
|---|
| 1205 |         } else if (enddir == expdir + 1 && *expdir == '/') {
 | 
|---|
| 1206 |                 cp = "/";
 | 
|---|
| 1207 |         } else {
 | 
|---|
| 1208 |                 cp = expdir;
 | 
|---|
| 1209 |                 enddir[-1] = '\0';
 | 
|---|
| 1210 |         }
 | 
|---|
| 1211 |         if ((dirp = opendir(cp)) == NULL)
 | 
|---|
| 1212 |                 return;
 | 
|---|
| 1213 |         if (enddir != expdir)
 | 
|---|
| 1214 |                 enddir[-1] = '/';
 | 
|---|
| 1215 |         if (*endname == 0) {
 | 
|---|
| 1216 |                 atend = 1;
 | 
|---|
| 1217 |         } else {
 | 
|---|
| 1218 |                 atend = 0;
 | 
|---|
| 1219 |                 *endname++ = '\0';
 | 
|---|
| 1220 |         }
 | 
|---|
| 1221 |         matchdot = 0;
 | 
|---|
| 1222 |         p = start;
 | 
|---|
| 1223 |         while (*p == CTLQUOTEMARK)
 | 
|---|
| 1224 |                 p++;
 | 
|---|
| 1225 |         if (*p == CTLESC)
 | 
|---|
| 1226 |                 p++;
 | 
|---|
| 1227 |         if (*p == '.')
 | 
|---|
| 1228 |                 matchdot++;
 | 
|---|
| 1229 |         while (! int_pending() && (dp = readdir(dirp)) != NULL) {
 | 
|---|
| 1230 |                 if (dp->d_name[0] == '.' && ! matchdot)
 | 
|---|
| 1231 |                         continue;
 | 
|---|
| 1232 |                 if (patmatch(start, dp->d_name, 0)) {
 | 
|---|
| 1233 |                         if (atend) {
 | 
|---|
| 1234 |                                 scopy(dp->d_name, enddir);
 | 
|---|
| 1235 |                                 addfname(expdir);
 | 
|---|
| 1236 |                         } else {
 | 
|---|
| 1237 |                                 for (p = enddir, cp = dp->d_name;
 | 
|---|
| 1238 |                                      (*p++ = *cp++) != '\0';)
 | 
|---|
| 1239 |                                         continue;
 | 
|---|
| 1240 |                                 p[-1] = '/';
 | 
|---|
| 1241 |                                 expmeta(p, endname);
 | 
|---|
| 1242 |                         }
 | 
|---|
| 1243 |                 }
 | 
|---|
| 1244 |         }
 | 
|---|
| 1245 |         closedir(dirp);
 | 
|---|
| 1246 |         if (! atend)
 | 
|---|
| 1247 |                 endname[-1] = '/';
 | 
|---|
| 1248 | }
 | 
|---|
| 1249 | 
 | 
|---|
| 1250 | 
 | 
|---|
| 1251 | /*
 | 
|---|
| 1252 |  * Add a file name to the list.
 | 
|---|
| 1253 |  */
 | 
|---|
| 1254 | 
 | 
|---|
| 1255 | STATIC void
 | 
|---|
| 1256 | addfname(char *name)
 | 
|---|
| 1257 | {
 | 
|---|
| 1258 |         char *p;
 | 
|---|
| 1259 |         struct strlist *sp;
 | 
|---|
| 1260 | 
 | 
|---|
| 1261 |         p = stalloc(strlen(name) + 1);
 | 
|---|
| 1262 |         scopy(name, p);
 | 
|---|
| 1263 |         sp = (struct strlist *)stalloc(sizeof *sp);
 | 
|---|
| 1264 |         sp->text = p;
 | 
|---|
| 1265 |         *exparg.lastp = sp;
 | 
|---|
| 1266 |         exparg.lastp = &sp->next;
 | 
|---|
| 1267 | }
 | 
|---|
| 1268 | 
 | 
|---|
| 1269 | 
 | 
|---|
| 1270 | /*
 | 
|---|
| 1271 |  * Sort the results of file name expansion.  It calculates the number of
 | 
|---|
| 1272 |  * strings to sort and then calls msort (short for merge sort) to do the
 | 
|---|
| 1273 |  * work.
 | 
|---|
| 1274 |  */
 | 
|---|
| 1275 | 
 | 
|---|
| 1276 | STATIC struct strlist *
 | 
|---|
| 1277 | expsort(struct strlist *str)
 | 
|---|
| 1278 | {
 | 
|---|
| 1279 |         int len;
 | 
|---|
| 1280 |         struct strlist *sp;
 | 
|---|
| 1281 | 
 | 
|---|
| 1282 |         len = 0;
 | 
|---|
| 1283 |         for (sp = str ; sp ; sp = sp->next)
 | 
|---|
| 1284 |                 len++;
 | 
|---|
| 1285 |         return msort(str, len);
 | 
|---|
| 1286 | }
 | 
|---|
| 1287 | 
 | 
|---|
| 1288 | 
 | 
|---|
| 1289 | STATIC struct strlist *
 | 
|---|
| 1290 | msort(struct strlist *list, int len)
 | 
|---|
| 1291 | {
 | 
|---|
| 1292 |         struct strlist *p, *q = NULL;
 | 
|---|
| 1293 |         struct strlist **lpp;
 | 
|---|
| 1294 |         int half;
 | 
|---|
| 1295 |         int n;
 | 
|---|
| 1296 | 
 | 
|---|
| 1297 |         if (len <= 1)
 | 
|---|
| 1298 |                 return list;
 | 
|---|
| 1299 |         half = len >> 1;
 | 
|---|
| 1300 |         p = list;
 | 
|---|
| 1301 |         for (n = half ; --n >= 0 ; ) {
 | 
|---|
| 1302 |                 q = p;
 | 
|---|
| 1303 |                 p = p->next;
 | 
|---|
| 1304 |         }
 | 
|---|
| 1305 |         q->next = NULL;                 /* terminate first half of list */
 | 
|---|
| 1306 |         q = msort(list, half);          /* sort first half of list */
 | 
|---|
| 1307 |         p = msort(p, len - half);               /* sort second half */
 | 
|---|
| 1308 |         lpp = &list;
 | 
|---|
| 1309 |         for (;;) {
 | 
|---|
| 1310 |                 if (strcmp(p->text, q->text) < 0) {
 | 
|---|
| 1311 |                         *lpp = p;
 | 
|---|
| 1312 |                         lpp = &p->next;
 | 
|---|
| 1313 |                         if ((p = *lpp) == NULL) {
 | 
|---|
| 1314 |                                 *lpp = q;
 | 
|---|
| 1315 |                                 break;
 | 
|---|
| 1316 |                         }
 | 
|---|
| 1317 |                 } else {
 | 
|---|
| 1318 |                         *lpp = q;
 | 
|---|
| 1319 |                         lpp = &q->next;
 | 
|---|
| 1320 |                         if ((q = *lpp) == NULL) {
 | 
|---|
| 1321 |                                 *lpp = p;
 | 
|---|
| 1322 |                                 break;
 | 
|---|
| 1323 |                         }
 | 
|---|
| 1324 |                 }
 | 
|---|
| 1325 |         }
 | 
|---|
| 1326 |         return list;
 | 
|---|
| 1327 | }
 | 
|---|
| 1328 | 
 | 
|---|
| 1329 | 
 | 
|---|
| 1330 | 
 | 
|---|
| 1331 | /*
 | 
|---|
| 1332 |  * Returns true if the pattern matches the string.
 | 
|---|
| 1333 |  */
 | 
|---|
| 1334 | 
 | 
|---|
| 1335 | int
 | 
|---|
| 1336 | patmatch(char *pattern, char *string, int squoted)
 | 
|---|
| 1337 | {
 | 
|---|
| 1338 | #ifdef notdef
 | 
|---|
| 1339 |         if (pattern[0] == '!' && pattern[1] == '!')
 | 
|---|
| 1340 |                 return 1 - pmatch(pattern + 2, string);
 | 
|---|
| 1341 |         else
 | 
|---|
| 1342 | #endif
 | 
|---|
| 1343 |                 return pmatch(pattern, string, squoted);
 | 
|---|
| 1344 | }
 | 
|---|
| 1345 | 
 | 
|---|
| 1346 | 
 | 
|---|
| 1347 | STATIC int
 | 
|---|
| 1348 | pmatch(char *pattern, char *string, int squoted)
 | 
|---|
| 1349 | {
 | 
|---|
| 1350 |         char *p, *q;
 | 
|---|
| 1351 |         char c;
 | 
|---|
| 1352 | 
 | 
|---|
| 1353 |         p = pattern;
 | 
|---|
| 1354 |         q = string;
 | 
|---|
| 1355 |         for (;;) {
 | 
|---|
| 1356 |                 switch (c = *p++) {
 | 
|---|
| 1357 |                 case '\0':
 | 
|---|
| 1358 |                         goto breakloop;
 | 
|---|
| 1359 |                 case CTLESC:
 | 
|---|
| 1360 |                         if (squoted && *q == CTLESC)
 | 
|---|
| 1361 |                                 q++;
 | 
|---|
| 1362 |                         if (*q++ != *p++)
 | 
|---|
| 1363 |                                 return 0;
 | 
|---|
| 1364 |                         break;
 | 
|---|
| 1365 |                 case CTLQUOTEMARK:
 | 
|---|
| 1366 |                         continue;
 | 
|---|
| 1367 |                 case '?':
 | 
|---|
| 1368 |                         if (squoted && *q == CTLESC)
 | 
|---|
| 1369 |                                 q++;
 | 
|---|
| 1370 |                         if (*q++ == '\0')
 | 
|---|
| 1371 |                                 return 0;
 | 
|---|
| 1372 |                         break;
 | 
|---|
| 1373 |                 case '*':
 | 
|---|
| 1374 |                         c = *p;
 | 
|---|
| 1375 |                         while (c == CTLQUOTEMARK || c == '*')
 | 
|---|
| 1376 |                                 c = *++p;
 | 
|---|
| 1377 |                         if (c != CTLESC &&  c != CTLQUOTEMARK &&
 | 
|---|
| 1378 |                             c != '?' && c != '*' && c != '[') {
 | 
|---|
| 1379 |                                 while (*q != c) {
 | 
|---|
| 1380 |                                         if (squoted && *q == CTLESC &&
 | 
|---|
| 1381 |                                             q[1] == c)
 | 
|---|
| 1382 |                                                 break;
 | 
|---|
| 1383 |                                         if (*q == '\0')
 | 
|---|
| 1384 |                                                 return 0;
 | 
|---|
| 1385 |                                         if (squoted && *q == CTLESC)
 | 
|---|
| 1386 |                                                 q++;
 | 
|---|
| 1387 |                                         q++;
 | 
|---|
| 1388 |                                 }
 | 
|---|
| 1389 |                         }
 | 
|---|
| 1390 |                         do {
 | 
|---|
| 1391 |                                 if (pmatch(p, q, squoted))
 | 
|---|
| 1392 |                                         return 1;
 | 
|---|
| 1393 |                                 if (squoted && *q == CTLESC)
 | 
|---|
| 1394 |                                         q++;
 | 
|---|
| 1395 |                         } while (*q++ != '\0');
 | 
|---|
| 1396 |                         return 0;
 | 
|---|
| 1397 |                 case '[': {
 | 
|---|
| 1398 |                         char *endp;
 | 
|---|
| 1399 |                         int invert, found;
 | 
|---|
| 1400 |                         char chr;
 | 
|---|
| 1401 | 
 | 
|---|
| 1402 |                         endp = p;
 | 
|---|
| 1403 |                         if (*endp == '!')
 | 
|---|
| 1404 |                                 endp++;
 | 
|---|
| 1405 |                         for (;;) {
 | 
|---|
| 1406 |                                 while (*endp == CTLQUOTEMARK)
 | 
|---|
| 1407 |                                         endp++;
 | 
|---|
| 1408 |                                 if (*endp == '\0')
 | 
|---|
| 1409 |                                         goto dft;               /* no matching ] */
 | 
|---|
| 1410 |                                 if (*endp == CTLESC)
 | 
|---|
| 1411 |                                         endp++;
 | 
|---|
| 1412 |                                 if (*++endp == ']')
 | 
|---|
| 1413 |                                         break;
 | 
|---|
| 1414 |                         }
 | 
|---|
| 1415 |                         invert = 0;
 | 
|---|
| 1416 |                         if (*p == '!') {
 | 
|---|
| 1417 |                                 invert++;
 | 
|---|
| 1418 |                                 p++;
 | 
|---|
| 1419 |                         }
 | 
|---|
| 1420 |                         found = 0;
 | 
|---|
| 1421 |                         chr = *q++;
 | 
|---|
| 1422 |                         if (squoted && chr == CTLESC)
 | 
|---|
| 1423 |                                 chr = *q++;
 | 
|---|
| 1424 |                         if (chr == '\0')
 | 
|---|
| 1425 |                                 return 0;
 | 
|---|
| 1426 |                         c = *p++;
 | 
|---|
| 1427 |                         do {
 | 
|---|
| 1428 |                                 if (c == CTLQUOTEMARK)
 | 
|---|
| 1429 |                                         continue;
 | 
|---|
| 1430 |                                 if (c == CTLESC)
 | 
|---|
| 1431 |                                         c = *p++;
 | 
|---|
| 1432 |                                 if (*p == '-' && p[1] != ']') {
 | 
|---|
| 1433 |                                         p++;
 | 
|---|
| 1434 |                                         while (*p == CTLQUOTEMARK)
 | 
|---|
| 1435 |                                                 p++;
 | 
|---|
| 1436 |                                         if (*p == CTLESC)
 | 
|---|
| 1437 |                                                 p++;
 | 
|---|
| 1438 |                                         if (chr >= c && chr <= *p)
 | 
|---|
| 1439 |                                                 found = 1;
 | 
|---|
| 1440 |                                         p++;
 | 
|---|
| 1441 |                                 } else {
 | 
|---|
| 1442 |                                         if (chr == c)
 | 
|---|
| 1443 |                                                 found = 1;
 | 
|---|
| 1444 |                                 }
 | 
|---|
| 1445 |                         } while ((c = *p++) != ']');
 | 
|---|
| 1446 |                         if (found == invert)
 | 
|---|
| 1447 |                                 return 0;
 | 
|---|
| 1448 |                         break;
 | 
|---|
| 1449 |                 }
 | 
|---|
| 1450 | dft:            default:
 | 
|---|
| 1451 |                         if (squoted && *q == CTLESC)
 | 
|---|
| 1452 |                                 q++;
 | 
|---|
| 1453 |                         if (*q++ != c)
 | 
|---|
| 1454 |                                 return 0;
 | 
|---|
| 1455 |                         break;
 | 
|---|
| 1456 |                 }
 | 
|---|
| 1457 |         }
 | 
|---|
| 1458 | breakloop:
 | 
|---|
| 1459 |         if (*q != '\0')
 | 
|---|
| 1460 |                 return 0;
 | 
|---|
| 1461 |         return 1;
 | 
|---|
| 1462 | }
 | 
|---|
| 1463 | 
 | 
|---|
| 1464 | 
 | 
|---|
| 1465 | 
 | 
|---|
| 1466 | /*
 | 
|---|
| 1467 |  * Remove any CTLESC characters from a string.
 | 
|---|
| 1468 |  */
 | 
|---|
| 1469 | 
 | 
|---|
| 1470 | void
 | 
|---|
| 1471 | rmescapes(char *str)
 | 
|---|
| 1472 | {
 | 
|---|
| 1473 |         char *p, *q;
 | 
|---|
| 1474 | 
 | 
|---|
| 1475 |         p = str;
 | 
|---|
| 1476 |         while (*p != CTLESC && *p != CTLQUOTEMARK) {
 | 
|---|
| 1477 |                 if (*p++ == '\0')
 | 
|---|
| 1478 |                         return;
 | 
|---|
| 1479 |         }
 | 
|---|
| 1480 |         q = p;
 | 
|---|
| 1481 |         while (*p) {
 | 
|---|
| 1482 |                 if (*p == CTLQUOTEMARK) {
 | 
|---|
| 1483 |                         p++;
 | 
|---|
| 1484 |                         continue;
 | 
|---|
| 1485 |                 }
 | 
|---|
| 1486 |                 if (*p == CTLESC)
 | 
|---|
| 1487 |                         p++;
 | 
|---|
| 1488 |                 *q++ = *p++;
 | 
|---|
| 1489 |         }
 | 
|---|
| 1490 |         *q = '\0';
 | 
|---|
| 1491 | }
 | 
|---|
| 1492 | 
 | 
|---|
| 1493 | 
 | 
|---|
| 1494 | 
 | 
|---|
| 1495 | /*
 | 
|---|
| 1496 |  * See if a pattern matches in a case statement.
 | 
|---|
| 1497 |  */
 | 
|---|
| 1498 | 
 | 
|---|
| 1499 | int
 | 
|---|
| 1500 | casematch(union node *pattern, char *val)
 | 
|---|
| 1501 | {
 | 
|---|
| 1502 |         struct stackmark smark;
 | 
|---|
| 1503 |         int result;
 | 
|---|
| 1504 |         char *p;
 | 
|---|
| 1505 | 
 | 
|---|
| 1506 |         setstackmark(&smark);
 | 
|---|
| 1507 |         argbackq = pattern->narg.backquote;
 | 
|---|
| 1508 |         STARTSTACKSTR(expdest);
 | 
|---|
| 1509 |         ifslastp = NULL;
 | 
|---|
| 1510 |         argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
 | 
|---|
| 1511 |         STPUTC('\0', expdest);
 | 
|---|
| 1512 |         p = grabstackstr(expdest);
 | 
|---|
| 1513 |         result = patmatch(p, val, 0);
 | 
|---|
| 1514 |         popstackmark(&smark);
 | 
|---|
| 1515 |         return result;
 | 
|---|
| 1516 | }
 | 
|---|
| 1517 | 
 | 
|---|
| 1518 | /*
 | 
|---|
| 1519 |  * Our own itoa().
 | 
|---|
| 1520 |  */
 | 
|---|
| 1521 | 
 | 
|---|
| 1522 | STATIC char *
 | 
|---|
| 1523 | cvtnum(int num, char *buf)
 | 
|---|
| 1524 | {
 | 
|---|
| 1525 |         char temp[32];
 | 
|---|
| 1526 |         int neg = num < 0;
 | 
|---|
| 1527 |         char *p = temp + 31;
 | 
|---|
| 1528 | 
 | 
|---|
| 1529 |         temp[31] = '\0';
 | 
|---|
| 1530 | 
 | 
|---|
| 1531 |         do {
 | 
|---|
| 1532 |                 *--p = num % 10 + '0';
 | 
|---|
| 1533 |         } while ((num /= 10) != 0);
 | 
|---|
| 1534 | 
 | 
|---|
| 1535 |         if (neg)
 | 
|---|
| 1536 |                 *--p = '-';
 | 
|---|
| 1537 | 
 | 
|---|
| 1538 |         while (*p)
 | 
|---|
| 1539 |                 STPUTC(*p++, buf);
 | 
|---|
| 1540 |         return buf;
 | 
|---|
| 1541 | }
 | 
|---|
| 1542 | 
 | 
|---|
| 1543 | /*
 | 
|---|
| 1544 |  * Do most of the work for wordexp(3).
 | 
|---|
| 1545 |  */
 | 
|---|
| 1546 | 
 | 
|---|
| 1547 | int
 | 
|---|
| 1548 | wordexpcmd(int argc, char **argv)
 | 
|---|
| 1549 | {
 | 
|---|
| 1550 |         size_t len;
 | 
|---|
| 1551 |         int i;
 | 
|---|
| 1552 | 
 | 
|---|
| 1553 |         out1fmt("%d", argc - 1);
 | 
|---|
| 1554 |         out1c('\0');
 | 
|---|
| 1555 |         for (i = 1, len = 0; i < argc; i++)
 | 
|---|
| 1556 |                 len += strlen(argv[i]);
 | 
|---|
| 1557 |         out1fmt("%zd", len);
 | 
|---|
| 1558 |         out1c('\0');
 | 
|---|
| 1559 |         for (i = 1; i < argc; i++) {
 | 
|---|
| 1560 |                 out1str(argv[i]);
 | 
|---|
| 1561 |                 out1c('\0');
 | 
|---|
| 1562 |         }
 | 
|---|
| 1563 |         return (0);
 | 
|---|
| 1564 | }
 | 
|---|