1 /* $NetBSD: lex.c,v 1.15 2011/10/16 17:12:11 joerg Exp $ */
4 * lexical analysis and source input
9 __RCSID("$NetBSD: lex.c,v 1.15 2011/10/16 17:12:11 joerg Exp $");
17 /* Structure to keep track of the lexing state and the various pieces of info
18 * needed for each particular state.
20 typedef struct lex_state Lex_state
;
25 struct scsparen_info
{
26 int nparen
; /* count open parenthesis */
27 int csstate
; /* XXX remove */
28 #define ls_scsparen ls_info.u_scsparen
32 struct sasparen_info
{
33 int nparen
; /* count open parenthesis */
34 int start
; /* marks start of $(( in output str */
35 #define ls_sasparen ls_info.u_sasparen
39 struct sletparen_info
{
40 int nparen
; /* count open parenthesis */
41 #define ls_sletparen ls_info.u_sletparen
46 int indquotes
; /* true if in double quotes: "`...`" */
47 #define ls_sbquote ls_info.u_sbquote
50 Lex_state
*base
; /* used to point to next state block */
54 typedef struct State_info State_info
;
61 static void readhere
ARGS((struct ioword
*iop
));
62 static int getsc__
ARGS((void));
63 static void getsc_line
ARGS((Source
*s
));
64 static int getsc_bn
ARGS((void));
65 static char *get_brace_var
ARGS((XString
*wsp
, char *wp
));
66 static int arraysub
ARGS((char **strp
));
67 static const char *ungetsc
ARGS((int c
));
68 static void gethere
ARGS((void));
69 static Lex_state
*push_state_
ARGS((State_info
*si
, Lex_state
*old_end
));
70 static Lex_state
*pop_state_
ARGS((State_info
*si
, Lex_state
*old_end
));
72 static int backslash_skip
;
73 static int ignore_backslash_newline
;
75 /* optimized getsc_bn() */
76 #define getsc() (*source->str != '\0' && *source->str != '\\' \
77 && !backslash_skip ? *source->str++ : getsc_bn())
78 /* optimized getsc__() */
79 #define getsc_() ((*source->str != '\0') ? *source->str++ : getsc__())
81 #define STATE_BSIZE 32
83 #define PUSH_STATE(s) do { \
84 if (++statep == state_info.end) \
85 statep = push_state_(&state_info, statep); \
86 state = statep->ls_state = (s); \
89 #define POP_STATE() do { \
90 if (--statep == state_info.base) \
91 statep = pop_state_(&state_info, statep); \
92 state = statep->ls_state; \
100 * tokens are not regular expressions, they are LL(1).
101 * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
102 * hence the state stack.
109 Lex_state states
[STATE_BSIZE
], *statep
;
110 State_info state_info
;
111 register int c
, state
;
112 XString ws
; /* expandable output word */
113 register char *wp
; /* output word pointer */
119 states
[0].ls_state
= -1;
120 states
[0].ls_info
.base
= (Lex_state
*) 0;
122 state_info
.base
= states
;
123 state_info
.end
= &states
[STATE_BSIZE
];
125 Xinit(ws
, wp
, 64, ATEMP
);
128 ignore_backslash_newline
= 0;
133 else if (cf
&LETEXPR
) {
134 *wp
++ = OQUOTE
; /* enclose arguments in (double) quotes */
136 statep
->ls_sletparen
.nparen
= 0;
139 else { /* normal lexing */
140 state
= (cf
& HEREDELIM
) ? SHEREDELIM
: SBASE
;
141 while ((c
= getsc()) == ' ' || c
== '\t')
144 ignore_backslash_newline
++;
145 while ((c
= getsc()) != '\0' && c
!= '\n')
147 ignore_backslash_newline
--;
151 if (source
->flags
& SF_ALIAS
) { /* trailing ' ' in alias definition */
152 source
->flags
&= ~SF_ALIAS
;
153 /* In POSIX mode, a trailing space only counts if we are
154 * parsing a simple command
156 if (!Flag(FPOSIX
) || (cf
& CMDWORD
))
160 /* Initial state: one of SBASE SHEREDELIM SWORD SASPAREN */
161 statep
->ls_state
= state
;
163 /* collect non-special or quoted characters to form word */
164 while (!((c
= getsc()) == 0
165 || ((state
== SBASE
|| state
== SHEREDELIM
)
166 && ctype(c
, C_LEX1
))))
171 if (c
== '[' && (cf
& (VARASN
|ARRAYVAR
))) {
172 *wp
= EOS
; /* temporary */
173 if (is_wdvarname(Xstring(ws
, wp
), FALSE
))
177 if (arraysub(&tmp
)) {
180 for (p
= tmp
; *p
; ) {
203 Sbase1
: /* includes *(...|...) pattern (*+?@!) */
205 if (c
== '*' || c
== '@' || c
== '+' || c
== '?'
209 if (c2
== '(' /*)*/ ) {
212 PUSH_STATE(SPATTERN
);
219 Sbase2
: /* doesn't include *(...|...) pattern (*+?@!) */
224 if (isalnum((unsigned char)c
)) {
225 *wp
++ = CHAR
, *wp
++ = '\\';
226 *wp
++ = CHAR
, *wp
++ = c
;
229 if (c
) /* trailing \ is lost */
230 *wp
++ = QCHAR
, *wp
++ = c
;
234 ignore_backslash_newline
++;
256 *wp
++ = QCHAR
, *wp
++ = c
;
259 if ((cf
& HEREDOC
) == 0) {
260 *wp
++ = QCHAR
, *wp
++ = c
;
266 if (c
) { /* trailing \ is lost */
267 *wp
++ = CHAR
, *wp
++ = '\\';
268 *wp
++ = CHAR
, *wp
++ = c
;
275 if (c
== '(') /*)*/ {
277 if (c
== '(') /*)*/ {
278 PUSH_STATE(SASPAREN
);
279 statep
->ls_sasparen
.nparen
= 2;
280 statep
->ls_sasparen
.start
=
285 PUSH_STATE(SCSPAREN
);
286 statep
->ls_scsparen
.nparen
= 1;
287 statep
->ls_scsparen
.csstate
= 0;
290 } else if (c
== '{') /*}*/ {
293 wp
= get_brace_var(&ws
, wp
);
295 /* allow :# and :% (ksh88 compat) */
297 *wp
++ = CHAR
, *wp
++ = c
;
300 /* If this is a trim operation,
301 * treat (,|,) specially in STBRACE.
303 if (c
== '#' || c
== '%') {
310 } else if (ctype(c
, C_ALPHA
)) {
317 } while (ctype(c
, C_ALPHA
|C_DIGIT
));
322 } else if (ctype(c
, C_DIGIT
|C_VAR1
)) {
331 *wp
++ = CHAR
, *wp
++ = '$';
338 /* Need to know if we are inside double quotes
339 * since sh/at&t-ksh translate the \" to " in
340 * "`..\"..`". POSIX also requires this.
341 * An earlier version of ksh misinterpreted
342 * the POSIX specification and performed
343 * removal of backslash escapes only if
344 * posix mode was not in effect.
346 statep
->ls_sbquote
.indquotes
= 0;
348 base
= state_info
.base
;
350 for (; s
!= base
; s
--) {
351 if (s
->ls_state
== SDQUOTE
) {
352 statep
->ls_sbquote
.indquotes
= 1;
358 if (!(s
= s
->ls_info
.base
))
360 base
= s
-- - STATE_BSIZE
;
364 *wp
++ = CHAR
, *wp
++ = c
;
372 ignore_backslash_newline
--;
374 *wp
++ = QCHAR
, *wp
++ = c
;
385 case SCSPAREN
: /* $( .. ) */
386 /* todo: deal with $(...) quoting properly
387 * kludge to partly fake quoting inside $(..): doesn't
388 * really work because nested $(..) or ${..} inside
389 * double quotes aren't dealt with.
391 switch (statep
->ls_scsparen
.csstate
) {
395 statep
->ls_scsparen
.nparen
++;
398 statep
->ls_scsparen
.nparen
--;
401 statep
->ls_scsparen
.csstate
= 1;
404 statep
->ls_scsparen
.csstate
= 2;
407 statep
->ls_scsparen
.csstate
= 4;
408 ignore_backslash_newline
++;
413 case 1: /* backslash in normal mode */
414 case 3: /* backslash in double quotes */
415 --statep
->ls_scsparen
.csstate
;
418 case 2: /* double quotes */
420 statep
->ls_scsparen
.csstate
= 0;
422 statep
->ls_scsparen
.csstate
= 3;
425 case 4: /* single quotes */
427 statep
->ls_scsparen
.csstate
= 0;
428 ignore_backslash_newline
--;
432 if (statep
->ls_scsparen
.nparen
== 0) {
434 *wp
++ = 0; /* end of COMSUB */
439 case SASPAREN
: /* $(( .. )) */
440 /* todo: deal with $((...); (...)) properly */
441 /* XXX should nest using existing state machine
442 * (embed "..", $(...), etc.) */
444 statep
->ls_sasparen
.nparen
++;
446 statep
->ls_sasparen
.nparen
--;
447 if (statep
->ls_sasparen
.nparen
== 1) {
449 if ((c2
= getsc()) == ')') {
451 *wp
++ = 0; /* end of EXPRSUB */
457 /* mismatched parenthesis -
458 * assume we were really
459 * parsing a $(..) expression
462 statep
->ls_sasparen
.start
);
463 memmove(s
+ 1, s
, wp
- s
);
467 statep
->ls_scsparen
.nparen
= 1;
468 statep
->ls_scsparen
.csstate
= 0;
469 state
= statep
->ls_state
489 /* Same as SBRACE, except (,|,) treated specially */
495 } else if (c
== '|') {
497 } else if (c
== '(') {
499 *wp
++ = ' '; /* simile for @ */
500 PUSH_STATE(SPATTERN
);
509 } else if (c
== '\\') {
510 switch (c
= getsc()) {
516 if (statep
->ls_sbquote
.indquotes
) {
522 if (c
) { /* trailing \ is lost */
532 case SWORD
: /* ONEWORD */
536 case SLETPAREN
: /* LETEXPR: (( ... )) */
539 if (statep
->ls_sletparen
.nparen
> 0)
540 --statep
->ls_sletparen
.nparen
;
542 else if ((c2
= getsc()) == ')') {
549 /* parenthesis inside quotes and backslashes
550 * are lost, but at&t ksh doesn't count them
553 ++statep
->ls_sletparen
.nparen
;
557 case SHEREDELIM
: /* <<,<<- delimiter */
558 /* XXX chuck this state (and the next) - use
559 * the existing states ($ and \`..` should be
560 * stripped of their specialness after the
563 /* here delimiters need a special case since
564 * $ and `..` are not to be treated specially
568 if (c
) { /* trailing \ is lost */
572 } else if (c
== '\'') {
575 ignore_backslash_newline
++;
576 } else if (c
== '"') {
577 state
= statep
->ls_state
= SHEREDQUOTE
;
585 case SHEREDQUOTE
: /* " in <<,<<- delimiter */
588 state
= statep
->ls_state
= SHEREDELIM
;
591 switch (c
= getsc()) {
596 if (c
) { /* trailing \ lost */
608 case SPATTERN
: /* in *(...|...) pattern (*+?@!) */
609 if ( /*(*/ c
== ')') {
612 } else if (c
== '|') {
614 } else if (c
== '(') {
616 *wp
++ = ' '; /* simile for @ */
617 PUSH_STATE(SPATTERN
);
625 if (statep
!= &states
[1])
626 /* XXX figure out what is missing */
627 yyerror("no closing quote\n");
629 /* This done to avoid tests for SHEREDELIM wherever SBASE tested */
630 if (state
== SHEREDELIM
)
633 dp
= Xstring(ws
, wp
);
634 if ((c
== '<' || c
== '>') && state
== SBASE
635 && ((c2
= Xlength(ws
, wp
)) == 0
636 || (c2
== 2 && dp
[0] == CHAR
&& digit(dp
[1]))))
639 (struct ioword
*) alloc(sizeof(*iop
), ATEMP
);
642 iop
->unit
= dp
[1] - '0';
644 iop
->unit
= c
== '>'; /* 0 for <, 1 for > */
647 /* <<, >>, <> are ok, >< is not */
648 if (c
== c2
|| (c
== '<' && c2
== '>')) {
649 iop
->flag
= c
== c2
?
650 (c
== '>' ? IOCAT
: IOHERE
) : IORDWR
;
651 if (iop
->flag
== IOHERE
) {
652 if ((c2
= getsc()) == '-') {
658 } else if (c2
== '&')
659 iop
->flag
= IODUP
| (c
== '<' ? IORDUP
: 0);
661 iop
->flag
= c
== '>' ? IOWRITE
: IOREAD
;
662 if (c
== '>' && c2
== '|')
668 iop
->name
= (char *) 0;
669 iop
->delim
= (char *) 0;
670 iop
->heredoc
= (char *) 0;
671 Xfree(ws
, wp
); /* free word */
676 if (wp
== dp
&& state
== SBASE
) {
677 Xfree(ws
, wp
); /* free word */
678 /* no word, process LEX1 character */
686 if ((c2
= getsc()) == c
)
687 c
= (c
== ';') ? BREAK
:
689 (c
== '&') ? LOGAND
:
692 else if (c
== '|' && c2
== '&')
707 if ((c2
= getsc()) == '(') /*)*/
708 /* XXX need to handle ((...); (...)) */
720 *wp
++ = EOS
; /* terminate word */
721 yylval
.cp
= Xclose(ws
, wp
);
724 || state
== SLETPAREN
728 ungetsc(c
); /* unget terminator */
730 /* copy word to unprefixed string ident */
731 for (sp
= yylval
.cp
, dp
= ident
; dp
< ident
+IDENT
&& (c
= *sp
++) == CHAR
; )
733 /* Make sure the ident array stays '\0' padded */
734 memset(dp
, 0, (ident
+IDENT
) - dp
+ 1);
736 *ident
= '\0'; /* word is not unquoted */
738 if (*ident
!= '\0' && (cf
&(KEYWORD
|ALIAS
))) {
743 if ((cf
& KEYWORD
) && (p
= tsearch(&keywords
, ident
, h
))
744 && (!(cf
& ESACONLY
) || p
->val
.i
== ESAC
|| p
->val
.i
== '}'))
746 afree(yylval
.cp
, ATEMP
);
749 if ((cf
& ALIAS
) && (p
= tsearch(&aliases
, ident
, h
))
750 && (p
->flag
& ISSET
))
754 for (s
= source
; s
->type
== SALIAS
; s
= s
->next
)
757 /* push alias expansion */
758 s
= pushs(SALIAS
, source
->areap
);
759 s
->start
= s
->str
= p
->val
.s
;
763 afree(yylval
.cp
, ATEMP
);
774 register struct ioword
**p
;
776 for (p
= heres
; p
< herep
; p
++)
782 * read "<<word" text into temp file
797 eof
= evalstr(iop
->delim
, 0);
799 if (!(iop
->flag
& IOEVAL
))
800 ignore_backslash_newline
++;
802 Xinit(xs
, xp
, 256, ATEMP
);
806 skiptabs
= iop
->flag
& IOSKIP
;
807 xpos
= Xsavepos(xs
, xp
);
808 while ((c
= getsc()) != 0) {
820 /* Allow EOF here so commands with out trailing newlines
821 * will work (eg, ksh -c '...', $(...), etc).
823 if (*eofp
== '\0' && (c
== 0 || c
== '\n')) {
824 xp
= Xrestpos(xs
, xp
, xpos
);
828 while ((c
= getsc()) != '\n') {
830 yyerror("here document `%s' unclosed\n", eof
);
838 iop
->heredoc
= Xclose(xs
, xp
);
840 if (!(iop
->flag
& IOEVAL
))
841 ignore_backslash_newline
--;
845 #ifdef HAVE_PROTOTYPES
846 yyerror(const char *fmt
, ...)
848 yyerror(fmt
, va_alist
)
855 /* pop aliases and re-reads */
856 while (source
->type
== SALIAS
|| source
->type
== SREREAD
)
857 source
= source
->next
;
858 source
->str
= null
; /* zap pending input */
861 SH_VA_START(va
, fmt
);
862 shf_vfprintf(shl_out
, fmt
, va
);
868 * input for yylex with alias expansion
878 s
= (Source
*) alloc(sizeof(Source
), areap
);
888 if (type
== SFILE
|| type
== SSTDIN
) {
890 Xinit(s
->xs
, dummy
, 256, s
->areap
);
892 memset(&s
->xs
, 0, sizeof(s
->xs
));
899 register Source
*s
= source
;
902 while ((c
= *s
->str
++) == 0) {
903 s
->str
= NULL
; /* return 0 for EOF by default */
921 s
->start
= s
->str
= *s
->u
.strv
++;
926 if (*s
->u
.strv
== NULL
) {
927 s
->start
= s
->str
= newline
;
930 s
->start
= s
->str
= space
;
936 if (s
->flags
& SF_ALIASEND
) {
937 /* pass on an unused SF_ALIAS flag */
939 source
->flags
|= s
->flags
& SF_ALIAS
;
941 } else if (*s
->u
.tblp
->val
.s
942 && isspace((unsigned char)strchr(s
->u
.tblp
->val
.s
, 0)[-1]))
944 source
= s
= s
->next
; /* pop source stack */
945 /* Note that this alias ended with a space,
946 * enabling alias expansion on the following
949 s
->flags
|= SF_ALIAS
;
951 /* At this point, we need to keep the current
952 * alias in the source list so recursive
953 * aliases can be detected and we also need
954 * to return the next character. Do this
955 * by temporarily popping the alias to get
956 * the next character and then put it back
957 * in the source list with the SF_ALIASEND
960 source
= s
->next
; /* pop source stack */
961 source
->flags
|= s
->flags
& SF_ALIAS
;
964 s
->flags
|= SF_ALIASEND
;
965 s
->ugbuf
[0] = c
; s
->ugbuf
[1] = '\0';
966 s
->start
= s
->str
= s
->ugbuf
;
971 /* avoid reading eof twice */
979 if (s
->start
!= s
->ugbuf
) /* yuck */
980 afree(s
->u
.freeme
, ATEMP
);
981 source
= s
= s
->next
;
984 if (s
->str
== NULL
) {
986 s
->start
= s
->str
= null
;
989 if (s
->flags
& SF_ECHO
) {
990 shf_puts(s
->str
, shl_out
);
1001 char *xp
= Xstring(s
->xs
, xp
);
1002 int interactive
= Flag(FTALKING
) && s
->type
== SSTDIN
;
1003 int have_tty
= interactive
&& (s
->flags
& SF_TTY
);
1005 /* Done here to ensure nothing odd happens when a timeout occurs */
1006 XcheckN(s
->xs
, xp
, LINE
);
1008 s
->start
= s
->str
= xp
;
1011 if (have_tty
&& ksh_tmout
) {
1012 ksh_tmout_state
= TMOUT_READING
;
1022 || Flag(FEMACS
) || Flag(FGMACS
)
1028 nread
= x_read(xp
, LINE
);
1029 if (nread
< 0) /* read error */
1043 char *p
= shf_getse(xp
, Xnleft(s
->xs
, xp
), s
->u
.shf
);
1045 if (!p
&& shf_error(s
->u
.shf
)
1046 && shf_errno(s
->u
.shf
) == EINTR
)
1048 shf_clearerr(s
->u
.shf
);
1053 if (!p
|| (xp
= p
, xp
[-1] == '\n'))
1055 /* double buffer size */
1056 xp
++; /* move past null so doubling works... */
1057 XcheckN(s
->xs
, xp
, Xlength(s
->xs
, xp
));
1058 xp
--; /* ...and move back again */
1060 /* flush any unwanted input so other programs/builtins
1061 * can read it. Not very optimal, but less error prone
1062 * than flushing else where, dealing with redirections,
1064 * todo: reduce size of shf buffer (~128?) if SSTDIN
1066 if (s
->type
== SSTDIN
)
1067 shf_flush(s
->u
.shf
);
1069 /* XXX: temporary kludge to restore source after a
1070 * trap may have been executed.
1074 if (have_tty
&& ksh_tmout
)
1076 ksh_tmout_state
= TMOUT_EXECUTING
;
1080 s
->start
= s
->str
= Xstring(s
->xs
, xp
);
1081 strip_nuls(Xstring(s
->xs
, xp
), Xlength(s
->xs
, xp
));
1082 /* Note: if input is all nulls, this is not eof */
1083 if (Xlength(s
->xs
, xp
) == 0) { /* EOF */
1084 if (s
->type
== SFILE
)
1085 shf_fdclose(s
->u
.shf
);
1087 } else if (interactive
) {
1089 char *p
= Xstring(s
->xs
, xp
);
1090 if (cur_prompt
== PS1
)
1091 while (*p
&& ctype(*p
, C_IFS
) && ctype(*p
, C_IFSWS
))
1094 # ifdef EASY_HISTORY
1095 if (cur_prompt
== PS2
)
1096 histappend(Xstring(s
->xs
, xp
), 1);
1098 # endif /* EASY_HISTORY */
1101 histsave(s
->line
, s
->str
, 1);
1104 #endif /* HISTORY */
1107 set_prompt(PS2
, (Source
*) 0);
1118 case PS1
: /* command */
1120 /* Substitute ! and !! here, before substitutions are done
1121 * so ! in expanded variables are not expanded.
1122 * NOTE: this is not what at&t ksh does (it does it after
1123 * substitutions, POSIX doesn't say which is to be done.
1127 char * volatile ps1
;
1130 ps1
= str_val(global("PS1"));
1131 shf
= shf_sopen((char *) 0, strlen(ps1
) * 2,
1132 SHF_WR
| SHF_DYNAMIC
, (struct shf
*) 0);
1134 if (*ps1
!= '!' || *++ps1
== '!')
1135 shf_putchar(*ps1
++, shf
);
1137 shf_fprintf(shf
, "%d",
1138 s
? s
->line
+ 1 : 0);
1140 ps1
= shf_sclose(shf
);
1141 saved_atemp
= ATEMP
;
1143 if (ksh_sigsetjmp(e
->jbuf
, 0)) {
1144 prompt
= safe_prompt
;
1145 /* Don't print an error - assume it has already
1146 * been printed. Reason is we may have forked
1147 * to run a command and the child may be
1148 * unwinding its stack through this code as it
1152 prompt
= str_save(substitute(ps1
, 0),
1157 prompt
= str_val(global("PS1"));
1161 case PS2
: /* command continuation */
1162 prompt
= str_val(global("PS2"));
1167 /* See also related routine, promptlen() in edit.c */
1169 pprompt(cp
, ntruncate
)
1180 else if (*++cp
== '!')
1186 shf_snprintf(p
= nbuf
, sizeof(nbuf
), "%d",
1190 if (ntruncate
>= len
) {
1198 shf_write(p
, len
, shl_out
);
1204 shf_putc(c
, shl_out
);
1207 shf_puts(cp
+ ntruncate
, shl_out
);
1211 /* Read the variable part of a ${...} expression (ie, up to but not including
1212 * the :[-+?=#%] or close-brace.
1215 get_brace_var(wsp
, wp
)
1220 PS_INITIAL
, PS_SAW_HASH
, PS_IDENT
,
1221 PS_NUMBER
, PS_VAR1
, PS_END
1229 /* State machine to figure out where the variable part ends. */
1233 state
= PS_SAW_HASH
;
1236 /* fall through.. */
1242 else if (ctype(c
, C_VAR1
))
1253 if (!arraysub(&tmp
))
1254 yyerror("missing ]\n");
1256 for (p
= tmp
; *p
; ) {
1261 c
= getsc(); /* the ] */
1272 case PS_END
: /* keep gcc happy */
1275 if (state
== PS_END
) {
1276 *wp
++ = '\0'; /* end of variable part */
1287 * Save an array subscript - returns true if matching bracket found, false
1288 * if eof or newline was found.
1289 * (Returned string double null terminated)
1298 int depth
= 1; /* we are just past the initial [ */
1300 Xinit(ws
, wp
, 32, ATEMP
);
1310 } while (depth
> 0 && c
&& c
!= '\n');
1313 *strp
= Xclose(ws
, wp
);
1315 return depth
== 0 ? 1 : 0;
1318 /* Unget a char: handles case when we are already at the start of the buffer */
1325 /* Don't unget eof... */
1326 if (source
->str
== null
&& c
== '\0')
1328 if (source
->str
> source
->start
)
1333 s
= pushs(SREREAD
, source
->areap
);
1334 s
->ugbuf
[0] = c
; s
->ugbuf
[1] = '\0';
1335 s
->start
= s
->str
= s
->ugbuf
;
1343 /* Called to get a char that isn't a \newline sequence. */
1345 getsc_bn
ARGS((void))
1349 if (ignore_backslash_newline
)
1352 if (backslash_skip
== 1) {
1362 if ((c2
= getsc_()) == '\n')
1363 /* ignore the \newline; get the next char... */
1373 push_state_(si
, old_end
)
1377 Lex_state
*new = alloc(sizeof(Lex_state
) * STATE_BSIZE
, ATEMP
);
1379 new[0].ls_info
.base
= old_end
;
1381 si
->end
= &new[STATE_BSIZE
];
1386 pop_state_(si
, old_end
)
1390 Lex_state
*old_base
= si
->base
;
1392 si
->base
= old_end
->ls_info
.base
- STATE_BSIZE
;
1393 si
->end
= old_end
->ls_info
.base
;
1395 afree(old_base
, ATEMP
);
1397 return si
->base
+ STATE_BSIZE
- 1;