-#undef I3__FILE__
-#define I3__FILE__ "config_parser.c"
/*
* 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.
*
* nearest <error> token.
*
*/
+#include "all.h"
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
-
-#include "all.h"
+#include <xcb/xcb_xrm.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;
/* 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);
/* 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) {
}
}
-// 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);
for (int i = 0; i < statelist_idx; i++) {
if (statelist[i] != _next_state)
continue;
- statelist_idx = i+1;
+ statelist_idx = i + 1;
return;
}
*
*/
static const char *start_of_line(const char *walk, const char *beginning) {
- while (*walk != '\n' && *walk != '\r' && walk >= beginning) {
+ while (walk >= beginning && *walk != '\n' && *walk != '\r') {
walk--;
}
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;
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);
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
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;
if (*walk == '"') {
beginning++;
walk++;
- while (*walk != '\0' && (*walk != '"' || *(walk-1) == '\\'))
+ while (*walk != '\0' && (*walk != '"' || *(walk - 1) == '\\'))
walk++;
} else {
if (token->name[0] == 's') {
* 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];
}
}
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) {
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) {
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';
/* 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);
* we find the nearest state which contains an <error> 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)
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;
}
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;
static char *argv[] = {
NULL, /* will be replaced by the executable path */
- NULL
- };
+ NULL};
exec_i3_utility("i3-migrate-config-to-v4", argv);
}
/* 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]);
/* read the script’s output */
int conv_size = 65535;
- char *converted = malloc(conv_size);
- int read_bytes = 0;
+ char *converted = scalloc(conv_size, 1);
+ 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) {
wait(&status);
if (!WIFEXITED(status)) {
fprintf(stderr, "Child did not terminate normally, using old config file (will lead to broken behaviour)\n");
+ FREE(converted);
return NULL;
}
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 */
}
+ FREE(converted);
return NULL;
}
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;
}
/*
* 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));
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));
+ FREE(current_config);
+ current_config = scalloc(stbuf.st_size + 1, 1);
+ if ((ssize_t)fread(current_config, 1, stbuf.st_size, fstr) != stbuf.st_size) {
+ die("Could not fread: %s\n", strerror(errno));
+ }
+ rewind(fstr);
+
+ bool invalid_sets = false;
+
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. */
+ value[0] = '\0';
+ 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;
+ }
- /* sscanf implicitly strips whitespace. Also, we skip comments and empty lines. */
- if (sscanf(buffer, "%s %[^\n]", key, value) < 1 ||
- key[0] == '#' || strlen(key) < 3)
+ strncpy(buf + strlen(buf), buffer, strlen(buffer) + 1);
+
+ /* Skip comments and empty lines. */
+ if (skip_line || comment) {
continue;
+ }
+
+ if (strcasecmp(key, "set") == 0 && *value != '\0') {
+ char v_key[512];
+ char v_value[4096] = {'\0'};
+
+ if (sscanf(value, "%511s %4095[^\n]", v_key, v_value) < 1) {
+ ELOG("Failed to parse variable specification '%s', skipping it.\n", value);
+ invalid_sets = true;
+ continue;
+ }
- if (strcasecmp(key, "set") == 0) {
- if (value[0] != '$') {
+ if (v_key[0] != '$') {
ELOG("Malformed variable assignment, name has to start with $\n");
+ invalid_sets = true;
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] = {'\0'};
+ char v_key[512];
+ char fallback[4096] = {'\0'};
+
+ /* Ensure that this string is terminated. For example, a user might
+ * want a variable to be empty if the resource can't be found and
+ * uses
+ * set_from_resource $foo i3wm.foo
+ * Without explicitly terminating the string first, sscanf() will
+ * leave it uninitialized, causing garbage in the config.*/
+ fallback[0] = '\0';
+
+ if (sscanf(value, "%511s %511s %4095[^\n]", v_key, res_name, fallback) < 1) {
+ ELOG("Failed to parse resource specification '%s', skipping it.\n", value);
+ invalid_sets = true;
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");
+ invalid_sets = true;
+ 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;
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 = scalloc(stbuf.st_size + extra_bytes + 1, 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) {
int version = detect_version(buf);
if (version == 3) {
/* We need to convert this v3 configuration */
- char *converted = migrate_config(new, stbuf.st_size);
+ char *converted = migrate_config(new, strlen(new));
if (converted != NULL) {
ELOG("\n");
ELOG("****************************************************************\n");
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 || invalid_sets)) {
+ 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 || invalid_sets);
}
+ bool has_errors = context->has_errors;
+
FREE(context->line_copy);
free(context);
free(new);
SLIST_REMOVE_HEAD(&variables, variables);
FREE(current);
}
+
+ return !has_errors;
}
#endif