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