]> git.sur5r.net Git - i3/i3/commitdiff
Bugfix: Properly handle workspace names with double quotes (+test) (Thanks kvapen)
authorMichael Stapelberg <michael@stapelberg.de>
Mon, 30 Jan 2012 16:55:06 +0000 (16:55 +0000)
committerMichael Stapelberg <michael@stapelberg.de>
Tue, 7 Feb 2012 22:50:27 +0000 (22:50 +0000)
i3bar/src/xcb.c
src/commands_parser.c
testcases/t/187-commands-parser.t

index 978288e1aeb441d254c5d3d08ef3d3f26a4a7c7f..655107a2602454a413f7f3327bd1eebd97cad1e4 100644 (file)
@@ -294,10 +294,36 @@ void handle_button(xcb_button_press_event_t *event) {
             break;
     }
 
-    const size_t len = strlen(cur_ws->name) + strlen("workspace \"\"") + 1;
-    char buffer[len];
-    snprintf(buffer, len, "workspace \"%s\"", cur_ws->name);
+    /* To properly handle workspace names with double quotes in them, we need
+     * to escape the double quotes. Unfortunately, that’s rather ugly in C: We
+     * first count the number of double quotes, then we allocate a large enough
+     * buffer, then we copy character by character. */
+    int num_quotes = 0;
+    size_t namelen = 0;
+    for (char *walk = cur_ws->name; *walk != '\0'; walk++) {
+        if (*walk == '"')
+            num_quotes++;
+        /* While we’re looping through the name anyway, we can save one
+         * strlen(). */
+        namelen++;
+    }
+
+    const size_t len = namelen + strlen("workspace \"\"") + 1;
+    char *buffer = scalloc(len+num_quotes);
+    strncpy(buffer, "workspace \"", strlen("workspace \""));
+    int inpos, outpos;
+    for (inpos = 0, outpos = strlen("workspace \"");
+         inpos < namelen;
+         inpos++, outpos++) {
+        if (cur_ws->name[inpos] == '"') {
+            buffer[outpos] = '\\';
+            outpos++;
+        }
+        buffer[outpos] = cur_ws->name[inpos];
+    }
+    buffer[++outpos] = '"';
     i3_send_msg(I3_IPC_MESSAGE_TYPE_COMMAND, buffer);
+    free(buffer);
 }
 
 /*
index 62dba8617db6aeec6b5d9ac45843e326afa2506a..1b030102ec98894c25e3a757410f7bdeea94af70 100644 (file)
@@ -274,7 +274,18 @@ char *parse_command(const char *input) {
                 }
                 if (walk != beginning) {
                     char *str = scalloc(walk-beginning + 1);
-                    strncpy(str, beginning, walk-beginning);
+                    /* We copy manually to handle escaping of characters. */
+                    int inpos, outpos;
+                    for (inpos = 0, outpos = 0;
+                         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] == '"')
+                            inpos++;
+                        str[outpos] = beginning[inpos];
+                    }
                     if (token->identifier)
                         push_string(token->identifier, str);
                     DLOG("str is \"%s\"\n", str);
index 1a11068b782ce805c7a65c048655a5b5daf82e4d..ca81e2f6b66350af1f4ec2a574f61712457e791e 100644 (file)
@@ -157,4 +157,16 @@ is(parser_calls('move something to somewhere'),
    "                   ^^^^^^^^^^^^^^^^^^^^^^",
    'error for unknown literal ok');
 
+################################################################################
+# 3: Verify that escaping of double quotes works correctly
+################################################################################
+
+is(parser_calls('workspace "foo"'),
+   'cmd_workspace_name(foo)',
+   'Command with simple double quotes ok');
+
+is(parser_calls('workspace "foo \"bar"'),
+   'cmd_workspace_name(foo "bar)',
+   'Command with escaped double quotes ok');
+
 done_testing;