merge
This commit is contained in:
taiyu 2015-09-22 09:41:32 -07:00
commit 4944996170
9 changed files with 316 additions and 152 deletions

View file

@ -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);

View file

@ -8,10 +8,11 @@ extern int setenv(const char *, const char *, int);
#endif #endif
// array of whitespace characters to use for delims // array of whitespace characters to use for delims
extern const char *whitespace; 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);
@ -27,5 +28,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

View file

@ -20,6 +20,40 @@
#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_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;
swayc_t *sp_view; swayc_t *sp_view;
int sp_index = 0; int sp_index = 0;
@ -147,37 +181,33 @@ 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) {
if (!config->active) return CMD_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 };
/* Failed to fork */ sway_log(L_DEBUG, "Executing %s", cmd);
if (pid < 0) {
sway_log(L_ERROR, "exec command failed, sway did not fork"); pid_t pid;
if ((pid = fork()) == 0) {
execv("/bin/sh", args);
_exit(-1);
} else if (pid < 0) {
sway_log(L_ERROR, "exec command failed, sway could not fork");
return CMD_FAILURE; return CMD_FAILURE;
} }
/* Child process */
if (pid == 0) {
char *args = join_args(argv, argc);
sway_log(L_DEBUG, "Executing %s", args);
execl("/bin/sh", "sh", "-c", args, (char *)NULL);
/* Execl doesnt return unless failure */
sway_log(L_ERROR, "could not find /bin/sh");
free(args);
exit(-1);
}
/* 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) { if (!config->active) return CMD_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);
@ -194,8 +224,8 @@ static void kill_views(swayc_t *container, void *data) {
} }
static enum cmd_status cmd_exit(int argc, char **argv) { static enum cmd_status cmd_exit(int argc, char **argv) {
if (!checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0) if (config->reading) return CMD_INVALID;
|| config->reading || !config->active) { if (!checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0)) {
return CMD_FAILURE; return CMD_FAILURE;
} }
// Close all views // Close all views
@ -205,8 +235,8 @@ static enum cmd_status cmd_exit(int argc, char **argv) {
} }
static enum cmd_status cmd_floating(int argc, char **argv) { static enum cmd_status cmd_floating(int argc, char **argv) {
if (!checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1) if (config->reading) return CMD_INVALID;
|| config->reading || !config->active) { if (!checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1)) {
return CMD_FAILURE; return CMD_FAILURE;
} }
@ -267,8 +297,8 @@ static enum cmd_status cmd_floating(int argc, char **argv) {
} }
static enum cmd_status cmd_floating_mod(int argc, char **argv) { static enum cmd_status cmd_floating_mod(int argc, char **argv) {
if (!checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1) if (!config->reading) return CMD_INVALID;
|| !config->reading) { if (!checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1)) {
return CMD_FAILURE; return CMD_FAILURE;
} }
int i, j; int i, j;
@ -292,10 +322,10 @@ static enum cmd_status cmd_floating_mod(int argc, char **argv) {
} }
static enum cmd_status cmd_focus(int argc, char **argv) { static enum cmd_status cmd_focus(int argc, char **argv) {
if (config->reading) return CMD_INVALID;
static int floating_toggled_index = 0; static int floating_toggled_index = 0;
static int tiled_toggled_index = 0; static int tiled_toggled_index = 0;
if (!checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1) if (!checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1)) {
|| config->reading || !config->active) {
return CMD_FAILURE; return CMD_FAILURE;
} }
if (strcasecmp(argv[0], "left") == 0) { if (strcasecmp(argv[0], "left") == 0) {
@ -350,6 +380,7 @@ static enum cmd_status cmd_focus(int argc, char **argv) {
} }
static enum cmd_status cmd_focus_follows_mouse(int argc, char **argv) { static enum cmd_status cmd_focus_follows_mouse(int argc, char **argv) {
if (!config->reading) return CMD_INVALID;
if (!checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1)) { if (!checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1)) {
return CMD_FAILURE; return CMD_FAILURE;
} }
@ -359,7 +390,7 @@ static enum cmd_status cmd_focus_follows_mouse(int argc, char **argv) {
} }
static void hide_view_in_scratchpad(swayc_t *sp_view) { static void hide_view_in_scratchpad(swayc_t *sp_view) {
if(sp_view == NULL) { if (sp_view == NULL) {
return; return;
} }
@ -375,15 +406,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) {
if (!config->reading) return CMD_INVALID;
// 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;
@ -406,16 +441,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) if (config->reading) return CMD_FAILURE;
|| config->reading || !config->active) { if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) {
return CMD_FAILURE; return CMD_FAILURE;
} }
@ -485,10 +522,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) if (!config->reading) return CMD_FAILURE;
|| !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) {
@ -502,6 +540,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) {
if (!config->reading) return CMD_FAILURE;
if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) { if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) {
return CMD_FAILURE; return CMD_FAILURE;
} }
@ -512,7 +551,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;
} }
@ -953,7 +991,7 @@ static enum cmd_status cmd_log_colors(int argc, char **argv) {
} }
if (strcasecmp(argv[0], "no") == 0) { if (strcasecmp(argv[0], "no") == 0) {
sway_log_colors(0); sway_log_colors(0);
} else if(strcasecmp(argv[0], "yes") == 0) { } else if (strcasecmp(argv[0], "yes") == 0) {
sway_log_colors(1); sway_log_colors(1);
} else { } else {
sway_log(L_ERROR, "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]); sway_log(L_ERROR, "Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]);
@ -1089,57 +1127,88 @@ 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); 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; if (!argc) goto cleanup;
struct cmd_handler *handler;
if (!argc) { sway_log(L_INFO, "handling config command '%s'", exec);
status = CMD_SUCCESS; // Endblock
if (**argv == '}') {
status = CMD_BLOCK_END;
goto cleanup; goto cleanup;
} }
// TODO better block handling struct cmd_handler *handler = find_handler(argv[0]);
if (strncmp(argv[0], "}", 1) == 0) { if (!handler) {
config->current_mode = config->modes->items[0]; status = CMD_INVALID;
status = CMD_SUCCESS;
goto cleanup; goto cleanup;
} }
if ((handler = find_handler(argv[0]))) { int i;
// Dont replace first argument in cmd_set // Var replacement, for all but first argument of set
int i = handler->handle == cmd_set ? 2 : 1; for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
int e = argc; argv[i] = do_var_replacement(argv[i]);
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);
} }
/* 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;

View file

@ -110,47 +110,49 @@ static void config_defaults(struct sway_config *config) {
static char *get_config_path(void) { static char *get_config_path(void) {
char *config_path = NULL; char *config_path = NULL;
char *paths[3] = {getenv("HOME"), getenv("XDG_CONFIG_HOME"), ""}; char *paths[3] = { getenv("HOME"), getenv("XDG_CONFIG_HOME"), "" };
int pathlen[3] = {0, 0, 0}; int pathlen[3] = { 0, 0, 0 };
int i; int i;
#define home paths[0] #define home paths[0]
#define conf paths[1] #define conf paths[1]
// Get home and config directories // Get home and config directories
conf = conf ? strdup(conf) : NULL;
home = home ? strdup(home) : NULL; home = home ? strdup(home) : NULL;
if (conf) { // If config folder is unset, set it to $HOME/.config
conf = strdup(conf); if (!conf && home) {
} else if (home) {
const char *def = "/.config"; const char *def = "/.config";
conf = malloc(strlen(home) + strlen(def) + 1); conf = malloc(strlen(home) + strlen(def) + 1);
strcpy(conf, home); strcpy(conf, home);
strcat(conf, def); strcat(conf, def);
} else {
home = strdup("");
conf = strdup("");
} }
pathlen[0] = strlen(home); // Get path lengths
pathlen[1] = strlen(conf); pathlen[0] = home ? strlen(home) : 0;
pathlen[1] = conf ? strlen(conf) : 0;
#undef home #undef home
#undef conf #undef conf
// Search for config file from search paths // Search for config file from search paths
static const char *search_paths[] = { static const char *search_paths[] = {
"/.sway/config", // Prepend with $home "/.sway/config", // Prepend with $home
"/sway/config", // Prepend with $config "/sway/config", // Prepend with $config
"/etc/sway/config", "/etc/sway/config",
"/.i3/config", // $home "/.i3/config", // $home
"/.i3/config", // $config "/i3/config", // $config
"/etc/i3/config" "/etc/i3/config"
}; };
for (i = 0; i < (int)(sizeof(search_paths) / sizeof(char *)); ++i) { for (i = 0; i < (int)(sizeof(search_paths) / sizeof(char *)); ++i) {
char *test = malloc(pathlen[i%3] + strlen(search_paths[i]) + 1); // Only try path if it is set by enviroment variables
strcpy(test, paths[i%3]); if (paths[i%3]) {
strcat(test, search_paths[i]); char *test = malloc(pathlen[i%3] + strlen(search_paths[i]) + 1);
sway_log(L_DEBUG, "Checking for config at %s", test); strcpy(test, paths[i%3]);
if (file_exists(test)) { strcpy(test + pathlen[i%3], search_paths[i]);
config_path = test; sway_log(L_DEBUG, "Checking for config at %s", test);
goto cleanup; if (file_exists(test)) {
config_path = test;
goto cleanup;
}
free(test);
} }
free(test);
} }
sway_log(L_DEBUG, "Trying to find config in XDG_CONFIG_DIRS"); sway_log(L_DEBUG, "Trying to find config in XDG_CONFIG_DIRS");
@ -225,14 +227,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);
} }

View file

@ -405,7 +405,7 @@ swayc_t *swayc_by_test(swayc_t *container, bool (*test)(swayc_t *view, void *dat
} }
static bool test_name(swayc_t *view, void *data) { static bool test_name(swayc_t *view, void *data) {
if (!view && !view->name) { if (!view || !view->name) {
return false; return false;
} }
return strcmp(view->name, data) == 0; return strcmp(view->name, data) == 0;

View file

@ -331,7 +331,7 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier
struct sway_binding *binding = mode->bindings->items[i]; struct sway_binding *binding = mode->bindings->items[i];
if ((modifiers->mods ^ binding->modifiers) == 0) { if ((modifiers->mods ^ binding->modifiers) == 0) {
bool match; bool match = false;
int j; int j;
for (j = 0; j < binding->keys->length; ++j) { for (j = 0; j < binding->keys->length; ++j) {
xkb_keysym_t *key = binding->keys->items[j]; xkb_keysym_t *key = binding->keys->items[j];

View file

@ -206,7 +206,8 @@ void swap_geometry(swayc_t *a, swayc_t *b) {
void move_container(swayc_t *container, enum movement_direction dir) { void move_container(swayc_t *container, enum movement_direction dir) {
enum swayc_layouts layout; enum swayc_layouts layout;
if (container->is_floating) { if (container->is_floating
|| (container->type != C_VIEW && container->type != C_CONTAINER)) {
return; return;
} }
if (dir == MOVE_UP || dir == MOVE_DOWN) { if (dir == MOVE_UP || dir == MOVE_DOWN) {

View file

@ -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) {

View file

@ -1,5 +1,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <strings.h> #include <strings.h>
#include <ctype.h> #include <ctype.h>
#include "stringop.h" #include "stringop.h"
@ -7,7 +8,7 @@
#include "string.h" #include "string.h"
#include "list.h" #include "list.h"
const char *whitespace = " \f\n\r\t\v"; const char whitespace[] = " \f\n\r\t\v";
/* Note: This returns 8 characters for trimmed_start per tab character. */ /* Note: This returns 8 characters for trimmed_start per tab character. */
char *strip_whitespace(char *_str) { char *strip_whitespace(char *_str) {
@ -105,40 +106,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;
@ -312,6 +313,56 @@ char *join_list(list_t *list, char *separator) {
return res; return res;
} }
char *cmdsep(char **stringp, const char *delim) {
// skip over leading delims
char *head = *stringp + strspn(*stringp, delim);
// Find end token
char *tail = *stringp += strcspn(*stringp, delim);
// Set stringp to begining of next token
*stringp += strspn(*stringp, delim);
// Set stringp to null if last token
if (!**stringp) *stringp = NULL;
// Nullify end of first token
*tail = 0;
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;
}
char *strdup(const char *str) { char *strdup(const char *str) {
char *dup = malloc(strlen(str) + 1); char *dup = malloc(strlen(str) + 1);
if (dup) { if (dup) {
@ -319,3 +370,4 @@ char *strdup(const char *str) {
} }
return dup; return dup;
} }