From d673a72705ea29afe58784b3ce169fb5bde73c11 Mon Sep 17 00:00:00 2001
From: taiyu <taiyu.len@gmail.com>
Date: Tue, 8 Sep 2015 08:54:57 -0700
Subject: [PATCH] set variable changes

---
 include/list.h  |  2 ++
 sway.5.txt      |  5 ++++
 sway/commands.c | 34 +++++++++++++++++++-----
 sway/config.c   | 69 ++++++++++++++++++++++++++-----------------------
 sway/list.c     |  4 +++
 5 files changed, 75 insertions(+), 39 deletions(-)

diff --git a/include/list.h b/include/list.h
index 29b988aa..aff6800f 100644
--- a/include/list.h
+++ b/include/list.h
@@ -13,5 +13,7 @@ void list_add(list_t *list, void *item);
 void list_insert(list_t *list, int index, void *item);
 void list_del(list_t *list, int index);
 void list_cat(list_t *list, list_t *source);
+// See qsort
+void list_sort(list_t *list, int compare(const void *left, const void *right));
 
 #endif
diff --git a/sway.5.txt b/sway.5.txt
index 595333c4..9bcfef42 100644
--- a/sway.5.txt
+++ b/sway.5.txt
@@ -85,6 +85,11 @@ Commands
 	Sets the layout mode of the focused container. _mode_ can be one of _splith_,
 	_splitv_, or _toggle split_.
 
+**mode** <mode_name>::
+	Switches to the given mode_name. the default mode is simply _default_. To
+	create a new mode in config append _{_ to this command, the following lines
+	will be keybinds for that mode, and _}_ on its own line to close the block.
+
 **move** <left|right|up|down>::
 	Moves the focused container _left_, _right_, _up_, or _down_.
 
diff --git a/sway/commands.c b/sway/commands.c
index e7ddfa71..44407bfa 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -139,8 +139,7 @@ static bool cmd_bindsym(int argc, char **argv) {
 	// TODO: Check if there are other commands with this key binding
 	struct sway_mode *mode = config->current_mode;
 	list_add(mode->bindings, binding);
-	qsort(mode->bindings->items, mode->bindings->length,
-			sizeof(mode->bindings->items[0]), bindsym_sort);
+	list_sort(mode->bindings, bindsym_sort);
 
 	sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command);
 	return true;
@@ -828,14 +827,36 @@ static bool cmd_scratchpad(int argc, char **argv) {
 	}
 }
 
+// sort in order of longest->shortest
+static int compare_set(const void *_l, const void *_r) {
+	struct sway_variable * const *l = _l;
+	struct sway_variable * const *r = _r;
+	return strlen((*r)->name) - strlen((*l)->name);
+}
+
 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 = strdup(argv[0]);
+	struct sway_variable *var = NULL;
+	// Find old variable if it exists
+	int i;
+	for (i = 0; i < config->symbols->length; ++i) {
+		var = config->symbols->items[i];
+		if (strcmp(var->name, argv[0]) == 0) {
+			break;
+		}
+		var = NULL;
+	}
+	if (var) {
+		free(var->value);
+	} else {
+		var = malloc(sizeof(struct sway_variable));
+		var->name = strdup(argv[0]);
+		list_add(config->symbols, var);
+		list_sort(config->symbols, compare_set);
+	}
 	var->value = strdup(argv[1]);
-	list_add(config->symbols, var);
 	return true;
 }
 
@@ -1048,7 +1069,8 @@ bool handle_command(char *exec) {
 	bool exec_success = false;
 	if (handler) {
 		int i;
-		for (i = 1; i < argc; ++i) {
+		// Skip var replacement for first value of cmd_set
+		for (i = (handler->handle == cmd_set ? 2 : 1); i < argc; ++i) {
 			argv[i] = do_var_replacement(argv[i]);
 		}
 		exec_success = handler->handle(argc - 1, argv + 1);
diff --git a/sway/config.c b/sway/config.c
index 6c8fe8c1..daaedeed 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -230,19 +230,17 @@ bool read_config(FILE *file, bool is_active) {
 	char *line;
 	while (!feof(file)) {
 		line = read_line(file);
-		line = strip_whitespace(line);
 		line = strip_comments(line);
-		if (line[0] == '\0') {
-			goto _continue;
-		}
-		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, whitespace);
+		if (!args->length) {
+			goto cleanup;
+		}
+		//TODO make this better, it only handles modes right now, and very
+		//simply at that
+		if (strncmp(args->items[0], "}", 1) == 0) {
+			config->current_mode = default_mode;
+			goto cleanup;
+		}
 		struct cmd_handler *handler;
 		if ((handler = find_handler(args->items[0]))) {
 			if (handler->config_type == CMD_KEYBIND) {
@@ -259,9 +257,8 @@ bool read_config(FILE *file, bool is_active) {
 		} else {
 			sway_log(L_ERROR, "Invalid command ``%s''", line);
 		}
+		cleanup:
 		free_flat_list(args);
-
-_continue:
 		free(line);
 	}
 
@@ -277,27 +274,33 @@ _continue:
 }
 
 char *do_var_replacement(char *str) {
-	// TODO: Handle escaping $ and using $ in string literals
 	int i;
-	for (i = 0; str[i]; ++i) {
-		if (str[i] == '$') {
-			// Try for match (note: this could be faster)
-			int j;
-			for (j = 0; j < config->symbols->length; ++j) {
-				struct sway_variable *var = config->symbols->items[j];
-				if (strstr(str + i, var->name) == str + i) {
-					// Match, do replacement
-					char *new_string = malloc(
-						strlen(str) -
-						strlen(var->name) +
-						strlen(var->value) + 1);
-					strncpy(new_string, str, i);
-					new_string[i] = 0;
-					strcat(new_string, var->value);
-					strcat(new_string, str + i + strlen(var->name));
-					free(str);
-					str = new_string;
-				}
+	char *find = str;
+	while ((find = strchr(find, '$'))) {
+		// Skip if escaped.
+		if (find > str + 1 && find[-1] == '\\') {
+			if (!(find > str + 2 && find[-2] == '\\')) {
+				continue;
+			}
+		}
+		// Find matching variable
+		for (i = 0; i < config->symbols->length; ++i) {
+			struct sway_variable *var = config->symbols->items[i];
+			int vnlen = strlen(var->name);
+			if (strncmp(find, var->name, vnlen) == 0) {
+				int vvlen = strlen(var->value);
+				char *newstr = malloc(strlen(str) - vnlen + vvlen + 1);
+				char *newptr = newstr;
+				int offset = find - str;
+				strncpy(newptr, str, offset);
+				newptr += offset;
+				strncpy(newptr, var->value, vvlen);
+				newptr += vvlen;
+				strcpy(newptr, find + vnlen);
+				free(str);
+				str = newstr;
+				find = str + offset + vvlen;
+				break;
 			}
 		}
 	}
diff --git a/sway/list.c b/sway/list.c
index 1be1e17c..45efc16f 100644
--- a/sway/list.c
+++ b/sway/list.c
@@ -49,3 +49,7 @@ void list_cat(list_t *list, list_t *source) {
 		list_add(list, source->items[i]);
 	}
 }
+
+void list_sort(list_t *list, int compare(const void *left, const void *right)) {
+	qsort(list->items, list->length, sizeof(void *), compare);
+}