X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fconfig_parser.c;h=5b5e236303626796e4baa2d7287a085ef099e3b5;hb=47562b414389711a82db25a694b064ad39117f82;hp=bbd59a4572b08c47eedd6245e637708e9542ce7d;hpb=f682841ae1d7fd0ebde8cd5440fc0c877be37915;p=i3%2Fi3 diff --git a/src/config_parser.c b/src/config_parser.c index bbd59a45..5b5e2363 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -4,7 +4,7 @@ * vim:ts=4:sw=4:expandtab * * i3 - an improved dynamic tiling window manager - * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE) + * © 2009 Michael Stapelberg and contributors (see also: LICENSE) * * config_parser.c: hand-written parser to parse configuration directives. * @@ -35,12 +35,15 @@ #include #include #include +#include #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)) + +xcb_xrm_database_t *database = NULL; #ifndef TEST_PARSER pid_t config_error_nagbar_pid = -1; @@ -122,7 +125,7 @@ static void push_string(const char *identifier, const char *str) { /* 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 " + 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(1); @@ -142,11 +145,10 @@ static void push_long(const char *identifier, long num) { /* 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 " + 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(1); - } static const char *get_string(const char *identifier) { @@ -159,7 +161,7 @@ static const char *get_string(const char *identifier) { return NULL; } -static const long get_long(const char *identifier) { +static long get_long(const char *identifier) { for (int c = 0; c < 10; c++) { if (stack[c].identifier == NULL) break; @@ -179,79 +181,31 @@ static void clear_stack(void) { } } -// 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); -} - -/* - * 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); - } -} -#endif - /******************************************************************************* * The parser itself. ******************************************************************************/ 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 +223,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 +258,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 +277,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,20 +289,20 @@ 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 /* The "<=" operator is intentional: We also handle the terminating 0-byte * explicitly by looking for an 'end' token. */ - while ((walk - input) <= len) { + while ((size_t)(walk - input) <= len) { /* Skip whitespace before every token, newlines are relevant since they * separate configuration directives. */ 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 +352,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 +364,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, 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 +397,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 +411,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,13 +470,12 @@ 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); /* Contains the same amount of characters as 'input' has, but with * the unparseable part highlighted using ^ characters. */ - char *position = scalloc(strlen(error_line) + 1); + char *position = scalloc(strlen(error_line) + 1, 1); const char *copywalk; for (copywalk = error_line; *copywalk != '\n' && *copywalk != '\r' && *copywalk != '\0'; @@ -540,10 +489,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); @@ -585,7 +534,7 @@ struct ConfigResult *parse_config(const char *input, struct context *context) { y(map_close); /* Skip the rest of this line, but continue parsing. */ - while ((walk - input) <= len && *walk != '\n') + while ((size_t)(walk - input) <= len && *walk != '\n') walk++; free(position); @@ -596,7 +545,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 +640,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 +672,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 +723,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 +734,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]); @@ -802,12 +745,12 @@ 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; + char *converted = smalloc(conv_size); + int read_bytes = 0, ret; do { if (read_bytes == conv_size) { conv_size += 65535; - converted = realloc(converted, conv_size); + converted = srealloc(converted, conv_size); } ret = read(readpipe[0], converted + read_bytes, conv_size - read_bytes); if (ret == -1) { @@ -831,11 +774,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,53 +786,94 @@ static char *migrate_config(char *input, off_t size) { return converted; } +/** + * Launch nagbar to indicate errors in the configuration file. + */ +void start_config_error_nagbar(const char *configpath, 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); + char *argv[] = { + NULL, /* will be replaced by the executable path */ + "-f", + (config.font.pattern ? config.font.pattern : "fixed"), + "-t", + (has_errors ? "error" : "warning"), + "-m", + (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), + (has_errors ? "show errors" : "show warnings"), + pageraction, + NULL}; + + start_nagbar(&config_error_nagbar_pid, argv); + free(editaction); + free(pageraction); +} + /* - * 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. + * Inserts or updates a variable assignment depending on whether it already exists. * */ -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; +static void upsert_variable(struct variables_head *variables, char *key, char *value) { + struct Variable *current; + SLIST_FOREACH(current, variables, variables) { + if (strcmp(current->key, key) != 0) { + continue; + } - /* 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; + DLOG("Updated variable: %s = %s -> %s\n", key, current->value, value); + FREE(current->value); + current->value = sstrdup(value); + return; + } - /* 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; + DLOG("Defined new variable: %s = %s\n", key, value); + struct Variable *new = scalloc(1, sizeof(struct Variable)); + struct Variable *test = NULL, *loc = NULL; + new->key = sstrdup(key); + new->value = sstrdup(value); + /* ensure that the correct variable is matched in case of one being + * the prefix of another */ + SLIST_FOREACH(test, variables, variables) { + if (strlen(new->key) >= strlen(test->key)) + break; + loc = test; + } - /* 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; + if (loc == NULL) { + SLIST_INSERT_HEAD(variables, new, variables); + } else { + SLIST_INSERT_AFTER(loc, new, variables); + } +} - 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); - } +static char *get_resource(char *name) { + if (conn == NULL) { + return NULL; + } + + /* Load the resource database lazily. */ + if (database == NULL) { + database = xcb_xrm_database_from_default(conn); + + if (database == NULL) { + ELOG("Failed to open the resource database.\n"); + + /* Load an empty database so we don't keep trying to load the + * default database over and over again. */ + database = xcb_xrm_database_from_string(""); + + return NULL; } } + + char *resource; + xcb_xrm_resource_get_string(database, name, NULL, &resource); + return resource; } /* @@ -897,13 +881,13 @@ static void check_for_duplicate_bindings(struct context *context) { * parse_config and possibly launching i3-nagbar. * */ -void parse_file(const char *f) { - SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables); - int fd, ret, read_bytes = 0; +bool parse_file(const char *f, bool use_nagbar) { + struct variables_head variables = SLIST_HEAD_INITIALIZER(&variables); + int fd; struct stat stbuf; char *buf; FILE *fstr; - char buffer[1026], key[512], value[512]; + char buffer[4096], key[512], value[4096], *continuation = NULL; if ((fd = open(f, O_RDONLY)) == -1) die("Could not open configuration file: %s\n", strerror(errno)); @@ -911,61 +895,94 @@ void parse_file(const char *f) { if (fstat(fd, &stbuf) == -1) die("Could not fstat file: %s\n", strerror(errno)); - buf = scalloc((stbuf.st_size + 1) * sizeof(char)); - while (read_bytes < stbuf.st_size) { - if ((ret = read(fd, buf + read_bytes, (stbuf.st_size - read_bytes))) < 0) - die("Could not read(): %s\n", strerror(errno)); - read_bytes += ret; - } - - if (lseek(fd, 0, SEEK_SET) == (off_t)-1) - die("Could not lseek: %s\n", strerror(errno)); + buf = scalloc(stbuf.st_size + 1, 1); if ((fstr = fdopen(fd, "r")) == NULL) die("Could not fdopen: %s\n", strerror(errno)); while (!feof(fstr)) { - if (fgets(buffer, 1024, fstr) == NULL) { + if (!continuation) + continuation = buffer; + if (fgets(continuation, sizeof(buffer) - (continuation - buffer), fstr) == NULL) { if (feof(fstr)) break; die("Could not read configuration file\n"); } + if (buffer[strlen(buffer) - 1] != '\n' && !feof(fstr)) { + ELOG("Your line continuation is too long, it exceeds %zd bytes\n", sizeof(buffer)); + } - /* sscanf implicitly strips whitespace. Also, we skip comments and empty lines. */ - if (sscanf(buffer, "%s %[^\n]", key, value) < 1 || - key[0] == '#' || strlen(key) < 3) + /* sscanf implicitly strips whitespace. */ + const bool skip_line = (sscanf(buffer, "%511s %4095[^\n]", key, value) < 1 || strlen(key) < 3); + const bool comment = (key[0] == '#'); + value[4095] = '\n'; + + continuation = strstr(buffer, "\\\n"); + if (continuation) { + if (!comment) { + continue; + } + DLOG("line continuation in comment is ignored: \"%.*s\"\n", (int)strlen(buffer) - 1, buffer); + continuation = NULL; + } + + strncpy(buf + strlen(buf), buffer, strlen(buffer) + 1); + + /* Skip comments and empty lines. */ + if (skip_line || comment) { continue; + } if (strcasecmp(key, "set") == 0) { - if (value[0] != '$') { + char v_key[512]; + char v_value[4096]; + + if (sscanf(value, "%511s %4095[^\n]", v_key, v_value) < 1) { + ELOG("Failed to parse variable specification '%s', skipping it.\n", value); + continue; + } + + if (v_key[0] != '$') { ELOG("Malformed variable assignment, name has to start with $\n"); continue; } - /* get key/value for this variable */ - char *v_key = value, *v_value; - if (strstr(value, " ") == NULL && strstr(value, "\t") == NULL) { - ELOG("Malformed variable assignment, need a value\n"); + upsert_variable(&variables, v_key, v_value); + continue; + } else if (strcasecmp(key, "set_from_resource") == 0) { + char res_name[512]; + char v_key[512]; + char fallback[4096]; + + if (sscanf(value, "%511s %511s %4095[^\n]", v_key, res_name, fallback) < 1) { + ELOG("Failed to parse resource specification '%s', skipping it.\n", value); continue; } - if (!(v_value = strstr(value, " "))) - v_value = strstr(value, "\t"); + if (v_key[0] != '$') { + ELOG("Malformed variable assignment, name has to start with $\n"); + continue; + } - *(v_value++) = '\0'; - while (*v_value == '\t' || *v_value == ' ') - v_value++; + char *res_value = get_resource(res_name); + if (res_value == NULL) { + DLOG("Could not get resource '%s', using fallback '%s'.\n", res_name, fallback); + res_value = sstrdup(fallback); + } - struct Variable *new = scalloc(sizeof(struct Variable)); - new->key = sstrdup(v_key); - new->value = sstrdup(v_value); - SLIST_INSERT_HEAD(&variables, new, variables); - DLOG("Got new variable %s = %s\n", v_key, v_value); + upsert_variable(&variables, v_key, res_value); + FREE(res_value); continue; } } fclose(fstr); + if (database != NULL) { + xcb_xrm_database_free(database); + /* Explicitly set the database to NULL again in case the config gets reloaded. */ + database = NULL; + } + /* For every custom variable, see how often it occurs in the file and * how much extra bytes it requires when replaced. */ struct Variable *current, *nearest; @@ -979,7 +996,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; @@ -988,14 +1005,14 @@ void parse_file(const char *f) { FREE(bufcopy); /* Then, allocate a new buffer and copy the file over to the new one, - * but replace occurences of our variables */ + * but replace occurrences of our variables */ char *walk = buf, *destwalk; - char *new = smalloc((stbuf.st_size + extra_bytes + 1) * sizeof(char)); + char *new = smalloc(stbuf.st_size + extra_bytes + 1); destwalk = new; 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,57 +1057,35 @@ 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 = scalloc(1, 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); + extract_workspace_names_from_bindings(); check_for_duplicate_bindings(context); + reorder_bindings(); - if (context->has_errors || context->has_warnings) { - ELOG("FYI: You are using i3 version " I3_VERSION "\n"); + if (use_nagbar && (context->has_errors || context->has_warnings)) { + ELOG("FYI: You are using i3 version %s\n", i3_version); if (version == 3) ELOG("Please convert your configfile first, then fix any remaining errors (see above).\n"); - char *editaction, - *pageraction; - sasprintf(&editaction, "i3-sensible-editor \"%s\" && i3-msg reload\n", f); - sasprintf(&pageraction, "i3-sensible-pager \"%s\"\n", errorfilename); - char *argv[] = { - NULL, /* will be replaced by the executable path */ - "-f", - (config.font.pattern ? config.font.pattern : "fixed"), - "-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."), - "-b", - "edit config", - editaction, - (errorfilename ? "-b" : NULL), - (context->has_errors ? "show errors" : "show warnings"), - pageraction, - NULL - }; - - start_nagbar(&config_error_nagbar_pid, argv); - free(editaction); - free(pageraction); + start_config_error_nagbar(f, context->has_errors); } + bool has_errors = context->has_errors; + FREE(context->line_copy); free(context); free(new); @@ -1103,6 +1098,8 @@ void parse_file(const char *f) { SLIST_REMOVE_HEAD(&variables, variables); FREE(current); } + + return !has_errors; } #endif