1 /* $OpenBSD: eval.c,v 1.63 2018/07/09 00:20:35 anton Exp $ */
4 * Expansion - quoting, separation, substitution, globbing
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) */
44 #define XSUBMID 6 /* middle of expanding ${} */
46 /* States used for field splitting */
47 #define IFS_WORD 0 /* word has chars (or quotes) */
48 #define IFS_WS 1 /* have seen IFS white-space */
49 #define IFS_NWS 2 /* have seen IFS non-white-space */
51 static int varsub(Expand
*, char *, char *, int *, int *);
52 static int comsub(Expand
*, char *);
53 static char *trimsub(char *, char *, int);
54 static void glob(char *, XPtrV
*, int);
55 static void globit(XString
*, char **, char *, XPtrV
*, int);
56 static char *maybe_expand_tilde(char *, XString
*, char **, int);
57 static char *tilde(char *);
58 static char *homedir(char *);
59 static void alt_expand(XPtrV
*, char *, char *, char *, int);
61 static struct tbl
*varcpy(struct tbl
*);
63 /* compile and expand word */
65 substitute(const char *cp
, int f
)
67 struct source
*s
, *sold
;
70 s
= pushs(SWSTR
, ATEMP
);
71 s
->start
= s
->str
= cp
;
73 if (yylex(ONEWORD
) != LWORD
)
74 internal_errorf("substitute");
77 return evalstr(yylval
.cp
, f
);
84 eval(char **ap
, int f
)
91 XPput(w
, NULL
); /* space for shell name */
95 return (char **) XPclose(w
) + 1;
102 evalstr(char *cp
, int f
)
108 cp
= (XPsize(w
) == 0) ? null
: (char*) *XPptrv(w
);
114 * expand string - return only one component
115 * used from iosetup to expand redirection files
118 evalonestr(char *cp
, int f
)
129 cp
= (char*) *XPptrv(w
);
132 cp
= evalstr(cp
, f
&~DOGLOB
);
139 /* for nested substitution: ${var:=$var2} */
140 typedef struct SubType
{
141 short stype
; /* [=+-?%#] action after expanded word */
142 short base
; /* begin position of expanded word */
143 short f
; /* saved value of f (DOPAT, etc) */
144 struct tbl
*var
; /* variable for ${var..} */
145 short quote
; /* saved value of quote (for ${..[%#]..}) */
146 struct SubType
*prev
; /* old type */
147 struct SubType
*next
; /* poped type (to avoid re-allocating) */
151 expand(char *cp
, /* input word */
152 XPtrV
*wp
, /* output words */
153 int f
) /* DO* flags */
156 int type
; /* expansion type */
157 int quote
= 0; /* quoted */
158 XString ds
; /* destination string */
159 char *dp
, *sp
; /* dest., source */
160 int fdo
, word
; /* second pass flags; have word */
161 int doblank
; /* field splitting of parameter/command subst */
163 /* expansion variables */
164 NULL
, { NULL
}, NULL
, 0
166 SubType st_head
, *st
;
167 int newlines
= 0; /* For trailing newlines in COMSUB */
168 int saw_eq
, tilde_ok
;
173 internal_errorf("expand(NULL)");
174 /* for alias, readonly, set, typeset commands */
175 if ((f
& DOVACHECK
) && is_wdvarassign(cp
)) {
176 f
&= ~(DOVACHECK
|DOBLANK
|DOGLOB
|DOTILDE
);
183 if (Flag(FBRACEEXPAND
) && (f
& DOGLOB
))
186 Xinit(ds
, dp
, 128, ATEMP
); /* init dest. string */
191 tilde_ok
= (f
& (DOTILDE
|DOASNTILDE
)) ? 1 : 0; /* must be 1/0 */
194 word
= (f
&DOBLANK
) ? IFS_WS
: IFS_WORD
;
196 memset(&st_head
, 0, sizeof(st_head
));
203 case XBASE
: /* original prefixed string */
213 quote
|= 2; /* temporary quote */
226 if (f
& DONTRUNCOMMAND
) {
228 *dp
++ = '$'; *dp
++ = '(';
229 while (*sp
!= '\0') {
235 type
= comsub(&x
, sp
);
236 if (type
== XCOM
&& (f
&DOBLANK
))
238 sp
= strchr(sp
, 0) + 1;
245 if (f
& DONTRUNCOMMAND
) {
246 *dp
++ = '$'; *dp
++ = '('; *dp
++ = '(';
247 while (*sp
!= '\0') {
251 *dp
++ = ')'; *dp
++ = ')';
256 v
.flag
= DEFINED
|ISSET
|INTEGER
;
257 v
.type
= 10; /* not default */
259 v_evaluate(&v
, substitute(sp
, 0),
260 KSH_UNWIND_ERROR
, true);
261 sp
= strchr(sp
, 0) + 1;
262 for (p
= str_val(&v
); *p
; ) {
268 case OSUBST
: /* ${{#}var{:}[=+-?#%]word} */
270 * OSUBST [{x] plain-variable-part \0
271 * compiled-word-part CSUBST [}x]
272 * This is where all syntax checking gets done...
275 char *varname
= ++sp
; /* skip the { or x (}) */
279 sp
= strchr(sp
, '\0') + 1; /* skip variable */
280 type
= varsub(&x
, varname
, sp
, &stype
, &slen
);
285 sp
= varname
- 2; /* restore sp */
286 end
= (char *) wdscan(sp
, CSUBST
);
287 /* ({) the } or x is already skipped */
290 str
= snptreef(NULL
, 64, "%S", sp
);
292 errorf("%s: bad substitution", str
);
297 if (type
== XBASE
) { /* expand? */
302 sizeof(SubType
), ATEMP
);
309 st
->base
= Xsavepos(ds
, dp
);
311 st
->var
= varcpy(x
.var
);
313 /* skip qualifier(s) */
316 switch (stype
& 0x7f) {
319 /* ! DOBLANK,DOBRACE_,DOTILDE */
320 f
= DOPAT
| (f
&DONTRUNCOMMAND
) |
323 /* Prepend open pattern (so |
324 * in a trim will work as
331 /* Enabling tilde expansion
333 * non-standard ksh, but is
334 * consistent with rules for
335 * other assignments. Not
336 * sure what POSIX thinks of
338 * Not doing tilde expansion
339 * for integer variables is a
340 * non-POSIX thing - makes
341 * sense though, since ~ is
342 * a arithmetic operator.
344 if (!(x
.var
->flag
& INTEGER
))
345 f
|= DOASNTILDE
|DOTILDE
;
347 /* These will be done after the
348 * value has been assigned.
350 f
&= ~(DOBLANK
|DOGLOB
|DOBRACE_
);
358 /* Enable tilde expansion */
364 sp
= (char *) wdscan(sp
, CSUBST
);
367 case CSUBST
: /* only get here if expanding word */
368 sp
++; /* ({) skip the } or x */
369 tilde_ok
= 0; /* in case of ${unset:-} */
375 switch (st
->stype
&0x7f) {
378 /* Append end-pattern */
379 *dp
++ = MAGIC
; *dp
++ = ')'; *dp
= '\0';
380 dp
= Xrestpos(ds
, dp
, st
->base
);
381 /* Must use st->var since calling
382 * global would break things
385 x
.str
= trimsub(str_val(st
->var
),
387 if (x
.str
[0] != '\0' || st
->quote
)
396 /* Restore our position and substitute
397 * the value of st->var (may not be
398 * the assigned value in the presence
399 * of integer/right-adj/etc attributes).
401 dp
= Xrestpos(ds
, dp
, st
->base
);
402 /* Must use st->var since calling
403 * global would cause with things
404 * like x[i+=1] to be evaluated twice.
406 /* Note: not exported by FEXPORT
409 /* XXX POSIX says readonly is only
410 * fatal for special builtins (setstr
411 * does readonly check).
413 len
= strlen(dp
) + 1;
415 debunk(alloc(len
, ATEMP
),
416 dp
, len
), KSH_UNWIND_ERROR
);
417 x
.str
= str_val(st
->var
);
425 char *s
= Xrestpos(ds
, dp
, st
->base
);
427 errorf("%s: %s", st
->var
->name
,
429 "parameter null or not set" :
430 (debunk(s
, s
, strlen(s
) + 1), s
));
437 case OPAT
: /* open pattern: *(foo|bar) */
438 /* Next char is the type of pattern */
443 case SPAT
: /* pattern separator (|) */
448 case CPAT
: /* close pattern */
456 /* Special case for "$@" (and "${foo[@]}") - no
457 * word is generated if $# is 0 (unless there is
458 * other stuff inside the quotes).
463 /* not really correct: x=; "$x$@" should
464 * generate a null argument and
465 * set A; "${@:+}" shouldn't.
467 if (dp
== Xstring(ds
, dp
))
474 if ((c
= *x
.str
++) == 0) {
486 if ((c
= *x
.str
++) == '\0') {
487 /* force null words to be created so
488 * set -- '' 2 ''; foo "$@" will do
491 if (quote
&& x
.split
)
493 if ((x
.str
= *x
.u
.strv
++) == NULL
) {
501 if (quote
&& !x
.split
)
505 if (quote
&& x
.split
) {
506 /* terminate word for "$@" */
514 if (x
.u
.shf
== NULL
) /* $(< ...) failed, fake EOF */
516 else if (newlines
) { /* Spit out saved nl's */
520 while ((c
= shf_getc(x
.u
.shf
)) == 0 || c
== '\n')
522 newlines
++; /* Save newlines */
523 if (newlines
&& c
!= EOF
) {
524 shf_ungetc(c
, x
.u
.shf
);
534 subst_exstat
= waitlast();
536 subst_exstat
= (x
.u
.shf
== NULL
);
545 /* check for end of word or IFS separation */
546 if (c
== 0 || (!quote
&& (f
& DOBLANK
) && doblank
&&
547 !make_magic
&& ctype(c
, C_IFS
))) {
548 /* How words are broken up:
551 * -----------------------------------
552 * IFS_WORD w/WS w/NWS w
553 * IFS_WS -/WS w/NWS -
554 * IFS_NWS -/NWS w/NWS w
555 * (w means generate a word)
556 * Note that IFS_NWS/0 generates a word (at&t ksh
557 * doesn't do this, but POSIX does).
559 if (word
== IFS_WORD
||
560 (!ctype(c
, C_IFSWS
) && c
&& word
== IFS_NWS
)) {
566 /* also does globbing */
568 p
+ Xlength(ds
, (dp
- 1)),
569 fdo
| (f
& DOMARKDIRS
));
570 else if (fdo
& DOGLOB
)
571 glob(p
, wp
, f
& DOMARKDIRS
);
572 else if ((f
& DOPAT
) || !(fdo
& DOMAGIC_
))
575 XPput(*wp
, debunk(p
, p
, strlen(p
) + 1));
578 tilde_ok
= (f
& (DOTILDE
|DOASNTILDE
)) ? 1 : 0;
580 Xinit(ds
, dp
, 128, ATEMP
);
585 word
= ctype(c
, C_IFSWS
) ? IFS_WS
: IFS_NWS
;
588 if (word
== IFS_NWS
&&
589 Xlength(ds
, dp
) == 0) {
592 if ((p
= strdup("")) == NULL
)
593 internal_errorf("unable "
594 "to allocate memory");
600 /* age tilde_ok info - ~ code tests second bit */
602 /* mark any special second pass chars */
609 /* For character classes - doesn't hurt
610 * to have magic !,-,]'s outside of
613 if (f
& (DOPAT
| DOGLOB
)) {
622 if (f
& (DOPAT
| DOGLOB
)) {
623 fdo
|= DOMAGIC_
| (f
& DOGLOB
);
630 if ((f
& DOBRACE_
) && (c
== OBRACE
||
632 fdo
|= DOBRACE_
|DOMAGIC_
;
637 /* Note first unquoted = for ~ */
638 if (!(f
& DOTEMP_
) && !saw_eq
) {
644 /* Note unquoted : for ~ */
645 if (!(f
& DOTEMP_
) && (f
& DOASNTILDE
))
649 /* tilde_ok is reset whenever
650 * any of ' " $( $(( ${ } are seen.
651 * Note that tilde_ok must be preserved
652 * through the sequence ${A=a=}~
655 (f
& (DOTILDE
|DOASNTILDE
)) &&
660 p
= maybe_expand_tilde(sp
,
674 quote
&= ~2; /* undo temporary */
678 fdo
|= DOMAGIC_
| (f
& DOGLOB
);
680 } else if (ISMAGIC(c
)) {
684 *dp
++ = c
; /* save output char */
690 for (st
= &st_head
; st
!= NULL
; st
= st
->next
) {
691 if (st
->var
== NULL
|| (st
->var
->flag
& RDONLY
) == 0)
694 afree(st
->var
, ATEMP
);
699 * Prepare to generate the string returned by ${} substitution.
702 varsub(Expand
*xp
, char *sp
, char *word
,
703 int *stypep
, /* becomes qualifier type */
704 int *slenp
) /* " " len (=, :=, etc.) valid iff *stypep != 0 */
707 int state
; /* next state: XBASE, XARG, XSUB, XNULLSUB */
708 int stype
; /* substitution type */
714 if (sp
[0] == '\0') /* Bad variable name */
719 /* ${#var}, string length or array size */
720 if (sp
[0] == '#' && (c
= sp
[1]) != '\0') {
721 /* Can't have any modifiers for ${#...} */
725 /* Check for size of array */
726 if ((p
=strchr(sp
,'[')) && (p
[1]=='*'||p
[1]=='@') && p
[2]==']') {
729 vp
= global(arrayname(sp
));
730 if (vp
->flag
& (ISSET
|ARRAY
))
732 for (; vp
; vp
= vp
->u
.array
)
733 if (vp
->flag
& ISSET
)
735 c
= n
; /* ksh88/ksh93 go for number, not max index */
736 } else if (c
== '*' || c
== '@')
739 p
= str_val(global(sp
));
743 if (Flag(FNOUNSET
) && c
== 0 && !zero_ok
)
744 errorf("%s: parameter not set", sp
);
745 *stypep
= 0; /* unqualified variable/string substitution */
746 xp
->str
= str_save(u64ton((uint64_t)c
, 10), ATEMP
);
750 /* Check for qualifiers in word part */
752 c
= word
[slen
= 0] == CHAR
? word
[1] : 0;
756 c
= word
[slen
+ 0] == CHAR
? word
[slen
+ 1] : 0;
758 if (ctype(c
, C_SUBOP1
)) {
761 } else if (ctype(c
, C_SUBOP2
)) { /* Note: ksh88 allows :%, :%%, etc */
764 if (word
[slen
+ 0] == CHAR
&& c
== word
[slen
+ 1]) {
768 } else if (stype
) /* : is not ok */
770 if (!stype
&& *word
!= CSUBST
)
776 if (c
== '*' || c
== '@') {
777 switch (stype
& 0x7f) {
778 case '=': /* can't assign to a vector */
779 case '%': /* can't trim a vector (yet) */
783 if (genv
->loc
->argc
== 0) {
785 xp
->var
= global(sp
);
786 state
= c
== '@' ? XNULLSUB
: XSUB
;
788 xp
->u
.strv
= (const char **) genv
->loc
->argv
+ 1;
789 xp
->str
= *xp
->u
.strv
++;
790 xp
->split
= c
== '@'; /* $@ */
793 zero_ok
= 1; /* exempt "$@" and "$*" from 'set -u' */
795 if ((p
=strchr(sp
,'[')) && (p
[1]=='*'||p
[1]=='@') && p
[2]==']') {
798 switch (stype
& 0x7f) {
799 case '=': /* can't assign to a vector */
800 case '%': /* can't trim a vector (yet) */
806 vp
= global(arrayname(sp
));
807 for (; vp
; vp
= vp
->u
.array
) {
808 if (!(vp
->flag
&ISSET
))
810 XPput(wv
, str_val(vp
));
812 if (XPsize(wv
) == 0) {
814 state
= p
[1] == '@' ? XNULLSUB
: XSUB
;
818 xp
->u
.strv
= (const char **) XPptrv(wv
);
819 xp
->str
= *xp
->u
.strv
++;
820 xp
->split
= p
[1] == '@'; /* ${foo[@]} */
824 /* Can't assign things like $! or $1 */
825 if ((stype
& 0x7f) == '=' &&
826 (ctype(*sp
, C_VAR1
) || digit(*sp
)))
828 xp
->var
= global(sp
);
829 xp
->str
= str_val(xp
->var
);
835 /* test the compiler's code generator */
836 if (ctype(c
, C_SUBOP2
) ||
837 (((stype
&0x80) ? *xp
->str
=='\0' : xp
->str
==null
) ? /* undef? */
838 c
== '=' || c
== '-' || c
== '?' : c
== '+'))
839 state
= XBASE
; /* expand word instead of variable value */
840 if (Flag(FNOUNSET
) && xp
->str
== null
&& !zero_ok
&&
841 (ctype(c
, C_SUBOP2
) || (state
!= XBASE
&& c
!= '+')))
842 errorf("%s: parameter not set", sp
);
847 * Run the command in $(...) and read its output.
850 comsub(Expand
*xp
, char *cp
)
856 s
= pushs(SSTRING
, ATEMP
);
857 s
->start
= s
->str
= cp
;
866 if (t
!= NULL
&& t
->type
== TCOM
&& /* $(<file) */
867 *t
->args
== NULL
&& *t
->vars
== NULL
&& t
->ioact
!= NULL
) {
868 struct ioword
*io
= *t
->ioact
;
871 if ((io
->flag
&IOTYPE
) != IOREAD
)
872 errorf("funny $() command: %s",
873 snptreef(NULL
, 32, "%R", io
));
874 shf
= shf_open(name
= evalstr(io
->name
, DOTILDE
), O_RDONLY
, 0,
875 SHF_MAPHI
|SHF_CLEXEC
);
877 warningf(!Flag(FTALKING
),
878 "%s: cannot open $(<) input", name
);
879 xp
->split
= 0; /* no waitlast() */
883 shf
= shf_fdopen(pv
[0], SHF_RD
, NULL
);
886 ksh_dup2(pv
[1], 1, false);
889 execute(t
, XFORK
|XXCOM
|XPIPEO
, NULL
);
892 xp
->split
= 1; /* waitlast() */
900 * perform #pattern and %pattern substitution in ${}
904 trimsub(char *str
, char *pat
, int how
)
906 char *end
= strchr(str
, 0);
909 switch (how
&0xff) { /* UCHAR_MAX maybe? */
910 case '#': /* shortest at beginning */
911 for (p
= str
; p
<= end
; p
++) {
913 if (gmatch(str
, pat
, false)) {
920 case '#'|0x80: /* longest match at beginning */
921 for (p
= end
; p
>= str
; p
--) {
923 if (gmatch(str
, pat
, false)) {
930 case '%': /* shortest match at end */
931 for (p
= end
; p
>= str
; p
--) {
932 if (gmatch(p
, pat
, false))
933 return str_nsave(str
, p
- str
, ATEMP
);
936 case '%'|0x80: /* longest match at end */
937 for (p
= str
; p
<= end
; p
++) {
938 if (gmatch(p
, pat
, false))
939 return str_nsave(str
, p
- str
, ATEMP
);
944 return str
; /* no match, return string */
949 * Name derived from V6's /etc/glob, the program that expanded filenames.
952 /* XXX cp not const 'cause slashes are temporarily replaced with nulls... */
954 glob(char *cp
, XPtrV
*wp
, int markdirs
)
956 int oldsize
= XPsize(*wp
);
958 if (glob_str(cp
, wp
, markdirs
) == 0)
959 XPput(*wp
, debunk(cp
, cp
, strlen(cp
) + 1));
961 qsortp(XPptrv(*wp
) + oldsize
, (size_t)(XPsize(*wp
) - oldsize
),
966 #define GF_EXCHECK BIT(0) /* do existence check on file */
967 #define GF_GLOBBED BIT(1) /* some globbing has been done */
968 #define GF_MARKDIR BIT(2) /* add trailing / to directories */
970 /* Apply file globbing to cp and store the matching files in wp. Returns
971 * the number of matches found.
974 glob_str(char *cp
, XPtrV
*wp
, int markdirs
)
976 int oldsize
= XPsize(*wp
);
980 Xinit(xs
, xp
, 256, ATEMP
);
981 globit(&xs
, &xp
, cp
, wp
, markdirs
? GF_MARKDIR
: GF_NONE
);
984 return XPsize(*wp
) - oldsize
;
988 globit(XString
*xs
, /* dest string */
989 char **xpp
, /* ptr to dest end */
990 char *sp
, /* source path */
991 XPtrV
*wp
, /* output list */
992 int check
) /* GF_* flags */
994 char *np
; /* next source component */
999 /* This to allow long expansions to be interrupted */
1002 if (sp
== NULL
) { /* end of source path */
1003 /* We only need to check if the file exists if a pattern
1004 * is followed by a non-pattern (eg, foo*x/bar; no check
1005 * is needed for foo* since the match must exist) or if
1006 * any patterns were expanded and the markdirs option is set.
1007 * Symlinks make things a bit tricky...
1009 if ((check
& GF_EXCHECK
) ||
1010 ((check
& GF_MARKDIR
) && (check
& GF_GLOBBED
))) {
1011 #define stat_check() (stat_done ? stat_done : \
1012 (stat_done = stat(Xstring(*xs, xp), &statb) < 0 \
1014 struct stat lstatb
, statb
;
1015 int stat_done
= 0; /* -1: failed, 1 ok */
1017 if (lstat(Xstring(*xs
, xp
), &lstatb
) < 0)
1019 /* special case for systems which strip trailing
1020 * slashes from regular files (eg, /etc/passwd/).
1021 * SunOS 4.1.3 does this...
1023 if ((check
& GF_EXCHECK
) && xp
> Xstring(*xs
, xp
) &&
1024 xp
[-1] == '/' && !S_ISDIR(lstatb
.st_mode
) &&
1025 (!S_ISLNK(lstatb
.st_mode
) ||
1026 stat_check() < 0 || !S_ISDIR(statb
.st_mode
)))
1028 /* Possibly tack on a trailing / if there isn't already
1029 * one and if the file is a directory or a symlink to a
1032 if (((check
& GF_MARKDIR
) && (check
& GF_GLOBBED
)) &&
1033 xp
> Xstring(*xs
, xp
) && xp
[-1] != '/' &&
1034 (S_ISDIR(lstatb
.st_mode
) ||
1035 (S_ISLNK(lstatb
.st_mode
) && stat_check() > 0 &&
1036 S_ISDIR(statb
.st_mode
)))) {
1041 XPput(*wp
, str_nsave(Xstring(*xs
, xp
), Xlength(*xs
, xp
), ATEMP
));
1045 if (xp
> Xstring(*xs
, xp
))
1047 while (*sp
== '/') {
1051 np
= strchr(sp
, '/');
1054 odirsep
= *np
; /* don't assume '/', can be multiple kinds */
1057 odirsep
= '\0'; /* keep gcc quiet */
1058 se
= sp
+ strlen(sp
);
1062 /* Check if sp needs globbing - done to avoid pattern checks for strings
1063 * containing MAGIC characters, open ['s without the matching close ],
1064 * etc. (otherwise opendir() will be called which may fail because the
1065 * directory isn't readable - if no globbing is needed, only execute
1066 * permission should be required (as per POSIX)).
1068 if (!has_globbing(sp
, se
)) {
1069 XcheckN(*xs
, xp
, se
- sp
+ 1);
1070 debunk(xp
, sp
, Xnleft(*xs
, xp
));
1073 globit(xs
, xpp
, np
, wp
, check
);
1082 prefix_len
= Xlength(*xs
, xp
);
1083 dirp
= opendir(prefix_len
? Xstring(*xs
, xp
) : ".");
1086 while ((d
= readdir(dirp
)) != NULL
) {
1088 if (name
[0] == '.' &&
1089 (name
[1] == 0 || (name
[1] == '.' && name
[2] == 0)))
1090 continue; /* always ignore . and .. */
1091 if ((*name
== '.' && *sp
!= '.') ||
1092 !gmatch(name
, sp
, true))
1095 len
= strlen(d
->d_name
) + 1;
1096 XcheckN(*xs
, xp
, len
);
1097 memcpy(xp
, name
, len
);
1098 *xpp
= xp
+ len
- 1;
1099 globit(xs
, xpp
, np
, wp
,
1100 (check
& GF_MARKDIR
) | GF_GLOBBED
1101 | (np
? GF_EXCHECK
: GF_NONE
));
1102 xp
= Xstring(*xs
, xp
) + prefix_len
;
1112 /* remove MAGIC from string */
1114 debunk(char *dp
, const char *sp
, size_t dlen
)
1118 if ((s
= strchr(sp
, MAGIC
))) {
1119 size_t slen
= s
- sp
;
1122 memcpy(dp
, sp
, slen
);
1123 for (d
= dp
+ slen
; *s
&& (d
< dp
+ dlen
); s
++)
1124 if (!ISMAGIC(*s
) || !(*++s
& 0x80) ||
1125 !strchr("*+?@! ", *s
& 0x7f))
1128 /* extended pattern operators: *+?@! */
1129 if ((*s
& 0x7f) != ' ')
1135 } else if (dp
!= sp
)
1136 strlcpy(dp
, sp
, dlen
);
1140 /* Check if p is an unquoted name, possibly followed by a / or :. If so
1141 * puts the expanded version in *dcp,dp and returns a pointer in p just
1142 * past the name, otherwise returns 0.
1145 maybe_expand_tilde(char *p
, XString
*dsp
, char **dpp
, int isassign
)
1151 Xinit(ts
, tp
, 16, ATEMP
);
1152 /* : only for DOASNTILDE form */
1153 while (p
[0] == CHAR
&& p
[1] != '/' && (!isassign
|| p
[1] != ':'))
1160 r
= (p
[0] == EOS
|| p
[0] == CHAR
|| p
[0] == CSUBST
) ?
1161 tilde(Xstring(ts
, tp
)) : NULL
;
1179 * based on a version by Arnold Robbins
1188 dp
= str_val(global("HOME"));
1189 else if (cp
[0] == '+' && cp
[1] == '\0')
1190 dp
= str_val(global("PWD"));
1191 else if (cp
[0] == '-' && cp
[1] == '\0')
1192 dp
= str_val(global("OLDPWD"));
1195 /* If HOME, PWD or OLDPWD are not set, don't expand ~ */
1202 * map userid to user's home directory.
1203 * note that 4.3's getpw adds more than 6K to the shell,
1204 * and the YP version probably adds much more.
1205 * we might consider our own version of getpwnam() to keep the size down.
1213 ap
= ktenter(&homedirs
, name
, hash(name
));
1214 if (!(ap
->flag
& ISSET
)) {
1217 pw
= getpwnam(name
);
1220 ap
->val
.s
= str_save(pw
->pw_dir
, APERM
);
1221 ap
->flag
|= DEFINED
|ISSET
|ALLOC
;
1227 alt_expand(XPtrV
*wp
, char *start
, char *exp_start
, char *end
, int fdo
)
1230 char *brace_start
, *brace_end
, *comma
= NULL
;
1234 /* search for open brace */
1235 for (p
= exp_start
; (p
= strchr(p
, MAGIC
)) && p
[1] != OBRACE
; p
+= 2)
1239 /* find matching close brace, if any */
1243 for (p
+= 2; *p
&& count
; p
++) {
1247 else if (*p
== CBRACE
)
1249 else if (*p
== ',' && count
== 1)
1254 /* no valid expansions... */
1255 if (!p
|| count
!= 0) {
1256 /* Note that given a{{b,c} we do not expand anything (this is
1257 * what at&t ksh does. This may be changed to do the {b,c}
1261 glob(start
, wp
, fdo
& DOMARKDIRS
);
1263 XPput(*wp
, debunk(start
, start
, end
- start
));
1268 alt_expand(wp
, start
, brace_end
, end
, fdo
);
1272 /* expand expression */
1273 field_start
= brace_start
+ 2;
1275 for (p
= brace_start
+ 2; p
!= brace_end
; p
++) {
1279 else if ((*p
== CBRACE
&& --count
== 0) ||
1280 (*p
== ',' && count
== 1)) {
1284 l1
= brace_start
- start
;
1285 l2
= (p
- 1) - field_start
;
1286 l3
= end
- brace_end
;
1287 new = alloc(l1
+ l2
+ l3
+ 1, ATEMP
);
1288 memcpy(new, start
, l1
);
1289 memcpy(new + l1
, field_start
, l2
);
1290 memcpy(new + l1
+ l2
, brace_end
, l3
);
1291 new[l1
+ l2
+ l3
] = '\0';
1292 alt_expand(wp
, new, new + l1
,
1293 new + l1
+ l2
+ l3
, fdo
);
1294 field_start
= p
+ 1;
1302 * Copy the given variable if it's flagged as read-only.
1303 * Such variables have static storage and only one can therefore be referenced
1305 * This is necessary in order to allow variable expansion expressions to refer
1306 * to multiple read-only variables.
1309 varcpy(struct tbl
*vp
)
1313 if (vp
== NULL
|| (vp
->flag
& RDONLY
) == 0)
1316 cpy
= alloc(sizeof(struct tbl
), ATEMP
);
1317 memcpy(cpy
, vp
, sizeof(struct tbl
));