1 /* $OpenBSD: lex.c,v 1.78 2018/01/15 14:58:05 jca Exp $ */
4 * lexical analysis and source input
17 * states while lexing word
19 #define SINVALID -1 /* invalid state */
20 #define SBASE 0 /* outside any lexical constructs */
21 #define SWORD 1 /* implicit quoting for substitute() */
22 #define SLETPAREN 2 /* inside (( )), implicit quoting */
23 #define SSQUOTE 3 /* inside '' */
24 #define SDQUOTE 4 /* inside "" */
25 #define SBRACE 5 /* inside ${} */
26 #define SCSPAREN 6 /* inside $() */
27 #define SBQUOTE 7 /* inside `` */
28 #define SASPAREN 8 /* inside $(( )) */
29 #define SHEREDELIM 9 /* parsing <<,<<- delimiter */
30 #define SHEREDQUOTE 10 /* parsing " in <<,<<- delimiter */
31 #define SPATTERN 11 /* parsing *(...|...) pattern (*+?@!) */
32 #define STBRACE 12 /* parsing ${..[#%]..} */
33 #define SBRACEQ 13 /* inside "${}" */
35 /* Structure to keep track of the lexing state and the various pieces of info
36 * needed for each particular state.
38 typedef struct lex_state Lex_state
;
43 struct scsparen_info
{
44 int nparen
; /* count open parenthesis */
45 int csstate
; /* XXX remove */
46 #define ls_scsparen ls_info.u_scsparen
50 struct sasparen_info
{
51 int nparen
; /* count open parenthesis */
52 int start
; /* marks start of $(( in output str */
53 #define ls_sasparen ls_info.u_sasparen
57 struct sletparen_info
{
58 int nparen
; /* count open parenthesis */
59 #define ls_sletparen ls_info.u_sletparen
64 int indquotes
; /* true if in double quotes: "`...`" */
65 #define ls_sbquote ls_info.u_sbquote
68 Lex_state
*base
; /* used to point to next state block */
72 typedef struct State_info State_info
;
79 static void readhere(struct ioword
*);
80 static int getsc__(void);
81 static void getsc_line(Source
*);
82 static int getsc_bn(void);
83 static char *get_brace_var(XString
*, char *);
84 static int arraysub(char **);
85 static const char *ungetsc(int);
86 static void gethere(void);
87 static Lex_state
*push_state_(State_info
*, Lex_state
*);
88 static Lex_state
*pop_state_(State_info
*, Lex_state
*);
89 static char *special_prompt_expand(char *);
90 static int dopprompt(const char *, int, const char **, int);
91 int promptlen(const char *cp
, const char **spp
);
93 static int backslash_skip
;
94 static int ignore_backslash_newline
;
96 Source
*source
; /* yyparse/yylex source */
97 YYSTYPE yylval
; /* result from yylex */
98 struct ioword
*heres
[HERES
], **herep
;
101 char **history
; /* saved commands */
102 char **histptr
; /* last history item */
103 uint32_t histsize
; /* history size */
105 /* optimized getsc_bn() */
106 #define getsc() (*source->str != '\0' && *source->str != '\\' \
107 && !backslash_skip ? *source->str++ : getsc_bn())
108 /* optimized getsc__() */
109 #define getsc_() ((*source->str != '\0') ? *source->str++ : getsc__())
111 #define STATE_BSIZE 32
113 #define PUSH_STATE(s) do { \
114 if (++statep == state_info.end) \
115 statep = push_state_(&state_info, statep); \
116 state = statep->ls_state = (s); \
119 #define POP_STATE() do { \
120 if (--statep == state_info.base) \
121 statep = pop_state_(&state_info, statep); \
122 state = statep->ls_state; \
130 * tokens are not regular expressions, they are LL(1).
131 * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
132 * hence the state stack.
138 Lex_state states
[STATE_BSIZE
], *statep
;
139 State_info state_info
;
141 XString ws
; /* expandable output word */
142 char *wp
; /* output word pointer */
148 states
[0].ls_state
= SINVALID
;
149 states
[0].ls_info
.base
= NULL
;
151 state_info
.base
= states
;
152 state_info
.end
= &states
[STATE_BSIZE
];
154 Xinit(ws
, wp
, 64, ATEMP
);
157 ignore_backslash_newline
= 0;
161 else if (cf
&LETEXPR
) {
162 *wp
++ = OQUOTE
; /* enclose arguments in (double) quotes */
164 statep
->ls_sletparen
.nparen
= 0;
165 } else { /* normal lexing */
166 state
= (cf
& HEREDELIM
) ? SHEREDELIM
: SBASE
;
167 while ((c
= getsc()) == ' ' || c
== '\t')
170 ignore_backslash_newline
++;
171 while ((c
= getsc()) != '\0' && c
!= '\n')
173 ignore_backslash_newline
--;
177 if (source
->flags
& SF_ALIAS
) { /* trailing ' ' in alias definition */
178 source
->flags
&= ~SF_ALIAS
;
179 /* In POSIX mode, a trailing space only counts if we are
180 * parsing a simple command
182 if (!Flag(FPOSIX
) || (cf
& CMDWORD
))
186 /* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */
187 statep
->ls_state
= state
;
189 /* collect non-special or quoted characters to form word */
190 while (!((c
= getsc()) == 0 ||
191 ((state
== SBASE
|| state
== SHEREDELIM
) && ctype(c
, C_LEX1
)))) {
195 if (Flag(FCSHHISTORY
) && (source
->flags
& SF_TTY
) &&
197 char **replace
= NULL
;
199 char match
[200] = { 0 }, *str
= match
;
203 if (c2
== '\0' || c2
== ' ' || c2
== '\t')
206 replace
= hist_get_newest(0);
207 else if (isdigit(c2
) || c2
== '-' ||
213 if ((c2
= getsc()) == '\0')
215 if (c2
== '\t' || c2
== ' ' ||
221 } while (str
< &match
[sizeof(match
)-1]);
225 int h
= findhistrel(match
);
227 replace
= &history
[h
];
229 int h
= findhist(-1, 0, match
, true);
231 replace
= &history
[h
];
236 * XXX ksh history buffer saves un-expanded
237 * commands. Until the history buffer code is
238 * changed to contain expanded commands, we
239 * ignore the bad commands (spinning sucks)
241 if (replace
&& **replace
== '!')
246 /* do not strdup replacement via alloc */
247 s
= pushs(SREREAD
, source
->areap
);
248 s
->start
= s
->str
= *replace
;
253 } else if (*match
!= '\0') {
254 /* restore what followed the '!' */
255 mlen
= strlen(match
);
256 for (i
= mlen
-1; i
>= 0; i
--)
261 if (c
== '[' && (cf
& (VARASN
|ARRAYVAR
))) {
262 *wp
= EOS
; /* temporary */
263 if (is_wdvarname(Xstring(ws
, wp
), false)) {
266 if (arraysub(&tmp
)) {
269 for (p
= tmp
; *p
; ) {
292 Sbase1
: /* includes *(...|...) pattern (*+?@!) */
293 if (c
== '*' || c
== '@' || c
== '+' || c
== '?' ||
296 if (c2
== '(' /*)*/ ) {
299 PUSH_STATE(SPATTERN
);
305 Sbase2
: /* doesn't include *(...|...) pattern (*+?@!) */
309 if (c
) /* trailing \ is lost */
310 *wp
++ = QCHAR
, *wp
++ = c
;
313 if ((cf
& HEREDOC
) || state
== SBRACEQ
) {
314 *wp
++ = CHAR
, *wp
++ = c
;
318 ignore_backslash_newline
++;
337 *wp
++ = QCHAR
, *wp
++ = c
;
340 if ((cf
& HEREDOC
) == 0) {
341 *wp
++ = QCHAR
, *wp
++ = c
;
347 *wp
++ = QCHAR
, *wp
++ = c
;
351 if (c
) { /* trailing \ is lost */
352 *wp
++ = CHAR
, *wp
++ = '\\';
353 *wp
++ = CHAR
, *wp
++ = c
;
360 if (c
== '(') /*)*/ {
362 if (c
== '(') /*)*/ {
363 PUSH_STATE(SASPAREN
);
364 statep
->ls_sasparen
.nparen
= 2;
365 statep
->ls_sasparen
.start
=
370 PUSH_STATE(SCSPAREN
);
371 statep
->ls_scsparen
.nparen
= 1;
372 statep
->ls_scsparen
.csstate
= 0;
375 } else if (c
== '{') /*}*/ {
378 wp
= get_brace_var(&ws
, wp
);
380 /* allow :# and :% (ksh88 compat) */
382 *wp
++ = CHAR
, *wp
++ = c
;
385 /* If this is a trim operation,
386 * treat (,|,) specially in STBRACE.
388 if (c
== '#' || c
== '%') {
393 if (state
== SDQUOTE
||
399 } else if (ctype(c
, C_ALPHA
)) {
406 } while (ctype(c
, C_ALPHA
) || digit(c
));
411 } else if (ctype(c
, C_VAR1
) || digit(c
)) {
420 *wp
++ = CHAR
, *wp
++ = '$';
427 /* Need to know if we are inside double quotes
428 * since sh/at&t-ksh translate the \" to " in
431 statep
->ls_sbquote
.indquotes
= 0;
432 Lex_state
*s
= statep
;
433 Lex_state
*base
= state_info
.base
;
435 for (; s
!= base
; s
--) {
436 if (s
->ls_state
== SDQUOTE
) {
437 statep
->ls_sbquote
.indquotes
= 1;
443 if (!(s
= s
->ls_info
.base
))
445 base
= s
-- - STATE_BSIZE
;
449 *wp
++ = CHAR
, *wp
++ = c
;
456 if (state
== SBRACEQ
) {
457 *wp
++ = CHAR
, *wp
++ = c
;
461 ignore_backslash_newline
--;
463 *wp
++ = QCHAR
, *wp
++ = c
;
474 case SCSPAREN
: /* $( .. ) */
475 /* todo: deal with $(...) quoting properly
476 * kludge to partly fake quoting inside $(..): doesn't
477 * really work because nested $(..) or ${..} inside
478 * double quotes aren't dealt with.
480 switch (statep
->ls_scsparen
.csstate
) {
484 statep
->ls_scsparen
.nparen
++;
487 statep
->ls_scsparen
.nparen
--;
490 statep
->ls_scsparen
.csstate
= 1;
493 statep
->ls_scsparen
.csstate
= 2;
496 statep
->ls_scsparen
.csstate
= 4;
497 ignore_backslash_newline
++;
502 case 1: /* backslash in normal mode */
503 case 3: /* backslash in double quotes */
504 --statep
->ls_scsparen
.csstate
;
507 case 2: /* double quotes */
509 statep
->ls_scsparen
.csstate
= 0;
511 statep
->ls_scsparen
.csstate
= 3;
514 case 4: /* single quotes */
516 statep
->ls_scsparen
.csstate
= 0;
517 ignore_backslash_newline
--;
521 if (statep
->ls_scsparen
.nparen
== 0) {
523 *wp
++ = 0; /* end of COMSUB */
528 case SASPAREN
: /* $(( .. )) */
529 /* todo: deal with $((...); (...)) properly */
530 /* XXX should nest using existing state machine
531 * (embed "..", $(...), etc.) */
533 statep
->ls_sasparen
.nparen
++;
535 statep
->ls_sasparen
.nparen
--;
536 if (statep
->ls_sasparen
.nparen
== 1) {
538 if ((c2
= getsc()) == ')') {
540 *wp
++ = 0; /* end of EXPRSUB */
546 /* mismatched parenthesis -
547 * assume we were really
548 * parsing a $(..) expression
551 statep
->ls_sasparen
.start
);
552 memmove(s
+ 1, s
, wp
- s
);
556 statep
->ls_scsparen
.nparen
= 1;
557 statep
->ls_scsparen
.csstate
= 0;
558 state
= statep
->ls_state
=
587 /* Same as SBRACE, except (,|,) treated specially */
593 } else if (c
== '|') {
595 } else if (c
== '(') {
597 *wp
++ = ' '; /* simile for @ */
598 PUSH_STATE(SPATTERN
);
607 } else if (c
== '\\') {
608 switch (c
= getsc()) {
614 if (statep
->ls_sbquote
.indquotes
) {
620 if (c
) { /* trailing \ is lost */
630 case SWORD
: /* ONEWORD */
633 case SLETPAREN
: /* LETEXPR: (( ... )) */
636 if (statep
->ls_sletparen
.nparen
> 0)
637 --statep
->ls_sletparen
.nparen
;
639 else if ((c2
= getsc()) == ')') {
646 /* parenthesis inside quotes and backslashes
647 * are lost, but at&t ksh doesn't count them
650 ++statep
->ls_sletparen
.nparen
;
653 case SHEREDELIM
: /* <<,<<- delimiter */
654 /* XXX chuck this state (and the next) - use
655 * the existing states ($ and \`..` should be
656 * stripped of their specialness after the
659 /* here delimiters need a special case since
660 * $ and `..` are not to be treated specially
664 if (c
) { /* trailing \ is lost */
668 } else if (c
== '\'') {
671 ignore_backslash_newline
++;
672 } else if (c
== '"') {
673 state
= statep
->ls_state
= SHEREDQUOTE
;
681 case SHEREDQUOTE
: /* " in <<,<<- delimiter */
684 state
= statep
->ls_state
= SHEREDELIM
;
687 switch (c
= getsc()) {
692 if (c
) { /* trailing \ lost */
704 case SPATTERN
: /* in *(...|...) pattern (*+?@!) */
705 if ( /*(*/ c
== ')') {
708 } else if (c
== '|') {
710 } else if (c
== '(') {
712 *wp
++ = ' '; /* simile for @ */
713 PUSH_STATE(SPATTERN
);
721 if (statep
!= &states
[1])
722 /* XXX figure out what is missing */
723 yyerror("no closing quote\n");
725 /* This done to avoid tests for SHEREDELIM wherever SBASE tested */
726 if (state
== SHEREDELIM
)
729 dp
= Xstring(ws
, wp
);
730 if ((c
== '<' || c
== '>') && state
== SBASE
&&
731 ((c2
= Xlength(ws
, wp
)) == 0 ||
732 (c2
== 2 && dp
[0] == CHAR
&& digit(dp
[1])))) {
733 struct ioword
*iop
= alloc(sizeof(*iop
), ATEMP
);
736 iop
->unit
= dp
[1] - '0';
738 iop
->unit
= c
== '>'; /* 0 for <, 1 for > */
741 /* <<, >>, <> are ok, >< is not */
742 if (c
== c2
|| (c
== '<' && c2
== '>')) {
743 iop
->flag
= c
== c2
?
744 (c
== '>' ? IOCAT
: IOHERE
) : IORDWR
;
745 if (iop
->flag
== IOHERE
) {
746 if ((c2
= getsc()) == '-')
751 } else if (c2
== '&')
752 iop
->flag
= IODUP
| (c
== '<' ? IORDUP
: 0);
754 iop
->flag
= c
== '>' ? IOWRITE
: IOREAD
;
755 if (c
== '>' && c2
== '|')
764 Xfree(ws
, wp
); /* free word */
769 if (wp
== dp
&& state
== SBASE
) {
770 Xfree(ws
, wp
); /* free word */
771 /* no word, process LEX1 character */
779 if ((c2
= getsc()) == c
)
780 c
= (c
== ';') ? BREAK
:
782 (c
== '&') ? LOGAND
:
784 else if (c
== '|' && c2
== '&')
798 if ((c2
= getsc()) == '(') /*)*/
799 /* XXX need to handle ((...); (...)) */
811 *wp
++ = EOS
; /* terminate word */
812 yylval
.cp
= Xclose(ws
, wp
);
813 if (state
== SWORD
|| state
== SLETPAREN
) /* ONEWORD? */
815 ungetsc(c
); /* unget terminator */
817 /* copy word to unprefixed string ident */
818 for (sp
= yylval
.cp
, dp
= ident
; dp
< ident
+IDENT
&& (c
= *sp
++) == CHAR
; )
820 /* Make sure the ident array stays '\0' padded */
821 memset(dp
, 0, (ident
+IDENT
) - dp
+ 1);
823 *ident
= '\0'; /* word is not unquoted */
825 if (*ident
!= '\0' && (cf
&(KEYWORD
|ALIAS
))) {
830 if ((cf
& KEYWORD
) && (p
= ktsearch(&keywords
, ident
, h
)) &&
831 (!(cf
& ESACONLY
) || p
->val
.i
== ESAC
|| p
->val
.i
== '}')) {
832 afree(yylval
.cp
, ATEMP
);
835 if ((cf
& ALIAS
) && (p
= ktsearch(&aliases
, ident
, h
)) &&
839 for (s
= source
; s
->type
== SALIAS
; s
= s
->next
)
842 /* push alias expansion */
843 s
= pushs(SALIAS
, source
->areap
);
844 s
->start
= s
->str
= p
->val
.s
;
848 afree(yylval
.cp
, ATEMP
);
861 for (p
= heres
; p
< herep
; p
++)
867 * read "<<word" text into temp file
871 readhere(struct ioword
*iop
)
881 eof
= evalstr(iop
->delim
, 0);
883 if (!(iop
->flag
& IOEVAL
))
884 ignore_backslash_newline
++;
886 Xinit(xs
, xp
, 256, ATEMP
);
890 skiptabs
= iop
->flag
& IOSKIP
;
891 xpos
= Xsavepos(xs
, xp
);
892 while ((c
= getsc()) != 0) {
904 /* Allow EOF here so commands with out trailing newlines
905 * will work (eg, ksh -c '...', $(...), etc).
907 if (*eofp
== '\0' && (c
== 0 || c
== '\n')) {
908 xp
= Xrestpos(xs
, xp
, xpos
);
912 while ((c
= getsc()) != '\n') {
914 yyerror("here document `%s' unclosed\n", eof
);
922 iop
->heredoc
= Xclose(xs
, xp
);
924 if (!(iop
->flag
& IOEVAL
))
925 ignore_backslash_newline
--;
929 yyerror(const char *fmt
, ...)
933 /* pop aliases and re-reads */
934 while (source
->type
== SALIAS
|| source
->type
== SREREAD
)
935 source
= source
->next
;
936 source
->str
= null
; /* zap pending input */
940 shf_vfprintf(shl_out
, fmt
, va
);
946 * input for yylex with alias expansion
950 pushs(int type
, Area
*areap
)
954 s
= alloc(sizeof(Source
), areap
);
965 if (type
== SFILE
|| type
== SSTDIN
) {
967 Xinit(s
->xs
, dummy
, 256, s
->areap
);
969 memset(&s
->xs
, 0, sizeof(s
->xs
));
979 while ((c
= *s
->str
++) == 0) {
980 s
->str
= NULL
; /* return 0 for EOF by default */
998 s
->start
= s
->str
= *s
->u
.strv
++;
1003 if (*s
->u
.strv
== NULL
) {
1004 s
->start
= s
->str
= "\n";
1007 s
->start
= s
->str
= " ";
1013 if (s
->flags
& SF_ALIASEND
) {
1014 /* pass on an unused SF_ALIAS flag */
1016 source
->flags
|= s
->flags
& SF_ALIAS
;
1018 } else if (*s
->u
.tblp
->val
.s
&&
1019 isspace((unsigned char)strchr(s
->u
.tblp
->val
.s
, 0)[-1])) {
1020 source
= s
= s
->next
; /* pop source stack */
1021 /* Note that this alias ended with a space,
1022 * enabling alias expansion on the following
1025 s
->flags
|= SF_ALIAS
;
1027 /* At this point, we need to keep the current
1028 * alias in the source list so recursive
1029 * aliases can be detected and we also need
1030 * to return the next character. Do this
1031 * by temporarily popping the alias to get
1032 * the next character and then put it back
1033 * in the source list with the SF_ALIASEND
1036 source
= s
->next
; /* pop source stack */
1037 source
->flags
|= s
->flags
& SF_ALIAS
;
1040 s
->flags
|= SF_ALIASEND
;
1041 s
->ugbuf
[0] = c
; s
->ugbuf
[1] = '\0';
1042 s
->start
= s
->str
= s
->ugbuf
;
1047 /* avoid reading eof twice */
1055 if (s
->start
!= s
->ugbuf
) /* yuck */
1056 afree(s
->u
.freeme
, ATEMP
);
1057 source
= s
= s
->next
;
1060 if (s
->str
== NULL
) {
1062 s
->start
= s
->str
= null
;
1065 if (s
->flags
& SF_ECHO
) {
1066 shf_puts(s
->str
, shl_out
);
1074 getsc_line(Source
*s
)
1076 char *xp
= Xstring(s
->xs
, xp
);
1077 int interactive
= Flag(FTALKING
) && s
->type
== SSTDIN
;
1078 int have_tty
= interactive
&& (s
->flags
& SF_TTY
);
1080 /* Done here to ensure nothing odd happens when a timeout occurs */
1081 XcheckN(s
->xs
, xp
, LINE
);
1083 s
->start
= s
->str
= xp
;
1085 if (have_tty
&& ksh_tmout
) {
1086 ksh_tmout_state
= TMOUT_READING
;
1094 || Flag(FEMACS
) || Flag(FGMACS
)
1099 nread
= x_read(xp
, LINE
);
1100 if (nread
< 0) /* read error */
1111 char *p
= shf_getse(xp
, Xnleft(s
->xs
, xp
), s
->u
.shf
);
1113 if (!p
&& shf_error(s
->u
.shf
) &&
1114 s
->u
.shf
->errno_
== EINTR
) {
1115 shf_clearerr(s
->u
.shf
);
1120 if (!p
|| (xp
= p
, xp
[-1] == '\n'))
1122 /* double buffer size */
1123 xp
++; /* move past null so doubling works... */
1124 XcheckN(s
->xs
, xp
, Xlength(s
->xs
, xp
));
1125 xp
--; /* ...and move back again */
1127 /* flush any unwanted input so other programs/builtins
1128 * can read it. Not very optimal, but less error prone
1129 * than flushing else where, dealing with redirections,
1131 * todo: reduce size of shf buffer (~128?) if SSTDIN
1133 if (s
->type
== SSTDIN
)
1134 shf_flush(s
->u
.shf
);
1136 /* XXX: temporary kludge to restore source after a
1137 * trap may have been executed.
1140 if (have_tty
&& ksh_tmout
) {
1141 ksh_tmout_state
= TMOUT_EXECUTING
;
1144 s
->start
= s
->str
= Xstring(s
->xs
, xp
);
1145 strip_nuls(Xstring(s
->xs
, xp
), Xlength(s
->xs
, xp
));
1146 /* Note: if input is all nulls, this is not eof */
1147 if (Xlength(s
->xs
, xp
) == 0) { /* EOF */
1148 if (s
->type
== SFILE
)
1149 shf_fdclose(s
->u
.shf
);
1151 } else if (interactive
) {
1152 char *p
= Xstring(s
->xs
, xp
);
1153 if (cur_prompt
== PS1
)
1154 while (*p
&& ctype(*p
, C_IFS
) && ctype(*p
, C_IFSWS
))
1158 histsave(s
->line
, s
->str
, 1);
1166 special_prompt_expand(char *str
)
1170 while ((p
= strstr(p
, "\\$")) != NULL
) {
1185 case PS1
: /* command */
1186 ps1
= str_save(str_val(global("PS1")), ATEMP
);
1187 saved_atemp
= ATEMP
; /* ps1 is freed by substitute() */
1189 if (sigsetjmp(genv
->jbuf
, 0)) {
1190 prompt
= safe_prompt
;
1191 /* Don't print an error - assume it has already
1192 * been printed. Reason is we may have forked
1193 * to run a command and the child may be
1194 * unwinding its stack through this code as it
1198 /* expand \$ before other substitutions are done */
1199 char *tmp
= special_prompt_expand(ps1
);
1200 prompt
= str_save(substitute(tmp
, 0), saved_atemp
);
1204 case PS2
: /* command continuation */
1205 prompt
= str_val(global("PS2"));
1211 dopprompt(const char *sp
, int ntruncate
, const char **spp
, int doprint
)
1213 char strbuf
[1024], tmpbuf
[1024], *p
, *str
, nbuf
[32], delimiter
= '\0';
1214 int len
, c
, n
, totlen
= 0, indelimit
= 0, counting
= 1, delimitthis
;
1215 const char *cp
= sp
;
1219 if (*cp
&& cp
[1] == '\r') {
1226 if (indelimit
&& *cp
!= delimiter
)
1228 else if (*cp
== '\n' || *cp
== '\r') {
1231 } else if (*cp
== '\t') {
1233 totlen
= (totlen
| 7) + 1;
1234 } else if (*cp
== delimiter
) {
1235 indelimit
= !indelimit
;
1243 /* Expand \h and \$ for both, sh(1) and ksh(1) */
1244 if (Flag(FSH
) && !(*cp
== 'h' || *cp
== 'p'))
1245 snprintf(strbuf
, sizeof strbuf
, "\\%c", *cp
);
1247 case 'a': /* '\' 'a' bell */
1251 case 'd': /* '\' 'd' Dow Mon DD */
1254 strftime(strbuf
, sizeof strbuf
, "%a %b %d", tm
);
1256 case 'D': /* '\' 'D' '{' strftime format '}' */
1257 p
= strchr(cp
+ 2, '}');
1258 if (cp
[1] != '{' || p
== NULL
) {
1259 snprintf(strbuf
, sizeof strbuf
,
1263 strlcpy(tmpbuf
, cp
+ 2, sizeof tmpbuf
);
1264 p
= strchr(tmpbuf
, '}');
1269 strftime(strbuf
, sizeof strbuf
, tmpbuf
, tm
);
1270 cp
= strchr(cp
+ 2, '}');
1272 case 'e': /* '\' 'e' escape */
1276 case 'h': /* '\' 'h' shortened hostname */
1277 gethostname(strbuf
, sizeof strbuf
);
1278 p
= strchr(strbuf
, '.');
1282 case 'H': /* '\' 'H' full hostname */
1283 gethostname(strbuf
, sizeof strbuf
);
1285 case 'j': /* '\' 'j' number of jobs */
1286 snprintf(strbuf
, sizeof strbuf
, "%d",
1289 case 'l': /* '\' 'l' basename of tty */
1294 strlcpy(strbuf
, p
, sizeof strbuf
);
1296 case 'n': /* '\' 'n' newline */
1299 totlen
= 0; /* reset for prompt re-print */
1302 case 'p': /* '\' '$' $ or # */
1303 strbuf
[0] = ksheuid
? '$' : '#';
1306 case 'r': /* '\' 'r' return */
1309 totlen
= 0; /* reset for prompt re-print */
1312 case 's': /* '\' 's' basename $0 */
1313 strlcpy(strbuf
, kshname
, sizeof strbuf
);
1315 case 't': /* '\' 't' 24 hour HH:MM:SS */
1318 strftime(strbuf
, sizeof strbuf
, "%T", tm
);
1320 case 'T': /* '\' 'T' 12 hour HH:MM:SS */
1323 strftime(strbuf
, sizeof strbuf
, "%l:%M:%S", tm
);
1325 case '@': /* '\' '@' 12 hour am/pm format */
1328 strftime(strbuf
, sizeof strbuf
, "%r", tm
);
1330 case 'A': /* '\' 'A' 24 hour HH:MM */
1333 strftime(strbuf
, sizeof strbuf
, "%R", tm
);
1335 case 'u': /* '\' 'u' username */
1336 strlcpy(strbuf
, username
, sizeof strbuf
);
1338 case 'v': /* '\' 'v' version (short) */
1339 p
= strchr(ksh_version
, ' ');
1341 p
= strchr(p
+ 1, ' ');
1344 strlcpy(strbuf
, p
, sizeof strbuf
);
1345 p
= strchr(strbuf
, ' ');
1350 case 'V': /* '\' 'V' version (long) */
1351 strlcpy(strbuf
, ksh_version
, sizeof strbuf
);
1353 case 'w': /* '\' 'w' cwd */
1354 p
= str_val(global("PWD"));
1355 n
= strlen(str_val(global("HOME")));
1356 if (strcmp(p
, "/") == 0) {
1357 strlcpy(strbuf
, p
, sizeof strbuf
);
1358 } else if (strcmp(p
, str_val(global("HOME"))) == 0) {
1361 } else if (strncmp(p
, str_val(global("HOME")), n
)
1362 == 0 && p
[n
] == '/') {
1363 snprintf(strbuf
, sizeof strbuf
, "~/%s",
1364 str_val(global("PWD")) + n
+ 1);
1366 strlcpy(strbuf
, p
, sizeof strbuf
);
1368 case 'W': /* '\' 'W' basename(cwd) */
1369 p
= str_val(global("PWD"));
1370 if (strcmp(p
, str_val(global("HOME"))) == 0) {
1374 strlcpy(strbuf
, basename(p
), sizeof strbuf
);
1376 case '!': /* '\' '!' history line number */
1377 snprintf(strbuf
, sizeof strbuf
, "%d",
1380 case '#': /* '\' '#' command line number */
1381 snprintf(strbuf
, sizeof strbuf
, "%d",
1382 source
->line
- source
->cmd_offset
+ 1);
1384 case '0': /* '\' '#' '#' ' #' octal numeric handling */
1392 if ((cp
[1] > '7' || cp
[1] < '0') ||
1393 (cp
[2] > '7' || cp
[2] < '0')) {
1394 snprintf(strbuf
, sizeof strbuf
,
1398 n
= (cp
[0] - '0') * 8 * 8 + (cp
[1] - '0') * 8 +
1400 snprintf(strbuf
, sizeof strbuf
, "%c", n
);
1403 case '\\': /* '\' '\' */
1407 case '[': /* '\' '[' .... stop counting */
1411 case ']': /* '\' ']' restart counting */
1417 snprintf(strbuf
, sizeof strbuf
, "\\%c", *cp
);
1425 if (ntruncate
>= len
) {
1434 shf_write(str
, len
, shl_out
);
1435 if (counting
&& !indelimit
&& !delimitthis
)
1438 } else if (*cp
!= '!')
1440 else if (*++cp
== '!')
1443 shf_snprintf(p
= nbuf
, sizeof(nbuf
), "%d",
1447 if (ntruncate
>= len
) {
1456 shf_write(p
, len
, shl_out
);
1457 if (counting
&& !indelimit
&& !delimitthis
)
1461 if (counting
&& ntruncate
)
1464 shf_putc(c
, shl_out
);
1466 if (counting
&& !indelimit
&& !delimitthis
)
1477 pprompt(const char *cp
, int ntruncate
)
1479 dopprompt(cp
, ntruncate
, NULL
, 1);
1483 promptlen(const char *cp
, const char **spp
)
1485 return dopprompt(cp
, 0, spp
, 0);
1488 /* Read the variable part of a ${...} expression (ie, up to but not including
1489 * the :[-+?=#%] or close-brace.
1492 get_brace_var(XString
*wsp
, char *wp
)
1495 PS_INITIAL
, PS_SAW_HASH
, PS_IDENT
,
1496 PS_NUMBER
, PS_VAR1
, PS_END
1504 /* State machine to figure out where the variable part ends. */
1508 state
= PS_SAW_HASH
;
1517 else if (ctype(c
, C_VAR1
))
1528 if (!arraysub(&tmp
))
1529 yyerror("missing ]\n");
1531 for (p
= tmp
; *p
; ) {
1536 c
= getsc(); /* the ] */
1547 case PS_END
: /* keep gcc happy */
1550 if (state
== PS_END
) {
1551 *wp
++ = '\0'; /* end of variable part */
1562 * Save an array subscript - returns true if matching bracket found, false
1563 * if eof or newline was found.
1564 * (Returned string double null terminated)
1567 arraysub(char **strp
)
1572 int depth
= 1; /* we are just past the initial [ */
1574 Xinit(ws
, wp
, 32, ATEMP
);
1584 } while (depth
> 0 && c
&& c
!= '\n');
1587 *strp
= Xclose(ws
, wp
);
1589 return depth
== 0 ? 1 : 0;
1592 /* Unget a char: handles case when we are already at the start of the buffer */
1598 /* Don't unget eof... */
1599 if (source
->str
== null
&& c
== '\0')
1601 if (source
->str
> source
->start
)
1606 s
= pushs(SREREAD
, source
->areap
);
1607 s
->ugbuf
[0] = c
; s
->ugbuf
[1] = '\0';
1608 s
->start
= s
->str
= s
->ugbuf
;
1616 /* Called to get a char that isn't a \newline sequence. */
1622 if (ignore_backslash_newline
)
1625 if (backslash_skip
== 1) {
1635 if ((c2
= getsc_()) == '\n')
1636 /* ignore the \newline; get the next char... */
1646 push_state_(State_info
*si
, Lex_state
*old_end
)
1648 Lex_state
*new = areallocarray(NULL
, STATE_BSIZE
,
1649 sizeof(Lex_state
), ATEMP
);
1651 new[0].ls_info
.base
= old_end
;
1653 si
->end
= &new[STATE_BSIZE
];
1658 pop_state_(State_info
*si
, Lex_state
*old_end
)
1660 Lex_state
*old_base
= si
->base
;
1662 si
->base
= old_end
->ls_info
.base
- STATE_BSIZE
;
1663 si
->end
= old_end
->ls_info
.base
;
1665 afree(old_base
, ATEMP
);
1667 return si
->base
+ STATE_BSIZE
- 1;