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