diff options
-rw-r--r-- | include/commands.h | 16 | ||||
-rw-r--r-- | include/stringop.h | 6 | ||||
-rw-r--r-- | sway/commands.c | 214 | ||||
-rw-r--r-- | sway/config.c | 34 | ||||
-rw-r--r-- | sway/log.c | 8 | ||||
-rw-r--r-- | sway/stringop.c | 111 |
6 files changed, 280 insertions, 109 deletions
diff --git a/include/commands.h b/include/commands.h index 5c87be51..1b4cd9ca 100644 --- a/include/commands.h +++ b/include/commands.h | |||
@@ -3,13 +3,15 @@ | |||
3 | #include <stdbool.h> | 3 | #include <stdbool.h> |
4 | #include "config.h" | 4 | #include "config.h" |
5 | 5 | ||
6 | struct cmd_handler { | 6 | |
7 | char *command; | 7 | enum cmd_status { |
8 | enum cmd_status { | 8 | CMD_SUCCESS, |
9 | CMD_SUCCESS, | 9 | CMD_FAILURE, |
10 | CMD_FAILURE, | 10 | CMD_INVALID, |
11 | CMD_DEFER, | 11 | CMD_DEFER, |
12 | } (*handle)(int argc, char **argv); | 12 | // Config Blocks |
13 | CMD_BLOCK_END, | ||
14 | CMD_BLOCK_MODE, | ||
13 | }; | 15 | }; |
14 | 16 | ||
15 | enum cmd_status handle_command(char *command); | 17 | enum cmd_status handle_command(char *command); |
diff --git a/include/stringop.h b/include/stringop.h index dde50f13..dc81cdae 100644 --- a/include/stringop.h +++ b/include/stringop.h | |||
@@ -7,6 +7,7 @@ extern const char *whitespace; | |||
7 | 7 | ||
8 | char *strip_whitespace(char *str); | 8 | char *strip_whitespace(char *str); |
9 | char *strip_comments(char *str); | 9 | char *strip_comments(char *str); |
10 | void strip_quotes(char *str); | ||
10 | 11 | ||
11 | // Simply split a string with delims, free with `free_flat_list` | 12 | // Simply split a string with delims, free with `free_flat_list` |
12 | list_t *split_string(const char *str, const char *delims); | 13 | list_t *split_string(const char *str, const char *delims); |
@@ -22,5 +23,10 @@ int unescape_string(char *string); | |||
22 | char *join_args(char **argv, int argc); | 23 | char *join_args(char **argv, int argc); |
23 | char *join_list(list_t *list, char *separator); | 24 | char *join_list(list_t *list, char *separator); |
24 | 25 | ||
26 | // split string into 2 by delim. | ||
27 | char *cmdsep(char **stringp, const char *delim); | ||
28 | // Split string into 2 by delim, handle quotes | ||
29 | char *argsep(char **stringp, const char *delim); | ||
30 | |||
25 | char *strdup(const char *); | 31 | char *strdup(const char *); |
26 | #endif | 32 | #endif |
diff --git a/sway/commands.c b/sway/commands.c index e79746ae..c426928e 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -18,6 +18,45 @@ | |||
18 | #include "sway.h" | 18 | #include "sway.h" |
19 | #include "resize.h" | 19 | #include "resize.h" |
20 | 20 | ||
21 | typedef enum cmd_status sway_cmd(int argc, char **argv); | ||
22 | |||
23 | struct cmd_handler { | ||
24 | char *command; | ||
25 | sway_cmd *handle; | ||
26 | }; | ||
27 | |||
28 | static sway_cmd cmd_bindsym; | ||
29 | static sway_cmd cmd_orientation; | ||
30 | static sway_cmd cmd_exec; | ||
31 | static sway_cmd cmd_exec_always; | ||
32 | static sway_cmd cmd_exit; | ||
33 | static sway_cmd cmd_floating; | ||
34 | static sway_cmd cmd_floating_mod; | ||
35 | static sway_cmd cmd_focus; | ||
36 | static sway_cmd cmd_focus_follows_mouse; | ||
37 | static sway_cmd cmd_for_window; | ||
38 | static sway_cmd cmd_fullscreen; | ||
39 | static sway_cmd cmd_gaps; | ||
40 | static sway_cmd cmd_kill; | ||
41 | static sway_cmd cmd_layout; | ||
42 | static sway_cmd cmd_log_colors; | ||
43 | static sway_cmd cmd_mode; | ||
44 | static sway_cmd cmd_move; | ||
45 | static sway_cmd cmd_output; | ||
46 | static sway_cmd cmd_reload; | ||
47 | static sway_cmd cmd_resize; | ||
48 | static sway_cmd cmd_scratchpad; | ||
49 | static sway_cmd cmd_set; | ||
50 | static sway_cmd cmd_split; | ||
51 | static sway_cmd cmd_splith; | ||
52 | static sway_cmd cmd_splitv; | ||
53 | static sway_cmd cmd_workspace; | ||
54 | static sway_cmd cmd_ws_auto_back_and_forth; | ||
55 | |||
56 | #define NO_BIND() if (!config->reading) return CMD_FAILURE; | ||
57 | #define NO_CONF() if (config->reading) return CMD_FAILURE; | ||
58 | #define DEFER() if (!config->active) return CMD_DEFER; | ||
59 | |||
21 | swayc_t *sp_view; | 60 | swayc_t *sp_view; |
22 | int sp_index = 0; | 61 | int sp_index = 0; |
23 | 62 | ||
@@ -145,14 +184,19 @@ static enum cmd_status cmd_bindsym(int argc, char **argv) { | |||
145 | } | 184 | } |
146 | 185 | ||
147 | static enum cmd_status cmd_exec_always(int argc, char **argv) { | 186 | static enum cmd_status cmd_exec_always(int argc, char **argv) { |
187 | DEFER(); | ||
148 | if (!checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0)) { | 188 | if (!checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0)) { |
149 | return CMD_FAILURE; | 189 | return CMD_FAILURE; |
150 | } | 190 | } |
151 | if (!config->active) { | 191 | // Put argument into cmd array |
152 | return CMD_DEFER; | 192 | char *tmp = join_args(argv, argc); |
153 | } | 193 | char cmd[4096]; |
194 | strcpy(cmd, tmp); | ||
195 | free(tmp); | ||
196 | |||
197 | char *args[] = {"sh", "-c", cmd, 0 }; | ||
154 | 198 | ||
155 | pid_t pid = fork(); | 199 | pid_t pid = vfork(); |
156 | /* Failed to fork */ | 200 | /* Failed to fork */ |
157 | if (pid < 0) { | 201 | if (pid < 0) { |
158 | sway_log(L_ERROR, "exec command failed, sway did not fork"); | 202 | 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) { | |||
160 | } | 204 | } |
161 | /* Child process */ | 205 | /* Child process */ |
162 | if (pid == 0) { | 206 | if (pid == 0) { |
163 | char *args = join_args(argv, argc); | 207 | sway_log(L_DEBUG, "Executing %s", cmd); |
164 | sway_log(L_DEBUG, "Executing %s", args); | 208 | execv("/bin/sh", args); |
165 | execl("/bin/sh", "sh", "-c", args, (char *)NULL); | 209 | /* Execv doesnt return unless failure */ |
166 | /* Execl doesnt return unless failure */ | 210 | sway_log(L_ERROR, "execv failde to return"); |
167 | sway_log(L_ERROR, "could not find /bin/sh"); | 211 | _exit(-1); |
168 | free(args); | ||
169 | exit(-1); | ||
170 | } | 212 | } |
171 | /* Parent */ | 213 | /* Parent */ |
172 | return CMD_SUCCESS; | 214 | return CMD_SUCCESS; |
173 | } | 215 | } |
174 | 216 | ||
175 | static enum cmd_status cmd_exec(int argc, char **argv) { | 217 | static enum cmd_status cmd_exec(int argc, char **argv) { |
176 | if (!config->active) { | 218 | DEFER(); |
177 | return CMD_DEFER; | ||
178 | } | ||
179 | if (config->reloading) { | 219 | if (config->reloading) { |
180 | char *args = join_args(argv, argc); | 220 | char *args = join_args(argv, argc); |
181 | sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args); | 221 | 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) { | |||
373 | } | 413 | } |
374 | 414 | ||
375 | static enum cmd_status cmd_mode(int argc, char **argv) { | 415 | static enum cmd_status cmd_mode(int argc, char **argv) { |
376 | if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) { | 416 | if (!checkarg(argc, "mode", EXPECTED_AT_LEAST, 1)) { |
377 | return CMD_FAILURE; | 417 | return CMD_FAILURE; |
378 | } | 418 | } |
379 | bool mode_make = !strcmp(argv[argc-1], "{"); | 419 | char *mode_name = join_args(argv, argc); |
380 | if (mode_make && !config->reading) { | 420 | int mode_len = strlen(mode_name); |
381 | return CMD_FAILURE; | 421 | bool mode_make = mode_name[mode_len-1] == '{'; |
422 | if (mode_make) { | ||
423 | NO_BIND(); | ||
424 | // Trim trailing spaces | ||
425 | do { | ||
426 | mode_name[--mode_len] = 0; | ||
427 | } while(isspace(mode_name[mode_len-1])); | ||
382 | } | 428 | } |
383 | |||
384 | char *mode_name = join_args(argv, argc - mode_make); | ||
385 | struct sway_mode *mode = NULL; | 429 | struct sway_mode *mode = NULL; |
386 | // Find mode | 430 | // Find mode |
387 | int i, len = config->modes->length; | 431 | int i, len = config->modes->length; |
@@ -404,16 +448,18 @@ static enum cmd_status cmd_mode(int argc, char **argv) { | |||
404 | free(mode_name); | 448 | free(mode_name); |
405 | return CMD_FAILURE; | 449 | return CMD_FAILURE; |
406 | } | 450 | } |
407 | sway_log(L_DEBUG, "Switching to mode `%s'",mode->name); | 451 | if ((config->reading && mode_make) || (!config->reading && !mode_make)) { |
452 | sway_log(L_DEBUG, "Switching to mode `%s'",mode->name); | ||
453 | } | ||
408 | free(mode_name); | 454 | free(mode_name); |
409 | // Set current mode | 455 | // Set current mode |
410 | config->current_mode = mode; | 456 | config->current_mode = mode; |
411 | return CMD_SUCCESS; | 457 | return mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS; |
412 | } | 458 | } |
413 | 459 | ||
414 | static enum cmd_status cmd_move(int argc, char **argv) { | 460 | static enum cmd_status cmd_move(int argc, char **argv) { |
415 | if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1) | 461 | NO_CONF(); |
416 | || config->reading || !config->active) { | 462 | if (!checkarg(argc, "move", EXPECTED_AT_LEAST, 1)) { |
417 | return CMD_FAILURE; | 463 | return CMD_FAILURE; |
418 | } | 464 | } |
419 | 465 | ||
@@ -483,10 +529,11 @@ static enum cmd_status cmd_move(int argc, char **argv) { | |||
483 | } | 529 | } |
484 | 530 | ||
485 | static enum cmd_status cmd_orientation(int argc, char **argv) { | 531 | static enum cmd_status cmd_orientation(int argc, char **argv) { |
486 | if (!checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1) | 532 | NO_BIND(); |
487 | || !config->reading) { | 533 | if (!checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1)) { |
488 | return CMD_FAILURE; | 534 | return CMD_FAILURE; |
489 | } | 535 | } |
536 | |||
490 | if (strcasecmp(argv[0], "horizontal") == 0) { | 537 | if (strcasecmp(argv[0], "horizontal") == 0) { |
491 | config->default_orientation = L_HORIZ; | 538 | config->default_orientation = L_HORIZ; |
492 | } else if (strcasecmp(argv[0], "vertical") == 0) { | 539 | } else if (strcasecmp(argv[0], "vertical") == 0) { |
@@ -500,6 +547,7 @@ static enum cmd_status cmd_orientation(int argc, char **argv) { | |||
500 | } | 547 | } |
501 | 548 | ||
502 | static enum cmd_status cmd_output(int argc, char **argv) { | 549 | static enum cmd_status cmd_output(int argc, char **argv) { |
550 | NO_BIND(); | ||
503 | if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) { | 551 | if (!checkarg(argc, "output", EXPECTED_AT_LEAST, 1)) { |
504 | return CMD_FAILURE; | 552 | return CMD_FAILURE; |
505 | } | 553 | } |
@@ -510,7 +558,6 @@ static enum cmd_status cmd_output(int argc, char **argv) { | |||
510 | output->enabled = true; | 558 | output->enabled = true; |
511 | 559 | ||
512 | // TODO: atoi doesn't handle invalid numbers | 560 | // TODO: atoi doesn't handle invalid numbers |
513 | |||
514 | if (strcasecmp(argv[1], "disable") == 0) { | 561 | if (strcasecmp(argv[1], "disable") == 0) { |
515 | output->enabled = false; | 562 | output->enabled = false; |
516 | } | 563 | } |
@@ -960,6 +1007,11 @@ static enum cmd_status cmd_log_colors(int argc, char **argv) { | |||
960 | return CMD_SUCCESS; | 1007 | return CMD_SUCCESS; |
961 | } | 1008 | } |
962 | 1009 | ||
1010 | __attribute__((unused)) | ||
1011 | enum cmd_status cmd_for_window(int argc, char **argv) { | ||
1012 | return CMD_FAILURE; | ||
1013 | } | ||
1014 | |||
963 | static enum cmd_status cmd_fullscreen(int argc, char **argv) { | 1015 | static enum cmd_status cmd_fullscreen(int argc, char **argv) { |
964 | if (!checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0) | 1016 | if (!checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0) |
965 | || config->reading || !config->active) { | 1017 | || config->reading || !config->active) { |
@@ -1087,57 +1139,89 @@ static struct cmd_handler *find_handler(char *line) { | |||
1087 | return res; | 1139 | return res; |
1088 | } | 1140 | } |
1089 | 1141 | ||
1090 | enum cmd_status handle_command(char *exec) { | 1142 | enum cmd_status handle_command(char *_exec) { |
1091 | sway_log(L_INFO, "Handling command '%s'", exec); | 1143 | enum cmd_status status = CMD_SUCCESS; |
1092 | int argc; | 1144 | char *exec = strdup(_exec); |
1093 | char **argv = split_args(exec, &argc); | 1145 | char *head = exec; |
1094 | enum cmd_status status = CMD_FAILURE; | 1146 | char *cmdlist; |
1095 | struct cmd_handler *handler; | 1147 | char *cmd; |
1096 | if (!argc) { | 1148 | char *criteria __attribute__((unused)); |
1097 | return status; | 1149 | |
1098 | } | 1150 | head = exec; |
1099 | if ((handler = find_handler(argv[0])) == NULL | 1151 | do { |
1100 | || (status = handler->handle(argc - 1, argv + 1)) != CMD_SUCCESS) { | 1152 | // Handle criteria |
1101 | sway_log(L_ERROR, "Command failed: %s", argv[0]); | 1153 | if (*head == '[') { |
1102 | } | 1154 | criteria = argsep(&head, "]"); |
1103 | free_argv(argc, argv); | 1155 | if (head) { |
1156 | ++head; | ||
1157 | // TODO handle criteria | ||
1158 | } else { | ||
1159 | sway_log(L_ERROR, "Unmatched ["); | ||
1160 | status = CMD_INVALID; | ||
1161 | } | ||
1162 | // Skip leading whitespace | ||
1163 | head += strspn(head, whitespace); | ||
1164 | } | ||
1165 | // Split command list | ||
1166 | cmdlist = argsep(&head, ";"); | ||
1167 | cmdlist += strspn(cmdlist, whitespace); | ||
1168 | do { | ||
1169 | // Split commands | ||
1170 | cmd = argsep(&cmdlist, ","); | ||
1171 | cmd += strspn(cmd, whitespace); | ||
1172 | sway_log(L_INFO, "Handling command '%s'", cmd); | ||
1173 | //TODO better handling of argv | ||
1174 | int argc; | ||
1175 | char **argv = split_args(cmd, &argc); | ||
1176 | if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { | ||
1177 | strip_quotes(argv[1]); | ||
1178 | } | ||
1179 | struct cmd_handler *handler = find_handler(argv[0]); | ||
1180 | enum cmd_status res = CMD_INVALID; | ||
1181 | if (!handler | ||
1182 | || (res = handler->handle(argc-1, argv+1)) != CMD_SUCCESS) { | ||
1183 | sway_log(L_ERROR, "Command '%s' failed", cmd); | ||
1184 | free_argv(argc, argv); | ||
1185 | status = res; | ||
1186 | goto cleanup; | ||
1187 | } | ||
1188 | free_argv(argc, argv); | ||
1189 | } while(cmdlist); | ||
1190 | } while(head); | ||
1191 | cleanup: | ||
1192 | free(exec); | ||
1104 | return status; | 1193 | return status; |
1105 | } | 1194 | } |
1106 | 1195 | ||
1107 | enum cmd_status config_command(char *exec) { | 1196 | enum cmd_status config_command(char *exec) { |
1108 | sway_log(L_INFO, "handling config command '%s'", exec); | 1197 | sway_log(L_INFO, "handling config command '%s'", exec); |
1198 | enum cmd_status status = CMD_SUCCESS; | ||
1109 | int argc; | 1199 | int argc; |
1110 | char **argv = split_args(exec, &argc); | 1200 | char **argv = split_args(exec, &argc); |
1111 | enum cmd_status status = CMD_FAILURE; | ||
1112 | struct cmd_handler *handler; | ||
1113 | if (!argc) { | 1201 | if (!argc) { |
1114 | status = CMD_SUCCESS; | ||
1115 | goto cleanup; | 1202 | goto cleanup; |
1116 | } | 1203 | } |
1117 | // TODO better block handling | 1204 | // Endblock |
1118 | if (strncmp(argv[0], "}", 1) == 0) { | 1205 | if (**argv == '}') { |
1119 | config->current_mode = config->modes->items[0]; | 1206 | status = CMD_BLOCK_END; |
1120 | status = CMD_SUCCESS; | ||
1121 | goto cleanup; | 1207 | goto cleanup; |
1122 | } | 1208 | } |
1123 | if ((handler = find_handler(argv[0]))) { | 1209 | struct cmd_handler *handler = find_handler(argv[0]); |
1124 | // Dont replace first argument in cmd_set | 1210 | if (!handler) { |
1125 | int i = handler->handle == cmd_set ? 2 : 1; | 1211 | status = CMD_INVALID; |
1126 | int e = argc; | 1212 | goto cleanup; |
1127 | for (; i < e; ++i) { | 1213 | } |
1128 | argv[i] = do_var_replacement(argv[i]); | 1214 | int i; |
1129 | } | 1215 | // Var replacement, for all but first argument of set |
1130 | status = handler->handle(argc - 1, argv + 1); | 1216 | for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { |
1131 | if (status == CMD_FAILURE) { | 1217 | argv[i] = do_var_replacement(argv[i]); |
1132 | sway_log(L_ERROR, "Config load failed for line `%s'", exec); | 1218 | } |
1133 | } else if (status == CMD_DEFER) { | 1219 | /* Strip quotes for first argument. |
1134 | sway_log(L_DEBUG, "Defferring command `%s'", exec); | 1220 | * TODO This part needs to be handled much better */ |
1135 | list_add(config->cmd_queue, strdup(exec)); | 1221 | if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) { |
1136 | status = CMD_SUCCESS; | 1222 | strip_quotes(argv[1]); |
1137 | } | ||
1138 | } else { | ||
1139 | sway_log(L_ERROR, "Unknown command `%s'", exec); | ||
1140 | } | 1223 | } |
1224 | status = handler->handle(argc-1, argv+1); | ||
1141 | cleanup: | 1225 | cleanup: |
1142 | free_argv(argc, argv); | 1226 | free_argv(argc, argv); |
1143 | return status; | 1227 | return status; |
diff --git a/sway/config.c b/sway/config.c index 23d6ac0d..0026e0af 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -225,14 +225,46 @@ bool read_config(FILE *file, bool is_active) { | |||
225 | config->active = true; | 225 | config->active = true; |
226 | } | 226 | } |
227 | bool success = true; | 227 | bool success = true; |
228 | enum cmd_status block = CMD_BLOCK_END; | ||
228 | 229 | ||
229 | char *line; | 230 | char *line; |
230 | while (!feof(file)) { | 231 | while (!feof(file)) { |
231 | line = read_line(file); | 232 | line = read_line(file); |
232 | line = strip_comments(line); | 233 | line = strip_comments(line); |
233 | if (config_command(line) == CMD_FAILURE) { | 234 | switch(config_command(line)) { |
235 | case CMD_FAILURE: | ||
236 | case CMD_INVALID: | ||
234 | sway_log(L_ERROR, "Error on line '%s'", line); | 237 | sway_log(L_ERROR, "Error on line '%s'", line); |
235 | success = false; | 238 | success = false; |
239 | break; | ||
240 | |||
241 | case CMD_DEFER: | ||
242 | sway_log(L_DEBUG, "Defferring command `%s'", line); | ||
243 | list_add(config->cmd_queue, strdup(line)); | ||
244 | break; | ||
245 | |||
246 | case CMD_BLOCK_MODE: | ||
247 | if (block == CMD_BLOCK_END) { | ||
248 | block = CMD_BLOCK_MODE; | ||
249 | } else { | ||
250 | sway_log(L_ERROR, "Invalid block '%s'", line); | ||
251 | } | ||
252 | break; | ||
253 | |||
254 | case CMD_BLOCK_END: | ||
255 | switch(block) { | ||
256 | case CMD_BLOCK_MODE: | ||
257 | sway_log(L_DEBUG, "End of mode block"); | ||
258 | config->current_mode = config->modes->items[0]; | ||
259 | break; | ||
260 | |||
261 | case CMD_BLOCK_END: | ||
262 | sway_log(L_ERROR, "Unmatched }"); | ||
263 | break; | ||
264 | |||
265 | default:; | ||
266 | } | ||
267 | default:; | ||
236 | } | 268 | } |
237 | free(line); | 269 | free(line); |
238 | } | 270 | } |
@@ -14,10 +14,10 @@ int colored = 1; | |||
14 | log_importance_t v = L_SILENT; | 14 | log_importance_t v = L_SILENT; |
15 | 15 | ||
16 | static const char *verbosity_colors[] = { | 16 | static const char *verbosity_colors[] = { |
17 | "", // L_SILENT | 17 | [L_SILENT] = "", |
18 | "\x1B[1;31m", // L_ERROR | 18 | [L_ERROR ] = "\x1B[1;31m", |
19 | "\x1B[1;34m", // L_INFO | 19 | [L_INFO ] = "\x1B[1;34m", |
20 | "\x1B[1;30m", // L_DEBUG | 20 | [L_DEBUG ] = "\x1B[1;30m", |
21 | }; | 21 | }; |
22 | 22 | ||
23 | void init_log(log_importance_t verbosity) { | 23 | void init_log(log_importance_t verbosity) { |
diff --git a/sway/stringop.c b/sway/stringop.c index 191e40c8..90f963d6 100644 --- a/sway/stringop.c +++ b/sway/stringop.c | |||
@@ -105,40 +105,40 @@ char **split_args(const char *start, int *argc) { | |||
105 | bool in_char = false; | 105 | bool in_char = false; |
106 | bool escaped = false; | 106 | bool escaped = false; |
107 | const char *end = start; | 107 | const char *end = start; |
108 | while (*start) { | 108 | if (start) { |
109 | if (!in_token) { | 109 | while (*start) { |
110 | start = (end += strspn(end, whitespace)); | 110 | if (!in_token) { |
111 | in_token = true; | 111 | start = (end += strspn(end, whitespace)); |
112 | } | 112 | in_token = true; |
113 | if (*end == '"' && !in_char && !escaped) { | 113 | } |
114 | in_string = !in_string; | 114 | if (*end == '"' && !in_char && !escaped) { |
115 | } else if (*end == '\'' && !in_string && !escaped) { | 115 | in_string = !in_string; |
116 | in_char = !in_char; | 116 | } else if (*end == '\'' && !in_string && !escaped) { |
117 | } else if (*end == '\\') { | 117 | in_char = !in_char; |
118 | escaped = !escaped; | 118 | } else if (*end == '\\') { |
119 | } else if (*end == '\0' || (!in_string && !in_char && !escaped | 119 | escaped = !escaped; |
120 | && strchr(whitespace, *end))) { | 120 | } else if (*end == '\0' || (!in_string && !in_char && !escaped |
121 | goto add_part; | 121 | && strchr(whitespace, *end))) { |
122 | } | 122 | goto add_token; |
123 | if (*end != '\\') { | 123 | } |
124 | escaped = false; | 124 | if (*end != '\\') { |
125 | } | 125 | escaped = false; |
126 | ++end; | 126 | } |
127 | continue; | 127 | ++end; |
128 | add_part: | 128 | continue; |
129 | if (end - start > 0) { | 129 | add_token: |
130 | char *token = malloc(end - start + 1); | 130 | if (end - start > 0) { |
131 | strncpy(token, start, end - start + 1); | 131 | char *token = malloc(end - start + 1); |
132 | token[end - start] = '\0'; | 132 | strncpy(token, start, end - start + 1); |
133 | strip_quotes(token); | 133 | token[end - start] = '\0'; |
134 | unescape_string(token); | 134 | argv[*argc] = token; |
135 | argv[*argc] = token; | 135 | if (++*argc + 1 == alloc) { |
136 | if (++*argc + 1 == alloc) { | 136 | argv = realloc(argv, (alloc *= 2) * sizeof(char *)); |
137 | argv = realloc(argv, (alloc *= 2) * sizeof(char *)); | 137 | } |
138 | } | 138 | } |
139 | in_token = false; | ||
140 | escaped = false; | ||
139 | } | 141 | } |
140 | in_token = false; | ||
141 | escaped = false; | ||
142 | } | 142 | } |
143 | argv[*argc] = NULL; | 143 | argv[*argc] = NULL; |
144 | return argv; | 144 | return argv; |
@@ -311,3 +311,50 @@ char *join_list(list_t *list, char *separator) { | |||
311 | 311 | ||
312 | return res; | 312 | return res; |
313 | } | 313 | } |
314 | |||
315 | char *cmdsep(char **stringp, const char *delim) { | ||
316 | char *head = strsep(stringp, delim); | ||
317 | // But skip over trailing delims. '3 tokens here' -> '3' 'tokens here' | ||
318 | if (*stringp) { | ||
319 | *stringp += strspn(*stringp, delim); | ||
320 | // If skiping over delims brings us to the end of string, set to NULL | ||
321 | if (!**stringp) *stringp = NULL; | ||
322 | } | ||
323 | return head; | ||
324 | } | ||
325 | |||
326 | char *argsep(char **stringp, const char *delim) { | ||
327 | char *start = *stringp; | ||
328 | char *end = start; | ||
329 | bool in_string = false; | ||
330 | bool in_char = false; | ||
331 | bool escaped = false; | ||
332 | while (1) { | ||
333 | if (*end == '"' && !in_char && !escaped) { | ||
334 | in_string = !in_string; | ||
335 | } else if (*end == '\'' && !in_string && !escaped) { | ||
336 | in_char = !in_char; | ||
337 | } else if (*end == '\\') { | ||
338 | escaped = !escaped; | ||
339 | } else if (*end == '\0') { | ||
340 | *stringp = NULL; | ||
341 | goto found; | ||
342 | } else if (!in_string && !in_char && !escaped && strchr(delim, *end)) { | ||
343 | if (end - start) { | ||
344 | *(end++) = 0; | ||
345 | *stringp = end + strspn(end, delim);; | ||
346 | if (!**stringp) *stringp = NULL; | ||
347 | goto found; | ||
348 | } else { | ||
349 | ++start; | ||
350 | end = start; | ||
351 | } | ||
352 | } | ||
353 | if (*end != '\\') { | ||
354 | escaped = false; | ||
355 | } | ||
356 | ++end; | ||
357 | } | ||
358 | found: | ||
359 | return start; | ||
360 | } | ||