]>
git.saurik.com Git - apple/shell_cmds.git/blob - sh/expand.c
2 * Copyright (c) 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 1997-2005
5 * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved.
6 * Copyright (c) 2010-2015
7 * Jilles Tjoelker <jilles@stack.nl>. All rights reserved.
9 * This code is derived from software contributed to Berkeley by
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 static char sccsid
[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95";
42 #include <sys/cdefs.h>
43 __FBSDID("$FreeBSD: head/bin/sh/expand.c 303586 2016-07-31 13:11:34Z jilles $");
45 #include <sys/types.h>
61 * Routines to expand arguments to commands. We have to deal with
62 * backquotes, shell variables, and file metacharacters.
84 enum wordstate
{ WORD_IDLE
, WORD_WS_DELIMITED
, WORD_QUOTEMARK
};
91 static char *expdest
; /* output of current string */
92 static struct nodelist
*argbackq
; /* list of back quote expressions */
94 static const char *argstr(const char *, int, struct worddest
*);
95 static const char *exptilde(const char *, int);
96 static const char *expari(const char *, int, struct worddest
*);
97 static void expbackq(union node
*, int, int, struct worddest
*);
98 static void subevalvar_trim(const char *, int, int, int);
99 static int subevalvar_misc(const char *, const char *, int, int, int);
100 static const char *evalvar(const char *, int, struct worddest
*);
101 static int varisset(const char *, int);
102 static void strtodest(const char *, int, int, int, struct worddest
*);
103 static void reprocess(int, int, int, int, struct worddest
*);
104 static void varvalue(const char *, int, int, int, struct worddest
*);
105 static void expandmeta(char *, struct arglist
*);
106 static void expmeta(char *, char *, struct arglist
*);
107 static int expsortcmp(const void *, const void *);
108 static int patmatch(const char *, const char *);
109 static void cvtnum(int, char *);
110 static int collate_range_cmp(wchar_t, wchar_t);
113 emptyarglist(struct arglist
*list
)
116 list
->args
= list
->smallarg
;
118 list
->capacity
= sizeof(list
->smallarg
) / sizeof(list
->smallarg
[0]);
122 appendarglist(struct arglist
*list
, char *str
)
127 if (list
->count
>= list
->capacity
) {
128 newcapacity
= list
->capacity
* 2;
129 if (newcapacity
< 16)
131 if (newcapacity
> INT_MAX
/ (int)sizeof(newargs
[0]))
132 error("Too many entries in arglist");
133 newargs
= stalloc(newcapacity
* sizeof(newargs
[0]));
134 memcpy(newargs
, list
->args
, list
->count
* sizeof(newargs
[0]));
135 list
->args
= newargs
;
136 list
->capacity
= newcapacity
;
138 list
->args
[list
->count
++] = str
;
142 collate_range_cmp(wchar_t c1
, wchar_t c2
)
144 static wchar_t s1
[2], s2
[2];
148 return (wcscoll(s1
, s2
));
152 stputs_quotes(const char *data
, const char *syntax
, char *p
)
156 if (syntax
[(int)*data
] == CCTL
)
162 #define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
165 nextword(char c
, int flag
, char *p
, struct worddest
*dst
)
169 is_ws
= c
== '\t' || c
== '\n' || c
== ' ';
170 if (p
!= stackblock() || (is_ws
? dst
->state
== WORD_QUOTEMARK
:
171 dst
->state
!= WORD_WS_DELIMITED
) || c
== '\0') {
174 expandmeta(grabstackstr(p
), dst
->list
);
176 appendarglist(dst
->list
, grabstackstr(p
));
177 dst
->state
= is_ws
? WORD_WS_DELIMITED
: WORD_IDLE
;
178 } else if (!is_ws
&& dst
->state
== WORD_WS_DELIMITED
)
179 dst
->state
= WORD_IDLE
;
180 /* Reserve space while the stack string is empty. */
181 appendarglist(dst
->list
, NULL
);
186 #define NEXTWORD(c, flag, p, dstlist) p = nextword(c, flag, p, dstlist)
189 stputs_split(const char *data
, const char *syntax
, int flag
, char *p
,
190 struct worddest
*dst
)
195 ifs
= ifsset() ? ifsval() : " \t\n";
199 if (strchr(ifs
, c
) != NULL
) {
200 NEXTWORD(c
, flag
, p
, dst
);
203 if (flag
& EXP_GLOB
&& syntax
[(int)c
] == CCTL
)
209 #define STPUTS_SPLIT(data, syntax, flag, p, dst) p = stputs_split((data), syntax, flag, p, dst)
212 * Perform expansions on an argument, placing the resulting list of arguments
213 * in arglist. Parameter expansion, command substitution and arithmetic
214 * expansion are always performed; additional expansions can be requested
216 * The result is left in the stack string.
217 * When arglist is NULL, perform here document expansion.
219 * Caution: this function uses global state and is not reentrant.
220 * However, a new invocation after an interrupted invocation is safe
221 * and will reset the global state for the new call.
224 expandarg(union node
*arg
, struct arglist
*arglist
, int flag
)
226 struct worddest exparg
;
230 argbackq
= arg
->narg
.backquote
;
231 exparg
.list
= arglist
;
232 exparg
.state
= WORD_IDLE
;
233 STARTSTACKSTR(expdest
);
234 argstr(arg
->narg
.text
, flag
, &exparg
);
235 if (arglist
== NULL
) {
236 STACKSTRNUL(expdest
);
237 return; /* here document expanded */
239 if ((flag
& EXP_SPLIT
) == 0 || expdest
!= stackblock() ||
240 exparg
.state
== WORD_QUOTEMARK
) {
241 STPUTC('\0', expdest
);
242 if (flag
& EXP_SPLIT
) {
244 expandmeta(grabstackstr(expdest
), exparg
.list
);
246 appendarglist(exparg
.list
, grabstackstr(expdest
));
249 if ((flag
& EXP_SPLIT
) == 0)
250 appendarglist(arglist
, grabstackstr(expdest
));
256 * Perform parameter expansion, command substitution and arithmetic
257 * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
258 * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'.
259 * This is used to expand word in ${var+word} etc.
260 * If EXP_GLOB or EXP_CASE are set, keep and/or generate CTLESC
261 * characters to allow for further processing.
263 * If EXP_SPLIT is set, dst receives any complete words produced.
266 argstr(const char *p
, int flag
, struct worddest
*dst
)
269 int quotes
= flag
& (EXP_GLOB
| EXP_CASE
); /* do CTLESC */
274 split_lit
= flag
& EXP_SPLIT_LIT
;
275 lit_quoted
= flag
& EXP_LIT_QUOTED
;
276 flag
&= ~(EXP_SPLIT_LIT
| EXP_LIT_QUOTED
);
277 if (*p
== '~' && (flag
& (EXP_TILDE
| EXP_VARTILDE
)))
278 p
= exptilde(p
, flag
);
280 CHECKSTRSPACE(2, expdest
);
289 /* "$@" syntax adherence hack */
290 if (p
[0] == CTLVAR
&& (p
[1] & VSQUOTE
) != 0 &&
291 p
[2] == '@' && p
[3] == '=')
293 if ((flag
& EXP_SPLIT
) != 0 && expdest
== stackblock())
294 dst
->state
= WORD_QUOTEMARK
;
301 if (split_lit
&& !lit_quoted
&&
302 strchr(ifsset() ? ifsval() : " \t\n", c
) != NULL
) {
303 NEXTWORD(c
, flag
, expdest
, dst
);
307 USTPUTC(CTLESC
, expdest
);
311 p
= evalvar(p
, flag
, dst
);
314 case CTLBACKQ
|CTLQUOTE
:
315 expbackq(argbackq
->n
, c
& CTLQUOTE
, flag
, dst
);
316 argbackq
= argbackq
->next
;
319 p
= expari(p
, flag
, dst
);
324 * sort of a hack - expand tildes in variable
325 * assignments (after the first '=' and after ':'s).
327 if (split_lit
&& !lit_quoted
&&
328 strchr(ifsset() ? ifsval() : " \t\n", c
) != NULL
) {
329 NEXTWORD(c
, flag
, expdest
, dst
);
333 if (flag
& EXP_VARTILDE
&& *p
== '~' &&
334 (c
!= '=' || firsteq
)) {
337 p
= exptilde(p
, flag
);
341 if (split_lit
&& !lit_quoted
&&
342 strchr(ifsset() ? ifsval() : " \t\n", c
) != NULL
) {
343 NEXTWORD(c
, flag
, expdest
, dst
);
352 * Perform tilde expansion, placing the result in the stack string and
353 * returning the next position in the input string to process.
356 exptilde(const char *p
, int flag
)
359 const char *startp
= p
;
368 case CTLESC
: /* This means CTL* are always considered quoted. */
371 case CTLBACKQ
| CTLQUOTE
:
377 if ((flag
& EXP_VARTILDE
) == 0)
383 len
= p
- startp
- 1;
384 STPUTBIN(startp
+ 1, len
, expdest
);
385 STACKSTRNUL(expdest
);
386 user
= expdest
- len
;
388 home
= lookupvar("HOME");
391 home
= pw
!= NULL
? pw
->pw_dir
: NULL
;
393 STADJUST(-len
, expdest
);
394 if (home
== NULL
|| *home
== '\0')
396 strtodest(home
, flag
, VSNORMAL
, 1, NULL
);
405 * Expand arithmetic expression.
408 expari(const char *p
, int flag
, struct worddest
*dst
)
416 quoted
= *p
++ == '"';
417 begoff
= expdest
- stackblock();
418 p
= argstr(p
, 0, NULL
);
419 STPUTC('\0', expdest
);
420 start
= stackblock() + begoff
;
422 q
= grabstackstr(expdest
);
423 result
= arith(start
);
424 ungrabstackstr(q
, expdest
);
426 start
= stackblock() + begoff
;
427 adj
= start
- expdest
;
428 STADJUST(adj
, expdest
);
430 CHECKSTRSPACE((int)(DIGITS(result
) + 1), expdest
);
431 fmtstr(expdest
, DIGITS(result
), ARITH_FORMAT_STR
, result
);
432 adj
= strlen(expdest
);
433 STADJUST(adj
, expdest
);
435 reprocess(expdest
- adj
- stackblock(), flag
, VSNORMAL
, 0, dst
);
441 * Perform command substitution.
444 expbackq(union node
*cmd
, int quoted
, int flag
, struct worddest
*dst
)
450 char *dest
= expdest
;
451 struct nodelist
*saveargbackq
;
453 char const *syntax
= quoted
? DQSYNTAX
: BASESYNTAX
;
454 int quotes
= flag
& (EXP_GLOB
| EXP_CASE
);
459 saveargbackq
= argbackq
;
460 p
= grabstackstr(dest
);
461 evalbackcmd(cmd
, &in
);
462 ungrabstackstr(p
, dest
);
463 argbackq
= saveargbackq
;
467 if (!quoted
&& flag
& EXP_SPLIT
)
468 ifs
= ifsset() ? ifsval() : " \t\n";
471 /* Don't copy trailing newlines */
473 if (--in
.nleft
< 0) {
476 while ((i
= read(in
.fd
, buf
, sizeof buf
)) < 0 && errno
== EINTR
)
478 TRACE(("expbackq: read returns %d\n", i
));
491 if (strchr(ifs
, '\n') != NULL
) {
492 NEXTWORD('\n', flag
, dest
, dst
);
495 CHECKSTRSPACE(nnl
+ 2, dest
);
502 if (strchr(ifs
, lastc
) != NULL
)
503 NEXTWORD(lastc
, flag
, dest
, dst
);
505 CHECKSTRSPACE(2, dest
);
506 if (quotes
&& syntax
[(int)lastc
] == CCTL
)
507 USTPUTC(CTLESC
, dest
);
508 USTPUTC(lastc
, dest
);
518 exitstatus
= waitforjob(in
.jp
, (int *)NULL
);
519 TRACE(("expbackq: size=%td: \"%.*s\"\n",
520 ((dest
- stackblock()) - startloc
),
521 (int)((dest
- stackblock()) - startloc
),
522 stackblock() + startloc
));
530 recordleft(const char *str
, const char *loc
, char *startp
)
534 amount
= ((str
- 1) - (loc
- startp
)) - expdest
;
535 STADJUST(amount
, expdest
);
536 while (loc
!= str
- 1)
541 subevalvar_trim(const char *p
, int strloc
, int subtype
, int startloc
)
547 struct nodelist
*saveargbackq
= argbackq
;
550 argstr(p
, EXP_CASE
| EXP_TILDE
, NULL
);
551 STACKSTRNUL(expdest
);
552 argbackq
= saveargbackq
;
553 startp
= stackblock() + startloc
;
554 str
= stackblock() + strloc
;
558 for (loc
= startp
; loc
< str
; loc
++) {
561 if (patmatch(str
, startp
)) {
563 recordleft(str
, loc
, startp
);
571 for (loc
= str
- 1; loc
>= startp
;) {
574 if (patmatch(str
, startp
)) {
576 recordleft(str
, loc
, startp
);
585 for (loc
= str
- 1; loc
>= startp
;) {
586 if (patmatch(str
, loc
)) {
587 amount
= loc
- expdest
;
588 STADJUST(amount
, expdest
);
596 for (loc
= startp
; loc
< str
- 1; loc
++) {
597 if (patmatch(str
, loc
)) {
598 amount
= loc
- expdest
;
599 STADJUST(amount
, expdest
);
609 amount
= (expdest
- stackblock() - strloc
) + 1;
610 STADJUST(-amount
, expdest
);
615 subevalvar_misc(const char *p
, const char *var
, int subtype
, int startloc
,
619 struct nodelist
*saveargbackq
= argbackq
;
622 argstr(p
, EXP_TILDE
, NULL
);
623 STACKSTRNUL(expdest
);
624 argbackq
= saveargbackq
;
625 startp
= stackblock() + startloc
;
629 setvar(var
, startp
, 0);
630 amount
= startp
- expdest
;
631 STADJUST(amount
, expdest
);
635 if (*p
!= CTLENDVAR
) {
636 outfmt(out2
, "%s\n", startp
);
639 error("%.*s: parameter %snot set", (int)(p
- var
- 1),
640 var
, (varflags
& VSNUL
) ? "null or " : "");
650 * Expand a variable, and return a pointer to the next character in the
655 evalvar(const char *p
, int flag
, struct worddest
*dst
)
670 varflags
= (unsigned char)*p
++;
671 subtype
= varflags
& VSTYPE
;
676 p
= strchr(p
, '=') + 1;
677 again
: /* jump here after setting a variable with ${var=text} */
678 if (varflags
& VSLINENO
) {
682 } else if (special
) {
683 set
= varisset(var
, varflags
& VSNUL
);
686 val
= bltinlookup(var
, 1);
687 if (val
== NULL
|| ((varflags
& VSNUL
) && val
[0] == '\0')) {
694 startloc
= expdest
- stackblock();
695 if (!set
&& uflag
&& *var
!= '@' && *var
!= '*') {
703 error("%.*s: parameter not set", (int)(p
- var
- 1),
707 if (set
&& subtype
!= VSPLUS
) {
708 /* insert the value of the variable */
710 if (varflags
& VSLINENO
) {
711 if (p
- var
> (ptrdiff_t)sizeof(buf
))
713 memcpy(buf
, var
, p
- var
- 1);
714 buf
[p
- var
- 1] = '\0';
715 strtodest(buf
, flag
, subtype
,
716 varflags
& VSQUOTE
, dst
);
718 varvalue(var
, varflags
& VSQUOTE
, subtype
, flag
,
720 if (subtype
== VSLENGTH
) {
721 varlenb
= expdest
- stackblock() - startloc
;
724 val
= stackblock() + startloc
;
725 for (;val
!= expdest
; val
++)
726 if ((*val
& 0xC0) == 0x80)
729 STADJUST(-varlenb
, expdest
);
732 if (subtype
== VSLENGTH
) {
735 (*val
& 0xC0) != 0x80)
739 strtodest(val
, flag
, subtype
,
740 varflags
& VSQUOTE
, dst
);
744 if (subtype
== VSPLUS
)
750 strtodest(buf
, flag
, VSNORMAL
, varflags
& VSQUOTE
, dst
);
759 argstr(p
, flag
| (flag
& EXP_SPLIT
? EXP_SPLIT_LIT
: 0) |
760 (varflags
& VSQUOTE
? EXP_LIT_QUOTED
: 0), dst
);
772 * Terminate the string and start recording the pattern
775 STPUTC('\0', expdest
);
776 patloc
= expdest
- stackblock();
777 subevalvar_trim(p
, patloc
, subtype
, startloc
);
778 reprocess(startloc
, flag
, VSNORMAL
, varflags
& VSQUOTE
, dst
);
779 if (flag
& EXP_SPLIT
&& *var
== '@' && varflags
& VSQUOTE
)
780 dst
->state
= WORD_QUOTEMARK
;
786 if (subevalvar_misc(p
, var
, subtype
, startloc
,
797 error("${%.*s%s}: Bad substitution", c
, var
,
798 (c
> 0 && *p
!= CTLENDVAR
) ? "..." : "");
804 if (subtype
!= VSNORMAL
) { /* skip to end of alternative */
807 if ((c
= *p
++) == CTLESC
)
809 else if (c
== CTLBACKQ
|| c
== (CTLBACKQ
|CTLQUOTE
)) {
811 argbackq
= argbackq
->next
;
812 } else if (c
== CTLVAR
) {
813 if ((*p
++ & VSTYPE
) != VSNORMAL
)
815 } else if (c
== CTLENDVAR
) {
827 * Test whether a specialized variable is set.
831 varisset(const char *name
, int nulok
)
835 return backgndpidset();
836 else if (*name
== '@' || *name
== '*') {
837 if (*shellparam
.p
== NULL
)
843 for (av
= shellparam
.p
; *av
; av
++)
848 } else if (is_digit(*name
)) {
853 num
= strtol(name
, NULL
, 10);
854 if (errno
!= 0 || num
> shellparam
.nparam
)
860 ap
= shellparam
.p
[num
- 1];
862 if (nulok
&& (ap
== NULL
|| *ap
== '\0'))
869 strtodest(const char *p
, int flag
, int subtype
, int quoted
,
870 struct worddest
*dst
)
872 if (subtype
== VSLENGTH
|| subtype
== VSTRIMLEFT
||
873 subtype
== VSTRIMLEFTMAX
|| subtype
== VSTRIMRIGHT
||
874 subtype
== VSTRIMRIGHTMAX
)
876 else if (flag
& EXP_SPLIT
&& !quoted
&& dst
!= NULL
)
877 STPUTS_SPLIT(p
, BASESYNTAX
, flag
, expdest
, dst
);
878 else if (flag
& (EXP_GLOB
| EXP_CASE
))
879 STPUTS_QUOTES(p
, quoted
? DQSYNTAX
: BASESYNTAX
, expdest
);
885 reprocess(int startloc
, int flag
, int subtype
, int quoted
,
886 struct worddest
*dst
)
888 static char *buf
= NULL
;
889 static size_t buflen
= 0;
891 size_t len
, zpos
, zlen
;
893 startp
= stackblock() + startloc
;
894 len
= expdest
- startp
;
895 if (len
>= SIZE_MAX
/ 2)
904 while (len
>= buflen
)
907 buf
= ckmalloc(buflen
);
909 memcpy(buf
, startp
, len
);
911 STADJUST(-len
, expdest
);
913 zlen
= strlen(buf
+ zpos
);
914 strtodest(buf
+ zpos
, flag
, subtype
, quoted
, dst
);
918 if (flag
& EXP_SPLIT
&& (quoted
|| (zlen
> 0 && zpos
< len
)))
919 NEXTWORD('\0', flag
, expdest
, dst
);
924 * Add the value of a specialized variable to the stack string.
928 varvalue(const char *name
, int quoted
, int subtype
, int flag
,
929 struct worddest
*dst
)
937 char buf
[(NSHORTOPTS
> 10 ? NSHORTOPTS
: 10) + 1];
939 if (subtype
== VSLENGTH
)
941 splitlater
= subtype
== VSTRIMLEFT
|| subtype
== VSTRIMLEFTMAX
||
942 subtype
== VSTRIMRIGHT
|| subtype
== VSTRIMRIGHTMAX
;
952 num
= shellparam
.nparam
;
955 num
= backgndpidval();
959 for (i
= 0 ; i
< NSHORTOPTS
; i
++) {
964 strtodest(buf
, flag
, subtype
, quoted
, dst
);
967 if (flag
& EXP_SPLIT
&& quoted
) {
968 for (ap
= shellparam
.p
; (p
= *ap
++) != NULL
; ) {
969 strtodest(p
, flag
, subtype
, quoted
, dst
);
972 STPUTC('\0', expdest
);
974 NEXTWORD('\0', flag
, expdest
,
978 if (shellparam
.nparam
> 0)
979 dst
->state
= WORD_QUOTEMARK
;
985 sep
[0] = ifsval()[0];
989 for (ap
= shellparam
.p
; (p
= *ap
++) != NULL
; ) {
990 strtodest(p
, flag
, subtype
, quoted
, dst
);
994 strtodest(sep
, flag
, subtype
, quoted
, dst
);
995 else if (flag
& EXP_SPLIT
&& !quoted
&& **ap
!= '\0') {
997 STPUTC('\0', expdest
);
999 NEXTWORD('\0', flag
, expdest
, dst
);
1004 if (is_digit(*name
)) {
1008 else if (num
> 0 && num
<= shellparam
.nparam
)
1009 p
= shellparam
.p
[num
- 1];
1012 strtodest(p
, flag
, subtype
, quoted
, dst
);
1017 strtodest(buf
, flag
, subtype
, quoted
, dst
);
1022 static char expdir
[PATH_MAX
];
1023 #define expdir_end (expdir + sizeof(expdir))
1026 * Perform pathname generation and remove control characters.
1027 * At this point, the only control characters should be CTLESC.
1028 * The results are stored in the list dstlist.
1031 expandmeta(char *pattern
, struct arglist
*dstlist
)
1037 firstmatch
= dstlist
->count
;
1039 for (; (c
= *p
) != '\0'; p
++) {
1040 /* fast check for meta chars */
1041 if (c
== '*' || c
== '?' || c
== '[') {
1043 expmeta(expdir
, pattern
, dstlist
);
1048 if (dstlist
->count
== firstmatch
) {
1053 appendarglist(dstlist
, pattern
);
1055 qsort(&dstlist
->args
[firstmatch
],
1056 dstlist
->count
- firstmatch
,
1057 sizeof(dstlist
->args
[0]), expsortcmp
);
1063 * Do metacharacter (i.e. *, ?, [...]) expansion.
1067 expmeta(char *enddir
, char *name
, struct arglist
*arglist
)
1084 for (p
= name
; esc
= 0, *p
; p
+= esc
+ 1) {
1085 if (*p
== '*' || *p
== '?')
1087 else if (*p
== '[') {
1089 if (*q
== '!' || *q
== '^')
1094 if (*q
== '/' || *q
== '\0')
1101 } else if (*p
== '\0')
1106 if (p
[esc
] == '/') {
1109 start
= p
+ esc
+ 1;
1113 if (metaflag
== 0) { /* we've reached the end of the file name */
1114 if (enddir
!= expdir
)
1116 for (p
= name
; ; p
++) {
1122 if (enddir
== expdir_end
)
1125 if (metaflag
== 0 || lstat(expdir
, &statb
) >= 0)
1126 appendarglist(arglist
, stsavestr(expdir
));
1129 endname
= name
+ (p
- name
);
1130 if (start
!= name
) {
1136 if (enddir
== expdir_end
)
1140 if (enddir
== expdir
) {
1142 } else if (enddir
== expdir
+ 1 && *expdir
== '/') {
1148 if ((dirp
= opendir(p
)) == NULL
)
1150 if (enddir
!= expdir
)
1152 if (*endname
== 0) {
1165 while (! int_pending() && (dp
= readdir(dirp
)) != NULL
) {
1166 if (dp
->d_name
[0] == '.' && ! matchdot
)
1168 if (patmatch(start
, dp
->d_name
)) {
1169 namlen
= dp
->d_namlen
;
1170 if (enddir
+ namlen
+ 1 > expdir_end
)
1172 memcpy(enddir
, dp
->d_name
, namlen
+ 1);
1174 appendarglist(arglist
, stsavestr(expdir
));
1176 if (dp
->d_type
!= DT_UNKNOWN
&&
1177 dp
->d_type
!= DT_DIR
&&
1178 dp
->d_type
!= DT_LNK
)
1180 if (enddir
+ namlen
+ 2 > expdir_end
)
1182 enddir
[namlen
] = '/';
1183 enddir
[namlen
+ 1] = '\0';
1184 expmeta(enddir
+ namlen
+ 1, endname
, arglist
);
1190 endname
[-esc
- 1] = esc
? CTLESC
: '/';
1195 expsortcmp(const void *p1
, const void *p2
)
1197 const char *s1
= *(const char * const *)p1
;
1198 const char *s2
= *(const char * const *)p2
;
1200 return (strcoll(s1
, s2
));
1206 get_wc(const char **p
)
1211 chrlen
= mbtowc(&c
, *p
, 4);
1214 else if (chrlen
== -1)
1223 * See if a character matches a character class, starting at the first colon
1225 * If a valid character class is recognized, a pointer to the next character
1226 * after the final closing bracket is stored into *end, otherwise a null
1227 * pointer is stored into *end.
1230 match_charclass(const char *p
, wchar_t chr
, const char **end
)
1233 const char *nameend
;
1238 nameend
= strstr(p
, ":]");
1239 if (nameend
== NULL
|| (size_t)(nameend
- p
) >= sizeof(name
) ||
1242 memcpy(name
, p
, nameend
- p
);
1243 name
[nameend
- p
] = '\0';
1245 cclass
= wctype(name
);
1246 /* An unknown class matches nothing but is valid nevertheless. */
1249 return iswctype(chr
, cclass
);
1254 * Returns true if the pattern matches the string.
1258 patmatch(const char *pattern
, const char *string
)
1260 const char *p
, *q
, *end
;
1261 const char *bt_p
, *bt_q
;
1285 * A '?' does not match invalid UTF-8 but a
1286 * '*' does, so backtrack.
1298 * If the pattern ends here, we know the string
1299 * matches without needing to look at the rest of it.
1304 * First try the shortest match for the '*' that
1305 * could work. We can forget any earlier '*' since
1306 * there is no way having it match more characters
1307 * can help us, given that we are already here.
1313 const char *savep
, *saveq
;
1317 savep
= p
, saveq
= q
;
1319 if (*p
== '!' || *p
== '^') {
1331 chr
= (unsigned char)*q
++;
1335 p
= savep
, q
= saveq
;
1339 if (c
== '[' && *p
== ':') {
1340 found
|= match_charclass(p
, chr
, &end
);
1346 if (localeisutf8
&& c
& 0x80) {
1349 if (wc
== 0) /* bad utf-8 */
1352 wc
= (unsigned char)c
;
1353 if (*p
== '-' && p
[1] != ']') {
1359 if (wc2
== 0) /* bad utf-8 */
1362 wc2
= (unsigned char)*p
++;
1363 if ( collate_range_cmp(chr
, wc
) >= 0
1364 && collate_range_cmp(chr
, wc2
) <= 0
1371 } while ((c
= *p
++) != ']');
1372 if (found
== invert
)
1383 * If we have a mismatch (other than hitting the end
1384 * of the string), go back to the last '*' seen and
1385 * have it match one additional character.
1402 * Remove any CTLESC and CTLQUOTEMARK characters from a string.
1406 rmescapes(char *str
)
1411 while (*p
!= CTLESC
&& *p
!= CTLQUOTEMARK
&& *p
!= CTLQUOTEEND
) {
1417 if (*p
== CTLQUOTEMARK
|| *p
== CTLQUOTEEND
) {
1431 * See if a pattern matches in a case statement.
1435 casematch(union node
*pattern
, const char *val
)
1437 struct stackmark smark
;
1441 setstackmark(&smark
);
1442 argbackq
= pattern
->narg
.backquote
;
1443 STARTSTACKSTR(expdest
);
1444 argstr(pattern
->narg
.text
, EXP_TILDE
| EXP_CASE
, NULL
);
1445 STPUTC('\0', expdest
);
1446 p
= grabstackstr(expdest
);
1447 result
= patmatch(p
, val
);
1448 popstackmark(&smark
);
1457 cvtnum(int num
, char *buf
)
1461 char *p
= temp
+ 31;
1466 *--p
= num
% 10 + '0';
1467 } while ((num
/= 10) != 0);
1472 memcpy(buf
, p
, temp
+ 32 - p
);
1476 * Do most of the work for wordexp(3).
1480 wordexpcmd(int argc
, char **argv
)
1485 out1fmt("%08x", argc
- 1);
1486 for (i
= 1, len
= 0; i
< argc
; i
++)
1487 len
+= strlen(argv
[i
]);
1488 out1fmt("%08x", (int)len
);
1489 for (i
= 1; i
< argc
; i
++)
1490 outbin(argv
[i
], strlen(argv
[i
]) + 1, out1
);
1495 * Do most of the work for wordexp(3), new version.
1499 freebsd_wordexpcmd(int argc __unused
, char **argv __unused
)
1501 struct arglist arglist
;
1502 union node
*args
, *n
;
1509 while ((ch
= nextopt("f:p")) != '\0') {
1512 fd
= number(shoptarg
);
1519 if (*argptr
!= NULL
)
1520 error("wrong number of arguments");
1522 error("missing fd");
1526 args
= parsewordexp();
1527 popfile(); /* will also close fd */
1529 for (n
= args
; n
!= NULL
; n
= n
->narg
.next
) {
1530 if (n
->narg
.backquote
!= NULL
) {
1531 outcslow('C', out1
);
1532 error("command substitution disabled");
1535 outcslow(' ', out1
);
1536 emptyarglist(&arglist
);
1537 for (n
= args
; n
!= NULL
; n
= n
->narg
.next
)
1538 expandarg(n
, &arglist
, EXP_FULL
| EXP_TILDE
);
1539 for (i
= 0, len
= 0; i
< arglist
.count
; i
++)
1540 len
+= strlen(arglist
.args
[i
]);
1541 out1fmt("%016x %016zx", arglist
.count
, len
);
1542 for (i
= 0; i
< arglist
.count
; i
++)
1543 outbin(arglist
.args
[i
], strlen(arglist
.args
[i
]) + 1, out1
);