X-Git-Url: https://git.sur5r.net/?p=i3%2Fi3;a=blobdiff_plain;f=src%2Fconfig_parser.c;h=e97a37e1dce4a7ddce1bc6f8ed0cf89165480ef0;hp=f325481275b7f07d09e4dbf02ffbc3d8ade03a63;hb=fbfbdb8e124480bc90bbd6a8b59c1692c4ebd531;hpb=cd4bc2adf5743bcf8b398a5beea9d5c1df690fe0 diff --git a/src/config_parser.c b/src/config_parser.c index f3254812..e97a37e1 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. * @@ -122,7 +122,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,7 +142,7 @@ 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); @@ -178,53 +178,6 @@ 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. ******************************************************************************/ @@ -414,7 +367,7 @@ struct ConfigResultIR *parse_config(const char *input, struct context *context) } } 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; @@ -519,7 +472,7 @@ struct ConfigResultIR *parse_config(const char *input, struct context *context) /* 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'; @@ -789,12 +742,12 @@ static char *migrate_config(char *input, off_t size) { /* read the script’s output */ int conv_size = 65535; - char *converted = malloc(conv_size); + 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) { @@ -830,6 +783,34 @@ 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); +} + /* * Parses the given file by first replacing the variables, then calling * parse_config and possibly launching i3-nagbar. @@ -837,11 +818,11 @@ static char *migrate_config(char *input, off_t size) { */ 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)); @@ -849,28 +830,31 @@ bool parse_file(const char *f, bool use_nagbar) { 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; @@ -894,7 +878,7 @@ bool parse_file(const char *f, bool use_nagbar) { while (*v_value == '\t' || *v_value == ' ') v_value++; - struct Variable *new = scalloc(sizeof(struct Variable)); + struct Variable *new = scalloc(1, sizeof(struct Variable)); new->key = sstrdup(v_key); new->value = sstrdup(v_value); SLIST_INSERT_HEAD(&variables, new, variables); @@ -926,9 +910,9 @@ bool parse_file(const char *f, bool use_nagbar) { 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 */ @@ -987,42 +971,22 @@ bool parse_file(const char *f, bool use_nagbar) { } } - 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 (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;