VirtualBox

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

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

keywords.

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

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