Magellan Linux

Contents of /trunk/mkinitrd-magellan/klibc/usr/dash/expand.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1122 - (show annotations) (download)
Wed Aug 18 21:11:40 2010 UTC (13 years, 8 months ago) by niro
File MIME type: text/plain
File size: 33025 byte(s)
-updated to klibc-1.5.19
1 /*-
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1997-2005
5 * Herbert Xu <herbert@gondor.apana.org.au>. 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/types.h>
36 #include <sys/time.h>
37 #include <sys/stat.h>
38 #include <dirent.h>
39 #include <unistd.h>
40 #ifdef HAVE_GETPWNAM
41 #include <pwd.h>
42 #endif
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <stdint.h>
46 #include <limits.h>
47 #include <string.h>
48 #include <fnmatch.h>
49 #ifdef HAVE_GLOB
50 #include <glob.h>
51 #endif
52 #include <ctype.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 "output.h"
70 #include "memalloc.h"
71 #include "error.h"
72 #include "mystring.h"
73 #include "show.h"
74 #include "system.h"
75
76 /*
77 * _rmescape() flags
78 */
79 #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
80 #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
81 #define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
82 #define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
83
84 /* Add CTLESC when necessary. */
85 #define QUOTES_ESC (EXP_FULL | EXP_CASE | EXP_QPAT)
86 /* Do not skip NUL characters. */
87 #define QUOTES_KEEPNUL EXP_TILDE
88
89 /*
90 * Structure specifying which parts of the string should be searched
91 * for IFS characters.
92 */
93
94 struct ifsregion {
95 struct ifsregion *next; /* next region in list */
96 int begoff; /* offset of start of region */
97 int endoff; /* offset of end of region */
98 int nulonly; /* search for nul bytes only */
99 };
100
101 /* output of current string */
102 static char *expdest;
103 /* list of back quote expressions */
104 static struct nodelist *argbackq;
105 /* first struct in list of ifs regions */
106 static struct ifsregion ifsfirst;
107 /* last struct in list */
108 static struct ifsregion *ifslastp;
109 /* holds expanded arg list */
110 static struct arglist exparg;
111
112 STATIC void argstr(char *, int);
113 STATIC char *exptilde(char *, char *, int);
114 STATIC void expbackq(union node *, int);
115 STATIC const char *subevalvar(char *, char *, int, int, int, int, int);
116 STATIC char *evalvar(char *, int);
117 STATIC size_t strtodest(const char *, const char *, int);
118 STATIC void memtodest(const char *, size_t, const char *, int);
119 STATIC ssize_t varvalue(char *, int, int);
120 STATIC void ifsfree(void);
121 STATIC void expandmeta(struct strlist *, int);
122 #ifdef HAVE_GLOB
123 STATIC void addglob(const glob_t *);
124 #else
125 STATIC void expmeta(char *, char *);
126 STATIC struct strlist *expsort(struct strlist *);
127 STATIC struct strlist *msort(struct strlist *, int);
128 #endif
129 STATIC void addfname(char *);
130 STATIC int patmatch(char *, const char *);
131 #ifndef HAVE_FNMATCH
132 STATIC int pmatch(const char *, const char *);
133 #else
134 #define pmatch(a, b) !fnmatch((a), (b), 0)
135 #endif
136 STATIC int cvtnum(intmax_t);
137 STATIC size_t esclen(const char *, const char *);
138 STATIC char *scanleft(char *, char *, char *, char *, int, int);
139 STATIC char *scanright(char *, char *, char *, char *, int, int);
140 STATIC void varunset(const char *, const char *, const char *, int)
141 __attribute__((__noreturn__));
142
143
144 /*
145 * Prepare a pattern for a glob(3) call.
146 *
147 * Returns an stalloced string.
148 */
149
150 STATIC inline char *
151 preglob(const char *pattern, int flag) {
152 flag |= RMESCAPE_GLOB;
153 return _rmescapes((char *)pattern, flag);
154 }
155
156
157 STATIC size_t
158 esclen(const char *start, const char *p) {
159 size_t esc = 0;
160
161 while (p > start && *--p == (char)CTLESC) {
162 esc++;
163 }
164 return esc;
165 }
166
167
168 static inline const char *getpwhome(const char *name)
169 {
170 #ifdef HAVE_GETPWNAM
171 struct passwd *pw = getpwnam(name);
172 return pw ? pw->pw_dir : 0;
173 #else
174 return 0;
175 #endif
176 }
177
178
179 /*
180 * Perform variable substitution and command substitution on an argument,
181 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
182 * perform splitting and file name expansion. When arglist is NULL, perform
183 * here document expansion.
184 */
185
186 void
187 expandarg(union node *arg, struct arglist *arglist, int flag)
188 {
189 struct strlist *sp;
190 char *p;
191
192 argbackq = arg->narg.backquote;
193 STARTSTACKSTR(expdest);
194 ifsfirst.next = NULL;
195 ifslastp = NULL;
196 argstr(arg->narg.text, flag);
197 p = _STPUTC('\0', expdest);
198 expdest = p - 1;
199 if (arglist == NULL) {
200 return; /* here document expanded */
201 }
202 p = grabstackstr(p);
203 exparg.lastp = &exparg.list;
204 /*
205 * TODO - EXP_REDIR
206 */
207 if (flag & EXP_FULL) {
208 ifsbreakup(p, &exparg);
209 *exparg.lastp = NULL;
210 exparg.lastp = &exparg.list;
211 expandmeta(exparg.list, flag);
212 } else {
213 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
214 rmescapes(p);
215 sp = (struct strlist *)stalloc(sizeof (struct strlist));
216 sp->text = p;
217 *exparg.lastp = sp;
218 exparg.lastp = &sp->next;
219 }
220 if (ifsfirst.next)
221 ifsfree();
222 *exparg.lastp = NULL;
223 if (exparg.list) {
224 *arglist->lastp = exparg.list;
225 arglist->lastp = exparg.lastp;
226 }
227 }
228
229
230
231 /*
232 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
233 * characters to allow for further processing. Otherwise treat
234 * $@ like $* since no splitting will be performed.
235 */
236
237 STATIC void
238 argstr(char *p, int flag)
239 {
240 static const char spclchars[] = {
241 '=',
242 ':',
243 CTLQUOTEMARK,
244 CTLENDVAR,
245 CTLESC,
246 CTLVAR,
247 CTLBACKQ,
248 CTLENDARI,
249 0
250 };
251 const char *reject = spclchars;
252 int c;
253 int breakall = (flag & (EXP_WORD | EXP_QUOTED)) == EXP_WORD;
254 int inquotes;
255 size_t length;
256 int startloc;
257
258 if (!(flag & EXP_VARTILDE)) {
259 reject += 2;
260 } else if (flag & EXP_VARTILDE2) {
261 reject++;
262 }
263 inquotes = 0;
264 length = 0;
265 if (flag & EXP_TILDE) {
266 char *q;
267
268 flag &= ~EXP_TILDE;
269 tilde:
270 q = p;
271 if (*q == '~')
272 p = exptilde(p, q, flag);
273 }
274 start:
275 startloc = expdest - (char *)stackblock();
276 for (;;) {
277 length += strcspn(p + length, reject);
278 c = (signed char)p[length];
279 if (c && (!(c & 0x80) || c == CTLENDARI)) {
280 /* c == '=' || c == ':' || c == CTLENDARI */
281 length++;
282 }
283 if (length > 0) {
284 int newloc;
285 expdest = stnputs(p, length, expdest);
286 newloc = expdest - (char *)stackblock();
287 if (breakall && !inquotes && newloc > startloc) {
288 recordregion(startloc, newloc, 0);
289 }
290 startloc = newloc;
291 }
292 p += length + 1;
293 length = 0;
294
295 switch (c) {
296 case '\0':
297 goto breakloop;
298 case '=':
299 if (flag & EXP_VARTILDE2) {
300 p--;
301 continue;
302 }
303 flag |= EXP_VARTILDE2;
304 reject++;
305 /* fall through */
306 case ':':
307 /*
308 * sort of a hack - expand tildes in variable
309 * assignments (after the first '=' and after ':'s).
310 */
311 if (*--p == '~') {
312 goto tilde;
313 }
314 continue;
315 }
316
317 switch (c) {
318 case CTLENDVAR: /* ??? */
319 goto breakloop;
320 case CTLQUOTEMARK:
321 inquotes ^= EXP_QUOTED;
322 /* "$@" syntax adherence hack */
323 if (inquotes && !memcmp(p, dolatstr + 1,
324 DOLATSTRLEN - 1)) {
325 p = evalvar(p + 1, flag | inquotes) + 1;
326 goto start;
327 }
328 addquote:
329 if (flag & QUOTES_ESC) {
330 p--;
331 length++;
332 startloc++;
333 }
334 break;
335 case CTLESC:
336 startloc++;
337 length++;
338
339 /*
340 * Quoted parameter expansion pattern: remove quote
341 * unless inside inner quotes or we have a literal
342 * backslash.
343 */
344 if (((flag | inquotes) & (EXP_QPAT | EXP_QUOTED)) ==
345 EXP_QPAT && *p != '\\')
346 break;
347
348 goto addquote;
349 case CTLVAR:
350 p = evalvar(p, flag | inquotes);
351 goto start;
352 case CTLBACKQ:
353 expbackq(argbackq->n, flag | inquotes);
354 argbackq = argbackq->next;
355 goto start;
356 case CTLENDARI:
357 p--;
358 expari(flag | inquotes);
359 goto start;
360 }
361 }
362 breakloop:
363 ;
364 }
365
366 STATIC char *
367 exptilde(char *startp, char *p, int flag)
368 {
369 signed char c;
370 char *name;
371 const char *home;
372 int quotes = flag & QUOTES_ESC;
373 int startloc;
374
375 name = p + 1;
376
377 while ((c = *++p) != '\0') {
378 switch(c) {
379 case CTLESC:
380 return (startp);
381 case CTLQUOTEMARK:
382 return (startp);
383 case ':':
384 if (flag & EXP_VARTILDE)
385 goto done;
386 break;
387 case '/':
388 case CTLENDVAR:
389 goto done;
390 }
391 }
392 done:
393 *p = '\0';
394 if (*name == '\0') {
395 home = lookupvar(homestr);
396 } else {
397 home = getpwhome(name);
398 }
399 if (!home || !*home)
400 goto lose;
401 *p = c;
402 startloc = expdest - (char *)stackblock();
403 strtodest(home, SQSYNTAX, quotes);
404 recordregion(startloc, expdest - (char *)stackblock(), 0);
405 return (p);
406 lose:
407 *p = c;
408 return (startp);
409 }
410
411
412 void
413 removerecordregions(int endoff)
414 {
415 if (ifslastp == NULL)
416 return;
417
418 if (ifsfirst.endoff > endoff) {
419 while (ifsfirst.next != NULL) {
420 struct ifsregion *ifsp;
421 INTOFF;
422 ifsp = ifsfirst.next->next;
423 ckfree(ifsfirst.next);
424 ifsfirst.next = ifsp;
425 INTON;
426 }
427 if (ifsfirst.begoff > endoff)
428 ifslastp = NULL;
429 else {
430 ifslastp = &ifsfirst;
431 ifsfirst.endoff = endoff;
432 }
433 return;
434 }
435
436 ifslastp = &ifsfirst;
437 while (ifslastp->next && ifslastp->next->begoff < endoff)
438 ifslastp=ifslastp->next;
439 while (ifslastp->next != NULL) {
440 struct ifsregion *ifsp;
441 INTOFF;
442 ifsp = ifslastp->next->next;
443 ckfree(ifslastp->next);
444 ifslastp->next = ifsp;
445 INTON;
446 }
447 if (ifslastp->endoff > endoff)
448 ifslastp->endoff = endoff;
449 }
450
451
452 /*
453 * Expand arithmetic expression. Backup to start of expression,
454 * evaluate, place result in (backed up) result, adjust string position.
455 */
456 void
457 expari(int flag)
458 {
459 struct stackmark sm;
460 char *p, *start;
461 int begoff;
462 int len;
463 intmax_t result;
464
465 /* ifsfree(); */
466
467 /*
468 * This routine is slightly over-complicated for
469 * efficiency. Next we scan backwards looking for the
470 * start of arithmetic.
471 */
472 start = stackblock();
473 p = expdest;
474 pushstackmark(&sm, p - start);
475 *--p = '\0';
476 p--;
477 do {
478 int esc;
479
480 while (*p != (char)CTLARI) {
481 p--;
482 #ifdef DEBUG
483 if (p < start) {
484 sh_error("missing CTLARI (shouldn't happen)");
485 }
486 #endif
487 }
488
489 esc = esclen(start, p);
490 if (!(esc % 2)) {
491 break;
492 }
493
494 p -= esc + 1;
495 } while (1);
496
497 begoff = p - start;
498
499 removerecordregions(begoff);
500
501 expdest = p;
502
503 if (likely(flag & QUOTES_ESC))
504 rmescapes(p + 1);
505
506 result = arith(p + 1);
507 popstackmark(&sm);
508
509 len = cvtnum(result);
510
511 if (likely(!(flag & EXP_QUOTED)))
512 recordregion(begoff, begoff + len, 0);
513 }
514
515
516 /*
517 * Expand stuff in backwards quotes.
518 */
519
520 STATIC void
521 expbackq(union node *cmd, int flag)
522 {
523 struct backcmd in;
524 int i;
525 char buf[128];
526 char *p;
527 char *dest;
528 int startloc;
529 char const *syntax = flag & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
530 struct stackmark smark;
531
532 INTOFF;
533 startloc = expdest - (char *)stackblock();
534 pushstackmark(&smark, startloc);
535 evalbackcmd(cmd, (struct backcmd *) &in);
536 popstackmark(&smark);
537
538 p = in.buf;
539 i = in.nleft;
540 if (i == 0)
541 goto read;
542 for (;;) {
543 memtodest(p, i, syntax, flag & QUOTES_ESC);
544 read:
545 if (in.fd < 0)
546 break;
547 do {
548 i = read(in.fd, buf, sizeof buf);
549 } while (i < 0 && errno == EINTR);
550 TRACE(("expbackq: read returns %d\n", i));
551 if (i <= 0)
552 break;
553 p = buf;
554 }
555
556 if (in.buf)
557 ckfree(in.buf);
558 if (in.fd >= 0) {
559 close(in.fd);
560 back_exitstatus = waitforjob(in.jp);
561 }
562 INTON;
563
564 /* Eat all trailing newlines */
565 dest = expdest;
566 for (; dest > (char *)stackblock() && dest[-1] == '\n';)
567 STUNPUTC(dest);
568 expdest = dest;
569
570 if (!(flag & EXP_QUOTED))
571 recordregion(startloc, dest - (char *)stackblock(), 0);
572 TRACE(("evalbackq: size=%d: \"%.*s\"\n",
573 (dest - (char *)stackblock()) - startloc,
574 (dest - (char *)stackblock()) - startloc,
575 stackblock() + startloc));
576 }
577
578
579 STATIC char *
580 scanleft(
581 char *startp, char *rmesc, char *rmescend, char *str, int quotes,
582 int zero
583 ) {
584 char *loc;
585 char *loc2;
586 char c;
587
588 loc = startp;
589 loc2 = rmesc;
590 do {
591 int match;
592 const char *s = loc2;
593 c = *loc2;
594 if (zero) {
595 *loc2 = '\0';
596 s = rmesc;
597 }
598 match = pmatch(str, s);
599 *loc2 = c;
600 if (match)
601 return loc;
602 if (quotes && *loc == (char)CTLESC)
603 loc++;
604 loc++;
605 loc2++;
606 } while (c);
607 return 0;
608 }
609
610
611 STATIC char *
612 scanright(
613 char *startp, char *rmesc, char *rmescend, char *str, int quotes,
614 int zero
615 ) {
616 int esc = 0;
617 char *loc;
618 char *loc2;
619
620 for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
621 int match;
622 char c = *loc2;
623 const char *s = loc2;
624 if (zero) {
625 *loc2 = '\0';
626 s = rmesc;
627 }
628 match = pmatch(str, s);
629 *loc2 = c;
630 if (match)
631 return loc;
632 loc--;
633 if (quotes) {
634 if (--esc < 0) {
635 esc = esclen(startp, loc);
636 }
637 if (esc % 2) {
638 esc--;
639 loc--;
640 }
641 }
642 }
643 return 0;
644 }
645
646 STATIC const char *
647 subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int flag)
648 {
649 int quotes = flag & QUOTES_ESC;
650 char *startp;
651 char *loc;
652 struct nodelist *saveargbackq = argbackq;
653 int amount;
654 char *rmesc, *rmescend;
655 int zero;
656 char *(*scan)(char *, char *, char *, char *, int , int);
657
658 argstr(p, EXP_TILDE | (subtype != VSASSIGN && subtype != VSQUESTION ?
659 (flag & EXP_QUOTED ? EXP_QPAT : EXP_CASE) : 0));
660 STPUTC('\0', expdest);
661 argbackq = saveargbackq;
662 startp = stackblock() + startloc;
663
664 switch (subtype) {
665 case VSASSIGN:
666 setvar(str, startp, 0);
667 amount = startp - expdest;
668 STADJUST(amount, expdest);
669 return startp;
670
671 case VSQUESTION:
672 varunset(p, str, startp, varflags);
673 /* NOTREACHED */
674 }
675
676 subtype -= VSTRIMRIGHT;
677 #ifdef DEBUG
678 if (subtype < 0 || subtype > 3)
679 abort();
680 #endif
681
682 rmesc = startp;
683 rmescend = stackblock() + strloc;
684 if (quotes) {
685 rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
686 if (rmesc != startp) {
687 rmescend = expdest;
688 startp = stackblock() + startloc;
689 }
690 }
691 rmescend--;
692 str = stackblock() + strloc;
693 preglob(str, 0);
694
695 /* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
696 zero = subtype >> 1;
697 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
698 scan = (subtype & 1) ^ zero ? scanleft : scanright;
699
700 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
701 if (loc) {
702 if (zero) {
703 memmove(startp, loc, str - loc);
704 loc = startp + (str - loc) - 1;
705 }
706 *loc = '\0';
707 amount = loc - expdest;
708 STADJUST(amount, expdest);
709 }
710 return loc;
711 }
712
713
714 /*
715 * Expand a variable, and return a pointer to the next character in the
716 * input string.
717 */
718 STATIC char *
719 evalvar(char *p, int flag)
720 {
721 int subtype;
722 int varflags;
723 char *var;
724 int patloc;
725 int c;
726 int startloc;
727 ssize_t varlen;
728 int easy;
729 int quoted;
730
731 varflags = *p++;
732 subtype = varflags & VSTYPE;
733
734 if (!subtype)
735 sh_error("Bad substitution");
736
737 quoted = flag & EXP_QUOTED;
738 var = p;
739 easy = (!quoted || (*var == '@' && shellparam.nparam));
740 startloc = expdest - (char *)stackblock();
741 p = strchr(p, '=') + 1;
742
743 again:
744 varlen = varvalue(var, varflags, flag);
745 if (varflags & VSNUL)
746 varlen--;
747
748 if (subtype == VSPLUS) {
749 varlen = -1 - varlen;
750 goto vsplus;
751 }
752
753 if (subtype == VSMINUS) {
754 vsplus:
755 if (varlen < 0) {
756 argstr(p, flag | EXP_TILDE | EXP_WORD);
757 goto end;
758 }
759 if (easy)
760 goto record;
761 goto end;
762 }
763
764 if (subtype == VSASSIGN || subtype == VSQUESTION) {
765 if (varlen < 0) {
766 if (subevalvar(p, var, 0, subtype, startloc,
767 varflags, flag & ~QUOTES_ESC)) {
768 varflags &= ~VSNUL;
769 /*
770 * Remove any recorded regions beyond
771 * start of variable
772 */
773 removerecordregions(startloc);
774 goto again;
775 }
776 goto end;
777 }
778 if (easy)
779 goto record;
780 goto end;
781 }
782
783 if (varlen < 0 && uflag)
784 varunset(p, var, 0, 0);
785
786 if (subtype == VSLENGTH) {
787 cvtnum(varlen > 0 ? varlen : 0);
788 goto record;
789 }
790
791 if (subtype == VSNORMAL) {
792 if (!easy)
793 goto end;
794 record:
795 recordregion(startloc, expdest - (char *)stackblock(), quoted);
796 goto end;
797 }
798
799 #ifdef DEBUG
800 switch (subtype) {
801 case VSTRIMLEFT:
802 case VSTRIMLEFTMAX:
803 case VSTRIMRIGHT:
804 case VSTRIMRIGHTMAX:
805 break;
806 default:
807 abort();
808 }
809 #endif
810
811 if (varlen >= 0) {
812 /*
813 * Terminate the string and start recording the pattern
814 * right after it
815 */
816 STPUTC('\0', expdest);
817 patloc = expdest - (char *)stackblock();
818 if (subevalvar(p, NULL, patloc, subtype,
819 startloc, varflags, flag) == 0) {
820 int amount = expdest - (
821 (char *)stackblock() + patloc - 1
822 );
823 STADJUST(-amount, expdest);
824 }
825 /* Remove any recorded regions beyond start of variable */
826 removerecordregions(startloc);
827 goto record;
828 }
829
830 end:
831 if (subtype != VSNORMAL) { /* skip to end of alternative */
832 int nesting = 1;
833 for (;;) {
834 if ((c = (signed char)*p++) == CTLESC)
835 p++;
836 else if (c == CTLBACKQ) {
837 if (varlen >= 0)
838 argbackq = argbackq->next;
839 } else if (c == CTLVAR) {
840 if ((*p++ & VSTYPE) != VSNORMAL)
841 nesting++;
842 } else if (c == CTLENDVAR) {
843 if (--nesting == 0)
844 break;
845 }
846 }
847 }
848 return p;
849 }
850
851
852 /*
853 * Put a string on the stack.
854 */
855
856 STATIC void
857 memtodest(const char *p, size_t len, const char *syntax, int quotes) {
858 char *q;
859
860 if (unlikely(!len))
861 return;
862
863 q = makestrspace(len * 2, expdest);
864
865 do {
866 int c = (signed char)*p++;
867 if (c) {
868 if ((quotes & QUOTES_ESC) &&
869 ((syntax[c] == CCTL) ||
870 (((quotes & EXP_FULL) || syntax != BASESYNTAX) &&
871 syntax[c] == CBACK)))
872 USTPUTC(CTLESC, q);
873 } else if (!(quotes & QUOTES_KEEPNUL))
874 continue;
875 USTPUTC(c, q);
876 } while (--len);
877
878 expdest = q;
879 }
880
881
882 STATIC size_t
883 strtodest(p, syntax, quotes)
884 const char *p;
885 const char *syntax;
886 int quotes;
887 {
888 size_t len = strlen(p);
889 memtodest(p, len, syntax, quotes);
890 return len;
891 }
892
893
894
895 /*
896 * Add the value of a specialized variable to the stack string.
897 */
898
899 STATIC ssize_t
900 varvalue(char *name, int varflags, int flags)
901 {
902 int num;
903 char *p;
904 int i;
905 int sep;
906 char sepc;
907 char **ap;
908 char const *syntax;
909 int quoted = flags & EXP_QUOTED;
910 int subtype = varflags & VSTYPE;
911 int discard = subtype == VSPLUS || subtype == VSLENGTH;
912 int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL;
913 ssize_t len = 0;
914
915 sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0;
916 syntax = quoted ? DQSYNTAX : BASESYNTAX;
917
918 switch (*name) {
919 case '$':
920 num = rootpid;
921 goto numvar;
922 case '?':
923 num = exitstatus;
924 goto numvar;
925 case '#':
926 num = shellparam.nparam;
927 goto numvar;
928 case '!':
929 num = backgndpid;
930 if (num == 0)
931 return -1;
932 numvar:
933 len = cvtnum(num);
934 break;
935 case '-':
936 p = makestrspace(NOPTS, expdest);
937 for (i = NOPTS - 1; i >= 0; i--) {
938 if (optlist[i]) {
939 USTPUTC(optletters[i], p);
940 len++;
941 }
942 }
943 expdest = p;
944 break;
945 case '@':
946 if (sep)
947 goto param;
948 /* fall through */
949 case '*':
950 sep = ifsset() ? ifsval()[0] : ' ';
951 param:
952 if (!(ap = shellparam.p))
953 return -1;
954 sepc = sep;
955 while ((p = *ap++)) {
956 len += strtodest(p, syntax, quotes);
957
958 if (*ap && sep) {
959 len++;
960 memtodest(&sepc, 1, syntax, quotes);
961 }
962 }
963 break;
964 case '0':
965 case '1':
966 case '2':
967 case '3':
968 case '4':
969 case '5':
970 case '6':
971 case '7':
972 case '8':
973 case '9':
974 num = atoi(name);
975 if (num < 0 || num > shellparam.nparam)
976 return -1;
977 p = num ? shellparam.p[num - 1] : arg0;
978 goto value;
979 default:
980 p = lookupvar(name);
981 value:
982 if (!p)
983 return -1;
984
985 len = strtodest(p, syntax, quotes);
986 break;
987 }
988
989 if (discard)
990 STADJUST(-len, expdest);
991 return len;
992 }
993
994
995
996 /*
997 * Record the fact that we have to scan this region of the
998 * string for IFS characters.
999 */
1000
1001 void
1002 recordregion(int start, int end, int nulonly)
1003 {
1004 struct ifsregion *ifsp;
1005
1006 if (ifslastp == NULL) {
1007 ifsp = &ifsfirst;
1008 } else {
1009 INTOFF;
1010 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
1011 ifsp->next = NULL;
1012 ifslastp->next = ifsp;
1013 INTON;
1014 }
1015 ifslastp = ifsp;
1016 ifslastp->begoff = start;
1017 ifslastp->endoff = end;
1018 ifslastp->nulonly = nulonly;
1019 }
1020
1021
1022
1023 /*
1024 * Break the argument string into pieces based upon IFS and add the
1025 * strings to the argument list. The regions of the string to be
1026 * searched for IFS characters have been stored by recordregion.
1027 */
1028 void
1029 ifsbreakup(char *string, struct arglist *arglist)
1030 {
1031 struct ifsregion *ifsp;
1032 struct strlist *sp;
1033 char *start;
1034 char *p;
1035 char *q;
1036 const char *ifs, *realifs;
1037 int ifsspc;
1038 int nulonly;
1039
1040
1041 start = string;
1042 if (ifslastp != NULL) {
1043 ifsspc = 0;
1044 nulonly = 0;
1045 realifs = ifsset() ? ifsval() : defifs;
1046 ifsp = &ifsfirst;
1047 do {
1048 p = string + ifsp->begoff;
1049 nulonly = ifsp->nulonly;
1050 ifs = nulonly ? nullstr : realifs;
1051 ifsspc = 0;
1052 while (p < string + ifsp->endoff) {
1053 q = p;
1054 if (*p == (char)CTLESC)
1055 p++;
1056 if (strchr(ifs, *p)) {
1057 if (!nulonly)
1058 ifsspc = (strchr(defifs, *p) != NULL);
1059 /* Ignore IFS whitespace at start */
1060 if (q == start && ifsspc) {
1061 p++;
1062 start = p;
1063 continue;
1064 }
1065 *q = '\0';
1066 sp = (struct strlist *)stalloc(sizeof *sp);
1067 sp->text = start;
1068 *arglist->lastp = sp;
1069 arglist->lastp = &sp->next;
1070 p++;
1071 if (!nulonly) {
1072 for (;;) {
1073 if (p >= string + ifsp->endoff) {
1074 break;
1075 }
1076 q = p;
1077 if (*p == (char)CTLESC)
1078 p++;
1079 if (strchr(ifs, *p) == NULL ) {
1080 p = q;
1081 break;
1082 } else if (strchr(defifs, *p) == NULL) {
1083 if (ifsspc) {
1084 p++;
1085 ifsspc = 0;
1086 } else {
1087 p = q;
1088 break;
1089 }
1090 } else
1091 p++;
1092 }
1093 }
1094 start = p;
1095 } else
1096 p++;
1097 }
1098 } while ((ifsp = ifsp->next) != NULL);
1099 if (nulonly)
1100 goto add;
1101 }
1102
1103 if (!*start)
1104 return;
1105
1106 add:
1107 sp = (struct strlist *)stalloc(sizeof *sp);
1108 sp->text = start;
1109 *arglist->lastp = sp;
1110 arglist->lastp = &sp->next;
1111 }
1112
1113 STATIC void
1114 ifsfree(void)
1115 {
1116 struct ifsregion *p;
1117
1118 INTOFF;
1119 p = ifsfirst.next;
1120 do {
1121 struct ifsregion *ifsp;
1122 ifsp = p->next;
1123 ckfree(p);
1124 p = ifsp;
1125 } while (p);
1126 ifslastp = NULL;
1127 ifsfirst.next = NULL;
1128 INTON;
1129 }
1130
1131
1132
1133 /*
1134 * Expand shell metacharacters. At this point, the only control characters
1135 * should be escapes. The results are stored in the list exparg.
1136 */
1137
1138 #ifdef HAVE_GLOB
1139 STATIC void
1140 expandmeta(str, flag)
1141 struct strlist *str;
1142 int flag;
1143 {
1144 /* TODO - EXP_REDIR */
1145
1146 while (str) {
1147 const char *p;
1148 glob_t pglob;
1149 int i;
1150
1151 if (fflag)
1152 goto nometa;
1153 INTOFF;
1154 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
1155 i = glob(p, GLOB_NOMAGIC, 0, &pglob);
1156 if (p != str->text)
1157 ckfree(p);
1158 switch (i) {
1159 case 0:
1160 if (!(pglob.gl_flags & GLOB_MAGCHAR))
1161 goto nometa2;
1162 addglob(&pglob);
1163 globfree(&pglob);
1164 INTON;
1165 break;
1166 case GLOB_NOMATCH:
1167 nometa2:
1168 globfree(&pglob);
1169 INTON;
1170 nometa:
1171 *exparg.lastp = str;
1172 rmescapes(str->text);
1173 exparg.lastp = &str->next;
1174 break;
1175 default: /* GLOB_NOSPACE */
1176 sh_error("Out of space");
1177 }
1178 str = str->next;
1179 }
1180 }
1181
1182
1183 /*
1184 * Add the result of glob(3) to the list.
1185 */
1186
1187 STATIC void
1188 addglob(pglob)
1189 const glob_t *pglob;
1190 {
1191 char **p = pglob->gl_pathv;
1192
1193 do {
1194 addfname(*p);
1195 } while (*++p);
1196 }
1197
1198
1199 #else /* HAVE_GLOB */
1200 STATIC char *expdir;
1201
1202
1203 STATIC void
1204 expandmeta(struct strlist *str, int flag)
1205 {
1206 static const char metachars[] = {
1207 '*', '?', '[', 0
1208 };
1209 /* TODO - EXP_REDIR */
1210
1211 while (str) {
1212 struct strlist **savelastp;
1213 struct strlist *sp;
1214 char *p;
1215
1216 if (fflag)
1217 goto nometa;
1218 if (!strpbrk(str->text, metachars))
1219 goto nometa;
1220 savelastp = exparg.lastp;
1221
1222 INTOFF;
1223 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
1224 {
1225 int i = strlen(str->text);
1226 expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
1227 }
1228
1229 expmeta(expdir, p);
1230 ckfree(expdir);
1231 if (p != str->text)
1232 ckfree(p);
1233 INTON;
1234 if (exparg.lastp == savelastp) {
1235 /*
1236 * no matches
1237 */
1238 nometa:
1239 *exparg.lastp = str;
1240 rmescapes(str->text);
1241 exparg.lastp = &str->next;
1242 } else {
1243 *exparg.lastp = NULL;
1244 *savelastp = sp = expsort(*savelastp);
1245 while (sp->next != NULL)
1246 sp = sp->next;
1247 exparg.lastp = &sp->next;
1248 }
1249 str = str->next;
1250 }
1251 }
1252
1253
1254 /*
1255 * Do metacharacter (i.e. *, ?, [...]) expansion.
1256 */
1257
1258 STATIC void
1259 expmeta(char *enddir, char *name)
1260 {
1261 char *p;
1262 const char *cp;
1263 char *start;
1264 char *endname;
1265 int metaflag;
1266 struct stat64 statb;
1267 DIR *dirp;
1268 struct dirent *dp;
1269 int atend;
1270 int matchdot;
1271 int esc;
1272
1273 metaflag = 0;
1274 start = name;
1275 for (p = name; esc = 0, *p; p += esc + 1) {
1276 if (*p == '*' || *p == '?')
1277 metaflag = 1;
1278 else if (*p == '[') {
1279 char *q = p + 1;
1280 if (*q == '!')
1281 q++;
1282 for (;;) {
1283 if (*q == '\\')
1284 q++;
1285 if (*q == '/' || *q == '\0')
1286 break;
1287 if (*++q == ']') {
1288 metaflag = 1;
1289 break;
1290 }
1291 }
1292 } else {
1293 if (*p == '\\')
1294 esc++;
1295 if (p[esc] == '/') {
1296 if (metaflag)
1297 break;
1298 start = p + esc + 1;
1299 }
1300 }
1301 }
1302 if (metaflag == 0) { /* we've reached the end of the file name */
1303 if (enddir != expdir)
1304 metaflag++;
1305 p = name;
1306 do {
1307 if (*p == '\\')
1308 p++;
1309 *enddir++ = *p;
1310 } while (*p++);
1311 if (metaflag == 0 || lstat64(expdir, &statb) >= 0)
1312 addfname(expdir);
1313 return;
1314 }
1315 endname = p;
1316 if (name < start) {
1317 p = name;
1318 do {
1319 if (*p == '\\')
1320 p++;
1321 *enddir++ = *p++;
1322 } while (p < start);
1323 }
1324 if (enddir == expdir) {
1325 cp = ".";
1326 } else if (enddir == expdir + 1 && *expdir == '/') {
1327 cp = "/";
1328 } else {
1329 cp = expdir;
1330 enddir[-1] = '\0';
1331 }
1332 if ((dirp = opendir(cp)) == NULL)
1333 return;
1334 if (enddir != expdir)
1335 enddir[-1] = '/';
1336 if (*endname == 0) {
1337 atend = 1;
1338 } else {
1339 atend = 0;
1340 *endname = '\0';
1341 endname += esc + 1;
1342 }
1343 matchdot = 0;
1344 p = start;
1345 if (*p == '\\')
1346 p++;
1347 if (*p == '.')
1348 matchdot++;
1349 while (! int_pending() && (dp = readdir(dirp)) != NULL) {
1350 if (dp->d_name[0] == '.' && ! matchdot)
1351 continue;
1352 if (pmatch(start, dp->d_name)) {
1353 if (atend) {
1354 scopy(dp->d_name, enddir);
1355 addfname(expdir);
1356 } else {
1357 for (p = enddir, cp = dp->d_name;
1358 (*p++ = *cp++) != '\0';)
1359 continue;
1360 p[-1] = '/';
1361 expmeta(p, endname);
1362 }
1363 }
1364 }
1365 closedir(dirp);
1366 if (! atend)
1367 endname[-esc - 1] = esc ? '\\' : '/';
1368 }
1369 #endif /* HAVE_GLOB */
1370
1371
1372 /*
1373 * Add a file name to the list.
1374 */
1375
1376 STATIC void
1377 addfname(char *name)
1378 {
1379 struct strlist *sp;
1380
1381 sp = (struct strlist *)stalloc(sizeof *sp);
1382 sp->text = sstrdup(name);
1383 *exparg.lastp = sp;
1384 exparg.lastp = &sp->next;
1385 }
1386
1387
1388 #ifndef HAVE_GLOB
1389 /*
1390 * Sort the results of file name expansion. It calculates the number of
1391 * strings to sort and then calls msort (short for merge sort) to do the
1392 * work.
1393 */
1394
1395 STATIC struct strlist *
1396 expsort(struct strlist *str)
1397 {
1398 int len;
1399 struct strlist *sp;
1400
1401 len = 0;
1402 for (sp = str ; sp ; sp = sp->next)
1403 len++;
1404 return msort(str, len);
1405 }
1406
1407
1408 STATIC struct strlist *
1409 msort(struct strlist *list, int len)
1410 {
1411 struct strlist *p, *q = NULL;
1412 struct strlist **lpp;
1413 int half;
1414 int n;
1415
1416 if (len <= 1)
1417 return list;
1418 half = len >> 1;
1419 p = list;
1420 for (n = half ; --n >= 0 ; ) {
1421 q = p;
1422 p = p->next;
1423 }
1424 q->next = NULL; /* terminate first half of list */
1425 q = msort(list, half); /* sort first half of list */
1426 p = msort(p, len - half); /* sort second half */
1427 lpp = &list;
1428 for (;;) {
1429 if (strcmp(p->text, q->text) < 0) {
1430 *lpp = p;
1431 lpp = &p->next;
1432 if ((p = *lpp) == NULL) {
1433 *lpp = q;
1434 break;
1435 }
1436 } else {
1437 *lpp = q;
1438 lpp = &q->next;
1439 if ((q = *lpp) == NULL) {
1440 *lpp = p;
1441 break;
1442 }
1443 }
1444 }
1445 return list;
1446 }
1447 #endif
1448
1449
1450 /*
1451 * Returns true if the pattern matches the string.
1452 */
1453
1454 STATIC inline int
1455 patmatch(char *pattern, const char *string)
1456 {
1457 return pmatch(preglob(pattern, 0), string);
1458 }
1459
1460
1461 #ifndef HAVE_FNMATCH
1462 STATIC int ccmatch(const char *p, int chr, const char **r)
1463 {
1464 static const struct class {
1465 char name[10];
1466 int (*fn)(int);
1467 } classes[] = {
1468 { .name = ":alnum:]", .fn = isalnum },
1469 { .name = ":cntrl:]", .fn = iscntrl },
1470 { .name = ":lower:]", .fn = islower },
1471 { .name = ":space:]", .fn = isspace },
1472 { .name = ":alpha:]", .fn = isalpha },
1473 { .name = ":digit:]", .fn = isdigit },
1474 { .name = ":print:]", .fn = isprint },
1475 { .name = ":upper:]", .fn = isupper },
1476 { .name = ":blank:]", .fn = isblank },
1477 { .name = ":graph:]", .fn = isgraph },
1478 { .name = ":punct:]", .fn = ispunct },
1479 { .name = ":xdigit:]", .fn = isxdigit },
1480 };
1481 const struct class *class, *end;
1482
1483 end = classes + sizeof(classes) / sizeof(classes[0]);
1484 for (class = classes; class < end; class++) {
1485 const char *q;
1486
1487 q = prefix(p, class->name);
1488 if (!q)
1489 continue;
1490 *r = q;
1491 return class->fn(chr);
1492 }
1493
1494 *r = 0;
1495 return 0;
1496 }
1497
1498 STATIC int
1499 pmatch(const char *pattern, const char *string)
1500 {
1501 const char *p, *q;
1502 char c;
1503
1504 p = pattern;
1505 q = string;
1506 for (;;) {
1507 switch (c = *p++) {
1508 case '\0':
1509 goto breakloop;
1510 case '\\':
1511 if (*p) {
1512 c = *p++;
1513 }
1514 goto dft;
1515 case '?':
1516 if (*q++ == '\0')
1517 return 0;
1518 break;
1519 case '*':
1520 c = *p;
1521 while (c == '*')
1522 c = *++p;
1523 if (c != '\\' && c != '?' && c != '*' && c != '[') {
1524 while (*q != c) {
1525 if (*q == '\0')
1526 return 0;
1527 q++;
1528 }
1529 }
1530 do {
1531 if (pmatch(p, q))
1532 return 1;
1533 } while (*q++ != '\0');
1534 return 0;
1535 case '[': {
1536 const char *startp;
1537 int invert, found;
1538 char chr;
1539
1540 startp = p;
1541 invert = 0;
1542 if (*p == '!') {
1543 invert++;
1544 p++;
1545 }
1546 found = 0;
1547 chr = *q++;
1548 if (chr == '\0')
1549 return 0;
1550 c = *p++;
1551 do {
1552 if (!c) {
1553 p = startp;
1554 c = *p;
1555 goto dft;
1556 }
1557 if (c == '[') {
1558 const char *r;
1559
1560 found |= !!ccmatch(p, chr, &r);
1561 if (r) {
1562 p = r;
1563 continue;
1564 }
1565 } else if (c == '\\')
1566 c = *p++;
1567 if (*p == '-' && p[1] != ']') {
1568 p++;
1569 if (*p == '\\')
1570 p++;
1571 if (chr >= c && chr <= *p)
1572 found = 1;
1573 p++;
1574 } else {
1575 if (chr == c)
1576 found = 1;
1577 }
1578 } while ((c = *p++) != ']');
1579 if (found == invert)
1580 return 0;
1581 break;
1582 }
1583 dft: default:
1584 if (*q++ != c)
1585 return 0;
1586 break;
1587 }
1588 }
1589 breakloop:
1590 if (*q != '\0')
1591 return 0;
1592 return 1;
1593 }
1594 #endif
1595
1596
1597
1598 /*
1599 * Remove any CTLESC characters from a string.
1600 */
1601
1602 char *
1603 _rmescapes(char *str, int flag)
1604 {
1605 char *p, *q, *r;
1606 static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
1607 unsigned inquotes;
1608 int notescaped;
1609 int globbing;
1610
1611 p = strpbrk(str, qchars);
1612 if (!p) {
1613 return str;
1614 }
1615 q = p;
1616 r = str;
1617 if (flag & RMESCAPE_ALLOC) {
1618 size_t len = p - str;
1619 size_t fulllen = len + strlen(p) + 1;
1620
1621 if (flag & RMESCAPE_GROW) {
1622 int strloc = str - (char *)stackblock();
1623
1624 r = makestrspace(fulllen, expdest);
1625 str = (char *)stackblock() + strloc;
1626 p = str + len;
1627 } else if (flag & RMESCAPE_HEAP) {
1628 r = ckmalloc(fulllen);
1629 } else {
1630 r = stalloc(fulllen);
1631 }
1632 q = r;
1633 if (len > 0) {
1634 q = mempcpy(q, str, len);
1635 }
1636 }
1637 inquotes = 0;
1638 globbing = flag & RMESCAPE_GLOB;
1639 notescaped = globbing;
1640 while (*p) {
1641 if (*p == (char)CTLQUOTEMARK) {
1642 inquotes = ~inquotes;
1643 p++;
1644 notescaped = globbing;
1645 continue;
1646 }
1647 if (*p == (char)CTLESC) {
1648 p++;
1649 if (notescaped)
1650 *q++ = '\\';
1651 } else if (*p == '\\' && !inquotes) {
1652 /* naked back slash */
1653 notescaped = 0;
1654 goto copy;
1655 }
1656 notescaped = globbing;
1657 copy:
1658 *q++ = *p++;
1659 }
1660 *q = '\0';
1661 if (flag & RMESCAPE_GROW) {
1662 expdest = r;
1663 STADJUST(q - r + 1, expdest);
1664 }
1665 return r;
1666 }
1667
1668
1669
1670 /*
1671 * See if a pattern matches in a case statement.
1672 */
1673
1674 int
1675 casematch(union node *pattern, char *val)
1676 {
1677 struct stackmark smark;
1678 int result;
1679
1680 setstackmark(&smark);
1681 argbackq = pattern->narg.backquote;
1682 STARTSTACKSTR(expdest);
1683 ifslastp = NULL;
1684 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
1685 STACKSTRNUL(expdest);
1686 result = patmatch(stackblock(), val);
1687 popstackmark(&smark);
1688 return result;
1689 }
1690
1691 /*
1692 * Our own itoa().
1693 */
1694
1695 STATIC int
1696 cvtnum(intmax_t num)
1697 {
1698 int len = max_int_length(sizeof(num));
1699
1700 expdest = makestrspace(len, expdest);
1701 len = fmtstr(expdest, len, "%jd", num);
1702 STADJUST(len, expdest);
1703 return len;
1704 }
1705
1706 STATIC void
1707 varunset(const char *end, const char *var, const char *umsg, int varflags)
1708 {
1709 const char *msg;
1710 const char *tail;
1711
1712 tail = nullstr;
1713 msg = "parameter not set";
1714 if (umsg) {
1715 if (*end == (char)CTLENDVAR) {
1716 if (varflags & VSNUL)
1717 tail = " or null";
1718 } else
1719 msg = umsg;
1720 }
1721 sh_error("%.*s: %s%s", end - var - 1, var, msg, tail);
1722 }