/* * scanner-smi.l -- * * Lexical rules for scanning the SMIv1/v2 MIB module language. * * Copyright (c) 1999 Frank Strauss, Technical University of Braunschweig. * * See the file "COPYING" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * @(#) $Id: scanner-smi.l 6539 2007-02-01 08:23:54Z strauss $ */ %option noyywrap %{ #include #include #include #include #include #if defined(HAVE_WIN_H) #include "win.h" #endif #include "error.h" #include "util.h" #include "parser-smi.h" #include "parser-smi.tab.h" #include "scanner-smi.h" #ifdef HAVE_DMALLOC_H #include #endif /* we need a reentrant parser, so yylex gets arguments */ #if 0 #define YY_DECL int yylex YY_PROTO((YYSTYPE *lvalp, void *parser)) #else #define YY_DECL int yylex(YYSTYPE *lvalp, void *parser) #endif #define thisParser (*(Parser *) parser) #define MAX_UNSIGNED64 "18446744073709551615" #define MIN_UNSIGNED64 0 #define MAX_UNSIGNED32 4294967295 #define MIN_UNSIGNED32 0 #define MAX_INTEGER32 2147483647 #define MIN_INTEGER32 -2147483648 /* * This makes the usual notation when referencing attributes also * work with our pure parser code. */ #define yylval (*lvalp) static YY_BUFFER_STATE yybuffer[MAX_LEX_DEPTH]; static int lexDepth = 0; int smiEnterLexRecursion(file) FILE *file; { if (lexDepth >= MAX_LEX_DEPTH) { return (-1); } yybuffer[lexDepth++] = YY_CURRENT_BUFFER; yy_switch_to_buffer(yy_create_buffer(file, YY_BUF_SIZE)); return (lexDepth); } void smiLeaveLexRecursion() { yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(yybuffer[--lexDepth]); } %} /* * Lex pattern definitions. */ delim ([^a-zA-Z0-9-])|-- enddelim ([^a-zA-Z0-9-])|--|"" eol ("\n"|"\n\015"|"\015\n"|"\015") /* * Lex state definitions. */ %s Macro %s Choice %s Exports %s Comment %s Skipline %% /* * Lex rules. */ /* * Lex rules for skipping MACRO. */ MACRO { BEGIN(Macro); yylval.id = yytext; return MACRO; } {eol} { thisParser.line++; } . { } END/{delim} { BEGIN(INITIAL); yylval.id = yytext; return END; } /* * Lex rules for skipping EXPORTS. */ EXPORTS { BEGIN(Exports); yylval.id = yytext; return EXPORTS; } {eol} { thisParser.line++; } [^\;]* { } \; { BEGIN(INITIAL); return yytext[0]; } /* * Lex rules for skipping CHOICE. */ CHOICE { BEGIN(Choice); yylval.id = yytext; return CHOICE; } {eol} { thisParser.line++; } [^\}] { } \} { BEGIN(INITIAL); return yytext[0]; } /* * Lex rules for comments. */ "--" { BEGIN(Comment); } "--" { thisParser.lcline = thisParser.line; BEGIN(INITIAL); } /* Special-case a common error (dashed seperator lines with * 4n+1 dashes, meaning a single dash outside the comment). * Print the "lexically unexpected" error that you would normally * get, and stick in the comment FYI also. */ "---"{eol} { smiPrintError(parser, ERR_COMMENT_TERMINATES); smiPrintError(parser, ERR_LEX_UNEXPECTED_CHAR); thisParser.line++; BEGIN(INITIAL); } {eol} { thisParser.line++; BEGIN(INITIAL); } [^-\n\015]* { } "-" { } /* * Lex rules for some special tokens. */ [\[\]\{\}\(\)\:\;\,\-\.\|] { return yytext[0]; } ".." { return DOT_DOT; } "::=" { return COLON_COLON_EQUAL; } /* * Lex rules for white space. */ [ \t]* { } {eol} { thisParser.line++; } /* * Lex rules for known keywords. */ /* SMI(ng) specific */ ACCESS/{delim} { yylval.id = yytext; return ACCESS; } /* SMI(ng) specific */ AGENT-CAPABILITIES/{delim} { yylval.id = yytext; return AGENT_CAPABILITIES; } APPLICATION/{delim} { yylval.id = yytext; return APPLICATION; } AUGMENTS/{delim} { yylval.id = yytext; return AUGMENTS; } BEGIN/{delim} { yylval.id = yytext; return BEGIN_; } BITS/{delim} { yylval.id = yytext; return BITS; } CONTACT-INFO/{delim} { yylval.id = yytext; return CONTACT_INFO; } /* SMI(ng) specific */ CREATION-REQUIRES/{delim} { yylval.id = yytext; return CREATION_REQUIRES; } /* SMI(ng) specific */ Counter32/{delim} { yylval.id = yytext; return COUNTER32; } /* SMI(ng) specific */ Counter64/{delim} { yylval.id = yytext; return COUNTER64; } /* SMI(ng) specific */ DEFINITIONS/{delim} { yylval.id = yytext; return DEFINITIONS; } DEFVAL/{delim} { yylval.id = yytext; return DEFVAL; } DESCRIPTION/{delim} { yylval.id = yytext; return DESCRIPTION; } DISPLAY-HINT/{delim} { yylval.id = yytext; return DISPLAY_HINT; } END/{enddelim} { yylval.id = yytext; return END; } /* SMI(ng) specific */ ENTERPRISE/{delim} { yylval.id = yytext; return ENTERPRISE; } /* SPPI specific */ EXTENDS/{delim} { yylval.id = yytext; return EXTENDS; } FROM/{delim} { yylval.id = yytext; return FROM; } GROUP/{delim} { yylval.id = yytext; return GROUP; } /* SMI(ng) specific */ Gauge32/{delim} { yylval.id = yytext; return GAUGE32; } IDENTIFIER/{delim} { yylval.id = yytext; return IDENTIFIER; } IMPLICIT/{delim} { yylval.id = yytext; return IMPLICIT; } IMPLIED/{delim} { yylval.id = yytext; return IMPLIED; } IMPORTS/{delim} { yylval.id = yytext; return IMPORTS; } /* SMI(ng) specific */ INCLUDES/{delim} { yylval.id = yytext; return INCLUDES; } INDEX/{delim} { yylval.id = yytext; return INDEX; } /* SPPI specific */ INSTALL-ERRORS/{delim} { yylval.id = yytext; return INSTALL_ERRORS; } INTEGER/{delim} { yylval.id = yytext; return INTEGER; } Integer32/{delim} { yylval.id = yytext; return INTEGER32; } /* SPPI specific */ Integer64/{delim} { yylval.id = yytext; return INTEGER64; } IpAddress/{delim} { yylval.id = yytext; return IPADDRESS; } LAST-UPDATED/{delim} { yylval.id = yytext; return LAST_UPDATED; } MANDATORY-GROUPS/{delim} { yylval.id = yytext; return MANDATORY_GROUPS; } /* SMI(ng) specific */ MAX-ACCESS/{delim} { yylval.id = yytext; return MAX_ACCESS; } /* SMI(ng) specific */ MIN-ACCESS/{delim} { yylval.id = yytext; return MIN_ACCESS; } MODULE/{delim} { yylval.id = yytext; return MODULE; } MODULE-COMPLIANCE/{delim} { yylval.id = yytext; return MODULE_COMPLIANCE; } MODULE-IDENTITY/{delim} { yylval.id = yytext; return MODULE_IDENTITY; } /* SMI(ng) specific */ NOTIFICATION-GROUP/{delim} { yylval.id = yytext; return NOTIFICATION_GROUP; } /* SMI(ng) specific */ NOTIFICATION-TYPE/{delim} { yylval.id = yytext; return NOTIFICATION_TYPE; } /* SMI(ng) specific */ NOTIFICATIONS/{delim} { yylval.id = yytext; return NOTIFICATIONS; } OBJECT/{delim} { yylval.id = yytext; return OBJECT; } OBJECT-GROUP/{delim} { yylval.id = yytext; return OBJECT_GROUP; } OBJECT-IDENTITY/{delim} { yylval.id = yytext; return OBJECT_IDENTITY; } OBJECT-TYPE/{delim} { yylval.id = yytext; return OBJECT_TYPE; } OBJECTS/{delim} { yylval.id = yytext; return OBJECTS; } OCTET/{delim} { yylval.id = yytext; return OCTET; } OF/{delim} { yylval.id = yytext; return OF; } ORGANIZATION/{delim} { yylval.id = yytext; return ORGANIZATION; } Opaque/{delim} { yylval.id = yytext; return OPAQUE; } /* SPPI specific */ PIB-ACCESS/{delim} { yylval.id = yytext; return PIB_ACCESS; } /* SPPI specific */ PIB-DEFINITIONS/{delim} { yylval.id = yytext; return PIB_DEFINITIONS; } /* SPPI specific */ PIB-INDEX/{delim} { yylval.id = yytext; return PIB_INDEX; } /* SPPI specific */ PIB-MIN-ACCESS/{delim} { yylval.id = yytext; return PIB_MIN_ACCESS; } /* SPPI specific */ PIB-REFERENCES/{delim} { yylval.id = yytext; return PIB_REFERENCES; } /* SPPI specific */ PIB-TAG/{delim} { yylval.id = yytext; return PIB_TAG; } /* SPPI specific */ POLICY-ACCESS/{delim} { yylval.id = yytext; return POLICY_ACCESS; } /* SMI(ng) specific */ PRODUCT-RELEASE/{delim} { yylval.id = yytext; return PRODUCT_RELEASE; } REFERENCE/{delim} { yylval.id = yytext; return REFERENCE; } REVISION/{delim} { yylval.id = yytext; return REVISION; } SEQUENCE/{delim} { yylval.id = yytext; return SEQUENCE; } SIZE/{delim} { yylval.id = yytext; return SIZE; } STATUS/{delim} { yylval.id = yytext; return STATUS; } STRING/{delim} { yylval.id = yytext; return STRING; } /* SPPI specific */ SUBJECT-CATEGORIES/{delim} { yylval.id = yytext; return SUBJECT_CATEGORIES; } /* SMI(ng) specific */ SUPPORTS/{delim} { yylval.id = yytext; return SUPPORTS; } SYNTAX/{delim} { yylval.id = yytext; return SYNTAX; } TEXTUAL-CONVENTION/{delim} { yylval.id = yytext; return TEXTUAL_CONVENTION; } TimeTicks/{delim} { yylval.id = yytext; return TIMETICKS; } /* SMI(ng) specific */ TRAP-TYPE/{delim} { yylval.id = yytext; return TRAP_TYPE; } /* SPPI specific */ UNIQUENESS/{delim} { yylval.id = yytext; return UNIQUENESS; } UNITS/{delim} { yylval.id = yytext; return UNITS; } /* SMI(ng) specific */ UNIVERSAL/{delim} { yylval.id = yytext; return UNIVERSAL; } Unsigned32/{delim} { yylval.id = yytext; return UNSIGNED32; } /* SPPI specific */ Unsigned64/{delim} { yylval.id = yytext; return UNSIGNED64; } /* SPPI specific */ VALUE/{delim} { yylval.id = yytext; return VALUE; } /* SMI(ng) specific */ VARIABLES/{delim} { yylval.id = yytext; return VARIABLES; } /* SMI(ng) specific */ VARIATION/{delim} { yylval.id = yytext; return VARIATION; } /* SMI(ng) specific */ WRITE-SYNTAX/{delim} { yylval.id = yytext; return WRITE_SYNTAX; } /* * Lex rules for forbidden keywords. Do you use {delim} here because it * introduces a trailing context which is (a) slow and (b) causes * REJECT to be used. */ ABSENT|ANY|BIT|BOOLEAN|BY|COMPONENT|COMPONENTS|DEFAULT|DEFINED|ENUMERATED|EXPLICIT|EXTERNAL|FALSE|MAX|MIN|MINUS-INFINITY|NULL|OPTIONAL|PLUS-INFINITY|PRESENT|PRIVATE|REAL|SET|TAGS|TRUE|WITH/[^a-zA-Z0-9-] { smiPrintError(parser, ERR_ILLEGAL_KEYWORD, yytext); } ABSENT|ANY|BIT|BOOLEAN|BY|COMPONENT|COMPONENTS|DEFAULT|DEFINED|ENUMERATED|EXPLICIT|EXTERNAL|FALSE|MAX|MIN|MINUS-INFINITY|NULL|OPTIONAL|PLUS-INFINITY|PRESENT|PRIVATE|REAL|SET|TAGS|TRUE|WITH/-- { smiPrintError(parser, ERR_ILLEGAL_KEYWORD, yytext); } /* * Lex rules for descriptors. */ /* e.g. module names: REF: draft,p.12-13 */ [A-Z](-?[a-zA-Z0-9_]+)*-? { if (yytext[yyleng-1] == '-') { smiPrintError(parser, ERR_ID_ENDS_IN_HYPHEN, yytext); } if (strchr(yytext, '_')) { smiPrintError(parser, ERR_UNDERSCORE_IN_IDENTIFIER, yytext); } yylval.id = smiStrdup(yytext); return UPPERCASE_IDENTIFIER; } /* same for lowercase names */ [a-z](-?[a-zA-Z0-9_]+)*-? { if (yytext[yyleng-1] == '-') { smiPrintError(parser, ERR_ID_ENDS_IN_HYPHEN, yytext); } if (strchr(yytext, '_')) { smiPrintError(parser, ERR_UNDERSCORE_IN_IDENTIFIER, yytext); } yylval.id = smiStrdup(yytext); return LOWERCASE_IDENTIFIER; } /* * Lex rules for numbers. */ 0+/[0-9] { smiPrintError(parser, ERR_LEADING_ZEROS); } ([1-9][0-9]*|0)/[^0-9] { errno = 0; yylval.unsigned32 = strtoul(yytext, NULL, 10); if (errno == ERANGE) { /* TODO: proper handling for SPPI vs SMI modules. */ if ((thisParser.modulePtr->export.language == SMI_LANGUAGE_SPPI) || (thisParser.modulePtr->export.language == SMI_LANGUAGE_SMIV2)) { yylval.unsigned64 = strtoull(yytext, NULL, 10); if (errno == ERANGE) { /* * Dirty hack - on my system strtoull returns ERANGE for * numbers that definitely are in range for 64 bit types. * Nevertheless strtoull returns the correct value. To * check this we convert the number back to a string. */ char tmpbuf[24]; sprintf(tmpbuf, "%llu", yylval.unsigned64); if (strcmp(yytext, tmpbuf)) smiPrintError(parser, ERR_SPPI_UNSIGNED64_NUMBER_RANGE, yytext); } return NUMBER64; } /* We don't want to see this error for the SNMPv2-SMI module, * neither for COPS-PR-SPPI. */ if (strcmp(thisParser.modulePtr->export.name, "SNMPv2-SMI") && strcmp(thisParser.modulePtr->export.name, "COPS-PR-SPPI")) { smiPrintError(parser, ERR_SMIV2_UNSIGNED_NUMBER_RANGE, yytext); } } return NUMBER; } -([1-9][0-9]*|0)/[^0-9] { errno = 0; yylval.integer32 = strtol(yytext, NULL, 10); if (errno == ERANGE) { /* TODO: proper handling for SPPI vs SMI modules. */ if (thisParser.modulePtr->export.language == SMI_LANGUAGE_SPPI) { yylval.integer64 = strtoll(yytext, NULL, 10); if (errno == ERANGE) { /* * Dirty hack - on my system strtoull returns ERANGE for * numbers that definitely are in range for 64 bit types. * Nevertheless strtoull returns the correct value. To * check this we convert the number back to a string. */ char tmpbuf[24]; sprintf(tmpbuf, "%lld", yylval.integer64); if (strcmp(yytext, tmpbuf)) smiPrintError(parser, ERR_SPPI_SIGNED64_NUMBER_RANGE, yytext); } return NEGATIVENUMBER64; } /* We don't want to see this error for the COPS-PR-SPPI module. */ if (strcmp(thisParser.modulePtr->export.name, "COPS-PR-SPPI")) smiPrintError(parser, ERR_SMIV2_SIGNED_NUMBER_RANGE, yytext); } return NEGATIVENUMBER; } /* * Lex rules for binary, hexadecimal and quoted strings (RFC 2578 3.1.1) */ \'[01]*\'[bB] { yylval.text = yytext + 1; yytext[yyleng-2] = '\0'; if ((yyleng-3) % 8) { smiPrintError(parser, ERR_BIN_STRING_MUL8, yylval.text); } return BIN_STRING; } \'[0123456789AaBbCcDdEeFf]*\'[hH] { yylval.text = yytext + 1; yytext[yyleng-2] = '\0'; if ((yyleng-3) % 2) { smiPrintError(parser, ERR_HEX_STRING_MUL2, yylval.text); } return HEX_STRING; } \"[^\"]*\" { char *s, *d; /* the source and destination pointer */ int column = 0; /* the current column */ int newlineflag = 0; /* we have just passed a newline */ int cutoffcolumn = 0; /* cut off white space up to this column */ /* (computed by caculating the indentation */ /* of the first column) */ yytext[yyleng-1] = '\0'; for (d = yytext, s = yytext+1; s[0]; s++, d++) { if ((s[0] == '\n' && s[1] == '\r') /* newline sequence */ || (s[0] == '\r' && s[1] == '\n')) { thisParser.line++; d[0] = '\n'; s++; newlineflag = 1; column = 0; if (cutoffcolumn < 0) cutoffcolumn = 0; } else if (s[0] == '\n' || s[0] == '\r') { /* simple newline */ thisParser.line++; d[0] = '\n'; newlineflag = 1; column = 0; if (cutoffcolumn < 0) cutoffcolumn = 0; } else { if (newlineflag && isspace((int)(unsigned char)s[0])) { /* space after newline */ if (cutoffcolumn <= 0) { cutoffcolumn -= (s[0] == '\t') ? (8-((column-1) % 8)) : 1; } column += (s[0] == '\t') ? (8-((column-1) % 8)) : 1; if (cutoffcolumn <= 0 || column <= cutoffcolumn) { d--; } else { d[0] = s[0]; newlineflag = 0; } } else { /* everything else */ if (! isascii(s[0])) { smiPrintError(parser, ERR_ILLEGAL_CHAR_IN_STRING, s[0], (unsigned char) s[0]); } d[0] = s[0]; newlineflag = 0; if (cutoffcolumn < 0) { cutoffcolumn *= -1; } } } } d[0] = '\0'; yylval.text = yytext; return QUOTED_STRING; } /* * Skip the remainder of the line */ .*{eol} { thisParser.line++; BEGIN(INITIAL); } /* * Everything else... */ . { smiPrintError(parser, ERR_LEX_UNEXPECTED_CHAR); BEGIN(Skipline); } %%