From 4658f4f9a10235ce34b3b6127091b7accffab858 Mon Sep 17 00:00:00 2001 From: Christophe CURIS Date: Sat, 23 Jun 2012 18:18:31 +0200 Subject: [PATCH] Remove dependency to CPP: add support for conditional directives It is now possible to use #ifdef/#ifndef to exclude some part of the file. The implementation uses a stack to track conditionals so it is possible to use nested constructs. --- WINGs/menuparser.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++ WINGs/menuparser.h | 10 +++++ WINGs/menuparser_macros.c | 8 ++++ 3 files changed, 119 insertions(+) diff --git a/WINGs/menuparser.c b/WINGs/menuparser.c index 6c74144d..7659817d 100644 --- a/WINGs/menuparser.c +++ b/WINGs/menuparser.c @@ -33,6 +33,9 @@ static WMenuParser menu_parser_create_new(const char *file_name, void *file, static char *menu_parser_isolate_token(WMenuParser parser, WParserMacro *list_macros); static void menu_parser_get_directive(WMenuParser parser); static Bool menu_parser_include_file(WMenuParser parser); +static void menu_parser_condition_ifmacro(WMenuParser parser, Bool check_exists); +static void menu_parser_condition_else(WMenuParser parser); +static void menu_parser_condition_end(WMenuParser parser); /***** Constructor and Destructor for the Menu Parser object *****/ @@ -131,6 +134,14 @@ Bool WMenuParserGetLine(WMenuParser top_parser, char **title, char **command, ch read_next_line: if (fgets(cur_parser->line_buffer, sizeof(cur_parser->line_buffer), cur_parser->file_handle) == NULL) { + if (cur_parser->cond.depth > 0) { + int i; + + for (i = 0; i < cur_parser->cond.depth; i++) + WMenuParserError(cur_parser, _("missing #endif to match #%s at line %d"), + cur_parser->cond.stack[i].name, cur_parser->cond.stack[i].line); + } + if (cur_parser->parent_file == NULL) /* Not inside an included file -> we have reached the end */ return False; @@ -159,6 +170,8 @@ Bool WMenuParserGetLine(WMenuParser top_parser, char **title, char **command, ch menu_parser_get_directive(cur_parser); goto read_next_line_with_filechange; } + if (cur_parser->cond.stack[0].skip) + goto read_next_line; /* Found a word */ token = menu_parser_isolate_token(cur_parser, top_parser->macros); @@ -368,6 +381,18 @@ static void menu_parser_get_directive(WMenuParser parser) } else if (strcmp(command, "define") == 0) { menu_parser_define_macro(parser); + } else if (strcmp(command, "ifdef") == 0) { + menu_parser_condition_ifmacro(parser, 1); + + } else if (strcmp(command, "ifndef") == 0) { + menu_parser_condition_ifmacro(parser, 0); + + } else if (strcmp(command, "else") == 0) { + menu_parser_condition_else(parser); + + } else if (strcmp(command, "endif") == 0) { + menu_parser_condition_end(parser); + } else { WMenuParserError(parser, _("unknow directive '#%s'"), command); return; @@ -409,6 +434,11 @@ static Bool menu_parser_include_file(WMenuParser parser) return False; found_end_define_fname: + /* If we're inside a #if sequence, we abort now, but not sooner in + order to keep the syntax check */ + if (parser->cond.stack[0].skip) + return False; + { /* Check we are not nesting too many includes */ WMenuParser p; int count; @@ -475,3 +505,74 @@ static Bool menu_parser_include_file(WMenuParser parser) parser->include_file->parent_file = parser; return True; } + +/* Check wether a macro exists or not, and marks the parser to ignore the + following data accordingly */ +static void menu_parser_condition_ifmacro(WMenuParser parser, Bool check_exists) +{ + WParserMacro *macro; + int idx; + const char *cmd_name, *macro_name; + + cmd_name = check_exists?"ifdef":"ifndef"; + if (!menu_parser_skip_spaces_and_comments(parser)) { + WMenuParserError(parser, _("missing macro name argument to #%s"), cmd_name); + return; + } + + /* jump to end of provided name for later checks that no extra stuff is following */ + macro_name = parser->rd; + while (isnamechr(*parser->rd)) + parser->rd++; + + /* Add this condition to the stack of conditions */ + if (parser->cond.depth >= sizeof(parser->cond.stack) / sizeof(parser->cond.stack[0])) { + WMenuParserError(parser, _("too many nested #if sequences") ); + return; + } + for (idx = parser->cond.depth - 1; idx >= 0; idx--) + parser->cond.stack[idx + 1] = parser->cond.stack[idx]; + parser->cond.depth++; + + if (parser->cond.stack[1].skip) + parser->cond.stack[0].skip = True; + else { + macro = menu_parser_find_macro(parser, macro_name); + parser->cond.stack[0].skip = + ((check_exists) && (macro == NULL)) || + ((!check_exists) && (macro != NULL)) ; + } + strcpy(parser->cond.stack[0].name, cmd_name); + parser->cond.stack[0].line = parser->line_number; +} + +/* Swap the 'data ignore' flag because a #else condition was found */ +static void menu_parser_condition_else(WMenuParser parser) +{ + if (parser->cond.depth <= 0) { + WMenuParserError(parser, _("found #%s but have no matching #if"), "else" ); + return; + } + if ((parser->cond.depth > 1) && (parser->cond.stack[1].skip)) + // The containing #if is false, so we continue skipping anyway + parser->cond.stack[0].skip = True; + else + parser->cond.stack[0].skip = !parser->cond.stack[0].skip; +} + +/* Closes the current conditional, removing it from the stack */ +static void menu_parser_condition_end(WMenuParser parser) +{ + int idx; + + if (parser->cond.depth <= 0) { + WMenuParserError(parser, _("found #%s but have no matching #if"), "endif" ); + return; + } + + if (--parser->cond.depth > 0) + for (idx = 0; idx < parser->cond.depth; idx++) + parser->cond.stack[idx] = parser->cond.stack[idx + 1]; + else + parser->cond.stack[0].skip = False; +} diff --git a/WINGs/menuparser.h b/WINGs/menuparser.h index 4407fc91..692ef69b 100644 --- a/WINGs/menuparser.h +++ b/WINGs/menuparser.h @@ -44,6 +44,16 @@ struct w_menu_parser { FILE *file_handle; int line_number; WParserMacro *macros; + struct { + /* Conditional text parsing is implemented using a stack of the + skip states for each nested #if */ + int depth; + struct { + Bool skip; + char name[8]; + int line; + } stack[32]; + } cond; char *rd; char line_buffer[MAXLINE]; }; diff --git a/WINGs/menuparser_macros.c b/WINGs/menuparser_macros.c index 12e75480..bada6203 100644 --- a/WINGs/menuparser_macros.c +++ b/WINGs/menuparser_macros.c @@ -229,6 +229,14 @@ void menu_parser_define_macro(WMenuParser parser) } else macro->arg_count = -1; // Means no parenthesis at all to expect + /* If we're inside a #if sequence, we abort now, but not sooner in + order to keep the syntax check */ + if (parser->cond.stack[0].skip) { + wfree(macro); + *parser->rd = '\0'; // Ignore macro content + return; + } + /* Get the macro's definition */ menu_parser_skip_spaces_and_comments(parser); if (!menu_parser_read_macro_def(parser, macro, arg_name)) { -- 2.11.4.GIT