2 * lexical analysis and source input
9 * states while lexing word
11 #define SBASE 0 /* outside any lexical constructs */
12 #define SWORD 1 /* implicit quoting for substitute() */
13 #define SSQUOTE 2 /* inside '' */
14 #define SDQUOTE 3 /* inside "" */
15 #define SBRACE 4 /* inside ${} */
16 #define SCSPAREN 5 /* inside $() */
17 #define SBQUOTE 6 /* inside `` */
18 #define SASPAREN 7 /* inside $(( )) */
19 #define SHEREDELIM 8 /* parsing <<,<<- delimiter */
20 #define SHEREDQUOTE 9 /* parsing " in <<,<<- delimiter */
21 #define STBRACE 10 /* parsing ${..[#%]..} */
22 #define SQBRACE 11 /* inside "${}" */
25 /* Structure to keep track of the lexing state and the various pieces of info
26 * needed for each particular state. */
27 typedef struct lex_state Lex_state
;
32 struct scsparen_info
{
33 int nparen
; /* count open parenthesis */
34 int csstate
; /* XXX remove */
35 #define ls_scsparen ls_info.u_scsparen
39 struct sasparen_info
{
40 int nparen
; /* count open parenthesis */
41 int start
; /* marks start of $(( in output str */
42 #define ls_sasparen ls_info.u_sasparen
46 struct sletparen_info
{
47 int nparen
; /* count open parenthesis */
48 #define ls_sletparen ls_info.u_sletparen
53 int indquotes
; /* true if in double quotes: "`...`" */
54 #define ls_sbquote ls_info.u_sbquote
58 struct sletarray_info
{
59 int nparen
; /* count open parentheses */
60 #define ls_sletarray ls_info.u_sletarray
65 unsigned char nparen
; /* count open parentheses */
66 #define SADELIM_BASH 0
67 #define SADELIM_MAKE 1
69 unsigned char delimiter
;
71 unsigned char flags
; /* ofs. into sadelim_flags[] */
72 #define ls_sadelim ls_info.u_sadelim
75 Lex_state
*base
; /* used to point to next state block */
79 typedef struct State_info State_info
;
85 static void readhere(struct ioword
*);
86 static int getsc__(void);
87 static void getsc_line(Source
*);
88 static int getsc_bn(void);
89 static char *get_brace_var(XString
*, char *);
90 static int arraysub(char **);
91 static const char *ungetsc(int);
92 static void gethere(void);
93 static Lex_state
*push_state_(State_info
*, Lex_state
*);
94 static Lex_state
*pop_state_(State_info
*, Lex_state
*);
96 static int backslash_skip
;
97 static int ignore_backslash_newline
;
99 /* optimized getsc_bn() */
100 #define getsc() (*source->str != '\0' && *source->str != '\\' \
101 && !backslash_skip && !(source->flags & SF_FIRST) \
102 ? *source->str++ : getsc_bn())
103 /* optimised getsc__() */
104 #define getsc_() ((*source->str != '\0') && !(source->flags & SF_FIRST) \
105 ? *source->str++ : getsc__())
107 #define STATE_BSIZE 32
109 #define PUSH_STATE(s) do { \
110 if (++statep == state_info.end) \
111 statep = push_state_(&state_info, statep); \
112 state = statep->ls_state = (s); \
115 #define POP_STATE() do { \
116 if (--statep == state_info.base) \
117 statep = pop_state_(&state_info, statep); \
118 state = statep->ls_state; \
124 * tokens are not regular expressions, they are LL(1).
125 * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
126 * hence the state stack.
132 Lex_state states
[STATE_BSIZE
], *statep
;
133 State_info state_info
;
135 XString ws
; /* expandable output word */
136 char *wp
; /* output word pointer */
141 states
[0].ls_state
= -1;
142 states
[0].ls_info
.base
= NULL
;
144 state_info
.base
= states
;
145 state_info
.end
= &states
[STATE_BSIZE
];
147 Xinit(ws
, wp
, 64, ATEMP
);
150 ignore_backslash_newline
= 0;
154 else { /* normal lexing */
155 state
= (cf
& HEREDELIM
) ? SHEREDELIM
: SBASE
;
156 while ((c
= getsc()) == ' ' || c
== '\t')
159 ignore_backslash_newline
++;
160 while ((c
= getsc()) != '\0' && c
!= '\n')
162 ignore_backslash_newline
--;
166 if (source
->flags
& SF_ALIAS
) { /* trailing ' ' in alias definition */
167 source
->flags
&= ~SF_ALIAS
;
170 /* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */
171 statep
->ls_state
= state
;
173 /* collect non-special or quoted characters to form word */
174 while (!((c
= getsc()) == 0
175 || ((state
== SBASE
|| state
== SHEREDELIM
)
176 && ctype(c
, C_LEX1
))))
181 if (c
== '[' && (cf
& (VARASN
|ARRAYVAR
))) {
182 *wp
= EOS
; /* temporary */
183 if (is_wdvarname(Xstring(ws
, wp
), FALSE
)) {
186 if (arraysub(&tmp
)) {
189 for (p
= tmp
; *p
; ) {
212 Sbase2
: /* doesn't include *(...|...) pattern (*+?@!) */
217 if (c
) /* trailing \ is lost */
218 *wp
++ = QCHAR
, *wp
++ = c
;
223 ignore_backslash_newline
++;
247 *wp
++ = QCHAR
, *wp
++ = c
;
252 if (c
) { /* trailing \ is lost */
253 *wp
++ = CHAR
, *wp
++ = '\\';
254 *wp
++ = CHAR
, *wp
++ = c
;
262 if (c
== '(') /*)*/ {
264 if (c
== '(') /*)*/ {
265 PUSH_STATE(SASPAREN
);
266 statep
->ls_sasparen
.nparen
= 2;
267 statep
->ls_sasparen
.start
=
272 PUSH_STATE(SCSPAREN
);
273 statep
->ls_scsparen
.nparen
= 1;
274 statep
->ls_scsparen
.csstate
= 0;
277 } else if (c
== '{') /*}*/ {
280 wp
= get_brace_var(&ws
, wp
);
282 /* If this is a trim operation,
283 * treat (,|,) specially in STBRACE.
285 if (c
== '#' || c
== '%') {
290 if (state
== SDQUOTE
)
295 } else if (isalpha(c
) || c
=='_') {
302 } while (isalnum(c
) || c
=='_');
307 } else if (isdigit(c
) || ctype(c
, C_VAR1
)) {
317 *wp
++ = CHAR
, *wp
++ = '$';
325 statep
->ls_sbquote
.indquotes
= 0;
326 Lex_state
*s
= statep
;
327 Lex_state
*base
= state_info
.base
;
329 for (; s
!= base
; s
--) {
330 if (s
->ls_state
== SDQUOTE
) {
331 statep
->ls_sbquote
.indquotes
= 1;
337 if (!(s
= s
->ls_info
.base
))
339 base
= s
-- - STATE_BSIZE
;
344 *wp
++ = CHAR
, *wp
++ = c
;
352 ignore_backslash_newline
--;
354 *wp
++ = QCHAR
, *wp
++ = c
;
365 case SCSPAREN
: /* $( .. ) */
366 /* todo: deal with $(...) quoting properly
367 * kludge to partly fake quoting inside $(..): doesn't
368 * really work because nested $(..) or ${..} inside
369 * double quotes aren't dealt with.
371 switch (statep
->ls_scsparen
.csstate
) {
375 statep
->ls_scsparen
.nparen
++;
378 statep
->ls_scsparen
.nparen
--;
381 statep
->ls_scsparen
.csstate
= 1;
384 statep
->ls_scsparen
.csstate
= 2;
387 statep
->ls_scsparen
.csstate
= 4;
388 ignore_backslash_newline
++;
393 case 1: /* backslash in normal mode */
394 case 3: /* backslash in double quotes */
395 --statep
->ls_scsparen
.csstate
;
398 case 2: /* double quotes */
400 statep
->ls_scsparen
.csstate
= 0;
402 statep
->ls_scsparen
.csstate
= 3;
405 case 4: /* single quotes */
407 statep
->ls_scsparen
.csstate
= 0;
408 ignore_backslash_newline
--;
412 if (statep
->ls_scsparen
.nparen
== 0) {
414 *wp
++ = 0; /* end of COMSUB */
419 case SASPAREN
: /* $(( .. )) */
420 /* todo: deal with $((...); (...)) properly */
421 /* XXX should nest using existing state machine
422 * (embed "..", $(...), etc.) */
424 statep
->ls_sasparen
.nparen
++;
426 statep
->ls_sasparen
.nparen
--;
427 if (statep
->ls_sasparen
.nparen
== 1) {
429 if ((c2
= getsc()) == ')') {
431 *wp
++ = 0; /* end of EXPRSUB */
437 /* mismatched parenthesis -
438 * assume we were really
439 * parsing a $(..) expression
442 statep
->ls_sasparen
.start
);
443 memmove(s
+ 1, s
, wp
- s
);
447 statep
->ls_scsparen
.nparen
= 1;
448 statep
->ls_scsparen
.csstate
= 0;
449 state
= statep
->ls_state
=
470 else if (c
!= /*{*/ '}')
478 /* Same as SBRACE, except (,|,) treated specially */
492 } else if (c
== '\\') {
493 switch (c
= getsc()) {
499 if (statep
->ls_sbquote
.indquotes
) {
505 if (c
) { /* trailing \ is lost */
515 case SWORD
: /* ONEWORD */
519 case SLETPAREN
: /* LETEXPR: (( ... )) */
522 if (statep
->ls_sletparen
.nparen
> 0)
523 --statep
->ls_sletparen
.nparen
;
525 else if ((c2
= getsc()) == ')') {
532 /* parenthesis inside quotes and backslashes
533 * are lost, but at&t ksh doesn't count them
536 ++statep
->ls_sletparen
.nparen
;
540 case SHEREDELIM
: /* <<,<<- delimiter */
541 /* XXX chuck this state (and the next) - use
542 * the existing states ($ and \`..` should be
543 * stripped of their specialness after the
546 /* here delimiters need a special case since
547 * $ and `..` are not to be treated specially
551 if (c
) { /* trailing \ is lost */
555 } else if (c
== '\'') {
558 ignore_backslash_newline
++;
559 } else if (c
== '"') {
560 state
= statep
->ls_state
= SHEREDQUOTE
;
568 case SHEREDQUOTE
: /* " in <<,<<- delimiter */
571 state
= statep
->ls_state
= SHEREDELIM
;
574 switch (c
= getsc()) {
579 if (c
) { /* trailing \ lost */
595 if (statep
!= &states
[1])
596 /* XXX figure out what is missing */
597 yyerror("no closing quote\n");
599 /* This done to avoid tests for SHEREDELIM wherever SBASE tested */
600 if (state
== SHEREDELIM
)
603 dp
= Xstring(ws
, wp
);
604 if ((c
== '<' || c
== '>') && state
== SBASE
605 && ((c2
= Xlength(ws
, wp
)) == 0
606 || (c2
== 2 && dp
[0] == CHAR
&& isdigit(dp
[1]))))
609 (struct ioword
*) alloc(sizeof(*iop
), ATEMP
);
612 iop
->unit
= dp
[1] - '0';
614 iop
->unit
= c
== '>'; /* 0 for <, 1 for > */
617 /* <<, >>, <> are ok, >< is not */
618 if (c
== c2
|| (c
== '<' && c2
== '>')) {
619 iop
->flag
= c
== c2
?
620 (c
== '>' ? IOCAT
: IOHERE
) : IORDWR
;
621 if (iop
->flag
== IOHERE
) {
622 if ((c2
= getsc()) == '-')
627 } else if (c2
== '&')
628 iop
->flag
= IODUP
| (c
== '<' ? IORDUP
: 0);
630 iop
->flag
= c
== '>' ? IOWRITE
: IOREAD
;
631 if (c
== '>' && c2
== '|')
640 Xfree(ws
, wp
); /* free word */
645 if (wp
== dp
&& state
== SBASE
) {
646 Xfree(ws
, wp
); /* free word */
647 /* no word, process LEX1 character */
655 if ((c2
= getsc()) == c
)
656 c
= (c
== ';') ? BREAK
:
658 (c
== '&') ? LOGAND
:
661 else if (c
== '|' && c2
== '&')
676 if ((c2
= getsc()) == '(') /*)*/
677 /* XXX need to handle ((...); (...)) */
689 *wp
++ = EOS
; /* terminate word */
690 yylval
.cp
= Xclose(ws
, wp
);
691 if (state
== SWORD
) /* ONEWORD? */
693 ungetsc(c
); /* unget terminator */
695 /* copy word to unprefixed string ident */
698 if ((cf
& HEREDELIM
) && (sp
[1] == '<'))
699 while (dp
< ident
+IDENT
) {
700 if ((c
= *sp
++) == CHAR
)
702 else if ((c
!= OQUOTE
) && (c
!= CQUOTE
))
706 while (dp
< ident
+IDENT
&& (c
= *sp
++) == CHAR
)
708 /* Make sure the ident array stays '\0' padded */
709 memset(dp
, 0, (ident
+IDENT
) - dp
+ 1);
711 *ident
= '\0'; /* word is not unquoted */
713 if (*ident
!= '\0' && (cf
&(KEYWORD
|ALIAS
))) {
717 if ((cf
& KEYWORD
) && (p
= transitional_tsearch(&keywords
.root
, ident
))
718 && (!(cf
& ESACONLY
) || p
->val
.i
== ESAC
|| p
->val
.i
== '}'))
720 afree(yylval
.cp
, ATEMP
);
723 if ((cf
& ALIAS
) && (p
= transitional_tsearch(&aliases
.root
, ident
))
724 && (p
->flag
& ISSET
))
728 for (s
= source
; s
->type
== SALIAS
; s
= s
->next
)
731 /* push alias expansion */
732 s
= pushs(SALIAS
, source
->areap
);
733 s
->start
= s
->str
= p
->val
.s
;
737 afree(yylval
.cp
, ATEMP
);
750 for (p
= heres
; p
< herep
; p
++)
756 * read "<<word" text into temp file
760 readhere(struct ioword
*iop
)
770 eof
= evalstr(iop
->delim
, 0);
772 if (!(iop
->flag
& IOEVAL
))
773 ignore_backslash_newline
++;
775 Xinit(xs
, xp
, 256, ATEMP
);
779 skiptabs
= iop
->flag
& IOSKIP
;
780 xpos
= Xsavepos(xs
, xp
);
781 while ((c
= getsc()) != 0) {
793 /* Allow EOF here so commands with out trailing newlines
794 * will work (eg, ksh -c '...', $(...), etc).
796 if (*eofp
== '\0' && (c
== 0 || c
== '\n')) {
797 xp
= Xrestpos(xs
, xp
, xpos
);
801 while ((c
= getsc()) != '\n') {
803 yyerror("here document `%s' unclosed\n", eof
);
811 iop
->heredoc
= Xclose(xs
, xp
);
813 if (!(iop
->flag
& IOEVAL
))
814 ignore_backslash_newline
--;
818 yyerror(const char *fmt
, ...)
822 /* pop aliases and re-reads */
823 while (source
->type
== SALIAS
|| source
->type
== SREREAD
)
824 source
= source
->next
;
825 source
->str
= null
; /* zap pending input */
828 SH_VA_START(va
, fmt
);
829 shf_vfprintf(shl_out
, fmt
, va
);
835 * input for yylex with alias expansion
839 pushs(int type
, Area
*areap
)
843 s
= alloc(sizeof(Source
), areap
);
853 if (type
== SFILE
|| type
== SSTDIN
) {
855 Xinit(s
->xs
, dummy
, 256, s
->areap
);
857 memset(&s
->xs
, 0, sizeof(s
->xs
));
868 while ((c
= *s
->str
++) == 0) {
869 s
->str
= NULL
; /* return 0 for EOF by default */
887 s
->start
= s
->str
= *s
->u
.strv
++;
892 if (*s
->u
.strv
== NULL
) {
893 s
->start
= s
->str
= "\n";
896 s
->start
= s
->str
= " ";
902 if (s
->flags
& SF_ALIASEND
) {
903 /* pass on an unused SF_ALIAS flag */
905 source
->flags
|= s
->flags
& SF_ALIAS
;
907 } else if (*s
->u
.tblp
->val
.s
908 && isspace(strchr(s
->u
.tblp
->val
.s
, 0)[-1]))
910 source
= s
= s
->next
; /* pop source stack */
911 /* Note that this alias ended with a space,
912 * enabling alias expansion on the following
915 s
->flags
|= SF_ALIAS
;
917 /* At this point, we need to keep the current
918 * alias in the source list so recursive
919 * aliases can be detected and we also need
920 * to return the next character. Do this
921 * by temporarily popping the alias to get
922 * the next character and then put it back
923 * in the source list with the SF_ALIASEND
926 source
= s
->next
; /* pop source stack */
927 source
->flags
|= s
->flags
& SF_ALIAS
;
930 s
->flags
|= SF_ALIASEND
;
931 s
->ugbuf
[0] = c
; s
->ugbuf
[1] = '\0';
932 s
->start
= s
->str
= s
->ugbuf
;
937 /* avoid reading eof twice */
945 if (s
->start
!= s
->ugbuf
) /* yuck */
946 afree(s
->u
.freeme
, ATEMP
);
947 source
= s
= s
->next
;
950 if (s
->str
== NULL
) {
952 s
->start
= s
->str
= null
;
955 if (s
->flags
& SF_ECHO
) {
956 shf_puts(s
->str
, shl_out
);
960 /* check for UTF-8 byte order mark */
961 if (s
->flags
& SF_FIRST
) {
962 s
->flags
&= ~SF_FIRST
;
963 if (((unsigned char)c
== 0xEF) &&
964 (((const unsigned char *)(s
->str
))[0] == 0xBB) &&
965 (((const unsigned char *)(s
->str
))[1] == 0xBF)) {
975 getsc_line(Source
*s
)
977 char *xp
= Xstring(s
->xs
, xp
);
978 int interactive
= Flag(FTALKING
) && s
->type
== SSTDIN
;
979 /* int have_tty = interactive && (s->flags & SF_TTY); */
981 /* Done here to ensure nothing odd happens when a timeout occurs */
982 XcheckN(s
->xs
, xp
, LINE
);
984 s
->start
= s
->str
= xp
;
987 if (have_tty
&& ksh_tmout
) {
988 ksh_tmout_state
= TMOUT_READING
;
998 || Flag(FEMACS
) || Flag(FGMACS
)
1004 nread
= x_read(xp
, LINE
);
1005 if (nread
< 0) /* read error */
1018 char *p
= shf_getse(xp
, Xnleft(s
->xs
, xp
), s
->u
.shf
);
1020 if (!p
&& shf_error(s
->u
.shf
)
1021 && shf_errno(s
->u
.shf
) == EINTR
)
1023 shf_clearerr(s
->u
.shf
);
1028 if (!p
|| (xp
= p
, xp
[-1] == '\n'))
1030 /* double buffer size */
1031 xp
++; /* move past null so doubling works... */
1032 XcheckN(s
->xs
, xp
, Xlength(s
->xs
, xp
));
1033 xp
--; /* ...and move back again */
1035 /* flush any unwanted input so other programs/builtins
1036 * can read it. Not very optimal, but less error prone
1037 * than flushing else where, dealing with redirections,
1039 * todo: reduce size of shf buffer (~128?) if SSTDIN
1041 if (s
->type
== SSTDIN
)
1042 shf_flush(s
->u
.shf
);
1044 /* XXX: temporary kludge to restore source after a
1045 * trap may have been executed.
1049 if (have_tty
&& ksh_tmout
) {
1050 ksh_tmout_state
= TMOUT_EXECUTING
;
1054 s
->start
= s
->str
= Xstring(s
->xs
, xp
);
1055 strip_nuls(Xstring(s
->xs
, xp
), Xlength(s
->xs
, xp
));
1056 /* Note: if input is all nulls, this is not eof */
1057 if (Xlength(s
->xs
, xp
) == 0) { /* EOF */
1058 if (s
->type
== SFILE
)
1059 shf_fdclose(s
->u
.shf
);
1061 } else if (interactive
) {
1063 char *p
= Xstring(s
->xs
, xp
);
1064 if (cur_prompt
== PS1
)
1065 while (*p
&& ctype(*p
, C_IFS
) && ctype(*p
, C_IFSWS
))
1068 # ifdef EASY_HISTORY
1069 if (cur_prompt
== PS2
)
1070 histappend(Xstring(s
->xs
, xp
), 1);
1072 # endif /* EASY_HISTORY */
1075 histsave(s
->line
, s
->str
, 1);
1078 #endif /* HISTORY */
1081 set_prompt(PS2
, NULL
);
1085 set_prompt(int to
, Source
UNUSED(*s
))
1090 case PS1
: /* command */
1092 /* Substitute ! and !! here, before substitutions are done
1093 * so ! in expanded variables are not expanded.
1094 * NOTE: this is not what at&t ksh does (it does it after
1095 * substitutions, POSIX doesn't say which is to be done.
1102 ps1
= str_val(global("PS1"));
1103 shf
= shf_sopen((char *) 0, strlen(ps1
) * 2,
1104 SHF_WR
| SHF_DYNAMIC
, (struct shf
*) 0);
1106 if (*ps1
!= '!' || *++ps1
== '!')
1107 shf_putchar(*ps1
++, shf
);
1109 shf_fprintf(shf
, "%d",
1110 s
? s
->line
+ 1 : 0);
1112 ps1
= shf_sclose(shf
);
1113 saved_atemp
= ATEMP
;
1115 if (sigsetjmp(e
->jbuf
, 0)) {
1116 prompt
= safe_prompt
;
1117 /* Don't print an error - assume it has already
1118 * been printed. Reason is we may have forked
1119 * to run a command and the child may be
1120 * unwinding its stack through this code as it
1124 prompt
= str_save(substitute(ps1
, 0),
1129 prompt
= str_val(global("PS1"));
1133 case PS2
: /* command continuation */
1134 prompt
= str_val(global("PS2"));
1139 /* See also related routine, promptlen() in edit.c */
1141 pprompt(cp
, ntruncate
)
1152 else if (*++cp
== '!')
1158 shf_snprintf(p
= nbuf
, sizeof(nbuf
), "%d",
1162 if (ntruncate
>= len
) {
1170 shf_write(p
, len
, shl_out
);
1176 shf_putc(c
, shl_out
);
1179 shf_puts(cp
+ ntruncate
, shl_out
);
1183 /* Read the variable part of a ${...} expression (ie, up to but not including
1184 * the :[-+?=#%] or close-brace.
1187 get_brace_var(XString
*wsp
, char *wp
)
1190 PS_INITIAL
, PS_SAW_HASH
, PS_IDENT
,
1191 PS_NUMBER
, PS_VAR1
, PS_END
1199 /* State machine to figure out where the variable part ends. */
1203 state
= PS_SAW_HASH
;
1206 /* fall through.. */
1208 if (isalpha(c
) || c
=='_')
1210 else if (isdigit(c
))
1212 else if (ctype(c
, C_VAR1
))
1218 if (!isalnum(c
) && c
!='_') {
1223 if (!arraysub(&tmp
))
1224 yyerror("missing ]\n");
1226 for (p
= tmp
; *p
; ) {
1231 c
= getsc(); /* the ] */
1242 case PS_END
: /* keep gcc happy */
1245 if (state
== PS_END
) {
1246 *wp
++ = '\0'; /* end of variable part */
1257 * Save an array subscript - returns true if matching bracket found, false
1258 * if eof or newline was found.
1259 * (Returned string double null terminated)
1262 arraysub(char **strp
)
1267 int depth
= 1; /* we are just past the initial [ */
1269 Xinit(ws
, wp
, 32, ATEMP
);
1279 } while (depth
> 0 && c
&& c
!= '\n');
1282 *strp
= Xclose(ws
, wp
);
1284 return depth
== 0 ? 1 : 0;
1287 /* Unget a char: handles case when we are already at the start of the buffer */
1293 /* Don't unget eof... */
1294 if (source
->str
== null
&& c
== '\0')
1296 if (source
->str
> source
->start
)
1301 s
= pushs(SREREAD
, source
->areap
);
1302 s
->ugbuf
[0] = c
; s
->ugbuf
[1] = '\0';
1303 s
->start
= s
->str
= s
->ugbuf
;
1311 /* Called to get a char that isn't a \newline sequence. */
1317 if (ignore_backslash_newline
)
1320 if (backslash_skip
== 1) {
1330 if ((c2
= getsc_()) == '\n')
1331 /* ignore the \newline; get the next char... */
1341 push_state_(State_info
*si
, Lex_state
*old_end
)
1343 Lex_state
*new = alloc(sizeof(Lex_state
) * STATE_BSIZE
, ATEMP
);
1345 new[0].ls_info
.base
= old_end
;
1347 si
->end
= &new[STATE_BSIZE
];
1352 pop_state_(State_info
*si
, Lex_state
*old_end
)
1354 Lex_state
*old_base
= si
->base
;
1356 si
->base
= old_end
->ls_info
.base
- STATE_BSIZE
;
1357 si
->end
= old_end
->ls_info
.base
;
1359 afree(old_base
, ATEMP
);
1361 return si
->base
+ STATE_BSIZE
- 1;