X-Git-Url: https://git.sur5r.net/?p=i3%2Fi3;a=blobdiff_plain;f=src%2Fcommands_parser.c;h=4299c008335570ced9c8daebc12bdaa1cefd74e9;hp=a4602d1556de46ba1da69f0b1993470b83c12770;hb=HEAD;hpb=bc52fae15c75635e9cec57e25619d304dba3a69b diff --git a/src/commands_parser.c b/src/commands_parser.c index a4602d15..4299c008 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "commands_parser.c" /* * vim:ts=4:sw=4:expandtab * @@ -25,6 +23,8 @@ * instead of actually calling any function). * */ +#include "all.h" + #include #include #include @@ -32,8 +32,6 @@ #include #include -#include "all.h" - // Macros to make the YAJL API a bit easier to use. #define y(x, ...) (command_output.json_gen != NULL ? yajl_gen_##x(command_output.json_gen, ##__VA_ARGS__) : 0) #define ystr(str) (command_output.json_gen != NULL ? yajl_gen_string(command_output.json_gen, (unsigned char *)str, strlen(str)) : 0) @@ -73,7 +71,14 @@ typedef struct tokenptr { struct stack_entry { /* Just a pointer, not dynamically allocated. */ const char *identifier; - char *str; + enum { + STACK_STR = 0, + STACK_LONG = 1, + } type; + union { + char *str; + long num; + } val; }; /* 10 entries should be enough for everybody. */ @@ -90,7 +95,8 @@ static void push_string(const char *identifier, char *str) { continue; /* Found a free slot, let’s store it here. */ stack[c].identifier = identifier; - stack[c].str = str; + stack[c].val.str = str; + stack[c].type = STACK_STR; return; } @@ -103,73 +109,61 @@ static void push_string(const char *identifier, char *str) { exit(1); } -// XXX: ideally, this would be const char. need to check if that works with all -// called functions. -static char *get_string(const char *identifier) { +// TODO move to a common util +static void push_long(const char *identifier, long num) { + for (int c = 0; c < 10; c++) { + if (stack[c].identifier != NULL) { + continue; + } + + stack[c].identifier = identifier; + stack[c].val.num = num; + stack[c].type = STACK_LONG; + return; + } + + /* When we arrive here, the stack is full. This should not happen and + * means there’s either a bug in this parser or the specification + * contains a command with more than 10 identified tokens. */ + fprintf(stderr, "BUG: commands_parser stack full. This means either a bug " + "in the code, or a new command which contains more than " + "10 identified tokens.\n"); + exit(1); +} + +// TODO move to a common util +static const char *get_string(const char *identifier) { for (int c = 0; c < 10; c++) { if (stack[c].identifier == NULL) break; if (strcmp(identifier, stack[c].identifier) == 0) - return stack[c].str; + return stack[c].val.str; } return NULL; } -static void clear_stack(void) { +// TODO move to a common util +static long get_long(const char *identifier) { for (int c = 0; c < 10; c++) { - if (stack[c].str != NULL) - free(stack[c].str); - stack[c].identifier = NULL; - stack[c].str = NULL; + if (stack[c].identifier == NULL) + break; + if (strcmp(identifier, stack[c].identifier) == 0) + return stack[c].val.num; } -} - -// TODO: remove this if it turns out we don’t need it for testing. -#if 0 -/******************************************************************************* - * A dynamically growing linked list which holds the criteria for the current - * command. - ******************************************************************************/ - -typedef struct criterion { - char *type; - char *value; - TAILQ_ENTRY(criterion) criteria; -} criterion; - -static TAILQ_HEAD(criteria_head, criterion) criteria = - TAILQ_HEAD_INITIALIZER(criteria); - -/* - * Stores the given type/value in the list of criteria. - * Accepts a pointer as first argument, since it is 'call'ed by the parser. - * - */ -static void push_criterion(void *unused_criteria, const char *type, - const char *value) { - struct criterion *criterion = malloc(sizeof(struct criterion)); - criterion->type = strdup(type); - criterion->value = strdup(value); - TAILQ_INSERT_TAIL(&criteria, criterion, criteria); + return 0; } -/* - * Clears the criteria linked list. - * Accepts a pointer as first argument, since it is 'call'ed by the parser. - * - */ -static void clear_criteria(void *unused_criteria) { - struct criterion *criterion; - while (!TAILQ_EMPTY(&criteria)) { - criterion = TAILQ_FIRST(&criteria); - free(criterion->type); - free(criterion->value); - TAILQ_REMOVE(&criteria, criterion, criteria); - free(criterion); +// TODO move to a common util +static void clear_stack(void) { + for (int c = 0; c < 10; c++) { + if (stack[c].type == STACK_STR) + free(stack[c].val.str); + stack[c].identifier = NULL; + stack[c].val.str = NULL; + stack[c].val.num = 0; } } -#endif /******************************************************************************* * The parser itself. @@ -316,6 +310,29 @@ CommandResult *parse_command(const char *input, yajl_gen gen) { continue; } + if (strcmp(token->name, "number") == 0) { + /* Handle numbers. We only accept decimal numbers for now. */ + char *end = NULL; + errno = 0; + long int num = strtol(walk, &end, 10); + if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) || + (errno != 0 && num == 0)) + continue; + + /* No valid numbers found */ + if (end == walk) + continue; + + if (token->identifier != NULL) + push_long(token->identifier, num); + + /* Set walk to the first non-number character */ + walk = end; + next_state(token); + token_handled = true; + break; + } + if (strcmp(token->name, "string") == 0 || strcmp(token->name, "word") == 0) { char *str = parse_string(&walk, (token->name[0] != 's'));