VirtualBox

source: kBuild/trunk/src/ash/expand.c@ 623

Last change on this file since 623 was 622, checked in by bird, 18 years ago

Current libc code (based on NetBSD sh 2005-07-03).

File size: 32.1 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#include <sys/cdefs.h>
36#ifndef lint
37#if 0
38static 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
81struct 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
89char *expdest; /* output of current string */
90struct nodelist *argbackq; /* list of back quote expressions */
91struct ifsregion ifsfirst; /* first struct in list of ifs regions */
92struct ifsregion *ifslastp; /* last struct in list */
93struct arglist exparg; /* holds expanded arg list */
94
95STATIC void argstr(char *, int);
96STATIC char *exptilde(char *, int);
97STATIC void expbackq(union node *, int, int);
98STATIC int subevalvar(char *, char *, int, int, int, int);
99STATIC char *evalvar(char *, int);
100STATIC int varisset(char *, int);
101STATIC void varvalue(char *, int, int, int);
102STATIC void recordregion(int, int, int);
103STATIC void removerecordregions(int);
104STATIC void ifsbreakup(char *, struct arglist *);
105STATIC void ifsfree(void);
106STATIC void expandmeta(struct strlist *, int);
107STATIC void expmeta(char *, char *);
108STATIC void addfname(char *);
109STATIC struct strlist *expsort(struct strlist *);
110STATIC struct strlist *msort(struct strlist *, int);
111STATIC int pmatch(char *, char *, int);
112STATIC char *cvtnum(int, char *);
113
114/*
115 * Expand shell variables and backquotes inside a here document.
116 */
117
118void
119expandhere(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
134void
135expandarg(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
183STATIC void
184argstr(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
259STATIC char *
260exptilde(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 }
282done:
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);
301lose:
302 *p = c;
303 return (startp);
304}
305
306
307STATIC void
308removerecordregions(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 */
351void
352expari(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
413STATIC void
414expbackq(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
487STATIC int
488subevalvar(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
587recordleft:
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
595recordright:
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
609STATIC char *
610evalvar(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
631again: /* 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
786STATIC int
787varisset(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
827STATIC void
828varvalue(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;
864numvar:
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
917STATIC void
918recordregion(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 */
948STATIC void
949ifsbreakup(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
1047STATIC void
1048ifsfree(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
1069char *expdir;
1070
1071
1072STATIC void
1073expandmeta(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 */
1106nometa:
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
1126STATIC void
1127expmeta(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
1255STATIC void
1256addfname(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
1276STATIC struct strlist *
1277expsort(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
1289STATIC struct strlist *
1290msort(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
1335int
1336patmatch(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
1347STATIC int
1348pmatch(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 }
1450dft: default:
1451 if (squoted && *q == CTLESC)
1452 q++;
1453 if (*q++ != c)
1454 return 0;
1455 break;
1456 }
1457 }
1458breakloop:
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
1470void
1471rmescapes(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
1499int
1500casematch(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
1522STATIC char *
1523cvtnum(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
1547int
1548wordexpcmd(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}
Note: See TracBrowser for help on using the repository browser.

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