* 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.
*
/* 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);
}
}
-// 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.
******************************************************************************/
}
}
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;
/* 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';
/* 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 = 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) {
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;
}
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);
+}
+
/*
* 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;
+ int fd;
struct stat stbuf;
char *buf;
FILE *fstr;
- char buffer[1026], key[512], value[512];
+ char buffer[4096], key[512], value[512], *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));
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));
+ }
+ continuation = strstr(buffer, "\\\n");
+ if (continuation) {
+ continue;
+ }
+
+ strncpy(buf + strlen(buf), buffer, strlen(buffer) + 1);
/* sscanf implicitly strips whitespace. Also, we skip comments and empty lines. */
- if (sscanf(buffer, "%s %[^\n]", key, value) < 1 ||
+ if (sscanf(buffer, "%511s %511[^\n]", key, value) < 1 ||
key[0] == '#' || strlen(key) < 3)
continue;
while (*v_value == '\t' || *v_value == ' ')
v_value++;
- struct Variable *new = scalloc(sizeof(struct Variable));
+ struct Variable *new = scalloc(1, sizeof(struct Variable));
+ struct Variable *test = NULL, *loc = NULL;
new->key = sstrdup(v_key);
new->value = sstrdup(v_value);
- SLIST_INSERT_HEAD(&variables, new, variables);
+ /* 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;
+ }
+
+ if (loc == NULL) {
+ SLIST_INSERT_HEAD(&variables, new, variables);
+ } else {
+ SLIST_INSERT_AFTER(loc, new, variables);
+ }
+
DLOG("Got new variable %s = %s\n", v_key, v_value);
continue;
}
* variables (otherwise we will count them twice, which is bad when
* 'extra' is negative) */
char *bufcopy = sstrdup(buf);
- SLIST_FOREACH (current, &variables, variables) {
+ SLIST_FOREACH(current, &variables, variables) {
int extra = (strlen(current->value) - strlen(current->key));
char *next;
for (next = bufcopy;
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);
+ SLIST_FOREACH(current, &variables, variables)
+ current->next_match = strcasestr(walk, current->key);
nearest = NULL;
int distance = stbuf.st_size;
- SLIST_FOREACH (current, &variables, variables) {
+ SLIST_FOREACH(current, &variables, variables) {
if (current->next_match == NULL)
continue;
if ((current->next_match - walk) < distance) {
}
}
- context = scalloc(sizeof(struct context));
+ context = scalloc(1, sizeof(struct context));
context->filename = f;
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);
SLIST_REMOVE_HEAD(&variables, variables);
FREE(current);
}
+
+ return !has_errors;
}
#endif