1 /* $NetBSD: eval.c,v 1.11 2009/04/25 05:11:37 lukem Exp $ */
4 * Expansion - quoting, separation, substitution, globbing
9 __RCSID("$NetBSD: eval.c,v 1.11 2009/04/25 05:11:37 lukem Exp $");
21 * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
22 * second pass: alternation ({,}), filename expansion (*?[]).
25 /* expansion generator state */
26 typedef struct Expand
{
27 /* int type; */ /* see expand() */
28 const char *str
; /* string */
30 const char **strv
;/* string[] */
31 struct shf
*shf
;/* file */
33 struct tbl
*var
; /* variable in ${var..} */
34 short split
; /* split "$@" / call waitlast $() */
37 #define XBASE 0 /* scanning original */
38 #define XSUB 1 /* expanding ${} string */
39 #define XARGSEP 2 /* ifs0 between "$*" */
40 #define XARG 3 /* expanding $*, $@ */
41 #define XCOM 4 /* expanding $() */
42 #define XNULLSUB 5 /* "$@" when $# is 0 (don't generate word) */
44 /* States used for field splitting */
45 #define IFS_WORD 0 /* word has chars (or quotes) */
46 #define IFS_WS 1 /* have seen IFS white-space */
47 #define IFS_NWS 2 /* have seen IFS non-white-space */
49 static int varsub
ARGS((Expand
*xp
, char *sp
, char *word
, int *stypep
, int *slenp
));
50 static int comsub
ARGS((Expand
*xp
, char *cp
));
51 static char *trimsub
ARGS((char *str
, char *pat
, int how
));
52 static void glob
ARGS((char *cp
, XPtrV
*wp
, int markdirs
));
53 static void globit
ARGS((XString
*xs
, char **xpp
, char *sp
, XPtrV
*wp
,
55 static char *maybe_expand_tilde
ARGS((char *p
, XString
*dsp
, char **dpp
,
57 static char *tilde
ARGS((char *acp
));
58 static char *homedir
ARGS((char *name
));
60 static void alt_expand
ARGS((XPtrV
*wp
, char *start
, char *exp_start
,
64 /* compile and expand word */
70 struct source
*s
, *sold
;
73 s
= pushs(SWSTR
, ATEMP
);
74 s
->start
= s
->str
= cp
;
76 if (yylex(ONEWORD
) != LWORD
)
77 internal_errorf(1, "substitute");
80 return evalstr(yylval
.cp
, f
);
96 XPput(w
, NULL
); /* space for shell name */
98 XPput(w
, NULL
); /* and space for one arg */
101 expand(*ap
++, &w
, f
);
104 return (char **) XPclose(w
) + 2;
106 return (char **) XPclose(w
) + 1;
122 cp
= (XPsize(w
) == 0) ? null
: (char*) *XPptrv(w
);
128 * expand string - return only one component
129 * used from iosetup to expand redirection files
145 cp
= (char*) *XPptrv(w
);
148 cp
= evalstr(cp
, f
&~DOGLOB
);
155 /* for nested substitution: ${var:=$var2} */
156 typedef struct SubType
{
157 short stype
; /* [=+-?%#] action after expanded word */
158 short base
; /* begin position of expanded word */
159 short f
; /* saved value of f (DOPAT, etc) */
160 struct tbl
*var
; /* variable for ${var..} */
161 short quote
; /* saved value of quote (for ${..[%#]..}) */
162 struct SubType
*prev
; /* old type */
163 struct SubType
*next
; /* poped type (to avoid re-allocating) */
168 char *cp
; /* input word */
169 register XPtrV
*wp
; /* output words */
170 int f
; /* DO* flags */
172 register int UNINITIALIZED(c
);
173 register int type
; /* expansion type */
174 register int quote
= 0; /* quoted */
175 XString ds
; /* destination string */
176 register char *dp
, *sp
; /* dest., source */
177 int fdo
, word
; /* second pass flags; have word */
178 int doblank
; /* field splitting of parameter/command subst */
179 Expand x
; /* expansion variables */
180 SubType st_head
, *st
;
181 int UNINITIALIZED(newlines
); /* For trailing newlines in COMSUB */
182 int saw_eq
, tilde_ok
;
186 x
.split
= 0; /* XXX gcc */
187 x
.str
= NULL
; /* XXX gcc */
189 internal_errorf(1, "expand(NULL)");
190 /* for alias, readonly, set, typeset commands */
191 if ((f
& DOVACHECK
) && is_wdvarassign(cp
)) {
192 f
&= ~(DOVACHECK
|DOBLANK
|DOGLOB
|DOTILDE
);
200 if (Flag(FBRACEEXPAND
) && (f
& DOGLOB
))
202 #endif /* BRACE_EXPAND */
204 Xinit(ds
, dp
, 128, ATEMP
); /* init dest. string */
209 tilde_ok
= (f
& (DOTILDE
|DOASNTILDE
)) ? 1 : 0; /* must be 1/0 */
212 word
= (f
&DOBLANK
) ? IFS_WS
: IFS_WORD
;
213 st_head
.next
= (SubType
*) 0;
220 case XBASE
: /* original prefixed string */
230 quote
|= 2; /* temporary quote */
243 if (f
& DONTRUNCOMMAND
) {
245 *dp
++ = '$'; *dp
++ = '(';
246 while (*sp
!= '\0') {
252 type
= comsub(&x
, sp
);
253 if (type
== XCOM
&& (f
&DOBLANK
))
255 sp
= strchr(sp
, 0) + 1;
262 if (f
& DONTRUNCOMMAND
) {
263 *dp
++ = '$'; *dp
++ = '('; *dp
++ = '(';
264 while (*sp
!= '\0') {
268 *dp
++ = ')'; *dp
++ = ')';
273 v
.flag
= DEFINED
|ISSET
|INTEGER
;
274 v
.type
= 10; /* not default */
276 v_evaluate(&v
, substitute(sp
, 0),
278 sp
= strchr(sp
, 0) + 1;
279 for (p
= str_val(&v
); *p
; ) {
285 case OSUBST
: /* ${{#}var{:}[=+-?#%]word} */
287 * OSUBST [{x] plain-variable-part \0
288 * compiled-word-part CSUBST [}x]
289 * This is were all syntax checking gets done...
292 char *varname
= ++sp
; /* skip the { or x (}) */
296 slen
= -1; /* XXX gcc */
297 sp
= strchr(sp
, '\0') + 1; /* skip variable */
298 type
= varsub(&x
, varname
, sp
, &stype
, &slen
);
303 end
= (char *) wdscan(sp
, CSUBST
);
304 /* ({) the } or x is already skipped */
307 str
= snptreef((char *) 0, 64, "%S",
310 errorf("%s: bad substitution", str
);
315 if (type
== XBASE
) { /* expand? */
319 newst
= (SubType
*) alloc(
320 sizeof(SubType
), ATEMP
);
321 newst
->next
= (SubType
*) 0;
327 st
->base
= Xsavepos(ds
, dp
);
331 /* skip qualifier(s) */
334 switch (stype
& 0x7f) {
337 /* ! DOBLANK,DOBRACE_,DOTILDE */
338 f
= DOPAT
| (f
&DONTRUNCOMMAND
)
341 /* Prepend open pattern (so |
342 * in a trim will work as
349 /* Enabling tilde expansion
351 * non-standard ksh, but is
352 * consistent with rules for
353 * other assignments. Not
354 * sure what POSIX thinks of
356 * Not doing tilde expansion
357 * for integer variables is a
358 * non-POSIX thing - makes
359 * sense though, since ~ is
360 * a arithmetic operator.
362 if (!(x
.var
->flag
& INTEGER
))
363 f
|= DOASNTILDE
|DOTILDE
;
365 /* These will be done after the
366 * value has been assigned.
368 f
&= ~(DOBLANK
|DOGLOB
|DOBRACE_
);
376 /* Enable tilde expansion */
382 sp
= (char *) wdscan(sp
, CSUBST
);
385 case CSUBST
: /* only get here if expanding word */
386 sp
++; /* ({) skip the } or x */
387 tilde_ok
= 0; /* in case of ${unset:-} */
393 switch (st
->stype
&0x7f) {
396 /* Append end-pattern */
397 *dp
++ = MAGIC
; *dp
++ = ')'; *dp
= '\0';
398 dp
= Xrestpos(ds
, dp
, st
->base
);
399 /* Must use st->var since calling
400 * global would break things
403 x
.str
= trimsub(str_val(st
->var
),
411 /* Restore our position and substitute
412 * the value of st->var (may not be
413 * the assigned value in the presence
414 * of integer/right-adj/etc attributes).
416 dp
= Xrestpos(ds
, dp
, st
->base
);
417 /* Must use st->var since calling
418 * global would cause with things
419 * like x[i+=1] to be evaluated twice.
421 /* Note: not exported by FEXPORT
424 /* XXX POSIX says readonly is only
425 * fatal for special builtins (setstr
426 * does readonly check).
428 len
= strlen(dp
) + 1;
430 debunk((char *) alloc(len
, ATEMP
),
433 x
.str
= str_val(st
->var
);
441 char *s
= Xrestpos(ds
, dp
, st
->base
);
443 errorf("%s: %s", st
->var
->name
,
445 "parameter null or not set"
446 : (debunk(s
, s
, strlen(s
) + 1), s
));
453 case OPAT
: /* open pattern: *(foo|bar) */
454 /* Next char is the type of pattern */
459 case SPAT
: /* pattern separator (|) */
464 case CPAT
: /* close pattern */
472 /* Special case for "$@" (and "${foo[@]}") - no
473 * word is generated if $# is 0 (unless there is
474 * other stuff inside the quotes).
479 /* not really correct: x=; "$x$@" should
480 * generate a null argument and
481 * set A; "${@:+}" shouldn't.
483 if (dp
== Xstring(ds
, dp
))
489 if ((c
= *x
.str
++) == 0) {
501 if ((c
= *x
.str
++) == '\0') {
502 /* force null words to be created so
503 * set -- '' 2 ''; foo "$@" will do
506 if (quote
&& x
.split
)
508 if ((x
.str
= *x
.u
.strv
++) == NULL
) {
516 if (quote
&& !x
.split
)
520 if (quote
&& x
.split
) {
521 /* terminate word for "$@" */
529 if (newlines
) { /* Spit out saved nl's */
533 while ((c
= shf_getc(x
.u
.shf
)) == 0 || c
== '\n')
535 newlines
++; /* Save newlines */
536 if (newlines
&& c
!= EOF
) {
537 shf_ungetc(c
, x
.u
.shf
);
546 subst_exstat
= waitlast();
555 /* check for end of word or IFS separation */
556 if (c
== 0 || (!quote
&& (f
& DOBLANK
) && doblank
&& !make_magic
559 /* How words are broken up:
562 * -----------------------------------
563 * IFS_WORD w/WS w/NWS w
564 * IFS_WS -/WS w/NWS -
565 * IFS_NWS -/NWS w/NWS w
566 * (w means generate a word)
567 * Note that IFS_NWS/0 generates a word (at&t ksh
568 * doesn't do this, but POSIX does).
571 || (!ctype(c
, C_IFSWS
) && (c
|| word
== IFS_NWS
)))
579 /* also does globbing */
581 p
+ Xlength(ds
, (dp
- 1)),
582 fdo
| (f
& DOMARKDIRS
));
584 #endif /* BRACE_EXPAND */
586 glob(p
, wp
, f
& DOMARKDIRS
);
587 else if ((f
& DOPAT
) || !(fdo
& DOMAGIC_
))
590 XPput(*wp
, debunk(p
, p
, strlen(p
) + 1));
593 tilde_ok
= (f
& (DOTILDE
|DOASNTILDE
)) ? 1 : 0;
595 Xinit(ds
, dp
, 128, ATEMP
);
600 word
= ctype(c
, C_IFSWS
) ? IFS_WS
: IFS_NWS
;
602 /* age tilde_ok info - ~ code tests second bit */
604 /* mark any special second pass chars */
611 /* For character classes - doesn't hurt
612 * to have magic !,-,]'s outside of
615 if (f
& (DOPAT
| DOGLOB
)) {
624 if (f
& (DOPAT
| DOGLOB
)) {
625 fdo
|= DOMAGIC_
| (f
& DOGLOB
);
633 if ((f
& DOBRACE_
) && (c
== OBRACE
634 || (fdo
& DOBRACE_
)))
636 fdo
|= DOBRACE_
|DOMAGIC_
;
640 #endif /* BRACE_EXPAND */
642 /* Note first unquoted = for ~ */
643 if (!(f
& DOTEMP_
) && !saw_eq
) {
648 case PATHSEP
: /* : */
649 /* Note unquoted : for ~ */
650 if (!(f
& DOTEMP_
) && (f
& DOASNTILDE
))
654 /* tilde_ok is reset whenever
655 * any of ' " $( $(( ${ } are seen.
656 * Note that tilde_ok must be preserved
657 * through the sequence ${A=a=}~
660 && (f
& (DOTILDE
|DOASNTILDE
))
666 p
= maybe_expand_tilde(sp
,
680 quote
&= ~2; /* undo temporary */
684 fdo
|= DOMAGIC_
| (f
& DOGLOB
);
686 } else if (ISMAGIC(c
)) {
690 *dp
++ = c
; /* save output char */
697 * Prepare to generate the string returned by ${} substitution.
700 varsub(xp
, sp
, word
, stypep
, slenp
)
704 int *stypep
; /* becomes qualifier type */
705 int *slenp
; /* " " len (=, :=, etc.) valid iff *stypep != 0 */
708 int state
; /* next state: XBASE, XARG, XSUB, XNULLSUB */
709 int stype
; /* substitution type */
714 if (sp
[0] == '\0') /* Bad variable name */
717 xp
->var
= (struct tbl
*) 0;
719 /* ${#var}, string length or array size */
720 if (sp
[0] == '#' && (c
= sp
[1]) != '\0') {
723 /* Can't have any modifiers for ${#...} */
727 /* Check for size of array */
728 if ((p
=strchr(sp
,'[')) && (p
[1]=='*'||p
[1]=='@') && p
[2]==']') {
731 vp
= global(arrayname(sp
));
732 if (vp
->flag
& (ISSET
|ARRAY
))
734 for (; vp
; vp
= vp
->u
.array
)
735 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) {
789 state
= c
== '@' ? XNULLSUB
: XSUB
;
791 xp
->u
.strv
= (const char **) e
->loc
->argv
+ 1;
792 xp
->str
= *xp
->u
.strv
++;
793 xp
->split
= c
== '@'; /* $@ */
797 if ((p
=strchr(sp
,'[')) && (p
[1]=='*'||p
[1]=='@') && p
[2]==']') {
800 switch (stype
& 0x7f) {
801 case '=': /* can't assign to a vector */
802 case '%': /* can't trim a vector (yet) */
807 vp
= global(arrayname(sp
));
808 for (; vp
; vp
= vp
->u
.array
) {
809 if (!(vp
->flag
&ISSET
))
811 XPput(wv
, str_val(vp
));
813 if (XPsize(wv
) == 0) {
815 state
= p
[1] == '@' ? XNULLSUB
: XSUB
;
819 xp
->u
.strv
= (const char **) XPptrv(wv
);
820 xp
->str
= *xp
->u
.strv
++;
821 xp
->split
= p
[1] == '@'; /* ${foo[@]} */
825 /* Can't assign things like $! or $1 */
826 if ((stype
& 0x7f) == '='
827 && (ctype(*sp
, C_VAR1
) || digit(*sp
)))
829 xp
->var
= global(sp
);
830 xp
->str
= str_val(xp
->var
);
836 /* test the compiler's code generator */
837 if (ctype(c
, C_SUBOP2
) ||
838 (((stype
&0x80) ? *xp
->str
=='\0' : xp
->str
==null
) ? /* undef? */
839 c
== '=' || c
== '-' || c
== '?' : c
== '+'))
840 state
= XBASE
; /* expand word instead of variable value */
841 if (Flag(FNOUNSET
) && xp
->str
== null
842 && (ctype(c
, C_SUBOP2
) || (state
!= XBASE
&& c
!= '+')))
843 errorf("%s: parameter not set", sp
);
848 * Run the command in $(...) and read its output.
856 register struct op
*t
;
859 s
= pushs(SSTRING
, ATEMP
);
860 s
->start
= s
->str
= cp
;
869 if (t
!= NULL
&& t
->type
== TCOM
&& /* $(<file) */
870 *t
->args
== NULL
&& *t
->vars
== NULL
&& t
->ioact
!= NULL
) {
871 register struct ioword
*io
= *t
->ioact
;
874 if ((io
->flag
&IOTYPE
) != IOREAD
)
875 errorf("funny $() command: %s",
876 snptreef((char *) 0, 32, "%R", io
));
877 shf
= shf_open(name
= evalstr(io
->name
, DOTILDE
), O_RDONLY
, 0,
878 SHF_MAPHI
|SHF_CLEXEC
);
880 errorf("%s: cannot open $() input", name
);
881 xp
->split
= 0; /* no waitlast() */
885 shf
= shf_fdopen(pv
[0], SHF_RD
, (struct shf
*) 0);
886 ofd1
= savefd(1, 0); /* fd 1 may be closed... */
888 ksh_dup2(pv
[1], 1, FALSE
);
891 execute(t
, XFORK
|XXCOM
|XPIPEO
);
894 xp
->split
= 1; /* waitlast() */
902 * perform #pattern and %pattern substitution in ${}
906 trimsub(str
, pat
, how
)
911 register char *end
= strchr(str
, 0);
914 switch (how
&0xff) { /* UCHAR_MAX maybe? */
915 case '#': /* shortest at beginning */
916 for (p
= str
; p
<= end
; p
++) {
918 if (gmatch(str
, pat
, FALSE
)) {
925 case '#'|0x80: /* longest match at beginning */
926 for (p
= end
; p
>= str
; p
--) {
928 if (gmatch(str
, pat
, FALSE
)) {
935 case '%': /* shortest match at end */
936 for (p
= end
; p
>= str
; p
--) {
937 if (gmatch(p
, pat
, FALSE
))
938 return str_nsave(str
, p
- str
, ATEMP
);
941 case '%'|0x80: /* longest match at end */
942 for (p
= str
; p
<= end
; p
++) {
943 if (gmatch(p
, pat
, FALSE
))
944 return str_nsave(str
, p
- str
, ATEMP
);
949 return str
; /* no match, return string */
954 * Name derived from V6's /etc/glob, the program that expanded filenames.
957 /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */
959 glob(cp
, wp
, markdirs
)
964 int oldsize
= XPsize(*wp
);
966 if (glob_str(cp
, wp
, markdirs
) == 0)
967 XPput(*wp
, debunk(cp
, cp
, strlen(cp
) + 1));
969 qsortp(XPptrv(*wp
) + oldsize
, (size_t)(XPsize(*wp
) - oldsize
),
974 #define GF_EXCHECK BIT(0) /* do existence check on file */
975 #define GF_GLOBBED BIT(1) /* some globbing has been done */
976 #define GF_MARKDIR BIT(2) /* add trailing / to directories */
978 /* Apply file globbing to cp and store the matching files in wp. Returns
979 * the number of matches found.
982 glob_str(cp
, wp
, markdirs
)
987 int oldsize
= XPsize(*wp
);
991 Xinit(xs
, xp
, 256, ATEMP
);
992 globit(&xs
, &xp
, cp
, wp
, markdirs
? GF_MARKDIR
: GF_NONE
);
995 return XPsize(*wp
) - oldsize
;
999 globit(xs
, xpp
, sp
, wp
, check
)
1000 XString
*xs
; /* dest string */
1001 char **xpp
; /* ptr to dest end */
1002 char *sp
; /* source path */
1003 register XPtrV
*wp
; /* output list */
1004 int check
; /* GF_* flags */
1006 register char *np
; /* next source component */
1011 /* This to allow long expansions to be interrupted */
1014 if (sp
== NULL
) { /* end of source path */
1015 /* We only need to check if the file exists if a pattern
1016 * is followed by a non-pattern (eg, foo*x/bar; no check
1017 * is needed for foo* since the match must exist) or if
1018 * any patterns were expanded and the markdirs option is set.
1019 * Symlinks make things a bit tricky...
1021 if ((check
& GF_EXCHECK
)
1022 || ((check
& GF_MARKDIR
) && (check
& GF_GLOBBED
)))
1024 #define stat_check() (stat_done ? stat_done : \
1025 (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
1027 struct stat lstatb
, statb
;
1028 int stat_done
= 0; /* -1: failed, 1 ok */
1030 if (lstat(Xstring(*xs
, xp
), &lstatb
) < 0)
1032 /* special case for systems which strip trailing
1033 * slashes from regular files (eg, /etc/passwd/).
1034 * SunOS 4.1.3 does this...
1036 if ((check
& GF_EXCHECK
) && xp
> Xstring(*xs
, xp
)
1037 && ISDIRSEP(xp
[-1]) && !S_ISDIR(lstatb
.st_mode
)
1039 && (!S_ISLNK(lstatb
.st_mode
)
1041 || !S_ISDIR(statb
.st_mode
))
1042 #endif /* S_ISLNK */
1045 /* Possibly tack on a trailing / if there isn't already
1046 * one and if the file is a directory or a symlink to a
1049 if (((check
& GF_MARKDIR
) && (check
& GF_GLOBBED
))
1050 && xp
> Xstring(*xs
, xp
) && !ISDIRSEP(xp
[-1])
1051 && (S_ISDIR(lstatb
.st_mode
)
1053 || (S_ISLNK(lstatb
.st_mode
)
1055 && S_ISDIR(statb
.st_mode
))
1056 #endif /* S_ISLNK */
1063 #ifdef OS2 /* Done this way to avoid bug in gcc 2.7.2... */
1064 /* Ugly kludge required for command
1065 * completion - see how search_access()
1066 * is implemented for OS/2...
1068 # define KLUDGE_VAL 4
1070 # define KLUDGE_VAL 0
1072 XPput(*wp
, str_nsave(Xstring(*xs
, xp
), Xlength(*xs
, xp
)
1073 + KLUDGE_VAL
, ATEMP
));
1077 if (xp
> Xstring(*xs
, xp
))
1079 while (ISDIRSEP(*sp
)) {
1083 np
= ksh_strchr_dirsep(sp
);
1086 odirsep
= *np
; /* don't assume DIRSEP, can be multiple kinds */
1089 odirsep
= '\0'; /* keep gcc quiet */
1090 se
= sp
+ strlen(sp
);
1094 /* Check if sp needs globbing - done to avoid pattern checks for strings
1095 * containing MAGIC characters, open ['s without the matching close ],
1096 * etc. (otherwise opendir() will be called which may fail because the
1097 * directory isn't readable - if no globbing is needed, only execute
1098 * permission should be required (as per POSIX)).
1100 if (!has_globbing(sp
, se
)) {
1101 XcheckN(*xs
, xp
, se
- sp
+ 1);
1102 debunk(xp
, sp
, Xnleft(*xs
, xp
));
1105 globit(xs
, xpp
, np
, wp
, check
);
1113 /* xp = *xpp; copy_non_glob() may have re-alloc'd xs */
1115 prefix_len
= Xlength(*xs
, xp
);
1116 dirp
= ksh_opendir(prefix_len
? Xstring(*xs
, xp
) : ".");
1119 while ((d
= readdir(dirp
)) != NULL
) {
1121 if ((*name
== '.' && *sp
!= '.')
1122 || !gmatch(name
, sp
, TRUE
))
1125 len
= NLENGTH(d
) + 1;
1126 XcheckN(*xs
, xp
, len
);
1127 memcpy(xp
, name
, len
);
1128 *xpp
= xp
+ len
- 1;
1129 globit(xs
, xpp
, np
, wp
,
1130 (check
& GF_MARKDIR
) | GF_GLOBBED
1131 | (np
? GF_EXCHECK
: GF_NONE
));
1132 xp
= Xstring(*xs
, xp
) + prefix_len
;
1143 /* Check if p contains something that needs globbing; if it does, 0 is
1144 * returned; if not, p is copied into xs/xp after stripping any MAGICs
1146 static int copy_non_glob
ARGS((XString
*xs
, char **xpp
, char *p
));
1148 copy_non_glob(xs
, xpp
, p
)
1154 int len
= strlen(p
);
1156 XcheckN(*xs
, *xpp
, len
);
1162 if (c
== '*' || c
== '?')
1167 if (ISMAGIC(*q
) && q
[1] == NOT
)
1169 if (ISMAGIC(*q
) && q
[1] == ']')
1172 if (ISMAGIC(*q
) && *++q
== ']')
1174 /* pass a literal [ through */
1176 /* must be a MAGIC-MAGIC, or MAGIC-!, MAGIC--, etc. */
1186 /* remove MAGIC from string */
1188 debunk(dp
, sp
, dlen
)
1195 if ((s
= strchr(sp
, MAGIC
))) {
1196 if (s
- sp
>= (ptrdiff_t)dlen
)
1198 memcpy(dp
, sp
, s
- sp
);
1199 for (d
= dp
+ (s
- sp
); *s
&& (d
- dp
< (ptrdiff_t)dlen
); s
++)
1200 if (!ISMAGIC(*s
) || !(*++s
& 0x80)
1201 || !strchr("*+?@! ", *s
& 0x7f))
1204 /* extended pattern operators: *+?@! */
1205 if ((*s
& 0x7f) != ' ')
1207 if (d
- dp
< (ptrdiff_t)dlen
)
1211 } else if (dp
!= sp
)
1212 strlcpy(dp
, sp
, dlen
);
1216 /* Check if p is an unquoted name, possibly followed by a / or :. If so
1217 * puts the expanded version in *dcp,dp and returns a pointer in p just
1218 * past the name, otherwise returns 0.
1221 maybe_expand_tilde(p
, dsp
, dpp
, isassign
)
1231 Xinit(ts
, tp
, 16, ATEMP
);
1232 /* : only for DOASNTILDE form */
1233 while (p
[0] == CHAR
&& !ISDIRSEP(p
[1])
1234 && (!isassign
|| p
[1] != PATHSEP
))
1241 r
= (p
[0] == EOS
|| p
[0] == CHAR
|| p
[0] == CSUBST
) ? tilde(Xstring(ts
, tp
)) : (char *) 0;
1259 * based on a version by Arnold Robbins
1269 dp
= str_val(global("HOME"));
1270 else if (cp
[0] == '+' && cp
[1] == '\0')
1271 dp
= str_val(global("PWD"));
1272 else if (cp
[0] == '-' && cp
[1] == '\0')
1273 dp
= str_val(global("OLDPWD"));
1276 /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
1283 * map userid to user's home directory.
1284 * note that 4.3's getpw adds more than 6K to the shell,
1285 * and the YP version probably adds much more.
1286 * we might consider our own version of getpwnam() to keep the size down.
1293 register struct tbl
*ap
;
1295 ap
= tenter(&homedirs
, name
, hash(name
));
1296 if (!(ap
->flag
& ISSET
)) {
1298 /* No usernames in OS2 - punt */
1304 pw
= getpwnam(name
);
1307 n
= strlen(pw
->pw_dir
);
1308 if (n
> 0 && '/' != pw
->pw_dir
[n
- 1]) {
1309 ap
->val
.s
= str_nsave(pw
->pw_dir
, n
+ 1, APERM
);
1311 ap
->val
.s
[n
+ 1] = '\0';
1313 ap
->val
.s
= str_save(pw
->pw_dir
, APERM
);
1315 ap
->flag
|= DEFINED
|ISSET
|ALLOC
;
1323 alt_expand(wp
, start
, exp_start
, end
, fdo
)
1325 char *start
, *exp_start
;
1329 int UNINITIALIZED(count
);
1330 char *brace_start
, *brace_end
, *UNINITIALIZED(comma
);
1334 /* search for open brace */
1335 for (p
= exp_start
; (p
= strchr(p
, MAGIC
)) && p
[1] != OBRACE
; p
+= 2)
1339 /* find matching close brace, if any */
1343 for (p
+= 2; *p
&& count
; p
++) {
1347 else if (*p
== CBRACE
)
1349 else if (*p
== ',' && count
== 1)
1354 /* no valid expansions... */
1355 if (!p
|| count
!= 0) {
1356 /* Note that given a{{b,c} we do not expand anything (this is
1357 * what at&t ksh does. This may be changed to do the {b,c}
1361 glob(start
, wp
, fdo
& DOMARKDIRS
);
1363 XPput(*wp
, debunk(start
, start
, end
- start
));
1368 alt_expand(wp
, start
, brace_end
, end
, fdo
);
1372 /* expand expression */
1373 field_start
= brace_start
+ 2;
1375 for (p
= brace_start
+ 2; p
!= brace_end
; p
++) {
1379 else if ((*p
== CBRACE
&& --count
== 0)
1380 || (*p
== ',' && count
== 1))
1385 l1
= brace_start
- start
;
1386 l2
= (p
- 1) - field_start
;
1387 l3
= end
- brace_end
;
1388 new = (char *) alloc(l1
+ l2
+ l3
+ 1, ATEMP
);
1389 memcpy(new, start
, l1
);
1390 memcpy(new + l1
, field_start
, l2
);
1391 memcpy(new + l1
+ l2
, brace_end
, l3
);
1392 new[l1
+ l2
+ l3
] = '\0';
1393 alt_expand(wp
, new, new + l1
,
1394 new + l1
+ l2
+ l3
, fdo
);
1395 field_start
= p
+ 1;
1401 #endif /* BRACE_EXPAND */