]>
git.cameronkatri.com Git - apple_cmds.git/blob - shell_cmds/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 * 3. 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 318269 2017-05-14 13:14:19Z 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 */
93 static const char *argstr(const char *, struct nodelist
**restrict
, int,
95 static const char *exptilde(const char *, int);
96 static const char *expari(const char *, struct nodelist
**restrict
, int,
98 static void expbackq(union node
*, int, int, struct worddest
*);
99 static const char *subevalvar_trim(const char *, struct nodelist
**restrict
,
101 static const char *subevalvar_misc(const char *, struct nodelist
**restrict
,
102 const char *, int, int, int);
103 static const char *evalvar(const char *, struct nodelist
**restrict
, int,
105 static int varisset(const char *, int);
106 static void strtodest(const char *, int, int, int, struct worddest
*);
107 static void reprocess(int, int, int, int, struct worddest
*);
108 static void varvalue(const char *, int, int, int, struct worddest
*);
109 static void expandmeta(char *, struct arglist
*);
110 static void expmeta(char *, char *, struct arglist
*);
111 static int expsortcmp(const void *, const void *);
112 static int patmatch(const char *, const char *);
113 static void cvtnum(int, char *);
114 static int collate_range_cmp(wchar_t, wchar_t);
117 emptyarglist(struct arglist
*list
)
120 list
->args
= list
->smallarg
;
122 list
->capacity
= sizeof(list
->smallarg
) / sizeof(list
->smallarg
[0]);
126 appendarglist(struct arglist
*list
, char *str
)
131 if (list
->count
>= list
->capacity
) {
132 newcapacity
= list
->capacity
* 2;
133 if (newcapacity
< 16)
135 if (newcapacity
> INT_MAX
/ (int)sizeof(newargs
[0]))
136 error("Too many entries in arglist");
137 newargs
= stalloc(newcapacity
* sizeof(newargs
[0]));
138 memcpy(newargs
, list
->args
, list
->count
* sizeof(newargs
[0]));
139 list
->args
= newargs
;
140 list
->capacity
= newcapacity
;
142 list
->args
[list
->count
++] = str
;
146 collate_range_cmp(wchar_t c1
, wchar_t c2
)
148 wchar_t s1
[2], s2
[2];
154 return (wcscoll(s1
, s2
));
158 stputs_quotes(const char *data
, const char *syntax
, char *p
)
162 if (syntax
[(int)*data
] == CCTL
)
168 #define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p)
171 nextword(char c
, int flag
, char *p
, struct worddest
*dst
)
175 is_ws
= c
== '\t' || c
== '\n' || c
== ' ';
176 if (p
!= stackblock() || (is_ws
? dst
->state
== WORD_QUOTEMARK
:
177 dst
->state
!= WORD_WS_DELIMITED
) || c
== '\0') {
180 expandmeta(grabstackstr(p
), dst
->list
);
182 appendarglist(dst
->list
, grabstackstr(p
));
183 dst
->state
= is_ws
? WORD_WS_DELIMITED
: WORD_IDLE
;
184 } else if (!is_ws
&& dst
->state
== WORD_WS_DELIMITED
)
185 dst
->state
= WORD_IDLE
;
186 /* Reserve space while the stack string is empty. */
187 appendarglist(dst
->list
, NULL
);
192 #define NEXTWORD(c, flag, p, dstlist) p = nextword(c, flag, p, dstlist)
195 stputs_split(const char *data
, const char *syntax
, int flag
, char *p
,
196 struct worddest
*dst
)
201 ifs
= ifsset() ? ifsval() : " \t\n";
205 if (strchr(ifs
, c
) != NULL
) {
206 NEXTWORD(c
, flag
, p
, dst
);
209 if (flag
& EXP_GLOB
&& syntax
[(int)c
] == CCTL
)
215 #define STPUTS_SPLIT(data, syntax, flag, p, dst) p = stputs_split((data), syntax, flag, p, dst)
218 * Perform expansions on an argument, placing the resulting list of arguments
219 * in arglist. Parameter expansion, command substitution and arithmetic
220 * expansion are always performed; additional expansions can be requested
222 * The result is left in the stack string.
223 * When arglist is NULL, perform here document expansion.
225 * When doing something that may cause this to be re-entered, make sure
226 * the stack string is empty via grabstackstr() and do not assume expdest
230 expandarg(union node
*arg
, struct arglist
*arglist
, int flag
)
232 struct worddest exparg
;
233 struct nodelist
*argbackq
;
237 argbackq
= arg
->narg
.backquote
;
238 exparg
.list
= arglist
;
239 exparg
.state
= WORD_IDLE
;
240 STARTSTACKSTR(expdest
);
241 argstr(arg
->narg
.text
, &argbackq
, flag
, &exparg
);
242 if (arglist
== NULL
) {
243 STACKSTRNUL(expdest
);
244 return; /* here document expanded */
246 if ((flag
& EXP_SPLIT
) == 0 || expdest
!= stackblock() ||
247 exparg
.state
== WORD_QUOTEMARK
) {
248 STPUTC('\0', expdest
);
249 if (flag
& EXP_SPLIT
) {
251 expandmeta(grabstackstr(expdest
), exparg
.list
);
253 appendarglist(exparg
.list
, grabstackstr(expdest
));
256 if ((flag
& EXP_SPLIT
) == 0)
257 appendarglist(arglist
, grabstackstr(expdest
));
263 * Perform parameter expansion, command substitution and arithmetic
264 * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE.
265 * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'.
266 * This is used to expand word in ${var+word} etc.
267 * If EXP_GLOB or EXP_CASE are set, keep and/or generate CTLESC
268 * characters to allow for further processing.
270 * If EXP_SPLIT is set, dst receives any complete words produced.
273 argstr(const char *p
, struct nodelist
**restrict argbackq
, int flag
,
274 struct worddest
*dst
)
277 int quotes
= flag
& (EXP_GLOB
| EXP_CASE
); /* do CTLESC */
282 split_lit
= flag
& EXP_SPLIT_LIT
;
283 lit_quoted
= flag
& EXP_LIT_QUOTED
;
284 flag
&= ~(EXP_SPLIT_LIT
| EXP_LIT_QUOTED
);
285 if (*p
== '~' && (flag
& (EXP_TILDE
| EXP_VARTILDE
)))
286 p
= exptilde(p
, flag
);
288 CHECKSTRSPACE(2, expdest
);
297 /* "$@" syntax adherence hack */
298 if (p
[0] == CTLVAR
&& (p
[1] & VSQUOTE
) != 0 &&
299 p
[2] == '@' && p
[3] == '=')
301 if ((flag
& EXP_SPLIT
) != 0 && expdest
== stackblock())
302 dst
->state
= WORD_QUOTEMARK
;
309 if (split_lit
&& !lit_quoted
&&
310 strchr(ifsset() ? ifsval() : " \t\n", c
) != NULL
) {
311 NEXTWORD(c
, flag
, expdest
, dst
);
315 USTPUTC(CTLESC
, expdest
);
319 p
= evalvar(p
, argbackq
, flag
, dst
);
322 case CTLBACKQ
|CTLQUOTE
:
323 expbackq((*argbackq
)->n
, c
& CTLQUOTE
, flag
, dst
);
324 *argbackq
= (*argbackq
)->next
;
327 p
= expari(p
, argbackq
, flag
, dst
);
332 * sort of a hack - expand tildes in variable
333 * assignments (after the first '=' and after ':'s).
335 if (split_lit
&& !lit_quoted
&&
336 strchr(ifsset() ? ifsval() : " \t\n", c
) != NULL
) {
337 NEXTWORD(c
, flag
, expdest
, dst
);
341 if (flag
& EXP_VARTILDE
&& *p
== '~' &&
342 (c
!= '=' || firsteq
)) {
345 p
= exptilde(p
, flag
);
349 if (split_lit
&& !lit_quoted
&&
350 strchr(ifsset() ? ifsval() : " \t\n", c
) != NULL
) {
351 NEXTWORD(c
, flag
, expdest
, dst
);
360 * Perform tilde expansion, placing the result in the stack string and
361 * returning the next position in the input string to process.
364 exptilde(const char *p
, int flag
)
367 const char *startp
= p
;
376 case CTLESC
: /* This means CTL* are always considered quoted. */
379 case CTLBACKQ
| CTLQUOTE
:
385 if ((flag
& EXP_VARTILDE
) == 0)
391 len
= p
- startp
- 1;
392 STPUTBIN(startp
+ 1, len
, expdest
);
393 STACKSTRNUL(expdest
);
394 user
= expdest
- len
;
396 home
= lookupvar("HOME");
399 home
= pw
!= NULL
? pw
->pw_dir
: NULL
;
401 STADJUST(-len
, expdest
);
402 if (home
== NULL
|| *home
== '\0')
404 strtodest(home
, flag
, VSNORMAL
, 1, NULL
);
413 * Expand arithmetic expression.
416 expari(const char *p
, struct nodelist
**restrict argbackq
, int flag
,
417 struct worddest
*dst
)
425 quoted
= *p
++ == '"';
426 begoff
= expdest
- stackblock();
427 p
= argstr(p
, argbackq
, 0, NULL
);
428 STPUTC('\0', expdest
);
429 start
= stackblock() + begoff
;
431 q
= grabstackstr(expdest
);
432 result
= arith(start
);
433 ungrabstackstr(q
, expdest
);
435 start
= stackblock() + begoff
;
436 adj
= start
- expdest
;
437 STADJUST(adj
, expdest
);
439 CHECKSTRSPACE((int)(DIGITS(result
) + 1), expdest
);
440 fmtstr(expdest
, DIGITS(result
), ARITH_FORMAT_STR
, result
);
441 adj
= strlen(expdest
);
442 STADJUST(adj
, expdest
);
444 * If this is quoted, a '-' must not indicate a range in [...].
445 * If this is not quoted, splitting may occur.
448 result
< 0 && begoff
> 1 && flag
& (EXP_GLOB
| EXP_CASE
) :
450 reprocess(expdest
- adj
- stackblock(), flag
, VSNORMAL
, quoted
,
457 * Perform command substitution.
460 expbackq(union node
*cmd
, int quoted
, int flag
, struct worddest
*dst
)
466 char *dest
= expdest
;
468 char const *syntax
= quoted
? DQSYNTAX
: BASESYNTAX
;
469 int quotes
= flag
& (EXP_GLOB
| EXP_CASE
);
475 p
= grabstackstr(dest
);
476 evalbackcmd(cmd
, &in
);
477 ungrabstackstr(p
, dest
);
480 startloc
= dest
- stackblock();
482 if (!quoted
&& flag
& EXP_SPLIT
)
483 ifs
= ifsset() ? ifsval() : " \t\n";
486 /* Remove trailing newlines */
488 if (--in
.nleft
< 0) {
491 while ((i
= read(in
.fd
, buf
, sizeof buf
)) < 0 && errno
== EINTR
)
493 TRACE(("expbackq: read returns %d\n", i
));
502 if (nnl
> 0 && lastc
!= '\n') {
503 NEXTWORD('\n', flag
, dest
, dst
);
506 if (strchr(ifs
, lastc
) != NULL
) {
510 NEXTWORD(lastc
, flag
, dest
, dst
);
512 CHECKSTRSPACE(2, dest
);
513 if (quotes
&& syntax
[(int)lastc
] == CCTL
)
514 USTPUTC(CTLESC
, dest
);
515 USTPUTC(lastc
, dest
);
518 while (dest
> stackblock() + startloc
&& STTOPC(dest
) == '\n')
526 p
= grabstackstr(dest
);
527 exitstatus
= waitforjob(in
.jp
, (int *)NULL
);
528 ungrabstackstr(p
, dest
);
530 TRACE(("expbackq: done\n"));
538 recordleft(const char *str
, const char *loc
, char *startp
)
542 amount
= ((str
- 1) - (loc
- startp
)) - expdest
;
543 STADJUST(amount
, expdest
);
544 while (loc
!= str
- 1)
549 subevalvar_trim(const char *p
, struct nodelist
**restrict argbackq
, int strloc
,
550 int subtype
, int startloc
)
558 p
= argstr(p
, argbackq
, EXP_CASE
| EXP_TILDE
, NULL
);
559 STACKSTRNUL(expdest
);
560 startp
= stackblock() + startloc
;
561 str
= stackblock() + strloc
;
565 for (loc
= startp
; loc
< str
; loc
++) {
568 if (patmatch(str
, startp
)) {
570 recordleft(str
, loc
, startp
);
578 for (loc
= str
- 1; loc
>= startp
;) {
581 if (patmatch(str
, startp
)) {
583 recordleft(str
, loc
, startp
);
592 for (loc
= str
- 1; loc
>= startp
;) {
593 if (patmatch(str
, loc
)) {
594 amount
= loc
- expdest
;
595 STADJUST(amount
, expdest
);
603 for (loc
= startp
; loc
< str
- 1; loc
++) {
604 if (patmatch(str
, loc
)) {
605 amount
= loc
- expdest
;
606 STADJUST(amount
, expdest
);
616 amount
= (expdest
- stackblock() - strloc
) + 1;
617 STADJUST(-amount
, expdest
);
623 subevalvar_misc(const char *p
, struct nodelist
**restrict argbackq
,
624 const char *var
, int subtype
, int startloc
, int varflags
)
629 p
= argstr(p
, argbackq
, EXP_TILDE
, NULL
);
630 STACKSTRNUL(expdest
);
631 startp
= stackblock() + startloc
;
635 setvar(var
, startp
, 0);
636 amount
= startp
- expdest
;
637 STADJUST(amount
, expdest
);
641 if (*p
!= CTLENDVAR
) {
642 outfmt(out2
, "%s\n", startp
);
645 error("%.*s: parameter %snot set", (int)(p
- var
- 1),
646 var
, (varflags
& VSNUL
) ? "null or " : "");
655 * Expand a variable, and return a pointer to the next character in the
660 evalvar(const char *p
, struct nodelist
**restrict argbackq
, int flag
,
661 struct worddest
*dst
)
676 varflags
= (unsigned char)*p
++;
677 subtype
= varflags
& VSTYPE
;
682 p
= strchr(p
, '=') + 1;
683 if (varflags
& VSLINENO
) {
687 } else if (special
) {
688 set
= varisset(var
, varflags
& VSNUL
);
691 val
= bltinlookup(var
, 1);
692 if (val
== NULL
|| ((varflags
& VSNUL
) && val
[0] == '\0')) {
699 startloc
= expdest
- stackblock();
700 if (!set
&& uflag
&& *var
!= '@' && *var
!= '*') {
708 error("%.*s: parameter not set", (int)(p
- var
- 1),
712 if (set
&& subtype
!= VSPLUS
) {
713 /* insert the value of the variable */
715 if (varflags
& VSLINENO
) {
716 if (p
- var
> (ptrdiff_t)sizeof(buf
))
718 memcpy(buf
, var
, p
- var
- 1);
719 buf
[p
- var
- 1] = '\0';
720 strtodest(buf
, flag
, subtype
,
721 varflags
& VSQUOTE
, dst
);
723 varvalue(var
, varflags
& VSQUOTE
, subtype
, flag
,
725 if (subtype
== VSLENGTH
) {
726 varlenb
= expdest
- stackblock() - startloc
;
729 val
= stackblock() + startloc
;
730 for (;val
!= expdest
; val
++)
731 if ((*val
& 0xC0) == 0x80)
734 STADJUST(-varlenb
, expdest
);
737 if (subtype
== VSLENGTH
) {
740 (*val
& 0xC0) != 0x80)
744 strtodest(val
, flag
, subtype
,
745 varflags
& VSQUOTE
, dst
);
749 if (subtype
== VSPLUS
)
755 strtodest(buf
, flag
, VSNORMAL
, varflags
& VSQUOTE
, dst
);
764 return argstr(p
, argbackq
,
765 flag
| (flag
& EXP_SPLIT
? EXP_SPLIT_LIT
: 0) |
766 (varflags
& VSQUOTE
? EXP_LIT_QUOTED
: 0), dst
);
777 * Terminate the string and start recording the pattern
780 STPUTC('\0', expdest
);
781 patloc
= expdest
- stackblock();
782 p
= subevalvar_trim(p
, argbackq
, patloc
, subtype
, startloc
);
783 reprocess(startloc
, flag
, VSNORMAL
, varflags
& VSQUOTE
, dst
);
784 if (flag
& EXP_SPLIT
&& *var
== '@' && varflags
& VSQUOTE
)
785 dst
->state
= WORD_QUOTEMARK
;
791 p
= subevalvar_misc(p
, argbackq
, var
, subtype
,
793 /* assert(subtype == VSASSIGN); */
794 val
= lookupvar(var
);
795 strtodest(val
, flag
, subtype
, varflags
& VSQUOTE
, dst
);
802 error("${%.*s%s}: Bad substitution", c
, var
,
803 (c
> 0 && *p
!= CTLENDVAR
) ? "..." : "");
809 { /* skip to end of alternative */
812 if ((c
= *p
++) == CTLESC
)
814 else if (c
== CTLBACKQ
|| c
== (CTLBACKQ
|CTLQUOTE
))
815 *argbackq
= (*argbackq
)->next
;
816 else if (c
== CTLVAR
) {
817 if ((*p
++ & VSTYPE
) != VSNORMAL
)
819 } else if (c
== CTLENDVAR
) {
831 * Test whether a special or positional parameter is set.
835 varisset(const char *name
, int nulok
)
839 return backgndpidset();
840 else if (*name
== '@' || *name
== '*') {
841 if (*shellparam
.p
== NULL
)
847 for (av
= shellparam
.p
; *av
; av
++)
852 } else if (is_digit(*name
)) {
857 num
= strtol(name
, NULL
, 10);
858 if (errno
!= 0 || num
> shellparam
.nparam
)
864 ap
= shellparam
.p
[num
- 1];
866 if (nulok
&& (ap
== NULL
|| *ap
== '\0'))
873 strtodest(const char *p
, int flag
, int subtype
, int quoted
,
874 struct worddest
*dst
)
876 if (subtype
== VSLENGTH
|| subtype
== VSTRIMLEFT
||
877 subtype
== VSTRIMLEFTMAX
|| subtype
== VSTRIMRIGHT
||
878 subtype
== VSTRIMRIGHTMAX
)
880 else if (flag
& EXP_SPLIT
&& !quoted
&& dst
!= NULL
)
881 STPUTS_SPLIT(p
, BASESYNTAX
, flag
, expdest
, dst
);
882 else if (flag
& (EXP_GLOB
| EXP_CASE
))
883 STPUTS_QUOTES(p
, quoted
? DQSYNTAX
: BASESYNTAX
, expdest
);
889 reprocess(int startloc
, int flag
, int subtype
, int quoted
,
890 struct worddest
*dst
)
892 static char *buf
= NULL
;
893 static size_t buflen
= 0;
895 size_t len
, zpos
, zlen
;
897 startp
= stackblock() + startloc
;
898 len
= expdest
- startp
;
899 if (len
>= SIZE_MAX
/ 2)
908 while (len
>= buflen
)
911 buf
= ckmalloc(buflen
);
913 memcpy(buf
, startp
, len
);
915 STADJUST(-len
, expdest
);
917 zlen
= strlen(buf
+ zpos
);
918 strtodest(buf
+ zpos
, flag
, subtype
, quoted
, dst
);
922 if (flag
& EXP_SPLIT
&& (quoted
|| (zlen
> 0 && zpos
< len
)))
923 NEXTWORD('\0', flag
, expdest
, dst
);
928 * Add the value of a special or positional parameter to the stack string.
932 varvalue(const char *name
, int quoted
, int subtype
, int flag
,
933 struct worddest
*dst
)
941 char buf
[(NSHORTOPTS
> 10 ? NSHORTOPTS
: 10) + 1];
943 if (subtype
== VSLENGTH
)
945 splitlater
= subtype
== VSTRIMLEFT
|| subtype
== VSTRIMLEFTMAX
||
946 subtype
== VSTRIMRIGHT
|| subtype
== VSTRIMRIGHTMAX
;
956 num
= shellparam
.nparam
;
959 num
= backgndpidval();
963 for (i
= 0 ; i
< NSHORTOPTS
; i
++) {
968 strtodest(buf
, flag
, subtype
, quoted
, dst
);
971 if (flag
& EXP_SPLIT
&& quoted
) {
972 for (ap
= shellparam
.p
; (p
= *ap
++) != NULL
; ) {
973 strtodest(p
, flag
, subtype
, quoted
, dst
);
976 STPUTC('\0', expdest
);
978 NEXTWORD('\0', flag
, expdest
,
982 if (shellparam
.nparam
> 0)
983 dst
->state
= WORD_QUOTEMARK
;
989 sep
[0] = ifsval()[0];
993 for (ap
= shellparam
.p
; (p
= *ap
++) != NULL
; ) {
994 strtodest(p
, flag
, subtype
, quoted
, dst
);
998 strtodest(sep
, flag
, subtype
, quoted
, dst
);
999 else if (flag
& EXP_SPLIT
&& !quoted
&& **ap
!= '\0') {
1001 STPUTC('\0', expdest
);
1003 NEXTWORD('\0', flag
, expdest
, dst
);
1008 if (is_digit(*name
)) {
1012 else if (num
> 0 && num
<= shellparam
.nparam
)
1013 p
= shellparam
.p
[num
- 1];
1016 strtodest(p
, flag
, subtype
, quoted
, dst
);
1021 strtodest(buf
, flag
, subtype
, quoted
, dst
);
1026 static char expdir
[PATH_MAX
];
1027 #define expdir_end (expdir + sizeof(expdir))
1030 * Perform pathname generation and remove control characters.
1031 * At this point, the only control characters should be CTLESC.
1032 * The results are stored in the list dstlist.
1035 expandmeta(char *pattern
, struct arglist
*dstlist
)
1041 firstmatch
= dstlist
->count
;
1043 for (; (c
= *p
) != '\0'; p
++) {
1044 /* fast check for meta chars */
1045 if (c
== '*' || c
== '?' || c
== '[') {
1047 expmeta(expdir
, pattern
, dstlist
);
1052 if (dstlist
->count
== firstmatch
) {
1057 appendarglist(dstlist
, pattern
);
1059 qsort(&dstlist
->args
[firstmatch
],
1060 dstlist
->count
- firstmatch
,
1061 sizeof(dstlist
->args
[0]), expsortcmp
);
1067 * Do metacharacter (i.e. *, ?, [...]) expansion.
1071 expmeta(char *enddir
, char *name
, struct arglist
*arglist
)
1088 for (p
= name
; esc
= 0, *p
; p
+= esc
+ 1) {
1089 if (*p
== '*' || *p
== '?')
1091 else if (*p
== '[') {
1093 if (*q
== '!' || *q
== '^')
1098 if (*q
== '/' || *q
== '\0')
1105 } else if (*p
== '\0')
1110 if (p
[esc
] == '/') {
1113 start
= p
+ esc
+ 1;
1117 if (metaflag
== 0) { /* we've reached the end of the file name */
1118 if (enddir
!= expdir
)
1120 for (p
= name
; ; p
++) {
1126 if (enddir
== expdir_end
)
1129 if (metaflag
== 0 || lstat(expdir
, &statb
) >= 0)
1130 appendarglist(arglist
, stsavestr(expdir
));
1133 endname
= name
+ (p
- name
);
1134 if (start
!= name
) {
1140 if (enddir
== expdir_end
)
1144 if (enddir
== expdir
) {
1146 } else if (enddir
== expdir
+ 1 && *expdir
== '/') {
1152 if ((dirp
= opendir(p
)) == NULL
)
1154 if (enddir
!= expdir
)
1156 if (*endname
== 0) {
1169 while (! int_pending() && (dp
= readdir(dirp
)) != NULL
) {
1170 if (dp
->d_name
[0] == '.' && ! matchdot
)
1172 if (patmatch(start
, dp
->d_name
)) {
1173 namlen
= dp
->d_namlen
;
1174 if (enddir
+ namlen
+ 1 > expdir_end
)
1176 memcpy(enddir
, dp
->d_name
, namlen
+ 1);
1178 appendarglist(arglist
, stsavestr(expdir
));
1180 if (dp
->d_type
!= DT_UNKNOWN
&&
1181 dp
->d_type
!= DT_DIR
&&
1182 dp
->d_type
!= DT_LNK
)
1184 if (enddir
+ namlen
+ 2 > expdir_end
)
1186 enddir
[namlen
] = '/';
1187 enddir
[namlen
+ 1] = '\0';
1188 expmeta(enddir
+ namlen
+ 1, endname
, arglist
);
1194 endname
[-esc
- 1] = esc
? CTLESC
: '/';
1199 expsortcmp(const void *p1
, const void *p2
)
1201 const char *s1
= *(const char * const *)p1
;
1202 const char *s2
= *(const char * const *)p2
;
1204 return (strcoll(s1
, s2
));
1210 get_wc(const char **p
)
1215 chrlen
= mbtowc(&c
, *p
, 4);
1218 else if (chrlen
== -1)
1227 * See if a character matches a character class, starting at the first colon
1229 * If a valid character class is recognized, a pointer to the next character
1230 * after the final closing bracket is stored into *end, otherwise a null
1231 * pointer is stored into *end.
1234 match_charclass(const char *p
, wchar_t chr
, const char **end
)
1237 const char *nameend
;
1242 nameend
= strstr(p
, ":]");
1243 if (nameend
== NULL
|| (size_t)(nameend
- p
) >= sizeof(name
) ||
1246 memcpy(name
, p
, nameend
- p
);
1247 name
[nameend
- p
] = '\0';
1249 cclass
= wctype(name
);
1250 /* An unknown class matches nothing but is valid nevertheless. */
1253 return iswctype(chr
, cclass
);
1258 * Returns true if the pattern matches the string.
1262 patmatch(const char *pattern
, const char *string
)
1264 const char *p
, *q
, *end
;
1265 const char *bt_p
, *bt_q
;
1289 * A '?' does not match invalid UTF-8 but a
1290 * '*' does, so backtrack.
1302 * If the pattern ends here, we know the string
1303 * matches without needing to look at the rest of it.
1308 * First try the shortest match for the '*' that
1309 * could work. We can forget any earlier '*' since
1310 * there is no way having it match more characters
1311 * can help us, given that we are already here.
1317 const char *savep
, *saveq
;
1321 savep
= p
, saveq
= q
;
1323 if (*p
== '!' || *p
== '^') {
1335 chr
= (unsigned char)*q
++;
1339 p
= savep
, q
= saveq
;
1343 if (c
== '[' && *p
== ':') {
1344 found
|= match_charclass(p
, chr
, &end
);
1350 if (localeisutf8
&& c
& 0x80) {
1353 if (wc
== 0) /* bad utf-8 */
1356 wc
= (unsigned char)c
;
1357 if (*p
== '-' && p
[1] != ']') {
1363 if (wc2
== 0) /* bad utf-8 */
1366 wc2
= (unsigned char)*p
++;
1367 if ( collate_range_cmp(chr
, wc
) >= 0
1368 && collate_range_cmp(chr
, wc2
) <= 0
1375 } while ((c
= *p
++) != ']');
1376 if (found
== invert
)
1387 * If we have a mismatch (other than hitting the end
1388 * of the string), go back to the last '*' seen and
1389 * have it match one additional character.
1406 * Remove any CTLESC and CTLQUOTEMARK characters from a string.
1410 rmescapes(char *str
)
1415 while (*p
!= CTLESC
&& *p
!= CTLQUOTEMARK
&& *p
!= CTLQUOTEEND
) {
1421 if (*p
== CTLQUOTEMARK
|| *p
== CTLQUOTEEND
) {
1435 * See if a pattern matches in a case statement.
1439 casematch(union node
*pattern
, const char *val
)
1441 struct stackmark smark
;
1442 struct nodelist
*argbackq
;
1446 setstackmark(&smark
);
1447 argbackq
= pattern
->narg
.backquote
;
1448 STARTSTACKSTR(expdest
);
1449 argstr(pattern
->narg
.text
, &argbackq
, EXP_TILDE
| EXP_CASE
, NULL
);
1450 STPUTC('\0', expdest
);
1451 p
= grabstackstr(expdest
);
1452 result
= patmatch(p
, val
);
1453 popstackmark(&smark
);
1462 cvtnum(int num
, char *buf
)
1466 char *p
= temp
+ 31;
1471 *--p
= num
% 10 + '0';
1472 } while ((num
/= 10) != 0);
1477 memcpy(buf
, p
, temp
+ 32 - p
);
1481 * Do most of the work for wordexp(3).
1485 wordexpcmd(int argc
, char **argv
)
1490 out1fmt("%08x", argc
- 1);
1491 for (i
= 1, len
= 0; i
< argc
; i
++)
1492 len
+= strlen(argv
[i
]);
1493 out1fmt("%08x", (int)len
);
1494 for (i
= 1; i
< argc
; i
++)
1495 outbin(argv
[i
], strlen(argv
[i
]) + 1, out1
);
1500 * Do most of the work for wordexp(3), new version.
1504 freebsd_wordexpcmd(int argc __unused
, char **argv __unused
)
1506 struct arglist arglist
;
1507 union node
*args
, *n
;
1514 while ((ch
= nextopt("f:p")) != '\0') {
1517 fd
= number(shoptarg
);
1524 if (*argptr
!= NULL
)
1525 error("wrong number of arguments");
1527 error("missing fd");
1531 args
= parsewordexp();
1532 popfile(); /* will also close fd */
1534 for (n
= args
; n
!= NULL
; n
= n
->narg
.next
) {
1535 if (n
->narg
.backquote
!= NULL
) {
1536 outcslow('C', out1
);
1537 error("command substitution disabled");
1540 outcslow(' ', out1
);
1541 emptyarglist(&arglist
);
1542 for (n
= args
; n
!= NULL
; n
= n
->narg
.next
)
1543 expandarg(n
, &arglist
, EXP_FULL
| EXP_TILDE
);
1544 for (i
= 0, len
= 0; i
< arglist
.count
; i
++)
1545 len
+= strlen(arglist
.args
[i
]);
1546 out1fmt("%016x %016zx", arglist
.count
, len
);
1547 for (i
= 0; i
< arglist
.count
; i
++)
1548 outbin(arglist
.args
[i
], strlen(arglist
.args
[i
]) + 1, out1
);