view usr/src/cmd/mdb/common/mdb/mdb_lex.l @ 21423:bf3d3f4f3fdb

10941 mdb deserves a modulus operator Reviewed by: John Levon <john.levon@joyent.com> Reviewed by: Patrick Mooney <patrick.mooney@joyent.com> Reviewed by: Toomas Soome <toomas@me.com> Reviewed by: Andy Fiddaman <andy@omniosce.org> Reviewed by: Gordon Ross <gwr@nexenta.com> Approved by: Dan McDonald <danmcd@joyent.com>
author Robert Mustacchi <rm@joyent.com>
date Thu, 31 Jan 2019 05:09:40 +0000
parents 5109214ed909
children
line wrap: on
line source

%pointer	/* Make yytext a pointer, not an array */

%{
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
 * Copyright (c) 2017 by Delphix. All rights reserved.
 * Copyright 2019, Joyent, Inc.
 */

#include <sys/types.h>
#include <sys/isa_defs.h>

#include <strings.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#include <mdb/mdb_types.h>
#include <mdb/mdb_debug.h>
#include <mdb/mdb_nv.h>
#include <mdb/mdb_lex.h>
#include <mdb/mdb_frame.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_stdlib.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb.h>

#include "mdb_grammar.h"

/*
 * lex hardcodes yyin and yyout to stdin and stdout, respectively, before we get
 * control.  We've redirected printf and fprintf (see mdb_lex.h) to yyprintf and
 * yyfprintf, which ignore the FILE * provided by yyout.  __iob-based stdin and
 * stdout are useless in kmdb, since we don't have stdio.  We define __iob here
 * to shut the linker up.
 */
#ifdef _KMDB
FILE __iob[_NFILE];
#endif

/*
 * We need to undefine lex's input, unput, and output macros so that references
 * to these call the functions we provide at the end of this source file,
 * instead of the default versions based on libc's stdio.
 */
#ifdef input
#undef input
#endif

#ifdef unput
#undef unput
#endif

#ifdef output
#undef output
#endif

static int input(void);
static void unput(int);
static void output(int);

static void string_unquote(char *);

extern int yydebug;

/*
 * This will prevent lex from trying to malloc() and resize our yytext variable,
 * instead it will just print out an error message and exit(), which seems
 * a lesser of two evils.
 */
#define YYISARRAY

%}

%o	9000
%a	5000

%s	S_SHELLCMD
%s	S_INITIAL
%s	S_FMTLIST
%s	S_ARGLIST
%s	S_EXPR

RGX_CMD_CHAR	[?%@A-Z\^_`a-z]
RGX_SYMBOL	[a-zA-Z_.][0-9a-zA-Z_.`]*
RGX_SIMPLE_CHAR	[^ \t\n;!|"'\$]
RGX_CHR_SEQ	([^'\n]|\\[^'\n]|\\')*
RGX_STR_SEQ	([^"\\\n]|\\[^"\n]|\\\")*
RGX_COMMENT	"//".*\n

%%

<S_INITIAL>{RGX_COMMENT}	|
<S_FMTLIST>{RGX_COMMENT}	|
<S_ARGLIST>{RGX_COMMENT}	{
		/*
		 * Comments are legal in these three states -- if we see one
		 * eat the line and return the newline character.
		 */
		BEGIN(S_INITIAL);	
		return ('\n');
	}

<S_INITIAL>"=="	|
<S_EXPR>"=="	return (MDB_TOK_EQUAL); /* Equality operator */

<S_INITIAL>"!="	|
<S_EXPR>"!="	return (MDB_TOK_NOTEQUAL); /* Inequality operator */

<S_INITIAL>"%%"	|
<S_EXPR>"%%"	return (MDB_TOK_MODULUS); /* Modulus operator */

<S_INITIAL>"!"	|
<S_FMTLIST>"!"	|
<S_ARGLIST>"!"	{
		/*
		 * Shell escapes are legal in all of these states -- switch to
		 * the shell command state and return the ! character.
		 */
		BEGIN(S_SHELLCMD);
		return (yytext[0]);
	}

<S_FMTLIST>"|"	|
<S_ARGLIST>"|"	{
		/*
		 * Pipelines can appear in any of these states -- switch to
		 * the initial state and return the | character.
		 */
		BEGIN(S_INITIAL);
		return (yytext[0]);
	}

<S_SHELLCMD>[^;\n]+	{
		/*
		 * Once in the shell-command state, we return all remaining
		 * characters up to a newline or ';' delimiter as a single
		 * string which will be passed to $SHELL -c.
		 */
		yylval.l_string = strdup(yytext);
		BEGIN(S_INITIAL);
		return (MDB_TOK_STRING);
	}

<S_INITIAL>"::"{RGX_SYMBOL}	{
		/*
		 * Verb ::command-name -- lookup the correspond dcmd and
		 * switch to the argument list state.
		 */
		if ((yylval.l_dcmd = mdb_dcmd_lookup(yytext + 2)) == NULL)
			yyperror("invalid command '%s'", yytext);

		BEGIN(S_ARGLIST);
		return (MDB_TOK_DCMD);
	}

<S_INITIAL>"$<<"|"$<"|"$>"	|
<S_INITIAL>[\$:]{RGX_CMD_CHAR}	{
		/*
		 * Old-style :c or $c command -- lookup the corresponding dcmd
		 * and switch to the argument list state.
		 */
		if ((yylval.l_dcmd = mdb_dcmd_lookup(yytext)) == NULL)
			yyperror("invalid command '%s'", yytext);

		BEGIN(S_ARGLIST);
		return (MDB_TOK_DCMD);
	}

<S_INITIAL>">/"[a-zA-Z0-9]"/"	{
		/*
		 * Variable assignment with size cast -- append the cast letter
		 * to the argument list, and switch to the argument list state.
		 */
		mdb_arg_t arg;

		arg.a_un.a_char = yytext[2];
		arg.a_type = MDB_TYPE_CHAR;

		mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);
		yylval.l_dcmd = mdb_dcmd_lookup(">");

		BEGIN(S_ARGLIST);
		return (MDB_TOK_DCMD);
	}

<S_INITIAL>">"	{
		/*
		 * Variable assignment -- switch to the argument list state.
		 */
		yylval.l_dcmd = mdb_dcmd_lookup(yytext);
		BEGIN(S_ARGLIST);
		return (MDB_TOK_DCMD);
	}

<S_INITIAL>[/\\?][ \t]*[vwzWZlLM]	{
		/*
		 * Format verb followed by write or match signifier -- switch
		 * to the value list state and return the verb character.  We
		 * also append the actual format character to the arg list.
		 */
		mdb_arg_t arg;

		arg.a_un.a_char = yytext[yyleng - 1];
		arg.a_type = MDB_TYPE_CHAR;

		mdb_argvec_append(&mdb.m_frame->f_argvec, &arg);

		BEGIN(S_ARGLIST);
		return yytext[0];
	}

<S_INITIAL>[/\\@?=]	{
		/*
		 * Format verb -- switch to the format list state and return
		 * the actual verb character verbatim.
		 */
		BEGIN(S_FMTLIST);
		return (yytext[0]);
	}

<S_INITIAL>'{RGX_CHR_SEQ}$	|
<S_EXPR>'{RGX_CHR_SEQ}$		yyerror("syntax error: ' unmatched");

<S_INITIAL>'{RGX_CHR_SEQ}' 	|
<S_EXPR>'{RGX_CHR_SEQ}'		{
		char *s, *p, *q;
		size_t nbytes;

		/*
		 * If the character sequence is zero-length, return 0.
		 */
		if (yyleng == 2) {
			yylval.l_immediate = 0;
			return (MDB_TOK_IMMEDIATE);
		}

		s = yytext + 1;			/* Skip past initial quote */
		yytext[yyleng - 1] = '\0';	/* Overwrite final quote */
		nbytes = stresc2chr(s);		/* Convert escapes */
		yylval.l_immediate = 0;		/* Initialize token value */

		if (nbytes > sizeof (uintmax_t)) {
			yyerror("character constant may not exceed %lu bytes\n",
			    (ulong_t)sizeof (uintmax_t));
		}

#ifdef _LITTLE_ENDIAN
		p = ((char*)&yylval.l_immediate) + nbytes - 1;

		for (q = s; nbytes != 0; nbytes--)
			*p-- = *q++;
#else
		bcopy(s, ((char *)&yylval.l_immediate) +
		    sizeof (uintmax_t) - nbytes, nbytes);
#endif
		return (MDB_TOK_IMMEDIATE);
	}

\"{RGX_STR_SEQ}$	yyerror("syntax error: \" unmatched");

\"{RGX_STR_SEQ}\"	{
		/*
		 * Quoted string -- convert C escape sequences and return the
		 * string as a token.
		 */
		yylval.l_string = strndup(yytext + 1, yyleng - 2);
		(void) stresc2chr(yylval.l_string);
		return (MDB_TOK_STRING);
	}

<S_ARGLIST>"$["	|
<S_FMTLIST>"$["	{
		/*
		 * Start of expression -- begin expression state and save the
		 * current state so we can return at the end of the expression.
		 */
		mdb.m_frame->f_oldstate = YYSTATE;
		BEGIN(S_EXPR);
		return (MDB_TOK_LEXPR);
	}

<S_ARGLIST>{RGX_SIMPLE_CHAR}*("'"{RGX_CHR_SEQ}"'"|\"{RGX_STR_SEQ}\"|{RGX_SIMPLE_CHAR}+)* {
		/*
		 * String token -- create a copy of the string and return it.
		 * We need to handle embedded single and double-quote pairs,
		 * which overcomplicates this slightly.
		 */
		yylval.l_string = strdup(yytext);
		string_unquote(yylval.l_string);
		return (MDB_TOK_STRING);
	}

<S_FMTLIST>[0-9]+	{
		/*
		 * Immediate value -- in the format list, all immediates
		 * are assumed to be in decimal.
		 */
		yylval.l_immediate = mdb_strtonum(yytext, 10);
		return (MDB_TOK_IMMEDIATE);
	}

<S_FMTLIST>{RGX_SIMPLE_CHAR}	{
		/*
		 * Non-meta character -- in the format list, we return each
		 * character as a separate token to be added as an argument.
		 */
		yylval.l_char = yytext[0];
		return (MDB_TOK_CHAR);
	}

<S_EXPR>";"|"!"|\n	{
		/*
		 * In the expression state only, we cannot see a command
		 * delimiter or shell escape before we end the expression.
		 */
		yyerror("syntax error: $[ unmatched");
	}

<S_EXPR>"]"	{
		/*
		 * End of expression state.  Restore the state we were in
		 * before the "$[" which started this expression.
		 */
		BEGIN(mdb.m_frame->f_oldstate);
		return (MDB_TOK_REXPR);
	}

<S_INITIAL>"<"{RGX_SYMBOL}	|
<S_INITIAL>"<"[0-9]		|
<S_EXPR>"<"{RGX_SYMBOL}		|
<S_EXPR>"<"[0-9]	{
		/*
		 * Variable reference -- lookup the variable and return a
		 * pointer to it.  Referencing undefined variables is an error.
		 */
		yylval.l_var = mdb_nv_lookup(&mdb.m_nv, &yytext[1]);

		if (yylval.l_var == NULL)
			yyerror("variable '%s' is not defined", &yytext[1]);

		return (MDB_TOK_VAR_REF);
	}

<S_INITIAL>"<<"	|
<S_EXPR>"<<"	return (MDB_TOK_LSHIFT); /* Logical shift left operator */

<S_INITIAL>">>"	|
<S_EXPR>">>"	return (MDB_TOK_RSHIFT); /* Logical shift right operator */

<S_INITIAL>"*/"[a-zA-Z0-9]"/"	|
<S_EXPR>"*/"[a-zA-Z0-9]"/"	{
		switch (yytext[2]) {
			case 'c': case '1':
				return (MDB_TOK_COR1_DEREF);
			case 's': case '2':
				return (MDB_TOK_COR2_DEREF);
			case 'i': case '4':
#ifdef _ILP32
			case 'l':
#endif
				return (MDB_TOK_COR4_DEREF);
#ifdef _LP64
			case 'l':
#endif
			case '8':
				return (MDB_TOK_COR8_DEREF);
		}
		yyerror("invalid cast -- %s\n", yytext);
	}

<S_INITIAL>"%/"[a-zA-Z0-9]"/"	|
<S_EXPR>"%/"[a-zA-Z0-9]"/"	{
		switch (yytext[2]) {
			case 'c': case '1':
				return (MDB_TOK_OBJ1_DEREF);
			case 's': case '2':
				return (MDB_TOK_OBJ2_DEREF);
			case 'i': case '4':
#ifdef _ILP32
			case 'l':
#endif
				return (MDB_TOK_OBJ4_DEREF);
#ifdef _LP64
			case 'l':
#endif
			case '8':
				return (MDB_TOK_OBJ8_DEREF);
		}
		yyerror("invalid cast -- %s\n", yytext);
	}

<S_INITIAL>0[iI][0-1]+	|
<S_EXPR>0[iI][0-1]+	{
		/*
		 * Binary immediate value.
		 */
		yylval.l_immediate = mdb_strtonum(yytext + 2, 2);
		return (MDB_TOK_IMMEDIATE);
	}

<S_INITIAL>0[oO][0-7]+	|
<S_EXPR>0[oO][0-7]+	{
		/*
		 * Octal immediate value.
		 */
		yylval.l_immediate = mdb_strtonum(yytext + 2, 8);
		return (MDB_TOK_IMMEDIATE);
	}

<S_INITIAL>0[tT][0-9]+"."[0-9]+	|
<S_EXPR>0[tT][0-9]+"."[0-9]+	{
#ifdef _KMDB
		yyerror("floating point not supported\n");
#else
		/*
		 * Decimal floating point value.
		 */
		char *p, c;
		double d;
		int i;

		if ((p = strsplit(yytext, '.')) == NULL)
			yyerror("internal scanning error -- expected '.'\n");

		d = (double)mdb_strtonum(yytext + 2, 10);

		for (i = 0; (c = *p++) != '\0'; i++)
			d = d * 10 + c - '0';

		while (i-- != 0)
			d /= 10;

                yylval.l_immediate = *((uintmax_t *)&d);
		return (MDB_TOK_IMMEDIATE);
#endif
	}

<S_INITIAL>0[tT][0-9]+	|
<S_EXPR>0[tT][0-9]+	{
		/*
		 * Decimal immediate value.
		 */
		yylval.l_immediate = mdb_strtonum(yytext + 2, 10);
		return (MDB_TOK_IMMEDIATE);
	}

<S_INITIAL>0[xX][0-9a-fA-F]+	|
<S_EXPR>0[xX][0-9a-fA-F]+	{
		/*
		 * Hexadecimal value.
		 */
		yylval.l_immediate = mdb_strtonum(yytext + 2, 16);
		return (MDB_TOK_IMMEDIATE);
	}

<S_INITIAL>[0-9a-fA-F]+	|
<S_EXPR>[0-9a-fA-F]+	{
		GElf_Sym sym;
		/*
		 * Immediate values without an explicit base are converted
		 * using the default radix (user configurable).  However, if
		 * the token does *not* begin with a digit, it is also a
		 * potential symbol (e.g. "f") so we have to check that first.
		 */
		if (strchr("0123456789", yytext[0]) == NULL &&
		    mdb_tgt_lookup_by_name(mdb.m_target,
		    MDB_TGT_OBJ_EVERY, yytext, &sym, NULL) == 0)
			yylval.l_immediate = (uintmax_t)sym.st_value;
		else
			yylval.l_immediate = mdb_strtonum(yytext, mdb.m_radix);
		return (MDB_TOK_IMMEDIATE);
	}

<S_INITIAL>{RGX_SYMBOL}	|
<S_EXPR>{RGX_SYMBOL}	{
		/*
		 * Symbol -- parser will look up in symbol table.
		 */
		yylval.l_string = strdup(yytext);
		return (MDB_TOK_SYMBOL);
	}

";"|\n	{
		/*
		 * End of command -- return to start state and return literal.
		 */
		BEGIN(S_INITIAL);
		return (yytext[0]);
	}

[ \t]	;			/* Ignore whitespace */

.	return (yytext[0]);	/* Return anything else */

%%

void
mdb_lex_debug(int i)
{
	yydebug = i;
}

void
mdb_lex_reset(void)
{
	BEGIN(S_INITIAL);
}

void
yydiscard(void)
{
	int c;

	/*
	 * If stdin is a string, pipeline, or tty, throw away all our buffered 
	 * data. Otherwise discard characters up to the next likely delimiter.
	 */
	if (mdb_iob_isastr(mdb.m_in) || mdb_iob_isatty(mdb.m_in) ||
	    mdb_iob_isapipe(mdb.m_in))
		mdb_iob_discard(mdb.m_in);
	else {
		while ((c = mdb_iob_getc(mdb.m_in)) != (int)EOF) {
			if (c == ';' || c == '\n')
				break;
		}
	}

	BEGIN(S_INITIAL);
}

static void
yyerror_reset(void)
{
	yydiscard();
	mdb_argvec_reset(&mdb.m_frame->f_argvec);
	longjmp(mdb.m_frame->f_pcb, MDB_ERR_PARSE);
}

void
yyerror(const char *format, ...)
{
	va_list alist;
	char *s;

	mdb_iob_printf(mdb.m_err, "%s: ", mdb.m_pname);
	va_start(alist, format);
	mdb_iob_vprintf(mdb.m_err, format, alist);
	va_end(alist);

	if (strchr(format, '\n') == NULL) {
		if (!mdb_iob_isatty(mdb.m_in)) {
			mdb_iob_printf(mdb.m_err, " on line %d of %s",
			    yylineno, mdb_iob_name(mdb.m_in));
		}

		s = strchr2esc(yytext, strlen(yytext));
		mdb_iob_printf(mdb.m_err, " near \"%s\"\n", s);
		strfree(s);
	}

	yyerror_reset();
}

void
yyperror(const char *format, ...)
{
	va_list alist;

	va_start(alist, format);
	vwarn(format, alist);
	va_end(alist);

	yyerror_reset();
}

int
yywrap(void)
{
	mdb_dprintf(MDB_DBG_PARSER, "yywrap at line %d\n", yylineno);
	return (1); /* indicate that lex should return a zero token for EOF */
}

/*PRINTFLIKE2*/
/*ARGSUSED*/
int
yyfprintf(FILE *stream, const char *format, ...)
{
	va_list alist;

	va_start(alist, format);
	mdb_iob_vprintf(mdb.m_err, format, alist);
	va_end(alist);
	return (0);
}

/*PRINTFLIKE1*/
int
yyprintf(const char *format, ...)
{
	va_list alist;

	va_start(alist, format);
	mdb_iob_vprintf(mdb.m_err, format, alist);
	va_end(alist);
	return (0);
}

static int
input(void)
{
	int c = mdb_iob_getc(mdb.m_in);

	if (c == '\n')
		yylineno++;

	return (c == EOF ? 0 : c);
}

static void
unput(int c)
{
	if (c == '\n')
		yylineno--;

	(void) mdb_iob_ungetc(mdb.m_in, c == 0 ? EOF : c);
}

static void
output(int c)
{
	char ch = c;
	mdb_iob_nputs(mdb.m_out, &ch, sizeof (ch));
}

static char *
string_nextquote(char *s, char q1, char q2)
{
	char c = 0;

	do {
		if (c != '\\' && (*s == q1 || *s == q2))
			return (s);
	} while ((c = *s++) != '\0');

	return (NULL);
}

static void
string_unquote(char *s)
{
	char *o, *p, *q, c;

	for (o = p = s; (p = string_nextquote(p, '\'', '"')) != NULL; o = p) {
		/*
		 * If the quote wasn't the first character, advance
		 * the destination buffer past what we skipped.
		 */
		if (p > o) {
			/* Using memmove to prevent possible overlap. */
			(void) memmove(s, o, p - o);
			s += p - o;
		}

		c = *p;	/* Save the current quote */

		/*
		 * Look ahead and find the matching quote.  If none is
		 * found, use yyerror to longjmp out of the lexer.
		 */
		if (c == '"')
			q = string_nextquote(p + 1, c, c);
		else
			q = strchr(p + 1, c);

		if (q == NULL)
			yyerror("syntax error: %c unmatched", c);

		/*
		 * If the string is non-empty, copy it to the destination
		 * and convert escape sequences if *p is double-quote.
		 */
		if (q > p + 1) {
			(void) memmove(s, p + 1, q - p - 1);
			if (c == '"') {
                                s[q - p - 1] = '\0';
				s += stresc2chr(s);
			} else
				s += q - p - 1;
		}

		p = q + 1; /* Advance p past matching quote */
	}

	(void) memmove(s, o, strlen(o) + 1);
}

/*
 * Unfortunately, lex and yacc produces code that is inherently global.  They do
 * not provide routines to save and restore state, instead relying on global
 * variables.  There is one single lex state, so that if a frame switch then
 * tries to perform any evaluation, the old values are corrupted.  This
 * structure and corresponding function provide a means of preserving lex state
 * across frame switches.  Note that this is tied to the lex implementation, so
 * if the lex compiler is changed or upgraded to a different format, then this
 * may need to be altered.  This is unavoidable due to the implementation of lex
 * and yacc. This is essentially a collection of all the global variables
 * defined by the lex code, excluding those that do not change through the
 * course of yylex() and yyparse().
 */
extern struct yysvf *yylstate[], **yylsp, **yyolsp; extern int yyprevious;
extern int *yyfnd;

extern YYSTYPE *yypv;
extern int *yyps;
extern int yytmp;
extern int yystate;
extern int yynerrs;
extern int yyerrflag;
extern int yychar;
extern YYSTYPE yylval;
extern YYSTYPE yyval;
extern int *yys;
extern YYSTYPE *yyv;

typedef struct mdb_lex_state {
	/* Variables needed by yylex */
	int	yyleng;
	char	yytext[YYLMAX];
	int	yymorfg;
	int	yylineno;
	void	*yyestate;
	void	*yylstate[BUFSIZ];
	void	*yylsp;
	void	*yyolsp;
	int	*yyfnd;
	int	yyprevious;
	void	*yybgin;
	/* Variables needed by yyparse */
	void 	*yypv;
	int	*yyps;
	int	yytmp;
	int	yystate;
	int	yynerrs;
	int	yyerrflag;
	int	yychar;
	YYSTYPE	yylval;
	YYSTYPE	yyval;
	int	yys[YYMAXDEPTH];
	YYSTYPE	yyv[YYMAXDEPTH];
} mdb_lex_state_t;

void 
mdb_lex_state_save(mdb_lex_state_t *s)
{
	ASSERT(s != NULL);

	s->yyleng = yyleng;
	s->yymorfg = yymorfg;
	s->yylineno = yylineno;
	s->yyestate = yyestate;
	bcopy(yylstate, s->yylstate, YYLMAX * sizeof (void *));
	s->yylsp = yylsp;
	s->yyolsp = yyolsp;
	s->yyfnd = yyfnd;
	s->yyprevious = yyprevious;
	s->yybgin = yybgin;

	s->yypv = yypv;
	s->yyps = yyps;
	s->yystate = yystate;
	s->yytmp = yytmp;
	s->yynerrs = yynerrs;
	s->yyerrflag = yyerrflag;
	s->yychar = yychar;
	s->yylval = yylval;
	s->yyval = yyval;
}

void 
mdb_lex_state_restore(mdb_lex_state_t *s)
{
	ASSERT(s != NULL);

	yyleng = s->yyleng;
	yytext = s->yytext;
	yymorfg = s->yymorfg;
	yylineno = s->yylineno;
	yyestate = s->yyestate;
	bcopy(s->yylstate, yylstate, YYLMAX * sizeof (void *));
	yylsp = s->yylsp;
	yyolsp = s->yyolsp;
	yyfnd = s->yyfnd;
	yyprevious = s->yyprevious;
	yybgin = s->yybgin;

	yypv = s->yypv;
	yyps = s->yyps;
	yystate = s->yystate;
	yytmp = s->yytmp;
	yynerrs = s->yynerrs;
	yyerrflag = s->yyerrflag;
	yychar = s->yychar;
	yylval = s->yylval;
	yyval = s->yyval;
	yys = s->yys;
	yyv = s->yyv;
}

/*
 * Create and initialize the lex/yacc-specific state associated with a frame
 * structure.  We set all fields to known safe values so that
 * mdb_lex_state_restore() can be used safely before mdb_lex_state_save().
 */
void
mdb_lex_state_create(mdb_frame_t *f)
{
	f->f_lstate = mdb_alloc(sizeof (mdb_lex_state_t), UM_SLEEP);

	yyleng = 0;
	yymorfg = 0;
	/* yytext is fine with garbage in it */
	yytext = f->f_lstate->yytext;
	yylineno = 1;
	yyestate = NULL;
	bzero(yylstate, YYLMAX * sizeof (void *));
	yylsp = NULL;
	yyolsp = NULL;
	yyfnd = 0;
	yyprevious = YYNEWLINE;
	yys = f->f_lstate->yys;
	yyv = f->f_lstate->yyv;
	mdb_argvec_create(&f->f_argvec);
	f->f_oldstate = 0;
	mdb_lex_reset(); /* Responsible for setting yybgin */
}

void
mdb_lex_state_destroy(mdb_frame_t *f)
{
	mdb_free(f->f_lstate, sizeof (mdb_lex_state_t));
	f->f_lstate = NULL;
	mdb_argvec_destroy(&f->f_argvec);
}