mirror of
https://github.com/swaywm/sway.git
synced 2024-11-22 16:01:27 +00:00
multi command keybinds
This commit is contained in:
parent
e505abfe75
commit
0bea2e2122
|
@ -3,13 +3,15 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
struct cmd_handler {
|
|
||||||
char *command;
|
enum cmd_status {
|
||||||
enum cmd_status {
|
CMD_SUCCESS,
|
||||||
CMD_SUCCESS,
|
CMD_FAILURE,
|
||||||
CMD_FAILURE,
|
CMD_INVALID,
|
||||||
CMD_DEFER,
|
CMD_DEFER,
|
||||||
} (*handle)(int argc, char **argv);
|
// Config Blocks
|
||||||
|
CMD_BLOCK_END,
|
||||||
|
CMD_BLOCK_MODE,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum cmd_status handle_command(char *command);
|
enum cmd_status handle_command(char *command);
|
||||||
|
|
|
@ -7,6 +7,7 @@ extern const char *whitespace;
|
||||||
|
|
||||||
char *strip_whitespace(char *str);
|
char *strip_whitespace(char *str);
|
||||||
char *strip_comments(char *str);
|
char *strip_comments(char *str);
|
||||||
|
void strip_quotes(char *str);
|
||||||
|
|
||||||
// Simply split a string with delims, free with `free_flat_list`
|
// Simply split a string with delims, free with `free_flat_list`
|
||||||
list_t *split_string(const char *str, const char *delims);
|
list_t *split_string(const char *str, const char *delims);
|
||||||
|
@ -22,5 +23,10 @@ int unescape_string(char *string);
|
||||||
char *join_args(char **argv, int argc);
|
char *join_args(char **argv, int argc);
|
||||||
char *join_list(list_t *list, char *separator);
|
char *join_list(list_t *list, char *separator);
|
||||||
|
|
||||||
|
// split string into 2 by delim.
|
||||||
|
char *cmdsep(char **stringp, const char *delim);
|
||||||
|
// Split string into 2 by delim, handle quotes
|
||||||
|
char *argsep(char **stringp, const char *delim);
|
||||||
|
|
||||||
char *strdup(const char *);
|
char *strdup(const char *);
|
||||||
#endif
|
#endif
|
||||||
|
|
214
sway/commands.c
214
sway/commands.c
|
@ -18,6 +18,45 @@
|
||||||
#include "sway.h"
|
#include "sway.h"
|
||||||
#include "resize.h"
|
#include "resize.h"
|
||||||
|
|
||||||
|
typedef enum cmd_status sway_cmd(int argc, char **argv);
|
||||||
|
|
||||||
|
struct cmd_handler {
|
||||||
|
char *command;
|
||||||
|
sway_cmd *handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
static sway_cmd cmd_bindsym;
|
||||||
|
static sway_cmd cmd_orientation;
|
||||||
|
static sway_cmd cmd_exec;
|
||||||
|
static sway_cmd cmd_exec_always;
|
||||||
|
static sway_cmd cmd_exit;
|
||||||
|
static sway_cmd cmd_floating;
|
||||||
|
static sway_cmd cmd_floating_mod;
|
||||||
|
static sway_cmd cmd_focus;
|
||||||
|
static sway_cmd cmd_focus_follows_mouse;
|
||||||
|
static sway_cmd cmd_for_window;
|
||||||
|
static sway_cmd cmd_fullscreen;
|
||||||
|
static sway_cmd cmd_gaps;
|
||||||
|
static sway_cmd cmd_kill;
|
||||||
|
static sway_cmd cmd_layout;
|
||||||
|
static sway_cmd cmd_log_colors;
|
||||||
|
static sway_cmd cmd_mode;
|
||||||
|
static sway_cmd cmd_move;
|
||||||
|
static sway_cmd cmd_output;
|
||||||
|
static sway_cmd cmd_reload;
|
||||||
|
static sway_cmd cmd_resize;
|
||||||
|
static sway_cmd cmd_scratchpad;
|
||||||
|
static sway_cmd cmd_set;
|
||||||
|
static sway_cmd cmd_split;
|
||||||
|
static sway_cmd cmd_splith;
|
||||||
|
static sway_cmd cmd_splitv;
|
||||||
|
static sway_cmd cmd_workspace;
|
||||||
|
static sway_cmd cmd_ws_auto_back_and_forth;
|
||||||
|
|
||||||
|
#define NO_BIND() if (!config->reading) return CMD_FAILURE;
|
||||||
|
#define NO_CONF() if (config->reading) return CMD_FAILURE;
|
||||||
|
#define DEFER() if (!config->active) return CMD_DEFER;
|
||||||
|
|
||||||
swayc_t *sp_view;
|
swayc_t *sp_view;
|
||||||
int sp_index = 0;
|
int sp_index = 0;
|
||||||
|
|
||||||
|
@ -145,14 +184,19 @@ static enum cmd_status cmd_bindsym(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum cmd_status cmd_exec_always(int argc, char **argv) {
|
static enum cmd_status cmd_exec_always(int argc, char **argv) {
|
||||||
|
DEFER();
|
||||||
if (!checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0)) {
|
if (!checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0)) {
|
||||||
return CMD_FAILURE;
|
return CMD_FAILURE;
|
||||||
}
|
}
|
||||||
if (!config->active) {
|
// Put argument into cmd array
|
||||||
return CMD_DEFER;
|
char *tmp = join_args(argv, argc);
|
||||||
}
|
char cmd[4096];
|
||||||
|
strcpy(cmd, tmp);
|
||||||
|
free(tmp);
|
||||||
|
|
||||||
pid_t pid = fork();
|
char *args[] = {"sh", "-c", cmd, 0 };
|
||||||
|
|
||||||
|
pid_t pid = vfork();
|
||||||
/* Failed to fork */
|
/* Failed to fork */
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
sway_log(L_ERROR, "exec command failed, sway did not fork");
|
sway_log(L_ERROR, "exec command failed, sway did not fork");
|
||||||
|
@ -160,22 +204,18 @@ static enum cmd_status cmd_exec_always(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
/* Child process */
|
/* Child process */
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
char *args = join_args(argv, argc);
|
sway_log(L_DEBUG, "Executing %s", cmd);
|
||||||
sway_log(L_DEBUG, "Executing %s", args);
|
execv("/bin/sh", args);
|
||||||
execl("/bin/sh", "sh", "-c", args, (char *)NULL);
|
/* Execv doesnt return unless failure */
|
||||||
/* Execl doesnt return unless failure */
|
sway_log(L_ERROR, "execv failde to return");
|
||||||
sway_log(L_ERROR, "could not find /bin/sh");
|
_exit(-1);
|
||||||
free(args);
|
|
||||||
exit(-1);
|
|
||||||
}
|
}
|
||||||
/* Parent */
|
/* Parent */
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum cmd_status cmd_exec(int argc, char **argv) {
|
static enum cmd_status cmd_exec(int argc, char **argv) {
|
||||||
if (!config->active) {
|
DEFER();
|
||||||
return CMD_DEFER;
|
|
||||||
}
|
|
||||||
if (config->reloading) {
|
if (config->reloading) {
|
||||||
char *args = join_args(argv, argc);
|
char *args = join_args(argv, argc);
|
||||||
sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
|
sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
|
||||||
|
@ -373,15 +413,19 @@ static void hide_view_in_scratchpad(swayc_t *sp_view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum cmd_status cmd_mode(int argc, char **argv) {
|
static enum cmd_status cmd_mode(int argc, char **argv) {
|
||||||
if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) {
|
if (!checkarg(argc, "mode", EXPECTED_AT_LEAST, 1)) {
|
||||||
return CMD_FAILURE;
|
return CMD_FAILURE;
|
||||||
}
|
}
|
||||||
bool mode_make = !strcmp(argv[argc-1], "{");
|
char *mode_name = join_args(argv, argc);
|
||||||
if (mode_make && !config->reading) {
|
int mode_len = strlen(mode_name);
|
||||||
return CMD_FAILURE;
|
bool mode_make = mode_name[mode_len-1] == '{';
|
||||||
|
if (mode_make) {
|
||||||
|
NO_BIND();
|
||||||
|
// Trim trailing spaces
|
||||||
|
do {
|
||||||
|
mode_name[--mode_len] = 0;
|
||||||
|
} while(isspace(mode_name[mode_len-1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
char *mode_name = join_args(argv, argc - mode_make);
|
|
||||||
struct sway_mode *mode = NULL;
|
struct sway_mode *mode = NULL;
|
||||||
// Find mode
|
// Find mode
|
||||||
int i, len = config->modes->length;
|
int i, len = config->modes->length;
|
||||||
|
@ -404,16 +448,18 @@ static enum cmd_status cmd_mode(int argc, char **argv) {
|
||||||
free(mode_name);
|
free(mode_name);
|
||||||
return CMD_FAILURE;
|
return CMD_FAILURE;
|
||||||
}
|
}
|
||||||
sway_log(L_DEBUG, "Switching to mode `%s'",mode->name);
|
if ((config->reading && mode_make) || (!config->reading && !mode_make)) {
|
||||||
|
sway_log(L_DEBUG, "Switching to mode `%s'",mode->name);
|
||||||
|
}
|
||||||
free(mode_name);
|
free(mode_name);
|
||||||
// Set current mode
|
// Set current mode
|
||||||
config->current_mode = mode;
|
config->current_mode = mode;
|
||||||
return CMD_SUCCESS;
|
return mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum cmd_status cmd_move(int argc, char **argv) {
|
static enum cmd_status cmd_move(int argc, char **argv) {
|
||||||
if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)
|
NO_CONF();
|
||||||
|| config->reading || !config->active) {
|
if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) {
|
||||||
return CMD_FAILURE;
|
return CMD_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,10 +529,11 @@ static enum cmd_status cmd_move(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum cmd_status cmd_orientation(int argc, char **argv) {
|
static enum cmd_status cmd_orientation(int argc, char **argv) {
|
||||||
if (!checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1)
|
NO_BIND();
|
||||||
|| !config->reading) {
|
if (!checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1)) {
|
||||||
return CMD_FAILURE;
|
return CMD_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcasecmp(argv[0], "horizontal") == 0) {
|
if (strcasecmp(argv[0], "horizontal") == 0) {
|
||||||
config->default_orientation = L_HORIZ;
|
config->default_orientation = L_HORIZ;
|
||||||
} else if (strcasecmp(argv[0], "vertical") == 0) {
|
} else if (strcasecmp(argv[0], "vertical") == 0) {
|
||||||
|
@ -500,6 +547,7 @@ static enum cmd_status cmd_orientation(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum cmd_status cmd_output(int argc, char **argv) {
|
static enum cmd_status cmd_output(int argc, char **argv) {
|
||||||
|
NO_BIND();
|
||||||
if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) {
|
if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) {
|
||||||
return CMD_FAILURE;
|
return CMD_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -510,7 +558,6 @@ static enum cmd_status cmd_output(int argc, char **argv) {
|
||||||
output->enabled = true;
|
output->enabled = true;
|
||||||
|
|
||||||
// TODO: atoi doesn't handle invalid numbers
|
// TODO: atoi doesn't handle invalid numbers
|
||||||
|
|
||||||
if (strcasecmp(argv[1], "disable") == 0) {
|
if (strcasecmp(argv[1], "disable") == 0) {
|
||||||
output->enabled = false;
|
output->enabled = false;
|
||||||
}
|
}
|
||||||
|
@ -960,6 +1007,11 @@ static enum cmd_status cmd_log_colors(int argc, char **argv) {
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__attribute__((unused))
|
||||||
|
enum cmd_status cmd_for_window(int argc, char **argv) {
|
||||||
|
return CMD_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
static enum cmd_status cmd_fullscreen(int argc, char **argv) {
|
static enum cmd_status cmd_fullscreen(int argc, char **argv) {
|
||||||
if (!checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0)
|
if (!checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0)
|
||||||
|| config->reading || !config->active) {
|
|| config->reading || !config->active) {
|
||||||
|
@ -1087,57 +1139,89 @@ static struct cmd_handler *find_handler(char *line) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum cmd_status handle_command(char *exec) {
|
enum cmd_status handle_command(char *_exec) {
|
||||||
sway_log(L_INFO, "Handling command '%s'", exec);
|
enum cmd_status status = CMD_SUCCESS;
|
||||||
int argc;
|
char *exec = strdup(_exec);
|
||||||
char **argv = split_args(exec, &argc);
|
char *head = exec;
|
||||||
enum cmd_status status = CMD_FAILURE;
|
char *cmdlist;
|
||||||
struct cmd_handler *handler;
|
char *cmd;
|
||||||
if (!argc) {
|
char *criteria __attribute__((unused));
|
||||||
return status;
|
|
||||||
}
|
head = exec;
|
||||||
if ((handler = find_handler(argv[0])) == NULL
|
do {
|
||||||
|| (status = handler->handle(argc - 1, argv + 1)) != CMD_SUCCESS) {
|
// Handle criteria
|
||||||
sway_log(L_ERROR, "Command failed: %s", argv[0]);
|
if (*head == '[') {
|
||||||
}
|
criteria = argsep(&head, "]");
|
||||||
free_argv(argc, argv);
|
if (head) {
|
||||||
|
++head;
|
||||||
|
// TODO handle criteria
|
||||||
|
} else {
|
||||||
|
sway_log(L_ERROR, "Unmatched [");
|
||||||
|
status = CMD_INVALID;
|
||||||
|
}
|
||||||
|
// Skip leading whitespace
|
||||||
|
head += strspn(head, whitespace);
|
||||||
|
}
|
||||||
|
// Split command list
|
||||||
|
cmdlist = argsep(&head, ";");
|
||||||
|
cmdlist += strspn(cmdlist, whitespace);
|
||||||
|
do {
|
||||||
|
// Split commands
|
||||||
|
cmd = argsep(&cmdlist, ",");
|
||||||
|
cmd += strspn(cmd, whitespace);
|
||||||
|
sway_log(L_INFO, "Handling command '%s'", cmd);
|
||||||
|
//TODO better handling of argv
|
||||||
|
int argc;
|
||||||
|
char **argv = split_args(cmd, &argc);
|
||||||
|
if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
|
||||||
|
strip_quotes(argv[1]);
|
||||||
|
}
|
||||||
|
struct cmd_handler *handler = find_handler(argv[0]);
|
||||||
|
enum cmd_status res = CMD_INVALID;
|
||||||
|
if (!handler
|
||||||
|
|| (res = handler->handle(argc-1, argv+1)) != CMD_SUCCESS) {
|
||||||
|
sway_log(L_ERROR, "Command '%s' failed", cmd);
|
||||||
|
free_argv(argc, argv);
|
||||||
|
status = res;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
free_argv(argc, argv);
|
||||||
|
} while(cmdlist);
|
||||||
|
} while(head);
|
||||||
|
cleanup:
|
||||||
|
free(exec);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum cmd_status config_command(char *exec) {
|
enum cmd_status config_command(char *exec) {
|
||||||
sway_log(L_INFO, "handling config command '%s'", exec);
|
sway_log(L_INFO, "handling config command '%s'", exec);
|
||||||
|
enum cmd_status status = CMD_SUCCESS;
|
||||||
int argc;
|
int argc;
|
||||||
char **argv = split_args(exec, &argc);
|
char **argv = split_args(exec, &argc);
|
||||||
enum cmd_status status = CMD_FAILURE;
|
|
||||||
struct cmd_handler *handler;
|
|
||||||
if (!argc) {
|
if (!argc) {
|
||||||
status = CMD_SUCCESS;
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
// TODO better block handling
|
// Endblock
|
||||||
if (strncmp(argv[0], "}", 1) == 0) {
|
if (**argv == '}') {
|
||||||
config->current_mode = config->modes->items[0];
|
status = CMD_BLOCK_END;
|
||||||
status = CMD_SUCCESS;
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
if ((handler = find_handler(argv[0]))) {
|
struct cmd_handler *handler = find_handler(argv[0]);
|
||||||
// Dont replace first argument in cmd_set
|
if (!handler) {
|
||||||
int i = handler->handle == cmd_set ? 2 : 1;
|
status = CMD_INVALID;
|
||||||
int e = argc;
|
goto cleanup;
|
||||||
for (; i < e; ++i) {
|
|
||||||
argv[i] = do_var_replacement(argv[i]);
|
|
||||||
}
|
|
||||||
status = handler->handle(argc - 1, argv + 1);
|
|
||||||
if (status == CMD_FAILURE) {
|
|
||||||
sway_log(L_ERROR, "Config load failed for line `%s'", exec);
|
|
||||||
} else if (status == CMD_DEFER) {
|
|
||||||
sway_log(L_DEBUG, "Defferring command `%s'", exec);
|
|
||||||
list_add(config->cmd_queue, strdup(exec));
|
|
||||||
status = CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sway_log(L_ERROR, "Unknown command `%s'", exec);
|
|
||||||
}
|
}
|
||||||
|
int i;
|
||||||
|
// Var replacement, for all but first argument of set
|
||||||
|
for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
|
||||||
|
argv[i] = do_var_replacement(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]);
|
||||||
|
}
|
||||||
|
status = handler->handle(argc-1, argv+1);
|
||||||
cleanup:
|
cleanup:
|
||||||
free_argv(argc, argv);
|
free_argv(argc, argv);
|
||||||
return status;
|
return status;
|
||||||
|
|
|
@ -225,14 +225,46 @@ bool read_config(FILE *file, bool is_active) {
|
||||||
config->active = true;
|
config->active = true;
|
||||||
}
|
}
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
enum cmd_status block = CMD_BLOCK_END;
|
||||||
|
|
||||||
char *line;
|
char *line;
|
||||||
while (!feof(file)) {
|
while (!feof(file)) {
|
||||||
line = read_line(file);
|
line = read_line(file);
|
||||||
line = strip_comments(line);
|
line = strip_comments(line);
|
||||||
if (config_command(line) == CMD_FAILURE) {
|
switch(config_command(line)) {
|
||||||
|
case CMD_FAILURE:
|
||||||
|
case CMD_INVALID:
|
||||||
sway_log(L_ERROR, "Error on line '%s'", line);
|
sway_log(L_ERROR, "Error on line '%s'", line);
|
||||||
success = false;
|
success = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMD_DEFER:
|
||||||
|
sway_log(L_DEBUG, "Defferring command `%s'", line);
|
||||||
|
list_add(config->cmd_queue, strdup(line));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMD_BLOCK_MODE:
|
||||||
|
if (block == CMD_BLOCK_END) {
|
||||||
|
block = CMD_BLOCK_MODE;
|
||||||
|
} else {
|
||||||
|
sway_log(L_ERROR, "Invalid block '%s'", line);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMD_BLOCK_END:
|
||||||
|
switch(block) {
|
||||||
|
case CMD_BLOCK_MODE:
|
||||||
|
sway_log(L_DEBUG, "End of mode block");
|
||||||
|
config->current_mode = config->modes->items[0];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMD_BLOCK_END:
|
||||||
|
sway_log(L_ERROR, "Unmatched }");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
default:;
|
||||||
}
|
}
|
||||||
free(line);
|
free(line);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@ int colored = 1;
|
||||||
log_importance_t v = L_SILENT;
|
log_importance_t v = L_SILENT;
|
||||||
|
|
||||||
static const char *verbosity_colors[] = {
|
static const char *verbosity_colors[] = {
|
||||||
"", // L_SILENT
|
[L_SILENT] = "",
|
||||||
"\x1B[1;31m", // L_ERROR
|
[L_ERROR ] = "\x1B[1;31m",
|
||||||
"\x1B[1;34m", // L_INFO
|
[L_INFO ] = "\x1B[1;34m",
|
||||||
"\x1B[1;30m", // L_DEBUG
|
[L_DEBUG ] = "\x1B[1;30m",
|
||||||
};
|
};
|
||||||
|
|
||||||
void init_log(log_importance_t verbosity) {
|
void init_log(log_importance_t verbosity) {
|
||||||
|
|
111
sway/stringop.c
111
sway/stringop.c
|
@ -105,40 +105,40 @@ char **split_args(const char *start, int *argc) {
|
||||||
bool in_char = false;
|
bool in_char = false;
|
||||||
bool escaped = false;
|
bool escaped = false;
|
||||||
const char *end = start;
|
const char *end = start;
|
||||||
while (*start) {
|
if (start) {
|
||||||
if (!in_token) {
|
while (*start) {
|
||||||
start = (end += strspn(end, whitespace));
|
if (!in_token) {
|
||||||
in_token = true;
|
start = (end += strspn(end, whitespace));
|
||||||
}
|
in_token = true;
|
||||||
if (*end == '"' && !in_char && !escaped) {
|
}
|
||||||
in_string = !in_string;
|
if (*end == '"' && !in_char && !escaped) {
|
||||||
} else if (*end == '\'' && !in_string && !escaped) {
|
in_string = !in_string;
|
||||||
in_char = !in_char;
|
} else if (*end == '\'' && !in_string && !escaped) {
|
||||||
} else if (*end == '\\') {
|
in_char = !in_char;
|
||||||
escaped = !escaped;
|
} else if (*end == '\\') {
|
||||||
} else if (*end == '\0' || (!in_string && !in_char && !escaped
|
escaped = !escaped;
|
||||||
&& strchr(whitespace, *end))) {
|
} else if (*end == '\0' || (!in_string && !in_char && !escaped
|
||||||
goto add_part;
|
&& strchr(whitespace, *end))) {
|
||||||
}
|
goto add_token;
|
||||||
if (*end != '\\') {
|
}
|
||||||
|
if (*end != '\\') {
|
||||||
|
escaped = false;
|
||||||
|
}
|
||||||
|
++end;
|
||||||
|
continue;
|
||||||
|
add_token:
|
||||||
|
if (end - start > 0) {
|
||||||
|
char *token = malloc(end - start + 1);
|
||||||
|
strncpy(token, start, end - start + 1);
|
||||||
|
token[end - start] = '\0';
|
||||||
|
argv[*argc] = token;
|
||||||
|
if (++*argc + 1 == alloc) {
|
||||||
|
argv = realloc(argv, (alloc *= 2) * sizeof(char *));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in_token = false;
|
||||||
escaped = false;
|
escaped = false;
|
||||||
}
|
}
|
||||||
++end;
|
|
||||||
continue;
|
|
||||||
add_part:
|
|
||||||
if (end - start > 0) {
|
|
||||||
char *token = malloc(end - start + 1);
|
|
||||||
strncpy(token, start, end - start + 1);
|
|
||||||
token[end - start] = '\0';
|
|
||||||
strip_quotes(token);
|
|
||||||
unescape_string(token);
|
|
||||||
argv[*argc] = token;
|
|
||||||
if (++*argc + 1 == alloc) {
|
|
||||||
argv = realloc(argv, (alloc *= 2) * sizeof(char *));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in_token = false;
|
|
||||||
escaped = false;
|
|
||||||
}
|
}
|
||||||
argv[*argc] = NULL;
|
argv[*argc] = NULL;
|
||||||
return argv;
|
return argv;
|
||||||
|
@ -311,3 +311,50 @@ char *join_list(list_t *list, char *separator) {
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *cmdsep(char **stringp, const char *delim) {
|
||||||
|
char *head = strsep(stringp, delim);
|
||||||
|
// But skip over trailing delims. '3 tokens here' -> '3' 'tokens here'
|
||||||
|
if (*stringp) {
|
||||||
|
*stringp += strspn(*stringp, delim);
|
||||||
|
// If skiping over delims brings us to the end of string, set to NULL
|
||||||
|
if (!**stringp) *stringp = NULL;
|
||||||
|
}
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *argsep(char **stringp, const char *delim) {
|
||||||
|
char *start = *stringp;
|
||||||
|
char *end = start;
|
||||||
|
bool in_string = false;
|
||||||
|
bool in_char = false;
|
||||||
|
bool escaped = false;
|
||||||
|
while (1) {
|
||||||
|
if (*end == '"' && !in_char && !escaped) {
|
||||||
|
in_string = !in_string;
|
||||||
|
} else if (*end == '\'' && !in_string && !escaped) {
|
||||||
|
in_char = !in_char;
|
||||||
|
} else if (*end == '\\') {
|
||||||
|
escaped = !escaped;
|
||||||
|
} else if (*end == '\0') {
|
||||||
|
*stringp = NULL;
|
||||||
|
goto found;
|
||||||
|
} else if (!in_string && !in_char && !escaped && strchr(delim, *end)) {
|
||||||
|
if (end - start) {
|
||||||
|
*(end++) = 0;
|
||||||
|
*stringp = end + strspn(end, delim);;
|
||||||
|
if (!**stringp) *stringp = NULL;
|
||||||
|
goto found;
|
||||||
|
} else {
|
||||||
|
++start;
|
||||||
|
end = start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (*end != '\\') {
|
||||||
|
escaped = false;
|
||||||
|
}
|
||||||
|
++end;
|
||||||
|
}
|
||||||
|
found:
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue