]> git.sur5r.net Git - i3/i3/blobdiff - src/config_parser.c
format **/*.c with clang-format-3.5
[i3/i3] / src / config_parser.c
index 0e04bf1cfa559cccfbc6192d312bd5244e9a3f6c..21cd06fb5a6e5d12b85e04249612a3b284f918ce 100644 (file)
@@ -4,7 +4,7 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * config_parser.c: hand-written parser to parse configuration directives.
  *
 #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
-static pid_t configerror_pid = -1;
-/* The path to the temporary script files used by i3-nagbar. We need to keep
- * them around to delete the files in the i3-nagbar SIGCHLD handler. */
-static char *edit_script_path, *pager_script_path;
+pid_t config_error_nagbar_pid = -1;
 static struct context *context;
 #endif
 
@@ -149,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) {
@@ -162,7 +158,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;
@@ -235,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);
@@ -272,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;
     }
 
@@ -307,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;
@@ -326,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);
 
@@ -342,20 +333,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(&current_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;
@@ -405,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') {
@@ -417,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];
                     }
@@ -449,24 +440,34 @@ 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;
+            }
+
             if (strcmp(token->name, "end") == 0) {
                 //printf("checking for end: *%s*\n", walk);
                 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(&current_match, &subcommand_output, INITIAL);
 #endif
                     linecnt++;
                     walk++;
                     break;
-               }
-           }
+                }
+            }
         }
 
         if (!token_handled) {
@@ -513,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);
 
@@ -533,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);
@@ -578,7 +578,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);
@@ -589,7 +589,7 @@ struct ConfigResult *parse_config(const char *input, struct context *context) {
              * 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)
@@ -664,93 +664,6 @@ int main(int argc, char *argv[]) {
 
 #else
 
-/*
- * Writes the given command as a shell script to path.
- * Returns true unless something went wrong.
- *
- */
-static bool write_nagbar_script(const char *path, const char *command) {
-    int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR);
-    if (fd == -1) {
-        warn("Could not create temporary script to store the nagbar command");
-        return false;
-    }
-    write(fd, "#!/bin/sh\n", strlen("#!/bin/sh\n"));
-    write(fd, command, strlen(command));
-    close(fd);
-    return true;
-}
-
-/*
- * Handler which will be called when we get a SIGCHLD for the nagbar, meaning
- * it exited (or could not be started, depending on the exit code).
- *
- */
-static void nagbar_exited(EV_P_ ev_child *watcher, int revents) {
-    ev_child_stop(EV_A_ watcher);
-
-    if (unlink(edit_script_path) != 0)
-        warn("Could not delete temporary i3-nagbar script %s", edit_script_path);
-    if (unlink(pager_script_path) != 0)
-        warn("Could not delete temporary i3-nagbar script %s", pager_script_path);
-
-    if (!WIFEXITED(watcher->rstatus)) {
-        fprintf(stderr, "ERROR: i3-nagbar did not exit normally.\n");
-        return;
-    }
-
-    int exitcode = WEXITSTATUS(watcher->rstatus);
-    printf("i3-nagbar process exited with status %d\n", exitcode);
-    if (exitcode == 2) {
-        fprintf(stderr, "ERROR: i3-nagbar could not be found. Is it correctly installed on your system?\n");
-    }
-
-    configerror_pid = -1;
-}
-
-/* We need ev >= 4 for the following code. Since it is not *that* important (it
- * only makes sure that there are no i3-nagbar instances left behind) we still
- * support old systems with libev 3. */
-#if EV_VERSION_MAJOR >= 4
-/*
- * Cleanup handler. Will be called when i3 exits. Kills i3-nagbar with signal
- * SIGKILL (9) to make sure there are no left-over i3-nagbar processes.
- *
- */
-static void nagbar_cleanup(EV_P_ ev_cleanup *watcher, int revent) {
-    if (configerror_pid != -1) {
-        LOG("Sending SIGKILL (9) to i3-nagbar with PID %d\n", configerror_pid);
-        kill(configerror_pid, SIGKILL);
-    }
-}
-#endif
-
-/*
- * Kills the configerror i3-nagbar process, if any.
- *
- * Called when reloading/restarting.
- *
- * If wait_for_it is set (restarting), this function will waitpid(), otherwise,
- * ev is assumed to handle it (reloading).
- *
- */
-void kill_configerror_nagbar(bool wait_for_it) {
-    if (configerror_pid == -1)
-        return;
-
-    if (kill(configerror_pid, SIGTERM) == -1)
-        warn("kill(configerror_nagbar) failed");
-
-    if (!wait_for_it)
-        return;
-
-    /* When restarting, we don’t enter the ev main loop anymore and after the
-     * exec(), our old pid is no longer watched. So, ev won’t handle SIGCHLD
-     * for us and we would end up with a <defunct> process. Therefore we
-     * waitpid() here. */
-    waitpid(configerror_pid, NULL, 0);
-}
-
 /*
  * Goes through each line of buf (separated by \n) and checks for statements /
  * commands which only occur in i3 v4 configuration files. If it finds any, it
@@ -771,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;
         }
 
@@ -803,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;
@@ -854,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);
     }
 
@@ -924,136 +836,10 @@ static char *migrate_config(char *input, off_t size) {
 }
 
 /*
- * 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.
+ * Parses the given file by first replacing the variables, then calling
+ * parse_config and possibly launching 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);
-            }
-        }
-    }
-}
-
-/*
- * Starts an i3-nagbar process which alerts the user that his configuration
- * file contains one or more errors. Also offers two buttons: One to launch an
- * $EDITOR on the config file and another one to launch a $PAGER on the error
- * logfile.
- *
- */
-static void start_configerror_nagbar(const char *config_path) {
-    if (only_check_config)
-        return;
-
-    fprintf(stderr, "Starting i3-nagbar due to configuration errors\n");
-
-    /* We need to create a custom script containing our actual command
-     * since not every terminal emulator which is contained in
-     * i3-sensible-terminal supports -e with multiple arguments (and not
-     * all of them support -e with one quoted argument either).
-     *
-     * NB: The paths need to be unique, that is, don’t assume users close
-     * their nagbars at any point in time (and they still need to work).
-     * */
-    edit_script_path = get_process_filename("nagbar-cfgerror-edit");
-    pager_script_path = get_process_filename("nagbar-cfgerror-pager");
-
-    configerror_pid = fork();
-    if (configerror_pid == -1) {
-        warn("Could not fork()");
-        return;
-    }
-
-    /* child */
-    if (configerror_pid == 0) {
-        char *edit_command, *pager_command;
-        sasprintf(&edit_command, "i3-sensible-editor \"%s\" && i3-msg reload\n", config_path);
-        sasprintf(&pager_command, "i3-sensible-pager \"%s\"\n", errorfilename);
-        if (!write_nagbar_script(edit_script_path, edit_command) ||
-            !write_nagbar_script(pager_script_path, pager_command))
-            return;
-
-        char *editaction,
-             *pageraction;
-        sasprintf(&editaction, "i3-sensible-terminal -e \"%s\"", edit_script_path);
-        sasprintf(&pageraction, "i3-sensible-terminal -e \"%s\"", pager_script_path);
-        char *argv[] = {
-            NULL, /* will be replaced by the executable path */
-            "-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
-        };
-        exec_i3_utility("i3-nagbar", argv);
-    }
-
-    /* parent */
-    /* install a child watcher */
-    ev_child *child = smalloc(sizeof(ev_child));
-    ev_child_init(child, &nagbar_exited, configerror_pid, 0);
-    ev_child_start(main_loop, child);
-
-/* We need ev >= 4 for the following code. Since it is not *that* important (it
- * only makes sure that there are no i3-nagbar instances left behind) we still
- * support old systems with libev 3. */
-#if EV_VERSION_MAJOR >= 4
-    /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is
-     * still running) */
-    ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup));
-    ev_cleanup_init(cleanup, nagbar_cleanup);
-    ev_cleanup_start(main_loop, cleanup);
-#endif
-}
-
-
-
 void parse_file(const char *f) {
     SLIST_HEAD(variables_head, Variable) variables = SLIST_HEAD_INITIALIZER(&variables);
     int fd, ret, read_bytes = 0;
@@ -1131,12 +917,12 @@ void parse_file(const char *f) {
      * 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;
              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;
@@ -1151,11 +937,11 @@ void parse_file(const char *f) {
     destwalk = new;
     while (walk < (buf + stbuf.st_size)) {
         /* Find the next variable */
-        SLIST_FOREACH(current, &variables, variables)
+        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) {
@@ -1197,20 +983,19 @@ 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);
@@ -1219,7 +1004,30 @@ void parse_file(const char *f) {
         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");
-        start_configerror_nagbar(f);
+
+        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);
     }
 
     FREE(context->line_copy);