mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-19 20:38:08 +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:
committed by
Carlos R. Mafra
parent
ed9482b626
commit
88a82ab8df
@@ -872,7 +872,7 @@ extern char *WMUserDefaultsDidChangeNotification;
|
||||
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, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
@@ -28,29 +28,47 @@
|
||||
|
||||
#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 void menu_parser_get_directive(WMenuParser parser);
|
||||
static Bool menu_parser_include_file(WMenuParser parser);
|
||||
|
||||
|
||||
/***** 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;
|
||||
|
||||
parser = menu_parser_create_new(file_name, file);
|
||||
parser = menu_parser_create_new(file_name, file, include_default_paths);
|
||||
return 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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
parser = wmalloc(sizeof(*parser));
|
||||
parser->include_default_paths = include_default_paths;
|
||||
parser->file_name = file_name;
|
||||
parser->file_handle = file;
|
||||
parser->rd = parser->line_buffer;
|
||||
@@ -68,11 +86,20 @@ void WMenuParserError(WMenuParser parser, const char *msg, ...)
|
||||
{
|
||||
char buf[MAXLINE];
|
||||
va_list args;
|
||||
WMenuParser parent;
|
||||
|
||||
while (parser->include_file)
|
||||
parser = parser->include_file;
|
||||
|
||||
va_start(args, msg);
|
||||
vsnprintf(buf, sizeof(buf), msg, args);
|
||||
va_end(args);
|
||||
__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 *****/
|
||||
@@ -92,11 +119,24 @@ Bool WMenuParserGetLine(WMenuParser top_parser, char **title, char **command, ch
|
||||
*shortcut = NULL;
|
||||
scanmode = GET_TITLE;
|
||||
|
||||
read_next_line_with_filechange:
|
||||
cur_parser = top_parser;
|
||||
while (cur_parser->include_file)
|
||||
cur_parser = cur_parser->include_file;
|
||||
|
||||
read_next_line:
|
||||
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;
|
||||
|
||||
/* 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->rd = cur_parser->line_buffer;
|
||||
@@ -109,6 +149,11 @@ Bool WMenuParserGetLine(WMenuParser top_parser, char **title, char **command, ch
|
||||
else
|
||||
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 */
|
||||
token = menu_parser_isolate_token(cur_parser);
|
||||
@@ -259,3 +304,129 @@ static char *menu_parser_isolate_token(WMenuParser parser)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -28,8 +28,12 @@
|
||||
*/
|
||||
|
||||
#define MAXLINE 1024
|
||||
#define MAX_NESTED_INCLUDES 16 // To avoid infinite includes case
|
||||
|
||||
struct w_menu_parser {
|
||||
WMenuParser include_file;
|
||||
WMenuParser parent_file;
|
||||
const char *include_default_paths;
|
||||
const char *file_name;
|
||||
FILE *file_handle;
|
||||
int line_number;
|
||||
|
||||
@@ -969,7 +969,7 @@ static WMenu *readMenuFile(WScreen * scr, char *file_name)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
parser = WMenuParserCreate(file_name, file);
|
||||
parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
|
||||
|
||||
while (WMenuParserGetLine(parser, &title, &command, ¶ms, &shortcut)) {
|
||||
|
||||
@@ -1060,7 +1060,7 @@ static WMenu *readMenuPipe(WScreen * scr, char **file_name)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
parser = WMenuParserCreate(flat_file, file);
|
||||
parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
|
||||
|
||||
while (WMenuParserGetLine(parser, &title, &command, ¶ms, &shortcut)) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user