VirtualBox

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

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

more fixes - it all compiles now (linking fails of course).

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