1 /* $NetBSD: eval.c,v 1.15 2013/10/18 19:53:34 christos Exp $ */
4 * Expansion - quoting, separation, substitution, globbing
9 __RCSID("$NetBSD: eval.c,v 1.15 2013/10/18 19:53:34 christos Exp $");
22 * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
23 * second pass: alternation ({,}), filename expansion (*?[]).
26 /* expansion generator state */
27 typedef struct Expand
{
28 /* int type; */ /* see expand() */
29 const char *str
; /* string */
31 const char **strv
;/* string[] */
32 struct shf
*shf
;/* file */
34 struct tbl
*var
; /* variable in ${var..} */
35 short split
; /* split "$@" / call waitlast $() */
38 #define XBASE 0 /* scanning original */
39 #define XSUB 1 /* expanding ${} string */
40 #define XARGSEP 2 /* ifs0 between "$*" */
41 #define XARG 3 /* expanding $*, $@ */
42 #define XCOM 4 /* expanding $() */
43 #define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */
45 /* States used for field splitting */
46 #define IFS_WORD 0 /* word has chars (or quotes) */
47 #define IFS_WS 1 /* have seen IFS white-space */
48 #define IFS_NWS 2 /* have seen IFS non-white-space */
50 static int varsub
ARGS((Expand
*xp
, char *sp
, char *word
, int *stypep
, int *slenp
));
51 static int comsub
ARGS((Expand
*xp
, char *cp
));
52 static char *trimsub
ARGS((char *str
, char *pat
, int how
));
53 static void glob
ARGS((char *cp
, XPtrV
*wp
, int markdirs
));
54 static void globit
ARGS((XString
*xs
, char **xpp
, char *sp
, XPtrV
*wp
,
56 static char *maybe_expand_tilde
ARGS((char *p
, XString
*dsp
, char **dpp
,
58 static char *tilde
ARGS((char *acp
));
59 static char *homedir
ARGS((char *name
));
61 static void alt_expand
ARGS((XPtrV
*wp
, char *start
, char *exp_start
,
65 /* compile and expand word */
71 struct source
*s
, *sold
;
74 s
= pushs(SWSTR
, ATEMP
);
75 s
->start
= s
->str
= cp
;
77 if (yylex(ONEWORD
) != LWORD
)
78 internal_errorf(1, "substitute");
81 return evalstr(yylval
.cp
, f
);
97 XPput(w
, NULL
); /* space for shell name */
99 XPput(w
, NULL
); /* and space for one arg */
102 expand(*ap
++, &w
, f
);
105 return (char **) XPclose(w
) + 2;
107 return (char **) XPclose(w
) + 1;
123 cp
= (XPsize(w
) == 0) ? null
: (char*) *XPptrv(w
);
129 * expand string - return only one component
130 * used from iosetup to expand redirection files
146 cp
= (char*) *XPptrv(w
);
149 cp
= evalstr(cp
, f
&~DOGLOB
);
156 /* for nested substitution: ${var:=$var2} */
157 typedef struct SubType
{
158 short stype
; /* [=+-?%#] action after expanded word */
159 short base
; /* begin position of expanded word */
160 short f
; /* saved value of f (DOPAT, etc) */
161 struct tbl
*var
; /* variable for ${var..} */
162 short quote
; /* saved value of quote (for ${..[%#]..}) */
163 struct SubType
*prev
; /* old type */
164 struct SubType
*next
; /* poped type (to avoid re-allocating) */
169 char *cp
; /* input word */
170 register XPtrV
*wp
; /* output words */
171 int f
; /* DO* flags */
173 register int UNINITIALIZED(c
);
174 register int type
; /* expansion type */
175 register int quote
= 0; /* quoted */
176 XString ds
; /* destination string */
177 register char *dp
, *sp
; /* dest., source */
178 int fdo
, word
; /* second pass flags; have word */
179 int doblank
; /* field splitting of parameter/command subst */
180 Expand x
; /* expansion variables */
181 SubType st_head
, *st
;
182 int UNINITIALIZED(newlines
); /* For trailing newlines in COMSUB */
183 int saw_eq
, tilde_ok
;
187 x
.split
= 0; /* XXX gcc */
188 x
.str
= NULL
; /* XXX gcc */
189 x
.u
.strv
= NULL
;/* XXX gcc */
191 internal_errorf(1, "expand(NULL)");
192 /* for alias, readonly, set, typeset commands */
193 if ((f
& DOVACHECK
) && is_wdvarassign(cp
)) {
194 f
&= ~(DOVACHECK
|DOBLANK
|DOGLOB
|DOTILDE
);
202 if (Flag(FBRACEEXPAND
) && (f
& DOGLOB
))
204 #endif /* BRACE_EXPAND */
206 Xinit(ds
, dp
, 128, ATEMP
); /* init dest. string */
211 tilde_ok
= (f
& (DOTILDE
|DOASNTILDE
)) ? 1 : 0; /* must be 1/0 */
214 word
= (f
&DOBLANK
) ? IFS_WS
: IFS_WORD
;
215 st_head
.next
= (SubType
*) 0;
222 case XBASE
: /* original prefixed string */
232 quote
|= 2; /* temporary quote */
245 if (f
& DONTRUNCOMMAND
) {
247 *dp
++ = '$'; *dp
++ = '(';
248 while (*sp
!= '\0') {
254 type
= comsub(&x
, sp
);
255 if (type
== XCOM
&& (f
&DOBLANK
))
257 sp
= strchr(sp
, 0) + 1;
264 if (f
& DONTRUNCOMMAND
) {
265 *dp
++ = '$'; *dp
++ = '('; *dp
++ = '(';
266 while (*sp
!= '\0') {
270 *dp
++ = ')'; *dp
++ = ')';
275 v
.flag
= DEFINED
|ISSET
|INTEGER
;
276 v
.type
= 10; /* not default */
278 v_evaluate(&v
, substitute(sp
, 0),
280 sp
= strchr(sp
, 0) + 1;
281 for (p
= str_val(&v
); *p
; ) {
287 case OSUBST
: /* ${{#}var{:}[=+-?#%]word} */
289 * OSUBST [{x] plain-variable-part \0
290 * compiled-word-part CSUBST [}x]
291 * This is were all syntax checking gets done...
294 char *varname
= ++sp
; /* skip the { or x (}) */
298 slen
= -1; /* XXX gcc */
299 sp
= strchr(sp
, '\0') + 1; /* skip variable */
300 type
= varsub(&x
, varname
, sp
, &stype
, &slen
);
305 end
= (char *) wdscan(sp
, CSUBST
);
306 /* ({) the } or x is already skipped */
309 str
= snptreef((char *) 0, 64, "%S",
312 errorf("%s: bad substitution", str
);
317 if (type
== XBASE
) { /* expand? */
321 newst
= (SubType
*) alloc(
322 sizeof(SubType
), ATEMP
);
323 newst
->next
= (SubType
*) 0;
329 st
->base
= Xsavepos(ds
, dp
);
333 /* skip qualifier(s) */
336 switch (stype
& 0x7f) {
339 /* ! DOBLANK,DOBRACE_,DOTILDE */
340 f
= DOPAT
| (f
&DONTRUNCOMMAND
)
343 /* Prepend open pattern (so |
344 * in a trim will work as
351 /* Enabling tilde expansion
353 * non-standard ksh, but is
354 * consistent with rules for
355 * other assignments. Not
356 * sure what POSIX thinks of
358 * Not doing tilde expansion
359 * for integer variables is a
360 * non-POSIX thing - makes
361 * sense though, since ~ is
362 * a arithmetic operator.
364 if (!(x
.var
->flag
& INTEGER
))
365 f
|= DOASNTILDE
|DOTILDE
;
367 /* These will be done after the
368 * value has been assigned.
370 f
&= ~(DOBLANK
|DOGLOB
|DOBRACE_
);
378 /* Enable tilde expansion */
384 sp
= (char *) wdscan(sp
, CSUBST
);
387 case CSUBST
: /* only get here if expanding word */
388 sp
++; /* ({) skip the } or x */
389 tilde_ok
= 0; /* in case of ${unset:-} */
395 switch (st
->stype
&0x7f) {
398 /* Append end-pattern */
399 *dp
++ = MAGIC
; *dp
++ = ')'; *dp
= '\0';
400 dp
= Xrestpos(ds
, dp
, st
->base
);
401 /* Must use st->var since calling
402 * global would break things
405 x
.str
= trimsub(str_val(st
->var
),
413 /* Restore our position and substitute
414 * the value of st->var (may not be
415 * the assigned value in the presence
416 * of integer/right-adj/etc attributes).
418 dp
= Xrestpos(ds
, dp
, st
->base
);
419 /* Must use st->var since calling
420 * global would cause with things
421 * like x[i+=1] to be evaluated twice.
423 /* Note: not exported by FEXPORT
426 /* XXX POSIX says readonly is only
427 * fatal for special builtins (setstr
428 * does readonly check).
430 len
= strlen(dp
) + 1;
432 debunk((char *) alloc(len
, ATEMP
),
435 x
.str
= str_val(st
->var
);
443 char *s
= Xrestpos(ds
, dp
, st
->base
);
445 errorf("%s: %s", st
->var
->name
,
447 "parameter null or not set"
448 : (debunk(s
, s
, strlen(s
) + 1), s
));
455 case OPAT
: /* open pattern: *(foo|bar) */
456 /* Next char is the type of pattern */
461 case SPAT
: /* pattern separator (|) */
466 case CPAT
: /* close pattern */
474 /* Special case for "$@" (and "${foo[@]}") - no
475 * word is generated if $# is 0 (unless there is
476 * other stuff inside the quotes).
481 /* not really correct: x=; "$x$@" should
482 * generate a null argument and
483 * set A; "${@:+}" shouldn't.
485 if (dp
== Xstring(ds
, dp
))
491 if ((c
= *x
.str
++) == 0) {
503 if ((c
= *x
.str
++) == '\0') {
504 /* force null words to be created so
505 * set -- '' 2 ''; foo "$@" will do
508 if (quote
&& x
.split
)
510 if ((x
.str
= *x
.u
.strv
++) == NULL
) {
518 if (quote
&& !x
.split
)
522 if (quote
&& x
.split
) {
523 /* terminate word for "$@" */
531 if (newlines
) { /* Spit out saved nl's */
535 while ((c
= shf_getc(x
.u
.shf
)) == 0 || c
== '\n')
537 newlines
++; /* Save newlines */
538 if (newlines
&& c
!= EOF
) {
539 shf_ungetc(c
, x
.u
.shf
);
548 subst_exstat
= waitlast();
557 /* check for end of word or IFS separation */
558 if (c
== 0 || (!quote
&& (f
& DOBLANK
) && doblank
&& !make_magic
561 /* How words are broken up:
564 * -----------------------------------
565 * IFS_WORD w/WS w/NWS w
566 * IFS_WS -/WS w/NWS -
567 * IFS_NWS -/NWS w/NWS w
568 * (w means generate a word)
569 * Note that IFS_NWS/0 generates a word (at&t ksh
570 * doesn't do this, but POSIX does).
573 || (!ctype(c
, C_IFSWS
) && (c
|| word
== IFS_NWS
)))
581 /* also does globbing */
583 p
+ Xlength(ds
, (dp
- 1)),
584 fdo
| (f
& DOMARKDIRS
));
586 #endif /* BRACE_EXPAND */
588 glob(p
, wp
, f
& DOMARKDIRS
);
589 else if ((f
& DOPAT
) || !(fdo
& DOMAGIC_
))
592 XPput(*wp
, debunk(p
, p
, strlen(p
) + 1));
595 tilde_ok
= (f
& (DOTILDE
|DOASNTILDE
)) ? 1 : 0;
597 Xinit(ds
, dp
, 128, ATEMP
);
602 word
= ctype(c
, C_IFSWS
) ? IFS_WS
: IFS_NWS
;
604 /* age tilde_ok info - ~ code tests second bit */
606 /* mark any special second pass chars */
613 /* For character classes - doesn't hurt
614 * to have magic !,-,]'s outside of
617 if (f
& (DOPAT
| DOGLOB
)) {
626 if (f
& (DOPAT
| DOGLOB
)) {
627 fdo
|= DOMAGIC_
| (f
& DOGLOB
);
635 if ((f
& DOBRACE_
) && (c
== OBRACE
636 || (fdo
& DOBRACE_
)))
638 fdo
|= DOBRACE_
|DOMAGIC_
;
642 #endif /* BRACE_EXPAND */
644 /* Note first unquoted = for ~ */
645 if (!(f
& DOTEMP_
) && !saw_eq
) {
650 case PATHSEP
: /* : */
651 /* Note unquoted : for ~ */
652 if (!(f
& DOTEMP_
) && (f
& DOASNTILDE
))
656 /* tilde_ok is reset whenever
657 * any of ' " $( $(( ${ } are seen.
658 * Note that tilde_ok must be preserved
659 * through the sequence ${A=a=}~
662 && (f
& (DOTILDE
|DOASNTILDE
))
668 p
= maybe_expand_tilde(sp
,
682 quote
&= ~2; /* undo temporary */
686 fdo
|= DOMAGIC_
| (f
& DOGLOB
);
688 } else if (ISMAGIC(c
)) {
692 *dp
++ = c
; /* save output char */
699 * Prepare to generate the string returned by ${} substitution.
702 varsub(xp
, sp
, word
, stypep
, slenp
)
706 int *stypep
; /* becomes qualifier type */
707 int *slenp
; /* " " len (=, :=, etc.) valid iff *stypep != 0 */
710 int state
; /* next state: XBASE, XARG, XSUB, XNULLSUB */
711 int stype
; /* substitution type */
716 if (sp
[0] == '\0') /* Bad variable name */
721 /* ${#var}, string length or array size */
722 if (sp
[0] == '#' && (c
= sp
[1]) != '\0') {
725 /* Can't have any modifiers for ${#...} */
729 /* Check for size of array */
730 if ((p
=strchr(sp
,'[')) && (p
[1]=='*'||p
[1]=='@') && p
[2]==']') {
732 vp
= global(arrayname(sp
));
733 if (vp
->flag
& (ISSET
|ARRAY
))
735 for (; vp
; vp
= vp
->u
.array
)
736 if (vp
->flag
& ISSET
) {
739 c
= n
; /* ksh88/ksh93 go for number, not max index */
740 } else if (c
== '*' || c
== '@')
743 p
= str_val(global(sp
));
747 if (Flag(FNOUNSET
) && c
== 0 && !zero_ok
)
748 errorf("%s: parameter not set", sp
);
749 *stypep
= 0; /* unqualified variable/string substitution */
750 xp
->str
= str_save(ulton((unsigned long)c
, 10), ATEMP
);
754 /* Check for qualifiers in word part */
756 c
= word
[slen
= 0] == CHAR
? word
[1] : 0;
760 c
= word
[slen
+ 0] == CHAR
? word
[slen
+ 1] : 0;
762 if (ctype(c
, C_SUBOP1
)) {
765 } else if (ctype(c
, C_SUBOP2
)) { /* Note: ksh88 allows :%, :%%, etc */
768 if (word
[slen
+ 0] == CHAR
&& c
== word
[slen
+ 1]) {
772 } else if (stype
) /* : is not ok */
774 if (!stype
&& *word
!= CSUBST
)
780 if (c
== '*' || c
== '@') {
781 switch (stype
& 0x7f) {
782 case '=': /* can't assign to a vector */
783 case '%': /* can't trim a vector (yet) */
787 if (e
->loc
->argc
== 0) {
790 state
= c
== '@' ? XNULLSUB
: XSUB
;
792 char **t
= &e
->loc
->argv
[1];
793 xp
->u
.strv
= (void *)(uintptr_t)t
;
794 xp
->str
= *xp
->u
.strv
++;
795 xp
->split
= c
== '@'; /* $@ */
799 if ((p
=strchr(sp
,'[')) && (p
[1]=='*'||p
[1]=='@') && p
[2]==']') {
802 switch (stype
& 0x7f) {
803 case '=': /* can't assign to a vector */
804 case '%': /* can't trim a vector (yet) */
809 vp
= global(arrayname(sp
));
810 for (; vp
; vp
= vp
->u
.array
) {
811 if (!(vp
->flag
&ISSET
))
813 XPput(wv
, str_val(vp
));
815 if (XPsize(wv
) == 0) {
817 state
= p
[1] == '@' ? XNULLSUB
: XSUB
;
821 xp
->u
.strv
= (const char **) XPptrv(wv
);
822 xp
->str
= *xp
->u
.strv
++;
823 xp
->split
= p
[1] == '@'; /* ${foo[@]} */
827 /* Can't assign things like $! or $1 */
828 if ((stype
& 0x7f) == '='
829 && (ctype(*sp
, C_VAR1
) || digit(*sp
)))
831 xp
->var
= global(sp
);
832 xp
->str
= str_val(xp
->var
);
838 /* test the compiler's code generator */
839 if (ctype(c
, C_SUBOP2
) ||
840 (((stype
&0x80) ? *xp
->str
=='\0' : xp
->str
==null
) ? /* undef? */
841 c
== '=' || c
== '-' || c
== '?' : c
== '+'))
842 state
= XBASE
; /* expand word instead of variable value */
843 if (Flag(FNOUNSET
) && xp
->str
== null
844 && (ctype(c
, C_SUBOP2
) || (state
!= XBASE
&& c
!= '+')))
845 errorf("%s: parameter not set", sp
);
850 * Run the command in $(...) and read its output.
858 register struct op
*t
;
861 s
= pushs(SSTRING
, ATEMP
);
862 s
->start
= s
->str
= cp
;
871 if (t
!= NULL
&& t
->type
== TCOM
&& /* $(<file) */
872 *t
->args
== NULL
&& *t
->vars
== NULL
&& t
->ioact
!= NULL
) {
873 register struct ioword
*io
= *t
->ioact
;
876 if ((io
->flag
&IOTYPE
) != IOREAD
)
877 errorf("funny $() command: %s",
878 snptreef((char *) 0, 32, "%R", io
));
879 shf
= shf_open(name
= evalstr(io
->name
, DOTILDE
), O_RDONLY
, 0,
880 SHF_MAPHI
|SHF_CLEXEC
);
882 errorf("%s: cannot open $() input", name
);
883 xp
->split
= 0; /* no waitlast() */
887 shf
= shf_fdopen(pv
[0], SHF_RD
, (struct shf
*) 0);
888 ofd1
= savefd(1, 0); /* fd 1 may be closed... */
890 ksh_dup2(pv
[1], 1, FALSE
);
893 execute(t
, XFORK
|XXCOM
|XPIPEO
);
896 xp
->split
= 1; /* waitlast() */
904 * perform #pattern and %pattern substitution in ${}
908 trimsub(str
, pat
, how
)
913 register char *end
= strchr(str
, 0);
916 switch (how
&0xff) { /* UCHAR_MAX maybe? */
917 case '#': /* shortest at beginning */
918 for (p
= str
; p
<= end
; p
++) {
920 if (gmatch(str
, pat
, FALSE
)) {
927 case '#'|0x80: /* longest match at beginning */
928 for (p
= end
; p
>= str
; p
--) {
930 if (gmatch(str
, pat
, FALSE
)) {
937 case '%': /* shortest match at end */
938 for (p
= end
; p
>= str
; p
--) {
939 if (gmatch(p
, pat
, FALSE
))
940 return str_nsave(str
, p
- str
, ATEMP
);
943 case '%'|0x80: /* longest match at end */
944 for (p
= str
; p
<= end
; p
++) {
945 if (gmatch(p
, pat
, FALSE
))
946 return str_nsave(str
, p
- str
, ATEMP
);
951 return str
; /* no match, return string */
956 * Name derived from V6's /etc/glob, the program that expanded filenames.
959 /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */
961 glob(cp
, wp
, markdirs
)
966 int oldsize
= XPsize(*wp
);
968 if (glob_str(cp
, wp
, markdirs
) == 0)
969 XPput(*wp
, debunk(cp
, cp
, strlen(cp
) + 1));
971 qsortp(XPptrv(*wp
) + oldsize
, (size_t)(XPsize(*wp
) - oldsize
),
976 #define GF_EXCHECK BIT(0) /* do existence check on file */
977 #define GF_GLOBBED BIT(1) /* some globbing has been done */
978 #define GF_MARKDIR BIT(2) /* add trailing / to directories */
980 /* Apply file globbing to cp and store the matching files in wp. Returns
981 * the number of matches found.
984 glob_str(cp
, wp
, markdirs
)
989 int oldsize
= XPsize(*wp
);
993 Xinit(xs
, xp
, 256, ATEMP
);
994 globit(&xs
, &xp
, cp
, wp
, markdirs
? GF_MARKDIR
: GF_NONE
);
997 return XPsize(*wp
) - oldsize
;
1001 globit(xs
, xpp
, sp
, wp
, check
)
1002 XString
*xs
; /* dest string */
1003 char **xpp
; /* ptr to dest end */
1004 char *sp
; /* source path */
1005 register XPtrV
*wp
; /* output list */
1006 int check
; /* GF_* flags */
1008 register char *np
; /* next source component */
1013 /* This to allow long expansions to be interrupted */
1016 if (sp
== NULL
) { /* end of source path */
1017 /* We only need to check if the file exists if a pattern
1018 * is followed by a non-pattern (eg, foo*x/bar; no check
1019 * is needed for foo* since the match must exist) or if
1020 * any patterns were expanded and the markdirs option is set.
1021 * Symlinks make things a bit tricky...
1023 if ((check
& GF_EXCHECK
)
1024 || ((check
& GF_MARKDIR
) && (check
& GF_GLOBBED
)))
1026 #define stat_check() (stat_done ? stat_done : \
1027 (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
1029 struct stat lstatb
, statb
;
1030 int stat_done
= 0; /* -1: failed, 1 ok */
1032 if (lstat(Xstring(*xs
, xp
), &lstatb
) < 0)
1034 /* special case for systems which strip trailing
1035 * slashes from regular files (eg, /etc/passwd/).
1036 * SunOS 4.1.3 does this...
1038 if ((check
& GF_EXCHECK
) && xp
> Xstring(*xs
, xp
)
1039 && ISDIRSEP(xp
[-1]) && !S_ISDIR(lstatb
.st_mode
)
1041 && (!S_ISLNK(lstatb
.st_mode
)
1043 || !S_ISDIR(statb
.st_mode
))
1044 #endif /* S_ISLNK */
1047 /* Possibly tack on a trailing / if there isn't already
1048 * one and if the file is a directory or a symlink to a
1051 if (((check
& GF_MARKDIR
) && (check
& GF_GLOBBED
))
1052 && xp
> Xstring(*xs
, xp
) && !ISDIRSEP(xp
[-1])
1053 && (S_ISDIR(lstatb
.st_mode
)
1055 || (S_ISLNK(lstatb
.st_mode
)
1057 && S_ISDIR(statb
.st_mode
))
1058 #endif /* S_ISLNK */
1065 #ifdef OS2 /* Done this way to avoid bug in gcc 2.7.2... */
1066 /* Ugly kludge required for command
1067 * completion - see how search_access()
1068 * is implemented for OS/2...
1070 # define KLUDGE_VAL 4
1072 # define KLUDGE_VAL 0
1074 XPput(*wp
, str_nsave(Xstring(*xs
, xp
), Xlength(*xs
, xp
)
1075 + KLUDGE_VAL
, ATEMP
));
1079 if (xp
> Xstring(*xs
, xp
))
1081 while (ISDIRSEP(*sp
)) {
1085 np
= ksh_strchr_dirsep(sp
);
1088 odirsep
= *np
; /* don't assume DIRSEP, can be multiple kinds */
1091 odirsep
= '\0'; /* keep gcc quiet */
1092 se
= sp
+ strlen(sp
);
1096 /* Check if sp needs globbing - done to avoid pattern checks for strings
1097 * containing MAGIC characters, open ['s without the matching close ],
1098 * etc. (otherwise opendir() will be called which may fail because the
1099 * directory isn't readable - if no globbing is needed, only execute
1100 * permission should be required (as per POSIX)).
1102 if (!has_globbing(sp
, se
)) {
1103 XcheckN(*xs
, xp
, se
- sp
+ 1);
1104 debunk(xp
, sp
, Xnleft(*xs
, xp
));
1107 globit(xs
, xpp
, np
, wp
, check
);
1115 /* xp = *xpp; copy_non_glob() may have re-alloc'd xs */
1117 prefix_len
= Xlength(*xs
, xp
);
1118 dirp
= ksh_opendir(prefix_len
? Xstring(*xs
, xp
) : ".");
1121 while ((d
= readdir(dirp
)) != NULL
) {
1123 if ((*name
== '.' && *sp
!= '.')
1124 || !gmatch(name
, sp
, TRUE
))
1127 len
= NLENGTH(d
) + 1;
1128 XcheckN(*xs
, xp
, len
);
1129 memcpy(xp
, name
, len
);
1130 *xpp
= xp
+ len
- 1;
1131 globit(xs
, xpp
, np
, wp
,
1132 (check
& GF_MARKDIR
) | GF_GLOBBED
1133 | (np
? GF_EXCHECK
: GF_NONE
));
1134 xp
= Xstring(*xs
, xp
) + prefix_len
;
1145 /* Check if p contains something that needs globbing; if it does, 0 is
1146 * returned; if not, p is copied into xs/xp after stripping any MAGICs
1148 static int copy_non_glob
ARGS((XString
*xs
, char **xpp
, char *p
));
1150 copy_non_glob(xs
, xpp
, p
)
1156 int len
= strlen(p
);
1158 XcheckN(*xs
, *xpp
, len
);
1164 if (c
== '*' || c
== '?')
1169 if (ISMAGIC(*q
) && q
[1] == NOT
)
1171 if (ISMAGIC(*q
) && q
[1] == ']')
1174 if (ISMAGIC(*q
) && *++q
== ']')
1176 /* pass a literal [ through */
1178 /* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */
1188 /* remove MAGIC from string */
1190 debunk(dp
, sp
, dlen
)
1197 if ((s
= strchr(sp
, MAGIC
))) {
1198 if (s
- sp
>= (ptrdiff_t)dlen
)
1200 memcpy(dp
, sp
, s
- sp
);
1201 for (d
= dp
+ (s
- sp
); *s
&& (d
- dp
< (ptrdiff_t)dlen
); s
++)
1202 if (!ISMAGIC(*s
) || !(*++s
& 0x80)
1203 || !strchr("*+?@! ", *s
& 0x7f))
1206 /* extended pattern operators: *+?@! */
1207 if ((*s
& 0x7f) != ' ')
1209 if (d
- dp
< (ptrdiff_t)dlen
)
1213 } else if (dp
!= sp
)
1214 strlcpy(dp
, sp
, dlen
);
1218 /* Check if p is an unquoted name, possibly followed by a / or :. If so
1219 * puts the expanded version in *dcp,dp and returns a pointer in p just
1220 * past the name, otherwise returns 0.
1223 maybe_expand_tilde(p
, dsp
, dpp
, isassign
)
1233 Xinit(ts
, tp
, 16, ATEMP
);
1234 /* : only for DOASNTILDE form */
1235 while (p
[0] == CHAR
&& !ISDIRSEP(p
[1])
1236 && (!isassign
|| p
[1] != PATHSEP
))
1243 r
= (p
[0] == EOS
|| p
[0] == CHAR
|| p
[0] == CSUBST
) ? tilde(Xstring(ts
, tp
)) : (char *) 0;
1261 * based on a version by Arnold Robbins
1271 dp
= str_val(global("HOME"));
1272 else if (cp
[0] == '+' && cp
[1] == '\0')
1273 dp
= str_val(global("PWD"));
1274 else if (cp
[0] == '-' && cp
[1] == '\0')
1275 dp
= str_val(global("OLDPWD"));
1278 /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
1285 * map userid to user's home directory.
1286 * note that 4.3's getpw adds more than 6K to the shell,
1287 * and the YP version probably adds much more.
1288 * we might consider our own version of getpwnam() to keep the size down.
1295 register struct tbl
*ap
;
1297 ap
= tenter(&homedirs
, name
, hash(name
));
1298 if (!(ap
->flag
& ISSET
)) {
1300 /* No usernames in OS2 - punt */
1306 pw
= getpwnam(name
);
1309 n
= strlen(pw
->pw_dir
);
1310 if (n
> 0 && '/' != pw
->pw_dir
[n
- 1]) {
1311 ap
->val
.s
= str_nsave(pw
->pw_dir
, n
+ 1, APERM
);
1313 ap
->val
.s
[n
+ 1] = '\0';
1315 ap
->val
.s
= str_save(pw
->pw_dir
, APERM
);
1317 ap
->flag
|= DEFINED
|ISSET
|ALLOC
;
1325 alt_expand(wp
, start
, exp_start
, end
, fdo
)
1327 char *start
, *exp_start
;
1331 int UNINITIALIZED(count
);
1332 char *brace_start
, *brace_end
, *UNINITIALIZED(comma
);
1336 /* search for open brace */
1337 for (p
= exp_start
; (p
= strchr(p
, MAGIC
)) && p
[1] != OBRACE
; p
+= 2)
1341 /* find matching close brace, if any */
1345 for (p
+= 2; *p
&& count
; p
++) {
1349 else if (*p
== CBRACE
)
1351 else if (*p
== ',' && count
== 1)
1356 /* no valid expansions... */
1357 if (!p
|| count
!= 0) {
1358 /* Note that given a{{b,c} we do not expand anything (this is
1359 * what at&t ksh does. This may be changed to do the {b,c}
1363 glob(start
, wp
, fdo
& DOMARKDIRS
);
1365 XPput(*wp
, debunk(start
, start
, end
- start
));
1370 alt_expand(wp
, start
, brace_end
, end
, fdo
);
1374 /* expand expression */
1375 field_start
= brace_start
+ 2;
1377 for (p
= brace_start
+ 2; p
!= brace_end
; p
++) {
1381 else if ((*p
== CBRACE
&& --count
== 0)
1382 || (*p
== ',' && count
== 1))
1387 l1
= brace_start
- start
;
1388 l2
= (p
- 1) - field_start
;
1389 l3
= end
- brace_end
;
1390 new = (char *) alloc(l1
+ l2
+ l3
+ 1, ATEMP
);
1391 memcpy(new, start
, l1
);
1392 memcpy(new + l1
, field_start
, l2
);
1393 memcpy(new + l1
+ l2
, brace_end
, l3
);
1394 new[l1
+ l2
+ l3
] = '\0';
1395 alt_expand(wp
, new, new + l1
,
1396 new + l1
+ l2
+ l3
, fdo
);
1397 field_start
= p
+ 1;
1403 #endif /* BRACE_EXPAND */