2 * lexical analysis and source input
10 /* Structure to keep track of the lexing state and the various pieces of info
11 * needed for each particular state.
13 typedef struct lex_state Lex_state
;
18 struct scsparen_info
{
19 int nparen
; /* count open parenthesis */
20 int csstate
; /* XXX remove */
21 #define ls_scsparen ls_info.u_scsparen
25 struct sasparen_info
{
26 int nparen
; /* count open parenthesis */
27 int start
; /* marks start of $(( in output str */
28 #define ls_sasparen ls_info.u_sasparen
32 struct sletparen_info
{
33 int nparen
; /* count open parenthesis */
34 #define ls_sletparen ls_info.u_sletparen
39 int indquotes
; /* true if in double quotes: "`...`" */
40 #define ls_sbquote ls_info.u_sbquote
43 Lex_state
*base
; /* used to point to next state block */
47 typedef struct State_info State_info
;
54 static void readhere(struct ioword
*);
55 static int getsc__(void);
56 static void getsc_line(Source
*);
57 static int getsc_bn(void);
58 static char *get_brace_var(XString
*, char *);
59 static int arraysub(char **);
60 static const char *ungetsc(int);
61 static void gethere(void);
62 static Lex_state
*push_state_(State_info
*, Lex_state
*);
63 static Lex_state
*pop_state_(State_info
*, Lex_state
*);
64 static char *special_prompt_expand(char *);
66 static int backslash_skip
;
67 static int ignore_backslash_newline
;
69 /* optimized getsc_bn() */
70 #define getsc() (*source->str != '\0' && *source->str != '\\' \
71 && !backslash_skip ? *source->str++ : getsc_bn())
72 /* optimized getsc__() */
73 #define getsc_() ((*source->str != '\0') ? *source->str++ : getsc__())
75 #define STATE_BSIZE 32
77 #define PUSH_STATE(s) do { \
78 if (++statep == state_info.end) \
79 statep = push_state_(&state_info, statep); \
80 state = statep->ls_state = (s); \
83 #define POP_STATE() do { \
84 if (--statep == state_info.base) \
85 statep = pop_state_(&state_info, statep); \
86 state = statep->ls_state; \
94 * tokens are not regular expressions, they are LL(1).
95 * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
96 * hence the state stack.
102 Lex_state states
[STATE_BSIZE
], *statep
;
103 State_info state_info
;
105 XString ws
; /* expandable output word */
106 char *wp
; /* output word pointer */
109 int last_terminal_was_bracket
;
113 states
[0].ls_state
= -1;
114 states
[0].ls_info
.base
= (Lex_state
*) 0;
116 state_info
.base
= states
;
117 state_info
.end
= &states
[STATE_BSIZE
];
119 Xinit(ws
, wp
, 64, ATEMP
);
122 ignore_backslash_newline
= 0;
126 else if (cf
&LETEXPR
) {
127 *wp
++ = OQUOTE
; /* enclose arguments in (double) quotes */
129 statep
->ls_sletparen
.nparen
= 0;
131 else { /* normal lexing */
132 state
= (cf
& HEREDELIM
) ? SHEREDELIM
: SBASE
;
133 while ((c
= getsc()) == ' ' || c
== '\t')
136 ignore_backslash_newline
++;
137 while ((c
= getsc()) != '\0' && c
!= '\n')
139 ignore_backslash_newline
--;
143 if (source
->flags
& SF_ALIAS
) { /* trailing ' ' in alias definition */
144 source
->flags
&= ~SF_ALIAS
;
145 /* In POSIX mode, a trailing space only counts if we are
146 * parsing a simple command
148 if (!Flag(FPOSIX
) || (cf
& CMDWORD
))
152 /* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */
153 statep
->ls_state
= state
;
155 /* collect non-special or quoted characters to form word */
156 while (!((c
= getsc()) == 0 ||
157 ((state
== SBASE
|| state
== SHEREDELIM
) && ctype(c
, C_LEX1
))))
162 if (c
== '[' && (cf
& (VARASN
|ARRAYVAR
))) {
163 *wp
= EOS
; /* temporary */
164 if (is_wdvarname(Xstring(ws
, wp
), false))
168 if (arraysub(&tmp
)) {
171 for (p
= tmp
; *p
; ) {
194 Sbase1
: /* includes *(...|...) pattern (*+?@!) */
195 if (c
== '*' || c
== '@' || c
== '+' || c
== '?' ||
199 if (c2
== '(' /*)*/ ) {
202 PUSH_STATE(SPATTERN
);
208 Sbase2
: /* doesn't include *(...|...) pattern (*+?@!) */
212 if (c
) /* trailing \ is lost */
213 *wp
++ = QCHAR
, *wp
++ = c
;
217 ignore_backslash_newline
++;
236 *wp
++ = QCHAR
, *wp
++ = c
;
240 if (c
) { /* trailing \ is lost */
241 *wp
++ = CHAR
, *wp
++ = '\\';
242 *wp
++ = CHAR
, *wp
++ = c
;
249 if (c
== '(') /*)*/ {
251 if (c
== '(') /*)*/ {
252 PUSH_STATE(SASPAREN
);
253 statep
->ls_sasparen
.nparen
= 2;
254 statep
->ls_sasparen
.start
=
259 PUSH_STATE(SCSPAREN
);
260 statep
->ls_scsparen
.nparen
= 1;
261 statep
->ls_scsparen
.csstate
= 0;
264 } else if (c
== '{') /*}*/ {
267 wp
= get_brace_var(&ws
, wp
);
269 /* allow :# and :% (ksh88 compat) */
271 *wp
++ = CHAR
, *wp
++ = c
;
274 /* If this is a trim operation,
275 * treat (,|,) specially in STBRACE.
277 if (c
== '#' || c
== '%') {
284 } else if (ctype(c
, C_ALPHA
)) {
291 } while (ctype(c
, C_ALPHA
|C_DIGIT
));
296 } else if (ctype(c
, C_DIGIT
|C_VAR1
)) {
305 *wp
++ = CHAR
, *wp
++ = '$';
312 /* Need to know if we are inside double quotes
313 * since sh/at&t-ksh translate the \" to " in
315 * This is not done in posix mode (section
316 * 3.2.3, Double Quotes: "The backquote shall
317 * retain its special meaning introducing the
318 * other form of command substitution (see
319 * 3.6.3). The portion of the quoted string
320 * from the initial backquote and the
321 * characters up to the next backquote that
322 * is not preceded by a backslash (having
323 * escape characters removed) defines that
324 * command whose output replaces `...` when
325 * the word is expanded."
326 * Section 3.6.3, Command Substitution:
327 * "Within the backquoted style of command
328 * substitution, backslash shall retain its
329 * literal meaning, except when followed by
332 statep
->ls_sbquote
.indquotes
= 0;
334 Lex_state
*s
= statep
;
335 Lex_state
*base
= state_info
.base
;
337 for (; s
!= base
; s
--) {
338 if (s
->ls_state
== SDQUOTE
) {
339 statep
->ls_sbquote
.indquotes
= 1;
345 if (!(s
= s
->ls_info
.base
))
347 base
= s
-- - STATE_BSIZE
;
352 *wp
++ = CHAR
, *wp
++ = c
;
360 ignore_backslash_newline
--;
362 *wp
++ = QCHAR
, *wp
++ = c
;
373 case SCSPAREN
: /* $( .. ) */
374 /* todo: deal with $(...) quoting properly
375 * kludge to partly fake quoting inside $(..): doesn't
376 * really work because nested $(..) or ${..} inside
377 * double quotes aren't dealt with.
379 switch (statep
->ls_scsparen
.csstate
) {
383 statep
->ls_scsparen
.nparen
++;
386 statep
->ls_scsparen
.nparen
--;
389 statep
->ls_scsparen
.csstate
= 1;
392 statep
->ls_scsparen
.csstate
= 2;
395 statep
->ls_scsparen
.csstate
= 4;
396 ignore_backslash_newline
++;
401 case 1: /* backslash in normal mode */
402 case 3: /* backslash in double quotes */
403 --statep
->ls_scsparen
.csstate
;
406 case 2: /* double quotes */
408 statep
->ls_scsparen
.csstate
= 0;
410 statep
->ls_scsparen
.csstate
= 3;
413 case 4: /* single quotes */
415 statep
->ls_scsparen
.csstate
= 0;
416 ignore_backslash_newline
--;
420 if (statep
->ls_scsparen
.nparen
== 0) {
422 *wp
++ = 0; /* end of COMSUB */
427 case SASPAREN
: /* $(( .. )) */
428 /* todo: deal with $((...); (...)) properly */
429 /* XXX should nest using existing state machine
430 * (embed "..", $(...), etc.) */
432 statep
->ls_sasparen
.nparen
++;
434 statep
->ls_sasparen
.nparen
--;
435 if (statep
->ls_sasparen
.nparen
== 1) {
437 if ((c2
= getsc()) == ')') {
439 *wp
++ = 0; /* end of EXPRSUB */
445 /* mismatched parenthesis -
446 * assume we were really
447 * parsing a $(..) expression
450 statep
->ls_sasparen
.start
);
451 memmove(s
+ 1, s
, wp
- s
);
455 statep
->ls_scsparen
.nparen
= 1;
456 statep
->ls_scsparen
.csstate
= 0;
457 state
= statep
->ls_state
477 /* Same as SBRACE, except (,|,) treated specially */
483 } else if (c
== '|') {
485 } else if (c
== '(') {
487 *wp
++ = ' '; /* simile for @ */
488 PUSH_STATE(SPATTERN
);
497 } else if (c
== '\\') {
498 switch (c
= getsc()) {
504 if (statep
->ls_sbquote
.indquotes
) {
510 if (c
) { /* trailing \ is lost */
520 case SWORD
: /* ONEWORD */
523 case SLETPAREN
: /* LETEXPR: (( ... )) */
526 if (statep
->ls_sletparen
.nparen
> 0)
527 --statep
->ls_sletparen
.nparen
;
529 else if ((c2
= getsc()) == ')') {
536 /* parenthesis inside quotes and backslashes
537 * are lost, but at&t ksh doesn't count them
540 ++statep
->ls_sletparen
.nparen
;
543 case SHEREDELIM
: /* <<,<<- delimiter */
544 /* XXX chuck this state (and the next) - use
545 * the existing states ($ and \`..` should be
546 * stripped of their specialness after the
549 /* here delimiters need a special case since
550 * $ and `..` are not to be treated specially
554 if (c
) { /* trailing \ is lost */
558 } else if (c
== '\'') {
561 ignore_backslash_newline
++;
562 } else if (c
== '"') {
563 state
= statep
->ls_state
= SHEREDQUOTE
;
571 case SHEREDQUOTE
: /* " in <<,<<- delimiter */
574 state
= statep
->ls_state
= SHEREDELIM
;
577 switch (c
= getsc()) {
582 if (c
) { /* trailing \ lost */
594 case SPATTERN
: /* in *(...|...) pattern (*+?@!) */
595 if ( /*(*/ c
== ')') {
598 } else if (c
== '|') {
600 } else if (c
== '(') {
602 *wp
++ = ' '; /* simile for @ */
603 PUSH_STATE(SPATTERN
);
611 if (statep
!= &states
[1])
612 /* XXX figure out what is missing */
613 yyerror("no closing quote\n");
615 /* This done to avoid tests for SHEREDELIM wherever SBASE tested */
616 if (state
== SHEREDELIM
)
619 dp
= Xstring(ws
, wp
);
620 if ((c
== '<' || c
== '>') && state
== SBASE
621 && ((c2
= Xlength(ws
, wp
)) == 0
622 || (c2
== 2 && dp
[0] == CHAR
&& digit(dp
[1]))))
625 (struct ioword
*) alloc(sizeof(*iop
), ATEMP
);
628 iop
->unit
= dp
[1] - '0';
630 iop
->unit
= c
== '>'; /* 0 for <, 1 for > */
633 /* <<, >>, <> are ok, >< is not */
634 if (c
== c2
|| (c
== '<' && c2
== '>')) {
635 iop
->flag
= c
== c2
?
636 (c
== '>' ? IOCAT
: IOHERE
) : IORDWR
;
637 if (iop
->flag
== IOHERE
) {
638 if ((c2
= getsc()) == '-')
643 } else if (c2
== '&')
644 iop
->flag
= IODUP
| (c
== '<' ? IORDUP
: 0);
646 iop
->flag
= c
== '>' ? IOWRITE
: IOREAD
;
647 if (c
== '>' && c2
== '|')
653 iop
->name
= (char *) 0;
654 iop
->delim
= (char *) 0;
655 iop
->heredoc
= (char *) 0;
656 Xfree(ws
, wp
); /* free word */
661 if (wp
== dp
&& state
== SBASE
) {
662 Xfree(ws
, wp
); /* free word */
663 /* no word, process LEX1 character */
671 if ((c2
= getsc()) == c
)
672 c
= (c
== ';') ? BREAK
:
674 (c
== '&') ? LOGAND
:
676 else if (c
== '|' && c2
== '&')
690 if ((c2
= getsc()) == '(') /*)*/
691 /* XXX need to handle ((...); (...)) */
703 *wp
++ = EOS
; /* terminate word */
704 yylval
.cp
= Xclose(ws
, wp
);
705 if (state
== SWORD
|| state
== SLETPAREN
) /* ONEWORD? */
708 last_terminal_was_bracket
= c
== '(';
709 ungetsc(c
); /* unget terminator */
711 /* copy word to unprefixed string ident */
712 for (sp
= yylval
.cp
, dp
= ident
; dp
< ident
+IDENT
&& (c
= *sp
++) == CHAR
; )
714 /* Make sure the ident array stays '\0' paded */
715 memset(dp
, 0, (ident
+IDENT
) - dp
+ 1);
717 *ident
= '\0'; /* word is not unquoted */
719 if (*ident
!= '\0' && (cf
&(KEYWORD
|ALIAS
))) {
724 if ((cf
& KEYWORD
) && (p
= ktsearch(&keywords
, ident
, h
))
725 && (!(cf
& ESACONLY
) || p
->val
.i
== ESAC
|| p
->val
.i
== '}'))
727 afree(yylval
.cp
, ATEMP
);
730 if ((cf
& ALIAS
) && (p
= ktsearch(aliases
, ident
, h
))
731 && (p
->flag
& ISSET
))
734 if (last_terminal_was_bracket
) {
735 /* The token is probably part of function's definition,
736 * and is should not be aliased. Moreover we remove the alias
737 * so it won't clash with the function name
738 * robert@debian.org, Feb 26th, 2005
744 for (s
= source
; s
->type
== SALIAS
; s
= s
->next
)
747 /* push alias expansion */
748 s
= pushs(SALIAS
, source
->areap
);
749 s
->start
= s
->str
= p
->val
.s
;
753 afree(yylval
.cp
, ATEMP
);
767 for (p
= heres
; p
< herep
; p
++)
773 * read "<<word" text into temp file
777 readhere(struct ioword
*iop
)
787 eof
= evalstr(iop
->delim
, 0);
789 if (!(iop
->flag
& IOEVAL
))
790 ignore_backslash_newline
++;
792 Xinit(xs
, xp
, 256, ATEMP
);
796 skiptabs
= iop
->flag
& IOSKIP
;
797 xpos
= Xsavepos(xs
, xp
);
798 while ((c
= getsc()) != 0) {
810 /* Allow EOF here so commands with out trailing newlines
811 * will work (eg, ksh -c '...', $(...), etc).
813 if (*eofp
== '\0' && (c
== 0 || c
== '\n')) {
814 xp
= Xrestpos(xs
, xp
, xpos
);
818 while ((c
= getsc()) != '\n') {
820 yyerror("here document `%s' unclosed\n", eof
);
828 iop
->heredoc
= Xclose(xs
, xp
);
830 if (!(iop
->flag
& IOEVAL
))
831 ignore_backslash_newline
--;
835 yyerror(const char *fmt
, ...)
839 /* pop aliases and re-reads */
840 while (source
->type
== SALIAS
|| source
->type
== SREREAD
)
841 source
= source
->next
;
842 source
->str
= null
; /* zap pending input */
846 shf_vfprintf(shl_out
, fmt
, va
);
852 * input for yylex with alias expansion
856 pushs(int type
, Area
*areap
)
860 s
= (Source
*) alloc(sizeof(Source
), areap
);
871 if (type
== SFILE
|| type
== SSTDIN
) {
873 Xinit(s
->xs
, dummy
, 256, s
->areap
);
874 (void)dummy
; // Unused
876 memset(&s
->xs
, 0, sizeof(s
->xs
));
886 while ((c
= *s
->str
++) == 0) {
887 s
->str
= NULL
; /* return 0 for EOF by default */
905 s
->start
= s
->str
= *s
->u
.strv
++;
910 if (*s
->u
.strv
== NULL
) {
911 s
->start
= s
->str
= newline
;
914 s
->start
= s
->str
= space
;
920 if (s
->flags
& SF_ALIASEND
) {
921 /* pass on an unused SF_ALIAS flag */
923 source
->flags
|= s
->flags
& SF_ALIAS
;
925 } else if (*s
->u
.tblp
->val
.s
926 && isspace(strchr(s
->u
.tblp
->val
.s
, 0)[-1]))
928 source
= s
= s
->next
; /* pop source stack */
929 /* Note that this alias ended with a space,
930 * enabling alias expansion on the following
933 s
->flags
|= SF_ALIAS
;
935 /* At this point, we need to keep the current
936 * alias in the source list so recursive
937 * aliases can be detected and we also need
938 * to return the next character. Do this
939 * by temporarily popping the alias to get
940 * the next character and then put it back
941 * in the source list with the SF_ALIASEND
944 source
= s
->next
; /* pop source stack */
945 source
->flags
|= s
->flags
& SF_ALIAS
;
948 s
->flags
|= SF_ALIASEND
;
949 s
->ugbuf
[0] = c
; s
->ugbuf
[1] = '\0';
950 s
->start
= s
->str
= s
->ugbuf
;
955 /* avoid reading eof twice */
963 if (s
->start
!= s
->ugbuf
) /* yuck */
964 afree(s
->u
.freeme
, ATEMP
);
965 source
= s
= s
->next
;
968 if (s
->str
== NULL
) {
970 s
->start
= s
->str
= null
;
973 if (s
->flags
& SF_ECHO
) {
974 shf_puts(s
->str
, shl_out
);
982 getsc_line(Source
*s
)
984 char *xp
= Xstring(s
->xs
, xp
);
985 int interactive
= Flag(FTALKING
) && s
->type
== SSTDIN
;
986 int have_tty
= interactive
&& (s
->flags
& SF_TTY
);
988 /* Done here to ensure nothing odd happens when a timeout occurs */
989 XcheckN(s
->xs
, xp
, LINE
);
991 s
->start
= s
->str
= xp
;
993 if (have_tty
&& ksh_tmout
) {
994 ksh_tmout_state
= TMOUT_READING
;
1004 char *p
= shf_getse(xp
, Xnleft(s
->xs
, xp
), s
->u
.shf
);
1006 if (!p
&& shf_error(s
->u
.shf
)
1007 && shf_errno(s
->u
.shf
) == EINTR
)
1009 shf_clearerr(s
->u
.shf
);
1014 if (!p
|| (xp
= p
, xp
[-1] == '\n'))
1016 /* double buffer size */
1017 xp
++; /* move past null so doubling works... */
1018 XcheckN(s
->xs
, xp
, Xlength(s
->xs
, xp
));
1019 xp
--; /* ...and move back again */
1021 /* flush any unwanted input so other programs/builtins
1022 * can read it. Not very optimal, but less error prone
1023 * than flushing else where, dealing with redirections,
1025 * todo: reduce size of shf buffer (~128?) if SSTDIN
1027 if (s
->type
== SSTDIN
)
1028 shf_flush(s
->u
.shf
);
1030 /* XXX: temporary kludge to restore source after a
1031 * trap may have been executed.
1034 if (have_tty
&& ksh_tmout
)
1036 ksh_tmout_state
= TMOUT_EXECUTING
;
1039 s
->start
= s
->str
= Xstring(s
->xs
, xp
);
1040 strip_nuls(Xstring(s
->xs
, xp
), Xlength(s
->xs
, xp
));
1041 /* Note: if input is all nulls, this is not eof */
1042 if (Xlength(s
->xs
, xp
) == 0) { /* EOF */
1043 if (s
->type
== SFILE
)
1044 shf_fdclose(s
->u
.shf
);
1046 } else if (interactive
) {
1048 char *p
= Xstring(s
->xs
, xp
);
1049 if (cur_prompt
== PS1
)
1050 while (*p
&& ctype(*p
, C_IFS
) && ctype(*p
, C_IFSWS
))
1054 histsave(s
->line
, s
->str
, 1);
1056 #endif /* HISTORY */
1059 set_prompt(PS2
, (Source
*) 0);
1063 special_prompt_expand(char *str
)
1067 while ((p
= strstr(p
, "\\$")) != NULL
) {
1074 set_prompt(int to
, Source
*s
)
1081 case PS1
: /* command */
1082 ps1
= str_save(str_val(global("PS1")), ATEMP
);
1083 saved_atemp
= ATEMP
; /* ps1 is freed by substitute() */
1085 if (ksh_sigsetjmp(e
->jbuf
, 0)) {
1086 prompt
= safe_prompt
;
1087 /* Don't print an error - assume it has already
1088 * been printed. Reason is we may have forked
1089 * to run a command and the child may be
1090 * unwinding its stack through this code as it
1094 /* expand \$ before other substitutions are done */
1095 char *tmp
= special_prompt_expand(ps1
);
1096 prompt
= str_save(substitute(tmp
, 0), saved_atemp
);
1101 case PS2
: /* command continuation */
1102 prompt
= str_val(global("PS2"));
1108 static int gethostname(char* name
, size_t len
)
1116 dopprompt(const char *sp
, int ntruncate
, const char **spp
, int doprint
)
1118 char strbuf
[1024], tmpbuf
[1024], *p
, *str
, nbuf
[32], delimiter
= '\0';
1119 int len
, c
, n
, totlen
= 0, indelimit
= 0, counting
= 1, delimitthis
;
1120 const char *cp
= sp
;
1124 if (*cp
&& cp
[1] == '\r') {
1131 if (indelimit
&& *cp
!= delimiter
)
1133 else if (*cp
== '\n' || *cp
== '\r') {
1136 } else if (*cp
== '\t') {
1138 totlen
= (totlen
| 7) + 1;
1139 } else if (*cp
== delimiter
) {
1140 indelimit
= !indelimit
;
1149 snprintf(strbuf
, sizeof strbuf
, "\\%c", *cp
);
1151 case 'a': /* '\' 'a' bell */
1155 case 'd': /* '\' 'd' Dow Mon DD */
1158 strftime(strbuf
, sizeof strbuf
, "%a %b %d", tm
);
1160 case 'D': /* '\' 'D' '{' strftime format '}' */
1161 p
= strchr(cp
+ 2, '}');
1162 if (cp
[1] != '{' || p
== NULL
) {
1163 snprintf(strbuf
, sizeof strbuf
,
1167 strlcpy(tmpbuf
, cp
+ 2, sizeof tmpbuf
);
1168 p
= strchr(tmpbuf
, '}');
1173 strftime(strbuf
, sizeof strbuf
, tmpbuf
, tm
);
1174 cp
= strchr(cp
+ 2, '}');
1176 case 'e': /* '\' 'e' escape */
1180 case 'h': /* '\' 'h' shortened hostname */
1181 gethostname(strbuf
, sizeof strbuf
);
1182 p
= strchr(strbuf
, '.');
1186 case 'H': /* '\' 'H' full hostname */
1187 gethostname(strbuf
, sizeof strbuf
);
1189 case 'j': /* '\' 'j' number of jobs */
1190 snprintf(strbuf
, sizeof strbuf
, "%d",
1193 case 'l': /* '\' 'l' basename of tty */
1194 #if !defined (__amigaos4__) && !defined(__AROS__)
1197 p
= strdup("CONSOLE:");
1202 strlcpy(strbuf
, p
, sizeof strbuf
);
1204 case 'n': /* '\' 'n' newline */
1207 totlen
= 0; /* reset for prompt re-print */
1210 case 'p': /* '\' '$' $ or # */
1211 strbuf
[0] = ksheuid
? '$' : '#';
1214 case 'r': /* '\' 'r' return */
1217 totlen
= 0; /* reset for prompt re-print */
1220 case 's': /* '\' 's' basename $0 */
1221 strlcpy(strbuf
, kshname
, sizeof strbuf
);
1223 case 't': /* '\' 't' 24 hour HH:MM:SS */
1226 strftime(strbuf
, sizeof strbuf
, "%T", tm
);
1228 case 'T': /* '\' 'T' 12 hour HH:MM:SS */
1231 strftime(strbuf
, sizeof strbuf
, "%l:%M:%S", tm
);
1233 case '@': /* '\' '@' 12 hour am/pm format */
1236 strftime(strbuf
, sizeof strbuf
, "%r", tm
);
1238 case 'A': /* '\' 'A' 24 hour HH:MM */
1241 strftime(strbuf
, sizeof strbuf
, "%R", tm
);
1243 case 'v': /* '\' 'v' version (short) */
1244 p
= strchr(ksh_version
, ' ');
1246 p
= strchr(p
+ 1, ' ');
1249 strlcpy(strbuf
, p
, sizeof strbuf
);
1250 p
= strchr(strbuf
, ' ');
1255 case 'V': /* '\' 'V' version (long) */
1256 strlcpy(strbuf
, ksh_version
, sizeof strbuf
);
1258 case 'w': /* '\' 'w' cwd */
1259 p
= str_val(global("PWD"));
1260 n
= strlen(str_val(global("HOME")));
1261 if (strcmp(p
, "/") == 0) {
1262 strlcpy(strbuf
, p
, sizeof strbuf
);
1263 } else if (strcmp(p
, str_val(global("HOME"))) == 0) {
1266 } else if (strncmp(p
, str_val(global("HOME")), n
)
1267 == 0 && p
[n
] == '/') {
1268 snprintf(strbuf
, sizeof strbuf
, "~/%s",
1269 str_val(global("PWD")) + n
+ 1);
1271 strlcpy(strbuf
, p
, sizeof strbuf
);
1273 case 'W': /* '\' 'W' basename(cwd) */
1274 p
= str_val(global("PWD"));
1275 strlcpy(strbuf
, basename(p
), sizeof strbuf
);
1277 case '!': /* '\' '!' history line number */
1278 snprintf(strbuf
, sizeof strbuf
, "%d",
1281 case '#': /* '\' '#' command line number */
1282 snprintf(strbuf
, sizeof strbuf
, "%d",
1283 source
->line
- source
->cmd_offset
+ 1);
1285 case '0': /* '\' '#' '#' ' #' octal numeric handling */
1293 if ((cp
[1] > '7' || cp
[1] < '0') ||
1294 (cp
[2] > '7' || cp
[2] < '0')) {
1295 snprintf(strbuf
, sizeof strbuf
,
1299 n
= cp
[0] * 8 * 8 + cp
[1] * 8 + cp
[2];
1300 snprintf(strbuf
, sizeof strbuf
, "%c", n
);
1303 case '\\': /* '\' '\' */
1307 case '[': /* '\' '[' .... stop counting */
1311 case ']': /* '\' ']' restart counting */
1317 snprintf(strbuf
, sizeof strbuf
, "\\%c", *cp
);
1325 if (ntruncate
>= len
) {
1334 shf_write(str
, len
, shl_out
);
1335 if (counting
&& !indelimit
&& !delimitthis
)
1338 } else if (*cp
!= '!')
1340 else if (*++cp
== '!')
1345 shf_snprintf(p
= nbuf
, sizeof(nbuf
), "%d",
1349 if (ntruncate
>= len
) {
1358 shf_write(p
, len
, shl_out
);
1359 if (counting
&& !indelimit
&& !delimitthis
)
1363 if (counting
&& ntruncate
)
1366 shf_putc(c
, shl_out
);
1368 if (counting
&& !indelimit
&& !delimitthis
)
1379 pprompt(const char *cp
, int ntruncate
)
1381 dopprompt(cp
, ntruncate
, NULL
, 1);
1385 promptlen(const char *cp
, const char **spp
)
1387 return dopprompt(cp
, 0, spp
, 0);
1390 /* Read the variable part of a ${...} expression (ie, up to but not including
1391 * the :[-+?=#%] or close-brace.
1394 get_brace_var(XString
*wsp
, char *wp
)
1397 PS_INITIAL
, PS_SAW_HASH
, PS_IDENT
,
1398 PS_NUMBER
, PS_VAR1
, PS_END
1406 /* State machine to figure out where the variable part ends. */
1410 state
= PS_SAW_HASH
;
1419 else if (ctype(c
, C_VAR1
))
1430 if (!arraysub(&tmp
))
1431 yyerror("missing ]\n");
1433 for (p
= tmp
; *p
; ) {
1438 c
= getsc(); /* the ] */
1449 case PS_END
: /* keep gcc happy */
1452 if (state
== PS_END
) {
1453 *wp
++ = '\0'; /* end of variable part */
1464 * Save an array subscript - returns true if matching bracket found, false
1465 * if eof or newline was found.
1466 * (Returned string double null terminated)
1469 arraysub(char **strp
)
1474 int depth
= 1; /* we are just past the initial [ */
1476 Xinit(ws
, wp
, 32, ATEMP
);
1486 } while (depth
> 0 && c
&& c
!= '\n');
1489 *strp
= Xclose(ws
, wp
);
1491 return depth
== 0 ? 1 : 0;
1494 /* Unget a char: handles case when we are already at the start of the buffer */
1500 /* Don't unget eof... */
1501 if (source
->str
== null
&& c
== '\0')
1503 if (source
->str
> source
->start
)
1508 s
= pushs(SREREAD
, source
->areap
);
1509 s
->ugbuf
[0] = c
; s
->ugbuf
[1] = '\0';
1510 s
->start
= s
->str
= s
->ugbuf
;
1518 /* Called to get a char that isn't a \newline sequence. */
1524 if (ignore_backslash_newline
)
1527 if (backslash_skip
== 1) {
1537 if ((c2
= getsc_()) == '\n')
1538 /* ignore the \newline; get the next char... */
1548 push_state_(State_info
*si
, Lex_state
*old_end
)
1550 Lex_state
*new = alloc(sizeof(Lex_state
) * STATE_BSIZE
, ATEMP
);
1552 new[0].ls_info
.base
= old_end
;
1554 si
->end
= &new[STATE_BSIZE
];
1559 pop_state_(State_info
*si
, Lex_state
*old_end
)
1561 Lex_state
*old_base
= si
->base
;
1563 si
->base
= old_end
->ls_info
.base
- STATE_BSIZE
;
1564 si
->end
= old_end
->ls_info
.base
;
1566 afree(old_base
, ATEMP
);
1568 return si
->base
+ STATE_BSIZE
- 1;;