/* * We want a reentrant scanner. */ %option reentrant /* * We don't use input, so don't generate code for it. */ %option noinput /* * We don't use unput, so don't generate code for it. */ %option nounput /* * We don't read interactively from the terminal. */ %option never-interactive /* * The language we're scanning is case-insensitive. */ %option caseless /* * We use start condition stacks. */ %option stack /* * We want to stop processing when we get to the end of the input. */ %option noyywrap /* * The type for the state we keep for a scanner. */ %option extra-type="DiamDict_scanner_state_t *" /* * Prefix scanner routines with "DiamDict_" rather than "yy", so this scanner * can coexist with other scanners. */ %option prefix="DiamDict_" %option outfile="diam_dict.c" /* * We have to override the memory allocators so that we don't get * "unused argument" warnings from the yyscanner argument (which * we don't use, as we have a global memory allocator). * * We provide, as macros, our own versions of the routines generated by Flex, * which just call malloc()/realloc()/free() (as the Flex versions do), * discarding the extra argument. */ %option noyyalloc %option noyyrealloc %option noyyfree %{ /* ** diam_dict.h ** Diameter Dictionary Import Routines ** ** (c) 2007, Luis E. Garcia Ontanon ** ** This library is free software; you can redistribute it and/or ** modify it under the terms of the GNU Library General Public ** License as published by the Free Software Foundation; either ** version 2 of the License, or (at your option) any later version. ** ** This library is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ** Library General Public License for more details. ** ** You should have received a copy of the GNU Library General Public ** License along with this library; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, ** Boston, MA 02110-1301, USA. */ #include "config.h" #include #include #include #include #include #include #include "diam_dict.h" #include #include /* * Deprecated CRT functions. We call malloc+free quite a bit here. We could * probably improve startup performance by using wmem instead. */ #ifdef _WIN32 #define ws_strdup _strdup #else #define ws_strdup strdup #endif typedef struct entity_t { char* name; char* file; struct entity_t* next; } entity_t; #define ATTR_UINT(cont) do { D(("attr_uint " #cont "\t" )); yyextra->attr_uint = &(cont); yy_push_state(GET_UINT_ATTR, yyscanner); } while(0) #define ATTR_STR(cont) do { D(("attr_str " #cont "\t" )); yyextra->attr_str = &(cont); yy_push_state(GET_ATTR, yyscanner); } while(0) #define IGNORE() do { D(("ignore: %s\t",yytext)); yy_push_state(IGNORE_ATTR, yyscanner); } while(0) #define D(args) ddict_debug args #define MAX_INCLUDE_DEPTH 10 #define YY_INPUT(buf,result,max_size) { result = yyextra->current_yyinput(buf,max_size,yyscanner); } #define YY_USER_INIT { \ DiamDict_scanner_state_t *scanner_state = DiamDict_get_extra(yyscanner); \ BEGIN(scanner_state->start_state); \ } #define ECHO #define APPEND(txt,len) append_to_buffer(txt,len,yyextra) typedef struct { const char* sys_dir; char* write_ptr; char* read_ptr; char* strbuf; unsigned size_strbuf; unsigned len_strbuf; ddict_t* dict; ddict_application_t* appl; ddict_avp_t* avp; ddict_enum_t* enumitem; ddict_gavp_t* gavp; ddict_typedefn_t* typedefn; ddict_cmd_t* cmd; ddict_vendor_t* vnd; ddict_xmlpi_t* xmlpi; ddict_application_t* last_appl; ddict_avp_t* last_avp; ddict_enum_t* last_enumitem; ddict_gavp_t* last_gavp; ddict_typedefn_t* last_typedefn; ddict_cmd_t* last_cmd; ddict_vendor_t* last_vnd; ddict_xmlpi_t* last_xmlpi; entity_t *ents; char** attr_str; unsigned* attr_uint; size_t (*current_yyinput)(char*,size_t,yyscan_t); YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr; int start_state; } DiamDict_scanner_state_t; static void ddict_debug(const char* fmt, ...) G_GNUC_PRINTF(1, 2); static void append_to_buffer(const char* txt, unsigned len, DiamDict_scanner_state_t *statep); static FILE* ddict_open(const char*, const char*); /* * Sleazy hack to suppress compiler warnings in yy_fatal_error(). */ #define YY_EXIT_FAILURE ((void)yyscanner, 2) /* * Macros for the allocators, to discard the extra argument. */ #define DiamDict_alloc(size, yyscanner) (void *)malloc(size) #define DiamDict_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size)) #define DiamDict_free(ptr, yyscanner) free((char *)ptr) %} xmlpi_start [[:blank:] \r\n]*<\?[[:blank:] \r\n]* xmlpi_end [[:blank:] \r\n]*\?>[[:blank:] \r\n]* xmlpi_key_attr [[:blank:] \r\n]*key[[:blank:] \r\n]*=[[:blank:] \r\n]*\042 xmlpi_value_attr [[:blank:] \r\n]*value[[:blank:] \r\n]*=[[:blank:] \r\n]*\042 comment_start [[:blank:] \r\n]*[[:blank:] \r\n]* open_tag [[:blank:] \r\n]*<[[:blank:] \r\n]* end_tag [[:blank:] \r\n]*\/>[[:blank:] \r\n]* close_tag [[:blank:] \r\n]*>[[:blank:] \r\n]* open_closetag [[:blank:] \r\n]*<\/[[:blank:] \r\n]* equals [[:blank:] \r\n]*=[[:blank:] \r\n]* whitespace [[:blank:] \r\n]* dquoted \042[^\042]*\042 doctype [[:blank:] \r\n]*[[:blank:] \r\n]* start_entity [[:blank:] \r\n]*<\!ENTITY[[:blank:] \r\n]* system [[:blank:] \r\n]*SYSTEM[[:blank:] \r\n]*\042 entityname [a-z0-9-]+ ndquot [^\042]+ end_entity \042[[:blank:] \r\n]*>[[:blank:] \r\n]* entity \&[a-z0-9-]+; any . stop > stop_end \/> dquot \042 number [-]?[0-9]* dictionary_start dictionary_end <\/dictionary> base_start *]*> base_end <\/base> application_start command_start typedefn_start type_start grouped_end <\/grouped> vendor_start gavp_start {doctype} ; {doctype_end} ; {comment_start} BEGIN LOADING_COMMENT; . ; {comment_end} BEGIN LOADING; {xmlpi_start} BEGIN LOADING_XMLPI; {whitespace} ; {entityname} { yyextra->xmlpi = g_new(ddict_xmlpi_t,1); yyextra->xmlpi->name = g_strdup(yytext); yyextra->xmlpi->key = NULL; yyextra->xmlpi->value = NULL; yyextra->xmlpi->next = NULL; if (!yyextra->dict->xmlpis) yyextra->last_xmlpi = yyextra->dict->xmlpis = yyextra->xmlpi; else yyextra->last_xmlpi = yyextra->last_xmlpi->next = yyextra->xmlpi; BEGIN XMLPI_ATTRS; } {xmlpi_key_attr} BEGIN XMLPI_GETKEY; {ndquot} { yyextra->xmlpi->key = ws_strdup(yytext); BEGIN XMLPI_ATTRS; } {xmlpi_value_attr} BEGIN XMLPI_GETVAL; {ndquot} { yyextra->xmlpi->value = ws_strdup(yytext); BEGIN XMLPI_ATTRS; } . {xmlpi_end} BEGIN LOADING; {start_entity} BEGIN ENTITY; {entityname} { entity_t* e = g_new(entity_t,1); e->name = ws_strdup(yytext); e->next = yyextra->ents; yyextra->ents = e; BEGIN GET_SYSTEM; }; {system} BEGIN GET_FILE; {ndquot} { yyextra->ents->file = ws_strdup(yytext); BEGIN END_ENTITY; } {end_entity} BEGIN LOADING; {open_tag} APPEND("<",1); {close_tag} APPEND(">",1); {end_tag} APPEND("/>",2); {open_closetag} APPEND("{whitespace} APPEND(" ",1); {dquoted} APPEND(yytext, (unsigned) yyleng); {equals} APPEND("=",1); {any} APPEND(yytext, (unsigned) yyleng); {entity} { char* p = ++yytext; entity_t* e; while(*p != ';') p++; *p = '\0'; D(("looking for entity: %s\n",yytext)); if ( yyextra->include_stack_ptr >= MAX_INCLUDE_DEPTH ) { fprintf(stderr, "included files nested to deeply\n"); yyterminate(); } for (e = yyextra->ents; e; e = e->next) { if (strcmp(e->name,yytext) == 0) { yyin = ddict_open(yyextra->sys_dir,e->file); D(("entity: %s filename: %s yyin: %p\n",e->name,e->file,(void*)yyin)); if (!yyin) { if (errno) fprintf(stderr, "Could not open file: '%s', error: %s\n", e->file, g_strerror(errno) ); else fprintf(stderr, "Could not open file: '%s', error unknown (errno == 0)\n", e->file ); yyterminate(); } else { yyextra->include_stack[yyextra->include_stack_ptr++] = YY_CURRENT_BUFFER; yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE, yyscanner), yyscanner); BEGIN LOADING; } break; } } if (!e) { fprintf(stderr, "Could not find entity: '%s'\n", yytext ); yyterminate(); } } <> { if (!yyin) yyterminate(); fclose(yyin); D(("closing: %p %i\n",(void*)yyin,yyextra->include_stack_ptr)); if ( --yyextra->include_stack_ptr < 0 ) { D(("DONE READING\n")); yyin = NULL; yyterminate(); } else { yy_delete_buffer( YY_CURRENT_BUFFER, yyscanner); yy_switch_to_buffer(yyextra->include_stack[yyextra->include_stack_ptr], yyscanner); BEGIN LOADING; } } {ndquot} { *(yyextra->attr_str) = ws_strdup(yytext); D(("%s\n",yytext)); yyextra->attr_str = NULL; BEGIN END_ATTR; } {number} { *(yyextra->attr_uint) = (unsigned) strtoul(yytext,NULL,10); D(("%s\n",yytext);); yyextra->attr_uint = NULL; BEGIN END_ATTR; } {dquot} { yy_pop_state(yyscanner); } . { /* XXX: should go?*/ D(("{%s}",yytext)); } {ignored_quoted} { D(("=>%s<=\n",yytext)); yy_pop_state(yyscanner); } {dictionary_start} { D(("dictionary_start\n")); BEGIN IN_DICT; } {base_start} { D(("base_start\n")); BEGIN IN_APPL; } {application_start} { D(("application_start\n")); yyextra->appl = g_new(ddict_application_t,1); yyextra->appl->name = NULL; yyextra->appl->code = 0; yyextra->appl->next = NULL; if (!yyextra->dict->applications) yyextra->last_appl = yyextra->dict->applications = yyextra->appl; else yyextra->last_appl = yyextra->last_appl->next = yyextra->appl; BEGIN APPL_ATTRS; } {name_attr} { ATTR_STR(yyextra->appl->name); } {id_attr} { ATTR_UINT(yyextra->appl->code); } {stop} BEGIN IN_APPL; {stop_end} BEGIN IN_DICT; {command_end} ; {command_start} { D(("command_start\n")); yyextra->cmd = g_new(ddict_cmd_t,1); yyextra->cmd->name = NULL; yyextra->cmd->vendor = NULL; yyextra->cmd->code = 0; yyextra->cmd->next = NULL; if (!yyextra->dict->cmds) yyextra->last_cmd = yyextra->dict->cmds = yyextra->cmd; else yyextra->last_cmd = yyextra->last_cmd->next = yyextra->cmd; BEGIN COMMAND_ATTRS; } {name_attr} { ATTR_STR(yyextra->cmd->name); } {vendor_attr} { ATTR_STR(yyextra->cmd->vendor); } {code_attr} { ATTR_UINT(yyextra->cmd->code); } {stop} | {stop_end} { BEGIN IN_APPL; } {vendor_start} { D(("vendor_start\n")); yyextra->vnd = g_new(ddict_vendor_t,1); yyextra->vnd->name = NULL; yyextra->vnd->code = 0; yyextra->vnd->next = NULL; if (!yyextra->dict->vendors) yyextra->last_vnd = yyextra->dict->vendors = yyextra->vnd; else yyextra->last_vnd = yyextra->last_vnd->next = yyextra->vnd; BEGIN VENDOR_ATTRS; } {name_attr} { ATTR_STR(yyextra->vnd->desc); } {vendor_attr} { ATTR_STR(yyextra->vnd->name); } {code_attr} { ATTR_UINT(yyextra->vnd->code); } {stop} { BEGIN IN_APPL; } {stop_end} { BEGIN IN_DICT; } {typedefn_start} { D(("typedefn_start\n")); yyextra->typedefn = g_new(ddict_typedefn_t,1); yyextra->typedefn->name = NULL; yyextra->typedefn->parent = NULL; yyextra->typedefn->next = NULL; if (!yyextra->dict->typedefns) yyextra->last_typedefn = yyextra->dict->typedefns = yyextra->typedefn; else yyextra->last_typedefn = yyextra->last_typedefn->next = yyextra->typedefn; BEGIN TYPEDEFN_ATTRS; } {typename_attr} { ATTR_STR(yyextra->typedefn->name); } {typeparent_attr} { ATTR_STR(yyextra->typedefn->parent); } {stop} | {stop_end} { BEGIN IN_APPL; } {avp_start} { D(("avp_start\n")); yyextra->avp = g_new(ddict_avp_t,1); yyextra->avp->name = NULL; yyextra->avp->description = NULL; yyextra->avp->vendor = NULL; yyextra->avp->code = 0; yyextra->avp->type = NULL; yyextra->avp->enums = NULL; yyextra->avp->gavps = NULL; yyextra->avp->next = NULL; if (! yyextra->dict->avps ) yyextra->last_avp = yyextra->dict->avps = yyextra->avp; else yyextra->last_avp = yyextra->last_avp->next = yyextra->avp; BEGIN AVP_ATTRS; } {name_attr} { ATTR_STR(yyextra->avp->name); } {description_attr} { ATTR_STR(yyextra->avp->description); } {vendor_attr} { ATTR_STR(yyextra->avp->vendor); } {code_attr} { ATTR_UINT(yyextra->avp->code); } {stop} { BEGIN IN_AVP; } {stop_end} { BEGIN IN_APPL; } {grouped_start} { yyextra->avp->type = ws_strdup("Grouped"); }; {grouped_end} ; {type_start} { BEGIN TYPE_ATTRS; } {typename_attr} { ATTR_STR(yyextra->avp->type); } {gavp_start} { D(("gavp_start\n")); yyextra->gavp = g_new(ddict_gavp_t,1); yyextra->gavp->name = NULL; yyextra->gavp->code = 0; yyextra->gavp->next = NULL; if (!yyextra->avp->gavps) yyextra->last_gavp = yyextra->avp->gavps = yyextra->gavp; else yyextra->last_gavp = yyextra->last_gavp->next = yyextra->gavp; BEGIN GAVP_ATTRS; } {name_attr} { ATTR_STR(yyextra->gavp->name); } {enum_start} { D(("enum_start\n")); yyextra->enumitem = g_new(ddict_enum_t,1); yyextra->enumitem->name = NULL; yyextra->enumitem->code = 0; yyextra->enumitem->next = NULL; if (!yyextra->avp->enums) yyextra->last_enumitem = yyextra->avp->enums = yyextra->enumitem; else yyextra->last_enumitem = yyextra->last_enumitem->next = yyextra->enumitem; BEGIN ENUM_ATTRS; } {name_attr} { ATTR_STR(yyextra->enumitem->name); } {code_attr} { ATTR_UINT(yyextra->enumitem->code); } {stop} { BEGIN IN_AVP; } {stop_end} { BEGIN IN_AVP; } {avp_end} { D(("avp_end\n")); BEGIN IN_APPL; } {application_end} { D(("application_end\n")); BEGIN IN_DICT; } {stop_end} { D(("application_stop_end\n")); BEGIN IN_DICT; } {vendor_end} { D(("vendor_end\n")); BEGIN IN_DICT; } {base_end} { D(("base_end\n")); BEGIN IN_DICT; } {dictionary_end} { yyterminate(); } {ignored_attr} IGNORE(); . ; %% static int debugging = 0; static void ddict_debug(const char* fmt, ...) { va_list ap; va_start(ap, fmt); if (debugging) vfprintf(stderr, fmt, ap); va_end(ap); fflush(stderr); } /* * Sleazy hack to avoid unused function warnings for yy_top_state. */ extern void ddict_unused(yyscan_t yyscanner); void ddict_unused(yyscan_t yyscanner) { yy_top_state(yyscanner); } static void append_to_buffer(const char* txt, unsigned len, DiamDict_scanner_state_t *statep) { if (statep->strbuf == NULL) { statep->strbuf = (char*)g_malloc(statep->size_strbuf); statep->read_ptr = statep->strbuf; statep->write_ptr = statep->strbuf; } if ( (statep->len_strbuf + len) >= statep->size_strbuf ) { statep->strbuf = (char*)g_realloc(statep->strbuf,statep->size_strbuf *= 2); statep->read_ptr = statep->strbuf; } statep->write_ptr = statep->strbuf + statep->len_strbuf; strncpy(statep->write_ptr,txt,len); statep->len_strbuf += len; } static size_t file_input(char* buf, size_t max, yyscan_t scanner) { FILE *in = yyget_in(scanner); size_t read_cnt; read_cnt = fread(buf,1,max,in); if ( read_cnt == max ) { return max; } else if (read_cnt > 0) { return read_cnt; } else { return YY_NULL; } } static size_t string_input(char* buf, size_t max, yyscan_t scanner) { DiamDict_scanner_state_t *statep = yyget_extra(scanner); if (statep->read_ptr >= statep->write_ptr ) { return YY_NULL; } else if ( statep->read_ptr + max > statep->write_ptr ) { max = statep->write_ptr - statep->read_ptr; } memcpy(buf,statep->read_ptr,max); statep->read_ptr += max; return max; } static FILE * ddict_open(const char* system_directory, const char* filename) { FILE* fh; char* fname; if (system_directory) { fname = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", system_directory,filename); } else { fname = g_strdup(filename); } fh = ws_fopen(fname,"r"); D(("fname: %s fh: %p\n",fname,(void*)fh)); g_free(fname); return fh; } ddict_t * ddict_scan(const char* system_directory, const char* filename, int dbg) { DiamDict_scanner_state_t state; FILE *in; yyscan_t scanner; debugging = dbg; state.sys_dir = system_directory; state.write_ptr = NULL; state.read_ptr = NULL; state.strbuf = NULL; state.size_strbuf = 8192; state.len_strbuf = 0; state.dict = g_new(ddict_t,1); state.dict->applications = NULL; state.dict->cmds = NULL; state.dict->vendors = NULL; state.dict->typedefns = NULL; state.dict->avps = NULL; state.dict->xmlpis = NULL; state.appl = NULL; state.avp = NULL; state.enumitem = NULL; state.gavp = NULL; state.typedefn = NULL; state.cmd = NULL; state.vnd = NULL; state.xmlpi = NULL; state.last_appl = NULL; state.last_avp = NULL; state.last_enumitem = NULL; state.last_gavp = NULL; state.last_typedefn = NULL; state.last_cmd = NULL; state.last_vnd = NULL; state.last_xmlpi = NULL; state.ents = NULL; state.attr_str = NULL; state.attr_uint = NULL; /* * Pass 1. * * Reads the file, does some work, and stores a modified version * of the file contents in memory. */ state.current_yyinput = file_input; state.include_stack_ptr = 0; in = ddict_open(system_directory,filename); if (in == NULL) { D(("unable to open %s: %s\n", filename, g_strerror(errno))); g_free(state.dict); return NULL; } if (DiamDict_lex_init(&scanner) != 0) { D(("Can't initialize scanner: %s\n", g_strerror(errno))); fclose(in); g_free(state.dict); return NULL; } DiamDict_set_in(in, scanner); /* Associate the state with the scanner */ DiamDict_set_extra(&state, scanner); state.start_state = LOADING; DiamDict_lex(scanner); DiamDict_lex_destroy(scanner); /* * XXX - can the lexical analyzer terminate without closing * all open input files? */ D(("\n---------------\n%s\n------- %u -------\n",state.strbuf,state.len_strbuf)); /* * Pass 2. * * Reads the modified version of the file contents and does the * rest of the work. */ state.current_yyinput = string_input; if (DiamDict_lex_init(&scanner) != 0) { D(("Can't initialize scanner: %s\n", g_strerror(errno))); g_free(state.dict); g_free(state.strbuf); return NULL; } /* Associate the state with the scanner */ DiamDict_set_extra(&state, scanner); state.start_state = OUTSIDE; DiamDict_lex(scanner); DiamDict_lex_destroy(scanner); g_free(state.strbuf); return state.dict; } void ddict_free(ddict_t* d) { ddict_application_t *p, *pn; ddict_vendor_t *v, *vn; ddict_cmd_t *c, *cn; ddict_typedefn_t *t, *tn; ddict_avp_t *a, *an; #define FREE_NAMEANDOBJ(n) do { if(n->name) g_free(n->name); g_free(n); } while(0) for (p = d->applications; p; p = pn ) { pn = p->next; FREE_NAMEANDOBJ(p); } for (v = d->vendors; v; v = vn) { vn = v->next; if (v->desc) g_free(v->desc); FREE_NAMEANDOBJ(v); } for (c = d->cmds; c; c = cn ) { cn = c->next; FREE_NAMEANDOBJ(c); } for (t = d->typedefns; t; t = tn) { tn = t->next; if (t->parent) g_free(t->parent); FREE_NAMEANDOBJ(t); } for (a = d->avps; a; a = an) { ddict_gavp_t* g, *gn; ddict_enum_t* e, *en; an = a->next; for (g = a->gavps; g; g = gn) { gn = g->next; FREE_NAMEANDOBJ(g); } for (e = a->enums; e; e = en) { en = e->next; FREE_NAMEANDOBJ(e); } if (a->vendor) g_free(a->vendor); if (a->type) g_free(a->type); if (a->description) g_free(a->description); FREE_NAMEANDOBJ(a); } g_free(d); } void ddict_print(FILE* fh, ddict_t* d) { ddict_application_t* p; ddict_vendor_t* v; ddict_cmd_t* c; ddict_typedefn_t* t; ddict_avp_t* a; for (p = d->applications; p; p = p->next) { fprintf(fh,"Application: %s[%u]:\n", p->name ? p->name : "-", p->code); } for (v = d->vendors; v; v = v->next) { fprintf(fh,"Vendor: %s[%u]:\n", v->name ? v->name : "-", v->code); } for (c = d->cmds; c; c = c->next) { fprintf(fh,"Command: %s[%u] \n", c->name ? c->name : "-", c->code); } for (t = d->typedefns; t; t = t->next) { fprintf(fh,"Type: %s -> %s \n", t->name ? t->name : "-", t->parent ? t->parent : "" ); } for (a = d->avps; a; a = a->next) { ddict_gavp_t* g; ddict_enum_t* e; fprintf(fh,"AVP: %s[%u:%s] %s %s\n", a->name ? a->name : "-", a->code, a->vendor ? a->vendor : "None", a->type ? a->type : "-", a->description ? a->description : ""); for (g = a->gavps; g; g = g->next) { fprintf(fh,"\tGAVP: %s\n", g->name ? g->name : "-" ); } for (e = a->enums; e; e = e->next) { fprintf(fh,"\tEnum: %s[%u]\n", e->name ? e->name : "-", e->code); } } } #ifdef TEST_DIAM_DICT_STANDALONE int main(int argc, char** argv) { ddict_t* d; char* dname = NULL; char* fname; int i = 1; switch (argc) { case 3: dname = argv[i++]; case 2: fname = argv[i]; break; default: fprintf(stderr,"%s: usage [dictionary_dir] dictionary_filename\n",argv[0]); return 1; } d = ddict_scan(dname,fname,1); if (d == NULL) { fprintf(stderr, "Can't open dictionary\n"); return 2; } ddict_print(stdout, d); return 0; } #endif