sh: Add UTF-8 support to ${#var}.
[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
fd296645 39 * $FreeBSD: src/bin/sh/expand.c,v 1.84 2011/05/07 14:32:16 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;
fd296645 667 int varlenb;
984263bc
MD
668 int easy;
669 int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR);
670
af260b21 671 varflags = (unsigned char)*p++;
984263bc
MD
672 subtype = varflags & VSTYPE;
673 var = p;
674 special = 0;
675 if (! is_name(*p))
676 special = 1;
677 p = strchr(p, '=') + 1;
678again: /* jump here after setting a variable with ${var=text} */
e0752c76
SK
679 if (varflags & VSLINENO) {
680 set = 1;
681 special = 0;
682 val = var;
683 p[-1] = '\0'; /* temporarily overwrite '=' to have \0
684 terminated string */
685 } else if (special) {
984263bc
MD
686 set = varisset(var, varflags & VSNUL);
687 val = NULL;
688 } else {
689 val = bltinlookup(var, 1);
690 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
691 val = NULL;
692 set = 0;
693 } else
694 set = 1;
695 }
696 varlen = 0;
697 startloc = expdest - stackblock();
99512ac4 698 if (!set && uflag && *var != '@' && *var != '*') {
984263bc
MD
699 switch (subtype) {
700 case VSNORMAL:
701 case VSTRIMLEFT:
702 case VSTRIMLEFTMAX:
703 case VSTRIMRIGHT:
704 case VSTRIMRIGHTMAX:
705 case VSLENGTH:
af260b21
PA
706 error("%.*s: parameter not set", (int)(p - var - 1),
707 var);
984263bc
MD
708 }
709 }
710 if (set && subtype != VSPLUS) {
711 /* insert the value of the variable */
712 if (special) {
af260b21 713 varvalue(var, varflags & VSQUOTE, subtype, flag);
984263bc 714 if (subtype == VSLENGTH) {
fd296645
PA
715 varlenb = expdest - stackblock() - startloc;
716 varlen = varlenb;
717 if (localeisutf8) {
718 val = stackblock() + startloc;
719 for (;val != expdest; val++)
720 if ((*val & 0xC0) == 0x80)
721 varlen--;
722 }
723 STADJUST(-varlenb, expdest);
984263bc
MD
724 }
725 } else {
726 char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX
727 : BASESYNTAX;
728
729 if (subtype == VSLENGTH) {
730 for (;*val; val++)
fd296645
PA
731 if (!localeisutf8 ||
732 (*val & 0xC0) != 0x80)
733 varlen++;
984263bc
MD
734 }
735 else {
99512ac4
PA
736 if (quotes)
737 STPUTS_QUOTES(val, syntax, expdest);
738 else
739 STPUTS(val, expdest);
984263bc
MD
740
741 }
742 }
743 }
744
745 if (subtype == VSPLUS)
746 set = ! set;
747
748 easy = ((varflags & VSQUOTE) == 0 ||
749 (*var == '@' && shellparam.nparam != 1));
750
751
752 switch (subtype) {
753 case VSLENGTH:
754 expdest = cvtnum(varlen, expdest);
755 goto record;
756
757 case VSNORMAL:
758 if (!easy)
759 break;
760record:
761 recordregion(startloc, expdest - stackblock(),
762 varflags & VSQUOTE);
763 break;
764
765 case VSPLUS:
766 case VSMINUS:
767 if (!set) {
99512ac4
PA
768 argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) |
769 (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0));
984263bc
MD
770 break;
771 }
772 if (easy)
773 goto record;
774 break;
775
776 case VSTRIMLEFT:
777 case VSTRIMLEFTMAX:
778 case VSTRIMRIGHT:
779 case VSTRIMRIGHTMAX:
780 if (!set)
781 break;
782 /*
783 * Terminate the string and start recording the pattern
784 * right after it
785 */
786 STPUTC('\0', expdest);
787 patloc = expdest - stackblock();
788 if (subevalvar(p, NULL, patloc, subtype,
99512ac4 789 startloc, varflags, quotes) == 0) {
984263bc
MD
790 int amount = (expdest - stackblock() - patloc) + 1;
791 STADJUST(-amount, expdest);
792 }
793 /* Remove any recorded regions beyond start of variable */
794 removerecordregions(startloc);
795 goto record;
796
797 case VSASSIGN:
798 case VSQUESTION:
799 if (!set) {
99512ac4
PA
800 if (subevalvar(p, var, 0, subtype, startloc, varflags,
801 quotes)) {
984263bc 802 varflags &= ~VSNUL;
af260b21
PA
803 /*
804 * Remove any recorded regions beyond
805 * start of variable
984263bc
MD
806 */
807 removerecordregions(startloc);
808 goto again;
809 }
810 break;
811 }
812 if (easy)
813 goto record;
814 break;
815
af260b21
PA
816 case VSERROR:
817 c = p - var - 1;
818 error("${%.*s%s}: Bad substitution", c, var,
819 (c > 0 && *p != CTLENDVAR) ? "..." : "");
820
984263bc
MD
821 default:
822 abort();
823 }
e0752c76 824 p[-1] = '='; /* recover overwritten '=' */
984263bc
MD
825
826 if (subtype != VSNORMAL) { /* skip to end of alternative */
827 int nesting = 1;
828 for (;;) {
829 if ((c = *p++) == CTLESC)
830 p++;
831 else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
832 if (set)
833 argbackq = argbackq->next;
834 } else if (c == CTLVAR) {
835 if ((*p++ & VSTYPE) != VSNORMAL)
836 nesting++;
837 } else if (c == CTLENDVAR) {
838 if (--nesting == 0)
839 break;
840 }
841 }
842 }
843 return p;
844}
845
846
847
848/*
849 * Test whether a specialized variable is set.
850 */
851
99512ac4 852static int
984263bc
MD
853varisset(char *name, int nulok)
854{
855
856 if (*name == '!')
99512ac4 857 return backgndpidset();
984263bc
MD
858 else if (*name == '@' || *name == '*') {
859 if (*shellparam.p == NULL)
860 return 0;
861
862 if (nulok) {
863 char **av;
864
865 for (av = shellparam.p; *av; av++)
866 if (**av != '\0')
867 return 1;
868 return 0;
869 }
870 } else if (is_digit(*name)) {
871 char *ap;
872 int num = atoi(name);
873
874 if (num > shellparam.nparam)
875 return 0;
876
877 if (num == 0)
878 ap = arg0;
879 else
880 ap = shellparam.p[num - 1];
881
882 if (nulok && (ap == NULL || *ap == '\0'))
883 return 0;
884 }
885 return 1;
886}
887
99512ac4
PA
888static void
889strtodest(const char *p, int flag, int subtype, int quoted)
890{
891 if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH)
892 STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
893 else
894 STPUTS(p, expdest);
895}
984263bc
MD
896
897/*
898 * Add the value of a specialized variable to the stack string.
899 */
900
99512ac4 901static void
af260b21 902varvalue(char *name, int quoted, int subtype, int flag)
984263bc
MD
903{
904 int num;
905 char *p;
906 int i;
984263bc
MD
907 char sep;
908 char **ap;
984263bc
MD
909
910 switch (*name) {
911 case '$':
912 num = rootpid;
913 goto numvar;
914 case '?':
915 num = oexitstatus;
916 goto numvar;
917 case '#':
918 num = shellparam.nparam;
919 goto numvar;
920 case '!':
99512ac4 921 num = backgndpidval();
984263bc
MD
922numvar:
923 expdest = cvtnum(num, expdest);
924 break;
925 case '-':
926 for (i = 0 ; i < NOPTS ; i++) {
927 if (optlist[i].val)
928 STPUTC(optlist[i].letter, expdest);
929 }
930 break;
931 case '@':
af260b21 932 if (flag & EXP_FULL && quoted) {
984263bc 933 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
99512ac4 934 strtodest(p, flag, subtype, quoted);
984263bc
MD
935 if (*ap)
936 STPUTC('\0', expdest);
937 }
938 break;
939 }
af260b21 940 /* FALLTHROUGH */
984263bc 941 case '*':
af260b21 942 if (ifsset())
984263bc
MD
943 sep = ifsval()[0];
944 else
945 sep = ' ';
946 for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
99512ac4 947 strtodest(p, flag, subtype, quoted);
984263bc
MD
948 if (*ap && sep)
949 STPUTC(sep, expdest);
950 }
951 break;
952 case '0':
953 p = arg0;
99512ac4 954 strtodest(p, flag, subtype, quoted);
984263bc
MD
955 break;
956 default:
957 if (is_digit(*name)) {
958 num = atoi(name);
959 if (num > 0 && num <= shellparam.nparam) {
960 p = shellparam.p[num - 1];
99512ac4 961 strtodest(p, flag, subtype, quoted);
984263bc
MD
962 }
963 }
964 break;
965 }
966}
967
968
969
970/*
404d2224 971 * Record the fact that we have to scan this region of the
984263bc
MD
972 * string for IFS characters.
973 */
974
99512ac4
PA
975static void
976recordregion(int start, int end, int inquotes)
984263bc
MD
977{
978 struct ifsregion *ifsp;
979
980 if (ifslastp == NULL) {
981 ifsp = &ifsfirst;
982 } else {
99512ac4
PA
983 if (ifslastp->endoff == start
984 && ifslastp->inquotes == inquotes) {
985 /* extend previous area */
986 ifslastp->endoff = end;
987 return;
988 }
984263bc
MD
989 ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
990 ifslastp->next = ifsp;
991 }
992 ifslastp = ifsp;
993 ifslastp->next = NULL;
994 ifslastp->begoff = start;
995 ifslastp->endoff = end;
99512ac4 996 ifslastp->inquotes = inquotes;
984263bc
MD
997}
998
999
1000
1001/*
1002 * Break the argument string into pieces based upon IFS and add the
1003 * strings to the argument list. The regions of the string to be
1004 * searched for IFS characters have been stored by recordregion.
99512ac4
PA
1005 * CTLESC characters are preserved but have little effect in this pass
1006 * other than escaping CTL* characters. In particular, they do not escape
1007 * IFS characters: that should be done with the ifsregion mechanism.
1008 * CTLQUOTEMARK characters are used to preserve empty quoted strings.
1009 * This pass treats them as a regular character, making the string non-empty.
1010 * Later, they are removed along with the other CTL* characters.
984263bc 1011 */
99512ac4 1012static void
984263bc
MD
1013ifsbreakup(char *string, struct arglist *arglist)
1014{
1015 struct ifsregion *ifsp;
1016 struct strlist *sp;
1017 char *start;
1018 char *p;
1019 char *q;
492efe05 1020 const char *ifs;
99512ac4
PA
1021 const char *ifsspc;
1022 int had_param_ch = 0;
984263bc
MD
1023
1024 start = string;
99512ac4
PA
1025
1026 if (ifslastp == NULL) {
1027 /* Return entire argument, IFS doesn't apply to any of it */
1028 sp = (struct strlist *)stalloc(sizeof *sp);
1029 sp->text = start;
1030 *arglist->lastp = sp;
1031 arglist->lastp = &sp->next;
1032 return;
1033 }
1034
1035 ifs = ifsset() ? ifsval() : " \t\n";
1036
1037 for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) {
1038 p = string + ifsp->begoff;
1039 while (p < string + ifsp->endoff) {
1040 q = p;
1041 if (*p == CTLESC)
1042 p++;
1043 if (ifsp->inquotes) {
1044 /* Only NULs (should be from "$@") end args */
1045 had_param_ch = 1;
1046 if (*p != 0) {
984263bc 1047 p++;
99512ac4
PA
1048 continue;
1049 }
1050 ifsspc = NULL;
1051 } else {
1052 if (!strchr(ifs, *p)) {
1053 had_param_ch = 1;
984263bc 1054 p++;
99512ac4
PA
1055 continue;
1056 }
1057 ifsspc = strchr(" \t\n", *p);
1058
1059 /* Ignore IFS whitespace at start */
1060 if (q == start && ifsspc != NULL) {
984263bc 1061 p++;
99512ac4
PA
1062 start = p;
1063 continue;
1064 }
1065 had_param_ch = 0;
984263bc 1066 }
99512ac4
PA
1067
1068 /* Save this argument... */
1069 *q = '\0';
984263bc
MD
1070 sp = (struct strlist *)stalloc(sizeof *sp);
1071 sp->text = start;
1072 *arglist->lastp = sp;
1073 arglist->lastp = &sp->next;
99512ac4
PA
1074 p++;
1075
1076 if (ifsspc != NULL) {
1077 /* Ignore further trailing IFS whitespace */
1078 for (; p < string + ifsp->endoff; p++) {
1079 q = p;
1080 if (*p == CTLESC)
1081 p++;
1082 if (strchr(ifs, *p) == NULL) {
1083 p = q;
1084 break;
1085 }
1086 if (strchr(" \t\n", *p) == NULL) {
1087 p++;
1088 break;
1089 }
1090 }
1091 }
1092 start = p;
984263bc 1093 }
99512ac4
PA
1094 }
1095
1096 /*
1097 * Save anything left as an argument.
1098 * Traditionally we have treated 'IFS=':'; set -- x$IFS' as
1099 * generating 2 arguments, the second of which is empty.
1100 * Some recent clarification of the Posix spec say that it
1101 * should only generate one....
1102 */
1103 if (had_param_ch || *start != 0) {
984263bc
MD
1104 sp = (struct strlist *)stalloc(sizeof *sp);
1105 sp->text = start;
1106 *arglist->lastp = sp;
1107 arglist->lastp = &sp->next;
1108 }
1109}
1110
1111
99512ac4
PA
1112static char expdir[PATH_MAX];
1113#define expdir_end (expdir + sizeof(expdir))
984263bc
MD
1114
1115/*
99512ac4
PA
1116 * Perform pathname generation and remove control characters.
1117 * At this point, the only control characters should be CTLESC and CTLQUOTEMARK.
1118 * The results are stored in the list exparg.
984263bc 1119 */
99512ac4 1120static void
984263bc
MD
1121expandmeta(struct strlist *str, int flag __unused)
1122{
1123 char *p;
1124 struct strlist **savelastp;
1125 struct strlist *sp;
1126 char c;
1127 /* TODO - EXP_REDIR */
1128
1129 while (str) {
1130 if (fflag)
1131 goto nometa;
1132 p = str->text;
1133 for (;;) { /* fast check for meta chars */
1134 if ((c = *p++) == '\0')
1135 goto nometa;
99512ac4 1136 if (c == '*' || c == '?' || c == '[')
984263bc
MD
1137 break;
1138 }
1139 savelastp = exparg.lastp;
1140 INTOFF;
984263bc 1141 expmeta(expdir, str->text);
984263bc
MD
1142 INTON;
1143 if (exparg.lastp == savelastp) {
1144 /*
1145 * no matches
1146 */
1147nometa:
1148 *exparg.lastp = str;
1149 rmescapes(str->text);
1150 exparg.lastp = &str->next;
1151 } else {
1152 *exparg.lastp = NULL;
1153 *savelastp = sp = expsort(*savelastp);
1154 while (sp->next != NULL)
1155 sp = sp->next;
1156 exparg.lastp = &sp->next;
1157 }
1158 str = str->next;
1159 }
1160}
1161
1162
1163/*
1164 * Do metacharacter (i.e. *, ?, [...]) expansion.
1165 */
1166
99512ac4 1167static void
984263bc
MD
1168expmeta(char *enddir, char *name)
1169{
1170 char *p;
99512ac4 1171 char *q;
984263bc
MD
1172 char *start;
1173 char *endname;
1174 int metaflag;
1175 struct stat statb;
1176 DIR *dirp;
1177 struct dirent *dp;
1178 int atend;
1179 int matchdot;
99512ac4 1180 int esc;
984263bc
MD
1181
1182 metaflag = 0;
1183 start = name;
99512ac4 1184 for (p = name; esc = 0, *p; p += esc + 1) {
984263bc
MD
1185 if (*p == '*' || *p == '?')
1186 metaflag = 1;
1187 else if (*p == '[') {
1188 q = p + 1;
1189 if (*q == '!' || *q == '^')
1190 q++;
1191 for (;;) {
1192 while (*q == CTLQUOTEMARK)
1193 q++;
1194 if (*q == CTLESC)
1195 q++;
1196 if (*q == '/' || *q == '\0')
1197 break;
1198 if (*++q == ']') {
1199 metaflag = 1;
1200 break;
1201 }
1202 }
984263bc
MD
1203 } else if (*p == '\0')
1204 break;
1205 else if (*p == CTLQUOTEMARK)
1206 continue;
99512ac4
PA
1207 else {
1208 if (*p == CTLESC)
1209 esc++;
1210 if (p[esc] == '/') {
1211 if (metaflag)
1212 break;
1213 start = p + esc + 1;
1214 }
984263bc
MD
1215 }
1216 }
1217 if (metaflag == 0) { /* we've reached the end of the file name */
1218 if (enddir != expdir)
1219 metaflag++;
1220 for (p = name ; ; p++) {
1221 if (*p == CTLQUOTEMARK)
1222 continue;
1223 if (*p == CTLESC)
1224 p++;
1225 *enddir++ = *p;
1226 if (*p == '\0')
1227 break;
99512ac4
PA
1228 if (enddir == expdir_end)
1229 return;
984263bc 1230 }
85ef70cf 1231 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
984263bc
MD
1232 addfname(expdir);
1233 return;
1234 }
1235 endname = p;
1236 if (start != name) {
1237 p = name;
1238 while (p < start) {
1239 while (*p == CTLQUOTEMARK)
1240 p++;
1241 if (*p == CTLESC)
1242 p++;
1243 *enddir++ = *p++;
99512ac4
PA
1244 if (enddir == expdir_end)
1245 return;
984263bc
MD
1246 }
1247 }
1248 if (enddir == expdir) {
99512ac4 1249 p = __DECONST(char *, ".");
984263bc 1250 } else if (enddir == expdir + 1 && *expdir == '/') {
99512ac4 1251 p = __DECONST(char *, "/");
984263bc 1252 } else {
99512ac4 1253 p = expdir;
984263bc
MD
1254 enddir[-1] = '\0';
1255 }
99512ac4 1256 if ((dirp = opendir(p)) == NULL)
984263bc
MD
1257 return;
1258 if (enddir != expdir)
1259 enddir[-1] = '/';
1260 if (*endname == 0) {
1261 atend = 1;
1262 } else {
1263 atend = 0;
99512ac4
PA
1264 *endname = '\0';
1265 endname += esc + 1;
984263bc
MD
1266 }
1267 matchdot = 0;
1268 p = start;
1269 while (*p == CTLQUOTEMARK)
1270 p++;
1271 if (*p == CTLESC)
1272 p++;
1273 if (*p == '.')
1274 matchdot++;
1275 while (! int_pending() && (dp = readdir(dirp)) != NULL) {
1276 if (dp->d_name[0] == '.' && ! matchdot)
1277 continue;
1278 if (patmatch(start, dp->d_name, 0)) {
99512ac4
PA
1279 if (enddir + dp->d_namlen + 1 > expdir_end)
1280 continue;
1281 memcpy(enddir, dp->d_name, dp->d_namlen + 1);
1282 if (atend)
984263bc 1283 addfname(expdir);
99512ac4
PA
1284 else {
1285 if (enddir + dp->d_namlen + 2 > expdir_end)
984263bc 1286 continue;
99512ac4
PA
1287 enddir[dp->d_namlen] = '/';
1288 enddir[dp->d_namlen + 1] = '\0';
1289 expmeta(enddir + dp->d_namlen + 1, endname);
984263bc
MD
1290 }
1291 }
1292 }
1293 closedir(dirp);
1294 if (! atend)
99512ac4 1295 endname[-esc - 1] = esc ? CTLESC : '/';
984263bc
MD
1296}
1297
1298
1299/*
1300 * Add a file name to the list.
1301 */
1302
99512ac4 1303static void
984263bc
MD
1304addfname(char *name)
1305{
1306 char *p;
1307 struct strlist *sp;
1308
1309 p = stalloc(strlen(name) + 1);
1310 scopy(name, p);
1311 sp = (struct strlist *)stalloc(sizeof *sp);
1312 sp->text = p;
1313 *exparg.lastp = sp;
1314 exparg.lastp = &sp->next;
1315}
1316
1317
1318/*
1319 * Sort the results of file name expansion. It calculates the number of
1320 * strings to sort and then calls msort (short for merge sort) to do the
1321 * work.
1322 */
1323
99512ac4 1324static struct strlist *
984263bc
MD
1325expsort(struct strlist *str)
1326{
1327 int len;
1328 struct strlist *sp;
1329
1330 len = 0;
1331 for (sp = str ; sp ; sp = sp->next)
1332 len++;
1333 return msort(str, len);
1334}
1335
1336
99512ac4 1337static struct strlist *
984263bc
MD
1338msort(struct strlist *list, int len)
1339{
1340 struct strlist *p, *q = NULL;
1341 struct strlist **lpp;
1342 int half;
1343 int n;
1344
1345 if (len <= 1)
1346 return list;
1347 half = len >> 1;
1348 p = list;
1349 for (n = half ; --n >= 0 ; ) {
1350 q = p;
1351 p = p->next;
1352 }
1353 q->next = NULL; /* terminate first half of list */
1354 q = msort(list, half); /* sort first half of list */
1355 p = msort(p, len - half); /* sort second half */
1356 lpp = &list;
1357 for (;;) {
1358 if (strcmp(p->text, q->text) < 0) {
1359 *lpp = p;
1360 lpp = &p->next;
1361 if ((p = *lpp) == NULL) {
1362 *lpp = q;
1363 break;
1364 }
1365 } else {
1366 *lpp = q;
1367 lpp = &q->next;
1368 if ((q = *lpp) == NULL) {
1369 *lpp = p;
1370 break;
1371 }
1372 }
1373 }
1374 return list;
1375}
1376
1377
1378
1379/*
1380 * Returns true if the pattern matches the string.
1381 */
1382
1383int
99512ac4 1384patmatch(const char *pattern, const char *string, int squoted)
984263bc 1385{
99512ac4 1386 const char *p, *q;
984263bc
MD
1387 char c;
1388
1389 p = pattern;
1390 q = string;
1391 for (;;) {
1392 switch (c = *p++) {
1393 case '\0':
1394 goto breakloop;
1395 case CTLESC:
1396 if (squoted && *q == CTLESC)
1397 q++;
1398 if (*q++ != *p++)
1399 return 0;
1400 break;
1401 case CTLQUOTEMARK:
1402 continue;
1403 case '?':
1404 if (squoted && *q == CTLESC)
1405 q++;
1406 if (*q++ == '\0')
1407 return 0;
1408 break;
1409 case '*':
1410 c = *p;
1411 while (c == CTLQUOTEMARK || c == '*')
1412 c = *++p;
1413 if (c != CTLESC && c != CTLQUOTEMARK &&
1414 c != '?' && c != '*' && c != '[') {
1415 while (*q != c) {
1416 if (squoted && *q == CTLESC &&
1417 q[1] == c)
1418 break;
1419 if (*q == '\0')
1420 return 0;
1421 if (squoted && *q == CTLESC)
1422 q++;
1423 q++;
1424 }
1425 }
1426 do {
99512ac4 1427 if (patmatch(p, q, squoted))
984263bc
MD
1428 return 1;
1429 if (squoted && *q == CTLESC)
1430 q++;
1431 } while (*q++ != '\0');
1432 return 0;
1433 case '[': {
99512ac4 1434 const char *endp;
984263bc
MD
1435 int invert, found;
1436 char chr;
1437
1438 endp = p;
1439 if (*endp == '!' || *endp == '^')
1440 endp++;
1441 for (;;) {
1442 while (*endp == CTLQUOTEMARK)
1443 endp++;
1444 if (*endp == '\0')
1445 goto dft; /* no matching ] */
1446 if (*endp == CTLESC)
1447 endp++;
1448 if (*++endp == ']')
1449 break;
1450 }
1451 invert = 0;
1452 if (*p == '!' || *p == '^') {
1453 invert++;
1454 p++;
1455 }
1456 found = 0;
1457 chr = *q++;
1458 if (squoted && chr == CTLESC)
1459 chr = *q++;
1460 if (chr == '\0')
1461 return 0;
1462 c = *p++;
1463 do {
1464 if (c == CTLQUOTEMARK)
1465 continue;
1466 if (c == CTLESC)
1467 c = *p++;
1468 if (*p == '-' && p[1] != ']') {
1469 p++;
1470 while (*p == CTLQUOTEMARK)
1471 p++;
1472 if (*p == CTLESC)
1473 p++;
1474 if ( collate_range_cmp(chr, c) >= 0
1475 && collate_range_cmp(chr, *p) <= 0
1476 )
1477 found = 1;
1478 p++;
1479 } else {
1480 if (chr == c)
1481 found = 1;
1482 }
1483 } while ((c = *p++) != ']');
1484 if (found == invert)
1485 return 0;
1486 break;
1487 }
1488dft: default:
1489 if (squoted && *q == CTLESC)
1490 q++;
1491 if (*q++ != c)
1492 return 0;
1493 break;
1494 }
1495 }
1496breakloop:
1497 if (*q != '\0')
1498 return 0;
1499 return 1;
1500}
1501
1502
1503
1504/*
99512ac4 1505 * Remove any CTLESC and CTLQUOTEMARK characters from a string.
984263bc
MD
1506 */
1507
1508void
1509rmescapes(char *str)
1510{
1511 char *p, *q;
1512
1513 p = str;
99512ac4 1514 while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
984263bc
MD
1515 if (*p++ == '\0')
1516 return;
1517 }
1518 q = p;
1519 while (*p) {
99512ac4 1520 if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
984263bc
MD
1521 p++;
1522 continue;
1523 }
1524 if (*p == CTLESC)
1525 p++;
1526 *q++ = *p++;
1527 }
1528 *q = '\0';
1529}
1530
1531
1532
1533/*
1534 * See if a pattern matches in a case statement.
1535 */
1536
1537int
99512ac4 1538casematch(union node *pattern, const char *val)
984263bc
MD
1539{
1540 struct stackmark smark;
1541 int result;
1542 char *p;
1543
1544 setstackmark(&smark);
1545 argbackq = pattern->narg.backquote;
1546 STARTSTACKSTR(expdest);
1547 ifslastp = NULL;
1548 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
1549 STPUTC('\0', expdest);
1550 p = grabstackstr(expdest);
1551 result = patmatch(p, val, 0);
1552 popstackmark(&smark);
1553 return result;
1554}
1555
1556/*
1557 * Our own itoa().
1558 */
1559
99512ac4 1560static char *
984263bc
MD
1561cvtnum(int num, char *buf)
1562{
1563 char temp[32];
1564 int neg = num < 0;
1565 char *p = temp + 31;
1566
1567 temp[31] = '\0';
1568
1569 do {
1570 *--p = num % 10 + '0';
1571 } while ((num /= 10) != 0);
1572
1573 if (neg)
1574 *--p = '-';
1575
99512ac4 1576 STPUTS(p, buf);
984263bc
MD
1577 return buf;
1578}
d13d0ee2 1579
99512ac4
PA
1580/*
1581 * Check statically if expanding a string may have side effects.
1582 */
1583int
1584expandhassideeffects(const char *p)
1585{
1586 int c;
1587 int arinest;
1588
1589 arinest = 0;
1590 while ((c = *p++) != '\0') {
1591 switch (c) {
1592 case CTLESC:
1593 p++;
1594 break;
1595 case CTLVAR:
1596 c = *p++;
1597 /* Expanding $! sets the job to remembered. */
1598 if (*p == '!')
1599 return 1;
1600 if ((c & VSTYPE) == VSASSIGN)
1601 return 1;
1602 /*
1603 * If we are in arithmetic, the parameter may contain
1604 * '=' which may cause side effects. Exceptions are
1605 * the length of a parameter and $$, $# and $? which
1606 * are always numeric.
1607 */
1608 if ((c & VSTYPE) == VSLENGTH) {
1609 while (*p != '=')
1610 p++;
1611 p++;
1612 break;
1613 }
1614 if ((*p == '$' || *p == '#' || *p == '?') &&
1615 p[1] == '=') {
1616 p += 2;
1617 break;
1618 }
1619 if (arinest > 0)
1620 return 1;
1621 break;
1622 case CTLBACKQ:
1623 case CTLBACKQ | CTLQUOTE:
1624 if (arinest > 0)
1625 return 1;
1626 break;
1627 case CTLARI:
1628 arinest++;
1629 break;
1630 case CTLENDARI:
1631 arinest--;
1632 break;
1633 case '=':
1634 if (*p == '=') {
1635 /* Allow '==' operator. */
1636 p++;
1637 continue;
1638 }
1639 if (arinest > 0)
1640 return 1;
1641 break;
1642 case '!': case '<': case '>':
1643 /* Allow '!=', '<=', '>=' operators. */
1644 if (*p == '=')
1645 p++;
1646 break;
1647 }
1648 }
1649 return 0;
1650}
1651
d13d0ee2
PA
1652/*
1653 * Do most of the work for wordexp(3).
1654 */
1655
1656int
1657wordexpcmd(int argc, char **argv)
1658{
1659 size_t len;
1660 int i;
1661
1662 out1fmt("%08x", argc - 1);
1663 for (i = 1, len = 0; i < argc; i++)
1664 len += strlen(argv[i]);
1665 out1fmt("%08x", (int)len);
99512ac4
PA
1666 for (i = 1; i < argc; i++)
1667 outbin(argv[i], strlen(argv[i]) + 1, out1);
d13d0ee2
PA
1668 return (0);
1669}