1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-20 04:48:06 +01:00

Remove dependency to CPP: support for #include directive

The parser is prepared to handle '#' directives, starting with file
inclusion. The search path for the file are taken from what was
actually given to CPP. There is an arbitrary limit to the inclusion
nesting, which is actually not a design limitation but a security
to avoid infinite include loops.
This commit is contained in:
Christophe CURIS
2012-06-18 00:49:53 +02:00
committed by Carlos R. Mafra
parent ed9482b626
commit 88a82ab8df
4 changed files with 183 additions and 8 deletions

View File

@@ -872,7 +872,7 @@ extern char *WMUserDefaultsDidChangeNotification;
typedef struct w_menu_parser *WMenuParser; typedef struct w_menu_parser *WMenuParser;
WMenuParser WMenuParserCreate(const char *file_name, void *file); WMenuParser WMenuParserCreate(const char *file_name, void *file, const char *include_default_paths);
void WMenuParserError(WMenuParser parser, const char *msg, ...) void WMenuParserError(WMenuParser parser, const char *msg, ...)
__attribute__ ((format (printf, 2, 3))); __attribute__ ((format (printf, 2, 3)));

View File

@@ -28,29 +28,47 @@
#include "menuparser.h" #include "menuparser.h"
static WMenuParser menu_parser_create_new(const char *file_name, void *file); static WMenuParser menu_parser_create_new(const char *file_name, void *file,
const char *include_default_paths);
static char *menu_parser_isolate_token(WMenuParser parser); static char *menu_parser_isolate_token(WMenuParser parser);
static void menu_parser_get_directive(WMenuParser parser);
static Bool menu_parser_include_file(WMenuParser parser);
/***** Constructor and Destructor for the Menu Parser object *****/ /***** Constructor and Destructor for the Menu Parser object *****/
WMenuParser WMenuParserCreate(const char *file_name, void *file) WMenuParser WMenuParserCreate(const char *file_name, void *file,
const char *include_default_paths)
{ {
WMenuParser parser; WMenuParser parser;
parser = menu_parser_create_new(file_name, file); parser = menu_parser_create_new(file_name, file, include_default_paths);
return parser; return parser;
} }
void WMenuParserDelete(WMenuParser parser) void WMenuParserDelete(WMenuParser parser)
{ {
if (parser->include_file) {
/* Trick: the top parser's data are not wmalloc'd, we point on the
provided data so we do not wfree it; however for include files
we did wmalloc them.
This code should not be used as the wfree is done when we reach
the end of an include file; however this may not happen when an
early exit occurs (typically when 'readMenuFile' does not find
its expected command). */
fclose(parser->include_file->file_handle);
wfree((char *) parser->include_file->file_name);
WMenuParserDelete(parser->include_file);
}
wfree(parser); wfree(parser);
} }
static WMenuParser menu_parser_create_new(const char *file_name, void *file) static WMenuParser menu_parser_create_new(const char *file_name, void *file,
const char *include_default_paths)
{ {
WMenuParser parser; WMenuParser parser;
parser = wmalloc(sizeof(*parser)); parser = wmalloc(sizeof(*parser));
parser->include_default_paths = include_default_paths;
parser->file_name = file_name; parser->file_name = file_name;
parser->file_handle = file; parser->file_handle = file;
parser->rd = parser->line_buffer; parser->rd = parser->line_buffer;
@@ -68,11 +86,20 @@ void WMenuParserError(WMenuParser parser, const char *msg, ...)
{ {
char buf[MAXLINE]; char buf[MAXLINE];
va_list args; va_list args;
WMenuParser parent;
while (parser->include_file)
parser = parser->include_file;
va_start(args, msg); va_start(args, msg);
vsnprintf(buf, sizeof(buf), msg, args); vsnprintf(buf, sizeof(buf), msg, args);
va_end(args); va_end(args);
__wmessage("WMenuParser", parser->file_name, parser->line_number, WMESSAGE_TYPE_WARNING, buf); __wmessage("WMenuParser", parser->file_name, parser->line_number, WMESSAGE_TYPE_WARNING, buf);
for (parent = parser->parent_file; parent != NULL; parent = parent->parent_file)
__wmessage("WMenuParser", parser->file_name, parser->line_number, WMESSAGE_TYPE_WARNING,
_(" included from file \"%s\" at line %d"),
parent->file_name, parent->line_number);
} }
/***** Read one line from file and split content *****/ /***** Read one line from file and split content *****/
@@ -92,11 +119,24 @@ Bool WMenuParserGetLine(WMenuParser top_parser, char **title, char **command, ch
*shortcut = NULL; *shortcut = NULL;
scanmode = GET_TITLE; scanmode = GET_TITLE;
read_next_line_with_filechange:
cur_parser = top_parser; cur_parser = top_parser;
while (cur_parser->include_file)
cur_parser = cur_parser->include_file;
read_next_line: read_next_line:
if (fgets(cur_parser->line_buffer, sizeof(cur_parser->line_buffer), cur_parser->file_handle) == NULL) { if (fgets(cur_parser->line_buffer, sizeof(cur_parser->line_buffer), cur_parser->file_handle) == NULL) {
if (cur_parser->parent_file == NULL)
/* Not inside an included file -> we have reached the end */
return False; return False;
/* We have only reached the end of an included file -> go back to calling file */
fclose(cur_parser->file_handle);
wfree((char *) cur_parser->file_name);
cur_parser = cur_parser->parent_file;
wfree(cur_parser->include_file);
cur_parser->include_file = NULL;
goto read_next_line_with_filechange;
} }
cur_parser->line_number++; cur_parser->line_number++;
cur_parser->rd = cur_parser->line_buffer; cur_parser->rd = cur_parser->line_buffer;
@@ -109,6 +149,11 @@ Bool WMenuParserGetLine(WMenuParser top_parser, char **title, char **command, ch
else else
break; // Finished reading current line -> return it to caller break; // Finished reading current line -> return it to caller
} }
if ((scanmode == GET_TITLE) && (*cur_parser->rd == '#')) {
cur_parser->rd++;
menu_parser_get_directive(cur_parser);
goto read_next_line_with_filechange;
}
/* Found a word */ /* Found a word */
token = menu_parser_isolate_token(cur_parser); token = menu_parser_isolate_token(cur_parser);
@@ -259,3 +304,129 @@ static char *menu_parser_isolate_token(WMenuParser parser)
return token; return token;
} }
/***** Processing of special # directives *****/
static void menu_parser_get_directive(WMenuParser parser)
{
char *command;
/* Isolate the command */
while (isspace(*parser->rd))
parser->rd++;
command = parser->rd;
while (*parser->rd)
if (isspace(*parser->rd)) {
*parser->rd++ = '\0';
break;
} else parser->rd++;
if (strcmp(command, "include") == 0) {
if (!menu_parser_include_file(parser)) return;
} else {
WMenuParserError(parser, _("unknow directive '#%s'"), command);
return;
}
if (menu_parser_skip_spaces_and_comments(parser))
WMenuParserError(parser, _("extra text after '#' command is ignored: \"%.16s...\""),
parser->rd);
}
/* Extract the file name, search for it in known directories
and create a sub-parser to handle it.
Returns False if the file could not be found */
static Bool menu_parser_include_file(WMenuParser parser)
{
char buffer[MAXLINE];
char *req_filename, *fullfilename, *p;
char eot;
FILE *fh;
if (!menu_parser_skip_spaces_and_comments(parser)) {
WMenuParserError(parser, _("no file name found for #include") );
return False;
}
switch (*parser->rd++) {
case '<': eot = '>'; break;
case '"': eot = '"'; break;
default:
WMenuParserError(parser, _("file name must be enclosed in brackets or double-quotes for #define") );
return False;
}
req_filename = parser->rd;
while (*parser->rd)
if (*parser->rd == eot) {
*parser->rd++ = '\0';
goto found_end_define_fname;
} else parser->rd++;
WMenuParserError(parser, _("missing closing '%c' in filename specification"), eot);
return False;
found_end_define_fname:
{ /* Check we are not nesting too many includes */
WMenuParser p;
int count;
count = 0;
for (p = parser; p->parent_file; p = p->parent_file)
count++;
if (count > MAX_NESTED_INCLUDES) {
WMenuParserError(parser, _("too many nested includes") );
return False;
}
}
/* Absolute paths */
fullfilename = req_filename;
if (req_filename[0] != '/') {
/* Search first in the same directory as the current file */
p = strrchr(parser->file_name, '/');
if (p != NULL) {
int len;
len = p - parser->file_name + 1;
if (len > sizeof(buffer) - 1) len = sizeof(buffer) - 1;
strncpy(buffer, parser->file_name, len);
strncpy(buffer+len, req_filename, sizeof(buffer) - len - 1);
buffer[sizeof(buffer) - 1] = '\0';
fullfilename = buffer;
}
}
fh = fopen(fullfilename, "rb");
/* Not found? Search in wmaker's known places */
if (fh == NULL) {
if (req_filename[0] != '/') {
const char *src;
fullfilename = buffer;
src = parser->include_default_paths;
while (*src != '\0') {
p = buffer;
if (*src == '~') {
char *home = wgethomedir();
while (*home != '\0')
*p++ = *home++;
src++;
}
while ((*src != '\0') && (*src != ':'))
*p++ = *src++;
*p++ = '/';
strncpy(p, req_filename, sizeof(buffer) - (p - buffer - 1));
buffer[sizeof(buffer) - 1] = '\0';
fh = fopen(fullfilename, "rb");
if (fh != NULL) goto found_valid_file;
}
}
WMenuParserError(parser, _("could not find file \"%s\" for include"), req_filename);
return False;
}
/* Found the file, make it our new source */
found_valid_file:
parser->include_file = menu_parser_create_new(wstrdup(req_filename), fh, parser->include_default_paths);
parser->include_file->parent_file = parser;
return True;
}

View File

@@ -28,8 +28,12 @@
*/ */
#define MAXLINE 1024 #define MAXLINE 1024
#define MAX_NESTED_INCLUDES 16 // To avoid infinite includes case
struct w_menu_parser { struct w_menu_parser {
WMenuParser include_file;
WMenuParser parent_file;
const char *include_default_paths;
const char *file_name; const char *file_name;
FILE *file_handle; FILE *file_handle;
int line_number; int line_number;

View File

@@ -969,7 +969,7 @@ static WMenu *readMenuFile(WScreen * scr, char *file_name)
return NULL; return NULL;
} }
} }
parser = WMenuParserCreate(file_name, file); parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) { while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
@@ -1060,7 +1060,7 @@ static WMenu *readMenuPipe(WScreen * scr, char **file_name)
return NULL; return NULL;
} }
} }
parser = WMenuParserCreate(flat_file, file); parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) { while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {