aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/stringop.c56
-rw-r--r--include/stringop.h2
-rw-r--r--sway/commands.c113
-rw-r--r--sway/tree/workspace.c4
4 files changed, 98 insertions, 77 deletions
diff --git a/common/stringop.c b/common/stringop.c
index dea152cc..ac7df296 100644
--- a/common/stringop.c
+++ b/common/stringop.c
@@ -251,37 +251,61 @@ char *join_args(char **argv, int argc) {
251 return res; 251 return res;
252} 252}
253 253
254char *argsep(char **stringp, const char *delim) { 254static inline char *argsep_next_interesting(const char *src, const char *delim) {
255 char *special = strpbrk(src, "\"'\\");
256 char *next_delim = strpbrk(src, delim);
257 if (!special) {
258 return next_delim;
259 }
260 if (!next_delim) {
261 return special;
262 }
263 return (next_delim < special) ? next_delim : special;
264}
265
266char *argsep(char **stringp, const char *delim, char *matched) {
255 char *start = *stringp; 267 char *start = *stringp;
256 char *end = start; 268 char *end = start;
257 bool in_string = false; 269 bool in_string = false;
258 bool in_char = false; 270 bool in_char = false;
259 bool escaped = false; 271 bool escaped = false;
260 while (1) { 272 char *interesting = NULL;
261 if (*end == '"' && !in_char && !escaped) { 273
274 while ((interesting = argsep_next_interesting(end, delim))) {
275 if (escaped && interesting != end) {
276 escaped = false;
277 }
278 if (*interesting == '"' && !in_char && !escaped) {
262 in_string = !in_string; 279 in_string = !in_string;
263 } else if (*end == '\'' && !in_string && !escaped) { 280 end = interesting + 1;
281 } else if (*interesting == '\'' && !in_string && !escaped) {
264 in_char = !in_char; 282 in_char = !in_char;
265 } else if (*end == '\\') { 283 end = interesting + 1;
284 } else if (*interesting == '\\') {
266 escaped = !escaped; 285 escaped = !escaped;
267 } else if (*end == '\0') { 286 end = interesting + 1;
268 *stringp = NULL; 287 } else if (!in_string && !in_char && !escaped) {
269 break; 288 // We must have matched a separator
270 } else if (!in_string && !in_char && !escaped && strchr(delim, *end)) { 289 end = interesting;
290 if (matched) {
291 *matched = *end;
292 }
271 if (end - start) { 293 if (end - start) {
272 *(end++) = 0; 294 *(end++) = 0;
273 *stringp = end + strspn(end, delim);; 295 *stringp = end;
274 if (!**stringp) *stringp = NULL;
275 break; 296 break;
276 } else { 297 } else {
277 ++start; 298 end = ++start;
278 end = start;
279 } 299 }
300 } else {
301 end++;
280 } 302 }
281 if (*end != '\\') { 303 }
282 escaped = false; 304 if (!interesting) {
305 *stringp = NULL;
306 if (matched) {
307 *matched = '\0';
283 } 308 }
284 ++end;
285 } 309 }
286 return start; 310 return start;
287} 311}
diff --git a/include/stringop.h b/include/stringop.h
index 6f920999..2aabcee7 100644
--- a/include/stringop.h
+++ b/include/stringop.h
@@ -24,6 +24,6 @@ int unescape_string(char *string);
24char *join_args(char **argv, int argc); 24char *join_args(char **argv, int argc);
25 25
26// Split string into 2 by delim, handle quotes 26// Split string into 2 by delim, handle quotes
27char *argsep(char **stringp, const char *delim); 27char *argsep(char **stringp, const char *delim, char *matched_delim);
28 28
29#endif 29#endif
diff --git a/sway/commands.c b/sway/commands.c
index 377f2d01..a670f813 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -211,8 +211,8 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
211 list_t *res_list = create_list(); 211 list_t *res_list = create_list();
212 char *exec = strdup(_exec); 212 char *exec = strdup(_exec);
213 char *head = exec; 213 char *head = exec;
214 char *cmdlist;
215 char *cmd; 214 char *cmd;
215 char matched_delim = ';';
216 list_t *views = NULL; 216 list_t *views = NULL;
217 217
218 if (seat == NULL) { 218 if (seat == NULL) {
@@ -227,16 +227,13 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
227 227
228 head = exec; 228 head = exec;
229 do { 229 do {
230 // Split command list 230 for (; isspace(*head); ++head) {}
231 cmdlist = argsep(&head, ";"); 231 // Extract criteria (valid for this command list only).
232 do { 232 if (matched_delim == ';') {
233 // Skip leading whitespace
234 for (; isspace(*cmdlist); ++cmdlist) {}
235 // Extract criteria (valid for this command chain only).
236 config->handler_context.using_criteria = false; 233 config->handler_context.using_criteria = false;
237 if (*cmdlist == '[') { 234 if (*head == '[') {
238 char *error = NULL; 235 char *error = NULL;
239 struct criteria *criteria = criteria_parse(cmdlist, &error); 236 struct criteria *criteria = criteria_parse(head, &error);
240 if (!criteria) { 237 if (!criteria) {
241 list_add(res_list, 238 list_add(res_list,
242 cmd_results_new(CMD_INVALID, "%s", error)); 239 cmd_results_new(CMD_INVALID, "%s", error));
@@ -245,71 +242,71 @@ list_t *execute_command(char *_exec, struct sway_seat *seat,
245 } 242 }
246 list_free(views); 243 list_free(views);
247 views = criteria_get_views(criteria); 244 views = criteria_get_views(criteria);
248 cmdlist += strlen(criteria->raw); 245 head += strlen(criteria->raw);
249 criteria_destroy(criteria); 246 criteria_destroy(criteria);
250 config->handler_context.using_criteria = true; 247 config->handler_context.using_criteria = true;
251 // Skip leading whitespace 248 // Skip leading whitespace
252 for (; isspace(*cmdlist); ++cmdlist) {} 249 for (; isspace(*head); ++head) {}
253 }
254 // Split command chain into commands
255 cmd = argsep(&cmdlist, ",");
256 for (; isspace(*cmd); ++cmd) {}
257 if (strcmp(cmd, "") == 0) {
258 sway_log(SWAY_INFO, "Ignoring empty command.");
259 continue;
260 } 250 }
261 sway_log(SWAY_INFO, "Handling command '%s'", cmd); 251 }
262 //TODO better handling of argv 252 // Split command list
263 int argc; 253 cmd = argsep(&head, ";,", &matched_delim);
264 char **argv = split_args(cmd, &argc); 254 for (; isspace(*cmd); ++cmd) {}
265 if (strcmp(argv[0], "exec") != 0 && 255
266 strcmp(argv[0], "exec_always") != 0 && 256 if (strcmp(cmd, "") == 0) {
267 strcmp(argv[0], "mode") != 0) { 257 sway_log(SWAY_INFO, "Ignoring empty command.");
268 int i; 258 continue;
269 for (i = 1; i < argc; ++i) { 259 }
270 if (*argv[i] == '\"' || *argv[i] == '\'') { 260 sway_log(SWAY_INFO, "Handling command '%s'", cmd);
271 strip_quotes(argv[i]); 261 //TODO better handling of argv
272 } 262 int argc;
263 char **argv = split_args(cmd, &argc);
264 if (strcmp(argv[0], "exec") != 0 &&
265 strcmp(argv[0], "exec_always") != 0 &&
266 strcmp(argv[0], "mode") != 0) {
267 for (int i = 1; i < argc; ++i) {
268 if (*argv[i] == '\"' || *argv[i] == '\'') {
269 strip_quotes(argv[i]);
273 } 270 }
274 } 271 }
275 struct cmd_handler *handler = find_handler(argv[0], NULL, 0); 272 }
276 if (!handler) { 273 struct cmd_handler *handler = find_handler(argv[0], NULL, 0);
277 list_add(res_list, cmd_results_new(CMD_INVALID, 274 if (!handler) {
278 "Unknown/invalid command '%s'", argv[0])); 275 list_add(res_list, cmd_results_new(CMD_INVALID,
276 "Unknown/invalid command '%s'", argv[0]));
277 free_argv(argc, argv);
278 goto cleanup;
279 }
280
281 // Var replacement, for all but first argument of set
282 for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
283 argv[i] = do_var_replacement(argv[i]);
284 }
285
286 if (!config->handler_context.using_criteria) {
287 // The container or workspace which this command will run on.
288 struct sway_node *node = con ? &con->node :
289 seat_get_focus_inactive(seat, &root->node);
290 set_config_node(node);
291 struct cmd_results *res = handler->handle(argc-1, argv+1);
292 list_add(res_list, res);
293 if (res->status == CMD_INVALID) {
279 free_argv(argc, argv); 294 free_argv(argc, argv);
280 goto cleanup; 295 goto cleanup;
281 } 296 }
282 297 } else {
283 // Var replacement, for all but first argument of set 298 for (int i = 0; i < views->length; ++i) {
284 for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 299 struct sway_view *view = views->items[i];
285 argv[i] = do_var_replacement(argv[i]); 300 set_config_node(&view->container->node);
286 }
287
288 if (!config->handler_context.using_criteria) {
289 // The container or workspace which this command will run on.
290 struct sway_node *node = con ? &con->node :
291 seat_get_focus_inactive(seat, &root->node);
292 set_config_node(node);
293 struct cmd_results *res = handler->handle(argc-1, argv+1); 301 struct cmd_results *res = handler->handle(argc-1, argv+1);
294 list_add(res_list, res); 302 list_add(res_list, res);
295 if (res->status == CMD_INVALID) { 303 if (res->status == CMD_INVALID) {
296 free_argv(argc, argv); 304 free_argv(argc, argv);
297 goto cleanup; 305 goto cleanup;
298 } 306 }
299 } else {
300 for (int i = 0; i < views->length; ++i) {
301 struct sway_view *view = views->items[i];
302 set_config_node(&view->container->node);
303 struct cmd_results *res = handler->handle(argc-1, argv+1);
304 list_add(res_list, res);
305 if (res->status == CMD_INVALID) {
306 free_argv(argc, argv);
307 goto cleanup;
308 }
309 }
310 } 307 }
311 free_argv(argc, argv); 308 }
312 } while(cmdlist); 309 free_argv(argc, argv);
313 } while(head); 310 } while(head);
314cleanup: 311cleanup:
315 free(exec); 312 free(exec);
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 1a1f5c49..e1ef40f4 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -212,9 +212,9 @@ static void workspace_name_from_binding(const struct sway_binding * binding,
212 char *name = NULL; 212 char *name = NULL;
213 213
214 // workspace n 214 // workspace n
215 char *cmd = argsep(&cmdlist, " "); 215 char *cmd = argsep(&cmdlist, " ", NULL);
216 if (cmdlist) { 216 if (cmdlist) {
217 name = argsep(&cmdlist, ",;"); 217 name = argsep(&cmdlist, ",;", NULL);
218 } 218 }
219 219
220 // TODO: support "move container to workspace" bindings as well 220 // TODO: support "move container to workspace" bindings as well