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);
}
/*
}
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);
" ^^^^^^^^^^^^^^^^^^^^^^",
'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;