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