diff --git a/generate-command-parser.pl b/generate-command-parser.pl index e8c240c2..42f2da15 100755 --- a/generate-command-parser.pl +++ b/generate-command-parser.pl @@ -147,11 +147,12 @@ for my $state (@keys) { $next_state ||= 'INITIAL'; my $fmt = $cmd; # Replace the references to identified literals (like $workspace) with - # calls to get_string(). Also replaces state names (like FOR_WINDOW) - # with their ID (useful for cfg_criteria_init(FOR_WINDOW) e.g.). + # calls to parser_get_string(). Also replaces state names (like + # FOR_WINDOW) with their ID (useful for cfg_criteria_init(FOR_WINDOW) + # e.g.). $cmd =~ s/$_/$statenum{$_}/g for @keys; - $cmd =~ s/\$([a-z_]+)/get_string(stack, "$1")/g; - $cmd =~ s/\&([a-z_]+)/get_long(stack, "$1")/g; + $cmd =~ s/\$([a-z_]+)/parser_get_string(stack, "$1")/g; + $cmd =~ s/\&([a-z_]+)/parser_get_long(stack, "$1")/g; # For debugging/testing, we print the call using printf() and thus need # to generate a format string. The format uses %d for s, # literal numbers or state IDs and %s for NULL, s and literal diff --git a/include/config_directives.h b/include/config_directives.h index 0141b833..53a53c5e 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -43,7 +43,7 @@ CFGFUN(include, const char *pattern); CFGFUN(font, const char *font); CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command); CFGFUN(for_window, const char *command); -CFGFUN(gaps, const char *workspace, const char *type, const long value); +CFGFUN(gaps, const char *workspace, const char *scope, const long value); CFGFUN(smart_borders, const char *enable); CFGFUN(smart_gaps, const char *enable); CFGFUN(floating_minimum_size, const long width, const long height); @@ -79,7 +79,7 @@ CFGFUN(default_border, const char *windowtype, const char *border, const long wi CFGFUN(workspace, const char *workspace, const char *output); CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *exclude_titlebar, const char *command); -CFGFUN(enter_mode, const char *pango_markup, const char *mode); +CFGFUN(enter_mode, const char *pango_markup, const char *modename); CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *exclude_titlebar, const char *command); CFGFUN(bar_font, const char *font); @@ -103,7 +103,7 @@ CFGFUN(bar_i3bar_command, const char *i3bar_command); CFGFUN(bar_color, const char *colorclass, const char *border, const char *background, const char *text); CFGFUN(bar_socket_path, const char *socket_path); CFGFUN(bar_tray_output, const char *output); -CFGFUN(bar_tray_padding, const long spacing_px); +CFGFUN(bar_tray_padding, const long padding_px); CFGFUN(bar_color_single, const char *colorclass, const char *color); CFGFUN(bar_status_command, const char *command); CFGFUN(bar_workspace_command, const char *command); diff --git a/include/config_parser.h b/include/config_parser.h index 0330c89f..9bc924f2 100644 --- a/include/config_parser.h +++ b/include/config_parser.h @@ -13,26 +13,11 @@ #include +#include "parser_util.h" + SLIST_HEAD(variables_head, Variable); extern pid_t config_error_nagbar_pid; -struct stack_entry { - /* Just a pointer, not dynamically allocated. */ - const char *identifier; - enum { - STACK_STR = 0, - STACK_LONG = 1, - } type; - union { - char *str; - long num; - } val; -}; - -struct stack { - struct stack_entry stack[10]; -}; - struct parser_ctx { bool use_nagbar; diff --git a/include/parser_util.h b/include/parser_util.h new file mode 100644 index 00000000..5f7f3d68 --- /dev/null +++ b/include/parser_util.h @@ -0,0 +1,59 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved tiling window manager + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) + * + * parser_util.h: utility functions for the config and commands parser + * + */ +#pragma once + +struct stack_entry { + /* Just a pointer, not dynamically allocated. */ + const char *identifier; + enum { + STACK_STR = 0, + STACK_LONG = 1, + } type; + union { + char *str; + long num; + } val; +}; + +struct stack { + struct stack_entry stack[10]; +}; + +/** + * Pushes a string (identified by 'identifier') on the stack. + * If a string with the same identifier is already on the stack, the new + * string will be appended, separated by a comma. + * + */ +void parser_push_string(struct stack *stack, const char *identifier, const char *str); + +/** + * Pushes a long (identified by 'identifier') on the stack. + * + */ +void parser_push_long(struct stack *stack, const char *identifier, long num); + +/** + * Returns the string with the given identifier. + * + */ +const char *parser_get_string(const struct stack *stack, const char *identifier); + +/** + * Returns the long with the given identifier. + * + */ +long parser_get_long(const struct stack *stack, const char *identifier); + +/** + * Clears the stack. + * + */ +void parser_clear_stack(struct stack *stack); diff --git a/meson.build b/meson.build index 04c2f1b3..c629413d 100644 --- a/meson.build +++ b/meson.build @@ -403,6 +403,7 @@ i3srcs = [ 'src/match.c', 'src/move.c', 'src/output.c', + 'src/parser_util.c', 'src/randr.c', 'src/regex.c', 'src/render.c', @@ -685,6 +686,7 @@ executable( 'test.commands_parser', [ 'src/commands_parser.c', + 'src/parser_util.c', command_parser, ], include_directories: inc, @@ -697,6 +699,7 @@ executable( 'test.config_parser', [ 'src/config_parser.c', + 'src/parser_util.c', config_parser, ], include_directories: inc, diff --git a/src/commands_parser.c b/src/commands_parser.c index 469fad5e..46bd4029 100644 --- a/src/commands_parser.c +++ b/src/commands_parser.c @@ -24,6 +24,7 @@ * */ #include "all.h" +#include "parser_util.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) @@ -56,93 +57,6 @@ typedef struct tokenptr { #include "GENERATED_command_tokens.h" -/* - * Pushes a string (identified by 'identifier') on the stack. We simply use a - * single array, since the number of entries we have to store is very small. - * - */ -static void push_string(struct stack *stack, const char *identifier, char *str) { - for (int c = 0; c < 10; c++) { - if (stack->stack[c].identifier != NULL) { - continue; - } - /* Found a free slot, let’s store it here. */ - stack->stack[c].identifier = identifier; - stack->stack[c].val.str = str; - stack->stack[c].type = STACK_STR; - 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(EXIT_FAILURE); -} - -// TODO move to a common util -static void push_long(struct stack *stack, const char *identifier, long num) { - for (int c = 0; c < 10; c++) { - if (stack->stack[c].identifier != NULL) { - continue; - } - - stack->stack[c].identifier = identifier; - stack->stack[c].val.num = num; - stack->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(EXIT_FAILURE); -} - -// TODO move to a common util -static const char *get_string(struct stack *stack, const char *identifier) { - for (int c = 0; c < 10; c++) { - if (stack->stack[c].identifier == NULL) { - break; - } - if (strcmp(identifier, stack->stack[c].identifier) == 0) { - return stack->stack[c].val.str; - } - } - return NULL; -} - -// TODO move to a common util -static long get_long(struct stack *stack, const char *identifier) { - for (int c = 0; c < 10; c++) { - if (stack->stack[c].identifier == NULL) { - break; - } - if (strcmp(identifier, stack->stack[c].identifier) == 0) { - return stack->stack[c].val.num; - } - } - - return 0; -} - -// TODO move to a common util -static void clear_stack(struct stack *stack) { - for (int c = 0; c < 10; c++) { - if (stack->stack[c].type == STACK_STR) { - free(stack->stack[c].val.str); - } - stack->stack[c].identifier = NULL; - stack->stack[c].val.str = NULL; - stack->stack[c].val.num = 0; - } -} - /******************************************************************************* * The parser itself. ******************************************************************************/ @@ -171,13 +85,13 @@ static void next_state(const cmdp_token *token) { if (subcommand_output.needs_tree_render) { command_output.needs_tree_render = true; } - clear_stack(&stack); + parser_clear_stack(&stack); return; } state = token->next_state; if (state == INITIAL) { - clear_stack(&stack); + parser_clear_stack(&stack); } } @@ -187,7 +101,7 @@ static void next_state(const cmdp_token *token) { * workspace commands. * */ -char *parse_string(const char **walk, bool as_word) { +char *parse_string(const char **walk, const bool as_word) { const char *beginning = *walk; /* Handle quoted strings (or words). */ if (**walk == '"') { @@ -284,7 +198,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client walk++; } - cmdp_token_ptr *ptr = &(tokens[state]); + const cmdp_token_ptr *ptr = &(tokens[state]); token_handled = false; for (c = 0; c < ptr->n; c++) { token = &(ptr->array[c]); @@ -293,7 +207,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client if (token->name[0] == '\'') { if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) { if (token->identifier != NULL) { - push_string(&stack, token->identifier, sstrdup(token->name + 1)); + parser_push_string(&stack, token->identifier, token->name + 1); } walk += strlen(token->name) - 1; next_state(token); @@ -307,7 +221,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client /* Handle numbers. We only accept decimal numbers for now. */ char *end = NULL; errno = 0; - long int num = strtol(walk, &end, 10); + const long int num = strtol(walk, &end, 10); if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) || (errno != 0 && num == 0)) { continue; @@ -319,7 +233,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client } if (token->identifier != NULL) { - push_long(&stack, token->identifier, num); + parser_push_long(&stack, token->identifier, num); } /* Set walk to the first non-number character */ @@ -334,8 +248,9 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client char *str = parse_string(&walk, (token->name[0] != 's')); if (str != NULL) { if (token->identifier) { - push_string(&stack, token->identifier, str); + parser_push_string(&stack, token->identifier, str); } + free(str); /* If we are at the end of a quoted string, skip the ending * double quote. */ if (*walk == '"') { @@ -441,7 +356,7 @@ CommandResult *parse_command(const char *input, yajl_gen gen, ipc_client *client y(map_close); free(position); - clear_stack(&stack); + parser_clear_stack(&stack); break; } } diff --git a/src/config_directives.c b/src/config_directives.c index 23086065..d8245e80 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -52,8 +52,7 @@ CFGFUN(include, const char *pattern) { file->path = sstrdup(resolved_path); TAILQ_INSERT_TAIL(&included_files, file, files); - struct stack stack; - memset(&stack, '\0', sizeof(struct stack)); + struct stack stack = {0}; struct parser_ctx ctx = { .use_nagbar = result->ctx->use_nagbar, .stack = &stack, @@ -79,7 +78,6 @@ CFGFUN(include, const char *pattern) { default: /* missing case statement */ assert(false); - break; } result->ctx->variables = ctx.variables; /* In case head was modified */ } @@ -97,7 +95,7 @@ static int criteria_next_state; * commands.c for matching target windows of a command. * */ -CFGFUN(criteria_init, int _state) { +CFGFUN(criteria_init, const int _state) { criteria_next_state = _state; DLOG("Initializing criteria, current_match = %p, state = %d\n", current_match, _state); @@ -244,7 +242,7 @@ CFGFUN(for_window, const char *command) { TAILQ_INSERT_TAIL(&assignments, assignment, assignments); } -static void apply_gaps(gaps_t *gaps, gaps_mask_t mask, int value) { +static void apply_gaps(gaps_t *gaps, const gaps_mask_t mask, const int value) { if (gaps == NULL) { return; } @@ -316,8 +314,8 @@ static gaps_mask_t gaps_scope_to_mask(const char *scope) { } CFGFUN(gaps, const char *workspace, const char *scope, const long value) { - int pixels = logical_px(value); - gaps_mask_t mask = gaps_scope_to_mask(scope); + const int pixels = logical_px(value); + const gaps_mask_t mask = gaps_scope_to_mask(scope); if (workspace == NULL) { apply_gaps(&config.gaps, mask, pixels); @@ -536,9 +534,9 @@ CFGFUN(workspace, const char *workspace, const char *output) { struct Workspace_Assignment *assignment; /* When a new workspace line is encountered, for the first output word, - * $workspace from the config.spec is non-NULL. Afterwards, the parser calls - * clear_stack() because of the call line. Thus, we have to preserve the - * workspace string. */ + * $workspace from the config.spec is non-NULL. Afterward, the parser calls + * parser_clear_stack() because of the call line. Thus, we have to preserve + * the workspace string. */ if (workspace) { FREE(current_workspace); @@ -654,7 +652,7 @@ CFGFUN(assign_output, const char *output) { TAILQ_INSERT_TAIL(&assignments, assignment, assignments); } -CFGFUN(assign, const char *workspace, bool is_number) { +CFGFUN(assign, const char *workspace, const bool is_number) { if (current_match->error != NULL) { ELOG("match has error: %s\n", current_match->error); return; @@ -746,7 +744,7 @@ CFGFUN(bar_id, const char *bar_id) { } CFGFUN(bar_output, const char *output) { - int new_outputs = current_bar->num_outputs + 1; + const int new_outputs = current_bar->num_outputs + 1; current_bar->outputs = srealloc(current_bar->outputs, sizeof(char *) * new_outputs); current_bar->outputs[current_bar->num_outputs] = sstrdup(output); current_bar->num_outputs = new_outputs; @@ -810,7 +808,7 @@ static void bar_configure_binding(const char *button, const char *release, const return; } - int input_code = atoi(button + strlen("button")); + const int input_code = atoi(button + strlen("button")); if (input_code < 1) { ELOG("Button \"%s\" does not seem to be in format 'buttonX'.\n", button); return; diff --git a/src/config_parser.c b/src/config_parser.c index f1ed7daf..c417aae4 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -72,96 +72,6 @@ typedef struct tokenptr { #include "GENERATED_config_tokens.h" -/* - * Pushes a string (identified by 'identifier') on the stack. We simply use a - * single array, since the number of entries we have to store is very small. - * - */ -static void push_string(struct stack *ctx, const char *identifier, const char *str) { - for (int c = 0; c < 10; c++) { - if (ctx->stack[c].identifier != NULL && - strcmp(ctx->stack[c].identifier, identifier) != 0) { - continue; - } - if (ctx->stack[c].identifier == NULL) { - /* Found a free slot, let’s store it here. */ - ctx->stack[c].identifier = identifier; - ctx->stack[c].val.str = sstrdup(str); - ctx->stack[c].type = STACK_STR; - } else { - /* Append the value. */ - char *prev = ctx->stack[c].val.str; - sasprintf(&(ctx->stack[c].val.str), "%s,%s", prev, str); - free(prev); - } - 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: config_parser stack full. This means either a bug " - "in the code, or a new command which contains more than " - "10 identified tokens.\n"); - exit(EXIT_FAILURE); -} - -static void push_long(struct stack *ctx, const char *identifier, long num) { - for (int c = 0; c < 10; c++) { - if (ctx->stack[c].identifier != NULL) { - continue; - } - /* Found a free slot, let’s store it here. */ - ctx->stack[c].identifier = identifier; - ctx->stack[c].val.num = num; - ctx->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: config_parser stack full. This means either a bug " - "in the code, or a new command which contains more than " - "10 identified tokens.\n"); - exit(EXIT_FAILURE); -} - -static const char *get_string(const struct stack *ctx, const char *identifier) { - for (int c = 0; c < 10; c++) { - if (ctx->stack[c].identifier == NULL) { - break; - } - if (strcmp(identifier, ctx->stack[c].identifier) == 0) { - return ctx->stack[c].val.str; - } - } - return NULL; -} - -static long get_long(const struct stack *ctx, const char *identifier) { - for (int c = 0; c < 10; c++) { - if (ctx->stack[c].identifier == NULL) { - break; - } - if (strcmp(identifier, ctx->stack[c].identifier) == 0) { - return ctx->stack[c].val.num; - } - } - return 0; -} - -static void clear_stack(struct stack *ctx) { - for (int c = 0; c < 10; c++) { - if (ctx->stack[c].type == STACK_STR) { - free(ctx->stack[c].val.str); - } - ctx->stack[c].identifier = NULL; - ctx->stack[c].val.str = NULL; - ctx->stack[c].val.num = 0; - } -} - /******************************************************************************* * The parser itself. ******************************************************************************/ @@ -180,12 +90,12 @@ static void next_state(const cmdp_token *token, struct parser_ctx *ctx) { ctx->has_errors = true; } _next_state = subcommand_output.next_state; - clear_stack(ctx->stack); + parser_clear_stack(ctx->stack); } ctx->state = _next_state; if (ctx->state == INITIAL) { - clear_stack(ctx->stack); + parser_clear_stack(ctx->stack); } /* See if we are jumping back to a state in which we were in previously @@ -236,7 +146,7 @@ static void parse_config(struct parser_ctx *ctx, const char *input, struct conte const char *dumpwalk = input; int linecnt = 1; while (*dumpwalk != '\0') { - char *next_nl = strchr(dumpwalk, '\n'); + const char *next_nl = strchr(dumpwalk, '\n'); if (next_nl != NULL) { DLOG("CONFIG(line %3d): %.*s\n", linecnt, (int)(next_nl - dumpwalk), dumpwalk); dumpwalk = next_nl + 1; @@ -275,7 +185,7 @@ static void parse_config(struct parser_ctx *ctx, const char *input, struct conte walk++; } - cmdp_token_ptr *ptr = &(tokens[ctx->state]); + const cmdp_token_ptr *ptr = &(tokens[ctx->state]); token_handled = false; for (c = 0; c < ptr->n; c++) { token = &(ptr->array[c]); @@ -284,7 +194,7 @@ static void parse_config(struct parser_ctx *ctx, const char *input, struct conte if (token->name[0] == '\'') { if (strncasecmp(walk, token->name + 1, strlen(token->name) - 1) == 0) { if (token->identifier != NULL) { - push_string(ctx->stack, token->identifier, token->name + 1); + parser_push_string(ctx->stack, token->identifier, token->name + 1); } walk += strlen(token->name) - 1; next_state(token, ctx); @@ -298,7 +208,7 @@ static void parse_config(struct parser_ctx *ctx, const char *input, struct conte /* Handle numbers. We only accept decimal numbers for now. */ char *end = NULL; errno = 0; - long int num = strtol(walk, &end, 10); + const long int num = strtol(walk, &end, 10); if ((errno == ERANGE && (num == LONG_MIN || num == LONG_MAX)) || (errno != 0 && num == 0)) { continue; @@ -310,7 +220,7 @@ static void parse_config(struct parser_ctx *ctx, const char *input, struct conte } if (token->identifier != NULL) { - push_long(ctx->stack, token->identifier, num); + parser_push_long(ctx->stack, token->identifier, num); } /* Set walk to the first non-number character */ @@ -363,7 +273,7 @@ static void parse_config(struct parser_ctx *ctx, const char *input, struct conte str[outpos] = beginning[inpos]; } if (token->identifier) { - push_string(ctx->stack, token->identifier, str); + parser_push_string(ctx->stack, token->identifier, str); } free(str); /* If we are at the end of a quoted string, skip the ending @@ -488,7 +398,7 @@ static void parse_config(struct parser_ctx *ctx, const char *input, struct conte free(error_copy); /* Print context lines *after* the error, if any. */ for (int i = 0; i < 2; i++) { - char *error_line_end = strchr(error_line, '\n'); + const char *error_line_end = strchr(error_line, '\n'); if (error_line_end != NULL && *(error_line_end + 1) != '\0') { error_line = error_line_end + 1; error_copy = single_line(error_line); @@ -506,14 +416,14 @@ static void parse_config(struct parser_ctx *ctx, const char *input, struct conte free(position); free(errormessage); - clear_stack(ctx->stack); + parser_clear_stack(ctx->stack); /* To figure out in which state to go (e.g. MODE or INITIAL), * we find the nearest state which contains an token * and follow that one. */ bool error_token_found = false; for (int i = ctx->statelist_idx - 1; (i >= 0) && !error_token_found; i--) { - cmdp_token_ptr *errptr = &(tokens[ctx->statelist[i]]); + const cmdp_token_ptr *errptr = &(tokens[ctx->statelist[i]]); for (int j = 0; j < errptr->n; j++) { if (strcmp(errptr->array[j].name, "error") != 0) { continue; @@ -594,7 +504,7 @@ int main(int argc, char *argv[]) { /** * Launch nagbar to indicate errors in the configuration file. */ -void start_config_error_nagbar(const char *configpath, bool has_errors) { +void start_config_error_nagbar(const char *configpath, const bool has_errors) { char *editaction, *pageraction; sasprintf(&editaction, "i3-sensible-editor \"%s\" && i3-msg reload\n", configpath); sasprintf(&pageraction, "i3-sensible-pager \"%s\"\n", errorfilename); @@ -687,9 +597,8 @@ static char *get_resource(const char *name) { * */ void free_variables(struct parser_ctx *ctx) { - struct Variable *current; while (!SLIST_EMPTY(&(ctx->variables))) { - current = SLIST_FIRST(&(ctx->variables)); + struct Variable *current = SLIST_FIRST(&(ctx->variables)); FREE(current->key); FREE(current->value); SLIST_REMOVE_HEAD(&(ctx->variables), variables); @@ -852,14 +761,14 @@ static parse_file_result_t parse_file_inner(struct parser_ctx *ctx, const char * * 'extra' is negative) */ char *bufcopy = sstrdup(buf); SLIST_FOREACH (current, &(ctx->variables), variables) { - int extra = (strlen(current->value) - strlen(current->key)); + const int extra = (strlen(current->value) - strlen(current->key)); for (char *next = bufcopy; next < (bufcopy + stbuf.st_size) && (next = strcasestr(next, current->key)) != NULL;) { /* We need to invalidate variables completely (otherwise we may count * the same variable more than once, thus causing buffer overflow or * allocation failure) with spaces (variable names cannot contain spaces) */ - char *end = next + strlen(current->key); + const char *end = next + strlen(current->key); while (next < end) { *next++ = ' '; } @@ -870,7 +779,7 @@ static parse_file_result_t parse_file_inner(struct parser_ctx *ctx, const char * /* Then, allocate a new buffer and copy the file over to the new one, * but replace occurrences of our variables */ - char *walk = buf; + const char *walk = buf; char *new = scalloc(stbuf.st_size + extra_bytes + 1, 1); char *destwalk = new; while (walk < (buf + stbuf.st_size)) { @@ -878,7 +787,7 @@ static parse_file_result_t parse_file_inner(struct parser_ctx *ctx, const char * SLIST_FOREACH (current, &(ctx->variables), variables) { current->next_match = strcasestr(walk, current->key); } - struct Variable *nearest = NULL; + const struct Variable *nearest = NULL; int distance = stbuf.st_size; SLIST_FOREACH (current, &(ctx->variables), variables) { if (current->next_match == NULL) { diff --git a/src/parser_util.c b/src/parser_util.c new file mode 100644 index 00000000..a0fd1e10 --- /dev/null +++ b/src/parser_util.c @@ -0,0 +1,101 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * i3 - an improved tiling window manager + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) + * + * parser_util.c: utility functions for the config and commands parser + * + */ +#include "all.h" +#include "parser_util.h" + +/* + * Pushes a string (identified by 'identifier') on the stack. We simply use a + * single array, since the number of entries we have to store is very small. + * + */ +void parser_push_string(struct stack *stack, const char *identifier, const char *str) { + for (int c = 0; c < 10; c++) { + if (stack->stack[c].identifier != NULL && + strcmp(stack->stack[c].identifier, identifier) != 0) { + continue; + } + if (stack->stack[c].identifier == NULL) { + /* Found a free slot, let's store it here. */ + stack->stack[c].identifier = identifier; + stack->stack[c].val.str = sstrdup(str); + stack->stack[c].type = STACK_STR; + } else { + /* Append the value. */ + char *prev = stack->stack[c].val.str; + sasprintf(&stack->stack[c].val.str, "%s,%s", prev, str); + free(prev); + } + 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: parser stack full. This means either a bug " + "in the code, or a new command which contains more than " + "10 identified tokens.\n"); + exit(EXIT_FAILURE); +} + +void parser_push_long(struct stack *stack, const char *identifier, const long num) { + for (int c = 0; c < 10; c++) { + if (stack->stack[c].identifier != NULL) { + continue; + } + /* Found a free slot, let's store it here. */ + stack->stack[c].identifier = identifier; + stack->stack[c].val.num = num; + stack->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: parser stack full. This means either a bug " + "in the code, or a new command which contains more than " + "10 identified tokens.\n"); + exit(EXIT_FAILURE); +} + +const char *parser_get_string(const struct stack *stack, const char *identifier) { + for (int c = 0; c < 10; c++) { + if (stack->stack[c].identifier == NULL) { + break; + } + if (strcmp(identifier, stack->stack[c].identifier) == 0) { + return stack->stack[c].val.str; + } + } + return NULL; +} + +long parser_get_long(const struct stack *stack, const char *identifier) { + for (int c = 0; c < 10; c++) { + if (stack->stack[c].identifier == NULL) { + break; + } + if (strcmp(identifier, stack->stack[c].identifier) == 0) { + return stack->stack[c].val.num; + } + } + return 0; +} + +void parser_clear_stack(struct stack *stack) { + for (int c = 0; c < 10; c++) { + if (stack->stack[c].type == STACK_STR) { + free(stack->stack[c].val.str); + } + stack->stack[c].identifier = NULL; + stack->stack[c].val.str = NULL; + stack->stack[c].val.num = 0; + } +}