VirtualBox

source: kBuild/trunk/src/kash/expand.c@ 1203

Last change on this file since 1203 was 1203, checked in by bird, 17 years ago

converted a few more files.

  • Property svn:eol-style set to native
File size: 32.8 KB
Line 
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#ifdef HAVE_SYS_CDEFS_H
36#include <sys/cdefs.h>
37#endif
38#ifndef lint
39#if 0
40static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95";
41#else
42__RCSID("$NetBSD: expand.c,v 1.71 2005/06/01 15:41:19 lukem Exp $");
43#endif
44#endif /* not lint */
45
46#include <sys/types.h>
47#include <sys/time.h>
48#include <sys/stat.h>
49#include <errno.h>
50#include <dirent.h>
51#include <unistd.h>
52#include <pwd.h>
53#include <stdlib.h>
54#include <stdio.h>
55#ifdef __sun__
56#include <iso/limits_iso.h>
57#endif
58
59/*
60 * Routines to expand arguments to commands. We have to deal with
61 * backquotes, shell variables, and file metacharacters.
62 */
63
64#include "shell.h"
65#include "main.h"
66#include "nodes.h"
67#include "eval.h"
68#include "expand.h"
69#include "syntax.h"
70#include "parser.h"
71#include "jobs.h"
72#include "options.h"
73#include "var.h"
74#include "input.h"
75#include "output.h"
76#include "memalloc.h"
77#include "error.h"
78#include "mystring.h"
79#include "show.h"
80#include "shinstance.h"
81
82///*
83// * Structure specifying which parts of the string should be searched
84// * for IFS characters.
85// */
86//
87//struct ifsregion {
88// struct ifsregion *next; /* next region in list */
89// int begoff; /* offset of start of region */
90// int endoff; /* offset of end of region */
91// int inquotes; /* search for nul bytes only */
92//};
93//
94//
95//char *expdest; /* output of current string */
96//struct nodelist *argbackq; /* list of back quote expressions */
97//struct ifsregion ifsfirst; /* first struct in list of ifs regions */
98//struct ifsregion *ifslastp; /* last struct in list */
99//struct arglist exparg; /* holds expanded arg list */
100
101STATIC void argstr(shinstance *, char *, int);
102STATIC char *exptilde(shinstance *, char *, int);
103STATIC void expbackq(shinstance *, union node *, int, int);
104STATIC int subevalvar(shinstance *, char *, char *, int, int, int, int);
105STATIC char *evalvar(shinstance *, char *, int);
106STATIC int varisset(shinstance *, char *, int);
107STATIC void varvalue(shinstance *, char *, int, int, int);
108STATIC void recordregion(shinstance *, int, int, int);
109STATIC void removerecordregions(shinstance *, int);
110STATIC void ifsbreakup(shinstance *, char *, struct arglist *);
111STATIC void ifsfree(shinstance *);
112STATIC void expandmeta(shinstance *, struct strlist *, int);
113STATIC void expmeta(shinstance *, char *, char *);
114STATIC void addfname(shinstance *, char *);
115STATIC struct strlist *expsort(struct strlist *);
116STATIC struct strlist *msort(struct strlist *, int);
117STATIC int pmatch(char *, char *, int);
118STATIC char *cvtnum(shinstance *, int, char *);
119
120/*
121 * Expand shell variables and backquotes inside a here document.
122 */
123
124void
125expandhere(shinstance *psh, union node *arg, int fd)
126{
127 psh->herefd = fd;
128 expandarg(psh, arg, (struct arglist *)NULL, 0);
129 xwrite(psh, fd, stackblock(psh), psh->expdest - stackblock(psh));
130}
131
132
133/*
134 * Perform variable substitution and command substitution on an argument,
135 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
136 * perform splitting and file name expansion. When arglist is NULL, perform
137 * here document expansion.
138 */
139
140void
141expandarg(shinstance *psh, union node *arg, struct arglist *arglist, int flag)
142{
143 struct strlist *sp;
144 char *p;
145
146 psh->argbackq = arg->narg.backquote;
147 STARTSTACKSTR(psh, psh->expdest);
148 psh->ifsfirst.next = NULL;
149 psh->ifslastp = NULL;
150 argstr(psh, arg->narg.text, flag);
151 if (arglist == NULL) {
152 return; /* here document expanded */
153 }
154 STPUTC(psh, '\0', psh->expdest);
155 p = grabstackstr(psh, psh->expdest);
156 psh->exparg.lastp = &psh->exparg.list;
157 /*
158 * TODO - EXP_REDIR
159 */
160 if (flag & EXP_FULL) {
161 ifsbreakup(psh, p, &psh->exparg);
162 *psh->exparg.lastp = NULL;
163 psh->exparg.lastp = &psh->exparg.list;
164 expandmeta(psh, psh->exparg.list, flag);
165 } else {
166 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
167 rmescapes(psh, p);
168 sp = (struct strlist *)stalloc(psh, sizeof (struct strlist));
169 sp->text = p;
170 *psh->exparg.lastp = sp;
171 psh->exparg.lastp = &sp->next;
172 }
173 ifsfree(psh);
174 *psh->exparg.lastp = NULL;
175 if (psh->exparg.list) {
176 *arglist->lastp = psh->exparg.list;
177 arglist->lastp = psh->exparg.lastp;
178 }
179}
180
181
182
183/*
184 * Perform variable and command substitution.
185 * If EXP_FULL is set, output CTLESC characters to allow for further processing.
186 * Otherwise treat $@ like $* since no splitting will be performed.
187 */
188
189STATIC void
190argstr(shinstance *psh, char *p, int flag)
191{
192 char c;
193 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
194 int firsteq = 1;
195 const char *ifs = NULL;
196 int ifs_split = EXP_IFS_SPLIT;
197
198 if (flag & EXP_IFS_SPLIT)
199 ifs = ifsset(psh) ? ifsval(psh) : " \t\n";
200
201 if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
202 p = exptilde(psh, p, flag);
203 for (;;) {
204 switch (c = *p++) {
205 case '\0':
206 case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */
207 return;
208 case CTLQUOTEMARK:
209 /* "$@" syntax adherence hack */
210 if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
211 break;
212 if ((flag & EXP_FULL) != 0)
213 STPUTC(psh, c, psh->expdest);
214 ifs_split = 0;
215 break;
216 case CTLQUOTEEND:
217 ifs_split = EXP_IFS_SPLIT;
218 break;
219 case CTLESC:
220 if (quotes)
221 STPUTC(psh, c, psh->expdest);
222 c = *p++;
223 STPUTC(psh, c, psh->expdest);
224 break;
225 case CTLVAR:
226 p = evalvar(psh, p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split));
227 break;
228 case CTLBACKQ:
229 case CTLBACKQ|CTLQUOTE:
230 expbackq(psh, psh->argbackq->n, c & CTLQUOTE, flag);
231 psh->argbackq = psh->argbackq->next;
232 break;
233 case CTLENDARI:
234 expari(psh, flag);
235 break;
236 case ':':
237 case '=':
238 /*
239 * sort of a hack - expand tildes in variable
240 * assignments (after the first '=' and after ':'s).
241 */
242 STPUTC(psh, c, psh->expdest);
243 if (flag & EXP_VARTILDE && *p == '~') {
244 if (c == '=') {
245 if (firsteq)
246 firsteq = 0;
247 else
248 break;
249 }
250 p = exptilde(psh, p, flag);
251 }
252 break;
253 default:
254 STPUTC(psh, c, psh->expdest);
255 if (flag & EXP_IFS_SPLIT & ifs_split && strchr(ifs, c) != NULL) {
256 /* We need to get the output split here... */
257 recordregion(psh, (int)(psh->expdest - stackblock(psh) - 1),
258 (int)(psh->expdest - stackblock(psh)), 0);
259 }
260 break;
261 }
262 }
263}
264
265STATIC char *
266exptilde(shinstance *psh, char *p, int flag)
267{
268 char c, *startp = p;
269 struct passwd *pw;
270 const char *home;
271 int quotes = flag & (EXP_FULL | EXP_CASE);
272
273 while ((c = *p) != '\0') {
274 switch(c) {
275 case CTLESC:
276 return (startp);
277 case CTLQUOTEMARK:
278 return (startp);
279 case ':':
280 if (flag & EXP_VARTILDE)
281 goto done;
282 break;
283 case '/':
284 goto done;
285 }
286 p++;
287 }
288done:
289 *p = '\0';
290 if (*(startp+1) == '\0') {
291 if ((home = lookupvar(psh, "HOME")) == NULL)
292 goto lose;
293 } else {
294 if ((pw = getpwnam(startp+1)) == NULL)
295 goto lose;
296 home = pw->pw_dir;
297 }
298 if (*home == '\0')
299 goto lose;
300 *p = c;
301 while ((c = *home++) != '\0') {
302 if (quotes && SQSYNTAX[(int)c] == CCTL)
303 STPUTC(psh, CTLESC, psh->expdest);
304 STPUTC(psh, c, psh->expdest);
305 }
306 return (p);
307lose:
308 *p = c;
309 return (startp);
310}
311
312
313STATIC void
314removerecordregions(shinstance *psh, int endoff)
315{
316 if (psh->ifslastp == NULL)
317 return;
318
319 if (psh->ifsfirst.endoff > endoff) {
320 while (psh->ifsfirst.next != NULL) {
321 struct ifsregion *ifsp;
322 INTOFF;
323 ifsp = psh->ifsfirst.next->next;
324 ckfree(psh->ifsfirst.next);
325 psh->ifsfirst.next = ifsp;
326 INTON;
327 }
328 if (psh->ifsfirst.begoff > endoff)
329 psh->ifslastp = NULL;
330 else {
331 psh->ifslastp = &psh->ifsfirst;
332 psh->ifsfirst.endoff = endoff;
333 }
334 return;
335 }
336
337 psh->ifslastp = &psh->ifsfirst;
338 while (psh->ifslastp->next && psh->ifslastp->next->begoff < endoff)
339 psh->ifslastp=psh->ifslastp->next;
340 while (psh->ifslastp->next != NULL) {
341 struct ifsregion *ifsp;
342 INTOFF;
343 ifsp = psh->ifslastp->next->next;
344 ckfree(psh->ifslastp->next);
345 psh->ifslastp->next = ifsp;
346 INTON;
347 }
348 if (psh->ifslastp->endoff > endoff)
349 psh->ifslastp->endoff = endoff;
350}
351
352
353/*
354 * Expand arithmetic expression. Backup to start of expression,
355 * evaluate, place result in (backed up) result, adjust string position.
356 */
357void
358expari(shinstance *psh, int flag)
359{
360 char *p, *start;
361 int result;
362 int begoff;
363 int quotes = flag & (EXP_FULL | EXP_CASE);
364 int quoted;
365
366 /* ifsfree(); */
367
368 /*
369 * This routine is slightly over-complicated for
370 * efficiency. First we make sure there is
371 * enough space for the result, which may be bigger
372 * than the expression if we add exponentation. Next we
373 * scan backwards looking for the start of arithmetic. If the
374 * next previous character is a CTLESC character, then we
375 * have to rescan starting from the beginning since CTLESC
376 * characters have to be processed left to right.
377 */
378#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
379#error "integers with more than 10 digits are not supported"
380#endif
381 CHECKSTRSPACE(psh, 12 - 2, psh->expdest);
382 USTPUTC(psh, '\0', psh->expdest);
383 start = stackblock(psh);
384 p = psh->expdest - 1;
385 while (*p != CTLARI && p >= start)
386 --p;
387 if (*p != CTLARI)
388 error(psh, "missing CTLARI (shouldn't happen)");
389 if (p > start && *(p-1) == CTLESC)
390 for (p = start; *p != CTLARI; p++)
391 if (*p == CTLESC)
392 p++;
393
394 if (p[1] == '"')
395 quoted=1;
396 else
397 quoted=0;
398 begoff = (int)(p - start);
399 removerecordregions(psh, begoff);
400 if (quotes)
401 rmescapes(psh, p+2);
402 result = arith(p+2);
403 fmtstr(p, 12, "%d", result);
404
405 while (*p++)
406 ;
407
408 if (quoted == 0)
409 recordregion(psh, begoff, (int)(p - 1 - start), 0);
410 result = (int)(psh->expdest - p + 1);
411 STADJUST(psh, -result, psh->expdest);
412}
413
414
415/*
416 * Expand stuff in backwards quotes.
417 */
418
419STATIC void
420expbackq(shinstance *psh, union node *cmd, int quoted, int flag)
421{
422 struct backcmd in;
423 int i;
424 char buf[128];
425 char *p;
426 char *dest = psh->expdest;
427 struct ifsregion saveifs, *savelastp;
428 struct nodelist *saveargbackq;
429 char lastc;
430 int startloc = (int)(dest - stackblock(psh));
431 char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
432 int saveherefd;
433 int quotes = flag & (EXP_FULL | EXP_CASE);
434
435 INTOFF;
436 saveifs = psh->ifsfirst;
437 savelastp = psh->ifslastp;
438 saveargbackq = psh->argbackq;
439 saveherefd = psh->herefd;
440 psh->herefd = -1;
441 p = grabstackstr(psh, dest);
442 evalbackcmd(psh, cmd, &in);
443 ungrabstackstr(psh, p, dest);
444 psh->ifsfirst = saveifs;
445 psh->ifslastp = savelastp;
446 psh->argbackq = saveargbackq;
447 psh->herefd = saveherefd;
448
449 p = in.buf;
450 lastc = '\0';
451 for (;;) {
452 if (--in.nleft < 0) {
453 if (in.fd < 0)
454 break;
455 while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
456 TRACE((psh, "expbackq: read returns %d\n", i));
457 if (i <= 0)
458 break;
459 p = buf;
460 in.nleft = i - 1;
461 }
462 lastc = *p++;
463 if (lastc != '\0') {
464 if (quotes && syntax[(int)lastc] == CCTL)
465 STPUTC(psh, CTLESC, dest);
466 STPUTC(psh, lastc, dest);
467 }
468 }
469
470 /* Eat all trailing newlines */
471 p = stackblock(psh) + startloc;
472 while (dest > p && dest[-1] == '\n')
473 STUNPUTC(psh, dest);
474
475 if (in.fd >= 0)
476 close(in.fd);
477 if (in.buf)
478 ckfree(in.buf);
479 if (in.jp)
480 psh->back_exitstatus = waitforjob(psh, in.jp);
481 if (quoted == 0)
482 recordregion(psh, startloc, (int)(dest - stackblock(psh)), 0);
483 TRACE((psh, "evalbackq: size=%d: \"%.*s\"\n",
484 (dest - stackblock(psh)) - startloc,
485 (dest - stackblock(psh)) - startloc,
486 stackblock(psh) + startloc));
487 psh->expdest = dest;
488 INTON;
489}
490
491
492
493STATIC int
494subevalvar(shinstance *psh, char *p, char *str, int strloc, int subtype, int startloc, int varflags)
495{
496 char *startp;
497 char *loc = NULL;
498 char *q;
499 int c = 0;
500 int saveherefd = psh->herefd;
501 struct nodelist *saveargbackq = psh->argbackq;
502 int amount;
503
504 psh->herefd = -1;
505 argstr(psh, p, 0);
506 STACKSTRNUL(psh, psh->expdest);
507 psh->herefd = saveherefd;
508 psh->argbackq = saveargbackq;
509 startp = stackblock(psh) + startloc;
510 if (str == NULL)
511 str = stackblock(psh) + strloc;
512
513 switch (subtype) {
514 case VSASSIGN:
515 setvar(psh, str, startp, 0);
516 amount = (int)(startp - psh->expdest);
517 STADJUST(psh, amount, psh->expdest);
518 varflags &= ~VSNUL;
519 if (c != 0)
520 *loc = c;
521 return 1;
522
523 case VSQUESTION:
524 if (*p != CTLENDVAR) {
525 outfmt(&psh->errout, "%s\n", startp);
526 error(psh, (char *)NULL);
527 }
528 error(psh, "%.*s: parameter %snot set", p - str - 1,
529 str, (varflags & VSNUL) ? "null or "
530 : nullstr);
531 /* NOTREACHED */
532
533 case VSTRIMLEFT:
534 for (loc = startp; loc < str; loc++) {
535 c = *loc;
536 *loc = '\0';
537 if (patmatch(psh, str, startp, varflags & VSQUOTE))
538 goto recordleft;
539 *loc = c;
540 if ((varflags & VSQUOTE) && *loc == CTLESC)
541 loc++;
542 }
543 return 0;
544
545 case VSTRIMLEFTMAX:
546 for (loc = str - 1; loc >= startp;) {
547 c = *loc;
548 *loc = '\0';
549 if (patmatch(psh, str, startp, varflags & VSQUOTE))
550 goto recordleft;
551 *loc = c;
552 loc--;
553 if ((varflags & VSQUOTE) && loc > startp &&
554 *(loc - 1) == CTLESC) {
555 for (q = startp; q < loc; q++)
556 if (*q == CTLESC)
557 q++;
558 if (q > loc)
559 loc--;
560 }
561 }
562 return 0;
563
564 case VSTRIMRIGHT:
565 for (loc = str - 1; loc >= startp;) {
566 if (patmatch(psh, str, loc, varflags & VSQUOTE))
567 goto recordright;
568 loc--;
569 if ((varflags & VSQUOTE) && loc > startp &&
570 *(loc - 1) == CTLESC) {
571 for (q = startp; q < loc; q++)
572 if (*q == CTLESC)
573 q++;
574 if (q > loc)
575 loc--;
576 }
577 }
578 return 0;
579
580 case VSTRIMRIGHTMAX:
581 for (loc = startp; loc < str - 1; loc++) {
582 if (patmatch(psh, str, loc, varflags & VSQUOTE))
583 goto recordright;
584 if ((varflags & VSQUOTE) && *loc == CTLESC)
585 loc++;
586 }
587 return 0;
588
589 default:
590 abort();
591 }
592
593recordleft:
594 *loc = c;
595 amount = (int)(((str - 1) - (loc - startp)) - psh->expdest);
596 STADJUST(psh, amount, psh->expdest);
597 while (loc != str - 1)
598 *startp++ = *loc++;
599 return 1;
600
601recordright:
602 amount = (int)(loc - psh->expdest);
603 STADJUST(psh, amount, psh->expdest);
604 STPUTC(psh, '\0', psh->expdest);
605 STADJUST(psh, -1, psh->expdest);
606 return 1;
607}
608
609
610/*
611 * Expand a variable, and return a pointer to the next character in the
612 * input string.
613 */
614
615STATIC char *
616evalvar(shinstance *psh, char *p, int flag)
617{
618 int subtype;
619 int varflags;
620 char *var;
621 char *val;
622 int patloc;
623 int c;
624 int set;
625 int special;
626 int startloc;
627 int varlen;
628 int apply_ifs;
629 int quotes = flag & (EXP_FULL | EXP_CASE);
630
631 varflags = (unsigned char)*p++;
632 subtype = varflags & VSTYPE;
633 var = p;
634 special = !is_name(*p);
635 p = strchr(p, '=') + 1;
636
637again: /* jump here after setting a variable with ${var=text} */
638 if (special) {
639 set = varisset(psh, var, varflags & VSNUL);
640 val = NULL;
641 } else {
642 val = lookupvar(psh, var);
643 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
644 val = NULL;
645 set = 0;
646 } else
647 set = 1;
648 }
649
650 varlen = 0;
651 startloc = (int)(psh->expdest - stackblock(psh));
652
653 if (!set && uflag(psh)) {
654 switch (subtype) {
655 case VSNORMAL:
656 case VSTRIMLEFT:
657 case VSTRIMLEFTMAX:
658 case VSTRIMRIGHT:
659 case VSTRIMRIGHTMAX:
660 case VSLENGTH:
661 error(psh, "%.*s: parameter not set", p - var - 1, var);
662 /* NOTREACHED */
663 }
664 }
665
666 if (set && subtype != VSPLUS) {
667 /* insert the value of the variable */
668 if (special) {
669 varvalue(psh, var, varflags & VSQUOTE, subtype, flag);
670 if (subtype == VSLENGTH) {
671 varlen = (int)(psh->expdest - stackblock(psh) - startloc);
672 STADJUST(psh, -varlen, psh->expdest);
673 }
674 } else {
675 char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
676 : BASESYNTAX;
677
678 if (subtype == VSLENGTH) {
679 for (;*val; val++)
680 varlen++;
681 } else {
682 while (*val) {
683 if (quotes && syntax[(int)*val] == CCTL)
684 STPUTC(psh, CTLESC, psh->expdest);
685 STPUTC(psh, *val++, psh->expdest);
686 }
687
688 }
689 }
690 }
691
692
693 apply_ifs = ((varflags & VSQUOTE) == 0 ||
694 (*var == '@' && psh->shellparam.nparam != 1));
695
696 switch (subtype) {
697 case VSLENGTH:
698 psh->expdest = cvtnum(psh, varlen, psh->expdest);
699 break;
700
701 case VSNORMAL:
702 break;
703
704 case VSPLUS:
705 set = !set;
706 /* FALLTHROUGH */
707 case VSMINUS:
708 if (!set) {
709 argstr(psh, p, flag | (apply_ifs ? EXP_IFS_SPLIT : 0));
710 /*
711 * ${x-a b c} doesn't get split, but removing the
712 * 'apply_ifs = 0' apparantly breaks ${1+"$@"}..
713 * ${x-'a b' c} should generate 2 args.
714 */
715 /* We should have marked stuff already */
716 apply_ifs = 0;
717 }
718 break;
719
720 case VSTRIMLEFT:
721 case VSTRIMLEFTMAX:
722 case VSTRIMRIGHT:
723 case VSTRIMRIGHTMAX:
724 if (!set)
725 break;
726 /*
727 * Terminate the string and start recording the pattern
728 * right after it
729 */
730 STPUTC(psh, '\0', psh->expdest);
731 patloc = (int)(psh->expdest - stackblock(psh));
732 if (subevalvar(psh, p, NULL, patloc, subtype,
733 startloc, varflags) == 0) {
734 int amount = (int)(psh->expdest - stackblock(psh) - patloc) + 1;
735 STADJUST(psh, -amount, psh->expdest);
736 }
737 /* Remove any recorded regions beyond start of variable */
738 removerecordregions(psh, startloc);
739 apply_ifs = 1;
740 break;
741
742 case VSASSIGN:
743 case VSQUESTION:
744 if (set)
745 break;
746 if (subevalvar(psh, p, var, 0, subtype, startloc, varflags)) {
747 varflags &= ~VSNUL;
748 /*
749 * Remove any recorded regions beyond
750 * start of variable
751 */
752 removerecordregions(psh, startloc);
753 goto again;
754 }
755 apply_ifs = 0;
756 break;
757
758 default:
759 abort();
760 }
761
762 if (apply_ifs)
763 recordregion(psh, startloc, (int)(psh->expdest - stackblock(psh)),
764 varflags & VSQUOTE);
765
766 if (subtype != VSNORMAL) { /* skip to end of alternative */
767 int nesting = 1;
768 for (;;) {
769 if ((c = *p++) == CTLESC)
770 p++;
771 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
772 if (set)
773 psh->argbackq = psh->argbackq->next;
774 } else if (c == CTLVAR) {
775 if ((*p++ & VSTYPE) != VSNORMAL)
776 nesting++;
777 } else if (c == CTLENDVAR) {
778 if (--nesting == 0)
779 break;
780 }
781 }
782 }
783 return p;
784}
785
786
787
788/*
789 * Test whether a specialized variable is set.
790 */
791
792STATIC int
793varisset(shinstance *psh, char *name, int nulok)
794{
795 if (*name == '!')
796 return psh->backgndpid != -1;
797 else if (*name == '@' || *name == '*') {
798 if (*psh->shellparam.p == NULL)
799 return 0;
800
801 if (nulok) {
802 char **av;
803
804 for (av = psh->shellparam.p; *av; av++)
805 if (**av != '\0')
806 return 1;
807 return 0;
808 }
809 } else if (is_digit(*name)) {
810 char *ap;
811 int num = atoi(name);
812
813 if (num > psh->shellparam.nparam)
814 return 0;
815
816 if (num == 0)
817 ap = psh->arg0;
818 else
819 ap = psh->shellparam.p[num - 1];
820
821 if (nulok && (ap == NULL || *ap == '\0'))
822 return 0;
823 }
824 return 1;
825}
826
827
828
829/*
830 * Add the value of a specialized variable to the stack string.
831 */
832
833STATIC void
834varvalue(shinstance *psh, char *name, int quoted, int subtype, int flag)
835{
836 int num;
837 char *p;
838 int i;
839 char sep;
840 char **ap;
841 char const *syntax;
842
843#define STRTODEST(p) \
844 do {\
845 if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) { \
846 syntax = quoted? DQSYNTAX : BASESYNTAX; \
847 while (*p) { \
848 if (syntax[(int)*p] == CCTL) \
849 STPUTC(psh, CTLESC, psh->expdest); \
850 STPUTC(psh, *p++, psh->expdest); \
851 } \
852 } else \
853 while (*p) \
854 STPUTC(psh, *p++, psh->expdest); \
855 } while (0)
856
857
858 switch (*name) {
859 case '$':
860 num = psh->rootpid;
861 goto numvar;
862 case '?':
863 num = psh->exitstatus;
864 goto numvar;
865 case '#':
866 num = psh->shellparam.nparam;
867 goto numvar;
868 case '!':
869 num = psh->backgndpid;
870numvar:
871 psh->expdest = cvtnum(psh, num, psh->expdest);
872 break;
873 case '-':
874 for (i = 0; psh->optlist[i].name; i++) {
875 if (psh->optlist[i].val)
876 STPUTC(psh, psh->optlist[i].letter, psh->expdest);
877 }
878 break;
879 case '@':
880 if (flag & EXP_FULL && quoted) {
881 for (ap = psh->shellparam.p ; (p = *ap++) != NULL ; ) {
882 STRTODEST(p);
883 if (*ap)
884 STPUTC(psh, '\0', psh->expdest);
885 }
886 break;
887 }
888 /* fall through */
889 case '*':
890 if (ifsset(psh) != 0)
891 sep = ifsval(psh)[0];
892 else
893 sep = ' ';
894 for (ap = psh->shellparam.p ; (p = *ap++) != NULL ; ) {
895 STRTODEST(p);
896 if (*ap && sep)
897 STPUTC(psh, sep, psh->expdest);
898 }
899 break;
900 case '0':
901 p = psh->arg0;
902 STRTODEST(p);
903 break;
904 default:
905 if (is_digit(*name)) {
906 num = atoi(name);
907 if (num > 0 && num <= psh->shellparam.nparam) {
908 p = psh->shellparam.p[num - 1];
909 STRTODEST(p);
910 }
911 }
912 break;
913 }
914}
915
916
917
918/*
919 * Record the fact that we have to scan this region of the
920 * string for IFS characters.
921 */
922
923STATIC void
924recordregion(shinstance *psh, int start, int end, int inquotes)
925{
926 struct ifsregion *ifsp;
927
928 if (psh->ifslastp == NULL) {
929 ifsp = &psh->ifsfirst;
930 } else {
931 if (psh->ifslastp->endoff == start
932 && psh->ifslastp->inquotes == inquotes) {
933 /* extend previous area */
934 psh->ifslastp->endoff = end;
935 return;
936 }
937 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
938 psh->ifslastp->next = ifsp;
939 }
940 psh->ifslastp = ifsp;
941 psh->ifslastp->next = NULL;
942 psh->ifslastp->begoff = start;
943 psh->ifslastp->endoff = end;
944 psh->ifslastp->inquotes = inquotes;
945}
946
947
948
949/*
950 * Break the argument string into pieces based upon IFS and add the
951 * strings to the argument list. The regions of the string to be
952 * searched for IFS characters have been stored by recordregion.
953 */
954STATIC void
955ifsbreakup(shinstance *psh, char *string, struct arglist *arglist)
956{
957 struct ifsregion *ifsp;
958 struct strlist *sp;
959 char *start;
960 char *p;
961 char *q;
962 const char *ifs;
963 const char *ifsspc;
964 int inquotes;
965
966 start = string;
967 ifsspc = NULL;
968 inquotes = 0;
969
970 if (psh->ifslastp == NULL) {
971 /* Return entire argument, IFS doesn't apply to any of it */
972 sp = (struct strlist *)stalloc(psh, sizeof *sp);
973 sp->text = start;
974 *arglist->lastp = sp;
975 arglist->lastp = &sp->next;
976 return;
977 }
978
979 ifs = ifsset(psh) ? ifsval(psh) : " \t\n";
980
981 for (ifsp = &psh->ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
982 p = string + ifsp->begoff;
983 inquotes = ifsp->inquotes;
984 ifsspc = NULL;
985 while (p < string + ifsp->endoff) {
986 q = p;
987 if (*p == CTLESC)
988 p++;
989 if (inquotes) {
990 /* Only NULs (probably from "$@") end args */
991 if (*p != 0) {
992 p++;
993 continue;
994 }
995 } else {
996 if (!strchr(ifs, *p)) {
997 p++;
998 continue;
999 }
1000 ifsspc = strchr(" \t\n", *p);
1001
1002 /* Ignore IFS whitespace at start */
1003 if (q == start && ifsspc != NULL) {
1004 p++;
1005 start = p;
1006 continue;
1007 }
1008 }
1009
1010 /* Save this argument... */
1011 *q = '\0';
1012 sp = (struct strlist *)stalloc(psh, sizeof *sp);
1013 sp->text = start;
1014 *arglist->lastp = sp;
1015 arglist->lastp = &sp->next;
1016 p++;
1017
1018 if (ifsspc != NULL) {
1019 /* Ignore further trailing IFS whitespace */
1020 for (; p < string + ifsp->endoff; p++) {
1021 q = p;
1022 if (*p == CTLESC)
1023 p++;
1024 if (strchr(ifs, *p) == NULL) {
1025 p = q;
1026 break;
1027 }
1028 if (strchr(" \t\n", *p) == NULL) {
1029 p++;
1030 break;
1031 }
1032 }
1033 }
1034 start = p;
1035 }
1036 }
1037
1038 /*
1039 * Save anything left as an argument.
1040 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1041 * generating 2 arguments, the second of which is empty.
1042 * Some recent clarification of the Posix spec say that it
1043 * should only generate one....
1044 */
1045 if (*start /* || (!ifsspc && start > string) */) {
1046 sp = (struct strlist *)stalloc(psh, sizeof *sp);
1047 sp->text = start;
1048 *arglist->lastp = sp;
1049 arglist->lastp = &sp->next;
1050 }
1051}
1052
1053STATIC void
1054ifsfree(shinstance *psh)
1055{
1056 while (psh->ifsfirst.next != NULL) {
1057 struct ifsregion *ifsp;
1058 INTOFF;
1059 ifsp = psh->ifsfirst.next->next;
1060 ckfree(psh->ifsfirst.next);
1061 psh->ifsfirst.next = ifsp;
1062 INTON;
1063 }
1064 psh->ifslastp = NULL;
1065 psh->ifsfirst.next = NULL;
1066}
1067
1068
1069
1070/*
1071 * Expand shell metacharacters. At this point, the only control characters
1072 * should be escapes. The results are stored in the list psh->exparg.
1073 */
1074
1075//char *expdir;
1076
1077
1078STATIC void
1079expandmeta(shinstance *psh, struct strlist *str, int flag)
1080{
1081 char *p;
1082 struct strlist **savelastp;
1083 struct strlist *sp;
1084 char c;
1085 /* TODO - EXP_REDIR */
1086
1087 while (str) {
1088 if (fflag(psh))
1089 goto nometa;
1090 p = str->text;
1091 for (;;) { /* fast check for meta chars */
1092 if ((c = *p++) == '\0')
1093 goto nometa;
1094 if (c == '*' || c == '?' || c == '[' || c == '!')
1095 break;
1096 }
1097 savelastp = psh->exparg.lastp;
1098 INTOFF;
1099 if (psh->expdir == NULL) {
1100 size_t i = strlen(str->text);
1101 psh->expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
1102 }
1103
1104 expmeta(psh, psh->expdir, str->text);
1105 ckfree(psh->expdir);
1106 psh->expdir = NULL;
1107 INTON;
1108 if (psh->exparg.lastp == savelastp) {
1109 /*
1110 * no matches
1111 */
1112nometa:
1113 *psh->exparg.lastp = str;
1114 rmescapes(psh, str->text);
1115 psh->exparg.lastp = &str->next;
1116 } else {
1117 *psh->exparg.lastp = NULL;
1118 *savelastp = sp = expsort(*savelastp);
1119 while (sp->next != NULL)
1120 sp = sp->next;
1121 psh->exparg.lastp = &sp->next;
1122 }
1123 str = str->next;
1124 }
1125}
1126
1127
1128/*
1129 * Do metacharacter (i.e. *, ?, [...]) expansion.
1130 */
1131
1132STATIC void
1133expmeta(shinstance *psh, char *enddir, char *name)
1134{
1135 char *p;
1136 const char *cp;
1137 char *q;
1138 char *start;
1139 char *endname;
1140 int metaflag;
1141 struct stat statb;
1142 DIR *dirp;
1143 struct dirent *dp;
1144 int atend;
1145 int matchdot;
1146
1147 metaflag = 0;
1148 start = name;
1149 for (p = name ; ; p++) {
1150 if (*p == '*' || *p == '?')
1151 metaflag = 1;
1152 else if (*p == '[') {
1153 q = p + 1;
1154 if (*q == '!')
1155 q++;
1156 for (;;) {
1157 while (*q == CTLQUOTEMARK)
1158 q++;
1159 if (*q == CTLESC)
1160 q++;
1161 if (*q == '/' || *q == '\0')
1162 break;
1163 if (*++q == ']') {
1164 metaflag = 1;
1165 break;
1166 }
1167 }
1168 } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) {
1169 metaflag = 1;
1170 } else if (*p == '\0')
1171 break;
1172 else if (*p == CTLQUOTEMARK)
1173 continue;
1174 else if (*p == CTLESC)
1175 p++;
1176 if (*p == '/') {
1177 if (metaflag)
1178 break;
1179 start = p + 1;
1180 }
1181 }
1182 if (metaflag == 0) { /* we've reached the end of the file name */
1183 if (enddir != psh->expdir)
1184 metaflag++;
1185 for (p = name ; ; p++) {
1186 if (*p == CTLQUOTEMARK)
1187 continue;
1188 if (*p == CTLESC)
1189 p++;
1190 *enddir++ = *p;
1191 if (*p == '\0')
1192 break;
1193 }
1194 if (metaflag == 0 || lstat(psh->expdir, &statb) >= 0)
1195 addfname(psh, psh->expdir);
1196 return;
1197 }
1198 endname = p;
1199 if (start != name) {
1200 p = name;
1201 while (p < start) {
1202 while (*p == CTLQUOTEMARK)
1203 p++;
1204 if (*p == CTLESC)
1205 p++;
1206 *enddir++ = *p++;
1207 }
1208 }
1209 if (enddir == psh->expdir) {
1210 cp = ".";
1211 } else if (enddir == psh->expdir + 1 && *psh->expdir == '/') {
1212 cp = "/";
1213 } else {
1214 cp = psh->expdir;
1215 enddir[-1] = '\0';
1216 }
1217 if ((dirp = opendir(cp)) == NULL)
1218 return;
1219 if (enddir != psh->expdir)
1220 enddir[-1] = '/';
1221 if (*endname == 0) {
1222 atend = 1;
1223 } else {
1224 atend = 0;
1225 *endname++ = '\0';
1226 }
1227 matchdot = 0;
1228 p = start;
1229 while (*p == CTLQUOTEMARK)
1230 p++;
1231 if (*p == CTLESC)
1232 p++;
1233 if (*p == '.')
1234 matchdot++;
1235 while (! int_pending() && (dp = readdir(dirp)) != NULL) {
1236 if (dp->d_name[0] == '.' && ! matchdot)
1237 continue;
1238 if (patmatch(psh, start, dp->d_name, 0)) {
1239 if (atend) {
1240 scopy(dp->d_name, enddir);
1241 addfname(psh, psh->expdir);
1242 } else {
1243 for (p = enddir, cp = dp->d_name;
1244 (*p++ = *cp++) != '\0';)
1245 continue;
1246 p[-1] = '/';
1247 expmeta(psh, p, endname);
1248 }
1249 }
1250 }
1251 closedir(dirp);
1252 if (! atend)
1253 endname[-1] = '/';
1254}
1255
1256
1257/*
1258 * Add a file name to the list.
1259 */
1260
1261STATIC void
1262addfname(shinstance *psh, char *name)
1263{
1264 char *p;
1265 struct strlist *sp;
1266
1267 p = stalloc(psh, strlen(name) + 1);
1268 scopy(name, p);
1269 sp = (struct strlist *)stalloc(psh, sizeof *sp);
1270 sp->text = p;
1271 *psh->exparg.lastp = sp;
1272 psh->exparg.lastp = &sp->next;
1273}
1274
1275
1276/*
1277 * Sort the results of file name expansion. It calculates the number of
1278 * strings to sort and then calls msort (short for merge sort) to do the
1279 * work.
1280 */
1281
1282STATIC struct strlist *
1283expsort(struct strlist *str)
1284{
1285 int len;
1286 struct strlist *sp;
1287
1288 len = 0;
1289 for (sp = str ; sp ; sp = sp->next)
1290 len++;
1291 return msort(str, len);
1292}
1293
1294
1295STATIC struct strlist *
1296msort(struct strlist *list, int len)
1297{
1298 struct strlist *p, *q = NULL;
1299 struct strlist **lpp;
1300 int half;
1301 int n;
1302
1303 if (len <= 1)
1304 return list;
1305 half = len >> 1;
1306 p = list;
1307 for (n = half ; --n >= 0 ; ) {
1308 q = p;
1309 p = p->next;
1310 }
1311 q->next = NULL; /* terminate first half of list */
1312 q = msort(list, half); /* sort first half of list */
1313 p = msort(p, len - half); /* sort second half */
1314 lpp = &list;
1315 for (;;) {
1316 if (strcmp(p->text, q->text) < 0) {
1317 *lpp = p;
1318 lpp = &p->next;
1319 if ((p = *lpp) == NULL) {
1320 *lpp = q;
1321 break;
1322 }
1323 } else {
1324 *lpp = q;
1325 lpp = &q->next;
1326 if ((q = *lpp) == NULL) {
1327 *lpp = p;
1328 break;
1329 }
1330 }
1331 }
1332 return list;
1333}
1334
1335
1336
1337/*
1338 * Returns true if the pattern matches the string.
1339 */
1340
1341int
1342patmatch(shinstance *psh, char *pattern, char *string, int squoted)
1343{
1344#ifdef notdef
1345 if (pattern[0] == '!' && pattern[1] == '!')
1346 return 1 - pmatch(pattern + 2, string);
1347 else
1348#endif
1349 return pmatch(pattern, string, squoted);
1350}
1351
1352
1353STATIC int
1354pmatch(char *pattern, char *string, int squoted)
1355{
1356 char *p, *q;
1357 char c;
1358
1359 p = pattern;
1360 q = string;
1361 for (;;) {
1362 switch (c = *p++) {
1363 case '\0':
1364 goto breakloop;
1365 case CTLESC:
1366 if (squoted && *q == CTLESC)
1367 q++;
1368 if (*q++ != *p++)
1369 return 0;
1370 break;
1371 case CTLQUOTEMARK:
1372 continue;
1373 case '?':
1374 if (squoted && *q == CTLESC)
1375 q++;
1376 if (*q++ == '\0')
1377 return 0;
1378 break;
1379 case '*':
1380 c = *p;
1381 while (c == CTLQUOTEMARK || c == '*')
1382 c = *++p;
1383 if (c != CTLESC && c != CTLQUOTEMARK &&
1384 c != '?' && c != '*' && c != '[') {
1385 while (*q != c) {
1386 if (squoted && *q == CTLESC &&
1387 q[1] == c)
1388 break;
1389 if (*q == '\0')
1390 return 0;
1391 if (squoted && *q == CTLESC)
1392 q++;
1393 q++;
1394 }
1395 }
1396 do {
1397 if (pmatch(p, q, squoted))
1398 return 1;
1399 if (squoted && *q == CTLESC)
1400 q++;
1401 } while (*q++ != '\0');
1402 return 0;
1403 case '[': {
1404 char *endp;
1405 int invert, found;
1406 char chr;
1407
1408 endp = p;
1409 if (*endp == '!')
1410 endp++;
1411 for (;;) {
1412 while (*endp == CTLQUOTEMARK)
1413 endp++;
1414 if (*endp == '\0')
1415 goto dft; /* no matching ] */
1416 if (*endp == CTLESC)
1417 endp++;
1418 if (*++endp == ']')
1419 break;
1420 }
1421 invert = 0;
1422 if (*p == '!') {
1423 invert++;
1424 p++;
1425 }
1426 found = 0;
1427 chr = *q++;
1428 if (squoted && chr == CTLESC)
1429 chr = *q++;
1430 if (chr == '\0')
1431 return 0;
1432 c = *p++;
1433 do {
1434 if (c == CTLQUOTEMARK)
1435 continue;
1436 if (c == CTLESC)
1437 c = *p++;
1438 if (*p == '-' && p[1] != ']') {
1439 p++;
1440 while (*p == CTLQUOTEMARK)
1441 p++;
1442 if (*p == CTLESC)
1443 p++;
1444 if (chr >= c && chr <= *p)
1445 found = 1;
1446 p++;
1447 } else {
1448 if (chr == c)
1449 found = 1;
1450 }
1451 } while ((c = *p++) != ']');
1452 if (found == invert)
1453 return 0;
1454 break;
1455 }
1456dft: default:
1457 if (squoted && *q == CTLESC)
1458 q++;
1459 if (*q++ != c)
1460 return 0;
1461 break;
1462 }
1463 }
1464breakloop:
1465 if (*q != '\0')
1466 return 0;
1467 return 1;
1468}
1469
1470
1471
1472/*
1473 * Remove any CTLESC characters from a string.
1474 */
1475
1476void
1477rmescapes(shinstance *psh, char *str)
1478{
1479 char *p, *q;
1480
1481 p = str;
1482 while (*p != CTLESC && *p != CTLQUOTEMARK) {
1483 if (*p++ == '\0')
1484 return;
1485 }
1486 q = p;
1487 while (*p) {
1488 if (*p == CTLQUOTEMARK) {
1489 p++;
1490 continue;
1491 }
1492 if (*p == CTLESC)
1493 p++;
1494 *q++ = *p++;
1495 }
1496 *q = '\0';
1497}
1498
1499
1500
1501/*
1502 * See if a pattern matches in a case statement.
1503 */
1504
1505int
1506casematch(shinstance *psh, union node *pattern, char *val)
1507{
1508 struct stackmark smark;
1509 int result;
1510 char *p;
1511
1512 setstackmark(psh, &smark);
1513 psh->argbackq = pattern->narg.backquote;
1514 STARTSTACKSTR(psh, psh->expdest);
1515 psh->ifslastp = NULL;
1516 argstr(psh, pattern->narg.text, EXP_TILDE | EXP_CASE);
1517 STPUTC(psh, '\0', psh->expdest);
1518 p = grabstackstr(psh, psh->expdest);
1519 result = patmatch(psh, p, val, 0);
1520 popstackmark(psh, &smark);
1521 return result;
1522}
1523
1524/*
1525 * Our own itoa().
1526 */
1527
1528STATIC char *
1529cvtnum(shinstance *psh, int num, char *buf)
1530{
1531 char temp[32];
1532 int neg = num < 0;
1533 char *p = temp + 31;
1534
1535 temp[31] = '\0';
1536
1537 do {
1538 *--p = num % 10 + '0';
1539 } while ((num /= 10) != 0);
1540
1541 if (neg)
1542 *--p = '-';
1543
1544 while (*p)
1545 STPUTC(psh, *p++, buf);
1546 return buf;
1547}
1548
1549/*
1550 * Do most of the work for wordexp(3).
1551 */
1552
1553int
1554wordexpcmd(shinstance *psh, int argc, char **argv)
1555{
1556 size_t len;
1557 int i;
1558
1559 out1fmt(psh, "%d", argc - 1);
1560 out1c(psh, '\0');
1561 for (i = 1, len = 0; i < argc; i++)
1562 len += strlen(argv[i]);
1563 out1fmt(psh, "%zd", len);
1564 out1c(psh, '\0');
1565 for (i = 1; i < argc; i++) {
1566 out1str(psh, argv[i]);
1567 out1c(psh, '\0');
1568 }
1569 return (0);
1570}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette