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