From d3f966bf63191ee3803dadf0aa44cfef3fbb35b6 Mon Sep 17 00:00:00 2001
From: Christophe CURIS <[email protected]>
Date: Sat, 23 Jun 2012 18:18:31 +0200
Subject: [PATCH 6/7] 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.
---
src/rootmenu.h | 10 +++++
src/rootmenuparser.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 110 insertions(+)
diff --git a/src/rootmenu.h b/src/rootmenu.h
index 6df6c4b..5b8cc61 100644
--- a/src/rootmenu.h
+++ b/src/rootmenu.h
@@ -53,6 +53,16 @@ struct menu_parser {
FILE *file_handle;
int num_line;
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/src/rootmenuparser.c b/src/rootmenuparser.c
index 5478bf7..c51adf7 100644
--- a/src/rootmenuparser.c
+++ b/src/rootmenuparser.c
@@ -94,6 +94,9 @@ static void menu_parser_get_directive(WMenuParser parser);
/***** Handling of '#' directives *****/
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);
/***** Main parsing from the file *****/
/* The function returns False when the end of file is reached */
@@ -122,6 +125,14 @@ Bool menu_parser_get_line(WMenuParser top_parser, char **title, char **command,
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++)
+ menu_parser_warning(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;
@@ -150,6 +161,8 @@ Bool menu_parser_get_line(WMenuParser top_parser, char **title, char **command,
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);
@@ -359,6 +372,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 {
menu_parser_warning(parser, _("unknow directive '%s'"), command);
return;
@@ -400,6 +425,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;
@@ -467,3 +497,73 @@ 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;
+
+ cmd_name = check_exists?"ifdef":"ifndef";
+ if (!menu_parser_skip_spaces_and_comments(parser)) {
+ menu_parser_warning(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 */
+ 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])) {
+ menu_parser_warning(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, parser->rd);
+ 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->num_line;
+}
+
+/* Swap the 'data ignore' flag because a #else condition was found */
+static void menu_parser_condition_else(WMenuParser parser)
+{
+ if (parser->cond.depth <= 0) {
+ menu_parser_warning(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) {
+ menu_parser_warning(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;
+}
--
1.7.10