mirror of
https://github.com/i3/i3.git
synced 2025-12-05 01:10:19 +00:00
Refactor: Extract parser stack implementation to parser_util (#6514)
The command and config parsers both used a similar stack implementation for handling tokens. This resulted in duplicated code. This commit extracts the common stack implementation (push/get/clear functions for strings and longs) into a new `parser_util` module. This resolves a long standing TODO comment.
This commit is contained in:
@@ -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 <number>s,
|
||||
# literal numbers or state IDs and %s for NULL, <string>s and literal
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -13,26 +13,11 @@
|
||||
|
||||
#include <yajl/yajl_gen.h>
|
||||
|
||||
#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;
|
||||
|
||||
|
||||
59
include/parser_util.h
Normal file
59
include/parser_util.h
Normal file
@@ -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);
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <error> 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) {
|
||||
|
||||
101
src/parser_util.c
Normal file
101
src/parser_util.c
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user