| /* -*-C-*- |
| * |
| * Copyright 1994 Martin von Loewis |
| * Copyright 1998 Bertho A. Stultiens (BS) |
| * |
| * 20-Jun-1998 BS - Changed the filename conversion. Filenames are |
| * case-sensitive inder *nix, but not under dos. |
| * default behaviour is to convert to lower case. |
| * - All backslashes are converted to forward and |
| * both single and double slash is recognized as |
| * MS/Borland does. |
| * - Fixed a bug in 'yywf' case that prevented |
| * double quoted names to be scanned propperly. |
| * |
| * 19-May-1998 BS - Started to build a preprocessor. |
| * - Changed keyword processing completely to |
| * table-lookups. |
| * |
| * 20-Apr-1998 BS - Added ';' comment stripping |
| * |
| * 17-Apr-1998 BS - Made the win32 keywords optional when compiling in |
| * 16bit mode |
| * |
| * 15-Apr-1998 BS - Changed string handling to include escapes |
| * - Added unicode string handling (no codepage |
| * translation though). |
| * - 'Borrowed' the main idea of string scanning from |
| * the flex manual pages. |
| * - Added conditional handling of scanning depending |
| * on the state of the parser. This was mainly required |
| * to distinguish a file to load or raw data that |
| * follows. MS's definition of filenames is rather |
| * complex... It can be unquoted or double quoted. If |
| * double quoted, then the '\\' char is not automatically |
| * escaped according to Borland's rc compiler, but it |
| * accepts both "\\path\\file.rc" and "\path\file.rc". |
| * This makes life very hard! I go for the escaped |
| * version, as this seems to be the documented way... |
| * - Single quoted strings are now parsed and converted |
| * here. |
| * - Added comment stripping. The implementation is |
| * 'borrowed' from the flex manpages. |
| * - Rebuild string processing so that it may contain |
| * escaped '\0'. |
| */ |
| |
| /* Exclusive rules when looking for a filename */ |
| %x yywf |
| %x yywf_s |
| /* Exclusive string handling */ |
| %x yystr |
| /* Exclusive unicode string handling */ |
| %x yylstr |
| /* Exclusive rcdata single quoted data handling */ |
| %x yyrcd |
| /* Exclusive comment eating... */ |
| %x comment |
| /* Preprocessor exclusives */ |
| %x pp_incl |
| %x pp_def |
| %x pp_undef |
| %x pp_if |
| %x pp_ifdef |
| %x pp_ifndef |
| %x pp_elif |
| %x pp_else |
| %x pp_endif |
| %x pp_error |
| /* Set when accumulating #define's expansion text */ |
| %x pp_def_s |
| /* Set when processing function type defines */ |
| %x pp_ignore |
| /* Set when need to strip to eol */ |
| %x pp_ignore_eol |
| /* Set when handling a false #if case */ |
| %x pp_false |
| /* Set when stripping c-junk */ |
| %x pp_strips |
| %x pp_stripp |
| %x pp_stripp_final |
| |
| /*%option stack*/ |
| %option never-interactive |
| /*%option noyywrap */ |
| /* Some shortcut definitions */ |
| ws [ \f\t\r] |
| cident [a-zA-Z_][0-9a-zA-Z_]* |
| |
| %{ |
| |
| #if !defined(YY_FLEX_MAJOR_VERSION) || (1000 * YY_FLEX_MAJOR_VERSION + YY_FLEX_MINOR_VERSION < 2005) |
| #error Must use flex version 2.5.1 or higher (yy_scan_* routines are required). |
| #endif |
| |
| /*#define LEX_DEBUG*/ |
| |
| #include "config.h" |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| #include "wrc.h" |
| #include "utils.h" |
| #include "preproc.h" |
| #include "parser.h" |
| #include "newstruc.h" |
| |
| #include "y.tab.h" |
| |
| #define YY_USE_PROTOS |
| #define YY_NO_UNPUT |
| |
| /* Always update the current character position within a line */ |
| #define YY_USER_ACTION char_number+=yyleng; |
| |
| raw_data_t *new_raw_data(void); |
| |
| void addcchar(char c); |
| void addwchar(short s); |
| string_t *get_buffered_cstring(void); |
| string_t *get_buffered_wstring(void); |
| string_t *make_string(char *s); |
| string_t *make_filename(char *s, int len); |
| |
| int line_number = 1; /* The current line */ |
| int char_number = 1; /* The current char pos within the line */ |
| static char *cbuffer; /* Buffers for string collection */ |
| static int cbufidx; |
| static int cbufalloc = 0; |
| static short *wbuffer; |
| static int wbufidx; |
| static int wbufalloc = 0; |
| static int want_nl = 0; /* Set when newline needs to go to parser */ |
| static int want_ident = 0; /* Set is #ifdef, #ifndef or defined is seen */ |
| static int stripslevel = 0; /* Count {} during pp_strips mode */ |
| static int stripplevel = 0; /* Count () during pp_strips mode */ |
| static char *substtext = NULL; /* Holds the substition text while getting a define */ |
| static int cjunk_tagline; /* Where did we start stripping (helps error tracking) */ |
| |
| #ifdef YY_USE_STACK |
| void push_to(int start) { yy_push_state(start); } |
| void pop_start(void) { yy_pop_state(start); } |
| #else |
| #define MAXSTARTSTACK 32 |
| static int startstack[MAXSTARTSTACK]; |
| static int startstackidx = 0; |
| |
| void push_to(int start) |
| { |
| if(yydebug) |
| printf("push_to(%d): %d -> %d\n", line_number, YY_START, start); |
| if(startstackidx >= MAXSTARTSTACK-1) |
| internal_error(__FILE__, __LINE__, "Start condition stack overflow"); |
| startstack[startstackidx++] = YY_START; |
| BEGIN(start); |
| } |
| |
| void pop_start(void) |
| { |
| if(yydebug) |
| printf("pop_start(%d): %d <- %d\n", line_number, startstack[startstackidx-1], YY_START); |
| if(startstackidx <= 0) |
| internal_error(__FILE__, __LINE__, "Start condition stack underflow"); |
| --startstackidx; |
| BEGIN(startstack[startstackidx]); |
| } |
| #endif |
| |
| |
| struct bufferstackentry { |
| YY_BUFFER_STATE bufferstate; /* Buffer to switch back to */ |
| struct pp_entry *define; /* Points to expanding define |
| or NULL if handling includes |
| */ |
| int line_number; /* Line that we were handling */ |
| int char_number; /* The current position */ |
| char *filename; /* Filename that we were handling */ |
| }; |
| |
| #define MAXBUFFERSTACK 128 |
| static struct bufferstackentry bufferstack[MAXBUFFERSTACK]; |
| static int bufferstackidx = 0; |
| |
| void push_buffer(YY_BUFFER_STATE buf, struct pp_entry *ppp, char *filename) |
| { |
| if(yydebug) |
| printf("push_buffer: %p %p %p\n", buf, ppp, filename); |
| if(bufferstackidx >= MAXBUFFERSTACK-1) |
| internal_error(__FILE__, __LINE__, "Buffer stack overflow"); |
| memset(&bufferstack[bufferstackidx], 0, sizeof(bufferstack[0])); |
| bufferstack[bufferstackidx].bufferstate = buf; |
| bufferstack[bufferstackidx].define = ppp; |
| if(ppp) |
| ppp->expanding = 1; |
| else if(filename) |
| { |
| /* These will track the yyerror to the correct file and line */ |
| bufferstack[bufferstackidx].line_number = line_number; |
| bufferstack[bufferstackidx].char_number = char_number; |
| line_number = 1; |
| char_number = 1; |
| bufferstack[bufferstackidx].filename = input_name; |
| input_name = filename; |
| } |
| else |
| internal_error(__FILE__, __LINE__, "Pushing buffer without knowing where to go to"); |
| bufferstackidx++; |
| } |
| |
| YY_BUFFER_STATE pop_buffer(void) |
| { |
| if(bufferstackidx <= 0) |
| return (YY_BUFFER_STATE)0; |
| bufferstackidx--; |
| if(bufferstack[bufferstackidx].define) |
| bufferstack[bufferstackidx].define->expanding = 0; |
| else |
| { |
| line_number = bufferstack[bufferstackidx].line_number; |
| char_number = bufferstack[bufferstackidx].char_number; |
| input_name = bufferstack[bufferstackidx].filename; |
| fclose(yyin); |
| } |
| if(yydebug) |
| printf("pop_buffer: %p %p (%d, %d) %p\n", |
| bufferstack[bufferstackidx].bufferstate, |
| bufferstack[bufferstackidx].define, |
| bufferstack[bufferstackidx].line_number, |
| bufferstack[bufferstackidx].char_number, |
| bufferstack[bufferstackidx].filename); |
| yy_switch_to_buffer(bufferstack[bufferstackidx].bufferstate); |
| return bufferstack[bufferstackidx].bufferstate; |
| } |
| |
| void do_include(char *name, int namelen) |
| { |
| char *cpy = (char *)xmalloc(namelen); |
| strcpy(cpy, name+1); /* strip leading " or < */ |
| cpy[namelen-2] = '\0'; /* strip trailing " or > */ |
| if((yyin = open_include(cpy, name[0] == '"')) == NULL) |
| yyerror("Unable to open include file %s", cpy); |
| push_buffer(YY_CURRENT_BUFFER, NULL, cpy); |
| yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); |
| } |
| |
| |
| struct keyword { |
| char *keyword; |
| int token; |
| int isextension; |
| int needcase; |
| int alwayskeyword; |
| }; |
| |
| static struct keyword keywords[] = { |
| { "ACCELERATORS", ACCELERATORS, 0, 0, 0}, |
| { "ALT", ALT, 0, 0, 0}, |
| { "ASCII", ASCII, 0, 0, 0}, |
| { "AUTO3STATE", AUTO3STATE, 1, 0, 0}, |
| { "AUTOCHECKBOX", AUTOCHECKBOX, 1, 0, 0}, |
| { "AUTORADIOBUTTON", AUTORADIOBUTTON, 1, 0, 0}, |
| { "BEGIN", tBEGIN, 0, 0, 1}, |
| { "BITMAP", tBITMAP, 0, 0, 0}, |
| { "BLOCK", BLOCK, 0, 0, 1}, |
| { "BUTTON", BUTTON, 1, 0, 0}, |
| { "CAPTION", CAPTION, 0, 0, 0}, |
| { "CHARACTERISTICS", CHARACTERISTICS, 1, 0, 0}, |
| { "CHECKBOX", CHECKBOX, 0, 0, 0}, |
| { "CHECKED", CHECKED, 0, 0, 0}, |
| { "CLASS", CLASS, 0, 0, 0}, |
| { "COMBOBOX", COMBOBOX, 0, 0, 0}, |
| { "CONTROL", CONTROL, 0, 0, 0}, |
| { "CTEXT", CTEXT, 0, 0, 0}, |
| { "CURSOR", CURSOR, 0, 0, 0}, |
| { "defined", tDEFINED, 0, 1, 1}, |
| { "DEFPUSHBUTTON", DEFPUSHBUTTON, 0, 0, 1}, |
| { "DIALOG", DIALOG, 0, 0, 0}, |
| { "DIALOGEX", DIALOGEX, 1, 0, 0}, |
| { "DISCARDABLE", DISCARDABLE, 0, 0, 0}, |
| { "DLGINIT", DLGINIT, 0, 0, 0}, |
| { "EDITTEXT", EDITTEXT, 0, 0, 0}, |
| { "END", tEND, 0, 0, 1}, |
| { "EXSTYLE", EXSTYLE, 0, 0, 0}, |
| { "extern", tEXTERN, 0, 1, 1}, |
| { "FILEFLAGS", FILEFLAGS, 0, 0, 0}, |
| { "FILEFLAGSMASK", FILEFLAGSMASK, 0, 0, 0}, |
| { "FILEOS", FILEOS, 0, 0, 0}, |
| { "FILESUBTYPE", FILESUBTYPE, 0, 0, 0}, |
| { "FILETYPE", FILETYPE, 0, 0, 0}, |
| { "FILEVERSION", FILEVERSION, 0, 0, 0}, |
| { "FIXED", tFIXED, 0, 0, 0}, |
| { "FONT", FONT, 0, 0, 0}, |
| { "GRAYED", GRAYED, 0, 0, 0}, |
| { "GROUPBOX", GROUPBOX, 0, 0, 0}, |
| { "HELP", HELP, 0, 0, 0}, |
| { "ICON", ICON, 0, 0, 0}, |
| { "IMPURE", IMPURE, 0, 0, 0}, |
| { "INACTIVE", INACTIVE, 0, 0, 0}, |
| { "LANGUAGE", LANGUAGE, 1, 0, 1}, |
| { "LISTBOX", LISTBOX, 0, 0, 0}, |
| { "LOADONCALL", LOADONCALL, 0, 0, 0}, |
| { "LTEXT", LTEXT, 0, 0, 0}, |
| { "MENU", MENU, 0, 0, 0}, |
| { "MENUBARBREAK", MENUBARBREAK, 0, 0, 0}, |
| { "MENUBREAK", MENUBREAK, 0, 0, 0}, |
| { "MENUEX", MENUEX, 1, 0, 0}, |
| { "MENUITEM", MENUITEM, 0, 0, 0}, |
| { "MESSAGETABLE", MESSAGETABLE, 1, 0, 0}, |
| { "MOVEABLE", MOVEABLE, 0, 0, 0}, |
| { "NOINVERT", NOINVERT, 0, 0, 0}, |
| { "NOT", NOT, 0, 0, 0}, |
| { "POPUP", POPUP, 0, 0, 0}, |
| { "PRELOAD", PRELOAD, 0, 0, 0}, |
| { "PRODUCTVERSION", PRODUCTVERSION, 0, 0, 0}, |
| { "PURE", tPURE, 0, 0, 0}, |
| { "PUSHBUTTON", PUSHBUTTON, 0, 0, 0}, |
| { "RADIOBUTTON", RADIOBUTTON, 0, 0, 0}, |
| { "RCDATA", RCDATA, 0, 0, 0}, |
| { "RTEXT", RTEXT, 0, 0, 0}, |
| { "SCROLLBAR", SCROLLBAR, 0, 0, 0}, |
| { "SEPARATOR", SEPARATOR, 0, 0, 0}, |
| { "SHIFT", SHIFT, 0, 0, 0}, |
| { "STATE3", STATE3, 1, 0, 0}, |
| { "STRING", tSTRING, 0, 0, 0}, |
| { "STRINGTABLE", STRINGTABLE, 0, 0, 1}, |
| { "STYLE", STYLE, 0, 0, 0}, |
| { "TOOLBAR", TOOLBAR, 1, 0, 0}, |
| { "typedef", tTYPEDEF, 0, 1, 1}, |
| { "VALUE", VALUE, 0, 0, 0}, |
| { "VERSION", VERSION, 1, 0, 0}, |
| { "VERSIONINFO", VERSIONINFO, 0, 0, 0}, |
| { "VIRTKEY", VIRTKEY, 0, 0, 0} |
| }; |
| |
| #define NKEYWORDS (sizeof(keywords)/sizeof(keywords[0])) |
| #define KWP(p) ((struct keyword *)(p)) |
| int kw_cmp_func(const void *s1, const void *s2) |
| { |
| int ret; |
| ret = strcasecmp(KWP(s1)->keyword, KWP(s2)->keyword); |
| if(!ret && (KWP(s1)->needcase || KWP(s2)->needcase)) |
| return strcmp(KWP(s1)->keyword, KWP(s2)->keyword); |
| else |
| return ret; |
| } |
| |
| #define KW_BSEARCH |
| #define DO_SORT |
| struct keyword *iskeyword(char *kw) |
| { |
| struct keyword *kwp; |
| struct keyword key; |
| key.keyword = kw; |
| key.needcase = 0; |
| #ifdef DO_SORT |
| { |
| /* Make sure that it is sorted for bsearsh */ |
| static int sorted = 0; |
| if(!sorted) |
| { |
| qsort(keywords, NKEYWORDS, sizeof(keywords[0]), kw_cmp_func); |
| sorted = 1; |
| } |
| } |
| #endif |
| #ifdef KW_BSEARCH |
| kwp = bsearch(&key, keywords, NKEYWORDS, sizeof(keywords[0]), kw_cmp_func); |
| #else |
| { |
| int i; |
| for(i = 0; i < NKEYWORDS; i++) |
| { |
| if(!kw_cmp_func(&key, &keywords[i])) |
| break; |
| } |
| if(i < NKEYWORDS) |
| kwp = &keywords[i]; |
| else |
| kwp = NULL; |
| } |
| #endif |
| |
| #ifdef LEX_DEBUG |
| if(kwp && !strcmp(kwp->keyword, "LANGUAGE")) |
| printf("Got Language\n"); |
| #endif |
| if(kwp == NULL || (kwp->isextension && !extensions)) |
| return NULL; |
| else |
| return kwp; |
| } |
| |
| void add_to_substtext(char *text, int len) |
| { |
| if(!substtext) |
| { |
| substtext = xstrdup(text); |
| } |
| else |
| { |
| substtext = (char *)xrealloc(substtext, strlen(substtext)+len+1); |
| strcat(substtext, text); |
| } |
| } |
| |
| %} |
| |
| %% |
| /* #include handling */ |
| ^{ws}*#{ws}*include{ws}* push_to(pp_incl); |
| <pp_incl>\<[^\n\>]+\> do_include(yytext, yyleng); pop_start(); |
| <pp_incl>\"[^\n\>]+\" do_include(yytext, yyleng); pop_start(); |
| <pp_incl>. yyerror("Malformed #include"); |
| |
| /* #define handling */ |
| ^{ws}*#{ws}*define{ws}* push_to(pp_def); |
| <pp_def>{cident} { |
| set_define(yytext); |
| push_to(pp_def_s); |
| } |
| <pp_def>{cident}\( push_to(pp_ignore); /* Ignore function-like defines for now*/ |
| <pp_def>. yyerror("Malformed #define"); |
| |
| <pp_ignore,pp_def_s>[^\/\\\n]* { |
| if(YY_START == pp_def_s) |
| add_to_substtext(yytext, yyleng); |
| } |
| <pp_ignore,pp_def_s>\/[^\/\*][^\/\\\n]* { /* Comment is handled in normal handling */ |
| if(YY_START == pp_def_s) |
| add_to_substtext(yytext, yyleng); |
| } |
| <pp_ignore,pp_def_s>\\{ws}*\n line_number++; char_number = 1; /* Line continuation */ |
| <pp_ignore,pp_def_s>\n { |
| if(YY_START == pp_def_s) |
| { |
| add_define(substtext ? substtext : ""); |
| free(substtext); |
| substtext = NULL; |
| } |
| line_number++; |
| char_number = 1; |
| pop_start(); |
| pop_start(); |
| } |
| |
| /* #undef handling */ |
| ^{ws}*#{ws}*undef{ws}* push_to(pp_undef); |
| <pp_undef>{cident} { |
| del_define(yytext); |
| pop_start(); |
| /*push_to(pp_ignore);*/ |
| } |
| |
| /* Conditional handling */ |
| <INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*if{ws}* { |
| if(YY_START == pp_false) |
| { |
| if(yydebug) |
| printf("(%d)#if ignored\n", line_number); |
| push_if(0, 0, 1); |
| push_to(pp_ignore_eol); |
| } |
| else |
| { |
| push_to(INITIAL); |
| want_nl = 1; |
| return tIF; |
| } |
| } |
| <INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*ifdef{ws}* { |
| if(YY_START == pp_false) |
| { |
| if(yydebug) |
| printf("(%d)#ifdef ignored\n", line_number); |
| push_if(0, 0, 1); |
| push_to(pp_ignore_eol); |
| } |
| else |
| { |
| push_to(INITIAL); |
| want_nl = 1; |
| want_ident = 1; |
| return tIFDEF; |
| } |
| } |
| <INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*ifndef{ws}* { |
| if(YY_START == pp_false) |
| { |
| if(yydebug) |
| printf("(%d)#ifndef ignored\n", line_number); |
| push_if(0, 0, 1); |
| push_to(pp_ignore_eol); |
| } |
| else |
| { |
| push_to(INITIAL); |
| want_nl = 1; |
| want_ident = 1; |
| return tIFNDEF; |
| } |
| } |
| <INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*elif{ws}* { |
| if(!isnevertrue_if()) |
| { |
| push_to(INITIAL); |
| want_nl = 1; |
| return tELIF; |
| } |
| else if(YY_START == pp_false) |
| push_to(pp_ignore_eol); |
| if(yydebug) |
| printf("(%d)#elif ignored\n", line_number); |
| } |
| <INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*else{ws}* { |
| if(!isnevertrue_if()) |
| { |
| push_to(INITIAL); |
| want_nl = 1; |
| return tELSE; |
| } |
| if(yydebug) |
| printf("(%d)#else ignored\n", line_number); |
| } |
| <INITIAL,pp_strips,pp_stripp,pp_false>^{ws}*#{ws}*endif{ws}* { |
| if(!isnevertrue_if()) |
| { |
| want_nl = 1; |
| return tENDIF; |
| } |
| else |
| { |
| if(yydebug) |
| printf("(%d)#endif ignored\n", line_number); |
| pop_if(); |
| } |
| } |
| |
| /* The error directive */ |
| ^{ws}*#{ws}*error{ws}* push_to(pp_error); |
| <pp_error>[^\n]* yyerror("Error directive: %s", yytext); |
| |
| /* preprocessor junk */ |
| ^{ws}*#{ws}*pragma[^\n]* ; /* Ignore #pragma */ |
| ^{ws}*#{ws}*line[^\n]* ; /* Ignore #line */ |
| /* We'll get an error on malformed #xxx statements |
| * by not recognising '#' at all. This helps tracking |
| * preprocessor errors. |
| */ |
| /*^{ws}*#{ws}* ; Ignore # */ |
| |
| <pp_strips>\{ stripslevel++; |
| <pp_strips>\} stripslevel--; |
| <pp_strips>; if(!stripslevel) pop_start(); |
| <pp_strips>\/[^*\n] ; /* To catch comments */ |
| <pp_strips>[^\{\};\n#/]* ; /* Ignore rest */ |
| |
| <pp_stripp>\( stripplevel++; |
| <pp_stripp>\) { |
| stripplevel--; |
| if(!stripplevel) |
| { |
| pop_start(); |
| push_to(pp_stripp_final); |
| } |
| } |
| <pp_stripp>\/[^*\n] ; /* To catch comments */ |
| <pp_stripp>[^\(\);\n#/]* ; /* Ignore rest */ |
| |
| <pp_stripp_final>{ws}* ; /* Ignore */ |
| <pp_stripp_final>; pop_start(); /* Kill the semicolon */ |
| <pp_stripp_final>\n line_number++; char_number = 1; pop_start(); |
| <pp_stripp_final>. yyless(0); pop_start(); |
| |
| <pp_false>. ; /* Ignore everything except #xxx during false #if state */ |
| |
| <pp_ignore_eol>[^\n]* pop_start(); |
| |
| /* These are special cases due to filename scanning */ |
| <yywf>[Dd][Ii][Ss][Cc][Aa][Rr][Dd][Aa][Bb][Ll][Ee] return DISCARDABLE; |
| <yywf>[Ff][Ii][Xx][Ee][Dd] return tFIXED; |
| <yywf>[Ii][Mm][Pp][Uu][Rr][Ee] return IMPURE; |
| <yywf>[Mm][Oo][Vv][Ee][Aa][Bb][Ll][Ee] return MOVEABLE; |
| <yywf>[Ll][Oo][Aa][Dd][Oo][Nn][Cc][Aa][Ll][Ll] return LOADONCALL; |
| <yywf>[Pp][Rr][Ee][Ll][Oo][Aa][Dd] return PRELOAD; |
| <yywf>[Pp][Uu][Rr][Ee] return tPURE; |
| |
| \{ return tBEGIN; |
| \} return tEND; |
| |
| [0-9]+[lL]? { yylval.num = strtoul(yytext, 0, 10); return toupper(yytext[yyleng-1]) == 'L' ? LNUMBER : NUMBER; } |
| 0[xX][0-9A-Fa-f]+[lL]? { yylval.num = strtoul(yytext, 0, 16); return toupper(yytext[yyleng-1]) == 'L' ? LNUMBER : NUMBER; } |
| 0[oO][0-7]+[lL]? { yylval.num = strtoul(yytext+2, 0, 8); return toupper(yytext[yyleng-1]) == 'L' ? LNUMBER : NUMBER; } |
| [A-Za-z_0-9]+ { |
| struct keyword *token; |
| struct pp_entry *ppp; |
| |
| want_rscname = 0; |
| |
| if(want_ident) |
| { |
| /* Prevent preprocessor subst */ |
| want_ident = 0; |
| yylval.str = make_string(yytext); |
| #ifdef LEX_DEBUG |
| printf("want IDENT (%s, %d, %d): <%s>\n", input_name, line_number, char_number, yytext); |
| #endif |
| return IDENT; |
| } |
| else if((ppp = pp_lookup(yytext)) != NULL) |
| { |
| /* Do preprocessor substitution, |
| * but expand only if macro is not |
| * already expanding. |
| */ |
| if(!ppp->expanding) |
| { |
| #ifdef LEX_DEBUG |
| printf("expand IDENT (%s, %d, %d): <%s>\n", input_name, line_number, char_number, yytext); |
| #endif |
| push_buffer(YY_CURRENT_BUFFER, ppp, NULL); |
| yy_scan_string(ppp->subst); |
| } |
| } |
| else if((token = iskeyword(yytext)) != NULL |
| && !(!token->alwayskeyword && want_rscname)) |
| { |
| switch(token->token) |
| { |
| case tDEFINED: |
| want_ident = 1; |
| break; |
| /*case RCDATA:*/ |
| case CURSOR: |
| case tBITMAP: |
| case MESSAGETABLE: |
| case DLGINIT: |
| push_to(yywf); |
| break; |
| case FONT: |
| case ICON: |
| if(!indialog) |
| push_to(yywf); |
| break; |
| case DIALOG: |
| case DIALOGEX: |
| indialog = 1; |
| break; |
| } |
| return token->token; |
| } |
| else |
| { |
| yylval.str = make_string(yytext); |
| #ifdef LEX_DEBUG |
| printf("%s IDENT (%s, %d, %d): <%s>\n", |
| want_rscname ? "rscname" : "just", |
| input_name, |
| line_number, |
| char_number, |
| yytext); |
| #endif |
| return IDENT; |
| } |
| } |
| \|\| return LOGOR; |
| \&\& return LOGAND; |
| \=\= return EQ; |
| \!\= return NE; |
| \<\= return LTE; |
| \>\= return GTE; |
| |
| <yywf>[^ \f\t\r\n\"]* { pop_start(); yylval.str = make_filename(yytext, yyleng); return FILENAME; } |
| <yywf>\" push_to(yywf_s); |
| <yywf_s>[^\"\n]*\" { pop_start(); pop_start(); yylval.str = make_filename(yytext, yyleng-1); return FILENAME; } |
| <yywf_s>\n yyerror("Newline in filename"); |
| |
| L\" { |
| push_to(yylstr); |
| wbufidx = 0; |
| if(!win32) |
| yywarning("16bit resource contains unicode strings\n"); |
| } |
| <yylstr>\" { |
| pop_start(); |
| yylval.str = get_buffered_wstring(); |
| return tSTRING; |
| } |
| <yylstr>\n yyerror("Unterminated string"); |
| <yylstr>\\[0-7]{1,6} { /* octal escape sequence */ |
| int result; |
| result = strtol(yytext+1, 0, 8); |
| if ( result > 0xffff ) |
| yyerror("Character constant out of range"); |
| addwchar((short)result); |
| } |
| <yylstr>\\x[0-9a-fA-F]{4} { /* hex escape sequence */ |
| int result; |
| result = strtol(yytext+2, 0, 16); |
| addwchar((short)result); |
| } |
| <yylstr>\\[0-9]+ yyerror("Bad escape secuence"); |
| <yylstr>\\a addwchar('\a'); |
| <yylstr>\\b addwchar('\b'); |
| <yylstr>\\f addwchar('\f'); |
| <yylstr>\\n addwchar('\n'); |
| <yylstr>\\r addwchar('\r'); |
| <yylstr>\\t addwchar('\t'); |
| <yylstr>\\v addwchar('\v'); |
| <yylstr>\\(.|\n) addwchar(yytext[1]); |
| <yylstr>\"\" addcchar('\"'); /* "bla""bla" -> "bla\"bla" */ |
| <yylstr>\\\"\" addcchar('\"'); /* "bla\""bla" -> "bla\"bla" */ |
| <yylstr>\"{ws}+\" ; /* "bla" "bla" -> "blabla" */ |
| <yylstr>[^\\\n\"]+ { |
| char *yptr = yytext; |
| while(*yptr) /* FIXME: codepage translation */ |
| addwchar(*yptr++ & 0xff); |
| } |
| |
| \" { |
| push_to(yystr); |
| cbufidx = 0; |
| } |
| <yystr>\" { |
| pop_start(); |
| yylval.str = get_buffered_cstring(); |
| return tSTRING; |
| } |
| <yystr>\n yyerror("Unterminated string"); |
| <yystr>\\[0-7]{1,3} { /* octal escape sequence */ |
| int result; |
| result = strtol(yytext+1, 0, 8); |
| if ( result > 0xff ) |
| yyerror("Character constant out of range"); |
| addcchar((char)result); |
| } |
| <yystr>\\x[0-9a-fA-F]{2} { /* hex escape sequence */ |
| int result; |
| result = strtol(yytext+2, 0, 16); |
| addcchar((char)result); |
| } |
| <yystr>\\[0-9]+ yyerror("Bad escape secuence"); |
| <yystr>\\a addcchar('\a'); |
| <yystr>\\b addcchar('\b'); |
| <yystr>\\f addcchar('\f'); |
| <yystr>\\n addcchar('\n'); |
| <yystr>\\r addcchar('\r'); |
| <yystr>\\t addcchar('\t'); |
| <yystr>\\v addcchar('\v'); |
| <yystr>\\(.|\n) addcchar(yytext[1]); |
| <yystr>[^\\\n\"]+ { |
| char *yptr = yytext; |
| while(*yptr) |
| addcchar(*yptr++); |
| } |
| <yystr>\"\" addcchar('\"'); /* "bla""bla" -> "bla\"bla" */ |
| <yystr>\\\"\" addcchar('\"'); /* "bla\""bla" -> "bla\"bla" */ |
| <yystr>\"{ws}+\" ; /* "bla" "bla" -> "blabla" */ |
| <yystr>. yywarning("Matched %c"); |
| |
| |
| \' { |
| push_to(yyrcd); |
| cbufidx = 0; |
| } |
| <yyrcd>\' { |
| pop_start(); |
| yylval.raw = new_raw_data(); |
| yylval.raw->size = cbufidx; |
| yylval.raw->data = xmalloc(yylval.raw->size); |
| memcpy(yylval.raw->data, cbuffer, yylval.raw->size); |
| return RAWDATA; |
| } |
| <yyrcd>[0-9a-fA-F]{2} { |
| int result; |
| result = strtol(yytext, 0, 16); |
| addcchar((char)result); |
| } |
| <yyrcd>{ws}+ ; /* Ignore space */ |
| <yyrcd>\n line_number++; char_number = 1; |
| <yyrcd>. yyerror("Malformed data-line"); |
| |
| <INITIAL,pp_ignore,pp_def_s,pp_strips,pp_stripp>"/*" push_to(comment); /* Eat comment */ |
| <comment>[^*\n]* ; |
| <comment>"*"+[^*/\n]* ; |
| <comment>\n line_number++; char_number = 1; |
| <comment>"*"+"/" pop_start(); |
| |
| ;[^\n]* ; /* Eat comment */ |
| <INITIAL,pp_ignore,pp_def_s>"//"[^\n]* ; /* Eat comment */ |
| |
| <INITIAL,yywf,pp_false,pp_strips,pp_stripp>\n { |
| if(YY_START == yywf) |
| pop_start(); |
| line_number++; |
| char_number = 1; |
| if(want_nl) |
| { |
| want_nl = 0; |
| return tNL; |
| } |
| } |
| <INITIAL,yywf>{ws}+ ; /* Eat whitespace */ |
| |
| <INITIAL>. return yytext[0]; |
| <<EOF>> { |
| YY_BUFFER_STATE b = YY_CURRENT_BUFFER; |
| if(!pop_buffer()) |
| { |
| if(YY_START == pp_strips || YY_START == pp_stripp || YY_START == pp_stripp_final) |
| yyerror("Unexpected end of file during c-junk scanning (started at %d)", cjunk_tagline); |
| else |
| yyterminate(); |
| } |
| yy_delete_buffer(b); |
| } |
| |
| <*>.|\n { |
| /* Catch all rule to find any unmatched text */ |
| if(*yytext == '\n') |
| { |
| line_number++; |
| char_number = 1; |
| } |
| yywarning("Unmatched text '%c' (0x%02x) YY_START=%d", |
| isprint(*yytext) ? *yytext : '.', *yytext, YY_START); |
| } |
| |
| %% |
| |
| #ifndef yywrap |
| int yywrap(void) |
| { |
| #if 0 |
| if(bufferstackidx > 0) |
| { |
| return 0; |
| } |
| #endif |
| return 1; |
| } |
| #endif |
| |
| /* These dup functions copy the enclosed '\0' from |
| * the resource string. |
| */ |
| void addcchar(char c) |
| { |
| if(cbufidx >= cbufalloc) |
| { |
| cbufalloc += 1024; |
| cbuffer = xrealloc(cbuffer, cbufalloc * sizeof(cbuffer[0])); |
| if(cbufalloc > 65536) |
| yywarning("Reallocating string buffer larger than 64kB"); |
| } |
| cbuffer[cbufidx++] = c; |
| } |
| |
| void addwchar(short s) |
| { |
| if(wbufidx >= wbufalloc) |
| { |
| wbufalloc += 1024; |
| wbuffer = xrealloc(wbuffer, wbufalloc * sizeof(wbuffer[0])); |
| if(wbufalloc > 65536) |
| yywarning("Reallocating wide string buffer larger than 64kB"); |
| } |
| |
| /* |
| * BS 08-Aug-1999 FIXME: The '& 0xff' is probably a bug, but I have |
| * not experienced it yet and I seem to remember that this was for |
| * a reason. But, as so many things you tend to forget why. |
| * I guess that there were problems due to the sign extension of |
| * shorts WRT chars (e.g. 0x80 becomes 0xff80 instead of 0x0080). |
| * This should then be fixed in the lexer calling the function. |
| */ |
| wbuffer[wbufidx++] = (short)(s & 0xff); |
| } |
| |
| string_t *get_buffered_cstring(void) |
| { |
| string_t *str = new_string(); |
| str->size = cbufidx; |
| str->type = str_char; |
| str->str.cstr = (char *)xmalloc(cbufidx+1); |
| memcpy(str->str.cstr, cbuffer, cbufidx); |
| str->str.cstr[cbufidx] = '\0'; |
| /* printf("got cstring \"%s\"\n", str->str.cstr); */ |
| return str; |
| } |
| |
| string_t *get_buffered_wstring(void) |
| { |
| string_t *str = new_string(); |
| str->size = wbufidx; |
| str->type = str_unicode; |
| str->str.wstr = (short *)xmalloc(2*(wbufidx+1)); |
| memcpy(str->str.wstr, wbuffer, wbufidx); |
| str->str.wstr[wbufidx] = 0; |
| return str; |
| } |
| |
| string_t *make_string(char *s) |
| { |
| string_t *str = new_string(); |
| str->size = strlen(s); |
| str->type = str_char; |
| str->str.cstr = (char *)xmalloc(str->size+1); |
| memcpy(str->str.cstr, s, str->size+1); |
| return str; |
| } |
| |
| string_t *make_filename(char *s, int len) |
| { |
| char *cptr; |
| string_t *str = new_string(); |
| |
| str->size = len; |
| str->type = str_char; |
| str->str.cstr = (char *)xmalloc(str->size+1); |
| memcpy(str->str.cstr, s, str->size); |
| str->str.cstr[str->size] = '\0'; |
| |
| /* Remove escaped backslash and convert to forward */ |
| cptr = str->str.cstr; |
| for(cptr = str->str.cstr; (cptr = strchr(cptr, '\\')) != NULL; cptr++) |
| { |
| if(cptr[1] == '\\') |
| { |
| memmove(cptr, cptr+1, strlen(cptr)); |
| str->size--; |
| } |
| *cptr = '/'; |
| } |
| |
| /* Convert to lower case. Seems to be reasonable to do */ |
| for(cptr = str->str.cstr; !leave_case && *cptr; cptr++) |
| { |
| *cptr = tolower(*cptr); |
| } |
| return str; |
| } |
| |
| /* Called from the parser to signal filename request */ |
| void set_yywf(void) |
| { |
| push_to(yywf); |
| } |
| |
| /* Called from the parser to signal preprocessor if case */ |
| void set_pp_ignore(int state) |
| { |
| if(state) |
| push_to(pp_false); |
| else |
| pop_start(); |
| } |
| |
| /* Called from the parser to kill c-junk */ |
| void strip_til_semicolon(void) |
| { |
| cjunk_tagline = line_number; |
| push_to(pp_strips); |
| } |
| |
| void strip_til_parenthesis(void) |
| { |
| cjunk_tagline = line_number; |
| stripplevel = 1; /* One scanned already */ |
| push_to(pp_stripp); |
| } |
| |
| |