Alter config variable replacement process

Currently, variables cannot contain commands and cannot span more than
one argument. This is due to variable replacement happening after
determining the handler and after splitting the config line into
arguments.

This changes the process to:
0. Check for empty lines and block boundaries
1. Split the arguments as before
2. Verify that the first argument is not a variable. If needed the
following occurs
    a. Perform variable replacement on just the first argument
    b. Join the arguments back together then split the arguments again. This is needed when the variable
contains the command and arguments for the command.
3. Determine the handler
4. If the handler is cmd_set, escape the variable name so that it does
not get replaced
5. Join the arguments back together, do variable replacement on the full
command, and split the arguments again
6. Perform any needed quote stripping or unescaping on arguments
7. Run the command handler

This allows for config snippets such as:

```
set $super bindsym Mod4
$super+a exec some-command
```

and

```
set $bg bg #ffffff solid_color
output * $bg
```
This commit is contained in:
Brian Ashworth 2018-10-25 12:58:05 -04:00
parent 80a1c340a9
commit b277000601

View file

@ -353,12 +353,14 @@ struct cmd_results *config_command(char *exec) {
struct cmd_results *results = NULL; struct cmd_results *results = NULL;
int argc; int argc;
char **argv = split_args(exec, &argc); char **argv = split_args(exec, &argc);
// Check for empty lines
if (!argc) { if (!argc) {
results = cmd_results_new(CMD_SUCCESS, NULL, NULL); results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
goto cleanup; goto cleanup;
} }
// Start block // Check for the start of a block
if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) { if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) {
char *block = join_args(argv, argc - 1); char *block = join_args(argv, argc - 1);
results = cmd_results_new(CMD_BLOCK, block, NULL); results = cmd_results_new(CMD_BLOCK, block, NULL);
@ -366,22 +368,54 @@ struct cmd_results *config_command(char *exec) {
goto cleanup; goto cleanup;
} }
// Endblock // Check for the end of a block
if (strcmp(argv[argc - 1], "}") == 0) { if (strcmp(argv[argc - 1], "}") == 0) {
results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
goto cleanup; goto cleanup;
} }
wlr_log(WLR_INFO, "handling config command '%s'", exec);
struct cmd_handler *handler = find_handler(argv[0], NULL, 0); // Make sure the command is not stored in a variable
if (!handler) { if (*argv[0] == '$') {
char *input = argv[0] ? argv[0] : "(empty)"; argv[0] = do_var_replacement(argv[0]);
results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); char *temp = join_args(argv, argc);
free_argv(argc, argv);
argv = split_args(temp, &argc);
free(temp);
if (!argc) {
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
goto cleanup; goto cleanup;
} }
int i; }
// Var replacement, for all but first argument of set
// TODO commands // Determine the command handler
for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { wlr_log(WLR_INFO, "Config command: %s", exec);
struct cmd_handler *handler = find_handler(argv[0], NULL, 0);
if (!handler || !handler->handle) {
char *input = argv[0] ? argv[0] : "(empty)";
char *error = handler
? "This command is shimmed, but unimplemented"
: "Unknown/invalid command";
results = cmd_results_new(CMD_INVALID, input, error);
goto cleanup;
}
// Do variable replacement
if (handler->handle == cmd_set && argc > 1 && *argv[1] == '$') {
// Escape the variable name so it does not get replaced by one shorter
char *temp = calloc(1, strlen(argv[1]) + 2);
temp[0] = '$';
strcpy(&temp[1], argv[1]);
free(argv[1]);
argv[1] = temp;
}
char *command = do_var_replacement(join_args(argv, argc));
wlr_log(WLR_INFO, "After replacement: %s", command);
free_argv(argc, argv);
argv = split_args(command, &argc);
free(command);
// Strip quotes and unescape the string
for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
if (handler->handle != cmd_exec && handler->handle != cmd_exec_always if (handler->handle != cmd_exec && handler->handle != cmd_exec_always
&& handler->handle != cmd_bindsym && handler->handle != cmd_bindsym
&& handler->handle != cmd_bindcode && handler->handle != cmd_bindcode
@ -389,14 +423,11 @@ struct cmd_results *config_command(char *exec) {
&& (*argv[i] == '\"' || *argv[i] == '\'')) { && (*argv[i] == '\"' || *argv[i] == '\'')) {
strip_quotes(argv[i]); strip_quotes(argv[i]);
} }
argv[i] = do_var_replacement(argv[i]);
unescape_string(argv[i]); unescape_string(argv[i]);
} }
if (handler->handle) {
// Run command
results = handler->handle(argc - 1, argv + 1); results = handler->handle(argc - 1, argv + 1);
} else {
results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented");
}
cleanup: cleanup:
free_argv(argc, argv); free_argv(argc, argv);