]> git.sur5r.net Git - i3/i3/commitdiff
Refactor parse_command
authorTony Crisci <tony@dubstepdish.com>
Wed, 28 May 2014 06:01:50 +0000 (02:01 -0400)
committerMichael Stapelberg <michael@stapelberg.de>
Sat, 31 May 2014 12:59:35 +0000 (14:59 +0200)
parse_command returns a struct that contains useful information about
the result of a command as a whole (instead of the intermediate
representation used during parsing).

parse_command now requires the caller to allocate the yajl_gen used for
generating a json reply. This is passed as the second parameter to
parse_command. If NULL is passed, no json reply will be generated.

include/commands_parser.h
src/assignments.c
src/commands.c
src/commands_parser.c
src/ipc.c
src/key_press.c

index 8c733db4f0f4433429725cc51df81b14516ae498..6e531e9bd43985d13476e1d47ffc98f6634df6b8 100644 (file)
@@ -17,7 +17,7 @@
  * internally use this struct when calling cmd_floating and cmd_border.
  */
 struct CommandResultIR {
-    /* The JSON generator to append a reply to. */
+    /* The JSON generator to append a reply to (may be NULL). */
     yajl_gen json_gen;
 
     /* The next state to transition to. Passed to the function so that we can
@@ -29,4 +29,31 @@ struct CommandResultIR {
     bool needs_tree_render;
 };
 
-struct CommandResultIR *parse_command(const char *input);
+typedef struct CommandResult CommandResult;
+
+/**
+ * A struct that contains useful information about the result of a command as a
+ * whole (e.g. a compound command like "floating enable, border none").
+ * needs_tree_render is true if needs_tree_render of any individual command was
+ * true.
+ */
+struct CommandResult {
+    bool parse_error;
+    /* the error_message is currently only set for parse errors */
+    char *error_message;
+    bool needs_tree_render;
+};
+
+/**
+ * Parses and executes the given command. If a caller-allocated yajl_gen is
+ * passed, a json reply will be generated in the format specified by the ipc
+ * protocol. Pass NULL if no json reply is required.
+ *
+ * Free the returned CommandResult with command_result_free().
+ */
+CommandResult *parse_command(const char *input, yajl_gen gen);
+
+/**
+ * Frees a CommandResult
+ */
+void command_result_free(CommandResult *result);
index dbd4dfc3c94464340d4858037d24980bc013a632..23c91081d6f7fe326848b162b3b952d648ba4de7 100644 (file)
@@ -45,13 +45,13 @@ void run_assignments(i3Window *window) {
             DLOG("execute command %s\n", current->dest.command);
             char *full_command;
             sasprintf(&full_command, "[id=\"%d\"] %s", window->id, current->dest.command);
-            struct CommandResultIR *command_output = parse_command(full_command);
+            CommandResult *result = parse_command(full_command, NULL);
             free(full_command);
 
-            if (command_output->needs_tree_render)
+            if (result->needs_tree_render)
                 needs_tree_render = true;
 
-            yajl_gen_free(command_output->json_gen);
+            command_result_free(result);
         }
 
         /* Store that we ran this assignment to not execute it again */
index 6d8db6ff6a515c8a156890780d804fd2a46f8b53..73dba4fa9d0a6a5eaeb2a99830e593fe8b7a2c24 100644 (file)
 #include "shmlog.h"
 
 // Macros to make the YAJL API a bit easier to use.
-#define y(x, ...) yajl_gen_ ## x (cmd_output->json_gen, ##__VA_ARGS__)
-#define ystr(str) yajl_gen_string(cmd_output->json_gen, (unsigned char*)str, strlen(str))
+#define y(x, ...) (cmd_output->json_gen != NULL ? yajl_gen_ ## x (cmd_output->json_gen, ##__VA_ARGS__) : 0)
+#define ystr(str) (cmd_output->json_gen != NULL ? yajl_gen_string(cmd_output->json_gen, (unsigned char*)str, strlen(str)) : 0)
 #define ysuccess(success) do { \
-    y(map_open); \
-    ystr("success"); \
-    y(bool, success); \
-    y(map_close); \
+    if (cmd_output->json_gen != NULL) { \
+        y(map_open); \
+        ystr("success"); \
+        y(bool, success); \
+        y(map_close); \
+    } \
 } while (0)
 #define yerror(message) do { \
-    y(map_open); \
-    ystr("success"); \
-    y(bool, false); \
-    ystr("error"); \
-    ystr(message); \
-    y(map_close); \
+    if (cmd_output->json_gen != NULL) { \
+        y(map_open); \
+        ystr("success"); \
+        y(bool, false); \
+        ystr("error"); \
+        ystr(message); \
+        y(map_close); \
+    } \
 } while (0)
 
 /** When the command did not include match criteria (!), we use the currently
index 0723c270e0d8f593049087d50b38fa7b7a03c136..57249d2ec31b77e37b857bc6e18a7e135fca164b 100644 (file)
@@ -35,8 +35,8 @@
 #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, ...) (command_output.json_gen != NULL ? yajl_gen_ ## x (command_output.json_gen, ##__VA_ARGS__) : 0)
+#define ystr(str) (command_output.json_gen != NULL ? yajl_gen_string(command_output.json_gen, (unsigned char*)str, strlen(str)) : 0)
 
 /*******************************************************************************
  * The data structures used for parsing. Essentially the current state and a
@@ -205,12 +205,20 @@ static void next_state(const cmdp_token *token) {
     }
 }
 
-struct CommandResultIR *parse_command(const char *input) {
+/*
+ * Parses and executes the given command. If a caller-allocated yajl_gen is
+ * passed, a json reply will be generated in the format specified by the ipc
+ * protocol. Pass NULL if no json reply is required.
+ *
+ * Free the returned CommandResult with command_result_free().
+ */
+CommandResult *parse_command(const char *input, yajl_gen gen) {
     DLOG("COMMAND: *%s*\n", input);
     state = INITIAL;
+    CommandResult *result = scalloc(sizeof(CommandResult));
 
     /* A YAJL JSON generator used for formatting replies. */
-    command_output.json_gen = yajl_gen_alloc(NULL);
+    command_output.json_gen = gen;
 
     y(array_open);
     command_output.needs_tree_render = false;
@@ -378,6 +386,9 @@ struct CommandResultIR *parse_command(const char *input) {
             ELOG("Your command: %s\n", input);
             ELOG("              %s\n", position);
 
+            result->parse_error = true;
+            result->error_message = errormessage;
+
             /* Format this error message as a JSON reply. */
             y(map_open);
             ystr("success");
@@ -396,7 +407,6 @@ struct CommandResultIR *parse_command(const char *input) {
             y(map_close);
 
             free(position);
-            free(errormessage);
             clear_stack();
             break;
         }
@@ -404,7 +414,19 @@ struct CommandResultIR *parse_command(const char *input) {
 
     y(array_close);
 
-    return &command_output;
+    result->needs_tree_render = command_output.needs_tree_render;
+    return result;
+}
+
+/*
+ * Frees a CommandResult
+ */
+void command_result_free(CommandResult *result) {
+    if (result == NULL)
+        return;
+
+    FREE(result->error_message);
+    FREE(result);
 }
 
 /*******************************************************************************
@@ -442,6 +464,12 @@ int main(int argc, char *argv[]) {
         fprintf(stderr, "Syntax: %s <command>\n", argv[0]);
         return 1;
     }
-    parse_command(argv[1]);
+    yajl_gen gen = yajl_gen_alloc(NULL);
+
+    CommandResult *result = parse_command(argv[1], gen);
+
+    command_result_free(result);
+
+    yajl_gen_free(gen);
 }
 #endif
index c3b82b519a07820d2367a01d092c4eaa1e55cea9..0d2c92b87ecfe9fec062fa7804dcb862bc12e29d 100644 (file)
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -117,20 +117,24 @@ IPC_HANDLER(command) {
     char *command = scalloc(message_size + 1);
     strncpy(command, (const char*)message, message_size);
     LOG("IPC: received: *%s*\n", command);
-    struct CommandResultIR *command_output = parse_command((const char*)command);
+    yajl_gen gen = yajl_gen_alloc(NULL);
+
+    CommandResult *result = parse_command((const char*)command, gen);
     free(command);
 
-    if (command_output->needs_tree_render)
+    if (result->needs_tree_render)
         tree_render();
 
+    command_result_free(result);
+
     const unsigned char *reply;
     ylength length;
-    yajl_gen_get_buf(command_output->json_gen, &reply, &length);
+    yajl_gen_get_buf(gen, &reply, &length);
 
     ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_COMMAND,
                      (const uint8_t*)reply);
 
-    yajl_gen_free(command_output->json_gen);
+    yajl_gen_free(gen);
 }
 
 static void dump_rect(yajl_gen gen, const char *name, Rect r) {
index 2dd676dcd3ac5806123b95535ff13250e4f67e20..533561075851093ddc41dfba31b0f259440abc4a 100644 (file)
@@ -72,19 +72,23 @@ void handle_key_press(xcb_key_press_event_t *event) {
     if (bind == NULL)
         return;
 
+    yajl_gen gen = yajl_gen_alloc(NULL);
+
     char *command_copy = sstrdup(bind->command);
-    struct CommandResultIR *command_output = parse_command(command_copy);
+    CommandResult *result = parse_command(command_copy, gen);
     free(command_copy);
 
-    if (command_output->needs_tree_render)
+    if (result->needs_tree_render)
         tree_render();
 
+    command_result_free(result);
+
     /* We parse the JSON reply to figure out whether there was an error
      * ("success" being false in on of the returned dictionaries). */
     const unsigned char *reply;
     size_t length;
     yajl_handle handle = yajl_alloc(&command_error_callbacks, NULL, NULL);
-    yajl_gen_get_buf(command_output->json_gen, &reply, &length);
+    yajl_gen_get_buf(gen, &reply, &length);
 
     current_nesting_level = 0;
     parse_error_key = false;
@@ -116,5 +120,5 @@ void handle_key_press(xcb_key_press_event_t *event) {
 
     yajl_free(handle);
 
-    yajl_gen_free(command_output->json_gen);
+    yajl_gen_free(gen);
 }