This commit is contained in:
M. Stoeckl 2018-08-26 00:15:41 +00:00 committed by GitHub
commit 64283197e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 131 additions and 57 deletions

View file

@ -51,7 +51,7 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,
/** /**
* Parse and executes a command. * Parse and executes a command.
*/ */
struct cmd_results *execute_command(char *command, struct sway_seat *seat); struct cmd_results *execute_command(const char *command, struct sway_seat *seat);
/** /**
* Parse and handles a command during config file loading. * Parse and handles a command during config file loading.
* *

View file

@ -209,15 +209,93 @@ struct cmd_handler *find_handler(char *line, struct cmd_handler *cmd_handlers,
return res; return res;
} }
struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { /**
* A command is followed by its list of arguments. For instance, "floating" and
* ["toggle"]. This function is passed that list, in split_args format, and then
* strips quotes, unescapes the content, and expands variables. Some commands
* require slightly different preprocessing. (Variable expansion happens
* after unescaping so we don't double-unescape variable values.)
*
* The function returns the new (possibly smaller) value of argc, and if so
* zeros out the trailing values of argv.
*/
static int preprocess_arg(sway_cmd handle, char **argv, int argc) {
if (handle == cmd_exec) {
// Quotes are not stripped
for (int i = 0; i < argc; ++i) {
unescape_string(argv[i]);
argv[i] = do_var_replacement(argv[i]);
}
} else if (handle == cmd_for_window) {
// After first argument, merge remainder without preprocessing
if (*argv[0] == '\"' || *argv[0] == '\'') {
strip_quotes(argv[0]);
}
unescape_string(argv[0]);
argv[0] = do_var_replacement(argv[0]);
if (argc >= 2) {
// Compress the last arguments to one.
char *joined = join_args(argv + 1, argc - 1);
argv[1] = joined;
while (argc > 2) {
argc--;
free(argv[argc]);
argv[argc] = NULL;
}
}
} else if (handle == cmd_bindcode || handle == cmd_bindsym) {
// After the argument after the last '--X' option, merge
// remaining arguments without preprocessing.
int i = 0;
for (; i < argc; i++) {
if (*argv[i] == '\"' || *argv[i] == '\'') {
strip_quotes(argv[i]);
}
unescape_string(argv[i]);
argv[i] = do_var_replacement(argv[i]);
if (argv[i][0] != '-' || argv[i][1] != '-') {
break;
}
}
if (i < argc) {
if (*argv[i] == '\"' || *argv[i] == '\'') {
strip_quotes(argv[i]);
}
unescape_string(argv[i]);
argv[i] = do_var_replacement(argv[i]);
}
if (i + 1 < argc) {
char *joined = join_args(argv + i + 1, argc - i - 1);
argv[i + 1] = joined;
while (argc > i + 2) {
argc--;
free(argv[argc]);
argv[argc] = NULL;
}
}
} else {
// Default case: strip quotes, unescape, and expand variables
// When setting a variable, the first argument is ignored.
int start = handle == cmd_set ? 1 : 0;
for (int i = start; i < argc; ++i) {
if (*argv[i] == '\"' || *argv[i] == '\'') {
strip_quotes(argv[i]);
}
unescape_string(argv[i]);
argv[i] = do_var_replacement(argv[i]);
}
}
return argc;
}
struct cmd_results *execute_command(const char *_exec, struct sway_seat *seat) {
// Even though this function will process multiple commands we will only // Even though this function will process multiple commands we will only
// return the last error, if any (for now). (Since we have access to an // return the last error, if any (for now). (Since we have access to an
// error string we could e.g. concatenate all errors there.) // error string we could e.g. concatenate all errors there.)
struct cmd_results *results = NULL; struct cmd_results *results = NULL;
char *exec = strdup(_exec); char *exec = strdup(_exec);
char *head = exec; char *head = exec;
char *cmdlist;
char *cmd;
list_t *views = NULL; list_t *views = NULL;
if (seat == NULL) { if (seat == NULL) {
@ -251,43 +329,42 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
head += strspn(head, whitespace); head += strspn(head, whitespace);
} }
// Split command list // Split command list
cmdlist = argsep(&head, ";"); char *cmdlist = argsep(&head, ";");
cmdlist += strspn(cmdlist, whitespace); cmdlist += strspn(cmdlist, whitespace);
do { do {
// Split commands // Split commands
cmd = argsep(&cmdlist, ","); char *cmd = argsep(&cmdlist, ",");
cmd += strspn(cmd, whitespace); cmd += strspn(cmd, whitespace);
if (strcmp(cmd, "") == 0) { if (strcmp(cmd, "") == 0) {
wlr_log(WLR_INFO, "Ignoring empty command."); wlr_log(WLR_INFO, "Ignoring empty command.");
continue; continue;
} }
wlr_log(WLR_INFO, "Handling command '%s'", cmd); wlr_log(WLR_INFO, "Handling command '%s'", cmd);
//TODO better handling of argv
// Identify which command to execute.
int argc; int argc;
char **argv = split_args(cmd, &argc); char **argv = split_args(exec, &argc);
if (strcmp(argv[0], "exec") != 0) { if (!argv) {
int i; if (results) {
for (i = 1; i < argc; ++i) { free_cmd_results(results);
if (*argv[i] == '\"' || *argv[i] == '\'') {
strip_quotes(argv[i]);
}
} }
results = cmd_results_new(CMD_INVALID, cmd,
"Allocation failure");
goto cleanup;
} }
struct cmd_handler *handler = find_handler(argv[0], NULL, 0); struct cmd_handler *handler = find_handler(argv[0], NULL, 0);
if (!handler) { if (!handler) {
if (results) { if (results) {
free_cmd_results(results); free_cmd_results(results);
} }
results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command"); results = cmd_results_new(CMD_INVALID, cmd,
free_argv(argc, argv); "Unknown/invalid command");
goto cleanup; goto cleanup;
} }
// Var replacement, for all but first argument of set // Process command arguments
for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { argc = preprocess_arg(handler->handle, argv + 1, argc - 1) + 1;
argv[i] = do_var_replacement(argv[i]);
unescape_string(argv[i]);
}
if (!config->handler_context.using_criteria) { if (!config->handler_context.using_criteria) {
// without criteria, the command acts upon the focused // without criteria, the command acts upon the focused
@ -298,7 +375,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
"could not get focus-inactive for root container")) { "could not get focus-inactive for root container")) {
return NULL; return NULL;
} }
struct cmd_results *res = handler->handle(argc-1, argv+1); struct cmd_results *res = handler->handle(argc - 1, argv + 1);
if (res->status != CMD_SUCCESS) { if (res->status != CMD_SUCCESS) {
free_argv(argc, argv); free_argv(argc, argv);
if (results) { if (results) {
@ -312,7 +389,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
for (int i = 0; i < views->length; ++i) { for (int i = 0; i < views->length; ++i) {
struct sway_view *view = views->items[i]; struct sway_view *view = views->items[i];
config->handler_context.current_container = view->swayc; config->handler_context.current_container = view->swayc;
struct cmd_results *res = handler->handle(argc-1, argv+1); struct cmd_results *res = handler->handle(argc - 1, argv + 1);
if (res->status != CMD_SUCCESS) { if (res->status != CMD_SUCCESS) {
free_argv(argc, argv); free_argv(argc, argv);
if (results) { if (results) {
@ -324,7 +401,9 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
free_cmd_results(res); free_cmd_results(res);
} }
} }
free_argv(argc, argv); if (argc) {
free_argv(argc, argv);
}
} while(cmdlist); } while(cmdlist);
} while(head); } while(head);
cleanup: cleanup:
@ -372,18 +451,9 @@ struct cmd_results *config_command(char *exec) {
results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
goto cleanup; goto cleanup;
} }
int i;
// Var replacement, for all but first argument of set argc = preprocess_arg(handler->handle, argv + 1, argc - 1) + 1;
// TODO commands
for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
argv[i] = do_var_replacement(argv[i]);
unescape_string(argv[i]);
}
// Strip quotes for first argument.
// TODO This part needs to be handled much better
if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
strip_quotes(argv[1]);
}
if (handler->handle) { if (handler->handle) {
results = handler->handle(argc-1, argv+1); results = handler->handle(argc-1, argv+1);
} else { } else {
@ -407,11 +477,9 @@ struct cmd_results *config_subcommand(char **argv, int argc,
char *input = argv[0] ? argv[0] : "(empty)"; char *input = argv[0] ? argv[0] : "(empty)";
return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); return cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
} }
// Strip quotes for first argument.
// TODO This part needs to be handled much better argc = preprocess_arg(handler->handle, argv + 1, argc - 1) + 1;
if (argc > 1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
strip_quotes(argv[1]);
}
if (handler->handle) { if (handler->handle) {
return handler->handle(argc - 1, argv + 1); return handler->handle(argc - 1, argv + 1);
} }

View file

@ -206,14 +206,20 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
binding->type = BINDING_MOUSE; binding->type = BINDING_MOUSE;
} }
if (argc < 2) { // the arguments to bindsym and bindcode are specially preprocessed,
// so that the command to be executed is provided as the final argument.
if (argc != 2) {
free_sway_binding(binding); free_sway_binding(binding);
return cmd_results_new(CMD_FAILURE, bindtype, return cmd_results_new(CMD_FAILURE, bindtype,
"Invalid %s command " "Invalid %s command (expected exactly one non-option "
"(expected at least 2 non-option arguments, got %d)", bindtype, argc); "argument, followed by a command)", bindtype);
}
binding->command = strdup(argv[1]);
if (!binding->command) {
free_sway_binding(binding);
return cmd_results_new(CMD_FAILURE, bindtype,
"Unable to allocate a copy of the command");
} }
binding->command = join_args(argv + 1, argc - 1);
list_t *split = split_string(argv[0], "+"); list_t *split = split_string(argv[0], "+");
for (int i = 0; i < split->length; ++i) { for (int i = 0; i < split->length; ++i) {

View file

@ -8,10 +8,18 @@
struct cmd_results *cmd_for_window(int argc, char **argv) { struct cmd_results *cmd_for_window(int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
if ((error = checkarg(argc, "for_window", EXPECTED_AT_LEAST, 2))) { if ((error = checkarg(argc, "for_window", EXPECTED_EQUAL_TO, 2))) {
return error; return error;
} }
// the arguments to cmd_for_window are specially preprocessed,
// so that the command to be executed is provided as the final argument.
char *cmdlist = strdup(argv[1]);
if (!cmdlist) {
return cmd_results_new(CMD_FAILURE, "for_window",
"Unable to allocate a copy of the command");
}
char *err_str = NULL; char *err_str = NULL;
struct criteria *criteria = criteria_parse(argv[0], &err_str); struct criteria *criteria = criteria_parse(argv[0], &err_str);
if (!criteria) { if (!criteria) {
@ -21,7 +29,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) {
} }
criteria->type = CT_COMMAND; criteria->type = CT_COMMAND;
criteria->cmdlist = join_args(argv + 1, argc - 1); criteria->cmdlist = cmdlist;
list_add(config->criteria, criteria); list_add(config->criteria, criteria);
wlr_log(WLR_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist); wlr_log(WLR_DEBUG, "for_window: '%s' -> '%s' added", criteria->raw, criteria->cmdlist);

View file

@ -748,7 +748,6 @@ bool read_config(FILE *file, struct sway_config *config,
} }
char *do_var_replacement(char *str) { char *do_var_replacement(char *str) {
int i;
char *find = str; char *find = str;
while ((find = strchr(find, '$'))) { while ((find = strchr(find, '$'))) {
// Skip if escaped. // Skip if escaped.
@ -758,15 +757,8 @@ char *do_var_replacement(char *str) {
continue; continue;
} }
} }
// Unescape double $ and move on
if (find[1] == '$') {
size_t length = strlen(find + 1);
memmove(find, find + 1, length);
find[length] = '\0';
++find;
continue;
}
// Find matching variable // Find matching variable
int i;
for (i = 0; i < config->symbols->length; ++i) { for (i = 0; i < config->symbols->length; ++i) {
struct sway_variable *var = config->symbols->items[i]; struct sway_variable *var = config->symbols->items[i];
int vnlen = strlen(var->name); int vnlen = strlen(var->name);