xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_lex.l (revision 3d580eda65b7c5ad75a73a93dceeebddfae06ec9)
1 %pointer	/* Make yytext a pointer, not an array */
2 
3 %{
4 /*
5  * CDDL HEADER START
6  *
7  * The contents of this file are subject to the terms of the
8  * Common Development and Distribution License (the "License").
9  * You may not use this file except in compliance with the License.
10  *
11  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
12  * or http://www.opensolaris.org/os/licensing.
13  * See the License for the specific language governing permissions
14  * and limitations under the License.
15  *
16  * When distributing Covered Code, include this CDDL HEADER in each
17  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
18  * If applicable, add the following below this CDDL HEADER, with the
19  * fields enclosed by brackets "[]" replaced with your own identifying
20  * information: Portions Copyright [yyyy] [name of copyright owner]
21  *
22  * CDDL HEADER END
23  *
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*
29  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
30  * Copyright (c) 2017 by Delphix. All rights reserved.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/isa_defs.h>
35 
36 #include <strings.h>
37 #include <stdarg.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <errno.h>
41 
42 #include <mdb/mdb_types.h>
43 #include <mdb/mdb_debug.h>
44 #include <mdb/mdb_nv.h>
45 #include <mdb/mdb_lex.h>
46 #include <mdb/mdb_frame.h>
47 #include <mdb/mdb_string.h>
48 #include <mdb/mdb_stdlib.h>
49 #include <mdb/mdb_err.h>
50 #include <mdb/mdb.h>
51 
52 #include "mdb_grammar.h"
53 
54 /*
55  * lex hardcodes yyin and yyout to stdin and stdout, respectively, before we get
56  * control.  We've redirected printf and fprintf (see mdb_lex.h) to yyprintf and
57  * yyfprintf, which ignore the FILE * provided by yyout.  __iob-based stdin and
58  * stdout are useless in kmdb, since we don't have stdio.  We define __iob here
59  * to shut the linker up.
60  */
61 #ifdef _KMDB
62 FILE __iob[_NFILE];
63 #endif
64 
65 /*
66  * We need to undefine lex's input, unput, and output macros so that references
67  * to these call the functions we provide at the end of this source file,
68  * instead of the default versions based on libc's stdio.
69  */
70 #ifdef input
71 #undef input
72 #endif
73 
74 #ifdef unput
75 #undef unput
76 #endif
77 
78 #ifdef output
79 #undef output
80 #endif
81 
82 static int input(void);
83 static void unput(int);
84 static void output(int);
85 
86 static void string_unquote(char *);
87 
88 extern int yydebug;
89 
90 /*
91  * This will prevent lex from trying to malloc() and resize our yytext variable,
92  * instead it will just print out an error message and exit(), which seems
93  * a lesser of two evils.
94  */
95 #define YYISARRAY
96 
97 %}
98 
99 %o	9000
100 %a	5000
101 
102 %s	S_SHELLCMD
103 %s	S_INITIAL
104 %s	S_FMTLIST
105 %s	S_ARGLIST
106 %s	S_EXPR
107 
108 RGX_CMD_CHAR	[?%@A-Z\^_`a-z]
109 RGX_SYMBOL	[a-zA-Z_.][0-9a-zA-Z_.`]*
110 RGX_SIMPLE_CHAR	[^ \t\n;!|"'\$]
111 RGX_CHR_SEQ	([^'\n]|\\[^'\n]|\\')*
112 RGX_STR_SEQ	([^"\\\n]|\\[^"\n]|\\\")*
113 RGX_COMMENT	"//".*\n
114 
115 %%
116 
117 <S_INITIAL>{RGX_COMMENT}	|
118 <S_FMTLIST>{RGX_COMMENT}	|
119 <S_ARGLIST>{RGX_COMMENT}	{
120 		/*
121 		 * Comments are legal in these three states -- if we see one
122 		 * eat the line and return the newline character.
123 		 */
124 		BEGIN(S_INITIAL);
125 		return ('\n');
126 	}
127 
128 <S_INITIAL>"=="	|
129 <S_EXPR>"=="	return (MDB_TOK_EQUAL); /* Equality operator */
130 
131 <S_INITIAL>"!="	|
132 <S_EXPR>"!="	return (MDB_TOK_NOTEQUAL); /* Inequality operator */
133 
134 <S_INITIAL>"!"	|
135 <S_FMTLIST>"!"	|
136 <S_ARGLIST>"!"	{
137 		/*
138 		 * Shell escapes are legal in all of these states -- switch to
139 		 * the shell command state and return the ! character.
140 		 */
141 		BEGIN(S_SHELLCMD);
142 		return (yytext[0]);
143 	}
144 
145 <S_FMTLIST>"|"	|
146 <S_ARGLIST>"|"	{
147 		/*
148 		 * Pipelines can appear in any of these states -- switch to
149 		 * the initial state and return the | character.
150 		 */
151 		BEGIN(S_INITIAL);
152 		return (yytext[0]);
153 	}
154 
155 <S_SHELLCMD>[^;\n]+	{
156 		/*
157 		 * Once in the shell-command state, we return all remaining
158 		 * characters up to a newline or ';' delimiter as a single
159 		 * string which will be passed to $SHELL -c.
160 		 */
161 		yylval.l_string = strdup(yytext);
162 		BEGIN(S_INITIAL);
163 		return (MDB_TOK_STRING);
164 	}
165 
166 <S_INITIAL>"::"{RGX_SYMBOL}	{
167 		/*
168 		 * Verb ::command-name -- lookup the correspond dcmd and
169 		 * switch to the argument list state.
170 		 */
171 		if ((yylval.l_dcmd = mdb_dcmd_lookup(yytext + 2)) == NULL)
172 			yyperror("invalid command '%s'", yytext);
173 
174 		BEGIN(S_ARGLIST);
175 		return (MDB_TOK_DCMD);
176 	}
177 
178 <S_INITIAL>"$<<"|"$<"|"$>"	|
179 <S_INITIAL>[\$:]{RGX_CMD_CHAR}	{
180 		/*
181 		 * Old-style :c or $c command -- lookup the corresponding dcmd
182 		 * and switch to the argument list state.
183 		 */
184 		if ((yylval.l_dcmd = mdb_dcmd_lookup(yytext)) == NULL)
185 			yyperror("invalid command '%s'", yytext);
186 
187 		BEGIN(S_ARGLIST);
188 		return (MDB_TOK_DCMD);
189 	}
190 
191 <S_INITIAL>">/"[a-zA-Z0-9]"/"	{
192 		/*
193 		 * Variable assignment with size cast -- append the cast letter
194 		 * to the argument list, and switch to the argument list state.
195 		 */
196 		mdb_arg_t arg;
197 
198 		arg.a_un.a_char = yytext[2];
199 		arg.a_type = MDB_TYPE_CHAR;
200 
201 		mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
202 		yylval.l_dcmd = mdb_dcmd_lookup(">");
203 
204 		BEGIN(S_ARGLIST);
205 		return (MDB_TOK_DCMD);
206 	}
207 
208 <S_INITIAL>">"	{
209 		/*
210 		 * Variable assignment -- switch to the argument list state.
211 		 */
212 		yylval.l_dcmd = mdb_dcmd_lookup(yytext);
213 		BEGIN(S_ARGLIST);
214 		return (MDB_TOK_DCMD);
215 	}
216 
217 <S_INITIAL>[/\\?][ \t]*[vwzWZlLM]	{
218 		/*
219 		 * Format verb followed by write or match signifier -- switch
220 		 * to the value list state and return the verb character.  We
221 		 * also append the actual format character to the arg list.
222 		 */
223 		mdb_arg_t arg;
224 
225 		arg.a_un.a_char = yytext[yyleng - 1];
226 		arg.a_type = MDB_TYPE_CHAR;
227 
228 		mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
229 
230 		BEGIN(S_ARGLIST);
231 		return yytext[0];
232 	}
233 
234 <S_INITIAL>[/\\@?=]	{
235 		/*
236 		 * Format verb -- switch to the format list state and return
237 		 * the actual verb character verbatim.
238 		 */
239 		BEGIN(S_FMTLIST);
240 		return (yytext[0]);
241 	}
242 
243 <S_INITIAL>'{RGX_CHR_SEQ}$	|
244 <S_EXPR>'{RGX_CHR_SEQ}$		yyerror("syntax error: ' unmatched");
245 
246 <S_INITIAL>'{RGX_CHR_SEQ}' 	|
247 <S_EXPR>'{RGX_CHR_SEQ}'		{
248 		char *s, *p, *q;
249 		size_t nbytes;
250 
251 		/*
252 		 * If the character sequence is zero-length, return 0.
253 		 */
254 		if (yyleng == 2) {
255 			yylval.l_immediate = 0;
256 			return (MDB_TOK_IMMEDIATE);
257 		}
258 
259 		s = yytext + 1;			/* Skip past initial quote */
260 		yytext[yyleng - 1] = '\0';	/* Overwrite final quote */
261 		nbytes = stresc2chr(s);		/* Convert escapes */
262 		yylval.l_immediate = 0;		/* Initialize token value */
263 
264 		if (nbytes > sizeof (uintmax_t)) {
265 			yyerror("character constant may not exceed %lu bytes\n",
266 			    (ulong_t)sizeof (uintmax_t));
267 		}
268 
269 #ifdef _LITTLE_ENDIAN
270 		p = ((char*)&yylval.l_immediate) + nbytes - 1;
271 
272 		for (q = s; nbytes != 0; nbytes--)
273 			*p-- = *q++;
274 #else
275 		bcopy(s, ((char *)&yylval.l_immediate) +
276 		    sizeof (uintmax_t) - nbytes, nbytes);
277 #endif
278 		return (MDB_TOK_IMMEDIATE);
279 	}
280 
281 \"{RGX_STR_SEQ}$	yyerror("syntax error: \" unmatched");
282 
283 \"{RGX_STR_SEQ}\"	{
284 		/*
285 		 * Quoted string -- convert C escape sequences and return the
286 		 * string as a token.
287 		 */
288 		yylval.l_string = strndup(yytext + 1, yyleng - 2);
289 		(void) stresc2chr(yylval.l_string);
290 		return (MDB_TOK_STRING);
291 	}
292 
293 <S_ARGLIST>"$["	|
294 <S_FMTLIST>"$["	{
295 		/*
296 		 * Start of expression -- begin expression state and save the
297 		 * current state so we can return at the end of the expression.
298 		 */
299 		mdb.m_frame->f_oldstate = YYSTATE;
300 		BEGIN(S_EXPR);
301 		return (MDB_TOK_LEXPR);
302 	}
303 
304 <S_ARGLIST>{RGX_SIMPLE_CHAR}*("'"{RGX_CHR_SEQ}"'"|\"{RGX_STR_SEQ}\"|{RGX_SIMPLE_CHAR}+)* {
305 		/*
306 		 * String token -- create a copy of the string and return it.
307 		 * We need to handle embedded single and double-quote pairs,
308 		 * which overcomplicates this slightly.
309 		 */
310 		yylval.l_string = strdup(yytext);
311 		string_unquote(yylval.l_string);
312 		return (MDB_TOK_STRING);
313 	}
314 
315 <S_FMTLIST>[0-9]+	{
316 		/*
317 		 * Immediate value -- in the format list, all immediates
318 		 * are assumed to be in decimal.
319 		 */
320 		yylval.l_immediate = mdb_strtonum(yytext, 10);
321 		return (MDB_TOK_IMMEDIATE);
322 	}
323 
324 <S_FMTLIST>{RGX_SIMPLE_CHAR}	{
325 		/*
326 		 * Non-meta character -- in the format list, we return each
327 		 * character as a separate token to be added as an argument.
328 		 */
329 		yylval.l_char = yytext[0];
330 		return (MDB_TOK_CHAR);
331 	}
332 
333 <S_EXPR>";"|"!"|\n	{
334 		/*
335 		 * In the expression state only, we cannot see a command
336 		 * delimiter or shell escape before we end the expression.
337 		 */
338 		yyerror("syntax error: $[ unmatched");
339 	}
340 
341 <S_EXPR>"]"	{
342 		/*
343 		 * End of expression state.  Restore the state we were in
344 		 * before the "$[" which started this expression.
345 		 */
346 		BEGIN(mdb.m_frame->f_oldstate);
347 		return (MDB_TOK_REXPR);
348 	}
349 
350 <S_INITIAL>"<"{RGX_SYMBOL}	|
351 <S_INITIAL>"<"[0-9]		|
352 <S_EXPR>"<"{RGX_SYMBOL}		|
353 <S_EXPR>"<"[0-9]	{
354 		/*
355 		 * Variable reference -- lookup the variable and return a
356 		 * pointer to it.  Referencing undefined variables is an error.
357 		 */
358 		yylval.l_var = mdb_nv_lookup(&mdb.m_nv, &yytext[1]);
359 
360 		if (yylval.l_var == NULL)
361 			yyerror("variable '%s' is not defined", &yytext[1]);
362 
363 		return (MDB_TOK_VAR_REF);
364 	}
365 
366 <S_INITIAL>"<<"	|
367 <S_EXPR>"<<"	return (MDB_TOK_LSHIFT); /* Logical shift left operator */
368 
369 <S_INITIAL>">>"	|
370 <S_EXPR>">>"	return (MDB_TOK_RSHIFT); /* Logical shift right operator */
371 
372 <S_INITIAL>"*/"[a-zA-Z0-9]"/"	|
373 <S_EXPR>"*/"[a-zA-Z0-9]"/"	{
374 		switch (yytext[2]) {
375 			case 'c': case '1':
376 				return (MDB_TOK_COR1_DEREF);
377 			case 's': case '2':
378 				return (MDB_TOK_COR2_DEREF);
379 			case 'i': case '4':
380 #ifdef _ILP32
381 			case 'l':
382 #endif
383 				return (MDB_TOK_COR4_DEREF);
384 #ifdef _LP64
385 			case 'l':
386 #endif
387 			case '8':
388 				return (MDB_TOK_COR8_DEREF);
389 		}
390 		yyerror("invalid cast -- %s\n", yytext);
391 	}
392 
393 <S_INITIAL>"%/"[a-zA-Z0-9]"/"	|
394 <S_EXPR>"%/"[a-zA-Z0-9]"/"	{
395 		switch (yytext[2]) {
396 			case 'c': case '1':
397 				return (MDB_TOK_OBJ1_DEREF);
398 			case 's': case '2':
399 				return (MDB_TOK_OBJ2_DEREF);
400 			case 'i': case '4':
401 #ifdef _ILP32
402 			case 'l':
403 #endif
404 				return (MDB_TOK_OBJ4_DEREF);
405 #ifdef _LP64
406 			case 'l':
407 #endif
408 			case '8':
409 				return (MDB_TOK_OBJ8_DEREF);
410 		}
411 		yyerror("invalid cast -- %s\n", yytext);
412 	}
413 
414 <S_INITIAL>0[iI][0-1]+	|
415 <S_EXPR>0[iI][0-1]+	{
416 		/*
417 		 * Binary immediate value.
418 		 */
419 		yylval.l_immediate = mdb_strtonum(yytext + 2, 2);
420 		return (MDB_TOK_IMMEDIATE);
421 	}
422 
423 <S_INITIAL>0[oO][0-7]+	|
424 <S_EXPR>0[oO][0-7]+	{
425 		/*
426 		 * Octal immediate value.
427 		 */
428 		yylval.l_immediate = mdb_strtonum(yytext + 2, 8);
429 		return (MDB_TOK_IMMEDIATE);
430 	}
431 
432 <S_INITIAL>0[tT][0-9]+"."[0-9]+	|
433 <S_EXPR>0[tT][0-9]+"."[0-9]+	{
434 #ifdef _KMDB
435 		yyerror("floating point not supported\n");
436 #else
437 		/*
438 		 * Decimal floating point value.
439 		 */
440 		char *p, c;
441 		double d;
442 		int i;
443 
444 		if ((p = strsplit(yytext, '.')) == NULL)
445 			yyerror("internal scanning error -- expected '.'\n");
446 
447 		d = (double)mdb_strtonum(yytext + 2, 10);
448 
449 		for (i = 0; (c = *p++) != '\0'; i++)
450 			d = d * 10 + c - '0';
451 
452 		while (i-- != 0)
453 			d /= 10;
454 
455                 yylval.l_immediate = *((uintmax_t *)&d);
456 		return (MDB_TOK_IMMEDIATE);
457 #endif
458 	}
459 
460 <S_INITIAL>0[tT][0-9]+	|
461 <S_EXPR>0[tT][0-9]+	{
462 		/*
463 		 * Decimal immediate value.
464 		 */
465 		yylval.l_immediate = mdb_strtonum(yytext + 2, 10);
466 		return (MDB_TOK_IMMEDIATE);
467 	}
468 
469 <S_INITIAL>0[xX][0-9a-fA-F]+	|
470 <S_EXPR>0[xX][0-9a-fA-F]+	{
471 		/*
472 		 * Hexadecimal value.
473 		 */
474 		yylval.l_immediate = mdb_strtonum(yytext + 2, 16);
475 		return (MDB_TOK_IMMEDIATE);
476 	}
477 
478 <S_INITIAL>[0-9a-fA-F]+	|
479 <S_EXPR>[0-9a-fA-F]+	{
480 		GElf_Sym sym;
481 		/*
482 		 * Immediate values without an explicit base are converted
483 		 * using the default radix (user configurable).  However, if
484 		 * the token does *not* begin with a digit, it is also a
485 		 * potential symbol (e.g. "f") so we have to check that first.
486 		 */
487 		if (strchr("0123456789", yytext[0]) == NULL &&
488 		    mdb_tgt_lookup_by_name(mdb.m_target,
489 		    MDB_TGT_OBJ_EVERY, yytext, &sym, NULL) == 0)
490 			yylval.l_immediate = (uintmax_t)sym.st_value;
491 		else
492 			yylval.l_immediate = mdb_strtonum(yytext, mdb.m_radix);
493 		return (MDB_TOK_IMMEDIATE);
494 	}
495 
496 <S_INITIAL>{RGX_SYMBOL}	|
497 <S_EXPR>{RGX_SYMBOL}	{
498 		/*
499 		 * Symbol -- parser will look up in symbol table.
500 		 */
501 		yylval.l_string = strdup(yytext);
502 		return (MDB_TOK_SYMBOL);
503 	}
504 
505 ";"|\n	{
506 		/*
507 		 * End of command -- return to start state and return literal.
508 		 */
509 		BEGIN(S_INITIAL);
510 		return (yytext[0]);
511 	}
512 
513 [ \t]	;			/* Ignore whitespace */
514 
515 .	return (yytext[0]);	/* Return anything else */
516 
517 %%
518 
519 void
520 mdb_lex_debug(int i)
521 {
522 	yydebug = i;
523 }
524 
525 void
526 mdb_lex_reset(void)
527 {
528 	BEGIN(S_INITIAL);
529 }
530 
531 void
532 yydiscard(void)
533 {
534 	int c;
535 
536 	/*
537 	 * If stdin is a string, pipeline, or tty, throw away all our buffered
538 	 * data. Otherwise discard characters up to the next likely delimiter.
539 	 */
540 	if (mdb_iob_isastr(mdb.m_in) || mdb_iob_isatty(mdb.m_in) ||
541 	    mdb_iob_isapipe(mdb.m_in))
542 		mdb_iob_discard(mdb.m_in);
543 	else {
544 		while ((c = mdb_iob_getc(mdb.m_in)) != (int)EOF) {
545 			if (c == ';' || c == '\n')
546 				break;
547 		}
548 	}
549 
550 	BEGIN(S_INITIAL);
551 }
552 
553 static void
554 yyerror_reset(void)
555 {
556 	yydiscard();
557 	mdb_argvec_reset(&mdb.m_frame->f_argvec);
558 	longjmp(mdb.m_frame->f_pcb, MDB_ERR_PARSE);
559 }
560 
561 void
562 yyerror(const char *format, ...)
563 {
564 	va_list alist;
565 	char *s;
566 
567 	mdb_iob_printf(mdb.m_err, "%s: ", mdb.m_pname);
568 	va_start(alist, format);
569 	mdb_iob_vprintf(mdb.m_err, format, alist);
570 	va_end(alist);
571 
572 	if (strchr(format, '\n') == NULL) {
573 		if (!mdb_iob_isatty(mdb.m_in)) {
574 			mdb_iob_printf(mdb.m_err, " on line %d of %s",
575 			    yylineno, mdb_iob_name(mdb.m_in));
576 		}
577 
578 		s = strchr2esc(yytext, strlen(yytext));
579 		mdb_iob_printf(mdb.m_err, " near \"%s\"\n", s);
580 		strfree(s);
581 	}
582 
583 	yyerror_reset();
584 }
585 
586 void
587 yyperror(const char *format, ...)
588 {
589 	va_list alist;
590 
591 	va_start(alist, format);
592 	vwarn(format, alist);
593 	va_end(alist);
594 
595 	yyerror_reset();
596 }
597 
598 int
599 yywrap(void)
600 {
601 	mdb_dprintf(MDB_DBG_PARSER, "yywrap at line %d\n", yylineno);
602 	return (1); /* indicate that lex should return a zero token for EOF */
603 }
604 
605 /*PRINTFLIKE2*/
606 /*ARGSUSED*/
607 int
608 yyfprintf(FILE *stream, const char *format, ...)
609 {
610 	va_list alist;
611 
612 	va_start(alist, format);
613 	mdb_iob_vprintf(mdb.m_err, format, alist);
614 	va_end(alist);
615 	return (0);
616 }
617 
618 /*PRINTFLIKE1*/
619 int
620 yyprintf(const char *format, ...)
621 {
622 	va_list alist;
623 
624 	va_start(alist, format);
625 	mdb_iob_vprintf(mdb.m_err, format, alist);
626 	va_end(alist);
627 	return (0);
628 }
629 
630 static int
631 input(void)
632 {
633 	int c = mdb_iob_getc(mdb.m_in);
634 
635 	if (c == '\n')
636 		yylineno++;
637 
638 	return (c == EOF ? 0 : c);
639 }
640 
641 static void
642 unput(int c)
643 {
644 	if (c == '\n')
645 		yylineno--;
646 
647 	(void) mdb_iob_ungetc(mdb.m_in, c == 0 ? EOF : c);
648 }
649 
650 static void
651 output(int c)
652 {
653 	char ch = c;
654 	mdb_iob_nputs(mdb.m_out, &ch, sizeof (ch));
655 }
656 
657 static char *
658 string_nextquote(char *s, char q1, char q2)
659 {
660 	char c = 0;
661 
662 	do {
663 		if (c != '\\' && (*s == q1 || *s == q2))
664 			return (s);
665 	} while ((c = *s++) != '\0');
666 
667 	return (NULL);
668 }
669 
670 static void
671 string_unquote(char *s)
672 {
673 	char *o, *p, *q, c;
674 
675 	for (o = p = s; (p = string_nextquote(p, '\'', '"')) != NULL; o = p) {
676 		/*
677 		 * If the quote wasn't the first character, advance
678 		 * the destination buffer past what we skipped.
679 		 */
680 		if (p > o) {
681 			/* Using memmove to prevent possible overlap. */
682 			(void) memmove(s, o, p - o);
683 			s += p - o;
684 		}
685 
686 		c = *p;	/* Save the current quote */
687 
688 		/*
689 		 * Look ahead and find the matching quote.  If none is
690 		 * found, use yyerror to longjmp out of the lexer.
691 		 */
692 		if (c == '"')
693 			q = string_nextquote(p + 1, c, c);
694 		else
695 			q = strchr(p + 1, c);
696 
697 		if (q == NULL)
698 			yyerror("syntax error: %c unmatched", c);
699 
700 		/*
701 		 * If the string is non-empty, copy it to the destination
702 		 * and convert escape sequences if *p is double-quote.
703 		 */
704 		if (q > p + 1) {
705 			(void) memmove(s, p + 1, q - p - 1);
706 			if (c == '"') {
707                                 s[q - p - 1] = '\0';
708 				s += stresc2chr(s);
709 			} else
710 				s += q - p - 1;
711 		}
712 
713 		p = q + 1; /* Advance p past matching quote */
714 	}
715 
716 	(void) memmove(s, o, strlen(o) + 1);
717 }
718 
719 /*
720  * Unfortunately, lex and yacc produces code that is inherently global.  They do
721  * not provide routines to save and restore state, instead relying on global
722  * variables.  There is one single lex state, so that if a frame switch then
723  * tries to perform any evaluation, the old values are corrupted.  This
724  * structure and corresponding function provide a means of preserving lex state
725  * across frame switches.  Note that this is tied to the lex implementation, so
726  * if the lex compiler is changed or upgraded to a different format, then this
727  * may need to be altered.  This is unavoidable due to the implementation of lex
728  * and yacc. This is essentially a collection of all the global variables
729  * defined by the lex code, excluding those that do not change through the
730  * course of yylex() and yyparse().
731  */
732 extern struct yysvf *yylstate[], **yylsp, **yyolsp; extern int yyprevious;
733 extern int *yyfnd;
734 
735 extern YYSTYPE *yypv;
736 extern int *yyps;
737 extern int yytmp;
738 extern int yystate;
739 extern int yynerrs;
740 extern int yyerrflag;
741 extern int yychar;
742 extern YYSTYPE yylval;
743 extern YYSTYPE yyval;
744 extern int *yys;
745 extern YYSTYPE *yyv;
746 
747 typedef struct mdb_lex_state {
748 	/* Variables needed by yylex */
749 	int	yyleng;
750 	char	yytext[YYLMAX];
751 	int	yymorfg;
752 	int	yylineno;
753 	void	*yyestate;
754 	void	*yylstate[BUFSIZ];
755 	void	*yylsp;
756 	void	*yyolsp;
757 	int	*yyfnd;
758 	int	yyprevious;
759 	void	*yybgin;
760 	/* Variables needed by yyparse */
761 	void 	*yypv;
762 	int	*yyps;
763 	int	yytmp;
764 	int	yystate;
765 	int	yynerrs;
766 	int	yyerrflag;
767 	int	yychar;
768 	YYSTYPE	yylval;
769 	YYSTYPE	yyval;
770 	int	yys[YYMAXDEPTH];
771 	YYSTYPE	yyv[YYMAXDEPTH];
772 } mdb_lex_state_t;
773 
774 void
775 mdb_lex_state_save(mdb_lex_state_t *s)
776 {
777 	ASSERT(s != NULL);
778 
779 	s->yyleng = yyleng;
780 	s->yymorfg = yymorfg;
781 	s->yylineno = yylineno;
782 	s->yyestate = yyestate;
783 	bcopy(yylstate, s->yylstate, YYLMAX * sizeof (void *));
784 	s->yylsp = yylsp;
785 	s->yyolsp = yyolsp;
786 	s->yyfnd = yyfnd;
787 	s->yyprevious = yyprevious;
788 	s->yybgin = yybgin;
789 
790 	s->yypv = yypv;
791 	s->yyps = yyps;
792 	s->yystate = yystate;
793 	s->yytmp = yytmp;
794 	s->yynerrs = yynerrs;
795 	s->yyerrflag = yyerrflag;
796 	s->yychar = yychar;
797 	s->yylval = yylval;
798 	s->yyval = yyval;
799 }
800 
801 void
802 mdb_lex_state_restore(mdb_lex_state_t *s)
803 {
804 	ASSERT(s != NULL);
805 
806 	yyleng = s->yyleng;
807 	yytext = s->yytext;
808 	yymorfg = s->yymorfg;
809 	yylineno = s->yylineno;
810 	yyestate = s->yyestate;
811 	bcopy(s->yylstate, yylstate, YYLMAX * sizeof (void *));
812 	yylsp = s->yylsp;
813 	yyolsp = s->yyolsp;
814 	yyfnd = s->yyfnd;
815 	yyprevious = s->yyprevious;
816 	yybgin = s->yybgin;
817 
818 	yypv = s->yypv;
819 	yyps = s->yyps;
820 	yystate = s->yystate;
821 	yytmp = s->yytmp;
822 	yynerrs = s->yynerrs;
823 	yyerrflag = s->yyerrflag;
824 	yychar = s->yychar;
825 	yylval = s->yylval;
826 	yyval = s->yyval;
827 	yys = s->yys;
828 	yyv = s->yyv;
829 }
830 
831 /*
832  * Create and initialize the lex/yacc-specific state associated with a frame
833  * structure.  We set all fields to known safe values so that
834  * mdb_lex_state_restore() can be used safely before mdb_lex_state_save().
835  */
836 void
837 mdb_lex_state_create(mdb_frame_t *f)
838 {
839 	f->f_lstate = mdb_alloc(sizeof (mdb_lex_state_t), UM_SLEEP);
840 
841 	yyleng = 0;
842 	yymorfg = 0;
843 	/* yytext is fine with garbage in it */
844 	yytext = f->f_lstate->yytext;
845 	yylineno = 1;
846 	yyestate = NULL;
847 	bzero(yylstate, YYLMAX * sizeof (void *));
848 	yylsp = NULL;
849 	yyolsp = NULL;
850 	yyfnd = 0;
851 	yyprevious = YYNEWLINE;
852 	yys = f->f_lstate->yys;
853 	yyv = f->f_lstate->yyv;
854 	mdb_argvec_create(&f->f_argvec);
855 	f->f_oldstate = 0;
856 	mdb_lex_reset(); /* Responsible for setting yybgin */
857 }
858 
859 void
860 mdb_lex_state_destroy(mdb_frame_t *f)
861 {
862 	mdb_free(f->f_lstate, sizeof (mdb_lex_state_t));
863 	f->f_lstate = NULL;
864 	mdb_argvec_destroy(&f->f_argvec);
865 }
866