1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 * Copyright (c) 1997-2005
7 * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved.
8 * Copyright (c) 2010-2015
9 * Jilles Tjoelker <jilles@stack.nl>. All rights reserved.
10 *
11 * This code is derived from software contributed to Berkeley by
12 * Kenneth Almquist.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. 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.
37 */
38
39#include <dirent.h>
40#include <errno.h>
41#include <inttypes.h>
42#include <limits.h>
43#include <pwd.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <sys/stat.h>
48#include <sys/time.h>
49#include <sys/types.h>
50#include <unistd.h>
51#include <wchar.h>
52#include <wctype.h>
53
54/*
55 * Routines to expand arguments to commands. We have to deal with
56 * backquotes, shell variables, and file metacharacters.
57 */
58
59#include "arith.h"
60#include "builtins.h"
61#include "error.h"
62#include "eval.h"
63#include "expand.h"
64#include "input.h"
65#include "jobs.h"
66#include "main.h"
67#include "memalloc.h"
68#include "mystring.h"
69#include "nodes.h"
70#include "options.h"
71#include "output.h"
72#include "parser.h"
73#include "shell.h"
74#include "show.h"
75#include "syntax.h"
76#include "var.h"
77
78enum wordstate { WORD_IDLE, WORD_WS_DELIMITED, WORD_QUOTEMARK };
79
80struct worddest {
81 struct arglist *list;
82 enum wordstate state;
83};
84
85static char *expdest; /* output of current string */
86
87static const char *argstr(const char *, struct nodelist **restrict, int, struct worddest *);
88static const char *exptilde(const char *, int);
89static const char *expari(const char *, struct nodelist **restrict, int, struct worddest *);
90static void expbackq(union node *, int, int, struct worddest *);
91static const char *subevalvar_trim(const char *, struct nodelist **restrict, int, int, int);
92static const char *
93subevalvar_misc(const char *, struct nodelist **restrict, const char *, int, int, int);
94static const char *evalvar(const char *, struct nodelist **restrict, int, struct worddest *);
95static int varisset(const char *, int);
96static void strtodest(const char *, int, int, int, struct worddest *);
97static void reprocess(int, int, int, int, struct worddest *);
98static void varvalue(const char *, int, int, int, struct worddest *);
99static void expandmeta(char *, struct arglist *);
100static void expmeta(char *, char *, struct arglist *);
101static int expsortcmp(const void *, const void *);
102static int patmatch(const char *, const char *);
103static void cvtnum(int, char *);
104static int collate_range_cmp(wchar_t, wchar_t);
105
106void
107emptyarglist(struct arglist *list)
108{
109 list->args = list->smallarg;
110 list->count = 0;
111 list->capacity = sizeof(list->smallarg) / sizeof(list->smallarg[0]);
112}
113
114void
115appendarglist(struct arglist *list, char *str)
116{
117 char **newargs;
118 int newcapacity;
119
120 if (list->count >= list->capacity) {
121 newcapacity = list->capacity * 2;
122 if (newcapacity < 16)
123 newcapacity = 16;
124 if (newcapacity > INT_MAX / (int)sizeof(newargs[0]))
125 error("Too many entries in arglist");
126 newargs = stalloc(newcapacity * sizeof(newargs[0]));
127 memcpy(newargs, list->args, list->count * sizeof(newargs[0]));
128 list->args = newargs;
129 list->capacity = newcapacity;
130 }
131 list->args[list->count++] = str;
132}
133
134static int
135collate_range_cmp(wchar_t c1, wchar_t c2)
136{
137 wchar_t s1[2], s2[2];
138
139 s1[0] = c1;
140 s1[1] = L'\0';
141 s2[0] = c2;
142 s2[1] = L'\0';
143 return (wcscoll(s1, s2));
144}
145
146static char *
147stputs_quotes(const char *data, const char *syntax, char *p)
148{
149 while (*data) {
150 CHECKSTRSPACE(2, p);
151 if (syntax[(int)*data] == CCTL)
152 USTPUTC(CTLESC, p);
153 USTPUTC(*data++, p);
154 }
155 return (p);
156}
157#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
158
159static char *
160nextword(char c, int flag, char *p, struct worddest *dst)
161{
162 int is_ws;
163
164 is_ws = c == '\t' || c == '\n' || c == ' ';
165 if (p != stackblock() || (is_ws ? dst->state == WORD_QUOTEMARK : dst->state != WORD_WS_DELIMITED)
166 || c == '\0') {
167 STPUTC('\0', p);
168 if (flag & EXP_GLOB)
169 expandmeta(grabstackstr(p), dst->list);
170 else
171 appendarglist(dst->list, grabstackstr(p));
172 dst->state = is_ws ? WORD_WS_DELIMITED : WORD_IDLE;
173 } else if (!is_ws && dst->state == WORD_WS_DELIMITED)
174 dst->state = WORD_IDLE;
175 /* Reserve space while the stack string is empty. */
176 appendarglist(dst->list, NULL);
177 dst->list->count--;
178 STARTSTACKSTR(p);
179 return p;
180}
181#define NEXTWORD(c, flag, p, dstlist) p = nextword(c, flag, p, dstlist)
182
183static char *
184stputs_split(const char *data, const char *syntax, int flag, char *p, struct worddest *dst)
185{
186 const char *ifs;
187 char c;
188
189 ifs = ifsset() ? ifsval() : " \t\n";
190 while (*data) {
191 CHECKSTRSPACE(2, p);
192 c = *data++;
193 if (strchr(ifs, c) != NULL) {
194 NEXTWORD(c, flag, p, dst);
195 continue;
196 }
197 if (flag & EXP_GLOB && syntax[(int)c] == CCTL)
198 USTPUTC(CTLESC, p);
199 USTPUTC(c, p);
200 }
201 return (p);
202}
203#define STPUTS_SPLIT(data, syntax, flag, p, dst) p = stputs_split((data), syntax, flag, p, dst)
204
205/*
206 * Perform expansions on an argument, placing the resulting list of arguments
207 * in arglist. Parameter expansion, command substitution and arithmetic
208 * expansion are always performed; additional expansions can be requested
209 * via flag (EXP_*).
210 * The result is left in the stack string.
211 * When arglist is NULL, perform here document expansion.
212 *
213 * When doing something that may cause this to be re-entered, make sure
214 * the stack string is empty via grabstackstr() and do not assume expdest
215 * remains valid.
216 */
217void
218expandarg(union node *arg, struct arglist *arglist, int flag)
219{
220 struct worddest exparg;
221 struct nodelist *argbackq;
222
223 if (fflag)
224 flag &= ~EXP_GLOB;
225 argbackq = arg->narg.backquote;
226 exparg.list = arglist;
227 exparg.state = WORD_IDLE;
228 STARTSTACKSTR(expdest);
229 argstr(arg->narg.text, &argbackq, flag, &exparg);
230 if (arglist == NULL) {
231 STACKSTRNUL(expdest);
232 return; /* here document expanded */
233 }
234 if ((flag & EXP_SPLIT) == 0 || expdest != stackblock() || exparg.state == WORD_QUOTEMARK) {
235 STPUTC('\0', expdest);
236 if (flag & EXP_SPLIT) {
237 if (flag & EXP_GLOB)
238 expandmeta(grabstackstr(expdest), exparg.list);
239 else
240 appendarglist(exparg.list, grabstackstr(expdest));
241 }
242 }
243 if ((flag & EXP_SPLIT) == 0)
244 appendarglist(arglist, grabstackstr(expdest));
245}
246
247/*
248 * Perform parameter expansion, command substitution and arithmetic
249 * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
250 * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'.
251 * This is used to expand word in ${var+word} etc.
252 * If EXP_GLOB or EXP_CASE are set, keep and/or generate CTLESC
253 * characters to allow for further processing.
254 *
255 * If EXP_SPLIT is set, dst receives any complete words produced.
256 */
257static const char *
258argstr(const char *p, struct nodelist **restrict argbackq, int flag, struct worddest *dst)
259{
260 char c;
261 int quotes = flag & (EXP_GLOB | EXP_CASE); /* do CTLESC */
262 int firsteq = 1;
263 int split_lit;
264 int lit_quoted;
265
266 split_lit = flag & EXP_SPLIT_LIT;
267 lit_quoted = flag & EXP_LIT_QUOTED;
268 flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED);
269 if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
270 p = exptilde(p, flag);
271 for (;;) {
272 CHECKSTRSPACE(2, expdest);
273 switch (c = *p++) {
274 case '\0':
275 return (p - 1);
276 case CTLENDVAR:
277 case CTLENDARI:
278 return (p);
279 case CTLQUOTEMARK:
280 lit_quoted = 1;
281 /* "$@" syntax adherence hack */
282 if (p[0] == CTLVAR && (p[1] & VSQUOTE) != 0 && p[2] == '@' && p[3] == '=')
283 break;
284 if ((flag & EXP_SPLIT) != 0 && expdest == stackblock())
285 dst->state = WORD_QUOTEMARK;
286 break;
287 case CTLQUOTEEND:
288 lit_quoted = 0;
289 break;
290 case CTLESC:
291 c = *p++;
292 if (split_lit && !lit_quoted && strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) {
293 NEXTWORD(c, flag, expdest, dst);
294 break;
295 }
296 if (quotes)
297 USTPUTC(CTLESC, expdest);
298 USTPUTC(c, expdest);
299 break;
300 case CTLVAR:
301 p = evalvar(p, argbackq, flag, dst);
302 break;
303 case CTLBACKQ:
304 case CTLBACKQ | CTLQUOTE:
305 expbackq((*argbackq)->n, c & CTLQUOTE, flag, dst);
306 *argbackq = (*argbackq)->next;
307 break;
308 case CTLARI:
309 p = expari(p, argbackq, flag, dst);
310 break;
311 case ':':
312 case '=':
313 /*
314 * sort of a hack - expand tildes in variable
315 * assignments (after the first '=' and after ':'s).
316 */
317 if (split_lit && !lit_quoted && strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) {
318 NEXTWORD(c, flag, expdest, dst);
319 break;
320 }
321 USTPUTC(c, expdest);
322 if (flag & EXP_VARTILDE && *p == '~' && (c != '=' || firsteq)) {
323 if (c == '=')
324 firsteq = 0;
325 p = exptilde(p, flag);
326 }
327 break;
328 default:
329 if (split_lit && !lit_quoted && strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) {
330 NEXTWORD(c, flag, expdest, dst);
331 break;
332 }
333 USTPUTC(c, expdest);
334 }
335 }
336}
337
338/*
339 * Perform tilde expansion, placing the result in the stack string and
340 * returning the next position in the input string to process.
341 */
342static const char *
343exptilde(const char *p, int flag)
344{
345 char c;
346 const char *startp = p;
347 const char *user;
348 struct passwd *pw;
349 char *home;
350 int len;
351
352 for (;;) {
353 c = *p;
354 switch (c) {
355 case CTLESC: /* This means CTL* are always considered quoted. */
356 case CTLVAR:
357 case CTLBACKQ:
358 case CTLBACKQ | CTLQUOTE:
359 case CTLARI:
360 case CTLENDARI:
361 case CTLQUOTEMARK:
362 return (startp);
363 case ':':
364 if ((flag & EXP_VARTILDE) == 0)
365 break;
366 /* FALLTHROUGH */
367 case '\0':
368 case '/':
369 case CTLENDVAR:
370 len = p - startp - 1;
371 STPUTBIN(startp + 1, len, expdest);
372 STACKSTRNUL(expdest);
373 user = expdest - len;
374 if (*user == '\0') {
375 home = lookupvar("HOME");
376 } else {
377 pw = getpwnam(user);
378 home = pw != NULL ? pw->pw_dir : NULL;
379 }
380 STADJUST(-len, expdest);
381 if (home == NULL || *home == '\0')
382 return (startp);
383 strtodest(home, flag, VSNORMAL, 1, NULL);
384 return (p);
385 }
386 p++;
387 }
388}
389
390/*
391 * Expand arithmetic expression.
392 */
393static const char *
394expari(const char *p, struct nodelist **restrict argbackq, int flag, struct worddest *dst)
395{
396 char *q, *start;
397 arith_t result;
398 int begoff;
399 int quoted;
400 int adj;
401
402 quoted = *p++ == '"';
403 begoff = expdest - stackblock();
404 p = argstr(p, argbackq, 0, NULL);
405 STPUTC('\0', expdest);
406 start = stackblock() + begoff;
407
408 q = grabstackstr(expdest);
409 result = arith(start);
410 ungrabstackstr(q, expdest);
411
412 start = stackblock() + begoff;
413 adj = start - expdest;
414 STADJUST(adj, expdest);
415
416 CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest);
417 fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result);
418 adj = strlen(expdest);
419 STADJUST(adj, expdest);
420 /*
421 * If this is quoted, a '-' must not indicate a range in [...].
422 * If this is not quoted, splitting may occur.
423 */
424 if (quoted ? result < 0 && begoff > 1 && flag & (EXP_GLOB | EXP_CASE) : flag & EXP_SPLIT)
425 reprocess(expdest - adj - stackblock(), flag, VSNORMAL, quoted, dst);
426 return p;
427}
428
429/*
430 * Perform command substitution.
431 */
432static void
433expbackq(union node *cmd, int quoted, int flag, struct worddest *dst)
434{
435 struct backcmd in;
436 int i;
437 char buf[128];
438 char *p;
439 char *dest = expdest;
440 char lastc;
441 char const *syntax = quoted ? DQSYNTAX : BASESYNTAX;
442 int quotes = flag & (EXP_GLOB | EXP_CASE);
443 size_t nnl;
444 const char *ifs;
445 int startloc;
446
447 INTOFF;
448 p = grabstackstr(dest);
449 evalbackcmd(cmd, &in);
450 ungrabstackstr(p, dest);
451
452 p = in.buf;
453 startloc = dest - stackblock();
454 nnl = 0;
455 if (!quoted && flag & EXP_SPLIT)
456 ifs = ifsset() ? ifsval() : " \t\n";
457 else
458 ifs = "";
459 /* Remove trailing newlines */
460 for (;;) {
461 if (--in.nleft < 0) {
462 if (in.fd < 0)
463 break;
464 while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR)
465 ;
466 TRACE(("expbackq: read returns %d\n", i));
467 if (i <= 0)
468 break;
469 p = buf;
470 in.nleft = i - 1;
471 }
472 lastc = *p++;
473 if (lastc == '\0')
474 continue;
475 if (nnl > 0 && lastc != '\n') {
476 NEXTWORD('\n', flag, dest, dst);
477 nnl = 0;
478 }
479 if (strchr(ifs, lastc) != NULL) {
480 if (lastc == '\n')
481 nnl++;
482 else
483 NEXTWORD(lastc, flag, dest, dst);
484 } else {
485 CHECKSTRSPACE(2, dest);
486 if (quotes && syntax[(int)lastc] == CCTL)
487 USTPUTC(CTLESC, dest);
488 USTPUTC(lastc, dest);
489 }
490 }
491 while (dest > stackblock() + startloc && STTOPC(dest) == '\n')
492 STUNPUTC(dest);
493
494 if (in.fd >= 0)
495 close(in.fd);
496 if (in.buf)
497 ckfree(in.buf);
498 if (in.jp) {
499 p = grabstackstr(dest);
500 exitstatus = waitforjob(in.jp, (int *)NULL);
501 ungrabstackstr(p, dest);
502 }
503 TRACE(("expbackq: done\n"));
504 expdest = dest;
505 INTON;
506}
507
508static void
509recordleft(const char *str, const char *loc, char *startp)
510{
511 int amount;
512
513 amount = ((str - 1) - (loc - startp)) - expdest;
514 STADJUST(amount, expdest);
515 while (loc != str - 1)
516 *startp++ = *loc++;
517}
518
519static const char *
520subevalvar_trim(
521 const char *p, struct nodelist **restrict argbackq, int strloc, int subtype, int startloc
522)
523{
524 char *startp;
525 char *loc = NULL;
526 char *str;
527 int c = 0;
528 int amount;
529
530 p = argstr(p, argbackq, EXP_CASE | EXP_TILDE, NULL);
531 STACKSTRNUL(expdest);
532 startp = stackblock() + startloc;
533 str = stackblock() + strloc;
534
535 switch (subtype) {
536 case VSTRIMLEFT:
537 for (loc = startp; loc < str; loc++) {
538 c = *loc;
539 *loc = '\0';
540 if (patmatch(str, startp)) {
541 *loc = c;
542 recordleft(str, loc, startp);
543 return p;
544 }
545 *loc = c;
546 }
547 break;
548
549 case VSTRIMLEFTMAX:
550 for (loc = str - 1; loc >= startp;) {
551 c = *loc;
552 *loc = '\0';
553 if (patmatch(str, startp)) {
554 *loc = c;
555 recordleft(str, loc, startp);
556 return p;
557 }
558 *loc = c;
559 loc--;
560 }
561 break;
562
563 case VSTRIMRIGHT:
564 for (loc = str - 1; loc >= startp;) {
565 if (patmatch(str, loc)) {
566 amount = loc - expdest;
567 STADJUST(amount, expdest);
568 return p;
569 }
570 loc--;
571 }
572 break;
573
574 case VSTRIMRIGHTMAX:
575 for (loc = startp; loc < str - 1; loc++) {
576 if (patmatch(str, loc)) {
577 amount = loc - expdest;
578 STADJUST(amount, expdest);
579 return p;
580 }
581 }
582 break;
583
584 default:
585 abort();
586 }
587 amount = (expdest - stackblock() - strloc) + 1;
588 STADJUST(-amount, expdest);
589 return p;
590}
591
592static const char *
593subevalvar_misc(
594 const char *p,
595 struct nodelist **restrict argbackq,
596 const char *var,
597 int subtype,
598 int startloc,
599 int varflags
600)
601{
602 const char *end;
603 char *startp;
604 int amount;
605
606 end = argstr(p, argbackq, EXP_TILDE, NULL);
607 STACKSTRNUL(expdest);
608 startp = stackblock() + startloc;
609
610 switch (subtype) {
611 case VSASSIGN:
612 setvar(var, startp, 0);
613 amount = startp - expdest;
614 STADJUST(amount, expdest);
615 return end;
616
617 case VSQUESTION:
618 if (*p != CTLENDVAR) {
619 outfmt(out2, "%s\n", startp);
620 error((char *)NULL);
621 }
622 error(
623 "%.*s: parameter %snot set", (int)(p - var - 1), var, (varflags & VSNUL) ? "null or " : ""
624 );
625
626 default:
627 abort();
628 }
629}
630
631/*
632 * Expand a variable, and return a pointer to the next character in the
633 * input string.
634 */
635
636static const char *
637evalvar(const char *p, struct nodelist **restrict argbackq, int flag, struct worddest *dst)
638{
639 int subtype;
640 int varflags;
641 const char *var;
642 const char *val;
643 int patloc;
644 int c;
645 int set;
646 int special;
647 int startloc;
648 int varlen;
649 int varlenb;
650 char buf[21];
651
652 varflags = (unsigned char)*p++;
653 subtype = varflags & VSTYPE;
654 var = p;
655 special = 0;
656 if (!is_name(*p))
657 special = 1;
658 p = strchr(p, '=') + 1;
659 if (varflags & VSLINENO) {
660 set = 1;
661 special = 1;
662 val = NULL;
663 } else if (special) {
664 set = varisset(var, varflags & VSNUL);
665 val = NULL;
666 } else {
667 val = bltinlookup(var, 1);
668 if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
669 val = NULL;
670 set = 0;
671 } else
672 set = 1;
673 }
674 varlen = 0;
675 startloc = expdest - stackblock();
676 if (!set && uflag && *var != '@' && *var != '*') {
677 switch (subtype) {
678 case VSNORMAL:
679 case VSTRIMLEFT:
680 case VSTRIMLEFTMAX:
681 case VSTRIMRIGHT:
682 case VSTRIMRIGHTMAX:
683 case VSLENGTH:
684 error("%.*s: parameter not set", (int)(p - var - 1), var);
685 }
686 }
687 if (set && subtype != VSPLUS) {
688 /* insert the value of the variable */
689 if (special) {
690 if (varflags & VSLINENO) {
691 if (p - var > (ptrdiff_t)sizeof(buf))
692 abort();
693 memcpy(buf, var, p - var - 1);
694 buf[p - var - 1] = '\0';
695 strtodest(buf, flag, subtype, varflags & VSQUOTE, dst);
696 } else
697 varvalue(var, varflags & VSQUOTE, subtype, flag, dst);
698 if (subtype == VSLENGTH) {
699 varlenb = expdest - stackblock() - startloc;
700 varlen = varlenb;
701 if (localeisutf8) {
702 val = stackblock() + startloc;
703 for (; val != expdest; val++)
704 if ((*val & 0xC0) == 0x80)
705 varlen--;
706 }
707 STADJUST(-varlenb, expdest);
708 }
709 } else {
710 if (subtype == VSLENGTH) {
711 for (; *val; val++)
712 if (!localeisutf8 || (*val & 0xC0) != 0x80)
713 varlen++;
714 } else
715 strtodest(val, flag, subtype, varflags & VSQUOTE, dst);
716 }
717 }
718
719 if (subtype == VSPLUS)
720 set = !set;
721
722 switch (subtype) {
723 case VSLENGTH:
724 cvtnum(varlen, buf);
725 strtodest(buf, flag, VSNORMAL, varflags & VSQUOTE, dst);
726 break;
727
728 case VSNORMAL:
729 return p;
730
731 case VSPLUS:
732 case VSMINUS:
733 if (!set) {
734 return argstr(
735 p,
736 argbackq,
737 flag | (flag & EXP_SPLIT ? EXP_SPLIT_LIT : 0)
738 | (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0),
739 dst
740 );
741 }
742 break;
743
744 case VSTRIMLEFT:
745 case VSTRIMLEFTMAX:
746 case VSTRIMRIGHT:
747 case VSTRIMRIGHTMAX:
748 if (!set)
749 break;
750 /*
751 * Terminate the string and start recording the pattern
752 * right after it
753 */
754 STPUTC('\0', expdest);
755 patloc = expdest - stackblock();
756 p = subevalvar_trim(p, argbackq, patloc, subtype, startloc);
757 reprocess(startloc, flag, VSNORMAL, varflags & VSQUOTE, dst);
758 if (flag & EXP_SPLIT && *var == '@' && varflags & VSQUOTE)
759 dst->state = WORD_QUOTEMARK;
760 return p;
761
762 case VSASSIGN:
763 case VSQUESTION:
764 if (!set) {
765 p = subevalvar_misc(p, argbackq, var, subtype, startloc, varflags);
766 /* assert(subtype == VSASSIGN); */
767 val = lookupvar(var);
768 strtodest(val, flag, subtype, varflags & VSQUOTE, dst);
769 return p;
770 }
771 break;
772
773 case VSERROR:
774 c = p - var - 1;
775 error("${%.*s%s}: Bad substitution", c, var, (c > 0 && *p != CTLENDVAR) ? "..." : "");
776
777 default:
778 abort();
779 }
780
781 { /* skip to end of alternative */
782 int nesting = 1;
783 for (;;) {
784 if ((c = *p++) == CTLESC)
785 p++;
786 else if (c == CTLBACKQ || c == (CTLBACKQ | CTLQUOTE))
787 *argbackq = (*argbackq)->next;
788 else if (c == CTLVAR) {
789 if ((*p++ & VSTYPE) != VSNORMAL)
790 nesting++;
791 } else if (c == CTLENDVAR) {
792 if (--nesting == 0)
793 break;
794 }
795 }
796 }
797 return p;
798}
799
800/*
801 * Test whether a special or positional parameter is set.
802 */
803
804static int
805varisset(const char *name, int nulok)
806{
807 if (*name == '!')
808 return backgndpidset();
809 else if (*name == '@' || *name == '*') {
810 if (*shellparam.p == NULL)
811 return 0;
812
813 if (nulok) {
814 char **av;
815
816 for (av = shellparam.p; *av; av++)
817 if (**av != '\0')
818 return 1;
819 return 0;
820 }
821 } else if (is_digit(*name)) {
822 char *ap;
823 long num;
824
825 errno = 0;
826 num = strtol(name, NULL, 10);
827 if (errno != 0 || num > shellparam.nparam)
828 return 0;
829
830 if (num == 0)
831 ap = arg0;
832 else
833 ap = shellparam.p[num - 1];
834
835 if (nulok && (ap == NULL || *ap == '\0'))
836 return 0;
837 }
838 return 1;
839}
840
841static void
842strtodest(const char *p, int flag, int subtype, int quoted, struct worddest *dst)
843{
844 if (subtype == VSLENGTH || subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX
845 || subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX)
846 STPUTS(p, expdest);
847 else if (flag & EXP_SPLIT && !quoted && dst != NULL)
848 STPUTS_SPLIT(p, BASESYNTAX, flag, expdest, dst);
849 else if (flag & (EXP_GLOB | EXP_CASE))
850 STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest);
851 else
852 STPUTS(p, expdest);
853}
854
855static void
856reprocess(int startloc, int flag, int subtype, int quoted, struct worddest *dst)
857{
858 static char *buf = NULL;
859 static size_t buflen = 0;
860 char *startp;
861 size_t len, zpos, zlen;
862
863 startp = stackblock() + startloc;
864 len = expdest - startp;
865 if (len >= SIZE_MAX / 2 || len > PTRDIFF_MAX)
866 abort();
867 INTOFF;
868 if (len >= buflen) {
869 ckfree(buf);
870 buf = NULL;
871 }
872 if (buflen < 128)
873 buflen = 128;
874 while (len >= buflen)
875 buflen <<= 1;
876 if (buf == NULL)
877 buf = ckmalloc(buflen);
878 INTON;
879 memcpy(buf, startp, len);
880 buf[len] = '\0';
881 STADJUST(-(ptrdiff_t)len, expdest);
882 for (zpos = 0;;) {
883 zlen = strlen(buf + zpos);
884 strtodest(buf + zpos, flag, subtype, quoted, dst);
885 zpos += zlen + 1;
886 if (zpos == len + 1)
887 break;
888 if (flag & EXP_SPLIT && (quoted || (zlen > 0 && zpos < len)))
889 NEXTWORD('\0', flag, expdest, dst);
890 }
891}
892
893/*
894 * Add the value of a special or positional parameter to the stack string.
895 */
896
897static void
898varvalue(const char *name, int quoted, int subtype, int flag, struct worddest *dst)
899{
900 int num;
901 char *p;
902 int i;
903 int splitlater;
904 char sep[2];
905 char **ap;
906 char buf[(NSHORTOPTS > 10 ? NSHORTOPTS : 10) + 1];
907
908 if (subtype == VSLENGTH)
909 flag &= ~EXP_FULL;
910 splitlater = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || subtype == VSTRIMRIGHT
911 || subtype == VSTRIMRIGHTMAX;
912
913 switch (*name) {
914 case '$':
915 num = rootpid;
916 break;
917 case '?':
918 num = oexitstatus;
919 break;
920 case '#':
921 num = shellparam.nparam;
922 break;
923 case '!':
924 num = backgndpidval();
925 break;
926 case '-':
927 p = buf;
928 for (i = 0; i < NSHORTOPTS; i++) {
929 if (optval[i])
930 *p++ = optletter[i];
931 }
932 *p = '\0';
933 strtodest(buf, flag, subtype, quoted, dst);
934 return;
935 case '@':
936 if (flag & EXP_SPLIT && quoted) {
937 for (ap = shellparam.p; (p = *ap++) != NULL;) {
938 strtodest(p, flag, subtype, quoted, dst);
939 if (*ap) {
940 if (splitlater)
941 STPUTC('\0', expdest);
942 else
943 NEXTWORD('\0', flag, expdest, dst);
944 }
945 }
946 if (shellparam.nparam > 0)
947 dst->state = WORD_QUOTEMARK;
948 return;
949 }
950 /* FALLTHROUGH */
951 case '*':
952 if (ifsset())
953 sep[0] = ifsval()[0];
954 else
955 sep[0] = ' ';
956 sep[1] = '\0';
957 for (ap = shellparam.p; (p = *ap++) != NULL;) {
958 strtodest(p, flag, subtype, quoted, dst);
959 if (!*ap)
960 break;
961 if (sep[0])
962 strtodest(sep, flag, subtype, quoted, dst);
963 else if (flag & EXP_SPLIT && !quoted && **ap != '\0') {
964 if (splitlater)
965 STPUTC('\0', expdest);
966 else
967 NEXTWORD('\0', flag, expdest, dst);
968 }
969 }
970 return;
971 default:
972 if (is_digit(*name)) {
973 num = atoi(name);
974 if (num == 0)
975 p = arg0;
976 else if (num > 0 && num <= shellparam.nparam)
977 p = shellparam.p[num - 1];
978 else
979 return;
980 strtodest(p, flag, subtype, quoted, dst);
981 }
982 return;
983 }
984 cvtnum(num, buf);
985 strtodest(buf, flag, subtype, quoted, dst);
986}
987
988static char expdir[PATH_MAX];
989#define expdir_end (expdir + sizeof(expdir))
990
991/*
992 * Perform pathname generation and remove control characters.
993 * At this point, the only control characters should be CTLESC.
994 * The results are stored in the list dstlist.
995 */
996static void
997expandmeta(char *pattern, struct arglist *dstlist)
998{
999 char *p;
1000 int firstmatch;
1001 char c;
1002
1003 firstmatch = dstlist->count;
1004 p = pattern;
1005 for (; (c = *p) != '\0'; p++) {
1006 /* fast check for meta chars */
1007 if (c == '*' || c == '?' || c == '[') {
1008 INTOFF;
1009 expmeta(expdir, pattern, dstlist);
1010 INTON;
1011 break;
1012 }
1013 }
1014 if (dstlist->count == firstmatch) {
1015 /*
1016 * no matches
1017 */
1018 rmescapes(pattern);
1019 appendarglist(dstlist, pattern);
1020 } else {
1021 qsort(
1022 &dstlist->args[firstmatch],
1023 dstlist->count - firstmatch,
1024 sizeof(dstlist->args[0]),
1025 expsortcmp
1026 );
1027 }
1028}
1029
1030/*
1031 * Do metacharacter (i.e. *, ?, [...]) expansion.
1032 */
1033
1034static void
1035expmeta(char *enddir, char *name, struct arglist *arglist)
1036{
1037 const char *p;
1038 const char *q;
1039 const char *start;
1040 char *endname;
1041 int metaflag;
1042 struct stat statb;
1043 DIR *dirp;
1044 struct dirent *dp;
1045 int atend;
1046 int matchdot;
1047 int esc;
1048 int namlen;
1049
1050 metaflag = 0;
1051 start = name;
1052 for (p = name; esc = 0, *p; p += esc + 1) {
1053 if (*p == '*' || *p == '?')
1054 metaflag = 1;
1055 else if (*p == '[') {
1056 q = p + 1;
1057 if (*q == '!' || *q == '^')
1058 q++;
1059 for (;;) {
1060 if (*q == CTLESC)
1061 q++;
1062 if (*q == '/' || *q == '\0')
1063 break;
1064 if (*++q == ']') {
1065 metaflag = 1;
1066 break;
1067 }
1068 }
1069 } else if (*p == '\0')
1070 break;
1071 else {
1072 if (*p == CTLESC)
1073 esc++;
1074 if (p[esc] == '/') {
1075 if (metaflag)
1076 break;
1077 start = p + esc + 1;
1078 }
1079 }
1080 }
1081 if (metaflag == 0) { /* we've reached the end of the file name */
1082 if (enddir != expdir)
1083 metaflag++;
1084 for (p = name;; p++) {
1085 if (*p == CTLESC)
1086 p++;
1087 *enddir++ = *p;
1088 if (*p == '\0')
1089 break;
1090 if (enddir == expdir_end)
1091 return;
1092 }
1093 if (metaflag == 0 || lstat(expdir, &statb) >= 0)
1094 appendarglist(arglist, stsavestr(expdir));
1095 return;
1096 }
1097 endname = name + (p - name);
1098 if (start != name) {
1099 p = name;
1100 while (p < start) {
1101 if (*p == CTLESC)
1102 p++;
1103 *enddir++ = *p++;
1104 if (enddir == expdir_end)
1105 return;
1106 }
1107 }
1108 if (enddir == expdir) {
1109 p = ".";
1110 } else if (enddir == expdir + 1 && *expdir == '/') {
1111 p = "/";
1112 } else {
1113 p = expdir;
1114 enddir[-1] = '\0';
1115 }
1116 if ((dirp = opendir(p)) == NULL)
1117 return;
1118 if (enddir != expdir)
1119 enddir[-1] = '/';
1120 if (*endname == 0) {
1121 atend = 1;
1122 } else {
1123 atend = 0;
1124 *endname = '\0';
1125 endname += esc + 1;
1126 }
1127 matchdot = 0;
1128 p = start;
1129 if (*p == CTLESC)
1130 p++;
1131 if (*p == '.')
1132 matchdot++;
1133 while (!int_pending() && (dp = readdir(dirp)) != NULL) {
1134 if (dp->d_name[0] == '.' && !matchdot)
1135 continue;
1136 if (patmatch(start, dp->d_name)) {
1137 namlen = strlen(dp->d_name);
1138 if (enddir + namlen + 1 > expdir_end)
1139 continue;
1140 memcpy(enddir, dp->d_name, namlen + 1);
1141 if (atend)
1142 appendarglist(arglist, stsavestr(expdir));
1143 else {
1144 if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_DIR && dp->d_type != DT_LNK)
1145 continue;
1146 if (enddir + namlen + 2 > expdir_end)
1147 continue;
1148 enddir[namlen] = '/';
1149 enddir[namlen + 1] = '\0';
1150 expmeta(enddir + namlen + 1, endname, arglist);
1151 }
1152 }
1153 }
1154 closedir(dirp);
1155 if (!atend)
1156 endname[-esc - 1] = esc ? CTLESC : '/';
1157}
1158
1159static int
1160expsortcmp(const void *p1, const void *p2)
1161{
1162 const char *s1 = *(const char *const *)p1;
1163 const char *s2 = *(const char *const *)p2;
1164
1165 return (strcoll(s1, s2));
1166}
1167
1168static wchar_t
1169get_wc(const char **p)
1170{
1171 wchar_t c;
1172 int chrlen;
1173
1174 chrlen = mbtowc(&c, *p, 4);
1175 if (chrlen == 0)
1176 return 0;
1177 else if (chrlen == -1)
1178 c = 0;
1179 else
1180 *p += chrlen;
1181 return c;
1182}
1183
1184/*
1185 * See if a character matches a character class, starting at the first colon
1186 * of "[:class:]".
1187 * If a valid character class is recognized, a pointer to the next character
1188 * after the final closing bracket is stored into *end, otherwise a null
1189 * pointer is stored into *end.
1190 */
1191static int
1192match_charclass(const char *p, wchar_t chr, const char **end)
1193{
1194 char name[20];
1195 const char *nameend;
1196 wctype_t cclass;
1197
1198 *end = NULL;
1199 p++;
1200 nameend = strstr(p, ":]");
1201 if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) || nameend == p)
1202 return 0;
1203 memcpy(name, p, nameend - p);
1204 name[nameend - p] = '\0';
1205 *end = nameend + 2;
1206 cclass = wctype(name);
1207 /* An unknown class matches nothing but is valid nevertheless. */
1208 if (cclass == 0)
1209 return 0;
1210 return iswctype(chr, cclass);
1211}
1212
1213/*
1214 * Returns true if the pattern matches the string.
1215 */
1216
1217static int
1218patmatch(const char *pattern, const char *string)
1219{
1220 const char *p, *q, *end;
1221 const char *bt_p, *bt_q;
1222 char c;
1223 wchar_t wc, wc2;
1224
1225 p = pattern;
1226 q = string;
1227 bt_p = NULL;
1228 bt_q = NULL;
1229 for (;;) {
1230 switch (c = *p++) {
1231 case '\0':
1232 if (*q != '\0')
1233 goto backtrack;
1234 return 1;
1235 case CTLESC:
1236 if (*q++ != *p++)
1237 goto backtrack;
1238 break;
1239 case '?':
1240 if (*q == '\0')
1241 return 0;
1242 if (localeisutf8) {
1243 wc = get_wc(&q);
1244 /*
1245 * A '?' does not match invalid UTF-8 but a
1246 * '*' does, so backtrack.
1247 */
1248 if (wc == 0)
1249 goto backtrack;
1250 } else
1251 q++;
1252 break;
1253 case '*':
1254 c = *p;
1255 while (c == '*')
1256 c = *++p;
1257 /*
1258 * If the pattern ends here, we know the string
1259 * matches without needing to look at the rest of it.
1260 */
1261 if (c == '\0')
1262 return 1;
1263 /*
1264 * First try the shortest match for the '*' that
1265 * could work. We can forget any earlier '*' since
1266 * there is no way having it match more characters
1267 * can help us, given that we are already here.
1268 */
1269 bt_p = p;
1270 bt_q = q;
1271 break;
1272 case '[': {
1273 const char *savep, *saveq;
1274 int invert, found;
1275 wchar_t chr;
1276
1277 savep = p, saveq = q;
1278 invert = 0;
1279 if (*p == '!' || *p == '^') {
1280 invert++;
1281 p++;
1282 }
1283 found = 0;
1284 if (*q == '\0')
1285 return 0;
1286 if (localeisutf8) {
1287 chr = get_wc(&q);
1288 if (chr == 0)
1289 goto backtrack;
1290 } else
1291 chr = (unsigned char)*q++;
1292 c = *p++;
1293 do {
1294 if (c == '\0') {
1295 p = savep, q = saveq;
1296 c = '[';
1297 goto dft;
1298 }
1299 if (c == '[' && *p == ':') {
1300 found |= match_charclass(p, chr, &end);
1301 if (end != NULL) {
1302 p = end;
1303 continue;
1304 }
1305 }
1306 if (c == CTLESC)
1307 c = *p++;
1308 if (localeisutf8 && c & 0x80) {
1309 p--;
1310 wc = get_wc(&p);
1311 if (wc == 0) /* bad utf-8 */
1312 return 0;
1313 } else
1314 wc = (unsigned char)c;
1315 if (*p == '-' && p[1] != ']') {
1316 p++;
1317 if (*p == CTLESC)
1318 p++;
1319 if (localeisutf8) {
1320 wc2 = get_wc(&p);
1321 if (wc2 == 0) /* bad utf-8 */
1322 return 0;
1323 } else
1324 wc2 = (unsigned char)*p++;
1325 if (collate_range_cmp(chr, wc) >= 0 && collate_range_cmp(chr, wc2) <= 0)
1326 found = 1;
1327 } else {
1328 if (chr == wc)
1329 found = 1;
1330 }
1331 } while ((c = *p++) != ']');
1332 if (found == invert)
1333 goto backtrack;
1334 break;
1335 }
1336 dft:
1337 default:
1338 if (*q == '\0')
1339 return 0;
1340 if (*q++ == c)
1341 break;
1342 backtrack:
1343 /*
1344 * If we have a mismatch (other than hitting the end
1345 * of the string), go back to the last '*' seen and
1346 * have it match one additional character.
1347 */
1348 if (bt_p == NULL)
1349 return 0;
1350 if (*bt_q == '\0')
1351 return 0;
1352 bt_q++;
1353 p = bt_p;
1354 q = bt_q;
1355 break;
1356 }
1357 }
1358}
1359
1360/*
1361 * Remove any CTLESC and CTLQUOTEMARK characters from a string.
1362 */
1363
1364void
1365rmescapes(char *str)
1366{
1367 char *p, *q;
1368
1369 p = str;
1370 while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) {
1371 if (*p++ == '\0')
1372 return;
1373 }
1374 q = p;
1375 while (*p) {
1376 if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) {
1377 p++;
1378 continue;
1379 }
1380 if (*p == CTLESC)
1381 p++;
1382 *q++ = *p++;
1383 }
1384 *q = '\0';
1385}
1386
1387/*
1388 * See if a pattern matches in a case statement.
1389 */
1390
1391int
1392casematch(union node *pattern, const char *val)
1393{
1394 struct stackmark smark;
1395 struct nodelist *argbackq;
1396 int result;
1397 char *p;
1398
1399 setstackmark(&smark);
1400 argbackq = pattern->narg.backquote;
1401 STARTSTACKSTR(expdest);
1402 argstr(pattern->narg.text, &argbackq, EXP_TILDE | EXP_CASE, NULL);
1403 STPUTC('\0', expdest);
1404 p = grabstackstr(expdest);
1405 result = patmatch(p, val);
1406 popstackmark(&smark);
1407 return result;
1408}
1409
1410/*
1411 * Our own itoa().
1412 */
1413
1414static void
1415cvtnum(int num, char *buf)
1416{
1417 char temp[32];
1418 int neg = num < 0;
1419 char *p = temp + 31;
1420
1421 temp[31] = '\0';
1422
1423 do {
1424 *--p = num % 10 + '0';
1425 } while ((num /= 10) != 0);
1426
1427 if (neg)
1428 *--p = '-';
1429
1430 memcpy(buf, p, temp + 32 - p);
1431}
1432
1433/*
1434 * Do most of the work for wordexp(3).
1435 */
1436
1437int
1438wordexpcmd(int argc, char **argv)
1439{
1440 size_t len;
1441 int i;
1442
1443 out1fmt("%08x", argc - 1);
1444 for (i = 1, len = 0; i < argc; i++)
1445 len += strlen(argv[i]);
1446 out1fmt("%08x", (int)len);
1447 for (i = 1; i < argc; i++)
1448 outbin(argv[i], strlen(argv[i]) + 1, out1);
1449 return (0);
1450}
1451
1452/*
1453 * Do most of the work for wordexp(3), new version.
1454 */
1455
1456int
1457freebsd_wordexpcmd(int argc __unused, char **argv __unused)
1458{
1459 struct arglist arglist;
1460 union node *args, *n;
1461 size_t len;
1462 int ch;
1463 int protected = 0;
1464 int fd = -1;
1465 int i;
1466
1467 while ((ch = nextopt("f:p")) != '\0') {
1468 switch (ch) {
1469 case 'f':
1470 fd = number(shoptarg);
1471 break;
1472 case 'p':
1473 protected
1474 = 1;
1475 break;
1476 }
1477 }
1478 if (*argptr != NULL)
1479 error("wrong number of arguments");
1480 if (fd < 0)
1481 error("missing fd");
1482 INTOFF;
1483 setinputfd(fd, 1);
1484 INTON;
1485 args = parsewordexp();
1486 popfile(); /* will also close fd */
1487 if (protected)
1488 for (n = args; n != NULL; n = n->narg.next) {
1489 if (n->narg.backquote != NULL) {
1490 outcslow('C', out1);
1491 error("command substitution disabled");
1492 }
1493 }
1494 outcslow(' ', out1);
1495 emptyarglist(&arglist);
1496 for (n = args; n != NULL; n = n->narg.next)
1497 expandarg(n, &arglist, EXP_FULL | EXP_TILDE);
1498 for (i = 0, len = 0; i < arglist.count; i++)
1499 len += strlen(arglist.args[i]);
1500 out1fmt("%016x %016zx", arglist.count, len);
1501 for (i = 0; i < arglist.count; i++)
1502 outbin(arglist.args[i], strlen(arglist.args[i]) + 1, out1);
1503 return (0);
1504}