From 729b06e53d8ed2c5bd6c33f5c78f2e05e0448f36 Mon Sep 17 00:00:00 2001
From: Christophe CURIS <[email protected]>
Date: Sun, 17 Jun 2012 23:56:34 +0200
Subject: [PATCH 05/10] Remove dependency to CPP: new parser that handles
comments
Wrote a new parsing code that is able to skip over comments.
Note that because CPP is still active, you will not see any
effect unless you disable CPP pre-processing.
---
WINGs/menuparser.c | 237 +++++++++++++++++++++++++++++++++++-----------------
WINGs/menuparser.h | 2 +
2 files changed, 162 insertions(+), 77 deletions(-)
diff --git a/WINGs/menuparser.c b/WINGs/menuparser.c
index e36d1af..f1ff1f4 100644
--- a/WINGs/menuparser.c
+++ b/WINGs/menuparser.c
@@ -1,7 +1,7 @@
/*
* Window Maker window manager
*
- * Copyright (c) 1997-2003 Alfredo K. Kojima
+ * Copyright (c) 2012 Christophe Curis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <string.h>
+#include <ctype.h>
#include <stdarg.h>
#include <WINGs/WUtil.h>
@@ -28,7 +29,7 @@
#include "menuparser.h"
static WMenuParser menu_parser_create_new(const char *file_name, void *file);
-static void separateline(char *line, char **title, char **command, char **parameter, char **shortcut);
+static char *menu_parser_isolate_token(WMenuParser parser);
/***** Constructor and Destructor for the Menu Parser object *****/
@@ -75,104 +76,186 @@ void WMenuParserError(WMenuParser parser, const char *msg, ...)
}
/***** Read one line from file and split content *****/
+/* The function returns False when the end of file is reached */
Bool WMenuParserGetLine(WMenuParser top_parser, char **title, char **command, char **parameter, char **shortcut)
{
WMenuParser cur_parser;
- char *line = NULL, *result = NULL;
- size_t len;
- int done;
+ enum { GET_TITLE, GET_COMMAND, GET_PARAMETERS, GET_SHORTCUT } scanmode;
+ char *token;
+ char lineparam[MAXLINE];
+ char *params = NULL;
+ lineparam[0] = '\0';
*title = NULL;
*command = NULL;
*parameter = NULL;
*shortcut = NULL;
+ scanmode = GET_TITLE;
cur_parser = top_parser;
-again:
- done = 0;
- while (!done && fgets(cur_parser->line_buffer, sizeof(cur_parser->line_buffer), cur_parser->file_handle) != NULL) {
- line = wtrimspace(cur_parser->line_buffer);
- len = strlen(line);
- cur_parser->line_number++;
-
- /* allow line wrapping */
- if (len > 0 && line[len - 1] == '\\') {
- line[len - 1] = '\0';
- } else {
- done = 1;
+ read_next_line:
+ if (fgets(cur_parser->line_buffer, sizeof(cur_parser->line_buffer), cur_parser->file_handle) == NULL) {
+ return False;
+ }
+ cur_parser->line_number++;
+ cur_parser->rd = cur_parser->line_buffer;
+
+ for (;;) {
+ if (!menu_parser_skip_spaces_and_comments(cur_parser)) {
+ /* We reached the end of line */
+ if (scanmode == GET_TITLE)
+ goto read_next_line; // Empty line -> skip
+ else
+ break; // Finished reading current line -> return it to caller
}
- if (result == NULL) {
- result = line;
- } else {
- if (strlen(result) < MAXLINE) {
- result = wstrappend(result, line);
+ /* Found a word */
+ token = menu_parser_isolate_token(cur_parser);
+ switch (scanmode) {
+ case GET_TITLE:
+ *title = token;
+ scanmode = GET_COMMAND;
+ break;
+
+ case GET_COMMAND:
+ if (strcmp(token, "SHORTCUT") == 0) {
+ scanmode = GET_SHORTCUT;
+ wfree(token);
+ } else {
+ *command = token;
+ scanmode = GET_PARAMETERS;
+ }
+ break;
+
+ case GET_SHORTCUT:
+ if (*shortcut != NULL) {
+ WMenuParserError(top_parser, _("multiple SHORTCUT definition not valid") );
+ wfree(*shortcut);
+ }
+ *shortcut = token;
+ scanmode = GET_COMMAND;
+ break;
+
+ case GET_PARAMETERS:
+ {
+ char *src;
+
+ if (params == NULL) {
+ params = lineparam;
+ } else {
+ if ((params - lineparam) < sizeof(lineparam)-1)
+ *params++ = ' ';
+ }
+ src = token;
+ while ((params - lineparam) < sizeof(lineparam)-1)
+ if ( (*params = *src++) == '\0')
+ break;
+ else
+ params++;
+ wfree(token);
}
- wfree(line);
+ break;
}
}
- if (!done || ferror(cur_parser->file_handle)) {
- wfree(result);
- result = NULL;
- } else if (result != NULL && (result[0] == 0 || result[0] == '#' ||
- (result[0] == '/' && result[1] == '/'))) {
- wfree(result);
- result = NULL;
- goto again;
- } else if (result != NULL && strlen(result) >= MAXLINE) {
- WMenuParserError(cur_parser, _("maximal line size exceeded in menu config") );
- wfree(result);
- result = NULL;
- goto again;
- }
- if (result == NULL)
- return False;
+ if (params != NULL) {
+ lineparam[sizeof(lineparam) - 1] = '\0';
+ *parameter = wstrdup(lineparam);
+ }
- separateline(line, title, command, parameter, shortcut);
- wfree(line);
return True;
}
-static void separateline(char *line, char **title, char **command, char **parameter, char **shortcut)
+/* Return False when there's nothing left on the line,
+ otherwise increment parser's pointer to next token */
+Bool menu_parser_skip_spaces_and_comments(WMenuParser parser)
{
- char *suffix, *next = line;
-
- /* get the title */
- *title = wtokennext(line, &next);
- if (next == NULL)
- return;
- line = next;
-
- /* get the command or shortcut keyword */
- *command = wtokennext(line, &next);
- if (next == NULL)
- return;
- line = next;
-
- if (*command != NULL && strcmp(*command, "SHORTCUT") == 0) {
- /* get the shortcut */
- *shortcut = wtokennext(line, &next);
- if (next == NULL)
- return;
- line = next;
-
- /* get the command */
- *command = wtokennext(line, &next);
- if (next == NULL)
- return;
- line = next;
- }
+ for (;;) {
+ while (isspace(*parser->rd))
+ parser->rd++;
+
+ if (*parser->rd == '\0')
+ return False; // Found the end of current line
- /* get the parameters */
- suffix = wtrimspace(line);
+ else if ((parser->rd[0] == '\\') &&
+ (parser->rd[1] == '\n') &&
+ (parser->rd[2] == '\0')) {
+ // Means that the current line is expected to be continued on next line
+ if (fgets(parser->line_buffer, sizeof(parser->line_buffer), parser->file_handle) == NULL) {
+ WMenuParserError(parser, _("premature end of file while expecting a new line after '\\'") );
+ return False;
+ }
+ parser->line_number++;
+ parser->rd = parser->line_buffer;
+
+ } else if (parser->rd[0] == '/') {
+ if (parser->rd[1] == '/') // Single line C comment
+ return False; // Won't find anything more on this line
+ if (parser->rd[1] == '*') {
+ int start_line;
+
+ start_line = parser->line_number;
+ parser->rd += 2;
+ for (;;) {
+ /* Search end-of-comment marker */
+ while (*parser->rd != '\0') {
+ if (parser->rd[0] == '*')
+ if (parser->rd[1] == '/')
+ goto found_end_of_comment;
+ parser->rd++;
+ }
- /* should we keep this weird old behavior? */
- if (suffix[0] == '"') {
- *parameter = wtokennext(suffix, &next);
- wfree(suffix);
- } else {
- *parameter = suffix;
+ /* Marker not found -> load next line */
+ if (fgets(parser->line_buffer, sizeof(parser->line_buffer), parser->file_handle) == NULL) {
+ WMenuParserError(parser, _("reached end of file while searching '*/' for comment started at line %d"), start_line);
+ return False;
+ }
+ parser->line_number++;
+ parser->rd = parser->line_buffer;
+ }
+
+ found_end_of_comment:
+ parser->rd += 2; // Skip closing mark
+ continue; // Because there may be spaces after the comment
+ }
+ return True; // the '/' was not a comment, treat it as user data
+ } else
+ return True; // Found some data
}
}
+
+/* read a token (non-spaces suite of characters)
+ the result os wmalloc's, so it needs to be free'd */
+static char *menu_parser_isolate_token(WMenuParser parser)
+{
+ char *start;
+ char *token;
+
+ start = parser->rd;
+
+ while (*parser->rd != '\0')
+ if (isspace(*parser->rd))
+ break;
+ else if ((parser->rd[0] == '/') &&
+ ((parser->rd[1] == '*') || (parser->rd[1] == '/')))
+ break;
+ else if ((parser->rd[0] == '\\') && (parser->rd[1] == '\n'))
+ break;
+ else if ((*parser->rd == '"' ) || (*parser->rd == '\'')) {
+ char eot = *parser->rd++;
+ while ((*parser->rd != '\0') && (*parser->rd != '\n'))
+ if (*parser->rd++ == eot)
+ goto found_end_quote;
+ WMenuParserError(parser, _("missing closing quote or double-quote before end-of-line") );
+ found_end_quote:
+ ;
+ } else
+ parser->rd++;
+
+ token = wmalloc(parser->rd - start + 1);
+ strncpy(token, start, parser->rd - start);
+ token[parser->rd - start] = '\0';
+
+ return token;
+}
diff --git a/WINGs/menuparser.h b/WINGs/menuparser.h
index 752e105..594f581 100644
--- a/WINGs/menuparser.h
+++ b/WINGs/menuparser.h
@@ -37,4 +37,6 @@ struct w_menu_parser {
char line_buffer[MAXLINE];
};
+Bool menu_parser_skip_spaces_and_comments(WMenuParser parser);
+
#endif /* _MENUPARSER_H_INCLUDED */
--
1.7.10.4