diff --git a/include/commands.h b/include/commands.h index 808e64eb..8fb0c1d8 100644 --- a/include/commands.h +++ b/include/commands.h @@ -5,7 +5,7 @@ struct cmd_handler { char *command; - bool (*handle)(struct sway_config *config, int argc, char **argv); + bool (*handle)(int argc, char **argv); enum { CMD_COMPOSITOR_READY, CMD_KEYBIND, @@ -14,7 +14,7 @@ struct cmd_handler { }; struct cmd_handler *find_handler(char *line); -bool handle_command(struct sway_config *config, char *command); +bool handle_command(char *command); void remove_view_from_scratchpad(); diff --git a/include/config.h b/include/config.h index 653e790f..4070c9ef 100644 --- a/include/config.h +++ b/include/config.h @@ -60,7 +60,7 @@ struct sway_config { bool load_config(const char *file); bool read_config(FILE *file, bool is_active); -char *do_var_replacement(struct sway_config *config, char *str); +char *do_var_replacement(char *str); extern struct sway_config *config; diff --git a/include/stringop.h b/include/stringop.h index 7d17af95..dde50f13 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -2,17 +2,25 @@ #define _SWAY_STRINGOP_H #include "list.h" -char *strip_whitespace(char *str, int *trimmed_start); +// array of whitespace characters to use for delims +extern const char *whitespace; + +char *strip_whitespace(char *str); char *strip_comments(char *str); -// Must be freed 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); void free_flat_list(list_t *list); +// Splits an argument string, keeping quotes intact +char **split_args(const char *str, int *argc); +void free_argv(int argc, char **argv); + char *code_strchr(const char *string, char delimiter); char *code_strstr(const char *haystack, const char *needle); int unescape_string(char *string); char *join_args(char **argv, int argc); char *join_list(list_t *list, char *separator); +char *strdup(const char *); #endif diff --git a/sway/commands.c b/sway/commands.c index 5c782e99..e7ddfa71 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -96,7 +96,7 @@ static int bindsym_sort(const void *_lbind, const void *_rbind) { return (rbind->keys->length + rmod) - (lbind->keys->length + lmod); } -static bool cmd_bindsym(struct sway_config *config, int argc, char **argv) { +static bool cmd_bindsym(int argc, char **argv) { if (!checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1)) { return false; }; @@ -146,7 +146,7 @@ static bool cmd_bindsym(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_exec_always(struct sway_config *config, int argc, char **argv) { +static bool cmd_exec_always(int argc, char **argv) { if (!checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0)) { return false; } @@ -171,14 +171,14 @@ static bool cmd_exec_always(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_exec(struct sway_config *config, int argc, char **argv) { +static bool cmd_exec(int argc, char **argv) { if (config->reloading) { char *args = join_args(argv, argc); sway_log(L_DEBUG, "Ignoring exec %s due to reload", args); free(args); return true; } - return cmd_exec_always(config, argc, argv); + return cmd_exec_always(argc, argv); } static void kill_views(swayc_t *container, void *data) { @@ -187,7 +187,7 @@ static void kill_views(swayc_t *container, void *data) { } } -static bool cmd_exit(struct sway_config *config, int argc, char **argv) { +static bool cmd_exit(int argc, char **argv) { if (!checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0)) { return false; } @@ -197,7 +197,7 @@ static bool cmd_exit(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_floating(struct sway_config *config, int argc, char **argv) { +static bool cmd_floating(int argc, char **argv) { if (!checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1)) { return false; } @@ -258,7 +258,7 @@ static bool cmd_floating(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_floating_mod(struct sway_config *config, int argc, char **argv) { +static bool cmd_floating_mod(int argc, char **argv) { if (!checkarg(argc, "floating_modifier", EXPECTED_EQUAL_TO, 1)) { return false; } @@ -282,7 +282,7 @@ static bool cmd_floating_mod(struct sway_config *config, int argc, char **argv) return true; } -static bool cmd_focus(struct sway_config *config, int argc, char **argv) { +static bool cmd_focus(int argc, char **argv) { static int floating_toggled_index = 0; static int tiled_toggled_index = 0; if (!checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1)) { @@ -340,7 +340,7 @@ static bool cmd_focus(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_focus_follows_mouse(struct sway_config *config, int argc, char **argv) { +static bool cmd_focus_follows_mouse(int argc, char **argv) { if (!checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1)) { return false; } @@ -365,7 +365,39 @@ static void hide_view_in_scratchpad(swayc_t *sp_view) { set_focused_container(container_under_pointer()); } -static bool cmd_move(struct sway_config *config, int argc, char **argv) { +static bool cmd_mode(int argc, char **argv) { + if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) { + return false; + } + const char *mode_name = argv[0]; + struct sway_mode *mode = NULL; + // Find mode + int i, len = config->modes->length; + for (i = 0; i < len; ++i) { + struct sway_mode *find = config->modes->items[i]; + if (strcasecmp(find->name, mode_name)==0) { + mode = find; + break; + } + } + // Create mode if it doesnt exist + if (!mode && argc >= 2 && strncmp(argv[1],"{",1) == 0) { + mode = malloc(sizeof*mode); + mode->name = strdup(mode_name); + mode->bindings = create_list(); + list_add(config->modes, mode); + } + if (!mode) { + sway_log(L_ERROR, "Invalide mode `%s'", mode_name); + return false; + } + sway_log(L_DEBUG, "Switching to mode `%s'",mode->name); + // Set current mode + config->current_mode = mode; + return true; +} + +static bool cmd_move(int argc, char **argv) { if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) { return false; } @@ -435,7 +467,7 @@ static bool cmd_move(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_orientation(struct sway_config *config, int argc, char **argv) { +static bool cmd_orientation(int argc, char **argv) { if (strcasecmp(argv[0], "horizontal") == 0) { config->default_orientation = L_HORIZ; } else if (strcasecmp(argv[0], "vertical") == 0) { @@ -448,7 +480,7 @@ static bool cmd_orientation(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_output(struct sway_config *config, int argc, char **argv) { +static bool cmd_output(int argc, char **argv) { if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) { return false; } @@ -513,7 +545,7 @@ static bool cmd_output(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_gaps(struct sway_config *config, int argc, char **argv) { +static bool cmd_gaps(int argc, char **argv) { if (!checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1)) { return false; } @@ -655,13 +687,13 @@ static bool cmd_gaps(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_kill(struct sway_config *config, int argc, char **argv) { +static bool cmd_kill(int argc, char **argv) { swayc_t *view = get_focused_container(&root_container); wlc_view_close(view->handle); return true; } -static bool cmd_layout(struct sway_config *config, int argc, char **argv) { +static bool cmd_layout(int argc, char **argv) { if (!checkarg(argc, "layout", EXPECTED_MORE_THAN, 0)) { return false; } @@ -686,7 +718,7 @@ static bool cmd_layout(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_reload(struct sway_config *config, int argc, char **argv) { +static bool cmd_reload(int argc, char **argv) { if (!checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0)) { return false; } @@ -697,7 +729,7 @@ static bool cmd_reload(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_resize(struct sway_config *config, int argc, char **argv) { +static bool cmd_resize(int argc, char **argv) { if (!checkarg(argc, "resize", EXPECTED_AT_LEAST, 3)) { return false; } @@ -769,7 +801,7 @@ void remove_view_from_scratchpad(swayc_t *view) { } } -static bool cmd_scratchpad(struct sway_config *config, int argc, char **argv) { +static bool cmd_scratchpad(int argc, char **argv) { if (!checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1)) { return false; } @@ -796,20 +828,18 @@ static bool cmd_scratchpad(struct sway_config *config, int argc, char **argv) { } } -static bool cmd_set(struct sway_config *config, int argc, char **argv) { +static bool cmd_set(int argc, char **argv) { if (!checkarg(argc, "set", EXPECTED_EQUAL_TO, 2)) { return false; } struct sway_variable *var = malloc(sizeof(struct sway_variable)); - var->name = malloc(strlen(argv[0]) + 1); - strcpy(var->name, argv[0]); - var->value = malloc(strlen(argv[1]) + 1); - strcpy(var->value, argv[1]); + var->name = strdup(argv[0]); + var->value = strdup(argv[1]); list_add(config->symbols, var); return true; } -static bool _do_split(struct sway_config *config, int argc, char **argv, int layout) { +static bool _do_split(int argc, char **argv, int layout) { char *name = layout == L_VERT ? "splitv" : layout == L_HORIZ ? "splith" : "split"; if (!checkarg(argc, name, EXPECTED_EQUAL_TO, 0)) { @@ -840,15 +870,15 @@ static bool _do_split(struct sway_config *config, int argc, char **argv, int lay return true; } -static bool cmd_split(struct sway_config *config, int argc, char **argv) { +static bool cmd_split(int argc, char **argv) { if (!checkarg(argc, "split", EXPECTED_EQUAL_TO, 1)) { return false; } if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) { - _do_split(config, argc - 1, argv + 1, L_VERT); + _do_split(argc - 1, argv + 1, L_VERT); } else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) { - _do_split(config, argc - 1, argv + 1, L_HORIZ); + _do_split(argc - 1, argv + 1, L_HORIZ); } else { sway_log(L_ERROR, "Invalid split command (expected either horiziontal or vertical)."); return false; @@ -857,15 +887,15 @@ static bool cmd_split(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_splitv(struct sway_config *config, int argc, char **argv) { - return _do_split(config, argc, argv, L_VERT); +static bool cmd_splitv(int argc, char **argv) { + return _do_split(argc, argv, L_VERT); } -static bool cmd_splith(struct sway_config *config, int argc, char **argv) { - return _do_split(config, argc, argv, L_HORIZ); +static bool cmd_splith(int argc, char **argv) { + return _do_split(argc, argv, L_HORIZ); } -static bool cmd_log_colors(struct sway_config *config, int argc, char **argv) { +static bool cmd_log_colors(int argc, char **argv) { if (!checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1)) { return false; } @@ -878,7 +908,7 @@ static bool cmd_log_colors(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_fullscreen(struct sway_config *config, int argc, char **argv) { +static bool cmd_fullscreen(int argc, char **argv) { if (!checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0)) { return false; } @@ -897,7 +927,7 @@ static bool cmd_fullscreen(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_workspace(struct sway_config *config, int argc, char **argv) { +static bool cmd_workspace(int argc, char **argv) { if (!checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1)) { return false; } @@ -953,7 +983,7 @@ static bool cmd_workspace(struct sway_config *config, int argc, char **argv) { return true; } -static bool cmd_ws_auto_back_and_forth(struct sway_config *config, int argc, char **argv) { +static bool cmd_ws_auto_back_and_forth(int argc, char **argv) { if (!checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1)) { return false; } @@ -979,6 +1009,7 @@ static struct cmd_handler handlers[] = { { "kill", cmd_kill, CMD_KEYBIND }, { "layout", cmd_layout, CMD_KEYBIND }, { "log_colors", cmd_log_colors, CMD_ANYTIME }, + { "mode", cmd_mode, CMD_ANYTIME }, { "move", cmd_move, CMD_KEYBIND }, { "output", cmd_output, CMD_ANYTIME }, { "reload", cmd_reload, CMD_KEYBIND }, @@ -992,58 +1023,6 @@ static struct cmd_handler handlers[] = { { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth, CMD_ANYTIME }, }; -static char **split_directive(char *line, int *argc) { - const char *delimiters = " "; - *argc = 0; - while (isspace(*line) && *line) ++line; - - int capacity = 10; - char **parts = malloc(sizeof(char *) * capacity); - - if (!*line) return parts; - - int in_string = 0, in_character = 0; - int i, j, _; - for (i = 0, j = 0; line[i]; ++i) { - if (line[i] == '\\') { - ++i; - } else if (line[i] == '"' && !in_character) { - in_string = !in_string; - } else if (line[i] == '\'' && !in_string) { - in_character = !in_character; - } else if (!in_character && !in_string) { - if (strchr(delimiters, line[i]) != NULL) { - char *item = malloc(i - j + 1); - strncpy(item, line + j, i - j); - item[i - j] = '\0'; - item = strip_whitespace(item, &_); - if (item[0] == '\0') { - free(item); - } else { - if (*argc == capacity) { - capacity *= 2; - parts = realloc(parts, sizeof(char *) * capacity); - } - parts[*argc] = item; - j = i + 1; - ++*argc; - } - } - } - } - char *item = malloc(i - j + 1); - strncpy(item, line + j, i - j); - item[i - j] = '\0'; - item = strip_whitespace(item, &_); - if (*argc == capacity) { - capacity++; - parts = realloc(parts, sizeof(char *) * capacity); - } - parts[*argc] = item; - ++*argc; - return parts; -} - static int handler_compare(const void *_a, const void *_b) { const struct cmd_handler *a = _a; const struct cmd_handler *b = _b; @@ -1058,44 +1037,25 @@ struct cmd_handler *find_handler(char *line) { return res; } -bool handle_command(struct sway_config *config, char *exec) { +bool handle_command(char *exec) { sway_log(L_INFO, "Handling command '%s'", exec); - char *ptr, *cmd; - bool exec_success; - - if ((ptr = strchr(exec, ' ')) == NULL) { - cmd = exec; - } else { - int index = ptr - exec; - cmd = malloc(index + 1); - strncpy(cmd, exec, index); - cmd[index] = '\0'; + int argc; + char **argv = split_args(exec, &argc); + if (argc == 0) { + return false; } - struct cmd_handler *handler = find_handler(cmd); - if (handler == NULL) { - sway_log(L_ERROR, "Unknown command '%s'", cmd); - exec_success = false; // TODO: return error, probably - } else { - int argc; - char **argv = split_directive(exec + strlen(handler->command), &argc); + struct cmd_handler *handler = find_handler(argv[0]); + bool exec_success = false; + if (handler) { int i; - - // Perform var subs on all parts of the command - for (i = 0; i < argc; ++i) { - argv[i] = do_var_replacement(config, argv[i]); - } - - exec_success = handler->handle(config, argc, argv); - for (i = 0; i < argc; ++i) { - free(argv[i]); - } - free(argv); - if (!exec_success) { - sway_log(L_ERROR, "Command failed: %s", cmd); + for (i = 1; i < argc; ++i) { + argv[i] = do_var_replacement(argv[i]); } + exec_success = handler->handle(argc - 1, argv + 1); } - if (ptr) { - free(cmd); + if (exec_success == false) { + sway_log(L_ERROR, "Command failed: %s", argv[0]); } + free_argv(argc, argv); return exec_success; } diff --git a/sway/config.c b/sway/config.c index b64dd4b1..5ece2810 100644 --- a/sway/config.c +++ b/sway/config.c @@ -13,6 +13,7 @@ struct sway_config *config = NULL; + static void free_variable(struct sway_variable *var) { free(var->name); free(var->value); @@ -46,39 +47,7 @@ static void free_workspace_output(struct workspace_output *wo) { free(wo); } -static bool file_exists(const char *path) { - return access(path, R_OK) != -1; -} - -static void config_defaults(struct sway_config *config) { - config->symbols = create_list(); - config->modes = create_list(); - config->workspace_outputs = create_list(); - config->output_configs = create_list(); - - config->cmd_queue = create_list(); - - config->current_mode = malloc(sizeof(struct sway_mode)); - config->current_mode->name = NULL; - config->current_mode->bindings = create_list(); - list_add(config->modes, config->current_mode); - - config->floating_mod = 0; - config->default_layout = L_NONE; - config->default_orientation = L_NONE; - // Flags - config->focus_follows_mouse = true; - config->mouse_warping = true; - config->reloading = false; - config->active = false; - config->failed = false; - config->auto_back_and_forth = false; - - config->gaps_inner = 0; - config->gaps_outer = 0; -} - -void free_config(struct sway_config *config) { +static void free_config(struct sway_config *config) { int i; for (i = 0; i < config->symbols->length; ++i) { free_variable(config->symbols->items[i]); @@ -104,6 +73,40 @@ void free_config(struct sway_config *config) { free(config); } + +static bool file_exists(const char *path) { + return access(path, R_OK) != -1; +} + +static void config_defaults(struct sway_config *config) { + config->symbols = create_list(); + config->modes = create_list(); + config->workspace_outputs = create_list(); + config->output_configs = create_list(); + + config->cmd_queue = create_list(); + + config->current_mode = malloc(sizeof(struct sway_mode)); + config->current_mode->name = malloc(sizeof("default")); + strcpy(config->current_mode->name, "default"); + config->current_mode->bindings = create_list(); + list_add(config->modes, config->current_mode); + + config->floating_mod = 0; + config->default_layout = L_NONE; + config->default_orientation = L_NONE; + // Flags + config->focus_follows_mouse = true; + config->mouse_warping = true; + config->reloading = false; + config->active = false; + config->failed = false; + config->auto_back_and_forth = false; + + config->gaps_inner = 0; + config->gaps_outer = 0; +} + static char *get_config_path(void) { char *config_path = NULL; char *paths[3] = {getenv("HOME"), getenv("XDG_CONFIG_HOME"), ""}; @@ -210,47 +213,48 @@ bool load_config(const char *file) { } bool read_config(FILE *file, bool is_active) { - struct sway_config *temp_config = malloc(sizeof(struct sway_config)); - config_defaults(temp_config); + struct sway_config *old_config = config; + struct sway_mode *default_mode; + config = malloc(sizeof(struct sway_config)); + + config_defaults(config); + default_mode = config->current_mode; + if (is_active) { sway_log(L_DEBUG, "Performing configuration file reload"); - temp_config->reloading = true; - temp_config->active = true; + config->reloading = true; + config->active = true; } - bool success = true; - int temp_depth = 0; // Temporary: skip all config sections with depth - + char *line; while (!feof(file)) { - int _; - char *line = read_line(file); - line = strip_whitespace(line, &_); + line = read_line(file); + line = strip_whitespace(line); line = strip_comments(line); - if (!line[0]) { + if (line[0] == '\0') { goto _continue; } - if (temp_depth && line[0] == '}') { - temp_depth--; + if (line[0] == '}') { + config->current_mode = default_mode; goto _continue; } // Any command which would require wlc to be initialized // should be queued for later execution - list_t *args = split_string(line, " "); + list_t *args = split_string(line, whitespace); struct cmd_handler *handler; if ((handler = find_handler(args->items[0]))) { if (handler->config_type == CMD_KEYBIND) { sway_log(L_ERROR, "Invalid command during config ``%s''", line); } else if (handler->config_type == CMD_COMPOSITOR_READY && !is_active) { sway_log(L_DEBUG, "Deferring command ``%s''", line); - char *cmd = malloc(strlen(line) + 1); - strcpy(cmd, line); - list_add(temp_config->cmd_queue, cmd); - } else if (!temp_depth && !handle_command(temp_config, line)) { + char *cmd = strdup(line); + list_add(config->cmd_queue, cmd); + } else if (!handle_command(line)) { sway_log(L_DEBUG, "Config load failed for line ``%s''", line); success = false; - temp_config->failed = true; + config->failed = true; } } else { sway_log(L_ERROR, "Invalid command ``%s''", line); @@ -258,25 +262,21 @@ bool read_config(FILE *file, bool is_active) { free_flat_list(args); _continue: - if (line && line[strlen(line) - 1] == '{') { - temp_depth++; - } free(line); } if (is_active) { - temp_config->reloading = false; + config->reloading = false; arrange_windows(&root_container, -1, -1); } - if (config) { - free_config(config); + if (old_config) { + free_config(old_config); } - config = temp_config; return success; } -char *do_var_replacement(struct sway_config *config, char *str) { +char *do_var_replacement(char *str) { // TODO: Handle escaping $ and using $ in string literals int i; for (i = 0; str[i]; ++i) { diff --git a/sway/handlers.c b/sway/handlers.c index 482d52c6..c0472817 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -130,13 +130,6 @@ static void handle_output_resolution_change(wlc_handle output, const struct wlc_ if (!c) return; c->width = to->w; c->height = to->h; - if (config->default_layout == L_NONE && config->default_orientation == L_NONE) { - if (c->width >= c->height) { - ((swayc_t*)c->children->items[0])->layout = L_HORIZ; - } else { - ((swayc_t*)c->children->items[0])->layout = L_VERT; - } - } arrange_windows(&root_container, -1, -1); } @@ -348,7 +341,7 @@ static bool handle_key(wlc_handle view, uint32_t time, const struct wlc_modifier } if (match) { if (state == WLC_KEY_STATE_PRESSED) { - handle_command(config, binding->command); + handle_command(binding->command); return EVENT_HANDLED; } else if (state == WLC_KEY_STATE_RELEASED) { // TODO: --released @@ -486,7 +479,7 @@ static void handle_wlc_ready(void) { sway_log(L_DEBUG, "Compositor is ready, executing cmds in queue"); // Execute commands until there are none left while (config->cmd_queue->length) { - handle_command(config, config->cmd_queue->items[0]); + handle_command(config->cmd_queue->items[0]); free(config->cmd_queue->items[0]); list_del(config->cmd_queue, 0); } diff --git a/sway/ipc.c b/sway/ipc.c index f6217bfd..abf2ed0c 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -198,7 +198,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_COMMAND: { buf[client->payload_length] = '\0'; - bool success = handle_command(config, buf); + bool success = handle_command(buf); char reply[64]; int length = snprintf(reply, sizeof(reply), "{\"success\":%s}", success ? "true" : "false"); ipc_send_reply(client, reply, (uint32_t) length); diff --git a/sway/stringop.c b/sway/stringop.c index c39e2c34..1ba54ec6 100644 --- a/sway/stringop.c +++ b/sway/stringop.c @@ -1,27 +1,23 @@ -#include "stringop.h" #include #include +#include +#include +#include "stringop.h" +#include "log.h" #include "string.h" #include "list.h" -#include -#include + +const char *whitespace = " \f\n\r\t\v"; /* Note: This returns 8 characters for trimmed_start per tab character. */ -char *strip_whitespace(char *_str, int *trimmed_start) { - *trimmed_start = 0; +char *strip_whitespace(char *_str) { if (*_str == '\0') return _str; char *strold = _str; while (*_str == ' ' || *_str == '\t') { - if (*_str == '\t') { - *trimmed_start += 8; - } else { - *trimmed_start += 1; - } _str++; } - char *str = malloc(strlen(_str) + 1); - strcpy(str, _str); + char *str = strdup(_str); free(strold); int i; for (i = 0; str[i] != '\0'; ++i); @@ -41,7 +37,7 @@ char *strip_comments(char *str) { } else if (str[i] == '\'' && !in_string) { in_character = !in_character; } else if (!in_character && !in_string) { - if (str[i] == '#' && i == 0) { + if (str[i] == '#') { str[i] = '\0'; break; } @@ -51,26 +47,44 @@ char *strip_comments(char *str) { return str; } +void strip_quotes(char *str) { + bool in_str = false; + bool in_chr = false; + bool escaped = false; + char *end = strchr(str,0); + while (*str) { + if (*str == '\'' && !in_str && !escaped) { + in_chr = !in_chr; + goto shift_over; + } else if (*str == '\"' && !in_chr && !escaped) { + in_str = !in_str; + goto shift_over; + } else if (*str == '\\') { + escaped = !escaped; + ++str; + continue; + } + escaped = false; + ++str; + continue; + shift_over: + memmove(str, str+1, end-- - str); + } + *end = '\0'; +} + list_t *split_string(const char *str, const char *delims) { list_t *res = create_list(); - int i, j; - int len = strlen(str); - for (i = 0, j = 0; i < len + 1; ++i) { - if (strchr(delims, str[i]) || i == len) { - if (i - j == 0) { - continue; - } - char *left = malloc(i - j + 1); - memcpy(left, str + j, i - j); - left[i - j] = 0; - list_add(res, left); - j = i + 1; - while (j <= len && str[j] && strchr(delims, str[j])) { - j++; - i++; - } - } + char *copy = strdup(str); + char *token; + + token = strtok(copy, delims); + while(token) { + token = strdup(token); + list_add(res, token); + token = strtok(NULL, delims); } + free(copy); return res; } @@ -82,6 +96,60 @@ void free_flat_list(list_t *list) { list_free(list); } +char **split_args(const char *start, int *argc) { + *argc = 0; + int alloc = 2; + char **parts = malloc(sizeof(char *) * alloc); + bool in_token = false; + bool in_string = false; + bool in_char = false; + bool escaped = false; + const char *end = start; + while (*start) { + if (!in_token) { + start = (end += strspn(end, whitespace)); + in_token = true; + } + 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' || (!in_string && !in_char && !escaped + && strchr(whitespace, *end))) { + goto add_part; + } + if (*end != '\\') { + 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); + parts[*argc] = token; + if (++*argc == alloc) { + parts = realloc(parts, (alloc *= 2) * sizeof(char *)); + } + } + in_token = false; + escaped = false; + } + return parts; +} + +void free_argv(int argc, char **argv) { + while (--argc) { + free(argv[argc]); + } + free(argv); +} + char *code_strstr(const char *haystack, const char *needle) { /* TODO */ return strstr(haystack, needle); @@ -177,7 +245,7 @@ int unescape_string(char *string) { string[i - 1] = c; } } - memmove(string + i, string + i + shift, len - i); + memmove(string + i, string + i + shift, len - i + 1); } } return len;