aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/commands.h16
-rw-r--r--include/stringop.h6
-rw-r--r--sway/commands.c214
-rw-r--r--sway/config.c34
-rw-r--r--sway/log.c8
-rw-r--r--sway/stringop.c111
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
6struct cmd_handler { 6
7 char *command; 7enum 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
15enum cmd_status handle_command(char *command); 17enum 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
8char *strip_whitespace(char *str); 8char *strip_whitespace(char *str);
9char *strip_comments(char *str); 9char *strip_comments(char *str);
10void 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`
12list_t *split_string(const char *str, const char *delims); 13list_t *split_string(const char *str, const char *delims);
@@ -22,5 +23,10 @@ int unescape_string(char *string);
22char *join_args(char **argv, int argc); 23char *join_args(char **argv, int argc);
23char *join_list(list_t *list, char *separator); 24char *join_list(list_t *list, char *separator);
24 25
26// split string into 2 by delim.
27char *cmdsep(char **stringp, const char *delim);
28// Split string into 2 by delim, handle quotes
29char *argsep(char **stringp, const char *delim);
30
25char *strdup(const char *); 31char *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
21typedef enum cmd_status sway_cmd(int argc, char **argv);
22
23struct cmd_handler {
24 char *command;
25 sway_cmd *handle;
26};
27
28static sway_cmd cmd_bindsym;
29static sway_cmd cmd_orientation;
30static sway_cmd cmd_exec;
31static sway_cmd cmd_exec_always;
32static sway_cmd cmd_exit;
33static sway_cmd cmd_floating;
34static sway_cmd cmd_floating_mod;
35static sway_cmd cmd_focus;
36static sway_cmd cmd_focus_follows_mouse;
37static sway_cmd cmd_for_window;
38static sway_cmd cmd_fullscreen;
39static sway_cmd cmd_gaps;
40static sway_cmd cmd_kill;
41static sway_cmd cmd_layout;
42static sway_cmd cmd_log_colors;
43static sway_cmd cmd_mode;
44static sway_cmd cmd_move;
45static sway_cmd cmd_output;
46static sway_cmd cmd_reload;
47static sway_cmd cmd_resize;
48static sway_cmd cmd_scratchpad;
49static sway_cmd cmd_set;
50static sway_cmd cmd_split;
51static sway_cmd cmd_splith;
52static sway_cmd cmd_splitv;
53static sway_cmd cmd_workspace;
54static 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
21swayc_t *sp_view; 60swayc_t *sp_view;
22int sp_index = 0; 61int sp_index = 0;
23 62
@@ -145,14 +184,19 @@ static enum cmd_status cmd_bindsym(int argc, char **argv) {
145} 184}
146 185
147static enum cmd_status cmd_exec_always(int argc, char **argv) { 186static 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
175static enum cmd_status cmd_exec(int argc, char **argv) { 217static 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
375static enum cmd_status cmd_mode(int argc, char **argv) { 415static 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
414static enum cmd_status cmd_move(int argc, char **argv) { 460static 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
485static enum cmd_status cmd_orientation(int argc, char **argv) { 531static 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
502static enum cmd_status cmd_output(int argc, char **argv) { 549static 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))
1011enum cmd_status cmd_for_window(int argc, char **argv) {
1012 return CMD_FAILURE;
1013}
1014
963static enum cmd_status cmd_fullscreen(int argc, char **argv) { 1015static 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
1090enum cmd_status handle_command(char *exec) { 1142enum 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
1107enum cmd_status config_command(char *exec) { 1196enum 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 }
diff --git a/sway/log.c b/sway/log.c
index cf5c2092..3859fd92 100644
--- a/sway/log.c
+++ b/sway/log.c
@@ -14,10 +14,10 @@ int colored = 1;
14log_importance_t v = L_SILENT; 14log_importance_t v = L_SILENT;
15 15
16static const char *verbosity_colors[] = { 16static 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
23void init_log(log_importance_t verbosity) { 23void 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
315char *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
326char *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}