mirror of
https://github.com/swaywm/sway.git
synced 2026-04-29 06:46:22 -04:00
Split parsing and execution of commands
Due to variable substitution and string unescaping, parsing commands necessitates allocation. Because allocation can fail, commands (invoked, via bindings or criteria) may in turn fail when memory is restricted, which is most often the case in anomalous situations (i.e., a memory leak in sway, overcommit disabled, bad config/ipc, etc.). This commit defines a `struct stored_command` in which a parsed command can be stored, and then executed from. Command execution in execute_command now generates a series of struct stored_command, which are promptly invoked by execute_stored_command.
This commit is contained in:
parent
e86d99acd6
commit
f8b9c796f4
2 changed files with 71 additions and 46 deletions
|
|
@ -51,7 +51,20 @@ 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);
|
||||||
|
|
||||||
|
struct stored_command {
|
||||||
|
sway_cmd *handle;
|
||||||
|
int argc;
|
||||||
|
char** argv;
|
||||||
|
struct criteria* criteria;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a pre-parsed command. (Returns NULL if command was not unsuccessful: TODO, fix)
|
||||||
|
*/
|
||||||
|
struct cmd_results *execute_stored_command(const struct stored_command *command,
|
||||||
|
struct sway_seat *seat);
|
||||||
/**
|
/**
|
||||||
* Parse and handles a command during config file loading.
|
* Parse and handles a command during config file loading.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
102
sway/commands.c
102
sway/commands.c
|
|
@ -209,7 +209,7 @@ 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) {
|
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.)
|
||||||
|
|
@ -218,7 +218,6 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
|
||||||
char *head = exec;
|
char *head = exec;
|
||||||
char *cmdlist;
|
char *cmdlist;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
list_t *views = NULL;
|
|
||||||
|
|
||||||
if (seat == NULL) {
|
if (seat == NULL) {
|
||||||
// passing a NULL seat means we just pick the default seat
|
// passing a NULL seat means we just pick the default seat
|
||||||
|
|
@ -228,25 +227,20 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config->handler_context.seat = seat;
|
|
||||||
|
|
||||||
head = exec;
|
head = exec;
|
||||||
do {
|
do {
|
||||||
// Extract criteria (valid for this command list only).
|
// Extract criteria (valid for this command list only).
|
||||||
config->handler_context.using_criteria = false;
|
struct criteria *criteria = NULL;
|
||||||
if (*head == '[') {
|
if (*head == '[') {
|
||||||
char *error = NULL;
|
char *error = NULL;
|
||||||
struct criteria *criteria = criteria_parse(head, &error);
|
criteria = criteria_parse(head, &error);
|
||||||
if (!criteria) {
|
if (!criteria) {
|
||||||
results = cmd_results_new(CMD_INVALID, head,
|
results = cmd_results_new(CMD_INVALID, head,
|
||||||
"%s", error);
|
"%s", error);
|
||||||
free(error);
|
free(error);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
views = criteria_get_views(criteria);
|
|
||||||
head += strlen(criteria->raw);
|
head += strlen(criteria->raw);
|
||||||
criteria_destroy(criteria);
|
|
||||||
config->handler_context.using_criteria = true;
|
|
||||||
// Skip leading whitespace
|
// Skip leading whitespace
|
||||||
head += strspn(head, whitespace);
|
head += strspn(head, whitespace);
|
||||||
}
|
}
|
||||||
|
|
@ -280,6 +274,9 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
|
||||||
}
|
}
|
||||||
results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command");
|
results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command");
|
||||||
free_argv(argc, argv);
|
free_argv(argc, argv);
|
||||||
|
if (criteria) {
|
||||||
|
criteria_destroy(criteria);
|
||||||
|
}
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -289,53 +286,68 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) {
|
||||||
unescape_string(argv[i]);
|
unescape_string(argv[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->handler_context.using_criteria) {
|
struct stored_command command;
|
||||||
// without criteria, the command acts upon the focused
|
command.handle = handler->handle;
|
||||||
// container
|
command.argv = argv;
|
||||||
config->handler_context.current_container =
|
command.argc = argc;
|
||||||
seat_get_focus_inactive(seat, &root_container);
|
command.criteria = criteria;
|
||||||
if (!sway_assert(config->handler_context.current_container,
|
struct cmd_results *res = execute_stored_command(&command, seat);
|
||||||
"could not get focus-inactive for root container")) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
struct cmd_results *res = handler->handle(argc-1, argv+1);
|
|
||||||
if (res->status != CMD_SUCCESS) {
|
|
||||||
free_argv(argc, argv);
|
|
||||||
if (results) {
|
|
||||||
free_cmd_results(results);
|
|
||||||
}
|
|
||||||
results = res;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
free_cmd_results(res);
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < views->length; ++i) {
|
|
||||||
struct sway_view *view = views->items[i];
|
|
||||||
config->handler_context.current_container = view->swayc;
|
|
||||||
struct cmd_results *res = handler->handle(argc-1, argv+1);
|
|
||||||
if (res->status != CMD_SUCCESS) {
|
|
||||||
free_argv(argc, argv);
|
|
||||||
if (results) {
|
|
||||||
free_cmd_results(results);
|
|
||||||
}
|
|
||||||
results = res;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
free_cmd_results(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free_argv(argc, argv);
|
free_argv(argc, argv);
|
||||||
|
|
||||||
|
if (res->status != CMD_SUCCESS) {
|
||||||
|
if (results) {
|
||||||
|
free_cmd_results(results);
|
||||||
|
}
|
||||||
|
results = res;
|
||||||
|
if (criteria) {
|
||||||
|
criteria_destroy(criteria);
|
||||||
|
}
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
free_cmd_results(res);
|
||||||
|
|
||||||
} while(cmdlist);
|
} while(cmdlist);
|
||||||
} while(head);
|
} while(head);
|
||||||
cleanup:
|
cleanup:
|
||||||
free(exec);
|
free(exec);
|
||||||
list_free(views);
|
|
||||||
if (!results) {
|
if (!results) {
|
||||||
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct cmd_results *execute_stored_command(const struct stored_command* command,
|
||||||
|
struct sway_seat* seat) {
|
||||||
|
config->handler_context.seat = seat;
|
||||||
|
|
||||||
|
if (!command->criteria) {
|
||||||
|
// without criteria, the command acts upon the focused container
|
||||||
|
config->handler_context.using_criteria = true;
|
||||||
|
config->handler_context.current_container =
|
||||||
|
seat_get_focus_inactive(seat, &root_container);
|
||||||
|
if (!sway_assert(config->handler_context.current_container,
|
||||||
|
"could not get focus-inactive for root container")) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return command->handle(command->argc-1, command->argv+1);
|
||||||
|
} else {
|
||||||
|
config->handler_context.using_criteria = false;
|
||||||
|
list_t *views = criteria_get_views(command->criteria);
|
||||||
|
for (int i = 0; i < views->length; ++i) {
|
||||||
|
struct sway_view *view = views->items[i];
|
||||||
|
config->handler_context.current_container = view->swayc;
|
||||||
|
struct cmd_results *res = command->handle(command->argc-1, command->argv+1);
|
||||||
|
if (res->status != CMD_SUCCESS) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
free_cmd_results(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_free(views);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// this is like execute_command above, except:
|
// this is like execute_command above, except:
|
||||||
// 1) it ignores empty commands (empty lines)
|
// 1) it ignores empty commands (empty lines)
|
||||||
// 2) it does variable substitution
|
// 2) it does variable substitution
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue