1 /* $NetBSD: lex.c,v 1.12 2005/09/11 22:16:00 christos Exp $ */
4 * lexical analysis and source input
9 __RCSID("$NetBSD: lex.c,v 1.12 2005/09/11 22:16:00 christos 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
++;
253 *wp
++ = QCHAR
, *wp
++ = c
;
256 if ((cf
& HEREDOC
) == 0) {
257 *wp
++ = QCHAR
, *wp
++ = c
;
263 if (c
) { /* trailing \ is lost */
264 *wp
++ = CHAR
, *wp
++ = '\\';
265 *wp
++ = CHAR
, *wp
++ = c
;
272 if (c
== '(') /*)*/ {
274 if (c
== '(') /*)*/ {
275 PUSH_STATE(SASPAREN
);
276 statep
->ls_sasparen
.nparen
= 2;
277 statep
->ls_sasparen
.start
=
282 PUSH_STATE(SCSPAREN
);
283 statep
->ls_scsparen
.nparen
= 1;
284 statep
->ls_scsparen
.csstate
= 0;
287 } else if (c
== '{') /*}*/ {
290 wp
= get_brace_var(&ws
, wp
);
292 /* allow :# and :% (ksh88 compat) */
294 *wp
++ = CHAR
, *wp
++ = c
;
297 /* If this is a trim operation,
298 * treat (,|,) specially in STBRACE.
300 if (c
== '#' || c
== '%') {
307 } else if (ctype(c
, C_ALPHA
)) {
314 } while (ctype(c
, C_ALPHA
|C_DIGIT
));
319 } else if (ctype(c
, C_DIGIT
|C_VAR1
)) {
328 *wp
++ = CHAR
, *wp
++ = '$';
335 /* Need to know if we are inside double quotes
336 * since sh/at&t-ksh translate the \" to " in
337 * "`..\"..`". POSIX also requires this.
338 * An earlier version of ksh misinterpreted
339 * the POSIX specification and performed
340 * removal of backslash escapes only if
341 * posix mode was not in effect.
343 statep
->ls_sbquote
.indquotes
= 0;
344 Lex_state
*s
= statep
;
345 Lex_state
*base
= state_info
.base
;
347 for (; s
!= base
; s
--) {
348 if (s
->ls_state
== SDQUOTE
) {
349 statep
->ls_sbquote
.indquotes
= 1;
355 if (!(s
= s
->ls_info
.base
))
357 base
= s
-- - STATE_BSIZE
;
361 *wp
++ = CHAR
, *wp
++ = c
;
369 ignore_backslash_newline
--;
371 *wp
++ = QCHAR
, *wp
++ = c
;
382 case SCSPAREN
: /* $( .. ) */
383 /* todo: deal with $(...) quoting properly
384 * kludge to partly fake quoting inside $(..): doesn't
385 * really work because nested $(..) or ${..} inside
386 * double quotes aren't dealt with.
388 switch (statep
->ls_scsparen
.csstate
) {
392 statep
->ls_scsparen
.nparen
++;
395 statep
->ls_scsparen
.nparen
--;
398 statep
->ls_scsparen
.csstate
= 1;
401 statep
->ls_scsparen
.csstate
= 2;
404 statep
->ls_scsparen
.csstate
= 4;
405 ignore_backslash_newline
++;
410 case 1: /* backslash in normal mode */
411 case 3: /* backslash in double quotes */
412 --statep
->ls_scsparen
.csstate
;
415 case 2: /* double quotes */
417 statep
->ls_scsparen
.csstate
= 0;
419 statep
->ls_scsparen
.csstate
= 3;
422 case 4: /* single quotes */
424 statep
->ls_scsparen
.csstate
= 0;
425 ignore_backslash_newline
--;
429 if (statep
->ls_scsparen
.nparen
== 0) {
431 *wp
++ = 0; /* end of COMSUB */
436 case SASPAREN
: /* $(( .. )) */
437 /* todo: deal with $((...); (...)) properly */
438 /* XXX should nest using existing state machine
439 * (embed "..", $(...), etc.) */
441 statep
->ls_sasparen
.nparen
++;
443 statep
->ls_sasparen
.nparen
--;
444 if (statep
->ls_sasparen
.nparen
== 1) {
446 if ((c2
= getsc()) == ')') {
448 *wp
++ = 0; /* end of EXPRSUB */
454 /* mismatched parenthesis -
455 * assume we were really
456 * parsing a $(..) expression
459 statep
->ls_sasparen
.start
);
460 memmove(s
+ 1, s
, wp
- s
);
464 statep
->ls_scsparen
.nparen
= 1;
465 statep
->ls_scsparen
.csstate
= 0;
466 state
= statep
->ls_state
486 /* Same as SBRACE, except (,|,) treated specially */
492 } else if (c
== '|') {
494 } else if (c
== '(') {
496 *wp
++ = ' '; /* simile for @ */
497 PUSH_STATE(SPATTERN
);
506 } else if (c
== '\\') {
507 switch (c
= getsc()) {
513 if (statep
->ls_sbquote
.indquotes
) {
519 if (c
) { /* trailing \ is lost */
529 case SWORD
: /* ONEWORD */
533 case SLETPAREN
: /* LETEXPR: (( ... )) */
536 if (statep
->ls_sletparen
.nparen
> 0)
537 --statep
->ls_sletparen
.nparen
;
539 else if ((c2
= getsc()) == ')') {
546 /* parenthesis inside quotes and backslashes
547 * are lost, but at&t ksh doesn't count them
550 ++statep
->ls_sletparen
.nparen
;
554 case SHEREDELIM
: /* <<,<<- delimiter */
555 /* XXX chuck this state (and the next) - use
556 * the existing states ($ and \`..` should be
557 * stripped of their specialness after the
560 /* here delimiters need a special case since
561 * $ and `..` are not to be treated specially
565 if (c
) { /* trailing \ is lost */
569 } else if (c
== '\'') {
572 ignore_backslash_newline
++;
573 } else if (c
== '"') {
574 state
= statep
->ls_state
= SHEREDQUOTE
;
582 case SHEREDQUOTE
: /* " in <<,<<- delimiter */
585 state
= statep
->ls_state
= SHEREDELIM
;
588 switch (c
= getsc()) {
593 if (c
) { /* trailing \ lost */
605 case SPATTERN
: /* in *(...|...) pattern (*+?@!) */
606 if ( /*(*/ c
== ')') {
609 } else if (c
== '|') {
611 } else if (c
== '(') {
613 *wp
++ = ' '; /* simile for @ */
614 PUSH_STATE(SPATTERN
);
622 if (statep
!= &states
[1])
623 /* XXX figure out what is missing */
624 yyerror("no closing quote\n");
626 /* This done to avoid tests for SHEREDELIM wherever SBASE tested */
627 if (state
== SHEREDELIM
)
630 dp
= Xstring(ws
, wp
);
631 if ((c
== '<' || c
== '>') && state
== SBASE
632 && ((c2
= Xlength(ws
, wp
)) == 0
633 || (c2
== 2 && dp
[0] == CHAR
&& digit(dp
[1]))))
636 (struct ioword
*) alloc(sizeof(*iop
), ATEMP
);
639 iop
->unit
= dp
[1] - '0';
641 iop
->unit
= c
== '>'; /* 0 for <, 1 for > */
644 /* <<, >>, <> are ok, >< is not */
645 if (c
== c2
|| (c
== '<' && c2
== '>')) {
646 iop
->flag
= c
== c2
?
647 (c
== '>' ? IOCAT
: IOHERE
) : IORDWR
;
648 if (iop
->flag
== IOHERE
) {
649 if ((c2
= getsc()) == '-') {
655 } else if (c2
== '&')
656 iop
->flag
= IODUP
| (c
== '<' ? IORDUP
: 0);
658 iop
->flag
= c
== '>' ? IOWRITE
: IOREAD
;
659 if (c
== '>' && c2
== '|')
665 iop
->name
= (char *) 0;
666 iop
->delim
= (char *) 0;
667 iop
->heredoc
= (char *) 0;
668 Xfree(ws
, wp
); /* free word */
673 if (wp
== dp
&& state
== SBASE
) {
674 Xfree(ws
, wp
); /* free word */
675 /* no word, process LEX1 character */
683 if ((c2
= getsc()) == c
)
684 c
= (c
== ';') ? BREAK
:
686 (c
== '&') ? LOGAND
:
689 else if (c
== '|' && c2
== '&')
704 if ((c2
= getsc()) == '(') /*)*/
705 /* XXX need to handle ((...); (...)) */
717 *wp
++ = EOS
; /* terminate word */
718 yylval
.cp
= Xclose(ws
, wp
);
721 || state
== SLETPAREN
725 ungetsc(c
); /* unget terminator */
727 /* copy word to unprefixed string ident */
728 for (sp
= yylval
.cp
, dp
= ident
; dp
< ident
+IDENT
&& (c
= *sp
++) == CHAR
; )
730 /* Make sure the ident array stays '\0' padded */
731 memset(dp
, 0, (ident
+IDENT
) - dp
+ 1);
733 *ident
= '\0'; /* word is not unquoted */
735 if (*ident
!= '\0' && (cf
&(KEYWORD
|ALIAS
))) {
740 if ((cf
& KEYWORD
) && (p
= tsearch(&keywords
, ident
, h
))
741 && (!(cf
& ESACONLY
) || p
->val
.i
== ESAC
|| p
->val
.i
== '}'))
743 afree(yylval
.cp
, ATEMP
);
746 if ((cf
& ALIAS
) && (p
= tsearch(&aliases
, ident
, h
))
747 && (p
->flag
& ISSET
))
751 for (s
= source
; s
->type
== SALIAS
; s
= s
->next
)
754 /* push alias expansion */
755 s
= pushs(SALIAS
, source
->areap
);
756 s
->start
= s
->str
= p
->val
.s
;
760 afree(yylval
.cp
, ATEMP
);
771 register struct ioword
**p
;
773 for (p
= heres
; p
< herep
; p
++)
779 * read "<<word" text into temp file
794 eof
= evalstr(iop
->delim
, 0);
796 if (!(iop
->flag
& IOEVAL
))
797 ignore_backslash_newline
++;
799 Xinit(xs
, xp
, 256, ATEMP
);
803 skiptabs
= iop
->flag
& IOSKIP
;
804 xpos
= Xsavepos(xs
, xp
);
805 while ((c
= getsc()) != 0) {
817 /* Allow EOF here so commands with out trailing newlines
818 * will work (eg, ksh -c '...', $(...), etc).
820 if (*eofp
== '\0' && (c
== 0 || c
== '\n')) {
821 xp
= Xrestpos(xs
, xp
, xpos
);
825 while ((c
= getsc()) != '\n') {
827 yyerror("here document `%s' unclosed\n", eof
);
835 iop
->heredoc
= Xclose(xs
, xp
);
837 if (!(iop
->flag
& IOEVAL
))
838 ignore_backslash_newline
--;
842 #ifdef HAVE_PROTOTYPES
843 yyerror(const char *fmt
, ...)
845 yyerror(fmt
, va_alist
)
852 /* pop aliases and re-reads */
853 while (source
->type
== SALIAS
|| source
->type
== SREREAD
)
854 source
= source
->next
;
855 source
->str
= null
; /* zap pending input */
858 SH_VA_START(va
, fmt
);
859 shf_vfprintf(shl_out
, fmt
, va
);
865 * input for yylex with alias expansion
875 s
= (Source
*) alloc(sizeof(Source
), areap
);
885 if (type
== SFILE
|| type
== SSTDIN
) {
887 Xinit(s
->xs
, dummy
, 256, s
->areap
);
889 memset(&s
->xs
, 0, sizeof(s
->xs
));
896 register Source
*s
= source
;
899 while ((c
= *s
->str
++) == 0) {
900 s
->str
= NULL
; /* return 0 for EOF by default */
918 s
->start
= s
->str
= *s
->u
.strv
++;
923 if (*s
->u
.strv
== NULL
) {
924 s
->start
= s
->str
= newline
;
927 s
->start
= s
->str
= space
;
933 if (s
->flags
& SF_ALIASEND
) {
934 /* pass on an unused SF_ALIAS flag */
936 source
->flags
|= s
->flags
& SF_ALIAS
;
938 } else if (*s
->u
.tblp
->val
.s
939 && isspace((unsigned char)strchr(s
->u
.tblp
->val
.s
, 0)[-1]))
941 source
= s
= s
->next
; /* pop source stack */
942 /* Note that this alias ended with a space,
943 * enabling alias expansion on the following
946 s
->flags
|= SF_ALIAS
;
948 /* At this point, we need to keep the current
949 * alias in the source list so recursive
950 * aliases can be detected and we also need
951 * to return the next character. Do this
952 * by temporarily popping the alias to get
953 * the next character and then put it back
954 * in the source list with the SF_ALIASEND
957 source
= s
->next
; /* pop source stack */
958 source
->flags
|= s
->flags
& SF_ALIAS
;
961 s
->flags
|= SF_ALIASEND
;
962 s
->ugbuf
[0] = c
; s
->ugbuf
[1] = '\0';
963 s
->start
= s
->str
= s
->ugbuf
;
968 /* avoid reading eof twice */
976 if (s
->start
!= s
->ugbuf
) /* yuck */
977 afree(s
->u
.freeme
, ATEMP
);
978 source
= s
= s
->next
;
981 if (s
->str
== NULL
) {
983 s
->start
= s
->str
= null
;
986 if (s
->flags
& SF_ECHO
) {
987 shf_puts(s
->str
, shl_out
);
998 char *xp
= Xstring(s
->xs
, xp
);
999 int interactive
= Flag(FTALKING
) && s
->type
== SSTDIN
;
1000 int have_tty
= interactive
&& (s
->flags
& SF_TTY
);
1002 /* Done here to ensure nothing odd happens when a timeout occurs */
1003 XcheckN(s
->xs
, xp
, LINE
);
1005 s
->start
= s
->str
= xp
;
1008 if (have_tty
&& ksh_tmout
) {
1009 ksh_tmout_state
= TMOUT_READING
;
1019 || Flag(FEMACS
) || Flag(FGMACS
)
1025 nread
= x_read(xp
, LINE
);
1026 if (nread
< 0) /* read error */
1040 char *p
= shf_getse(xp
, Xnleft(s
->xs
, xp
), s
->u
.shf
);
1042 if (!p
&& shf_error(s
->u
.shf
)
1043 && shf_errno(s
->u
.shf
) == EINTR
)
1045 shf_clearerr(s
->u
.shf
);
1050 if (!p
|| (xp
= p
, xp
[-1] == '\n'))
1052 /* double buffer size */
1053 xp
++; /* move past null so doubling works... */
1054 XcheckN(s
->xs
, xp
, Xlength(s
->xs
, xp
));
1055 xp
--; /* ...and move back again */
1057 /* flush any unwanted input so other programs/builtins
1058 * can read it. Not very optimal, but less error prone
1059 * than flushing else where, dealing with redirections,
1061 * todo: reduce size of shf buffer (~128?) if SSTDIN
1063 if (s
->type
== SSTDIN
)
1064 shf_flush(s
->u
.shf
);
1066 /* XXX: temporary kludge to restore source after a
1067 * trap may have been executed.
1071 if (have_tty
&& ksh_tmout
)
1073 ksh_tmout_state
= TMOUT_EXECUTING
;
1077 s
->start
= s
->str
= Xstring(s
->xs
, xp
);
1078 strip_nuls(Xstring(s
->xs
, xp
), Xlength(s
->xs
, xp
));
1079 /* Note: if input is all nulls, this is not eof */
1080 if (Xlength(s
->xs
, xp
) == 0) { /* EOF */
1081 if (s
->type
== SFILE
)
1082 shf_fdclose(s
->u
.shf
);
1084 } else if (interactive
) {
1086 char *p
= Xstring(s
->xs
, xp
);
1087 if (cur_prompt
== PS1
)
1088 while (*p
&& ctype(*p
, C_IFS
) && ctype(*p
, C_IFSWS
))
1091 # ifdef EASY_HISTORY
1092 if (cur_prompt
== PS2
)
1093 histappend(Xstring(s
->xs
, xp
), 1);
1095 # endif /* EASY_HISTORY */
1098 histsave(s
->line
, s
->str
, 1);
1101 #endif /* HISTORY */
1104 set_prompt(PS2
, (Source
*) 0);
1115 case PS1
: /* command */
1117 /* Substitute ! and !! here, before substitutions are done
1118 * so ! in expanded variables are not expanded.
1119 * NOTE: this is not what at&t ksh does (it does it after
1120 * substitutions, POSIX doesn't say which is to be done.
1124 char * volatile ps1
;
1127 ps1
= str_val(global("PS1"));
1128 shf
= shf_sopen((char *) 0, strlen(ps1
) * 2,
1129 SHF_WR
| SHF_DYNAMIC
, (struct shf
*) 0);
1131 if (*ps1
!= '!' || *++ps1
== '!')
1132 shf_putchar(*ps1
++, shf
);
1134 shf_fprintf(shf
, "%d",
1135 s
? s
->line
+ 1 : 0);
1137 ps1
= shf_sclose(shf
);
1138 saved_atemp
= ATEMP
;
1140 if (ksh_sigsetjmp(e
->jbuf
, 0)) {
1141 prompt
= safe_prompt
;
1142 /* Don't print an error - assume it has already
1143 * been printed. Reason is we may have forked
1144 * to run a command and the child may be
1145 * unwinding its stack through this code as it
1149 prompt
= str_save(substitute(ps1
, 0),
1154 prompt
= str_val(global("PS1"));
1158 case PS2
: /* command continuation */
1159 prompt
= str_val(global("PS2"));
1164 /* See also related routine, promptlen() in edit.c */
1166 pprompt(cp
, ntruncate
)
1177 else if (*++cp
== '!')
1183 shf_snprintf(p
= nbuf
, sizeof(nbuf
), "%d",
1187 if (ntruncate
>= len
) {
1195 shf_write(p
, len
, shl_out
);
1201 shf_putc(c
, shl_out
);
1204 shf_puts(cp
+ ntruncate
, shl_out
);
1208 /* Read the variable part of a ${...} expression (ie, up to but not including
1209 * the :[-+?=#%] or close-brace.
1212 get_brace_var(wsp
, wp
)
1217 PS_INITIAL
, PS_SAW_HASH
, PS_IDENT
,
1218 PS_NUMBER
, PS_VAR1
, PS_END
1226 /* State machine to figure out where the variable part ends. */
1230 state
= PS_SAW_HASH
;
1233 /* fall through.. */
1239 else if (ctype(c
, C_VAR1
))
1250 if (!arraysub(&tmp
))
1251 yyerror("missing ]\n");
1253 for (p
= tmp
; *p
; ) {
1258 c
= getsc(); /* the ] */
1269 case PS_END
: /* keep gcc happy */
1272 if (state
== PS_END
) {
1273 *wp
++ = '\0'; /* end of variable part */
1284 * Save an array subscript - returns true if matching bracket found, false
1285 * if eof or newline was found.
1286 * (Returned string double null terminated)
1295 int depth
= 1; /* we are just past the initial [ */
1297 Xinit(ws
, wp
, 32, ATEMP
);
1307 } while (depth
> 0 && c
&& c
!= '\n');
1310 *strp
= Xclose(ws
, wp
);
1312 return depth
== 0 ? 1 : 0;
1315 /* Unget a char: handles case when we are already at the start of the buffer */
1322 /* Don't unget eof... */
1323 if (source
->str
== null
&& c
== '\0')
1325 if (source
->str
> source
->start
)
1330 s
= pushs(SREREAD
, source
->areap
);
1331 s
->ugbuf
[0] = c
; s
->ugbuf
[1] = '\0';
1332 s
->start
= s
->str
= s
->ugbuf
;
1340 /* Called to get a char that isn't a \newline sequence. */
1342 getsc_bn
ARGS((void))
1346 if (ignore_backslash_newline
)
1349 if (backslash_skip
== 1) {
1359 if ((c2
= getsc_()) == '\n')
1360 /* ignore the \newline; get the next char... */
1370 push_state_(si
, old_end
)
1374 Lex_state
*new = alloc(sizeof(Lex_state
) * STATE_BSIZE
, ATEMP
);
1376 new[0].ls_info
.base
= old_end
;
1378 si
->end
= &new[STATE_BSIZE
];
1383 pop_state_(si
, old_end
)
1387 Lex_state
*old_base
= si
->base
;
1389 si
->base
= old_end
->ls_info
.base
- STATE_BSIZE
;
1390 si
->end
= old_end
->ls_info
.base
;
1392 afree(old_base
, ATEMP
);
1394 return si
->base
+ STATE_BSIZE
- 1;