Remove dependency to CPP: new parser that handles comments
[wmaker-crm.git] / WINGs / menuparser.c
blobf1ff1f419d7cf18e4f02e1465be046845c7b4d30
1 /*
2 * Window Maker window manager
4 * Copyright (c) 2012 Christophe Curis
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "wconfig.h"
22 #include <stdio.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <stdarg.h>
27 #include <WINGs/WUtil.h>
29 #include "menuparser.h"
31 static WMenuParser menu_parser_create_new(const char *file_name, void *file);
32 static char *menu_parser_isolate_token(WMenuParser parser);
35 /***** Constructor and Destructor for the Menu Parser object *****/
36 WMenuParser WMenuParserCreate(const char *file_name, void *file)
38 WMenuParser parser;
40 parser = menu_parser_create_new(file_name, file);
41 return parser;
44 void WMenuParserDelete(WMenuParser parser)
46 wfree(parser);
49 static WMenuParser menu_parser_create_new(const char *file_name, void *file)
51 WMenuParser parser;
53 parser = wmalloc(sizeof(*parser));
54 parser->file_name = file_name;
55 parser->file_handle = file;
56 parser->rd = parser->line_buffer;
58 return parser;
61 /***** To report helpfull messages to user *****/
62 const char *WMenuParserGetFilename(WMenuParser parser)
64 return parser->file_name;
67 void WMenuParserError(WMenuParser parser, const char *msg, ...)
69 char buf[MAXLINE];
70 va_list args;
72 va_start(args, msg);
73 vsnprintf(buf, sizeof(buf), msg, args);
74 va_end(args);
75 __wmessage("WMenuParser", parser->file_name, parser->line_number, WMESSAGE_TYPE_WARNING, buf);
78 /***** Read one line from file and split content *****/
79 /* The function returns False when the end of file is reached */
80 Bool WMenuParserGetLine(WMenuParser top_parser, char **title, char **command, char **parameter, char **shortcut)
82 WMenuParser cur_parser;
83 enum { GET_TITLE, GET_COMMAND, GET_PARAMETERS, GET_SHORTCUT } scanmode;
84 char *token;
85 char lineparam[MAXLINE];
86 char *params = NULL;
88 lineparam[0] = '\0';
89 *title = NULL;
90 *command = NULL;
91 *parameter = NULL;
92 *shortcut = NULL;
93 scanmode = GET_TITLE;
95 cur_parser = top_parser;
97 read_next_line:
98 if (fgets(cur_parser->line_buffer, sizeof(cur_parser->line_buffer), cur_parser->file_handle) == NULL) {
99 return False;
101 cur_parser->line_number++;
102 cur_parser->rd = cur_parser->line_buffer;
104 for (;;) {
105 if (!menu_parser_skip_spaces_and_comments(cur_parser)) {
106 /* We reached the end of line */
107 if (scanmode == GET_TITLE)
108 goto read_next_line; // Empty line -> skip
109 else
110 break; // Finished reading current line -> return it to caller
113 /* Found a word */
114 token = menu_parser_isolate_token(cur_parser);
115 switch (scanmode) {
116 case GET_TITLE:
117 *title = token;
118 scanmode = GET_COMMAND;
119 break;
121 case GET_COMMAND:
122 if (strcmp(token, "SHORTCUT") == 0) {
123 scanmode = GET_SHORTCUT;
124 wfree(token);
125 } else {
126 *command = token;
127 scanmode = GET_PARAMETERS;
129 break;
131 case GET_SHORTCUT:
132 if (*shortcut != NULL) {
133 WMenuParserError(top_parser, _("multiple SHORTCUT definition not valid") );
134 wfree(*shortcut);
136 *shortcut = token;
137 scanmode = GET_COMMAND;
138 break;
140 case GET_PARAMETERS:
142 char *src;
144 if (params == NULL) {
145 params = lineparam;
146 } else {
147 if ((params - lineparam) < sizeof(lineparam)-1)
148 *params++ = ' ';
150 src = token;
151 while ((params - lineparam) < sizeof(lineparam)-1)
152 if ( (*params = *src++) == '\0')
153 break;
154 else
155 params++;
156 wfree(token);
158 break;
162 if (params != NULL) {
163 lineparam[sizeof(lineparam) - 1] = '\0';
164 *parameter = wstrdup(lineparam);
167 return True;
170 /* Return False when there's nothing left on the line,
171 otherwise increment parser's pointer to next token */
172 Bool menu_parser_skip_spaces_and_comments(WMenuParser parser)
174 for (;;) {
175 while (isspace(*parser->rd))
176 parser->rd++;
178 if (*parser->rd == '\0')
179 return False; // Found the end of current line
181 else if ((parser->rd[0] == '\\') &&
182 (parser->rd[1] == '\n') &&
183 (parser->rd[2] == '\0')) {
184 // Means that the current line is expected to be continued on next line
185 if (fgets(parser->line_buffer, sizeof(parser->line_buffer), parser->file_handle) == NULL) {
186 WMenuParserError(parser, _("premature end of file while expecting a new line after '\\'") );
187 return False;
189 parser->line_number++;
190 parser->rd = parser->line_buffer;
192 } else if (parser->rd[0] == '/') {
193 if (parser->rd[1] == '/') // Single line C comment
194 return False; // Won't find anything more on this line
195 if (parser->rd[1] == '*') {
196 int start_line;
198 start_line = parser->line_number;
199 parser->rd += 2;
200 for (;;) {
201 /* Search end-of-comment marker */
202 while (*parser->rd != '\0') {
203 if (parser->rd[0] == '*')
204 if (parser->rd[1] == '/')
205 goto found_end_of_comment;
206 parser->rd++;
209 /* Marker not found -> load next line */
210 if (fgets(parser->line_buffer, sizeof(parser->line_buffer), parser->file_handle) == NULL) {
211 WMenuParserError(parser, _("reached end of file while searching '*/' for comment started at line %d"), start_line);
212 return False;
214 parser->line_number++;
215 parser->rd = parser->line_buffer;
218 found_end_of_comment:
219 parser->rd += 2; // Skip closing mark
220 continue; // Because there may be spaces after the comment
222 return True; // the '/' was not a comment, treat it as user data
223 } else
224 return True; // Found some data
228 /* read a token (non-spaces suite of characters)
229 the result os wmalloc's, so it needs to be free'd */
230 static char *menu_parser_isolate_token(WMenuParser parser)
232 char *start;
233 char *token;
235 start = parser->rd;
237 while (*parser->rd != '\0')
238 if (isspace(*parser->rd))
239 break;
240 else if ((parser->rd[0] == '/') &&
241 ((parser->rd[1] == '*') || (parser->rd[1] == '/')))
242 break;
243 else if ((parser->rd[0] == '\\') && (parser->rd[1] == '\n'))
244 break;
245 else if ((*parser->rd == '"' ) || (*parser->rd == '\'')) {
246 char eot = *parser->rd++;
247 while ((*parser->rd != '\0') && (*parser->rd != '\n'))
248 if (*parser->rd++ == eot)
249 goto found_end_quote;
250 WMenuParserError(parser, _("missing closing quote or double-quote before end-of-line") );
251 found_end_quote:
253 } else
254 parser->rd++;
256 token = wmalloc(parser->rd - start + 1);
257 strncpy(token, start, parser->rd - start);
258 token[parser->rd - start] = '\0';
260 return token;