From 0d6eb56d6f76379b04ce9d632d615354bdc20f23 Mon Sep 17 00:00:00 2001
From: Christophe CURIS <[email protected]>
Date: Sat, 23 Jun 2012 18:18:31 +0200
Subject: [PATCH 09/10] 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 6c74144..7659817 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 4407fc9..692ef69 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 12e7548..bada620 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)) {
--
1.7.10.4