X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fconfig_parser.c;h=eef03cae238e0cf7455fd64ca1b18658e47d977f;hb=42515308e72e4ea6f6b49508c1ba286263dded64;hp=cb21dae7c9c058ad601eaf445470c1f67223a0ec;hpb=cd0cd0c3d22d7acba240ca10d8594621cc67ee34;p=i3%2Fi3 diff --git a/src/config_parser.c b/src/config_parser.c index cb21dae7..eef03cae 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -39,8 +39,8 @@ #include "all.h" // Macros to make the YAJL API a bit easier to use. -#define y(x, ...) yajl_gen_ ## x (command_output.json_gen, ##__VA_ARGS__) -#define ystr(str) yajl_gen_string(command_output.json_gen, (unsigned char*)str, strlen(str)) +#define y(x, ...) yajl_gen_##x(command_output.json_gen, ##__VA_ARGS__) +#define ystr(str) yajl_gen_string(command_output.json_gen, (unsigned char *)str, strlen(str)) #ifndef TEST_PARSER pid_t config_error_nagbar_pid = -1; @@ -146,7 +146,6 @@ static void push_long(const char *identifier, long num) { "in the code, or a new command which contains more than " "10 identified tokens.\n"); exit(1); - } static const char *get_string(const char *identifier) { @@ -232,26 +231,25 @@ static void clear_criteria(void *unused_criteria) { static cmdp_state state; static Match current_match; -static struct ConfigResult subcommand_output; -static struct ConfigResult command_output; +static struct ConfigResultIR subcommand_output; +static struct ConfigResultIR command_output; /* A list which contains the states that lead to the current state, e.g. * INITIAL, WORKSPACE_LAYOUT. * When jumping back to INITIAL, statelist_idx will simply be set to 1 * (likewise for other states, e.g. MODE or BAR). * This list is used to process the nearest error token. */ -static cmdp_state statelist[10] = { INITIAL }; +static cmdp_state statelist[10] = {INITIAL}; /* NB: statelist_idx points to where the next entry will be inserted */ static int statelist_idx = 1; #include "GENERATED_config_call.h" - static void next_state(const cmdp_token *token) { cmdp_state _next_state = token->next_state; - //printf("token = name %s identifier %s\n", token->name, token->identifier); - //printf("next_state = %d\n", token->next_state); + //printf("token = name %s identifier %s\n", token->name, token->identifier); + //printf("next_state = %d\n", token->next_state); if (token->next_state == __CALL) { subcommand_output.json_gen = command_output.json_gen; GENERATED_call(token->extra.call_identifier, &subcommand_output); @@ -269,7 +267,7 @@ static void next_state(const cmdp_token *token) { for (int i = 0; i < statelist_idx; i++) { if (statelist[i] != _next_state) continue; - statelist_idx = i+1; + statelist_idx = i + 1; return; } @@ -304,7 +302,7 @@ static char *single_line(const char *start) { return result; } -struct ConfigResult *parse_config(const char *input, struct context *context) { +struct ConfigResultIR *parse_config(const char *input, struct context *context) { /* Dump the entire config file into the debug log. We cannot just use * DLOG("%s", input); because one log message must not exceed 4 KiB. */ const char *dumpwalk = input; @@ -323,12 +321,8 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { state = INITIAL; statelist_idx = 1; -/* A YAJL JSON generator used for formatting replies. */ -#if YAJL_MAJOR >= 2 + /* A YAJL JSON generator used for formatting replies. */ command_output.json_gen = yajl_gen_alloc(NULL); -#else - command_output.json_gen = yajl_gen_alloc(NULL, NULL); -#endif y(array_open); @@ -339,7 +333,7 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { bool token_handled; linecnt = 1; - // TODO: make this testable +// TODO: make this testable #ifndef TEST_PARSER cfg_criteria_init(¤t_match, &subcommand_output, INITIAL); #endif @@ -352,7 +346,7 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { while ((*walk == ' ' || *walk == '\t') && *walk != '\0') walk++; - //printf("remaining input: %s\n", walk); + //printf("remaining input: %s\n", walk); cmdp_token_ptr *ptr = &(tokens[state]); token_handled = false; @@ -402,7 +396,7 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { if (*walk == '"') { beginning++; walk++; - while (*walk != '\0' && (*walk != '"' || *(walk-1) == '\\')) + while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\')) walk++; } else { if (token->name[0] == 's') { @@ -414,22 +408,22 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { * semicolon (;). */ while (*walk != ' ' && *walk != '\t' && *walk != ']' && *walk != ',' && - *walk != ';' && *walk != '\r' && + *walk != ';' && *walk != '\r' && *walk != '\n' && *walk != '\0') walk++; } } if (walk != beginning) { - char *str = scalloc(walk-beginning + 1); + char *str = scalloc(walk - beginning + 1); /* We copy manually to handle escaping of characters. */ int inpos, outpos; for (inpos = 0, outpos = 0; - inpos < (walk-beginning); + inpos < (walk - beginning); inpos++, outpos++) { /* We only handle escaped double quotes to not break * backwards compatibility with people using \w in * regular expressions etc. */ - if (beginning[inpos] == '\\' && beginning[inpos+1] == '"') + if (beginning[inpos] == '\\' && beginning[inpos + 1] == '"') inpos++; str[outpos] = beginning[inpos]; } @@ -447,13 +441,13 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { } if (strcmp(token->name, "line") == 0) { - while (*walk != '\0' && *walk != '\n' && *walk != '\r') - walk++; - next_state(token); - token_handled = true; - linecnt++; - walk++; - break; + while (*walk != '\0' && *walk != '\n' && *walk != '\r') + walk++; + next_state(token); + token_handled = true; + linecnt++; + walk++; + break; } if (strcmp(token->name, "end") == 0) { @@ -461,19 +455,19 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { if (*walk == '\0' || *walk == '\n' || *walk == '\r') { next_state(token); token_handled = true; - /* To make sure we start with an appropriate matching +/* To make sure we start with an appropriate matching * datastructure for commands which do *not* specify any * criteria, we re-initialize the criteria system after * every command. */ - // TODO: make this testable +// TODO: make this testable #ifndef TEST_PARSER cfg_criteria_init(¤t_match, &subcommand_output, INITIAL); #endif linecnt++; walk++; break; - } - } + } + } } if (!token_handled) { @@ -520,7 +514,6 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { possible_tokens); free(possible_tokens); - /* Go back to the beginning of the line */ const char *error_line = start_of_line(walk, input); @@ -540,10 +533,10 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { /* Print context lines *before* the error, if any. */ if (linecnt > 1) { - const char *context_p1_start = start_of_line(error_line-2, input); + const char *context_p1_start = start_of_line(error_line - 2, input); char *context_p1_line = single_line(context_p1_start); if (linecnt > 2) { - const char *context_p2_start = start_of_line(context_p1_start-2, input); + const char *context_p2_start = start_of_line(context_p1_start - 2, input); char *context_p2_line = single_line(context_p2_start); ELOG("CONFIG: Line %3d: %s\n", linecnt - 2, context_p2_line); free(context_p2_line); @@ -596,7 +589,7 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { * we find the nearest state which contains an token * and follow that one. */ bool error_token_found = false; - for (int i = statelist_idx-1; (i >= 0) && !error_token_found; i--) { + for (int i = statelist_idx - 1; (i >= 0) && !error_token_found; i--) { cmdp_token_ptr *errptr = &(tokens[statelist[i]]); for (int j = 0; j < errptr->n; j++) { if (strcmp(errptr->array[j].name, "error") != 0) @@ -691,7 +684,7 @@ static int detect_version(char *buf) { strncasecmp(line, "force_focus_wrapping", strlen("force_focus_wrapping")) == 0 || strncasecmp(line, "# i3 config file (v4)", strlen("# i3 config file (v4)")) == 0 || strncasecmp(line, "workspace_layout", strlen("workspace_layout")) == 0) { - printf("deciding for version 4 due to this line: %.*s\n", (int)(walk-line), line); + LOG("deciding for version 4 due to this line: %.*s\n", (int)(walk - line), line); return 4; } @@ -723,12 +716,12 @@ static int detect_version(char *buf) { strncasecmp(bind, "border borderless", strlen("border borderless")) == 0 || strncasecmp(bind, "--no-startup-id", strlen("--no-startup-id")) == 0 || strncasecmp(bind, "bar", strlen("bar")) == 0) { - printf("deciding for version 4 due to this line: %.*s\n", (int)(walk-line), line); + LOG("deciding for version 4 due to this line: %.*s\n", (int)(walk - line), line); return 4; } } -next: + next: /* advance to the next line */ walk++; line = walk; @@ -774,8 +767,7 @@ static char *migrate_config(char *input, off_t size) { static char *argv[] = { NULL, /* will be replaced by the executable path */ - NULL - }; + NULL}; exec_i3_utility("i3-migrate-config-to-v4", argv); } @@ -786,14 +778,9 @@ static char *migrate_config(char *input, off_t size) { /* write the whole config file to the pipe, the script will read everything * immediately */ - int written = 0; - int ret; - while (written < size) { - if ((ret = write(writepipe[1], input + written, size - written)) < 0) { - warn("Could not write to pipe"); - return NULL; - } - written += ret; + if (writeall(writepipe[1], input, size) == -1) { + warn("Could not write to pipe"); + return NULL; } close(writepipe[1]); @@ -803,7 +790,7 @@ static char *migrate_config(char *input, off_t size) { /* read the script’s output */ int conv_size = 65535; char *converted = malloc(conv_size); - int read_bytes = 0; + int read_bytes = 0, ret; do { if (read_bytes == conv_size) { conv_size += 65535; @@ -831,11 +818,11 @@ static char *migrate_config(char *input, off_t size) { fprintf(stderr, "Migration process exit code was != 0\n"); if (returncode == 2) { fprintf(stderr, "could not start the migration script\n"); - /* TODO: script was not found. tell the user to fix his system or create a v4 config */ + /* TODO: script was not found. tell the user to fix their system or create a v4 config */ } else if (returncode == 1) { fprintf(stderr, "This already was a v4 config. Please add the following line to your config file:\n"); fprintf(stderr, "# i3 config file (v4)\n"); - /* TODO: nag the user with a message to include a hint for i3 in his config file */ + /* TODO: nag the user with a message to include a hint for i3 in their config file */ } return NULL; } @@ -843,61 +830,12 @@ static char *migrate_config(char *input, off_t size) { return converted; } -/* - * Checks for duplicate key bindings (the same keycode or keysym is configured - * more than once). If a duplicate binding is found, a message is printed to - * stderr and the has_errors variable is set to true, which will start - * i3-nagbar. - * - */ -static void check_for_duplicate_bindings(struct context *context) { - Binding *bind, *current; - TAILQ_FOREACH(current, bindings, bindings) { - TAILQ_FOREACH(bind, bindings, bindings) { - /* Abort when we reach the current keybinding, only check the - * bindings before */ - if (bind == current) - break; - - /* Check if one is using keysym while the other is using bindsym. - * If so, skip. */ - /* XXX: It should be checked at a later place (when translating the - * keysym to keycodes) if there are any duplicates */ - if ((bind->symbol == NULL && current->symbol != NULL) || - (bind->symbol != NULL && current->symbol == NULL)) - continue; - - /* If bind is NULL, current has to be NULL, too (see above). - * If the keycodes differ, it can't be a duplicate. */ - if (bind->symbol != NULL && - strcasecmp(bind->symbol, current->symbol) != 0) - continue; - - /* Check if the keycodes or modifiers are different. If so, they - * can't be duplicate */ - if (bind->keycode != current->keycode || - bind->mods != current->mods || - bind->release != current->release) - continue; - - context->has_errors = true; - if (current->keycode != 0) { - ELOG("Duplicate keybinding in config file:\n modmask %d with keycode %d, command \"%s\"\n", - current->mods, current->keycode, current->command); - } else { - ELOG("Duplicate keybinding in config file:\n modmask %d with keysym %s, command \"%s\"\n", - current->mods, current->symbol, current->command); - } - } - } -} - /* * Parses the given file by first replacing the variables, then calling * parse_config and possibly launching i3-nagbar. * */ -void parse_file(const char *f) { +bool parse_file(const char *f, bool use_nagbar) { SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables); int fd, ret, read_bytes = 0; struct stat stbuf; @@ -979,7 +917,7 @@ void parse_file(const char *f) { char *next; for (next = bufcopy; next < (bufcopy + stbuf.st_size) && - (next = strcasestr(next, current->key)) != NULL; + (next = strcasestr(next, current->key)) != NULL; next += strlen(current->key)) { *next = '_'; extra_bytes += extra; @@ -995,7 +933,7 @@ void parse_file(const char *f) { while (walk < (buf + stbuf.st_size)) { /* Find the next variable */ SLIST_FOREACH(current, &variables, variables) - current->next_match = strcasestr(walk, current->key); + current->next_match = strcasestr(walk, current->key); nearest = NULL; int distance = stbuf.st_size; SLIST_FOREACH(current, &variables, variables) { @@ -1040,31 +978,30 @@ void parse_file(const char *f) { free(new); new = converted; } else { - printf("\n"); - printf("**********************************************************************\n"); - printf("ERROR: Could not convert config file. Maybe i3-migrate-config-to-v4\n"); - printf("was not correctly installed on your system?\n"); - printf("**********************************************************************\n"); - printf("\n"); + LOG("\n"); + LOG("**********************************************************************\n"); + LOG("ERROR: Could not convert config file. Maybe i3-migrate-config-to-v4\n"); + LOG("was not correctly installed on your system?\n"); + LOG("**********************************************************************\n"); + LOG("\n"); } } - context = scalloc(sizeof(struct context)); context->filename = f; - struct ConfigResult *config_output = parse_config(new, context); + struct ConfigResultIR *config_output = parse_config(new, context); yajl_gen_free(config_output->json_gen); check_for_duplicate_bindings(context); - if (context->has_errors || context->has_warnings) { + if (use_nagbar && (context->has_errors || context->has_warnings)) { ELOG("FYI: You are using i3 version " I3_VERSION "\n"); if (version == 3) ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n"); char *editaction, - *pageraction; + *pageraction; sasprintf(&editaction, "i3-sensible-editor \"%s\" && i3-msg reload\n", f); sasprintf(&pageraction, "i3-sensible-pager \"%s\"\n", errorfilename); char *argv[] = { @@ -1074,23 +1011,22 @@ void parse_file(const char *f) { "-t", (context->has_errors ? "error" : "warning"), "-m", - (context->has_errors ? - "You have an error in your i3 config file!" : - "Your config is outdated. Please fix the warnings to make sure everything works."), + (context->has_errors ? "You have an error in your i3 config file!" : "Your config is outdated. Please fix the warnings to make sure everything works."), "-b", "edit config", editaction, (errorfilename ? "-b" : NULL), (context->has_errors ? "show errors" : "show warnings"), pageraction, - NULL - }; + NULL}; start_nagbar(&config_error_nagbar_pid, argv); free(editaction); free(pageraction); } + bool has_errors = context->has_errors; + FREE(context->line_copy); free(context); free(new); @@ -1103,6 +1039,8 @@ void parse_file(const char *f) { SLIST_REMOVE_HEAD(&variables, variables); FREE(current); } + + return !has_errors; } #endif