source: mainline/uspace/app/ash/expand.c@ 8ccd2ea

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8ccd2ea was c28a023, checked in by Josef Cejka <malyzelenyhnus@…>, 17 years ago

Initial commit of ash shell.
It cannot be compiled yet.

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