Scandum vor 6 Jahren
Ursprung
Commit
4048d5a5ea
54 geänderte Dateien mit 12537 neuen und 6200 gelöschten Zeilen
  1. 1 1
      mods/igr.mods
  2. 1 1
      src/Makefile.in
  3. 11 11
      src/advertise.c
  4. 83 23
      src/buffer.c
  5. 147 131
      src/class.c
  6. 45 14
      src/config.c
  7. 137 49
      src/cursor.c
  8. 29 8
      src/daemon.c
  9. 132 57
      src/data.c
  10. 3 4
      src/debug.c
  11. 186 0
      src/dict.c
  12. 25 0
      src/dict.h
  13. 782 36
      src/draw.c
  14. 86 32
      src/event.c
  15. 14 6
      src/files.c
  16. 241 107
      src/help.c
  17. 2 3
      src/history.c
  18. 48 7
      src/input.c
  19. 64 5
      src/line.c
  20. 134 29
      src/list.c
  21. 13 7
      src/log.c
  22. 82 13
      src/main.c
  23. 6553 4424
      src/mapper.c
  24. 118 6
      src/math.c
  25. 2 3
      src/mccp.c
  26. 93 23
      src/memory.c
  27. 25 0
      src/misc.c
  28. 6 4
      src/msdp.c
  29. 309 15
      src/nest.c
  30. 7 29
      src/net.c
  31. 226 13
      src/parse.c
  32. 69 73
      src/path.c
  33. 3 4
      src/port.c
  34. 252 33
      src/regex.c
  35. 4 12
      src/scan.c
  36. 84 41
      src/screen.c
  37. 9 9
      src/session.c
  38. 19 2
      src/show.c
  39. 18 17
      src/split.c
  40. 580 20
      src/substitute.c
  41. 88 1
      src/system.c
  42. 242 85
      src/tables.c
  43. 7 2
      src/telnet.h
  44. 170 22
      src/telopt_client.c
  45. 39 3
      src/terminal.c
  46. 74 68
      src/text.c
  47. 264 105
      src/tintin.h
  48. 18 27
      src/tokenize.c
  49. 1 2
      src/trigger.c
  50. 96 33
      src/update.c
  51. 480 527
      src/utf8.c
  52. 3 3
      src/utils.c
  53. 326 41
      src/variable.c
  54. 86 9
      src/vt102.c

+ 1 - 1
mods/igr.mods

@@ -1,4 +1,4 @@
-Nov 2019        2.01.93
+Nov 2019        2.02.00
 ------------------------------------------------------------------------------
 
 regex.c         Added support for using %+4s to match 4 spaces, or %+0..2d

+ 1 - 1
src/Makefile.in

@@ -64,7 +64,7 @@ system.o mapper.o tables.o buffer.o event.o tokenize.o chat.o utf8.o \
 advertise.o list.o forkpty.o utils.o line.o data.o variable.o msdp.o \
 port.o scan.o telopt_client.o screen.o cursor.o nest.o show.o mccp.o \
 telopt_server.o draw.o log.o path.o session.o class.o config.o ssl.o \
-regex.o substitute.o daemon.o
+regex.o substitute.o daemon.o dict.o
 
 default: all
 

+ 11 - 11
src/advertise.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2009                       *
 ******************************************************************************/
@@ -102,10 +101,10 @@ struct advertisement_type advertisement_table[] =
 		"<178>To connect to Kallisti MUD enter: #session LoK kallistimud.com 4000\n"
 		"\n",
 	},
-
+/*
 	{
-		1400000000, /* 2014 */
- 		1800000000, /* 2027 */
+		1400000000, // 2014
+ 		1800000000, // 2027
 		100,
 
 		"\n"
@@ -134,7 +133,7 @@ struct advertisement_type advertisement_table[] =
 		"<178>To connect to Lowlands enter: #session lol lolamud.net 6969\n"
 		"\n"
 	},
-
+*/
 	{
 		1400000000,
 		1800000000,
@@ -176,7 +175,7 @@ struct advertisement_type advertisement_table[] =
 		"<078>Ateraan is a world of Intensive Roleplaying offering many unique and powerful\n"
 		"<078>guilds, races, politics, religion, justice, economy, and a storyline that is\n"
 		"<078>dominantly player controlled and based on a novel. The game is based on a\n"
-		"<078>Kingdom with fighters, merchants, mages, and thieves, and a fierce southern\n"
+		"<078>Kingdom with knights, merchants, mages, and thieves, and a fierce southern\n"
 		"<078>state that has warriors, shaman, slaves, and servants. Ships rule the seas and\n"
 		"<078>caravans travel the lands. With 100's of players and features like invasions,\n"
 		"<078>ship creation, house building, clans, theaters, leatherball fields, and massive\n"
@@ -194,10 +193,10 @@ struct advertisement_type advertisement_table[] =
                 "<178>To connect to New Worlds Ateraan enter: #session nwa ateraan.com 4002\n"
                 "\n"
 	},
-
+/*
 	{
-		1400000000,  /* 2014 */ 
-		1800000000,  /* 2027 */ 
+		1400000000,  // 2014
+		1800000000,  // 2027
 		100,
 
 		"\n"
@@ -224,6 +223,7 @@ struct advertisement_type advertisement_table[] =
 		"<178>To connect to Primal Darkness enter: #session pd mud.primaldarkness.com 5000\n"
 		"\n"
 	},
+*/
 /*	{
 		1400000000,
  		1800000000,

+ 83 - 23
src/buffer.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*              (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                   *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                     coded by Igor van den Hoven 2004                        *
+*                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
 
 #include "tintin.h"
@@ -98,7 +97,7 @@ void check_buffer(struct session *ses)
 	{
 		buffer = ses->scroll->buffer[index];
 
-		if (buffer->width < wrap)
+		if (buffer->height == 1 && buffer->width < wrap - 1)
 		{
 			buffer->height = buffer->lines;
 		}
@@ -269,7 +268,7 @@ void buffer_print(struct session *ses, int index, int start, int end)
 
 	col_len = get_scroll_cols(ses);
 
-	if (end + start == 0)
+	if (start == 0 && end == 0)
 	{
 		if (HAS_BIT(ses->flags, SES_FLAG_PRINTBUFFER) || ses->scroll->line == ses->scroll->used - 1)
 		{
@@ -284,7 +283,7 @@ void buffer_print(struct session *ses, int index, int start, int end)
 
 		if (ses->cur_row != ses->split->bot_row)
 		{
-			print_stdout("%02d \e[1;32mmisaligned", ses->cur_row);
+			print_stdout("%02d \e[1;32mmisaligned (%d)", ses->cur_row, ses->scroll->line);
 		}
 		else
 		{
@@ -299,7 +298,10 @@ void buffer_print(struct session *ses, int index, int start, int end)
 		if (buffer->height == 1)
 		{
 //			print_stdout("\e[1;37m%02d", ses->cur_row);//
-			print_stdout("%s", buffer->str);
+
+			word_wrap_split(ses, buffer->str, temp, ses->wrap, start, end, 0, &height, &width);
+
+			print_stdout("%s", temp);
 
 			erase_cols(col_len - buffer->width);
 
@@ -372,7 +374,7 @@ void buffer_print(struct session *ses, int index, int start, int end)
 
 int show_buffer(struct session *ses)
 {
-	int scroll_size, scroll_cnt, scroll_tmp, scroll_add, scroll_cut;
+	int scroll_size, scroll_cnt, scroll_tmp, scroll_add, scroll_cut, start, end;
 
 	if (ses != gtd->ses)
 	{
@@ -393,6 +395,8 @@ int show_buffer(struct session *ses)
 		scroll_add -= ses->scroll->base;
 	}
 
+	// scroll_cut is cut from top line
+
 	while (TRUE)
 	{
 		scroll_tmp = ses->scroll->buffer[scroll_cnt]->height;
@@ -438,28 +442,80 @@ int show_buffer(struct session *ses)
 		SET_BIT(ses->flags, SES_FLAG_READMUD);
 	}
 
+	// scroll_cut is taken from top line
+	// scroll_base is taken from the bot line
+
 	if (scroll_cut)
 	{
 		scroll_tmp = ses->scroll->buffer[scroll_cnt]->height;
 
-		if (ses->scroll->base >= scroll_size || scroll_cut == scroll_size)
+		// bottom
+
+		if (scroll_cut == scroll_size)
+		{
+			start = scroll_tmp - scroll_cut;
+			end   = scroll_tmp;
+
+			if (end - start != scroll_size)
+			{
+//				print_stdout("\e[1;32mcnt %d, base: %d, size: %d, add %d, tmp %d, scroll_cut %d start %d end %d\n", scroll_cnt, ses->scroll->base, scroll_size, scroll_add, scroll_tmp, scroll_cut, start, end);
+			}
+			else
+			{
+				buffer_print(ses, scroll_cnt, start, end);
+			}
+		}
+		// middle chunk
+
+		else if (scroll_cut > scroll_size)
 		{
-//			print_stdout("scroll_cnt %d, scroll->base: %d, scroll_tmp %d, scroll_cut %d\n", scroll_cnt, ses->scroll->base, scroll_tmp, scroll_cut);
-			buffer_print(ses, scroll_cnt, scroll_tmp - scroll_cut, scroll_tmp - scroll_cut + scroll_size);
+			start = scroll_tmp - scroll_cut;
+			end   = scroll_tmp - scroll_cut + scroll_size;
+
+//			if (end - start > scroll_size)
+			{
+//				print_stdout("\e[1;1H\e[1;33mcnt %d, base: %d, size: %d, add %d, tmp %d, scroll_cut %d start %d end %d\n", scroll_cnt, ses->scroll->base, scroll_size, scroll_add, scroll_tmp, scroll_cut, start, end);
+			}
+//			else
+			{
+				buffer_print(ses, scroll_cnt, start, end);
+			}
 
 			goto eof;
 		}
+
+		// top chunk
 		else if (scroll_add == 0)
 		{
-//			print_stdout("scroll_add %d, scroll_cnt %d, scroll->base %d, scroll_tmp %d, scroll_cut %d\n", scroll_add, scroll_cnt, ses->scroll->base, scroll_tmp, scroll_cut);
+			start = ses->scroll->base;
+			end   = scroll_tmp - scroll_cut;
 
-			buffer_print(ses, scroll_cnt, ses->scroll->base, scroll_tmp - scroll_cut);
+//			if (end - start > scroll_size)
+			{
+//				print_stdout("\e[1;1H\e[1;34mcnt %d, base: %d, size: %d, add %d, tmp %d, scroll_cut %d start %d end %d\n", scroll_cnt, ses->scroll->base, scroll_size, scroll_add, scroll_tmp, scroll_cut, start, end);
+			}
+//			else
+			{
+				buffer_print(ses, scroll_cnt, start, end);
+			}
 
 			goto eof;
 		}
+		// bot chunk
+
 		else
 		{
-			buffer_print(ses, scroll_cnt, scroll_tmp - scroll_cut, scroll_tmp);
+			start = scroll_tmp - scroll_cut;
+			end   = scroll_tmp;
+
+//			if (end - start > scroll_size)
+			{
+//				print_stdout("\e[1;1H\e[1;35mcnt %d, base: %d, size: %d, add %d, tmp %d, scroll_cut %d start %d end %d\n", scroll_cnt, ses->scroll->base, scroll_size, scroll_add, scroll_tmp, scroll_cut, start, end);
+			}
+//			else
+			{
+				buffer_print(ses, scroll_cnt, start, end);
+			}
 		}
 		scroll_cnt++;
 		scroll_cut = 0;
@@ -481,7 +537,10 @@ int show_buffer(struct session *ses)
 
 		scroll_add -= scroll_tmp;
 
-		buffer_print(ses, scroll_cnt, 0, scroll_tmp);
+		start = 0;
+		end   = scroll_tmp;
+
+		buffer_print(ses, scroll_cnt, start, end);
 
 		scroll_cnt++;
 	}
@@ -490,7 +549,10 @@ int show_buffer(struct session *ses)
 	{
 		scroll_tmp = ses->scroll->buffer[scroll_cnt]->height;
 
-		buffer_print(ses, scroll_cnt, 0, scroll_tmp - ses->scroll->base);
+		start = 0;
+		end   = scroll_tmp - ses->scroll->base;
+
+		buffer_print(ses, scroll_cnt, start, end);
 	}
 
 	eof:
@@ -499,8 +561,6 @@ int show_buffer(struct session *ses)
 
 	buffer_print(ses, 0, 0, 0);
 
-
-
 	restore_pos(ses);
 
 	if (IS_SPLIT(ses))
@@ -831,7 +891,7 @@ DO_BUFFER(buffer_get)
 
 	if (*arg3 == 0)
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg1, "%s", ses->scroll->buffer[min]->str);
+		set_nest_node_ses(ses, arg1, "%s", ses->scroll->buffer[min]->str);
 
 		return;
 	}
@@ -853,13 +913,13 @@ DO_BUFFER(buffer_get)
 
 	cnt = 0;
 
-	set_nest_node(ses->list[LIST_VARIABLE], arg1, "");
+	set_nest_node_ses(ses, arg1, "");
 
 	while (min <= max)
 	{
 		sprintf(arg2, "%s[%d]", arg1, ++cnt);
 
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", ses->scroll->buffer[min++]->str);
+		set_nest_node_ses(ses, arg2, "%s", ses->scroll->buffer[min++]->str);
 	}
 
 	show_message(ses, LIST_COMMAND, "#BUFFER GET: %d LINES SAVED TO {%s}.", cnt, arg1);
@@ -887,7 +947,7 @@ DO_BUFFER(buffer_write)
 		{
 			show_message(ses, LIST_COMMAND, "#OK: WRITING BUFFER TO '%s'.", arg1);
 
-			loginit(ses, fp, LOG_FLAG_OVERWRITE | HAS_BIT(ses->logmode, LOG_FLAG_HTML));
+			loginit(ses, fp, ses->logmode + LOG_FLAG_OVERWRITE);
 
 			for (cnt = 0 ; cnt < ses->scroll->used ; cnt++)
 			{

+ 147 - 131
src/class.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*              (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                   *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                     coded by Igor van den Hoven 2004                        *
+*                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
 
 
@@ -32,7 +31,6 @@ DO_COMMAND(do_class)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE];
 	int i;
-
 	struct listroot *root;
 	struct listnode *node;
 
@@ -55,7 +53,7 @@ DO_COMMAND(do_class)
 	}
 	else if (*arg2 == 0)
 	{
-		class_list(ses, arg1, arg2);
+		class_list(ses, NULL, arg1, arg2);
 	}
 	else
 	{
@@ -73,13 +71,15 @@ DO_COMMAND(do_class)
 		}
 		else
 		{
-			if (!search_node_list(ses->list[LIST_CLASS], arg1))
+			node = search_node_list(ses->list[LIST_CLASS], arg1);
+
+			if (node == NULL)
 			{
 				check_all_events(ses, SUB_ARG, 0, 1, "CLASS CREATED", arg1);
 
-				update_node_list(ses->list[LIST_CLASS], arg1, arg2, arg3, "");
+				node = update_node_list(ses->list[LIST_CLASS], arg1, "", arg3, "");
 			}
-			class_table[i].group(ses, arg1, arg3);
+			class_table[i].fun(ses, node, arg1, arg3);
 		}
 	}
 	return ses;
@@ -108,32 +108,31 @@ int count_class(struct session *ses, struct listnode *group)
 	return cnt;
 }
 
-
-DO_CLASS(class_open)
+DO_CLASS(class_clear)
 {
-	int count;
+	int type, index;
 
-	if (!strcmp(ses->group, arg1))
-	{
-		show_message(ses, LIST_CLASS, "#CLASS {%s} IS ALREADY OPENED AND ACTIVATED.", arg1);
-	}
-	else
+	for (type = 0 ; type < LIST_MAX ; type++)
 	{
-		if (*ses->group)
+		if (!HAS_BIT(ses->list[type]->flags, LIST_FLAG_CLASS))
 		{
-			check_all_events(ses, SUB_ARG, 0, 1, "CLASS DEACTIVATED", ses->group);
-			check_all_events(ses, SUB_ARG, 1, 1, "CLASS DEACTIVATED %s", ses->group, ses->group);
+			continue;
 		}
-		RESTRING(ses->group, arg1);
-
-		count = atoi(ses->list[LIST_CLASS]->list[0]->arg3);
 
-		update_node_list(ses->list[LIST_CLASS], arg1, "", ntos(--count), "");
+		for (index = 0 ; index < ses->list[type]->used ; index++)
+		{
+			if (!strcmp(ses->list[type]->list[index]->group, arg1))
+			{
+				delete_index_list(ses->list[type], index--);
+			}
+		}
+	}
 
-		show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN OPENED AND ACTIVATED.", arg1);
+	show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN CLEARED.", arg1);
 
-		check_all_events(ses, SUB_ARG, 0, 1, "CLASS ACTIVATED", arg1);
-		check_all_events(ses, SUB_ARG, 1, 1, "CLASS ACTIVATED %s", arg1, arg1);
+	if (!strcmp(ses->group, arg1))
+	{
+		class_close(ses, node, arg1, arg2);
 	}
 
 	return ses;
@@ -141,52 +140,42 @@ DO_CLASS(class_open)
 
 DO_CLASS(class_close)
 {
-	struct listnode *node;
-
-	node = search_node_list(ses->list[LIST_CLASS], arg1);
-
-	if (node == NULL)
+	if (atoi(node->arg3) == 0)
 	{
-		show_message(ses, LIST_CLASS, "#CLASS {%s} DOES NOT EXIST.", arg1);
+		show_message(ses, LIST_CLASS, "#CLASS {%s} IS ALREADY CLOSED.", arg1);
 	}
 	else
 	{
-		if (atoi(node->arg3) == 0)
-		{
-			show_message(ses, LIST_CLASS, "#CLASS {%s} IS ALREADY CLOSED.", arg1);
-		}
-		else
-		{
-			show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN CLOSED.", arg1);
+		show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN CLOSED.", arg1);
 
-			update_node_list(ses->list[LIST_CLASS], arg1, "", "0","");
+		update_node_list(ses->list[LIST_CLASS], arg1, "", "0","");
 
-			if (!strcmp(ses->group, arg1))
-			{
-				check_all_events(ses, SUB_ARG, 0, 1, "CLASS DEACTIVATED", ses->group);
-				check_all_events(ses, SUB_ARG, 1, 1, "CLASS DEACTIVATED %s", ses->group, ses->group);
+		if (!strcmp(ses->group, arg1))
+		{
+			check_all_events(ses, SUB_ARG, 0, 1, "CLASS DEACTIVATED", ses->group);
+			check_all_events(ses, SUB_ARG, 1, 1, "CLASS DEACTIVATED %s", ses->group, ses->group);
 
-				node = ses->list[LIST_CLASS]->list[0];
+			node = ses->list[LIST_CLASS]->list[0];
 
-				if (atoi(node->arg3))
-				{
-					RESTRING(ses->group, node->arg1);
+			if (atoi(node->arg3))
+			{
+				RESTRING(ses->group, node->arg1);
 
-					show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN ACTIVATED.", node->arg1);
+				show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN ACTIVATED.", node->arg1);
 
-					check_all_events(ses, SUB_ARG, 0, 1, "CLASS ACTIVATED", node->arg1);
-					check_all_events(ses, SUB_ARG, 1, 1, "CLASS ACTIVATED %s", arg1, arg1);
-				}
-				else
-				{
-					RESTRING(ses->group, "");
-				}
+				check_all_events(ses, SUB_ARG, 0, 1, "CLASS ACTIVATED", node->arg1);
+				check_all_events(ses, SUB_ARG, 1, 1, "CLASS ACTIVATED %s", arg1, arg1);
+			}
+			else
+			{
+				RESTRING(ses->group, "");
 			}
 		}
 	}
 	return ses;
 }
 
+
 DO_CLASS(class_list)
 {
 	int i, j;
@@ -223,36 +212,92 @@ DO_CLASS(class_list)
 	return ses;
 }
 
-DO_CLASS(class_read)
+
+DO_CLASS(class_kill)
 {
-	class_open(ses, arg1, arg2);
+	int group;
 
-	do_read(ses, arg2);
+	class_clear(ses, node, arg1, arg2);
 
-	class_close(ses, arg1, arg2);
+	group = search_index_list(ses->list[LIST_CLASS], arg1, NULL);
+
+	delete_index_list(ses->list[LIST_CLASS], group);
+
+	check_all_events(ses, SUB_ARG, 0, 1, "CLASS DESTROYED", arg1);
+	check_all_events(ses, SUB_ARG, 1, 1, "CLASS DESTROYED %s", arg1, arg1);
+
+	show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN KILLED.", arg1);
 
 	return ses;
 }
 
-
-DO_CLASS(class_write)
+DO_CLASS(class_load)
 {
 	FILE *file;
-	int list, index;
 
-	if (!search_node_list(ses->list[LIST_CLASS], arg1))
+	if (node->data == NULL)
 	{
-		show_error(ses, LIST_CLASS, "#CLASS {%s} DOES NOT EXIST.", arg1);
-		
+		show_error(ses, LIST_CLASS, "#CLASS {%s} DOES NOT HAVE ANY DATA SAVED.", arg1);
+
 		return ses;
 	}
+	file = fmemopen(node->data, node->val32[1], "r");
 
-	if (*arg2 == 0 || (file = fopen(arg2, "w")) == NULL)
+	read_file(ses, file, arg1);
+
+	return ses;
+}
+
+DO_CLASS(class_open)
+{
+	int count;
+
+	if (!strcmp(ses->group, arg1))
 	{
-		show_error(ses, LIST_CLASS, "#ERROR: #CLASS WRITE {%s} - COULDN'T OPEN FILE TO WRITE.", arg2);
-		
-		return ses;
+		show_message(ses, LIST_CLASS, "#CLASS {%s} IS ALREADY OPENED AND ACTIVATED.", arg1);
 	}
+	else
+	{
+		if (*ses->group)
+		{
+			check_all_events(ses, SUB_ARG, 0, 1, "CLASS DEACTIVATED", ses->group);
+			check_all_events(ses, SUB_ARG, 1, 1, "CLASS DEACTIVATED %s", ses->group, ses->group);
+		}
+		RESTRING(ses->group, arg1);
+
+		count = atoi(ses->list[LIST_CLASS]->list[0]->arg3);
+
+		update_node_list(ses->list[LIST_CLASS], arg1, "", ntos(--count), "");
+
+		show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN OPENED AND ACTIVATED.", arg1);
+
+		check_all_events(ses, SUB_ARG, 0, 1, "CLASS ACTIVATED", arg1);
+		check_all_events(ses, SUB_ARG, 1, 1, "CLASS ACTIVATED %s", arg1, arg1);
+	}
+
+	return ses;
+}
+
+
+DO_CLASS(class_read)
+{
+	class_open(ses, node, arg1, arg2);
+
+	do_read(ses, arg2);
+
+	class_close(ses, node, arg1, arg2);
+
+	return ses;
+}
+
+DO_CLASS(class_save)
+{
+	FILE *file;
+	int list, index;
+
+	str_cpy(&node->arg4, "");
+
+	file = open_memstream(&node->data, (size_t *) &node->val32[1]);
 
 	fprintf(file, "%cCLASS {%s} OPEN\n\n", gtd->tintin_char, arg1);
 
@@ -276,90 +321,61 @@ DO_CLASS(class_write)
 
 	fclose(file);
 
-	show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN WRITTEN TO FILE.", arg1);
+	show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN SAVED TO MEMORY (%d BYTES) (%s).", arg1, node->val32[1], node->data);
+
+	return ses;
+}	
+
+DO_CLASS(class_size)
+{
+	if (*arg1 == 0 || *arg2 == 0)
+	{
+		show_error(ses, LIST_CLASS, "#SYNTAX: #CLASS {<class name>} SIZE {<variable>}.");
+		
+		return ses;
+	}
+
+	set_nest_node_ses(ses, arg2, "%d", count_class(ses, node));
 
 	return ses;
 }
 
-DO_CLASS(class_kill)
+
+DO_CLASS(class_write)
 {
-	int type, index, group;
+	FILE *file;
+	int list, index;
 
-	if (!search_node_list(ses->list[LIST_CLASS], arg1))
+	if (*arg2 == 0 || (file = fopen(arg2, "w")) == NULL)
 	{
-		show_error(ses, LIST_CLASS, "#CLASS {%s} DOES NOT EXIST.", arg1);
-
+		show_error(ses, LIST_CLASS, "#ERROR: #CLASS WRITE {%s} - COULDN'T OPEN FILE TO WRITE.", arg2);
+		
 		return ses;
 	}
 
-	group = search_index_list(ses->list[LIST_CLASS], arg1, NULL);
+	fprintf(file, "%cCLASS {%s} OPEN\n\n", gtd->tintin_char, arg1);
 
-	for (type = 0 ; type < LIST_MAX ; type++)
+	for (list = 0 ; list < LIST_MAX ; list++)
 	{
-		if (!HAS_BIT(ses->list[type]->flags, LIST_FLAG_CLASS))
+		if (!HAS_BIT(ses->list[list]->flags, LIST_FLAG_CLASS))
 		{
 			continue;
 		}
 
-		for (index = 0 ; index < ses->list[type]->used ; index++)
+		for (index = 0 ; index < ses->list[list]->used ; index++)
 		{
-			if (!strcmp(ses->list[type]->list[index]->group, arg1))
+			if (!strcmp(ses->list[list]->list[index]->group, arg1))
 			{
-				delete_index_list(ses->list[type], index--);
+				write_node(ses, list, ses->list[list]->list[index], file);
 			}
 		}
 	}
 
-	check_all_events(ses, SUB_ARG, 0, 1, "CLASS DESTROYED", arg1);
-	check_all_events(ses, SUB_ARG, 1, 1, "CLASS DESTROYED %s", arg1, arg1);
-
-	delete_index_list(ses->list[LIST_CLASS], group);
-
-	show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN KILLED.", arg1);
-
-	if (!strcmp(ses->group, arg1))
-	{
-		check_all_events(ses, SUB_ARG, 0, 1, "CLASS DEACTIVATED", ses->group);
-
-		struct listnode *node = ses->list[LIST_CLASS]->list[0];
-
-		if (node && atoi(node->arg3))
-		{
-			RESTRING(ses->group, node->arg1);
-
-			show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN ACTIVATED.", node->arg1);
-
-			check_all_events(ses, SUB_ARG, 0, 1, "CLASS ACTIVATED", arg1);
-		}
-		else
-		{
-			RESTRING(ses->group, "");
-		}
-	}
-
-	return ses;
-}
-
-DO_CLASS(class_size)
-{
-	struct listnode *node;
+	fprintf(file, "\n%cCLASS {%s} CLOSE\n", gtd->tintin_char, arg1);
 
-	if (*arg1 == 0 || *arg2 == 0)
-	{
-		show_error(ses, LIST_CLASS, "#SYNTAX: #CLASS {<class name>} SIZE {<variable>}.");
-		
-		return ses;
-	}
+	fclose(file);
 
-	node = search_node_list(ses->list[LIST_CLASS], arg1);
+	show_message(ses, LIST_CLASS, "#CLASS {%s} HAS BEEN WRITTEN TO FILE.", arg1);
 
-	if (node == NULL)
-	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "0");
-	}
-	else
-	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", count_class(ses, node));
-	}
 	return ses;
 }

+ 45 - 14
src/config.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*   file: config.c - funtions related tintin++ configuration                  *
-*              (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                   *
-*                     coded by Igor van den Hoven 2004                        *
+*                               T I N T I N + +                               *
+*                                                                             *
+*                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
 
 
@@ -177,21 +176,26 @@ DO_CONFIG(config_charset)
 				DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
 			}
 		}
+		else if (is_abbrev(arg2, "ASCII"))
+		{
+			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+		}
 		else if (is_abbrev(arg2, "BIG-5"))
 		{
 			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
 			SET_BIT(ses->charset, CHARSET_FLAG_BIG5);
 		}
-		else if (is_abbrev(arg2, "UTF-8"))
+		else if (is_abbrev(arg2, "GBK-1"))
 		{
 			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
-			SET_BIT(ses->charset, CHARSET_FLAG_UTF8);
+			SET_BIT(ses->charset, CHARSET_FLAG_GBK1);
 		}
-		else if (is_abbrev(arg2, "ASCII"))
+		else if (is_abbrev(arg2, "UTF-8"))
 		{
 			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+			SET_BIT(ses->charset, CHARSET_FLAG_UTF8);
 		}
-		else if (is_abbrev(arg2, "BIG2UTF"))
+		else if (is_abbrev(arg2, "BIG5TOUTF8") || is_abbrev(arg2, "BIG2UTF"))
 		{
 			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
 			SET_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5TOUTF8);
@@ -201,14 +205,29 @@ DO_CONFIG(config_charset)
 			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
 			SET_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_FANSITOUTF8);
 		}
-		else if (is_abbrev(arg2, "KOI2UTF"))
+		else if (is_abbrev(arg2, "GBK1TOUTF8"))
+		{
+			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+			SET_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_GBK1TOUTF8);
+		}
+		else if (is_abbrev(arg2, "ISO1TOUTF8"))
+		{
+			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+			SET_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_ISO1TOUTF8);
+		}
+		else if (is_abbrev(arg2, "ISO2TOUTF8"))
+		{
+			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
+			SET_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_ISO2TOUTF8);
+		}
+		else if (is_abbrev(arg2, "KOI8TOUTF8"))
 		{
 			DEL_BIT(ses->charset, CHARSET_FLAG_ALL);
 			SET_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_KOI8TOUTF8);
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <AUTO|ASCII|BIG-5|FANSI|UTF-8|BIG2UTF|KOI2UTF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <AUTO|ASCII|BIG-5|BIG5TOUTF8|FANSI|GBK-1|GBK1TOUTF8|KOI8TOUTF8|UTF-8>", config_table[index].name);
 
 			return NULL;
 		}
@@ -219,17 +238,29 @@ DO_CONFIG(config_charset)
 		case CHARSET_FLAG_BIG5:
 			strcpy(arg2, "BIG-5");
 			break;
+		case CHARSET_FLAG_GBK1:
+			strcpy(arg2, "GBK-1");
+			break;
 		case CHARSET_FLAG_UTF8:
 			strcpy(arg2, "UTF-8");
 			break;
 		case CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5TOUTF8:
-			strcpy(arg2, "BIG2UTF");
+			strcpy(arg2, "BIG5TOUTF8");
 			break;
 		case CHARSET_FLAG_UTF8|CHARSET_FLAG_FANSITOUTF8:
 			strcpy(arg2, "FANSI");
 			break;
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_GBK1TOUTF8:
+			strcpy(arg2, "GBK1TOUTF8");
+			break;
 		case CHARSET_FLAG_UTF8|CHARSET_FLAG_KOI8TOUTF8:
-			strcpy(arg2, "KOI2UTF");
+			strcpy(arg2, "KOI8TOUTF8");
+			break;
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_ISO1TOUTF8:
+			strcpy(arg2, "ISO1TOUTF8");
+			break;
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_ISO2TOUTF8:
+			strcpy(arg2, "ISO2TOUTF8");
 			break;
 		default:
 			strcpy(arg2, "ASCII");
@@ -592,7 +623,7 @@ DO_CONFIG(config_mousetracking)
 		{
 			DEL_BIT(ses->flags, SES_FLAG_MOUSEINFO);
 			SET_BIT(ses->flags, SES_FLAG_MOUSEDEBUG);
-			SET_BIT(gtd->flags, SES_FLAG_MOUSETRACKING);
+			SET_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING);
 			print_stdout("\e[?1000h\e[?1002h\e[?1004h\e[?1006h");
 		}
 		else if (is_abbrev(arg2, "DEBUG INFO"))

+ 137 - 49
src/cursor.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,29 +13,27 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*              (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                   *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                     coded by Igor van den Hoven 2006                        *
+*                      coded by Igor van den Hoven 2006                       *
 ******************************************************************************/
 
-
 #include "tintin.h"
 
 DO_COMMAND(do_cursor)
 {
-	char all[BUFFER_SIZE], arg1[BUFFER_SIZE], right[BUFFER_SIZE], temp[BUFFER_SIZE];
+	char all[BUFFER_SIZE], arg1[BUFFER_SIZE], temp[BUFFER_SIZE];
 	int cnt;
 
 	get_arg_in_braces(ses, arg, all, GET_ALL);
 
 	arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
-	arg = sub_arg_in_braces(ses, arg, right, GET_ALL, SUB_VAR|SUB_FUN);
+//	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
 
 	if (*arg1 == 0)
 	{
@@ -60,7 +58,7 @@ DO_COMMAND(do_cursor)
 			{
 				if (is_abbrev(all, cursor_table[cnt].name))
 				{
-					cursor_table[cnt].fun(ses, right);
+					cursor_table[cnt].fun(ses, arg);
 
 					return ses;
 				}
@@ -69,7 +67,7 @@ DO_COMMAND(do_cursor)
 			{
 				if (is_abbrev(arg1, cursor_table[cnt].name))
 				{
-					cursor_table[cnt].fun(ses, right);
+					cursor_table[cnt].fun(ses, arg);
 
 					return ses;
 				}
@@ -225,23 +223,17 @@ int inputline_str_chk(int offset, int totlen)
 
 	while (offset < totlen)
 	{
-		if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_BIG5))
+		if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_EUC))
 		{
-			if (HAS_BIT(gtd->input_buf[offset], 128) && (unsigned char) gtd->input_buf[offset] < 255)
+			if (is_euc_head(gtd->ses, &gtd->input_buf[offset]))
 			{
-				if (offset + 1 >= totlen)
-				{
-					return FALSE;
-				}
+				size = get_euc_size(gtd->ses, &gtd->input_buf[offset]);
 
-				if (is_big5(&gtd->input_buf[offset]))
-				{
-					offset += 2;
-				}
-				else
+				if (size == 1 || offset + size > totlen)
 				{
-					offset += 1;
+					return FALSE;
 				}
+				offset += size;
 			}
 			else
 			{
@@ -457,13 +449,18 @@ DO_CURSOR(cursor_delete)
 		return;
 	}
 
-	if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && is_big5(&gtd->input_buf[gtd->input_cur]))
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(gtd->ses, &gtd->input_buf[gtd->input_cur]))
 	{
-		gtd->input_len--;
+		size = get_euc_width(gtd->ses, &gtd->input_buf[gtd->input_cur], &width);
 
-		memmove(&gtd->input_buf[gtd->input_cur+1], &gtd->input_buf[gtd->input_cur+2], gtd->input_len - gtd->input_cur + 1);
+		gtd->input_len -= size;
+
+		memmove(&gtd->input_buf[gtd->input_cur], &gtd->input_buf[gtd->input_cur + size], gtd->input_len - gtd->input_cur + 1);
 
-		input_printf("\e[2P");
+		if (width)
+		{
+			input_printf("\e[%dP", width);
+		}
 	}
 	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
 	{
@@ -605,15 +602,19 @@ DO_CURSOR(cursor_delete_word_right)
 
 DO_CURSOR(cursor_echo)
 {
-	if (*arg == 0)
+	char arg1[BUFFER_SIZE];
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
 	{
 		TOG_BIT(ses->telopts, TELOPT_FLAG_ECHO);
 	}
-	else if (!strcasecmp(arg, "ON"))
+	else if (!strcasecmp(arg1, "ON"))
 	{
 		SET_BIT(ses->telopts, TELOPT_FLAG_ECHO);
 	}
-	else if (!strcasecmp(arg, "OFF"))
+	else if (!strcasecmp(arg1, "OFF"))
 	{
 		DEL_BIT(ses->telopts, TELOPT_FLAG_ECHO);
 	}
@@ -679,13 +680,17 @@ DO_CURSOR(cursor_exit)
 
 DO_CURSOR(cursor_get)
 {
-	if (*arg == 0)
+	char arg1[BUFFER_SIZE];
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
 	{
 		show_error(ses, LIST_COMMAND, "#SYNTAX: #CURSOR GET {variable}");
 	}
 	else
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg, "%s", gtd->input_buf);
+		set_nest_node_ses(ses, arg1, "%s", gtd->input_buf);
 	}
 }
 
@@ -876,7 +881,7 @@ DO_CURSOR(cursor_history_find)
 
 	push_call("cursor_history_find(%s)", gtd->input_buf);
 
-	if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5))
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_EUC))
 	{
 		if (inputline_str_chk(0, gtd->input_len) == FALSE)
 		{
@@ -931,15 +936,19 @@ DO_CURSOR(cursor_home)
 
 DO_CURSOR(cursor_insert)
 {
-	if (*arg == 0)
+	char arg1[BUFFER_SIZE];
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
 	{
 		TOG_BIT(gtd->flags, TINTIN_FLAG_INSERTINPUT);
 	}
-	else if (!strcasecmp(arg, "ON"))
+	else if (!strcasecmp(arg1, "ON"))
 	{
 		SET_BIT(gtd->flags, TINTIN_FLAG_INSERTINPUT);
 	}
-	else if (!strcasecmp(arg, "OFF"))
+	else if (!strcasecmp(arg1, "OFF"))
 	{
 		DEL_BIT(gtd->flags, TINTIN_FLAG_INSERTINPUT);
 	}
@@ -956,7 +965,7 @@ DO_CURSOR(cursor_left)
 
 	if (gtd->input_cur > 0)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5))
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC))
 		{
 			gtd->input_cur--;
 			gtd->input_pos--;
@@ -1094,7 +1103,7 @@ DO_CURSOR(cursor_redraw_input)
 
 DO_CURSOR(cursor_redraw_line)
 {
-	if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5))
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8|CHARSET_FLAG_EUC))
 	{
 		if (inputline_str_chk(0, gtd->input_len) == FALSE)
 		{
@@ -1160,15 +1169,12 @@ DO_CURSOR(cursor_right)
 
 	if (gtd->input_cur < gtd->input_len)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && (gtd->input_buf[gtd->input_cur] & 128) == 128)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC))
 		{
-			if (gtd->input_cur + 1 < gtd->input_len && gtd->input_buf[gtd->input_cur+1])
-			{
-				gtd->input_cur += 2;
-				gtd->input_pos += 2;
+			gtd->input_cur += get_euc_width(gtd->ses, &gtd->input_buf[gtd->input_cur], &width);
 
-				input_printf("\e[2C");
-			}
+			input_printf("\e[%dC", width);
+			gtd->input_pos += width;
 		}
 		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
 		{
@@ -1230,17 +1236,21 @@ DO_CURSOR(cursor_right_word)
 
 DO_CURSOR(cursor_set)
 {
-	if (*arg == 0)
+	char arg1[BUFFER_SIZE];
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
 	{
 		return;
 	}
 
-	ins_sprintf(&gtd->input_buf[gtd->input_cur], "%s", arg);
+	ins_sprintf(&gtd->input_buf[gtd->input_cur], "%s", arg1);
 
-	gtd->input_len += strlen(arg);
-	gtd->input_cur += strlen(arg);
+	gtd->input_len += strlen(arg1);
+	gtd->input_cur += strlen(arg1);
 
-	gtd->input_pos += inputline_raw_str_len(gtd->input_cur - strlen(arg), gtd->input_cur);
+	gtd->input_pos += inputline_raw_str_len(gtd->input_cur - strlen(arg1), gtd->input_cur);
 
 	cursor_redraw_line(ses, "");
 
@@ -1466,6 +1476,84 @@ int cursor_calc_input_now(void)
 	return input_now;
 }
 
+DO_CURSOR(cursor_tab)
+{
+	char arg1[BUFFER_SIZE];
+	int flags;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+
+	while (*arg)
+	{
+		if (is_abbrev(arg1, "LIST"))
+		{
+			SET_BIT(flags, TAB_FLAG_LIST);
+		}
+		else if (is_abbrev(arg1, "SCROLLBACK"))
+		{
+			SET_BIT(flags, TAB_FLAG_SCROLLBACK);
+		}
+
+		arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+
+		if (*arg == COMMAND_SEPARATOR)
+		{
+			arg++;
+		}
+	}
+
+	if (is_abbrev(arg1, "FORWARD"))
+	{
+		if (!HAS_BIT(flags, TAB_FLAG_LIST|TAB_FLAG_SCROLLBACK))
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #CURSOR TAB <LIST|SCROLLBACK> FORWARD");
+		}
+		else
+		{
+			if (HAS_BIT(flags, TAB_FLAG_LIST|TAB_FLAG_SCROLLBACK) == (TAB_FLAG_LIST|TAB_FLAG_SCROLLBACK))
+			{
+				cursor_mixed_tab_forward(ses, "");
+			}
+			else if (HAS_BIT(flags, TAB_FLAG_LIST))
+			{
+				cursor_tab_forward(ses, "");
+			}
+			else
+			{
+				cursor_auto_tab_forward(ses, "");
+			}
+		}
+		SET_BIT(flags, TAB_FLAG_FORWARD);
+	}
+	else if (is_abbrev(arg1, "BACKWARD"))
+	{
+		if (!HAS_BIT(flags, TAB_FLAG_LIST|TAB_FLAG_SCROLLBACK))
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #CURSOR TAB <LIST|SCROLLBACK> BACKWARD");
+		}
+		else
+		{
+			if (HAS_BIT(flags, TAB_FLAG_LIST|TAB_FLAG_SCROLLBACK) == (TAB_FLAG_LIST|TAB_FLAG_SCROLLBACK))
+			{
+				cursor_mixed_tab_backward(ses, "");
+			}
+			else if (HAS_BIT(flags, TAB_FLAG_LIST))
+			{
+				cursor_tab_backward(ses, "");
+			}
+			else
+			{
+				cursor_auto_tab_backward(ses, "");
+			}
+		}
+		SET_BIT(flags, TAB_FLAG_BACKWARD);
+	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #CURSOR TAB <LIST|SCROLLBACK> <BACKWARD|FORWARD>");
+	}
+}
+
 DO_CURSOR(cursor_tab_forward)
 {
 	struct listroot *root = ses->list[LIST_COMMAND];
@@ -1643,7 +1731,7 @@ DO_CURSOR(cursor_screen_focus_in)
 {
 	gtd->screen->focus = 1;
 
-	check_all_events(gtd->ses, SUB_ARG, 1, 1, "SCREEN FOCUS", ntos(gtd->screen->focus));
+	check_all_events(gtd->ses, SUB_ARG, 0, 1, "SCREEN FOCUS", ntos(gtd->screen->focus));
 
 	msdp_update_all("SCREEN_FOCUS", "%d", gtd->screen->focus);
 }

+ 29 - 8
src/daemon.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*               (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                  *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                     coded by Igor van den Hoven 2007                        *
+*                      coded by Igor van den Hoven 2019                       *
 ******************************************************************************/
 
 #include "tintin.h"
@@ -186,8 +185,6 @@ DO_DAEMON(daemon_attach)
 		return;
 	}
 
-	DEL_BIT(gtd->flags, TINTIN_FLAG_DAEMONIZE);
-
 	sprintf(sock_file, "%s/%s.s", filename, arg2);
 
 	if (access(sock_file, F_OK) == -1)
@@ -203,12 +200,13 @@ DO_DAEMON(daemon_attach)
 
 		remove(sock_file);
 
-		if (*arg1 == 0)
+		if (HAS_BIT(gtd->flags, TINTIN_FLAG_DAEMONIZE) || *arg1 == 0)
 		{
 			goto start;
 		}
 		return;
 	}
+	DEL_BIT(gtd->flags, TINTIN_FLAG_DAEMONIZE);
 
 	memset(&addr_un, 0, sizeof(addr_un));
 
@@ -244,6 +242,16 @@ DO_DAEMON(daemon_attach)
 
 	show_message(ses, LIST_COMMAND, "#DAEMON ATTACH: CONNECTING {%d} TO {%d} {%s}", getpid(), gtd->attach_pid, sock_file);
 
+/*
+	error = select(gtd->attach_sock, NULL, &wds, NULL, &timeout);
+
+	if (error == -1)
+	{
+		syserr_printf(ses, "do_attach: %s: select:", sock_file);
+
+		return;
+	}
+*/
 	if (connect(gtd->attach_sock, (struct sockaddr *)&addr_un, sizeof(addr_un)) == -1)
 	{
 		syserr_printf(ses, "do_attach: %s: connect:", sock_file);
@@ -334,6 +342,11 @@ DO_DAEMON(daemon_detach)
 		return;
 	}
 
+	if (!get_daemon_dir(ses, filename))
+	{
+		return;
+	}
+
 	pid = fork();
 
 	if (pid < 0)
@@ -349,7 +362,7 @@ DO_DAEMON(daemon_detach)
 		{
 			DEL_BIT(gtd->flags, TINTIN_FLAG_DAEMONIZE);
 
-//			usleep(100000);
+			usleep(2000);
 
 			daemon_attach(ses, *arg1 ? arg1 : "pid");
 
@@ -640,6 +653,14 @@ void reset_daemon()
 
 		remove(gtd->detach_file);
 	}
+/*
+	if (gtd->attach_sock > 0)
+	{
+		print_stdout("unlinking(%s)\n", gtd->attach_file);
+
+		unlink(gtd->attach_file);
+	}
+*/
 }
 
 void winch_daemon()

+ 132 - 57
src/data.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                       coded by Igor van den Hoven 2004                      *
+*                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
 
 #include "tintin.h"
@@ -53,6 +52,7 @@ void kill_list(struct listroot *root)
 	{
 		delete_index_list(root, root->used - 1);
 	}
+//	root->update = 0;
 }
 
 void free_list(struct listroot *root)
@@ -79,10 +79,10 @@ struct listroot *copy_list(struct session *ses, struct listroot *sourcelist, int
 		{
 			node = (struct listnode *) calloc(1, sizeof(struct listnode));
 
-			node->arg1  = strdup(sourcelist->list[i]->arg1);
-			node->arg2  = strdup(sourcelist->list[i]->arg2);
-			node->arg3  = strdup(sourcelist->list[i]->arg3);
-			node->arg4  = strdup(sourcelist->list[i]->arg4);
+			node->arg1  = str_dup_clone(sourcelist->list[i]->arg1);
+			node->arg2  = str_dup_clone(sourcelist->list[i]->arg2);
+			node->arg3  = str_dup_clone(sourcelist->list[i]->arg3);
+			node->arg4  = str_dup_clone(sourcelist->list[i]->arg4);
 			node->flags = sourcelist->list[i]->flags;
 			node->group = strdup(sourcelist->list[i]->group);
 
@@ -129,15 +129,15 @@ struct listnode *insert_node_list(struct listroot *root, char *arg1, char *arg2,
 
 	node = (struct listnode *) calloc(1, sizeof(struct listnode));
 
-	if (HAS_BIT(root->flags, LIST_FLAG_PRIORITY) && *arg3 == 0)
+	if (list_table[root->type].priority_arg == 3 && *arg3 == 0)
 	{
 		strcpy(arg3, "5");
 	}
 
-	node->arg1 = strdup(arg1);
-	node->arg2 = strdup(arg2);
-	node->arg3 = strdup(arg3);
-	node->arg4 = strdup(arg4);
+	node->arg1 = str_dup(arg1);
+	node->arg2 = str_dup(arg2);
+	node->arg3 = str_dup(arg3);
+	node->arg4 = str_dup(arg4);
 
 	if (gtd->level->oneshot)
 	{
@@ -190,19 +190,18 @@ struct listnode *update_node_list(struct listroot *root, char *arg1, char *arg2,
 
 		if (strcmp(node->arg2, arg2) != 0)
 		{
-			free(node->arg2);
-			node->arg2 = strdup(arg2);
+			node->arg2 = str_cpy(&node->arg2, arg2);
 		}
 
 		switch (root->type)
 		{
 			case LIST_DELAY:
 			case LIST_TICKER:
-				node->data = 0;
+				node->val64 = 0;
 				break;
 		}
 
-		if (HAS_BIT(root->flags, LIST_FLAG_PRIORITY) && *arg3 == 0)
+		if (list_table[root->type].priority_arg == 3 && *arg3 == 0)
 		{
 			strcpy(arg3, "5");
 		}
@@ -226,8 +225,7 @@ struct listnode *update_node_list(struct listroot *root, char *arg1, char *arg2,
 			case SORT_DELAY:
 				if (strcmp(node->arg3, arg3) != 0)
 				{
-					free(node->arg3);
-					node->arg3 = strdup(arg3);
+					str_cpy(&node->arg3, arg3);
 				}
 				break;
 
@@ -282,10 +280,11 @@ void delete_index_list(struct listroot *root, int index)
 		root->update--;
 	}
 
-	free(node->arg1);
-	free(node->arg2);
-	free(node->arg3);
-	free(node->arg4);
+	str_free(node->arg1);
+	str_free(node->arg2);
+	str_free(node->arg3);
+	str_free(node->arg4);
+
 	free(node->group);
 
 	if (HAS_BIT(list_table[root->type].flags, LIST_FLAG_REGEX))
@@ -295,6 +294,21 @@ void delete_index_list(struct listroot *root, int index)
 			free(node->regex);
 		}
 	}
+
+	switch (root->type)
+	{
+		case LIST_TERRAIN:
+			free(node->room);
+			break;
+
+		case LIST_CLASS:
+			if (node->data)
+			{
+				free(node->data);
+			}
+			break;
+	}
+
 	free(node);
 
 	memmove(&root->list[index], &root->list[index + 1], (root->used - index) * sizeof(struct listnode *));
@@ -327,6 +341,7 @@ struct listnode *search_node_list(struct listroot *root, char *text)
 		pop_call();
 		return root->list[index];
 	}
+
 	pop_call();
 	return NULL;
 }
@@ -533,7 +548,7 @@ void show_node(struct listroot *root, struct listnode *node, int level)
 			tintin_printf2(root->ses, "%s" COLOR_TINTIN "#" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "} {" COLOR_STRING "%s" COLOR_BRACE "} {" COLOR_STRING "%s" COLOR_BRACE "} {" COLOR_STRING "%s" COLOR_BRACE "}", indent(level), list_table[root->type].name, node->arg1, str_arg2, node->arg3, node->arg4);
 			break;
 		case 3:
-			if (HAS_BIT(list_table[root->type].flags, LIST_FLAG_PRIORITY) && !strcmp(node->arg3, "5"))
+			if (list_table[root->type].priority_arg == 3 && !strcmp(node->arg3, "5"))
 			{
 				tintin_printf2(root->ses, "%s" COLOR_TINTIN "#" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "} {" COLOR_STRING "%s" COLOR_BRACE "}", indent(level), list_table[root->type].name, node->arg1, str_arg2);
 			}
@@ -583,35 +598,65 @@ int show_node_with_wild(struct session *ses, char *text, struct listroot *root)
 
 	if (node)
 	{
-		switch(root->type)
+		if (list_table[root->type].script_arg == 2)
 		{
-			case LIST_EVENT:
-			case LIST_FUNCTION:
-			case LIST_MACRO:
+			if (list_table[root->type].args == 2)
+			{
 				tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2));
-				break;
-
-			case LIST_ACTION:
-			case LIST_ALIAS:
-			case LIST_BUTTON:
-				if (!strcmp(node->arg3, "5"))
+			}
+			else if (list_table[root->type].args == 3)
+			{
+				if (list_table[root->type].priority_arg == 3)
 				{
-					tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2));
+					if (!strcmp(node->arg3, "5"))
+					{
+						tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2));
+					}
+					else
+					{
+						tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n{" COLOR_STRING "%s" COLOR_BRACE "}\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2), node->arg3);
+					}
 				}
 				else
 				{
 					tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n{" COLOR_STRING "%s" COLOR_BRACE "}\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2), node->arg3);
 				}
-				break;
-
-			case LIST_DELAY:
-			case LIST_TICKER:
-				tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n{" COLOR_STRING "%s" COLOR_BRACE "}\n\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2), node->arg3);
-				break;
+			}
+		}
+		else
+		{
+			show_node(root, node, 0);
+		}
 
-			default:
-				show_node(root, node, 0);
-				break;
+		switch(root->type)
+		{
+//			case LIST_EVENT:
+//			case LIST_FUNCTION:
+//			case LIST_MACRO:
+//				tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2));
+//				break;
+
+//			case LIST_ACTION:
+//			case LIST_ALIAS:
+//			case LIST_BUTTON:
+//				if (!strcmp(node->arg3, "5"))
+//				{
+//					tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2));
+//				}
+//				else
+//				{
+//					tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n{" COLOR_STRING "%s" COLOR_BRACE "}\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2), node->arg3);
+//				}
+//				break;
+
+//			case LIST_DELAY:
+//			case LIST_TICKER:
+//				tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s\n" COLOR_BRACE "}\n{" COLOR_STRING "%s" COLOR_BRACE "}\n\n", gtd->tintin_char, list_table[root->type].name, node->arg1, script_viewer(ses, node->arg2), node->arg3);
+//				break;
+
+//			default:
+//				show_node(root, node, 0);
+//				break;
 		}
 		pop_call();
 		return TRUE;
@@ -630,7 +675,7 @@ int show_node_with_wild(struct session *ses, char *text, struct listroot *root)
 	return found;
 }
 
-void delete_node_with_wild(struct session *ses, int type, char *text)
+int delete_node_with_wild(struct session *ses, int type, char *text)
 {
 	struct listroot *root = ses->list[type];
 	struct listnode *node;
@@ -647,7 +692,7 @@ void delete_node_with_wild(struct session *ses, int type, char *text)
 
 		delete_node_list(ses, type, node);
 
-		return;
+		return TRUE;
 	}
 
 	for (i = root->used - 1 ; i >= 0 ; i--)
@@ -665,7 +710,10 @@ void delete_node_with_wild(struct session *ses, int type, char *text)
 	if (found == 0)
 	{
 		show_message(ses, type, "#KILL: NO MATCHES FOUND FOR %s {%s}.", list_table[type].name, arg1);
+
+		return FALSE;
 	}
+	return TRUE;
 }
 
 
@@ -690,7 +738,10 @@ DO_COMMAND(do_kill)
 	{
 		for (index = 0 ; index < LIST_MAX ; index++)
 		{
-			kill_list(ses->list[index]);
+			if (!HAS_BIT(ses->list[index]->flags, LIST_FLAG_HIDE))
+			{
+				kill_list(ses->list[index]);
+			}
 		}
 		show_message(ses, LIST_COMMAND, "#KILL - ALL LISTS CLEARED.");
 
@@ -1002,13 +1053,13 @@ DO_COMMAND(do_info)
 				else if (is_abbrev(arg2, "SAVE"))
 				{
 					sprintf(name, "info[%s]", list_table[index].name);
-					delete_nest_node(ses->list[LIST_VARIABLE], name);
+//					delete_nest_node(ses->list[LIST_VARIABLE], name);
 
 					for (cnt = 0 ; cnt < root->used ; cnt++)
 					{
 						sprintf(name, "info[%s][%d]", list_table[index].name, cnt);
 
-						set_nest_node(ses->list[LIST_VARIABLE], name, "{arg1}{%s}{arg2}{%s}{arg3}{%s}{arg4}{%s}{class}{%s}{flags}{%d}", root->list[cnt]->arg1, root->list[cnt]->arg2, root->list[cnt]->arg3, root->list[cnt]->arg4, root->list[cnt]->group, root->list[cnt]->flags);
+						set_nest_node_ses(ses, name, "{arg1}{%s}{arg2}{%s}{arg3}{%s}{arg4}{%s}{class}{%s}{flags}{%d}", root->list[cnt]->arg1, root->list[cnt]->arg2, root->list[cnt]->arg3, root->list[cnt]->arg4, root->list[cnt]->group, root->list[cnt]->flags);
 					}
 					show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[%s]}", list_table[index].name);
 				}
@@ -1040,6 +1091,31 @@ DO_COMMAND(do_info)
 					tintin_printf2(ses, "#INFO MCCP3: TOTAL IN: %9ull TOTAL OUT: %9ull RATIO: %3d", ses->mccp3->total_in, ses->mccp3->total_out, 100 * ses->mccp3->total_out / ses->mccp3->total_in);
 				}
 			}
+			else if (is_abbrev(arg1, "SESSION"))
+			{
+				if (is_abbrev(arg2, "SAVE"))
+				{
+					sprintf(name, "info[SESSION]");
+
+					set_nest_node_ses(ses, name, "{SESSION_NAME}{%s}", ses->name);
+					add_nest_node_ses(ses, name, "{SESSION_CLASS}{%s}", ses->group);
+					add_nest_node_ses(ses, name, "{SESSION_CREATED}{%d}", ses->created);
+					add_nest_node_ses(ses, name, "{SESSION_HOST} {%s}", ses->session_host);
+					add_nest_node_ses(ses, name, "{SESSION_IP} {%s}", ses->session_ip);
+					add_nest_node_ses(ses, name, "{SESSION_PORT} {%s}", ses->session_port);
+
+					show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[SESSION]}");
+				}
+				else
+				{
+					tintin_printf2(ses, "{SESSION_NAME}{%s}", ses->name);
+					tintin_printf2(ses, "{SESSION_CLASS}{%s}", ses->group);
+					tintin_printf2(ses, "{SESSION_CREATED}{%d}", ses->created);
+					tintin_printf2(ses, "{SESSION_HOST} {%s}", ses->session_host);
+					tintin_printf2(ses, "{SESSION_IP} {%s}", ses->session_ip);
+					tintin_printf2(ses, "{SESSION_PORT} {%s}", ses->session_port);
+				}
+			}
 			else if (is_abbrev(arg1, "STACK"))
 			{
 				dump_stack();
@@ -1050,13 +1126,10 @@ DO_COMMAND(do_info)
 				{
 					sprintf(name, "info[SYSTEM]");
 
-					set_nest_node(ses->list[LIST_VARIABLE], name, "{CLIENT_NAME}{%s}{CLIENT_VERSION}{%-3s}", CLIENT_NAME, CLIENT_VERSION);
-
-					add_nest_node(ses->list[LIST_VARIABLE], name, "{CLIENT}{{NAME}{%s}{VERSION}{%-3s}}", CLIENT_NAME, CLIENT_VERSION);
-
-					add_nest_node(ses->list[LIST_VARIABLE], name, "{EXEC}{%s}{HOME}{%s}{LANG}{%s}{OS}{%s}{TERM}{%s}", gtd->exec, gtd->home, gtd->lang, gtd->os, gtd->term);
-
-					set_nest_node(ses->list[LIST_VARIABLE], name, "{DETACH_FILE}{%s}{ATTACH_FILE}{%-3s}", gtd->detach_port ? gtd->detach_file : "", gtd->attach_pid  ? gtd->attach_file : "");
+					set_nest_node_ses(ses, name, "{CLIENT_NAME}{%s}{CLIENT_VERSION}{%s}", CLIENT_NAME, CLIENT_VERSION);
+					add_nest_node_ses(ses, name, "{CLIENT}{{NAME}{%s}{VERSION}{%s}}", CLIENT_NAME, CLIENT_VERSION);
+					add_nest_node_ses(ses, name, "{EXEC}{%s}{HOME}{%s}{LANG}{%s}{OS}{%s}{TERM}{%s}", gtd->exec, gtd->home, gtd->lang, gtd->os, gtd->term);
+					add_nest_node_ses(ses, name, "{DETACH_FILE}{%s}{ATTACH_FILE}{%s}", gtd->detach_port > 0 ? gtd->detach_file : "", gtd->attach_sock > 0 ? gtd->attach_file : "");
 
 					show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[SYSTEM]}");
 				}
@@ -1069,8 +1142,10 @@ DO_COMMAND(do_info)
 					tintin_printf2(ses, "#INFO SYSTEM: LANG           = %s", gtd->lang);
 					tintin_printf2(ses, "#INFO SYSTEM: OS             = %s", gtd->os);
 					tintin_printf2(ses, "#INFO SYSTEM: TERM           = %s", gtd->term);
+					tintin_printf2(ses, "#INFO SYSTEM: DETACH_PORT    = %d", gtd->detach_port);
 					tintin_printf2(ses, "#INFO SYSTEM: DETACH_FILE    = %s", gtd->detach_port ? gtd->detach_file : "");
-					tintin_printf2(ses, "#INFO SYSTEM: ATTACH_FILE    = %s", gtd->attach_pid  ? gtd->attach_file : "");
+					tintin_printf2(ses, "#INFO SYSTEM: ATTACH_SOCK    = %d", gtd->attach_sock);
+					tintin_printf2(ses, "#INFO SYSTEM: ATTACH_FILE    = %s", gtd->attach_sock ? gtd->attach_file : "");
 				}
 			}
 			else

+ 3 - 4
src/debug.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*               (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                  *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                     coded by Igor van den Hoven 2004                        *
+*                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
 
 

+ 186 - 0
src/dict.c

@@ -0,0 +1,186 @@
+/******************************************************************************
+*   This file is part of TinTin++                                             *
+*                                                                             *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
+*                                                                             *
+*   TinTin++ is free software; you can redistribute it and/or modify          *
+*   it under the terms of the GNU General Public License as published by      *
+*   the Free Software Foundation; either version 3 of the License, or         *
+*   (at your option) any later version.                                       *
+*                                                                             *
+*   This program is distributed in the hope that it will be useful,           *
+*   but WITHOUT ANY WARRANTY; without even the implied warranty of            *
+*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
+*   GNU General Public License for more details.                              *
+*                                                                             *
+*   You should have received a copy of the GNU General Public License         *
+*   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
+******************************************************************************/
+
+/******************************************************************************
+*                               T I N T I N + +                               *
+*                                                                             *
+*                      coded by Igor van den Hoven 2019                       *
+******************************************************************************/
+
+#include "tintin.h"
+
+#include "dict.h"
+
+int dictionary_search(char **array, int array_size, char *key)
+{
+        // Copyright 2014-2020 Igor van den Hoven
+
+	register int mid, i, bot;
+	register char val;
+
+	bot = 0;
+	i = array_size - 1;
+	mid = i / 2;
+
+	while (mid)
+	{
+		val = strcmp(key, array[i - mid]);
+
+		if (val < 0)
+		{
+			i -= mid + 1;
+		}
+		else if (val > 0)
+		{
+			bot = i - mid;
+		}
+		else
+		{
+			return i - mid;
+		}
+		mid = (i - bot) / 2;
+	}
+
+	if (i > bot)
+	{
+		val = strcmp(key, array[i]);
+
+		if (val > 0)
+		{
+			return -1;
+		}
+		else if (val < 0)
+		{
+			--i;
+		}
+		else
+		{
+			return i;
+		}
+	}
+
+	if (!strcmp(key, array[i]))
+	{
+		return i;
+	}
+	return -1;
+}
+
+void dictionary_lowerstring(char *in, char *out)
+{
+	char *pti, *pto;
+
+	pti = in;
+	pto = out;
+
+	while (*pti)
+	{
+		if (isalpha(*pti))
+		{
+			*pto++ = tolower(*pti++);
+		}
+		else
+		{
+			pti++;
+		}
+	}
+	*pto = 0;
+}
+
+int spellcheck_count(struct session *ses, char *in)
+{
+	char *arg, arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
+	int cnt, hash, index;
+
+	cnt = 0;
+	arg = in;
+
+	while (*arg)
+	{
+		arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
+
+		dictionary_lowerstring(arg1, arg2);
+
+		if (isalpha(*arg2))
+		{
+			hash = *arg2 - 'a';
+
+			index = dictionary_search(wordlist[hash], wordlist_size[hash], arg2 + 1);
+
+			if (index == -1)
+			{
+				cnt++;
+			}
+		}
+
+		if (*arg == COMMAND_SEPARATOR)
+		{
+			arg++;
+		}
+	}
+	return cnt;
+}
+
+DO_COMMAND(do_dictionary)
+{
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE];
+	int hash, size, index;
+
+	sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0 || !isalpha(*arg1))
+	{
+		show_message(ses, LIST_COMMAND, "#SYNTAX: #DICTIONARY {WORD}");
+
+		return ses;
+	}
+
+	arg = arg1;
+
+	while (*arg)
+	{
+		arg = get_arg_in_braces(ses, arg, arg2, GET_ONE);
+
+		dictionary_lowerstring(arg2, arg3);
+
+		if (isalpha(*arg3))
+		{
+			hash = *arg3 - 'a';
+
+			size = wordlist_size[hash];
+
+			index = dictionary_search(wordlist[hash], size, arg3 + 1);
+
+			if (index == -1)
+			{
+				tintin_printf2(ses, "\e[1;31m%s", arg2);
+			}
+			else
+			{
+				tintin_printf2(ses, "\e[1;32m%s", arg2);
+			}
+		}
+
+		if (*arg == COMMAND_SEPARATOR)
+		{
+			arg++;
+		}
+	}
+	return ses;
+}

Datei-Diff unterdrückt, da er zu groß ist
+ 25 - 0
src/dict.h


Datei-Diff unterdrückt, da er zu groß ist
+ 782 - 36
src/draw.c


+ 86 - 32
src/event.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2007                       *
 ******************************************************************************/
@@ -65,7 +64,7 @@ DO_COMMAND(do_event)
 
 				node = update_node_list(ses->list[LIST_EVENT], arg1, arg2, "", "");
 
-				node->data = event_table[cnt].flags;
+				node->val64 = event_table[cnt].flags;
 
 				return ses;
 			}
@@ -86,7 +85,7 @@ int check_all_events(struct session *ses, int flags, int args, int vars, char *f
 {
 	struct session *ses_ptr;
 	struct listnode *node;
-	char name[BUFFER_SIZE], buf[BUFFER_SIZE];
+	char *name, buf[BUFFER_SIZE];
 	va_list list;
 	int cnt;
 
@@ -99,13 +98,13 @@ int check_all_events(struct session *ses, int flags, int args, int vars, char *f
 	{
 		va_start(list, fmt);
 
-		vsprintf(name, fmt, list);
+		vasprintf(&name, fmt, list);
 
 		va_end(list); 
 	}
 	else
 	{
-		strcpy(name, fmt);
+		name = strdup(fmt);
 	}
 
 	push_call("check_all_events(%p,%d,%d,%d,%s, ...)",ses,flags,args,vars,name);
@@ -152,6 +151,8 @@ int check_all_events(struct session *ses, int flags, int args, int vars, char *f
 
 				if (ses)
 				{
+					free(name);
+
 					pop_call();
 					return 1;
 				}
@@ -160,10 +161,14 @@ int check_all_events(struct session *ses, int flags, int args, int vars, char *f
 
 		if (ses)
 		{
+			free(name);
+
 			pop_call();
 			return 0;
 		}
 	}
+	free(name);
+
 	pop_call();
 	return 0;
 }
@@ -171,12 +176,21 @@ int check_all_events(struct session *ses, int flags, int args, int vars, char *f
 void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], line[BUFFER_SIZE], word[BUFFER_SIZE];
-	static char last[100];
+	static char last[100], dir[10];
 	static long long click[3];
-	int debug, info;
+	static int swipe[10];
+	int debug, info, rev_row, rev_col;
 
 	push_call("mouse_handler(%p,%d,%d,%d,%c)",ses,flags,row,col,type);
 
+	if (!HAS_BIT(ses->event_flags, EVENT_FLAG_MOUSE))
+	{
+		if (!HAS_BIT(ses->flags, SES_FLAG_MOUSEINFO) && !HAS_BIT(ses->list[LIST_EVENT]->flags, LIST_FLAG_INFO))
+		{
+			return;
+		}
+	}
+
 	if (HAS_BIT(flags, MOUSE_FLAG_MOTION))
 	{
 		strcpy(arg1, "MOVED");
@@ -297,7 +311,6 @@ void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 		}
 	}
 
-
 	debug = HAS_BIT(ses->flags, SES_FLAG_MOUSEDEBUG) ? 1 : 0;
 	info  = HAS_BIT(ses->flags, SES_FLAG_MOUSEINFO) ? 1 : 0;
 
@@ -306,18 +319,26 @@ void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 
 	check_all_buttons(ses, row, col, arg1, arg2, word, line);
 
-	check_all_events(ses, SUB_ARG, 2, 6, "%s %s", arg1, arg2, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+	rev_row = -1 - (gtd->screen->rows - row);
+	rev_col = -1 - (gtd->screen->cols - col);
 
-	check_all_events(ses, SUB_ARG, 3, 6, "%s %s %d", arg1, arg2, row, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+	check_all_events(ses, SUB_ARG, 2, 6, "%s %s", arg1, arg2, ntos(row), ntos(col), ntos(rev_row), ntos(-1 - (gtd->screen->cols - col)), word, line);
 
-	check_all_events(ses, SUB_ARG, 3, 6, "%s %s %d", arg1, arg2, -1 - (gtd->screen->rows - row), ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+	check_all_events(ses, SUB_ARG, 3, 6, "%s %s %d", arg1, arg2, row, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-	map_mouse_handler(ses, arg1, arg2, col, row);
+	check_all_events(ses, SUB_ARG, 3, 6, "%s %s %d", arg1, arg2, rev_row, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
+
+	map_mouse_handler(ses, arg1, arg2, col, row, 0, 0);
 
 	if (!strcmp(arg1, "PRESSED"))
 	{
 		sprintf(arg1, "PRESSED %s %d %d", arg2, col, row);
 
+		swipe[0] = row;
+		swipe[1] = col;
+		swipe[2] = rev_row;
+		swipe[3] = rev_col;
+
 		if (!strcmp(arg1, last))
 		{
 			click[2] = click[1];
@@ -330,13 +351,13 @@ void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 				{
 					check_all_buttons(ses, row, col, "TRIPLE-CLICKED", arg2, word, line);
 
-					check_all_events(ses, SUB_ARG, 1, 6, "TRIPLE-CLICKED %s", arg2, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+					check_all_events(ses, SUB_ARG, 1, 6, "TRIPLE-CLICKED %s", arg2, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-					check_all_events(ses, SUB_ARG, 2, 6, "TRIPLE-CLICKED %s %d", arg2, row, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+					check_all_events(ses, SUB_ARG, 2, 6, "TRIPLE-CLICKED %s %d", arg2, row, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-					check_all_events(ses, SUB_ARG, 2, 6, "TRIPLE-CLICKED %s %d", arg2, -1 - (gtd->screen->rows - row), ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+					check_all_events(ses, SUB_ARG, 2, 6, "TRIPLE-CLICKED %s %d", arg2, rev_row, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-					map_mouse_handler(ses, "TRIPPLE-CLICKED", arg2, col, row);
+					map_mouse_handler(ses, "TRIPPLE-CLICKED", arg2, col, row, 0, 0);
 
 					strcpy(last, "");
 				}
@@ -344,13 +365,13 @@ void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 				{
 					check_all_buttons(ses, row, col, "DOUBLE-CLICKED", arg2, word, line);
 
-					check_all_events(ses, SUB_ARG, 1, 6, "DOUBLE-CLICKED %s", arg2, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+					check_all_events(ses, SUB_ARG, 1, 6, "DOUBLE-CLICKED %s", arg2, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-					check_all_events(ses, SUB_ARG, 2, 6, "DOUBLE-CLICKED %s %d", arg2, row, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+					check_all_events(ses, SUB_ARG, 2, 6, "DOUBLE-CLICKED %s %d", arg2, row, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-					check_all_events(ses, SUB_ARG, 2, 6, "DOUBLE-CLICKED %s %d", arg2, -1 - (gtd->screen->rows - row), ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+					check_all_events(ses, SUB_ARG, 2, 6, "DOUBLE-CLICKED %s %d", arg2, rev_row, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-					map_mouse_handler(ses, "DOUBLE-CLICKED", arg2, col, row);
+					map_mouse_handler(ses, "DOUBLE-CLICKED", arg2, col, row, 0, 0);
 				}
 			}
 		}
@@ -365,29 +386,62 @@ void mouse_handler(struct session *ses, int flags, int row, int col, char type)
 	}
 	else if (!strcmp(arg1, "RELEASED"))
 	{
-		if (utime() - click[0] >= 500000)
+		swipe[4] = row;
+		swipe[5] = col;
+		swipe[6] = rev_row;
+		swipe[7] = rev_col;
+
+		swipe[8] = swipe[4] - swipe[0];
+		swipe[9] = swipe[5] - swipe[1];
+
+		if (abs(swipe[8]) > 3 || abs(swipe[9]) > 3)
+		{
+			if (abs(swipe[8]) <= 3)
+			{
+				strcpy(dir, swipe[9] > 0 ? "E" : "W");
+			}
+			else if (abs(swipe[9]) <= 3)
+			{
+				strcpy(dir, swipe[8] > 0 ? "S" : "N");
+			}
+			else if (swipe[8] > 0)
+			{
+				strcpy(dir, swipe[9] > 0 ? "SE" : "SW");
+			}
+			else
+			{
+				strcpy(dir, swipe[9] > 0 ? "NE" : "NW");
+			}
+			check_all_events(ses, SUB_ARG, 0, 12, "SWIPED", dir, arg2, ntos(swipe[0]), ntos(swipe[1]), ntos(swipe[2]), ntos(swipe[3]), ntos(swipe[4]), ntos(swipe[5]), ntos(swipe[6]), ntos(swipe[7]), ntos(swipe[8]), ntos(swipe[9]));			
+			check_all_events(ses, SUB_ARG, 1, 12, "SWIPED %s", dir, dir, arg2, ntos(swipe[0]), ntos(swipe[1]), ntos(swipe[2]), ntos(swipe[3]), ntos(swipe[4]), ntos(swipe[5]), ntos(swipe[6]), ntos(swipe[7]), ntos(swipe[8]), ntos(swipe[9]));
+			check_all_events(ses, SUB_ARG, 2, 12, "SWIPED %s %s", arg2, dir, dir, arg2, ntos(swipe[0]), ntos(swipe[1]), ntos(swipe[2]), ntos(swipe[3]), ntos(swipe[4]), ntos(swipe[5]), ntos(swipe[6]), ntos(swipe[7]), ntos(swipe[8]), ntos(swipe[9]));
+		}
+		else if (utime() - click[0] >= 500000)
 		{
 			check_all_buttons(ses, row, col, "LONG-CLICKED", arg2, word, line);
 
-			check_all_events(ses, SUB_ARG, 1, 6, "LONG-CLICKED %s", arg2, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+			check_all_events(ses, SUB_ARG, 1, 6, "LONG-CLICKED %s", arg2, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-			check_all_events(ses, SUB_ARG, 2, 6, "LONG-CLICKED %s %d", arg2, row, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+			check_all_events(ses, SUB_ARG, 2, 6, "LONG-CLICKED %s %d", arg2, row, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-			check_all_events(ses, SUB_ARG, 2, 6, "LONG-CLICKED %s %d", arg2, -1 - (gtd->screen->rows - row), ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+			check_all_events(ses, SUB_ARG, 2, 6, "LONG-CLICKED %s %d", arg2, rev_row, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-			map_mouse_handler(ses, "LONG-CLICKED", arg2, col, row);
+			map_mouse_handler(ses, "LONG-CLICKED", arg2, col, row, 0, 0);
 		}
 		else if (click[0] - click[1] >= 500000)
 		{
-			check_all_buttons(ses, row, col, "SHORT-CLICKED", arg2, word, line);
+			if (abs(swipe[0] - swipe[4]) <= 3 && abs(swipe[1] - swipe[5]) <= 3)
+			{
+				check_all_buttons(ses, row, col, "SHORT-CLICKED", arg2, word, line);
 
-			check_all_events(ses, SUB_ARG, 1, 6, "SHORT-CLICKED %s", arg2, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+				check_all_events(ses, SUB_ARG, 1, 6, "SHORT-CLICKED %s", arg2, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-			check_all_events(ses, SUB_ARG, 2, 6, "SHORT-CLICKED %s %d", arg2, row, ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+				check_all_events(ses, SUB_ARG, 2, 6, "SHORT-CLICKED %s %d", arg2, row, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-			check_all_events(ses, SUB_ARG, 2, 6, "SHORT-CLICKED %s %d", arg2, -1 - (gtd->screen->rows - row), ntos(row), ntos(col), ntos(-1 - (gtd->screen->rows - row)), ntos(-1 - (gtd->screen->cols - col)), word, line);
+				check_all_events(ses, SUB_ARG, 2, 6, "SHORT-CLICKED %s %d", arg2, rev_row, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), word, line);
 
-			map_mouse_handler(ses, "SHORT-CLICKED", arg2, col, row);
+				map_mouse_handler(ses, "SHORT-CLICKED", arg2, col, row, 0, 0);
+			}
 		}
 	}
 

+ 14 - 6
src/files.c

@@ -31,11 +31,8 @@
 
 DO_COMMAND(do_read)
 {
+	char filename[BUFFER_SIZE];
 	FILE *fp;
-	struct stat filedata;
-	char *bufi, *bufo, filename[BUFFER_SIZE], temp[BUFFER_SIZE], *pti, *pto, last = 0;
-	int lvl, cnt, com, lnc, fix, ok;
-	int counter[LIST_MAX];
 
 	sub_arg_in_braces(ses, arg, filename, GET_ONE, SUB_VAR|SUB_FUN);
 
@@ -48,13 +45,23 @@ DO_COMMAND(do_read)
 		return ses;
 	}
 
+	return read_file(ses, fp, filename);
+}
+
+struct session *read_file(struct session *ses, FILE *fp, char *filename)
+{
+	struct stat filedata;
+	char *bufi, *bufo, temp[INPUT_SIZE], *pti, *pto, last = 0;
+	int lvl, cnt, com, lnc, fix, ok;
+	int counter[LIST_MAX];
+
 	temp[0] = getc(fp);
 
 	if (!ispunct((int) temp[0]))
 	{
 		check_all_events(ses, SUB_ARG, 0, 2, "READ ERROR", filename, "INVALID START OF FILE");
 
-		tintin_printf(ses, "#ERROR: #READ {%s} - INVALID START OF FILE.", filename);
+		tintin_printf(ses, "#ERROR: #READ {%s} - INVALID START OF FILE '%c'.", filename, temp[0]);
 
 		fclose(fp);
 
@@ -95,10 +102,11 @@ DO_COMMAND(do_read)
 	{
 		if (com == 0)
 		{
-			if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+			if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 			{
 				*pto++ = *pti++;
 				*pto++ = *pti++;
+
 				continue;
 			}
 

+ 241 - 107
src/help.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
@@ -39,7 +38,7 @@ char *help_related(struct session *ses, int index, int html)
 {
 	char *arg;
 	char tmp[BUFFER_SIZE], link[BUFFER_SIZE];
-	static char buf[BUFFER_SIZE];
+	static char buf[INPUT_SIZE];
 
 	push_call("help_related(%p,%d,%d)",ses,index,html);
 
@@ -297,7 +296,7 @@ struct help_type help_table[] =
 	{
 		"ALIAS",
 
-		"<178>Command<278>: #alias <178>{<278>name<178>} {<278>commands<178>}<278>\n"
+		"<178>Command<278>: #alias <178>{<278>name<178>} {<278>commands<178>} {<278>priority<178>}<278>\n"
 		"\n"
 		"         The #alias command can be used to shorten up long or oftenly used\n"
 		"         commands. The %1-99 variables are substituted from the arguments when\n"
@@ -530,6 +529,16 @@ struct help_type help_table[] =
 
 		"default statements switch"
 	},
+	{
+		"CAT",
+
+		"<178>Command<278>: #cat <178>{<278>variable<178>} {<278>argument<178>}<278>\n"
+		"\n"
+		"         The cat command will concatinate the argument to the given variable.\n",
+		
+		"format function local math replace script variable"
+	},
+
 	{
 		"CHARACTERS",
 
@@ -548,7 +557,7 @@ struct help_type help_table[] =
 		"         well. Trailing semi-colons are ignored when reading a script file\n"
 		"         as this is a common error.\n"
 		"\n"
-		"{ }      Curly brackets aka braces are used for seperating multi word command\n"
+		"{ }      Curly brackets aka braces are used for separating multi word command\n"
 		"         arguments, nesting commands, and nesting variables. Braces cannot\n"
 		"         easily be escaped and must always be used in pairs.\n"
 		"\n"
@@ -610,13 +619,27 @@ struct help_type help_table[] =
 
 		"<178>Command<278>: #class <178>{<278>name<178>} {<278>open<178>|<278>close<178>|<278>list<178>|<278>read<178>|<278>size<178>|<278>write<178>|<278>kill<178>} {<278>arg<178>}<278>\n"
 		"\n"
-		"         The {open} option will open a class, closing a previously opened\n"
-		"         class. All triggers added afterwards are assigned to this class.\n"
-		"         The {close} option will close the given class.\n"
-		"         The {list} option will show the given list of the class.\n"
-		"         The {read} option will open the class, read, and close afterwards.\n"
-		"         The {size} option will store the size of the class in a variable.\n"
-		"         The {write} option will write all triggers of the given class to file.\n"
+		"         <178>#class {<name>} {open}\n"
+		"         <278>  Open a class, closing a previously opened class. All triggers\n"
+		"         <278>  added afterwards are assigned to this class.\n"
+		"         <178>#class {<name>} {clear}\n"
+		"         <278>  Will delete all triggers associated with the given class.\n"
+		"         <178>#class {<name>} {close}\n"
+		"         <278>  Close the given class, opening the last open class, if any.\n"
+		"         <178>#class {<name>} {kill}\n"
+		"         <278>  Will clear, close, and remove the class.\n"
+		"         <178>#class {<name>} {list}\n"
+		"         <278>  List all triggers associated with the given class.\n"
+		"         <178>#class {<name>} {load}\n"
+		"         <278>  Will load the saved copy of the class from memory.\n"
+		"         <178>#class {<name>} {read} {<filename>\n"
+		"         <278>  Will open the class, read the file, and close afterwards.\n"
+		"         <178>#class {<name>} {save}\n"
+		"         <278>  Will save all triggers of the given class to memory.\n"
+		"         <178>#class {<name>} {size} {<variable>}\n"
+		"         <278>  Will store the size of the class in a variable.\n"
+		"         <178>#class {<name>} {write} {<filename>}\n"
+		"         <278>  Will write all triggers of the given class to file.\n"
 		"         The {kill} option will delete all triggers of the given class.\n"
 		"\n"
 		"         Keep in mind that the kill and read option are very fast allowing\n"
@@ -709,7 +732,7 @@ struct help_type help_table[] =
 		"<178>Command<278>: #config <178>{<278>option<178>} {<278>argument<178>}<278>\n"
 		"\n"
 		"         This allows you to configure various settings, the settings can be\n"
-		"         written to file with the #write or #writesession command.\n"
+		"         written to file with the #write command.\n"
 		"\n"
 		"         If you configure the global session (the one you see as you start up\n"
 		"         tintin) all sessions started will inherite these settings.\n"
@@ -723,7 +746,7 @@ struct help_type help_table[] =
 		"         #CONFIG {CHILD LOCK}   {ON|OFF} Enable or disable command input.\n"
 		"         #CONFIG {CONVERT META} {ON|OFF} Shows color codes and key bindings.\n"
 		"         #CONFIG {DEBUG TELNET} {ON|OFF} Shows telnet negotiations y/n.\n"
-		"         #CONFIG {LOG LEVEL}  {LOW|HIGH} LOW logs mud output before triggers.\n"
+		"         #CONFIG {LOG LEVEL}  {LOW|HIGH} LOW logs server output before triggers.\n"
 		"         #CONFIG {INHERITANCE}  {ON|OFF} Session trigger inheritance y/n.\n"
 		"         #CONFIG {MCCP}         {ON|OFF} Enable or disable MCCP support.\n"
 		"         #CONFIG {PID}          {NUMBER} Set the PID of the master process.\n",
@@ -835,7 +858,7 @@ struct help_type help_table[] =
 		"         before executing the given command. tintin won't wait before\n"
 		"         executing following input commands if any.\n"
 		"\n"
-		"         Floating point precision for miliseconds is possible.\n"
+		"         Floating point precision for milliseconds is possible.\n"
 		"\n"
 		"<178>Example<278>: #showme first;#delay {1} {#showme last}\n"
 		"         This will print 'first', and 'last' around one second later.\n"
@@ -871,15 +894,21 @@ struct help_type help_table[] =
 		"         BUMPED     will precede the draw with an enter.\n"
 		"         CIRCLED    will circle the corners.\n"
 		"         CONVERT    will draw text with meta conversion.\n"
-		"         CORNERED   will draw lines with corners if possible.\n"
 		"         CROSSED    will cross the corners.\n"
+		"         FILLED     will fill circles and jewels.\n"
+		"         GRID       will draw TABLE as a grid.\n"
 		"         HORIZONTAL will draw horizontal if possible.\n"
+		"         HUGE       will draw text in huge letters.\n"
 		"         JEWELED    will diamond the corners.\n"
+		"         JOINTED    will draw corners.\n"
 		"         LEFT       will draw on the left side if possible.\n"
+		"         NUMBERED   will draw numbered, mostly for debugging.\n"
 		"         PRUNED     will prune the corners.\n"
 		"         RIGHT      will draw on the right side if possible.\n"
 		"         ROUNDED    will round the corners.\n"
+		"         SHADOWED   will shadow HUGE text.\n"
 		"         TEED       will tee the corners.\n"
+		"         TRACED     will trace HUGE text.\n"
 		"         TOP        will draw on the top side if possible.\n"
 		"         TUBED      will draw tubes instead of lines.\n"
 		"         UNICODE    will draw in unicode mode.\n"
@@ -887,16 +916,26 @@ struct help_type help_table[] =
 		"\n"
 		"         The following types are available.\n"
 		"\n"
-		"         BOX        will draw a box.\n"
-		"         LINE       will draw a line.\n"
-		"         SIDE       will draw one or more sides of a box.\n"
-		"         TILE       will draw a tile\n"
+		"         <178>[ASCII|UNICODE|HUGE] BOX {[TEXT1]} {[TEXT2]}\n"
+		"         <278>  will draw a box.\n"
+		"         <178>[BLANKED|CIRCLED|CROSSED|JEWELED|ROUNDED|TEED|PRUNED] CORNER\n"
+		"         <278>  will draw a corner.\n"
+		"         <178>[BLANKED|HORIZONTAL|NUMBERED|TUBED|VERTICAL] LINE {[TEXT]}\n"
+		"         <278>  will draw a line.\n"
+		"         <178>RAIN {<VARIABLE>} {[SPAWN]} {[FADE]} {[LEGEND]}\n"
+		"         <278>  will draw digital rain.\n"
+		"         <178>[JOINTED|TOP|LEFT|BOTTOM|RIGHT] SIDE\n"
+		"         <278>  will draw one or more sides of a box.\n"
+		"         <178>[GRID] TABLE {[LIST1]} {[LIST2]}\n"
+		"         <278> will draw a table.\n"
+		"         <178>[HUGE] TILE {[TEXT1]} {[TEXT2]}\n"
+		"         <278>  will draw a tile\n"
 		"\n"
 		"         All draw types take an optional text argument as long as a valid\n"
 		"         square with enough space has been defined. Text is automatically\n"
 		"         word wrapped.\n"
 		"\n"
-		"<178>Example<278>: #draw {box} 1 1 3 20 {Hello world!}\n",
+		"<178>Example<278>: #draw Blue box 1 1 3 20 {Hello world!}\n",
 
 		"buffer echo grep showme"
 	},
@@ -907,7 +946,7 @@ struct help_type help_table[] =
 		"<178>Command<278>: #echo <178>{<278>format<178>} {<278>argument1<178>} {<278>argument2<178>} {<278>etc<178>}<278>\n"
 		"\n"
 		"         Echo command displays text on the screen with formatting options. See\n"
-		"         the help file for the format command for more informations.\n"
+		"         the help file for the format command for more information.\n"
 		"\n"
 		"         The echo command does not trigger actions.\n"
 		"\n"
@@ -918,7 +957,7 @@ struct help_type help_table[] =
 		"         #echo {[%38s][%-38s]} {Hello World} {Hello World}\n"
 		"         #echo {{this is %s on the top row} {-1}} {printed}\n",
 		
-		"buffer grep showme"
+		"buffer format grep showme"
 	},
 	{
 		"ELSE",
@@ -1006,8 +1045,8 @@ struct help_type help_table[] =
 		"         IAC <VAR> <VAR>\n"
 		"         IAC SB GMCP <MODULE>   %0 data     %1 raw data\n"
 		"         IAC SB MSSP            %0 variable %1 value\n"
-		"         IAC SB MSDP            %0 variable %1 value\n"
-		"         IAC SB MSDP <VAR>      %1 value\n"
+		"         IAC SB MSDP            %0 variable %1 value %2 plain value\n"
+		"         IAC SB MSDP <VAR>      %0 variable %1 value %2 plain value\n"
 		"         IAC SB NEW-ENVIRON     %0 variable %1 value\n"
 		"         IAC SB ZMP <VAR>       %0 value\n"
 		"         IAC SB <VAR>           %0 raw text %1 raw data\n"
@@ -1019,6 +1058,7 @@ struct help_type help_table[] =
 		"         MAP EXIT ROOM          %0 old vnum %1 new vnum\n"
 		"         MAP EXIT ROOM <VAR>    %0 old vnum %1 new vnum\n"
 		"         MAP FOLLOW MAP         %0 old vnum %1 new vnum %2 exit name\n"
+		"         MAP MOUSE LOCATION     %0 vnum %1 location\n"
 		"         MAP UPDATED VTMAP\n"
 		"         MINUTE                 %5 minute\n"
 		"         MONTH                  %1 month\n"
@@ -1042,7 +1082,10 @@ struct help_type help_table[] =
 		"         SCAN CSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
 		"         SCAN TSV HEADER        %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
 		"         SCAN TSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
-		"         SCREEN RESIZE          %0 rows %1 cols %1 height %2 width\n"
+		"         SCREEN FOCUS           %0 focus (0 or 1)\n"
+		"         SCREEN LOCATION        %0 rows %1 cols  %2 height %3 width\n"
+		"         SCREEN MOUSE LOCATION  %0-3 screen row/col %4-7 cell row/col %8 loc\n"
+		"         SCREEN RESIZE          %0 rows %1 cols %2 height %3 width\n"
 		"         SCREEN SPLIT           %0 top row %1 top col %2 bot row %3 bot col\n"
 		"         SCREEN UNSPLIT         %0 top row %1 top col %2 bot row %3 bot col\n"
 		"         SCROLLED <VAR>         %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
@@ -1056,6 +1099,9 @@ struct help_type help_table[] =
 		"         SESSION DISCONNECTED   %0 name %1 host %2 ip %3 port\n"
 		"         SESSION TIMED OUT      %0 name %1 host %2 ip %3 port\n"
 		"         SHORT-CLICKED <VAR>    %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
+		"         SWIPED <DIR>\n"
+		"           %0 dir %1 button %2 row %3 col %4 -row %5 -col %6 row %7 col %8 -row\n"
+		"           %9 -col %10 rows %11 cols\n"
 		"         SYSTEM ERROR           %0 name %1 system msg %2 error %3 error msg\n"
 		"         TIME                   %4 hour : %5 minute : %6 second\n"
 		"         TRIPLE-CLICKED <VAR>   %0 row %1 col %2 -row %3 -col %4 word %5 line\n"
@@ -1064,8 +1110,6 @@ struct help_type help_table[] =
 		"         VARIABLE UPDATED <VAR> %0 name %1 new value\n"
 		"         VT100 SCROLL REGION    %0 top row %1 bot row %2 rows %3 cols %4 wrap\n"
 		"         WEEK <DAY>             %2 day of the week\n"
-		"         WINDOW FOCUS IN        %0 name\n"
-		"         WINDOW FOCUS OUT       %0 name\n"
 		"         WRITE ERROR            %0 filename %1 error message\n"
 		"         YEAR                   %0 year\n"
 		"\n"
@@ -1083,7 +1127,7 @@ struct help_type help_table[] =
 
 		"<178>This command is obsolete, please use foreach instead.\n",
 
-		"cr"
+		"foreach"
 	},
 	{
 		"FOREACH",
@@ -1129,13 +1173,16 @@ struct help_type help_table[] =
 		"         #format {test} {%t}   {format}  display time with strftime format\n"
 		"                                         optional {{format}{time}} syntax\n"
 		"         #format {test} {%u}   {string}  uppercase text\n"
-		"         #format {list} {%w}   {string}  store wordwrapped text in {list}\n"
+		"         #format {list} {%w}   {string}  store word wrapped text in {list}\n"
 		"                                         optional {{string}{width}} syntax\n"
 		"         #format {test} {%x}      {hex}  print corresponding charset character\n"
 		"         #format {test} {%A}     {char}  store corresponding character value\n"
+		"         #format {test} {%C}   {number}  store number in chronological notation\n"
 		"         #format {test} {%D}      {hex}  convert hex to decimal in {test}\n"
 		"         #format {hash} {%H}   {string}  store a 64 bit string hash in {hash}\n"
 		"         #format {test} {%L}   {string}  store the string length in {test}\n"
+		"         #format {test} {%M}   {number}  convert number to metric in {test}\n"
+		"         #format {test} {%S}   {string}  store the number of spelling errors\n"
 		"         #format {time} {%T}         {}  store the epoch time in {time}\n"
 		"         #format {time} {%U}         {}  store the micro epoch time in {time}\n"
 		"         #format {test} {%X}      {dec}  convert dec to hexadecimal in {test}\n\n"
@@ -1143,7 +1190,7 @@ struct help_type help_table[] =
 		"\n"
 		"<178>Comment<278>: See #help TIME for help on the %t argument.\n",
 		
-		"echo function local math replace script time variable"
+		"cat echo function local math replace script time variable"
 	},
 
 	{
@@ -1192,11 +1239,13 @@ struct help_type help_table[] =
 		"GREETING",
 
 		"<268>      #<268>##################################################################<268>#\n"
-		"<268>      #<278>                     T I N T I N + +   "CLIENT_VERSION"                   <268>#\n"
 		"<268>      #<278>                                                                  <268>#\n"
-		"<268>      #<278>                 <268>T<278>he K<268>i<278>cki<268>n<278> <268>T<278>ickin D<268>i<278>kuMUD Clie<268>n<278>t <268>                #\n"
+		"<268>      #<278>                    T I N T I N + +   "CLIENT_VERSION"                    <268>#\n"
 		"<268>      #<278>                                                                  <268>#\n"
+//		"<268>      #<278>                 <268>T<278>he K<268>i<278>cki<268>n<278> <268>T<278>ickin D<268>i<278>kuMUD Clie<268>n<278>t <268>                #\n"
+//		"<268>      #<278>                                                                  <268>#\n"
 		"<268>      #<278>      Code by Peter Unold, Bill Reis, and Igor van den Hoven      <268>#\n"
+		"<268>      #<278>                                                                  <268>#\n"
 		"<268>      #<268>##################################################################<268>#<288>\n",
 		
 		""
@@ -1234,7 +1283,7 @@ struct help_type help_table[] =
 	{
 		"HIGHLIGHT",
 
-		"<178>Command<278>: #highlight <178>{<278>string<178>} {<278>color names<178>}<278>\n"
+		"<178>Command<278>: #highlight <178>{<278>string<178>} {<278>color names<178>} {<278>priority<178>}<278>\n"
 		"\n"
 		"         The highlight command is used to allow you to highlight strings of text.\n"
 		"\n"
@@ -1321,7 +1370,7 @@ struct help_type help_table[] =
 		"         when you press enter on an empty line.\n"
 		"\n"
 		"         You can press ctrl-r to enter an interactive regex enabled history\n"
-		"         search mode, or by issueing #cursor {history search}.\n"
+		"         search mode, or by issuing #cursor {history search}.\n"
 		"\n"
 		"         TinTin++ tries to bind the arrow up and down keys to scroll through\n"
 		"         the history list by default. You can bind these with a macro yourself\n"
@@ -1396,7 +1445,7 @@ struct help_type help_table[] =
 		"\n"
 		"         All TinTin++ commands starts with a '#'.\n"
 		"\n"
-		"<178>Example<278>: #help -- #help is a mud client command, and isn't send to the mud\n"
+		"<178>Example<278>: #help -- #help is a client command, and isn't send to the\n"
 		"         server.\n"
 		"\n"
 		"         All TinTin++ commands can be abbreviated when typed.\n"
@@ -1411,12 +1460,12 @@ struct help_type help_table[] =
 		"         There are 3 ways ';'s can be overruled.\n"
 		"\n"
 		"         \\say Hello ;) -- Lines starting with a '\\' aren't parsed by TinTin++.\n"
-		"         say Hello \\;) -- The escape character can esape 1 letter.\n"
+		"         say Hello \\;) -- The escape character can escape 1 letter.\n"
 		"         #config verbatim on -- Everything is send as is except '#' commands.\n"
 		"<128>\n"
-		"         Connecting to a mud\n"
+		"         Connecting to a server\n"
 		"<178>\n"
-		"Command<278>: #session <178>{<278>session name<178>} {<278>mud address<178>} {<278>port<178>}<278>\n"
+		"Command<278>: #session <178>{<278>session name<178>} {<278>server address<178>} {<278>port<178>}<278>\n"
 		"\n"
 		"         Example: #session someone tintin.sourceforge.net 4321\n"
 		"\n"
@@ -1425,8 +1474,7 @@ struct help_type help_table[] =
 		"\n"
 		"         You can get a list of all sessions by typing: #session. The current\n"
 		"         active session is marked with (active). Snooped sessions with\n"
-		"         (snooped). MCCP sessions (mud client compression protocol) with\n"
-		"         (mccp 2).\n"
+		"         (snooped). MCCP sessions (compression) with (mccp 2) and (mccp 3).\n"
 		"\n"
 		"<128>\n"
 		"         Split\n"
@@ -1509,7 +1557,7 @@ struct help_type help_table[] =
 		"<178>Command<278>: #highlight <178>{<278>text<178>} {<278>color<178>}<278>\n"
 		"\n"
 		"         This command works a bit like #action. The purpose of this command is\n"
-		"         to substitute text from the mud with color you provide. This command\n"
+		"         to substitute text from the server with color you provide. This command\n"
 		"         is a simplified version of the #substitute command.\n"
 		"\n"
 		"<178>Example<278>: #high <178>{<278>Snowy<178>} {<278>light yellow<178>}<278>\n"
@@ -1538,10 +1586,9 @@ struct help_type help_table[] =
 		"\n"
 		"<178>Command<278>: #ticker <178>{<278>name<178>} {<278>commands<178>} {<278>seconds<178>}<278>\n"
 		"\n"
-		"         Every 60 seconds on a standard dikumud a so called tick occures. You\n"
-		"         regenerate hp/mana/move faster if you're sleeping/resting during a\n"
-		"         tick. So it's pretty nice to know when the next tick occures. TinTin++\n"
-		"         helps you with that.\n"
+		"         The name can be whatever you want it to be, and is only required for\n"
+		"         the unticker command. The commands will be executed every x amount of\n"
+		"         seconds, which is specified in the interval part.\n"
 		"\n"
 		"<178>Example<278>: #tick <178>{<278>tick<178>} {<278>#delay 50 #show 10 SECONDS TO TICK!;#show TICK!!!<178>} {<278>60<178>}<278>\n"
 		"\n"
@@ -1555,7 +1602,7 @@ struct help_type help_table[] =
 		"<278>\n"
 		"         When you order TinTin++ to read a command file, it parses all the text\n"
 		"         in the file. You can use command files to keep aliases/actions in,\n"
-		"         login to a mud (name, password etc..) and basically all kinds of\n"
+		"         login to a server (name, password etc..) and basically all kinds of\n"
 		"         commands.\n"
 		"\n"
 		"         You can make the command files with either a text editor (suggested),\n"
@@ -1647,6 +1694,9 @@ struct help_type help_table[] =
 		"         This data is written to the info variable.\n"
 		"\n"
 		"         #info cpu will show information about tintin's cpu usage.\n"
+		"         #info mccp will show information about data compression.\n"
+		"         #info stack will show the low level debugging stack.\n"
+		"         #info session will show some session information.\n"
 		"         #info system will show some system information.\n",
 
 		"class debug ignore kill message"
@@ -1657,19 +1707,19 @@ struct help_type help_table[] =
 		"<278>When TinTin++ starts up it sends \\e= to the terminal to enable the terminal's\n"
 		"application keypad mode, which can be disabled using #showme {\\e>}\n"
 		"\n"
-		"<178>     Configuration A            Configuration B            Configuration C<278>\n"
-		"+-----+-----+-----+-----+  +-----+-----+-----+-----+  +-----+-----+-----+-----+\n"
-		"|Num  |/    |*    |-    |  |Num  |/    |*    |-    |  |Num  |nkp/ |nkp* |nkp- |\n"
-		"+-----+-----+-----+-----+  +-----+-----+-----+-----+  +-----+-----+-----+-----+\n"
-		"|7    |8    |9    |     |  |Home |Up   |PgUp |     |  |nkp7 |nkp8 |nkp9 |     |\n"
-		"+-----+-----+-----+     |  +-----+-----+-----+     |  +-----+-----+-----+     |\n"
-		"|4    |5    |6    |+    |  |Left |Centr|Right|+    |  |nkp4 |nkp5 |nkp6 |nkp+ |\n"
-		"+-----+-----+-----+-----+  +-----+-----+-----+-----+  +-----+-----+-----+-----+\n"
-		"|1    |2    |3    |     |  |End  |Down |PgDn |     |  |nkp1 |nkp2 |nkp3 |     |\n"
-		"+-----+-----+-----+     |  +-----+-----+-----+     |  +-----+-----+-----+     |\n"
-		"|0          |.    |Enter|  |Ins        |Del  |Enter|  |nkp0       |nkp. |nkpEn|\n"
-		"+-----------+-----+-----+  +-----------+-----+-----+  +-----------+-----+-----+\n"
-		"\n"
+		"<178>      Configuration A           Configuration B           Configuration C<268>\n"
+		" ╭─────┬─────┬─────┬─────╮ ╭─────┬─────┬─────┬─────╮ ╭─────┬─────┬─────┬─────╮\n"
+		" │<178>num<268>  │<178>/<268>    │<178>*<268>    │<178>-<268>    │ │<178>num<268>  │<178>/<268>    │<178>*<268>    │<178>-<268>    │ │<178>Num<268>  │<178>nkp/<268> │<178>nkp*<268> │<178>nkp-<268> │\n"
+		" ├─────┼─────┼─────┼─────┤ ├─────┼─────┼─────┼─────┤ ├─────┼─────┼─────┼─────┤\n"
+		" │<178>7<268>    │<178>8<268>    │<178>9<268>    │<178>+<268>    │ │<178>Home<268> │<178>Up<268>   │<178>PgUp<268> │<178>+<268>    │ │<178>nkp7<268> │<178>nkp8<268> │<178>nkp9<268> │<178>nkp+<268> │\n"
+		" ├─────┼─────┼─────┤     │ ├─────┼─────┼─────┤     │ ├─────┼─────┼─────┤     │\n"
+		" │<178>4<268>    │<178>5<268>    │<178>6<268>    │     │ │<178>Left<268> │<178>Cntr<268> │<178>Right<268>│     │ │<178>nkp4<268> │<178>nkp5<268> │<178>nkp6<268> │     │\n"
+		" ├─────┼─────┼─────┼─────┤ ├─────┼─────┼─────┼─────┤ ├─────┼─────┼─────┼─────┤\n"
+		" │<178>1<268>    │<178>2<268>    │<178>3<268>    │<178>Enter<268>│ │<178>End<268>  │<178>Down<268> │<178>PgDn<268> │<178>Enter<268>│ │<178>nkp1<268> │<178>nkp2<268> │<178>nkp3<268> │<178>nkpEn<268>│\n"
+		" ├─────┴─────┼─────┤     │ ├─────┴─────┼─────┤     │ ├─────┴─────┼─────┤     │\n"
+		" │<178>0<268>          │<178>.<268>    │     │ │<178>Ins<268>        │<178>Del<268><268>  │     │ │<178>nkp0<268>       │<178>nkp.<268> │     │\n"
+		" ╰───────────┴─────┴─────╯ ╰───────────┴─────┴─────╯ ╰───────────┴─────┴─────╯\n"
+		"<278>\n"
 		"With keypad mode disabled numlock on will give you configuration A, and numlock\n"
 		"off will give you configuration B. With keypad mode enabled you'll get\n"
 		"configuration C.\n"
@@ -1719,6 +1769,9 @@ struct help_type help_table[] =
 		"         <178>#line background <argument>\n"
 		"         <278>  Prevent new session activation.\n"
 		"\n"
+		"         <178>#line capture <variable> <argument.\n"
+		"         <278>  Argument is executed and output stored in <variable>.\n"
+		"\n"
 		"         <178>#line debug <argument>\n"
 		"         <278>  Argument is executed in debug mode.\n"
 		"\n"
@@ -1773,12 +1826,15 @@ struct help_type help_table[] =
 		"\n"
 		"         #list {var} {add} {item}               Add {item} to the list\n"
 		"         #list {var} {clear}                    Empty the given list\n"
+		"         #list {var} {collapse}                 Turn list into a variable\n"
 		"         #list {var} {create} {item}            Create a list using {items}\n"
 		"         #list {var} {delete} {index} {number}  Delete the item at {index},\n"
 		"                                                the {number} is optional.\n"
+		"         #list {var} {explode}                  Turn list into a character list\n"
 		"         #list {var} {insert} {index} {string}  Insert {string} at given index\n"
 		"         #list {var} {find} {string} {variable} Return the found index\n"
 		"         #list {var} {get} {index} {variable}   Copy an item to {variable}\n"
+		"         #list {var} {shuffle}                  Shuffle the list\n"
 		"         #list {var} {set} {index} {string}     Change the item at {index}\n"
 		"         #list {var} {simplify} {variable}      Copy simple list to {variable}\n"
 		"         #list {var} {size} {variable}          Copy list size to {variable}\n"
@@ -1826,10 +1882,10 @@ struct help_type help_table[] =
 	{
 		"LOG",
 
-		"<178>Command<278>: #log <178>{<278>append<178>|<278>overwrite<178>} {<278>filename<178>}<278>\n"
+		"<178>Command<278>: #log <178>{<278>append<178>|<278>overwrite<178>|<278>off<178>} {<278>[filename]<178>}<278>\n"
 		"\n"
-		"         Logs session to a file, you can set the data type to either plain,\n"
-		"         raw, or html with the config command.\n",
+		"         Logs session output to a file, you can set the data type to either\n"
+		"         plain, raw, or html with the config command.\n",
 		
 		"read scan textin write"
 	},
@@ -1907,8 +1963,8 @@ struct help_type help_table[] =
 		"         <178>#map create <size>\n"
 		"         <278>  Creates a new map and room 1. The default size is 50000 rooms.\n"
 		"\n"
-		"         <178>#map destroy\n"
-		"         <278>  Deletes the map.\n"
+		"         <178>#map destroy {area|world} <name>\n"
+		"         <278>  Deletes the map or given area.\n"
 		"\n"
 		"         <178>#map delete <exit|vnum>\n"
 		"         <278>  Deletes the room for the given exit or vnum.\n"
@@ -1923,12 +1979,16 @@ struct help_type help_table[] =
 		"         <278>  to the given room vnum. If the room vnum doesn't exist a new\n"
 		"         <278>  room is created.\n"
 		"\n"
+		"         <178>#map entrance <exit> [option] [arg]\n"
+		"         <278>  Set the entrance data for the given exit. You must specify a\n"
+		"         <278>  valid two-way exit for this to work.\n"
+		"\n"
 		"         <178>#map exit <exit> <option> <arg>\n"
 		"         <278>  Set the exit data. Useful with a closed door where you can\n"
 		"         <278>  set the exit command: '#map exit e command {open east;e}'.\n"
 		"         <278>  Use #map exit <exit> for a list of available options.\n"
 		"\n"
-		"         <178>#map exitflag <exit> <HIDE|AVOID> [on|off]\n"
+		"         <178>#map exitflag <exit> <AVOID|BLOCK|HIDE|INVIS> [on|off]\n"
 		"         <278>  Set exit flags. See #map roomflag for more info.\n"
 		"\n"
 		"         <178>#map explore <exit>\n"
@@ -2006,6 +2066,12 @@ struct help_type help_table[] =
 		"         <278>  Jump to the given coordinate, which is relative\n"
 		"         <278>  to your current room.\n"
 		"\n"
+		"         <178>#map landmark <name> <vnum> [description] [size]\n"
+		"         <278>  Creates an alias to target the provided room vnum. The\n"
+		"         <278>  description is optional and should be brief. The size\n"
+		"         <278>  determines from how many rooms away the landmark can be\n"
+		"         <278>  seen.\n"
+		"\n"
 		"         <178>#map leave\n"
 		"         <278>  Makes you leave the map. Useful when entering a maze. You\n"
 		"         <278>  can return to your last known room using #map return.\n"
@@ -2080,12 +2146,17 @@ struct help_type help_table[] =
 		"         <278>\n"
 		"         <178>#map roomflag avoid\n"
 		"         <278>  When set, '#map find' will avoid a route leading\n"
-		"         <278>  through that room. Useful when you want to avoid death traps.\n"
+		"         <278>  through that room. Useful for locked doors, etc.\n"
+		"         <178>#map roomflag block\n"
+		"         <278>  When set the automapper will prevent movement into or through\n"
+		"         <278>  the room. Useful for death traps.\n"
 		"         <178>#map roomflag hide\n"
 		"         <278>  When set, '#map' will not display the map beyond\n"
 		"         <278>  this room. When mapping overlapping areas or areas that aren't\n"
 		"         <278>  build consistently you need this flag as well to stop\n"
 		"         <278>  auto-linking, unless you use void rooms.\n"
+		"         <178>#map roomflag invis\n"
+		"         <278>  When set the room will be colored with the INVIS color.\n"
 		"         <178>#map roomflag leave\n"
 		"         <278>  When entering a room with this flag, you will\n"
 		"         <278>  automatically leave the map. Useful when set at the entrance\n"
@@ -2112,6 +2183,22 @@ struct help_type help_table[] =
 		"         <278>  Set a map value for your current room, or given room if a room\n"
 		"         <278>  vnum is provided.\n"
 		"\n"
+		"         <178>#map sync <filename>\n"
+		"         <278>  Similar to #map read except the current map won't be unloaded\n"
+		"         <278>  or overwritten.\n"
+		"\n"
+		"         <178>#map terrain <name> <symbol> [flag]\n"
+		"         <278>  Set the terrain symbol and flag.\n"
+		"\n"
+		"         <178>#map terrain <name> <symbol> [DENSE|SPARSE|SCANT]\n"
+		"         <278>  Determine symbol density, omit for the default.\n"
+		"\n"
+		"         <178>#map terrain <name> <symbol> [NARROW|WIDE|VAST]\n"
+		"         <278>  Determine symbol spread range, omit for the default.\n"
+		"\n"
+		"         <178>#map terrain <name> <symbol> [FADEIN|FADEOUT]\n"
+		"         <278>  Determine symbol spread density, omit for the default.\n"
+		"\n"
 		"         <178>#map travel <direction> <delay>\n"
 		"         <278>  Follows the direction until a dead end or an intersection is\n"
 		"         <278>  found. Use braces around the direction if you use the delay,\n"
@@ -2126,11 +2213,17 @@ struct help_type help_table[] =
 		"         <178>#map uninsert <direction>\n"
 		"         <278>  Exact opposite of the insert command.\n"
 		"\n"
+		"         <178>#map unlandmark <name>\n"
+		"         <278>  Removes a landmark.\n"
+		"\n"
 		"         <178>#map unlink <direction> [both]\n"
 		"         <278>  Will remove the exit, this isn't two way so you can have the\n"
 		"         <278>  properly display no exit rooms and mazes.\n"
 		"         <278>  If you use the both argument the exit is removed two-ways.\n"
 		"\n"
+		"         <178>#map unterrain <name>\n"
+		"         <278>  Removes a terrain.\n"
+		"\n"
 		"         <178>#map update\n"
 		"         <278>  Sets the vtmap to update within the next 0.1 seconds.\n"
 		"\n"
@@ -2208,8 +2301,9 @@ struct help_type help_table[] =
 		"           {#if {{%0} == {Bubba} && $afk} {reply I'm away, my friend.}}\n"
 		"         When you are away from keyboard, it will only reply to your friend.\n",
 
-		"format function local mathematics replace script variable"
+		"cat format function local mathematics replace script variable"
 	},
+
 	{
 		"MATHEMATICS",
 
@@ -2242,7 +2336,7 @@ struct help_type help_table[] =
 		"         ^^             10            logical xor\n"
 		"         ||             11            logical or\n"
 		"\n"
-		"Operator priority can be ignored by using paranthesis, for example (1 + 1) * 2\n"
+		"Operator priority can be ignored by using parentheses, for example (1 + 1) * 2\n"
 		"equals 4, while 1 + 1 * 2 equals 3.\n"
 		"\n"
 		"<178>String operations<278>\n"
@@ -2275,6 +2369,33 @@ struct help_type help_table[] =
 
 		"class debug ignore info kill"
 	},
+
+	{
+		"METRIC SYSTEM",
+
+		"  Name  Symbol                              Factor\n"
+		"--------------------------------------------------\n"
+//		" Yotta       Y   1 000 000 000 000 000 000 000 000\n"
+//		" Zetta       Z       1 000 000 000 000 000 000 000\n"
+//		"   Exa       E           1 000 000 000 000 000 000\n"
+//		"  Peta       P               1 000 000 000 000 000\n"
+//		"  Tera       T                   1 000 000 000 000\n"
+//		"  Giga       G                       1 000 000 000\n"
+		"  Mega       M                           1 000 000\n"
+		"  Kilo       K                               1 000\n"
+		"\n"
+		" milli       m                               0.001\n"
+		" micro       u                           0.000 001\n",
+//		"  nano       n                       0.000 000 001\n"
+//		"  pico       p                   0.000 000 000 001\n"
+//		" femto       f               0.000 000 000 000 001\n"
+//		"  atto       a           0.000 000 000 000 000 001\n"
+//		" zepto       z       0.000 000 000 000 000 000 001\n"
+//		" yocto       y   0.000 000 000 000 000 000 000 001\n",
+		
+		"echo format math"
+	},
+	
 	{
 		"MSDP",
 
@@ -2454,7 +2575,7 @@ struct help_type help_table[] =
 		"         Perl Compatible Regular Expressions<278>\n"
 		"\n"
 		"         You can embed a PCRE (Perl Compatible Regular Expression) using curley\n"
-		"         braces { }, these braces are replaced with paranthesis ( ) unless you\n"
+		"         braces { }, these braces are replaced with parentheses ( ) unless you\n"
 		"         use %!{ }.\n"
 		"<178>\n"
 		"         Or<278>\n"
@@ -2498,17 +2619,17 @@ struct help_type help_table[] =
 		"         The example only triggers if someone provides a number between 1 and\n"
 		"         999.\n"
 		"\n"
-		"         <178>Paranthesis<278>\n"
+		"         <178>Parantheses<278>\n"
 		"\n"
 		"         TinTin Regular Expressions automatically add parenthesis, for example\n"
 		"         %* translates to (.*?) in PCRE unless the %* is found at the start or\n"
 		"         end of the line, in which cases it translates to (.*). Paranthesis in\n"
 		"         PCRE causes a change in execution priority similar to mathematical\n"
-		"         expressions, but paranthesis also causes the match to be stored to a\n"
+		"         expressions, but parentheses also causes the match to be stored to a\n"
 		"         variable.\n"
 		"\n"
-		"         When nesting multiple sets of paranthesis each nest is assigned its\n"
-		"         numercial variable in order of appearance.\n"
+		"         When nesting multiple sets of parentheses each nest is assigned its\n"
+		"         numerical variable in order of appearance.\n"
 		"\n"
 		"Example: #act {%* chats '{Mu(ha)+}'} {chat %2ha!}\n"
 		"\n"
@@ -2565,8 +2686,8 @@ struct help_type help_table[] =
 		"\n"
 		"Example: #action {~\\e[1;37m%1} {#var roomname %1}\n"
 		"\n"
-		"         If the room name is the only line on the mud in bright white this\n"
-		"         color trigger will save the roomname.\n"
+		"         If the room name is the only line on the server in bright white\n"
+		"         white color trigger will save the roomname.\n"
 		"\n"
 		"\n"
 		"         This covers the basics. PCRE has more options, most of which are\n"
@@ -2612,7 +2733,7 @@ struct help_type help_table[] =
 		"         <278>  Send data to socket\n"
 		"\n"
 		"         <178>#port {uninitialize}\n"
-		"         <278>  Unitialize the port session.\n"
+		"         <278>  Uninitialize the port session.\n"
 		"\n"
 		"         <178>#port {who}\n"
 		"         <278>  Show all connections\n"
@@ -2622,7 +2743,9 @@ struct help_type help_table[] =
 		"\n"
 		"         The port command is very similar to chat except that it creates a\n"
 		"         new session dedicated to receiving socket connections at the given\n"
-		"         port number without built-in support for a communication protocol.\n",
+		"         port number without built-in support for a communication protocol.\n"
+		"\n"
+		"         You can init with 0 as the port number to create a dummy session.\n",
 
 		"all chat run session sessionname snoop ssl zap"
 	},
@@ -2698,16 +2821,26 @@ struct help_type help_table[] =
 		"\n"
 		"         Of the following the (lazy) match is available at %1-%99 + 1\n"
 		"\n"
-		"      %w match zero to any number of word characters.\n"
-		"      %W match zero to any number of non word characters.\n"
+		"      %a match zero to any number of characters except newlines.\n"
+		"      %A match zero to any number of newlines.\n"
 		"      %d match zero to any number of digits.\n"
 		"      %D match zero to any number of non digits.\n"
+		"      %p match zero to any number of printable characters.\n"
+		"      %P match zero to any number of non printable characters.\n"
 		"      %s match zero to any number of spaces.\n"
 		"      %S match zero to any number of non spaces.\n"
+		"      %u match zero to any number of unicode characters.\n"
+		"      %U match zero to any number of non unicode characters.\n"
+		"      %w match zero to any number of word characters.\n"
+		"      %W match zero to any number of non word characters.\n"
 		"\n"
+		"      If you want to match 1 digit use %+1d, if you want to match between 3\n"
+		"      and 5 spaces use %+3..5s, if you want to match between 0 and 1 word\n"
+		"      characters use %+0..1w\n"
+		"\n"
+		"      %+ match one to any number of characters.\n"
 		"      %? match zero or one character.\n"
-		"      %. match one character. (<118>do not use<278>)\n"
-		"      %+ match one to any number of characters. (<118>do not use<278>)\n"
+		"      %. match one character.\n"
 		"      %* match zero to any number of characters.\n"
 		"\n"
 		"      %i matching becomes case insensitive.\n"
@@ -2740,10 +2873,10 @@ struct help_type help_table[] =
 
 		"<178>Command<278>: #replace <178>{<278>variable<178>} {<278>oldtext<178>} {<278>newtext<178>}<278>\n"
 		"\n"
-		"         Searches the variable text replacing each occurance of 'oldtext' with\n"
+		"         Searches the variable text replacing each occurrence of 'oldtext' with\n"
 		"         'newtext'.\n",
 
-		"format function local math script variable"
+		"cat format function local math script variable"
 	},
 	{
 		"RETURN",
@@ -2765,7 +2898,7 @@ struct help_type help_table[] =
 		"\n"
 		"         The run command works much like the system command except that it\n"
 		"         runs the command in a pseudo terminal. The run command also creates\n"
-		"         a session that treats the given shell command as a mud server. This\n"
+		"         a session that treats the given shell command as a server. This\n"
 		"         allows you to run ssh, as well as any other shell application, with\n"
 		"         full tintin scripting capabilities. If a file name is given the file\n"
 		"         is loaded prior to execution.\n"
@@ -2818,7 +2951,7 @@ struct help_type help_table[] =
 		"         <178>#scan {txt} <filename>\n"
 		"\n"
 		"         <278>  The scan txt <filename> command reads in a file and sends its content\n"
-		"           to the screen as if it was send by a mud. After using scan you can\n"
+		"           to the screen as if it was send by a server. After using scan you can\n"
 		"           use page-up and down to view the file.\n"
 		"\n"
 		"           This command is useful to convert ansi color files to html or viewing\n"
@@ -2900,10 +3033,10 @@ struct help_type help_table[] =
 
 		"<178>Command<278>: #config <178>{<278>SCREEN READER<178>} {<278>ON|OFF<178>}<278>\n"
 		"\n"
-		"         Screen reader mode is enabled by using #config screen on. The main\n"
-		"         purpose of the screen reader mode is to tell MUDs that a screen reader\n"
-		"         is being used by using the MTTS standard. The MTTS specification is\n"
-		"         available at:\n"
+		"         Screen reader mode is enabled by using #config screen on.  The main\n"
+		"         purpose of the screen reader mode is to report to servers that a\n"
+		"         screen reader is being used by utilizing the MTTS standard.  The MTTS\n"
+		"         specification is available at:\n"
 		"\n"
 		"         http://tintin.sourceforge.net/protocols/mtts\n"
 		"\n"
@@ -2940,8 +3073,8 @@ struct help_type help_table[] =
 
 		"<178>Command<278>: #send <178>{<278>text<178>}<278>\n"
 		"\n"
-		"         Sends the text directly to the MUD, useful if you want to start with an\n"
-		"         escape code.\n",
+		"         Sends the text directly to the server, useful if you want to start\n"
+		"         with an escape code.\n",
 
 		"textin"
 	},
@@ -2982,7 +3115,7 @@ struct help_type help_table[] =
 		"         The startup session is named 'gts' and can be used for relog scripts. Do\n"
 		"         keep in mind that tickers do not work in the startup session.\n"
 		"\n"
-		"<178>Example<278>: #event {SESSION DISCONNECTED} {#gts #delay 10 #ses %0 mymud.com 4321}\n",
+		"<178>Example<278>: #event {SESSION DISCONNECTED} {#gts #delay 10 #ses %0 tintin.net 4321}\n",
 
 		"all port run sessionname snoop ssl zap"
 	},
@@ -3002,7 +3135,7 @@ struct help_type help_table[] =
 		"session when used without an argument. If an argument is given it will be\n"
 		"executed by that session as a command, the session will not be activated.\n"
 		"\n"
-		"<178>Example<278>: #ses one mymud.com 23;#ses two mymud.com 23;#one;#two grin\n"
+		"<178>Example<278>: #ses one tintin.net 23;#ses two tintin.net 23;#one;#two grin\n"
 		"\n"
 		"This will create two sessions, the session that was created last (two in this\n"
 		"case) will be automatically activated upon creation. Using #one, session one is\n"
@@ -3017,9 +3150,9 @@ struct help_type help_table[] =
 
 		"<178>Command<278>: #showme <178>{<278>string<178>} {<278>row<178>} <178>{<278>col<178>}<278>\n"
 		"\n"
-		"         Display the string to the terminal, do not send to the mud.  Useful for\n"
-		"         status, warnings, etc.  The {row} and col number are optional and work\n"
-		"         the same way as the row number of the #prompt trigger.\n"
+		"         Display the string to the terminal, do not send to the server.  Useful\n"
+		"         for status, warnings, etc.  The {row} and col number are optional and\n"
+		"         work the same way as the row number of the #prompt trigger.\n"
 		"\n"
 		"         Actions can be triggered by the showme command. If you want to avoid\n"
 		"         this from happening use: #line ignore #showme {<string>}.\n"
@@ -3047,15 +3180,16 @@ struct help_type help_table[] =
 	{
 		"SPEEDWALK",
 
-		"         <278>Speedwalking allows you to type multiple directions not separated by\n"
-		"         semicolons, and now it lets you prefix a direction with a number, to\n"
-		"         signify how many times to go that direction. You can turn it on/off\n"
-		"         with #config.\n"
+		"         <278>Speedwalking allows you to enter multiple directions without using\n"
+		"         semicolons. Directions should be prefixed with a number and will be\n"
+		"         executed the given number of times.\n"
+		"\n"
+		"         You can enable speedwalking with #CONFIG {SPEEDWALK} {ON}.\n"
 		"\n"
 		"<178>Example<278>: Without speedwalk, you have to type:\n"
 		"         s;s;w;w;w;w;w;s;s;s;w;w;w;n;n;w\n"
 		"         With speedwalk, you only have to type:\n"
-		"         2s5w3s3w2nw\n",
+		"         2s5w3s3w2n1w\n",
 
 		"alias cursor history keypad macro tab"
 	},
@@ -3142,9 +3276,9 @@ struct help_type help_table[] =
 	{
 		"SUBSTITUTE",
 
-		"<178>Command<278>: #substitute <178>{<278>text<178>} {<278>new text<178>}<278>\n"
+		"<178>Command<278>: #substitute <178>{<278>text<178>} {<278>new text<178>} {<278>priority<178>}<278>\n"
 		"\n"
-		"         Allows you to replace original text from the mud with different text.\n"
+		"         Allows you to replace text from the server with the new text.\n"
 		"         This is helpful for complex coloring and making things more readable.\n"
 		"         The %1-%99 variables can be used to capture text and use it as part of\n"
 		"         the new output, and the ^ char is valid to only check the beginning of\n"
@@ -3235,7 +3369,7 @@ struct help_type help_table[] =
 		"<178>Command<278>: #textin <178>{<278>filename<178>} {<278>delay<178>}<278>\n"
 		"\n"
 		"         Textin allows the user to read in a file, and send its contents\n"
-		"         directly to the mud.  Useful for doing online creation, or message\n"
+		"         directly to the server.  Useful for doing online creation, or message\n"
 		"         writing.\n"
 		"\n"
 		"         The delay is in seconds and takes a floating point number which is\n"
@@ -3353,7 +3487,7 @@ struct help_type help_table[] =
 		"\n"
 		"<178>Comment<278>: You can remove a variable with the #unvariable command.\n",
 
-		"format function local math replace script"
+		"cat format function local math replace script"
 	},
 	{
 		"WHILE",

+ 2 - 3
src/history.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2006                       *
 ******************************************************************************/

+ 48 - 7
src/input.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2006                       *
 ******************************************************************************/
@@ -33,11 +32,30 @@ void process_input(void)
 
 	push_call("process_input(void)");
 
+	gtd->time_input = gtd->time;
+
 	if (gtd->detach_port)
 	{
-		if (gtd->detach_sock)
+		if (gtd->detach_sock > 0)
 		{
 			len = read(gtd->detach_sock, input, 1);
+
+			if (len <= 0)
+			{
+				if (len == -1)
+				{
+					syserr_printf(gtd->ses, "process_input: read", gtd->detach_sock);
+				}
+				else
+				{
+					show_message(gtd->ses, LIST_COMMAND, "#DAEMON UPDATE: DETACHING FROM PID {%d} DUE TO READ FAILURE.", gtd->detach_pid);
+				}
+
+				gtd->detach_sock = close(gtd->detach_sock);
+
+				pop_call();
+				return;
+			}
 		}
 		else
 		{
@@ -63,7 +81,7 @@ void process_input(void)
 
 		gtd->attach_sock = close(gtd->attach_sock);
 
-		show_message(gtd->ses, LIST_COMMAND, "#ATTACH: WRITE ERROR: UNATTACHING.");
+		show_message(gtd->ses, LIST_COMMAND, "#DAEMON ATTACH: WRITE ERROR: UNATTACHING.");
 	}
 
 	if (HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_SGA) && !HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_ECHO))
@@ -87,6 +105,7 @@ void process_input(void)
 	{
 		chat_paste(gtd->input_buf, NULL);
 
+		pop_call();
 		return;
 	}
 
@@ -355,10 +374,12 @@ int check_key(char *input, int len)
 	char buf[BUFFER_SIZE];
 	struct listroot *root;
 	struct listnode *node;
-	int cnt, val[3];
+	int cnt, val[5];
 
 	push_call("check_key(%p,%d)",input,len);
 
+//	tintin_printf2(gtd->ses, "check_key(%d,%s)",*gtd->macro_buf, gtd->macro_buf);
+
 	if (!HAS_BIT(gtd->ses->flags, SES_FLAG_CONVERTMETA))
 	{
 		root  = gtd->ses->list[LIST_MACRO];
@@ -463,10 +484,19 @@ int check_key(char *input, int len)
 				}
 				else if (gtd->macro_buf[2] >= '0' && gtd->macro_buf[2] <= '9')
 				{
-					val[0] = val[1] = val[2] = cnt = input[0] = 0;
+					cnt = input[0] = 0;
+					memset(val, 0, sizeof(val));
 
+//					tintin_printf2(gtd->ses, "debug: %d %d %d %d %d", val[0], val[1], val[2], val[3], val[4], val[5]);
+		
 					for (len = 2 ; gtd->macro_buf[len] ; len++)
 					{
+						if (cnt > 5)
+						{
+							pop_call();
+							return FALSE;
+						}
+
 						if (isdigit(gtd->macro_buf[len]))
 						{
 							cat_sprintf(input, "%c", gtd->macro_buf[len]);
@@ -499,6 +529,17 @@ int check_key(char *input, int len)
 									pop_call();
 									return TRUE;
 
+								case '&':
+									val[cnt++] = get_number(gtd->ses, input);
+									input[0] = 0;
+									break;
+
+								case 'w':
+									rqlp_handler(val[0], val[1], val[2], val[3]);
+									gtd->macro_buf[0] = 0;
+									pop_call();
+									return TRUE;
+
 								default:
 									pop_call();
 									return FALSE;

+ 64 - 5
src/line.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2008                       *
 ******************************************************************************/
@@ -92,6 +91,62 @@ DO_LINE(line_background)
 	return ses;
 }
 
+DO_LINE(line_benchmark)
+{
+	long long start, end;
+	char arg1[BUFFER_SIZE];
+
+	arg = get_arg_in_braces(ses, arg, arg1, GET_ALL);
+
+	if (*arg1 == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {BENCHMARK} {command}.");
+
+		return ses;
+	}
+
+	start = utime();
+
+	ses = script_driver(ses, LIST_COMMAND, arg1);
+
+	end = utime();
+
+	tintin_printf2(ses, "#LINE BENCHMARK: %lld USEC.", end - start);
+
+	return ses;
+}
+
+DO_LINE(line_capture)
+{
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1 && *arg2)
+	{
+		if (ses->line_capturefile)
+		{
+			free(ses->line_capturefile);
+		}
+		ses->line_capturefile  = strdup(arg1);
+		ses->line_captureindex = 1;
+
+		ses = script_driver(ses, LIST_COMMAND, arg2);
+
+		if (ses->line_capturefile)
+		{
+			free(ses->line_capturefile);
+			ses->line_capturefile = NULL;
+		}
+	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE CAPTURE {VARIABLE} {command}.");
+	}
+	return ses;
+}
+
 DO_LINE(line_debug)
 {
 	char arg1[BUFFER_SIZE];
@@ -242,19 +297,23 @@ DO_LINE(line_logmode)
 			break;
 	}
 
-	DEL_BIT(ses->logmode, LOG_FLAG_HTML|LOG_FLAG_PLAIN|LOG_FLAG_RAW);
-
 	if (is_abbrev(arg1, "HTML"))
 	{
 		SET_BIT(ses->logmode, LOG_FLAG_HTML);
+		DEL_BIT(ses->logmode, LOG_FLAG_PLAIN);
+		DEL_BIT(ses->logmode, LOG_FLAG_RAW);
 	}
 	else if (is_abbrev(arg1, "PLAIN"))
 	{
 		SET_BIT(ses->logmode, LOG_FLAG_PLAIN);
+		DEL_BIT(ses->logmode, LOG_FLAG_HTML);
+		DEL_BIT(ses->logmode, LOG_FLAG_RAW);
 	}
 	else if (is_abbrev(arg1, "RAW"))
 	{
 		SET_BIT(ses->logmode, LOG_FLAG_RAW);
+		DEL_BIT(ses->logmode, LOG_FLAG_HTML);
+		DEL_BIT(ses->logmode, LOG_FLAG_PLAIN);
 	}
 	else
 	{

+ 134 - 29
src/list.c

@@ -31,12 +31,9 @@
 DO_COMMAND(do_list)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
-	struct listroot *root;
 	struct listnode *node;
 	int index, cnt;
 
-	root = ses->list[LIST_VARIABLE];
-
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_NST, SUB_VAR|SUB_FUN);
 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
 
@@ -73,9 +70,9 @@ DO_COMMAND(do_list)
 		}
 		else
 		{
-			if ((node = search_nest_node(root, arg1)) == NULL)
+			if ((node = search_nest_node_ses(ses, arg1)) == NULL)
 			{
-				node = set_nest_node(root, arg1, "");
+				node = set_nest_node_ses(ses, arg1, "");
 			}
 			array_table[cnt].fun(ses, node, arg, arg1);
 		}
@@ -107,13 +104,16 @@ DO_ARRAY(array_add)
 
 			set_nest_node(list->root, ntos(index++), "%s", arg2);
 
-//			insert_node_list(list->root, ntos(index++), arg2, "");
-
 			if (*str == COMMAND_SEPARATOR)
 			{
 				str++;
 			}
 		}
+
+		if (*arg == COMMAND_SEPARATOR)
+		{
+			arg++;
+		}
 	}
 	return ses;
 }
@@ -127,8 +127,27 @@ DO_ARRAY(array_clear)
 		list->root = NULL;
 	}
 
-	set_nest_node(ses->list[LIST_VARIABLE], var, "");
+	set_nest_node_ses(ses, var, "");
+
+	return ses;
+}
+
+DO_ARRAY(array_collapse)
+{
+	int index;
+
+	if (list->root)
+	{
+		str_cpy(&list->arg2, "");
+
+		for (index = 0 ; index < list->root->used ; index++)
+		{
+			str_cat(&list->arg2, list->root->list[index]->arg2);
+		}
+		free_list(list->root);
 
+		list->root = NULL;
+	}
 	return ses;
 }
 
@@ -200,7 +219,7 @@ DO_ARRAY(array_delete)
 
 			for (cnt = index + 1 ; cnt < list->root->used ; cnt++)
 			{
-				list->root->list[cnt]->arg1 = restringf(list->root->list[cnt]->arg1, "%d", cnt);
+				str_cpy_printf(&list->root->list[cnt]->arg1, "%d", cnt);
 			}
 
 			delete_index_list(list->root, index);
@@ -213,6 +232,61 @@ DO_ARRAY(array_delete)
 	return ses;
 }
 
+DO_ARRAY(array_explode)
+{
+	char buf[BUFFER_SIZE], tmp[BUFFER_SIZE], *pti;
+	int index = 1;
+
+	if (list->root)
+	{
+		array_collapse(ses, list, "", "");
+	}
+
+	list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
+
+	pti = list->arg2;
+
+	while (*pti)
+	{
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
+		{
+			pti += sprintf(tmp, "%.*s", get_euc_size(ses, pti), pti);
+		}
+		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
+		{
+			pti += sprintf(tmp, "%.*s", get_utf8_size(pti), pti);
+		}
+		else
+		{
+			pti += sprintf(tmp, "%c", *pti);
+		}
+
+		set_nest_node(list->root, ntos(index++), "%s", tmp);
+	}
+	sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN);
+
+	pti = buf;
+
+	while (*pti)
+	{
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
+		{
+			pti += sprintf(tmp, "%.*s", get_euc_size(ses, pti), pti);
+		}
+		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
+		{
+			pti += sprintf(tmp, "%.*s", get_utf8_size(pti), pti);
+		}
+		else
+		{
+			pti += sprintf(tmp, "%c", *pti);
+		}
+
+		set_nest_node(list->root, ntos(index++), "%s", tmp);
+	}
+	return ses;
+}
+
 DO_ARRAY(array_find)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
@@ -239,17 +313,17 @@ DO_ARRAY(array_find)
 		}
 		if (index < list->root->used)
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", index + 1);
+			set_nest_node_ses(ses, arg2, "%d", index + 1);
 		}
 		else
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "0");
+			set_nest_node_ses(ses, arg2, "0");
 		}
 		return ses;
 	}
 	else
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "0");
+		set_nest_node_ses(ses, arg2, "0");
 	}
 
 	return ses;
@@ -276,17 +350,17 @@ DO_ARRAY(array_get)
 
 		if (atoi(arg1) == 0 || index == -1)
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "0");
+			set_nest_node_ses(ses, arg2, "0");
 		}
 		else
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", list->root->list[index]->arg2);
+			set_nest_node_ses(ses, arg2, "%s", list->root->list[index]->arg2);
 		}
 		return ses;
 	}
 	else
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "0");
+		set_nest_node_ses(ses, arg2, "0");
 	}
 
 	return ses;
@@ -321,7 +395,7 @@ DO_ARRAY(array_insert)
 
 	for (cnt = index ; cnt < list->root->used ; cnt++)
 	{
-		list->root->list[cnt]->arg1 = restringf(list->root->list[cnt]->arg1, "%d", cnt + 2);
+		str_cpy_printf(&list->root->list[cnt]->arg1, "%d", cnt + 2);
 	}
 
 	set_nest_node(list->root, ntos(index + 1), "%s", arg2);
@@ -333,38 +407,47 @@ DO_ARRAY(array_insert)
 
 DO_ARRAY(array_simplify)
 {
-	char arg1[BUFFER_SIZE], tmp[BUFFER_SIZE];
+	char arg1[BUFFER_SIZE], *str;
 	int index;
 
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
-
+/*
 	if (*arg1 == 0)
 	{
 		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {variable} SIMPLIFY {variable}");
 		
 		return ses;
 	}
-
+*/
 	if (list->root)
 	{
 		for (index = 0 ; index < list->root->used ; index++)
 		{
 			if (index == 0)
 			{
-				strcpy(tmp, list->root->list[index]->arg2);
+				str = str_dup(list->root->list[index]->arg2);
 			}
 			else
 			{
-				cat_sprintf(tmp, ";%s", list->root->list[index]->arg2);
+				str_cat_printf(&str, ";%s", list->root->list[index]->arg2);
 			}
 		}
-		set_nest_node(ses->list[LIST_VARIABLE], arg1, "%s", tmp);
+		if (*arg1 == 0)
+		{
+			set_nest_node_ses(ses, list->arg1, "%s", str);
+		}
+		else
+		{
+			set_nest_node_ses(ses, arg1, "%s", str);
+		}
+
+		str_free(str);
 
 		return ses;
 	}
 	else
 	{
-		show_error(ses, LIST_VARIABLE, "#LIST SIMPLIFY: {%s} is not a list.", arg1);
+		show_error(ses, LIST_VARIABLE, "#LIST SIMPLIFY: {%s} is not a list.", list->arg1);
 	}
 
 	return ses;
@@ -385,11 +468,11 @@ DO_ARRAY(array_size)
 
 	if (list->root)
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg1, "%d", list->root->used);
+		set_nest_node_ses(ses, arg1, "%d", list->root->used);
 	}
 	else
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg1, "0");
+		set_nest_node_ses(ses, arg1, "0");
 	}
 	return ses;
 }
@@ -412,7 +495,9 @@ DO_ARRAY(array_set)
 			return ses;
 		}
 
-		RESTRING(list->root->list[index]->arg2, arg2);
+		set_nest_node(list->root, ntos(index + 1), "%s", arg2);
+
+//		RESTRING(list->root->list[index]->arg2, arg2);
 
 		return ses;
 	}
@@ -422,6 +507,27 @@ DO_ARRAY(array_set)
 	return ses;
 }
 
+DO_ARRAY(array_shuffle)
+{
+	char *swap;
+	int cnt, rnd;
+
+	if (!list->root)
+	{
+		list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
+	}
+
+	for (cnt = 0 ; cnt < list->root->used ; cnt++)
+	{
+		rnd = generate_rand(ses) % list->root->used;
+
+		swap = list->root->list[cnt]->arg2;
+		list->root->list[cnt]->arg2 = list->root->list[rnd]->arg2;
+		list->root->list[rnd]->arg2 = swap;
+	}
+	return ses;
+}
+
 DO_ARRAY(array_sort)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE], *str;
@@ -492,9 +598,9 @@ DO_ARRAY(array_tokenize)
 
 	while (buf[i] != 0)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && buf[i] & 128 && buf[i+1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, &buf[i]))
 		{
-			i += sprintf(tmp, "%c%c", buf[i], buf[i+1]);
+			i += sprintf(tmp, "%.*s", get_euc_size(ses, &buf[i]), &buf[i]);
 		}
 		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&buf[i]))
 		{
@@ -509,4 +615,3 @@ DO_ARRAY(array_tokenize)
 	}
 	return ses;
 }
-

+ 13 - 7
src/log.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
@@ -78,7 +77,7 @@ void loginit(struct session *ses, FILE *file, int flags)
 	{
 		if (HAS_BIT(ses->logmode, LOG_FLAG_HTML))
 		{
-			write_html_header(ses, ses->logfile);
+			write_html_header(ses, file);
 		}
 	}
 	pop_call();
@@ -166,10 +165,15 @@ void write_html_header(struct session *ses, FILE *fp)
 		sprintf(header, "<html>\n"
 		"<head>\n"
 		"<meta http-equiv='content-type' content='text/html; charset=%s'>\n"
+		"<meta name='viewport' content='width=device-width, initial-scale=1.0'>\n"
 		"<meta name='description' content='Generated by TinTin++ "CLIENT_VERSION" - http://tintin.sourceforge.net'>\n"
 		"<style type='text/css'>\n"
-		"{\n\tfont-family: Courier;\n\tfont-size: 10pt;\n}\n"
-		"a:link {color: cyan;} a:visited {color: green;} a:hover {color: white;} a:active {color:purple;}\n"
+		"body {font-family:Consolas;font-size:12pt;}\n"
+		"a {text-decoration:none;}\n"
+		"a:link {color:#06b;}\n"
+		"a:visited {color:#6b0;}\n"
+		"a:hover {text-decoration:underline;}\n"
+		"a:active {color:#b06;}\n"
 		".d30{ color: #000; } .l30{ color: #555; } .b40{ background-color: #000; } .b50{ background-color: #555 }\n"
 		".d31{ color: #B00; } .l31{ color: #F55; } .b41{ background-color: #B00; } .b51{ background-color: #F55 }\n"
 		".d32{ color: #0B0; } .l32{ color: #5F5; } .b42{ background-color: #0B0; } .b52{ background-color: #5F5 }\n"
@@ -185,7 +189,9 @@ void write_html_header(struct session *ses, FILE *fp)
 		"</head>\n"
 		"<pre>\n"
 		"<span class='b49'><span class='d39'>\n",
-		HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) ? "utf-8" : HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) ? "big5" : "iso-8859-1");
+		HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) ? "utf-8" : 
+			HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) ? "big5" : 
+				HAS_BIT(ses->charset, CHARSET_FLAG_GBK1) ? "gb18030" : "iso-8859-1");
 
 	fputs(header, fp);
 }

+ 82 - 13
src/main.c

@@ -22,6 +22,7 @@
 *                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
 *                                                                             *
 *                         coded by Peter Unold 1992                           *
+*                    recoded by Igor van den Hoven 2004                       *
 ******************************************************************************/
 
 #include "tintin.h"
@@ -38,6 +39,26 @@ void pipe_handler(int signal)
 	syserr_printf(gtd->ses, "pipe_handler");
 }
 
+void xfsz_handler(int signal)
+{
+	syserr_printf(gtd->ses, "xfsz_handler");
+}
+
+void hub_handler(int signal)
+{
+	syserr_printf(gtd->ses, "hub_handler");
+}
+
+void ttin_handler(int signal)
+{
+	syserr_printf(gtd->ses, "ttin_handler");
+}
+
+void ttou_handler(int signal)
+{
+	syserr_printf(gtd->ses, "ttou_handler");
+}
+
 /*
 	when the screen size changes, take note of it
 */
@@ -69,7 +90,10 @@ void abort_handler(int signal)
 
 void child_handler(int signal)
 {
-	// syserr_fatal(signal, "child_handler");
+	return;
+	syserr_printf(gtd->ses, "child_handler");
+
+//	syserr_fatal(signal, "child_handler");
 }
 
 void interrupt_handler(int signal)
@@ -149,10 +173,11 @@ int main(int argc, char **argv)
 		syserr_fatal(-1, "signal SIGTERM");
 	}
 
-	if (signal(SIGCHLD, child_handler) == BADSIG)
+/*	if (signal(SIGCHLD, child_handler) == BADSIG)
 	{
 		syserr_fatal(-1, "signal SIGCHLD");
 	}
+*/
 /*
 	if (signal(SIGINT, interrupt_handler) == BADSIG)
 	{
@@ -164,17 +189,39 @@ int main(int argc, char **argv)
 	{
 		syserr_fatal(-1, "signal SIGTSTP");
 	}
-
+/*
 	if (signal(SIGPIPE, pipe_handler) == BADSIG)
 	{
 		syserr_fatal(-1, "signal SIGPIPE");
 	}
 
+	if (signal(SIGXFSZ, xfsz_handler) == BADSIG)
+	{
+		syserr_fatal(-1, "signal SIGXFSZ");
+	}
+
+	if (signal(SIGHUP, hub_handler) == BADSIG)
+	{
+		syserr_fatal(-1, "signal SIGHUP");
+	}
+
+	if (signal(SIGTTIN, hub_handler) == BADSIG)
+	{
+		syserr_fatal(-1, "signal SIGTTIN");
+	}
+
+	if (signal(SIGTTOU, hub_handler) == BADSIG)
+	{
+		syserr_fatal(-1, "signal SIGTTOU");
+	}
+*/
 	if (signal(SIGWINCH, winch_handler) == BADSIG)
 	{
 		syserr_fatal(-1, "signal SIGWINCH");
 	}
 
+	signal(SIGPIPE, SIG_IGN);
+
 	for (c = 0 ; c < argc ; c++)
 	{
 		if (c)
@@ -188,7 +235,7 @@ int main(int argc, char **argv)
 
 	if (argc > 1)
 	{
-		while ((c = getopt(argc, argv, "a: e: G h r: R:: s t: T v")) != EOF)
+		while ((c = getopt(argc, argv, "a: e: G h M:: r: R:: s t: T v V")) != EOF)
 		{
 			switch (c)
 			{
@@ -199,15 +246,17 @@ int main(int argc, char **argv)
 					printf("  -e  Execute given command.\n");
 					printf("  -G  Don't show the greeting screen.\n");
 					printf("  -h  This help section.\n");
+					printf("  -M  Matrix Digital Rain.\n");
 					printf("  -r  Read given file.\n");
 					printf("  -s  Enable screen reader mode.\n");
 					printf("  -t  Set given title.\n");
 					printf("  -T  Don't set the default title.\n");
 					printf("  -v  Enable verbose mode.\n");
+					printf("  -V  Show version information.\n");
 
 					exit(1);
-					break;
 
+				case 'M':
 				case 'G':
 					SET_BIT(greeting, STARTUP_FLAG_NOGREETING);
 					break;
@@ -215,6 +264,12 @@ int main(int argc, char **argv)
 				case 's':
 					SET_BIT(greeting, STARTUP_FLAG_SCREENREADER);
 					break;
+
+				case 'V':
+					printf("\nTinTin++ " CLIENT_VERSION "\n");
+					printf("\n(C) 2004-2019 Igor van den Hoven\n");
+					printf("\nLicense GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n\n");
+					exit(1);
 			}
 		}
 	}
@@ -241,7 +296,7 @@ int main(int argc, char **argv)
 
 		RESTRING(gtd->vars[2], argv[1]);
 
-		while ((c = getopt(argc, argv, "a: e: G h r: R:: s t: T v")) != EOF)
+		while ((c = getopt(argc, argv, "a: e: G h M:: r: R:: s t: T v")) != EOF)
 		{
 			switch (c)
 			{
@@ -259,6 +314,10 @@ int main(int argc, char **argv)
 				case 'G':
 					break;
 
+				case 'M':
+					do_test(gts, optarg ? optarg : "");
+					break;
+
 				case 'r':
 					gtd->level->input++;
 					gtd->ses = do_read(gtd->ses, optarg);
@@ -366,7 +425,8 @@ void init_tintin(int greeting)
 
 	gtd->level          = (struct level_data *) calloc(1, sizeof(struct level_data));
 
-	gtd->str_size       = sizeof(struct str_data);
+	gtd->memory         = calloc(1, sizeof(struct str_data));
+
 	gtd->buf            = str_alloc(STRING_SIZE);
 	gtd->out            = str_alloc(STRING_SIZE);
 
@@ -448,6 +508,7 @@ void init_tintin(int greeting)
 	gts->name           = strdup("gts");
 	gts->group          = strdup("");
 	gts->session_host   = strdup("");
+	gts->session_ip     = strdup("");
 	gts->session_port   = strdup("");
 	gts->cmd_color      = strdup("");
 	gts->telopts        = TELOPT_FLAG_ECHO;
@@ -467,12 +528,14 @@ void init_tintin(int greeting)
 	gts->split  = calloc(1, sizeof(struct split_data));
 	gts->scroll = calloc(1, sizeof(struct scroll_data));
 
-	init_terminal_size(gts);
-
 	init_local(gts);
 
+	init_terminal_size(gts);
+
 	gtd->level->input++;
 
+	do_class(gts, "{CONFIG} {OPEN}");
+
 	do_configure(gts, "{AUTO TAB}         {5000}");
 	do_configure(gts, "{BUFFER SIZE}     {10000}");
 	do_configure(gts, "{COLOR MODE}         {ON}");
@@ -499,7 +562,11 @@ void init_tintin(int greeting)
 	do_configure(gts, "{VERBOSE}           {OFF}");
 	do_configure(gts, "{WORDWRAP}           {ON}");
 
-	gtd->level->input--;
+	do_class(gts, "{CONFIG} {CLOSE}");
+
+
+
+	do_class(gts, "{PATHDIR} {OPEN}");
 
 	insert_node_list(gts->list[LIST_PATHDIR],  "n",  "s",  "1", "");
 	insert_node_list(gts->list[LIST_PATHDIR],  "e",  "w",  "2", "");
@@ -513,11 +580,13 @@ void init_tintin(int greeting)
 	insert_node_list(gts->list[LIST_PATHDIR], "se", "nw",  "6", "");
 	insert_node_list(gts->list[LIST_PATHDIR], "sw", "ne", "12", "");
 
+	do_class(gts, "{PATHDIR} {CLOSE}");
+
+	gtd->level->input--;
+
 	init_terminal(gts);
 
-	{
-		reset_screen(gts);
-	}
+	reset_screen(gts);
 
 	if (!HAS_BIT(greeting, STARTUP_FLAG_NOGREETING))
 	{

+ 6553 - 4424
src/mapper.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
@@ -38,21 +37,28 @@ int                 map_grid_y;
 
 extern  int dir_flags(struct session *ses, int room, int dir);
 extern void create_map(struct session *ses, char *arg);
-extern  int create_room(struct session *ses, char *format, ...);
+extern struct room_data *create_room(struct session *ses, char *format, ...);
 extern void delete_room(struct session *ses, int room, int exits);
 extern struct exit_data *create_exit(struct session *ses, int room, char *format, ...);
 extern void delete_exit(struct session *ses, int room, struct exit_data *exit);
 extern void search_keywords(struct session *ses, char *arg, char *out, char *var);
 extern void map_search_compile(struct session *ses, char *arg, char *var);
 extern  int match_room(struct session *ses, int room, struct search_data *search);
+extern  int find_location(struct session *ses, char *arg);
+extern  int find_path(struct session *ses, char *arg);
 extern  int find_room(struct session *ses, char *arg);
 extern void goto_room(struct session *ses, int room);
 extern  int find_new_room(struct session *ses);
 extern struct exit_data *find_exit(struct session *ses, int room, char *arg);
 extern  int get_exit_dir(struct session *ses, char *arg);
-extern  int get_exit_grid(struct session *ses, int dir);
+extern  int get_exit_length(struct session *ses, struct exit_data *exit);
+extern char *get_exit_color(struct session *ses, int room, struct exit_data *exit);
+extern  int dir_to_grid(int dir);
+extern  int revdir_to_grid(int dir);
 extern void set_room_exits(struct session *ses, int room);
 extern  int get_room_exits(struct session *ses, int room);
+extern  int get_terrain_index(struct session *ses, struct room_data *room, int x, int y);
+extern char *draw_terrain_symbol(struct session *ses, struct room_data *room, int line, int col, int x, int y, int flags);
 extern void displaygrid_build(struct session *ses, int room, int x, int y, int z);
 extern void add_undo(struct session *ses, char *format, ...);
 extern void del_undo(struct session *ses, struct link_data *link);
@@ -65,55 +71,31 @@ extern  int tunnel_void(struct session *ses, int from, int room, int dir);
 extern  int check_global(struct session *ses, int room);
 extern  int find_coord(struct session *ses, char *arg);
 extern  int spatialgrid_find(struct session *ses, int vnum, int x, int y, int z);
+extern void update_terrain(struct session *ses);
 
 DO_COMMAND(do_map)
 {
 	int cnt;
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
 
+	push_call("do_map(%p,%p)",ses,arg);
+
 	arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
 
 	if (*arg1 == 0)
 	{
-		tintin_printf2(ses, "Available map options");
-		tintin_printf2(ses, "");
-		tintin_printf2(ses, "#map at       <location>  <command>    (execute command at given location)");
-		tintin_printf2(ses, "#map color    <field>     <color>      (set the color for given field)");
-		tintin_printf2(ses, "#map create   [size]                   (creates the initial map)");
-		tintin_printf2(ses, "#map destroy                           (destroys the map)");
-		tintin_printf2(ses, "#map delete   <direction>              (delete the room at given dir)");
-		tintin_printf2(ses, "#map dig      <direction> [new] [vnum] (creates a new room)");
-		tintin_printf2(ses, "#map exit     <direction>  <command>   (sets the exit command)");
-		tintin_printf2(ses, "#map exitflag <direction> <exit flag>  (set the exit direction)");
-		tintin_printf2(ses, "#map explore  <direction>              (saves path to #path)");
-		tintin_printf2(ses, "#map info                              (info on map and current room)");
-		tintin_printf2(ses, "#map insert   <direction>  [room flag] (insert a new room)");
-		tintin_printf2(ses, "#map jump     <x> <y> <z>              (go to given coordinate)");
-		tintin_printf2(ses, "#map find     <location> [exits]       (saves path to #path)");
-		tintin_printf2(ses, "#map flag     <map flag>               (set map wide flags)");
-		tintin_printf2(ses, "#map get      <option>     <variable>  (get various values)");
-		tintin_printf2(ses, "#map global   <room vnum>              (sets the global exit room)");
-		tintin_printf2(ses, "#map goto     <location> [exits]       (moves you to given room)");
-		tintin_printf2(ses, "#map leave                             (leave the map, return with goto)");
-		tintin_printf2(ses, "#map legend   <symbols>                (sets the map legend)");
-		tintin_printf2(ses, "#map link     <direction>  <room name> (links 2 rooms)");
-		tintin_printf2(ses, "#map list     <location>               (shows list of matching rooms)");
-		tintin_printf2(ses, "#map map      <radius> <filename>      (shows an ascii map)");
-		tintin_printf2(ses, "#map move     <direction>              (move to given direction)");
-		tintin_printf2(ses, "#map offset   <square>                 (place vtmap in given square)");
-		tintin_printf2(ses, "#map read     <filename>               (load a map from file)");
-		tintin_printf2(ses, "#map resize   <size>                   (resize the maximum size)");
-		tintin_printf2(ses, "#map roomflag <room flag>              (set room based flags)");
-		tintin_printf2(ses, "#map set      <option>     <value>     (set various values)");
-		tintin_printf2(ses, "#map return                            (return to last room.)");
-		tintin_printf2(ses, "#map run      <location>   [delay]     (run to given room)");
-		tintin_printf2(ses, "#map travel   <direction>  [delay]     (run in given direction)");
-		tintin_printf2(ses, "#map undo                              (undo last move)");
-		tintin_printf2(ses, "#map uninsert <direction>              (opposite of insert)");
-		tintin_printf2(ses, "#map unlink   <direction> [both]       (deletes an exit)");
-		tintin_printf2(ses, "#map vnum     <low vnum> [high vnum]   (change room vnum)");
-		tintin_printf2(ses, "#map write    <filename>               (save the map)");
+		tintin_header(ses, " MAP OPTIONS ");
+
+		for (cnt = 0 ; *map_table[cnt].fun != NULL ; cnt++)
+		{
+			if (*map_table[cnt].desc)
+			{
+				tintin_printf2(ses, "  [%-13s] %s", map_table[cnt].name, map_table[cnt].desc);
+			}
+		}
+		tintin_header(ses, "");
 
+		pop_call();
 		return ses;
 	}
 	else
@@ -126,15 +108,18 @@ DO_COMMAND(do_map)
 				{
 					show_error(ses, LIST_COMMAND, "#MAP: This session has no map data. Use #map create or #map read to create one.");
 					
+					pop_call();
 					return ses;
 				}
 				if (map_table[cnt].check > 1 && ses->map->room_list[ses->map->in_room] == NULL)
 				{
 					show_error(ses, LIST_COMMAND, "#MAP: You are not inside the map. Use #map goto to enter it.");
 
+					pop_call();
 					return ses;
 				}
-				*arg1 = *arg2 = 0;
+				*arg1 = 0;
+				*arg2 = 0;
 
 				if (gtd->level->ignore == 0)
 				{
@@ -143,5904 +128,8048 @@ DO_COMMAND(do_map)
 						SET_BIT(ses->flags, SES_FLAG_UPDATEVTMAP);
 					}
 				}
+				pop_call();
 
-				map_table[cnt].map (ses, arg, arg1, arg2);
+				push_call("MAP_%s(%p,%p,%p,%p,%p)",map_table[cnt].name,ses,arg,arg1,arg2);
 
+				map_table[cnt].fun (ses, arg, arg1, arg2);
+
+				pop_call();
 				return ses;
 			}
 		}
 
 		do_map(ses, "");
 	}
+	pop_call();
 	return ses;
 }
 
-DO_MAP(map_at)
-{
-	struct exit_data *exit;
-	int new_room;
+/*
+	Utility functions
+*/
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_NONE);
+void create_map(struct session *ses, char *arg)
+{
+	int group, legend;
 
-	new_room = find_room(ses, arg1);
+	push_call("create_map(%p,%p)",ses,arg);
 
-	if (new_room == 0)
+	if (ses->map)
 	{
-		exit = find_exit(ses, ses->map->in_room, arg1);
-
-		if (exit == NULL)
-		{
-			show_error(ses, LIST_COMMAND, "#MAP AT: Couldn't find room or exit {%s}.", arg1);
-
-			return;
-		}
-		new_room = exit->vnum;
+		delete_map(ses);
 	}
 
-	ses->map->at_room = ses->map->in_room;
-	ses->map->in_room = new_room;
-
-	script_driver(ses, LIST_COMMAND, arg2);
+	ses->map = (struct map_data *) calloc(1, sizeof(struct map_data));
+	ses->map->size = atoi(arg) > 0 ? atoi(arg) : 50000;
 
-	if (ses->map)
-	{
-		ses->map->in_room = ses->map->at_room;
-	}
-}
+	ses->map->room_list = (struct room_data **) calloc(ses->map->size, sizeof(struct room_data *));
 
-DO_MAP(map_center)
-{
-	char arg3[BUFFER_SIZE];
+	ses->map->max_grid_x = 255;
+	ses->map->max_grid_y = 101;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	ses->map->grid_rooms = (struct room_data **) calloc(ses->map->max_grid_x * ses->map->max_grid_y, sizeof(struct room_data *));
 
-	if (*arg1 == 0)
-	{
-		ses->map->center_x = ses->map->center_y = ses->map->center_z = 0;
-	}
-	else
-	{
-		arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
-		arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+	ses->map->search = calloc(1, sizeof(struct search_data));
 
-		if (!is_math(ses, arg1) || !is_math(ses, arg2) || !is_math(ses, arg3))
-		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP CENTER {X} {Y} {Z}");
+	ses->map->flags = MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_DIRECTION|MAP_FLAG_TERRAIN;
 
-			return;
-		}
-		else
-		{
-			ses->map->center_x = get_number(ses, arg1);
-			ses->map->center_y = get_number(ses, arg2);
-			ses->map->center_z = get_number(ses, arg3);
+	ses->map->global_exit         = (struct exit_data *) calloc(1, sizeof(struct exit_data));
+		ses->map->global_exit->vnum   = ses->map->global_vnum;
+		ses->map->global_exit->name   = restringf(ses->map->global_exit->name, "%cnop global", gtd->tintin_char);
+		ses->map->global_exit->cmd    = restringf(ses->map->global_exit->cmd, "%cnop global", gtd->tintin_char);
+		ses->map->global_exit->data   = strdup("");
+		ses->map->global_exit->weight = 1;
+		ses->map->global_exit->color  = strdup("");
 
-			show_message(ses, LIST_COMMAND, "#MAP CENTER SET TO {%d} {%d} {%d}", ses->map->center_x, ses->map->center_y, ses->map->center_z);
-		}
-	}
-}
+	do_map(ses, "{COLOR} {RESET}");
 
-DO_MAP(map_color)
-{
-	char buf[BUFFER_SIZE];
-	int index;
+	ses->map->display_stamp = 1;
+	ses->map->search->stamp = 1;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+	do_map(ses, "TERRAIN {} { }");
+/*
+	do_map(ses, "TERRAIN BEACH        <eea>~");
+	do_map(ses, "TERRAIN CITY         <ebf>-");
+	do_map(ses, "TERRAIN DESERT       <ffa>.");
+	do_map(ses, "TERRAIN FIELD        <228>.");
+	do_map(ses, "TERRAIN FOREST       <128>^");
+	do_map(ses, "TERRAIN HILL         <ddd>^");
+	do_map(ses, "TERRAIN LAKE         <248>~");
+	do_map(ses, "TERRAIN MOUNTAIN     <acf>^");
+	do_map(ses, "TERRAIN OCEAN        <148>@");
+	do_map(ses, "TERRAIN SWAMP        <bda>.");
+	do_map(ses, "TERRAIN UNDERGROUND  <baa>-");
+*/
 
-	if (*arg1)
-	{
-		if (!strcasecmp(arg1, "RESET"))
-		{
-			for (index = 0 ; map_color_table[index].name ; index++)
-			{
-				strncpy(ses->map->color[index], map_color_table[index].code, COLOR_SIZE - 1);
-			}
+	create_room(ses, "%s", "{1} {0} {} {} { } {} {} {} {} {} {1.0} {}");
 
-			return;
-		}
+	strcpy(arg, "");
 
-		for (index = 0 ; map_color_table[index].name ; index++)
+	for (group = 0 ; map_group_table[group].name ; group++)
+	{
+		for (legend = 0 ; map_legend_table[legend].group ; legend++)
 		{
-			if (is_abbrev(arg1, map_color_table[index].name))
+			if (*map_group_table[group].group == 0 || is_abbrev(map_group_table[group].group, map_legend_table[legend].group))
 			{
-				if (is_abbrev(arg2, "RESET"))
-				{
-					strncpy(ses->map->color[index], map_color_table[index].code, COLOR_SIZE - 1);
-				}
-				else
-				{
-					strncpy(ses->map->color[index], arg2, COLOR_SIZE - 1);
-				}
-				get_color_names(ses, ses->map->color[index], buf);
-
-				show_message(ses, LIST_COMMAND, "#MAP COLOR %s%10s\e[0m SET TO {%s}", buf, map_color_table[index].name, ses->map->color[index]);
-
 				break;
 			}
 		}
 
-		if (map_color_table[index].name == NULL)
+		if (map_legend_table[legend].group)
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP COLOR {AVOID|BACKGROUND|EXIT|HIDE|INVIS|PATH|ROOM|USER} {COLOR CODE}");
-
-			return;
+			map_group_table[group].start = legend;
 		}
-		show_message(ses, LIST_COMMAND, "#MAP: %s color set to: %s", arg1, arg2);
-	}
-	else
-	{
-		for (index = 0 ; map_color_table[index].name ; index++)
+		else
 		{
-			get_color_names(ses, ses->map->color[index], buf);
+			show_error(ses, LIST_COMMAND, "create_map: unknown legend group: %s, %s", map_group_table[group].name, map_group_table[group].group);
 
-			show_message(ses, LIST_COMMAND, "#MAP COLOR %s%10s\e[0m SET TO {%s}", buf, map_color_table[index].name, ses->map->color[index]);
+			continue;
 		}
-	}
-}
-
-DO_MAP(map_create)
-{
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
-
-	create_map(ses, arg1);
-
-	tintin_printf2(ses, "#MAP: %d room map created, use #map goto 1, to proceed", ses->map->size);
-}
-
-DO_MAP(map_debug)
-{
-	tintin_printf2(ses, "max spatial grid x: %d", ses->map->max_grid_x);
-	tintin_printf2(ses, "max spatial grid y: %d", ses->map->max_grid_y);
-	tintin_printf2(ses, "     max undo size: %d", ses->map->undo_size);
-	tintin_printf2(ses, "           in room: %d", ses->map->in_room);
-	tintin_printf2(ses, "           at room: %d", ses->map->at_room);
-	tintin_printf2(ses, "         last room: %d", ses->map->last_room);
-	tintin_printf2(ses, "             stamp: %d", ses->map->search->stamp);
-	tintin_printf2(ses, "            length: %f", ses->map->room_list[ses->map->in_room]->length);
-	tintin_printf2(ses, "          nofollow: %d", ses->map->nofollow);
-
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
 
-	if (*arg1)
-	{
-		if (is_abbrev(arg1, "undo"))
+		while (map_legend_table[++legend].group)
 		{
-			struct link_data *link;
-
-			for (link = ses->map->undo_head ; link ; link = link->next)
+			if (*map_group_table[group].group && !is_abbrev(map_group_table[group].group, map_legend_table[legend].group))
 			{
-				tintin_printf2(ses, "%05s %05s %s", link->str1, link->str2, link->str3);
+				break;
 			}
 		}
+		map_group_table[group].end = legend;
 	}
+
+	gtd->level->quiet++;
+	do_map(ses, "LEGEND RESET");
+	gtd->level->quiet--;
+
+	pop_call();
+	return;
 }
 
-DO_MAP(map_delete)
+int delete_map(struct session *ses)
 {
-	int room;
-	struct exit_data *exit;
-
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+	int index, cnt;
 
-	if (is_number(arg1))
+	for (index = cnt = 0 ; index < ses->map->size ; index++)
 	{
-		room = find_room(ses, arg1);
-
-		if (room == 0)
+		if (ses->map->room_list[index])
 		{
-			show_error(ses, LIST_COMMAND, "#MAP DELETE {%s} - No room with that vnum found", arg1);
+			cnt++;
 
-			return;
+			delete_room(ses, index, FALSE);
 		}
 	}
-	else
-	{
-		exit = find_exit(ses, ses->map->in_room, arg1);
+	free(ses->map->room_list);
 
-		if (exit)
-		{
-			room = exit->vnum;
-		}
+	while (ses->map->undo_head)
+	{
+		del_undo(ses, ses->map->undo_head);
+	}
 
-		if (exit == NULL)
-		{
-			show_error(ses, LIST_COMMAND, "#MAP: No exit with that name found");
-			
-			return;
-		}
+	free(ses->map->global_exit->name);
+	free(ses->map->global_exit->cmd);
+	free(ses->map->global_exit->data);
+	free(ses->map->global_exit);
 
-		room = exit->vnum;
-	}
+	free(ses->map);
 
-	if (room == ses->map->in_room)
-	{
-		show_error(ses, LIST_COMMAND, "#MAP: You must first leave the room you're trying to delete");
-		
-		return;
-	}
+	ses->map = NULL;
 
-	delete_room(ses, room, TRUE);
+	kill_list(ses->list[LIST_LANDMARK]);
+	kill_list(ses->list[LIST_TERRAIN]);
 
-	show_message(ses, LIST_COMMAND, "#MAP: Room {%d} deleted", room);
+	return cnt;
 }
 
-DO_MAP(map_destroy)
+struct room_data *create_room(struct session *ses, char *format, ...)
 {
-	struct exit_data *exit;
-	int index, cnt;
+	char *arg, buf[BUFFER_SIZE];
+	struct room_data *newroom;
+	va_list args;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+	va_start(args, format);
+	vsprintf(buf, format, args);
+	va_end(args);
 
-	if (is_abbrev(arg1, "AREA"))
-	{
-		if (*arg2 == 0)
-		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP DESTROY AREA {<AREA NAME>}");
+	newroom = (struct room_data *) calloc(1, sizeof(struct room_data));
 
-			return;
-		}
+	arg = buf;
 
-		if (ses->map->room_list[ses->map->in_room] && !strcmp(arg2, ses->map->room_list[ses->map->in_room]->area))
-		{
-			show_error(ses, LIST_COMMAND, "#MAP DESTROY AREA: YOU MUST FIRST LEAVE THE AREA YOU ARE TRYING TO DESTROY.");
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->vnum    = atoi(buf);
 
-			return;
-		}
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_SYNC) && ses->map->room_list[newroom->vnum] != NULL)
+	{
+		int vnum = newroom->vnum;
 
-		for (index = cnt = 0 ; index < ses->map->size ; index++)
-		{
-			if (ses->map->room_list[index])
-			{
-				if (!strcmp(arg2, ses->map->room_list[index]->area))
-				{
-					cnt++;
+		free(newroom);
 
-					delete_room(ses, index, FALSE);
-				}
-			}
-		}
+		return ses->map->room_list[vnum];
+	}
 
-		for (index = 0 ; index < ses->map->size ; index++)
-		{
-			if (ses->map->room_list[index])
-			{
-				for (exit = ses->map->room_list[index]->f_exit ; exit ; exit = exit->next)
-				{
-					if (ses->map->room_list[exit->vnum] == NULL)
-					{
-						delete_exit(ses, index, exit);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->flags   = atoi(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->color   = strdup(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->name    = strdup(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->symbol  = strdup(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->desc    = strdup(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->area    = strdup(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->note    = strdup(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->terrain = strdup(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->data    = strdup(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->weight  = atof(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->id      = strdup(buf);
 
-						if (ses->map->room_list[index]->f_exit)
-						{
-							exit = ses->map->room_list[index]->f_exit;
-						}
-						else
-						{
-							break;
-						}
-					}
-				}
-			}
-		}
-		show_message(ses, LIST_COMMAND, "#MAP DESTROY AREA: DELETED %d ROOMS.", cnt);
+	if (HAS_BIT(newroom->flags, ROOM_FLAG_AVOID))
+	{
+		SET_BIT(newroom->flags, ROOM_FLAG_AVOID_TMP);
 	}
-	else if (is_abbrev(arg1, "WORLD"))
+	if (HAS_BIT(newroom->flags, ROOM_FLAG_HIDE))
 	{
-		cnt = delete_map(ses);
+		SET_BIT(newroom->flags, ROOM_FLAG_HIDE_TMP);
+	}
+	if (HAS_BIT(newroom->flags, ROOM_FLAG_LEAVE))
+	{
+		SET_BIT(newroom->flags, ROOM_FLAG_LEAVE_TMP);
+	}
+	if (HAS_BIT(newroom->flags, ROOM_FLAG_VOID))
+	{
+		SET_BIT(newroom->flags, ROOM_FLAG_VOID_TMP);
+	}
+	if (HAS_BIT(newroom->flags, ROOM_FLAG_CURVED))
+	{
+		SET_BIT(newroom->flags, ROOM_FLAG_CURVED_TMP);
+	}
 
-		tintin_printf2(ses, "#MAP DESTROY WORLD: DELETED %d ROOMS.", cnt);
+	if (newroom->weight <= 0)
+	{
+		newroom->weight = 1;
 	}
-	else
+
+	if (newroom->vnum)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP DESTROY {AREA|WORLD} {<ARGUMENT>}");
+		ses->map->room_list[newroom->vnum] = newroom;
 	}
+
+	show_message(ses, LIST_COMMAND, "#MAP CREATE ROOM %5d {%s}.", newroom->vnum, newroom->name);
+
+	return newroom;
 }
 
-DO_MAP(map_dig)
+void delete_room(struct session *ses, int room, int exits)
 {
-	char arg3[BUFFER_SIZE];
-	int room;
-	struct exit_data *exit;
-	struct listnode *node;
-
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
+	struct exit_data *exit, *exit_next;
+	int cnt;
 
-	if (*arg1 == 0)
+	while (ses->map->room_list[room]->f_exit)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP DIG {<DIRECTION>|<VNUM>} {<LOCATION>|NEW}");
-		
-		return;
+		delete_exit(ses, room, ses->map->room_list[room]->f_exit);
 	}
 
-	room = atoi(arg1);
+	free(ses->map->room_list[room]->area);
+	free(ses->map->room_list[room]->color);
+	free(ses->map->room_list[room]->id);
+	free(ses->map->room_list[room]->name);
+	free(ses->map->room_list[room]->symbol);
+	free(ses->map->room_list[room]->desc);
+	free(ses->map->room_list[room]->note);
+	free(ses->map->room_list[room]->terrain);
+	free(ses->map->room_list[room]->data); 
 
-	if (room > 0 && room < ses->map->size)
+	free(ses->map->room_list[room]);
+
+	ses->map->room_list[room] = NULL;
+
+	if (exits)
 	{
-		if (ses->map->room_list[room] == NULL)
+		for (cnt = 0 ; cnt < ses->map->size ; cnt++)
 		{
-			add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_CREATE);
+			if (ses->map->room_list[cnt])
+			{
+				for (exit = ses->map->room_list[cnt]->f_exit ; exit ; exit = exit_next)
+				{
+					exit_next = exit->next;
 
-			create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {}", room);
+					if (exit->vnum == room)
+					{
+						delete_exit(ses, cnt, exit);
+					}
+				}
+			}
 		}
-		return;
 	}
 
-	exit = find_exit(ses, ses->map->in_room, arg1);
+}
 
-	if (exit)
-	{
-		show_message(ses, LIST_COMMAND, "#MAP DIG: There is already a room in that direction.");
-		return;
-	}
+struct exit_data *create_exit(struct session *ses, int vnum, char *format, ...)
+{
+	struct exit_data *newexit;
+	struct room_data *room;
+	va_list args;
+	char *arg, buf[BUFFER_SIZE];
 
-	if (*arg2 && strcasecmp(arg2, "new"))
-	{
-		if (is_number(arg2))
-		{
-			room = get_number(ses, arg2);
-		}
-		else
-		{
-			room = find_room(ses, arg2);
-		}
+	push_call("create_exit(%p,%d,%p)",ses,vnum,format);
 
-		if (room == 0 && !strcasecmp(arg3, "new"))
-		{
-			room = find_new_room(ses);
-		}
+	va_start(args, format);
+	vsprintf(buf, format, args);
+	va_end(args);
 
-		if (room <= 0 || room >= ses->map->size)
-		{
-			show_error(ses, LIST_COMMAND, "#MAP DIG {%s}: Couldn't find room {%s}.", arg1, arg2);
+	newexit = (struct exit_data *) calloc(1, sizeof(struct exit_data));
 
-			return;
-		}
+	room = ses->map->room_list[vnum];
 
-		if (ses->map->room_list[room] == NULL)
-		{
-			add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_CREATE|MAP_UNDO_LINK);
+	arg = buf;
 
-			create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {%s}", room, ses->map->search->id ? ses->map->search->id : "");
-			create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", room, arg1, arg1);
-		}
-		else
-		{
-			add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_LINK);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);	newexit->vnum   = atoi(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);
 
-			create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", room, arg1, arg1);
-		}
-		return;
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_SYNC) && find_exit(ses, vnum, buf))
+	{
+		free(newexit);
+
+		return find_exit(ses, vnum, buf);
 	}
+	newexit->name = strdup(buf);
 
-	room = find_coord(ses, arg1);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ALL);	newexit->cmd    = strdup(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);	newexit->dir    = atoi(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);	newexit->flags  = atoi(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ALL);	newexit->data   = strdup(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);	newexit->weight = atof(buf);
+	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);        newexit->color  = strdup(buf);
 
-	if (room && strcasecmp(arg2, "new"))
+	if (newexit->dir == 0)
 	{
-		show_message(ses, LIST_COMMAND, "#MAP CREATE LINK %5d {%s}.", room, ses->map->room_list[room]->name);
-
-		add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_LINK);
-
-		create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", room, arg1, arg1);
+		newexit->dir = get_exit_dir(ses, newexit->name);
 	}
-	else
-	{
-		for (room = 1 ; room < ses->map->size ; room++)
-		{
-			if (ses->map->room_list[room] == NULL)
-			{
-				break;
-			}
-		}
 
-		if (room == ses->map->size)
-		{
-			show_error(ses, LIST_COMMAND, "#MAP DIG: Maximum amount of rooms of %d reached.", ses->map->size);
-			
-			return;
-		}
-		add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_CREATE|MAP_UNDO_LINK);
+	newexit->grid = dir_to_grid(newexit->dir);
 
-		create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {}", room);
-		create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", room, arg1, arg1);
+	if (room->exit_grid[newexit->grid] == NULL)
+	{
+		room->exit_grid[newexit->grid] = newexit;
 	}
 
-	if ((node = search_node_list(ses->list[LIST_PATHDIR], arg1)) != NULL)
+	if (newexit->weight <= 0)
 	{
-		if (find_exit(ses, room, node->arg2) == NULL)
-		{
-			create_exit(ses, room, "{%d} {%s} {%s}", ses->map->in_room, node->arg2, node->arg2);
-		}
+		newexit->weight = 1;
 	}
+
+	LINK(newexit, room->f_exit, room->l_exit);
+
+	room->exit_size++;
+
+	SET_BIT(room->exit_dirs, (1LL << newexit->dir));
+
+	show_message(ses, LIST_COMMAND, "#MAP CREATE EXIT {%s} {%s} TO ROOM %d.", newexit->name, newexit->cmd, newexit->vnum);
+
+	pop_call();
+	return newexit;
 }
 
-DO_MAP(map_exit)
+void delete_exit(struct session *ses, int room, struct exit_data *exit)
 {
-	char arg3[BUFFER_SIZE];
-	struct exit_data *exit;
-	int room, dir;
+	free(exit->name);
+	free(exit->cmd);
+	free(exit->data);
+	free(exit->color);
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+	UNLINK(exit, ses->map->room_list[room]->f_exit, ses->map->room_list[room]->l_exit)
 
-	exit = find_exit(ses, ses->map->in_room, arg1);
+	set_room_exits(ses, room);
 
-	if (exit == NULL)
-	{
-		show_error(ses, LIST_COMMAND, "#MAP: Exit {%s} not found.", arg1);
-		
-		return;
-	}
+	free(exit);
+}
 
-	if (*arg2 == 0)
+int get_exit_dir(struct session *ses, char *arg)
+{
+	struct listnode *node;
+
+	node = search_node_list(ses->list[LIST_PATHDIR], arg);
+
+	if (node)
 	{
-		tintin_printf2(ses, "  command: %s", exit->cmd);
-		tintin_printf2(ses, "direction: %d", exit->dir);
-		tintin_printf2(ses, "    flags: %d", exit->flags);
-		tintin_printf2(ses, "  get/set: %s", exit->data);
-		tintin_printf2(ses, "     name: %s", exit->name);
-		tintin_printf2(ses, "     vnum: %d", exit->vnum);
-		tintin_printf2(ses, "   weight: %.3f", exit->weight);
+		return atoi(node->arg3);
 	}
-	else if (is_abbrev(arg2, "COMMAND"))
+	else
 	{
-		RESTRING(exit->cmd, arg3);
-
-		show_message(ses, LIST_COMMAND, "#MAP EXIT {%s} : COMMAND SET TO {%s}.", arg1, exit->cmd);
+		return 0;
 	}
-	else if (is_abbrev(arg2, "DIRECTION"))
+}
+
+int get_exit_length(struct session *ses, struct exit_data *exit)
+{
+	return (int) exit->weight + ses->map->room_list[exit->vnum]->length;
+}
+
+char *get_exit_color(struct session *ses, int room, struct exit_data *exit)
+{
+	push_call("get_exit_color(%p,%d,%p)",ses,room,exit);
+
+	if (exit)
 	{
-		if (is_math(ses, arg3))
-		{
-			dir = (int) get_number(ses, arg3);
-		}
-		else if ((dir = get_exit_dir(ses, arg3)) == 0)
+		if (*exit->color)
 		{
-			show_error(ses, LIST_COMMAND, "#MAP EXIT {%s} : DIRECTION {%s} NOT FOUND.", arg1, arg3);
-			
-			return;
+			pop_call();
+			return exit->color;
 		}
 
-		exit->dir = dir;
-
-		set_room_exits(ses, ses->map->in_room);
+		if (room)
+		{
+			struct exit_data *rev_exit = ses->map->room_list[exit->vnum]->exit_grid[revdir_to_grid(exit->dir)];
 
-		show_message(ses, LIST_COMMAND, "#MAP EXIT {%s} : DIRECTION {%s} SET TO {%d}.", arg1, arg3, dir);
-	}
-	else if (is_abbrev(arg2, "FLAGS"))
-	{
-		exit->flags = (int) get_number(ses, arg3);
+			if (rev_exit && rev_exit->vnum == room)
+			{
+				if (ses->map->room_list[exit->vnum]->length < ses->map->room_list[rev_exit->vnum]->length)
+				{
+					exit = rev_exit;
+				}
+			}
+		}
 
-		show_message(ses, LIST_COMMAND, "#MAP EXIT {%s} : FLAGS SET TO {%d}.", arg1, exit->flags);
-	}
-	else if (is_abbrev(arg2, "GET"))
-	{
-		if (*arg3)
+		if (HAS_BIT(exit->flags, EXIT_FLAG_AVOID) && *ses->map->color[MAP_COLOR_AVOID])
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg3, "%s", exit->data);
+			pop_call();
+			return ses->map->color[MAP_COLOR_AVOID];
 		}
-		else
+		if (HAS_BIT(exit->flags, EXIT_FLAG_BLOCK) && *ses->map->color[MAP_COLOR_BLOCK])
 		{
-			tintin_printf2(ses, "#MAP EXIT GET: No destination variable.");
+			pop_call();
+			return ses->map->color[MAP_COLOR_BLOCK];
 		}
-	}
-	else if (is_abbrev(arg2, "NAME"))
-	{
-		RESTRING(exit->name, arg3);
-
-		show_message(ses, LIST_COMMAND, "#MAP EXIT {%s} : NAME SET TO {%s}.", arg1, exit->name);
-	}
-	else if (is_abbrev(arg2, "SAVE"))
-	{
-		if (*arg3)
+		if (HAS_BIT(exit->flags, EXIT_FLAG_HIDE) && *ses->map->color[MAP_COLOR_HIDE])
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg3, "{command}{%s}{destination}{%d}{dir}{%d}{flags}{%d}{name}{%s}{vnum}{%d}{weight}{%.3f}", exit->cmd, tunnel_void(ses, ses->map->in_room, exit->vnum, exit->dir), exit->dir, exit->flags, exit->name, exit->vnum, exit->weight);
+			pop_call();
+			return ses->map->color[MAP_COLOR_HIDE];
 		}
-		else
+		if (HAS_BIT(exit->flags, EXIT_FLAG_INVIS) && *ses->map->color[MAP_COLOR_INVIS])
 		{
-			show_error(ses, LIST_COMMAND, "#MAP EXIT SAVE: No destination variable.");
+			pop_call();
+			return ses->map->color[MAP_COLOR_INVIS];
 		}
+		pop_call();
+		return ses->map->color[MAP_COLOR_EXIT];
 	}
-	else if (is_abbrev(arg2, "SET"))
+	else
 	{
-		RESTRING(exit->data, arg3);
+		pop_call();
+		return "";
+	}
+}
 
-		show_message(ses, LIST_COMMAND, "#MAP EXIT {%s} : DATA SET TO {%s}.", arg1, exit->data);
+int revdir_to_grid(int dir)
+{
+	switch (dir)
+	{
+		case 0:
+			return EXIT_GRID_0;
+		case MAP_EXIT_N:
+			return EXIT_GRID_S;
+		case MAP_EXIT_E:
+			return EXIT_GRID_W;
+		case MAP_EXIT_S:
+			return EXIT_GRID_N;
+		case MAP_EXIT_W:
+			return EXIT_GRID_E;
+		case MAP_EXIT_N|MAP_EXIT_E:
+			return EXIT_GRID_SW;
+		case MAP_EXIT_N|MAP_EXIT_W:
+			return EXIT_GRID_SE;
+		case MAP_EXIT_S|MAP_EXIT_E:
+			return EXIT_GRID_NW;
+		case MAP_EXIT_S|MAP_EXIT_W:
+			return EXIT_GRID_NE;
 	}
-	else if (is_abbrev(arg2, "VNUM"))
+
+	if (HAS_BIT(dir, MAP_EXIT_D))
 	{
-		room = atoi(arg3);
+		return EXIT_GRID_U;
+	}
 
-		if (room <= 0 || room >= ses->map->size)
-		{
-			show_error(ses, LIST_COMMAND, "#MAP EXIT VNUM: Invalid room vnum: %d.", room);
-			return;
-		}
+	if (HAS_BIT(dir, MAP_EXIT_U))
+	{
+		return EXIT_GRID_D;
+	}
 
-		if (ses->map->room_list[room] == NULL)
-		{
-			show_error(ses, LIST_COMMAND, "#MAP EXIT VNUM: Non existant room vnum: %d.", room);
-			return;
-		}
-		exit->vnum = room;
+	return EXIT_GRID_0;
+}
 
-		show_message(ses, LIST_COMMAND, "#MAP EXIT {%s} : VNUM SET TO {%s}.", arg1, arg3);
-	}
-	else if (is_abbrev(arg2, "WEIGHT"))
+int dir_to_grid(int dir)
+{
+	switch (dir)
 	{
-		if (get_number(ses, arg3) < 0.001)
-		{
-			show_message(ses, LIST_COMMAND, "#MAP EXIT {%s} : WEIGHT SHOULD BE AT LEAST 0.001", arg1);
-		}
-		else
-		{
-			exit->weight = (float) get_number(ses, arg3);
+		case 0:
+			return EXIT_GRID_0;
+		case MAP_EXIT_N:
+			return EXIT_GRID_N;
+		case MAP_EXIT_E:
+			return EXIT_GRID_E;
+		case MAP_EXIT_S:
+			return EXIT_GRID_S;
+		case MAP_EXIT_W:
+			return EXIT_GRID_W;
+		case MAP_EXIT_N|MAP_EXIT_E:
+			return EXIT_GRID_NE;
+		case MAP_EXIT_N|MAP_EXIT_W:
+			return EXIT_GRID_NW;
+		case MAP_EXIT_S|MAP_EXIT_E:
+			return EXIT_GRID_SE;
+		case MAP_EXIT_S|MAP_EXIT_W:
+			return EXIT_GRID_SW;
+	}
 
-			show_message(ses, LIST_COMMAND, "#MAP EXIT {%s} : WEIGHT SET TO {%.3f}", arg1, exit->weight);
-		}
+	if (HAS_BIT(dir, MAP_EXIT_D))
+	{
+		return EXIT_GRID_D;
 	}
-	else
+
+	if (HAS_BIT(dir, MAP_EXIT_U))
 	{
-		show_error(ses, LIST_COMMAND, "Syntax: #MAP EXIT {<NAME>} {COMMAND|DIRECTION|GET|NAME|FLAGS|SAVE|SET|VNUM|WEIGHT} {<argument>}");
+		return EXIT_GRID_U;
 	}
+
+	return EXIT_GRID_0;
 }
 
-DO_MAP(map_exitflag)
+int get_room_exits(struct session *ses, int room)
+{
+	return ses->map->room_list[room]->exit_size;
+}
+
+void set_room_exits(struct session *ses, int vnum)
 {
 	struct exit_data *exit;
-	char arg3[BUFFER_SIZE];
-	int flag;
+	struct room_data *room;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+	room = ses->map->room_list[vnum];
 
-	exit = find_exit(ses, ses->map->in_room, arg1);
+	room->exit_dirs = 0;
+	room->exit_size = 0;
 
-	if (exit == NULL)
+	memset(room->exit_grid, 0, sizeof(room->exit_grid));
+
+	for (exit = room->f_exit ; exit ; exit = exit->next)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP EXITFLAG: EXIT {%s} NOT FOUND.", arg1);
+		SET_BIT(room->exit_dirs, 1LL << exit->dir);
 
-		return;
+		if (room->exit_grid[exit->grid] == NULL)
+		{
+			room->exit_grid[exit->grid] = exit;
+		}
+		room->exit_size++;
 	}
+}
 
-	if (*arg2 == 0)
-	{
-		tintin_printf2(ses, "#MAP: AVOID FLAG IS SET TO: %s.", HAS_BIT(exit->flags, EXIT_FLAG_AVOID) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: HIDE FLAG IS SET TO: %s.", HAS_BIT(exit->flags, EXIT_FLAG_HIDE) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: INVIS FLAG IS SET TO: %s.", HAS_BIT(exit->flags, EXIT_FLAG_INVIS) ? "ON" : "OFF");
+int get_terrain_index(struct session *ses, struct room_data *room, int x, int y)
+{
+	struct listroot *root = ses->list[LIST_TERRAIN];
+	struct room_data *wide_grid[11], *vast_grid[11];
+	int terrain;
 
-		return;
+	if (room)
+	{
+		return room->terrain_index;
 	}
 
-	if (is_abbrev(arg2, "AVOID"))
+	wide_grid[EXIT_GRID_N]  = ses->map->grid_rooms[x     + map_grid_x * (y + 1)];
+	wide_grid[EXIT_GRID_NE] = ses->map->grid_rooms[x + 1 + map_grid_x * (y + 1)];
+	wide_grid[EXIT_GRID_E]  = ses->map->grid_rooms[x + 1 + map_grid_x * (y    )];
+	wide_grid[EXIT_GRID_SE] = ses->map->grid_rooms[x + 1 + map_grid_x * (y - 1)];
+	wide_grid[EXIT_GRID_S]  = ses->map->grid_rooms[x     + map_grid_x * (y - 1)];
+	wide_grid[EXIT_GRID_SW] = ses->map->grid_rooms[x - 1 + map_grid_x * (y - 1)];
+	wide_grid[EXIT_GRID_W]  = ses->map->grid_rooms[x - 1 + map_grid_x * (y    )];
+	wide_grid[EXIT_GRID_NW]	= ses->map->grid_rooms[x - 1 + map_grid_x * (y + 1)];
+
+	if (wide_grid[EXIT_GRID_N] && wide_grid[EXIT_GRID_N]->terrain_index != -1 && wide_grid[EXIT_GRID_N]->vnum && HAS_BIT(root->list[wide_grid[EXIT_GRID_N]->terrain_index]->room->terrain_flags, TERRAIN_FLAG_WIDE))
 	{
-		flag = EXIT_FLAG_AVOID;
+		terrain = wide_grid[EXIT_GRID_N]->terrain_index;
+
+		if ((wide_grid[EXIT_GRID_E] == NULL || wide_grid[EXIT_GRID_E]->terrain_index == terrain) && (wide_grid[EXIT_GRID_S] == NULL || wide_grid[EXIT_GRID_S]->terrain_index == terrain) && (wide_grid[EXIT_GRID_W] == NULL || wide_grid[EXIT_GRID_W]->terrain_index == terrain))
+		{
+			return wide_grid[EXIT_GRID_N]->terrain_index;
+		}
 	}
-	else if (is_abbrev(arg2, "HIDE"))
+
+	if (wide_grid[EXIT_GRID_E] && wide_grid[EXIT_GRID_E]->terrain_index != -1 && wide_grid[EXIT_GRID_E]->vnum && HAS_BIT(root->list[wide_grid[EXIT_GRID_E]->terrain_index]->room->terrain_flags, TERRAIN_FLAG_WIDE))
 	{
-		flag = EXIT_FLAG_HIDE;
+		terrain = wide_grid[EXIT_GRID_E]->terrain_index;
+
+		if ((wide_grid[EXIT_GRID_S] == NULL || wide_grid[EXIT_GRID_S]->terrain_index == terrain) && (wide_grid[EXIT_GRID_W] == NULL || wide_grid[EXIT_GRID_W]->terrain_index == terrain) && (wide_grid[EXIT_GRID_N] == NULL || wide_grid[EXIT_GRID_N]->terrain_index == terrain))
+		{
+			return wide_grid[EXIT_GRID_E]->terrain_index;
+		}
 	}
-	else if (is_abbrev(arg2, "INVISIBLE"))
+
+	if (wide_grid[EXIT_GRID_S] && wide_grid[EXIT_GRID_S]->terrain_index != -1 && wide_grid[EXIT_GRID_S]->vnum && HAS_BIT(root->list[wide_grid[EXIT_GRID_S]->terrain_index]->room->terrain_flags, TERRAIN_FLAG_WIDE))
 	{
-		flag = EXIT_FLAG_INVIS;
+		terrain = wide_grid[EXIT_GRID_S]->terrain_index;
+
+		if ((wide_grid[EXIT_GRID_W] == NULL || wide_grid[EXIT_GRID_W]->terrain_index == terrain) && (wide_grid[EXIT_GRID_N] == NULL || wide_grid[EXIT_GRID_N]->terrain_index == terrain) && (wide_grid[EXIT_GRID_E] == NULL || wide_grid[EXIT_GRID_E]->terrain_index == terrain))
+		{
+			return wide_grid[EXIT_GRID_S]->terrain_index;
+		}
 	}
-	else
+
+	if (wide_grid[EXIT_GRID_W] && wide_grid[EXIT_GRID_W]->terrain_index != -1 && wide_grid[EXIT_GRID_W]->vnum && HAS_BIT(root->list[wide_grid[EXIT_GRID_W]->terrain_index]->room->terrain_flags, TERRAIN_FLAG_WIDE))
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP EXITFLAG {%s} <AVOID|HIDE|INVIS> [ON|OFF]", arg1);
+		terrain = wide_grid[EXIT_GRID_W]->terrain_index;
 
-		return;
+		if ((wide_grid[EXIT_GRID_N] == NULL || wide_grid[EXIT_GRID_N]->terrain_index == terrain) && (wide_grid[EXIT_GRID_E] == NULL || wide_grid[EXIT_GRID_E]->terrain_index == terrain) && (wide_grid[EXIT_GRID_S] == NULL || wide_grid[EXIT_GRID_S]->terrain_index == terrain))
+		{
+			return wide_grid[EXIT_GRID_W]->terrain_index;
+		}
 	}
 
-	if (*arg3 == 0)
+	if (x < 2 || y < 2 || x > map_grid_x - 3 || y > map_grid_y - 3)
 	{
-		TOG_BIT(exit->flags, flag);
+		return -1;
 	}
-	else if (is_abbrev(arg3, "ON"))
+
+	vast_grid[EXIT_GRID_N]  = ses->map->grid_rooms[x     + map_grid_x * (y + 2)];
+	vast_grid[EXIT_GRID_NE] = ses->map->grid_rooms[x + 2 + map_grid_x * (y + 2)];
+	vast_grid[EXIT_GRID_E]  = ses->map->grid_rooms[x + 2 + map_grid_x * (y    )];
+	vast_grid[EXIT_GRID_SE] = ses->map->grid_rooms[x + 2 + map_grid_x * (y - 2)];
+	vast_grid[EXIT_GRID_S]  = ses->map->grid_rooms[x     + map_grid_x * (y - 2)];
+	vast_grid[EXIT_GRID_SW] = ses->map->grid_rooms[x - 2 + map_grid_x * (y - 2)];
+	vast_grid[EXIT_GRID_W]  = ses->map->grid_rooms[x - 2 + map_grid_x * (y    )];
+	vast_grid[EXIT_GRID_NW]	= ses->map->grid_rooms[x - 2 + map_grid_x * (y + 2)];
+
+	if (vast_grid[EXIT_GRID_N] && vast_grid[EXIT_GRID_N]->terrain_index != -1 && vast_grid[EXIT_GRID_N]->vnum && HAS_BIT(root->list[vast_grid[EXIT_GRID_N]->terrain_index]->room->terrain_flags, TERRAIN_FLAG_VAST))
 	{
-		SET_BIT(exit->flags, flag);
+		terrain = vast_grid[EXIT_GRID_N]->terrain_index;
+
+		if ((wide_grid[EXIT_GRID_E] == NULL || wide_grid[EXIT_GRID_E]->terrain_index == terrain) && (wide_grid[EXIT_GRID_S] == NULL || wide_grid[EXIT_GRID_S]->terrain_index == terrain) && (wide_grid[EXIT_GRID_W] == NULL || wide_grid[EXIT_GRID_W]->terrain_index == terrain))
+		{
+			return vast_grid[EXIT_GRID_N]->terrain_index;
+		}
 	}
-	else if (is_abbrev(arg3, "OFF"))
+
+	if (vast_grid[EXIT_GRID_E] && vast_grid[EXIT_GRID_E]->terrain_index != -1 && vast_grid[EXIT_GRID_E]->vnum && HAS_BIT(root->list[vast_grid[EXIT_GRID_E]->terrain_index]->room->terrain_flags, TERRAIN_FLAG_VAST))
 	{
-		DEL_BIT(exit->flags, flag);
+		terrain = vast_grid[EXIT_GRID_E]->terrain_index;
+
+		if ((wide_grid[EXIT_GRID_S] == NULL || wide_grid[EXIT_GRID_S]->terrain_index == terrain) && (wide_grid[EXIT_GRID_W] == NULL || wide_grid[EXIT_GRID_W]->terrain_index == terrain) && (wide_grid[EXIT_GRID_N] == NULL || wide_grid[EXIT_GRID_N]->terrain_index == terrain))
+		{
+			return vast_grid[EXIT_GRID_E]->terrain_index;
+		}
 	}
-	else
+
+	if (vast_grid[EXIT_GRID_S] && vast_grid[EXIT_GRID_S]->terrain_index != -1 && vast_grid[EXIT_GRID_S]->vnum && HAS_BIT(root->list[vast_grid[EXIT_GRID_S]->terrain_index]->room->terrain_flags, TERRAIN_FLAG_VAST))
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP EXITFLAG {%s} {%s} [ON|OFF]", arg3);
+		terrain = vast_grid[EXIT_GRID_S]->terrain_index;
+
+		if ((wide_grid[EXIT_GRID_W] == NULL || wide_grid[EXIT_GRID_W]->terrain_index == terrain) && (wide_grid[EXIT_GRID_N] == NULL || wide_grid[EXIT_GRID_N]->terrain_index == terrain) && (wide_grid[EXIT_GRID_E] == NULL || wide_grid[EXIT_GRID_E]->terrain_index == terrain))
+		{
+			return vast_grid[EXIT_GRID_S]->terrain_index;
+		}
 	}
 
-	if (is_abbrev(arg2, "AVOID"))
+	if (vast_grid[EXIT_GRID_W] && vast_grid[EXIT_GRID_W]->terrain_index != -1 && vast_grid[EXIT_GRID_W]->vnum && HAS_BIT(root->list[vast_grid[EXIT_GRID_W]->terrain_index]->room->terrain_flags, TERRAIN_FLAG_VAST))
 	{
-		show_message(ses, LIST_COMMAND, "#MAP: AVOID FLAG SET TO %s.", HAS_BIT(exit->flags, EXIT_FLAG_AVOID) ? "ON" : "OFF");
+		terrain = vast_grid[EXIT_GRID_W]->terrain_index;
+
+		if ((wide_grid[EXIT_GRID_N] == NULL || wide_grid[EXIT_GRID_N]->terrain_index == terrain) && (wide_grid[EXIT_GRID_E] == NULL || wide_grid[EXIT_GRID_E]->terrain_index == terrain) && (wide_grid[EXIT_GRID_S] == NULL || wide_grid[EXIT_GRID_S]->terrain_index == terrain))
+		{
+			return vast_grid[EXIT_GRID_W]->terrain_index;
+		}
 	}
-	else if (is_abbrev(arg2, "HIDE"))
+
+	return -1;
+}
+
+int get_terrain_density(struct room_data *room, int width)
+{
+	int flag = 0;
+
+	switch (width)
 	{
-		show_message(ses, LIST_COMMAND, "#MAP: HIDE FLAG SET TO %s.", HAS_BIT(exit->flags, EXIT_FLAG_HIDE) ? "ON" : "OFF");
+		case 0:
+			if (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_FADEIN))
+			{
+				switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_DENSE|TERRAIN_FLAG_AMPLE|TERRAIN_FLAG_SPARSE|TERRAIN_FLAG_SCANT))
+				{
+					case TERRAIN_FLAG_DENSE:
+						switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_NARROW|TERRAIN_FLAG_STANDARD|TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST))
+						{
+							case TERRAIN_FLAG_NARROW:
+								flag = TERRAIN_FLAG_DENSE;
+								break;
+							case TERRAIN_FLAG_STANDARD:
+								flag = TERRAIN_FLAG_AMPLE;
+								break;
+							case TERRAIN_FLAG_WIDE:
+								flag = TERRAIN_FLAG_SPARSE;
+								break;
+							case TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST:
+								flag = TERRAIN_FLAG_SCANT;
+								break;
+						}
+						break;
+					case TERRAIN_FLAG_AMPLE:
+						switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_NARROW|TERRAIN_FLAG_STANDARD|TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST))
+						{
+							case TERRAIN_FLAG_NARROW:
+								flag = TERRAIN_FLAG_AMPLE;
+								break;
+							case TERRAIN_FLAG_STANDARD:
+								flag = TERRAIN_FLAG_SPARSE;
+								break;
+							case TERRAIN_FLAG_WIDE:
+								flag = TERRAIN_FLAG_SCANT;
+								break;
+							case TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST:
+								flag = TERRAIN_FLAG_SCANT;
+								break;
+						}
+						break;
+
+					case TERRAIN_FLAG_SPARSE:
+						switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_NARROW|TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST))
+						{
+							case TERRAIN_FLAG_NARROW:
+								flag = TERRAIN_FLAG_SPARSE;
+								break;
+							case TERRAIN_FLAG_STANDARD:
+							case TERRAIN_FLAG_WIDE:
+							case TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST:
+								flag = TERRAIN_FLAG_SCANT;
+								break;
+						}
+						break;
+
+					case TERRAIN_FLAG_SCANT:
+						flag = TERRAIN_FLAG_SCANT;
+						break;
+				}
+			}
+			else if (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_FADEOUT))
+			{
+				switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_DENSE|TERRAIN_FLAG_AMPLE|TERRAIN_FLAG_SPARSE|TERRAIN_FLAG_SCANT))
+				{
+					case TERRAIN_FLAG_DENSE:
+						flag = TERRAIN_FLAG_DENSE;
+						break;
+					case TERRAIN_FLAG_AMPLE:
+						flag = TERRAIN_FLAG_AMPLE;
+						break;
+					case TERRAIN_FLAG_SPARSE:
+						flag = TERRAIN_FLAG_SPARSE;
+						break;
+					case TERRAIN_FLAG_SCANT:
+						flag = TERRAIN_FLAG_SCANT;
+						break;
+				}
+			}
+			else
+			{
+				flag = room->terrain_flags;
+			}
+			break;
+
+		case 1:
+			if (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_FADEIN))
+			{
+				switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_DENSE|TERRAIN_FLAG_SPARSE|TERRAIN_FLAG_SCANT))
+				{
+					case TERRAIN_FLAG_DENSE:
+						switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_NARROW|TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST))
+						{
+							case TERRAIN_FLAG_NARROW:
+							case TERRAIN_FLAG_STANDARD:
+								flag = TERRAIN_FLAG_DENSE;
+								break;
+
+							case TERRAIN_FLAG_WIDE:
+								flag = TERRAIN_FLAG_AMPLE;
+								break;
+								
+							case TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST:
+								flag = TERRAIN_FLAG_SPARSE;
+								break;
+						}
+						break;
+
+					case TERRAIN_FLAG_AMPLE:
+						switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_NARROW|TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST))
+						{
+							case TERRAIN_FLAG_NARROW:
+							case TERRAIN_FLAG_STANDARD:
+								flag = TERRAIN_FLAG_AMPLE;
+								break;
+							case TERRAIN_FLAG_WIDE:
+								flag = TERRAIN_FLAG_SPARSE;
+								break;
+							case TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST:
+								flag = TERRAIN_FLAG_SCANT;
+								break;
+						}
+						break;
+
+					case TERRAIN_FLAG_SPARSE:
+						switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_NARROW|TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST))
+						{
+							case TERRAIN_FLAG_NARROW:
+							case TERRAIN_FLAG_STANDARD:
+							case TERRAIN_FLAG_WIDE:
+								flag = TERRAIN_FLAG_SPARSE;
+								break;
+							case TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST:
+								flag = TERRAIN_FLAG_SCANT;
+								break;
+						}
+						break;
+
+					case TERRAIN_FLAG_SCANT:
+						flag = TERRAIN_FLAG_SCANT;
+						break;
+				}
+			}
+			else if (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_FADEOUT))
+			{
+				switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_DENSE|TERRAIN_FLAG_SPARSE|TERRAIN_FLAG_SCANT))
+				{
+					case TERRAIN_FLAG_DENSE:
+						flag = TERRAIN_FLAG_AMPLE;
+						break;
+
+					case TERRAIN_FLAG_AMPLE:
+						flag = TERRAIN_FLAG_SPARSE;
+						break;
+
+					case TERRAIN_FLAG_SPARSE:
+					case TERRAIN_FLAG_SCANT:
+						flag = TERRAIN_FLAG_SCANT;
+						break;
+				}
+			}
+			else
+			{
+				flag = room->terrain_flags;
+			}
+			break;
+
+		case 2:
+			if (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_FADEIN))
+			{
+				switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_DENSE|TERRAIN_FLAG_SPARSE|TERRAIN_FLAG_SCANT))
+				{
+					case TERRAIN_FLAG_DENSE:
+						switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_NARROW|TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST))
+						{
+							case TERRAIN_FLAG_NARROW:
+							case TERRAIN_FLAG_STANDARD:
+							case TERRAIN_FLAG_WIDE:
+								flag = TERRAIN_FLAG_DENSE;
+								break;
+
+							case TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST:
+								flag = TERRAIN_FLAG_AMPLE;
+								break;
+						}
+						break;
+
+					case TERRAIN_FLAG_AMPLE:
+						switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_NARROW|TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST))
+						{
+							case TERRAIN_FLAG_NARROW:
+							case TERRAIN_FLAG_STANDARD:
+							case TERRAIN_FLAG_WIDE:
+								flag = TERRAIN_FLAG_AMPLE;
+								break;
+
+							case TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST:
+								flag = TERRAIN_FLAG_SPARSE;
+								break;
+						}
+						break;
+
+					case TERRAIN_FLAG_SPARSE:
+						switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_NARROW|TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST))
+						{
+							case TERRAIN_FLAG_NARROW:
+							case TERRAIN_FLAG_STANDARD:
+							case TERRAIN_FLAG_WIDE:
+								flag = TERRAIN_FLAG_SPARSE;
+								break;
+							case TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST:
+								flag = TERRAIN_FLAG_SCANT;
+								break;
+						}
+						break;
+
+					case TERRAIN_FLAG_SCANT:
+						flag = TERRAIN_FLAG_SCANT;
+						break;
+				}
+			}
+			else if (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_FADEOUT))
+			{
+				switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_DENSE|TERRAIN_FLAG_SPARSE|TERRAIN_FLAG_SCANT))
+				{
+					case TERRAIN_FLAG_DENSE:
+						flag = TERRAIN_FLAG_SPARSE;
+						break;
+
+					default:
+						flag = TERRAIN_FLAG_SCANT;
+						break;
+				}
+			}
+			else
+			{
+				flag = room->terrain_flags;
+			}
+			break;
+
+		default:
+			if (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_FADEIN))
+			{
+				switch (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_DENSE|TERRAIN_FLAG_SPARSE|TERRAIN_FLAG_SCANT))
+				{
+					case TERRAIN_FLAG_DENSE:
+						flag = TERRAIN_FLAG_DENSE;
+						break;
+					default:
+						flag = 0;
+						break;
+
+					case TERRAIN_FLAG_SPARSE:
+						flag = TERRAIN_FLAG_SPARSE;
+						break;
+
+					case TERRAIN_FLAG_SCANT:
+						flag = TERRAIN_FLAG_SCANT;
+						break;
+				}
+			}
+			else if (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_FADEOUT))
+			{
+				flag = TERRAIN_FLAG_SCANT;
+			}
+			else
+			{
+				flag = room->terrain_flags;
+			}
+			break;
 	}
-	else if (is_abbrev(arg2, "INVISIBLE"))
+
+	if (HAS_BIT(room->terrain_flags, TERRAIN_FLAG_DOUBLE))
 	{
-		show_message(ses, LIST_COMMAND, "#MAP: INVIS FLAG SET TO %s.", HAS_BIT(exit->flags, EXIT_FLAG_INVIS) ? "ON" : "OFF");
+		SET_BIT(flag, TERRAIN_FLAG_DOUBLE);
 	}
+	return flag;
 }
 
-DO_MAP(map_explore)
+char *blank_terrain_symbol(struct session *ses, struct room_data *room, int index, int flags)
 {
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
-
-	explore_path(ses, FALSE, arg1, "");
-}
+	if (HAS_BIT(flags, TERRAIN_FLAG_DOUBLE))
+	{
+		if (room && room->terrain_index != -1 && HAS_BIT(ses->list[LIST_TERRAIN]->list[room->terrain_index]->room->terrain_flags, TERRAIN_FLAG_DOUBLE))
+		{
+			if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
+			{
+				if (index % 2 == 1)
+				{
+					SET_BIT(ses->map->flags, MAP_FLAG_DOUBLED);
 
-DO_MAP(map_find)
-{
-	shortest_path(ses, FALSE, arg1, arg);
+					return "  ";
+				}
+				else
+				{
+					DEL_BIT(ses->map->flags, MAP_FLAG_DOUBLED);
+					return "";
+				}
+			}
+			else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS))
+			{
+				switch (index)
+				{
+					case 1:
+					case 3:
+						SET_BIT(ses->map->flags, MAP_FLAG_DOUBLED);
+						return "  ";
+					case 2:
+					case 4:
+						DEL_BIT(ses->map->flags, MAP_FLAG_DOUBLED);
+						return "";
+					case 5:
+						return "\e[1;31m5";
+				}
+			}
+		}
+//		DEL_BIT(ses->map->flags, MAP_FLAG_DOUBLED);
+		return " ";
+	}
+	return " ";
 }
 
-DO_MAP(map_flag)
+char *draw_terrain_symbol(struct session *ses, struct room_data *room, int line, int index, int x, int y, int flags)
 {
-	int flag = 0, unflag = 0;
+	struct room_data *room_grid[11], *terrain_room;
+	int terrain, width = 0, density, hash;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+	if (!HAS_BIT(ses->map->flags, MAP_FLAG_TERRAIN))
+	{
+		return " ";
+	}
 
-	if (*arg1)
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_DOUBLED))
 	{
-		if (is_abbrev(arg1, "static"))
+		DEL_BIT(ses->map->flags, MAP_FLAG_DOUBLED);
+
+		if (HAS_BIT(flags, TERRAIN_FLAG_DOUBLE) && index != 1)
 		{
-			flag = MAP_FLAG_STATIC;
+			return "";
 		}
-		else if (is_abbrev(arg1, "vtmap"))
+//		return "\e[1;31m?";
+	}
+
+	room_grid[EXIT_GRID_0]  = ses->map->grid_rooms[x     + map_grid_x * (y    )];
+	room_grid[EXIT_GRID_N]  = ses->map->grid_rooms[x     + map_grid_x * (y + 1)];
+	room_grid[EXIT_GRID_NE] = ses->map->grid_rooms[x + 1 + map_grid_x * (y + 1)];
+	room_grid[EXIT_GRID_E]  = ses->map->grid_rooms[x + 1 + map_grid_x * (y    )];
+	room_grid[EXIT_GRID_SE] = ses->map->grid_rooms[x + 1 + map_grid_x * (y - 1)];
+	room_grid[EXIT_GRID_S]  = ses->map->grid_rooms[x     + map_grid_x * (y - 1)];
+	room_grid[EXIT_GRID_SW] = ses->map->grid_rooms[x - 1 + map_grid_x * (y - 1)];
+	room_grid[EXIT_GRID_W]  = ses->map->grid_rooms[x - 1 + map_grid_x * (y    )];
+	room_grid[EXIT_GRID_NW]	= ses->map->grid_rooms[x - 1 + map_grid_x * (y + 1)];
+
+	hash = index + (room && room->vnum ? room->vnum : x + y);
+
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
+	{
+		if (room == NULL)
 		{
-			flag = MAP_FLAG_VTMAP;
+			width++;
+
+			switch (line)
+			{
+				case 1:
+					switch (index)
+					{
+						case 1:
+						case 2:
+							room = room_grid[EXIT_GRID_N] ? room_grid[EXIT_GRID_N] : room_grid[EXIT_GRID_W] ? room_grid[EXIT_GRID_W] : room_grid[EXIT_GRID_NW] ? room_grid[EXIT_GRID_NW] : NULL;
+							break;
+						case 3:
+						case 4:
+							room = room_grid[EXIT_GRID_N] ? room_grid[EXIT_GRID_N] : NULL;
+							break;
+						case 5:
+						case 6:
+							room = room_grid[EXIT_GRID_N] ? room_grid[EXIT_GRID_N] : room_grid[EXIT_GRID_E] ? room_grid[EXIT_GRID_E] : room_grid[EXIT_GRID_NE] ? room_grid[EXIT_GRID_NE] : NULL;
+							break;
+					}
+					break;
+
+				case 2:
+					switch (index)
+					{
+						case 1:
+						case 2:
+							room = room_grid[EXIT_GRID_W] ? room_grid[EXIT_GRID_W] : room_grid[EXIT_GRID_NW] ? room_grid[EXIT_GRID_NW] : room_grid[EXIT_GRID_SW] ? room_grid[EXIT_GRID_SW] : NULL;
+							break;
+						case 3:
+						case 4:
+							room = room_grid[EXIT_GRID_N] ? room_grid[EXIT_GRID_N] : room_grid[EXIT_GRID_S] ? room_grid[EXIT_GRID_S] : NULL;
+							break;
+						case 5:
+						case 6:
+							room = room_grid[EXIT_GRID_E] ? room_grid[EXIT_GRID_E] : room_grid[EXIT_GRID_NE] ? room_grid[EXIT_GRID_NE] : room_grid[EXIT_GRID_SE] ? room_grid[EXIT_GRID_SE] : NULL;
+							break;
+					}
+					break;
+
+				case 3:
+					switch (index)
+					{
+						case 1:
+						case 2:
+							room = room_grid[EXIT_GRID_S] ? room_grid[EXIT_GRID_S] : room_grid[EXIT_GRID_W] ? room_grid[EXIT_GRID_W] : room_grid[EXIT_GRID_SW] ? room_grid[EXIT_GRID_SW] : NULL;
+							break;
+						case 3:
+						case 4:
+							room = room_grid[EXIT_GRID_S] ? room_grid[EXIT_GRID_S] : NULL;
+							break;
+						case 5:
+						case 6:
+							room = room_grid[EXIT_GRID_S] ? room_grid[EXIT_GRID_S] : room_grid[EXIT_GRID_E] ? room_grid[EXIT_GRID_E] : room_grid[EXIT_GRID_SE] ? room_grid[EXIT_GRID_SE] : NULL;
+							break;
+					}
+					break;
+			}
+
+			if (room == NULL || room->terrain_index == -1 || HAS_BIT(ses->list[LIST_TERRAIN]->list[room->terrain_index]->room->terrain_flags, TERRAIN_FLAG_NARROW))
+			{
+				return blank_terrain_symbol(ses, room, index, flags);
+			}
 		}
-		else if (is_abbrev(arg1, "asciigraphics"))
+
+		terrain = room->terrain_index;
+
+		if (terrain == -1)
 		{
-			flag = MAP_FLAG_ASCIIGRAPHICS;
-			unflag = MAP_FLAG_MUDFONT|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_BLOCKGRAPHICS;
+			return blank_terrain_symbol(ses, room, index, flags);
 		}
-		else if (is_abbrev(arg1, "asciivnums"))
+
+		terrain_room = ses->list[LIST_TERRAIN]->list[terrain]->room;
+
+		if (HAS_BIT(ses->list[LIST_TERRAIN]->list[room->terrain_index]->room->terrain_flags, TERRAIN_FLAG_DOUBLE))
 		{
-			flag = MAP_FLAG_ASCIIVNUMS;
+			if (HAS_BIT(flags, TERRAIN_FLAG_DOUBLE))
+			{
+				if (index % 2 == 0)
+				{
+					return "\e[1;36m?";
+				}
+				SET_BIT(ses->map->flags, MAP_FLAG_DOUBLED);
+			}
+			else
+			{
+				return " ";
+			}
 		}
-		else if (is_abbrev(arg1, "blockgraphics"))
+
+		if (HAS_BIT(terrain_room->terrain_flags, TERRAIN_FLAG_NARROW) && room->exit_grid[EXIT_GRID_E] == NULL)
 		{
-			flag = MAP_FLAG_BLOCKGRAPHICS;
-			unflag = MAP_FLAG_MUDFONT|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_ASCIIGRAPHICS;
+			switch (line * 10 + index)
+			{
+				case 16:
+				case 26:
+				case 36:
+					return blank_terrain_symbol(ses, room, index, flags);
+			}
 		}
-		else if (is_abbrev(arg1, "direction"))
+
+		if (room->vnum == 0)
 		{
-			flag = MAP_FLAG_DIRECTION;
+			width++;
+
+			density = 0;
+
+			density += (room_grid[EXIT_GRID_N] && room_grid[EXIT_GRID_N]->vnum);
+			density += (room_grid[EXIT_GRID_NE] && room_grid[EXIT_GRID_NE]->vnum);
+			density += (room_grid[EXIT_GRID_E] && room_grid[EXIT_GRID_E]->vnum);
+			density += (room_grid[EXIT_GRID_SE] && room_grid[EXIT_GRID_SE]->vnum);
+			density += (room_grid[EXIT_GRID_S] && room_grid[EXIT_GRID_S]->vnum);
+			density += (room_grid[EXIT_GRID_SW] && room_grid[EXIT_GRID_SW]->vnum);
+			density += (room_grid[EXIT_GRID_W] && room_grid[EXIT_GRID_W]->vnum);
+			density += (room_grid[EXIT_GRID_NW] && room_grid[EXIT_GRID_NW]->vnum);
+
+			if (density == 0)
+			{
+				width++;
+			}
 		}
-		else if (is_abbrev(arg1, "mudfont"))
+
+		density = get_terrain_density(terrain_room, width);
+
+		if (HAS_BIT(density, TERRAIN_FLAG_DENSE))
 		{
-			flag = MAP_FLAG_MUDFONT;
-			unflag = MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_BLOCKGRAPHICS;
+			return ses->list[LIST_TERRAIN]->list[terrain]->arg2;
 		}
-		else if (is_abbrev(arg1, "nofollow"))
+
+		if (HAS_BIT(density, TERRAIN_FLAG_SPARSE))
 		{
-			flag = MAP_FLAG_NOFOLLOW;
+			switch (line * 10 + index)
+			{
+				case 11:
+				case 23:
+				case 35:
+					return ses->list[LIST_TERRAIN]->list[terrain]->arg2;
+			}
+			return blank_terrain_symbol(ses, room, index, flags);
 		}
-		else if (is_abbrev(arg1, "simplegraphics"))
+
+		if (HAS_BIT(density, TERRAIN_FLAG_SCANT))
 		{
-			unflag = MAP_FLAG_ASCIIVNUMS|MAP_FLAG_SYMBOLGRAPHICS|MAP_FLAG_MUDFONT|MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_BLOCKGRAPHICS;
+			switch (line * 10 + index)
+			{
+				case 11:
+					return hash % 3 == 0 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				case 23:
+					return hash % 3 == 1 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				case 35:
+					return hash % 3 == 2 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+			}
+			return blank_terrain_symbol(ses, room, index, flags);
 		}
-		else if (is_abbrev(arg1, "symbolgraphics"))
+
+		if (HAS_BIT(density, TERRAIN_FLAG_DOUBLE))
 		{
-			flag = MAP_FLAG_SYMBOLGRAPHICS;
-			unflag = MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_BLOCKGRAPHICS;
+			switch (y % 2 * 30 + line * 10 + index)
+			{
+				case 11:
+				case 15:
+				case 23:
+				case 31:
+				case 35:
+				case 43:
+				case 51:
+				case 55:
+				case 63:
+					return ses->list[LIST_TERRAIN]->list[terrain]->arg2;
+			}
+			return blank_terrain_symbol(ses, room, index, flags);
 		}
-		else if (is_abbrev(arg1, "unicodegraphics"))
+
+		switch (y % 2 * 30 + line * 10 + index)
 		{
-			flag = MAP_FLAG_UNICODEGRAPHICS;
-			unflag = MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_MUDFONT|MAP_FLAG_BLOCKGRAPHICS;
-		}
-		else
-		{
-			show_error(ses, LIST_COMMAND, "#MAP: Invalid flag {%s}.", arg1);
-
-			return;
-		}
-	}
-	else
-	{
-		tintin_printf2(ses, "#MAP: AsciiGraphics flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: AsciiVnums flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: BlockGraphics flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: Direction flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_DIRECTION) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: MudFont flag is set to %s", HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: NoFollow flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: SimpleGraphics flag is set to %s.", !HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS|MAP_FLAG_SYMBOLGRAPHICS|MAP_FLAG_MUDFONT|MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_BLOCKGRAPHICS) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: Static flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_STATIC) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: SymbolGraphics flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_SYMBOLGRAPHICS) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: UnicodeGraphics flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) ? "ON" : "OFF");
-		tintin_printf2(ses, "#MAP: VTmap flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP) ? "ON" : "OFF");
-
-		return;
-	}
+			case 11:
+			case 13:
+			case 15:
+			case 22:
+			case 24:
+			case 26:
+			case 31:
+			case 33:
+			case 35:
+			case 42:
+			case 44:
+			case 46:
+			case 51:
+			case 53:
+			case 55:
+			case 62:
+			case 64:
+			case 66:
+				return ses->list[LIST_TERRAIN]->list[terrain]->arg2;
+		}
+		return blank_terrain_symbol(ses, room, index, flags);
+	}
+
+	// UNICODE
 
-	if (is_abbrev(arg2, "ON"))
-	{
-		SET_BIT(ses->map->flags, flag);
-	}
-	else if (is_abbrev(arg2, "OFF"))
-	{
-		DEL_BIT(ses->map->flags, flag);
-	}
-	else
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS))
 	{
-		TOG_BIT(ses->map->flags, flag);
-	}
+		if (room == NULL)
+		{
+			width++;
 
-	if (unflag)
-	{
-		DEL_BIT(ses->map->flags, unflag);
-	}
+			switch (line)
+			{
+				case 1:
+					switch (index)
+					{
+						case 1:
+						case 2:
+							room = room_grid[EXIT_GRID_W] ? room_grid[EXIT_GRID_W] : room_grid[EXIT_GRID_NW] ? room_grid[EXIT_GRID_NW] : NULL;
+							break;
+						case 3:
+						case 4:
+							room = room_grid[EXIT_GRID_N] ? room_grid[EXIT_GRID_N] : NULL;
+							break;
+						case 5:
+							room = room_grid[EXIT_GRID_N] ? room_grid[EXIT_GRID_N] : NULL;
+							break;
+					}
+					break;
 
-	if (is_abbrev(arg1, "asciigraphics"))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: AsciiGraphics flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS) ? "ON" : "OFF");
-	}
-	else if (is_abbrev(arg1, "asciivnums"))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: AsciiVnums flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS) ? "ON" : "OFF");
-	}
-	else if (is_abbrev(arg1, "direction"))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: Direction flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_DIRECTION) ? "ON" : "OFF");
-	}
-	else if (is_abbrev(arg1, "mudfont"))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: MudFont flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT) ? "ON" : "OFF");
-	}
-	else if (is_abbrev(arg1, "nofollow"))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: NoFollow flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW) ? "ON" : "OFF");
-	}
-	else if (is_abbrev(arg1, "static"))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: Static flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_STATIC) ? "ON" : "OFF");
-	}
-	else if (is_abbrev(arg1, "simplegraphics"))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: All graphic modes have been disabled.");
-	}
-	else if (is_abbrev(arg1, "symbolgraphics"))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: SymbolGraphics flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_SYMBOLGRAPHICS) ? "ON" : "OFF");
-	}
-	else if (is_abbrev(arg1, "unicodegraphics"))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: UnicodeGraphics flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) ? "ON" : "OFF");
-	}
-	else if (is_abbrev(arg1, "vtmap"))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: VTmap flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP) ? "ON" : "OFF");
-	}
+				case 2:
+					switch (index)
+					{
+						case 1:
+						case 2:
+							room = room_grid[EXIT_GRID_W] ? room_grid[EXIT_GRID_W] : NULL;
+							break;
+						case 3:
+						case 4:
+							if (room_grid[EXIT_GRID_S])
+							{
+								room = room_grid[EXIT_GRID_S];
+							}
+							else if (room_grid[EXIT_GRID_N])
+							{
+								room = room_grid[EXIT_GRID_N];
+							}
+							break;
+						case 5:
+							room = room_grid[EXIT_GRID_S] ? room_grid[EXIT_GRID_S] : NULL;
+							break;
+					}
+					break;
+			}
 
+			if (room == NULL || room->terrain_index == -1)
+			{
+				return blank_terrain_symbol(ses, room, index, flags);
+			}
 
-}
+			if (HAS_BIT(ses->list[LIST_TERRAIN]->list[room->terrain_index]->room->terrain_flags, TERRAIN_FLAG_NARROW))
+			{
+				switch (line * 10 + index)
+				{
+					case 23:
+					case 24:
+					case 25:
+						return blank_terrain_symbol(ses, room, index, flags);
+				}
 
-DO_MAP(map_get)
-{
-	struct room_data *room = ses->map->room_list[ses->map->in_room];
-	struct exit_data *exit;
-	char exits[BUFFER_SIZE], arg3[BUFFER_SIZE];
+				if (room_grid[EXIT_GRID_N] == NULL)
+				{
+					switch (line * 10 + index)
+					{
+						case 13:
+						case 14:
+						case 15:
+							return blank_terrain_symbol(ses, room, index, flags);
+					}
+				}
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
+				if (room_grid[EXIT_GRID_W] == NULL && room_grid[EXIT_GRID_NW] == NULL)
+				{
+					switch (line * 10 + index)
+					{
+						case 11:
+						case 12:
+							return blank_terrain_symbol(ses, room, index, flags);
+					}
+				}
 
-	if (*arg3)
-	{
-		if (atoi(arg3) > 0 && atoi(arg3) < ses->map->size)
-		{
-			room = ses->map->room_list[atoi(arg3)];
+				if (room_grid[EXIT_GRID_W] == NULL)
+				{
+					switch (line * 10 + index)
+					{
+						case 21:
+						case 22:
+							return blank_terrain_symbol(ses, room, index, flags);
+					}
+				}
+			}
 		}
 		else
 		{
-			room = NULL;
-		}
-	}
-
-	if (room == NULL)
-	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "0");
-	}
-	else if (*arg1 == 0 || *arg2 == 0)
-	{
-		tintin_printf2(ses, " worldflags: %d", ses->map->flags);
-		tintin_printf2(ses, "  worldsize: %d", ses->map->size);
-		tintin_printf2(ses, "");
-		tintin_printf2(ses, "   roomvnum: %d", room->vnum);
-		tintin_printf2(ses, "   roomarea: %s", room->area);
-		tintin_printf2(ses, "  roomcolor: %s", room->color);
-		tintin_printf2(ses, "   roomdata: %s", room->data);
-		tintin_printf2(ses, "   roomdesc: %s", room->desc);
-		tintin_printf2(ses, "  roomexits: %d", get_room_exits(ses, room->vnum));
-		tintin_printf2(ses, "  roomflags: %d", room->flags);
-		tintin_printf2(ses, "     roomid: %s", room->id);
-		tintin_printf2(ses, "   roomname: %s", room->name);
-		tintin_printf2(ses, "   roomnote: %s", room->note);
-		tintin_printf2(ses, " roomsymbol: %s", room->symbol);
-		tintin_printf2(ses, "roomterrain: %s", room->terrain);
-		tintin_printf2(ses, " roomweight: %.3f", room->weight);
-	}
-	else
-	{
-		if (is_abbrev(arg1, "all"))
-		{
-			exits[0] = 0;
+			switch (line * 10 + index)
+			{
+				case 11:
+				case 12:
+					if (room_grid[EXIT_GRID_N] && hash % 4 == 0)
+					{
+						room = room_grid[EXIT_GRID_N];
+					}
+					else if (room_grid[EXIT_GRID_W] && hash % 4 == 1)
+					{
+						room = room_grid[EXIT_GRID_W];
+					}
+					else if (room_grid[EXIT_GRID_NW] && hash % 4 == 2)
+					{
+						room = room_grid[EXIT_GRID_NW];
+					}
+					break;
+				case 13:
+				case 14:
+					if (room_grid[EXIT_GRID_N] && hash % 2 == 0)
+					{
+						room = room_grid[EXIT_GRID_N];
+					}
+					break;
+				case 21:
+				case 22:
+					if (room_grid[EXIT_GRID_W] && hash % 2 == 0)
+					{
+						room = room_grid[EXIT_GRID_W];
+					}
+					break;
+			}
 
-			for (exit = room->f_exit ; exit ; exit = exit->next)
+			if (room == NULL)
 			{
-				cat_sprintf(exits, "{%s}{%d}", exit->name, exit->vnum);
+				return blank_terrain_symbol(ses, room, index, flags);
 			}
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "{area}{%s}{color}{%s}{data}{%s}{desc}{%s}{exits}{%s}{flags}{%d}{id}{%d}{name}{%s}{note}{%s}{symbol}{%s}{terrain}{%s}{vnum}{%d}{weight}{%.3f}", room->area, room->color, room->data, room->desc, exits, room->flags, room->id, room->name, room->note, room->symbol, room->terrain, room->vnum, room->weight);
 		}
-		else if (is_abbrev(arg1, "roomarea"))
+
+		terrain = room->terrain_index;
+
+		if (terrain == -1)
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", room->area);
+			return blank_terrain_symbol(ses, room, index, flags);
 		}
-		else if (is_abbrev(arg1, "roomcolor"))
+
+		terrain_room = ses->list[LIST_TERRAIN]->list[terrain]->room;
+
+		if (HAS_BIT(terrain_room->terrain_flags, TERRAIN_FLAG_DOUBLE))
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", room->color);
+			if (HAS_BIT(flags, TERRAIN_FLAG_DOUBLE))
+			{
+				if (index == 5)
+				{
+					return "\e[1;32m5";
+				}
+				if (index % 2 == 0)
+				{
+					DEL_BIT(ses->map->flags, MAP_FLAG_DOUBLED);
+					if (index == 2)
+					{
+						return " ";
+					}
+					if (index == 4)
+					{
+						return " ";
+					}
+					return "\e[1;35m?";
+				}
+				SET_BIT(ses->map->flags, MAP_FLAG_DOUBLED);
+			}
+			else
+			{
+				return blank_terrain_symbol(ses, room, index, flags);
+			}
 		}
-		else if (is_abbrev(arg1, "roomdata"))
+
+		if (room->vnum == 0)
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", room->data);
+			width++;
+
+			density = 0;
+
+			density += (room_grid[EXIT_GRID_N] && room_grid[EXIT_GRID_N]->vnum);
+			density += (room_grid[EXIT_GRID_NE] && room_grid[EXIT_GRID_NE]->vnum);
+			density += (room_grid[EXIT_GRID_E] && room_grid[EXIT_GRID_E]->vnum);
+			density += (room_grid[EXIT_GRID_SE] && room_grid[EXIT_GRID_SE]->vnum);
+			density += (room_grid[EXIT_GRID_S] && room_grid[EXIT_GRID_S]->vnum);
+			density += (room_grid[EXIT_GRID_SW] && room_grid[EXIT_GRID_SW]->vnum);
+			density += (room_grid[EXIT_GRID_W] && room_grid[EXIT_GRID_W]->vnum);
+			density += (room_grid[EXIT_GRID_NW] && room_grid[EXIT_GRID_NW]->vnum);
+
+			if (density == 0)
+			{
+				width++;
+			}
 		}
-		else if (is_abbrev(arg1, "roomdesc"))
+
+		density = get_terrain_density(terrain_room, width);
+
+		if (HAS_BIT(density, TERRAIN_FLAG_DENSE))
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", room->desc);
+			return ses->list[LIST_TERRAIN]->list[terrain]->arg2;
 		}
-		else if (is_abbrev(arg1, "roomflags"))
+
+		if (HAS_BIT(density, TERRAIN_FLAG_SPARSE))
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", room->flags);
+			if (HAS_BIT(flags, TERRAIN_FLAG_DOUBLE) && HAS_BIT(terrain_room->terrain_flags, TERRAIN_FLAG_DOUBLE))
+			{
+				if (room_grid[EXIT_GRID_0] && room_grid[EXIT_GRID_N] && room_grid[EXIT_GRID_0]->terrain_index != room_grid[EXIT_GRID_N]->terrain_index)
+				{
+					switch (line * 10 + index)
+					{
+						case 13:
+							return hash % 2 == 0 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+					}
+				}
+
+				switch (line * 10 + index)
+				{
+					case 11:
+						return hash % 2 == 1 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+
+					case 23:
+						return hash % 2 == 0 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				}
+				return blank_terrain_symbol(ses, room, index, flags);
+			}
+
+			if (room_grid[EXIT_GRID_0] && room_grid[EXIT_GRID_N] && room_grid[EXIT_GRID_0]->terrain_index != room_grid[EXIT_GRID_N]->terrain_index)
+			{
+				switch (line * 10 + index)
+				{
+					case 12:
+						return hash % 2 == 0 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+
+					case 14:
+						return hash % 2 == 1 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				}
+			}
+
+			switch (line * 10 + index)
+			{
+				case 11:
+					return hash % 5 == 0 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				case 13:
+					return hash % 5 == 2 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				case 15:
+					return hash % 5 == 4 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+
+				case 22:
+					return hash % 2 == 0 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				case 24:
+					return hash % 2 == 1 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+			}
+			return blank_terrain_symbol(ses, room, index, flags);
 		}
-		else if (is_abbrev(arg1, "roomid"))
+
+		if (HAS_BIT(density, TERRAIN_FLAG_SCANT))
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", room->id);
+			if (HAS_BIT(flags, TERRAIN_FLAG_DOUBLE) && HAS_BIT(terrain_room->terrain_flags, TERRAIN_FLAG_DOUBLE))
+			{
+				if (room_grid[EXIT_GRID_0] && room_grid[EXIT_GRID_N] && room_grid[EXIT_GRID_0]->terrain_index != room_grid[EXIT_GRID_N]->terrain_index)
+				{
+					switch (line * 10 + index)
+					{
+						case 13:
+							return hash % 4 == 2 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+					}
+				}
+
+				switch (line * 10 + index)
+				{
+					case 11:
+						return hash % 4 == 0 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+//					case 13:
+//						return hash % 8 == 1 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+//					case 21:
+//						return hash % 8 == 2 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+					case 23:
+						return hash % 4 == 1 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				}
+				return blank_terrain_symbol(ses, room, index, flags);
+			}
+
+			if (room_grid[EXIT_GRID_0] && room_grid[EXIT_GRID_N] && room_grid[EXIT_GRID_0]->terrain_index != room_grid[EXIT_GRID_N]->terrain_index)
+			{
+				switch (line * 10 + index)
+				{
+					case 12:
+						return hash % 7 == 5 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+					case 14:
+						return hash % 7 == 6 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				}
+			}
+
+			switch (line * 10 + index)
+			{
+				case 11:
+					return hash % 7 == 0 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				case 13:
+					return hash % 7 == 1 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				case 15:
+					return hash % 7 == 2 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				case 22:
+					return hash % 7 == 3 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+				case 24:
+					return hash % 7 == 4 ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+			}
+			return blank_terrain_symbol(ses, room, index, flags);
 		}
-		else if (is_abbrev(arg1, "roomname"))
+
+		if (HAS_BIT(flags, TERRAIN_FLAG_DOUBLE) && HAS_BIT(terrain_room->terrain_flags, TERRAIN_FLAG_DOUBLE))
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", room->name);
+			switch (line * 10 + index)
+			{
+				case 13:
+					return room_grid[EXIT_GRID_0] && room_grid[EXIT_GRID_N] && room_grid[EXIT_GRID_0]->terrain_index != room_grid[EXIT_GRID_N]->terrain_index ? ses->list[LIST_TERRAIN]->list[terrain]->arg2 : blank_terrain_symbol(ses, room, index, flags);
+
+				case 11:
+//					return "\e[1;31m1 ";
+				case 23:
+//					return "\e[1;34m3 ";
+					return ses->list[LIST_TERRAIN]->list[terrain]->arg2;
+			}
+			return blank_terrain_symbol(ses, room, index, flags);
 		}
-		else if (is_abbrev(arg1, "roomnote"))
+
+		switch (line * 10 + index)
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", room->note);
+			case 12:
+			case 14:
+				if (room_grid[EXIT_GRID_0] && room_grid[EXIT_GRID_N] && room_grid[EXIT_GRID_0]->terrain_index != -1 && room_grid[EXIT_GRID_0]->terrain_index != room_grid[EXIT_GRID_N]->terrain_index)
+				{
+					if (!HAS_BIT(ses->list[LIST_TERRAIN]->list[room_grid[EXIT_GRID_N]->terrain_index]->room->terrain_flags, TERRAIN_FLAG_DOUBLE))
+					{
+						return ses->list[LIST_TERRAIN]->list[room_grid[EXIT_GRID_N]->terrain_index]->arg2;
+					}
+				}
+				return blank_terrain_symbol(ses, room, index, flags);
+			case 11:
+			case 13:
+			case 15:
+			case 22:
+			case 24:
+				return ses->list[LIST_TERRAIN]->list[terrain]->arg2;
 		}
-		else if (is_abbrev(arg1, "roomsymbol"))
+		return blank_terrain_symbol(ses, room, index, flags);
+	}
+	return blank_terrain_symbol(ses, room, index, flags);
+}
+
+int follow_map(struct session *ses, char *argument)
+{
+	struct room_data *room;
+	struct exit_data *exit;;
+	int in_room, vnum;
+
+	push_call("follow_map(%p,%p)",ses,argument);
+
+	room = ses->map->room_list[ses->map->in_room];
+
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW))
+	{
+		if (check_global(ses, room->vnum) && find_exit(ses, ses->map->global_vnum, argument))
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", room->symbol);
+			in_room = ses->map->global_vnum;
 		}
-		else if (is_abbrev(arg1, "roomterrain"))
+		else
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", room->terrain);
+			in_room = ses->map->in_room;
 		}
-		else if (is_abbrev(arg1, "roomvnum"))
+		exit = find_exit(ses, in_room, argument);
+
+		if (exit)
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", room->vnum);
+			ses->map->dir = exit->dir;
+
+			vnum = tunnel_void(ses, in_room, exit->vnum, exit->dir);
+
+			check_all_events(ses, SUB_ARG, 0, 5, "MAP FOLLOW MAP", ntos(in_room), ntos(vnum), exit->name);
 		}
-		else if (is_abbrev(arg1, "roomweight"))
+		pop_call();
+		return 0;
+	}
+
+	if (check_global(ses, room->vnum))
+	{
+		if (find_exit(ses, ses->map->global_vnum, argument))
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%.3f", room->weight);
+			goto_room(ses, ses->map->global_vnum);
 		}
-		else if (is_abbrev(arg1, "roomexits"))
+	}
+
+	exit = find_exit(ses, ses->map->in_room, argument);
+
+	if (exit)
+	{
+		ses->map->dir = exit->dir;
+
+		in_room = ses->map->in_room;
+
+		vnum = tunnel_void(ses, in_room, exit->vnum, exit->dir);
+
+		if (ses->map->nofollow == 0)
 		{
-			exits[0] = 0;
+			if (HAS_BIT(exit->flags, EXIT_FLAG_BLOCK) || HAS_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_BLOCK))
+			{
+				show_error(ses, LIST_COMMAND, "#MAP FOLLOW: %s {%d} HAS THE BLOCK FLAG SET.", HAS_BIT(exit->flags, EXIT_FLAG_BLOCK) ? "EXIT" : "ROOM", vnum);
 
-			for (exit = room->f_exit ; exit ; exit = exit->next)
+				pop_call();
+				return 0;
+			}
+			else
 			{
-				cat_sprintf(exits, "{%s}{%d}", exit->name, exit->vnum);
+				ses->map->nofollow++;
+				script_driver(ses, LIST_COMMAND, exit->cmd);
+				ses->map->nofollow--;
 			}
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", exits);
-		}
-		else if (is_abbrev(arg1, "worldflags"))
-		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->map->flags);
 		}
-		else if (is_abbrev(arg1, "worldsize"))
+
+		check_all_events(ses, SUB_ARG, 0, 5, "MAP FOLLOW MAP", ntos(in_room), ntos(vnum), exit->name);
+
+		add_undo(ses, "%d %d %d", vnum, in_room, MAP_UNDO_MOVE);
+
+		goto_room(ses, vnum);
+
+		if (HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_LEAVE))
 		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->map->size);
+			show_message(ses, LIST_COMMAND, "#MAP: LEAVE FLAG FOUND IN ROOM {%d}. LEAVING MAP.", ses->map->in_room);
+
+			map_leave(ses, "", "", "");
 		}
-		else
+
+		if (HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP))
 		{
-			show_message(ses, LIST_COMMAND, "#MAP GET: unknown option: %s.", arg1);
+			SET_BIT(ses->flags, SES_FLAG_UPDATEVTMAP);
 		}
-	}
-}
 
-DO_MAP(map_global)
-{
-	int room;
-
-	sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-
-	if (*arg1 == 0)
-	{
-		show_message(ses, LIST_COMMAND, "#MAP GLOBAL: GLOBAL ROOM SET TO VNUM %d.", ses->map->global_vnum);
+		pop_call();
+		return 1;
 	}
-	else if (!strcmp(arg1, "0"))
-	{
-		ses->map->global_vnum = ses->map->global_exit->vnum = 0;
 
-		show_message(ses, LIST_COMMAND, "#MAP GLOBAL: GLOBAL ROOM SET TO VNUM %d.", 0);
-	}
-	else
+	if (!HAS_BIT(ses->map->flags, MAP_FLAG_STATIC) && !HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_STATIC))
 	{
-		room = find_room(ses, arg);
+		struct listnode *node;
 
-		if (room)
+		if ((node = search_node_list(ses->list[LIST_PATHDIR], argument)) == NULL)
 		{
-			ses->map->global_vnum = ses->map->global_exit->vnum = room;
+			pop_call();
+			return 0;
+		}
 
-			show_message(ses, LIST_COMMAND, "#MAP GLOBAL: GLOBAL ROOM SET TO VNUM %d.", room);
+		in_room = find_coord(ses, argument);
+
+		if (in_room)
+		{
+			show_message(ses, LIST_COMMAND, "#MAP CREATE LINK %5d {%s}.", in_room, ses->map->room_list[in_room]->name);
+
+			add_undo(ses, "%d %d %d", in_room, ses->map->in_room, MAP_UNDO_MOVE|MAP_UNDO_LINK);
 		}
 		else
 		{
-			show_message(ses, LIST_COMMAND, "#MAP GLOBAL: COULDN'T FIND ROOM %s.", arg1);
-		}
-	}
-}
-
-DO_MAP(map_goto)
-{
-	struct exit_data *exit;
-	int room;
-
-	room = find_room(ses, arg);
+			for (in_room = 1 ; in_room < ses->map->size ; in_room++)
+			{
+				if (ses->map->room_list[in_room] == NULL)
+				{
+					break;
+				}
+			}
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN); // look for dig argument
+			if (in_room == ses->map->size)
+			{
+				show_error(ses, LIST_COMMAND, "#MAP: Maximum amount of rooms of %d reached. Use #MAP RESIZE to increase the maximum.", ses->map->size);
 
-	if (room == 0 && ses->map->search->vnum > 0 && ses->map->search->vnum < ses->map->size && !strcasecmp(arg2, "dig"))
-	{
-		room = ses->map->search->vnum;
+				pop_call();
+				return 1;
+			}
+			add_undo(ses, "%d %d %d", in_room, ses->map->in_room, MAP_UNDO_MOVE|MAP_UNDO_CREATE|MAP_UNDO_LINK);
 
-		create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {}", room);
-	}
+			create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {}", in_room);
+		}
 
-	if (room == 0)
-	{
-		room = find_room(ses, arg1);
-	}
+		exit = create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", in_room, node->arg1, node->arg1);
 
-	if (room == 0 && ses->map->search->id && *ses->map->search->id && !strcasecmp(arg2, "dig"))
-	{
-		room = find_new_room(ses);
+		ses->map->dir = exit->dir;
 
-		if (room)
+		if (find_exit(ses, in_room, node->arg2) == NULL)
 		{
-			create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {%s}", room, ses->map->search->id);
+			create_exit(ses, in_room, "{%d} {%s} {%s}", ses->map->in_room, node->arg2, node->arg2);
 		}
-	}
-
-	if (room == 0)
-	{
-		exit = find_exit(ses, ses->map->in_room, arg1);
 
-		if (exit == NULL)
+		if (ses->map->nofollow == 0)
 		{
-			show_message(ses, LIST_COMMAND, "#MAP GOTO: COULDN'T FIND ROOM %s.", arg1);
+			ses->map->nofollow++;
 
-                        return;
-                }
-                room = exit->vnum;
-	}
+			script_driver(ses, LIST_COMMAND, argument);
 
-	add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_MOVE);
+			ses->map->nofollow--;
+		}
+		goto_room(ses, in_room);
 
-	goto_room(ses, room);
+		if (HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP))
+		{
+			SET_BIT(ses->flags, SES_FLAG_UPDATEVTMAP);
+		}
 
-	show_message(ses, LIST_COMMAND, "#MAP GOTO: MOVED TO ROOM %d {%s}.", room, *ses->map->room_list[room]->name ? ses->map->room_list[room]->name : ses->map->room_list[room]->id);
+		pop_call();
+		return 1;
+	}
+	pop_call();
+	return 0;
 }
 
-DO_MAP(map_info)
+void add_undo(struct session *ses, char *format, ...)
 {
-	int room, cnt, exits;
-	struct exit_data *exit;
-	struct room_data *in_room = ses->map->room_list[ses->map->in_room];
+	struct link_data *link;
+	char buf[BUFFER_SIZE], *arg, dir[BUFFER_SIZE], rev[BUFFER_SIZE], flag[BUFFER_SIZE];
+	va_list args;
 
-	for (room = cnt = exits = 0 ; room < ses->map->size ; room++)
-	{
-		if (ses->map->room_list[room])
-		{
-			cnt++;
+	push_call("add_undo(%s,%s)",ses->name, format);
 
-			exits += get_room_exits(ses, room);
-		}
-	}
+	va_start(args, format);
+	vsprintf(buf, format, args);
+	va_end(args);
 
+	arg = get_arg_in_braces(ses, buf, dir, GET_ONE);
+	arg = get_arg_in_braces(ses, arg, rev, GET_ONE);
+	arg = get_arg_in_braces(ses, arg, flag, GET_ONE);
 
-	tintin_printf2(ses, " %+16s %-7d %+16s %-7d %+16s %-7d", "Total rooms:", cnt, "Total exits:", exits, "World size:", ses->map->size);
-	tintin_printf2(ses, " %+16s %-7d %+16s %-7d %+16s %-7d",  "Direction:", ses->map->dir, "Last room:", ses->map->last_room, "Undo size:", ses->map->undo_size);
-	tintin_printf2(ses, "");
+	link = (struct link_data *) calloc(1, sizeof(struct link_data));
 
-	strcpy(arg1, "");
-	cat_sprintf(arg1, " %+16s %-7s", "AsciiGraphics:", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS) ? "ON" : "off");
-	cat_sprintf(arg1, " %+16s %-7s", "AsciiVnums:", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS) ? "ON" : "off");
-	cat_sprintf(arg1, " %+16s %-7s", "BlockGraphics:", HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS) ? "ON" : "off");
-	tintin_puts2(ses, arg1);
+	link->str1 = strdup(dir);
+	link->str2 = strdup(rev);
+	link->str3 = strdup(flag);
 
-	strcpy(arg1, "");
-	cat_sprintf(arg1, " %+16s %-7s", "Direction:", HAS_BIT(ses->map->flags, MAP_FLAG_DIRECTION) ? "ON" : "OFF");
-	cat_sprintf(arg1, " %+16s %-7s", "MudFont:", HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT) ? "ON" : "off");
-	cat_sprintf(arg1, " %+16s %-7s", "Nofollow:", HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW) ? "ON" : "off");
-	tintin_puts2(ses, arg1);
+	LINK(link, ses->map->undo_head, ses->map->undo_tail);
 
-	strcpy(arg1, "");
-	cat_sprintf(arg1, " %+16s %-7s", "Static:", HAS_BIT(ses->map->flags, MAP_FLAG_STATIC) ? "ON" : "off");
-	cat_sprintf(arg1, " %+16s %-7s", "SymbolGraphics:", HAS_BIT(ses->map->flags, MAP_FLAG_SYMBOLGRAPHICS) ? "ON" : "off");
-	cat_sprintf(arg1, " %+16s %-7s", "UnicodeGraphics:", HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) ? "ON" : "off");
-	tintin_puts2(ses, arg1);
+	ses->map->undo_size++;
 
-	strcpy(arg1, "");
-	cat_sprintf(arg1, " %+16s %-7s", "Vtmap:", HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP) ? "ON" : "off");
-	tintin_puts2(ses, arg1);
-/*
-	strcpy(arg1, "");
-	cat_sprintf(arg1, " %+16s %-7s",
-	cat_sprintf(arg1, " %+16s %-7s",
-	cat_sprintf(arg1, " %+16s %-7s",
-	tintin_puts2(ses, arg1);
-*/
-	if (ses->map->in_room == 0)
+	if (ses->map->undo_size > 100)
 	{
-		return;
+		del_undo(ses, ses->map->undo_head);
 	}
+	pop_call();
+	return;
+}
 
-	tintin_printf2(ses, "");
+void del_undo(struct session *ses, struct link_data *link)
+{
+	UNLINK(link, ses->map->undo_head, ses->map->undo_tail);
 
-	tintin_printf2(ses, "%+16s %s", "Room area:",    in_room->area);
-	tintin_printf2(ses, "%+16s %s", "Room data:",    in_room->data);
-	tintin_printf2(ses, "%+16s %s", "Room desc:",    in_room->desc);
-	tintin_printf2(ses, "%+16s %s", "Room id:",      in_room->id);
-	tintin_printf2(ses, "%+16s %s", "Room name:",    in_room->name);
-	tintin_printf2(ses, "%+16s %s", "Room note:",    in_room->note);
-	tintin_printf2(ses, "%+16s %s", "Room color:",   str_convert_meta(in_room->color, TRUE));
-	tintin_printf2(ses, "%+16s %s", "Room terrain:", in_room->terrain);
+	free(link->str1);
+	free(link->str2);
+	free(link->str3);
 
-	tintin_printf2(ses, "");
-	tintin_printf2(ses, " %+16s %-7d %+16s %-7.3f %+16s %-7s", "Room vnum:", ses->map->in_room, "Room weight:", in_room->weight, "Room symbol:", in_room->symbol);
+	free(link);
 
-	tintin_printf2(ses, " %+16s %-7d %+16s %-7d %+16s %-7d",   "Center x:", ses->map->center_x, "Center y:", ses->map->center_y, "Center z:", ses->map->center_z);
+	ses->map->undo_size--;
+}
 
-	tintin_printf2(ses, "");
+/*
+	Draws a map on a grid, original breadth first improvements by Bryan Turner
+*/
 
-	strcpy(arg1, "");
-	cat_sprintf(arg1, " %+16s %-7s",    "Avoid:", HAS_BIT(in_room->flags, ROOM_FLAG_AVOID)    ? "on" : "off");
-	cat_sprintf(arg1, " %+16s %-7s",   "Curved:", HAS_BIT(in_room->flags, ROOM_FLAG_CURVED)   ? "on" : "off");
-	cat_sprintf(arg1, " %+16s %-7s",     "Hide:", HAS_BIT(in_room->flags, ROOM_FLAG_HIDE)     ? "on" : "off");
 
-	tintin_puts2(ses, arg1);
 
-	strcpy(arg1, "");
-	cat_sprintf(arg1, " %+16s %-7s",    "Invis:", HAS_BIT(in_room->flags, ROOM_FLAG_INVIS)    ? "on" : "off");
-	cat_sprintf(arg1, " %+16s %-7s",    "Leave:", HAS_BIT(in_room->flags, ROOM_FLAG_LEAVE)    ? "on" : "off");
-	cat_sprintf(arg1, " %+16s %-7s", "NoGlobal:", HAS_BIT(in_room->flags, ROOM_FLAG_NOGLOBAL) ? "on" : "off");
-	tintin_puts2(ses, arg1);
+struct grid_node
+{
+	int vnum;
+	int from;
+	int flags;
+	float length;
+	int dir;
+	int w;
+	int x;
+	int y;
+	int z;
+};
 
-	strcpy(arg1, "");
-	cat_sprintf(arg1, " %+16s %-7s",   "Static:", HAS_BIT(in_room->flags, ROOM_FLAG_STATIC)   ? "on" : "off");
-	cat_sprintf(arg1, " %+16s %-7s",     "Void:", HAS_BIT(in_room->flags, ROOM_FLAG_VOID)     ? "on" : "off");
-	tintin_puts2(ses, arg1);
+void displaygrid_build(struct session *ses, int vnum, int x, int y, int z)
+{
+	int head, tail;
+	struct grid_node *node, *temp, list[MAP_BF_SIZE];
+	struct exit_data *exit;
+	struct room_data *room, *toroom;
 
-	tintin_printf2(ses, "");
+	push_call("displaygrid_build(%p,%d,%d,%d,%d)",ses,vnum,x,y,z);
 
-	for (exit = in_room->f_exit ; exit ; exit = exit->next)
-	{
-		tintin_printf2(ses, "%+16s %-3s (%3s)   to room: %-5d (%5s)", "Exit:", exit->name, exit->cmd, exit->vnum, ses->map->room_list[exit->vnum]->name);
-	}
+	map_grid_x = x;
+	map_grid_y = y;
 
-	tintin_printf2(ses, "");
+	head = 0;
+	tail = 1;
 
-	for (room = 0 ; room < ses->map->size ; room++)
+	node = &list[head];
+
+	node->vnum   = vnum;
+	node->length = ses->map->room_list[vnum]->weight;
+	node->flags  = 0;
+
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS))
 	{
-		if (ses->map->room_list[room])
-		{
-			for (exit = ses->map->room_list[room]->f_exit ; exit ; exit = exit->next)
-			{
-				if (exit->vnum == ses->map->in_room)
-				{
-					tintin_printf2(ses, "%+16s %-3s (%3s) from room: %-5d (%5s)", "Entrance:", exit->name, exit->cmd, room, ses->map->room_list[room]->name);
-				}
-			}
-		}
+		node->x      = (x - 2) / 2 + ses->map->center_x;
 	}
-/*
-	for (exit = in_room->f_exit ; exit ; exit = exit->next)
+	else
 	{
-		tintin_printf2(ses, "%+14s %-4s %+14s %5d %+14s %5d %+14s %5s", "Exit name:", exit->name, "vnum:", exit->vnum, "flags:", exit->flags, "command:", exit->cmd);
-		tintin_printf2(ses, "%+14s %s", "Exit data:", exit->data);
+		node->x      = x / 2 + ses->map->center_x;
 	}
-*/
-}
-
-DO_MAP(map_insert)
-{
-	int room, in_room, to_room;
-	struct exit_data *exit;
-	struct listnode *node;
-
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	node->y      = y / 2 + ses->map->center_y;
+	node->z      = z / 2 + ses->map->center_z;
 
-	for (room = 1 ; room < ses->map->size ; room++)
-	{
-		if (ses->map->room_list[room] == NULL)
-		{
-			break;
-		}
-	}
 
-	exit = find_exit(ses, ses->map->in_room, arg1);
 
-	node = search_node_list(ses->list[LIST_PATHDIR], arg1);
+	ses->map->display_stamp++;
 
-	if (exit == NULL)
+	for (vnum = 0 ; vnum < x * y ; vnum++)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP: There is no room in that direction.");
-
-		return;
+		ses->map->grid_rooms[vnum] = NULL;
 	}
 
-	if (room == ses->map->size)
+	while (head != tail)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP: Maximum amount of rooms of %d reached.", ses->map->size);
-		return;
-	}
+		node = &list[head];
 
-	if (node == NULL)
-	{
-		show_error(ses, LIST_COMMAND, "#MAP: Given direction must be a pathdir.");
-		return;
-	}
+		head = (head + 1) % MAP_BF_SIZE;
 
-	in_room = ses->map->in_room;
-	to_room = exit->vnum;
+		room = ses->map->room_list[node->vnum];
 
-	add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_INSERT);
+		if (ses->map->display_stamp != room->display_stamp)
+		{
+			room->display_stamp = ses->map->display_stamp;
+		}
+		else if (room->length <= node->length /*&& !HAS_BIT(room->flags, ROOM_FLAG_VOID)*/)
+		{
+			continue;
+		}
 
-	create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {}", room);
+		room->length = node->length;
 
-	create_exit(ses, room, "{%d} {%s} {%s}", to_room, node->arg1, node->arg1);
+		if (node->x >= 0 && node->x < map_grid_x && node->y >= 0 && node->y < map_grid_y && node->z == 0)
+		{
+			if (ses->map->grid_rooms[node->x + map_grid_x * node->y] == NULL)
+			{
+				ses->map->grid_rooms[node->x + map_grid_x * node->y] = room;
+			}
+			else if (!HAS_BIT(room->flags, ROOM_FLAG_VOID))
+			{
+				continue;
+			}
+		}
 
-	create_exit(ses, room, "{%d} {%s} {%s}", in_room, node->arg2, node->arg2);
+		for (exit = room->f_exit ; exit ; exit = exit->next)
+		{
+			if (exit->dir == 0)
+			{
+				continue;
+			}
 
-	exit->vnum = room;
+			toroom = ses->map->room_list[exit->vnum];
 
-	if ((exit = find_exit(ses, to_room, node->arg2)) != NULL)
-	{
-		exit->vnum = room;
-	}
+			if (ses->map->display_stamp == toroom->display_stamp)
+			{
+				if (HAS_BIT(room->flags, ROOM_FLAG_VOID) || !HAS_BIT(toroom->flags, ROOM_FLAG_VOID))
+				{
+					if (room->length >= ses->map->room_list[exit->vnum]->length)
+					{
+						continue;
+					}
+				}
+			}
 
-	if (*arg)
-	{
-		ses->map->in_room = room;
-		map_roomflag(ses, arg, arg1, arg2);
-		ses->map->in_room = in_room;
-	}
-	show_message(ses, LIST_COMMAND, "#MAP: Inserted room {%d}.", room);
-}
+			if (HAS_BIT(exit->flags, EXIT_FLAG_HIDE) || HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_HIDE))
+			{
+				continue;
+			}
 
-DO_MAP(map_jump)
-{
-	char arg3[BUFFER_SIZE];
-	int room, x, y, z;
+			if (head == (tail + 1) % MAP_BF_SIZE)
+			{
+				break;
+			}
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
+			if (HAS_BIT(room->flags, ROOM_FLAG_VOID) && ses->map->in_room != room->vnum && get_room_exits(ses, room->vnum) % 2 == 0)
+			{
+				exit = room->exit_grid[dir_to_grid(node->dir)];
+			
+				if (exit == NULL)
+				{
+					if (ses->map->room_list[room->f_exit->vnum]->display_stamp != ses->map->display_stamp)
+					{
+						exit = room->f_exit;
+					}
+					else if (ses->map->room_list[room->l_exit->vnum]->display_stamp != ses->map->display_stamp)
+					{
+						exit = room->l_exit;
+					}
+					else if (ses->map->room_list[room->f_exit->vnum]->length <= ses->map->room_list[room->l_exit->vnum]->length)
+					{
+						exit = room->f_exit;
+					}
+					else
+					{
+						exit = room->l_exit;
+					}
+				}
+			}
 
-	x = get_number(ses, arg1);
-	y = get_number(ses, arg2);
-	z = get_number(ses, arg3);
+			temp = &list[tail];
 
-	room = spatialgrid_find(ses, ses->map->in_room, x, y, z);
+			temp->vnum   = exit->vnum;
+			temp->dir    = exit->dir;
+			temp->x      = node->x + (HAS_BIT(exit->dir, MAP_EXIT_E) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_W) ? -1 : 0);
+			temp->y      = node->y + (HAS_BIT(exit->dir, MAP_EXIT_N) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_S) ? -1 : 0);
+			temp->z      = node->z + (HAS_BIT(exit->dir, MAP_EXIT_U) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_D) ? -1 : 0);
+			temp->length = node->length + exit->weight + ses->map->room_list[exit->vnum]->weight;
 
-	if (room)
-	{
-		add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_MOVE);
+			temp->flags  = 0;
 
-		goto_room(ses, room);
+			tail = (tail + 1) % MAP_BF_SIZE;
 
-		show_message(ses, LIST_COMMAND, "#MAP JUMP: JUMPED TO ROOM %d {%s}.", room, *ses->map->room_list[room]->name ? ses->map->room_list[room]->name : ses->map->room_list[room]->id);
-	}
-	else
-	{
-		show_message(ses, LIST_COMMAND, "#MAP JUMP: Couldn't find a room at {%d} {%d} {%d}.", x, y, z);
+			if (HAS_BIT(room->flags, ROOM_FLAG_VOID) && ses->map->in_room != room->vnum && get_room_exits(ses, room->vnum) % 2 == 0)
+			{
+				temp->length = node->length + 0.000001;
+				break;
+			}
+		}
 	}
-}
 
-DO_MAP(map_leave)
-{
-	if (ses->map->in_room == 0)
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_UPDATETERRAIN))
 	{
-		show_error(ses, LIST_COMMAND, "#MAP: You're not currently inside the map.");
+		update_terrain(ses);
 	}
-	else
+
 	{
-		ses->map->last_room = ses->map->in_room;
-		ses->map->in_room = 0;
+		int terrain;
 
-		show_message(ses, LIST_COMMAND, "#MAP: Leaving the map. Use goto or return to return.");
+		for (y = map_grid_y - 2 ; y >= 1 ; y--)
+		{
+			for (x = 1 ; x < map_grid_x - 1 ; x++)
+			{
+				if (ses->map->grid_rooms[x + map_grid_x * y] == NULL)
+				{
+					terrain = get_terrain_index(ses, NULL, x, y);
 
-		check_all_events(ses, SUB_ARG|SUB_SEC, 0, 1, "MAP EXIT MAP", ntos(ses->map->in_room));
+					if (terrain != -1)
+					{
+						ses->map->grid_rooms[x + map_grid_x * y] = ses->list[LIST_TERRAIN]->list[terrain]->room;
+					}
+				}
+			}
+		}
 	}
+
+	pop_call();
+	return;
 }
 
-void map_legend_index(struct session *ses, char *arg, int head, int tail)
+
+void show_vtmap(struct session *ses)
 {
-	char esc[BUFFER_SIZE], raw[BUFFER_SIZE];
-	int cnt;
+	char buf[BUFFER_SIZE], out[BUFFER_SIZE], tmp[BUFFER_SIZE];
+	int x, y, line;
+	int top_row, top_col, bot_row, bot_col, rows, cols, row;
 
-	for (cnt = head ; cnt < tail ; cnt++)
-	{
-		arg = sub_arg_in_braces(ses, arg, raw, GET_ONE, SUB_NONE);
+	push_call("show_vtmap(%p)",ses);
 
-		substitute(ses, raw, esc, SUB_ESC);
+	if (ses->map == NULL || !HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP))
+	{
+		pop_call();
+		return;
+	}
 
-		if (is_number(esc))
-		{
-			numbertocharacter(ses, esc);
-		}
-		snprintf(ses->map->legend[cnt],     LEGEND_SIZE - 1, "%s", esc);
-		snprintf(ses->map->legend_raw[cnt], LEGEND_SIZE - 1, "%s", raw);
-
-		if (*arg == 0)
-		{
-			break;
-		}
-	}
-	return;
-}
-
-DO_MAP(map_legend)
-{
-	char buf[BUFFER_SIZE], arg3[BUFFER_SIZE];
-	int group, legend;
-
-	push_call("map_legend(%p,%p,%p,%p)",ses,arg,arg1,arg2);
-
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
-
-	strcpy(buf, arg2);
-
-	if (*arg1 == 0)
+	if (ses->map->room_list[ses->map->in_room] == NULL)
 	{
-		for (group = 0 ; map_group_table[group].name ; group++)
-		{
-			tintin_printf2(ses, "  [%-22s]  [%-22s]  [%3d]  [%3d]",
-				map_group_table[group].group,
-				map_group_table[group].name,
-				map_group_table[group].start,
-				map_group_table[group].end);
-		}
 		pop_call();
 		return;
 	}
 
-	if (is_abbrev(arg1, "RESET"))
+	if (ses != gtd->ses || HAS_BIT(gtd->ses->flags, SES_FLAG_READMUD))
 	{
-		map_legend(ses, "{ASCII} {RESET}", arg1, arg2);
-		map_legend(ses, "{NESW} {RESET}", arg1, arg2);
-		map_legend(ses, "{MUDFONT BRAILLE TUBE} {RESET}", arg1, arg2);
-
 		pop_call();
 		return;
 	}
 
-	if (is_math(ses, arg1))
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_RESIZE))
 	{
-		legend = (int) get_number(ses, arg1);
+		DEL_BIT(ses->map->flags, MAP_FLAG_RESIZE);
 
-		if (legend < map_group_table[0].start || legend >= map_group_table[0].end)
-		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP LEGEND {%d - %d} {[SYMBOL]}", map_group_table[0].start,  map_group_table[0].end);
-		}
-		else if (*arg2 == 0)
-		{
-			tintin_printf2(ses, "  [%-22s]  [%-20s]  [%3d]  [ %8s ]  [ %s ]",
-				map_legend_table[legend].group,
-				map_legend_table[legend].name,
-				legend,
-				ses->map->legend_raw[legend],
-				ses->map->legend[legend]);
-		}
-		else
-		{
-			map_legend_index(ses, arg2, legend, legend + 1);
-		}
-		pop_call();
-		return;
+		map_offset(ses, NULL, "", "");
 	}
 
-	for (group = 0 ; map_group_table[group].name ; group++)
+	if (ses->map->rows > 1 && ses->map->cols > 1)
 	{
-		if (is_abbrev(arg1, map_group_table[group].name))
-		{
-			break;
-		}
+		top_row = ses->map->top_row;
+		top_col = ses->map->top_col;
+		bot_row = ses->map->bot_row;
+		bot_col = ses->map->bot_col;
+		rows    = ses->map->rows;
+		cols    = ses->map->cols;
+	}
+	else
+	{
+		top_row = 1;
+		top_col = 1;
+		bot_row = ses->split->top_row - 2;
+		bot_col = gtd->screen->cols;
+
+		rows    = ses->split->top_row - 2;
+		cols    = gtd->screen->cols;
 	}
 
+//	print_stdout("\e[%d;%d;%d;%d${", top_row, top_col, bot_row, bot_col);
 
-	if (!map_group_table[group].name)
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
 	{
-		show_error(ses, LIST_COMMAND, "#MAP LEGEND: UNKNOWN LEGEND {%s} TRY:", arg1);
-
-		map_legend(ses, "", arg1, arg2);
+		erase_square(ses, top_row, top_col, bot_row, bot_col);
 
-		pop_call();
-		return;
+		map_grid_y = 2 + rows / 3;
+		map_grid_x = 2 + cols / 6;
 	}
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS))
+	{
+		erase_square(ses, top_row, top_col, bot_row - 1, bot_col);
 
-	if (*arg2 == 0)
+		map_grid_y = 2 + (rows + 2) / 2;
+		map_grid_x = 2 + (cols + 4) / 5;
+	}
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
 	{
-		for (legend = map_group_table[group].start ; legend < map_group_table[group].end ; legend++)
-		{
-			tintin_printf2(ses, "  [%-22s]  [%-20s]  [%3d]  [ %8s ]  [ %s ]",
-				map_group_table[group].name,
-				map_legend_table[legend].name,
-				legend,
-				ses->map->legend_raw[legend],
-				ses->map->legend[legend]);
-		}
-		pop_call();
-		return;
+		erase_square(ses, top_row, top_col, bot_row, bot_col);
+
+		map_grid_y = 2 + rows / 2;
+		map_grid_x = 2 + cols / 5;
 	}
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
+	{
+		erase_square(ses, top_row, top_col, bot_row, bot_col);
 
-	if (is_abbrev(arg2, "RESET"))
+		map_grid_y = 2 + rows;
+		map_grid_x = 2 + cols / 2;
+	}
+	else
 	{
-		map_legend_index(ses, map_group_table[group].reset, map_group_table[group].start, map_group_table[group].end);
+		erase_square(ses, top_row, top_col, bot_row, bot_col);
 
-		pop_call();
-		return;
+		map_grid_y = 2 + rows;
+		map_grid_x = 2 + cols;
 	}
 
-	for (legend = map_group_table[group].start ; legend < map_group_table[group].end ; legend++)
+	displaygrid_build(ses, ses->map->in_room, map_grid_x, map_grid_y, 0);
+
+	save_pos(ses);
+
+	goto_pos(ses, top_row, 1);
+
+	row = top_row;
+
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
 	{
-		if (!strcasecmp(space_out(arg2), space_out(map_legend_table[legend].name)))
+		for (y = map_grid_y - 2 ; y >= 1 ; y--)
 		{
-			if (*arg3 == 0)
-			{
-				tintin_printf2(ses, "  [%-22s]  [%-20s]  [%3d]  [ %8s ]  [ %s ]",
-					map_group_table[group].name,
-					map_legend_table[legend].name,
-					legend,
-					ses->map->legend_raw[legend],
-					ses->map->legend[legend]);
-			}
-			else
+			for (line = 1 ; line <= 3 ; line++)
 			{
-				map_legend_index(ses, arg3, legend, legend + 1);
+				strcpy(buf, ses->map->color[MAP_COLOR_BACK]);
+
+				for (x = 1 ; x < map_grid_x - 1 ; x++)
+				{
+					strcat(buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], line, x, y));
+				}
+				substitute(ses, buf, out, SUB_COL|SUB_CMP|SUB_LIT);
+
+				print_stdout("\e[%d;%dH%s\e[0m", row++, top_col, out);
 			}
-			break;
 		}
 	}
-
-	if (legend == map_group_table[group].end)
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS))
 	{
-		for (legend = map_group_table[group].start ; legend < map_group_table[group].end ; legend++)
+		for (y = map_grid_y - 2 ; y >= 1 ; y--)
 		{
-			if (is_abbrev(space_out(arg2), space_out(map_legend_table[legend].name)))
+			for (line = 1 ; line <= 2 ; line++)
 			{
-				if (*arg3 == 0)
+				if (line == 2 && y == 1)
 				{
-					tintin_printf2(ses, "  [%-22s]  [%-20s]  [%3d]  [ %8s ]  [ %s ]",
-						map_group_table[group].name,
-						map_legend_table[legend].name,
-						legend,
-						ses->map->legend_raw[legend],
-						ses->map->legend[legend]);
+					continue;
 				}
-				else
+				strcpy(buf, ses->map->color[MAP_COLOR_BACK]);
+
+				for (x = 1 ; x < map_grid_x - 1 ; x++)
 				{
-					map_legend_index(ses, arg3, legend, legend + 1);
+					if (x == map_grid_x - 2)
+					{
+						strcpy(tmp, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], line, x, y));
+
+						cat_sprintf(buf, "%.*s", string_str_raw_len(ses, tmp, 0, 2), tmp);
+					}
+					else
+					{
+						strcat(buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], line, x, y));
+					}
 				}
-				break;
+				substitute(ses, buf, out, SUB_COL|SUB_CMP|SUB_LIT);
+
+				print_stdout("\e[%d;%dH%s\e[0m", row++, top_col, out);
 			}
 		}
 	}
-
-
-	if (legend == map_group_table[group].end)
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
 	{
-		if (strlen(arg2) > (map_group_table[group].end - map_group_table[group].start) * 2)
+		for (y = map_grid_y - 2 ; y >= 1 ; y--)
 		{
-			map_legend_index(ses, arg2, map_group_table[group].start, map_group_table[group].end);
+			for (line = 1 ; line <= 2 ; line++)
+			{
+				strcpy(buf, ses->map->color[MAP_COLOR_BACK]);
+
+				for (x = 1 ; x < map_grid_x - 1 ; x++)
+				{
+					strcat(buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], line, x, y));
+				}
+				substitute(ses, buf, out, SUB_COL|SUB_CMP|SUB_LIT);
+
+				print_stdout("\e[%d;%dH%s\e[0m", row++, top_col, out);
+			}
 		}
-		else
+	}
+	else
+	{
+		for (y = map_grid_y - 2 ; y >= 1 ; y--)
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP LEGEND {%s} {{arg %d} {arg %d} ... {arg %d} {arg %d}",
-				map_group_table[group].group,
-				map_group_table[group].start,
-				map_group_table[group].start - 1,
-				map_group_table[group].end - 1,
-				map_group_table[group].end);
+			strcpy(buf, ses->map->color[MAP_COLOR_BACK]);
+
+			for (x = 1 ; x < map_grid_x - 1 ; x++)
+			{
+				strcat(buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], 0, x, y));
+			}
+			substitute(ses, buf, out, SUB_COL|SUB_CMP|SUB_LIT);
+
+			print_stdout("\e[%d;%dH%s\e[0m", row++, top_col, out);
 		}
 	}
 
+	restore_pos(ses);
+
 	pop_call();
 	return;
 }
 
-DO_MAP(map_link)
-{
-	char arg3[BUFFER_SIZE];
-	struct listnode *node;
-	struct exit_data *exit;
-	int room;
-
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+// http://shapecatcher.com/unicode/block/Mathematical_Operators diagonal #⊥⊤#
 
-	if (*arg1 == 0 || *arg2 == 0)
-	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP LINK {<DIRECTION>} {<LOCATION>} {BOTH}");
-		return;
-	}
+char *draw_room(struct session *ses, struct room_data *room, int line, int x, int y)
+{
+	static char buf[201], *room_color, room_left[101], room_right[101];
+	int index, symsize = 1, exits, exit1, exit2, room1, room2, offset, flags = 0;
+//	struct room_data * room_grid[11];
 
-	room = find_room(ses, arg2);
+	push_call("draw_room(%p,%p,%d,%d,%d)",ses,room,line,x,y);
 
-	if (room == 0)
-	{
-		show_error(ses, LIST_COMMAND, "#MAP: Couldn't find room {%s}.", arg1);
-		return;
-	}
+	offset = HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) ? LEGEND_UNICODE : LEGEND_ASCII;
 
-	exit = find_exit(ses, ses->map->in_room, arg1);
+	room_color = ses->map->color[MAP_COLOR_ROOM];
 
-	if (exit)
+	if (room && room->vnum)
 	{
-		delete_exit(ses, ses->map->in_room, exit);
-	}
-
-	create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", room, arg1, arg1);
+		symsize = strip_color_strlen(ses, room->symbol);
 
-	if (is_abbrev(arg3, "both"))
-	{
-		if ((node = search_node_list(ses->list[LIST_PATHDIR], arg1)) != NULL)
+		if (HAS_BIT(room->flags, ROOM_FLAG_PATH) && room->search_stamp == ses->map->search->stamp)
 		{
-			if (find_exit(ses, room, node->arg2) == NULL)
+			room_color = ses->map->color[MAP_COLOR_PATH];
+		}
+		else if (*room->color)
+		{
+			room_color = room->color;
+		}
+		else
+		{
+			if (HAS_BIT(room->flags, ROOM_FLAG_INVIS))
 			{
-				create_exit(ses, room, "{%d} {%s} {%s}", ses->map->in_room, node->arg2, node->arg2);
+				room_color = ses->map->color[MAP_COLOR_INVIS];
+			}
+			else if (HAS_BIT(room->flags, ROOM_FLAG_HIDE))
+			{
+				room_color = ses->map->color[MAP_COLOR_HIDE];
+			}
+			else if (HAS_BIT(room->flags, ROOM_FLAG_AVOID))
+			{
+				room_color = ses->map->color[MAP_COLOR_AVOID];
+			}
+			else if (HAS_BIT(room->flags, ROOM_FLAG_BLOCK))
+			{
+				room_color = ses->map->color[MAP_COLOR_BLOCK];
+			}
+			else if (HAS_BIT(ses->map->flags, MAP_FLAG_SYMBOLGRAPHICS))
+			{
+				room_color = ses->map->color[MAP_COLOR_SYMBOL];
 			}
 		}
-	}
-	show_message(ses, LIST_COMMAND, "#MAP LINK: Connected room {%s} to {%s}.", ses->map->room_list[ses->map->in_room]->name, ses->map->room_list[room]->name);
-}
-
-DO_MAP(map_list)
-{
-	struct room_data *room;
-	char var[BUFFER_SIZE];
-	int vnum;
-
-	map_search_compile(ses, "0", var);
 
-	searchgrid_find(ses, ses->map->in_room, ses->map->search);
+		if (room->vnum == ses->map->in_room)
+		{
+			if (HAS_BIT(ses->map->flags, MAP_FLAG_DIRECTION))
+			{
+				exits = ses->map->dir;
 
-	map_search_compile(ses, arg, var);
+				DEL_BIT(exits, MAP_EXIT_U|MAP_EXIT_D);
 
-	set_nest_node(ses->list[LIST_VARIABLE], var, "");
+				switch (exits)
+				{
+					case MAP_EXIT_N:
+						index = LEGEND_ASCII_DIRS + 0;
+						break;
+					case MAP_EXIT_N+MAP_EXIT_E:
+						index = LEGEND_ASCII_DIRS + 1;
+						break;
+					case MAP_EXIT_E:
+						index = LEGEND_ASCII_DIRS + 2;
+						break;
+					case MAP_EXIT_S+MAP_EXIT_E:
+						index = LEGEND_ASCII_DIRS + 3;
+						break;
+					case MAP_EXIT_S:
+						index = LEGEND_ASCII_DIRS + 4;
+						break;
+					case MAP_EXIT_W+MAP_EXIT_S:
+						index = LEGEND_ASCII_DIRS + 5;
+						break;
+					case MAP_EXIT_W:
+						index = LEGEND_ASCII_DIRS + 6;
+						break;
+					case MAP_EXIT_W+MAP_EXIT_N:
+						index = LEGEND_ASCII_DIRS + 7;
+						break;
+					default:
+						index = LEGEND_ASCII_MISC + 1;
+						break;
+				}
+			}
+			else
+			{
+				index = LEGEND_ASCII_MISC + 0;
+			}
+		}
+	}
 
-	for (vnum = 0 ; vnum < ses->map->size ; vnum++)
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS))
 	{
-		if (match_room(ses, vnum, ses->map->search))
-		{
-			room = ses->map->room_list[vnum];
+		struct room_data *room_n, *room_nw, *room_w;
+		long long exit_n, exit_nw, exit_w;
 
-			if (*var)
+		if (room)
+		{
+			if (HAS_BIT(room->flags, ROOM_FLAG_CURVED))
 			{
-				add_nest_node(ses->list[LIST_VARIABLE], var, "{%d} {{distance}{%.3f}{x}{%d}{y}{%d}{z}{%d}}", room->vnum, ses->map->search->stamp == room->search_stamp ? room->length  : -1, room->x, room->y, room->z);
+				sprintf(room_left,  "%s%s", room_color, ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_RL_CURVED]);
+				sprintf(room_right, "%s%s", room_color, ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_RR_CURVED]);
 			}
 			else
 			{
-				if (ses->map->search->stamp == room->search_stamp)
-				{
-					if (room->w == 0)
-					{
-						tintin_printf2(ses, "vnum: %5d  dist: %8.3f  x: %4d  y: %4d  z: %4d  name: %s", room->vnum, room->length, room->x, room->y, room->z, room->name);
-					}
-					else
-					{
-						tintin_printf2(ses, "vnum: %5d  dist: %8.3f  x: %4s  y: %4s  z: %4s  name: %s", room->vnum, room->length, "?", "?", "?", room->name);
-					}
-				}
-				else
-				{
-						tintin_printf2(ses, "vnum: %5d  dist: %8.8s  x:    ?  y:    ?  z:    ?  name: %s", room->vnum, "-1", room->name);
-				}
+				
+				sprintf(room_left,  "%s%s", room_color, ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_RL]);
+				sprintf(room_right, "%s%s", room_color, ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_RR]);
 			}
 		}
-	}
-}
+		room_n  = ses->map->grid_rooms[x + 0 + map_grid_x * (y + 1)];
+		room_nw = ses->map->grid_rooms[x - 1 + map_grid_x * (y + 1)];
+		room_w  = ses->map->grid_rooms[x - 1 + map_grid_x * (y + 0)];
 
-DO_MAP(map_map)
-{
-	char arg3[BUFFER_SIZE], arg4[BUFFER_SIZE];
-	FILE *logfile = NULL;
-	int x, y, line, row;
+		exit_n = exit_nw = exit_w = 0;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg4, GET_ALL, SUB_VAR|SUB_FUN);
+		if (room_nw && room_nw->exit_grid[EXIT_GRID_SE])
+		{
+			SET_BIT(exit_nw, UNICODE_DIR_SE);
+		}
 
-	push_call("map_map(%p,%p)",ses,arg);
+		if (room_w && room_w->exit_grid[EXIT_GRID_NE])
+		{
+			SET_BIT(exit_nw, UNICODE_DIR_NE);
+		}
 
-	if (is_math(ses, arg1))
-	{
-		map_grid_y = get_number(ses, arg1);
+		if (room_n && room_n->exit_grid[EXIT_GRID_SW])
+		{
+			SET_BIT(exit_nw, UNICODE_DIR_SW);
+		}
 
-		if (map_grid_y <= 0)
+		if (room && HAS_BIT(room->exit_dirs, MAP_DIR_NW))
 		{
-			map_grid_y = UMAX(0, get_scroll_rows(ses) + map_grid_y);
+			SET_BIT(exit_nw, UNICODE_DIR_NW);
 		}
-	}
-	else
-	{
-		map_grid_y = get_scroll_rows(ses);
-	}
 
-	if (is_math(ses, arg2))
-	{
-		map_grid_x = get_number(ses, arg2);
 
-		if (map_grid_x <= 0)
+		if (room_n && HAS_BIT(room_n->exit_dirs, MAP_DIR_D))
 		{
-			map_grid_x = UMAX(0, gtd->screen->cols + map_grid_x);
+			SET_BIT(exit_n, MAP_DIR_D);
 		}
-	}
-	else
-	{
-		map_grid_x = get_scroll_cols(ses);
-	}
 
-	if (*arg3)
-	{
-		switch (*arg3)
+		if (room && room->exit_grid[EXIT_GRID_N])
 		{
-			case 'a':
-			case 'A':
-
-				strcpy(arg3, "APPEND");
+			SET_BIT(exit_n, MAP_DIR_N);
+		}
 
-				logfile = fopen(arg4, "a");
+		if (room_n && room_n->exit_grid[EXIT_GRID_S])
+		{
+			SET_BIT(exit_n, MAP_DIR_S);
+		}
 
-				loginit(ses, logfile, LOG_FLAG_APPEND | HAS_BIT(ses->logmode, LOG_FLAG_HTML));
+		if (room && room->exit_grid[EXIT_GRID_W])
+		{
+			SET_BIT(exit_w, UNICODE_DIR_W);
+		}
 
-				break;
+		if (room_w && room_w->exit_grid[EXIT_GRID_E])
+		{
+			SET_BIT(exit_w, UNICODE_DIR_E);
+		}
 
-			case 'd':
-			case 'D':
-				strcpy(arg3, "DRAW");
+		sprintf(buf, "%s", ses->map->color[MAP_COLOR_EXIT]);
 
-				if (*arg4 == 0)
+		switch (line)
+		{
+			case 1:
+				switch (exit_nw)
 				{
-					show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP MAP {%s} {%s} {%s} {square}", arg1, arg2, arg3);
-					pop_call();
-					return;
-				}
-				break;
+					case 0:
+						strcat(buf, draw_terrain_symbol(ses, room, line, 1, x, y, TERRAIN_FLAG_DOUBLE));
+						strcat(buf, draw_terrain_symbol(ses, room, line, 2, x, y, TERRAIN_FLAG_DOUBLE));
+						break;
 
-			case 'o':
-			case 'O':
-				strcpy(arg3, "OVERWRITE");
+					case UNICODE_DIR_SE:
+						cat_sprintf(buf, "%s%s", get_exit_color(ses, 0, room_nw->exit_grid[EXIT_GRID_SE]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SE]);
+						strcat(buf, draw_terrain_symbol(ses, room, line, 2, x, y, flags));
+						break;
+					case UNICODE_DIR_NE:
+						cat_sprintf(buf, "%s%s", get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_NE]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NE]);
+						strcat(buf, draw_terrain_symbol(ses, room, line, 2, x, y, flags));
+						break;
+					case UNICODE_DIR_SE|UNICODE_DIR_NE:
+						cat_sprintf(buf, "%s%s", room_nw->length < room_w->length ? get_exit_color(ses, 0, room_nw->exit_grid[EXIT_GRID_SE]) : get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_NE]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SE + UNICODE_DIR_NE]);
+						strcat(buf, draw_terrain_symbol(ses, room, line, 2, x, y, flags));
+						break;
 
-				logfile = fopen(arg4, "w");
+					case UNICODE_DIR_SE|UNICODE_DIR_NW:
+						cat_sprintf(buf, "%s%s", room_nw->length < room->length ? get_exit_color(ses, 0, room_nw->exit_grid[EXIT_GRID_SE]) : get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_NW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SE + UNICODE_DIR_NW]);
+						break;
 
-				loginit(ses, logfile, LOG_FLAG_OVERWRITE | HAS_BIT(ses->logmode, LOG_FLAG_HTML));
+					case UNICODE_DIR_NE|UNICODE_DIR_SW:
+						cat_sprintf(buf, "%s%s", room_w->length < room_n->length ? get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_NE]) : get_exit_color(ses, 0, room_n->exit_grid[EXIT_GRID_SW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NE + UNICODE_DIR_SW]);
+						break;
+//
+					case UNICODE_DIR_NE|UNICODE_DIR_NW:
+						cat_sprintf(buf, "%s%s%s%s", get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_NE]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NE], get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_NW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NW]);
+						break;
 
-				break;
+					case UNICODE_DIR_NE|UNICODE_DIR_SW|UNICODE_DIR_NW:
+						cat_sprintf(buf, "%s%s%s%s", get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_NE]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NE], room_n->length < room->length ? get_exit_color(ses, 0, room_n->exit_grid[EXIT_GRID_SW]) : get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_NW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NW + UNICODE_DIR_SW]);
+						break;
 
-			case 'l':
-			case 'L':
-				strcpy(arg3, "LIST");
-				break;
+					case UNICODE_DIR_SE|UNICODE_DIR_SW:
+						cat_sprintf(buf, "%s%s%s%s", get_exit_color(ses, 0, room_nw->exit_grid[EXIT_GRID_SE]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SE], get_exit_color(ses, 0, room_n->exit_grid[EXIT_GRID_SW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SW]);
+						break;
 
-			case 'v':
-			case 'V':
-				strcpy(arg3, "VARIABLE");
-				break;
+					case UNICODE_DIR_SE|UNICODE_DIR_SW|UNICODE_DIR_NW:
+						cat_sprintf(buf, "%s%s%s%s", get_exit_color(ses, 0, room_nw->exit_grid[EXIT_GRID_SE]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SE], room_n->length < room->length ? get_exit_color(ses, 0, room_n->exit_grid[EXIT_GRID_SW]) : get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_NW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NW + UNICODE_DIR_SW]);
+						break;
 
-			default:
-				show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP MAP {rows} {cols} {append|overwrite|list|variable} {name}");
-				pop_call();
-				return;
-		}
+					case UNICODE_DIR_SE|UNICODE_DIR_NE|UNICODE_DIR_SW:
+						cat_sprintf(buf, "%s%s%s%s", room_nw->length < room_w->length ? get_exit_color(ses, 0, room_nw->exit_grid[EXIT_GRID_SE]) : get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_NE]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SE + UNICODE_DIR_NE], get_exit_color(ses, 0, room_n->exit_grid[EXIT_GRID_SW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SW]);
+						break;
 
-		if (*arg4 == 0)
-		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP MAP {%s} {%s} {%s} {name}", arg1, arg2, arg3);
-			pop_call();
-			return;
-		}
-	}
+					case UNICODE_DIR_SE|UNICODE_DIR_NE|UNICODE_DIR_NW:
+						cat_sprintf(buf, "%s%s%s%s", room_nw->length < room_w->length ? get_exit_color(ses, 0, room_nw->exit_grid[EXIT_GRID_SE]) : get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_NE]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SE + UNICODE_DIR_NE], get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_NW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NW]);
+						break;
 
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
-	{
-		map_grid_y = 2 + map_grid_y / 3;
-		map_grid_x = 2 + map_grid_x / 6;
-	}
-	else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS))
-	{
-		map_grid_y = 2 + map_grid_y / 2;
-		map_grid_x = 2 + map_grid_x / 5;
-	}
-	else if (HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
-	{
-		map_grid_y = 2 + map_grid_y / 2;
-		map_grid_x = 2 + map_grid_x / 5;
-	}
-	else if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
-	{
-		map_grid_y = 2 + map_grid_y / 1;
-		map_grid_x = 2 + map_grid_x / 2;
-	}
-	else
-	{
-		map_grid_y = 2 + map_grid_y;
-		map_grid_x = 2 + map_grid_x;
-	}
+					case UNICODE_DIR_SW:
+						strcat(buf, draw_terrain_symbol(ses, room, line, 1, x, y, flags));
+						cat_sprintf(buf, "%s%s", get_exit_color(ses, 0, room_n->exit_grid[EXIT_GRID_SW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SW]);
+						break;
 
-	if (map_grid_x > ses->map->max_grid_x || map_grid_y > ses->map->max_grid_y)
-	{
-		if (map_grid_x > ses->map->max_grid_x)
-		{
-			ses->map->max_grid_x = map_grid_x + 1;
-		}
-		if (map_grid_y > ses->map->max_grid_y)
-		{
-			ses->map->max_grid_y = map_grid_y + 1;
-		}
+					case UNICODE_DIR_NW:
+						strcat(buf, draw_terrain_symbol(ses, room, line, 1, x, y, flags));
+						cat_sprintf(buf, "%s%s", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_NW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NW]);
+						break;
 
-		ses->map->grid_rooms = (struct room_data **) realloc(ses->map->grid_rooms, ses->map->max_grid_x * ses->map->max_grid_y * sizeof(struct room_data *));
-		ses->map->grid_flags =               (int *) realloc(ses->map->grid_flags, ses->map->max_grid_x * ses->map->max_grid_y * sizeof(int));
-	}
-
-	displaygrid_build(ses, ses->map->in_room, map_grid_x, map_grid_y, 0);
-
-	*arg1 = row = 0;
-
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
-	{
-		for (y = map_grid_y - 2 ; y >= 1 ; y--)
-		{
-			for (line = 1 ; line <= 3 ; line++)
-			{
-				str_cpy(&gtd->buf, ses->map->color[MAP_COLOR_BACK]);
+					case UNICODE_DIR_SW|UNICODE_DIR_NW:
+						strcat(buf, draw_terrain_symbol(ses, room, line, 1, x, y, flags));
+						cat_sprintf(buf, "%s%s", room_n->length < room->length ? get_exit_color(ses, 0, room_n->exit_grid[EXIT_GRID_SW]) : get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_NW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SW + UNICODE_DIR_NW]);
+						break;
 
-				for (x = 1 ; x < map_grid_x - 1 ; x++)
-				{
-					str_cat(&gtd->buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], line, x, y));
+					case UNICODE_DIR_NW|UNICODE_DIR_SE|UNICODE_DIR_NE|UNICODE_DIR_SW:
+//						cat_sprintf(buf, "ᐳᐸ");
+						cat_sprintf(buf, "%s%s%s%s", room_nw->length < room_w->length ? get_exit_color(ses, 0, room_nw->exit_grid[EXIT_GRID_SE]) : get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_NE]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_SE + UNICODE_DIR_NE], room_n->length < room->length ? get_exit_color(ses, 0, room_n->exit_grid[EXIT_GRID_SW]) : get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_NW]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NW + UNICODE_DIR_SW]);
+						break;
+					default:
+						cat_sprintf(buf, "??");
+						break;
 				}
 
-				str_clone(&gtd->out, gtd->buf);
-
-				substitute(ses, gtd->buf, gtd->out, SUB_COL|SUB_CMP|SUB_LIT);
-
-				if (logfile)
+				if (!HAS_BIT(exit_n, MAP_DIR_D) && !HAS_BIT(exit_n, MAP_DIR_N|MAP_DIR_S))
 				{
-					logit(ses, gtd->out, logfile, LOG_FLAG_LINEFEED);
+					strcat(buf, draw_terrain_symbol(ses, room, line, 3, x, y, TERRAIN_FLAG_DOUBLE));
+					strcat(buf, draw_terrain_symbol(ses, room, line, 4, x, y, TERRAIN_FLAG_DOUBLE));
 				}
-				else if (*arg3 == 'L')
+				else
 				{
-					cat_sprintf(arg1, "{%02d}{%s}", ++row, gtd->out);
+					if (HAS_BIT(exit_n, MAP_DIR_D))
+					{
+						cat_sprintf(buf, "%s%s", get_exit_color(ses, 0, room_n->exit_grid[EXIT_GRID_D]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_D]);
+					}
+					else
+					{
+						strcat(buf, draw_terrain_symbol(ses, room, line, 3, x, y, flags));
+					}
+
+					switch (HAS_BIT(exit_n, MAP_DIR_N|MAP_DIR_S))
+					{
+						case MAP_DIR_N:
+							cat_sprintf(buf, "%s%s", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_N]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_N]);
+							break;
+						case MAP_DIR_S:
+							cat_sprintf(buf, "%s%s", get_exit_color(ses, 0, room_n->exit_grid[EXIT_GRID_S]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_S]);
+							break;
+						case MAP_DIR_N|MAP_DIR_S:
+							cat_sprintf(buf, "%s%s", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_N]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_NS]);
+							break;
+						default:
+							strcat(buf, draw_terrain_symbol(ses, room, line, 4, x, y, flags));
+							break;
+					}
 				}
-				else if (*arg3 == 'V' || *arg3 == 'D')
+
+				if (room && room->exit_grid[EXIT_GRID_U])
 				{
-					cat_sprintf(arg1, "%s\n", gtd->out);
+					cat_sprintf(buf, "%s%s", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_U]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_U]);
 				}
 				else
 				{
-					tintin_puts2(ses, gtd->out);
+					strcat(buf, draw_terrain_symbol(ses, room, line, 5, x, y, flags));
 				}
-			}
-		}
-	}
-	else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) || HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
-	{
-		for (y = map_grid_y - 2 ; y >= 1 ; y--)
-		{
-			for (line = 1 ; line <= 2 ; line++)
-			{
-				str_cpy(&gtd->buf, ses->map->color[MAP_COLOR_BACK]);
+				break;
 
-				for (x = 1 ; x < map_grid_x - 1 ; x++)
+			case 2:
+				buf[0] = 0;
+
+				if (room == NULL || room->vnum == 0)
 				{
-					str_cat(&gtd->buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], line, x, y));
-				}
+					if (HAS_BIT(exit_w, MAP_DIR_W))
+					{
+						sprintf(buf, "%s%s%s",
+							get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_E]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_E],
+							draw_terrain_symbol(ses, room, line, 2, x, y, flags));
+					}
+					else
+					{
+						strcat(buf, draw_terrain_symbol(ses, room, line, 1, x, y, TERRAIN_FLAG_DOUBLE));
+						strcat(buf, draw_terrain_symbol(ses, room, line, 2, x, y, TERRAIN_FLAG_DOUBLE));
+					}
+					strcat(buf, draw_terrain_symbol(ses, room, line, 3, x, y, TERRAIN_FLAG_DOUBLE));
+					strcat(buf, draw_terrain_symbol(ses, room, line, 4, x, y, TERRAIN_FLAG_DOUBLE));
 
-				str_clone(&gtd->out, gtd->buf);
+					strcat(buf, draw_terrain_symbol(ses, room, line, 5, x, y, flags));
 
-				substitute(ses, gtd->buf, gtd->out, SUB_COL|SUB_CMP|SUB_LIT);
+					pop_call();
+					return buf;
+				}
 
-				if (logfile)
+				switch (exit_w)
 				{
-					fprintf(logfile, "%s\n", gtd->out);
+					case 0:
+						strcat(buf, draw_terrain_symbol(ses, room, line, 1, x, y, TERRAIN_FLAG_DOUBLE));
+						strcat(buf, draw_terrain_symbol(ses, room, line, 2, x, y, TERRAIN_FLAG_DOUBLE));
+						break;
+					case UNICODE_DIR_E:
+						sprintf(buf, "%s%s%s", get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_E]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_E], draw_terrain_symbol(ses, room, line, 2, x, y, flags));
+						break;
+					case UNICODE_DIR_W:
+						sprintf(buf, "%s%s%s", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_W]), draw_terrain_symbol(ses, room, line, 1, x, y, flags), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_W]);
+						break;
+					case UNICODE_DIR_E|UNICODE_DIR_W:
+						if (room->exit_grid[EXIT_GRID_W]->vnum == room_w->vnum && room_w->exit_grid[EXIT_GRID_E]->vnum == room->vnum)
+						{
+							// ‒‒
+							sprintf(buf, "%s%s%s%s", get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_E]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_EW], get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_W]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_EW]);
+						}
+						else
+						{
+							sprintf(buf, "%s%s%s%s", get_exit_color(ses, 0, room_w->exit_grid[EXIT_GRID_E]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_E], get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_W]), ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_W]);
+						}
+						break;
+					default:
+						strcat(buf, "??");
+						break;
 				}
-				else if (*arg3 == 'L')
+
+				if (room->vnum == ses->map->in_room)
 				{
-					cat_sprintf(arg1, "{%02d}{%s\e[0m}", ++row, gtd->out);
+					cat_sprintf(buf, "%s%s%s%s", room_left, ses->map->color[MAP_COLOR_USER], ses->map->legend[offset + index], room_right);
 				}
-				else if (*arg3 == 'V' || *arg3 == 'D')
+				else if (symsize > 3)
 				{
-					cat_sprintf(arg1, "%s\e[0m\n", gtd->out);
+					cat_sprintf(buf, "%s%-3s", ses->map->color[MAP_COLOR_SYMBOL], room->symbol);
 				}
 				else
 				{
-					tintin_puts2(ses, gtd->out);
+					if (symsize > 1)
+					{
+						cat_sprintf(buf, "%s%-3s", ses->map->color[MAP_COLOR_SYMBOL], room->symbol);
+					}
+					else if (HAS_BIT(room->flags, ROOM_FLAG_VOID))
+					{
+						if (HAS_BIT(room->exit_dirs, MAP_DIR_W))
+						{
+							cat_sprintf(buf, "%s─", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_W]));
+						}
+						else
+						{
+							cat_sprintf(buf, " ");
+						}
+
+						if (*room->symbol != ' ' && symsize == 1)
+						{
+							cat_sprintf(buf, "%s%-1s", ses->map->color[MAP_COLOR_SYMBOL], room->symbol);
+						}
+						else
+						{
+							if (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_E|MAP_DIR_S|MAP_DIR_W) == (MAP_DIR_N|MAP_DIR_E|MAP_DIR_S|MAP_DIR_W))
+							{
+								if (get_exit_length(ses, room->exit_grid[EXIT_GRID_N]) < get_exit_length(ses, room->exit_grid[EXIT_GRID_W]))
+								{
+									cat_sprintf(buf, "%s│", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_N]));
+								}
+								else
+								{
+									cat_sprintf(buf, "%s─", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_W]));
+								}
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_E|MAP_DIR_W) == (MAP_DIR_E|MAP_DIR_W))
+							{
+								cat_sprintf(buf, "%s─", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_W]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_S) == (MAP_DIR_N|MAP_DIR_S))
+							{
+								cat_sprintf(buf, "%s│", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_N]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_E|MAP_DIR_S|MAP_DIR_W|MAP_DIR_NW|MAP_DIR_NE|MAP_DIR_SE|MAP_DIR_SW) == (MAP_DIR_S))
+							{
+								cat_sprintf(buf, "%s│", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_S]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_E|MAP_DIR_S|MAP_DIR_W|MAP_DIR_NW|MAP_DIR_NE|MAP_DIR_SE|MAP_DIR_SW) == (MAP_DIR_N))
+							{
+								cat_sprintf(buf, "%s│", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_N]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_E|MAP_DIR_S|MAP_DIR_W|MAP_DIR_NW|MAP_DIR_NE|MAP_DIR_SE|MAP_DIR_SW) == (MAP_DIR_E))
+							{
+								cat_sprintf(buf, "%s─", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_E]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_E|MAP_DIR_S|MAP_DIR_W|MAP_DIR_NW|MAP_DIR_NE|MAP_DIR_SE|MAP_DIR_SW) == (MAP_DIR_W))
+							{
+								cat_sprintf(buf, "%s─", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_W]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_E|MAP_DIR_S|MAP_DIR_W|MAP_DIR_NW|MAP_DIR_NE|MAP_DIR_SE|MAP_DIR_SW) == (MAP_DIR_N|MAP_DIR_E|MAP_DIR_W))
+							{
+								cat_sprintf(buf, "%s├", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_N]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_E|MAP_DIR_S|MAP_DIR_W|MAP_DIR_NW|MAP_DIR_NE|MAP_DIR_SE|MAP_DIR_SW) == (MAP_DIR_S|MAP_DIR_E|MAP_DIR_W))
+							{
+								cat_sprintf(buf, "%s┬", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_E]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_NW|MAP_DIR_SW) && HAS_BIT(room->exit_dirs, MAP_DIR_E))
+							{
+								cat_sprintf(buf, "%s─", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_E]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_NE))
+							{
+								cat_sprintf(buf, "%s╴", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_NE]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_SE))
+							{
+								cat_sprintf(buf, "%s╴", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_SE]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_SW))
+							{
+								cat_sprintf(buf, "%s╴", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_SW]));
+							}
+							else if (HAS_BIT(room->exit_dirs, MAP_DIR_NW))
+							{
+								cat_sprintf(buf, "%s╴", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_NW]));
+							}
+							else
+							{
+								cat_sprintf(buf, " ");
+							}
+						}
+
+						if (HAS_BIT(room->exit_dirs, MAP_DIR_E))
+						{
+							cat_sprintf(buf, "%s─", get_exit_color(ses, 0, room->exit_grid[EXIT_GRID_E]));
+						}
+						else
+						{
+							cat_sprintf(buf, " ");
+						}
+					}
+					else
+					{
+						cat_sprintf(buf, "%s%s%-1s%s", room_left, ses->map->color[MAP_COLOR_SYMBOL], room->symbol, room_right);
+					}
 				}
-			}
+				cat_sprintf(buf, "%s", ses->map->color[MAP_COLOR_BACK]);
+
+				break;
 		}
+		pop_call();
+		return buf;
 	}
-	else
-	{
-		for (y = map_grid_y - 2 ; y >= 1 ; y--)
-		{
-			str_cpy(&gtd->buf, ses->map->color[MAP_COLOR_BACK]);
 
-			for (x = 1 ; x < map_grid_x - 1 ; x++)
-			{
-				str_cat(&gtd->buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], 0, x, y));
-			}
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
+	{
+		struct room_data *room_w;
+		long long exit_w;
 
-			substitute(ses, gtd->buf, gtd->out, SUB_COL|SUB_CMP|SUB_LIT);
+		room_w = ses->map->grid_rooms[x - 1 + map_grid_x * (y + 0)];
+		exit_w = 0;
 
-			if (logfile)
-			{
-				fprintf(logfile, "%s\n", gtd->out);
-			}
-			else if (*arg3 == 'L')
-			{
-				cat_sprintf(arg1, "{%02d}{%s}", ++row, gtd->out);
-			}
-			else if (*arg3 == 'V' || *arg3 == 'D')
+		if (room_w)
+		{
+			if (HAS_BIT(room_w->exit_dirs, MAP_DIR_E))
 			{
-				cat_sprintf(arg1, "%s\n", gtd->out);
+				SET_BIT(exit_w, MAP_DIR_E);
 			}
-			else
+			if (HAS_BIT(room_w->exit_dirs, MAP_DIR_U))
 			{
-				tintin_puts2(ses, gtd->out);
+				SET_BIT(exit_w, MAP_DIR_U);
 			}
 		}
-	}
-
-	if (logfile)
-	{
-		fclose(logfile);
-	}
-	else if (*arg3 == 'D')
-	{
-		draw_map(ses, 1, 2, 3, 4, 5, 6, 7, "", arg4, arg2, arg1);
-	}
-	else if (*arg3 == 'L')
-	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg4, "%s", arg1);
-	}
-	else if (*arg3 == 'V')
-	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg4, "%s", arg1);
-	}
-
-	pop_call();
-	return;
-}
 
-DO_MAP(map_move)
-{
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+		sprintf(buf, "%s", room_color);
 
-	arg = arg1;
+		switch (line)
+		{
+			case 1:
+				switch (exit_w)
+				{
+					case 0:
+						strcat(buf, " ");
+						break;
+					case MAP_DIR_E:
+						strcat(buf, "═");
+						break;
+					case MAP_DIR_E|MAP_DIR_U:
+						strcat(buf, "═̂");
+						break;
+					case MAP_DIR_U:
+						strcat(buf, " ̂");
+						break;
+					default:
+						strcat(buf, "?");
+						break;
+				}
 
-	while (*arg)
-	{
-		arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
+				if (room == NULL)
+				{
+					strcat(buf, "    ");
+				}
+				else
+				{
+					strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_W) ? "═" : " ");
 
-		ses->map->nofollow++;
+					if (room->vnum == ses->map->in_room)
+					{
+						cat_sprintf(buf, "%s", ses->map->color[MAP_COLOR_USER]);
+					}
 
-		follow_map(ses, arg2);
-
-		ses->map->nofollow--;
-
-		if (*arg == COMMAND_SEPARATOR)
-		{
-			arg++;
-		}
-	}
-}
-
-DO_MAP(map_name)
-{
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
-
-	RESTRING(ses->map->room_list[ses->map->in_room]->name, arg1);
-}
-
-DO_MAP(map_offset)
-{
-	char arg3[BUFFER_SIZE], arg4[BUFFER_SIZE];
-
-	if (arg)
-	{
-		arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-		arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
-		arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
-		arg = sub_arg_in_braces(ses, arg, arg4, GET_ONE, SUB_VAR|SUB_FUN);
-
-		ses->map->sav_top_row = get_number(ses, arg1);
-		ses->map->sav_top_col = get_number(ses, arg2);
-		ses->map->sav_bot_row = get_number(ses, arg3);
-		ses->map->sav_bot_col = get_number(ses, arg4);
-	}
+					switch (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_W))
+					{
+						case MAP_DIR_N:
+							strcat(buf, "║");
+							break;
+						case MAP_DIR_W:
+							strcat(buf, "═");
+							break;
+						case MAP_DIR_N|MAP_DIR_W:
+							strcat(buf, "╝");
+							break;
+						default:
+							strcat(buf, "╔");
+							break;
+					}
 
-	if (ses->map->sav_top_row == 0)
-	{
-		ses->map->top_row = 1;
-	}
-	else if (ses->map->sav_top_row < 0)
-	{
-		ses->map->top_row = 1 + gtd->screen->rows + ses->map->sav_top_row;
-	}
-	else
-	{
-		ses->map->top_row = ses->map->sav_top_row;
-	}
 
-	if (ses->map->sav_top_col == 0)
-	{
-		ses->map->top_col = 1;
-	}
-	else if (ses->map->sav_top_col < 0)
-	{
-		ses->map->top_col = 1 + gtd->screen->cols + ses->map->sav_top_col;
-	}
-	else
-	{
-		ses->map->top_col = ses->map->sav_top_col;
-	}
+					strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_N) ? " " : "═");
 
-	if (ses->map->sav_bot_row == 0)
-	{
-		ses->map->bot_row = ses->split->top_row - 1;
-	}
-	else if (ses->map->sav_bot_row < 0)
-	{
-		ses->map->bot_row = 1 + gtd->screen->rows + ses->map->sav_bot_row;
-	}
-	else
-	{
-		ses->map->bot_row = ses->map->sav_bot_row;
-	}
 
-	if (ses->map->sav_bot_col == 0)
-	{
-		ses->map->bot_col = gtd->screen->cols;
-	}
-	else if (ses->map->sav_bot_col < 0)
-	{
-		ses->map->bot_col = 1 + gtd->screen->cols + ses->map->sav_bot_col;
-	}
-	else
-	{
-		ses->map->bot_col = ses->map->sav_bot_col;
-	}
+					switch (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_E))
+					{
+						case MAP_DIR_N:
+							strcat(buf, "║");
+							break;
+						case MAP_DIR_E:
+							strcat(buf, "═");
+							break;
+						case MAP_DIR_N|MAP_DIR_E:
+							strcat(buf, "╚");
+							break;
+						default:
+							strcat(buf, "╗");
+							break;
+					}
+				}
+				break;
 
-	ses->map->rows = ses->map->bot_row - ses->map->top_row;
-	ses->map->cols = ses->map->bot_col - ses->map->top_col;
+			case 2:
+				switch (exit_w)
+				{
+					case 0:
+						strcat(buf, " ");
+						break;
+					case MAP_DIR_E:
+						strcat(buf, "═");
+						break;
+					case MAP_DIR_E|MAP_DIR_U:
+						strcat(buf, "═");
+						break;
+					default:
+						strcat(buf, " ");
+						break;
+				}
 
-	if (arg)
-	{
-		show_message(ses, LIST_COMMAND, "#MAP OFFSET: SQUARE {%d, %d, %d, %d} ROWS {%d} COLS {%d}", ses->map->top_row, ses->map->top_col, ses->map->bot_row, ses->map->bot_col, ses->map->rows, ses->map->cols);
-	}
-}
+				if (room == NULL)
+				{
+					strcat(buf, " ");
+				}
+				else
+				{
+					switch (HAS_BIT(room->exit_dirs, MAP_DIR_W|MAP_DIR_D))
+					{
+						case MAP_DIR_W:
+							strcat(buf, "═");
+							break;
+						case MAP_DIR_W|MAP_DIR_D:
+							strcat(buf, "═̬");
+							break;
+						case MAP_DIR_D:
+							strcat(buf, " ̬");
+							break;
+						default:
+							strcat(buf, " ");
+							break;
+					}
+				}
 
-DO_MAP(map_read)
-{
-	FILE *myfile;
-	struct exit_data *exit;
-	char buffer[BUFFER_SIZE], file[BUFFER_SIZE], *cptr;
-	int line = 1, room = 0;
+				if (room == NULL)
+				{
+					strcat(buf, "   ");
+				}
+				else
+				{
+					if (room->vnum == ses->map->in_room)
+					{
+						cat_sprintf(buf, "%s", ses->map->color[MAP_COLOR_USER]);
+					}
 
-	arg = sub_arg_in_braces(ses, arg, file, GET_ALL, SUB_VAR|SUB_FUN);
+					switch (HAS_BIT(room->exit_dirs, MAP_DIR_S|MAP_DIR_W))
+					{
+						case MAP_DIR_S:
+							strcat(buf, "║");
+							break;
+						case MAP_DIR_W:
+							strcat(buf, "═");
+							break;
+						case MAP_DIR_S|MAP_DIR_W:
+							strcat(buf, "╗");
+								break;
+						default:
+							strcat(buf, "╚");
+							break;
+					}
 
-	if ((myfile = fopen(file, "r")) == NULL)
-	{
-		show_error(ses, LIST_COMMAND, "#MAP: Map file {%s} not found.", file);
+					strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_S) ? " " : "═");
 
-		return;
+					switch (HAS_BIT(room->exit_dirs, MAP_DIR_S|MAP_DIR_E))
+					{
+						case MAP_DIR_S:
+							strcat(buf, "║");
+							break;
+						case MAP_DIR_E:
+							strcat(buf, "═");
+							break;
+						case MAP_DIR_S|MAP_DIR_E:
+							strcat(buf, "╔");
+							break;
+						default:
+							strcat(buf, "╝");
+							break;
+					}
+				}
+				break;
+		}
+		pop_call();
+		return buf;
 	}
 
-	gtd->level->quiet++;
-
-	if (fgets(buffer, BUFFER_SIZE - 1, myfile))
+	if (room == NULL || room->vnum == 0)
 	{
-		cptr = strchr(buffer, '\r'); /* For map files editor on Windows systems. */
-
-		if (cptr)
+		if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
 		{
-			*cptr = 0;
-		}
-
-		cptr = strchr(buffer, '\n');
+			strcpy(buf, "");
 
-		if (cptr)
-		{
-			*cptr = 0;
+			for (index = 1 ; index <= 6 ; index++)
+			{
+				strcat(buf, draw_terrain_symbol(ses, room, line, index, x, y, TERRAIN_FLAG_DOUBLE));
+			}
 		}
-
-		if (buffer[0] == 'C' && buffer[1] == ' ')
+		else if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
 		{
-			create_map(ses, buffer + 2);
+			sprintf(buf, "  ");
 		}
 		else
 		{
-			gtd->level->quiet--;
-
-			show_error(ses, LIST_COMMAND, "#MAP READ {%s}: INVALID START OF FILE. ABORTING READ..", file);
-
-			fclose(myfile);
-
-			return;
+			sprintf(buf, " ");
 		}
+		pop_call();
+		return buf;
 	}
-	else
-	{
-		gtd->level->quiet--;
-
-		show_error(ses, LIST_COMMAND, "#MAP: INVALID READ ON LINE %d. ABORTING READ..", line);
 
-		fclose(myfile);
-		
-		return;
-	}
 
-	while (fgets(buffer, BUFFER_SIZE - 1, myfile))
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
 	{
-		line++;
+		strcpy(buf, "");
 
-		cptr = strchr(buffer, '\r'); /* For map files editor on Windows systems. */
-
-		if (cptr)
-		{
-			*cptr = 0;
-		}
-
-		cptr = strchr(buffer, '\n');
-
-		if (cptr)
-		{
-			*cptr = 0;
-		}
-
-		switch (buffer[0])
+		switch (line)
 		{
-			case 'C':
-				switch (buffer[1])
+			case 1:
+				if (room->exit_grid[EXIT_GRID_NW])
 				{
-					case ' ':
-						gtd->level->quiet--;
-
-						show_error(ses, LIST_COMMAND, "#MAP: INVALID COMMAND {%d} {%s} ON LINE %d. ABORTING READ..", buffer[0], buffer, line);
-
-						fclose(myfile);
-
-						delete_map(ses);
-
-						return;
-
-					case 'A':
-					case 'B':
-					case 'E':
-					case 'H':
-					case 'I':
-					case 'P':
-					case 'R':
-					case 'S':
-					case 'U':
-						map_color(ses, buffer + 1, arg1, arg2);
-						break;
-
-					default:
-						show_error(ses, LIST_COMMAND, "#MAP READ: INVALID COMMAND {%d} {%s} ON LINE %d. ABORTING READ..", buffer[0], buffer, line);
-						break;
+					strcat(buf, get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_NW]));
+					strcat(buf, "\\");
+					strcat(buf, draw_terrain_symbol(ses, room, line, 2, x, y, flags));
+				}
+				else
+				{
+					strcat(buf, draw_terrain_symbol(ses, room, line, 1, x, y, TERRAIN_FLAG_DOUBLE));
+					strcat(buf, draw_terrain_symbol(ses, room, line, 2, x, y, TERRAIN_FLAG_DOUBLE));
 				}
-				break;
-
-			case 'E':
-				create_exit(ses, room, "%s", buffer + 2);
-				break;
-
-			case 'F':
-				ses->map->flags = atoi(buffer + 2);
-				break;
 
-			case 'G':
-				ses->map->global_vnum = ses->map->global_exit->vnum = atoi(buffer + 2);
-				break;
+				if (room->exit_grid[EXIT_GRID_N] || room->exit_grid[EXIT_GRID_U])
+				{
+					cat_sprintf(buf, "%s%s", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_N]), room->exit_grid[EXIT_GRID_N]  ? "|"  : draw_terrain_symbol(ses, room, line, 3, x, y, flags));
+					cat_sprintf(buf, "%s%s", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_U]), room->exit_grid[EXIT_GRID_U]  ? "+"  : draw_terrain_symbol(ses, room, line, 4, x, y, flags));
+				}
+				else
+				{
+					strcat(buf, draw_terrain_symbol(ses, room, line, 3, x, y, TERRAIN_FLAG_DOUBLE));
+					strcat(buf, draw_terrain_symbol(ses, room, line, 4, x, y, TERRAIN_FLAG_DOUBLE));
+				}
 
-			case 'I':
-				ses->map->last_room = atoi(buffer + 2);
+				if (room->exit_grid[EXIT_GRID_NE])
+				{
+					cat_sprintf(buf, "%s%s%s", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_NE]), "/", draw_terrain_symbol(ses, room, line, 6, x, y, flags));
+				}
+				else
+				{
+					strcat(buf, draw_terrain_symbol(ses, room, line, 5, x, y, TERRAIN_FLAG_DOUBLE));
+					strcat(buf, draw_terrain_symbol(ses, room, line, 6, x, y, TERRAIN_FLAG_DOUBLE));
+				}
 				break;
 
-			case 'L':
-				map_legend(ses, buffer + 2, arg1, arg2);
-				break;
+			case 2:
+				if (HAS_BIT(room->flags, ROOM_FLAG_CURVED))
+				{
+					sprintf(room_left,  "%s%s", room_color, ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_RL_CURVED]);
+					sprintf(room_right, "%s%s", room_color, ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_RR_CURVED]);
+				}
+				else
+				{
+					sprintf(room_left,  "%s%s", room_color, ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_RL]);
+					sprintf(room_right, "%s%s", room_color, ses->map->legend[LEGEND_UNICODE_GRAPHICS + UNICODE_DIR_RR]);
+				}
 
-			case 'R':
-				room = create_room(ses, "%s", buffer + 2);
-				break;
+				if (!HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS) && symsize <= 3)
+				{
+					cat_sprintf(buf, "%s%s",
+						get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_W]),
+						room->exit_grid[EXIT_GRID_W]  ? "-"  : draw_terrain_symbol(ses, room, line, 1, x, y, flags));
+				}
 
-			case 'V':
-				ses->map->version = atoi(buffer + 2);
-				break;
+				if (room->vnum == ses->map->in_room)
+				{
+					if (!HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS))
+					{
+						cat_sprintf(buf, "%s%s%s%s", room_left, ses->map->color[MAP_COLOR_USER], ses->map->legend[index], room_right);
+					}
+					else
+					{
+						if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIILENGTH))
+						{
+							cat_sprintf(buf, "%s%5.1f", ses->map->color[MAP_COLOR_USER], room->length, ses->map->color[MAP_COLOR_EXIT]);
+						}
+						else
+						{
+							cat_sprintf(buf, "%s%05d", ses->map->color[MAP_COLOR_USER], room->vnum);
+						}
+					}
+				}
+				else
+				{
+					if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS))
+					{
+						if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIILENGTH))
+						{
+							cat_sprintf(buf, "%s%5.1f%s", ses->map->color[MAP_COLOR_USER], room->length, ses->map->color[MAP_COLOR_EXIT]);
+						}
+						else
+						{
+							cat_sprintf(buf, "%s%05d%s", room_color, room->vnum, ses->map->color[MAP_COLOR_EXIT]);
+						}
+					}
+					else if (symsize > 3)
+					{
+						cat_sprintf(buf, "%s%-5s", ses->map->color[MAP_COLOR_SYMBOL], room->symbol);
+					}
+					else if (HAS_BIT(room->flags, ROOM_FLAG_VOID))
+					{
+						if (*room->symbol != ' ' && symsize == 1)
+						{
+							if (room->exit_dirs == (MAP_DIR_E|MAP_DIR_W))
+							{
+								cat_sprintf(buf, "%s-%s%s%s-", ses->map->color[MAP_COLOR_EXIT], ses->map->color[MAP_COLOR_SYMBOL], room->symbol, ses->map->color[MAP_COLOR_EXIT]);
+							}
+							else
+							{
+								cat_sprintf(buf, "%s %-2s", ses->map->color[MAP_COLOR_SYMBOL], room->symbol);
+							}
+						}
+						else if (*room->symbol != ' ' && strip_color_strlen(ses, room->symbol) > 1)
+						{
+							cat_sprintf(buf, "%s %-2s", ses->map->color[MAP_COLOR_SYMBOL], room->symbol);
+						}
+						else
+						{
+							strcat(buf, ses->map->color[MAP_COLOR_EXIT]);
 
-			case '#':
-				buffer[0] = gtd->tintin_char;
-				ses = script_driver(ses, LIST_COMMAND, buffer);
-				break;
+							switch (room->exit_dirs)
+							{
+								case MAP_DIR_N|MAP_DIR_S:
+									cat_sprintf(buf, " | ");
+									break;
+								case MAP_DIR_E|MAP_DIR_W:
+									cat_sprintf(buf, "---");
+									break;
+								case MAP_DIR_NE|MAP_DIR_SW:
+									cat_sprintf(buf, " / ");
+									break;
+								case MAP_DIR_NW|MAP_DIR_SE:
+									cat_sprintf(buf, " \\ ");
+									break;
+								default:
+									cat_sprintf(buf, " * ");
+									break;
+							}
+						}
+					}
+					else
+					{
+						if (strip_color_strlen(ses, room->symbol) <= 1)
+						{
+							cat_sprintf(buf, "%s%s%-1s%s", room_left, ses->map->color[MAP_COLOR_SYMBOL], room->symbol, room_right);
+						}
+						else
+						{
+							cat_sprintf(buf, "%s%s%-3s", room_color, ses->map->color[MAP_COLOR_SYMBOL], room->symbol);
+						}
+					}
+				}
 
-			case  0:
-			case 13:
+				if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS) || symsize > 3)
+				{
+					cat_sprintf(buf, "%s%s", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_E]), room->exit_grid[EXIT_GRID_E] ? "-" : draw_terrain_symbol(ses, room, line, 6, x, y, flags));
+				}
+				else
+				{
+					if (room->exit_grid[EXIT_GRID_E])
+					{
+						cat_sprintf(buf, "%s--", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_E]));
+					}
+					else
+					{
+						strcat(buf, draw_terrain_symbol(ses, room, line, 5, x, y, TERRAIN_FLAG_DOUBLE));
+						strcat(buf, draw_terrain_symbol(ses, room, line, 6, x, y, TERRAIN_FLAG_DOUBLE));
+					}
+				}
 				break;
 
-			default:
-				gtd->level->quiet--;
-
-				show_error(ses, LIST_COMMAND, "#MAP: INVALID COMMAND {%d} {%s} ON LINE %d. ABORTING READ..", buffer[0], buffer, line);
-
-				fclose(myfile);
-
-				delete_map(ses);
-
-				return;
-		}
-	}
-
-	gtd->level->quiet--;
-
-	fclose(myfile);
-
-	for (room = 0 ; room < ses->map->size ; room++)
-	{
-		if (ses->map->room_list[room] == NULL)
-		{
-			continue;
-		}
-
-		for (exit = ses->map->room_list[room]->f_exit ; exit ; exit = exit->next)
-		{
-			if (exit->vnum < 0 || exit->vnum >= ses->map->size || ses->map->room_list[exit->vnum] == NULL)
-			{
-				show_error(ses, LIST_COMMAND, "#MAP READ: Room %d - invalid exit '%s' to room %d.", room, exit->name, exit->vnum);
+			case 3:
+				if (room->exit_grid[EXIT_GRID_SW] || room->exit_grid[EXIT_GRID_D])
+				{
+					cat_sprintf(buf, "%s%s", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_SW]), room->exit_grid[EXIT_GRID_SW] ? "/"  : draw_terrain_symbol(ses, room, line, 1, x, y, flags));
+					cat_sprintf(buf, "%s%s", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_D]), room->exit_grid[EXIT_GRID_D]  ? "-"  : draw_terrain_symbol(ses, room, line, 2, x, y, flags));
+				}
+				else
+				{
+					strcat(buf, draw_terrain_symbol(ses, room, line, 1, x, y, TERRAIN_FLAG_DOUBLE));
+					strcat(buf, draw_terrain_symbol(ses, room, line, 2, x, y, TERRAIN_FLAG_DOUBLE));
+				}
 
-				delete_exit(ses, room, exit);
+				if (room->exit_grid[EXIT_GRID_S])
+				{
+					cat_sprintf(buf, "%s|%s", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_S]), draw_terrain_symbol(ses, room, line, 4, x, y, flags));
+				}
+				else
+				{
+					strcat(buf, draw_terrain_symbol(ses, room, line, 3, x, y, TERRAIN_FLAG_DOUBLE));
+					strcat(buf, draw_terrain_symbol(ses, room, line, 4, x, y, TERRAIN_FLAG_DOUBLE));
+				}
 
-				if (ses->map->room_list[room]->f_exit)
+				if (room->exit_grid[EXIT_GRID_SE])
 				{
-					exit = ses->map->room_list[room]->f_exit;
+					cat_sprintf(buf, "%s\\%s", get_exit_color(ses, room->vnum, room->exit_grid[EXIT_GRID_SE]), draw_terrain_symbol(ses, room, line, 6, x, y, flags));
 				}
 				else
 				{
-					break;
+					strcat(buf, draw_terrain_symbol(ses, room, line, 5, x, y, TERRAIN_FLAG_DOUBLE));
+					strcat(buf, draw_terrain_symbol(ses, room, line, 6, x, y, TERRAIN_FLAG_DOUBLE));
 				}
-			}
+				break;
 		}
+		pop_call();
+		return buf;
 	}
 
-	show_message(ses, LIST_COMMAND, "#MAP READ: Map file {%s} loaded.", file);
-
-
-}
-
-DO_MAP(map_resize)
-{
-	int size, vnum, room;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+	if (room->vnum == ses->map->in_room)
+	{
+		exits = ses->map->dir;
 
-	size = atoi(arg1);
+		DEL_BIT(exits, MAP_EXIT_U|MAP_EXIT_D);
 
-	if (size <= ses->map->size)
-	{
-		for (room = vnum = 1 ; vnum < ses->map->size ; vnum++)
+		if (HAS_BIT(ses->map->flags, MAP_FLAG_DIRECTION))
 		{
-			if (ses->map->room_list[vnum])
+			switch (exits)
 			{
-				room = vnum;
+				case MAP_EXIT_N:
+					index = 24;
+					break;
+				case MAP_EXIT_N+MAP_EXIT_E:
+					index = 25;
+					break;
+				case MAP_EXIT_E:
+					index = 26;
+					break;
+				case MAP_EXIT_S+MAP_EXIT_E:
+					index = 27;
+					break;
+				case MAP_EXIT_S:
+					index = 28;
+					break;
+				case MAP_EXIT_S+MAP_EXIT_W:
+					index = 29;
+					break;
+				case MAP_EXIT_W:
+					index = 30;
+					break;
+				case MAP_EXIT_N+MAP_EXIT_W:
+					index = 31;
+					break;
+
+				default:
+					index = 17;
+					break;
 			}
 		}
-
-		if (room >= size)
+		else
 		{
-			show_error(ses, LIST_COMMAND, "#MAP RESIZE: YOU MUST DELETE ALL ROOMS WITH VNUMS ABOVE (%d) FIRST.", size);
-			return;
+			index = 16;
 		}
-	}
 
-	ses->map->room_list = (struct room_data **) realloc(ses->map->room_list, size * sizeof(struct room_data *));
-
-	if (ses->map->size < size)
-	{
-		while (ses->map->size < size)
+		if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
 		{
-			ses->map->room_list[ses->map->size++] = NULL;
+			sprintf(buf, "%s%s%s", ses->map->color[MAP_COLOR_USER], ses->map->legend[offset + index], ses->map->legend[offset + index]);
 		}
+		else
+		{
+			sprintf(buf, "%s%s", ses->map->color[MAP_COLOR_USER], ses->map->legend[offset + index]);
+		}
+		pop_call();
+		return buf;
 	}
-	else
+
+	exit1 = 0;
+	exit2 = 0;
+	exits = 0;
+
+	if (HAS_BIT(room->exit_dirs, MAP_DIR_N))
 	{
-		ses->map->size = size;
+		SET_BIT(exit1, 1 << 0);
+		SET_BIT(exit2, 1 << 0);
+		SET_BIT(exits, MAP_EXIT_N);
 	}
 
-	show_message(ses, LIST_COMMAND, "#MAP RESIZE: MAP RESIZED TO %d ROOMS.", ses->map->size);
-}
+	if (HAS_BIT(room->exit_dirs, MAP_DIR_W))
+	{
+		SET_BIT(exit1, 1 << 2);
+		SET_BIT(exits, MAP_EXIT_W);
+	}
 
-DO_MAP(map_return)
-{
-	if (ses->map == NULL || ses->map->room_list[ses->map->last_room] == NULL)
+	if (HAS_BIT(room->exit_dirs, MAP_DIR_E))
 	{
-		show_error(ses, LIST_COMMAND, "#MAP RETURN: NO KNOWN LAST ROOM.");
+		SET_BIT(exit2, 1 << 2);
+		SET_BIT(exits, MAP_EXIT_E);
+	}
 
-		return;
+	if (HAS_BIT(room->exit_dirs, MAP_DIR_S))
+	{
+		SET_BIT(exit1, 1 << 4);
+		SET_BIT(exit2, 1 << 4);
+		SET_BIT(exits, MAP_EXIT_S);
 	}
 
-	if (ses->map->in_room)
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
 	{
-		show_error(ses, LIST_COMMAND, "#MAP RETURN: ALREADY IN THE MAP.");
+		if (HAS_BIT(room->exit_dirs, MAP_DIR_NW))
+		{
+			SET_BIT(exit1, 1 << 1);
+		}
+		if (HAS_BIT(room->exit_dirs, MAP_DIR_NE))
+		{
+			SET_BIT(exit2, 1 << 1);
+		}
+		if (HAS_BIT(room->exit_dirs, MAP_DIR_SW))
+		{
+			SET_BIT(exit1, 1 << 3);
+		}
+		if (HAS_BIT(room->exit_dirs, MAP_DIR_SE))
+		{
+			SET_BIT(exit2, 1 << 3);
+		}
+
+		room1 = exit1 + LEGEND_MUDFONT_NWS;
+		room2 = exit2 + LEGEND_MUDFONT_NES;
+
+		if (HAS_BIT(room->flags, ROOM_FLAG_VOID))
+		{
+			room1 += 64;
+			room2 += 64;
+		}
+
+		if (HAS_BIT(room->flags, ROOM_FLAG_CURVED))
+		{
+			switch (room->exit_dirs)
+			{
+				case MAP_DIR_N|MAP_DIR_E:
+				case MAP_DIR_N|MAP_DIR_SE:
+					room1 = LEGEND_MUDFONT_CURVED + 0;
+					break;
+				case MAP_DIR_S|MAP_DIR_E:
+				case MAP_DIR_S|MAP_DIR_NE:
+					room1 = LEGEND_MUDFONT_CURVED + 1;
+					break;
+				case MAP_DIR_S|MAP_DIR_W:
+				case MAP_DIR_S|MAP_DIR_NW:
+					room2 = LEGEND_MUDFONT_CURVED + 2;
+					break;
+				case MAP_DIR_N|MAP_DIR_W:
+				case MAP_DIR_N|MAP_DIR_SW:
+					room2 = LEGEND_MUDFONT_CURVED + 3;
+					break;
+			}
+		}
+
+		sprintf(buf, "%s%s%s", room_color, ses->map->legend[room1], ses->map->legend[room2]);
 	}
 	else
 	{
-		goto_room(ses, ses->map->last_room);
-
-		show_message(ses, LIST_COMMAND, "#MAP RETURN: RETURNED TO ROOM %d {%s}.", ses->map->in_room, ses->map->room_list[ses->map->in_room]->name);
+		if (HAS_BIT(ses->map->flags, MAP_FLAG_SYMBOLGRAPHICS) && room->symbol[0] && room->symbol[0] != ' ')
+		{
+			sprintf(buf, "%s%-1s", room_color, room->symbol);
+		}
+		else
+		{
+			if (HAS_BIT(room->flags, ROOM_FLAG_VOID) && (exits == MAP_EXIT_N+MAP_EXIT_S || exits == MAP_EXIT_E+MAP_EXIT_W))
+			{
+				sprintf(buf, "%s%s", room_color, exits == MAP_EXIT_N+MAP_EXIT_S ? ses->map->legend[offset+16+2] : ses->map->legend[offset+16+3]);
+			}
+			else
+			{
+				if (HAS_BIT(room->flags, ROOM_FLAG_CURVED))
+				{
+					switch (room->exit_dirs)
+					{
+						case MAP_DIR_N|MAP_DIR_E:
+							exits = 16 + 4;
+							break;
+						case MAP_DIR_S|MAP_DIR_E:
+							exits = 16 + 5;
+							break;
+						case MAP_DIR_S|MAP_DIR_W:
+							exits = 16 + 6;
+							break;
+						case MAP_DIR_N|MAP_DIR_W:
+							exits = 16 + 7;
+							break;
+					}
+				}
+				sprintf(buf, "%s%s", room_color, ses->map->legend[offset + exits]);
+			}
+		}
 	}
+	pop_call();
+	return buf;
 }
 
-DO_MAP(map_roomflag)
+void search_keywords(struct session *ses, char *arg, char *out, char *var)
 {
-	char buf[BUFFER_SIZE], *str, arg3[BUFFER_SIZE];
-	int flag = 0;
+	char buf[MAP_SEARCH_MAX][BUFFER_SIZE], arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
+	int type, max;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
+	push_call("search_keywords(%p,%p,%p,%p)",ses,arg,out,var);
 
-	if (*arg1 == 0)
+	for (type = 0 ; type < MAP_SEARCH_MAX ; type++)
 	{
-		tintin_printf2(ses, "#MAP: Avoid flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_AVOID) ? "ON" : "off");
-		tintin_printf2(ses, "#MAP: Hide flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_HIDE) ? "ON" : "off");
-		tintin_printf2(ses, "#MAP: Invis flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_INVIS) ? "ON" : "off");
-		tintin_printf2(ses, "#MAP: Leave flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_LEAVE) ? "ON" : "off");
-		tintin_printf2(ses, "#MAP: Void flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_VOID) ? "ON" : "off");
-		tintin_printf2(ses, "#MAP: Static flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_STATIC) ? "ON" : "off");
-		tintin_printf2(ses, "#MAP: Curved flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_CURVED) ? "ON" : "off");
-		tintin_printf2(ses, "#MAP: NoGlobal flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_NOGLOBAL) ? "ON" : "off");
-		return;
+		buf[type][0] = 0;
 	}
 
-	str = arg1;
+	var[0] = 0;
 
-	while (*str)
+	type = 0;
+
+	while (*arg && type < MAP_SEARCH_MAX)
 	{
-		str = get_arg_in_braces(ses, str, buf, GET_ONE);
+		arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
 
-		if (is_abbrev(buf, "avoid"))
+		if (*arg1 == '{')
 		{
-			SET_BIT(flag, ROOM_FLAG_AVOID);
+			strcpy(arg2, arg1);
+
+			arg = get_arg_in_braces(ses, arg2, arg1, GET_ALL);
 		}
-		else if (is_abbrev(buf, "curved"))
+
+		if (!strcasecmp(arg1, "roomid"))
 		{
-			SET_BIT(flag, ROOM_FLAG_CURVED);
+			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_ID], GET_ALL, SUB_VAR|SUB_FUN);
 		}
-		else if (is_abbrev(buf, "hide"))
+		else if (!strcasecmp(arg1, "roomname"))
 		{
-			SET_BIT(flag, ROOM_FLAG_HIDE);
+			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_NAME], GET_ALL, SUB_VAR|SUB_FUN);
 		}
-		else if (is_abbrev(buf, "invisible"))
+		else if (!strcasecmp(arg1, "roomexits"))
 		{
-			SET_BIT(flag, ROOM_FLAG_INVIS);
+			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_EXITS], GET_ALL, SUB_VAR|SUB_FUN);
 		}
-		else if (is_abbrev(buf, "leave"))
+		else if (!strcasecmp(arg1, "roomdesc"))
 		{
-			SET_BIT(flag, ROOM_FLAG_LEAVE);
+			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_DESC], GET_ALL, SUB_VAR|SUB_FUN);
 		}
-		else if (is_abbrev(buf, "noglobal"))
+		else if (!strcasecmp(arg1, "roomarea"))
 		{
-			SET_BIT(flag, ROOM_FLAG_NOGLOBAL);
+			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_AREA], GET_ALL, SUB_VAR|SUB_FUN);
 		}
-		else if (is_abbrev(buf, "static"))
+		else if (!strcasecmp(arg1, "roomnote"))
 		{
-			SET_BIT(flag, ROOM_FLAG_STATIC);
+			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_NOTE], GET_ALL, SUB_VAR|SUB_FUN);
 		}
-		else if (is_abbrev(buf, "void"))
+		else if (!strcasecmp(arg1, "roomterrain"))
 		{
-			SET_BIT(flag, ROOM_FLAG_VOID);
+			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_TERRAIN], GET_ALL, SUB_VAR|SUB_FUN);
+		}
+		else if (!strcasecmp(arg1, "roomflag"))
+		{
+			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_FLAG], GET_ALL, SUB_VAR|SUB_FUN);
+		}
+		else if (!strcasecmp(arg1, "variable"))
+		{
+			arg = sub_arg_in_braces(ses, arg, var, GET_ALL, SUB_VAR|SUB_FUN);
 		}
 		else
 		{
-			show_error(ses, LIST_COMMAND, "#MAP: Invalid room flag {%s}.", buf);
-
-			return;
+			strcpy(buf[type++], arg1);
 		}
+	}
 
-		if (*str == COMMAND_SEPARATOR)
+	for (max = MAP_SEARCH_MAX - 1 ; max >= 0 ; max--)
+	{
+		if (*buf[max])
 		{
-			str++;
+			break;
 		}
 	}
 
-	if (*arg2 == 0)
+	out[0] = 0;
+
+	for (type = 0 ; type <= max ; type++)
 	{
-		TOG_BIT(ses->map->room_list[ses->map->in_room]->flags, flag);	
+		cat_sprintf(out, "{%s}", buf[type]);
 	}
-	else if (is_abbrev(arg2, "ON"))
+	pop_call();
+	return;
+}
+
+void map_search_compile(struct session *ses, char *arg, char *var)
+{
+	char tmp[BUFFER_SIZE], buf[BUFFER_SIZE], *ptb;
+	struct listnode *node;
+
+	push_call("map_search_compile(%p,%p,%p)",ses,arg,var);
+
+	search_keywords(ses, arg, tmp, var);
+
+	arg = sub_arg_in_braces(ses, tmp, buf, GET_ALL, SUB_VAR|SUB_FUN); // name
+
+	if (is_math(ses, buf))
 	{
-		SET_BIT(ses->map->room_list[ses->map->in_room]->flags, flag);
+		ses->map->search->vnum = (int) get_number(ses, buf);
 	}
-	else if (is_abbrev(arg2, "OFF"))
+	else
 	{
-		DEL_BIT(ses->map->room_list[ses->map->in_room]->flags, flag);
+		ses->map->search->vnum = 0;
 	}
-	else if (is_abbrev(arg2, "GET"))
+
+	if (ses->map->search->vnum)
 	{
-		if (*arg3 == 0)
-		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX #MAP ROOMFLAG {%s} {GET} {<VARIABLE>}.", buf);
-		}
-		else
-		{
-			set_nest_node(ses->list[LIST_VARIABLE], arg3, "%d", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, flag));
-		}
+		pop_call();
 		return;
 	}
-	else
+
+	if (ses->map->search->arg)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX #MAP ROOMFLAG {%s} {[GET|ON|OFF]}.", buf);
+		free(ses->map->search->arg);
 	}
 
-
-	if (HAS_BIT(flag, ROOM_FLAG_AVOID))
+	if (*buf)
 	{
-		show_message(ses, LIST_COMMAND, "#MAP: Avoid flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_AVOID) ? "ON" : "off");
+		ses->map->search->arg = strdup(buf);
+
+		node = search_node_list(ses->list[LIST_LANDMARK], ses->map->search->arg);
+
+		if (node)
+		{
+			ses->map->search->vnum = node->val32[0];
+
+			if (ses->map->search->vnum)
+			{
+				pop_call();
+				return;
+			}
+		}
 	}
-	if (HAS_BIT(flag, ROOM_FLAG_CURVED))
+	else
 	{
-		show_message(ses, LIST_COMMAND, "#MAP: Curved flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_CURVED) ? "ON" : "off");
+		ses->map->search->arg = NULL;
 	}
-	if (HAS_BIT(flag, ROOM_FLAG_HIDE))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: Hide flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_HIDE) ? "ON" : "off");
-	}
-	if (HAS_BIT(flag, ROOM_FLAG_INVIS))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: Invis flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_INVIS) ? "ON" : "off");
-	}
-	if (HAS_BIT(flag, ROOM_FLAG_LEAVE))
-	{
-		show_message(ses, LIST_COMMAND, "#MAP: Leave flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_LEAVE) ? "ON" : "off");
-	}
-	if (HAS_BIT(flag, ROOM_FLAG_NOGLOBAL))
+
+	if (ses->map->search->name)
 	{
-		show_message(ses, LIST_COMMAND, "#MAP: NoGlobal flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_NOGLOBAL) ? "ON" : "off");
+		free(ses->map->search->name);
 	}
-	if (HAS_BIT(flag, ROOM_FLAG_VOID))
+
+	if (*buf)
 	{
-		show_message(ses, LIST_COMMAND, "#MAP: Void flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_VOID) ? "ON" : "off");
+		strcat(buf, "$");
+
+		ses->map->search->name = tintin_regexp_compile(ses, NULL, buf, PCRE_ANCHORED);
 	}
-	if (HAS_BIT(flag, ROOM_FLAG_STATIC))
+	else
 	{
-		show_message(ses, LIST_COMMAND, "#MAP: Static flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_STATIC) ? "ON" : "off");
+		ses->map->search->name = NULL;
 	}
 
-}
-
-DO_MAP(map_set)
-{
-	struct room_data *room = ses->map->room_list[ses->map->in_room];
-	char arg3[BUFFER_SIZE];
+	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // exits
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
+	ses->map->search->exit_dirs = 0;
+	ses->map->search->exit_size = 0;
 
-	if (*arg3)
+	if (ses->map->search->exit_list)
 	{
-		if (atoi(arg3) > 0 && atoi(arg3) < ses->map->size)
-		{
-			room = ses->map->room_list[atoi(arg3)];
-		}
-		else
-		{
-			room = NULL;
-		}
+		free(ses->map->search->exit_list);
 	}
 
-	if (room == NULL)
-	{
-		show_message(ses, LIST_COMMAND, "#MAP SET: invalid room vnum: %s", arg3);
-	}
-	else if (*arg1 == 0)
-	{
-		tintin_printf2(ses, "   roomarea: %s", room->area);
-		tintin_printf2(ses, "  roomcolor: %s", room->color);
-		tintin_printf2(ses, "   roomdata: %s", room->data);
-		tintin_printf2(ses, "   roomdesc: %s", room->desc);
-		tintin_printf2(ses, "  roomflags: %d", room->flags);
-		tintin_printf2(ses, "     roomid: %s", room->id);
-		tintin_printf2(ses, "   roomname: %s", room->name);
-		tintin_printf2(ses, "   roomnote: %s", room->note);
-		tintin_printf2(ses, " roomsymbol: %s", room->symbol);
-		tintin_printf2(ses, "roomterrain: %s", room->terrain);
-		tintin_printf2(ses, " roomweight: %.3f", room->weight);
-	}
-	else
+	if (*buf)
 	{
-		if (is_abbrev(arg1, "roomarea"))
-		{
-			RESTRING(room->area, arg2);
-			show_message(ses, LIST_COMMAND, "#MAP SET: roomarea set to: %s", room->area);
-		}
-		else if (is_abbrev(arg1, "roomcolor"))
-		{
-			RESTRING(room->color, arg2);
-			show_message(ses, LIST_COMMAND, "#MAP SET: roomcolor set to: %s", arg2);
-		}
-		else if (is_abbrev(arg1, "roomdata"))
-		{
-			RESTRING(room->data, arg2);
-			show_message(ses, LIST_COMMAND, "#MAP SET: roomdata set to: %s", arg2);
-		}
-		else if (is_abbrev(arg1, "roomdesc"))
-		{
-			RESTRING(room->desc, arg2);
-			show_message(ses, LIST_COMMAND, "#MAP SET: roomdesc set to: %s", arg2);
-		}
-		else if (is_abbrev(arg1, "roomflags"))
-		{
-			room->flags = (int) get_number(ses, arg2);
-
-			show_message(ses, LIST_COMMAND, "#MAP SET: roomflags set to: %d", room->flags);
-		}
-		else if (is_abbrev(arg1, "roomid"))
-		{
-			RESTRING(room->id, arg2);
-
-			show_message(ses, LIST_COMMAND, "#MAP SET: roomid set to: %s", room->id);
-		}
-		else if (is_abbrev(arg1, "roomname"))
-		{
-			RESTRING(room->name, arg2);
+		struct listnode *node;
+		char exit[BUFFER_SIZE];
+		ptb = buf;
 
-			show_message(ses, LIST_COMMAND, "#MAP SET: roomname set to: %s", room->name);
-		}
-		else if (is_abbrev(arg1, "roomnote"))
-		{
-			RESTRING(room->note, arg2);
-			show_message(ses, LIST_COMMAND, "#MAP SET: roomnote set to: %s", arg2);
-		}
-		else if (is_abbrev(arg1, "roomsymbol"))
-		{
-			RESTRING(room->symbol, arg2);
+		tmp[0] = 0;
 
-			show_message(ses, LIST_COMMAND, "#MAP SET: roomsymbol set to: %s", room->symbol);
-		}
-		else if (is_abbrev(arg1, "roomterrain"))
-		{
-			RESTRING(room->terrain, arg2);
-			show_message(ses, LIST_COMMAND, "#MAP SET: roomterrain set to: %s", arg2);
-		}
-		else if (is_abbrev(arg1, "roomweight"))
+		if (is_math(ses, buf))
 		{
-			if (get_number(ses, arg2) < 0.001)
-			{
-				show_message(ses, LIST_COMMAND, "#MAP SET: roomweight should be at least 0.001");
-			}
-			else
-			{
-				room->weight = (float) get_number(ses, arg2);
+			ses->map->search->exit_dirs = get_number(ses, buf);
 
-				show_message(ses, LIST_COMMAND, "#MAP SET: roomweight set to: %.3f", room->weight);
-			}
+			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_N))  ses->map->search->exit_size++;
+			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_E))  ses->map->search->exit_size++;
+			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_S))  ses->map->search->exit_size++;
+			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_W))  ses->map->search->exit_size++;
+			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_U))  ses->map->search->exit_size++;
+			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_D))  ses->map->search->exit_size++;
+			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_NE)) ses->map->search->exit_size++;
+			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_NW)) ses->map->search->exit_size++;
+			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_SE)) ses->map->search->exit_size++;
+			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_SW)) ses->map->search->exit_size++;
 		}
 		else
 		{
-			show_message(ses, LIST_COMMAND, "#MAP SET: unknown option: %s", arg1);
-		}
-	}
-}
+			while (*ptb)
+			{
+				ptb = get_arg_in_braces(ses, ptb, exit, GET_ONE);
 
-DO_MAP(map_travel)
-{
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+				node = search_node_list(ses->list[LIST_PATHDIR], exit);
 
-	explore_path(ses, TRUE, arg1, arg2);
-}
+				ses->map->search->exit_size++;
 
+				if (node)
+				{
+					SET_BIT(ses->map->search->exit_dirs, 1LL << atoi(node->arg3));
+				}
+				else
+				{
+					SET_BIT(ses->map->search->exit_dirs, 1); // flag indicates no exits
 
-DO_MAP(map_uninsert)
-{
-	int room1, room2, room3;
-	struct exit_data *exit1, *exit2, *exit3;
-	struct listnode *node;
+					cat_sprintf(tmp, "{%s}", exit);
+				}
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+				if (*ptb == COMMAND_SEPARATOR)
+				{
+					ptb++;
+				}
+			}
+		}
+		ses->map->search->exit_list = strdup(tmp);
+	}
+	else
+	{
+		ses->map->search->exit_list = strdup("");
+	}
 
-	room1 = ses->map->in_room;
-	exit1 = find_exit(ses, room1, arg1);
+	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // desc
 
-	node = search_node_list(ses->list[LIST_PATHDIR], arg1);
+	if (ses->map->search->desc)
+	{
+		free(ses->map->search->desc);
+	}
 
-	if (exit1 == NULL)
+	if (*buf)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP UNINSERT: There is no room in that direction.");
+		strcat(buf, "$");
 
-		return;
+		ses->map->search->desc = tintin_regexp_compile(ses, NULL, buf, PCRE_ANCHORED);
 	}
-
-	if (node == NULL)
+	else
 	{
-		show_error(ses, LIST_COMMAND, "#MAP UNINSERT: Given direction must be a pathdir.");
-		return;
+		ses->map->search->desc = NULL;
 	}
 
-	room2 = exit1->vnum;
-	exit2 = find_exit(ses, room2, node->arg1);
+	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // area
 
-	if (exit2 == NULL)
+	if (ses->map->search->area)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP UNINSERT: Unable to find backlink room.");
-		return;
+		free(ses->map->search->area);
 	}
 
-	room3 = exit2->vnum;
-	exit3 = find_exit(ses, room3, node->arg2);
-
-	if (exit3 == NULL)
+	if (*buf)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP UNINSERT: Unable to find backlink exit.");
+		strcat(buf, "$");
 
-		return;
+		ses->map->search->area = tintin_regexp_compile(ses, NULL, buf, PCRE_ANCHORED);
 	}
-
-	exit1->vnum = room3;
-	exit3->vnum = room1;
-
-	delete_room(ses, room2, TRUE);
-
-	show_message(ses, LIST_COMMAND, "#MAP UNINSERT: Uninserted room {%d}.", room2);
-}
-
-// 1) timestamp 2) type 3) data
-
-DO_MAP(map_undo)
-{
-	struct link_data *link;
-	struct room_data *room;
-	int undo_flag;
-	struct exit_data *exit1, *exit2, *exit3;
-
-	link = ses->map->undo_tail;
-
-	if (link == NULL)
+	else
 	{
-		show_error(ses, LIST_COMMAND, "#MAP UNDO: No known last move.");
-
-		return;
+		ses->map->search->area = NULL;
 	}
 
-	room = ses->map->room_list[atoi(link->str1)];
+	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // note
 
-	if (room == NULL)
+	if (ses->map->search->note)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP UNDO: Room %s does not exist.", link->str2);
-		return;
+		free(ses->map->search->note);
 	}
 
-	if (ses->map->room_list[atoi(link->str2)] == NULL)
+	if (*buf)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP UNDO: Invalid last move.");
-		return;
+		strcat(buf, "$");
+
+		ses->map->search->note = tintin_regexp_compile(ses, NULL, buf, PCRE_ANCHORED);
+	}
+	else
+	{
+		ses->map->search->note = NULL;
 	}
 
-	undo_flag = atoi(link->str3);
+	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // terrain
 
-	if (HAS_BIT(undo_flag, MAP_UNDO_MOVE))
+	if (ses->map->search->terrain)
 	{
- 		if (ses->map->in_room != room->vnum)
-		{
-			show_error(ses, LIST_COMMAND, "#MAP UNDO: Invalid last move.");
-			return;
-		}
-		show_message(ses, LIST_COMMAND, "#MAP UNDO: Moving to room %s.", link->str2);
-
-		goto_room(ses, atoi(link->str2));
+		free(ses->map->search->terrain);
 	}
 
-	if (HAS_BIT(undo_flag, MAP_UNDO_CREATE))
+	if (*buf)
 	{
-		show_message(ses, LIST_COMMAND, "#MAP UNDO: Deleting room %d.", room->vnum);
-		delete_room(ses, room->vnum, TRUE);
+		strcat(buf, "$");
+
+		ses->map->search->terrain = tintin_regexp_compile(ses, NULL, buf, PCRE_ANCHORED);
 	}
-	else if (HAS_BIT(undo_flag, MAP_UNDO_LINK))
+	else
 	{
-		exit1 = find_exit(ses, room->vnum, link->str2);
-
-		if (exit1)
-		{
-			show_message(ses, LIST_COMMAND, "#MAP UNDO: Deleting exit leading %s.", exit1->name);
-			delete_exit(ses, room->vnum, exit1);
-		}
+		ses->map->search->terrain = NULL;
+	}
 
-		exit2 = find_exit(ses, atoi(link->str2), link->str1);
+	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // flag
 
-		if (exit2)
-		{
-			show_message(ses, LIST_COMMAND, "#MAP UNDO: Deleting exit leading %s.", exit2->name);
-			delete_exit(ses, atoi(link->str2), exit2);
-		}
-	}
-	else if (HAS_BIT(undo_flag, MAP_UNDO_INSERT))
+	if (*buf)
 	{
-		exit1 = find_exit(ses, atoi(link->str2), link->str1);
+		char flags[BUFFER_SIZE];
 
-		if (exit1 == NULL)
-		{
-			show_error(ses, LIST_COMMAND, "#MAP UNDO: Can't find exit between %s and %s.", link->str2, link->str1);
-			return;
-		}
+		ses->map->search->flag = get_number(ses, buf);
 
-		exit2 = find_exit(ses, room->vnum, exit1->name);
+		ptb = buf;
 
-		if (exit2 == NULL)
+		while (*buf)
 		{
-			show_error(ses, LIST_COMMAND, "#MAP UNDO: No valid exit found in room %d.", room->vnum);
-			return;
-		}
+			ptb = sub_arg_in_braces(ses, ptb, flags, GET_ONE, SUB_NONE);
 
-		exit3 = find_exit(ses, exit2->vnum, ntos(room->vnum));
+			if (is_abbrev(buf, "avoid"))
+			{
+				SET_BIT(ses->map->search->flag, ROOM_FLAG_AVOID);
+			}
+			else if (is_abbrev(buf, "curved"))
+			{
+				SET_BIT(ses->map->search->flag, ROOM_FLAG_CURVED);
+			}
+			else if (is_abbrev(buf, "hide"))
+			{
+				SET_BIT(ses->map->search->flag, ROOM_FLAG_HIDE);
+			}
+			else if (is_abbrev(buf, "invis"))
+			{
+				SET_BIT(ses->map->search->flag, ROOM_FLAG_INVIS);
+			}
+			else if (is_abbrev(buf, "leave"))
+			{
+				SET_BIT(ses->map->search->flag, ROOM_FLAG_LEAVE);
+			}
+			else if (is_abbrev(buf, "void"))
+			{
+				SET_BIT(ses->map->search->flag, ROOM_FLAG_VOID);
+			}
+			else if (is_abbrev(buf, "static"))
+			{
+				SET_BIT(ses->map->search->flag, ROOM_FLAG_STATIC);
+			}
 
-		if (exit3 == NULL)
-		{
-			show_error(ses, LIST_COMMAND, "#MAP UNDO: Can't find exit between %d and %d.", room->vnum, exit2->vnum);
-			return;
+			if (*ptb == COMMAND_SEPARATOR)
+			{
+				ptb++;
+			}
 		}
-
-		exit1->vnum = exit2->vnum;
-		exit3->vnum = atoi(link->str2);
-
-		delete_room(ses, room->vnum, TRUE);
-
-		show_message(ses, LIST_COMMAND, "#MAP UNDO: Uninserting room %s.", link->str1);
 	}
-	del_undo(ses, link);
-}
-
-DO_MAP(map_unlink)
-{
-	struct exit_data *exit1;
-	struct exit_data *exit2;
-	struct listnode *node;
-
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
-
-	node = search_node_list(ses->list[LIST_PATHDIR], arg1);
+	else
+	{
+		ses->map->search->flag = 0;
+	}
 
-	exit1 = find_exit(ses, ses->map->in_room, arg1);
+	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // id
 
-	if (exit1 == NULL)
+	if (ses->map->search->id)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP UNLINK: No exit with that name found");
-
-		return;
+		free(ses->map->search->id);
 	}
 
-	if (*arg2 == 'b' || *arg == 'B')
+	if (*buf)
 	{
-		if (node)
-		{
-			exit2 = find_exit(ses, exit1->vnum, node->arg2);
-
-			if (exit2)
-			{
-				delete_exit(ses, exit1->vnum, exit2);
-			}
-		}
+		ses->map->search->id = strdup(buf);
+	}
+	else
+	{
+		ses->map->search->id = NULL;
 	}
 
-	delete_exit(ses, ses->map->in_room, exit1);
-
-	show_message(ses, LIST_COMMAND, "#MAP UNLINK: Exit deleted.");
+	pop_call();
+	return;
 }
 
-DO_MAP(map_update)
+int match_room(struct session *ses, int vnum, struct search_data *search)
 {
-	if (ses->map == NULL)
-	{
-		show_message(ses, LIST_COMMAND, "#MAP UPDATE: NO MAP DATA.");
-	}
-	else if (ses->map->room_list[ses->map->in_room] == NULL)
+	struct room_data *room = ses->map->room_list[vnum];
+
+	if (room == NULL)
 	{
-		show_message(ses, LIST_COMMAND, "#MAP UPDATE: NOT INSIDE MAP.");
+		return 0;
 	}
-	else if (!HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP))
+
+	if (search->vnum)
 	{
-		show_message(ses, LIST_COMMAND, "#MAP UPDATE: VTMAP FLAG NOT SET.");
+		return room->vnum == search->vnum;
 	}
-	else if (ses != gtd->ses)
+
+	if (search->id)
 	{
-		show_message(ses, LIST_COMMAND, "#MAP UPDATE: NOT THE ACTIVE SESSION.");
+		return !strcmp(room->id, search->id);
 	}
-	else
+
+	if (search->name)
 	{
-		show_message(ses, LIST_COMMAND, "#MAP UPDATE: OK.");
-		
-		SET_BIT(ses->flags, SES_FLAG_UPDATEVTMAP);
+		if (!regexp_compare(ses, search->name, room->name, "", 0, 0))
+		{
+			return 0;
+		}
 	}
-}
 
-DO_MAP(map_run)
-{
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+	if (search->exit_dirs)
+	{
+		char *arg, exit[BUFFER_SIZE];
 
-	shortest_path(ses, TRUE, arg2, arg1);
-}
+		if (search->exit_dirs != room->exit_dirs)
+		{
+			return 0;
+		}
+		if (search->exit_size != room->exit_size)
+		{
+			return 0;
+		}
 
-DO_MAP(map_vnum)
-{
-	int vnum, vnum1, vnum2, old_room, new_room;
-	struct exit_data *exit;
+		arg = search->exit_list;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+		while (*arg)
+		{
+			arg = get_arg_in_braces(ses, arg, exit, GET_ONE);
 
-	vnum1 = atoi(arg1);
+			if (!find_exit(ses, vnum, exit))
+			{
+				return 0;
+			}
 
-	if (*arg2)
-	{
-		vnum2 = atoi(arg2);
+			if (*arg == COMMAND_SEPARATOR)
+			{
+				arg++;
+			}
+		}
 	}
-	else
+
+	if (search->desc)
 	{
-		vnum2 = vnum1;
+		if (!regexp_compare(ses, search->desc, room->desc, "", 0, 0))
+		{
+			return 0;
+		}
 	}
-	
-	if (vnum1 <= 0 || vnum1 >= ses->map->size || vnum2 <= 0 || vnum2 >= ses->map->size)
+
+	if (search->area)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP VNUM {%s} {%s} - VNUMS MUST BE BETWEEN {1} and {%d}", arg1, arg2, ses->map->size - 1);
-		return;
+		if (!regexp_compare(ses, search->area, room->area, "", 0, 0))
+		{
+			return 0;
+		}
 	}
 
-	for (vnum = vnum1 ; vnum <= vnum2 ; vnum++)
+	if (search->note)
 	{
-		if (ses->map->room_list[vnum] == NULL)
+		if (!regexp_compare(ses, search->note, room->note, "", 0, 0))
 		{
-			break;
+			return 0;
 		}
 	}
 
-	if (vnum > vnum2)
+	if (search->terrain)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP VNUM {%s} {%s} - NO FREE VNUM FOUND.", arg1, arg2);
-		return;
+		if (!regexp_compare(ses, search->terrain, room->terrain, "", 0, 0))
+		{
+			return 0;
+		}
 	}
 
-	old_room = ses->map->in_room;
-	new_room = vnum;
-
-	ses->map->room_list[new_room] = ses->map->room_list[old_room];
-	ses->map->room_list[new_room]->vnum = new_room;
-	ses->map->room_list[old_room] = NULL;
-	ses->map->in_room = new_room;
-
-	if (ses->map->at_room == old_room)
+	if (search->flag)
 	{
-		ses->map->at_room = new_room;
+		if ((room->flags & search->flag) != search->flag)
+		{
+			return 0;
+		}
 	}
+	return 1;
+}
 
-	for (vnum = 1 ; vnum < ses->map->size ; vnum++)
+int find_path(struct session *ses, char *arg)
+{
+	struct exit_data *exit;
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
+	int room;
+
+	push_call("find_path(%p,%p)",ses,arg);
+
+	arg = substitute_speedwalk(ses, arg, arg1);
+
+	room = ses->map->in_room;
+
+	while (*arg)
 	{
-		if (ses->map->room_list[vnum] == NULL)
+		arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
+
+		exit = find_exit(ses, room, arg2);
+
+		if (exit == NULL)
 		{
-			continue;
+			pop_call();
+			return 0;
 		}
 
-		for (exit = ses->map->room_list[vnum]->f_exit ; exit ; exit = exit->next)
+		room = tunnel_void(ses, room, exit->vnum, exit->dir);
+
+		if (*arg == COMMAND_SEPARATOR)
 		{
-			if (exit->vnum == old_room)
-			{
-				exit->vnum = new_room;
-			}
+			arg++;
 		}
 	}
 
-	tintin_printf(ses, "#MAP VNUM: MOVED ROOM %d TO %d.", old_room, new_room);
+	pop_call();
+	return room == ses->map->in_room ? 0 : room;
 }
 
-DO_MAP(map_write)
+int find_location(struct session *ses, char *arg)
 {
-	FILE *file;
-	struct exit_data *exit;
-	int index;
-
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+	struct listnode *node;
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE];
+	int x, y, z;
+	
+	push_call("find_location(%p,%p)",ses,arg);
 
-	if (*arg1 == 0)
+	if (find_exit(ses, ses->map->in_room, arg))
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP WRITE {<filename>} {FORCE}");
-
-		return;
+		pop_call();
+		return find_exit(ses, ses->map->in_room, arg)->vnum;
 	}
 
-	if (!str_suffix(arg1, ".tin") && !is_abbrev(arg2, "FORCE"))
+	if (is_math(ses, arg))
 	{
-		show_error(ses, LIST_COMMAND, "#MAP WRITE {%s}: USE {FORCE} TO OVERWRITE .tin FILES.");
+		arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+		arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+		arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
 
-		return;
+		x = get_number(ses, arg1);
+		y = get_number(ses, arg2);
+		z = get_number(ses, arg3);
 	}
-
-	if ((file = fopen(arg1, "w")) == NULL)
+	else
 	{
-		show_error(ses, LIST_COMMAND, "#MAP WRITE {%s} - COULDN'T OPEN FILE TO WRITE.", arg1);
-
-		return;
-	}
+		x = y = z = 0;
 
-	fprintf(file, "C %d\n\n", ses->map->size);
-
-	fprintf(file, "V 2020\n\n");
-
-	for (index = 0 ; map_color_table[index].name ; index++)
-	{
-		fprintf(file, "C%s %s\n", map_color_table[index].name, ses->map->color[index]);
-	}
-	fprintf(file, "\n");
+		arg = substitute_speedwalk(ses, arg, arg2);
 
-	fprintf(file, "F %d\n\n", ses->map->flags);
+		while (*arg)
+		{
+			arg = get_arg_in_braces(ses, arg, arg3, GET_ALL);
 
-	fprintf(file, "G %d\n\n", ses->map->global_vnum);
+			node = search_node_list(ses->list[LIST_PATHDIR], arg3);
 
-	fprintf(file, "I %d\n\n", ses->map->in_room ? ses->map->in_room : ses->map->last_room);
+			if (node == NULL)
+			{
+//				show_error(ses, LIST_COMMAND, "#ERROR: #MAP FIND_LOCATION: {%s} IS AN INVALID PATHDIR.", arg3);
 
-	for (index = 0 ; map_legend_table[index].name ; index++)
-	{
-		fprintf(file, "L {%s} {%s} {%s}\n", map_legend_table[index].group, map_legend_table[index].name, ses->map->legend_raw[index]);
-	}
-	fprintf(file, "\n\n");
+				pop_call();
+				return 0;
+			}
 
-	for (index = 0 ; index < ses->map->size ; index++)
-	{
-		if (ses->map->room_list[index])
-		{
-			fprintf(file, "\nR {%5d} {%d} {%s} {%s} {%s} {%s} {%s} {%s} {%s} {%s} {%.3f} {%s}\n",
-				ses->map->room_list[index]->vnum,
-				ses->map->room_list[index]->flags,
-				ses->map->room_list[index]->color,
-				ses->map->room_list[index]->name,
-				ses->map->room_list[index]->symbol,
-				ses->map->room_list[index]->desc,
-				ses->map->room_list[index]->area,
-				ses->map->room_list[index]->note,
-				ses->map->room_list[index]->terrain,
-				ses->map->room_list[index]->data,
-				ses->map->room_list[index]->weight,
-				ses->map->room_list[index]->id);
+			x += (HAS_BIT(atoi(node->arg3), MAP_EXIT_E) ? 1 : HAS_BIT(atoi(node->arg3), MAP_EXIT_W) ? -1 : 0);
+			y += (HAS_BIT(atoi(node->arg3), MAP_EXIT_N) ? 1 : HAS_BIT(atoi(node->arg3), MAP_EXIT_S) ? -1 : 0);
+			z += (HAS_BIT(atoi(node->arg3), MAP_EXIT_U) ? 1 : HAS_BIT(atoi(node->arg3), MAP_EXIT_D) ? -1 : 0);
 
-			for (exit = ses->map->room_list[index]->f_exit ; exit ; exit = exit->next)
+			if (*arg == COMMAND_SEPARATOR)
 			{
-				fprintf(file, "E {%5d} {%s} {%s} {%d} {%d} {%s} {%.3f}\n",
-					exit->vnum,
-					exit->name,
-					exit->cmd,
-					exit->dir,
-					exit->flags,
-					exit->data,
-					exit->weight);
+				arg++;
 			}
 		}
 	}
 
-	fclose(file);
-
-	show_message(ses, LIST_COMMAND, "#MAP: Map file written to {%s}.", arg1);
+	pop_call();
+	return spatialgrid_find(ses, ses->map->in_room, x, y, z);
 }
 
-void create_map(struct session *ses, char *arg)
+int find_room(struct session *ses, char *arg)
 {
-	int group, legend;
+	char var[BUFFER_SIZE];
+	struct listnode *node;
+	int room;
 
-	push_call("create_map(%p,%p)",ses,arg);
+	push_call("find_room(%p,%s)",ses,arg);
 
-	if (ses->map)
+	map_search_compile(ses, arg, var);
+
+	if (ses->map->search->vnum > 0 && ses->map->search->vnum < ses->map->size)
 	{
-		delete_map(ses);
+		if (ses->map->room_list[ses->map->search->vnum])
+		{
+			pop_call();
+			return ses->map->search->vnum;
+		}
+		pop_call();
+		return 0;
 	}
 
-	ses->map = (struct map_data *) calloc(1, sizeof(struct map_data));
-	ses->map->size = atoi(arg) > 0 ? atoi(arg) : 50000;
-
-	ses->map->room_list = (struct room_data **) calloc(ses->map->size, sizeof(struct room_data *));
-
-	ses->map->max_grid_x = 255;
-	ses->map->max_grid_y = 101;
-
-	ses->map->grid_rooms = (struct room_data **) calloc(ses->map->max_grid_x * ses->map->max_grid_y, sizeof(struct room_data *));
-	ses->map->grid_flags =             (int *) calloc(ses->map->max_grid_x * ses->map->max_grid_y, sizeof(struct room_data *));
-
-	ses->map->search = (struct search_data *) calloc(1, sizeof(struct search_data));
-
-	ses->map->flags = MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_DIRECTION;
-
-	ses->map->global_exit         = (struct exit_data *) calloc(1, sizeof(struct exit_data));
-		ses->map->global_exit->vnum   = ses->map->global_vnum;
-		ses->map->global_exit->name   = restringf(ses->map->global_exit->name, "%cnop global", gtd->tintin_char);
-		ses->map->global_exit->cmd    = restringf(ses->map->global_exit->cmd, "%cnop global", gtd->tintin_char);
-		ses->map->global_exit->data   = strdup("");
-		ses->map->global_exit->weight = 1;
-
-	do_map(ses, "{COLOR} {RESET}");
-
-	ses->map->display_stamp = 1;
-	ses->map->search->stamp = 1;
-
-	create_room(ses, "%s", "{1} {0} {} {} { } {} {} {} {} {} {1.0} {}");
-
-	strcpy(arg, "");
-
-	for (group = 0 ; map_group_table[group].name ; group++)
+	if (ses->map->search->arg)
 	{
-		for (legend = 0 ; map_legend_table[legend].group ; legend++)
+		node = search_node_list(ses->list[LIST_LANDMARK], ses->map->search->arg);
+
+		if (node)
 		{
-			if (*map_group_table[group].group == 0 || is_abbrev(map_group_table[group].group, map_legend_table[legend].group))
+			if (ses->map->room_list[node->val32[0]])
 			{
-				break;
+				pop_call();
+				return node->val32[0];
 			}
+			pop_call();
+			return 0;
 		}
+	}
 
-		if (map_legend_table[legend].group)
+	if (ses->map->in_room)
+	{
+		room = searchgrid_find(ses, ses->map->in_room, ses->map->search);
+
+		if (room)
 		{
-			map_group_table[group].start = legend;
+			pop_call();
+			return room;
 		}
-		else
-		{
-			show_error(ses, LIST_COMMAND, "create_map: unknown legend group: %s, %s", map_group_table[group].name, map_group_table[group].group);
+	}
 
+	for (room = 0 ; room < ses->map->size ; room++)
+	{
+		if (ses->map->room_list[room] == NULL)
+		{
 			continue;
 		}
 
-		while (map_legend_table[++legend].group)
+		if (!match_room(ses, room, ses->map->search))
 		{
-			if (*map_group_table[group].group && !is_abbrev(map_group_table[group].group, map_legend_table[legend].group))
-			{
-				break;
-			}
+			continue;
 		}
-		map_group_table[group].end = legend;
+		pop_call();
+		return room;
 	}
-				
-	gtd->level->quiet++;
-	do_map(ses, "LEGEND RESET");
-	gtd->level->quiet--;
-
 	pop_call();
-	return;
+	return 0;
 }
 
-int delete_map(struct session *ses)
+void goto_room(struct session *ses, int room)
 {
-	int index, cnt;
-
-	for (index = cnt = 0 ; index < ses->map->size ; index++)
-	{
-		if (ses->map->room_list[index])
-		{
-			cnt++;
+	int last_room = ses->map->in_room;
 
-			delete_room(ses, index, FALSE);
-		}
-	}
-	free(ses->map->room_list);
+	push_call("goto_room(%p,%d)",ses,room);
 
-	while (ses->map->undo_head)
+	if (ses->map->in_room)
 	{
-		del_undo(ses, ses->map->undo_head);
+		check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "MAP EXIT ROOM", ntos(last_room), ntos(room));
+		check_all_events(ses, SUB_ARG|SUB_SEC, 1, 2, "MAP EXIT ROOM %d", last_room, ntos(last_room), ntos(room));
 	}
 
-	free(ses->map->global_exit->name);
-	free(ses->map->global_exit->cmd);
-	free(ses->map->global_exit->data);
-	free(ses->map->global_exit);
+	ses->map->in_room = room;
 
-	free(ses->map);
+	DEL_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_PATH);
 
-	ses->map = NULL;
+	if (last_room == 0)
+	{
+		check_all_events(ses, SUB_ARG|SUB_SEC, 0, 1, "MAP ENTER MAP", ntos(room));
+	}
 
-	return cnt;
+	check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "MAP ENTER ROOM", ntos(room), ntos(last_room));
+	check_all_events(ses, SUB_ARG|SUB_SEC, 1, 2, "MAP ENTER ROOM %d", room, ntos(room), ntos(last_room));
+
+	pop_call();
+	return;
 }
 
-int create_room(struct session *ses, char *format, ...)
+int find_new_room(struct session *ses)
 {
-	char *arg, buf[BUFFER_SIZE];
-	struct room_data *newroom;
-	va_list args;
-
-	va_start(args, format);
-	vsprintf(buf, format, args);
-	va_end(args);
-
-	newroom = (struct room_data *) calloc(1, sizeof(struct room_data));
-
-	arg = buf;
-
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->vnum    = atoi(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->flags   = atoi(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->color   = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->name    = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->symbol  = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->desc    = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->area    = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->note    = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->terrain = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->data    = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->weight  = atof(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE); newroom->id      = strdup(buf);
+	int room;
 
-	if (HAS_BIT(newroom->flags, ROOM_FLAG_AVOID))
-	{
-		SET_BIT(newroom->flags, ROOM_FLAG_AVOID_TMP);
-	}
-	if (HAS_BIT(newroom->flags, ROOM_FLAG_HIDE))
-	{
-		SET_BIT(newroom->flags, ROOM_FLAG_HIDE_TMP);
-	}
-	if (HAS_BIT(newroom->flags, ROOM_FLAG_LEAVE))
-	{
-		SET_BIT(newroom->flags, ROOM_FLAG_LEAVE_TMP);
-	}
-	if (HAS_BIT(newroom->flags, ROOM_FLAG_VOID))
-	{
-		SET_BIT(newroom->flags, ROOM_FLAG_VOID_TMP);
-	}
-	if (HAS_BIT(newroom->flags, ROOM_FLAG_CURVED))
+	for (room = 1 ; room < ses->map->size ; room++)
 	{
-		SET_BIT(newroom->flags, ROOM_FLAG_CURVED_TMP);
+		if (ses->map->room_list[room] == NULL)
+		{
+			break;
+		}
 	}
 
-	if (newroom->weight <= 0)
+	if (room == ses->map->size)
 	{
-		newroom->weight = 1;
-	}
-
-	ses->map->room_list[newroom->vnum] = newroom;
-
-	show_message(ses, LIST_COMMAND, "#MAP CREATE ROOM %5d {%s}.", newroom->vnum, newroom->name);
+		show_error(ses, LIST_COMMAND, "#MAP CREATE ROOM: Maximum amount of rooms of %d reached. Use #map resize.", ses->map->size);
 
-	return newroom->vnum;
+		return 0;
+	}
+	return room;
 }
 
-void delete_room(struct session *ses, int room, int exits)
+int dir_flags(struct session *ses, int room, int dir)
 {
-	struct exit_data *exit, *exit_next;
-	int cnt;
+	struct exit_data *exit;
 
-	while (ses->map->room_list[room]->f_exit)
+	for (exit = ses->map->room_list[room]->f_exit ; exit ; exit = exit->next)
 	{
-		delete_exit(ses, room, ses->map->room_list[room]->f_exit);
+		if (exit->dir == dir)
+		{
+			return exit->flags; /* | HAS_BIT(ses->map->room_list[exit->vnum]->flags, EXIT_FLAG_ALL);*/
+		}
 	}
+	return 0;
+}
 
+struct exit_data *find_exit(struct session *ses, int room, char *arg)
+{
+	struct exit_data *exit;
 
-	free(ses->map->room_list[room]->area);
-	free(ses->map->room_list[room]->color);
-	free(ses->map->room_list[room]->id);
-	free(ses->map->room_list[room]->name);
-	free(ses->map->room_list[room]->symbol);
-	free(ses->map->room_list[room]->desc);
-	free(ses->map->room_list[room]->note);
-	free(ses->map->room_list[room]->terrain);
-	free(ses->map->room_list[room]->data); 
-
-	free(ses->map->room_list[room]);
-
-	ses->map->room_list[room] = NULL;
-
-	if (exits)
+	for (exit = ses->map->room_list[room]->f_exit ; exit ; exit = exit->next)
 	{
-		for (cnt = 0 ; cnt < ses->map->size ; cnt++)
+//		if (!strcmp(exit->name, arg) || exit->vnum == atoi(arg))
+		if (!strcmp(exit->name, arg))
 		{
-			if (ses->map->room_list[cnt])
-			{
-				for (exit = ses->map->room_list[cnt]->f_exit ; exit ; exit = exit_next)
-				{
-					exit_next = exit->next;
-
-					if (exit->vnum == room)
-					{
-						delete_exit(ses, cnt, exit);
-					}
-				}
-			}
+			return exit;
 		}
 	}
+	return NULL;
 }
 
-struct exit_data *create_exit(struct session *ses, int vnum, char *format, ...)
+int check_global(struct session *ses, int room)
 {
-	struct exit_data *newexit;
-	struct room_data *room;
-	va_list args;
-	char *arg, buf[BUFFER_SIZE];
-
-	push_call("create_exit(%p,%d,%p)",ses,vnum,format);
-
-	va_start(args, format);
-	vsprintf(buf, format, args);
-	va_end(args);
-
-	newexit = (struct exit_data *) calloc(1, sizeof(struct exit_data));
-
-	room = ses->map->room_list[vnum];
-
-	arg = buf;
-
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);	newexit->vnum   = atoi(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);	newexit->name   = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ALL);	newexit->cmd    = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);	newexit->dir    = atoi(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);	newexit->flags  = atoi(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ALL);	newexit->data   = strdup(buf);
-	arg = get_arg_in_braces(ses, arg, buf, GET_ONE);	newexit->weight = atof(buf);
-
-	if (newexit->dir == 0)
+	if (HAS_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_NOGLOBAL))
 	{
-		newexit->dir = get_exit_dir(ses, newexit->name);
+		return FALSE;
 	}
-
-	newexit->grid = get_exit_grid(ses, newexit->dir);
-
-	if (room->exit_grid[newexit->grid] == NULL)
+	
+	if (ses->map->room_list[ses->map->global_vnum] == NULL)
 	{
-		room->exit_grid[newexit->grid] = newexit;
+		return FALSE;
 	}
 
-	if (newexit->weight <= 0)
+	if (room == ses->map->global_vnum)
 	{
-		newexit->weight = 1;
+		return FALSE;
 	}
-
-	LINK(newexit, room->f_exit, room->l_exit);
-
-	room->exit_size++;
-
-	SET_BIT(room->exit_dirs, (1LL << newexit->dir));
-
-	show_message(ses, LIST_COMMAND, "#MAP CREATE EXIT {%s} {%s} TO ROOM %d.", newexit->name, newexit->cmd, newexit->vnum);
-
-	pop_call();
-	return newexit;
-}
-
-void delete_exit(struct session *ses, int room, struct exit_data *exit)
-{
-	free(exit->name);
-	free(exit->cmd);
-	free(exit->data);
-
-	UNLINK(exit, ses->map->room_list[room]->f_exit, ses->map->room_list[room]->l_exit)
-
-	set_room_exits(ses, room);
-
-	free(exit);
+	return TRUE;
 }
 
-int get_exit_dir(struct session *ses, char *arg)
+int tunnel_void(struct session *ses, int from, int room, int dir)
 {
-	struct listnode *node;
-
-	node = search_node_list(ses->list[LIST_PATHDIR], arg);
-
-	if (node)
-	{
-		return atoi(node->arg3);
-	}
-	else
+	if (!HAS_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_VOID))
 	{
-		return 0;
+		return room;
 	}
-}
 
-int get_exit_grid(struct session *ses, int dir)
-{
-	switch (dir)
+	if (get_room_exits(ses, room) != 2)
 	{
-		case 0:
-			return EXIT_GRID_0;
-		case MAP_EXIT_N:
-			return EXIT_GRID_N;
-		case MAP_EXIT_E:
-			return EXIT_GRID_E;
-		case MAP_EXIT_S:
-			return EXIT_GRID_S;
-		case MAP_EXIT_W:
-			return EXIT_GRID_W;
-		case MAP_EXIT_N|MAP_EXIT_E:
-			return EXIT_GRID_NE;
-		case MAP_EXIT_N|MAP_EXIT_W:
-			return EXIT_GRID_NW;
-		case MAP_EXIT_S|MAP_EXIT_E:
-			return EXIT_GRID_SE;
-		case MAP_EXIT_S|MAP_EXIT_W:
-			return EXIT_GRID_SW;
+		struct exit_data *exit = ses->map->room_list[room]->exit_grid[dir_to_grid(dir)];
+
+		if (exit)
+		{
+			return tunnel_void(ses, room, exit->vnum, exit->dir);
+		}
+		return room;
 	}
 
-	if (HAS_BIT(dir, MAP_EXIT_D))
+	if (ses->map->room_list[room]->f_exit->vnum != from)
 	{
-		return EXIT_GRID_D;
+		return tunnel_void(ses, room, ses->map->room_list[room]->f_exit->vnum, ses->map->room_list[room]->f_exit->dir);
 	}
-
-	if (HAS_BIT(dir, MAP_EXIT_U))
+	else
 	{
-		return EXIT_GRID_U;
+		return tunnel_void(ses, room, ses->map->room_list[room]->l_exit->vnum, ses->map->room_list[room]->l_exit->dir);
 	}
-
-	return EXIT_GRID_0;
 }
 
-int get_room_exits(struct session *ses, int room)
-{
-	return ses->map->room_list[room]->exit_size;
-}
+// shortest_path() utilities
 
-void set_room_exits(struct session *ses, int vnum)
+int searchgrid_find(struct session *ses, int from, struct search_data *search)
 {
+	int vnum, head, tail, index;
+	float length;
+	struct grid_node *node, *temp, list[MAP_BF_SIZE];
 	struct exit_data *exit;
 	struct room_data *room;
 
-	room = ses->map->room_list[vnum];
+	search->stamp++;
 
-	room->exit_dirs = 0;
-	room->exit_size = 0;
+	head = 0;
+	tail = 1;
 
-	memset(room->exit_grid, 0, sizeof(struct exit_data *) * 11);
+	node = &list[head];
 
-	for (exit = room->f_exit ; exit ; exit = exit->next)
-	{
-		SET_BIT(room->exit_dirs, 1LL << exit->dir);
+	node->vnum   = from;
+	node->length = ses->map->room_list[from]->weight;
 
-		if (room->exit_grid[exit->grid] == NULL)
-		{
-			room->exit_grid[exit->grid] = exit;
-		}
-		room->exit_size++;
-	}
-}
-
-int follow_map(struct session *ses, char *argument)
-{
-	struct room_data *room;
-	struct exit_data *exit;;
-	int in_room, vnum;
-
-	push_call("follow_map(%p,%p)",ses,argument);
+	// for map_list
 
-	room = ses->map->room_list[ses->map->in_room];
+	node->w      = 0;
+	node->x      = 0;
+	node->y      = 0;
+	node->z      = 0;
 
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW))
+	while (head != tail)
 	{
-		if (check_global(ses, room->vnum) && find_exit(ses, ses->map->global_vnum, argument))
-		{
-			in_room = ses->map->global_vnum;
-		}
-		else
-		{
-			in_room = ses->map->in_room;
-		}
-		exit = find_exit(ses, in_room, argument);
+		node = &list[head];
 
-		if (exit)
-		{
-			ses->map->dir = exit->dir;
+		room = ses->map->room_list[node->vnum];
 
-			vnum = tunnel_void(ses, in_room, exit->vnum, exit->dir);
+		length = node->length;
 
-			check_all_events(ses, SUB_ARG, 0, 5, "MAP FOLLOW MAP", ntos(in_room), ntos(vnum), exit->name);
-		}
-		pop_call();
-		return 0;
-	}
+		head = (head + 1) % MAP_BF_SIZE;
 
-	if (check_global(ses, room->vnum))
-	{
-		if (find_exit(ses, ses->map->global_vnum, argument))
+		if (search->stamp != room->search_stamp)
 		{
-			goto_room(ses, ses->map->global_vnum);
-		}
-	}
-
-	exit = find_exit(ses, ses->map->in_room, argument);
+			room->search_stamp = search->stamp;
 
-	if (exit)
-	{
-		ses->map->dir = exit->dir;
+			// first come first serve like with spatialgrid_find
 
-		in_room = ses->map->in_room;
+			room->w = node->w;
+			room->x = node->x;
+			room->y = node->y;
+			room->z = node->z;
 
-		if (ses->map->nofollow == 0)
+			DEL_BIT(room->flags, ROOM_FLAG_PATH);
+		}
+		else if (length >= room->length)
 		{
-			ses->map->nofollow++;
-
-			script_driver(ses, LIST_COMMAND, exit->cmd);
-
-			ses->map->nofollow--;
+			if (room->vnum != ses->map->global_vnum && room->w && node->w == 0)
+			{
+				room->w = node->w;
+				room->x = node->x;
+				room->y = node->y;
+				room->z = node->z;
+			}
+			continue;
 		}
 
-		vnum = tunnel_void(ses, in_room, exit->vnum, exit->dir);
-
-		check_all_events(ses, SUB_ARG, 0, 5, "MAP FOLLOW MAP", ntos(in_room), ntos(vnum), exit->name);
-
-		add_undo(ses, "%d %d %d", vnum, in_room, MAP_UNDO_MOVE);
-
-		goto_room(ses, vnum);
+		room->length = length;
 
-		if (HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_LEAVE))
+		if (search->vnum)
 		{
-			show_message(ses, LIST_COMMAND, "#MAP: LEAVE FLAG FOUND IN ROOM {%d}. LEAVING MAP.", ses->map->in_room);
-
-			map_leave(ses, "", "", "");
+			if (room->vnum == search->vnum)
+			{
+				return room->vnum;
+			}
 		}
-
-		if (HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP))
+		else
 		{
-			SET_BIT(ses->flags, SES_FLAG_UPDATEVTMAP);
+			if (match_room(ses, room->vnum, search))
+			{
+				return room->vnum;
+			}
 		}
 
-		pop_call();
-		return 1;
-	}
-
-	if (!HAS_BIT(ses->map->flags, MAP_FLAG_STATIC) && !HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_STATIC))
-	{
-		struct listnode *node;
-
-		if ((node = search_node_list(ses->list[LIST_PATHDIR], argument)) == NULL)
+		if (check_global(ses, room->vnum))
 		{
-			pop_call();
-			return 0;
+			exit = ses->map->global_exit;
 		}
-
-		in_room = find_coord(ses, argument);
-
-		if (in_room)
+		else
 		{
-			show_message(ses, LIST_COMMAND, "#MAP CREATE LINK %5d {%s}.", in_room, ses->map->room_list[in_room]->name);
-
-			add_undo(ses, "%d %d %d", in_room, ses->map->in_room, MAP_UNDO_MOVE|MAP_UNDO_LINK);
+			exit = room->f_exit;
 		}
-		else
+
+		for ( ; exit ; exit = exit->next)
 		{
-			for (in_room = 1 ; in_room < ses->map->size ; in_room++)
+			vnum = tunnel_void(ses, room->vnum, exit->vnum, exit->dir);
+
+			if (HAS_BIT(exit->flags, EXIT_FLAG_AVOID|EXIT_FLAG_BLOCK) || HAS_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_AVOID|ROOM_FLAG_BLOCK))
 			{
-				if (ses->map->room_list[in_room] == NULL)
+				goto next_exit;
+			}
+
+			length = room->length + exit->weight + ses->map->room_list[vnum]->weight;
+
+			if (search->stamp == ses->map->room_list[vnum]->search_stamp)
+			{
+				if (length >= ses->map->room_list[vnum]->length)
 				{
-					break;
+					goto next_exit;
 				}
 			}
 
-			if (in_room == ses->map->size)
+			temp = &list[tail];
+
+			temp->vnum   = vnum;
+			temp->length = length;
+			temp->w      = room->vnum == ses->map->global_vnum ? 1 : room->w;
+			temp->x      = room->x + (HAS_BIT(exit->dir, MAP_EXIT_E) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_W) ? -1 : 0);
+			temp->y      = room->y + (HAS_BIT(exit->dir, MAP_EXIT_N) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_S) ? -1 : 0);
+			temp->z      = room->z + (HAS_BIT(exit->dir, MAP_EXIT_U) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_D) ? -1 : 0);
+
+			/*
+				list must remain ordered by length
+			*/
+
+			index = tail;
+
+			while (index != head)
 			{
-				show_error(ses, LIST_COMMAND, "#MAP: Maximum amount of rooms of %d reached. Use #MAP RESIZE to increase the maximum.", ses->map->size);
+				temp = &list[index];
 
-				pop_call();
-				return 1;
-			}
-			add_undo(ses, "%d %d %d", in_room, ses->map->in_room, MAP_UNDO_MOVE|MAP_UNDO_CREATE|MAP_UNDO_LINK);
+				node = &list[index ? index - 1 : MAP_BF_SIZE - 1];
 
-			create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {}", in_room);
-		}
+				if (temp->length >= node->length)
+				{
+					break;
+				}
 
-		exit = create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", in_room, node->arg1, node->arg1);
+				vnum         = temp->vnum;
+				length       = temp->length;
 
-		ses->map->dir = exit->dir;
+				temp->vnum   = node->vnum;
+				temp->length = node->length;
 
-		if (find_exit(ses, in_room, node->arg2) == NULL)
-		{
-			create_exit(ses, in_room, "{%d} {%s} {%s}", ses->map->in_room, node->arg2, node->arg2);
-		}
+				node->vnum   = vnum;
+				node->length = length;
 
-		if (ses->map->nofollow == 0)
-		{
-			ses->map->nofollow++;
+				index = index ? index - 1 : MAP_BF_SIZE - 1;
+			}
 
-			script_driver(ses, LIST_COMMAND, argument);
+			tail = (tail + 1) % MAP_BF_SIZE;
 
-			ses->map->nofollow--;
-		}
-		goto_room(ses, in_room);
+			if (tail == head)
+			{
+				show_error(ses, LIST_COMMAND, "#SHORTEST PATH: MAP TOO BIG FOR BF STACK OF %d", MAP_BF_SIZE);
+				break;
+			}
 
-		if (HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP))
-		{
-			SET_BIT(ses->flags, SES_FLAG_UPDATEVTMAP);
-		}
+			next_exit:
 
-		pop_call();
-		return 1;
+			if (exit == ses->map->global_exit)
+			{
+				exit->next = room->f_exit;
+			}
+		}
 	}
-	pop_call();
 	return 0;
 }
 
-void add_undo(struct session *ses, char *format, ...)
+int searchgrid_walk(struct session *ses, int offset, int from, int dest)
 {
-	struct link_data *link;
-	char buf[BUFFER_SIZE], *arg, dir[BUFFER_SIZE], rev[BUFFER_SIZE], flag[BUFFER_SIZE];
-	va_list args;
+	int vnum, trim, head, tail, index;
+	float length;
+	struct grid_node *node, *temp, list[MAP_BF_SIZE];
+	struct exit_data *exit;
+	struct room_data *room;
 
-	push_call("add_undo(%s,%s)",ses->name, format);
+	head = 0;
+	tail = 1;
 
-	va_start(args, format);
-	vsprintf(buf, format, args);
-	va_end(args);
+	list[head].vnum   = from;
+	list[head].length = ses->map->room_list[from]->weight;
 
-	arg = get_arg_in_braces(ses, buf, dir, GET_ONE);
-	arg = get_arg_in_braces(ses, arg, rev, GET_ONE);
-	arg = get_arg_in_braces(ses, arg, flag, GET_ONE);
+	while (head != tail)
+	{
+		node = &list[head];
 
-	link = (struct link_data *) calloc(1, sizeof(struct link_data));
+		room = ses->map->room_list[node->vnum];
 
-	link->str1 = strdup(dir);
-	link->str2 = strdup(rev);
-	link->str3 = strdup(flag);
+		length = node->length;
 
-	LINK(link, ses->map->undo_head, ses->map->undo_tail);
+		head = (head + 1) % MAP_BF_SIZE;
 
-	ses->map->undo_size++;
 
-	if (ses->map->undo_size > 100)
-	{
-		del_undo(ses, ses->map->undo_head);
-	}
-	pop_call();
-	return;
-}
+		if (length >= room->length)
+		{
+			continue;
+		}
 
-void del_undo(struct session *ses, struct link_data *link)
-{
-	UNLINK(link, ses->map->undo_head, ses->map->undo_tail);
+		room->length = length;
 
-	free(link->str1);
-	free(link->str2);
-	free(link->str3);
-
-	free(link);
-
-	ses->map->undo_size--;
-}
-
-/*
-	Draws a map on a grid, original breadth first improvements by Bryan Turner
-*/
+		if (room->vnum == dest)
+		{
+			return room->vnum;
+		}
 
+		trim = 1;
 
+		if (check_global(ses, room->vnum))
+		{
+			exit = ses->map->global_exit;
+		}
+		else
+		{
+			exit = room->f_exit;
+		}
 
-struct grid_node
-{
-	int vnum;
-	int flags;
-	float length;
-	int w;
-	int x;
-	int y;
-	int z;
-};
+		for ( ; exit ; exit = exit->next)
+		{
+			vnum = tunnel_void(ses, room->vnum, exit->vnum, exit->dir);
 
-void displaygrid_build(struct session *ses, int vnum, int x, int y, int z)
-{
-	int head, tail;
-	struct grid_node *node, *temp, list[MAP_BF_SIZE];
-	struct exit_data *exit;
-	struct room_data *room;
+			if (HAS_BIT(exit->flags, EXIT_FLAG_AVOID|EXIT_FLAG_BLOCK) || HAS_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_AVOID|ROOM_FLAG_BLOCK))
+			{
+				goto next_exit;
+			}
 
-	push_call("displaygrid_build(%p,%d,%d,%d,%d)",ses,vnum,x,y,z);
+			length = room->length + exit->weight + ses->map->room_list[vnum]->weight;
 
-	map_grid_x = x;
-	map_grid_y = y;
+			if (ses->map->search->stamp != ses->map->room_list[vnum]->search_stamp)
+			{
+				goto next_exit;
+			}
 
-	head = 0;
-	tail = 1;
+			if (length >= ses->map->room_list[vnum]->length || length >= ses->map->room_list[dest]->length)
+			{
+				goto next_exit;
+			}
 
-	node = &list[head];
+			temp = &list[tail];
 
-	node->vnum   = vnum;
-	node->x      = x / 2 + ses->map->center_x;
-	node->y      = y / 2 + ses->map->center_y;
-	node->z      = z / 2 + ses->map->center_z;
-	node->length = 0;
-	node->flags  = 0;
+			temp->vnum   = vnum;
+			temp->length = length;
 
-	ses->map->display_stamp++;
+			/*
+				list must remain ordered by length
+			*/
 
-	for (vnum = 0 ; vnum < x * y ; vnum++)
-	{
-		ses->map->grid_rooms[vnum] = NULL;
-		ses->map->grid_flags[vnum] = 0;
-	}
+			index = tail;
 
-	while (head != tail)
-	{
-		node = &list[head];
+			while (index != head)
+			{
+				temp = &list[index];
 
-		head = (head + 1) % MAP_BF_SIZE;
+				node = &list[index ? index - 1 : MAP_BF_SIZE - 1];
 
-		room = ses->map->room_list[node->vnum];
+				if (temp->length >= node->length)
+				{
+					break;
+				}
 
-		if (ses->map->display_stamp != room->display_stamp)
-		{
-			room->display_stamp = ses->map->display_stamp;
-		}
-		else if (room->length <= node->length)
-		{
-			continue;
-		}
+				vnum = temp->vnum;
+				length = temp->length;
 
-		room->length = node->length;
+				temp->vnum = node->vnum;
+				temp->length = node->length;
 
-		if (node->x >= 0 && node->x < map_grid_x && node->y >= 0 && node->y < map_grid_y && node->z == 0)
-		{
-			if (ses->map->grid_rooms[node->x + map_grid_x * node->y] == NULL/* || HAS_BIT(ses->map->grid_flags[node->x + map_grid_x * node->y], GRID_FLAG_HIDE)*/)
-			{
-				ses->map->grid_rooms[node->x + map_grid_x * node->y] = room;
-				ses->map->grid_flags[node->x + map_grid_x * node->y] = node->flags;
-			}
-			else
-			{
-				continue;
-			}
-		}
-/*
-		if (HAS_BIT(node->flags, GRID_FLAG_HIDE))
-		{
-			continue;
-		}
-*/
-		for (exit = room->f_exit ; exit ; exit = exit->next)
-		{
-			if (ses->map->display_stamp == ses->map->room_list[exit->vnum]->display_stamp)
-			{
-				if (room->length >= ses->map->room_list[exit->vnum]->length)
-				{
-					continue;
-				}
-			}
+				node->vnum = vnum;
+				node->length = length;
 
-			if (exit->dir == 0)
-			{
-				continue;
+				index = index ? index - 1 : MAP_BF_SIZE - 1;
 			}
 
-			if (HAS_BIT(exit->flags, EXIT_FLAG_HIDE) || HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_HIDE))
-			{
-				continue;
-			}
+			tail = (tail + 1) % MAP_BF_SIZE;
 
-			if (head == (tail + 1) % MAP_BF_SIZE)
+			if (tail == head)
 			{
+				show_error(ses, LIST_COMMAND, "#SHORTEST PATH: MAP TOO BIG FOR BF STACK OF %d", MAP_BF_SIZE);
 				break;
 			}
+			trim = 0;
 
-			temp = &list[tail];
-
-			temp->vnum   = exit->vnum;
-			temp->x      = node->x + (HAS_BIT(exit->dir, MAP_EXIT_E) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_W) ? -1 : 0);
-			temp->y      = node->y + (HAS_BIT(exit->dir, MAP_EXIT_N) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_S) ? -1 : 0);
-			temp->z      = node->z + (HAS_BIT(exit->dir, MAP_EXIT_U) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_D) ? -1 : 0);
-			temp->length = node->length + 1;
-			temp->flags  = 0;
-/*
-			if (HAS_BIT(exit->flags, EXIT_FLAG_HIDE) || HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_HIDE))
-			{
-				SET_BIT(temp->flags, GRID_FLAG_HIDE);
+			next_exit:
 
-				temp->length += 1000;
-			}
-*/
-			if (HAS_BIT(exit->flags, EXIT_FLAG_INVIS) || HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_INVIS))
+			if (exit == ses->map->global_exit)
 			{
-				SET_BIT(temp->flags, GRID_FLAG_INVIS);
+				exit->next = room->f_exit;
 			}
+		}
 
-			tail = (tail + 1) % MAP_BF_SIZE;
+		if (trim)
+		{
+			room->length = 0;
 		}
 	}
-	pop_call();
-	return;
+	return 0;
 }
 
-
-
-
-void show_vtmap(struct session *ses)
+void shortest_path(struct session *ses, int run, char *delay, char *arg)
 {
-	char buf[BUFFER_SIZE], out[BUFFER_SIZE];
-	int x, y, line;
-	int top_row, top_col, bot_row, bot_col, rows, cols;
-
-	push_call("show_vtmap(%p)",ses);
+	char var[BUFFER_SIZE];
+	struct exit_data *exit;
+	struct room_data *room;
+	int vnum, dest;
 
-	if (ses->map == NULL || !HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP))
+	if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
 	{
-		pop_call();
+		show_error(ses, LIST_COMMAND, "#SHORTEST PATH: You have to use #PATH END first.");
+
 		return;
 	}
 
-	if (ses->map->room_list[ses->map->in_room] == NULL)
+	kill_list(ses->list[LIST_PATH]);
+
+	ses->list[LIST_PATH]->update = 0;
+
+	map_search_compile(ses, arg, var);
+
+	dest = searchgrid_find(ses, ses->map->in_room, ses->map->search);
+
+	if (dest == 0 || dest == ses->map->global_vnum)
 	{
-		pop_call();
+		show_error(ses, LIST_COMMAND, "#SHORTEST PATH: NO PATH FOUND TO %s.", arg);
 		return;
 	}
 
-	if (ses != gtd->ses || HAS_BIT(gtd->ses->flags, SES_FLAG_READMUD))
+	if (dest == ses->map->in_room)
 	{
-		pop_call();
+		show_error(ses, LIST_COMMAND, "#SHORTEST PATH: Already there.");
 		return;
 	}
 
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_RESIZE))
-	{
-		DEL_BIT(ses->map->flags, MAP_FLAG_RESIZE);
+	vnum = ses->map->in_room;
 
-		map_offset(ses, NULL, "", "");
-	}
+	// Slower than a backtrace, but works with mazes.
 
-	if (ses->map->rows > 1 && ses->map->cols > 1)
-	{
-		top_row = ses->map->top_row;
-		top_col = ses->map->top_col;
-		bot_row = ses->map->bot_row;
-		bot_col = ses->map->bot_col;
-		rows    = ses->map->rows;
-		cols    = ses->map->cols;
-	}
-	else
+	while (TRUE)
 	{
-		top_row = 1;
-		top_col = 1;
-		bot_row = ses->split->top_row - 2;
-		bot_col = gtd->screen->cols;
+		room = ses->map->room_list[vnum];
 
-		rows    = ses->split->top_row - 2;
-		cols    = gtd->screen->cols;
-	}
+		if (check_global(ses, room->vnum))
+		{
+			exit = ses->map->global_exit;
+		}
+		else
+		{
+			exit = room->f_exit;
+		}
 
-	erase_square(ses, top_row, top_col, bot_row, bot_col);
+		for ( ; exit ; exit = exit->next)
+		{
+			if (HAS_BIT(exit->flags, EXIT_FLAG_AVOID|EXIT_FLAG_BLOCK) || HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_AVOID|ROOM_FLAG_BLOCK))
+			{
+				goto exit_next;
+			}
 
-//	print_stdout("\e[%d;%d;%d;%d${", top_row, top_col, bot_row, bot_col);
+			vnum = tunnel_void(ses, room->vnum, exit->vnum, exit->dir);
 
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
-	{
-		map_grid_y = 2 + rows / 3;
-		map_grid_x = 2 + cols / 6;
-	}
-	else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) || HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
-	{
-		map_grid_y = 2 + rows / 2;
-		map_grid_x = 2 + cols / 5;
-	}
-	else if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
-	{
-		map_grid_y = 2 + rows;
-		map_grid_x = 2 + cols / 2;
-	}
-	else
-	{
-		map_grid_y = 2 + rows;
-		map_grid_x = 2 + cols;
-	}
+			if (searchgrid_walk(ses, room->length, vnum, dest))
+			{
+				break;
+			}
 
-	displaygrid_build(ses, ses->map->in_room, map_grid_x, map_grid_y, 0);
+			exit_next:
 
-	save_pos(ses);
+			if (exit == ses->map->global_exit)
+			{
+				exit->next = room->f_exit;
+			}
+		}
 
-	goto_pos(ses, top_row, 1);
+		if (exit == NULL)
+		{
+			show_error(ses, LIST_COMMAND, "#SHORTEST PATH: UNKNOWN ERROR.");
+			return;
+		}
 
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
-	{
-		for (y = map_grid_y - 2 ; y >= 1 ; y--)
+		if (exit != ses->map->global_exit)
 		{
-			for (line = 1 ; line <= 3 ; line++)
+			if (HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW))
 			{
-				strcpy(buf, ses->map->color[MAP_COLOR_BACK]);
+				check_append_path(ses, exit->cmd, "", 0);
+			}
+			else
+			{
+				check_append_path(ses, exit->name, "", 0);
+			}
+		}
 
-				for (x = 1 ; x < map_grid_x - 1 ; x++)
-				{
-					strcat(buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], line, x, y));
-				}
+		SET_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_PATH);
 
-				substitute(ses, buf, out, SUB_COL|SUB_CMP|SUB_LIT);
+		if (ses->map->room_list[vnum]->search_stamp != ses->map->search->stamp)
+		{
+			show_error(ses, LIST_COMMAND, "%d bad search stamp %d vs %d", vnum, ses->map->room_list[vnum]->search_stamp, ses->map->search->stamp);
+		}
 
-				print_stdout("\e[%dG%s\e[0m\n", top_col, out);
-			}
+		if (vnum == dest)
+		{
+			break;
 		}
 	}
-	else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) || HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
+
+	if (run)
 	{
-		for (y = map_grid_y - 2 ; y >= 1 ; y--)
-		{
-			for (line = 1 ; line <= 2 ; line++)
-			{
-				strcpy(buf, ses->map->color[MAP_COLOR_BACK]);
+		path_run(ses, delay);
+	}
+}
 
-				for (x = 1 ; x < map_grid_x - 1 ; x++)
-				{
-					strcat(buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], line, x, y));
-				}
-				substitute(ses, buf, out, SUB_COL|SUB_CMP|SUB_LIT);
+/*
+	Virtual coordinate search for linkable rooms when creating a new room.
+*/
 
-				print_stdout("\e[%dG%s\e[0m\n", top_col, out);
-			}
-		}
+int find_coord(struct session *ses, char *arg)
+{
+	int dir, x, y, z, room;
+
+	dir = get_exit_dir(ses, arg);
+
+	if (dir == 0)
+	{
+		return 0;
 	}
-	else
+
+	x = (HAS_BIT(dir, MAP_EXIT_E) ? 1 : HAS_BIT(dir, MAP_EXIT_W) ? -1 : 0);
+	y = (HAS_BIT(dir, MAP_EXIT_N) ? 1 : HAS_BIT(dir, MAP_EXIT_S) ? -1 : 0);
+	z = (HAS_BIT(dir, MAP_EXIT_U) ? 1 : HAS_BIT(dir, MAP_EXIT_D) ? -1 : 0);
+
+	room = spatialgrid_find(ses, ses->map->in_room, x, y, z);
+
+	if (ses->map->room_list[room])
 	{
-		for (y = map_grid_y - 2 ; y >= 1 ; y--)
+		if (HAS_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_STATIC))
 		{
-			strcpy(buf, ses->map->color[MAP_COLOR_BACK]);
-
-			for (x = 1 ; x < map_grid_x - 1 ; x++)
-			{
-				strcat(buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], 0, x, y));
-			}
-			substitute(ses, buf, out, SUB_COL|SUB_CMP|SUB_LIT);
+			show_message(ses, LIST_COMMAND, "#MAP: Linkable room is marked static. Creating overlapping room instead.");
 
-			print_stdout("\e[%dG%s\e[0m\n", top_col, out);
+			return 0;
 		}
 	}
-
-	restore_pos(ses);
-
-	pop_call();
-	return;
+	return room;
 }
 
-// http://shapecatcher.com/unicode/block/Mathematical_Operators diagonal #⊥⊤#
+// Used by #map jump and the auto linker
 
-char *draw_room(struct session *ses, struct room_data *room, int line, int x, int y)
+int spatialgrid_find(struct session *ses, int from, int x, int y, int z)
 {
-	static char buf[201], *room_color, room_left[101], room_right[101];
-	int index, flags, exits, exit1, exit2, room1, room2, offset;
+	int head, tail;
+	struct grid_node *node, *temp, list[MAP_BF_SIZE];
+	struct exit_data *exit;
+	struct room_data *room;
 
-	push_call("draw_room(%p,%p,%d,%d,%d)",ses,room,line,x,y);
+	push_call("spatialgrid_find(%s,%d,%d,%d,%d)",ses->name,from,x,y,z);
 
-	offset = HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) ? LEGEND_UNICODE : LEGEND_ASCII;
+	head = 0;
+	tail = 1;
 
-	room_color = ses->map->color[MAP_COLOR_ROOM];
+	node = &list[head];
 
-	if (room)
+	node->vnum   = from;
+	node->x      = 0;
+	node->y      = 0;
+	node->z      = 0;
+	node->length = 0;
+	node->flags  = 0;
+
+	ses->map->display_stamp++;
+
+	while (head != tail)
 	{
-		if (HAS_BIT(room->flags, ROOM_FLAG_PATH) && room->search_stamp == ses->map->search->stamp)
-		{
-			room_color = ses->map->color[MAP_COLOR_PATH];
-		}
-		else if (*room->color)
+		node = &list[head];
+
+		head = (head + 1) % MAP_BF_SIZE;
+
+		room = ses->map->room_list[node->vnum];
+
+		if (ses->map->display_stamp != room->display_stamp)
 		{
-			room_color = room->color;
+			room->display_stamp = ses->map->display_stamp;
 		}
-		else
+		else if (room->length <= node->length)
 		{
-			if (HAS_BIT(ses->map->grid_flags[x + map_grid_x * y], GRID_FLAG_INVIS))
-			{
-				room_color = ses->map->color[MAP_COLOR_INVIS];
-			}
-			else if (HAS_BIT(room->flags, ROOM_FLAG_INVIS))
-			{
-				room_color = ses->map->color[MAP_COLOR_INVIS];
-			}
-/*			else if (HAS_BIT(ses->map->grid_flags[x + map_grid_x * y], GRID_FLAG_HIDE))
-			{
-				room_color = ses->map->color[MAP_COLOR_HIDE];
-			}*/
-			else if (HAS_BIT(room->flags, ROOM_FLAG_HIDE))
-			{
-				room_color = ses->map->color[MAP_COLOR_HIDE];
-			}
-			else if (HAS_BIT(room->flags, ROOM_FLAG_AVOID))
-			{
-				room_color = ses->map->color[MAP_COLOR_AVOID];
-			}
-			else if (HAS_BIT(ses->map->flags, MAP_FLAG_SYMBOLGRAPHICS))
-			{
-				room_color = ses->map->color[MAP_COLOR_SYMBOL];
-			}
+			continue;
 		}
 
-		if (HAS_BIT(room->flags, ROOM_FLAG_CURVED))
+		room->length = node->length;
+/*
+		if (HAS_BIT(node->flags, GRID_FLAG_HIDE))
 		{
-			sprintf(room_left, "%s(", room_color);
-			sprintf(room_right, "%s)", room_color);
+			continue;
 		}
-		else
+*/
+		if (node->x == x && node->y == y && node->z == z)
 		{
-			sprintf(room_left, "%s[", room_color);
-			sprintf(room_right, "%s]", room_color);
+			pop_call();
+			return node->vnum;
 		}
 
-		if (room->vnum == ses->map->in_room)
+		for (exit = room->f_exit ; exit ; exit = exit->next)
 		{
-			if (HAS_BIT(ses->map->flags, MAP_FLAG_DIRECTION))
+			if (ses->map->display_stamp == ses->map->room_list[exit->vnum]->display_stamp)
 			{
-				exits = ses->map->dir;
-
-				DEL_BIT(exits, MAP_EXIT_U|MAP_EXIT_D);
-
-				switch (exits)
+				if (room->length >= ses->map->room_list[exit->vnum]->length)
 				{
-					case MAP_EXIT_N:
-						index = 24;
-						break;
-					case MAP_EXIT_N+MAP_EXIT_E:
-						index = 25;
-						break;
-					case MAP_EXIT_E:
-						index = 26;
-						break;
-					case MAP_EXIT_S+MAP_EXIT_E:
-						index = 27;
-						break;
-					case MAP_EXIT_S:
-						index = 28;
-						break;
-					case MAP_EXIT_W+MAP_EXIT_S:
-						index = 29;
-						break;
-					case MAP_EXIT_W:
-						index = 30;
-						break;
-					case MAP_EXIT_W+MAP_EXIT_N:
-						index = 31;
-						break;
-					default:
-						index = 17;
-						break;
+					continue;
 				}
 			}
-			else
+
+			if (exit->dir == 0)
 			{
-				index = 16;
+				continue;
 			}
-		}
-	}
-
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS))
-	{
-		struct room_data *room_n, *room_nw, *room_w;
-		long long exit_n, exit_nw, exit_w;
 
-		room_n  = ses->map->grid_rooms[x + 0 + map_grid_x * (y + 1)];
-		room_nw = ses->map->grid_rooms[x - 1 + map_grid_x * (y + 1)];
-		room_w  = ses->map->grid_rooms[x - 1 + map_grid_x * (y + 0)];
+			if (HAS_BIT(exit->flags, EXIT_FLAG_HIDE) || HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_HIDE))
+			{
+				continue;
+			}
 
-		exit_n = exit_nw = exit_w = 0;
+			if (head == (tail + 1) % MAP_BF_SIZE)
+			{
+				break;
+			}
 
-		if (room && room->exit_grid[EXIT_GRID_N])
-		{
-			SET_BIT(exit_n, MAP_DIR_N);
-		}
+			temp = &list[tail];
 
-		if (room_n && HAS_BIT(room_n->exit_dirs, MAP_DIR_S))
-		{
-			SET_BIT(exit_n, MAP_DIR_S);
-		}
+			temp->vnum   = exit->vnum;
+			temp->w      = node->w;
+			temp->x      = node->x + (HAS_BIT(exit->dir, MAP_EXIT_E) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_W) ? -1 : 0);
+			temp->y      = node->y + (HAS_BIT(exit->dir, MAP_EXIT_N) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_S) ? -1 : 0);
+			temp->z      = node->z + (HAS_BIT(exit->dir, MAP_EXIT_U) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_D) ? -1 : 0);
+			temp->length = node->length + 1;
+			temp->flags  = 0;
+/*
+			if (HAS_BIT(exit->flags, EXIT_FLAG_HIDE) || HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_HIDE))
+			{
+				SET_BIT(temp->flags, GRID_FLAG_HIDE);
 
-		if (room_n && HAS_BIT(room_n->exit_dirs, MAP_DIR_D))
-		{
-			SET_BIT(exit_n, MAP_DIR_D);
+				temp->length += 1000;
+			}
+*/
+			tail = (tail + 1) % MAP_BF_SIZE;
 		}
+	}
+	pop_call();
+	return 0;
+}
 
-		if (room && HAS_BIT(room->exit_dirs, MAP_DIR_NW))
-		{
-			SET_BIT(exit_nw, MAP_DIR_SE);
-		}
+void explore_path(struct session *ses, int run, char *arg1, char *arg2)
+{
+	struct exit_data *exit;
+	int room, vnum;
 
-		if (room_n && HAS_BIT(room_n->exit_dirs, MAP_DIR_SW))
-		{
-			SET_BIT(exit_nw, MAP_DIR_NE);
-		}
-		if (room_nw && HAS_BIT(room_nw->exit_dirs, MAP_DIR_SE))
+	for (vnum = 0 ; vnum < ses->map->size ; vnum++)
+	{
+		if (ses->map->room_list[vnum])
 		{
-			SET_BIT(exit_nw, MAP_DIR_NW);
+			DEL_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_PATH);
 		}
+	}
 
-		if (room_w && HAS_BIT(room_w->exit_dirs, MAP_DIR_NE))
-		{
-			SET_BIT(exit_nw, MAP_DIR_SW);
-		}
+	if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
+	{
+		show_error(ses, LIST_COMMAND, "#MAP EXPLORE: You have to use #PATH END first.");
 
-		if (room && room->exit_grid[EXIT_GRID_W])
-		{
-			SET_BIT(exit_w, MAP_DIR_E);
-		}
+		return;
+	}
 
-		if (room_w && room_w->exit_grid[EXIT_GRID_E])
-		{
-			SET_BIT(exit_w, MAP_DIR_W);
-		}
+	kill_list(ses->list[LIST_PATH]);
 
-		sprintf(buf, "%s", ses->map->color[MAP_COLOR_EXIT]);
+	ses->list[LIST_PATH]->update = 0;
 
-		switch (line)
-		{
-			case 1:
-				switch (exit_nw)
-				{
-					case 0:
-						strcat(buf, "  ");
-						break;
-					case MAP_DIR_NE:
-						strcat(buf, " ⸍");
-						break;
-					case MAP_DIR_SE:
-						strcat(buf, " ⸜");
-						break;
-					case MAP_DIR_NE|MAP_DIR_SE:
-						strcat(buf, " <");
-						break;
-					case MAP_DIR_SW:
-						strcat(buf, "⸝ ");
-						break;
-					case MAP_DIR_NE|MAP_DIR_SW:
-						strcat(buf, "/");
-						break;
-					case MAP_DIR_SE|MAP_DIR_SW:
-						strcat(buf, "⸝⸜");
-						break;
-					case MAP_DIR_NE|MAP_DIR_SE|MAP_DIR_SW:
-						strcat(buf, "⸝<");
-						break;
-					case MAP_DIR_NW:
-						strcat(buf, "⸌ ");
-						break;
-					case MAP_DIR_NE|MAP_DIR_NW:
-						strcat(buf, "⸌⸍");
-						break;
-					case MAP_DIR_SE|MAP_DIR_NW:
-						strcat(buf, "\");
-						break;
-					case MAP_DIR_SW|MAP_DIR_NW:
-						strcat(buf, "> ");
-						break;
-					case MAP_DIR_NE|MAP_DIR_SE|MAP_DIR_NW:
-						strcat(buf, "⸌<");
-						break;
-					case MAP_DIR_NE|MAP_DIR_SW|MAP_DIR_NW:
-						strcat(buf, ">⸍");
-						break;
-					case MAP_DIR_SE|MAP_DIR_SW|MAP_DIR_NW:
-						strcat(buf, ">⸜");
-						break;
-					case MAP_DIR_NW|MAP_DIR_SE|MAP_DIR_NE|MAP_DIR_SW:
-//						strcat(buf, "ᐳᐸ");
-						strcat(buf, "><");
-						break;
-					default:
-						strcat(buf, "??");
-						break;
-				}
+	room = ses->map->in_room;
 
-				if (HAS_BIT(exit_n, MAP_DIR_D))
-				{
-					flags = dir_flags(ses, room_n->vnum, MAP_EXIT_D); // merging
+	exit = find_exit(ses, room, arg1);
 
-					if (HAS_BIT(flags, EXIT_FLAG_AVOID))
-					{
-						cat_sprintf(buf, "%s-%s", ses->map->color[MAP_COLOR_AVOID], ses->map->color[MAP_COLOR_EXIT]);
-					}
-					else if (HAS_BIT(flags, EXIT_FLAG_HIDE))
-					{
-						cat_sprintf(buf, "%s-%s", ses->map->color[MAP_COLOR_HIDE], ses->map->color[MAP_COLOR_EXIT]);
-					}
-					else if (HAS_BIT(flags, EXIT_FLAG_INVIS))
-					{
-						cat_sprintf(buf, "%s-%s", ses->map->color[MAP_COLOR_INVIS], ses->map->color[MAP_COLOR_EXIT]);
-					}
-					else
-					{
-						strcat(buf, "-");
-//						strcat(buf, "⏷");
-					}
-				}
-				else
-				{
-					strcat(buf, " ");
-				}
+	if (exit == NULL)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP: There's no exit named '%s'.", arg1);
+		return;
+	}
 
-				switch (HAS_BIT(exit_n, MAP_DIR_N|MAP_DIR_S))
-				{
-					case MAP_DIR_N:
-						strcat(buf, "↑");
-						break;
-					case MAP_DIR_S:
-						strcat(buf, "↓");
-						break;
-					case MAP_DIR_N|MAP_DIR_S:
-						strcat(buf, "│");
-						break;
-					default:
-						strcat(buf, " ");
-						break;
-				}
+	vnum = exit->vnum;
 
-				if (room && room->exit_grid[EXIT_GRID_U])
-				{
-					flags = room->exit_grid[EXIT_GRID_U]->flags;
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW))
+	{
+		check_append_path(ses, exit->cmd, "", 0);
+	}
+	else
+	{
+		check_append_path(ses, exit->name, "", 0);
+	}
 
-					if (HAS_BIT(flags, EXIT_FLAG_AVOID))
-					{
-						cat_sprintf(buf, "%s+%s", ses->map->color[MAP_COLOR_AVOID], ses->map->color[MAP_COLOR_EXIT]);
-					}
-					else if (HAS_BIT(flags, EXIT_FLAG_HIDE))
-					{
-						cat_sprintf(buf, "%s+%s", ses->map->color[MAP_COLOR_HIDE], ses->map->color[MAP_COLOR_EXIT]);
-					}
-					else if (HAS_BIT(flags, EXIT_FLAG_INVIS))
-					{
-						cat_sprintf(buf, "%s+%s", ses->map->color[MAP_COLOR_INVIS], ses->map->color[MAP_COLOR_EXIT]);
-					}
-					else
-					{
-						strcat(buf, "+");
-//						strcat(buf, "⏶");
-					}
-				}
-				else
-				{
-					strcat(buf, " ");
-				}
-				break;
+	SET_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_PATH);
+	SET_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_PATH);
 
-			case 2:
-				if (room == NULL)
-				{
-					if (HAS_BIT(exit_w, MAP_DIR_W))
-					{
-						sprintf(buf, "%s→    ", ses->map->color[MAP_COLOR_EXIT]);
-					}
-					else
-					{
-						strcpy(buf, "     ");
-					}
-					pop_call();
-					return buf;
-				}
+	while (get_room_exits(ses, vnum) == 2)
+	{
+		exit = ses->map->room_list[vnum]->f_exit;
 
-				switch (exit_w)
-				{
-					case 0:
-						strcpy(buf, "  ");
-						break;
-					case MAP_DIR_W:
-						sprintf(buf, "%s→ ", ses->map->color[MAP_COLOR_EXIT]);
-						break;
-					case MAP_DIR_E:
-						sprintf(buf, "%s ←", ses->map->color[MAP_COLOR_EXIT]);
-						break;
-					case MAP_DIR_W|MAP_DIR_E:
-						if (room->exit_grid[EXIT_GRID_W]->vnum == room_w->vnum && room_w->exit_grid[EXIT_GRID_E]->vnum == room->vnum)
-						{
-							// ‒‒
-							sprintf(buf, "%s──", ses->map->color[MAP_COLOR_EXIT]);
-						}
-						else
-						{
-							sprintf(buf, "%s→←", ses->map->color[MAP_COLOR_EXIT]);
-						}
-						break;
-					default:
-						strcat(buf, "??");
-						break;
-				}
+		if (HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_PATH))
+		{
+			exit = ses->map->room_list[vnum]->l_exit;
 
-				if (room->vnum == ses->map->in_room)
-				{
-					cat_sprintf(buf, "%s%s%s%s%s", room_left, ses->map->color[MAP_COLOR_USER], ses->map->legend[offset + index], room_right, ses->map->color[MAP_COLOR_EXIT]);
-					pop_call();
-					return buf;
-				}
-				else
-				{
-					// use a yet to be made 1x1 8 exit legend for void room drawing, call semi-recursively.
+			if (HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_PATH))
+			{
+				break;
+			}
+		}
 
-					if (strip_color_strlen(ses, room->symbol) > 1)
-					{
-						cat_sprintf(buf, "%s%-3s%s", ses->map->color[MAP_COLOR_SYMBOL], room->symbol, ses->map->color[MAP_COLOR_EXIT]);
-					}
-					else if (HAS_BIT(room->flags, ROOM_FLAG_VOID))
-					{
-						if (HAS_BIT(room->exit_dirs, MAP_DIR_W) && !HAS_BIT(room->exit_dirs, MAP_DIR_NW|MAP_DIR_SW))
-						{
-							cat_sprintf(buf, "─");
-						}
-						else
-						{
-							cat_sprintf(buf, " ");
-						}
+		if (!HAS_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_VOID))
+		{
+			if (HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW))
+			{
+				check_append_path(ses, exit->cmd, "", 0);
+			}
+			else
+			{
+				check_append_path(ses, exit->name, "", 0);
+			}
+		}
 
-						if (*room->symbol != ' ' && strip_color_strlen(ses, room->symbol) == 1)
-						{
-							cat_sprintf(buf, "%s%-1s%s", ses->map->color[MAP_COLOR_SYMBOL], room->symbol, ses->map->color[MAP_COLOR_EXIT]);
-						}
-						else
-						{
+		vnum = exit->vnum;
 
-							if (room->vnum == ses->map->in_room)
-							{
-								cat_sprintf(buf, "%s", ses->map->color[MAP_COLOR_USER]);
-							}
-							else
-							{
-								cat_sprintf(buf, "%s", ses->map->color[MAP_COLOR_EXIT]);
-							}
+		SET_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_PATH);
+	}
 
-							switch (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_E|MAP_DIR_S|MAP_DIR_W|MAP_DIR_NW|MAP_DIR_NE|MAP_DIR_SE|MAP_DIR_SW))
-							{
-								case MAP_DIR_S:
-								case MAP_DIR_N:
-								case MAP_DIR_N|MAP_DIR_S:
-									cat_sprintf(buf, "│");
-									break;
+	DEL_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_PATH);
 
-								case MAP_DIR_E:
-								case MAP_DIR_W:
-								case MAP_DIR_E|MAP_DIR_W:
-									cat_sprintf(buf, "─");
-									break;
+	if (run)
+	{
+		path_run(ses, arg2);
+	}
+}
 
-								default:
-									cat_sprintf(buf, "*");
-									break;
-							}
+void map_mouse_handler(struct session *ses, char *arg1, char *arg2, int x, int y, int height, int width)
+{
+	char exit[10];
+	int max_x, max_y;
+	int top_row, top_col, bot_row, bot_col, rows, cols, char_height;
 
-							if (room->vnum == ses->map->in_room)
-							{
-								cat_sprintf(buf, "%s", ses->map->color[MAP_COLOR_EXIT]);
-							}
-						}
+	push_call("map_mouse_handler(%p,%p,%p,%d,%d)",ses,arg1,arg2,x,y);
 
-						if (HAS_BIT(room->exit_dirs, MAP_DIR_E) && !HAS_BIT(room->exit_dirs, MAP_DIR_NE|MAP_DIR_SE))
-						{
-							cat_sprintf(buf, "─");
-						}
-						else
-						{
-							cat_sprintf(buf, " ");
-						}
-					}
-					else
-					{
-						cat_sprintf(buf, "%s%s%-1s%s%s", room_left, ses->map->color[MAP_COLOR_SYMBOL], room->symbol, room_right, ses->map->color[MAP_COLOR_EXIT]);
-					}
-				}
-				break;
-		}
+	if (ses->map == NULL || !HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP) || ses->map->room_list[ses->map->in_room] == NULL)
+	{
 		pop_call();
-		return buf;
+		return;
+	}
+
+	if (!HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
+	{
+		if (arg1 && arg2)
+		{
+//			do_screen(ses, "{RAISE} {SCREEN MOUSE LOCATION}");
+		}
 	}
 
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
+	exit[0] = 0;
+
+	char_height = 1 + height % UMAX(1, gtd->screen->char_height);
+
+	if (ses->map->rows > 1 && ses->map->cols > 1)
+	{
+		top_row = ses->map->top_row;
+		top_col = ses->map->top_col;
+		bot_row = ses->map->bot_row;
+		bot_col = ses->map->bot_col;
+		rows    = ses->map->rows;
+		cols    = ses->map->cols;
+	}
+	else
+	{
+		top_row = 1;
+		top_col = 1;
+		bot_row = ses->split->top_row - 2;
+		bot_col = gtd->screen->cols;
+		rows    = ses->split->top_row - 2;
+		cols    = gtd->screen->cols;
+	}
+
+	y = y - 1;
+	x = x - 1;
+
+	if (y > bot_row || y < top_row)
+	{
+		pop_call();
+		return;
+	}
+
+	if (x > bot_col || x < top_col)
+	{
+		pop_call();
+		return;
+	}
+
+	y = y + 1 - top_row;
+	x = x + 1 - top_col;
+
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
+	{
+		char *grid[] = { "nw",  "2",  "n",  "u", "ne",  "6",
+				  "w", "RL", "RC", "RR",  "e", "e",
+				 "sw",  "d",  "s", "16", "se", "18" };
+
+//		tintin_printf2(ses, "\e[1;32mdebug: y=%d x=%d mod(y)=%d mod(x)=%d", y / 3, y / 6, y % 3, x % 6);
+
+		strcpy(exit, grid[URANGE(0, y % 3 * 6 + x % 6, 17)]);
+
+		y /= 3;
+		x /= 6;
+
+		max_y = 2 + rows / 3;
+		max_x = 2 + cols / 6;
+	}
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS))
+	{
+		char *grid[] = { "se", "sw",  "d",  "s",  "5",   "ne", "nw",  "8",  "n",  "u",   "e", "w", "RL", "RC", "RR",   "e",  "w", "RL", "RC", "RR" };
+		int x_mod, y_mod;
+
+		y_mod = y  % 2 * 2 + (char_height * 2 / gtd->screen->char_height ? 1 : 0);
+		x_mod = x  % 5;
+
+		y = y  / 2;
+		x = x  / 5;
+
+		strcpy(exit, grid[URANGE(0, 5 * y_mod + x_mod, 20)]);
+
+		switch (5 * y_mod + x_mod)
+		{
+			case 0:
+				y--;
+			case 5:
+			case 10:
+			case 15:
+				x--;
+				break;
+			case 1:
+			case 2:
+			case 3:
+			case 4:
+				y--;
+				break;
+		}
+
+//		tintin_printf2(ses, "\e[1;32mdebug: y=%d x=%d y_mod=%d x_mod=%d y+x=%d y=%d x=%d", y, x, y_mod, x_mod, 5*y_mod+x_mod, y, x);
+
+		max_y = 2 + (rows + 2) / 2;
+		max_x = 2 + (cols + 4) / 5;
+	}
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
+	{
+		y /= 2;
+		x /= 5;
+
+		max_y = 2 + rows / 2;
+		max_x = 2 + cols / 5;
+	}
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
+	{
+		x /= 2;
+
+		max_y = 2 + rows;
+		max_x = 2 + cols / 2;
+	}
+	else
+	{
+		max_y = 2 + rows;
+		max_x = 2 + cols;
+	}
+
+	y = max_y - 1 - y;
+
+	if (x < 0 || y < 0)
+	{
+		pop_call();
+		return;
+	}
+
+	if (max_x != map_grid_x || max_y != map_grid_y)
+	{
+		pop_call();
+		return;
+	}
+
+	if (ses->map->grid_rooms[x + 1 + max_x * (y - 1)])
+	{
+		if (arg1 && arg2)
+		{
+			check_all_events(ses, SUB_ARG, 2, 2, "MAP %s %s", arg1, arg2, ntos(ses->map->grid_rooms[x + 1 + max_x * (y - 1)]->vnum), exit);
+		}
+		else
+		{
+			check_all_events(ses, SUB_ARG, 0, 2, "MAP MOUSE LOCATION", ntos(ses->map->grid_rooms[x + 1 + max_x * (y - 1)]->vnum), exit);
+		}
+	}
+	pop_call();
+	return;
+}
+
+void update_terrain(struct session *ses)
+{
+	struct room_data *room;
+	int vnum;
+
+	DEL_BIT(ses->map->flags, MAP_FLAG_UPDATETERRAIN);
+
+	for (vnum = 1 ; vnum < ses->map->size ; vnum++)
+	{
+		room = ses->map->room_list[vnum];
+
+		if (room)
+		{
+			room->terrain_index = bsearch_alpha_list(ses->list[LIST_TERRAIN], room->terrain, 0);
+		}
+	}
+}
+
+/*
+	Map options
+*/
+
+
+DO_MAP(map_at)
+{
+	int new_room;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_NONE);
+
+	new_room = find_room(ses, arg1);
+
+	ses->map->at_room = ses->map->in_room;
+
+	if (new_room == 0)
+	{
+		new_room = find_location(ses, arg1);
+
+		if (new_room == 0)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP AT: Couldn't find room or exit {%s}.", arg1);
+
+			return;
+		}
+	}
+
+	ses->map->in_room = new_room;
+
+	script_driver(ses, LIST_COMMAND, arg2);
+
+	if (ses->map)
+	{
+		ses->map->in_room = ses->map->at_room;
+	}
+}
+
+DO_MAP(map_center)
+{
+	char arg3[BUFFER_SIZE];
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
+	{
+		ses->map->center_x = ses->map->center_y = ses->map->center_z = 0;
+	}
+	else
+	{
+		arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+		arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+
+		if (!is_math(ses, arg1) || !is_math(ses, arg2) || !is_math(ses, arg3))
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP CENTER {X} {Y} {Z}");
+
+			return;
+		}
+		else
+		{
+			ses->map->center_x = get_number(ses, arg1);
+			ses->map->center_y = get_number(ses, arg2);
+			ses->map->center_z = get_number(ses, arg3);
+
+			show_message(ses, LIST_COMMAND, "#MAP CENTER SET TO {%d} {%d} {%d}", ses->map->center_x, ses->map->center_y, ses->map->center_z);
+		}
+	}
+}
+
+DO_MAP(map_color)
+{
+	char buf[BUFFER_SIZE];
+	int index;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1)
+	{
+		if (!strcasecmp(arg1, "RESET"))
+		{
+			for (index = 0 ; map_color_table[index].name ; index++)
+			{
+				strncpy(ses->map->color[index], map_color_table[index].code, COLOR_SIZE - 1);
+				strncpy(ses->map->color_raw[index], map_color_table[index].code, COLOR_SIZE - 1);
+			}
+
+			return;
+		}
+
+		for (index = 0 ; map_color_table[index].name ; index++)
+		{
+			if (is_abbrev(arg1, map_color_table[index].name))
+			{
+				if (is_abbrev(arg2, "RESET"))
+				{
+					translate_color_names(ses, map_color_table[index].code, ses->map->color[index]);
+					strncpy(ses->map->color_raw[index], map_color_table[index].code, COLOR_SIZE - 1);
+				}
+				else
+				{
+					translate_color_names(ses, arg2, ses->map->color[index]);
+					strncpy(ses->map->color_raw[index], arg2, COLOR_SIZE - 1);
+				}
+
+				get_color_names(ses, ses->map->color[index], buf);
+
+				show_message(ses, LIST_COMMAND, "#MAP COLOR %s%10s\e[0m SET TO {%s}", ses->map->color[index], buf, map_color_table[index].name, ses->map->color_raw[index]);
+
+				break;
+			}
+		}
+
+		if (map_color_table[index].name == NULL)
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP COLOR {AVOID|BACKGROUND|EXIT|HIDE|INVIS|PATH|ROOM|USER} {COLOR CODE}");
+
+			return;
+		}
+		show_message(ses, LIST_COMMAND, "#MAP: %s color set to: %s", arg1, arg2);
+	}
+	else
+	{
+		for (index = 0 ; map_color_table[index].name ; index++)
+		{
+			get_color_names(ses, ses->map->color[index], buf);
+
+			show_message(ses, LIST_COMMAND, "#MAP COLOR %s%10s\e[0m SET TO {%s}", buf, map_color_table[index].name, ses->map->color_raw[index]);
+		}
+	}
+}
+
+DO_MAP(map_create)
+{
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	create_map(ses, arg1);
+
+	tintin_printf2(ses, "#MAP: %d room map created, use #map goto 1, to proceed", ses->map->size);
+}
+
+DO_MAP(map_debug)
+{
+	tintin_printf2(ses, "max spatial grid x: %d", ses->map->max_grid_x);
+	tintin_printf2(ses, "max spatial grid y: %d", ses->map->max_grid_y);
+	tintin_printf2(ses, "     max undo size: %d", ses->map->undo_size);
+	tintin_printf2(ses, "           in room: %d", ses->map->in_room);
+	tintin_printf2(ses, "           at room: %d", ses->map->at_room);
+	tintin_printf2(ses, "         last room: %d", ses->map->last_room);
+	tintin_printf2(ses, "             stamp: %d", ses->map->search->stamp);
+	tintin_printf2(ses, "            length: %f", ses->map->room_list[ses->map->in_room]->length);
+	tintin_printf2(ses, "          nofollow: %d", ses->map->nofollow);
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1)
+	{
+		if (is_abbrev(arg1, "undo"))
+		{
+			struct link_data *link;
+
+			for (link = ses->map->undo_head ; link ; link = link->next)
+			{
+				tintin_printf2(ses, "%05s %05s %s", link->str1, link->str2, link->str3);
+			}
+		}
+	}
+}
+
+DO_MAP(map_delete)
+{
+	int room;
+	struct exit_data *exit;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (is_number(arg1))
+	{
+		room = find_room(ses, arg1);
+
+		if (room == 0)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP DELETE {%s} - No room with that vnum found", arg1);
+
+			return;
+		}
+	}
+	else
+	{
+		exit = find_exit(ses, ses->map->in_room, arg1);
+
+		if (exit)
+		{
+			room = exit->vnum;
+		}
+
+		if (exit == NULL)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP: No exit with that name found");
+			
+			return;
+		}
+
+		room = exit->vnum;
+	}
+
+	if (room == ses->map->in_room)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP: You must first leave the room you're trying to delete");
+		
+		return;
+	}
+
+	delete_room(ses, room, TRUE);
+
+	show_message(ses, LIST_COMMAND, "#MAP: Room {%d} deleted", room);
+}
+
+DO_MAP(map_destroy)
+{
+	struct exit_data *exit;
+	int index, cnt;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (is_abbrev(arg1, "AREA"))
+	{
+		if (*arg2 == 0)
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP DESTROY AREA {<AREA NAME>}");
+
+			return;
+		}
+
+		if (ses->map->room_list[ses->map->in_room] && !strcmp(arg2, ses->map->room_list[ses->map->in_room]->area))
+		{
+			show_error(ses, LIST_COMMAND, "#MAP DESTROY AREA: YOU MUST FIRST LEAVE THE AREA YOU ARE TRYING TO DESTROY.");
+
+			return;
+		}
+
+		for (index = cnt = 0 ; index < ses->map->size ; index++)
+		{
+			if (ses->map->room_list[index])
+			{
+				if (!strcmp(arg2, ses->map->room_list[index]->area))
+				{
+					cnt++;
+
+					delete_room(ses, index, FALSE);
+				}
+			}
+		}
+
+		for (index = 0 ; index < ses->map->size ; index++)
+		{
+			if (ses->map->room_list[index])
+			{
+				for (exit = ses->map->room_list[index]->f_exit ; exit ; exit = exit->next)
+				{
+					if (ses->map->room_list[exit->vnum] == NULL)
+					{
+						delete_exit(ses, index, exit);
+
+						if (ses->map->room_list[index]->f_exit)
+						{
+							exit = ses->map->room_list[index]->f_exit;
+						}
+						else
+						{
+							break;
+						}
+					}
+				}
+			}
+		}
+		show_message(ses, LIST_COMMAND, "#MAP DESTROY AREA: DELETED %d ROOMS.", cnt);
+	}
+	else if (is_abbrev(arg1, "WORLD"))
+	{
+		cnt = delete_map(ses);
+
+		tintin_printf2(ses, "#MAP DESTROY WORLD: DELETED %d ROOMS.", cnt);
+	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP DESTROY {AREA|WORLD} {<ARGUMENT>}");
+	}
+}
+
+DO_MAP(map_dig)
+{
+	char arg3[BUFFER_SIZE];
+	int room;
+	struct exit_data *exit;
+	struct listnode *node;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP DIG {<DIRECTION>|<VNUM>} {<LOCATION>|NEW}");
+		
+		return;
+	}
+
+	room = atoi(arg1);
+
+	if (room > 0 && room < ses->map->size)
+	{
+		if (ses->map->room_list[room] == NULL)
+		{
+			add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_CREATE);
+
+			create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {}", room);
+		}
+		return;
+	}
+
+	exit = find_exit(ses, ses->map->in_room, arg1);
+
+	if (exit)
+	{
+		show_message(ses, LIST_COMMAND, "#MAP DIG: There is already a room in that direction.");
+		return;
+	}
+
+	if (*arg2 && strcasecmp(arg2, "new"))
+	{
+		if (is_number(arg2))
+		{
+			room = get_number(ses, arg2);
+		}
+		else
+		{
+			room = find_room(ses, arg2);
+		}
+
+		if (room == 0 && !strcasecmp(arg3, "new"))
+		{
+			room = find_new_room(ses);
+		}
+
+		if (room <= 0 || room >= ses->map->size)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP DIG {%s}: Couldn't find room {%s}.", arg1, arg2);
+
+			return;
+		}
+
+		if (ses->map->room_list[room] == NULL)
+		{
+			add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_CREATE|MAP_UNDO_LINK);
+
+			create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {%s}", room, ses->map->search->id ? ses->map->search->id : "");
+			create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", room, arg1, arg1);
+		}
+		else
+		{
+			add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_LINK);
+
+			create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", room, arg1, arg1);
+		}
+		return;
+	}
+
+	room = find_coord(ses, arg1);
+
+	if (room && strcasecmp(arg2, "new"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP CREATE LINK %5d {%s}.", room, ses->map->room_list[room]->name);
+
+		add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_LINK);
+
+		create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", room, arg1, arg1);
+	}
+	else
+	{
+		for (room = 1 ; room < ses->map->size ; room++)
+		{
+			if (ses->map->room_list[room] == NULL)
+			{
+				break;
+			}
+		}
+
+		if (room == ses->map->size)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP DIG: Maximum amount of rooms of %d reached.", ses->map->size);
+			
+			return;
+		}
+		add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_CREATE|MAP_UNDO_LINK);
+
+		create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {}", room);
+		create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", room, arg1, arg1);
+	}
+
+	if ((node = search_node_list(ses->list[LIST_PATHDIR], arg1)) != NULL)
+	{
+		if (find_exit(ses, room, node->arg2) == NULL)
+		{
+			create_exit(ses, room, "{%d} {%s} {%s}", ses->map->in_room, node->arg2, node->arg2);
+		}
+	}
+}
+
+void exit_edit(struct session *ses, struct exit_data *exit, char *arg, char *arg1, char *arg2, char *arg3)
+{
+	int room, dir;
+
+	if (*arg2 == 0)
+	{
+		tintin_printf2(ses, "    color: %s", str_convert_meta(exit->color, TRUE));
+		tintin_printf2(ses, "  command: %s", exit->cmd);
+		tintin_printf2(ses, "direction: %d", exit->dir);
+		tintin_printf2(ses, "    flags: %d", exit->flags);
+		tintin_printf2(ses, "  get/set: %s", exit->data);
+		tintin_printf2(ses, "     name: %s", exit->name);
+		tintin_printf2(ses, "     vnum: %d", exit->vnum);
+		tintin_printf2(ses, "   weight: %.3f", exit->weight);
+	}
+	else if (is_abbrev(arg2, "COLOR"))
+	{
+		RESTRING(exit->color, arg3);
+
+		show_message(ses, LIST_COMMAND, "#MAP %s {%s} : COLOR SET TO {%s}.", arg, arg1, exit->color);
+	}
+	else if (is_abbrev(arg2, "COMMAND"))
+	{
+		RESTRING(exit->cmd, arg3);
+
+		show_message(ses, LIST_COMMAND, "#MAP %s {%s} : COMMAND SET TO {%s}.", arg, arg1, exit->cmd);
+	}
+	else if (is_abbrev(arg2, "DIRECTION"))
+	{
+		if (is_math(ses, arg3))
+		{
+			dir = (int) get_number(ses, arg3);
+		}
+		else if ((dir = get_exit_dir(ses, arg3)) == 0)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP %s {%s} : DIRECTION {%s} NOT FOUND.", arg, arg1, arg3);
+			
+			return;
+		}
+
+		exit->dir = dir;
+
+		exit->grid = dir_to_grid(exit->dir);
+
+		set_room_exits(ses, ses->map->in_room);
+
+		show_message(ses, LIST_COMMAND, "#MAP %s {%s} : DIRECTION {%s} SET TO {%d}.", arg, arg1, arg3, dir);
+	}
+	else if (is_abbrev(arg2, "FLAGS"))
+	{
+		exit->flags = (int) get_number(ses, arg3);
+
+		show_message(ses, LIST_COMMAND, "#MAP %s {%s} : FLAGS SET TO {%d}.", arg, arg1, exit->flags);
+	}
+	else if (is_abbrev(arg2, "GET"))
+	{
+		if (*arg3)
+		{
+			set_nest_node_ses(ses, arg3, "%s", exit->data);
+		}
+		else
+		{
+			tintin_printf2(ses, "#MAP %s GET: No destination variable.", arg);
+		}
+	}
+	else if (is_abbrev(arg2, "NAME"))
+	{
+		RESTRING(exit->name, arg3);
+
+		show_message(ses, LIST_COMMAND, "#MAP %s {%s} : NAME SET TO {%s}.", arg, arg1, exit->name);
+	}
+	else if (is_abbrev(arg2, "SAVE"))
+	{
+		if (*arg3)
+		{
+			set_nest_node_ses(ses, arg3, "{command}{%s}{destination}{%d}{dir}{%d}{flags}{%d}{name}{%s}{vnum}{%d}{weight}{%.3f}", exit->cmd, tunnel_void(ses, ses->map->in_room, exit->vnum, exit->dir), exit->dir, exit->flags, exit->name, exit->vnum, exit->weight);
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#MAP %s SAVE: No destination variable.", arg);
+		}
+	}
+	else if (is_abbrev(arg2, "SET"))
+	{
+		RESTRING(exit->data, arg3);
+
+		show_message(ses, LIST_COMMAND, "#MAP %s {%s} : DATA SET TO {%s}.", arg, arg1, exit->data);
+	}
+	else if (is_abbrev(arg2, "VNUM"))
+	{
+		room = atoi(arg3);
+
+		if (room <= 0 || room >= ses->map->size)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP %s VNUM: Invalid room vnum: %d.", arg, room);
+			return;
+		}
+
+		if (ses->map->room_list[room] == NULL)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP %s VNUM: Non existant room vnum: %d.", arg, room);
+			return;
+		}
+		exit->vnum = room;
+
+		show_message(ses, LIST_COMMAND, "#MAP %s {%s} : VNUM SET TO {%s}.", arg, arg1, arg3);
+	}
+	else if (is_abbrev(arg2, "WEIGHT"))
+	{
+		if (get_number(ses, arg3) < 0.001)
+		{
+			show_message(ses, LIST_COMMAND, "#MAP %s {%s} : WEIGHT SHOULD BE AT LEAST 0.001", arg, arg1);
+		}
+		else
+		{
+			exit->weight = (float) get_number(ses, arg3);
+
+			show_message(ses, LIST_COMMAND, "#MAP %s {%s} : WEIGHT SET TO {%.3f}", arg, arg1, exit->weight);
+		}
+	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "Syntax: #MAP %s {<NAME>} {COMMAND|DIRECTION|GET|NAME|FLAGS|SAVE|SET|VNUM|WEIGHT} {<argument>}", arg);
+	}
+}
+
+DO_MAP(map_entrance)
+{
+	char arg3[BUFFER_SIZE];
+	struct exit_data *exit, *rev_exit;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+
+	exit = find_exit(ses, ses->map->in_room, arg1);
+
+	if (exit == NULL)
+	{
+		show_message(ses, LIST_COMMAND, "#MAP ENTRANCE: Exit {%s} not found.", arg1);
+		
+		return;
+	}
+
+	rev_exit = ses->map->room_list[exit->vnum]->exit_grid[revdir_to_grid(exit->dir)];
+
+	if (rev_exit == NULL)
+	{
+		show_message(ses, LIST_COMMAND, "#MAP ENTRANCE {%s}: Exit {%s} has no matching entrance.");
+
+		return;
+	}
+
+	exit_edit(ses, rev_exit, "ENTRANCE", arg1, arg2, arg3);
+}
+
+DO_MAP(map_exit)
+{
+	char arg3[BUFFER_SIZE];
+	struct exit_data *exit;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+
+	exit = find_exit(ses, ses->map->in_room, arg1);
+
+	if (exit == NULL)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP EXIT: Exit {%s} not found.", arg1);
+		
+		return;
+	}
+
+	exit_edit(ses, exit, "EXIT", arg1, arg2, arg3);
+}
+
+DO_MAP(map_exitflag)
+{
+	struct exit_data *exit;
+	char arg3[BUFFER_SIZE];
+	int flag;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+
+	exit = find_exit(ses, ses->map->in_room, arg1);
+
+	if (exit == NULL)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP EXITFLAG: EXIT {%s} NOT FOUND.", arg1);
+
+		return;
+	}
+
+	if (*arg2 == 0)
+	{
+		tintin_printf2(ses, "#MAP: AVOID FLAG IS SET TO: %s.", HAS_BIT(exit->flags, EXIT_FLAG_AVOID) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: HIDE FLAG IS SET TO: %s.", HAS_BIT(exit->flags, EXIT_FLAG_HIDE) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: INVIS FLAG IS SET TO: %s.", HAS_BIT(exit->flags, EXIT_FLAG_INVIS) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: BLOCK FLAG IS SET TO: %s.", HAS_BIT(exit->flags, EXIT_FLAG_BLOCK) ? "ON" : "OFF");
+
+		return;
+	}
+
+	if (is_abbrev(arg2, "AVOID"))
+	{
+		flag = EXIT_FLAG_AVOID;
+	}
+	else if (is_abbrev(arg2, "BLOCK"))
+	{
+		flag = EXIT_FLAG_BLOCK;
+	}
+	else if (is_abbrev(arg2, "HIDE"))
+	{
+		flag = EXIT_FLAG_HIDE;
+	}
+	else if (is_abbrev(arg2, "INVISIBLE"))
+	{
+		flag = EXIT_FLAG_INVIS;
+	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP EXITFLAG {%s} <AVOID|HIDE|INVIS> [ON|OFF]", arg1);
+
+		return;
+	}
+
+	if (*arg3 == 0)
+	{
+		TOG_BIT(exit->flags, flag);
+	}
+	else if (is_abbrev(arg3, "ON"))
+	{
+		SET_BIT(exit->flags, flag);
+	}
+	else if (is_abbrev(arg3, "OFF"))
+	{
+		DEL_BIT(exit->flags, flag);
+	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP EXITFLAG {%s} {%s} [ON|OFF]", arg3);
+	}
+
+	if (is_abbrev(arg2, "AVOID"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: AVOID FLAG SET TO %s.", HAS_BIT(exit->flags, EXIT_FLAG_AVOID) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg2, "BLOCK"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: BLOCK FLAG SET TO %s.", HAS_BIT(exit->flags, EXIT_FLAG_BLOCK) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg2, "HIDE"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: HIDE FLAG SET TO %s.", HAS_BIT(exit->flags, EXIT_FLAG_HIDE) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg2, "INVISIBLE"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: INVIS FLAG SET TO %s.", HAS_BIT(exit->flags, EXIT_FLAG_INVIS) ? "ON" : "OFF");
+	}
+}
+
+DO_MAP(map_explore)
+{
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	explore_path(ses, FALSE, arg1, "");
+}
+
+DO_MAP(map_find)
+{
+	shortest_path(ses, FALSE, arg1, arg);
+}
+
+DO_MAP(map_flag)
+{
+	int flag = 0, unflag = 0;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1)
+	{
+		if (is_abbrev(arg1, "static"))
+		{
+			flag   = MAP_FLAG_STATIC;
+		}
+		else if (is_abbrev(arg1, "vtmap"))
+		{
+			flag   = MAP_FLAG_VTMAP;
+		}
+		else if (is_abbrev(arg1, "asciigraphics"))
+		{
+			flag   = MAP_FLAG_ASCIIGRAPHICS;
+			unflag = MAP_FLAG_MUDFONT|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_BLOCKGRAPHICS;
+		}
+		else if (is_abbrev(arg1, "asciilength"))
+		{
+			flag   = MAP_FLAG_ASCIIVNUMS|MAP_FLAG_ASCIILENGTH;
+		}
+		else if (is_abbrev(arg1, "asciivnums"))
+		{
+			flag   = MAP_FLAG_ASCIIVNUMS;
+			unflag = MAP_FLAG_ASCIILENGTH;
+		}
+		else if (is_abbrev(arg1, "blockgraphics"))
+		{
+			flag = MAP_FLAG_BLOCKGRAPHICS;
+			unflag = MAP_FLAG_MUDFONT|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_ASCIIGRAPHICS;
+		}
+		else if (is_abbrev(arg1, "direction"))
+		{
+			flag = MAP_FLAG_DIRECTION;
+		}
+		else if (is_abbrev(arg1, "mudfont"))
+		{
+			flag = MAP_FLAG_MUDFONT;
+			unflag = MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_BLOCKGRAPHICS;
+		}
+		else if (is_abbrev(arg1, "nofollow"))
+		{
+			flag = MAP_FLAG_NOFOLLOW;
+		}
+		else if (is_abbrev(arg1, "simplegraphics"))
+		{
+			unflag = MAP_FLAG_ASCIIVNUMS|MAP_FLAG_SYMBOLGRAPHICS|MAP_FLAG_MUDFONT|MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_BLOCKGRAPHICS;
+		}
+		else if (is_abbrev(arg1, "symbolgraphics"))
+		{
+			flag = MAP_FLAG_SYMBOLGRAPHICS;
+			unflag = MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_BLOCKGRAPHICS;
+		}
+		else if (is_abbrev(arg1, "terrain"))
+		{
+			flag = MAP_FLAG_TERRAIN;
+		}
+		else if (is_abbrev(arg1, "unicodegraphics"))
+		{
+			flag = MAP_FLAG_UNICODEGRAPHICS;
+			unflag = MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_MUDFONT|MAP_FLAG_BLOCKGRAPHICS;
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#MAP: Invalid flag {%s}.", arg1);
+
+			return;
+		}
+	}
+	else
+	{
+		tintin_printf2(ses, "#MAP: AsciiGraphics flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: AsciiVnums flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: BlockGraphics flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: Direction flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_DIRECTION) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: MudFont flag is set to %s", HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: NoFollow flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: SimpleGraphics flag is set to %s.", !HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS|MAP_FLAG_SYMBOLGRAPHICS|MAP_FLAG_MUDFONT|MAP_FLAG_ASCIIGRAPHICS|MAP_FLAG_UNICODEGRAPHICS|MAP_FLAG_BLOCKGRAPHICS) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: Static flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_STATIC) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: SymbolGraphics flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_SYMBOLGRAPHICS) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: Terrain flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_TERRAIN) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: UnicodeGraphics flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: VTmap flag is set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP) ? "ON" : "OFF");
+
+		return;
+	}
+
+	if (is_abbrev(arg2, "ON"))
+	{
+		SET_BIT(ses->map->flags, flag);
+	}
+	else if (is_abbrev(arg2, "OFF"))
+	{
+		DEL_BIT(ses->map->flags, flag);
+	}
+	else
+	{
+		TOG_BIT(ses->map->flags, flag);
+	}
+
+	if (unflag)
+	{
+		DEL_BIT(ses->map->flags, unflag);
+	}
+
+	if (is_abbrev(arg1, "asciigraphics"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: AsciiGraphics flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg1, "asciivnums"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: AsciiVnums flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg1, "direction"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: Direction flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_DIRECTION) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg1, "mudfont"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: MudFont flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg1, "nofollow"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: NoFollow flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg1, "static"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: Static flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_STATIC) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg1, "simplegraphics"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: All graphic modes have been disabled.");
+	}
+	else if (is_abbrev(arg1, "symbolgraphics"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: SymbolGraphics flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_SYMBOLGRAPHICS) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg1, "terrain"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: Terrain flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_TERRAIN) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg1, "unicodegraphics"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: UnicodeGraphics flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) ? "ON" : "OFF");
+	}
+	else if (is_abbrev(arg1, "vtmap"))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: VTmap flag set to %s.", HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP) ? "ON" : "OFF");
+	}
+
+
+}
+
+DO_MAP(map_get)
+{
+	struct room_data *room = ses->map->room_list[ses->map->in_room];
+	struct exit_data *exit;
+	char exits[BUFFER_SIZE], arg3[BUFFER_SIZE];
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg3)
+	{
+		if (atoi(arg3) > 0 && atoi(arg3) < ses->map->size)
+		{
+			room = ses->map->room_list[atoi(arg3)];
+		}
+		else
+		{
+			room = NULL;
+		}
+	}
+
+	if (room == NULL)
+	{
+		set_nest_node_ses(ses, arg2, "0");
+	}
+	else if (*arg1 == 0 || *arg2 == 0)
+	{
+		tintin_printf2(ses, " worldflags: %d", ses->map->flags);
+		tintin_printf2(ses, "  worldsize: %d", ses->map->size);
+		tintin_printf2(ses, "");
+		tintin_printf2(ses, "   roomvnum: %d", room->vnum);
+		tintin_printf2(ses, "   roomarea: %s", room->area);
+		tintin_printf2(ses, "  roomcolor: %s", room->color);
+		tintin_printf2(ses, "   roomdata: %s", room->data);
+		tintin_printf2(ses, "   roomdesc: %s", room->desc);
+		tintin_printf2(ses, "  roomexits: %d", get_room_exits(ses, room->vnum));
+		tintin_printf2(ses, "  roomflags: %d", room->flags);
+		tintin_printf2(ses, "     roomid: %s", room->id);
+		tintin_printf2(ses, "   roomname: %s", room->name);
+		tintin_printf2(ses, "   roomnote: %s", room->note);
+		tintin_printf2(ses, " roomsymbol: %s", room->symbol);
+		tintin_printf2(ses, "roomterrain: %s", room->terrain);
+		tintin_printf2(ses, " roomweight: %.3f", room->weight);
+	}
+	else
+	{
+		if (is_abbrev(arg1, "all"))
+		{
+			exits[0] = 0;
+
+			for (exit = room->f_exit ; exit ; exit = exit->next)
+			{
+				cat_sprintf(exits, "{%s}{%d}", exit->name, exit->vnum);
+			}
+			set_nest_node_ses(ses, arg2, "{area}{%s}{color}{%s}{data}{%s}{desc}{%s}{exits}{%s}{flags}{%d}{id}{%d}{name}{%s}{note}{%s}{symbol}{%s}{terrain}{%s}{vnum}{%d}{weight}{%.3f}", room->area, room->color, room->data, room->desc, exits, room->flags, room->id, room->name, room->note, room->symbol, room->terrain, room->vnum, room->weight);
+		}
+		else if (is_abbrev(arg1, "roomarea"))
+		{
+			set_nest_node_ses(ses, arg2, "%s", room->area);
+		}
+		else if (is_abbrev(arg1, "roomcolor"))
+		{
+			set_nest_node_ses(ses, arg2, "%s", room->color);
+		}
+		else if (is_abbrev(arg1, "roomdata"))
+		{
+			set_nest_node_ses(ses, arg2, "%s", room->data);
+		}
+		else if (is_abbrev(arg1, "roomdesc"))
+		{
+			set_nest_node_ses(ses, arg2, "%s", room->desc);
+		}
+		else if (is_abbrev(arg1, "roomflags"))
+		{
+			set_nest_node_ses(ses, arg2, "%d", room->flags);
+		}
+		else if (is_abbrev(arg1, "roomid"))
+		{
+			set_nest_node_ses(ses, arg2, "%s", room->id);
+		}
+		else if (is_abbrev(arg1, "roomname"))
+		{
+			set_nest_node_ses(ses, arg2, "%s", room->name);
+		}
+		else if (is_abbrev(arg1, "roomnote"))
+		{
+			set_nest_node_ses(ses, arg2, "%s", room->note);
+		}
+		else if (is_abbrev(arg1, "roomsymbol"))
+		{
+			set_nest_node_ses(ses, arg2, "%s", room->symbol);
+		}
+		else if (is_abbrev(arg1, "roomterrain"))
+		{
+			set_nest_node_ses(ses, arg2, "%s", room->terrain);
+		}
+		else if (is_abbrev(arg1, "roomvnum"))
+		{
+			set_nest_node_ses(ses, arg2, "%d", room->vnum);
+		}
+		else if (is_abbrev(arg1, "roomweight"))
+		{
+			set_nest_node_ses(ses, arg2, "%.3f", room->weight);
+		}
+		else if (is_abbrev(arg1, "roomexits"))
+		{
+			exits[0] = 0;
+
+			for (exit = room->f_exit ; exit ; exit = exit->next)
+			{
+				cat_sprintf(exits, "{%s}{%d}", exit->name, exit->vnum);
+			}
+			set_nest_node_ses(ses, arg2, "%s", exits);
+		}
+		else if (is_abbrev(arg1, "worldflags"))
+		{
+			set_nest_node_ses(ses, arg2, "%d", ses->map->flags);
+		}
+		else if (is_abbrev(arg1, "worldsize"))
+		{
+			set_nest_node_ses(ses, arg2, "%d", ses->map->size);
+		}
+		else
+		{
+			show_message(ses, LIST_COMMAND, "#MAP GET: unknown option: %s.", arg1);
+		}
+	}
+}
+
+DO_MAP(map_global)
+{
+	int room;
+
+	sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
+	{
+		show_message(ses, LIST_COMMAND, "#MAP GLOBAL: GLOBAL ROOM SET TO VNUM %d.", ses->map->global_vnum);
+	}
+	else if (!strcmp(arg1, "0"))
+	{
+		ses->map->global_vnum = ses->map->global_exit->vnum = 0;
+
+		show_message(ses, LIST_COMMAND, "#MAP GLOBAL: GLOBAL ROOM SET TO VNUM %d.", 0);
+	}
+	else
+	{
+		room = find_room(ses, arg);
+
+		if (room)
+		{
+			ses->map->global_vnum = ses->map->global_exit->vnum = room;
+
+			show_message(ses, LIST_COMMAND, "#MAP GLOBAL: GLOBAL ROOM SET TO VNUM %d.", room);
+		}
+		else
+		{
+			show_message(ses, LIST_COMMAND, "#MAP GLOBAL: COULDN'T FIND ROOM %s.", arg1);
+		}
+	}
+}
+
+DO_MAP(map_goto)
+{
+	int room;
+
+	room = find_room(ses, arg);
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN); // look for dig argument
+
+	if (room == 0 && ses->map->search->vnum > 0 && ses->map->search->vnum < ses->map->size && !strcasecmp(arg2, "dig"))
+	{
+		room = ses->map->search->vnum;
+
+		create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {}", room);
+	}
+
+	if (room == 0)
+	{
+		room = find_room(ses, arg1);
+	}
+
+	if (room == 0 && ses->map->search->id && *ses->map->search->id && !strcasecmp(arg2, "dig"))
+	{
+		room = find_new_room(ses);
+
+		if (room)
+		{
+			create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {%s}", room, ses->map->search->id);
+		}
+	}
+
+	if (room == 0 && ses->map->in_room)
+	{
+		room = find_path(ses, arg1);
+
+		if (room == 0)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP GOTO: COULDN'T FIND ROOM OR EXIT {%s}.", arg1);
+
+			return;
+		}
+	}
+
+	if (room == 0)
+	{
+		show_message(ses, LIST_COMMAND, "#MAP GOTO: COULDN'T FIND ROOM %s.", arg1);
+
+		return;
+	}
+	add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_MOVE);
+
+	goto_room(ses, room);
+
+	show_message(ses, LIST_COMMAND, "#MAP GOTO: MOVED TO ROOM %d {%s}.", room, *ses->map->room_list[room]->name ? ses->map->room_list[room]->name : ses->map->room_list[room]->id);
+}
+
+DO_MAP(map_info)
+{
+	int room, cnt, exits;
+	struct exit_data *exit;
+	struct room_data *in_room = ses->map->room_list[ses->map->in_room];
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (ses->map->in_room)
+	{
+		exit = find_exit(ses, ses->map->in_room, arg1);
+
+		if (exit)
+		{
+			tintin_printf2(ses, "    color: %s", str_convert_meta(exit->color, TRUE));
+			tintin_printf2(ses, "  command: %s", exit->cmd);
+			tintin_printf2(ses, "direction: %d", exit->dir);
+			tintin_printf2(ses, "    flags: %d", exit->flags);
+			tintin_printf2(ses, "  get/set: %s", exit->data);
+			tintin_printf2(ses, "   length: %d", get_exit_length(ses, exit));
+			tintin_printf2(ses, "     name: %s", exit->name);
+			tintin_printf2(ses, "     vnum: %d", exit->vnum);
+			tintin_printf2(ses, "   weight: %.3f", exit->weight);
+
+			return;
+		}
+	}
+
+	for (room = cnt = exits = 0 ; room < ses->map->size ; room++)
+	{
+		if (ses->map->room_list[room])
+		{
+			cnt++;
+
+			exits += get_room_exits(ses, room);
+		}
+	}
+
+	tintin_printf2(ses, " %+16s %-7d %+16s %-7d %+16s %-7d", "Total rooms:", cnt, "Total exits:", exits, "World size:", ses->map->size);
+	tintin_printf2(ses, " %+16s %-7d %+16s %-7d %+16s %-7d",  "Direction:", ses->map->dir, "Last room:", ses->map->last_room, "Undo size:", ses->map->undo_size);
+	tintin_printf2(ses, "");
+
+	strcpy(arg1, "");
+	cat_sprintf(arg1, " %+16s %-7s", "AsciiGraphics:", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS) ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s", "AsciiVnums:", HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS) ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s", "BlockGraphics:", HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS) ? "on" : "off");
+	tintin_puts2(ses, arg1);
+
+	strcpy(arg1, "");
+	cat_sprintf(arg1, " %+16s %-7s", "Direction:", HAS_BIT(ses->map->flags, MAP_FLAG_DIRECTION) ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s", "MudFont:", HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT) ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s", "Nofollow:", HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW) ? "on" : "off");
+	tintin_puts2(ses, arg1);
+
+	strcpy(arg1, "");
+	cat_sprintf(arg1, " %+16s %-7s", "Static:", HAS_BIT(ses->map->flags, MAP_FLAG_STATIC) ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s", "SymbolGraphics:", HAS_BIT(ses->map->flags, MAP_FLAG_SYMBOLGRAPHICS) ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s", "UnicodeGraphics:", HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) ? "on" : "off");
+	tintin_puts2(ses, arg1);
+
+	strcpy(arg1, "");
+	cat_sprintf(arg1, " %+16s %-7s", "Vtmap:", HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP) ? "on" : "off");
+	tintin_puts2(ses, arg1);
+
+/*
+	strcpy(arg1, "");
+	cat_sprintf(arg1, " %+16s %-7s",
+	cat_sprintf(arg1, " %+16s %-7s",
+	cat_sprintf(arg1, " %+16s %-7s",
+	tintin_puts2(ses, arg1);
+*/
+
+	tintin_printf2(ses, "");
+	tintin_printf2(ses, " %+16s %4d %4d %4d %4d", "Map Offset:", ses->map->sav_top_row, ses->map->sav_top_col, ses->map->sav_bot_row, ses->map->sav_bot_col);
+	tintin_printf2(ses, " %+16s %4d %4d %4d %4d", "Current Offset:", ses->map->top_row, ses->map->top_col, ses->map->bot_row, ses->map->bot_col);
+
+	if (ses->map->in_room == 0)
+	{
+		return;
+	}
+
+	tintin_printf2(ses, "");
+
+	tintin_printf2(ses, "%+16s %s", "Room area:",    in_room->area);
+	tintin_printf2(ses, "%+16s %s", "Room data:",    in_room->data);
+	tintin_printf2(ses, "%+16s %s", "Room desc:",    in_room->desc);
+	tintin_printf2(ses, "%+16s %s", "Room id:",      in_room->id);
+	tintin_printf2(ses, "%+16s %s", "Room name:",    in_room->name);
+	tintin_printf2(ses, "%+16s %s", "Room note:",    in_room->note);
+	tintin_printf2(ses, "%+16s %s", "Room color:",   str_convert_meta(in_room->color, TRUE));
+	tintin_printf2(ses, "%+16s %s (%d)", "Room terrain:", in_room->terrain, in_room->terrain_index);
+
+	tintin_printf2(ses, "");
+	tintin_printf2(ses, " %+16s %-7d %+16s %-7.3f %+16s %-7s", "Room vnum:", ses->map->in_room, "Room weight:", in_room->weight, "Room symbol:", in_room->symbol);
+
+	tintin_printf2(ses, " %+16s %-7d %+16s %-7d %+16s %-7d",   "Center x:", ses->map->center_x, "Center y:", ses->map->center_y, "Center z:", ses->map->center_z);
+
+	tintin_printf2(ses, "");
+
+	strcpy(arg1, "");
+	cat_sprintf(arg1, " %+16s %-7s",    "Avoid:", HAS_BIT(in_room->flags, ROOM_FLAG_AVOID)    ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s",   "Curved:", HAS_BIT(in_room->flags, ROOM_FLAG_CURVED)   ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s",     "Hide:", HAS_BIT(in_room->flags, ROOM_FLAG_HIDE)     ? "on" : "off");
+
+	tintin_puts2(ses, arg1);
+
+	strcpy(arg1, "");
+	cat_sprintf(arg1, " %+16s %-7s",    "Invis:", HAS_BIT(in_room->flags, ROOM_FLAG_INVIS)    ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s",    "Leave:", HAS_BIT(in_room->flags, ROOM_FLAG_LEAVE)    ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s", "NoGlobal:", HAS_BIT(in_room->flags, ROOM_FLAG_NOGLOBAL) ? "on" : "off");
+	tintin_puts2(ses, arg1);
+
+	strcpy(arg1, "");
+	cat_sprintf(arg1, " %+16s %-7s",   "Static:", HAS_BIT(in_room->flags, ROOM_FLAG_STATIC)   ? "on" : "off");
+	cat_sprintf(arg1, " %+16s %-7s",     "Void:", HAS_BIT(in_room->flags, ROOM_FLAG_VOID)     ? "on" : "off");
+	tintin_puts2(ses, arg1);
+
+	tintin_printf2(ses, "");
+
+	for (exit = in_room->f_exit ; exit ; exit = exit->next)
+	{
+		tintin_printf2(ses, "%+16s %-3s (%3s)   to room: %-5d (%5s)", "Exit:", exit->name, exit->cmd, exit->vnum, ses->map->room_list[exit->vnum]->name);
+	}
+
+	tintin_printf2(ses, "");
+
+	for (room = 0 ; room < ses->map->size ; room++)
+	{
+		if (ses->map->room_list[room])
+		{
+			for (exit = ses->map->room_list[room]->f_exit ; exit ; exit = exit->next)
+			{
+				if (exit->vnum == ses->map->in_room)
+				{
+					tintin_printf2(ses, "%+16s %-3s (%3s) from room: %-5d (%5s)", "Entrance:", exit->name, exit->cmd, room, ses->map->room_list[room]->name);
+				}
+			}
+		}
+	}
+/*
+	for (exit = in_room->f_exit ; exit ; exit = exit->next)
+	{
+		tintin_printf2(ses, "%+14s %-4s %+14s %5d %+14s %5d %+14s %5s", "Exit name:", exit->name, "vnum:", exit->vnum, "flags:", exit->flags, "command:", exit->cmd);
+		tintin_printf2(ses, "%+14s %s", "Exit data:", exit->data);
+	}
+*/
+}
+
+DO_MAP(map_insert)
+{
+	int room, in_room, to_room;
+	struct exit_data *exit;
+	struct listnode *node;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+
+	for (room = 1 ; room < ses->map->size ; room++)
+	{
+		if (ses->map->room_list[room] == NULL)
+		{
+			break;
+		}
+	}
+
+	exit = find_exit(ses, ses->map->in_room, arg1);
+
+	node = search_node_list(ses->list[LIST_PATHDIR], arg1);
+
+	if (exit == NULL)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP: There is no room in that direction.");
+
+		return;
+	}
+
+	if (room == ses->map->size)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP: Maximum amount of rooms of %d reached.", ses->map->size);
+		return;
+	}
+
+	if (node == NULL)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP: Given direction must be a pathdir.");
+		return;
+	}
+
+	in_room = ses->map->in_room;
+	to_room = exit->vnum;
+
+	add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_INSERT);
+
+	create_room(ses, "{%d} {0} {} {} { } {} {} {} {} {} {1.0} {}", room);
+
+	create_exit(ses, room, "{%d} {%s} {%s}", to_room, node->arg1, node->arg1);
+
+	create_exit(ses, room, "{%d} {%s} {%s}", in_room, node->arg2, node->arg2);
+
+	exit->vnum = room;
+
+	if ((exit = find_exit(ses, to_room, node->arg2)) != NULL)
+	{
+		exit->vnum = room;
+	}
+
+	if (*arg)
+	{
+		ses->map->in_room = room;
+		map_roomflag(ses, arg, arg1, arg2);
+		ses->map->in_room = in_room;
+	}
+	show_message(ses, LIST_COMMAND, "#MAP: Inserted room {%d}.", room);
+}
+
+DO_MAP(map_jump)
+{
+	int room;
+
+	sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	room = find_location(ses, arg1);
+
+	if (room)
+	{
+		add_undo(ses, "%d %d %d", room, ses->map->in_room, MAP_UNDO_MOVE);
+
+		goto_room(ses, room);
+
+		show_message(ses, LIST_COMMAND, "#MAP JUMP: JUMPED TO ROOM %d {%s}.", room, *ses->map->room_list[room]->name ? ses->map->room_list[room]->name : ses->map->room_list[room]->id);
+	}
+	else
+	{
+		show_message(ses, LIST_COMMAND, "#MAP JUMP: Couldn't find a room at {%s}.", arg1);
+	}
+}
+
+DO_MAP(map_landmark)
+{
+	struct listroot *root = ses->list[LIST_LANDMARK];
+	struct listnode *node;
+	char arg3[BUFFER_SIZE], arg4[BUFFER_SIZE];
+	int room, i, found;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg4, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0 || *arg2 == 0)
+	{
+		i = bsearch_alpha_list(root, arg1, 0);
+
+		if (i > 0)
+		{
+			tintin_printf2(ses, "name: %-16s  vnum:%7d  size: %7s desc %s", root->list[i]->arg1, root->list[i]->val32[0], root->list[i]->arg4, root->list[i]->arg3);
+		}
+		else
+		{
+			for (found = i = 0 ; i < root->used ; i++)
+			{
+				if (*arg1 == 0 || match(ses, root->list[i]->arg1, arg1, SUB_NONE))
+				{
+					tintin_printf2(ses, "name: %-16s  vnum:%7d  size: %7s desc: %s", root->list[i]->arg1, root->list[i]->val32[0], root->list[i]->arg4, root->list[i]->arg3);
+
+					found = TRUE;
+				}
+			}
+
+			if (found == FALSE)
+			{
+				show_message(ses, LIST_COMMAND, "#MAP LANDMARK: NO MATCHES FOUND FOR {%s}.", arg1);
+			}
+		}
+	}
+	else
+	{
+		if (is_math(ses, arg2))
+		{
+			room = (int) get_number(ses, arg2);
+		}
+
+		if (room <= 0 || room >= ses->map->size)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP LANDMARK: INVALID VNUM {%s}.", arg2);
+		}
+		else
+		{
+			node = update_node_list(root, arg1, arg2, arg3, arg4);
+
+			node->val32[0] = room;
+
+			show_message(ses, LIST_COMMAND, "#OK. LANDMARK {%s} HAS VNUM {%d} AND IS DESCRIBED AS {%s} WITH SIZE {%s}.", arg1, room, arg3, arg4);
+		}
+	} 
+}
+
+DO_MAP(map_unlandmark)
+{
+	delete_node_with_wild(ses, LIST_LANDMARK, arg);
+}
+
+DO_MAP(map_leave)
+{
+	if (ses->map->in_room == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP: You're not currently inside the map.");
+	}
+	else
+	{
+		ses->map->last_room = ses->map->in_room;
+		ses->map->in_room = 0;
+
+		show_message(ses, LIST_COMMAND, "#MAP: Leaving the map. Use goto or return to return.");
+
+		check_all_events(ses, SUB_ARG|SUB_SEC, 0, 1, "MAP EXIT MAP", ntos(ses->map->in_room));
+	}
+}
+
+void map_legend_index(struct session *ses, char *arg, int head, int tail)
+{
+	char esc[BUFFER_SIZE], raw[BUFFER_SIZE];
+	int cnt;
+
+	for (cnt = head ; cnt < tail ; cnt++)
+	{
+		arg = sub_arg_in_braces(ses, arg, raw, GET_ONE, SUB_NONE);
+
+		substitute(ses, raw, esc, SUB_ESC);
+
+		if (is_number(esc))
+		{
+			numbertocharacter(ses, esc);
+		}
+		snprintf(ses->map->legend[cnt],     LEGEND_SIZE - 1, "%s", esc);
+		snprintf(ses->map->legend_raw[cnt], LEGEND_SIZE - 1, "%s", raw);
+
+		if (*arg == 0)
+		{
+			break;
+		}
+	}
+	return;
+}
+
+DO_MAP(map_legend)
+{
+	char buf[BUFFER_SIZE], arg3[BUFFER_SIZE];
+	int group, legend;
+
+	push_call("map_legend(%p,%p,%p,%p)",ses,arg,arg1,arg2);
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
+
+	strcpy(buf, arg2);
+
+	if (*arg1 == 0)
+	{
+		for (group = 0 ; map_group_table[group].name ; group++)
+		{
+			tintin_printf2(ses, " [%-22s] [%-22s] [%3d] [%3d]",
+				map_group_table[group].group,
+				map_group_table[group].name,
+				map_group_table[group].start,
+				map_group_table[group].end);
+		}
+		pop_call();
+		return;
+	}
+
+	if (is_abbrev(arg1, "RESET"))
+	{
+		map_legend(ses, "{ASCII} {RESET}", arg1, arg2);
+		map_legend(ses, "{NESW} {RESET}", arg1, arg2);
+		map_legend(ses, "{MUDFONT BRAILLE TUBE} {RESET}", arg1, arg2);
+		map_legend(ses, "{UNICODE GRAPHICS} {RESET}", arg1, arg2);
+
+		pop_call();
+		return;
+	}
+
+	if (is_math(ses, arg1))
+	{
+		legend = (int) get_number(ses, arg1);
+
+		if (legend < map_group_table[0].start || legend >= map_group_table[0].end)
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP LEGEND {%d - %d} {[SYMBOL]}", map_group_table[0].start,  map_group_table[0].end);
+		}
+		else if (*arg2 == 0)
+		{
+			if (strip_vt102_strlen(ses, ses->map->legend[legend]) > 1)
+			{
+				tintin_printf2(ses, " [%-22s] [%-20s] [%3d] [ %12s ] [ %s]",
+					map_legend_table[legend].group,
+					map_legend_table[legend].name,
+					legend,
+					ses->map->legend_raw[legend],
+					ses->map->legend[legend]);
+			}
+			else
+			{
+				tintin_printf2(ses, " [%-22s] [%-20s] [%3d] [ %12s ] [ %s ]",
+					map_legend_table[legend].group,
+					map_legend_table[legend].name,
+					legend,
+					ses->map->legend_raw[legend],
+					ses->map->legend[legend]);
+			}
+		}
+		else
+		{
+			map_legend_index(ses, arg2, legend, legend + 1);
+		}
+		pop_call();
+		return;
+	}
+
+	for (group = 0 ; map_group_table[group].name ; group++)
+	{
+		if (is_abbrev(arg1, map_group_table[group].name))
+		{
+			break;
+		}
+	}
+
+
+	if (!map_group_table[group].name)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP LEGEND: UNKNOWN LEGEND {%s} TRY:", arg1);
+
+		map_legend(ses, "", arg1, arg2);
+
+		pop_call();
+		return;
+	}
+
+	if (*arg2 == 0)
+	{
+		for (legend = map_group_table[group].start ; legend < map_group_table[group].end ; legend++)
+		{
+			if (strip_vt102_strlen(ses, ses->map->legend[legend]) > 1)
+			{
+				tintin_printf2(ses, " [%-22s] [%-20s] [%3d] [ %12s ] [ %s]",
+					map_legend_table[legend].group,
+					map_legend_table[legend].name,
+					legend,
+					ses->map->legend_raw[legend],
+					ses->map->legend[legend]);
+			}
+			else
+			{
+				tintin_printf2(ses, " [%-22s] [%-20s] [%3d] [ %12s ] [ %s ]",
+					map_legend_table[legend].group,
+					map_legend_table[legend].name,
+					legend,
+					ses->map->legend_raw[legend],
+					ses->map->legend[legend]);
+			}
+		}
+		pop_call();
+		return;
+	}
+
+	if (is_abbrev(arg2, "RESET"))
+	{
+		map_legend_index(ses, map_group_table[group].reset, map_group_table[group].start, map_group_table[group].end);
+
+		pop_call();
+		return;
+	}
+
+	for (legend = map_group_table[group].start ; legend < map_group_table[group].end ; legend++)
+	{
+		if (!strcasecmp(space_out(arg2), space_out(map_legend_table[legend].name)))
+		{
+			if (*arg3 == 0)
+			{
+				tintin_printf2(ses, "  [%-22s]  [%-20s]  [%3d]  [ %8s ]  [ %s ]",
+					map_group_table[group].name,
+					map_legend_table[legend].name,
+					legend,
+					ses->map->legend_raw[legend],
+					ses->map->legend[legend]);
+			}
+			else
+			{
+				map_legend_index(ses, arg3, legend, legend + 1);
+			}
+			break;
+		}
+	}
+
+	if (legend == map_group_table[group].end)
+	{
+		for (legend = map_group_table[group].start ; legend < map_group_table[group].end ; legend++)
+		{
+			if (is_abbrev(space_out(arg2), space_out(map_legend_table[legend].name)))
+			{
+				if (*arg3 == 0)
+				{
+					tintin_printf2(ses, "  [%-22s]  [%-20s]  [%3d]  [ %8s ]  [ %s ]",
+						map_group_table[group].name,
+						map_legend_table[legend].name,
+						legend,
+						ses->map->legend_raw[legend],
+						ses->map->legend[legend]);
+				}
+				else
+				{
+					map_legend_index(ses, arg3, legend, legend + 1);
+				}
+				break;
+			}
+		}
+	}
+
+
+	if (legend == map_group_table[group].end)
+	{
+		if (strlen(arg2) > (map_group_table[group].end - map_group_table[group].start) * 2)
+		{
+			map_legend_index(ses, arg2, map_group_table[group].start, map_group_table[group].end);
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP LEGEND {%s} {{arg %d} {arg %d} ... {arg %d} {arg %d}",
+				map_group_table[group].group,
+				map_group_table[group].start,
+				map_group_table[group].start - 1,
+				map_group_table[group].end - 1,
+				map_group_table[group].end);
+		}
+	}
+
+	pop_call();
+	return;
+}
+
+DO_MAP(map_link)
+{
+	char arg3[BUFFER_SIZE];
+	struct listnode *node;
+	struct exit_data *exit;
+	int room;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0 || *arg2 == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP LINK {<DIRECTION>} {<LOCATION>} {BOTH}");
+		return;
+	}
+
+	room = find_room(ses, arg2);
+
+	if (room == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP: Couldn't find room {%s}.", arg1);
+		return;
+	}
+
+	exit = find_exit(ses, ses->map->in_room, arg1);
+
+	if (exit)
 	{
-		struct room_data *room_w;
-		long long exit_w;
+		delete_exit(ses, ses->map->in_room, exit);
+	}
 
-		room_w = ses->map->grid_rooms[x - 1 + map_grid_x * (y + 0)];
-		exit_w = 0;
+	create_exit(ses, ses->map->in_room, "{%d} {%s} {%s}", room, arg1, arg1);
 
-		if (room_w)
+	if (is_abbrev(arg3, "both"))
+	{
+		if ((node = search_node_list(ses->list[LIST_PATHDIR], arg1)) != NULL)
 		{
-			if (HAS_BIT(room_w->exit_dirs, MAP_DIR_E))
-			{
-				SET_BIT(exit_w, MAP_DIR_E);
-			}
-			if (HAS_BIT(room_w->exit_dirs, MAP_DIR_U))
+			if (find_exit(ses, room, node->arg2) == NULL)
 			{
-				SET_BIT(exit_w, MAP_DIR_U);
+				create_exit(ses, room, "{%d} {%s} {%s}", ses->map->in_room, node->arg2, node->arg2);
 			}
 		}
+	}
+	show_message(ses, LIST_COMMAND, "#MAP LINK: Connected room {%s} to {%s}.", ses->map->room_list[ses->map->in_room]->name, ses->map->room_list[room]->name);
+}
 
-		sprintf(buf, "%s", room_color);
-
-		switch (line)
-		{
-			case 1:
-				switch (exit_w)
-				{
-					case 0:
-						strcat(buf, " ");
-						break;
-					case MAP_DIR_E:
-						strcat(buf, "═");
-						break;
-					case MAP_DIR_E|MAP_DIR_U:
-						strcat(buf, "═̂");
-						break;
-					case MAP_DIR_U:
-						strcat(buf, " ̂");
-						break;
-					default:
-						strcat(buf, "?");
-						break;
-				}
-
-				if (room == NULL)
-				{
-					strcat(buf, "    ");
-				}
-				else
-				{
-					strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_W) ? "═" : " ");
+DO_MAP(map_list)
+{
+	struct room_data *room;
+	char var[BUFFER_SIZE];
+	int vnum;
 
-					if (room->vnum == ses->map->in_room)
-					{
-						cat_sprintf(buf, "%s", ses->map->color[MAP_COLOR_USER]);
-					}
+	map_search_compile(ses, "0", var);
 
-					switch (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_W))
-					{
-						case MAP_DIR_N:
-							strcat(buf, "║");
-							break;
-						case MAP_DIR_W:
-							strcat(buf, "═");
-							break;
-						case MAP_DIR_N|MAP_DIR_W:
-							strcat(buf, "╝");
-							break;
-						default:
-							strcat(buf, "╔");
-							break;
-					}
+	searchgrid_find(ses, ses->map->in_room, ses->map->search);
 
+	map_search_compile(ses, arg, var);
 
-					strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_N) ? " " : "═");
+	set_nest_node_ses(ses, var, "");
 
+	for (vnum = 0 ; vnum < ses->map->size ; vnum++)
+	{
+		if (match_room(ses, vnum, ses->map->search))
+		{
+			room = ses->map->room_list[vnum];
 
-					switch (HAS_BIT(room->exit_dirs, MAP_DIR_N|MAP_DIR_E))
+			if (*var)
+			{
+				add_nest_node_ses(ses, var, "{%d} {{distance}{%.3f}{x}{%d}{y}{%d}{z}{%d}}", room->vnum, ses->map->search->stamp == room->search_stamp ? room->length  : -1, room->x, room->y, room->z);
+			}
+			else
+			{
+				if (ses->map->search->stamp == room->search_stamp)
+				{
+					if (room->w == 0)
 					{
-						case MAP_DIR_N:
-							strcat(buf, "║");
-							break;
-						case MAP_DIR_E:
-							strcat(buf, "═");
-							break;
-						case MAP_DIR_N|MAP_DIR_E:
-							strcat(buf, "╚");
-							break;
-						default:
-							strcat(buf, "╗");
-							break;
+						tintin_printf2(ses, "vnum: %5d  dist: %8.3f  x: %4d  y: %4d  z: %4d  name: %s", room->vnum, room->length, room->x, room->y, room->z, room->name);
 					}
-				}
-				break;
-
-			case 2:
-				switch (exit_w)
-				{
-					case 0:
-						strcat(buf, " ");
-						break;
-					case MAP_DIR_E:
-						strcat(buf, "═");
-						break;
-					case MAP_DIR_E|MAP_DIR_U:
-						strcat(buf, "═");
-						break;
-					default:
-						strcat(buf, " ");
-						break;
-				}
-
-				if (room == NULL)
-				{
-					strcat(buf, " ");
-				}
-				else
-				{
-					switch (HAS_BIT(room->exit_dirs, MAP_DIR_W|MAP_DIR_D))
+					else
 					{
-						case MAP_DIR_W:
-							strcat(buf, "═");
-							break;
-						case MAP_DIR_W|MAP_DIR_D:
-							strcat(buf, "═̬");
-							break;
-						case MAP_DIR_D:
-							strcat(buf, " ̬");
-							break;
-						default:
-							strcat(buf, " ");
-							break;
+						tintin_printf2(ses, "vnum: %5d  dist: %8.3f  x: %4s  y: %4s  z: %4s  name: %s", room->vnum, room->length, "?", "?", "?", room->name);
 					}
 				}
-
-				if (room == NULL)
-				{
-					strcat(buf, "   ");
-				}
 				else
 				{
-					if (room->vnum == ses->map->in_room)
-					{
-						cat_sprintf(buf, "%s", ses->map->color[MAP_COLOR_USER]);
-					}
-
-					switch (HAS_BIT(room->exit_dirs, MAP_DIR_S|MAP_DIR_W))
-					{
-						case MAP_DIR_S:
-							strcat(buf, "║");
-							break;
-						case MAP_DIR_W:
-							strcat(buf, "═");
-							break;
-						case MAP_DIR_S|MAP_DIR_W:
-							strcat(buf, "╗");
-								break;
-						default:
-							strcat(buf, "╚");
-							break;
-					}
-
-					strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_S) ? " " : "═");
-
-					switch (HAS_BIT(room->exit_dirs, MAP_DIR_S|MAP_DIR_E))
-					{
-						case MAP_DIR_S:
-							strcat(buf, "║");
-							break;
-						case MAP_DIR_E:
-							strcat(buf, "═");
-							break;
-						case MAP_DIR_S|MAP_DIR_E:
-							strcat(buf, "╔");
-							break;
-						default:
-							strcat(buf, "╝");
-							break;
-					}
+						tintin_printf2(ses, "vnum: %5d  dist: %8.8s  x:    ?  y:    ?  z:    ?  name: %s", room->vnum, "-1", room->name);
 				}
-				break;
+			}
 		}
-		pop_call();
-		return buf;
 	}
+}
 
-	if (room == NULL)
+DO_MAP(map_map)
+{
+	char arg3[BUFFER_SIZE], arg4[BUFFER_SIZE];
+	FILE *logfile = NULL;
+	int x, y, line, row;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg4, GET_ALL, SUB_VAR|SUB_FUN);
+
+	push_call("map_map(%p,%p)",ses,arg);
+
+	if (is_math(ses, arg1))
 	{
-		if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
-		{
-			sprintf(buf, "      ");
-		}
-		else if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
+		map_grid_y = get_number(ses, arg1);
+
+		if (map_grid_y <= 0)
 		{
-			sprintf(buf, "  ");
+			map_grid_y = UMAX(0, get_scroll_rows(ses) + map_grid_y);
 		}
-		else
+	}
+	else
+	{
+		map_grid_y = get_scroll_rows(ses);
+	}
+
+	if (is_math(ses, arg2))
+	{
+		map_grid_x = get_number(ses, arg2);
+
+		if (map_grid_x <= 0)
 		{
-			sprintf(buf, " ");
+			map_grid_x = UMAX(0, gtd->screen->cols + map_grid_x);
 		}
-		pop_call();
-		return buf;
+	}
+	else
+	{
+		map_grid_x = get_scroll_cols(ses);
 	}
 
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
+	if (*arg3)
 	{
-		sprintf(buf, "%s", ses->map->color[MAP_COLOR_EXIT]);
+		switch (*arg3)
+		{
+			case 'a':
+			case 'A':
+
+				strcpy(arg3, "APPEND");
+
+				logfile = fopen(arg4, "a");
 
-		switch (line)
-		{
-			case 1:
-				strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_NW) ? "\\ " : "  ");
-				strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_N)  ? "|"   : " ");
-				strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_U)  ? "+"   : " ");
-				strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_NE) ? "/ "  : "  ");
-				break;
+				loginit(ses, logfile, LOG_FLAG_APPEND | HAS_BIT(ses->logmode, LOG_FLAG_HTML));
 
-			case 2:
-				if (!HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS))
-				{
-					strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_W) ? "-" : " ");
-				}
+				break;
 
-				if (room->vnum == ses->map->in_room)
-				{
-					if (!HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS))
-					{
-						cat_sprintf(buf, "%s%s%s%s%s", room_left, ses->map->color[MAP_COLOR_USER], ses->map->legend[index], room_right, ses->map->color[MAP_COLOR_EXIT]);
-					}
-					else
-					{
-						cat_sprintf(buf, "%s%05d%s", ses->map->color[MAP_COLOR_USER], room->vnum, ses->map->color[MAP_COLOR_EXIT]);
-					}
-				}
-				else
-				{
-					if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS))
-					{
-						cat_sprintf(buf, "%s%05d%s", room_color, room->vnum, ses->map->color[MAP_COLOR_EXIT]);
-					}
-					else if (HAS_BIT(room->flags, ROOM_FLAG_VOID))
-					{
-						switch (room->exit_dirs)
-						{
-							case MAP_DIR_N|MAP_DIR_S:
-								cat_sprintf(buf, " | ");
-								break;
-							case MAP_DIR_E|MAP_DIR_W:
-								cat_sprintf(buf, "---");
-								break;
-							case MAP_DIR_NE|MAP_DIR_SW:
-								cat_sprintf(buf, " / ");
-								break;
-							case MAP_DIR_NW|MAP_DIR_SE:
-								cat_sprintf(buf, " \\ ");
-								break;
-							default:
-								cat_sprintf(buf, " * ");
-								break;
-						}
-					}
-					else
-					{
-						if (strip_color_strlen(ses, room->symbol) <= 1)
-						{
-							cat_sprintf(buf, "%s%s%-1s%s%s", room_left, ses->map->color[MAP_COLOR_SYMBOL], room->symbol, room_right, ses->map->color[MAP_COLOR_EXIT]);
-						}
-						else
-						{
-							cat_sprintf(buf, "%s%s%-3s%s", room_color, ses->map->color[MAP_COLOR_SYMBOL], room->symbol, ses->map->color[MAP_COLOR_EXIT]);
-						}
-					}
-				}
+			case 'd':
+			case 'D':
+				strcpy(arg3, "DRAW");
 
-				if (!HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIVNUMS))
-				{
-					strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_E) ? "--" : "  ");
-				}
-				else
+				if (*arg4 == 0)
 				{
-					strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_E) ? "-" : " ");
+					show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP MAP {%s} {%s} {%s} {square}", arg1, arg2, arg3);
+					pop_call();
+					return;
 				}
 				break;
 
-			case 3:
-				strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_SW) ? "/"   : " ");
-				strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_D)  ? "-"   : " ");
-				strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_S)  ? "| "  : "  ");
-				strcat(buf, HAS_BIT(room->exit_dirs, MAP_DIR_SE) ? "\\ " : "  ");
-				break;
-		}
-		pop_call();
-		return buf;
-	}
+			case 'o':
+			case 'O':
+				strcpy(arg3, "OVERWRITE");
 
+				logfile = fopen(arg4, "w");
 
-	if (room->vnum == ses->map->in_room)
-	{
-		exits = ses->map->dir;
+				loginit(ses, logfile, LOG_FLAG_OVERWRITE | HAS_BIT(ses->logmode, LOG_FLAG_HTML));
 
-		DEL_BIT(exits, MAP_EXIT_U|MAP_EXIT_D);
+				break;
 
-		if (HAS_BIT(ses->map->flags, MAP_FLAG_DIRECTION))
-		{
-			switch (exits)
-			{
-				case MAP_EXIT_N:
-					index = 24;
-					break;
-				case MAP_EXIT_N+MAP_EXIT_E:
-					index = 25;
-					break;
-				case MAP_EXIT_E:
-					index = 26;
-					break;
-				case MAP_EXIT_S+MAP_EXIT_E:
-					index = 27;
-					break;
-				case MAP_EXIT_S:
-					index = 28;
-					break;
-				case MAP_EXIT_S+MAP_EXIT_W:
-					index = 29;
-					break;
-				case MAP_EXIT_W:
-					index = 30;
-					break;
-				case MAP_EXIT_N+MAP_EXIT_W:
-					index = 31;
-					break;
+			case 'l':
+			case 'L':
+				strcpy(arg3, "LIST");
+				break;
 
-				default:
-					index = 17;
-					break;
-			}
-		}
-		else
-		{
-			index = 16;
-		}
+			case 'v':
+			case 'V':
+				strcpy(arg3, "VARIABLE");
+				break;
 
-		if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
-		{
-			sprintf(buf, "%s%s%s", ses->map->color[MAP_COLOR_USER], ses->map->legend[offset + index], ses->map->legend[offset + index]);
+			default:
+				show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP MAP {rows} {cols} {append|overwrite|list|variable} {name}");
+				pop_call();
+				return;
 		}
-		else
+
+		if (*arg4 == 0)
 		{
-			sprintf(buf, "%s%s", ses->map->color[MAP_COLOR_USER], ses->map->legend[offset + index]);
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP MAP {%s} {%s} {%s} {name}", arg1, arg2, arg3);
+			pop_call();
+			return;
 		}
-		pop_call();
-		return buf;
 	}
 
-	exit1 = 0;
-	exit2 = 0;
-	exits = 0;
-
-	if (HAS_BIT(room->exit_dirs, MAP_DIR_N))
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
 	{
-		SET_BIT(exit1, 1 << 0);
-		SET_BIT(exit2, 1 << 0);
-		SET_BIT(exits, MAP_EXIT_N);
+		map_grid_y = 2 + map_grid_y / 3;
+		map_grid_x = 2 + map_grid_x / 6;
 	}
-
-	if (HAS_BIT(room->exit_dirs, MAP_DIR_W))
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS))
 	{
-		SET_BIT(exit1, 1 << 2);
-		SET_BIT(exits, MAP_EXIT_W);
+		map_grid_y = 2 + (map_grid_y + 1) / 2;
+		map_grid_x = 2 + (map_grid_x + 3) / 5;
 	}
-
-	if (HAS_BIT(room->exit_dirs, MAP_DIR_E))
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
 	{
-		SET_BIT(exit2, 1 << 2);
-		SET_BIT(exits, MAP_EXIT_E);
+		map_grid_y = 2 + map_grid_y / 2;
+		map_grid_x = 2 + map_grid_x / 5;
 	}
-
-	if (HAS_BIT(room->exit_dirs, MAP_DIR_S))
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
 	{
-		SET_BIT(exit1, 1 << 4);
-		SET_BIT(exit2, 1 << 4);
-		SET_BIT(exits, MAP_EXIT_S);
+		map_grid_y = 2 + map_grid_y / 1;
+		map_grid_x = 2 + map_grid_x / 2;
+	}
+	else
+	{
+		map_grid_y = 2 + map_grid_y;
+		map_grid_x = 2 + map_grid_x;
 	}
 
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
+	if (map_grid_x > ses->map->max_grid_x || map_grid_y > ses->map->max_grid_y)
 	{
-		if (HAS_BIT(room->exit_dirs, MAP_DIR_NW))
-		{
-			SET_BIT(exit1, 1 << 1);
-		}
-		if (HAS_BIT(room->exit_dirs, MAP_DIR_NE))
-		{
-			SET_BIT(exit2, 1 << 1);
-		}
-		if (HAS_BIT(room->exit_dirs, MAP_DIR_SW))
+		if (map_grid_x > ses->map->max_grid_x)
 		{
-			SET_BIT(exit1, 1 << 3);
+			ses->map->max_grid_x = map_grid_x + 1;
 		}
-		if (HAS_BIT(room->exit_dirs, MAP_DIR_SE))
+		if (map_grid_y > ses->map->max_grid_y)
 		{
-			SET_BIT(exit2, 1 << 3);
+			ses->map->max_grid_y = map_grid_y + 1;
 		}
 
-		room1 = exit1 + LEGEND_MUDFONT_NWS;
-		room2 = exit2 + LEGEND_MUDFONT_NES;
+		ses->map->grid_rooms = (struct room_data **) realloc(ses->map->grid_rooms, ses->map->max_grid_x * ses->map->max_grid_y * sizeof(struct room_data *));
+	}
 
-		if (HAS_BIT(room->flags, ROOM_FLAG_VOID))
+	displaygrid_build(ses, ses->map->in_room, map_grid_x, map_grid_y, 0);
+
+	*arg1 = row = 0;
+
+	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
+	{
+		for (y = map_grid_y - 2 ; y >= 1 ; y--)
 		{
-			room1 += 64;
-			room2 += 64;
-		}
+			for (line = 1 ; line <= 3 ; line++)
+			{
+				str_cpy(&gtd->buf, ses->map->color[MAP_COLOR_BACK]);
 
-		if (HAS_BIT(room->flags, ROOM_FLAG_CURVED))
+				for (x = 1 ; x < map_grid_x - 1 ; x++)
+				{
+					str_cat(&gtd->buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], line, x, y));
+				}
+
+				str_clone(&gtd->out, gtd->buf);
+
+				substitute(ses, gtd->buf, gtd->out, SUB_COL|SUB_CMP|SUB_LIT);
+
+				if (logfile)
+				{
+					logit(ses, gtd->out, logfile, LOG_FLAG_LINEFEED);
+				}
+				else if (*arg3 == 'L')
+				{
+					cat_sprintf(arg1, "{%02d}{%s}", ++row, gtd->out);
+				}
+				else if (*arg3 == 'V' || *arg3 == 'D')
+				{
+					cat_sprintf(arg1, "%s\n", gtd->out);
+				}
+				else
+				{
+					tintin_puts2(ses, gtd->out);
+				}
+			}
+		}
+	}
+	else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) || HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
+	{
+		for (y = map_grid_y - 2 ; y >= 1 ; y--)
 		{
-			switch (room->exit_dirs)
+			for (line = 1 ; line <= 2 ; line++)
 			{
-				case MAP_DIR_N|MAP_DIR_E:
-				case MAP_DIR_N|MAP_DIR_SE:
-					room1 = LEGEND_MUDFONT_CURVED + 0;
-					break;
-				case MAP_DIR_S|MAP_DIR_E:
-				case MAP_DIR_S|MAP_DIR_NE:
-					room1 = LEGEND_MUDFONT_CURVED + 1;
-					break;
-				case MAP_DIR_S|MAP_DIR_W:
-				case MAP_DIR_S|MAP_DIR_NW:
-					room2 = LEGEND_MUDFONT_CURVED + 2;
-					break;
-				case MAP_DIR_N|MAP_DIR_W:
-				case MAP_DIR_N|MAP_DIR_SW:
-					room2 = LEGEND_MUDFONT_CURVED + 3;
-					break;
+				str_cpy(&gtd->buf, ses->map->color[MAP_COLOR_BACK]);
+
+				for (x = 1 ; x < map_grid_x - 1 ; x++)
+				{
+					str_cat(&gtd->buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], line, x, y));
+				}
+
+				str_clone(&gtd->out, gtd->buf);
+
+				substitute(ses, gtd->buf, gtd->out, SUB_COL|SUB_CMP|SUB_LIT);
+
+				if (logfile)
+				{
+					fprintf(logfile, "%s\n", gtd->out);
+				}
+				else if (*arg3 == 'L')
+				{
+					cat_sprintf(arg1, "{%02d}{%s\e[0m}", ++row, gtd->out);
+				}
+				else if (*arg3 == 'V' || *arg3 == 'D')
+				{
+					cat_sprintf(arg1, "%s\e[0m\n", gtd->out);
+				}
+				else
+				{
+					tintin_puts2(ses, gtd->out);
+				}
 			}
 		}
-
-		sprintf(buf, "%s%s%s", room_color, ses->map->legend[room1], ses->map->legend[room2]);
 	}
 	else
 	{
-		if (HAS_BIT(ses->map->flags, MAP_FLAG_SYMBOLGRAPHICS) && room->symbol[0] && room->symbol[0] != ' ')
-		{
-			sprintf(buf, "%s%-1s", room_color, room->symbol);
-		}
-		else
+		for (y = map_grid_y - 2 ; y >= 1 ; y--)
 		{
-			if (HAS_BIT(room->flags, ROOM_FLAG_VOID) && (exits == MAP_EXIT_N+MAP_EXIT_S || exits == MAP_EXIT_E+MAP_EXIT_W))
+			str_cpy(&gtd->buf, ses->map->color[MAP_COLOR_BACK]);
+
+			for (x = 1 ; x < map_grid_x - 1 ; x++)
 			{
-				sprintf(buf, "%s%s", room_color, exits == MAP_EXIT_N+MAP_EXIT_S ? ses->map->legend[offset+16+2] : ses->map->legend[offset+16+3]);
+				str_cat(&gtd->buf, draw_room(ses, ses->map->grid_rooms[x + map_grid_x * y], 0, x, y));
+			}
+
+			substitute(ses, gtd->buf, gtd->out, SUB_COL|SUB_CMP|SUB_LIT);
+
+			if (logfile)
+			{
+				fprintf(logfile, "%s\n", gtd->out);
+			}
+			else if (*arg3 == 'L')
+			{
+				cat_sprintf(arg1, "{%02d}{%s}", ++row, gtd->out);
+			}
+			else if (*arg3 == 'V' || *arg3 == 'D')
+			{
+				cat_sprintf(arg1, "%s\n", gtd->out);
 			}
 			else
 			{
-				if (HAS_BIT(room->flags, ROOM_FLAG_CURVED))
-				{
-					switch (room->exit_dirs)
-					{
-						case MAP_DIR_N|MAP_DIR_E:
-							exits = 16 + 4;
-							break;
-						case MAP_DIR_S|MAP_DIR_E:
-							exits = 16 + 5;
-							break;
-						case MAP_DIR_S|MAP_DIR_W:
-							exits = 16 + 6;
-							break;
-						case MAP_DIR_N|MAP_DIR_W:
-							exits = 16 + 7;
-							break;
-					}
-				}
-				sprintf(buf, "%s%s", room_color, ses->map->legend[offset + exits]);
+				tintin_puts2(ses, gtd->out);
 			}
 		}
 	}
-	pop_call();
-	return buf;
-}
-
-void search_keywords(struct session *ses, char *arg, char *out, char *var)
-{
-	char buf[MAP_SEARCH_MAX][BUFFER_SIZE], tmp[BUFFER_SIZE];
-	int type, max;
-
-	push_call("search_keywords(%p,%p,%p,%p)",ses,arg,out,var);
 
-	for (type = 0 ; type < MAP_SEARCH_MAX ; type++)
+	if (logfile)
 	{
-		buf[type][0] = 0;
+		fclose(logfile);
 	}
-
-	var[0] = 0;
-
-	type = 0;
-
-	while (*arg && type < MAP_SEARCH_MAX)
+	else if (*arg3 == 'D')
 	{
-		arg = sub_arg_in_braces(ses, arg, tmp, GET_ALL, SUB_VAR|SUB_FUN);
-
-		if (!strcasecmp(tmp, "roomid"))
-		{
-			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_ID], GET_ALL, SUB_VAR|SUB_FUN);
-		}
-		else if (!strcasecmp(tmp, "roomname"))
-		{
-			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_NAME], GET_ALL, SUB_VAR|SUB_FUN);
-		}
-		else if (!strcasecmp(tmp, "roomexits"))
-		{
-			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_EXITS], GET_ALL, SUB_VAR|SUB_FUN);
-		}
-		else if (!strcasecmp(tmp, "roomdesc"))
-		{
-			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_DESC], GET_ALL, SUB_VAR|SUB_FUN);
-		}
-		else if (!strcasecmp(tmp, "roomarea"))
-		{
-			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_AREA], GET_ALL, SUB_VAR|SUB_FUN);
-		}
-		else if (!strcasecmp(tmp, "roomnote"))
-		{
-			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_NOTE], GET_ALL, SUB_VAR|SUB_FUN);
-		}
-		else if (!strcasecmp(tmp, "roomterrain"))
-		{
-			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_TERRAIN], GET_ALL, SUB_VAR|SUB_FUN);
-		}
-		else if (!strcasecmp(tmp, "roomflag"))
-		{
-			arg = sub_arg_in_braces(ses, arg, buf[MAP_SEARCH_FLAG], GET_ALL, SUB_VAR|SUB_FUN);
-		}
-		else if (!strcasecmp(tmp, "variable"))
-		{
-			arg = sub_arg_in_braces(ses, arg, var, GET_ALL, SUB_VAR|SUB_FUN);
-		}
-		else
-		{
-			strcpy(buf[type++], tmp);
-		}
+		draw_map(ses, 1, 2, 3, 4, 5, 6, 7, "", arg4, arg2, arg1);
 	}
-
-	for (max = MAP_SEARCH_MAX - 1 ; max >= 0 ; max--)
+	else if (*arg3 == 'L')
 	{
-		if (*buf[max])
-		{
-			break;
-		}
+		set_nest_node_ses(ses, arg4, "%s", arg1);
 	}
-
-	out[0] = 0;
-
-	for (type = 0 ; type <= max ; type++)
+	else if (*arg3 == 'V')
 	{
-		cat_sprintf(out, "{%s}", buf[type]);
+		set_nest_node_ses(ses, arg4, "%s", arg1);
 	}
+
 	pop_call();
 	return;
 }
 
-void map_search_compile(struct session *ses, char *arg, char *var)
+DO_MAP(map_move)
 {
-	char tmp[BUFFER_SIZE], buf[BUFFER_SIZE], *ptb;
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
 
-	push_call("map_search_compile(%p,%p,%p)",ses,arg,var);
+//	tintin_printf2(ses, "debug: %s vs %s", arg, arg1);
 
-	search_keywords(ses, arg, tmp, var);
+	arg = substitute_speedwalk(ses, arg1, arg2);
 
-	arg = sub_arg_in_braces(ses, tmp, buf, GET_ALL, SUB_VAR|SUB_FUN); // name
+//	tintin_printf2(ses, "debug: %s vs %s", arg, arg2);
 
-	if (is_math(ses, buf))
-	{
-		ses->map->search->vnum = (int) get_number(ses, buf);
-	}
-	else
-	{
-		ses->map->search->vnum = 0;
-	}
+	ses->map->nofollow++;
 
-	if (ses->map->search->vnum)
+	while (*arg)
 	{
-		pop_call();
-		return;
-	}
+		arg = get_arg_in_braces(ses, arg, arg1, GET_ALL);
 
-	if (ses->map->search->name)
-	{
-		free(ses->map->search->name);
+		follow_map(ses, arg1);
+
+		if (*arg == COMMAND_SEPARATOR)
+		{
+			arg++;
+		}
 	}
 
-	if (*buf)
-	{
-		strcat(buf, "$");
+	ses->map->nofollow--;
+}
 
-		ses->map->search->name = tintin_regexp_compile(ses, NULL, buf, PCRE_ANCHORED);
-	}
-	else
-	{
-		ses->map->search->name = NULL;
-	}
+DO_MAP(map_name)
+{
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
 
-	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // exits
+	RESTRING(ses->map->room_list[ses->map->in_room]->name, arg1);
+}
 
-	ses->map->search->exit_dirs = 0;
-	ses->map->search->exit_size = 0;
+DO_MAP(map_offset)
+{
+	char arg3[BUFFER_SIZE], arg4[BUFFER_SIZE];
 
-	if (ses->map->search->exit_list)
+	if (arg)
 	{
-		free(ses->map->search->exit_list);
+		arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+		arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+		arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+		arg = sub_arg_in_braces(ses, arg, arg4, GET_ONE, SUB_VAR|SUB_FUN);
+
+		ses->map->sav_top_row = get_number(ses, arg1);
+		ses->map->sav_top_col = get_number(ses, arg2);
+		ses->map->sav_bot_row = get_number(ses, arg3);
+		ses->map->sav_bot_col = get_number(ses, arg4);
 	}
 
-	if (*buf)
+	if (ses->map->sav_top_row == 0)
 	{
-		struct listnode *node;
-		char exit[BUFFER_SIZE];
-		ptb = buf;
-
-		tmp[0] = 0;
-
-		if (is_math(ses, buf))
-		{
-			ses->map->search->exit_dirs = get_number(ses, buf);
-
-			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_N))  ses->map->search->exit_size++;
-			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_E))  ses->map->search->exit_size++;
-			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_S))  ses->map->search->exit_size++;
-			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_W))  ses->map->search->exit_size++;
-			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_U))  ses->map->search->exit_size++;
-			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_D))  ses->map->search->exit_size++;
-			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_NE)) ses->map->search->exit_size++;
-			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_NW)) ses->map->search->exit_size++;
-			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_SE)) ses->map->search->exit_size++;
-			if (HAS_BIT(ses->map->search->exit_dirs, MAP_DIR_SW)) ses->map->search->exit_size++;
-		}
-		else
-		{
-			while (*ptb)
-			{
-				ptb = get_arg_in_braces(ses, ptb, exit, GET_ONE);
-
-				node = search_node_list(ses->list[LIST_PATHDIR], exit);
-
-				ses->map->search->exit_size++;
-
-				if (node)
-				{
-					SET_BIT(ses->map->search->exit_dirs, 1LL << atoi(node->arg3));
-				}
-				else
-				{
-					SET_BIT(ses->map->search->exit_dirs, 1); // flag indicates no exits
-
-					cat_sprintf(tmp, "{%s}", exit);
-				}
-
-				if (*ptb == COMMAND_SEPARATOR)
-				{
-					ptb++;
-				}
-			}
-		}
-		ses->map->search->exit_list = strdup(tmp);
+		ses->map->top_row = 1;
+	}
+	else if (ses->map->sav_top_row < 0)
+	{
+		ses->map->top_row = 1 + gtd->screen->rows + ses->map->sav_top_row;
 	}
 	else
 	{
-		ses->map->search->exit_list = strdup("");
+		ses->map->top_row = ses->map->sav_top_row;
 	}
 
-	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // desc
-
-	if (ses->map->search->desc)
+	if (ses->map->sav_top_col == 0)
 	{
-		free(ses->map->search->desc);
+		ses->map->top_col = 1;
 	}
-
-	if (*buf)
+	else if (ses->map->sav_top_col < 0)
 	{
-		strcat(buf, "$");
-
-		ses->map->search->desc = tintin_regexp_compile(ses, NULL, buf, PCRE_ANCHORED);
+		ses->map->top_col = 1 + gtd->screen->cols + ses->map->sav_top_col;
 	}
 	else
 	{
-		ses->map->search->desc = NULL;
+		ses->map->top_col = ses->map->sav_top_col;
 	}
 
-	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // area
-
-	if (ses->map->search->area)
+	if (ses->map->sav_bot_row == 0)
 	{
-		free(ses->map->search->area);
+		ses->map->bot_row = ses->split->top_row - 1;
 	}
-
-	if (*buf)
+	else if (ses->map->sav_bot_row < 0)
 	{
-		strcat(buf, "$");
-
-		ses->map->search->area = tintin_regexp_compile(ses, NULL, buf, PCRE_ANCHORED);
+		ses->map->bot_row = 1 + gtd->screen->rows + ses->map->sav_bot_row;
 	}
 	else
 	{
-		ses->map->search->area = NULL;
+		ses->map->bot_row = ses->map->sav_bot_row;
 	}
 
-	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // note
-
-	if (ses->map->search->note)
+	if (ses->map->sav_bot_col == 0)
 	{
-		free(ses->map->search->note);
+		ses->map->bot_col = gtd->screen->cols;
 	}
-
-	if (*buf)
+	else if (ses->map->sav_bot_col < 0)
 	{
-		strcat(buf, "$");
-
-		ses->map->search->note = tintin_regexp_compile(ses, NULL, buf, PCRE_ANCHORED);
+		ses->map->bot_col = 1 + gtd->screen->cols + ses->map->sav_bot_col;
 	}
 	else
 	{
-		ses->map->search->note = NULL;
+		ses->map->bot_col = ses->map->sav_bot_col;
 	}
 
-	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // terrain
+	ses->map->rows = ses->map->bot_row - ses->map->top_row;
+	ses->map->cols = ses->map->bot_col - ses->map->top_col;
 
-	if (ses->map->search->terrain)
+	if (arg)
 	{
-		free(ses->map->search->terrain);
+		show_message(ses, LIST_COMMAND, "#MAP OFFSET: SQUARE {%d, %d, %d, %d} ROWS {%d} COLS {%d}", ses->map->top_row, ses->map->top_col, ses->map->bot_row, ses->map->bot_col, ses->map->rows, ses->map->cols);
 	}
+}
 
-	if (*buf)
-	{
-		strcat(buf, "$");
 
-		ses->map->search->terrain = tintin_regexp_compile(ses, NULL, buf, PCRE_ANCHORED);
-	}
-	else
+DO_MAP(map_read)
+{
+	FILE *myfile;
+	struct room_data *room;
+	struct exit_data *exit;
+	char buffer[BUFFER_SIZE], file[BUFFER_SIZE], *cptr;
+	int line = 1, vnum = 0;
+
+	arg = sub_arg_in_braces(ses, arg, file, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if ((myfile = fopen(file, "r")) == NULL)
 	{
-		ses->map->search->terrain = NULL;
+		show_error(ses, LIST_COMMAND, "#MAP: Map file {%s} not found.", file);
+
+		return;
 	}
 
-	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // flag
+	gtd->level->quiet++;
 
-	if (*buf)
+	if (fgets(buffer, BUFFER_SIZE - 1, myfile))
 	{
-		char flags[BUFFER_SIZE];
+		cptr = strchr(buffer, '\r'); /* For map files editor on Windows systems. */
 
-		ses->map->search->flag = get_number(ses, buf);
+		if (cptr)
+		{
+			*cptr = 0;
+		}
 
-		ptb = buf;
+		cptr = strchr(buffer, '\n');
 
-		while (*buf)
+		if (cptr)
 		{
-			ptb = sub_arg_in_braces(ses, ptb, flags, GET_ONE, SUB_NONE);
+			*cptr = 0;
+		}
 
-			if (is_abbrev(buf, "avoid"))
-			{
-				SET_BIT(ses->map->search->flag, ROOM_FLAG_AVOID);
-			}
-			else if (is_abbrev(buf, "curved"))
-			{
-				SET_BIT(ses->map->search->flag, ROOM_FLAG_CURVED);
-			}
-			else if (is_abbrev(buf, "hide"))
-			{
-				SET_BIT(ses->map->search->flag, ROOM_FLAG_HIDE);
-			}
-			else if (is_abbrev(buf, "invis"))
-			{
-				SET_BIT(ses->map->search->flag, ROOM_FLAG_INVIS);
-			}
-			else if (is_abbrev(buf, "leave"))
-			{
-				SET_BIT(ses->map->search->flag, ROOM_FLAG_LEAVE);
-			}
-			else if (is_abbrev(buf, "void"))
-			{
-				SET_BIT(ses->map->search->flag, ROOM_FLAG_VOID);
-			}
-			else if (is_abbrev(buf, "static"))
+		if (buffer[0] == 'C' && buffer[1] == ' ')
+		{
+			if (ses->map == NULL || !HAS_BIT(ses->map->flags, MAP_FLAG_SYNC))
 			{
-				SET_BIT(ses->map->search->flag, ROOM_FLAG_STATIC);
+				create_map(ses, buffer + 2);
 			}
+		}
+		else
+		{
+			gtd->level->quiet--;
 
-			if (*ptb == COMMAND_SEPARATOR)
-			{
-				ptb++;
-			}
+			show_error(ses, LIST_COMMAND, "#MAP READ {%s}: INVALID START OF FILE. ABORTING READ..", file);
+
+			fclose(myfile);
+
+			return;
 		}
 	}
 	else
 	{
-		ses->map->search->flag = 0;
-	}
+		gtd->level->quiet--;
 
-	arg = sub_arg_in_braces(ses, arg, buf, GET_ALL, SUB_VAR|SUB_FUN); // id
+		show_error(ses, LIST_COMMAND, "#MAP: INVALID READ ON LINE %d. ABORTING READ..", line);
 
-	if (ses->map->search->id)
-	{
-		free(ses->map->search->id);
+		fclose(myfile);
+		
+		return;
 	}
 
-	if (*buf)
-	{
-		ses->map->search->id = strdup(buf);
-	}
-	else
+	while (fgets(buffer, BUFFER_SIZE - 1, myfile))
 	{
-		ses->map->search->id = NULL;
-	}
+		line++;
 
-	pop_call();
-	return;
-}
+		cptr = strchr(buffer, '\r'); /* For map files editor on Windows systems. */
 
-int match_room(struct session *ses, int vnum, struct search_data *search)
-{
-	struct room_data *room = ses->map->room_list[vnum];
+		if (cptr)
+		{
+			*cptr = 0;
+		}
 
-	if (room == NULL)
-	{
-		return 0;
-	}
+		cptr = strchr(buffer, '\n');
+
+		if (cptr)
+		{
+			*cptr = 0;
+		}
+
+		switch (buffer[0])
+		{
+			case 'C':
+				switch (buffer[1])
+				{
+					case ' ':
+						gtd->level->quiet--;
+
+						show_error(ses, LIST_COMMAND, "#MAP: INVALID COMMAND {%d} {%s} ON LINE %d. ABORTING READ..", buffer[0], buffer, line);
+
+						fclose(myfile);
+
+						delete_map(ses);
+
+						return;
+
+					case 'A':
+					case 'B':
+					case 'E':
+					case 'H':
+					case 'I':
+					case 'P':
+					case 'R':
+					case 'S':
+					case 'U':
+						if (!HAS_BIT(ses->map->flags, MAP_FLAG_SYNC))
+						{
+							map_color(ses, buffer + 1, arg1, arg2);
+						}
+						break;
+
+					default:
+						show_error(ses, LIST_COMMAND, "#MAP READ: INVALID COMMAND {%d} {%s} ON LINE %d. ABORTING READ..", buffer[0], buffer, line);
+						break;
+				}
+				break;
+
+			case 'E':
+				create_exit(ses, vnum, "%s", buffer + 2);
+				break;
+
+			case 'F':
+				if (!HAS_BIT(ses->map->flags, MAP_FLAG_SYNC))
+				{
+					ses->map->flags = atoi(buffer + 2);
+				}
+				break;
+
+			case 'G':
+				if (ses->map->global_vnum == 0)
+				{
+					ses->map->global_vnum = ses->map->global_exit->vnum = atoi(buffer + 2);
+				}
+				break;
+
+			case 'I':
+				if (ses->map->last_room == 0)
+				{
+					ses->map->last_room = atoi(buffer + 2);
+				}
+				break;
+
+			case 'L':
+				switch (buffer[1])
+				{
+					case ' ':
+						if (!HAS_BIT(ses->map->flags, MAP_FLAG_SYNC))
+						{
+							map_legend(ses, buffer + 2, arg1, arg2);
+						}
+						break;
+
+					case 'M':
+						map_landmark(ses, buffer + 3, arg1, arg2);
+						break;
+				}
+				break;
+
+			case 'R':
+				room = create_room(ses, "%s", buffer + 2);
+				vnum = room->vnum;
+				break;
+
+			case 'T':
+				if (!HAS_BIT(ses->map->flags, MAP_FLAG_SYNC))
+				{
+					map_terrain(ses, buffer + 2, arg1, arg2);
+				}
+				break;
+
+			case 'V':
+				if (ses->map->version == 0)
+				{
+					ses->map->version = atoi(buffer + 2);
+				}
+				break;
+
+			case '#':
+				buffer[0] = gtd->tintin_char;
+				ses = script_driver(ses, LIST_COMMAND, buffer);
+				break;
 
-	if (search->vnum)
-	{
-		return room->vnum == search->vnum;
-	}
+			case  0:
+			case 13:
+				break;
 
-	if (search->id)
-	{
-		return !strcmp(room->id, search->id);
-	}
+			default:
+				gtd->level->quiet--;
 
-	if (search->name)
-	{
-		if (!regexp_compare(search->name, room->name, "", 0, 0))
-		{
-			return 0;
+				show_error(ses, LIST_COMMAND, "#MAP: INVALID COMMAND {%d} {%s} ON LINE %d.", buffer[0], buffer, line);
+
+				gtd->level->quiet++;
 		}
 	}
 
-	if (search->exit_dirs)
-	{
-		char *arg, exit[BUFFER_SIZE];
+	gtd->level->quiet--;
 
-		if (search->exit_dirs != room->exit_dirs)
-		{
-			return 0;
-		}
-		if (search->exit_size != room->exit_size)
+	fclose(myfile);
+
+	for (vnum = 0 ; vnum < ses->map->size ; vnum++)
+	{
+		if (ses->map->room_list[vnum] == NULL)
 		{
-			return 0;
+			continue;
 		}
 
-		arg = search->exit_list;
-
-		while (*arg)
+		for (exit = ses->map->room_list[vnum]->f_exit ; exit ; exit = exit->next)
 		{
-			arg = get_arg_in_braces(ses, arg, exit, GET_ONE);
-
-			if (!find_exit(ses, vnum, exit))
+			if (exit->vnum < 0 || exit->vnum >= ses->map->size || ses->map->room_list[exit->vnum] == NULL)
 			{
-				return 0;
-			}
+				show_error(ses, LIST_COMMAND, "#MAP READ: Room %d - invalid exit '%s' to room %d.", vnum, exit->name, exit->vnum);
 
-			if (*arg == COMMAND_SEPARATOR)
-			{
-				arg++;
+				delete_exit(ses, vnum, exit);
+
+				if (ses->map->room_list[vnum]->f_exit)
+				{
+					exit = ses->map->room_list[vnum]->f_exit;
+				}
+				else
+				{
+					break;
+				}
 			}
 		}
 	}
 
-	if (search->desc)
-	{
-		if (!regexp_compare(search->desc, room->desc, "", 0, 0))
-		{
-			return 0;
-		}
-	}
+	show_message(ses, LIST_COMMAND, "#MAP READ: Map file {%s} loaded.", file);
 
-	if (search->area)
+
+}
+
+DO_MAP(map_resize)
+{
+	int size, vnum, room;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	size = atoi(arg1);
+
+	if (size <= ses->map->size)
 	{
-		if (!regexp_compare(search->area, room->area, "", 0, 0))
+		for (room = vnum = 1 ; vnum < ses->map->size ; vnum++)
 		{
-			return 0;
+			if (ses->map->room_list[vnum])
+			{
+				room = vnum;
+			}
 		}
-	}
 
-	if (search->note)
-	{
-		if (!regexp_compare(search->note, room->note, "", 0, 0))
+		if (room >= size)
 		{
-			return 0;
+			show_error(ses, LIST_COMMAND, "#MAP RESIZE: YOU MUST DELETE ALL ROOMS WITH VNUMS ABOVE (%d) FIRST.", size);
+			return;
 		}
 	}
 
-	if (search->terrain)
+	ses->map->room_list = (struct room_data **) realloc(ses->map->room_list, size * sizeof(struct room_data *));
+
+	if (ses->map->size < size)
 	{
-		if (!regexp_compare(search->terrain, room->terrain, "", 0, 0))
+		while (ses->map->size < size)
 		{
-			return 0;
+			ses->map->room_list[ses->map->size++] = NULL;
 		}
 	}
-
-	if (search->flag)
+	else
 	{
-		if ((room->flags & search->flag) != search->flag)
-		{
-			return 0;
-		}
+		ses->map->size = size;
 	}
-	return 1;
+
+	show_message(ses, LIST_COMMAND, "#MAP RESIZE: MAP RESIZED TO %d ROOMS.", ses->map->size);
 }
 
-int find_room(struct session *ses, char *arg)
+DO_MAP(map_return)
 {
-	char var[BUFFER_SIZE];
-	int room;
-
-	push_call("find_room(%p,%s)",ses,arg);
-
-	map_search_compile(ses, arg, var);
-
-	if (ses->map->search->vnum > 0 && ses->map->search->vnum < ses->map->size)
+	if (ses->map == NULL || ses->map->room_list[ses->map->last_room] == NULL)
 	{
-		if (ses->map->room_list[ses->map->search->vnum])
-		{
-			pop_call();
-			return ses->map->search->vnum;
-		}
-		pop_call();
-		return 0;
+		show_error(ses, LIST_COMMAND, "#MAP RETURN: NO KNOWN LAST ROOM.");
+
+		return;
 	}
 
 	if (ses->map->in_room)
 	{
-		room = searchgrid_find(ses, ses->map->in_room, ses->map->search);
-
-		if (room)
-		{
-			pop_call();
-			return room;
-		}
+		show_error(ses, LIST_COMMAND, "#MAP RETURN: ALREADY IN THE MAP.");
 	}
-
-	for (room = 0 ; room < ses->map->size ; room++)
+	else
 	{
-		if (ses->map->room_list[room] == NULL)
-		{
-			continue;
-		}
+		goto_room(ses, ses->map->last_room);
 
-		if (!match_room(ses, room, ses->map->search))
-		{
-			continue;
-		}
-		pop_call();
-		return room;
+		show_message(ses, LIST_COMMAND, "#MAP RETURN: RETURNED TO ROOM %d {%s}.", ses->map->in_room, ses->map->room_list[ses->map->in_room]->name);
 	}
-	pop_call();
-	return 0;
 }
 
-void goto_room(struct session *ses, int room)
+DO_MAP(map_roomflag)
 {
-	int last_room = ses->map->in_room;
+	char buf[BUFFER_SIZE], *str, arg3[BUFFER_SIZE];
+	int flag = 0;
 
-	push_call("goto_room(%p,%d)",ses,room);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
 
-	if (ses->map->in_room)
+	if (*arg1 == 0)
 	{
-		check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "MAP EXIT ROOM", ntos(last_room), ntos(room));
-		check_all_events(ses, SUB_ARG|SUB_SEC, 1, 2, "MAP EXIT ROOM %d", last_room, ntos(last_room), ntos(room));
+		tintin_printf2(ses, "#MAP: Avoid flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_AVOID) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: Block flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_BLOCK) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: Hide flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_HIDE) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: Invis flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_INVIS) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: Leave flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_LEAVE) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: Void flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_VOID) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: Static flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_STATIC) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: Curved flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_CURVED) ? "ON" : "OFF");
+		tintin_printf2(ses, "#MAP: NoGlobal flag is set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_NOGLOBAL) ? "ON" : "OFF");
+		return;
 	}
 
-	ses->map->in_room = room;
-
-	DEL_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_PATH);
+	str = arg1;
 
-	if (last_room == 0)
+	while (*str)
 	{
-		check_all_events(ses, SUB_ARG|SUB_SEC, 0, 1, "MAP ENTER MAP", ntos(room));
-	}
-
-	check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "MAP ENTER ROOM", ntos(room), ntos(last_room));
-	check_all_events(ses, SUB_ARG|SUB_SEC, 1, 2, "MAP ENTER ROOM %d", room, ntos(room), ntos(last_room));
+		str = get_arg_in_braces(ses, str, buf, GET_ONE);
 
-	pop_call();
-	return;
-}
+		if (is_abbrev(buf, "avoid"))
+		{
+			SET_BIT(flag, ROOM_FLAG_AVOID);
+		}
+		else if (is_abbrev(buf, "block"))
+		{
+			SET_BIT(flag, ROOM_FLAG_BLOCK);
+		}
+		else if (is_abbrev(buf, "curved"))
+		{
+			SET_BIT(flag, ROOM_FLAG_CURVED);
+		}
+		else if (is_abbrev(buf, "hide"))
+		{
+			SET_BIT(flag, ROOM_FLAG_HIDE);
+		}
+		else if (is_abbrev(buf, "invisible"))
+		{
+			SET_BIT(flag, ROOM_FLAG_INVIS);
+		}
+		else if (is_abbrev(buf, "leave"))
+		{
+			SET_BIT(flag, ROOM_FLAG_LEAVE);
+		}
+		else if (is_abbrev(buf, "noglobal"))
+		{
+			SET_BIT(flag, ROOM_FLAG_NOGLOBAL);
+		}
+		else if (is_abbrev(buf, "static"))
+		{
+			SET_BIT(flag, ROOM_FLAG_STATIC);
+		}
+		else if (is_abbrev(buf, "void"))
+		{
+			SET_BIT(flag, ROOM_FLAG_VOID);
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#MAP: Invalid room flag {%s}.", buf);
 
-int find_new_room(struct session *ses)
-{
-	int room;
+			return;
+		}
 
-	for (room = 1 ; room < ses->map->size ; room++)
-	{
-		if (ses->map->room_list[room] == NULL)
+		if (*str == COMMAND_SEPARATOR)
 		{
-			break;
+			str++;
 		}
 	}
 
-	if (room == ses->map->size)
+	if (*arg2 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP CREATE ROOM: Maximum amount of rooms of %d reached. Use #map resize.", ses->map->size);
-
-		return 0;
+		TOG_BIT(ses->map->room_list[ses->map->in_room]->flags, flag);	
 	}
-	return room;
-}
-
-int dir_flags(struct session *ses, int room, int dir)
-{
-	struct exit_data *exit;
-
-	for (exit = ses->map->room_list[room]->f_exit ; exit ; exit = exit->next)
+	else if (is_abbrev(arg2, "ON"))
 	{
-		if (exit->dir == dir)
+		SET_BIT(ses->map->room_list[ses->map->in_room]->flags, flag);
+	}
+	else if (is_abbrev(arg2, "OFF"))
+	{
+		DEL_BIT(ses->map->room_list[ses->map->in_room]->flags, flag);
+	}
+	else if (is_abbrev(arg2, "GET"))
+	{
+		if (*arg3 == 0)
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX #MAP ROOMFLAG {%s} {GET} {<VARIABLE>}.", buf);
+		}
+		else
 		{
-			return exit->flags; /* | HAS_BIT(ses->map->room_list[exit->vnum]->flags, EXIT_FLAG_ALL);*/
+			set_nest_node_ses(ses, arg3, "%d", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, flag));
 		}
+		return;
+	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX #MAP ROOMFLAG {%s} {[GET|ON|OFF]}.", buf);
 	}
-	return 0;
-}
 
-struct exit_data *find_exit(struct session *ses, int room, char *arg)
-{
-	struct exit_data *exit;
 
-	for (exit = ses->map->room_list[room]->f_exit ; exit ; exit = exit->next)
+	if (HAS_BIT(flag, ROOM_FLAG_AVOID))
 	{
-//		if (!strcmp(exit->name, arg) || exit->vnum == atoi(arg))
-		if (!strcmp(exit->name, arg))
-		{
-			return exit;
-		}
+		show_message(ses, LIST_COMMAND, "#MAP: Avoid flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_AVOID) ? "ON" : "OFF");
 	}
-	return NULL;
-}
-
-int check_global(struct session *ses, int room)
-{
-	if (HAS_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_NOGLOBAL))
+	if (HAS_BIT(flag, ROOM_FLAG_BLOCK))
 	{
-		return FALSE;
+		show_message(ses, LIST_COMMAND, "#MAP: Block flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_AVOID) ? "ON" : "OFF");
 	}
-	
-	if (ses->map->room_list[ses->map->global_vnum] == NULL)
+	if (HAS_BIT(flag, ROOM_FLAG_CURVED))
 	{
-		return FALSE;
+		show_message(ses, LIST_COMMAND, "#MAP: Curved flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_CURVED) ? "ON" : "OFF");
 	}
-
-	if (room == ses->map->global_vnum)
+	if (HAS_BIT(flag, ROOM_FLAG_HIDE))
 	{
-		return FALSE;
+		show_message(ses, LIST_COMMAND, "#MAP: Hide flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_HIDE) ? "ON" : "OFF");
 	}
-	return TRUE;
-}
-
-int tunnel_void(struct session *ses, int from, int room, int dir)
-{
-	if (!HAS_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_VOID))
+	if (HAS_BIT(flag, ROOM_FLAG_INVIS))
 	{
-		return room;
+		show_message(ses, LIST_COMMAND, "#MAP: Invis flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_INVIS) ? "ON" : "OFF");
 	}
-
-	if (get_room_exits(ses, room) != 2)
+	if (HAS_BIT(flag, ROOM_FLAG_LEAVE))
 	{
-		struct exit_data *exit;
-
-		for (exit = ses->map->room_list[room]->f_exit ; exit ; exit = exit->next)
-		{
-			if (exit->dir == dir)
-			{
-				return tunnel_void(ses, room, exit->vnum, exit->dir);
-			}
-		}
-		return room;
+		show_message(ses, LIST_COMMAND, "#MAP: Leave flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_LEAVE) ? "ON" : "OFF");
 	}
-
-	if (ses->map->room_list[room]->f_exit->vnum != from)
+	if (HAS_BIT(flag, ROOM_FLAG_NOGLOBAL))
 	{
-		return tunnel_void(ses, room, ses->map->room_list[room]->f_exit->vnum, ses->map->room_list[room]->f_exit->dir);
+		show_message(ses, LIST_COMMAND, "#MAP: NoGlobal flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_NOGLOBAL) ? "ON" : "OFF");
 	}
-	else
+	if (HAS_BIT(flag, ROOM_FLAG_VOID))
 	{
-		return tunnel_void(ses, room, ses->map->room_list[room]->l_exit->vnum, ses->map->room_list[room]->l_exit->dir);
+		show_message(ses, LIST_COMMAND, "#MAP: Void flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_VOID) ? "ON" : "OFF");
+	}
+	if (HAS_BIT(flag, ROOM_FLAG_STATIC))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP: Static flag set to %s.", HAS_BIT(ses->map->room_list[ses->map->in_room]->flags, ROOM_FLAG_STATIC) ? "ON" : "OFF");
 	}
-}
 
-// shortest_path() utilities
+}
 
-int searchgrid_find(struct session *ses, int from, struct search_data *search)
+DO_MAP(map_set)
 {
-	int vnum, head, tail, index;
-	float length;
-	struct grid_node *node, *temp, list[MAP_BF_SIZE];
-	struct exit_data *exit;
-	struct room_data *room;
-
-	search->stamp++;
-
-	head = 0;
-	tail = 1;
-
-	node = &list[head];
-
-	node->vnum   = from;
-	node->length = ses->map->room_list[from]->weight;
-
-	// for map_list
+	struct room_data *room = ses->map->room_list[ses->map->in_room];
+	char arg3[BUFFER_SIZE];
 
-	node->w      = 0;
-	node->x      = 0;
-	node->y      = 0;
-	node->z      = 0;
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
 
-	while (head != tail)
+	if (*arg3)
 	{
-		node = &list[head];
-
-		room = ses->map->room_list[node->vnum];
-
-		length = node->length;
-
-		head = (head + 1) % MAP_BF_SIZE;
-
-		if (search->stamp != room->search_stamp)
-		{
-			room->search_stamp = search->stamp;
-
-			// first come first serve like with spatialgrid_find
-
-			room->w = node->w;
-			room->x = node->x;
-			room->y = node->y;
-			room->z = node->z;
-
-			DEL_BIT(room->flags, ROOM_FLAG_PATH);
-		}
-		else if (length >= room->length)
-		{
-			if (room->vnum != ses->map->global_vnum && room->w && node->w == 0)
-			{
-				room->w = node->w;
-				room->x = node->x;
-				room->y = node->y;
-				room->z = node->z;
-			}
-			continue;
-		}
-
-		room->length = length;
-
-		if (search->vnum)
-		{
-			if (room->vnum == search->vnum)
-			{
-				return room->vnum;
-			}
-		}
-		else
-		{
-			if (match_room(ses, room->vnum, search))
-			{
-				return room->vnum;
-			}
-		}
-
-		if (check_global(ses, room->vnum))
+		if (atoi(arg3) > 0 && atoi(arg3) < ses->map->size)
 		{
-			exit = ses->map->global_exit;
+			room = ses->map->room_list[atoi(arg3)];
 		}
 		else
 		{
-			exit = room->f_exit;
-		}
-
-		for ( ; exit ; exit = exit->next)
-		{
-			vnum = tunnel_void(ses, room->vnum, exit->vnum, exit->dir);
-
-			if (HAS_BIT(exit->flags, EXIT_FLAG_AVOID) || HAS_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_AVOID))
-			{
-				goto next_exit;
-			}
-
-			length = room->length + exit->weight + ses->map->room_list[vnum]->weight;
-
-			if (search->stamp == ses->map->room_list[vnum]->search_stamp)
-			{
-				if (length >= ses->map->room_list[vnum]->length)
-				{
-					goto next_exit;
-				}
-			}
-
-			temp = &list[tail];
-
-			temp->vnum   = vnum;
-			temp->length = length;
-			temp->w      = room->vnum == ses->map->global_vnum ? 1 : room->w;
-			temp->x      = room->x + (HAS_BIT(exit->dir, MAP_EXIT_E) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_W) ? -1 : 0);
-			temp->y      = room->y + (HAS_BIT(exit->dir, MAP_EXIT_N) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_S) ? -1 : 0);
-			temp->z      = room->z + (HAS_BIT(exit->dir, MAP_EXIT_U) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_D) ? -1 : 0);
-
-			/*
-				list must remain ordered by length
-			*/
-
-			index = tail;
-
-			while (index != head)
-			{
-				temp = &list[index];
-
-				node = &list[index ? index - 1 : MAP_BF_SIZE - 1];
-
-				if (temp->length >= node->length)
-				{
-					break;
-				}
-
-				vnum         = temp->vnum;
-				length       = temp->length;
-
-				temp->vnum   = node->vnum;
-				temp->length = node->length;
-
-				node->vnum   = vnum;
-				node->length = length;
-
-				index = index ? index - 1 : MAP_BF_SIZE - 1;
-			}
-
-			tail = (tail + 1) % MAP_BF_SIZE;
-
-			if (tail == head)
-			{
-				show_error(ses, LIST_COMMAND, "#SHORTEST PATH: MAP TOO BIG FOR BF STACK OF %d", MAP_BF_SIZE);
-				break;
-			}
-
-			next_exit:
-
-			if (exit == ses->map->global_exit)
-			{
-				exit->next = room->f_exit;
-			}
+			room = NULL;
 		}
 	}
-	return 0;
-}
-
-int searchgrid_walk(struct session *ses, int offset, int from, int dest)
-{
-	int vnum, trim, head, tail, index;
-	float length;
-	struct grid_node *node, *temp, list[MAP_BF_SIZE];
-	struct exit_data *exit;
-	struct room_data *room;
-
-	head = 0;
-	tail = 1;
 
-	list[head].vnum   = from;
-	list[head].length = ses->map->room_list[from]->weight;
-
-	while (head != tail)
+	if (room == NULL)
+	{
+		show_message(ses, LIST_COMMAND, "#MAP SET: invalid room vnum: %s", arg3);
+	}
+	else if (*arg1 == 0)
+	{
+		tintin_printf2(ses, "   roomarea: %s", room->area);
+		tintin_printf2(ses, "  roomcolor: %s", room->color);
+		tintin_printf2(ses, "   roomdata: %s", room->data);
+		tintin_printf2(ses, "   roomdesc: %s", room->desc);
+		tintin_printf2(ses, "  roomflags: %d", room->flags);
+		tintin_printf2(ses, "     roomid: %s", room->id);
+		tintin_printf2(ses, "   roomname: %s", room->name);
+		tintin_printf2(ses, "   roomnote: %s", room->note);
+		tintin_printf2(ses, " roomsymbol: %s", room->symbol);
+		tintin_printf2(ses, "roomterrain: %s", room->terrain);
+		tintin_printf2(ses, " roomweight: %.3f", room->weight);
+	}
+	else
 	{
-		node = &list[head];
-
-		room = ses->map->room_list[node->vnum];
-
-		length = node->length;
-
-		head = (head + 1) % MAP_BF_SIZE;
-
-
-		if (length >= room->length)
+		if (is_abbrev(arg1, "roomarea"))
 		{
-			continue;
+			RESTRING(room->area, arg2);
+			show_message(ses, LIST_COMMAND, "#MAP SET: roomarea set to: %s", room->area);
 		}
-
-		room->length = length;
-
-		if (room->vnum == dest)
+		else if (is_abbrev(arg1, "roomcolor"))
 		{
-			return room->vnum;
+			RESTRING(room->color, arg2);
+			show_message(ses, LIST_COMMAND, "#MAP SET: roomcolor set to: %s", arg2);
 		}
-
-		trim = 1;
-
-		if (check_global(ses, room->vnum))
+		else if (is_abbrev(arg1, "roomdata"))
 		{
-			exit = ses->map->global_exit;
+			RESTRING(room->data, arg2);
+			show_message(ses, LIST_COMMAND, "#MAP SET: roomdata set to: %s", arg2);
 		}
-		else
+		else if (is_abbrev(arg1, "roomdesc"))
 		{
-			exit = room->f_exit;
+			RESTRING(room->desc, arg2);
+			show_message(ses, LIST_COMMAND, "#MAP SET: roomdesc set to: %s", arg2);
 		}
-
-		for ( ; exit ; exit = exit->next)
+		else if (is_abbrev(arg1, "roomflags"))
 		{
-			vnum = tunnel_void(ses, room->vnum, exit->vnum, exit->dir);
-
-			if (HAS_BIT(exit->flags, EXIT_FLAG_AVOID) || HAS_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_AVOID))
-			{
-				goto next_exit;
-			}
-
-			length = room->length + exit->weight + ses->map->room_list[vnum]->weight;
-
-			if (ses->map->search->stamp != ses->map->room_list[vnum]->search_stamp)
-			{
-				goto next_exit;
-			}
-
-			if (length >= ses->map->room_list[vnum]->length || length >= ses->map->room_list[dest]->length)
-			{
-				goto next_exit;
-			}
-
-			temp = &list[tail];
+			room->flags = (int) get_number(ses, arg2);
 
-			temp->vnum   = vnum;
-			temp->length = length;
+			show_message(ses, LIST_COMMAND, "#MAP SET: roomflags set to: %d", room->flags);
+		}
+		else if (is_abbrev(arg1, "roomid"))
+		{
+			RESTRING(room->id, arg2);
 
-			/*
-				list must remain ordered by length
-			*/
+			show_message(ses, LIST_COMMAND, "#MAP SET: roomid set to: %s", room->id);
+		}
+		else if (is_abbrev(arg1, "roomname"))
+		{
+			RESTRING(room->name, arg2);
 
-			index = tail;
+			show_message(ses, LIST_COMMAND, "#MAP SET: roomname set to: %s", room->name);
+		}
+		else if (is_abbrev(arg1, "roomnote"))
+		{
+			RESTRING(room->note, arg2);
+			show_message(ses, LIST_COMMAND, "#MAP SET: roomnote set to: %s", arg2);
+		}
+		else if (is_abbrev(arg1, "roomsymbol"))
+		{
+			RESTRING(room->symbol, arg2);
 
-			while (index != head)
+			show_message(ses, LIST_COMMAND, "#MAP SET: roomsymbol set to: %s", room->symbol);
+		}
+		else if (is_abbrev(arg1, "roomterrain"))
+		{
+			RESTRING(room->terrain, arg2);
+			room->terrain_index = bsearch_alpha_list(ses->list[LIST_TERRAIN], room->terrain, 0);
+			show_message(ses, LIST_COMMAND, "#MAP SET: roomterrain set to: %s (%d)", arg2, room->terrain_index);
+		}
+		else if (is_abbrev(arg1, "roomweight"))
+		{
+			if (get_number(ses, arg2) < 0.001)
 			{
-				temp = &list[index];
-
-				node = &list[index ? index - 1 : MAP_BF_SIZE - 1];
-
-				if (temp->length >= node->length)
-				{
-					break;
-				}
-
-				vnum = temp->vnum;
-				length = temp->length;
-
-				temp->vnum = node->vnum;
-				temp->length = node->length;
-
-				node->vnum = vnum;
-				node->length = length;
-
-				index = index ? index - 1 : MAP_BF_SIZE - 1;
+				show_message(ses, LIST_COMMAND, "#MAP SET: roomweight should be at least 0.001");
 			}
-
-			tail = (tail + 1) % MAP_BF_SIZE;
-
-			if (tail == head)
+			else
 			{
-				show_error(ses, LIST_COMMAND, "#SHORTEST PATH: MAP TOO BIG FOR BF STACK OF %d", MAP_BF_SIZE);
-				break;
-			}
-			trim = 0;
-
-			next_exit:
+				room->weight = (float) get_number(ses, arg2);
 
-			if (exit == ses->map->global_exit)
-			{
-				exit->next = room->f_exit;
+				show_message(ses, LIST_COMMAND, "#MAP SET: roomweight set to: %.3f", room->weight);
 			}
 		}
-
-		if (trim)
+		else
 		{
-			room->length = 0;
+			show_message(ses, LIST_COMMAND, "#MAP SET: unknown option: %s", arg1);
 		}
 	}
-	return 0;
 }
 
-void shortest_path(struct session *ses, int run, char *delay, char *arg)
+DO_MAP(map_sync)
 {
-	char var[BUFFER_SIZE];
-	struct exit_data *exit;
-	struct room_data *room;
-	int vnum, dest;
-
-	if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
+	if (ses->map)
 	{
-		show_error(ses, LIST_COMMAND, "#SHORTEST PATH: You have to use #PATH END first.");
-
-		return;
+		SET_BIT(ses->map->flags, MAP_FLAG_SYNC);
 	}
 
-	kill_list(ses->list[LIST_PATH]);
-
-	ses->list[LIST_PATH]->update = 0;
-
-	map_search_compile(ses, arg, var);
-
-	dest = searchgrid_find(ses, ses->map->in_room, ses->map->search);
-
-	if (dest == 0 || dest == ses->map->global_vnum)
-	{
-		show_error(ses, LIST_COMMAND, "#SHORTEST PATH: NO PATH FOUND TO %s.", arg);
-		return;
-	}
+	map_read(ses, arg, arg1, arg2);
 
-	if (dest == ses->map->in_room)
+	if (ses->map)
 	{
-		show_error(ses, LIST_COMMAND, "#SHORTEST PATH: Already there.");
-		return;
+		DEL_BIT(ses->map->flags, MAP_FLAG_SYNC);
 	}
+}
 
-	vnum = ses->map->in_room;
+DO_MAP(map_terrain)
+{
+	struct listroot *root = ses->list[LIST_TERRAIN];
+	struct listnode *node;
+	struct room_data *room;
+	char arg3[BUFFER_SIZE], buf1[BUFFER_SIZE];
+	int i, found, flags, density, spread;
 
-	// Slower than a backtrace, but works with mazes.
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ALL, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, buf1, GET_ALL, SUB_VAR|SUB_FUN);
 
-	while (TRUE)
+	if (*arg2 == 0 || (*arg1 == 0 && *arg2 == 0))
 	{
-		room = ses->map->room_list[vnum];
+		i = bsearch_alpha_list(root, arg1, 0);
 
-		if (check_global(ses, room->vnum))
+		if (i > 0)
 		{
-			exit = ses->map->global_exit;
+			tintin_printf2(ses, "name: %-16s  index %4d symbol: %-12s  flags: %s %s", root->list[i]->arg1, i, root->list[i]->arg2, root->list[i]->arg3, root->list[i]->arg4);
 		}
 		else
 		{
-			exit = room->f_exit;
-		}
+			for (found = i = 0 ; i < root->used ; i++)
+			{
+				if (*arg1 == 0 || match(ses, root->list[i]->arg1, arg1, SUB_NONE))
+				{
+					room = root->list[i]->room;
 
-		for ( ; exit ; exit = exit->next)
-		{
-			if (HAS_BIT(exit->flags, EXIT_FLAG_AVOID) || HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_AVOID))
+					if (!HAS_BIT(room->flags, ROOM_FLAG_TERRAIN))
+					{
+						tintin_printf2(ses, "error: no terrain flag set.");
+					}
+					tintin_printf2(ses, "name: %-16s  index %4d  symbol: %-16s  %s %s", room->name, room->terrain_index, room->symbol, root->list[i]->arg3, root->list[i]->arg4);
+
+					found = TRUE;
+				}
+			}
+
+			if (found == FALSE)
 			{
-				goto exit_next;
+				show_message(ses, LIST_COMMAND, "#MAP TERRAIN: NO MATCHES FOUND FOR {%s}.", arg1);
 			}
+		}
+	}
+	else
+	{
+		SET_BIT(ses->map->flags, MAP_FLAG_UPDATETERRAIN);
 
-			vnum = tunnel_void(ses, room->vnum, exit->vnum, exit->dir);
+		density = TERRAIN_FLAG_AMPLE;
+		spread = TERRAIN_FLAG_STANDARD;
+		flags = 0;
 
-			if (searchgrid_walk(ses, room->length, vnum, dest))
+		arg = arg3;
+
+		while (*arg)
+		{
+			arg = get_arg_in_braces(ses, arg, buf1, GET_ONE);
+
+			if (is_abbrev(buf1, "DENSE"))
 			{
-				break;
+				density = TERRAIN_FLAG_DENSE;
+			}
+			else if (is_abbrev(buf1, "AMPLE"))
+			{
+				density = TERRAIN_FLAG_AMPLE;
+			}
+			else if (is_abbrev(buf1, "SPARSE"))
+			{
+				density = TERRAIN_FLAG_SPARSE;
+			}
+			else if (is_abbrev(buf1, "SCANT"))
+			{
+				density = TERRAIN_FLAG_SCANT;
+			}
+			else if (is_abbrev(buf1, "NARROW"))
+			{
+				spread = TERRAIN_FLAG_NARROW;
+			}
+			else if (is_abbrev(buf1, "WIDE"))
+			{
+				spread = TERRAIN_FLAG_WIDE;
+			}
+			else if (is_abbrev(buf1, "VAST"))
+			{
+				spread = TERRAIN_FLAG_WIDE|TERRAIN_FLAG_VAST;
+			}
+			else if (is_abbrev(buf1, "FADEIN"))
+			{
+				SET_BIT(flags, TERRAIN_FLAG_FADEIN);
+			}
+			else if (is_abbrev(buf1, "FADEOUT"))
+			{
+				SET_BIT(flags, TERRAIN_FLAG_FADEOUT);
+			}
+			else if (is_abbrev(buf1, "DOUBLE"))
+			{
+				SET_BIT(flags, TERRAIN_FLAG_DOUBLE);
+			}
+			else
+			{
+				show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP TERRAIN {%s} [DENSE|DOUBLE|SPARSE|SCANT|NARROW|WIDE|VAST|FADEIN|FADEOUT]", arg1);
 			}
 
-			exit_next:
-
-			if (exit == ses->map->global_exit)
+			if (*arg == COMMAND_SEPARATOR)
 			{
-				exit->next = room->f_exit;
+				arg++;
 			}
 		}
 
-		if (exit == NULL)
-		{
-			show_error(ses, LIST_COMMAND, "#SHORTEST PATH: UNKNOWN ERROR.");
-			return;
-		}
+		strcpy(buf1, "");
 
-		if (exit != ses->map->global_exit)
+		SET_BIT(flags, density);
+		SET_BIT(flags, spread);
+
+		if (flags)
 		{
-			if (HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW))
+			if (HAS_BIT(flags, TERRAIN_FLAG_DENSE))
 			{
-				check_append_path(ses, exit->cmd, "", 0);
+				strcat(buf1, "DENSE ");
+			}
+			else if (HAS_BIT(flags, TERRAIN_FLAG_SPARSE))
+			{
+				strcat(buf1, "SPARSE ");
+			}
+			else if (HAS_BIT(flags, TERRAIN_FLAG_SCANT))
+			{
+				strcat(buf1, "SCANT ");
+			}
+
+			if (HAS_BIT(flags, TERRAIN_FLAG_NARROW))
+			{
+				strcat(buf1, "NARROW ");
+			}
+			else if (HAS_BIT(flags, TERRAIN_FLAG_VAST))
+			{
+				strcat(buf1, "VAST ");
+			}
+			else if (HAS_BIT(flags, TERRAIN_FLAG_WIDE))
+			{
+				strcat(buf1, "WIDE ");
+			}
+
+			if (HAS_BIT(flags, TERRAIN_FLAG_FADEIN))
+			{
+				strcat(buf1, "FADEIN ");
+			}
+			else if (HAS_BIT(flags, TERRAIN_FLAG_FADEOUT))
+			{
+				strcat(buf1, "FADEOUT ");
 			}
-			else
+
+			if (HAS_BIT(flags, TERRAIN_FLAG_DOUBLE))
 			{
-				check_append_path(ses, exit->name, "", 0);
+				strcat(buf1, "DOUBLE ");
 			}
+
+			buf1[strlen(buf1) - 1] = 0;
 		}
 
-		SET_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_PATH);
+		node = update_node_list(root, arg1, arg2, buf1, "");
 
-		if (ses->map->room_list[vnum]->search_stamp != ses->map->search->stamp)
+		if (node->room)
 		{
-			show_error(ses, LIST_COMMAND, "%d bad search stamp %d vs %d", vnum, ses->map->room_list[vnum]->search_stamp, ses->map->search->stamp);
+			if (node->room->symbol)
+			{
+				RESTRING(node->room->terrain, arg1);
+				RESTRING(node->room->symbol, arg2);
+			}
+		}
+		else
+		{
+			node->room = create_room(ses, "{0} {%d} {} {%s} {%s} {} {} {} {} {} {1.0} {}", ROOM_FLAG_TERRAIN, node->arg1, node->arg2);
 		}
 
-		if (vnum == dest)
+		node->room->terrain_flags = flags;
+
+		for (i = 0 ; i < root->used ; i++)
 		{
-			break;
+			root->list[i]->room->terrain_index = i;
 		}
+
+		show_message(ses, LIST_COMMAND, "#OK. TERRAIN {%s} HAS BEEN SET TO {%s} {%s}.", arg1, arg2, buf1);
 	}
+}
 
-	if (run)
+DO_MAP(map_unterrain)
+{
+	if (delete_node_with_wild(ses, LIST_TERRAIN, arg))
 	{
-		path_run(ses, delay);
+		SET_BIT(ses->map->flags, MAP_FLAG_UPDATETERRAIN);
 	}
 }
 
-/*
-	Virtual coordinate search for linkable rooms when creating a new room.
-*/
+DO_MAP(map_travel)
+{
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
 
-int find_coord(struct session *ses, char *arg)
+	explore_path(ses, TRUE, arg1, arg2);
+}
+
+
+DO_MAP(map_uninsert)
 {
-	int dir, x, y, z, room;
+	int room1, room2, room3;
+	struct exit_data *exit1, *exit2, *exit3;
+	struct listnode *node;
 
-	dir = get_exit_dir(ses, arg);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 
-	if (dir == 0)
+	room1 = ses->map->in_room;
+	exit1 = find_exit(ses, room1, arg1);
+
+	node = search_node_list(ses->list[LIST_PATHDIR], arg1);
+
+	if (exit1 == NULL)
 	{
-		return 0;
+		show_error(ses, LIST_COMMAND, "#MAP UNINSERT: There is no room in that direction.");
+
+		return;
 	}
 
-	x = (HAS_BIT(dir, MAP_EXIT_E) ? 1 : HAS_BIT(dir, MAP_EXIT_W) ? -1 : 0);
-	y = (HAS_BIT(dir, MAP_EXIT_N) ? 1 : HAS_BIT(dir, MAP_EXIT_S) ? -1 : 0);
-	z = (HAS_BIT(dir, MAP_EXIT_U) ? 1 : HAS_BIT(dir, MAP_EXIT_D) ? -1 : 0);
+	if (node == NULL)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP UNINSERT: Given direction must be a pathdir.");
+		return;
+	}
 
-	room = spatialgrid_find(ses, ses->map->in_room, x, y, z);
+	room2 = exit1->vnum;
+	exit2 = find_exit(ses, room2, node->arg1);
 
-	if (ses->map->room_list[room])
+	if (exit2 == NULL)
 	{
-		if (HAS_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_STATIC))
-		{
-			show_message(ses, LIST_COMMAND, "#MAP: Linkable room is marked static. Creating overlapping room instead.");
+		show_error(ses, LIST_COMMAND, "#MAP UNINSERT: Unable to find backlink room.");
+		return;
+	}
 
-			return 0;
-		}
+	room3 = exit2->vnum;
+	exit3 = find_exit(ses, room3, node->arg2);
+
+	if (exit3 == NULL)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP UNINSERT: Unable to find backlink exit.");
+
+		return;
 	}
-	return room;
+
+	exit1->vnum = room3;
+	exit3->vnum = room1;
+
+	delete_room(ses, room2, TRUE);
+
+	show_message(ses, LIST_COMMAND, "#MAP UNINSERT: Uninserted room {%d}.", room2);
 }
 
-// Used by #map jump and the auto linker
+// 1) timestamp 2) type 3) data
 
-int spatialgrid_find(struct session *ses, int from, int x, int y, int z)
+DO_MAP(map_undo)
 {
-	int head, tail;
-	struct grid_node *node, *temp, list[MAP_BF_SIZE];
-	struct exit_data *exit;
+	struct link_data *link;
 	struct room_data *room;
+	int undo_flag;
+	struct exit_data *exit1, *exit2, *exit3;
 
-	push_call("spatialgrid_find(%s,%d,%d,%d,%d)",ses->name,from,x,y,z);
+	link = ses->map->undo_tail;
 
-	head = 0;
-	tail = 1;
+	if (link == NULL)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP UNDO: No known last move.");
 
-	node = &list[head];
+		return;
+	}
 
-	node->vnum   = from;
-	node->x      = 0;
-	node->y      = 0;
-	node->z      = 0;
-	node->length = 0;
-	node->flags  = 0;
+	room = ses->map->room_list[atoi(link->str1)];
 
-	ses->map->display_stamp++;
+	if (room == NULL)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP UNDO: Room %s does not exist.", link->str2);
+		return;
+	}
 
-	while (head != tail)
+	if (ses->map->room_list[atoi(link->str2)] == NULL)
 	{
-		node = &list[head];
+		show_error(ses, LIST_COMMAND, "#MAP UNDO: Invalid last move.");
+		return;
+	}
 
-		head = (head + 1) % MAP_BF_SIZE;
+	undo_flag = atoi(link->str3);
 
-		room = ses->map->room_list[node->vnum];
+	if (HAS_BIT(undo_flag, MAP_UNDO_MOVE))
+	{
+ 		if (ses->map->in_room != room->vnum)
+		{
+			show_error(ses, LIST_COMMAND, "#MAP UNDO: Invalid last move.");
+			return;
+		}
+		show_message(ses, LIST_COMMAND, "#MAP UNDO: Moving to room %s.", link->str2);
 
-		if (ses->map->display_stamp != room->display_stamp)
+		goto_room(ses, atoi(link->str2));
+	}
+
+	if (HAS_BIT(undo_flag, MAP_UNDO_CREATE))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP UNDO: Deleting room %d.", room->vnum);
+		delete_room(ses, room->vnum, TRUE);
+	}
+	else if (HAS_BIT(undo_flag, MAP_UNDO_LINK))
+	{
+		exit1 = find_exit(ses, room->vnum, link->str2);
+
+		if (exit1)
 		{
-			room->display_stamp = ses->map->display_stamp;
+			show_message(ses, LIST_COMMAND, "#MAP UNDO: Deleting exit leading %s.", exit1->name);
+			delete_exit(ses, room->vnum, exit1);
 		}
-		else if (room->length <= node->length)
+
+		exit2 = find_exit(ses, atoi(link->str2), link->str1);
+
+		if (exit2)
 		{
-			continue;
+			show_message(ses, LIST_COMMAND, "#MAP UNDO: Deleting exit leading %s.", exit2->name);
+			delete_exit(ses, atoi(link->str2), exit2);
 		}
+	}
+	else if (HAS_BIT(undo_flag, MAP_UNDO_INSERT))
+	{
+		exit1 = find_exit(ses, atoi(link->str2), link->str1);
 
-		room->length = node->length;
-/*
-		if (HAS_BIT(node->flags, GRID_FLAG_HIDE))
+		if (exit1 == NULL)
 		{
-			continue;
+			show_error(ses, LIST_COMMAND, "#MAP UNDO: Can't find exit between %s and %s.", link->str2, link->str1);
+			return;
 		}
-*/
-		if (node->x == x && node->y == y && node->z == z)
+
+		exit2 = find_exit(ses, room->vnum, exit1->name);
+
+		if (exit2 == NULL)
 		{
-			pop_call();
-			return node->vnum;
+			show_error(ses, LIST_COMMAND, "#MAP UNDO: No valid exit found in room %d.", room->vnum);
+			return;
 		}
 
-		for (exit = room->f_exit ; exit ; exit = exit->next)
+		exit3 = find_exit(ses, exit2->vnum, ntos(room->vnum));
+
+		if (exit3 == NULL)
 		{
-			if (ses->map->display_stamp == ses->map->room_list[exit->vnum]->display_stamp)
-			{
-				if (room->length >= ses->map->room_list[exit->vnum]->length)
-				{
-					continue;
-				}
-			}
+			show_error(ses, LIST_COMMAND, "#MAP UNDO: Can't find exit between %d and %d.", room->vnum, exit2->vnum);
+			return;
+		}
 
-			if (exit->dir == 0)
-			{
-				continue;
-			}
+		exit1->vnum = exit2->vnum;
+		exit3->vnum = atoi(link->str2);
 
-			if (HAS_BIT(exit->flags, EXIT_FLAG_HIDE) || HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_HIDE))
-			{
-				continue;
-			}
+		delete_room(ses, room->vnum, TRUE);
 
-			if (head == (tail + 1) % MAP_BF_SIZE)
-			{
-				break;
-			}
+		show_message(ses, LIST_COMMAND, "#MAP UNDO: Uninserting room %s.", link->str1);
+	}
+	del_undo(ses, link);
+}
 
-			temp = &list[tail];
+DO_MAP(map_unlink)
+{
+	struct exit_data *exit1;
+	struct exit_data *exit2;
+	struct listnode *node;
 
-			temp->vnum   = exit->vnum;
-			temp->w      = node->w;
-			temp->x      = node->x + (HAS_BIT(exit->dir, MAP_EXIT_E) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_W) ? -1 : 0);
-			temp->y      = node->y + (HAS_BIT(exit->dir, MAP_EXIT_N) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_S) ? -1 : 0);
-			temp->z      = node->z + (HAS_BIT(exit->dir, MAP_EXIT_U) ?  1 : HAS_BIT(exit->dir, MAP_EXIT_D) ? -1 : 0);
-			temp->length = node->length + 1;
-			temp->flags  = 0;
-/*
-			if (HAS_BIT(exit->flags, EXIT_FLAG_HIDE) || HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_HIDE))
-			{
-				SET_BIT(temp->flags, GRID_FLAG_HIDE);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
 
-				temp->length += 1000;
+	node = search_node_list(ses->list[LIST_PATHDIR], arg1);
+
+	exit1 = find_exit(ses, ses->map->in_room, arg1);
+
+	if (exit1 == NULL)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP UNLINK: No exit with that name found");
+
+		return;
+	}
+
+	if (*arg2 == 'b' || *arg == 'B')
+	{
+		if (node)
+		{
+			exit2 = find_exit(ses, exit1->vnum, node->arg2);
+
+			if (exit2)
+			{
+				delete_exit(ses, exit1->vnum, exit2);
 			}
-*/
-			tail = (tail + 1) % MAP_BF_SIZE;
 		}
 	}
-	pop_call();
-	return 0;
+
+	delete_exit(ses, ses->map->in_room, exit1);
+
+	show_message(ses, LIST_COMMAND, "#MAP UNLINK: Exit deleted.");
 }
 
-void explore_path(struct session *ses, int run, char *arg1, char *arg2)
+DO_MAP(map_update)
 {
-	struct exit_data *exit;
-	int room, vnum;
-
-	for (vnum = 0 ; vnum < ses->map->size ; vnum++)
+	if (ses->map == NULL)
 	{
-		if (ses->map->room_list[vnum])
-		{
-			DEL_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_PATH);
-		}
+		show_message(ses, LIST_COMMAND, "#MAP UPDATE: NO MAP DATA.");
 	}
-
-	if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
+	else if (ses->map->room_list[ses->map->in_room] == NULL)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP EXPLORE: You have to use #PATH END first.");
-
-		return;
+		show_message(ses, LIST_COMMAND, "#MAP UPDATE: NOT INSIDE MAP.");
+	}
+	else if (!HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP))
+	{
+		show_message(ses, LIST_COMMAND, "#MAP UPDATE: VTMAP FLAG NOT SET.");
+	}
+	else if (ses != gtd->ses)
+	{
+		show_message(ses, LIST_COMMAND, "#MAP UPDATE: NOT THE ACTIVE SESSION.");
+	}
+	else
+	{
+		show_message(ses, LIST_COMMAND, "#MAP UPDATE: OK.");
+		
+		SET_BIT(ses->flags, SES_FLAG_UPDATEVTMAP);
 	}
+}
 
-	kill_list(ses->list[LIST_PATH]);
+DO_MAP(map_run)
+{
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
 
-	ses->list[LIST_PATH]->update = 0;
+	shortest_path(ses, TRUE, arg2, arg1);
+}
 
-	room = ses->map->in_room;
+DO_MAP(map_vnum)
+{
+	int vnum, vnum1, vnum2, old_room, new_room;
+	struct exit_data *exit;
 
-	exit = find_exit(ses, room, arg1);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
 
-	if (exit == NULL)
+	vnum1 = atoi(arg1);
+
+	if (*arg2)
 	{
-		show_error(ses, LIST_COMMAND, "#MAP: There's no exit named '%s'.", arg1);
+		vnum2 = atoi(arg2);
+	}
+	else
+	{
+		vnum2 = vnum1;
+	}
+	
+	if (vnum1 <= 0 || vnum1 >= ses->map->size || vnum2 <= 0 || vnum2 >= ses->map->size)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP VNUM {%s} {%s} - VNUMS MUST BE BETWEEN {1} and {%d}", arg1, arg2, ses->map->size - 1);
 		return;
 	}
 
-	vnum = exit->vnum;
-
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW))
+	for (vnum = vnum1 ; vnum <= vnum2 ; vnum++)
 	{
-		check_append_path(ses, exit->cmd, "", 0);
+		if (ses->map->room_list[vnum] == NULL)
+		{
+			break;
+		}
 	}
-	else
+
+	if (vnum > vnum2)
 	{
-		check_append_path(ses, exit->name, "", 0);
+		show_error(ses, LIST_COMMAND, "#MAP VNUM {%s} {%s} - NO FREE VNUM FOUND.", arg1, arg2);
+		return;
 	}
 
-	SET_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_PATH);
-	SET_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_PATH);
+	old_room = ses->map->in_room;
+	new_room = vnum;
 
-	while (get_room_exits(ses, vnum) == 2)
+	ses->map->room_list[new_room] = ses->map->room_list[old_room];
+	ses->map->room_list[new_room]->vnum = new_room;
+	ses->map->room_list[old_room] = NULL;
+	ses->map->in_room = new_room;
+
+	if (ses->map->at_room == old_room)
 	{
-		exit = ses->map->room_list[vnum]->f_exit;
+		ses->map->at_room = new_room;
+	}
 
-		if (HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_PATH))
+	for (vnum = 1 ; vnum < ses->map->size ; vnum++)
+	{
+		if (ses->map->room_list[vnum] == NULL)
 		{
-			exit = ses->map->room_list[vnum]->l_exit;
-
-			if (HAS_BIT(ses->map->room_list[exit->vnum]->flags, ROOM_FLAG_PATH))
-			{
-				break;
-			}
+			continue;
 		}
 
-		if (!HAS_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_VOID))
+		for (exit = ses->map->room_list[vnum]->f_exit ; exit ; exit = exit->next)
 		{
-			if (HAS_BIT(ses->map->flags, MAP_FLAG_NOFOLLOW))
-			{
-				check_append_path(ses, exit->cmd, "", 0);
-			}
-			else
+			if (exit->vnum == old_room)
 			{
-				check_append_path(ses, exit->name, "", 0);
+				exit->vnum = new_room;
 			}
 		}
-
-		vnum = exit->vnum;
-
-		SET_BIT(ses->map->room_list[vnum]->flags, ROOM_FLAG_PATH);
 	}
 
-	DEL_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_PATH);
-
-	if (run)
-	{
-		path_run(ses, arg2);
-	}
+	tintin_printf(ses, "#MAP VNUM: MOVED ROOM %d TO %d.", old_room, new_room);
 }
 
-void map_mouse_handler(struct session *ses, char *arg1, char *arg2, int x, int y)
+DO_MAP(map_write)
 {
-	int max_x, max_y;
-	int top_row, top_col, bot_row, bot_col, rows, cols;
+	struct listroot *root;
+	FILE *file;
+	struct exit_data *exit;
+	int index;
 
-	push_call("map_mouse_handler(%p,%p,%p,%d,%d)",ses,arg1,arg2,x,y);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
 
-	if (ses->map == NULL || !HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP) || ses->map->room_list[ses->map->in_room] == NULL)
+	if (*arg1 == 0)
 	{
-		pop_call();
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #MAP WRITE {<filename>} {FORCE}");
+
 		return;
 	}
 
-	if (ses->map->rows > 1 && ses->map->cols > 1)
-	{
-		top_row = ses->map->top_row;
-		top_col = ses->map->top_col;
-		bot_row = ses->map->bot_row;
-		bot_col = ses->map->bot_col;
-		rows    = ses->map->rows;
-		cols    = ses->map->cols;
-	}
-	else
+	if (!str_suffix(arg1, ".tin") && !is_abbrev(arg2, "FORCE"))
 	{
-		top_row = 1;
-		top_col = 1;
-		bot_row = ses->split->top_row - 2;
-		bot_col = gtd->screen->cols;
-		rows    = ses->split->top_row - 2;
-		cols    = gtd->screen->cols;
-	}
-
-	y = y - 1;
-	x = x - 1;
+		show_error(ses, LIST_COMMAND, "#MAP WRITE {%s}: USE {FORCE} TO OVERWRITE .tin FILES.");
 
-	if (y > bot_row || y < top_row)
-	{
-		pop_call();
 		return;
 	}
 
-	if (x > bot_col || x < top_col)
+	if ((file = fopen(arg1, "w")) == NULL)
 	{
-		pop_call();
+		show_error(ses, LIST_COMMAND, "#MAP WRITE {%s} - COULDN'T OPEN FILE TO WRITE.", arg1);
+
 		return;
 	}
 
-	y = y + 1 - top_row;
-	x = x + 1 - top_col;
+	fprintf(file, "C %d\n\n", ses->map->size);
 
-	if (HAS_BIT(ses->map->flags, MAP_FLAG_ASCIIGRAPHICS))
-	{
-		y /= 3;
-		x /= 6;
+	fprintf(file, "V 2020\n\n");
 
-		max_y = 2 + rows / 3;
-		max_x = 2 + cols / 6;
-	}
-	else if (HAS_BIT(ses->map->flags, MAP_FLAG_UNICODEGRAPHICS) || HAS_BIT(ses->map->flags, MAP_FLAG_BLOCKGRAPHICS))
+	for (index = 0 ; map_color_table[index].name ; index++)
 	{
-		y /= 2;
-		x /= 5;
-
-		max_y = 2 + rows / 2;
-		max_x = 2 + cols / 5;
+		fprintf(file, "C%s %s\n", map_color_table[index].name, ses->map->color[index]);
 	}
-	else if (HAS_BIT(ses->map->flags, MAP_FLAG_MUDFONT))
-	{
-		x /= 2;
+	fprintf(file, "\n");
 
-		max_y = 2 + rows;
-		max_x = 2 + cols / 2;
-	}
-	else
+	fprintf(file, "F %d\n\n", ses->map->flags);
+
+	fprintf(file, "G %d\n\n", ses->map->global_vnum);
+
+	fprintf(file, "I %d\n\n", ses->map->in_room ? ses->map->in_room : ses->map->last_room);
+
+	for (index = 0 ; map_legend_table[index].name ; index++)
 	{
-		max_y = 2 + rows;
-		max_x = 2 + cols;
+		fprintf(file, "L {%s} {%s} {%s}\n", map_legend_table[index].group, map_legend_table[index].name, ses->map->legend_raw[index]);
 	}
+	fprintf(file, "\n\n");
 
-	y = max_y - 1 - y;
+	root = ses->list[LIST_LANDMARK];
 
-	if (x < 0 || y < 0)
+	for (index = 0 ; index < root->used ; index++)
 	{
-		pop_call();
-		return;
+		fprintf(file, "LM {%s} {%d} {%s} {%s}\n", root->list[index]->arg1, root->list[index]->val32[0], root->list[index]->arg3, root->list[index]->arg4);
 	}
+	fprintf(file, "\n\n");	
 
-	if (max_x != map_grid_x || max_y != map_grid_y)
+	root = ses->list[LIST_TERRAIN];
+
+	for (index = 0 ; index < root->used ; index++)
 	{
-		pop_call();
-		return;
+		fprintf(file, "T {%s} {%s} {%s}\n", root->list[index]->arg1, root->list[index]->arg2, root->list[index]->arg3);
 	}
+	fprintf(file, "\n\n");	
 
-	if (ses->map->grid_rooms[x + 1 + max_x * (y - 1)])
+	for (index = 0 ; index < ses->map->size ; index++)
 	{
-		check_all_events(ses, SUB_ARG, 2, 1, "MAP %s %s", arg1, arg2, ntos(ses->map->grid_rooms[x + 1 + max_x * (y - 1)]->vnum));
+		if (ses->map->room_list[index])
+		{
+			DEL_BIT(ses->map->room_list[index]->flags, ROOM_FLAG_PATH);
+
+			fprintf(file, "\nR {%5d} {%d} {%s} {%s} {%s} {%s} {%s} {%s} {%s} {%s} {%.3f} {%s}\n",
+				ses->map->room_list[index]->vnum,
+				ses->map->room_list[index]->flags,
+				ses->map->room_list[index]->color,
+				ses->map->room_list[index]->name,
+				ses->map->room_list[index]->symbol,
+				ses->map->room_list[index]->desc,
+				ses->map->room_list[index]->area,
+				ses->map->room_list[index]->note,
+				ses->map->room_list[index]->terrain,
+				ses->map->room_list[index]->data,
+				ses->map->room_list[index]->weight,
+				ses->map->room_list[index]->id);
+
+			for (exit = ses->map->room_list[index]->f_exit ; exit ; exit = exit->next)
+			{
+				fprintf(file, "E {%5d} {%s} {%s} {%d} {%d} {%s} {%.3f} {%s}\n",
+					exit->vnum,
+					exit->name,
+					exit->cmd,
+					exit->dir,
+					exit->flags,
+					exit->data,
+					exit->weight,
+					exit->color);
+			}
+		}
 	}
+
+	fclose(file);
+
+	show_message(ses, LIST_COMMAND, "#MAP: Map file written to {%s}.", arg1);
 }

+ 118 - 6
src/math.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
@@ -71,7 +70,7 @@ DO_COMMAND(do_math)
 	{
 		result = get_number(ses, arg2);
 
-		node = set_nest_node(ses->list[LIST_VARIABLE], arg1, "%.*Lf", precision, result);
+		node = set_nest_node_ses(ses, arg1, "%.*Lf", precision, result);
 
 		show_message(ses, LIST_VARIABLE, "#MATH: VARIABLE {%s} HAS BEEN SET TO {%s}.", arg1, node->arg2);
 	}
@@ -201,6 +200,7 @@ void mathexp(struct session *ses, char *str, char *result, int seed)
 	if (badnumber && debug)                                 \
 	{                                                       \
 		badnumber = 0;                                  \
+		precision = 0;                                  \
 		show_debug(ses, LIST_VARIABLE, "#MATH EXP: INVALID NUMBER %s.", buf3); \
 	}                                                       \
 	*pta = 0;                                               \
@@ -210,6 +210,7 @@ void mathexp(struct session *ses, char *str, char *result, int seed)
 	status = newstatus;                                     \
 	pta = buf3;                                             \
 	point = -1;                                             \
+	metric = 0;                                             \
 }
 
 void add_math_node(char *arg1, char *arg2, char *arg3)
@@ -239,10 +240,11 @@ void del_math_node(struct link_data *node)
 int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 {
 	char buf1[BUFFER_SIZE], buf2[BUFFER_SIZE], buf3[STRING_SIZE], *pti, *pta;
-	int level, status, point, badnumber, nest;
+	int level, status, point, badnumber, metric, nest;
 
 	nest      = 0;
 	level     = 0;
+	metric    = 0;
 	point     = -1;
 	status    = EXP_VARIABLE;
 	precision = seed;
@@ -275,6 +277,16 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 					case '9':
 						*pta++ = *pti++;
 
+						if (metric)
+						{
+							badnumber = 1;
+
+							if (debug == 0)
+							{
+								return FALSE;
+							}
+						}
+
 						if (point >= 0)
 						{
 							point++;
@@ -313,6 +325,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						status = EXP_BRACE;
 						nest++;
 						break;
+
 					case '"':
 						if (pta != buf3)
 						{
@@ -396,6 +409,90 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						}
 						break;
 
+					case 'K':
+					case 'M':
+//					case 'G':
+//					case 'T':
+//					case 'P':
+//					case 'E':
+//					case 'Z':
+//					case 'Y':
+						if (pta == buf3 || metric == 1)
+						{
+							badnumber = 1;
+
+							if (debug == 0)
+							{
+								return FALSE;
+							}
+							*pta++ = *pti++;
+							*pta = 0;
+						}
+						else
+						{
+							MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR);
+
+							*pta++ = '*';
+							MATH_NODE(FALSE, EXP_PR_INTMUL, EXP_VARIABLE);
+
+							switch (*pti++)
+							{
+								case 'K': pta += sprintf(pta, "1000"); break;
+								case 'M': pta += sprintf(pta, "1000000"); break;
+								case 'G': pta += sprintf(pta, "1000000000"); break;
+								case 'T': pta += sprintf(pta, "1000000000000"); break;
+								case 'P': pta += sprintf(pta, "1000000000000000"); break;
+								case 'E': pta += sprintf(pta, "1000000000000000000"); break;
+								case 'Z': pta += sprintf(pta, "1000000000000000000000"); break;
+								case 'Y': pta += sprintf(pta, "1000000000000000000000000"); break;
+							}
+							metric = 1;
+						}
+						break;
+
+					case 'm':
+					case 'u':
+//					case 'n':
+//					case 'p':
+//					case 'f':
+//					case 'a':
+//					case 'z':
+//					case 'y':
+						if (pta == buf3 || metric == 1)
+						{
+							badnumber = 1;
+
+							if (debug == 0)
+							{
+								return FALSE;
+							}
+							*pta++ = *pti++;
+							*pta = 0;
+						}
+						else
+						{
+							MATH_NODE(FALSE, EXP_PR_VAR, EXP_OPERATOR);
+
+							*pta++ = '/';
+
+							MATH_NODE(FALSE, EXP_PR_INTMUL, EXP_VARIABLE);
+
+							switch (*pti++)
+							{
+								case 'm': pta += sprintf(pta, "1000"); break;
+								case 'u': pta += sprintf(pta, "1000000"); break;
+								case 'n': pta += sprintf(pta, "1000000000"); break;
+								case 'p': pta += sprintf(pta, "1000000000000"); break;
+								case 'f': pta += sprintf(pta, "1000000000000000"); break;
+								case 'a': pta += sprintf(pta, "1000000000000000000"); break;
+								case 'z': pta += sprintf(pta, "1000000000000000000000"); break;
+								case 'y': pta += sprintf(pta, "1000000000000000000000000"); break;
+							}
+							metric = 1;
+							precision = UMAX(precision, strlen(buf3) -1);
+						}
+						break;
+
 					default:
 						*pta++ = *pti++;
 						*pta = 0;
@@ -782,7 +879,9 @@ void mathexp_compute(struct session *ses, struct link_data *node)
 					if (tintoi(node->next->str3) == 0)
 					{
 						show_debug(ses, LIST_VARIABLE, "#MATH ERROR: DIVISION ZERO.");
-						value = tintoi(node->prev->str3);
+						value = 0;
+						precision = 0;
+//						value = tintoi(node->prev->str3);
 					}
 					else
 					{
@@ -1263,6 +1362,7 @@ long double tineval(struct session *ses, char *left, char *right)
 long double tindice(struct session *ses, char *left, char *right)
 {
 	unsigned long long cnt, numdice, sizedice, sum;
+	long double estimate;
 
 	numdice  = (unsigned long long) tintoi(left);
 	sizedice = (unsigned long long) tintoi(right);
@@ -1272,10 +1372,22 @@ long double tindice(struct session *ses, char *left, char *right)
 		return 0;
 	}
 
+	if (numdice > 100)
+	{
+		estimate = numdice / 100.0;
+		numdice = 100;
+	}
+	else
+	{
+		estimate = 1;
+	}
+
 	for (cnt = sum = 0 ; cnt < numdice ; cnt++)
 	{
 		sum += generate_rand(ses) % sizedice + 1;
 	}
 
+	sum *= estimate;
+
 	return (long double) sum;
 }

+ 2 - 3
src/mccp.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/

+ 93 - 23
src/memory.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2007                       *
 ******************************************************************************/
@@ -62,12 +61,16 @@ char *str_alloc(int size)
 {
 	char *str;
 
-	struct str_data *str_ptr = (struct str_data *) calloc(1, gtd->str_size + size + 1);
+	struct str_data *str_ptr = (struct str_data *) calloc(1, sizeof(struct str_data) + size + 1);
+
+	LINK(str_ptr, gtd->memory->next, gtd->memory->prev);
+
+	gtd->memory->max++;
 
 	str_ptr->max = size + 1;
 	str_ptr->len = 0;
 
-	str = (char *) str_ptr + gtd->str_size;
+	str = (char *) str_ptr + sizeof(struct str_data);
 
 	*str = 0;
 
@@ -80,7 +83,11 @@ struct str_data *str_realloc(struct str_data *str_ptr, int size)
 	{
 		int len = str_ptr->len;
 
-		str_ptr = (struct str_data *) realloc(str_ptr, gtd->str_size + size + 1);
+		UNLINK(str_ptr, gtd->memory->next, gtd->memory->prev);
+
+		str_ptr = (struct str_data *) realloc(str_ptr, sizeof(struct str_data) + size + 1);
+
+		LINK(str_ptr, gtd->memory->next, gtd->memory->prev);
 
 		str_ptr->max = size + 1;
 		str_ptr->len = len;
@@ -94,10 +101,7 @@ struct str_data *str_resize(struct str_data *str_ptr, int add)
 
 	if (str_ptr->max <= len + add)
 	{
-		str_ptr = (struct str_data *) realloc(str_ptr, gtd->str_size + (len + 1) * 2 + add + 1);
-
-		str_ptr->max = (len + 1) * 2 + add + 1;
-		str_ptr->len = len;
+		str_ptr = str_realloc(str_ptr, len * 2 + add);
 	}
 	return str_ptr;
 }
@@ -117,29 +121,42 @@ char *str_mim(char *original)
 
 void str_clone(char **clone, char *original)
 {
-	struct str_data *clo_ptr = (struct str_data *) (*clone - gtd->str_size);
+	struct str_data *clo_ptr = (struct str_data *) (*clone - sizeof(struct str_data));
 	int len = str_len(original);
 
 	if (clo_ptr->max < len)
 	{
 		clo_ptr = str_realloc(clo_ptr, len * 2);
 
-		*clone = (char *) clo_ptr + gtd->str_size;
+		*clone = (char *) clo_ptr + sizeof(struct str_data);
 	}
 }
 
+char *str_dup_clone(char *original)
+{
+	char *dup;
+	int len;
+
+	len = str_len(original);
+	dup = str_alloc(len);
+
+	memcpy(dup, original, len + 1);
+
+	return dup;
+}
+
 // call after a non str_ function alters *str to set the correct length.
 
 void str_fix(char *original)
 {
-	struct str_data *str_ptr = (struct str_data *) (original - gtd->str_size);
+	struct str_data *str_ptr = (struct str_data *) (original - sizeof(struct str_data));
 
 	str_ptr->len = strlen(original);
 }
 
 int str_len(char *str)
 {
-	struct str_data *str_ptr = (struct str_data *) (str - gtd->str_size);
+	struct str_data *str_ptr = (struct str_data *) (str - sizeof(struct str_data));
 
 	return str_ptr->len;
 }
@@ -179,13 +196,13 @@ char *str_cpy(char **str, char *buf)
 
 	buf_len = strlen(buf);
 
-	str_ptr = (struct str_data *) (*str - gtd->str_size);
+	str_ptr = (struct str_data *) (*str - sizeof(struct str_data));
 
 	if (str_ptr->max <= buf_len)
 	{
 		str_ptr = str_realloc(str_ptr, buf_len);
 
-		*str = (char *) str_ptr + gtd->str_size;
+		*str = (char *) str_ptr + sizeof(struct str_data);
 	}
 	str_ptr->len = buf_len;
 
@@ -235,13 +252,13 @@ char *str_ncpy(char **str, char *buf, int len)
 		buf_len = len;
 	}
 
-	str_ptr = (struct str_data *) (*str - gtd->str_size);
+	str_ptr = (struct str_data *) (*str - sizeof(struct str_data));
 
 	if (str_ptr->max <= buf_len)
 	{
 		str_ptr = str_realloc(str_ptr, len);
 
-		*str = (char *) str_ptr + gtd->str_size;
+		*str = (char *) str_ptr + sizeof(struct str_data);
 	}
 
 	str_ptr->len = UMIN(buf_len, len);
@@ -260,13 +277,13 @@ char *str_cat(char **str, char *buf)
 
 	buf_len = strlen(buf);
 
-	str_ptr = (struct str_data *) (*str - gtd->str_size);
+	str_ptr = (struct str_data *) (*str - sizeof(struct str_data));
 
 	if (str_ptr->max <= str_ptr->len + buf_len)
 	{
 		str_ptr = str_resize(str_ptr, buf_len);
 
-		*str = (char *) str_ptr + gtd->str_size;
+		*str = (char *) str_ptr + sizeof(struct str_data);
 	}
 
 	strcpy(&(*str)[str_ptr->len], buf);
@@ -282,13 +299,13 @@ char *str_cat_chr(char **ptr, char chr)
 {
 	struct str_data *str_ptr;
 
-	str_ptr = (struct str_data *) (*ptr - gtd->str_size);
+	str_ptr = (struct str_data *) (*ptr - sizeof(struct str_data));
 
 	if (str_ptr->max <= str_ptr->len + 1)
 	{
 		str_ptr = str_resize(str_ptr, 1);
 
-		*ptr = (char *) str_ptr + gtd->str_size;
+		*ptr = (char *) str_ptr + sizeof(struct str_data);
 	}
 
 	(*ptr)[str_ptr->len++] = chr;
@@ -315,7 +332,60 @@ char *str_cat_printf(char **ptr, char *fmt, ...)
 	return *ptr;
 }
 
+char *str_ins(char **str, int index, char *buf)
+{
+	int buf_len;
+	struct str_data *str_ptr;
+
+	buf_len = strlen(buf);
+
+	str_ptr = (struct str_data *) (*str - sizeof(struct str_data));
+
+	if (str_ptr->max <= str_ptr->len + buf_len)
+	{
+		str_ptr = str_resize(str_ptr, buf_len);
+
+		*str = (char *) str_ptr + sizeof(struct str_data);
+	}
+
+	if (index >= str_ptr->len)
+	{
+		strcpy(&(*str)[str_ptr->len], buf);
+	}
+	else
+	{
+		int cnt;
+		char *pta, *ptz;
+
+		pta = &(*str)[str_ptr->len + 1];
+		ptz = &(*str)[str_ptr->len + 1 + buf_len];
+
+		for (cnt = 0 ; cnt < buf_len + 1 ; cnt++)
+		{
+			*ptz-- = *pta--;
+		}
+
+		pta = &(*str)[index];
+		ptz = buf;
+
+		for (cnt = 0 ; cnt < buf_len ; cnt++)
+		{
+			*pta++ = *ptz++;
+		}
+	}
+
+	str_ptr->len += buf_len;
+
+	return *str;
+}
+
 void str_free(char *ptr)
 {
-	free((struct str_data *) (ptr - gtd->str_size));
+	struct str_data *str_ptr = (struct str_data *) (ptr - sizeof(struct str_data));
+
+	UNLINK(str_ptr, gtd->memory->next, gtd->memory->prev);
+
+	gtd->memory->max--;
+
+	free(str_ptr);
 }

+ 25 - 0
src/misc.c

@@ -257,5 +257,30 @@ DO_COMMAND(do_send)
 
 DO_COMMAND(do_test)
 {
+	char arg1[BUFFER_SIZE], arg2[100], arg3[100], arg4[100];
+
+	strcpy(arg2, "9");
+	strcpy(arg3, "<f0b8>");
+	strcpy(arg4, "1 9");
+
+	if (isdigit(arg[0]))
+	{
+		sprintf(arg2, "%d", (arg[0] - '0') * (arg[0] - '0'));
+
+		if (isxdigit(arg[1]) && isxdigit(arg[2]) && isxdigit(arg[3]))
+		{
+			sprintf(arg3, "<f%c%c%c>", arg[1], arg[2], arg[3]);
+
+			if (isdigit(arg[4]) && isdigit(arg[5]))
+			{
+				sprintf(arg4, "%d %d %s", (arg[4] - '0') * (arg[4] - '0') / 10, (arg[5] - '0') * (arg[5] - '0'), &arg[6]);
+			}
+		}
+	}
+	sprintf(arg1, "#line quiet {#event {RECEIVED KEYPRESS} {#end \\};#screen cursor hide;#screen clear all;#event {SECOND} #loop 0 %s cnt #delay {$cnt / (1.0+%s)} #draw %s rain 1 1 -1 -1 rain %s}", arg2, arg2, arg3, arg4);
+
+	script_driver(gtd->ses, LIST_COMMAND, arg1);
+
 	return ses;
 }
+

+ 6 - 4
src/msdp.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2011                       *
 ******************************************************************************/
@@ -121,7 +120,10 @@ void msdp_update_all(char *var, char *fmt, ...)
 		{
 			for (buddy = ses->port->next ; buddy ; buddy = buddy->next)
 			{
-				msdp_update_var(ses, buddy, var, buf);
+				if (buddy->msdp_data)
+				{
+					msdp_update_var(ses, buddy, var, buf);
+				}
 			}
 		}
 	}

+ 309 - 15
src/nest.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2009                       *
 ******************************************************************************/
@@ -39,6 +38,40 @@ struct listroot *search_nest_root(struct listroot *root, char *arg)
 	return node->root;
 }
 
+struct listroot *search_nest_base_ses(struct session *ses, char *arg)
+{
+	struct listnode *node;
+	struct listroot *root;
+
+	int index;
+
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_LOCAL))
+	{
+		for (index = gtd->script_index ; index ; index--)
+		{
+			root = gtd->script_stack[index]->local;
+
+			if (root->used)
+			{
+				node = search_node_list(root, arg);
+
+				if (node)
+				{
+					return root;
+				}
+			}
+		}
+	}
+
+	node = search_node_list(ses->list[LIST_VARIABLE], arg);
+
+	if (node == NULL)
+	{
+		return NULL;
+	}
+	return ses->list[LIST_VARIABLE];
+}
+
 struct listnode *search_base_node(struct listroot *root, char *variable)
 {
 	char name[BUFFER_SIZE];
@@ -48,6 +81,69 @@ struct listnode *search_base_node(struct listroot *root, char *variable)
 	return search_node_list(root, name);
 }
 
+struct listnode *search_nest_node_ses(struct session *ses, char *variable)
+{
+	char name[BUFFER_SIZE], *arg;
+	struct listroot *root;
+	struct listnode *node;
+	int index;
+
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_LOCAL))
+	{
+		for (index = gtd->script_index ; index ; index--)
+		{
+			root = gtd->script_stack[index]->local;
+
+			if (root->used)
+			{
+				arg = get_arg_to_brackets(root->ses, variable, name);
+
+				while (root && *arg)
+				{
+					root = search_nest_root(root, name);
+
+					if (root)
+					{
+						arg = get_arg_in_brackets(root->ses, arg, name);
+					}
+				}
+
+				if (root)
+				{
+					node = search_node_list(root, name);
+
+					if (node)
+					{
+						return node;
+					}
+				}
+			}
+		}
+	}
+
+	root = ses->list[LIST_VARIABLE];
+
+	arg = get_arg_to_brackets(ses, variable, name);
+
+	while (root && *arg)
+	{
+		root = search_nest_root(root, name);
+
+		if (root)
+		{
+			arg = get_arg_in_brackets(root->ses, arg, name);
+		}
+	}
+
+	if (root)
+	{
+		return search_node_list(root, name);
+	}
+
+	return NULL;
+}
+
+	
 struct listnode *search_nest_node(struct listroot *root, char *variable)
 {
 	char name[BUFFER_SIZE], *arg;
@@ -767,8 +863,166 @@ void view_nest_node(struct listnode *node, char **str_result, int nest, int init
 	}
 }
 
+struct listnode *set_nest_node_ses(struct session *ses, char *arg1, char *format, ...)
+{
+	struct listnode *node;
+	struct listroot *root;
+	char *arg, *arg2, name[BUFFER_SIZE];
+	va_list args;
+
+	push_call("set_nest_node_ses(%p,%s,%p,...)",ses,arg1,format);
+
+	va_start(args, format);
+	vasprintf(&arg2, format, args);
+	va_end(args);
+
+	arg = get_arg_to_brackets(ses, arg1, name);
+
+	check_all_events(ses, SUB_ARG, 1, 2, "VARIABLE UPDATE %s", name, name, arg2);
+
+	root = search_nest_base_ses(ses, name);
+
+	if (root == NULL)
+	{
+		root = ses->list[LIST_VARIABLE];
+		node = NULL;
+	}
+	else
+	{
+		node = search_nest_node(root, arg1);
+	}
+
+	while (*arg)
+	{
+		root = update_nest_root(root, name);
+
+		if (root)
+		{
+			arg = get_arg_in_brackets(root->ses, arg, name);
+		}
+	}
+
+	node = search_node_list(root, name);
+
+	if (node && node->root)
+	{
+		free_list(node->root);
+
+		node->root = NULL;
+	}
+
+	if (*space_out(arg2) == DEFAULT_OPEN)
+	{
+		update_nest_node(update_nest_root(root, name), arg2);
+
+		node = search_node_list(root, name);
+	}
+	else if (node)
+	{
+		str_cpy(&node->arg2, arg2);
+	}
+	else
+	{
+		node = update_node_list(root, name, arg2, "", "");
+	}
+
+	if (gtd->level->oneshot)
+	{
+		SET_BIT(node->flags, NODE_FLAG_ONESHOT);
+	}
+
+	check_all_events(root->ses, SUB_ARG, 1, 1, "VARIABLE UPDATED %s", name, name, arg2);
+
+	free(arg2);
+
+	pop_call();
+	return node;
+}
+
+// like set, but we're adding here.
+
+struct listnode *add_nest_node_ses(struct session *ses, char *arg1, char *format, ...)
+{
+	struct listnode *node;
+	struct listroot *root;
+	char *arg, *arg2, name[BUFFER_SIZE];
+	va_list args;
+
+	push_call("add_nest_node_ses(%p,%s,%p,...)",ses,arg1,format);
+
+	va_start(args, format);
+	vasprintf(&arg2, format, args);
+	va_end(args);
+
+	arg = get_arg_to_brackets(ses, arg1, name);
+
+	check_all_events(ses, SUB_ARG, 1, 2, "VARIABLE UPDATE %s", name, name, arg2);
+
+	root = search_nest_base_ses(ses, name);
+
+	if (root == NULL)
+	{
+		root = ses->list[LIST_VARIABLE];
+		node = NULL;
+	}
+	else
+	{
+		node = search_nest_node(root, arg1);
+	}
+
+	while (*arg)
+	{
+		root = update_nest_root(root, name);
+
+		if (root)
+		{
+			arg = get_arg_in_brackets(root->ses, arg, name);
+		}
+	}
+
+	node = search_node_list(root, name);
+
+/*
+	if (node && node->root)
+	{
+		free_list(node->root);
+
+		node->root = NULL;
+	}
+*/
+
+	if (*space_out(arg2) == DEFAULT_OPEN)
+	{
+		update_nest_node(update_nest_root(root, name), arg2);
+
+		node = search_node_list(root, name);
+	}
+	else if (node)
+	{
+		str_cpy(&node->arg2, arg2);
+	}
+	else
+	{
+		node = update_node_list(root, name, arg2, "", "");
+	}
+
+	if (gtd->level->oneshot)
+	{
+		SET_BIT(node->flags, NODE_FLAG_ONESHOT);
+	}
+
+	check_all_events(root->ses, SUB_ARG, 1, 1, "VARIABLE UPDATED %s", name, name, arg2);
+
+	free(arg2);
+
+	pop_call();
+	return node;
+}
+
+
 struct listnode *set_nest_node(struct listroot *root, char *arg1, char *format, ...)
 {
+	struct listroot *base;
 	struct listnode *node;
 	char *arg, *arg2, name[BUFFER_SIZE];
 	va_list args;
@@ -783,9 +1037,14 @@ struct listnode *set_nest_node(struct listroot *root, char *arg1, char *format,
 
 	check_all_events(root->ses, SUB_ARG, 1, 2, "VARIABLE UPDATE %s", name, name, arg2);
 
-	if (search_node_list(local_list(NULL), name))
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_LOCAL))
 	{
-		root = local_list(NULL);
+		base = search_nest_base_ses(root->ses, name);
+
+		if (base)
+		{
+			root = base;
+		}
 	}
 
 	while (*arg)
@@ -813,6 +1072,10 @@ struct listnode *set_nest_node(struct listroot *root, char *arg1, char *format,
 
 		node = search_node_list(root, name);
 	}
+	else if (node)
+	{
+		str_cpy(&node->arg2, arg2);
+	}
 	else
 	{
 		node = update_node_list(root, name, arg2, "", "");
@@ -835,16 +1098,31 @@ struct listnode *set_nest_node(struct listroot *root, char *arg1, char *format,
 
 struct listnode *add_nest_node(struct listroot *root, char *arg1, char *format, ...)
 {
-//	struct listnode *node;
-	char arg2[BUFFER_SIZE], name[BUFFER_SIZE], *arg;
+	struct listroot *base;
+	struct listnode *node;
+	char *arg, *arg2, name[BUFFER_SIZE];
 	va_list args;
 
+	push_call("add_nest_node(%p,%s,%p,...)",root,arg1,format);
+
 	va_start(args, format);
-	vsprintf(arg2, format, args);
+	vasprintf(&arg2, format, args);
 	va_end(args);
 
 	arg = get_arg_to_brackets(root->ses, arg1, name);
 
+	check_all_events(root->ses, SUB_ARG, 1, 2, "VARIABLE UPDATE %s", name, name, arg2);
+
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_LOCAL))
+	{
+		base = search_nest_base_ses(root->ses, name);
+
+		if (base)
+		{
+			root = base;
+		}
+	}
+
 	while (*arg)
 	{
 		root = update_nest_root(root, name);
@@ -855,11 +1133,8 @@ struct listnode *add_nest_node(struct listroot *root, char *arg1, char *format,
 		}
 	}
 
-/*
-	Adding here, so don't clear the variable.
-
 	node = search_node_list(root, name);
-
+/*
 	if (node && node->root)
 	{
 		free_list(node->root);
@@ -867,16 +1142,35 @@ struct listnode *add_nest_node(struct listroot *root, char *arg1, char *format,
 		node->root = NULL;
 	}
 */
+
 	if (*space_out(arg2) == DEFAULT_OPEN)
 	{
-		update_nest_node(update_nest_root(root, name), arg2);
+		root = update_nest_root(root, name);
 
-		return search_node_list(root, name);
+		update_nest_node(root, arg2);
+
+		node = search_node_list(root, name);
+	}
+	else if (node)
+	{
+		str_cat(&node->arg2, arg2);
 	}
 	else
 	{
-		return update_node_list(root, name, arg2, "", "");
+		node = update_node_list(root, name, arg2, "", "");
+	}
+
+	if (gtd->level->oneshot)
+	{
+		SET_BIT(node->flags, NODE_FLAG_ONESHOT);
 	}
+
+	check_all_events(root->ses, SUB_ARG, 1, 1, "VARIABLE UPDATED %s", name, name, arg2);
+
+	free(arg2);
+
+	pop_call();
+	return node;
 }
 
 

+ 7 - 29
src/net.c

@@ -223,24 +223,13 @@ void write_line_mud(struct session *ses, char *line, int size)
 		return;
 	}
 
-	if (!HAS_BIT(ses->telopts, TELOPT_FLAG_TELNET))
+	if (!HAS_BIT(ses->telopts, TELOPT_FLAG_TELNET) && HAS_BIT(ses->charset, CHARSET_FLAG_ALL_TOUTF8))
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8))
-		{
-			char buf[BUFFER_SIZE];
-
-			size = utf8_to_big5(line, buf);
-
-			strcpy(line, buf);
-		}
-		else if (HAS_BIT(ses->charset, CHARSET_FLAG_KOI8TOUTF8))
-		{
-			char buf[BUFFER_SIZE];
+		char buf[BUFFER_SIZE];
 
-			size = utf8_to_koi8(line, buf);
+		size = utf8_to_all(ses, line, buf);
 
-			strcpy(line, buf);
-		}
+		strcpy(line, buf);
 	}
 
 	check_all_events(ses, SUB_ARG|SUB_SEC, 0, 2, "SEND OUTPUT", line, ntos(size));
@@ -252,7 +241,6 @@ void write_line_mud(struct session *ses, char *line, int size)
 			result = client_write_compressed(ses, line, size);
 		}
 #ifdef HAVE_GNUTLS_H
-
 		else if (ses->ssl)
 		{
 			result = gnutls_record_send(ses->ssl, line, size);
@@ -426,22 +414,12 @@ void readmud(struct session *ses)
 			strcpy(linebuf, line);
 		}
 
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8) || HAS_BIT(ses->charset, CHARSET_FLAG_FANSITOUTF8) || HAS_BIT(ses->charset, CHARSET_FLAG_KOI8TOUTF8))
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_ALL_TOUTF8))
 		{
 			char tempbuf[BUFFER_SIZE];
 
-			if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8))
-			{
-				big5_to_utf8(linebuf, tempbuf);
-			}
-			else if (HAS_BIT(ses->charset, CHARSET_FLAG_FANSITOUTF8))
-			{
-				fansi_to_utf8(linebuf, tempbuf);
-			}
-			else
-			{
-				koi8_to_utf8(linebuf, tempbuf);
-			}
+			all_to_utf8(ses, linebuf, tempbuf);
+
 			process_mud_output(ses, tempbuf, next_line == NULL);
 		}
 		else

+ 226 - 13
src/parse.c

@@ -222,9 +222,74 @@ struct session *parse_command(struct session *ses, char *input)
 	return ses;
 }
 
+char *substitute_speedwalk(struct session *ses, char *input, char *output)
+{
+	char num[NUMBER_SIZE], name[BUFFER_SIZE], *pti, *ptn, *pto;
+	int cnt, max;
+
+	pti = input;
+	pto = output;
+
+	while (*pti && pto - output < INPUT_SIZE)
+	{
+		while (isspace(*pti))
+		{
+			pti++;
+		}
+
+		if (isdigit(*pti))
+		{
+			ptn = num;
+
+			while (isdigit(*pti))
+			{
+				if (ptn - num < 4)
+				{
+					*ptn++ = *pti++;
+				}
+				else
+				{
+					pti++;
+				}
+			}
+			*ptn = 0;
+
+			max = atoi(num);
+		}
+		else
+		{
+			max = 1;
+		}
+
+		pti = get_arg_stop_digits(ses, pti, name, GET_ONE);
+
+		if (*name == 0)
+		{
+			break;
+		}
+
+		for (cnt = 0 ; cnt < max ; cnt++)
+		{
+			if (output != pto)
+			{
+				*pto++ = COMMAND_SEPARATOR;
+			}
+			pto += sprintf(pto, "%s", name);
+		}
+
+		if (*pti == COMMAND_SEPARATOR)
+		{
+			pti++;
+		}
+	}
+	*pto = 0;
+
+	return output;
+}
+	
 int is_speedwalk(struct session *ses, char *input)
 {
-	int flag = FALSE;
+	int digit = 0, flag = FALSE;
 
 	while (*input)
 	{
@@ -236,7 +301,12 @@ int is_speedwalk(struct session *ses, char *input)
 			case 'w':
 			case 'u':
 			case 'd':
-				flag = TRUE;
+				if (digit > 3)
+				{
+					return FALSE;
+				}
+				digit = 0;
+				flag  = TRUE;
 				break;
 
 			case '0':
@@ -249,6 +319,7 @@ int is_speedwalk(struct session *ses, char *input)
 			case '7':
 			case '8':
 			case '9':
+				digit++;
 				flag = FALSE;
 				break;
 
@@ -346,6 +417,28 @@ struct session *parse_tintin_command(struct session *ses, char *input)
 }
 
 
+int cnt_arg_all(struct session *ses, char *string, int flag)
+{
+	char *arg, tmp[BUFFER_SIZE];
+	int cnt;
+
+	arg = string;
+	cnt = 0;
+
+	while (*arg)
+	{
+		cnt++;
+
+		arg = get_arg_in_braces(ses, arg, tmp, flag);
+
+		if (*arg == COMMAND_SEPARATOR)
+		{
+			arg++;
+		}
+	}
+	return cnt;
+}
+
 /*
 	get all arguments - only check for unescaped command separators
 */
@@ -354,7 +447,7 @@ struct session *parse_tintin_command(struct session *ses, char *input)
 char *get_arg_all(struct session *ses, char *string, char *result, int verbatim)
 {
 	char *pto, *pti;
-	int nest = 0;
+	int skip, nest = 0;
 
 	pti = string;
 	pto = result;
@@ -372,13 +465,24 @@ char *get_arg_all(struct session *ses, char *string, char *result, int verbatim)
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
 			continue;
 		}
 
+		skip = find_secure_color_code(pti);
+
+		if (skip)
+		{
+			while (skip--)
+			{
+				*pto++ = *pti++;
+			}
+			continue;
+		}
+
 		if (*pti == '\\' && pti[1] == COMMAND_SEPARATOR)
 		{
 			*pto++ = *pti++;
@@ -419,7 +523,7 @@ char *get_arg_all(struct session *ses, char *string, char *result, int verbatim)
 char *get_arg_in_braces(struct session *ses, char *string, char *result, int flag)
 {
 	char *pti, *pto;
-	int nest = 1;
+	int skip, nest = 1;
 
 	pti = space_out(string);
 	pto = result;
@@ -442,13 +546,24 @@ char *get_arg_in_braces(struct session *ses, char *string, char *result, int fla
 	while (*pti)
 	{
 		
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
 			continue;
 		}
 
+		skip = find_secure_color_code(pti);
+
+		if (skip)
+		{
+			while (skip--)
+			{
+				*pto++ = *pti++;
+			}
+			continue;
+		}
+
 		if (*pti == DEFAULT_OPEN)
 		{
 			nest++;
@@ -498,20 +613,31 @@ char *sub_arg_in_braces(struct session *ses, char *string, char *result, int fla
 char *get_arg_with_spaces(struct session *ses, char *string, char *result, int flag)
 {
 	char *pto, *pti;
-	int nest = 0;
+	int skip, nest = 0;
 
 	pti = space_out(string);
 	pto = result;
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
 			continue;
 		}
 
+		skip = find_secure_color_code(pti);
+
+		if (skip)
+		{
+			while (skip--)
+			{
+				*pto++ = *pti++;
+			}
+			continue;
+		}
+
 		if (*pti == '\\' && pti[1] == COMMAND_SEPARATOR)
 		{
 			*pto++ = *pti++;
@@ -542,20 +668,32 @@ char *get_arg_with_spaces(struct session *ses, char *string, char *result, int f
 char *get_arg_stop_spaces(struct session *ses, char *string, char *result, int flag)
 {
 	char *pto, *pti;
-	int nest = 0;
+	int skip, nest = 0;
 
 	pti = space_out(string);
 	pto = result;
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
 			continue;
 		}
 
+		skip = find_secure_color_code(pti);
+
+		if (skip)
+		{
+			while (skip--)
+			{
+				*pto++ = *pti++;
+			}
+			continue;
+		}
+
+
 		if (*pti == '\\' && pti[1] == COMMAND_SEPARATOR)
 		{
 			*pto++ = *pti++;
@@ -592,6 +730,61 @@ char *get_arg_stop_spaces(struct session *ses, char *string, char *result, int f
 	return pti;
 }
 
+// Get one arg, stop at numbers, used for speedwalks
+
+char *get_arg_stop_digits(struct session *ses, char *string, char *result, int flag)
+{
+	char *pto, *pti;
+	int nest = 0;
+
+	pti = space_out(string);
+	pto = result;
+
+	while (*pti)
+	{
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
+		{
+			*pto++ = *pti++;
+			*pto++ = *pti++;
+			continue;
+		}
+
+		if (*pti == '\\' && pti[1] == COMMAND_SEPARATOR)
+		{
+			*pto++ = *pti++;
+		}
+		else if (*pti == COMMAND_SEPARATOR && nest == 0)
+		{
+			break;
+		}
+		else if (isdigit((int) *pti) && nest == 0)
+		{
+			pti++;
+			break;
+		}
+		else if (*pti == DEFAULT_OPEN)
+		{
+			nest++;
+		}
+		else if (*pti == '[' && HAS_BIT(flag, GET_NST))
+		{
+			nest++;
+		}
+		else if (*pti == DEFAULT_CLOSE)
+		{
+			nest--;
+		}
+		else if (*pti == ']' && HAS_BIT(flag, GET_NST))
+		{
+			nest--;
+		}
+		*pto++ = *pti++;
+	}
+	*pto = '\0';
+
+	return pti;
+}
+
 /*
 	advance ptr to next none-space
 */
@@ -620,7 +813,7 @@ char *get_arg_to_brackets(struct session *ses, char *string, char *result)
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -687,7 +880,7 @@ char *get_arg_at_brackets(struct session *ses, char *string, char *result)
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -744,7 +937,7 @@ char *get_arg_in_brackets(struct session *ses, char *string, char *result)
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -780,6 +973,26 @@ char *get_arg_in_brackets(struct session *ses, char *string, char *result)
 	return pti;
 }
 
+char *get_char(struct session *ses, char *string, char *result)
+{
+	char *pti = string;
+
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
+	{
+		pti += sprintf(result, "%c%c", pti[0], pti[1]);
+	}
+	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
+	{
+		pti += sprintf(result, "%.*s", get_utf8_size(pti), pti);
+	}
+	else
+	{
+		pti += sprintf(result, "%c", pti[0]);
+	}
+
+	return pti;
+}
+
 /*
 	send command to the mud
 */

+ 69 - 73
src/path.c

@@ -81,7 +81,7 @@ DO_PATH(path_create)
 
 	root->update = 0;
 
-	show_message(ses, LIST_PATH, "#PATH CREATE: YOU START MAPPING A NEW PATH.");
+	show_message(ses, LIST_COMMAND, "#PATH CREATE: YOU START MAPPING A NEW PATH.");
 
 	SET_BIT(ses->flags, SES_FLAG_PATHMAPPING);
 }
@@ -97,7 +97,7 @@ DO_PATH(path_destroy)
 
 	DEL_BIT(ses->flags, SES_FLAG_PATHMAPPING);
 
-	show_message(ses, LIST_PATH, "#PATH DESTROY: PATH DESTROYED.");
+	show_message(ses, LIST_COMMAND, "#PATH DESTROY: PATH DESTROYED.");
 }
 
 
@@ -105,13 +105,13 @@ DO_PATH(path_start)
 {
 	if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
 	{
-		show_message(ses, LIST_PATH, "#PATH START: ERROR: YOU ARE ALREADY MAPPING A PATH.");
+		show_message(ses, LIST_COMMAND, "#PATH START: ERROR: YOU ARE ALREADY MAPPING A PATH.");
 	}
 	else
 	{
 		SET_BIT(ses->flags, SES_FLAG_PATHMAPPING);
 
-		show_message(ses, LIST_PATH, "#PATH START: YOU START MAPPING A PATH.");
+		show_message(ses, LIST_COMMAND, "#PATH START: YOU START MAPPING A PATH.");
 	}
 }
 
@@ -122,23 +122,23 @@ DO_PATH(path_stop)
 
 	if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
 	{
-		show_message(ses, LIST_PATH, "#PATH STOP: YOU STOP MAPPING A PATH.");
+		show_message(ses, LIST_COMMAND, "#PATH STOP: YOU STOP MAPPING A PATH.");
 
 		DEL_BIT(ses->flags, SES_FLAG_PATHMAPPING);
 	}
 	else
 	{
-		if (root->list[root->update]->data)
+		if (root->list[root->update]->val64)
 		{
 			for (index = 0 ; index < root->used ; index++)
 			{
-				root->list[index]->data = 0;
+				root->list[index]->val64 = 0;
 			}
-			show_message(ses, LIST_PATH, "#PATH STOP: YOU STOP RUNNING A PATH.");
+			show_message(ses, LIST_COMMAND, "#PATH STOP: YOU STOP RUNNING A PATH.");
 		}
 		else
 		{
-			show_message(ses, LIST_PATH, "#PATH STOP: YOU ARE NOT MAPPING OR RUNNING A PATH.");
+			show_message(ses, LIST_COMMAND, "#PATH STOP: YOU ARE NOT MAPPING OR RUNNING A PATH.");
 		}
 	}
 }
@@ -164,6 +164,8 @@ DO_PATH(path_describe)
 
 	i = x = y = z = 0;
 
+	i = root->update;
+
 	while (i < root->used)
 	{
 		node = search_node_list(ses->list[LIST_PATHDIR], root->list[i++]->arg1);
@@ -188,11 +190,18 @@ DO_PATH(path_describe)
 	{
 		if (z == 0)
 		{
-			tintin_printf2(ses, "The path is %d rooms long and the destination is right where you are.");
+			if (root->used > 2)
+			{
+				tintin_printf2(ses, "The path is %d rooms long and leads back to where you are at.", root->used);
+			}
+			else
+			{
+				tintin_printf2(ses, "The path is %d rooms long and the destination is right where you are at.", root->used);
+			}
 		}
 		else
 		{
-			tintin_printf2(ses, "The path is %d rooms long and the destination lies %d rooms %s you.", root->used, abs(z), z < 0 ? "below" : "above", s);
+			tintin_printf2(ses, "The path is %d rooms long and the destination lies %d rooms %s of you.", root->used, abs(z), z < 0 ? "below" : "above", s);
 		}
 	}
 	else
@@ -222,7 +231,7 @@ DO_PATH(path_map)
 
 	if (root->used == 0)
 	{
-		show_message(ses, LIST_PATH, "#PATH MAP: EMPTY PATH.");
+		show_message(ses, LIST_COMMAND, "#PATH MAP: EMPTY PATH.");
 	}
 	else
 	{
@@ -268,7 +277,7 @@ DO_PATH(path_save)
 	}
 	else if (*arg2 == 0)
 	{
-		show_error(ses, LIST_PATH, "#SYNTAX: #PATH SAVE <BACKWARD|FORWARD|LENGTH|POSITION> <VARIABLE NAME>");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH SAVE <BACKWARD|FORWARD|LENGTH|POSITION> <VARIABLE NAME>");
 	}
 	else if (is_abbrev(arg1, "BACKWARDS"))
 	{
@@ -283,9 +292,9 @@ DO_PATH(path_save)
 				cat_sprintf(result, "%c", COMMAND_SEPARATOR);
 			}
 		}
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", result);
+		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_PATH, "#PATH SAVE: BACKWARD PATH SAVED TO {%s}", arg2);
+		show_message(ses, LIST_COMMAND, "#PATH SAVE: BACKWARD PATH SAVED TO {%s}", arg2);
 	}
 	else if (is_abbrev(arg1, "FORWARDS"))
 	{
@@ -300,29 +309,29 @@ DO_PATH(path_save)
 				cat_sprintf(result, "%c", COMMAND_SEPARATOR);
 			}
 		}
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", result);
+		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_PATH, "#PATH SAVE: FORWARD PATH SAVED TO {%s}", arg2);
+		show_message(ses, LIST_COMMAND, "#PATH SAVE: FORWARD PATH SAVED TO {%s}", arg2);
 	}
 	else if (is_abbrev(arg1, "LENGTH"))
 	{
 		sprintf(result, "%d", root->used);
 
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", result);
+		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_PATH, "#PATH SAVE: PATH LENGTH SAVED TO {%s}", arg2);
+		show_message(ses, LIST_COMMAND, "#PATH SAVE: PATH LENGTH SAVED TO {%s}", arg2);
 	}
 	else if (is_abbrev(arg1, "POSITION"))
 	{
 		sprintf(result, "%d", root->update + 1);
 
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%s", result);
+		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_PATH, "#PATH SAVE: PATH POSITION SAVED TO {%s}", arg2);
+		show_message(ses, LIST_COMMAND, "#PATH SAVE: PATH POSITION SAVED TO {%s}", arg2);
 	}
 	else
 	{
-		show_error(ses, LIST_PATH, "#SYNTAX: #PATH SAVE <BACKWARD|FORWARD|LENGTH|POSITION> <VARIABLE NAME>");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH SAVE <BACKWARD|FORWARD|LENGTH|POSITION> <VARIABLE NAME>");
 	}
 }
 
@@ -335,7 +344,7 @@ DO_PATH(path_load)
 
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 
-	if ((node = search_node_list(ses->list[LIST_VARIABLE], arg1)) == NULL)
+	if ((node = search_nest_node_ses(ses, arg1)) == NULL)
 	{
 		arg = arg1;
 	}
@@ -366,7 +375,7 @@ DO_PATH(path_load)
 			insert_node_list(root, temp, temp, "0", "");
 		}
 	}
-	show_message(ses, LIST_PATH, "#PATH LOAD: PATH WITH %d NODES LOADED.", root->used);
+	show_message(ses, LIST_COMMAND, "#PATH LOAD: PATH WITH %d NODES LOADED.", root->used);
 }
 
 DO_PATH(path_delete)
@@ -375,7 +384,7 @@ DO_PATH(path_delete)
 
 	if (root->used)
 	{
-		show_message(ses, LIST_PATH, "#PATH DELETE: DELETED MOVE {%s}.", root->list[root->used - 1]->arg1);
+		show_message(ses, LIST_COMMAND, "#PATH DELETE: DELETED MOVE {%s}.", root->list[root->used - 1]->arg1);
 
 		delete_index_list(root, root->used - 1);
 
@@ -401,13 +410,13 @@ DO_PATH(path_insert)
 
 	if (*arg1 == 0 && *arg2 == 0)
 	{
-		show_message(ses, LIST_PATH, "#PATH INSERT: ERROR: YOU MUST GIVE A COMMAND TO INSERT");
+		show_message(ses, LIST_COMMAND, "#PATH INSERT: ERROR: YOU MUST GIVE A COMMAND TO INSERT");
 	}
 	else
 	{
 		insert_node_list(root, arg1, arg2, "0", "");
 
-		show_message(ses, LIST_PATH, "#PATH INSERT: FORWARD {%s} BACKWARD {%s}.", arg1, arg2);
+		show_message(ses, LIST_COMMAND, "#PATH INSERT: FORWARD {%s} BACKWARD {%s}.", arg1, arg2);
 
 		if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
 		{
@@ -444,7 +453,7 @@ DO_PATH(path_run)
 
 			for (index = root->update ; index < root->used ; index++)
 			{
-				root->list[index]->data = gtd->utime + total;
+				root->list[index]->val64 = gtd->utime + total;
 
 				total += delay;
 			}
@@ -453,7 +462,7 @@ DO_PATH(path_run)
 		{
 			while (root->update < root->used)
 			{
-				script_driver(ses, LIST_PATH, root->list[root->update++]->arg1);
+				script_driver(ses, LIST_COMMAND, root->list[root->update++]->arg1);
 			}
 		}
 	}
@@ -480,7 +489,7 @@ DO_PATH(path_walk)
 		}
 		else
 		{
-			script_driver(ses, LIST_PATH, root->list[--root->update]->arg2);
+			script_driver(ses, LIST_COMMAND, root->list[--root->update]->arg2);
 
 			if (root->update == 0)
 			{
@@ -496,7 +505,7 @@ DO_PATH(path_walk)
 		}
 		else
 		{
-			script_driver(ses, LIST_PATH, root->list[root->update++]->arg1);
+			script_driver(ses, LIST_COMMAND, root->list[root->update++]->arg1);
 
 			if (root->update == root->used)
 			{
@@ -506,7 +515,7 @@ DO_PATH(path_walk)
 	}
 	else
 	{
-		show_error(ses, LIST_PATH, "#SYNTAX: #PATH WALK {FORWARD|BACKWARD}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH WALK {FORWARD|BACKWARD}.");
 	}
 }
 
@@ -519,7 +528,7 @@ DO_PATH(path_swap)
 
 	if (root->used == 0)
 	{
-		show_error(ses, LIST_PATH, "#PATH SWAP: ERROR: PATH IS EMPTY.");
+		show_error(ses, LIST_COMMAND, "#PATH SWAP: ERROR: PATH IS EMPTY.");
 
 		return;
 	}
@@ -554,7 +563,7 @@ DO_PATH(path_swap)
 		root->list[z]->arg2 = arg;
 	}
 
-	show_message(ses, LIST_PATH, "#PATH SWAP: PATH HAS BEEN SWAPPED.");
+	show_message(ses, LIST_COMMAND, "#PATH SWAP: PATH HAS BEEN SWAPPED.");
 }
 
 
@@ -592,14 +601,8 @@ DO_PATH(path_zip)
 		}
 		else
 		{
-			if (cnt > 1)
-			{
-				cat_sprintf(arg1, "%d%s", cnt, root->list[i]->arg1);
-			}
-			else
-			{
-				cat_sprintf(arg1, "%s", root->list[i]->arg1);
-			}
+			cat_sprintf(arg1, "%d%s", cnt, root->list[i]->arg1);
+
 			cnt = 1;
 		}
 	}
@@ -627,14 +630,7 @@ DO_PATH(path_zip)
 		}
 		else
 		{
-			if (cnt > 1)
-			{
-				cat_sprintf(arg2, "%d%s", cnt, root->list[i]->arg2);
-			}
-			else
-			{
-				cat_sprintf(arg2, "%s", root->list[i]->arg2);
-			}
+			cat_sprintf(arg2, "%d%s", cnt, root->list[i]->arg2);
 			cnt = 1;
 		}
 	}
@@ -645,7 +641,7 @@ DO_PATH(path_zip)
 
 	insert_node_list(root, arg1, arg2, "0", "");
 
-	show_message(ses, LIST_PATH, "#PATH ZIP: THE PATH HAS BEEN ZIPPED TO {%s} {%s}.", arg1, arg2);
+	show_message(ses, LIST_COMMAND, "#PATH ZIP: THE PATH HAS BEEN ZIPPED TO {%s} {%s}.", arg1, arg2);
 }
 
 DO_PATH(path_unzip)
@@ -656,7 +652,7 @@ DO_PATH(path_unzip)
 
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 
-	if ((node = search_node_list(ses->list[LIST_VARIABLE], arg1)) == NULL)
+	if ((node = search_nest_node_ses(ses, arg1)) == NULL)
 	{
 		arg = arg1;
 	}
@@ -727,7 +723,7 @@ DO_PATH(path_unzip)
 			}
 		}
 	}
-	show_message(ses, LIST_PATH, "#PATH UNZIP: PATH WITH %d NODES UNZIPPED.", root->used);
+	show_message(ses, LIST_COMMAND, "#PATH UNZIP: PATH WITH %d NODES UNZIPPED.", root->used);
 }
 
 
@@ -742,25 +738,25 @@ DO_PATH(path_goto)
 	{
 		root->update = root->used;
 
-		show_message(ses, LIST_PATH, "#PATH GOTO: POSITION SET TO {%d}.", root->update + 1);
+		show_message(ses, LIST_COMMAND, "#PATH GOTO: POSITION SET TO {%d}.", root->update + 1);
 	}
 	else if (is_abbrev(arg1, "START"))
 	{
 		root->update = 0;
 
-		show_message(ses, LIST_PATH, "#PATH GOTO: POSITION SET TO %d.", root->update + 1);
+		show_message(ses, LIST_COMMAND, "#PATH GOTO: POSITION SET TO %d.", root->update + 1);
 	}
 	else if (is_math(ses, arg1))
 	{
 		if (get_number(ses, arg1) < 1 || get_number(ses, arg1) > root->used + 1)
 		{
-			show_message(ses, LIST_PATH, "#PATH GOTO: POSITION MUST BE BETWEEN 1 AND %d.", root->used + 1);
+			show_message(ses, LIST_COMMAND, "#PATH GOTO: POSITION MUST BE BETWEEN 1 AND %d.", root->used + 1);
 		}
 		else
 		{
 			root->update = get_number(ses, arg1) - 1;
 
-			show_message(ses, LIST_PATH, "#PATH GOTO: POSITION SET TO %d.", root->update + 1);
+			show_message(ses, LIST_COMMAND, "#PATH GOTO: POSITION SET TO %d.", root->update + 1);
 		}
 	}
 }
@@ -777,33 +773,33 @@ DO_PATH(path_move)
 	{
 		if (root->update == 0)
 		{
-			show_message(ses, LIST_PATH, "#PATH GOTO: ALREADY AT START OF PATH.", root->update);
+			show_message(ses, LIST_COMMAND, "#PATH GOTO: ALREADY AT START OF PATH.", root->update);
 		}
 		else
 		{
 			root->update--;
 
-			show_message(ses, LIST_PATH, "#PATH MOVE: POSITION SET TO %d.", root->update);
+			show_message(ses, LIST_COMMAND, "#PATH MOVE: POSITION SET TO %d.", root->update);
 		}
 	}
 	else if (is_abbrev(arg1, "FORWARD"))
 	{
 		if (root->update == root->used)
 		{
-			show_message(ses, LIST_PATH, "#PATH MOVE: ALREADY AT END OF PATH.", root->update);
+			show_message(ses, LIST_COMMAND, "#PATH MOVE: ALREADY AT END OF PATH.", root->update);
 		}
 		else
 		{
 			root->update++;
 
-			show_message(ses, LIST_PATH, "#PATH MOVE: POSITION SET TO %d.", root->update);
+			show_message(ses, LIST_COMMAND, "#PATH MOVE: POSITION SET TO %d.", root->update);
 		}
 	}
 	else if (is_math(ses, arg1))
 	{
 		root->update = URANGE(0, root->update + get_number(ses, arg1), root->used);
 
-		show_message(ses, LIST_PATH, "#PATH MOVE: POSITION SET TO %d.", root->update + 1);
+		show_message(ses, LIST_COMMAND, "#PATH MOVE: POSITION SET TO %d.", root->update + 1);
 	}
 }
 
@@ -813,28 +809,28 @@ DO_PATH(path_undo)
 
 	if (root->used == 0)
 	{
-		show_message(ses, LIST_PATH, "#PATH UNDO: ERROR: PATH IS EMPTY.");
+		show_message(ses, LIST_COMMAND, "#PATH UNDO: ERROR: PATH IS EMPTY.");
 
 		return;
 	}
 
 	if (root->update != root->used)
 	{
-		show_message(ses, LIST_PATH, "#PATH UNDO: ERROR: YOUR POSITION IS NOT AT END OF PATH.");
+		show_message(ses, LIST_COMMAND, "#PATH UNDO: ERROR: YOUR POSITION IS NOT AT END OF PATH.");
 	
 		return;
 	}
 
 	if (!HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
 	{
-		show_message(ses, LIST_PATH, "#PATH UNDO: ERROR: YOU ARE NOT CURRENTLY MAPPING A PATH.");
+		show_message(ses, LIST_COMMAND, "#PATH UNDO: ERROR: YOU ARE NOT CURRENTLY MAPPING A PATH.");
 
 		return;
 	}
 
 	DEL_BIT(ses->flags, SES_FLAG_PATHMAPPING);
 
-	script_driver(ses, LIST_PATH, root->list[root->used - 1]->arg2);
+	script_driver(ses, LIST_COMMAND, root->list[root->used - 1]->arg2);
 
 	SET_BIT(ses->flags, SES_FLAG_PATHMAPPING);
 
@@ -843,7 +839,7 @@ DO_PATH(path_undo)
 	root->update = root->used;
 
 
-	show_message(ses, LIST_PATH, "#PATH MOVE: POSITION SET TO %d.", root->update);
+	show_message(ses, LIST_COMMAND, "#PATH MOVE: POSITION SET TO %d.", root->update);
 }
 
 void check_append_path(struct session *ses, char *forward, char *backward, int follow)
@@ -933,11 +929,11 @@ DO_COMMAND(do_unpathdir)
 	{
 		if (delete_nest_node(ses->list[LIST_PATHDIR], arg1))
 		{
-			show_message(ses, LIST_VARIABLE, "#OK. {%s} IS NO LONGER A PATHDIR.", arg1);
+			show_message(ses, LIST_PATHDIR, "#OK. {%s} IS NO LONGER A PATHDIR.", arg1);
 		}
 		else
 		{
-			delete_node_with_wild(ses, LIST_VARIABLE, arg1);
+			delete_node_with_wild(ses, LIST_PATHDIR, arg1);
 		}
 		arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 	}
@@ -954,7 +950,7 @@ DO_PATH(path_new)
 
 	if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
 	{
-		show_message(ses, LIST_PATH, "#PATH NEW: YOU ARE ALREADY MAPPING A PATH.");
+		show_message(ses, LIST_COMMAND, "#PATH NEW: YOU ARE ALREADY MAPPING A PATH.");
 	}
 	else
 	{
@@ -962,7 +958,7 @@ DO_PATH(path_new)
 
 		root->update = 0;
 
-		show_message(ses, LIST_PATH, "#PATH NEW: YOU ARE NOW MAPPING A PATH.");
+		show_message(ses, LIST_COMMAND, "#PATH NEW: YOU ARE NOW MAPPING A PATH.");
 
 		SET_BIT(ses->flags, SES_FLAG_PATHMAPPING);
 	}
@@ -972,13 +968,13 @@ DO_PATH(path_end)
 {
 	if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
 	{
-		show_message(ses, LIST_PATH, "#PATH END: YOU ARE NO LONGER MAPPING A PATH.");
+		show_message(ses, LIST_COMMAND, "#PATH END: YOU ARE NO LONGER MAPPING A PATH.");
 
 		DEL_BIT(ses->flags, SES_FLAG_PATHMAPPING);
 	}
 	else
 	{
-		show_message(ses, LIST_PATH, "#PATH: YOU ARE NOT MAPPING A PATH.");
+		show_message(ses, LIST_COMMAND, "#PATH: YOU ARE NOT MAPPING A PATH.");
 	}
 }
 	

+ 3 - 4
src/port.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*               (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                  *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                     coded by Igor van den Hoven 2017                        *
+*                      coded by Igor van den Hoven 2017                       *
 ******************************************************************************/
 
 #include "tintin.h"

+ 252 - 33
src/regex.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
@@ -87,15 +86,14 @@ DO_COMMAND(do_regexp)
 	return ses;
 }
 
-int regexp_compare(pcre *nodepcre, char *str, char *exp, int option, int flag)
+int regexp_compare(struct session *ses, pcre *nodepcre, char *str, char *exp, int option, int flag)
 {
 	pcre *regex;
-	const char *error;
 	int i, j, matches, match[303];
 
 	if (nodepcre == NULL)
 	{
-		regex = pcre_compile(exp, option, &error, &i, NULL);
+		regex = regexp_compile(ses, exp, option);
 	}
 	else
 	{
@@ -163,11 +161,16 @@ int regexp_compare(pcre *nodepcre, char *str, char *exp, int option, int flag)
 	return TRUE;
 }
 
-pcre *regexp_compile(char *exp, int option)
+pcre *regexp_compile(struct session *ses, char *exp, int option)
 {
 	const char *error;
 	int i;
-
+/*
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
+	{
+		option |= PCRE_UTF8|PCRE_NO_UTF8_CHECK;
+	}
+*/
 	return pcre_compile(exp, option, &error, &i, NULL);
 }
 
@@ -213,6 +216,103 @@ int check_one_regexp(struct session *ses, struct listnode *node, char *line, cha
 	Keep synched with tintin_regexp and tintin_regexp_compile
 */
 
+int get_regex_range(char *in, char *out, int *var, int *arg)
+{
+	char *pti, *pto, *ptr, range[BUFFER_SIZE];
+
+	pto = out;
+	pti = in;
+	ptr = range;
+
+	while (*pti)
+	{
+		switch (*pti)
+		{
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				*ptr++ = *pti++;
+				continue;
+
+			case '.':
+				if (pti[1] != '.')
+				{
+					goto end;
+				}
+				if (ptr == range)
+				{
+					*ptr++ = '0';
+				}
+				*ptr++ = ',';
+				pti += 2;
+				continue;
+
+			case 'a':
+				pto += sprintf(pto, "(.");
+				break;
+			case 'A':
+				pto += sprintf(pto, "(\\n");
+				break;
+			case 'd':
+				pto += sprintf(pto, "([0-9]");
+				break;
+			case 'D':
+				pto += sprintf(pto, "([^0-9]");
+				break;
+			case 'p':
+				pto += sprintf(pto, "([\\x20-\\xfe]");
+				break;
+			case 'P':
+				pto += sprintf(pto, "([^\\x20-\\xfe]");
+				break;
+			case 's':
+				pto += sprintf(pto, "(\\s");
+				break;
+			case 'S':
+				pto += sprintf(pto, "(\\S");
+				break;
+			case 'u':
+				pto += sprintf(pto, "((?:[\\xC0-\\xFE][\\x80-\\xC0]{1,3})");
+				break;
+			case 'U':
+				pto += sprintf(pto, "([\\x00-\\x7F\\xFF]");
+				break;
+			case 'w':
+				pto += sprintf(pto, "([a-zA-Z]");
+				break;
+			case 'W':
+				pto += sprintf(pto, "([^a-zA-Z]");
+				break;
+
+			default:
+				goto end;
+		}
+		*ptr = 0;
+		pti++;
+
+		pto += sprintf(pto, "{%s}%s", range, *pti ? "?)" : ")");
+
+		return pti - in;
+	}
+	end:
+
+	if (var)
+	{
+		gtd->args[next_arg(*var)] = next_arg(*arg);
+	}
+	strcpy(out, in[2] == 0 ? "(.+)" : "(.+?)");
+
+	return 0;
+}
+
+
 int tintin_regexp_check(struct session *ses, char *exp)
 {
 	if (*exp == '^')
@@ -223,7 +323,7 @@ int tintin_regexp_check(struct session *ses, char *exp)
 	while (*exp)
 	{
 
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *exp & 128 && exp[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, exp))
 		{
 			exp += 2;
 			continue;
@@ -255,12 +355,19 @@ int tintin_regexp_check(struct session *ses, char *exp)
 					case '7':
 					case '8':
 					case '9':
+
+					case 'a':
+					case 'A':
 					case 'd':
 					case 'D':
 					case 'i':
 					case 'I':
+					case 'p':
+					case 'P':
 					case 's':
 					case 'S':
+					case 'u':
+					case 'U':
 					case 'w':
 					case 'W':
 					case '?':
@@ -273,10 +380,16 @@ int tintin_regexp_check(struct session *ses, char *exp)
 					case '!':
 						switch (exp[2])
 						{
+							case 'a':
+							case 'A':
 							case 'd':
 							case 'D':
+							case 'p':
+							case 'P':
 							case 's':
 							case 'S':
+							case 'u':
+							case 'U':
 							case 'w':
 							case 'W':
 							case '?':
@@ -294,7 +407,7 @@ int tintin_regexp_check(struct session *ses, char *exp)
 	}
 	return FALSE;
 }
-				
+
 int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int option, int flag)
 {
 	char out[BUFFER_SIZE], *pti, *pto;
@@ -310,7 +423,7 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 
@@ -403,6 +516,20 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 						pto += strlen(pto);
 						break;
 
+					case 'a':
+						gtd->args[next_arg(var)] = next_arg(arg);
+						pti += 2;
+						strcpy(pto, *pti == 0 ? "([^\\n]*)" : "([^\\n]*?)");
+						pto += strlen(pto);
+						break;
+
+					case 'A':
+						gtd->args[next_arg(var)] = next_arg(arg);
+						pti += 2;
+						strcpy(pto, *pti == 0 ? "(\\n*)" : "(\\n*?)");
+						pto += strlen(pto);
+						break;
+
 					case 'd':
 						gtd->args[next_arg(var)] = next_arg(arg);
 						pti += 2;
@@ -429,6 +556,18 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 						pto += strlen(pto);
 						break;
 
+					case 'p':
+						gtd->args[next_arg(var)] = next_arg(arg);
+						pti += 2;
+						pto += sprintf(pto, "%s", *pti == 0 ? "([\\x20-\\xfe]*)" : "([\\x20-\\xfe]*?)");
+						break;
+
+					case 'P':
+						gtd->args[next_arg(var)] = next_arg(arg);
+						pti += 2;
+						pto += sprintf(pto, "%s", *pti == 0 ? "([^\\x20-\\xfe]*)" : "([^\\x20-\\xfe]*?)");
+						break;
+
 					case 's':
 						gtd->args[next_arg(var)] = next_arg(arg);
 						pti += 2;
@@ -443,24 +582,31 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 						pto += strlen(pto);
 						break;
 
-					case 'w':
+					case 'u':
 						gtd->args[next_arg(var)] = next_arg(arg);
 						pti += 2;
-						strcpy(pto, *pti == 0 ? "([a-zA-Z]*)" : "([a-zA-Z]*?)");
+						strcpy(pto, *pti == 0 ? "((?:[\\x00-\\x7F|\\xC0-\\xFE][\\x80-\\xC0]{1,3})*)" : "((?:[\\xC0-\\xFE][\\x80-\\xC0]{1,3})*?)");
 						pto += strlen(pto);
 						break;
 
-					case 'W':
+					case 'U':
 						gtd->args[next_arg(var)] = next_arg(arg);
 						pti += 2;
-						strcpy(pto, *pti == 0 ? "([^a-zA-Z]*)" : "([^a-zA-Z]*?)");
+						strcpy(pto, *pti == 0 ? "(^[\xFF]*)" : "([\\x00-\\x7F\\xFF]*?)");
 						pto += strlen(pto);
 						break;
 
-					case '?':
+					case 'w':
 						gtd->args[next_arg(var)] = next_arg(arg);
 						pti += 2;
-						strcpy(pto, *pti == 0 ? "(.?)" : "(.?" "?)");
+						strcpy(pto, *pti == 0 ? "([a-zA-Z]*)" : "([a-zA-Z]*?)");
+						pto += strlen(pto);
+						break;
+
+					case 'W':
+						gtd->args[next_arg(var)] = next_arg(arg);
+						pti += 2;
+						strcpy(pto, *pti == 0 ? "([^a-zA-Z]*)" : "([^a-zA-Z]*?)");
 						pto += strlen(pto);
 						break;
 
@@ -472,12 +618,15 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 						break;
 
 					case '+':
-						gtd->args[next_arg(var)] = next_arg(arg);
-						pti += 2;
-						strcpy(pto, *pti == 0 ? "(.+)" : "(.+?)");
+						pti += 2 + get_regex_range(&pti[2], pto, &var, &arg);
 						pto += strlen(pto);
 						break;
 
+					case '%':
+						*pto++ = *pti++;
+						pti++;
+						break;
+
 					case '.':
 						gtd->args[next_arg(var)] = next_arg(arg);
 						pti += 2;
@@ -485,14 +634,30 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 						pto += strlen(pto);
 						break;
 
-					case '%':
-						*pto++ = *pti++;
-						pti++;
+					case '?':
+						gtd->args[next_arg(var)] = next_arg(arg);
+						pti += 2;
+						strcpy(pto, *pti == 0 ? "(.?)" : "(.?" "?)");
+						pto += strlen(pto);
 						break;
 
 					case '!':
 						switch (pti[2])
 						{
+							case 'a':
+								gtd->args[next_arg(var)] = next_arg(arg);
+								pti += 2;
+								strcpy(pto, *pti == 0 ? "[^\\n]*" : "[^\\n]*?");
+								pto += strlen(pto);
+								break;
+
+							case 'A':
+								gtd->args[next_arg(var)] = next_arg(arg);
+								pti += 2;
+								strcpy(pto, *pti == 0 ? "\\n*" : "\\n*?");
+								pto += strlen(pto);
+								break;
+
 							case 'd':
 								pti += 3;
 								strcpy(pto, *pti == 0 ? "[0-9]*" : "[0-9]*?");
@@ -505,6 +670,16 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 									pto += strlen(pto);
 								break;
 
+							case 'p':
+								pti += 3;
+								pto += sprintf(pto, "%s", *pti == 0 ? "[\\x20-\\xfe]*" : "[\\x20-\\xfe]*?");
+								break;
+
+							case 'P':
+								pti += 3;
+								pto += sprintf(pto, "%s", *pti == 0 ? "[^\\x20-\\xfe]*" : "[^\\x20-\\xfe]*?");
+								break;
+
 							case 's':
 								pti += 3;
 								strcpy(pto, *pti == 0 ? "\\s*" : "\\s*?");
@@ -517,6 +692,20 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 								pto += strlen(pto);
 								break;
 
+							case 'u':
+								gtd->args[next_arg(var)] = next_arg(arg);
+								pti += 3;
+								strcpy(pto, *pti == 0 ? "(?:[\\xC0-\\xFE][\\x80-\\xC0]{1,3})*" : "(?:[\\xC0-\\xFE][\\x80-\\xC0]{1,3})*?");
+								pto += strlen(pto);
+								break;
+
+							case 'U':
+								gtd->args[next_arg(var)] = next_arg(arg);
+								pti += 3;
+								strcpy(pto, *pti == 0 ? "[\\x00-\\x7F\\xFF]*" : "[\\x00-\\x7F\\xFF]*?");
+								pto += strlen(pto);
+								break;
+
 							case 'w':
 								pti += 3;
 								strcpy(pto, *pti == 0 ? "[a-zA-Z]*" : "[a-zA-Z]*?");
@@ -542,8 +731,7 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 								break;
 
 							case '+':
-								pti += 3;
-								strcpy(pto, *pti == 0 ? ".+" : ".+?");
+								pti += 3 + get_regex_range(&pti[3], pto, NULL, NULL);
 								pto += strlen(pto);
 								break;
 
@@ -577,7 +765,7 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 	}
 	*pto = 0;
 
-	return regexp_compare(nodepcre, str, out, option, flag + fix);
+	return regexp_compare(ses, nodepcre, str, out, option, flag + fix);
 }
 
 pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *exp, int option)
@@ -599,7 +787,7 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 
@@ -623,6 +811,7 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 			*pto++ = *pti++;
 			continue;
 		}
+
 		switch (pti[0])
 		{
 			case '\\':
@@ -740,6 +929,16 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 						pto += strlen(pto);
 						break;
 
+					case 'p':
+						pti += 2;
+						pto += sprintf(pto, "%s", *pti == 0 ? "([\\x20-\\xfe]*)" : "([\\x20-\\xfe]*?)");
+						break;
+
+					case 'P':
+						pti += 2;
+						pto += sprintf(pto, "%s", *pti == 0 ? "([^\\x20-\\xfe]*)" : "([^\\x20-\\xfe]*?)");
+						break;
+						
 					case 's':
 						pti += 2;
 						strcpy(pto, *pti == 0 ? "(\\s*)" : "(\\s*?)");
@@ -752,6 +951,18 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 						pto += strlen(pto);
 						break;
 
+					case 'u':
+						pti += 2;
+						strcpy(pto, *pti == 0 ? "((?:[\\xC0-\\xFE][\\x80-\\xC0]{1,3})*)" : "((?:[\\xC0-\\xFE][\\x80-\\xC0]{1,3})*?)");
+						pto += strlen(pto);
+						break;
+
+					case 'U':
+						pti += 2;
+						strcpy(pto, *pti == 0 ? "([\\x00-\\x7F\\xFF]*)" : "([\\x00-\\x7F\\xFF]*?)");
+						pto += strlen(pto);
+						break;
+
 					case 'w':
 						pti += 2;
 						strcpy(pto, *pti == 0 ? "([a-zA-Z]*)" : "([a-zA-Z]*?)");
@@ -777,8 +988,7 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 						break;
 
 					case '+':
-						pti += 2;
-						strcpy(pto, *pti == 0 ? "(.+)" : "(.+?)");
+						pti += 2 + get_regex_range(&pti[2], pto, NULL, NULL);
 						pto += strlen(pto);
 						break;
 
@@ -805,7 +1015,17 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 							case 'D':
 								pti += 3;
 								strcpy(pto, *pti == 0 ? "[^0-9]*" : "[^0-9]*?");
-									pto += strlen(pto);
+								pto += strlen(pto);
+								break;
+
+							case 'p':
+								pti += 3;
+								pto += sprintf(pto, "%s", *pti == 0 ? "[\\x21-\\x7E]*" : "[\\x21-\\x7E]?*");
+								break;
+
+							case 'P':
+								pti += 3;
+								pto += sprintf(pto, "%s", *pti == 0 ? "[^\\x20-\\xfe]*" : "[^\\x20-\\xfe]*?");
 								break;
 
 							case 's':
@@ -845,8 +1065,7 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 								break;
 
 							case '+':
-								pti += 3;
-								strcpy(pto, *pti == 0 ? ".+" : ".+?");
+								pti += 3 + get_regex_range(&pti[3], pto, NULL, NULL);
 								pto += strlen(pto);
 								break;
 
@@ -891,7 +1110,7 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 	}
 	*pto = 0;
 
-	return regexp_compile(out, option);
+	return regexp_compile(ses, out, option);
 }
 
 void tintin_macro_compile(char *input, char *output)

+ 4 - 12
src/scan.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*               (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                  *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                     coded by Igor van den Hoven 2005                        *
+*                      coded by Igor van den Hoven 2005                       *
 ******************************************************************************/
 
 #include "tintin.h"
@@ -52,7 +51,7 @@ char *get_arg_in_quotes(struct session *ses, char *string, char *result, int fla
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -230,13 +229,6 @@ char *get_arg_stop_tabs(struct session *ses, char *string, char *result, int fla
 
 	while (*pti)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
-		{
-			*pto++ = *pti++;
-			*pto++ = *pti++;
-			continue;
-		}
-
 		if (*pti == '\t')
 		{
 			pti++;

+ 84 - 41
src/screen.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*              (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                   *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                     coded by Igor van den Hoven 2019                        *
+*                      coded by Igor van den Hoven 2019                       *
 ******************************************************************************/
 
 #include "tintin.h"
@@ -101,10 +100,12 @@ DO_SCREEN(screen_cursor)
 {
 	if (is_abbrev(arg1, "HIDE"))
 	{
+		SET_BIT(gtd->flags, TINTIN_FLAG_HIDDENCURSOR);
 		screen_csi("?", "25", "", "", "l");
 	}
 	else if (is_abbrev(arg1, "SHOW"))
 	{
+		DEL_BIT(gtd->flags, TINTIN_FLAG_HIDDENCURSOR);
 		screen_csi("?", "25", "", "", "h");
 	}
 	else if (is_abbrev(arg1, "BAR"))
@@ -202,13 +203,16 @@ DO_SCREEN(screen_clear)
 
 DO_SCREEN(screen_fill)
 {
+	char buf[BUFFER_SIZE];
+
 	if (is_abbrev(arg1, "DEFAULT"))
 	{
-		char buf[BUFFER_SIZE];
-
-		sprintf(buf, "CLEAR ALL");
+		if (ses->split->sav_top_col || ses->split->sav_bot_col)
+		{
+			sprintf(buf, "CLEAR SPLIT");
 
-		do_screen(ses, buf);
+			do_screen(ses, buf);
+		}
 
 		if (ses->split->sav_top_row > 0)
 		{
@@ -328,83 +332,83 @@ DO_SCREEN(screen_get)
 
 	if (is_abbrev(arg1, "FOCUS"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->focus);
+		set_nest_node_ses(ses, arg2, "%d", gtd->screen->focus);
 	}
 	else if (is_abbrev(arg1, "CHAR_HEIGHT"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->char_height);
+		set_nest_node_ses(ses, arg2, "%d", gtd->screen->char_height);
 	}
 	else if (is_abbrev(arg1, "CHAR_WIDTH"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->char_width);
+		set_nest_node_ses(ses, arg2, "%d", gtd->screen->char_width);
 	}
 	else if (is_abbrev(arg1, "COLS"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->cols);
+		set_nest_node_ses(ses, arg2, "%d", gtd->screen->cols);
 	}
 	else if (is_abbrev(arg1, "CUR_COL"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->cur_col);
+		set_nest_node_ses(ses, arg2, "%d", ses->cur_col);
 	}
 	else if (is_abbrev(arg1, "CUR_ROW"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->cur_row);
+		set_nest_node_ses(ses, arg2, "%d", ses->cur_row);
 	}
 	else if (is_abbrev(arg1, "HEIGHT"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->height);
+		set_nest_node_ses(ses, arg2, "%d", gtd->screen->height);
 	}
 	else if (is_abbrev(arg1, "WIDTH"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->width);
+		set_nest_node_ses(ses, arg2, "%d", gtd->screen->width);
 	}
 
 	else if (is_abbrev(arg1, "ROWS"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", gtd->screen->rows);
+		set_nest_node_ses(ses, arg2, "%d", gtd->screen->rows);
 	}
 
 	else if (is_abbrev(arg1, "SPLIT_TOP_BAR"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->sav_top_row);
+		set_nest_node_ses(ses, arg2, "%d", ses->split->sav_top_row);
 	}
 	else if (is_abbrev(arg1, "SPLIT_LEFT_BAR"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->sav_top_col);
+		set_nest_node_ses(ses, arg2, "%d", ses->split->sav_top_col);
 	}
 	else if (is_abbrev(arg1, "SPLIT_BOT_BAR"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->sav_bot_row);
+		set_nest_node_ses(ses, arg2, "%d", ses->split->sav_bot_row);
 	}
 	else if (is_abbrev(arg1, "SPLIT_RIGHT_BAR"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->sav_bot_col);
+		set_nest_node_ses(ses, arg2, "%d", ses->split->sav_bot_col);
 	}
 
 	else if (is_abbrev(arg1, "SCROLL_ROWS"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->bot_row - ses->split->top_row);
+		set_nest_node_ses(ses, arg2, "%d", ses->split->bot_row - ses->split->top_row);
 	}
 	else if (is_abbrev(arg1, "SCROLL_COLS"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->wrap);
+		set_nest_node_ses(ses, arg2, "%d", ses->wrap);
 	}
 
 	else if (is_abbrev(arg1, "SCROLL_TOP_ROW"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->top_row);
+		set_nest_node_ses(ses, arg2, "%d", ses->split->top_row);
 	}
 	else if (is_abbrev(arg1, "SCROLL_TOP_COL"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->top_col);
+		set_nest_node_ses(ses, arg2, "%d", ses->split->top_col);
 	}
 	else if (is_abbrev(arg1, "SCROLL_BOT_ROW"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->bot_row);
+		set_nest_node_ses(ses, arg2, "%d", ses->split->bot_row);
 	}
 	else if (is_abbrev(arg1, "SCROLL_BOT_COL"))
 	{
-		set_nest_node(ses->list[LIST_VARIABLE], arg2, "%d", ses->split->bot_col);
+		set_nest_node_ses(ses, arg2, "%d", ses->split->bot_col);
 	}
 	else
 	{
@@ -723,7 +727,8 @@ DO_SCREEN(screen_raise)
 		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN DESKTOP DIMENSIONS}");
 		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN DIMENSIONS}");
 		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN MINIMIZED}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN POSITION}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN MOUSE LOCATION}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN LOCATION}");
 		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN RESIZE}");
 		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RAISE} {SCREEN SIZE}");
 
@@ -747,7 +752,11 @@ DO_SCREEN(screen_raise)
 	{
 		screen_csit(ses, "11", "", "");
 	}
-	else if (is_abbrev(arg1, "POSITION"))
+	else if (is_abbrev(arg1, "MOUSE LOCATION"))
+	{
+		print_stdout("%s", "\e[2;1'z\e['|");
+	}
+	else if (is_abbrev(arg1, "LOCATION") || is_abbrev(arg1, "POSITION"))
 	{
 		screen_csit(ses, "13", "", "");
 	}
@@ -823,7 +832,6 @@ int get_col_index(struct session *ses, char *arg)
 
 
 
-	
 void csit_handler(int ind, int var1, int var2)
 {
 //	tintin_printf2(gtd->ses, "csit_handler(%d,%d,%d)",ind,var1,var2);
@@ -843,11 +851,11 @@ void csit_handler(int ind, int var1, int var2)
 			break;
 
 		case 3:
-			gtd->screen->pos_height = var2;
-			gtd->screen->pos_width  = var1;
-			check_all_events(NULL, SUB_ARG, 0, 2, "SCREEN POSITION", ntos(var2), ntos(var1)); // swap x y
-			msdp_update_all("SCREEN_POSITION_HEIGHT", "%d", gtd->screen->pos_height);
-			msdp_update_all("SCREEN_POSITION_WIDTH", "%d", gtd->screen->pos_width);
+			gtd->screen->pos_height = UMAX(0, var2);
+			gtd->screen->pos_width  = UMAX(0, var1);
+			check_all_events(NULL, SUB_ARG, 0, 4, "SCREEN LOCATION", ntos(var2 / gtd->screen->char_height), ntos(var1 / gtd->screen->char_width), ntos(var2), ntos(var1)); // swap x y
+			msdp_update_all("SCREEN_LOCATION_HEIGHT", "%d", gtd->screen->pos_height);
+			msdp_update_all("SCREEN_LOCATION_WIDTH", "%d", gtd->screen->pos_width);
 			break;
 
 		case 4:
@@ -893,6 +901,41 @@ void csit_handler(int ind, int var1, int var2)
 	}
 }
 
+void rqlp_handler(int event, int button, int height, int width)
+{
+	int row, col, rev_row, rev_col, char_height, char_width, rev_char_height, rev_char_width, debug, info, grid_val;
+	char *grid[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};
+
+	row = 1 + height / UMAX(1, gtd->screen->char_height);
+	col = 1 + width / UMAX(1, gtd->screen->char_width);
+
+	char_height = 1 + height % UMAX(1, gtd->screen->char_height);
+	char_width  = 1 + width % UMAX(1, gtd->screen->char_width);
+
+	rev_row = -1 - (gtd->screen->rows - row);
+	rev_col = -1 - (gtd->screen->cols - col);
+
+	rev_char_height = -1 - (gtd->screen->char_height - char_height);
+	rev_char_width  = -1 - (gtd->screen->char_width - char_width);
+
+	grid_val = URANGE(0, char_height * 3 / gtd->screen->char_height, 2) * 3 + URANGE(0, char_width * 3 / gtd->screen->char_width, 2);
+
+	debug = HAS_BIT(gtd->ses->flags, SES_FLAG_MOUSEDEBUG) ? 1 : 0;
+	info  = HAS_BIT(gtd->ses->flags, SES_FLAG_MOUSEINFO) ? 1 : 0;
+
+	gtd->level->debug += debug;
+	gtd->level->info  += info;
+
+	check_all_events(gtd->ses, SUB_ARG, 0, 9, "SCREEN MOUSE LOCATION", ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), ntos(char_height), ntos(char_width), ntos(rev_char_height), ntos(rev_char_width), grid[grid_val]);
+
+	map_mouse_handler(gtd->ses, NULL, NULL, col, row, char_height, char_width);
+
+	gtd->level->debug -= debug;
+	gtd->level->info  -= info;
+
+//	tintin_printf2(gtd->ses, "rqlp_handler(%d,%d,%d,%d) (%d,%d,%d,%d) (%d,%d,%d,%d)", event,button,height,width, row,col,rev_row,rev_col, char_height,char_width,rev_char_height,rev_char_width);
+}
+
 void screen_osc(char *arg1, char *arg2)
 {
 	print_stdout("\e]%s;%s\a", arg1, arg2);
@@ -1035,12 +1078,12 @@ void init_screen(int rows, int cols, int height, int width)
 {
 	int cnt;
 
-	gtd->screen->rows        = rows;
-	gtd->screen->cols        = cols;
-	gtd->screen->height      = height;
-	gtd->screen->width       = width;
-	gtd->screen->char_height = gtd->screen->height / gtd->screen->rows;
-	gtd->screen->char_width  = gtd->screen->width / gtd->screen->cols;
+	gtd->screen->rows        = UMAX(1, rows);
+	gtd->screen->cols        = UMAX(1, cols);
+	gtd->screen->height      = UMAX(1, height);
+	gtd->screen->width       = UMAX(1, width);
+	gtd->screen->char_height = UMAX(1, gtd->screen->height / gtd->screen->rows);
+	gtd->screen->char_width  = UMAX(1, gtd->screen->width / gtd->screen->cols);
 
 	gtd->screen->focus  = 1;
 

+ 9 - 9
src/session.c

@@ -414,7 +414,9 @@ struct session *new_session(struct session *ses, char *name, char *arg, int desc
 		}
 	}
 
-	newses->split  = calloc(1, sizeof(struct split_data));
+	newses->event_flags = gts->event_flags;
+
+	newses->split   = calloc(1, sizeof(struct split_data));
 
 	memcpy(newses->split, gts->split, sizeof(struct split_data));
 
@@ -698,22 +700,19 @@ void dispose_session(struct session *ses)
 		fclose(ses->logline_file);
 	}
 
-	for (index = 0 ; index < LIST_MAX ; index++)
-	{
-		free_list(ses->list[index]);
-	}
-
 	if (ses->map)
 	{
 		delete_map(ses);
 	}
 
-	if (ses->mccp2)
+	for (index = 0 ; index < LIST_MAX ; index++)
 	{
-		inflateEnd(ses->mccp2);
-		free(ses->mccp2);
+		free_list(ses->list[index]);
 	}
 
+	client_end_mccp2(ses);
+	client_end_mccp3(ses);
+
 	init_buffer(ses, 0);
 
 	free(ses->name);
@@ -725,6 +724,7 @@ void dispose_session(struct session *ses)
 	free(ses->cmd_color);
 	free(ses->lognext_name);
 	free(ses->logline_name);
+	free(ses->split);
 
 	free(ses);
 

+ 19 - 2
src/show.c

@@ -441,7 +441,10 @@ void tintin_puts(struct session *ses, char *string)
 
 		gtd->level->ignore++;
 
-		show_info(ses, LIST_GAG, "#INFO GAG {%s}", string);
+		if (HAS_BIT(ses->list[LIST_GAG]->flags, LIST_FLAG_INFO))
+		{
+			show_info(ses, LIST_GAG, "#INFO GAG {%s}", string);
+		}
 
 		gtd->level->ignore--;
 	}
@@ -479,7 +482,7 @@ void tintin_puts2(struct session *ses, char *string)
 
 void tintin_puts3(struct session *ses, char *string)
 {
-	char *output;
+	char *output, temp[STRING_SIZE];
 
 	push_call("tintin_puts3(%p,%p)",ses,string);
 
@@ -488,6 +491,20 @@ void tintin_puts3(struct session *ses, char *string)
 		ses = gtd->ses;
 	}
 
+	if (ses->line_capturefile)
+	{
+		sprintf(temp, "{%d}{%s}", ses->line_captureindex++, string);
+
+		if (ses->line_captureindex == 1)
+		{
+			set_nest_node_ses(ses, ses->line_capturefile, "%s", temp);
+		}
+		else
+		{
+			add_nest_node_ses(ses, ses->line_capturefile, "%s", temp);
+		}
+	}
+
 	if (!HAS_BIT(gtd->ses->flags, SES_FLAG_VERBOSE) && gtd->level->quiet && gtd->level->verbose == 0)
 	{
 		pop_call();

+ 18 - 17
src/split.c

@@ -107,6 +107,8 @@ void init_split(struct session *ses, int top_row, int top_col, int bot_row, int
 {
 	push_call("init_split(%p,%d,%d,%d,%d)",ses,top_row,top_col,bot_row,bot_col);
 
+	SET_BIT(ses->scroll->flags, SCROLL_FLAG_RESIZE);
+
 	if (!HAS_BIT(ses->flags, SES_FLAG_SPLIT))
 	{
 		ses->split->top_row = 1;
@@ -117,8 +119,6 @@ void init_split(struct session *ses, int top_row, int top_col, int bot_row, int
 
 		init_pos(ses, gtd->screen->rows, 1);
 
-		SET_BIT(ses->scroll->flags, SCROLL_FLAG_RESIZE);
-
 		if (ses->map && HAS_BIT(ses->map->flags, MAP_FLAG_VTMAP))
 		{
 			SET_BIT(ses->flags, SES_FLAG_UPDATEVTMAP);
@@ -156,8 +156,6 @@ void init_split(struct session *ses, int top_row, int top_col, int bot_row, int
 
 	init_pos(ses, gtd->screen->rows, 1);
 
-	SET_BIT(ses->scroll->flags, SCROLL_FLAG_RESIZE);
-
 	if (HAS_BIT(ses->telopts, TELOPT_FLAG_NAWS))
 	{
 		client_send_sb_naws(ses, 0, NULL);
@@ -172,16 +170,19 @@ void init_split(struct session *ses, int top_row, int top_col, int bot_row, int
 	{
 		if (gtd->level->quiet == 0)
 		{
-			if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
+//			if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
 			{
 				do_screen(ses, "FILL DEFAULT");
 			}
-			else
+/*			else
 			{
+				fill_split_region(ses, "-");
+
 				erase_scroll_region(ses);
-				buffer_end(ses, "");
 				fill_split_region(ses, "-");
-			}
+				buffer_end(ses, "");
+
+			}*/
 		}
 	}
 	check_all_events(ses, SUB_ARG, 0, 4, "SCREEN SPLIT", ntos(ses->split->top_row), ntos(ses->split->top_col), ntos(ses->split->bot_row), ntos(ses->split->bot_col));
@@ -240,7 +241,7 @@ void dirty_screen(struct session *ses)
 
 void split_show(struct session *ses, char *prompt, int row, int col)
 {
-	char temp[BUFFER_SIZE];
+	char buf1[BUFFER_SIZE];
 	int original_row, original_col, len, clear;
 
 	original_row = row;
@@ -252,7 +253,7 @@ void split_show(struct session *ses, char *prompt, int row, int col)
 	}
 	else if (row == 0)
 	{
-		row = gtd->screen->rows - 2;
+		row = gtd->screen->rows - 1;
 	}
 
 	clear = 0;
@@ -281,7 +282,7 @@ void split_show(struct session *ses, char *prompt, int row, int col)
 		return;
 	}
 
-	if (row >= ses->split->top_row && row <= ses->split->bot_row)
+	if (row > ses->split->top_row && row <= ses->split->bot_row)
 	{
 		if (col >= ses->split->top_col && col <= ses->split->bot_col)
 		{
@@ -299,17 +300,17 @@ void split_show(struct session *ses, char *prompt, int row, int col)
 
 	if (len == 0)
 	{
-		sprintf(temp, "%.*s", gtd->screen->cols + 4, "\e[0m--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------");
+		sprintf(buf1, "%.*s", gtd->screen->cols + 4, "\e[0m--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------");
 	}
 	else if (col - 1 + len <= gtd->screen->cols)
 	{
-		sprintf(temp, "%s", prompt);
+		sprintf(buf1, "%s", prompt);
 	}
 	else
 	{
 		show_debug(ses, LIST_PROMPT, "#DEBUG PROMPT {%s}", prompt);
 
-		sprintf(temp, "#PROMPT SIZE (%d) LONGER THAN ROW SIZE (%d)", len, gtd->screen->cols);
+		sprintf(buf1, "#PROMPT SIZE (%d) LONGER THAN ROW SIZE (%d)", len, gtd->screen->cols);
 	}
 
 	save_pos(ses);
@@ -320,7 +321,7 @@ void split_show(struct session *ses, char *prompt, int row, int col)
 
 		goto_pos(ses, row, col);
 
-		print_stdout("%s%s", temp, gtd->input_buf);
+		print_stdout("%s%s", buf1, gtd->input_buf);
 
 		// bit of a hack
 
@@ -330,10 +331,10 @@ void split_show(struct session *ses, char *prompt, int row, int col)
 	{
 		goto_pos(ses, row, col);
 
-		print_stdout("%s%s", clear ? "\e[2K" : "", temp);
+		print_stdout("%s%s", clear ? "\e[2K" : "", buf1);
 	}
 
-//	set_line_screen(temp, row - 1, col - 1);
+//	set_line_screen(buf1, row - 1, col - 1);
 
 	restore_pos(ses);
 }

+ 580 - 20
src/substitute.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
@@ -134,6 +133,34 @@ char *c256to16_bg[256] =
 	 "\e[1;47m",  "\e[1;47m",  "\e[1;47m",  "\e[1;47m",  "\e[1;47m",  "\e[1;47m"
 };
 
+
+// input A to F
+
+int c256_val(char chr)
+{
+	static unsigned char c256_val[256] =
+	{
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   1,   2,    3,   4,   5,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   1,   2,    3,   4,   5,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,
+		  0,   0,   0,   0,    0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0
+	};
+
+	return (int) c256_val[(unsigned char) chr];
+}
+
 // input 00 to FF
 
 int c4096_val(char chr1, char chr2)
@@ -188,14 +215,19 @@ int c4096_to_256_val(char chr1, char chr2)
 	return (int) c4096_to_256[c4096_val(chr1, chr2)];
 }
 
+char int_to_hex[16] =
+{
+	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+char int_to_256[6] =
+{
+	'A', 'B', 'C', 'D', 'E', 'F'
+};
 
 void c4096_rnd(struct session *ses, char *str)
 {
-	static char dec_to_hex[16] =
-	{
-		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
-	};
-	sprintf(str, "%c%c%c", dec_to_hex[generate_rand(ses) % 16], dec_to_hex[generate_rand(ses) % 16], dec_to_hex[generate_rand(ses) % 16]);
+	sprintf(str, "%c%c%c", int_to_hex[generate_rand(ses) % 16], int_to_hex[generate_rand(ses) % 16], int_to_hex[generate_rand(ses) % 16]);
 }
 
 int is_c32(char chr)
@@ -234,7 +266,6 @@ char *c32_fg_bold[26] =
 	"", "<ff80>", "<ff08>", "", "<ff00>", "<fddd>", "<fdb0>", "", "<f80f>", "<ffff>", "", "<fff0>", ""
 };
 
-
 int is_variable(struct session *ses, char *str)
 {
 	struct listroot *root;
@@ -401,6 +432,486 @@ int is_color_code(char *pti)
 	return 0;
 }
 
+
+char *fuzzy_char(struct session *ses, char val1, char val2, int mode)
+{
+	static char out[10][3];
+	static int cnt;
+	int tmp;
+
+	cnt = (cnt + 1) % 10;
+
+	switch (mode)
+	{
+		case 8:
+			tmp = c256_val(val2) - 1 + generate_rand(ses) % 3;
+
+			tmp = URANGE(0, tmp, 5);
+
+			if (isupper(val2))
+			{
+				sprintf(out[cnt], "%c", toupper(int_to_256[tmp]));
+			}
+			else
+			{
+				sprintf(out[cnt], "%c", tolower(int_to_256[tmp]));
+			}
+			break;
+
+		case 12:
+			tmp = c4096_val(0, val2);
+
+			tmp = tmp - 2 + generate_rand(ses) % 4;
+
+			tmp = URANGE(0, tmp, 15);
+
+			sprintf(out[cnt], "%c", int_to_hex[tmp]);
+			break;
+
+		case 24:
+			tmp = c4096_val(val1, val2);
+
+			tmp = (tmp - 12 + generate_rand(ses) % 24);
+
+			tmp = URANGE(0, tmp, 255);
+
+			sprintf(out[cnt], "%c%c", int_to_hex[tmp / 16], int_to_hex[tmp % 16]);
+			break;
+
+		default:
+			tintin_printf2(ses, "dim_char: invalid mode");
+			break;
+	}
+	return out[cnt];
+}
+
+char *fuzzy_color_code(struct session *ses, char *in)
+{
+	static char *pto, out[10][100];
+	static int cnt;
+	char *pti, buf[100];
+	int tmp;
+
+	if (*in == 0 || strlen(in) > 50)
+	{
+		return "";
+	}
+
+	strcpy(buf, in);
+
+	pti = buf;
+	cnt = (cnt + 1) % 10;
+	pto = out[cnt];
+
+	while (*pti)
+	{
+		if (pti[0] == '<')
+		{
+			if (pti[1] == 0 || pti[2] == 0 || pti[3] == 0 || pti[4] == 0)
+			{
+				return out[cnt];
+			}
+
+			if (pti[4] == '>')
+			{
+				if (isdigit(pti[1]) && isdigit(pti[2]) && isdigit(pti[3]))
+				{
+					pto += sprintf(pto, "<%c%c%c>", pti[1], pti[2], pti[3]);
+				}
+				else if (pti[1] >= 'a' && pti[1] <= 'f' && pti[2] >= 'a' && pti[2] <= 'f' && pti[3] >= 'a' && pti[3] <= 'f')
+				{
+					pto += sprintf(pto, "<%s%s%s>", fuzzy_char(ses, 0, pti[1], 8), fuzzy_char(ses, 0, pti[2], 8), fuzzy_char(ses, 0, pti[3], 8));
+				}
+				else if (pti[1] >= 'A' && pti[1] <= 'F' && pti[2] >= 'A' && pti[2] <= 'F' && pti[3] >= 'A' && pti[3] <= 'F')
+				{
+					pto += sprintf(pto, "<%s%s%s>", fuzzy_char(ses, 0, pti[1], 8), fuzzy_char(ses, 0, pti[2], 8), fuzzy_char(ses, 0, pti[3], 8));
+				}
+				else if ((pti[1] == 'g' || pti[1] == 'G') && isdigit((int) pti[2]) && isdigit((int) pti[3]))
+				{
+					tmp = (pti[2] - '0') * 10 + (pti[3] - '0') - 3 + generate_rand(ses) % 7;
+
+					pto += sprintf(pto, "<%c%02d>", pti[1], URANGE(0, tmp, 23));
+				}
+				else
+				{
+					return out[cnt];
+				}
+				pti += 5;
+			}
+			else if (toupper((int) pti[1]) == 'F')
+			{
+				if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && pti[5] == '>')
+				{
+					pto += sprintf(pto, "<F%s%s%s>", fuzzy_char(ses, 0, pti[2], 12), fuzzy_char(ses, 0, pti[3], 12), fuzzy_char(ses, 0, pti[4], 12));
+
+					pti += 6;
+				}
+				else if (pti[2] == '?' && pti[3] == '?' && pti[4] == '?' && pti[5] == '>')
+				{
+					c4096_rnd(ses, &pti[2]);
+
+					pto += sprintf(pto, "<F%c%c%c>", pti[2], pti[3], pti[4]);
+
+					pti += 6;
+				}
+				else if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && isxdigit(pti[5]) && isxdigit(pti[6]) && isxdigit(pti[7]) && pti[8] == '>')
+				{
+					pto += sprintf(pto, "<F%s%s%s>", fuzzy_char(ses, pti[3], pti[4], 24), fuzzy_char(ses, pti[4], pti[5], 24), fuzzy_char(ses, pti[7], pti[7], 24));
+
+					pti += 9;
+				}
+				else
+				{
+					return out[cnt];
+				}
+			}
+			else if (toupper(pti[1]) == 'B')
+			{
+				if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && pti[5] == '>')
+				{
+					pto += sprintf(pto, "<B%s%s%s>", fuzzy_char(ses, 0, pti[2], 12), fuzzy_char(ses, 0, pti[3], 12), fuzzy_char(ses, 0, pti[4], 12));
+
+					pti += 6;
+				}
+				else if (pti[2] == '?' && pti[3] == '?' && pti[4] == '?' && pti[5] == '>')
+				{
+					c4096_rnd(ses, &pti[2]);
+
+					pto += sprintf(pto, "<B%c%c%c>", pti[2], pti[3], pti[4]);
+
+					pti += 6;
+				}
+				else if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && isxdigit(pti[5]) && isxdigit(pti[6]) && isxdigit(pti[7]) && pti[8] == '>')
+				{
+					pto += sprintf(pto, "<B%s%s%s>", fuzzy_char(ses, pti[2], pti[3], 24), fuzzy_char(ses, pti[4], pti[5], 24), fuzzy_char(ses, pti[6], pti[7], 24));
+
+					pti += 9;
+				}
+				else
+				{
+					return out[cnt];
+				}
+			}
+			else
+			{
+				return out[cnt];
+			}
+		}
+		else
+		{
+			return out[cnt];
+		}
+	}
+	return out[cnt];
+}
+
+char *dim_char(struct session *ses, char val1, char val2, int mod, int mode)
+{
+	static char out[10][3];
+	static int cnt;
+	int tmp;
+
+	cnt = (cnt + 1) % 10;
+
+	switch (mode)
+	{
+		case 8:
+			tmp = c256_val(val2) - mod;
+
+			tmp = URANGE(0, tmp, 5);
+
+			if (isupper(val2))
+			{
+				sprintf(out[cnt], "%c", toupper(int_to_256[tmp]));
+			}
+			else
+			{
+				sprintf(out[cnt], "%c", tolower(int_to_256[tmp]));
+			}
+			break;
+
+		case 12:
+			tmp = c4096_val(0, val2);
+
+			tmp = tmp - mod - generate_rand(ses) % 3;
+
+			tmp = URANGE(0, tmp, 15);
+
+			sprintf(out[cnt], "%c", int_to_hex[tmp]);
+			break;
+
+		case 24:
+			tmp = c4096_val(val1, val2);
+
+			tmp = tmp - mod - generate_rand(ses) % 2;
+
+			tmp = URANGE(0, tmp, 255);
+
+			sprintf(out[cnt], "%c%c", int_to_hex[tmp / 16], int_to_hex[tmp % 16]);
+			break;
+
+		default:
+			tintin_printf2(ses, "dim_char: invalid mode");
+			break;
+	}
+	return out[cnt];
+}
+
+char *dim_color_code(struct session *ses, char *in, int mod)
+{
+	static char *pto, out[10][60], buf[60];
+	char *pti;
+	static int cnt;
+	int tmp;
+
+	if (*in == 0 || strlen(in) > 40)
+	{
+		return "";
+	}
+
+	strcpy(buf, in);
+
+	pti = buf;
+	cnt = (cnt + 1) % 10;
+	pto = out[cnt];
+
+	if (mod < 0)
+	{
+		strcpy(pto, buf);
+
+		return out[cnt];
+	}
+
+	*pto = 0;
+
+	while (*pti)
+	{
+		if (pti[0] == '<')
+		{
+			if (pti[1] == 0 || pti[2] == 0 || pti[3] == 0 || pti[4] == 0)
+			{
+				return out[cnt];
+			}
+
+			if (pti[4] == '>')
+			{
+				if (isdigit(pti[1]) && isdigit(pti[2]) && isdigit(pti[3]))
+				{
+					pto += sprintf(pto, "<%c%c%c>", pti[1], pti[2], pti[3]);
+				}
+				else if (pti[1] >= 'a' && pti[1] <= 'f' && pti[2] >= 'a' && pti[2] <= 'f' && pti[3] >= 'a' && pti[3] <= 'f')
+				{
+					pto += sprintf(pto, "<%s%s%s>", dim_char(ses, 0, pti[1], mod, 8), dim_char(ses, 0, pti[2], mod, 8), dim_char(ses, 0, pti[3], mod, 8));
+				}
+				else if (pti[1] >= 'A' && pti[1] <= 'F' && pti[2] >= 'A' && pti[2] <= 'F' && pti[3] >= 'A' && pti[3] <= 'F')
+				{
+					pto += sprintf(pto, "<%s%s%s>", dim_char(ses, 0, pti[1], mod, 8), dim_char(ses, 0, pti[2], mod, 8), dim_char(ses, 0, pti[3], mod, 8));
+				}
+				else if ((pti[1] == 'g' || pti[1] == 'G') && isdigit((int) pti[2]) && isdigit((int) pti[3]))
+				{
+					tmp = (pti[2] - '0') * 10 + (pti[3] - '0') - mod;
+
+					pto += sprintf(pto, "<%c%02d>", pti[1], URANGE(0, tmp, 23));
+				}
+				else
+				{
+					return out[cnt];
+				}
+				pti += 5;
+			}
+			else if (toupper((int) pti[1]) == 'F')
+			{
+				if (pti[2] == '?' && pti[3] == '?' && pti[4] == '?' && pti[5] == '>')
+				{
+					c4096_rnd(ses, &pti[2]);
+				}
+
+				if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && pti[5] == '>')
+				{
+					pto += sprintf(pto, "<F%s%s%s>", dim_char(ses, 0, pti[2], mod, 12), dim_char(ses, 0, pti[3], mod, 12), dim_char(ses, 0, pti[4], mod, 12));
+
+					pti += 6;
+				}
+				else if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && isxdigit(pti[5]) && isxdigit(pti[6]) && isxdigit(pti[7]) && pti[8] == '>')
+				{
+					pto += sprintf(pto, "<F%s%s%s>", dim_char(ses, pti[2], pti[3], mod, 24), dim_char(ses, pti[4], pti[5], mod, 24), dim_char(ses, mod, pti[6], pti[7], 24));
+
+					pti += 9;
+				}
+				else
+				{
+					return out[cnt];
+				}
+			}
+			else if (toupper(pti[1]) == 'B')
+			{
+				if (pti[2] == '?' && pti[3] == '?' && pti[4] == '?' && pti[5] == '>')
+				{
+					c4096_rnd(ses, &pti[2]);
+				}
+
+				if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && pti[5] == '>')
+				{
+					pto += sprintf(pto, "<B%s%s%s>", dim_char(ses, 0, pti[2], mod, 12), dim_char(ses, 0, pti[3], mod, 12), dim_char(ses, 0, pti[4], mod, 12));
+
+					pti += 6;
+				}
+				else if (toupper(pti[1]) == 'B' && isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && isxdigit(pti[5]) && isxdigit(pti[6]) && isxdigit(pti[7]) && pti[8] == '>')
+				{
+					pto += sprintf(pto, "<B%s%s%s>", dim_char(ses, pti[2], pti[3], mod, 24), dim_char(ses, pti[4], pti[5], mod, 24), dim_char(ses, pti[6], pti[7], mod, 24));
+
+					pti += 9;
+				}
+				else
+				{
+					return out[cnt];
+				}
+			}
+			else
+			{
+				return out[cnt];
+			}
+		}
+	}
+	return out[cnt];
+
+}
+
+char lit_char(struct session *ses, char max, char val, int mod, int mode)
+{
+	int tmp;
+
+	switch (mode)
+	{
+		case 8:
+			tmp = val + mod;
+
+			return UMIN(max, tmp);
+
+		case 12:
+			tmp = c4096_val(0, val);
+
+			tmp = URANGE(0, tmp + mod, 15);
+
+			return int_to_hex[tmp];
+	}
+	return val;
+}
+
+char *lit_color_code(struct session *ses, char *pti, int mod)
+{
+	static char fuzzy[10][20];
+	static int cnt;
+	int tmp;
+
+	cnt = (cnt + 1) % 10;
+
+	if (pti[0] == '<')
+	{
+		if (pti[1] == 0 || pti[2] == 0 || pti[3] == 0 || pti[4] == 0)
+		{
+			return "";
+		}
+
+		if (pti[4] == '>')
+		{
+			if (isdigit(pti[1]) && isdigit(pti[2]) && isdigit(pti[3]))
+			{
+				sprintf(fuzzy[cnt], "<%c%c%c>%.9s", pti[1], pti[2], pti[3], &pti[5]);
+
+				return fuzzy[cnt];
+			}
+
+			if (pti[1] >= 'a' && pti[1] <= 'f' && pti[2] >= 'a' && pti[2] <= 'f' && pti[3] >= 'a' && pti[3] <= 'f')
+			{
+				sprintf(fuzzy[cnt], "<%c%c%c>%.9s", lit_char(ses, 'f', pti[1], mod, 8), lit_char(ses, 'f', pti[2], mod, 8), lit_char(ses, 'f', pti[3], mod, 8), &pti[5]);
+
+				return fuzzy[cnt];
+			}
+
+			if (pti[1] >= 'A' && pti[1] <= 'F' && pti[2] >= 'A' && pti[2] <= 'F' && pti[3] >= 'A' && pti[3] <= 'F')
+			{
+				sprintf(fuzzy[cnt], "<%c%c%c>%.9s", lit_char(ses, 'F', pti[1], mod, 8), lit_char(ses, 'F', pti[2], mod, 8), lit_char(ses, 'F', pti[3], mod, 8), &pti[5]);
+
+				return fuzzy[cnt];
+			}
+		}
+
+		{
+			if (pti[1] == 'g' || pti[1] == 'G')
+			{
+				if (isdigit((int) pti[2]) && isdigit((int) pti[3]))
+				{
+					tmp = (pti[2] - '0') * 10 + (pti[3] - '0');
+
+					tmp -= mod;
+
+					sprintf(fuzzy[cnt], "<%c%02d>%.9s", pti[1], URANGE(0, tmp, 23), &pti[5]);
+
+					return fuzzy[cnt];
+				}
+				return "";
+			}
+		}
+
+		if (pti[5] == 0)
+		{
+			return "";
+		}
+
+		if (toupper((int) pti[1]) == 'F')
+		{
+			if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && pti[5] == '>')
+			{
+				sprintf(fuzzy[cnt], "<F%c%c%c>%.9s", lit_char(ses, 'F', pti[2], mod, 12), lit_char(ses, 'F', pti[3], mod, 12), lit_char(ses, 'F', pti[4], mod, 12), &pti[6]);
+
+				return fuzzy[cnt];
+			}
+			else if (pti[2] == '?' && pti[3] == '?' && pti[4] == '?' && pti[5] == '>')
+			{
+				sprintf(fuzzy[cnt], "<F??%c>%.9s", '?', &pti[6]);
+
+				c4096_rnd(ses, &fuzzy[cnt][2]);
+
+				return dim_color_code(ses, fuzzy[cnt], mod);
+			}
+			else if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && isxdigit(pti[5]) && isxdigit(pti[6]) && isxdigit(pti[7]) && pti[8] == '>')
+			{
+				sprintf(fuzzy[cnt], "<F%c%c%c>%.9s", lit_char(ses, 'F', pti[2], mod, 12), lit_char(ses, 'F', pti[4], mod, 12), lit_char(ses, mod, pti[6], 'F', 12), &pti[9]);
+
+				return fuzzy[cnt];
+			}
+			return "";
+		}
+
+		if (toupper(pti[1]) == 'B')
+		{
+			if (isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && pti[5] == '>')
+			{
+				sprintf(fuzzy[cnt], "<B%c%c%c>%.9s", lit_char(ses, 'F', pti[2], mod, 12), lit_char(ses, 'F', pti[3], mod, 12), lit_char(ses, 'F', pti[4], mod, 12), &pti[6]);
+
+				return fuzzy[cnt];
+			}
+			if (toupper(pti[1]) == 'B' && pti[2] == '?' && pti[3] == '?' && pti[4] == '?' && pti[5] == '>')
+			{
+				sprintf(fuzzy[cnt], "<B??%c>%.9s", '?', &pti[6]);
+
+				c4096_rnd(ses, &fuzzy[cnt][2]);
+
+				return dim_color_code(ses, fuzzy[cnt], mod);
+			}
+			if (toupper(pti[1]) == 'B' && isxdigit(pti[2]) && isxdigit(pti[3]) && isxdigit(pti[4]) && isxdigit(pti[5]) && isxdigit(pti[6]) && isxdigit(pti[7]) && pti[8] == '>')
+			{
+				sprintf(fuzzy[cnt], "<B%c%c%c>%.9s", lit_char(ses, 'F', pti[2], mod, 12), lit_char(ses, 'F', pti[4], mod, 12), lit_char(ses, mod, pti[6], 'F', 12), &pti[9]);
+
+				return fuzzy[cnt];
+			}
+			return "";
+		}
+	}
+	return "";
+}
+
 int substitute(struct session *ses, char *string, char *result, int flags)
 {
 	struct listnode *node;
@@ -408,7 +919,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 	struct session *sesptr;
 	char temp[BUFFER_SIZE], buf[BUFFER_SIZE], buffer[BUFFER_SIZE], *pti, *pto, *ptt, *str;
 	char *pte, old[10] = { 0 };
-	int i, cnt, escape = FALSE, flags_neol = flags;
+	int i, skip, cnt, escape = FALSE, flags_neol = flags;
 
 	push_call("substitute(%p,%p,%p,%d)",ses,string,result,flags);
 
@@ -419,7 +930,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 
 	while (TRUE)
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
 			*pto++ = *pti++;
 			*pto++ = *pti++;
@@ -526,7 +1037,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 						substitute(sesptr, temp, pto, flags_neol);
 
 						pto += strlen(pto);
-						
+
 						continue;
 					}
 					else
@@ -662,7 +1173,14 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 						continue;
 					}
 
-					pti = get_arg_at_brackets(ses, &pti[i], temp + strlen(temp));
+					if (brace == FALSE)
+					{
+						pti = get_arg_at_brackets(ses, &pti[i], temp + strlen(temp));
+					}
+					else
+					{
+						pti = ptt;
+					}
 
 					substitute(ses, temp, buf, flags_neol);
 
@@ -725,14 +1243,17 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 
 					if (*temp)
 					{
-						root = local_list(ses);
+						root = search_nest_base_ses(ses, temp);
 
-						if ((node = search_node_list(root, temp)) == NULL)
+						if (root)
 						{
-							root = ses->list[LIST_VARIABLE];
-
 							node = search_node_list(root, temp);
 						}
+						else
+						{
+							root = ses->list[LIST_VARIABLE];
+							node = NULL;
+						}
 					}
 					else
 					{
@@ -760,7 +1281,14 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 						continue;
 					}
 
-					pti = get_arg_at_brackets(ses, &pti[i], temp + strlen(temp));
+					if (brace == FALSE)
+					{
+						pti = get_arg_at_brackets(ses, &pti[i], temp + strlen(temp));
+					}
+					else
+					{
+						pti = ptt;
+					}
 
 					substitute(ses, temp, buf, flags_neol);
 
@@ -885,7 +1413,14 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 						continue;
 					}
 
-					pti = get_arg_at_brackets(ses, &pti[i], temp + strlen(temp));
+					if (brace == FALSE)
+					{
+						pti = get_arg_at_brackets(ses, &pti[i], temp + strlen(temp));
+					}
+					else
+					{
+						pti = ptt;
+					}
 
 					substitute(ses, temp, buf, flags_neol);
 
@@ -928,7 +1463,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 
 						while (*ptt)
 						{
-							if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *ptt & 128 && ptt[1] != 0)
+							if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, ptt))
 							{
 								*pto++ = *ptt++;
 								*pto++ = *ptt++;
@@ -1003,6 +1538,11 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 						pti += isdigit((int) pti[2]) ? 3 : 2;
 					}
 				}
+				else if (pti[1] == '*') // avoid %*variable triggers
+				{
+					*pto++ = *pti++;
+					*pto++ = *pti++;
+				}
 				else
 				{
 					*pto++ = *pti++;
@@ -1345,6 +1885,26 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 				}
 				break;
 
+			case ASCII_ESC:
+				if (HAS_BIT(flags, SUB_COL) && ses->color == 0)
+				{
+					skip = find_color_code(pti);
+
+					if (skip)
+					{
+						pti += skip;
+					}
+					else
+					{
+						*pto++ = *pti++;
+					}
+				}
+				else
+				{
+					*pto++ = *pti++;
+				}
+				break;
+
 			default:
 				if (HAS_BIT(flags, SUB_SEC) && !HAS_BIT(flags, SUB_ARG))
 				{

+ 88 - 1
src/system.c

@@ -38,6 +38,8 @@
 #include <termios.h>
 #include <sys/un.h>
 
+#include <sys/wait.h>
+
 DO_COMMAND(do_run)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], temp[BUFFER_SIZE], file[BUFFER_SIZE];
@@ -147,7 +149,7 @@ DO_COMMAND(do_script)
 				cat_sprintf(var, "{%d}{%s}", index++, tmp);
 			}
 
-			set_nest_node(ses->list[LIST_VARIABLE], arg1, "%s", var);
+			set_nest_node_ses(ses, arg1, "%s", var);
 
 			pclose(script);
 		}
@@ -181,7 +183,92 @@ DO_COMMAND(do_suspend)
 	return ses;
 }
 
+// use #run + waitpid() ?
+
+/*
+DO_COMMAND(do_system)
+{
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], temp[BUFFER_SIZE], buffer[BUFFER_SIZE];
+	int desc, pid;
+	struct winsize size;
+	char *argv[4] = {"sh", "-c", "", NULL};
+	int filedes[2];
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SYSTEM {SHELL COMMAND}");
+		
+		return ses;
+	}
+
+	size.ws_row = get_scroll_rows(ses);
+	size.ws_col = get_scroll_cols(ses);
+	size.ws_ypixel = size.ws_row * gtd->screen->char_height;
+	size.ws_xpixel = size.ws_col * gtd->screen->char_width;
 
+	if (pipe(filedes) == -1)
+	{
+		perror("pipe");
+
+		return ses;
+	}
+
+	pid = fork();
+
+	switch (pid)
+	{
+		case -1:
+			syserr_printf(ses, "do_system: forkpty");
+			break;
+
+		case 0:
+			while ((dup2(filedes[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
+			close(filedes[1]);
+			close(filedes[0]);
+
+			sprintf(temp, "exec %s", arg1);
+			argv[2] = temp;
+			execv("/bin/sh", argv);
+			perror("execv");
+			break;
+
+		default:
+			close(filedes[1]);
+
+			while (TRUE)
+			{
+				ssize_t count = read(filedes[0], buffer, sizeof(buffer));
+
+				if (count == -1)
+				{
+					if (errno == EINTR)
+					{
+						continue;
+					}
+					else
+					{
+						perror("read");
+					}
+					return ses;
+				}
+				else if (count == 0)
+				{
+					break;
+				}
+				else
+				{
+					tintin_printf2(ses, "%s", buffer);
+				}
+			}
+			close(filedes[0]);
+			wait(0);
+			break;
+	}
+	return ses;
+}
+*/
 
 DO_COMMAND(do_system)
 {

+ 242 - 85
src/tables.c

@@ -34,12 +34,12 @@ struct command_type command_table[] =
 	{    "advertise",         do_advertise,         TOKEN_TYPE_COMMAND },
 	{    "alias",             do_alias,             TOKEN_TYPE_COMMAND },
 	{    "all",               do_all,               TOKEN_TYPE_COMMAND },
-//	{    "attach",            do_attach,            TOKEN_TYPE_COMMAND },
 	{    "bell",              do_bell,              TOKEN_TYPE_COMMAND },
 	{    "break",             do_nop,               TOKEN_TYPE_BREAK   },
 	{    "buffer",            do_buffer,            TOKEN_TYPE_COMMAND },
 	{    "button",            do_button,            TOKEN_TYPE_COMMAND },
 	{    "case",              do_nop,               TOKEN_TYPE_CASE    },
+	{    "cat",               do_cat,               TOKEN_TYPE_COMMAND },
 	{    "chat",              do_chat,              TOKEN_TYPE_COMMAND },
 	{    "class",             do_class,             TOKEN_TYPE_COMMAND },
 	{    "commands",          do_commands,          TOKEN_TYPE_COMMAND },
@@ -51,7 +51,7 @@ struct command_type command_table[] =
 	{    "debug",             do_debug,             TOKEN_TYPE_COMMAND },
 	{    "default",           do_nop,               TOKEN_TYPE_DEFAULT },
 	{    "delay",             do_delay,             TOKEN_TYPE_COMMAND },
-//	{    "detach",            do_detach,            TOKEN_TYPE_COMMAND },
+	{    "dictionary",        do_dictionary,        TOKEN_TYPE_COMMAND },
 	{    "draw",              do_draw,              TOKEN_TYPE_COMMAND },
 	{    "echo",              do_echo,              TOKEN_TYPE_COMMAND },
 	{    "else",              do_nop,               TOKEN_TYPE_ELSE    },
@@ -133,26 +133,28 @@ struct command_type command_table[] =
 
 struct list_type list_table[LIST_MAX] =
 {
-	{    "ACTION",            "ACTIONS",            SORT_PRIORITY,    3,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_REGEX|LIST_FLAG_PRIORITY },
-	{    "ALIAS",             "ALIASES",            SORT_PRIORITY,    3,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_REGEX|LIST_FLAG_PRIORITY },
-	{    "BUTTON",            "BUTTONS",            SORT_PRIORITY,    3,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_PRIORITY },
-	{    "CLASS",             "CLASSES",            SORT_PRIORITY,    2,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_INHERIT                                 },
-	{    "COMMAND",           "COMMANDS",           SORT_APPEND,      1,  LIST_FLAG_MESSAGE                                                                  },
-	{    "CONFIG",            "CONFIGURATIONS",     SORT_ALPHA,       2,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_INHERIT                 },
-	{    "DELAY",             "DELAYS",             SORT_DELAY,       3,  LIST_FLAG_MESSAGE|LIST_FLAG_READ                                                   },
-	{    "EVENT",             "EVENTS",             SORT_ALPHA,       2,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
-	{    "FUNCTION",          "FUNCTIONS",          SORT_ALPHA,       2,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
-	{    "GAG",               "GAGS",               SORT_ALPHA,       1,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
-	{    "HIGHLIGHT",         "HIGHLIGHTS",         SORT_PRIORITY,    3,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_REGEX|LIST_FLAG_PRIORITY },
-	{    "HISTORY",           "HISTORIES",          SORT_APPEND,      1,  LIST_FLAG_MESSAGE                                                                  },
-	{    "MACRO",             "MACROS",             SORT_ALPHA,       2,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
-	{    "PATH",              "PATHS",              SORT_APPEND,      2,  LIST_FLAG_MESSAGE                                                                  },
-	{    "PATHDIR",           "PATHDIRS",           SORT_ALPHA,       3,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
-	{    "PROMPT",            "PROMPTS",            SORT_PRIORITY,    4,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_REGEX|LIST_FLAG_PRIORITY },
-	{    "SUBSTITUTE",        "SUBSTITUTES",        SORT_PRIORITY,    3,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_REGEX|LIST_FLAG_PRIORITY },
-	{    "TAB",               "TABS",               SORT_ALPHA,       1,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
-	{    "TICKER",            "TICKERS",            SORT_ALPHA,       3,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
-	{    "VARIABLE",          "VARIABLES",          SORT_ALPHA,       2,  LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_NEST }
+	{    "ACTION",            "ACTIONS",            SORT_PRIORITY,    3, 2, 3, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_REGEX|LIST_FLAG_PRIORITY },
+	{    "ALIAS",             "ALIASES",            SORT_PRIORITY,    3, 2, 3, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_REGEX|LIST_FLAG_PRIORITY },
+	{    "BUTTON",            "BUTTONS",            SORT_PRIORITY,    3, 2, 3, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_PRIORITY },
+	{    "CLASS",             "CLASSES",            SORT_PRIORITY,    2, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_INHERIT                                 },
+	{    "COMMAND",           "COMMANDS",           SORT_ALPHA,       1, 0, 0, LIST_FLAG_MESSAGE                                                                  },
+	{    "CONFIG",            "CONFIGS",            SORT_ALPHA,       2, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
+	{    "DELAY",             "DELAYS",             SORT_DELAY,       3, 2, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ                                                   },
+	{    "EVENT",             "EVENTS",             SORT_ALPHA,       2, 2, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
+	{    "FUNCTION",          "FUNCTIONS",          SORT_ALPHA,       2, 2, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
+	{    "GAG",               "GAGS",               SORT_ALPHA,       1, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
+	{    "HIGHLIGHT",         "HIGHLIGHTS",         SORT_PRIORITY,    3, 0, 3, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_REGEX|LIST_FLAG_PRIORITY },
+	{    "HISTORY",           "HISTORIES",          SORT_APPEND,      1, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_HIDE },
+	{    "LANDMARK",          "LANDMARKS",          SORT_ALPHA,       4, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_HIDE },
+	{    "MACRO",             "MACROS",             SORT_ALPHA,       2, 2, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
+	{    "PATH",              "PATHS",              SORT_APPEND,      2, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_HIDE },
+	{    "PATHDIR",           "PATHDIRS",           SORT_ALPHA,       3, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
+	{    "PROMPT",            "PROMPTS",            SORT_ALPHA,       4, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_REGEX },
+	{    "SUBSTITUTE",        "SUBSTITUTES",        SORT_PRIORITY,    3, 0, 3, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_REGEX|LIST_FLAG_PRIORITY },
+	{    "TAB",               "TABS",               SORT_ALPHA,       1, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
+	{    "TERRAIN",           "TERRAINS",           SORT_ALPHA,       2, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_HIDE },
+	{    "TICKER",            "TICKERS",            SORT_ALPHA,       3, 2, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
+	{    "VARIABLE",          "VARIABLES",          SORT_ALPHA,       2, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT|LIST_FLAG_NEST }
 };
 
 struct substitution_type substitution_table[] =
@@ -273,8 +275,8 @@ struct config_type config_table[] =
 
 	{
 		"LOG LEVEL",
-		"TinTin++ only logs low level mud data",
-		"TinTin++ only logs high level mud data",
+		"TinTin++ only logs low level server data",
+		"TinTin++ only logs high level server data",
 		config_loglevel
 	},
 
@@ -287,8 +289,8 @@ struct config_type config_table[] =
 
 	{
 		"MOUSE TRACKING",
-		"Generates mouse tracking events.",
-		"Do does not generate mouse events.",
+		"Generate mouse tracking events.",
+		"Do not generate mouse events.",
 		config_mousetracking
 	},
 
@@ -336,8 +338,8 @@ struct config_type config_table[] =
 
 	{
 		"SCROLL LOCK",
-		"You do not see mud output while scrolling",
-		"You see mud output while scrolling",
+		"You do not see server output while scrolling",
+		"You see server output while scrolling",
 		config_scrolllock
 	},
 
@@ -392,8 +394,8 @@ struct config_type config_table[] =
 
 	{
 		"WORDWRAP",
-		"Mud output is word wrapped",
-		"Mud output is line wrapped",
+		"Server output is word wrapped",
+		"Server output is line wrapped",
 		config_wordwrap
 	},
 
@@ -531,6 +533,7 @@ struct color_type map_color_table[] =
 {
 	{     "AVOID",            "<118>" },
 	{     "BACKGROUND",       ""      },
+	{     "BLOCK",            "<218>" },
 	{     "EXITS",            "<278>" },
 	{     "HIDE",             "<168>" },
 	{     "INVISIBLE",        "<208>" },
@@ -545,11 +548,14 @@ struct class_type class_table[] =
 {
 	{    "OPEN",              class_open             },
 	{    "CLOSE",             class_close            },
+	{    "KILL",              class_kill             },
 	{    "LIST",              class_list             },
+	{    "LOAD",              class_load             },
 	{    "READ",              class_read             },
+	{    "SAVE",              class_save             },
 	{    "SIZE",              class_size             },
 	{    "WRITE",             class_write            },
-	{    "KILL",              class_kill             },
+
 	{    "",                  NULL                   },
 };
 
@@ -564,7 +570,7 @@ struct chat_type chat_table[] =
 	{     "DOWNLOADDIR",      chat_downloaddir,    1, 0, "Set the download directory"                     },
 	{     "EMOTE",            chat_emote,          0, 1, "Send an emoted chat message"                    },
 	{     "FORWARD",          chat_forward,        1, 0, "Forward all chat messages to a buddy"           },
-	{     "FORWARDALL",       chat_forwardall,     1, 0, "Forward all chat/mud messages to a buddy"       },
+	{     "FORWARDALL",       chat_forwardall,     1, 0, "Forward all chat/server messages to a buddy"    },
 	{     "FILESTAT",         chat_filestat,       1, 0, "Show file transfer data"                        },
 	{     "GROUP",            chat_group,          0, 1, "Assign a group to a buddy"                      },
 	{     "IGNORE",           chat_ignore,         1, 0, "Ignore all messages from a buddy"               },
@@ -630,14 +636,17 @@ struct array_type array_table[] =
 	{     "ADD",              array_add,         "Add an item to a list table"             },
 	{     "CLEAR",            array_clear,       "Clear a list"                            },
 	{     "CLR",              array_clear,       NULL                                      },
+	{     "COLLAPSE",         array_collapse,    "Collapse the list into a variable"       },
 	{     "CREATE",           array_create,      "Create a list table with given items"    },
 	{     "DELETE",           array_delete,      "Delete a list item with given index"     },
+	{     "EXPLODE",          array_explode,     "Explode the variable into a list"        },
 	{     "FIND",             array_find,        "Find a list item with given regex"       },
 	{     "FND",              array_find,        NULL                                      },
 	{     "GET",              array_get,         "Retrieve a list item with given index"   },
 	{     "INSERT",           array_insert,      "Insert a list item at given index"       },
 	{     "LENGTH",           array_size,        NULL                                      },
 	{     "SET",              array_set,         "Change a list item at given index"       },
+	{     "SHUFFLE",          array_shuffle,     "Sort a list table randomly"              },
 	{     "SIMPLIFY",         array_simplify,    "Turn a list table into a simple list"    },
 	{     "SIZE",             array_size,        NULL                                      },
 	{     "SORT",             array_sort,        "Sort a list table alphabetically"        },
@@ -650,52 +659,59 @@ struct array_type array_table[] =
 
 struct map_type map_table[] =
 {
-	{     "AT",               map_at,              0,              2    },
-	{     "CENTER",           map_center,          MAP_FLAG_VTMAP, 2    },
-	{     "COLOR",            map_color,           MAP_FLAG_VTMAP, 1    },
-	{     "CREATE",           map_create,          MAP_FLAG_VTMAP, 0    },
-	{     "DEBUG",            map_debug,           0,              2    },
-	{     "DELETE",           map_delete,          MAP_FLAG_VTMAP, 1    },
-	{     "DESTROY",          map_destroy,         MAP_FLAG_VTMAP, 1    },
-	{     "DIG",              map_dig,             MAP_FLAG_VTMAP, 2    },
-	{     "EXIT",             map_exit,            MAP_FLAG_VTMAP, 2    },
-	{     "EXITFLAG",         map_exitflag,        MAP_FLAG_VTMAP, 2    },
-	{     "EXPLORE",          map_explore,         MAP_FLAG_VTMAP, 2    },
-	{     "FIND",             map_find,            MAP_FLAG_VTMAP, 2    },
-	{     "FLAG",             map_flag,            MAP_FLAG_VTMAP, 1    },
-	{     "GET",              map_get,             0,              2    },
-	{     "GLOBAL",           map_global,          0,              1    },
-	{     "GOTO",             map_goto,            MAP_FLAG_VTMAP, 1    },
-	{     "INFO",             map_info,            0,              1    },
-	{     "INSERT",           map_insert,          MAP_FLAG_VTMAP, 2    },
-	{     "JUMP",             map_jump,            MAP_FLAG_VTMAP, 2    },
-	{     "LEAVE",            map_leave,           MAP_FLAG_VTMAP, 2    },
-	{     "LEGENDA",          map_legend,          MAP_FLAG_VTMAP, 1    },
-	{     "LINK",             map_link,            MAP_FLAG_VTMAP, 2    },
-	{     "LIST",             map_list,            0,              2    },
-	{     "MAP",              map_map,             0,              2    },
-	{     "MOVE",             map_move,            MAP_FLAG_VTMAP, 2    },
-	{     "NAME",             map_name,            MAP_FLAG_VTMAP, 2    },
-	{     "OFFSET",           map_offset,          MAP_FLAG_VTMAP, 1    },
-	{     "READ",             map_read,            MAP_FLAG_VTMAP, 0    },
-	{     "RESIZE",           map_resize,          0,              1    },
-	{     "RETURN",           map_return,          MAP_FLAG_VTMAP, 1    },
-	{     "ROOMFLAG",         map_roomflag,        MAP_FLAG_VTMAP, 2    },
-	{     "RUN",              map_run,             MAP_FLAG_VTMAP, 2    },
-	{     "SET",              map_set,             MAP_FLAG_VTMAP, 2    },
-	{     "TRAVEL",           map_travel,          MAP_FLAG_VTMAP, 2    },
-	{     "UNDO",             map_undo,            MAP_FLAG_VTMAP, 2    },
-	{     "UNINSERT",         map_uninsert,        MAP_FLAG_VTMAP, 2    },
-	{     "UNLINK",           map_unlink,          MAP_FLAG_VTMAP, 2    },
-	{     "UPDATE",           map_update,          0,              0    },
-	{     "VNUM",             map_vnum,            MAP_FLAG_VTMAP, 2    },
-	{     "WRITE",            map_write,           0,              1    },
-	{     "",                 NULL,                0    }
+	{     "AT",               map_at,              0,              2, "Execute command at given location"    },
+	{     "CENTER",           map_center,          MAP_FLAG_VTMAP, 2, "Set the center of the map display"    },
+	{     "COLOR",            map_color,           MAP_FLAG_VTMAP, 1, "Set the color for given field"        },
+	{     "CREATE",           map_create,          MAP_FLAG_VTMAP, 0, "Creates the initial map"              },
+	{     "DEBUG",            map_debug,           0,              2, "Obscure debug information"            },
+	{     "DELETE",           map_delete,          MAP_FLAG_VTMAP, 1, "Delete the room at given direction"   },
+	{     "DESTROY",          map_destroy,         MAP_FLAG_VTMAP, 1, "Destroy area or map"                  },
+	{     "DIG",              map_dig,             MAP_FLAG_VTMAP, 2, "Create new room at given direction"   },
+	{     "ENTRANCE",         map_entrance,        MAP_FLAG_VTMAP, 2, "Change the given exit's entrance"     },
+	{     "EXIT",             map_exit,            MAP_FLAG_VTMAP, 2, "Change the given exit"                },
+	{     "EXITFLAG",         map_exitflag,        MAP_FLAG_VTMAP, 2, "Change the given exit's flags"        },
+	{     "EXPLORE",          map_explore,         MAP_FLAG_VTMAP, 2, "Save explored path to #path"          },
+	{     "FIND",             map_find,            MAP_FLAG_VTMAP, 2, "Save found path to #path"             },
+	{     "FLAG",             map_flag,            MAP_FLAG_VTMAP, 1, "Change the map's flags"               },
+	{     "GET",              map_get,             0,              2, "Get various room values"              },
+	{     "GLOBAL",           map_global,          0,              1, "Set the global exit room"             },
+	{     "GOTO",             map_goto,            MAP_FLAG_VTMAP, 1, "Move to the given room"               },
+	{     "INFO",             map_info,            0,              1, "Display map and room information"     },
+	{     "INSERT",           map_insert,          MAP_FLAG_VTMAP, 2, "Insert a room at given direction"     },
+	{     "JUMP",             map_jump,            MAP_FLAG_VTMAP, 2, "Move to the given coordinate"         },
+	{     "LANDMARK",         map_landmark,        0,              1, "Create a global room reference"       },
+	{     "LEAVE",            map_leave,           MAP_FLAG_VTMAP, 2, "Leave the map"                        },
+	{     "LEGEND" ,          map_legend,          MAP_FLAG_VTMAP, 1, "Manipulate the map legend"            },
+	{     "LINK",             map_link,            MAP_FLAG_VTMAP, 2, "Link room at given direction"         },
+	{     "LIST",             map_list,            0,              2, "List matching rooms"                  },
+	{     "MAP",              map_map,             0,              2, "Display the map"                      },
+	{     "MOVE",             map_move,            MAP_FLAG_VTMAP, 2, "Move to the given direction"          },
+	{     "NAME",             map_name,            MAP_FLAG_VTMAP, 2, "(obsolete) Use SET ROOMNAME instead"  },
+	{     "OFFSET",           map_offset,          MAP_FLAG_VTMAP, 1, "Set the offset of the vt map"         },
+	{     "READ",             map_read,            MAP_FLAG_VTMAP, 0, "Read a map file"                      },
+	{     "RESIZE",           map_resize,          0,              1, "Resize the map room vnum range"       },
+	{     "RETURN",           map_return,          MAP_FLAG_VTMAP, 1, "Return to last known room"            },
+	{     "ROOMFLAG",         map_roomflag,        MAP_FLAG_VTMAP, 2, "Change the room's flags"              },
+	{     "RUN",              map_run,             MAP_FLAG_VTMAP, 2, "Save found path to #path and run it"  },
+	{     "SET",              map_set,             MAP_FLAG_VTMAP, 2, "Set various room values"              },
+	{     "SYNC",             map_sync,            MAP_FLAG_VTMAP, 0, "Read a map file without overwriting"  },
+	{     "TERRAIN",          map_terrain,         MAP_FLAG_VTMAP, 1, "Create a terrain type"                },
+	{     "TRAVEL",           map_travel,          MAP_FLAG_VTMAP, 2, "Save explored path to #path and run it" },
+	{     "UNDO",             map_undo,            MAP_FLAG_VTMAP, 2, "Undo last map action"                 },
+	{     "UNINSERT",         map_uninsert,        MAP_FLAG_VTMAP, 2, "Uninsert room in given direction"     },
+	{     "UNLANDMARK",       map_unlandmark,      0,              1, "Remove a landmark"                    },
+	{     "UNLINK",           map_unlink,          MAP_FLAG_VTMAP, 2, "Remove given exit"                    },
+	{     "UNTERRAIN",        map_unterrain,       0,              1, "Remove a terrain type"                },
+	{     "UPDATE",           map_update,          0,              0, "Mark vt map for an auto update"       },
+	{     "VNUM",             map_vnum,            MAP_FLAG_VTMAP, 2, "Change the room vnum to given vnum"   },
+	{     "WRITE",            map_write,           0,              1, "Save the map to given file"           },
+	{     "",                 NULL,                0,              0, ""                                     }
 };
 
 
 struct cursor_type cursor_table[] =
 {
+/*
 	{
 		"AUTO TAB BACKWARD",
 		"Tab completion from scrollback buffer, backward",
@@ -710,6 +726,7 @@ struct cursor_type cursor_table[] =
 		CURSOR_FLAG_GET_ALL,
 		cursor_auto_tab_forward
 	},
+*/
 	{
 		"BACKSPACE",
 		"Delete backward character",
@@ -888,6 +905,7 @@ struct cursor_type cursor_table[] =
 		CURSOR_FLAG_GET_ONE,
 		cursor_insert
 	},
+/*
 	{
 		"MIXED TAB BACKWARD",
 		"Tab completion on last word, search backward",
@@ -902,6 +920,7 @@ struct cursor_type cursor_table[] =
 		CURSOR_FLAG_GET_ALL,
 		cursor_mixed_tab_forward
 	},
+*/
 	{
 		"NEXT WORD",
 		"Move cursor to the next word",
@@ -958,6 +977,28 @@ struct cursor_type cursor_table[] =
 		CURSOR_FLAG_GET_ALL,
 		cursor_suspend
 	},
+	{
+		"TAB",
+		"<LIST|SCROLLBACK> <BACKWARD|FORWARD>",
+		"",
+		CURSOR_FLAG_GET_ONE,
+		cursor_tab
+	},
+	{
+		"TAB L S BACKWARD",
+		"Tab completion on last word, search backward",
+		"\e[Z", // shift-tab
+		CURSOR_FLAG_GET_ALL,
+		cursor_mixed_tab_backward
+	},
+	{
+		"TAB L S FORWARD",
+		"Tab completion on last word, search forward",
+		"\t",
+		CURSOR_FLAG_GET_ALL,
+		cursor_mixed_tab_forward
+	},
+/*
 	{
 		"TAB BACKWARD",
 		"Tab completion from tab list, backward",
@@ -972,7 +1013,7 @@ struct cursor_type cursor_table[] =
 		CURSOR_FLAG_GET_ALL,
 		cursor_tab_forward
 	},
-
+*/
 	{
 		"", "", "\e[5~",   0, cursor_buffer_up
 	},
@@ -1054,11 +1095,18 @@ struct draw_type draw_table[] =
 {
 	{
 		"BOX",
-		"Draw a box.",
+		"Draw four sides of a box.",
 		DRAW_FLAG_BOXED|DRAW_FLAG_LEFT|DRAW_FLAG_RIGHT|DRAW_FLAG_TOP|DRAW_FLAG_BOT,
 		draw_box
 	},
 
+	{
+		"CORNER",
+		"Draw a corner",
+		DRAW_FLAG_CORNERED,
+		draw_corner
+	},
+
 	{
 		"LINE",
 		"Draw a line.",
@@ -1066,13 +1114,27 @@ struct draw_type draw_table[] =
 		draw_line
 	},
 
+	{
+		"RAIN",
+		"Draw digital rain.",
+		DRAW_FLAG_NONE,
+		draw_rain
+	},
+
 	{
 		"SIDE",
-		"Draw the side of a box.",
+		"Draw a line with corners.",
 		DRAW_FLAG_BOXED,
 		draw_side
 	},
 
+	{
+		"TABLE",
+		"Draw a table.",
+		DRAW_FLAG_BOXED|DRAW_FLAG_LEFT|DRAW_FLAG_RIGHT|DRAW_FLAG_TOP|DRAW_FLAG_BOT,
+		draw_table_grid
+	},
+
 	{
 		"TILE",
 		"Draw a tile.",
@@ -1299,11 +1361,13 @@ struct event_type event_table[] =
 	{    "MAP EXIT MAP",                           EVENT_FLAG_MAP,      "Triggers when exiting the map."          },
 	{    "MAP EXIT ROOM",                          EVENT_FLAG_MAP,      "Triggers when exiting a map room."       },
 	{    "MAP FOLLOW MAP",                         EVENT_FLAG_MAP,      "Triggers when moving to a map room."     },
-	{    "MAP LONG-CLICKED ",                      EVENT_FLAG_MAP,      "Triggers on vt map click."               },
-	{    "MAP PRESSED ",                           EVENT_FLAG_MAP,      "Triggers on vt map click."               },
-	{    "MAP RELEASED ",                          EVENT_FLAG_MAP,      "Triggers on vt map click."               },
-	{    "MAP SHORT-CLICKED ",                     EVENT_FLAG_MAP,      "Triggers on vt map click."               },
-	{    "MAP TRIPLE-CLICKED ",                    EVENT_FLAG_MAP,      "Triggers on vt map click."               },
+	{    "MAP LOCATION",                           EVENT_FLAG_MOUSE,    "Triggers on vt map click."               },
+	{    "MAP LONG-CLICKED ",                      EVENT_FLAG_MOUSE,    "Triggers on vt map click."               },
+	{    "MAP MOUSE LOCATION",                     EVENT_FLAG_MOUSE,    "Triggers when called by #screen raise."  }, 
+	{    "MAP PRESSED ",                           EVENT_FLAG_MOUSE,    "Triggers on vt map click."               },
+	{    "MAP RELEASED ",                          EVENT_FLAG_MOUSE,    "Triggers on vt map click."               },
+	{    "MAP SHORT-CLICKED ",                     EVENT_FLAG_MOUSE,    "Triggers on vt map click."               },
+	{    "MAP TRIPLE-CLICKED ",                    EVENT_FLAG_MOUSE,    "Triggers on vt map click."               },
 	{    "MAP UPDATED VTMAP",                      EVENT_FLAG_MAP,      "Triggers on vt map update."              },
 	{    "MINUTE",                                 EVENT_FLAG_TIME,     "Triggers each minute or given minute."   },
 	{    "MONTH",                                  EVENT_FLAG_TIME,     "Triggers each month or given month."     },
@@ -1330,8 +1394,10 @@ struct event_type event_table[] =
 	{    "SCREEN DESKTOP DIMENSIONS",              EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
 	{    "SCREEN DESKTOP SIZE",                    EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
 	{    "SCREEN DIMENSIONS",                      EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
+	{    "SCREEN FOCUS",                           EVENT_FLAG_SCREEN,   "Triggers when focus changes.",           },
+	{    "SCREEN LOCATION",                        EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
 	{    "SCREEN MINIMIZED",                       EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
-	{    "SCREEN POSITION",                        EVENT_FLAG_SCREEN,   "Triggers when called by #screen raise."  },
+	{    "SCREEN MOUSE LOCATION",                  EVENT_FLAG_MOUSE,    "Triggers when called by #screen raise."  },
 	{    "SCREEN REFRESH",                         EVENT_FLAG_SCREEN,   "Triggers when the screen is refreshed."  },
 	{    "SCREEN RESIZE",                          EVENT_FLAG_SCREEN,   "Triggers when the screen is resized."    },
 	{    "SCREEN ROTATE LANDSCAPE",                EVENT_FLAG_SCREEN,   "Triggers when the screen is rotated."    },
@@ -1350,6 +1416,7 @@ struct event_type event_table[] =
 	{    "SESSION DISCONNECTED",                   EVENT_FLAG_SESSION,  "Triggers when a session disconnects."    },
 	{    "SESSION TIMED OUT",                      EVENT_FLAG_SESSION,  "Triggers when a session doesn't connect."},
 	{    "SHORT-CLICKED",                          EVENT_FLAG_MOUSE,    "Triggers when mouse is short-clicked."   },
+	{    "SWIPED",                                 EVENT_FLAG_MOUSE,    "Triggers on mouse swipe."},
 	{    "SYSTEM ERROR",                           EVENT_FLAG_SYSTEM,   "Triggers on system errors."              },
 	{    "TIME",                                   EVENT_FLAG_TIME,     "Triggers on the given time."             },
 	{    "TRIPLE-CLICKED",                         EVENT_FLAG_MOUSE,    "Triggers when mouse is triple-clicked."  },
@@ -1363,8 +1430,6 @@ struct event_type event_table[] =
 	{    "VT100 ENQ",                              EVENT_FLAG_VT100,    "Triggers on an \\x05 call."              },
 	{    "VT100 SCROLL REGION",                    EVENT_FLAG_VT100,    "Triggers on vt100 scroll region change." },
 	{    "WEEK",                                   EVENT_FLAG_TIME,     "Triggers each week or given week."       },
-	{    "WINDOW FOCUS IN",                        EVENT_FLAG_SCREEN,   "Triggers on window focussing in."        },
-	{    "WINDOW FOCUS OUT",                       EVENT_FLAG_SCREEN,   "Triggers on window focussing out."       },
 	{    "WRITE ERROR",                            EVENT_FLAG_SYSTEM,   "Triggers when the write command fails."  },
 	{    "YEAR",                                   EVENT_FLAG_TIME,     "Triggers each year or given year."       },
 	{    "",                                       0,                   ""                                        }
@@ -1399,6 +1464,8 @@ struct path_type path_table[] =
 struct line_type line_table[] =
 {
 	{    "BACKGROUND",        line_background,     "Execute line without stealing session focus."   },
+	{    "BENCHMARK",         line_benchmark,      "Execute line and provide timing information."   },
+	{    "CAPTURE",           line_capture,        "Capture output in the given variable."          },
 	{    "DEBUG",             line_debug,          "Execute line in debug mode."                    },
 	{    "GAG",               line_gag,            "Gag the next line."                             },
 	{    "IGNORE",            line_ignore,         "Execute line with triggers ignored."            },
@@ -1490,7 +1557,7 @@ struct telopt_type telopt_table[] =
 	{    "OLD-ENVIRON",       TEL_N,               0 },
 	{    "AUTH",              TEL_N,               0 },
 	{    "ENCRYPT",           TEL_N,               0 },
-	{    "NEW-ENVIRON",       TEL_N,               ANNOUNCE_DO },
+	{    "NEW-ENVIRON",       TEL_Y,               ANNOUNCE_DO },
 	{    "TN3270E",           TEL_N,               0 }, /* 40 */
 	{    "XAUTH",             TEL_N,               0 },
 	{    "CHARSET",           TEL_Y,               ANNOUNCE_WILL },
@@ -1921,6 +1988,35 @@ struct map_legend_type map_legend_table[] =
 	{ "S W",		"MUDFONT CURVED",	"1x2",	"1x2"	},
 	{ "N W",		"MUDFONT CURVED",	"1x2",	"1x2"	},
 
+        { "NO DIAGONAL",        "UNICODE GRAPHICS",     "2x5",  "2x5"   },
+	{ "SE",                 "UNICODE GRAPHICS",     "⸌",    "2x5"   },
+	{ "NE",                 "UNICODE GRAPHICS",     "⸝",    "2x5"   }, 
+	{ "SE NE",              "UNICODE GRAPHICS",     ">",    "2x5"   }, 
+	{ "SW",                 "UNICODE GRAPHICS",     "⸍",    "2x5"   }, 
+	{ "SE SW",              "UNICODE GRAPHICS",     "⸌⸍",   "2x5"   }, 
+	{ "NE SW",              "UNICODE GRAPHICS",     "/",   "2x5"   }, 
+	{ "SE NE SW",           "UNICODE GRAPHICS",     ">⸍",   "2x5"   }, 
+	{ "NW",                 "UNICODE GRAPHICS",     "⸜",    "2x5"   }, 
+	{ "SE NW",              "UNICODE GRAPHICS",     "\",   "2x5"   }, 
+	{ "NE NW",              "UNICODE GRAPHICS",     "⸝⸜",   "2x5"   }, 
+	{ "SE NE NW",           "UNICODE GRAPHICS",     ">⸜",   "2x5"   }, 
+	{ "SW NW",              "UNICODE GRAPHICS",     "<",    "2x5"   },
+	{ "SE SW NW",           "UNICODE GRAPHICS",     "⸌<",   "2x5"   }, 
+	{ "NE SW NW",           "UNICODE GRAPHICS",     "⸝<",   "2x5"   }, 
+	{ "SE NE SW NW",        "UNICODE GRAPHICS",     "><",   "2x5"   }, 
+	{ "D",                  "UNICODE GRAPHICS",     "-",    "2x5"   }, 
+	{ "N",                  "UNICODE GRAPHICS",     "↑",    "2x5"   }, 
+	{ "S",                  "UNICODE GRAPHICS",     "↓",    "2x5"   }, 
+	{ "N S",                "UNICODE GRAPHICS",     "│",    "2x5"   }, 
+	{ "U",                  "UNICODE GRAPHICS",     "+",    "2x5"   }, 
+	{ "E",                  "UNICODE GRAPHICS",     "🠆",    "2x5"   }, 
+	{ "W",                  "UNICODE GRAPHICS",     "🠄",    "2x5"   }, 
+	{ "E W",                "UNICODE GRAPHICS",     "─",    "2x5"   }, 
+	{ "ROOM L",             "UNICODE GRAPHICS",     "[",    "2x5"   }, 
+	{ "ROOM L CURVED",      "UNICODE GRAPHICS",     "(",    "2x5"   }, 
+	{ "ROOM R",             "UNICODE GRAPHICS",     "]",    "2x5"   }, 
+	{ "ROOM R CURVED",      "UNICODE GRAPHICS",     ")",    "2x5"   },
+
 	{ NULL,			NULL,			NULL,	NULL	}
 };
 
@@ -1956,5 +2052,66 @@ struct map_group_type map_group_table[] =
 	{ "MUDFONT CURVED",		"MUDFONT CURVED",	1, 2,	1, 2,	0, 0,	"" },
 	{ "MUDFONT CURVED BRAILLE",	"MUDFONT CURVED",	1, 2,	1, 2,	0, 0,	"{\\u2818} {\\u28A0} {\\u2844} {\\u2803} {} {} {} {}" },
 
+	{ "UNICODE GRAPHICS",           "UNICODE GRAPHICS",     2, 5,   2, 5,   0, 0,   "{ } {\\u2E0C} {\\u2E1D} {>} {\\u2E0D} {\\u2E0C\\u2E0D} {\\uFF0F} {>\\u2E0D} {\\u2E1C} {\\uFF3C} {\\u2E1D\\u2E1C} {>\\u2E1C} {<} {\\u2E0C<} {\\u2E1D<} {><} {-} {\\u2191} {\\u2193} {\\u2502} {+} {\\U01F806} {\\U01F804} {\\u2500} {[} {(} {]} {)}" },
+
 	{ NULL,				NULL,			0, 0,	0, 0,	0, 0,	"" }
 };
+
+struct stamp_type huge_stamp_table[] =
+{
+	{ " ",  23, "   \n   \n   \n   \n   \n   " },
+	{ "!",  23, "██╗\n██║\n██║\n╚═╝\n██╗\n╚═╝" },
+	{ "\"", 41, "██╗██╗\n2██║██║\n╚═╝╚═╝\n   \n   \n   " },
+	{ "#",  59, " ██╗ ██╗ \n████████╗\n╚██╔═██╔╝\n████████╗\n╚██╔═██╔╝\n ╚═╝ ╚═╝ " },
+	{ "&",  59, "  ████╗  \n ██╔═██╗ \n ╚████╔╝ \n██╔══██═╗\n╚█████╔█║\n ╚════╝╚╝" },
+	{ "%",  47, "██╗ ██╗\n╚═╝██╔╝\n  ██╔╝ \n ██╔╝  \n██╔╝██╗\n╚═╝ ╚═╝" },
+	{ "'",  23, "╗██╗\n██║\n╚═╝\n   \n   \n   " },
+
+	{ "+",  59, "   ██╗   \n   ██║   \n████████╗\n╚══██╔══╝\n   ██║   \n   ╚═╝   " },
+
+	{ "0",  53, " █████╗ \n██╔══██╗\n██║  ██║\n██║  ██║\n╚█████╔╝\n ╚════╝ " },
+	{ "1",  53, "  ▄██╗  \n ████║  \n ╚═██║  \n   ██║  \n ██████╗\n ╚═════╝" },
+	{ "2",  53, "██████╗ \n╚════██╗\n █████╔╝\n██╔═══╝ \n███████╗\n╚══════╝" },
+	{ "3",  53, "██████╗ \n╚════██╗\n █████╔╝\n ╚═══██╗\n██████╔╝\n╚═════╝ " },
+	{ "4",  53, "██╗  ██╗\n██║  ██║\n███████║\n╚════██║\n     ██║\n     ╚═╝" },
+	{ "5",  53, "███████╗\n██╔════╝\n███████╗\n╚════██║\n███████║\n╚══════╝" },
+	{ "6",  53, " █████╗ \n██╔═══╝ \n██████╗ \n██╔══██╗\n╚█████╔╝\n ╚════╝ " },
+	{ "7",  53, "███████╗\n╚════██║\n    ██╔╝\n   ██╔╝ \n   ██║  \n   ╚═╝  " },
+	{ "8",  53, " █████╗ \n██╔══██╗\n╚█████╔╝\n██╔══██╗\n╚█████╔╝\n ╚════╝ " },
+	{ "9",  53, " █████╗ \n██╔══██╗\n╚██████║\n ╚═══██║\n █████╔╝\n ╚════╝ " },
+
+	{ ":",  53, "        \n   ██╗  \n   ╚═╝  \n        \n   ██╗  \n   ╚═╝  " },
+
+	{ "A",  53, " █████╗ \n██╔══██╗\n███████║\n██╔══██║\n██║  ██║\n╚═╝  ╚═╝" },
+	{ "B",  53, "██████╗ \n██╔══██╗\n██████╔╝\n██╔══██╗\n██████╔╝\n╚═════╝ " },
+	{ "C",  53, " ██████╗\n██╔════╝\n██║     \n██║     \n╚██████╗\n ╚═════╝" },
+	{ "D",  53, "██████╗ \n██╔══██╗\n██║  ██║\n██║  ██║\n██████╔╝\n╚═════╝ " },
+	{ "E",  53, "███████╗\n██╔════╝\n█████╗  \n██╔══╝  \n███████╗\n╚══════╝" },
+	{ "F",  53, "███████╗\n██╔════╝\n█████╗  \n██╔══╝  \n██║     \n╚═╝     " },
+	{ "G",  59, " ██████╗ \n██╔════╝ \n██║  ███╗\n██║   ██║\n╚██████╔╝\n ╚═════╝ " },
+	{ "H",  53, "██╗  ██╗\n██║  ██║\n███████║\n██╔══██║\n██║  ██║\n╚═╝  ╚═╝" },
+	{ "I",  47, "██████╗\n╚═██╔═╝\n  ██║  \n  ██║  \n██████╗\n╚═════╝" },
+	{ "J",  53, "     ██╗\n     ██║\n     ██║\n██   ██║\n╚█████╔╝\n ╚════╝ " },
+	{ "K",  53, "██╗  ██╗\n██║ ██╔╝\n█████╔╝ \n██╔═██╗ \n██║  ██╗\n╚═╝  ╚═╝" },
+	{ "L",  53, "██╗     \n██║     \n██║     \n██║     \n███████╗\n╚══════╝" },
+	{ "M",  71, "███╗  ███╗\n████╗████║\n██╔███╔██║\n██║╚█╔╝██║\n██║ ╚╝ ██║\n╚═╝    ╚═╝" },
+	{ "N",  65, "███╗   ██╗\n████╗  ██║\n██╔██╗ ██║\n██║╚██╗██║\n██║ ╚████║\n╚═╝  ╚═══╝" },
+	{ "O",  59, " ██████╗ \n██╔═══██╗\n██║   ██║\n██║   ██║\n╚██████╔╝\n ╚═════╝ " },
+	{ "P",  53, "██████╗ \n██╔══██╗\n██████╔╝\n██╔═══╝ \n██║     \n╚═╝     " },
+	{ "Q",  59, " ██████╗ \n██╔═══██╗\n██║   ██║\n██║▄▄ ██║\n╚██████╔╝\n ╚══▀▀═╝ " },
+	{ "R",  53, "██████╗ \n██╔══██╗\n██████╔╝\n██╔══██╗\n██║  ██║\n╚═╝  ╚═╝" },
+	{ "S",  53, "███████╗\n██╔════╝\n███████╗\n╚════██║\n███████║\n╚══════╝" },
+	{ "T",  59, "████████╗\n╚══██╔══╝\n   ██║   \n   ██║   \n   ██║   \n   ╚═╝   " },
+	{ "U",  59, "██╗   ██╗\n██║   ██║\n██║   ██║\n██║   ██║\n╚██████╔╝\n ╚═════╝ " },
+	{ "V",  59, "██╗   ██╗\n██║   ██║\n██║   ██║\n╚██╗ ██╔╝\n ╚████╔╝ \n  ╚═══╝  " },
+	{ "W",  65, "██╗    ██╗\n██║    ██║\n██║ █╗ ██║\n██║███╗██║\n╚███╔███╔╝\n ╚══╝╚══╝ " },
+	{ "X",  53, "██╗  ██╗\n╚██╗██╔╝\n ╚███╔╝ \n ██╔██╗ \n██╔╝ ██╗\n╚═╝  ╚═╝" },
+	{ "Y",  59, "██╗   ██╗\n╚██╗ ██╔╝\n ╚████╔╝ \n  ╚██╔╝  \n   ██║   \n   ╚═╝   " },
+	{ "Z",  53, "███████╗\n╚══███╔╝\n  ███╔╝ \n ███╔╝  \n███████╗\n╚══════╝" },
+
+	{ "i",  23, "██╗\n╚═╝\n██╗\n██║\n██║\n╚═╝" },
+	{ "n",  47, "       \n       \n██▟███╗\n██║ ██║\n██║ ██║\n╚═╝ ╚═╝" },
+
+	{ NULL, 0, NULL }
+};
+

+ 7 - 2
src/telnet.h

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,11 +13,16 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
+/******************************************************************************
+*                               T I N T I N + +                               *
+*                                                                             *
+*                      coded by Igor van den Hoven 2004                       *
+******************************************************************************/
+
 #include "tintin.h"
 
 /*

+ 170 - 22
src/telopt_client.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2001-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
@@ -62,7 +61,6 @@ extern  int  client_recv_dont_mccp3(struct session *ses, int cplen, unsigned cha
 extern  int  client_recv_wont_mccp3(struct session *ses, int cplen, unsigned char *cpsrc);
 
 extern  int  client_init_mccp3(struct session *ses);
-extern void  client_end_mccp3(struct session *ses);
 extern  int  client_skip_sb(struct session *ses, int cplen, unsigned char *cpsrc);
 extern  int  client_recv_sb(struct session *ses, int cplen, unsigned char *cpsrc);
 
@@ -442,7 +440,7 @@ int client_translate_telopts(struct session *ses, unsigned char *src, int cplen)
 					continue;
 
 				case ASCII_ENQ:
-					check_all_events(ses, SUB_ARG, 0, 1, "VT100 ENQ", gtd->term);
+					check_all_events(ses, SUB_ARG, 0, 1, "VT100 ENQ", gtd->term); // obsolete, but we'll handle it for now.
 					cpsrc++;
 					cplen--;
 					continue;
@@ -672,7 +670,7 @@ int client_recv_sb_ttype(struct session *ses, int cplen, unsigned char *cpsrc)
 		sprintf(mtts, "MTTS %d",
 			(ses->color > 0 ? 1 : 0) +
 			(HAS_BIT(ses->flags, SES_FLAG_SPLIT) ? 0 : 2) +
-			(HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && !HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8) ? 4 : 0) +
+			(HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && !HAS_BIT(ses->charset, CHARSET_FLAG_ALL_TOUTF8) ? 4 : 0) +
 			(ses->color > 16 ? 8 : 0) +
 			(HAS_BIT(ses->flags, SES_FLAG_SCREENREADER) ? 64 : 0) +
 			(ses->color > 256 ? 256 : 0));
@@ -1006,7 +1004,7 @@ int client_recv_sb_mssp(struct session *ses, int cplen, unsigned char *src)
 
 int client_recv_sb_msdp(struct session *ses, int cplen, unsigned char *src)
 {
-	char var[BUFFER_SIZE], val[BUFFER_SIZE], *pto;
+	char var[BUFFER_SIZE], val[BUFFER_SIZE], plain[BUFFER_SIZE], *pto;
 	int i, nest, state[100], last;
 
 	var[0] = val[0] = state[0] = nest = last = 0;
@@ -1087,9 +1085,10 @@ int client_recv_sb_msdp(struct session *ses, int cplen, unsigned char *src)
 
 					if (last)
 					{
+						strip_vt102_codes(val, plain);
 						client_telopt_debug(ses, "RCVD IAC SB MSDP VAR %-20s VAL %s", var, val);
-						check_all_events(ses, SUB_ARG, 1, 2, "IAC SB MSDP %s", var, var, val);
-						check_all_events(ses, SUB_ARG, 0, 2, "IAC SB MSDP", var, val);
+						check_all_events(ses, SUB_ARG, 1, 3, "IAC SB MSDP %s", var, var, val, plain);
+						check_all_events(ses, SUB_ARG, 0, 3, "IAC SB MSDP", var, val, plain);
 					}
 					pto = var;
 				}
@@ -1115,9 +1114,10 @@ int client_recv_sb_msdp(struct session *ses, int cplen, unsigned char *src)
 
 					if (last != MSDP_VAR)
 					{
+						strip_vt102_codes(val, plain);
 						client_telopt_debug(ses, "RCVD IAC SB MSDP VAR %-20s VAL %s", var, val);
-						check_all_events(ses, SUB_ARG, 1, 2, "IAC SB MSDP %s", var, var, val);
-						check_all_events(ses, SUB_ARG, 0, 2, "IAC SB MSDP", var, val);
+						check_all_events(ses, SUB_ARG, 1, 3, "IAC SB MSDP %s", var, var, val, plain);
+						check_all_events(ses, SUB_ARG, 0, 3, "IAC SB MSDP", var, val, plain);
 					}
 					pto = val;
 				}
@@ -1164,9 +1164,10 @@ int client_recv_sb_msdp(struct session *ses, int cplen, unsigned char *src)
 
 		if (last)
 		{
+			strip_vt102_codes(val, plain);
 			client_telopt_debug(ses, "RCVD IAC SB MSDP VAR %-20s VAL %s", var, val);
-			check_all_events(ses, SUB_ARG, 1, 2, "IAC SB MSDP %s", var, var, val);
-			check_all_events(ses, SUB_ARG, 0, 2, "IAC SB MSDP", var, val);
+			check_all_events(ses, SUB_ARG, 1, 3, "IAC SB MSDP %s", var, var, val, plain);
+			check_all_events(ses, SUB_ARG, 0, 3, "IAC SB MSDP", var, val, plain);
 		}
 		i++;
 	}
@@ -1244,8 +1245,9 @@ int client_recv_sb_msdp(struct session *ses, int cplen, unsigned char *src)
 
 					if (last)
 					{
-						check_all_events(ses, SUB_ARG, 1, 2, "IAC SB MSDP2JSON %s", var, var, val);
-						check_all_events(ses, SUB_ARG, 0, 2, "IAC SB MSDP2JSON", var, val);
+						strip_vt102_codes(val, plain);
+						check_all_events(ses, SUB_ARG, 1, 2, "IAC SB MSDP2JSON %s", var, var, plain);
+						check_all_events(ses, SUB_ARG, 0, 2, "IAC SB MSDP2JSON", var, plain);
 					}
 					pto = var;
 				}
@@ -1277,8 +1279,9 @@ int client_recv_sb_msdp(struct session *ses, int cplen, unsigned char *src)
 
 					if (last != MSDP_VAR)
 					{
-						check_all_events(ses, SUB_ARG, 1, 2, "IAC SB MSDP2JSON %s", var, var, val);
-						check_all_events(ses, SUB_ARG, 0, 2, "IAC SB MSDP2JSON", var, val);
+						strip_vt102_codes(val, plain);
+						check_all_events(ses, SUB_ARG, 1, 2, "IAC SB MSDP2JSON %s", var, var, plain);
+						check_all_events(ses, SUB_ARG, 0, 2, "IAC SB MSDP2JSON", var, plain);
 					}
 					pto = val;
 				}
@@ -1322,8 +1325,9 @@ int client_recv_sb_msdp(struct session *ses, int cplen, unsigned char *src)
 
 		if (last)
 		{
-			check_all_events(ses, SUB_ARG, 1, 2, "IAC SB MSDP2JSON %s", var, var, val);
-			check_all_events(ses, SUB_ARG, 0, 2, "IAC SB MSDP2JSON", var, val);
+			strip_vt102_codes(val, plain);
+			check_all_events(ses, SUB_ARG, 1, 2, "IAC SB MSDP2JSON %s", var, var, plain);
+			check_all_events(ses, SUB_ARG, 0, 2, "IAC SB MSDP2JSON", var, plain);
 		}
 		i++;
 	}
@@ -1394,7 +1398,7 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 			{
 				if (!strcasecmp(var, "UTF-8"))
 				{
-					if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && !HAS_BIT(ses->charset, CHARSET_FLAG_BIG5TOUTF8) && !HAS_BIT(ses->charset, CHARSET_FLAG_FANSITOUTF8))
+					if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && !HAS_BIT(ses->charset, CHARSET_FLAG_ALL_TOUTF8))
 					{
 						telnet_printf(ses, 12, "%c%c%c%c UTF-8%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_ACCEPTED, IAC, SE);
 
@@ -1440,6 +1444,60 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 						}
 					}
 				}
+				else if (!strcasecmp(var, "ISO-8859-1") || !strcasecmp(var, "ISO-1"))
+				{
+					if (!check_all_events(ses, SUB_ARG|SUB_SEC, 2, 2, "CATCH IAC SB CHARSET %s %s", buf, var, buf, var))
+					{
+						if (HAS_BIT(ses->charset, CHARSET_FLAG_ISO1TOUTF8))
+						{
+							telnet_printf(ses, 11, "%c%c%c%c %s%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_ACCEPTED, var, IAC, SE);
+							
+							client_telopt_debug(ses, "SENT IAC SB CHARSET ACCEPTED %s", var);
+						}
+						else
+						{
+							telnet_printf(ses, 11, "%c%c%c%c %s%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_REJECTED, var, IAC, SE);
+
+							client_telopt_debug(ses, "SENT IAC SB CHARSET REJECTED %s", var);
+						}
+					}
+				}
+				else if (!strcasecmp(var, "ISO-8859-2") || !strcasecmp(var, "ISO-2"))
+				{
+					if (!check_all_events(ses, SUB_ARG|SUB_SEC, 2, 2, "CATCH IAC SB CHARSET %s %s", buf, var, buf, var))
+					{
+						if (HAS_BIT(ses->charset, CHARSET_FLAG_ISO2TOUTF8))
+						{
+							telnet_printf(ses, 11, "%c%c%c%c %s%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_ACCEPTED, var, IAC, SE);
+							
+							client_telopt_debug(ses, "SENT IAC SB CHARSET ACCEPTED %s", var);
+						}
+						else
+						{
+							telnet_printf(ses, 11, "%c%c%c%c %s%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_REJECTED, var, IAC, SE);
+
+							client_telopt_debug(ses, "SENT IAC SB CHARSET REJECTED %s", var);
+						}
+					}
+				}
+				else if (!strcasecmp(var, "GBK-1"))
+				{
+					if (!check_all_events(ses, SUB_ARG|SUB_SEC, 2, 2, "CATCH IAC SB CHARSET %s %s", buf, var, buf, var))
+					{
+						if (HAS_BIT(ses->charset, CHARSET_FLAG_GBK1TOUTF8))
+						{
+							telnet_printf(ses, 11, "%c%c%c%c GBK-1%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_ACCEPTED, IAC, SE);
+
+							client_telopt_debug(ses, "SENT IAC SB CHARSET ACCEPTED GBK-1");
+						}
+						else
+						{
+							telnet_printf(ses, 11, "%c%c%c%c GB-18030%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_REJECTED, IAC, SE);
+
+							client_telopt_debug(ses, "SENT IAC SB CHARSET REJECTED GBK-1");
+						}
+					}
+				}
 			}
 		}
 		i++;
@@ -1456,6 +1514,26 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 	NEW-ENVIRON
 */
 
+int get_mtts_val(struct session *ses)
+{
+	return
+		(ses->color > 0 ? 1 : 0)
+		+
+		(HAS_BIT(ses->flags, SES_FLAG_SPLIT) ? 0 : 2)
+		+
+		(HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && !HAS_BIT(ses->charset, CHARSET_FLAG_ALL_TOUTF8) ? 4 : 0)
+		+
+		(ses->color > 16 ? 8 : 0)
+		+
+		(HAS_BIT(ses->flags, SES_FLAG_SCREENREADER) ? 64 : 0)
+		+
+//		proxy ? 128 : 0
+//		+
+		(ses->color > 256 ? 256 : 0)
+		+
+		512;
+}
+
 int client_recv_sb_new_environ(struct session *ses, int cplen, unsigned char *src)
 {
 	char buf[BUFFER_SIZE], var[BUFFER_SIZE], val[BUFFER_SIZE], sub1[NUMBER_SIZE], sub2[NUMBER_SIZE];
@@ -1526,8 +1604,57 @@ int client_recv_sb_new_environ(struct session *ses, int cplen, unsigned char *sr
 				{
 					client_telopt_debug(ses, "IAC SB NEW-ENVIRON SEND %s", sub2);
 
-					check_all_events(ses, SUB_ARG|SUB_SEC, 0, 4, "IAC SB NEW-ENVIRON", sub1, sub2, var, "");
-					check_all_events(ses, SUB_ARG|SUB_SEC, 1, 4, "IAC SB NEW-ENVIRON SEND %s", var, sub1, sub2, var, "");
+					if (!check_all_events(ses, SUB_ARG|SUB_SEC, 0, 4, "CATCH IAC SB NEW-ENVIRON", sub1, sub2, var, ""))
+					{
+						check_all_events(ses, SUB_ARG|SUB_SEC, 0, 4, "IAC SB NEW-ENVIRON", sub1, sub2, var, "");
+
+						if (!check_all_events(ses, SUB_ARG|SUB_SEC, 1, 4, "CATCH IAC SB NEW-ENVIRON SEND %s", var, sub1, sub2, var, ""))
+						{
+							check_all_events(ses, SUB_ARG|SUB_SEC, 1, 4, "IAC SB NEW-ENVIRON SEND %s", var, sub1, sub2, var, "");
+
+							if (*var == 0)
+							{
+								telnet_printf(ses, -1, "%c%c%c" "%c%c%s%c%s" "%c%c%s%c%s" "%c%c%s%c%s" "%c%c%s%c%s" "%c%c%s%c%s" "%c%c",
+									IAC, SB, TELOPT_NEW_ENVIRON,
+									ENV_IS, ENV_VAR, "CHARSET", ENV_VAL, get_charset(ses),
+									ENV_IS, ENV_VAR, "CLIENT_NAME", ENV_VAL, CLIENT_NAME,
+									ENV_IS, ENV_VAR, "CLIENT_VERSION", ENV_VAL, CLIENT_VERSION,
+									ENV_IS, ENV_VAR, "MTTS", ENV_VAL, get_mtts_val(ses),
+									ENV_IS, ENV_VAR, "TERMINAL_TYPE", ENV_VAL, gtd->term,
+									IAC, SE);
+							}
+							else if (!strcasecmp(var, "CHARSET"))
+							{
+								telnet_printf(ses, -1, "%c%c%c%c%c%s%c%s%c%c", IAC, SB, TELOPT_NEW_ENVIRON, ENV_IS, ENV_VAR, "CHARSET", ENV_VAL, get_charset(ses), IAC, SE);
+
+								client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR %s VAL %s", "CHARSET", get_charset(ses));
+							}
+							else if (!strcasecmp(var, "CLIENT_NAME"))
+							{
+								telnet_printf(ses, -1, "%c%c%c%c%c%s%c%s%c%c", IAC, SB, TELOPT_NEW_ENVIRON, ENV_IS, ENV_VAR, "CLIENT_NAME", ENV_VAL, CLIENT_NAME, IAC, SE);
+
+								client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR %s VAL %s", "CLIENT_NAME", CLIENT_NAME);
+							}
+							else if (!strcasecmp(var, "CLIENT_VERSION"))
+							{
+								telnet_printf(ses, -1, "%c%c%c%c%c%s%c%s%c%c", IAC, SB, TELOPT_NEW_ENVIRON, ENV_IS, ENV_VAR, "CLIENT_VERSION", ENV_VAL, CLIENT_VERSION, IAC, SE);
+
+								client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR %s VAL %s", "CLIENT_VERSION", CLIENT_VERSION);
+							}
+							else if (!strcasecmp(var, "MTTS") || *var == 0)
+							{
+								telnet_printf(ses, -1, "%c%c%c%c%c%s%c%d%c%c", IAC, SB, TELOPT_NEW_ENVIRON, ENV_IS, ENV_VAR, "MTTS", ENV_VAL, get_mtts_val(ses), IAC, SE);
+
+								client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR MTTS VAL %d", get_mtts_val(ses));
+							}
+							else if (!strcasecmp(var, "TERMINAL_TYPE"))
+							{
+								telnet_printf(ses, -1, "%c%c%c%c%c%s%c%s%c%c", IAC, SB, TELOPT_NEW_ENVIRON, ENV_IS, ENV_VAR, "TERMINAL_TYPE", ENV_VAL, gtd->term, IAC, SE);
+
+								client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR TERMINAL_TYPE VAL %s", gtd->term);
+							}
+						}
+					}
 				}
 				break;
 
@@ -1944,6 +2071,26 @@ int client_init_mccp2(struct session *ses, int cplen, unsigned char *cpsrc)
 	return 5;
 }
 
+void client_end_mccp2(struct session *ses)
+{
+	if (ses->mccp2 == NULL)
+	{
+		return;
+	}
+
+	if (deflateEnd(ses->mccp2) != Z_OK)
+	{
+		tintin_printf2(ses, "MCCP2: FAILED TO DEFLATE_END");
+	}
+
+	free(ses->mccp2);
+
+	ses->mccp2 = NULL;
+
+	client_telopt_debug(ses, "MCCP2: COMPRESSION END, DISABLING MCCP2");
+
+	return;
+}
 
 
 // MCCP3
@@ -2034,6 +2181,7 @@ int client_init_mccp3(struct session *ses)
 	if (deflateInit(stream, Z_BEST_COMPRESSION) != Z_OK)
 	{
 		client_telopt_debug(ses, "MCCP3: FAILED TO INITIALIZE");
+
 		free(stream);
 
 		return FALSE;

+ 39 - 3
src/terminal.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2006                       *
 ******************************************************************************/
@@ -98,6 +97,7 @@ void reset_terminal(struct session *ses)
 	{
 		print_stdout("\e[?1000l\e[?1002l\e[?1004l\e[?1006l");
 	}
+	print_stdout("\e[?25h");
 	print_stdout("\e[23t");
 	print_stdout("\e[>4n");
 }
@@ -201,3 +201,39 @@ int get_scroll_cols(struct session *ses)
 {
 	return ses->wrap;
 }
+
+char *get_charset(struct session *ses)
+{
+	switch (HAS_BIT(ses->charset, CHARSET_FLAG_ALL))
+	{
+		case CHARSET_FLAG_BIG5:
+			return "BIG-5";
+
+		case CHARSET_FLAG_GBK1:
+			return "GBK-1";
+
+		case CHARSET_FLAG_UTF8:
+			return "UTF-8";
+
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5TOUTF8:
+			return "BIG5TOUTF8";
+
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_FANSITOUTF8:
+			return "FANSI";
+		
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_GBK1TOUTF8:
+			return "GBK1TOUTF8";
+
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_KOI8TOUTF8:
+			return "KOI8TOUTF8";
+
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_ISO1TOUTF8:
+			return "ISO1TOUTF8";
+
+		case CHARSET_FLAG_UTF8|CHARSET_FLAG_ISO2TOUTF8:
+			return "ISO2TOUTF8";
+
+		default:
+			return "ASCII";
+	}
+}

+ 74 - 68
src/text.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
@@ -122,7 +121,7 @@ int word_wrap(struct session *ses, char *textin, char *textout, int flags, int *
 {
 	char color[COLOR_SIZE] = { 0 };
 	char *pti, *pto, *lis, *los, *chi, *cho;
-	int cur_height, cur_width, size, skip, lines, cur_col, tab, wrap;
+	int cur_height, cur_width, size, i, skip, lines, cur_col, tab, wrap, cur_space;
 
 	push_call("word_wrap(%s,%p,%p)",ses->name,textin,textout);
 
@@ -145,18 +144,27 @@ int word_wrap(struct session *ses, char *textin, char *textout, int flags, int *
 
 	while (*pti && pto - textout < BUFFER_SIZE)
 	{
-		if (skip_vt102_codes(pti))
-		{
-			get_color_codes(color, pti, color, GET_ONE);
+		skip = skip_vt102_codes(pti);
 
-			if (HAS_BIT(flags, WRAP_FLAG_DISPLAY))
+		if (skip)
+		{
+			if (ses->color)
 			{
-				interpret_vt102_codes(ses, pti, TRUE);
-			}
+				get_color_codes(color, pti, color, GET_ONE);
 
-			for (skip = skip_vt102_codes(pti) ; skip > 0 ; skip--)
+				if (HAS_BIT(flags, WRAP_FLAG_DISPLAY))
+				{
+					interpret_vt102_codes(ses, pti, TRUE);
+				}
+
+				for (i = 0 ; i < skip ; i++)
+				{
+					*pto++ = *pti++;
+				}
+			}
+			else
 			{
-				*pto++ = *pti++;
+				pti += skip;
 			}
 			continue;
 		}
@@ -183,24 +191,25 @@ int word_wrap(struct session *ses, char *textin, char *textout, int flags, int *
 
 			cur_width    = 0;
 			ses->cur_col = 1;
+			cur_space    = 1;
 
 			continue;
 		}
 
-		if (*pti == ' ')
+		if (*pti == ' ' || *pti == '\t')
 		{
+			cur_space = ses->cur_col;
 			los = pto;
 			lis = pti;
 		}
 
 		if (ses->cur_col > wrap)
 		{
-			ses->cur_col = 1;
 			cur_height++;
 
 			if (HAS_BIT(ses->flags, SES_FLAG_WORDWRAP))
 			{
-				if (pto - los >= 15 || wrap <= 20 || !SCROLL(ses))
+				if (ses->cur_col - cur_space >= 15 || wrap <= 20 || !SCROLL(ses))
 				{
 					*pto++ = '\n';
 					pto += sprintf(pto, "%s", color);
@@ -228,16 +237,21 @@ int word_wrap(struct session *ses, char *textin, char *textout, int flags, int *
 			{
 				*pto++ = '\n';
 			}
+			ses->cur_col = 1;
+			cur_space = 1;
 		}
 		else
 		{
-			if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && *pti & 128 && pti[1] != 0)
+			if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 			{
-				*pto++ = *pti++;
-				*pto++ = *pti++;
+				size = get_euc_width(ses, pti, &tab);
 
-				cur_width += 2;
-				ses->cur_col += 2;
+				while (size--)
+				{
+					*pto++ = *pti++;
+				}
+				cur_width += tab;
+				ses->cur_col += tab;
 			}
 			else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
 			{
@@ -258,14 +272,12 @@ int word_wrap(struct session *ses, char *textin, char *textout, int flags, int *
 				{
 					tab = (wrap - ses->cur_col);
 				}
-				pto += sprintf(pto, "%.*s", tab, "        ");
+				pto += sprintf(pto, "%.*s", tab, "                ");
 				pti++;
 
 				cur_width += tab;
 				ses->cur_col += tab;
-
-				los = pto;
-				lis = pti;
+				cur_space = ses->cur_col;
 			}
 			else
 			{
@@ -297,7 +309,7 @@ int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap,
 {
 	char color[COLOR_SIZE] = { 0 };
 	char *pti, *pto, *lis, *los;
-	int cur_height, size, i, lines, cur_col, cur_width, tab;
+	int cur_height, size, i, lines, cur_col, cur_width, tab, skip, cur_space;
 
 	push_call("word_wrap_split(%s,%p,%p,%d,%d,%d,%d)",ses->name,textin,textout,wrap,start,end,flags);
 
@@ -330,31 +342,33 @@ int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap,
 
 	while (*pti && pto - textout < BUFFER_SIZE - 20)
 	{
-		push_call("skip");
-
-		if (cur_height > 10000 || cur_width > 10000)
+		if (cur_height > 10000 || cur_width > 100000)
 		{
-			print_stdout("debug: word_wrap_split: infinite loop?\n");
+			print_stdout("debug: word_wrap_split: wrap %d height %d width %d los %d start %d end %d\n", wrap, cur_height, cur_width, pto - los, start, end);
 			pop_call();
-			return 1;			
+			return 1;
 		}
-		tab = skip_vt102_codes(pti);
 
-		if (tab)
+		skip = skip_vt102_codes(pti);
+
+		if (skip)
 		{
-			get_color_codes(color, pti, color, GET_ONE);
+			if (ses->color)
+			{
+				get_color_codes(color, pti, color, GET_ONE);
 
-			for (i = 0 ; i < tab ; i++)
+				for (i = 0 ; i < skip ; i++)
+				{
+					*pto++ = *pti++;
+				}
+			}
+			else
 			{
-				*pto++ = *pti++;
+				pti += skip;
 			}
-			pop_call();
 			continue;
 		}
 
-		pop_call();
-		push_call("nl");
-
 		if (*pti == '\n')
 		{
 			lines++;
@@ -386,33 +400,26 @@ int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap,
 			}
 
 			cur_col = 1;
+			cur_space = 1;
 			cur_width = 0;
-			pop_call();
 			continue;
 		}
 
 		if (*pti == ' ' || *pti == '\t')
 		{
+			cur_space = cur_col;
 			lis = pti;
 			los = pto;
 		}
 
-		pop_call();
-		push_call("wrap");
-
 		if (cur_col > wrap)
 		{
-			cur_col = 1;
-
 			cur_height++;
 
 			if (HAS_BIT(ses->flags, SES_FLAG_WORDWRAP))
 			{
-				if (pto - los > 15 || wrap <= 20)
+				if (cur_col - cur_space > 15 || wrap <= 20)
 				{
-					los = pto;
-					lis = pti;
-
 					if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height > start && cur_height < end))
 					{
 						*pto++ = '\n';
@@ -421,6 +428,8 @@ int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap,
 					if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
 					{
 						pto += sprintf(pto, "%s", color);
+//						pto += sprintf(pto, "%s(%d,%d,%d)", color, start, end, cur_height);
+
 					}
 				}
 				else
@@ -438,13 +447,11 @@ int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap,
 						pto += sprintf(pto, "%s", color);
 
 					}
+					pti++;
 				}
 			}
 			else
 			{
-				los = pto;
-				lis = pti;
-
 				if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height > start && cur_height < end))
 				{
 					*pto++ = '\n';
@@ -455,26 +462,28 @@ int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap,
 					pto += sprintf(pto, "%s", color);
 				}
 			}
-			pop_call();
+			cur_col = 1;
+			cur_space = 1;
 			continue;
 		}
 
-		pop_call();
-		push_call("default");
-
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && is_big5(pti))
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
 		{
+			size = get_euc_width(ses, pti, &tab);
+
 			if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
 			{
-				*pto++ = *pti++;
-				*pto++ = *pti++;
+				while (size--)
+				{
+					*pto++ = *pti++;
+				}
 			}
 			else
 			{
-				pti += 2;
+				pti += size;
 			}
-			cur_width += 2;
-			cur_col += 2;
+			cur_width += tab;
+			cur_col += tab;
 		}
 		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
 		{
@@ -508,10 +517,7 @@ int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap,
 		{
 			if (*pti == '\t')
 			{
-				los = pto;
-				lis = pti;
-
-				tab = ses->tab_width - (ses->cur_col - 1) % ses->tab_width;
+				tab = ses->tab_width - (cur_col - 1) % ses->tab_width;
 
 				if (cur_col + tab >= wrap)
 				{
@@ -520,12 +526,13 @@ int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap,
 
 				if (!HAS_BIT(flags, WRAP_FLAG_SPLIT) || (cur_height >= start && cur_height < end))
 				{
-					pto += sprintf(pto, "%.*s", tab, "        ");
+					pto += sprintf(pto, "%.*s", tab, "                ");
 				}
 				pti++;
 
 				cur_width += tab;
 				cur_col += tab;
+				cur_space = cur_col;
 			}
 			else
 			{
@@ -541,7 +548,6 @@ int word_wrap_split(struct session *ses, char *textin, char *textout, int wrap,
 				cur_col++;
 			}
 		}
-		pop_call();
 	}
 	*pto = 0;
 

+ 264 - 105
src/tintin.h

@@ -142,6 +142,7 @@
 
 #define ASCII_NUL                        0
 #define ASCII_ENQ                        5 // Ignore if possible
+#define ASCII_HTML_AMP                   6 // Might conflict with VTON
 #define ASCII_BEL                        7
 #define ASCII_BS                         8
 #define ASCII_HTAB                       9
@@ -150,6 +151,9 @@
 #define ASCII_FF                        12
 #define ASCII_CR                        13
 #define ASCII_ESC                       27
+#define ASCII_HTML_OPEN                 28 // Also file separator, whatever that is
+#define ASCII_HTML_CLOSE                30 // Also record separator, whatever that is
+
 #define ASCII_DEL                      127
 
 #define DAEMON_DIR               "daemons"
@@ -157,16 +161,18 @@
 
 #define HISTORY_FILE         "history.txt"
 
-#define STRING_SIZE                  64000
-#define BUFFER_SIZE                  30000
+#define STRING_SIZE                  50000
+#define BUFFER_SIZE                  24000
+#define INPUT_SIZE                   10000
 #define STACK_SIZE                    1000
 #define NUMBER_SIZE                    100
 #define LEGEND_SIZE                     50
 #define COLOR_SIZE                      50
+#define CHAR_SIZE                        5
 #define LIST_SIZE                        2
 
 #define CLIENT_NAME              "TinTin++"
-#define CLIENT_VERSION           "2.01.92 "
+#define CLIENT_VERSION           "2.02.00 "
 
 #define XT_E                            0x27
 #define XT_C                            0x5B
@@ -249,15 +255,17 @@
 #define LIST_GAG                         9
 #define LIST_HIGHLIGHT                  10
 #define LIST_HISTORY                    11
-#define LIST_MACRO                      12
-#define LIST_PATH                       13
-#define LIST_PATHDIR                    14
-#define LIST_PROMPT                     15
-#define LIST_SUBSTITUTE                 16
-#define LIST_TAB                        17
-#define LIST_TICKER                     18
-#define LIST_VARIABLE                   19
-#define LIST_MAX                        20
+#define LIST_LANDMARK                   12
+#define LIST_MACRO                      13
+#define LIST_PATH                       14
+#define LIST_PATHDIR                    15
+#define LIST_PROMPT                     16
+#define LIST_SUBSTITUTE                 17
+#define LIST_TAB                        18
+#define LIST_TERRAIN                    19
+#define LIST_TICKER                     20
+#define LIST_VARIABLE                   21
+#define LIST_MAX                        22
 
 /*
 	Command type
@@ -335,12 +343,23 @@ enum operators
 
 #define BUFFER_FLAG_GREP              BV01
 
-#define CHARSET_FLAG_BIG5                 BV27
-#define CHARSET_FLAG_UTF8                 BV28
-#define CHARSET_FLAG_BIG5TOUTF8           BV29
-#define CHARSET_FLAG_KOI8TOUTF8           BV30
-#define CHARSET_FLAG_FANSITOUTF8          BV31
-#define CHARSET_FLAG_ALL                  CHARSET_FLAG_BIG5|CHARSET_FLAG_UTF8|CHARSET_FLAG_KOI8TOUTF8|CHARSET_FLAG_BIG5TOUTF8|CHARSET_FLAG_FANSITOUTF8
+
+#define CHARSET_FLAG_UTF8                 BV01
+#define CHARSET_FLAG_BIG5                 BV02
+#define CHARSET_FLAG_GBK1                 BV03
+
+#define CHARSET_FLAG_BIG5TOUTF8           BV04
+#define CHARSET_FLAG_FANSITOUTF8          BV05
+#define CHARSET_FLAG_GBK1TOUTF8           BV06
+#define CHARSET_FLAG_ISO1TOUTF8           BV07
+#define CHARSET_FLAG_ISO2TOUTF8           BV08
+#define CHARSET_FLAG_KOI8TOUTF8           BV09
+
+
+#define CHARSET_FLAG_EUC                  CHARSET_FLAG_BIG5|CHARSET_FLAG_GBK1
+#define CHARSET_FLAG_ALL_TOUTF8           CHARSET_FLAG_BIG5TOUTF8|CHARSET_FLAG_FANSITOUTF8|CHARSET_FLAG_GBK1TOUTF8|CHARSET_FLAG_ISO1TOUTF8|CHARSET_FLAG_ISO2TOUTF8|CHARSET_FLAG_KOI8TOUTF8
+#define CHARSET_FLAG_ALL                  CHARSET_FLAG_UTF8|CHARSET_FLAG_ALL_TOUTF8|CHARSET_FLAG_EUC
+
 
 #define COL_BLD                       BV01
 #define COL_UND                       BV02
@@ -408,29 +427,35 @@ enum operators
 
 #define DRAW_FLAG_NONE                   0
 #define DRAW_FLAG_ASCII               BV01
-#define DRAW_FLAG_UTF8                BV02
-#define DRAW_FLAG_CONVERT             BV03
-#define DRAW_FLAG_SCROLL              BV04
-#define DRAW_FLAG_COLOR               BV05
-#define DRAW_FLAG_BLANKED             BV06
-#define DRAW_FLAG_NUMBERED            BV07
-#define DRAW_FLAG_BOXED               BV08
-#define DRAW_FLAG_CROSSED             BV09
-#define DRAW_FLAG_PRUNED              BV10
-#define DRAW_FLAG_ROUNDED             BV11
-#define DRAW_FLAG_TEED                BV12
-#define DRAW_FLAG_CORNERED            BV13
-#define DRAW_FLAG_HOR                 BV14
-#define DRAW_FLAG_VER                 BV15
+#define DRAW_FLAG_BLANKED             BV02
+#define DRAW_FLAG_BOT                 BV03
+#define DRAW_FLAG_BOXED               BV04
+#define DRAW_FLAG_BUMP                BV05
+#define DRAW_FLAG_CIRCLED             BV06
+#define DRAW_FLAG_COLOR               BV07
+#define DRAW_FLAG_CONVERT             BV08
+#define DRAW_FLAG_COPY                BV30 // new
+#define DRAW_FLAG_CORNERED            BV09
+#define DRAW_FLAG_CROSSED             BV10
+#define DRAW_FLAG_FILLED              BV11
+#define DRAW_FLAG_GRID                BV12
+#define DRAW_FLAG_HOR                 BV13
+#define DRAW_FLAG_HUGE                BV14
+#define DRAW_FLAG_JEWELED             BV15
 #define DRAW_FLAG_LEFT                BV16
-#define DRAW_FLAG_RIGHT               BV17
-#define DRAW_FLAG_TOP                 BV18
-#define DRAW_FLAG_BOT                 BV19
-#define DRAW_FLAG_BUMP                BV20
-#define DRAW_FLAG_CIRCLED             BV21
-#define DRAW_FLAG_JEWELED             BV22
-#define DRAW_FLAG_FILLED              BV23
-#define DRAW_FLAG_TUBED               BV24
+#define DRAW_FLAG_LINED               BV17
+#define DRAW_FLAG_NUMBERED            BV18
+#define DRAW_FLAG_PRUNED              BV19
+#define DRAW_FLAG_RIGHT               BV20 
+#define DRAW_FLAG_ROUNDED             BV21
+#define DRAW_FLAG_SCROLL              BV22
+#define DRAW_FLAG_SHADOWED            BV23
+#define DRAW_FLAG_TEED                BV24
+#define DRAW_FLAG_TOP                 BV25
+#define DRAW_FLAG_TRACED              BV26
+#define DRAW_FLAG_TUBED               BV27
+#define DRAW_FLAG_UTF8                BV28
+#define DRAW_FLAG_VER                 BV29
 
 
 #define EVENT_FLAG_CATCH              BV01
@@ -515,14 +540,16 @@ enum operators
 #define SUB_CMP                       (1 << 10)
 #define SUB_LIT                       (1 << 11)
 
+#define TAB_FLAG_FORWARD              BV01
+#define TAB_FLAG_BACKWARD             BV02
+#define TAB_FLAG_LIST                 BV03
+#define TAB_FLAG_SCROLLBACK           BV04
+
 #define REGEX_FLAG_NONE               0
 #define REGEX_FLAG_FIX                (1 <<  0)
 #define REGEX_FLAG_ARG                (1 <<  1)
 #define REGEX_FLAG_CMD                (1 <<  2)
 
-#define WRAP_FLAG_DISPLAY             (1 <<  0)
-#define WRAP_FLAG_WORD                (1 <<  1)
-#define WRAP_FLAG_SPLIT               (1 <<  2)
 
 
 //#define TINTIN_FLAG_RESETBUFFER       (1 <<  0)
@@ -538,6 +565,8 @@ enum operators
 #define TINTIN_FLAG_MOUSETRACKING     (1 <<  9)
 #define TINTIN_FLAG_FLUSH             (1 << 10)
 #define TINTIN_FLAG_DAEMONIZE         (1 << 11)
+#define TINTIN_FLAG_HIDDENCURSOR      (1 << 12)
+#define TINTIN_FLAG_LOCAL             (1 << 13)
 
 #define SES_FLAG_ECHOCOMMAND          BV01
 #define SES_FLAG_SNOOP                BV02
@@ -627,19 +656,23 @@ enum operators
 #define ROOM_FLAG_PATH                BV07
 #define ROOM_FLAG_NOGLOBAL            BV08
 #define ROOM_FLAG_INVIS               BV09
-#define ROOM_FLAG_AVOID_TMP           BV10|ROOM_FLAG_AVOID
+
+#define ROOM_FLAG_AVOID_TMP           BV10|ROOM_FLAG_AVOID // To realign exit and room flags in the future.
 #define ROOM_FLAG_HIDE_TMP            BV11|ROOM_FLAG_HIDE
 #define ROOM_FLAG_LEAVE_TMP           BV12|ROOM_FLAG_LEAVE
 #define ROOM_FLAG_VOID_TMP            BV13|ROOM_FLAG_VOID
 #define ROOM_FLAG_STATIC_TMP          BV13|ROOM_FLAG_STATIC
 #define ROOM_FLAG_CURVED_TMP          BV14|ROOM_FLAG_CURVED
+#define ROOM_FLAG_BLOCK               BV15
+#define ROOM_FLAG_TERRAIN             BV20
 
 // keep synced with room flags
 
 #define EXIT_FLAG_HIDE                BV01
 #define EXIT_FLAG_AVOID               BV02
 #define EXIT_FLAG_INVIS               BV03
-#define EXIT_FLAG_ALL                 BV01|BV02|BV03
+#define EXIT_FLAG_BLOCK               BV04
+#define EXIT_FLAG_ALL                 BV01|BV02|BV03|BV04
 
 #define EXIT_GRID_0                     0
 #define EXIT_GRID_N                     1
@@ -653,9 +686,6 @@ enum operators
 #define EXIT_GRID_SE                    9
 #define EXIT_GRID_SW                   10
 
-#define GRID_FLAG_HIDE                BV02
-#define GRID_FLAG_INVIS               BV03
-
 #define MAP_FLAG_STATIC               BV01
 #define MAP_FLAG_VTMAP                BV02
 #define MAP_FLAG_DIRECTION            BV03
@@ -667,6 +697,11 @@ enum operators
 #define MAP_FLAG_UNICODEGRAPHICS      BV09
 #define MAP_FLAG_BLOCKGRAPHICS        BV10
 #define MAP_FLAG_RESIZE               BV11
+#define MAP_FLAG_SYNC                 BV12
+#define MAP_FLAG_ASCIILENGTH          BV13 // For debugging but might be useful
+#define MAP_FLAG_TERRAIN              BV14
+#define MAP_FLAG_UPDATETERRAIN        BV15
+#define MAP_FLAG_DOUBLED              BV16
 
 #define MAP_SEARCH_NAME                0
 #define MAP_SEARCH_EXITS               1
@@ -702,10 +737,17 @@ enum operators
 #define MAP_UNDO_LINK                 (1 <<  2)
 #define MAP_UNDO_INSERT               (1 <<  3)
 
-#define STR_HASH_FLAG_NOGREP          (1 <<  0)
-
-#define MAX_STR_HASH                  50000
-
+#define TERRAIN_FLAG_DENSE            BV01
+#define TERRAIN_FLAG_AMPLE            BV02
+#define TERRAIN_FLAG_SPARSE           BV03
+#define TERRAIN_FLAG_SCANT            BV04
+#define TERRAIN_FLAG_NARROW           BV05
+#define TERRAIN_FLAG_STANDARD         BV06
+#define TERRAIN_FLAG_WIDE             BV07
+#define TERRAIN_FLAG_VAST             BV08
+#define TERRAIN_FLAG_FADEIN           BV09
+#define TERRAIN_FLAG_FADEOUT          BV10
+#define TERRAIN_FLAG_DOUBLE           BV11
 
 #define MOUSE_FLAG_BUTTON_A             1
 #define MOUSE_FLAG_BUTTON_B             2
@@ -728,6 +770,9 @@ enum operators
 #define STARTUP_FLAG_ARGUMENT           8
 #define STARTUP_FLAG_NOTITLE           16
 
+#define WRAP_FLAG_DISPLAY             (1 <<  0)
+#define WRAP_FLAG_WORD                (1 <<  1)
+#define WRAP_FLAG_SPLIT               (1 <<  2)
 
 #define LEGEND_ASCII                    0
 #define LEGEND_ASCII_MISC              16
@@ -741,18 +786,40 @@ enum operators
 #define LEGEND_MUDFONT_NWS             64
 #define LEGEND_MUDFONT_NES             96
 #define LEGEND_MUDFONT_CURVED         192
-#define LEGEND_MAX                    200
+#define LEGEND_UNICODE_GRAPHICS       196
+#define LEGEND_MAX                    230
+
+#define UNICODE_DIR_SE                  1
+#define UNICODE_DIR_NE                  2
+#define UNICODE_DIR_SW                  4
+#define UNICODE_DIR_NW                  8
+#define UNICODE_DIR_D                  16
+#define UNICODE_DIR_N                  17
+#define UNICODE_DIR_S                  18
+#define UNICODE_DIR_NS                 19
+#define UNICODE_DIR_U                  20
+#define UNICODE_DIR_E                  21
+#define UNICODE_DIR_W                  22
+#define UNICODE_DIR_EW                 23
+#define UNICODE_DIR_RL                 24
+#define UNICODE_DIR_RL_CURVED          25
+#define UNICODE_DIR_RR                 26
+#define UNICODE_DIR_RR_CURVED          27
+
+
 
 #define MAP_COLOR_AVOID                 0
 #define MAP_COLOR_BACK                  1
-#define MAP_COLOR_EXIT                  2
-#define MAP_COLOR_HIDE                  3
-#define MAP_COLOR_INVIS                 4
-#define MAP_COLOR_PATH                  5
-#define MAP_COLOR_ROOM                  6
-#define MAP_COLOR_SYMBOL                7
-#define MAP_COLOR_USER                  8
-#define MAP_COLOR_MAX                   9
+#define MAP_COLOR_BLOCK                 2
+#define MAP_COLOR_EXIT                  3
+#define MAP_COLOR_HIDE                  4
+#define MAP_COLOR_INVIS                 5
+#define MAP_COLOR_PATH                  6
+#define MAP_COLOR_ROOM                  7
+#define MAP_COLOR_SYMBOL                8
+#define MAP_COLOR_USER                  9
+#define MAP_COLOR_MAX                   10
+
 
 /*
 	Some macros to deal with double linked lists
@@ -880,7 +947,7 @@ enum operators
 #define DO_ARRAY(array) struct session *array (struct session *ses, struct listnode *list, char *arg, char *var)
 #define DO_BUFFER(buffer) void buffer (struct session *ses, char *arg)
 #define DO_CHAT(chat) void chat (char *arg1, char *arg2)
-#define DO_CLASS(class) struct session *class (struct session *ses, char *arg1, char *arg2)
+#define DO_CLASS(class) struct session *class (struct session *ses, struct listnode *node, char *arg1, char *arg2)
 #define DO_COMMAND(command) struct session  *command (struct session *ses, char *arg)
 #define DO_CONFIG(config) struct session *config (struct session *ses, char *arg1, char *arg2, int index)
 #define DO_CURSOR(cursor) void cursor (struct session *ses, char *arg)
@@ -891,7 +958,7 @@ enum operators
 #define DO_PATH(path) void path (struct session *ses, char *arg)
 #define DO_PORT(port) struct session *port (struct session *ses, char *arg1, char *arg2, char *arg)
 #define DO_SCREEN(screen) void screen (struct session *ses, int ind, char *arg, char *arg1, char *arg2)
-#define DO_DRAW(draw) void draw (struct session *ses, int top_row, int top_col, int bot_row, int bot_col, int rows, int cols, int flags, char *color, char *arg, char *arg1, char *arg2)
+#define DO_DRAW(draw) void draw (struct session *ses, int top_row, int top_col, int bot_row, int bot_col, int rows, int cols, long long flags, char *color, char *arg, char *arg1, char *arg2)
 
 /*
 	Compatibility
@@ -925,13 +992,24 @@ struct listnode
 	int                     flags;
 	union
 	{
-		pcre          * regex;
-		long long       data;
-		short           val16[4];
-		int             val32[2];
+		pcre              * regex;      // act, alias, gag, highlight, substitute
+		char              * data;       // class
+		struct room_data  * room;       // terrain
+		long long           val64;      // delay, tick, path
+		short               val16[4];   // button
+		int                 val32[2];   // landmark, class
 	};
 };
 
+struct scriptroot
+{
+	struct scriptnode    * next;
+	struct scriptnode    * prev;
+	struct session       * ses;
+	struct listroot      * local;
+	int list;
+};
+
 struct process_data
 {
 	pid_t                   pid;
@@ -950,6 +1028,7 @@ struct tintin_data
 	struct termios          old_terminal;
 	struct screen_data    * screen;
 	struct level_data     * level;
+	struct str_data       * memory;
 	char                  * detach_file;
 	int                     detach_port;
 	struct process_data     detach_info;
@@ -984,17 +1063,20 @@ struct tintin_data
 	char                  * term;
 	char                  * exec;
 	time_t                  time;
+	time_t                  time_input;
+	time_t                  time_session;
 	struct tm		calendar;
 	unsigned long long      utime;
 	long long               timer[TIMER_CPU][5];
 	long long               total_io_ticks;
 	long long               total_io_exec;
 	long long               total_io_delay;
-	int                     str_size;
 	int                     history_size;
 	int                     command_ref[26];
 	int                     msdp_table_size;
 	int                     flags;
+	struct scriptroot     * script_stack[STACK_SIZE];
+	int                     script_index;
 	char                    tintin_char;
 	char                    verbatim_char;
 	char                    repeat_char;
@@ -1025,6 +1107,8 @@ struct session
 	FILE                  * logline_file;
 	char                  * logline_name;
 	time_t                  logline_time;
+	char                  * line_capturefile;
+	int                     line_captureindex;
 	struct listroot       * list[LIST_MAX];
 	int                     created;
 	int                     cur_row;
@@ -1202,6 +1286,8 @@ struct msdp_data
 
 struct str_data
 {
+	struct str_data         * next;
+	struct str_data         * prev;
 	int                       max;
 	int                       len;
 };
@@ -1245,7 +1331,6 @@ struct map_data
 {
 	struct room_data     ** room_list;
 	struct room_data     ** grid_rooms;
-	int                   * grid_flags;
 	FILE                  * logfile;
 	struct link_data      * undo_head;
 	struct link_data      * undo_tail;
@@ -1253,6 +1338,7 @@ struct map_data
 	char                  * buf;
 	char                  * out;
 	char                    color[MAP_COLOR_MAX][COLOR_SIZE];
+	char                    color_raw[MAP_COLOR_MAX][COLOR_SIZE];
 	int                     center_x;
 	int                     center_y;
 	int                     center_z;
@@ -1278,7 +1364,7 @@ struct map_data
 	int                     global_vnum;
 	struct exit_data      * global_exit;
 	int                     version;
-	int                     display_stamp;
+	short                   display_stamp;
 	int                     nofollow;
 	char                    legend[LEGEND_MAX][LEGEND_SIZE];
 	char                    legend_raw[LEGEND_MAX][LEGEND_SIZE];
@@ -1290,17 +1376,19 @@ struct room_data
 	struct exit_data        * l_exit;
 	struct exit_data        * exit_grid[11];
 	int                       vnum;
-	int                       exit_size;
+	short                     exit_size;
 	long long                 exit_dirs;
 	float                     length;
 	float                     weight;
-	int                       search_stamp;
-	int                       display_stamp;
+	short                     search_stamp;
+	short                     display_stamp;
 	int                       flags;
 	int                       w;
 	int                       x;
 	int                       y;
 	int                       z;
+	int                       terrain_index;
+	short                     terrain_flags;
 	char                    * area;
 	char                    * color;
 	char                    * data;
@@ -1324,6 +1412,7 @@ struct exit_data
 	float                     weight;
 	char                    * name;
 	char                    * cmd;
+	char                    * color;
 	char                    * data;
 };
 
@@ -1331,6 +1420,7 @@ struct search_data
 {
 	int                     vnum;
 	short                   stamp;
+	char                  * arg;
 	pcre                  * name;
 	int                     exit_size;
 	long long               exit_dirs;
@@ -1350,7 +1440,7 @@ struct search_data
 
 typedef struct session *ARRAY   (struct session *ses, struct listnode *list, char *arg, char *var);
 typedef void            CHAT    (char *arg1, char *arg2);
-typedef struct session *CLASS   (struct session *ses, char *left, char *right);
+typedef struct session *CLASS   (struct session *ses, struct listnode *node, char *left, char *right);
 typedef struct session *CONFIG  (struct session *ses, char *arg1, char *arg2, int index);
 typedef struct session *COMMAND (struct session *ses, char *arg);
 typedef void            DAEMON  (struct session *ses, char *arg);
@@ -1363,7 +1453,7 @@ typedef void            HISTORY (struct session *ses, char *arg);
 typedef void            BUFFER  (struct session *ses, char *arg);
 typedef void            MSDP    (struct session *ses, struct port_data *buddy, int index);
 typedef void            SCREEN  (struct session *ses, int ind, char *arg, char *arg1, char *arg2);
-typedef void            DRAW    (struct session *ses, int top_row, int top_col, int bot_row, int bot_col, int rows, int cols, int flags, char *color, char *arg, char *arg1, char *arg2);
+typedef void            DRAW    (struct session *ses, int top_row, int top_col, int bot_row, int bot_col, int rows, int cols, long long flags, char *color, char *arg, char *arg1, char *arg2);
 
 /*
 	Structures for tables.c
@@ -1395,7 +1485,7 @@ struct chat_type
 struct class_type
 {
 	char                  * name;
-	CLASS                 * group;
+	CLASS                 * fun;
 };
 
 struct color_type
@@ -1464,6 +1554,8 @@ struct list_type
 	char                  * name_multi;
 	int                     mode;
 	int                     args;
+	int                     script_arg;
+	int                     priority_arg;
 	int                     flags;
 };
 
@@ -1479,9 +1571,10 @@ struct line_type
 struct map_type
 {
 	char                  * name;
-	MAP                   * map;
+	MAP                   * fun;
 	int                     flags;
 	int                     check;
+	char                  * desc;
 };
 
 struct msdp_type
@@ -1531,6 +1624,13 @@ struct rank_type
 	int                     flags;
 };
 
+struct stamp_type
+{
+	char                  * name;
+	int                     length;
+	char                  * desc;
+};
+
 struct substitution_type
 {
 	char                  * name;
@@ -1596,11 +1696,14 @@ extern DO_COMMAND(do_advertise);
 extern DO_COMMAND(do_list);
 extern DO_ARRAY(array_add);
 extern DO_ARRAY(array_clear);
+extern DO_ARRAY(array_collapse);
 extern DO_ARRAY(array_create);
+extern DO_ARRAY(array_explode);
 extern DO_ARRAY(array_insert);
 extern DO_ARRAY(array_delete);
 extern DO_ARRAY(array_find);
 extern DO_ARRAY(array_get);
+extern DO_ARRAY(array_shuffle);
 extern DO_ARRAY(array_simplify);
 extern DO_ARRAY(array_size);
 extern DO_ARRAY(array_set);
@@ -1689,13 +1792,15 @@ extern DO_COMMAND(do_class);
 extern  int count_class(struct session *ses, struct listnode *group);
 extern void parse_class(struct session *ses, char *input, struct listnode *group);
 
-extern DO_CLASS(class_open);
 extern DO_CLASS(class_close);
+extern DO_CLASS(class_kill);
 extern DO_CLASS(class_list);
+extern DO_CLASS(class_load);
+extern DO_CLASS(class_open);
 extern DO_CLASS(class_read);
+extern DO_CLASS(class_save);
 extern DO_CLASS(class_size);
 extern DO_CLASS(class_write);
-extern DO_CLASS(class_kill);
 
 #endif
 
@@ -1746,6 +1851,7 @@ extern DO_CURSOR(cursor_right);
 extern DO_CURSOR(cursor_right_word);
 extern DO_CURSOR(cursor_set);
 extern DO_CURSOR(cursor_suspend);
+extern DO_CURSOR(cursor_tab);
 extern DO_CURSOR(cursor_tab_backward);
 extern DO_CURSOR(cursor_tab_forward);
 extern DO_CURSOR(cursor_auto_tab_backward);
@@ -1781,7 +1887,7 @@ extern DO_COMMAND(do_map);
 
 extern  int follow_map(struct session *ses, char *argument);
 extern void show_vtmap(struct session *ses);
-extern void map_mouse_handler(struct session *ses, char *left, char *right, int row, int col);
+extern void map_mouse_handler(struct session *ses, char *left, char *right, int row, int col, int height, int width);
 extern  int delete_map(struct session *ses);
 
 extern DO_MAP(map_at);
@@ -1792,6 +1898,7 @@ extern DO_MAP(map_debug);
 extern DO_MAP(map_delete);
 extern DO_MAP(map_destroy);
 extern DO_MAP(map_dig);
+extern DO_MAP(map_entrance);
 extern DO_MAP(map_exit);
 extern DO_MAP(map_exitflag);
 extern DO_MAP(map_explore);
@@ -1803,6 +1910,7 @@ extern DO_MAP(map_goto);
 extern DO_MAP(map_info);
 extern DO_MAP(map_insert);
 extern DO_MAP(map_jump);
+extern DO_MAP(map_landmark);
 extern DO_MAP(map_leave);
 extern DO_MAP(map_legend);
 extern DO_MAP(map_link);
@@ -1817,10 +1925,14 @@ extern DO_MAP(map_return);
 extern DO_MAP(map_roomflag);
 extern DO_MAP(map_run);
 extern DO_MAP(map_set);
+extern DO_MAP(map_sync);
+extern DO_MAP(map_terrain);
 extern DO_MAP(map_travel);
 extern DO_MAP(map_undo);
 extern DO_MAP(map_uninsert);
+extern DO_MAP(map_unlandmark);
 extern DO_MAP(map_unlink);
+extern DO_MAP(map_unterrain);
 extern DO_MAP(map_update);
 extern DO_MAP(map_vnum);
 extern DO_MAP(map_write);
@@ -1897,6 +2009,9 @@ extern DO_CONFIG(config_wordwrap);
 #ifndef __SUBSTITUTE_H__
 #define __SUBSTITUTE_H__
 
+extern char *fuzzy_color_code(struct session *ses, char *pti);
+extern char *dim_color_code(struct session *ses, char *pti, int mod);
+extern char *lit_color_code(struct session *ses, char *pti, int mod);
 extern int is_color_code(char *str);
 extern int substitute_color(char *input, char *output, int colors);
 
@@ -1935,7 +2050,7 @@ extern void show_node(struct listroot *root, struct listnode *node, int level);
 extern void show_nest(struct listnode *node, char *result);
 extern void show_list(struct listroot *root, int level);
 extern void delete_node_list(struct session *ses, int type, struct listnode *node);
-extern void delete_node_with_wild(struct session *ses, int index, char *string);
+extern  int delete_node_with_wild(struct session *ses, int index, char *string);
 extern void delete_index_list(struct listroot *root, int index);
 extern  int search_index_list(struct listroot *root, char *text, char *priority);
 extern  int locate_index_list(struct listroot *root, char *text, char *priority);
@@ -1964,6 +2079,15 @@ extern void dump_full_stack(void);
 
 #endif
 
+#ifndef __DICT_H__
+#define __DICT_H__
+
+DO_COMMAND(do_dictionary);
+
+extern int spellcheck_count(struct session *ses, char *in);
+
+#endif
+
 
 #ifndef __DRAW_H__
 #define __DRAW_H__
@@ -1973,23 +2097,21 @@ extern DO_COMMAND(do_draw);
 DO_DRAW(draw_blank);
 DO_DRAW(draw_bot_side);
 DO_DRAW(draw_box);
-DO_DRAW(draw_box_text);
-DO_DRAW(draw_center_left_side);
-DO_DRAW(draw_center_right_side);
+DO_DRAW(draw_corner);
 DO_DRAW(draw_horizontal_line);
 DO_DRAW(draw_left_side);
 DO_DRAW(draw_line);
 DO_DRAW(draw_map);
-DO_DRAW(draw_middle_bot_side);
-DO_DRAW(draw_middle_top_side);
 DO_DRAW(draw_right_side);
 DO_DRAW(draw_side);
-DO_DRAW(draw_vertical_lines);
-DO_DRAW(draw_vertical_lines_text);
 DO_DRAW(draw_square);
+DO_DRAW(draw_stamp);
+DO_DRAW(draw_rain);
+DO_DRAW(draw_table_grid);
 DO_DRAW(draw_text);
 DO_DRAW(draw_top_side);
-DO_DRAW(draw_vertical_line);
+//DO_DRAW(draw_vertical_line);
+DO_DRAW(draw_vertical_lines);
 
 #endif
 
@@ -2012,6 +2134,7 @@ extern void mouse_handler(struct session *ses, int val1, int val2, int val3, cha
 extern DO_COMMAND(do_read);
 extern DO_COMMAND(do_write);
 
+extern struct session *read_file(struct session *ses, FILE *fp, char *filename);
 extern void write_node(struct session *ses, int mode, struct listnode *node, FILE *file);
 
 #endif 
@@ -2051,6 +2174,8 @@ DO_HISTORY(history_write);
 
 extern DO_COMMAND(do_line);
 extern DO_LINE(line_background);
+extern DO_LINE(line_benchmark);
+extern DO_LINE(line_capture);
 extern DO_LINE(line_debug);
 extern DO_LINE(line_gag);
 extern DO_LINE(line_ignore);
@@ -2112,10 +2237,12 @@ void  zlib_free(void *opaque, void *address);
 extern char *restring(char *point, char *string);
 extern char *restringf(char *point, char *fmt, ...);
 extern  int str_len(char *str);
+extern void str_fix(char *str);
 extern char *str_alloc(int len);
 extern void str_clone(char **clone, char *original);
 extern char *str_mim(char *original);
 extern char *str_dup(char *original);
+extern char *str_dup_clone(char *original);
 extern char *str_dup_printf(char *fmt, ...);
 extern char *str_cpy(char **ptr, char *str);
 extern char *str_cpy_printf(char **ptr, char *fmt, ...);
@@ -2124,7 +2251,7 @@ extern char *str_ncpy(char **ptr, char *str, int len);
 extern char *str_cat(char **ptr, char *str);
 extern char *str_cat_chr(char **ptr, char chr);
 extern char *str_cat_printf(char **ptr, char *fmt, ...);
-extern void str_fix(char *str);
+extern char *str_ins(char **str, int index, char *buf);
 extern void str_free(char *ptr);
 
 #endif
@@ -2182,9 +2309,11 @@ extern struct msdp_type msdp_table[];
 #ifndef __NEST_H__
 #define __NEST_H__
 
+extern struct listroot *search_nest_base_ses(struct session *ses, char *arg);
 extern struct listroot *search_nest_root(struct listroot *root, char *arg);
 extern struct listnode *search_base_node(struct listroot *root, char *variable);
 extern struct listnode *search_nest_node(struct listroot *root, char *variable);
+extern struct listnode *search_nest_node_ses(struct session *ses, char *variable);
 extern int search_nest_index(struct listroot *root, char *variable);
 extern struct listroot *update_nest_root(struct listroot *root, char *arg);
 extern void update_nest_node(struct listroot *root, char *arg);
@@ -2196,6 +2325,8 @@ extern struct listnode *get_nest_node_val(struct listroot *root, char *variable,
 extern int get_nest_index(struct listroot *root, char *variable, char **result, int def);
 extern void show_nest_node(struct listnode *node, char **result, int initialize);
 extern void view_nest_node(struct listnode *node, char **str_result, int nest, int initialize);
+extern struct listnode *set_nest_node_ses(struct session *ses, char *arg1, char *format, ...);
+extern struct listnode *add_nest_node_ses(struct session *ses, char *arg1, char *format, ...);
 extern struct listnode *set_nest_node(struct listroot *root, char *arg1, char *format, ...);
 extern struct listnode *add_nest_node(struct listroot *root, char *arg1, char *format, ...);
 extern void copy_nest_node(struct listroot *dst_root, struct listnode *dst, struct listnode *src);
@@ -2222,17 +2353,21 @@ extern void filename_string(char *input, char *output);
 extern struct session *parse_input(struct session *ses, char *input);
 extern struct session *parse_command(struct session *ses, char *input);
 extern  int is_speedwalk(struct session *ses, char *input);
+extern char *substitute_speedwalk(struct session *ses, char *input, char *output);
 extern void process_speedwalk(struct session *ses, char *input);
 extern struct session *parse_tintin_command(struct session *ses, char *input);
+extern int cnt_arg_all(struct session *ses, char *string, int flag);
 extern char *get_arg_all(struct session *ses, char *string, char *result, int verbatim);
 extern char *get_arg_in_braces(struct session *ses, char *string, char *result, int flag);
 extern char *sub_arg_in_braces(struct session *ses, char *string, char *result, int flag, int sub);
 extern char *get_arg_with_spaces(struct session *ses, char *string, char *result, int flag);
 extern char *get_arg_stop_spaces(struct session *ses, char *string, char *result, int flag);
+extern char *get_arg_stop_digits(struct session *ses, char *string, char *result, int flag);
 extern char *space_out(char *string);
 extern char *get_arg_to_brackets(struct session *ses, char *string, char *result);
 extern char *get_arg_at_brackets(struct session *ses, char *string, char *result);
 extern char *get_arg_in_brackets(struct session *ses, char *string, char *result);
+extern char *get_char(struct session *ses, char *string, char *result);
 extern void write_mud(struct session *ses, char *command, int flags);
 extern void do_one_line(char *line, struct session *ses);
 
@@ -2346,6 +2481,7 @@ extern  int get_row_index(struct session *ses, char *arg);
 extern  int get_col_index(struct session *ses, char *arg);
 extern void csip_handler(int var1, int var2, int var3);
 extern void csit_handler(int var1, int var2, int var3);
+extern void rqlp_handler(int event, int button, int row, int col);
 extern void osc_handler(char ind, char *arg);
 extern void print_screen();
 extern void init_screen(int rows, int cols, int pix_rows, int pix_cols);
@@ -2400,7 +2536,7 @@ extern void print_lines(struct session *ses, int flags, char *format, ...);
 extern void show_lines(struct session *ses, char *str);
 extern void tintin_header(struct session *ses, char *format, ...);
 extern void socket_printf(struct session *ses, size_t length, char *format, ...);
-extern void telnet_printf(struct session *ses, size_t length, char *format, ...);
+extern void telnet_printf(struct session *ses, int length, char *format, ...);
 
 extern void tintin_printf2(struct session *ses, char *format, ...);
 extern void tintin_printf(struct session *ses, char *format, ...);
@@ -2471,6 +2607,7 @@ extern struct map_type map_table[];
 extern struct path_type path_table[];
 extern struct port_type port_table[];
 extern struct rank_type rank_table[];
+extern struct stamp_type huge_stamp_table[];
 extern struct substitution_type substitution_table[];
 extern struct telopt_type telopt_table[];
 extern   char *telcmds[];
@@ -2491,7 +2628,9 @@ extern  int client_send_sb_naws(struct session *ses, int cplen, unsigned char *c
 extern void announce_support(struct session *ses, struct port_data *buddy);
 extern  int server_translate_telopts(struct session *ses, struct port_data *buddy, unsigned char *src, int srclen, unsigned char *out, int outlen);
 extern void write_mccp2(struct session *ses, struct port_data *buddy, char *txt, int length);
+extern void client_end_mccp2(struct session *ses);
 extern void end_mccp2(struct session *ses, struct port_data *buddy);
+extern void client_end_mccp3(struct session *ses);
 extern void end_mccp3(struct session *ses, struct port_data *buddy);
 extern void init_msdp_table(void);
 
@@ -2501,15 +2640,16 @@ extern void init_msdp_table(void);
 #ifndef __TERMINAL_H__
 #define __TERMINAL_H__
 
-extern void init_terminal(struct session *ses);
-extern void reset_terminal(struct session *ses);
-extern void save_session_terminal(struct session *ses);
-extern void refresh_session_terminal(struct session *ses);
-extern void echo_on(struct session *ses);
-extern void echo_off(struct session *ses);
-extern void init_terminal_size(struct session *ses);
-extern  int get_scroll_rows(struct session *ses);
-extern  int get_scroll_cols(struct session *ses);
+extern void  init_terminal(struct session *ses);
+extern void  reset_terminal(struct session *ses);
+extern void  save_session_terminal(struct session *ses);
+extern void  refresh_session_terminal(struct session *ses);
+extern void  echo_on(struct session *ses);
+extern void  echo_off(struct session *ses);
+extern void  init_terminal_size(struct session *ses);
+extern  int  get_scroll_rows(struct session *ses);
+extern  int  get_scroll_cols(struct session *ses);
+extern char *get_charset(struct session *ses);
 
 #endif
 
@@ -2535,11 +2675,11 @@ DO_COMMAND(do_regexp);
 extern int substitute(struct session *ses, char *string, char *result, int flags);
 extern int match(struct session *ses, char *str, char *exp, int flags);
 extern int find(struct session *ses, char *str, char *exp, int sub, int flag);
-extern int regexp_compare(pcre *regex, char *str, char *exp, int option, int flag);
+extern int regexp_compare(struct session *ses, pcre *regex, char *str, char *exp, int option, int flag);
 extern int check_one_regexp(struct session *ses, struct listnode *node, char *line, char *original, int option);
 extern int tintin_regexp_check(struct session *ses, char *exp);
 extern int tintin_regexp(struct session *ses, pcre *pcre, char *str, char *exp, int option, int flag);
-extern pcre *regexp_compile(char *exp, int option);
+extern pcre *regexp_compile(struct session *ses, char *exp, int option);
 extern pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *exp, int option);
 extern void  tintin_macro_compile(char *input, char *output);
 
@@ -2649,12 +2789,25 @@ extern int get_utf8_width(char *str, int *width);
 extern int get_utf8_index(char *str, int *index);
 extern int unicode_to_utf8(int index, char *out);
 extern int utf8_strlen(char *str);
-extern int is_big5(char *str);
-extern int big5_to_utf8(char *input, char *output);
-extern int utf8_to_big5(char *input, char *output);
+
+extern int utf8_to_all(struct session *ses, char *in, char *out);
+extern int all_to_utf8(struct session *ses, char *in, char *out);
+extern int iso1_to_utf8(char *input, char *output);
+extern int utf8_to_iso1(char *input, char *output);
+extern int iso2_to_utf8(char *input, char *output);
+extern int utf8_to_iso2(char *input, char *output);
 extern int koi8_to_utf8(char *input, char *output);
 extern int utf8_to_koi8(char *input, char *output);
 extern int fansi_to_utf8(char *input, char *output);
+extern int is_euc_head(struct session *ses, char *str);
+extern int get_euc_size(struct session *ses, char *str);
+extern int get_euc_width(struct session *ses, char *str, int *width);
+extern int is_big5(char *str);
+extern int big5_to_utf8(char *input, char *output);
+extern int utf8_to_big5(char *input, char *output);
+extern int is_gbk1(char *str);
+extern int gbk1_to_utf8(char *input, char *output);
+extern int utf8_to_gbk1(char *input, char *output);
 
 #endif
 
@@ -2664,13 +2817,17 @@ extern int fansi_to_utf8(char *input, char *output);
 extern DO_COMMAND(do_variable);
 extern DO_COMMAND(do_unvariable);
 extern DO_COMMAND(do_local);
+extern DO_COMMAND(do_cat);
 extern DO_COMMAND(do_format);
 extern DO_COMMAND(do_replace);
 
+extern  int valid_variable(struct session *ses, char *arg);
 extern  int string_raw_str_len(struct session *ses, char *str, int start, int end);
 extern  int string_str_raw_len(struct session *ses, char *str, int start, int end);
+extern  int translate_color_names(struct session *ses, char *string, char *result);
 extern  int get_color_names(struct session *ses, char *htype, char *result);
 extern void lowerstring(char *str);
+extern void upperstring(char *str);
 extern void numbertocharacter(struct session *ses, char *str);
 extern void charactertonumber(struct session *ses, char *str);
 extern  int delete_variable(struct session *ses, char *variable);
@@ -2699,6 +2856,8 @@ extern void erase_scroll_region(struct session *ses);
 extern void reset(struct session *ses);
 extern void scroll_region(struct session *ses, int top, int bottom);
 extern void reset_scroll_region(struct session *ses);
+extern int find_color_code(char *str);
+extern int find_secure_color_code(char *str);
 extern int skip_vt102_codes(char *str);
 extern int skip_vt102_codes_non_graph(char *str);
 extern void strip_vt102_codes(char *str, char *buf);

+ 18 - 27
src/tokenize.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*               (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                  *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                     coded by Igor van den Hoven 2008                        *
+*                      coded by Igor van den Hoven 2008                       *
 ******************************************************************************/
 
 #include "tintin.h"
@@ -62,19 +61,6 @@ struct script_regex
 	int                    val;
 };
 
-struct scriptroot
-{
-	struct scriptnode    * next;
-	struct scriptnode    * prev;
-	struct session       * ses;
-	struct listroot      * local;
-	int list;
-};
-
-struct scriptroot *script_stack[1001];
-
-int script_index;
-
 void debugtoken(struct session *ses, struct scriptroot *root, struct scriptnode *token)
 {
 	push_call("debugtoken(%p,%d,%p,%d)",ses,root->list,token,token->type);
@@ -276,7 +262,7 @@ void handlereturntoken(struct session *ses, struct scriptnode *token)
 
 	substitute(ses, token->str, arg, SUB_VAR|SUB_FUN);
 
-	set_nest_node(ses->list[LIST_VARIABLE], "result", "%s", arg);
+	set_nest_node_ses(ses, "result", "%s", arg);
 }
 
 void handleswitchtoken(struct session *ses, struct scriptnode *token)
@@ -349,9 +335,9 @@ char *get_arg_parse(struct session *ses, struct scriptnode *token)
 {
 	static char buf[5];
 
-	if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && token->data->arg[0] & 128 && token->data->arg[1] != 0)
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, token->data->arg))
 	{
-		token->data->arg += sprintf(buf, "%c%c", token->data->arg[0], token->data->arg[1]);
+		token->data->arg += sprintf(buf, "%.*s", get_euc_size(ses, token->data->arg), token->data->arg);
 	}
 	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(token->data->arg))
 	{
@@ -455,7 +441,7 @@ void init_local(struct session *ses)
 	root->list = LIST_VARIABLE;
 	root->local = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 
-	script_stack[0] = root;
+	gtd->script_stack[0] = root;
 
 	return;
 }
@@ -466,7 +452,7 @@ struct listroot *local_list(struct session *ses)
 
 	push_call("local_list(%p)",ses);
 
-	root = script_stack[script_index]->local;
+	root = gtd->script_stack[gtd->script_index]->local;
 
 	pop_call();
 	return root;
@@ -843,7 +829,7 @@ struct scriptnode *parse_script(struct scriptroot *root, int lvl, struct scriptn
 				}
 				else
 				{
-					set_nest_node(root->ses->list[LIST_VARIABLE], token->str, "%s", get_arg_foreach(root, token));
+					set_nest_node_ses(root->ses, token->str, "%s", get_arg_foreach(root, token));
 
 					if (*token->data->arg == 0)
 					{
@@ -878,7 +864,7 @@ struct scriptnode *parse_script(struct scriptroot *root, int lvl, struct scriptn
 					resetlooptoken(root->ses, token);
 				}
 
-				set_nest_node(root->ses->list[LIST_VARIABLE], token->str, "%lld", token->data->cnt);
+				set_nest_node_ses(root->ses, token->str, "%lld", token->data->cnt);
 
 				token->data->cnt += token->data->inc;
 
@@ -911,7 +897,7 @@ struct scriptnode *parse_script(struct scriptroot *root, int lvl, struct scriptn
 
 				}
 
-				set_nest_node(root->ses->list[LIST_VARIABLE], token->str, "%s", get_arg_parse(root->ses, token));
+				set_nest_node_ses(root->ses, token->str, "%s", get_arg_parse(root->ses, token));
 
 				if (*token->data->arg == 0)
 				{
@@ -1183,13 +1169,16 @@ struct session *script_driver(struct session *ses, int list, char *str)
 
 	gtd->level->input += list != LIST_COMMAND;
 
-	script_stack[++script_index] = root;
+	gtd->script_stack[++gtd->script_index] = root;
 
 	tokenize_script(root, 0, str);
 
 	ses = (struct session *) parse_script(root, 0, root->next, root->prev);
 
-	script_index--;
+	if (--gtd->script_index == 0)
+	{
+		DEL_BIT(gtd->flags, TINTIN_FLAG_LOCAL);
+	}
 
 	gtd->level->input -= list != LIST_COMMAND;
 
@@ -1201,6 +1190,8 @@ struct session *script_driver(struct session *ses, int list, char *str)
 	free_list(root->local);
 	free(root);
 
+
+	
 	if (HAS_BIT(ses->flags, SES_FLAG_CLOSED))
 	{
 		pop_call();

+ 1 - 2
src/trigger.c

@@ -31,7 +31,7 @@ DO_COMMAND(do_action)
 {
 	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE];
 
-	arg = get_arg_in_braces(ses, arg, arg1, GET_ALL);
+	arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
 	arg = get_arg_in_braces(ses, arg, arg3, GET_ALL);
 
@@ -714,7 +714,6 @@ DO_COMMAND(do_prompt)
 	arg = get_arg_in_braces(ses, arg, arg1, GET_ALL);
 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
 
-
 	if (*arg1 == 0)
 	{
 		show_list(ses->list[LIST_PROMPT], 0);

+ 96 - 33
src/update.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,15 +13,14 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*               (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                  *
+*                               T I N T I N + +                               *
 *                                                                             *
-*                     coded by Igor van den Hoven 2006                        *
+*                      coded by Igor van den Hoven 2006                       *
 ******************************************************************************/
 
 #include "tintin.h"
@@ -51,8 +50,9 @@ extern void close_timer(int timer);
 
 void mainloop(void)
 {
-	static struct timeval start_time, end_time, span_time, wait_time;
+	static struct timeval start_time, end_time, wait_time;
 	static struct pulse_type pulse;
+	static int wait_time_val, span_time_val;
 
 	pulse.update_input    =  0 + PULSE_UPDATE_INPUT;
 	pulse.update_sessions =  0 + PULSE_UPDATE_SESSIONS;
@@ -73,8 +73,8 @@ void mainloop(void)
 	{
 		gettimeofday(&start_time, NULL);
 
-		gtd->total_io_exec  += span_time.tv_usec;
-		gtd->total_io_delay += wait_time.tv_usec;
+		gtd->total_io_exec  += span_time_val;
+		gtd->total_io_delay += wait_time_val;
 
 		if (--pulse.update_delays == 0)
 		{
@@ -169,19 +169,27 @@ void mainloop(void)
 
 		if (start_time.tv_sec == end_time.tv_sec)
 		{
-			span_time.tv_usec = end_time.tv_usec - start_time.tv_usec;
+			span_time_val = end_time.tv_usec - start_time.tv_usec;
 		}
 		else
 		{
-			span_time.tv_usec = (end_time.tv_sec * 1000000LL + end_time.tv_usec) - (start_time.tv_sec * 1000000LL + start_time.tv_usec);
+			span_time_val = (end_time.tv_sec * 1000000LL + end_time.tv_usec) - (start_time.tv_sec * 1000000LL + start_time.tv_usec);
 		}
 
-		wait_time.tv_usec = 1000000 / PULSE_PER_SECOND - span_time.tv_usec;
+		wait_time_val = 1000000 / PULSE_PER_SECOND - span_time_val;
+
+		wait_time.tv_usec = 1000000 / PULSE_PER_SECOND - span_time_val;
 
-		if (wait_time.tv_usec > 0)
+		if (wait_time_val > 0)
 		{
+			wait_time.tv_usec = wait_time_val;
+
 			select(0, NULL, NULL, NULL, &wait_time);
 		}
+		else
+		{
+			wait_time_val = 0;
+		}
 	}
 	pop_call();
 	return;
@@ -191,6 +199,7 @@ void update_input(void)
 {
 	fd_set read_fd;
 	static struct timeval timeout;
+	static int sleep;
 
 	if (gtd->detach_port)
 	{
@@ -219,6 +228,16 @@ void update_input(void)
 		return;
 	}
 
+	if (gtd->time_input + 10 < gtd->time)
+	{
+		if (sleep < 10)
+		{
+			sleep++;
+			return;
+		}
+		sleep = 0;
+	}
+
 	while (TRUE)
 	{
 		FD_ZERO(&read_fd);
@@ -251,9 +270,20 @@ void update_sessions(void)
 {
 	fd_set read_fd, error_fd;
 	static struct timeval timeout;
+	static int sleep;
 	struct session *ses;
 	int rv;
 
+	if (gtd->time_session + 10 < gtd->time)
+	{
+		if (sleep < 10)
+		{
+			sleep++;
+			return;
+		}
+		sleep = 0;
+	}
+
 	push_call("update_sessions(void)");
 
 	open_timer(TIMER_UPDATE_SESSIONS);
@@ -278,15 +308,15 @@ void update_sessions(void)
 
 					if (rv < 0)
 					{
-						break;
+						break; // bug report after removal.
 
-						syserr_printf(ses, "update_sessions: %s:", ses->name);
+						syserr_printf(ses, "update_sessions: select:");
 
 						cleanup_session(ses);
 
 						gtd->mud_output_len = 0;
 
-						break;;
+						break;
 					}
 
 					if (rv == 0)
@@ -320,6 +350,8 @@ void update_sessions(void)
 					}
 				}
 
+				gtd->time_session = gtd->time;
+
 				if (gtd->mud_output_len)
 				{
 					readmud(ses);
@@ -452,13 +484,17 @@ void update_daemon(void)
 			while (gtd->detach_sock)
 			{
 				FD_ZERO(&read_fd);
+//				FD_ZERO(&write_fd);
 				FD_ZERO(&error_fd);
 
 				FD_SET(gtd->detach_sock, &read_fd);
+//				FD_SET(gtd->detach_sock, &write_fd);
 				FD_SET(gtd->detach_sock, &error_fd);
 
 				rv = select(FD_SETSIZE, &read_fd, NULL, &error_fd, &timeout);
 
+//				tintin_printf2(gtd->ses, "debug: rv: %d (%d,%d,%d)\n", rv, FD_ISSET(gtd->detach_sock, &read_fd), FD_ISSET(gtd->detach_sock, &write_fd), FD_ISSET(gtd->detach_sock, &error_fd));
+
 				if (rv == 0)
 				{
 					break;
@@ -486,8 +522,20 @@ void update_daemon(void)
 						goto attach;
 					}
 
+/*					if (!FD_ISSET(gtd->detach_sock, &write_fd))
+					{
+						FD_CLR(gtd->detach_sock, &read_fd);
+
+						gtd->detach_sock = close(gtd->detach_sock);
+
+						show_error(gtd->ses, LIST_COMMAND, "update_daemon: detach_sock: write_fd");
+
+						goto attach;
+					}
+*/
 					if (!FD_ISSET(gtd->detach_sock, &read_fd))
 					{
+//						gtd->detach_sock = close(gtd->detach_sock); // experimental
 						break;
 					}
 					process_input();
@@ -697,18 +745,18 @@ void tick_update(void)
 		{
 			node = root->list[root->update];
 
-			if (node->data == 0)
+			if (node->val64 == 0)
 			{
-				node->data = gtd->utime + (long long) (get_number(ses, node->arg3) * 1000000LL);
+				node->val64 = gtd->utime + (long long) (get_number(ses, node->arg3) * 1000000LL);
 
-				show_info(ses, LIST_TICKER, "#INFO TICK {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->data);
+				show_info(ses, LIST_TICKER, "#INFO TICK {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->val64);
 			}
 
-			if (node->data <= gtd->utime)
+			if (node->val64 <= gtd->utime)
 			{
-				node->data += (long long) (get_number(ses, node->arg3) * 1000000LL);
+				node->val64 += (long long) (get_number(ses, node->arg3) * 1000000LL);
 
-				show_info(ses, LIST_TICKER, "#INFO TICK {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->data);
+				show_info(ses, LIST_TICKER, "#INFO TICK {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->val64);
 
 				if (!HAS_BIT(root->flags, LIST_FLAG_IGNORE))
 				{
@@ -752,14 +800,14 @@ void delay_update(void)
 		{
 			node = root->list[root->update];
 
-			if (node->data == 0)
+			if (node->val64 == 0)
 			{
-				node->data = gtd->utime + (long long) (get_number(ses, node->arg3) * 1000000LL);
+				node->val64 = gtd->utime + (long long) (get_number(ses, node->arg3) * 1000000LL);
 
-				show_info(ses, LIST_DELAY, "#INFO DELAY {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->data);
+				show_info(ses, LIST_DELAY, "#INFO DELAY {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->val64);
 			}
 
-			if (node->data <= gtd->utime)
+			if (node->val64 <= gtd->utime)
 			{
 				strcpy(buf, node->arg2);
 
@@ -792,15 +840,15 @@ void path_update(void)
 		{
 			node = root->list[root->update];
 
-			if (node->data > 0 && node->data <= gtd->utime)
+			if (node->val64 > 0 && node->val64 <= gtd->utime)
 			{
 				root->update++;
 
-				node->data = 0;
+				node->val64 = 0;
 
-				show_debug(ses, LIST_PATH, "#DEBUG PATH {%s}", node->arg1);
+				show_debug(ses, LIST_COMMAND, "#DEBUG PATH {%s}", node->arg1);
 
-				script_driver(ses, LIST_PATH, node->arg1);
+				script_driver(ses, LIST_COMMAND, node->arg1);
 			}
 			break;
 		}
@@ -834,8 +882,18 @@ void packet_update(void)
 
 			ses->more_output[0] = 0;
 
-			process_mud_output(ses, result, TRUE);
+			if (HAS_BIT(ses->charset, CHARSET_FLAG_ALL_TOUTF8))
+			{
+				char buf[BUFFER_SIZE];
+
+				all_to_utf8(ses, result, buf);
 
+				process_mud_output(ses, buf, TRUE);
+			}
+			else
+			{
+				process_mud_output(ses, result, TRUE);
+			}
 			DEL_BIT(ses->flags, SES_FLAG_READMUD);
 
 			if (HAS_BIT(ses->flags, SES_FLAG_SPLIT))
@@ -1024,14 +1082,14 @@ void time_update(void)
 
 void show_cpu(struct session *ses)
 {
-	long long total_cpu;
+	long long total_cpu = 0;
 	int timer;
 
 	tintin_printf2(ses, "Section                           Time (usec)    Freq (msec)  %%Prog         %%CPU");
 
 	tintin_printf2(ses, "");
 
-	for (total_cpu = timer = 0 ; timer < TIMER_CPU ; timer++)
+	for (timer = 0 ; timer < TIMER_CPU ; timer++)
 	{
 		total_cpu += display_timer(ses, timer);
 	}
@@ -1040,6 +1098,9 @@ void show_cpu(struct session *ses)
 
 	tintin_printf2(ses, "Unknown CPU Usage:             %7.3f percent", (gtd->total_io_exec - total_cpu) * 100.0 / (gtd->total_io_delay + gtd->total_io_exec));
 	tintin_printf2(ses, "Average CPU Usage:             %7.3f percent", (gtd->total_io_exec)             * 100.0 / (gtd->total_io_delay + gtd->total_io_exec));
+//	tintin_printf2(ses, "Total   CPU Usecs:             %10ld", gtd->total_io_exec);
+//	tintin_printf2(ses, "Total   CPU Delay:             %10ld", gtd->total_io_delay);
+
 }
 
 
@@ -1059,7 +1120,9 @@ long long display_timer(struct session *ses, int timer)
 		return 0;
 	}
 
-	indicated_usage = gtd->timer[timer][0] / gtd->timer[timer][1] * gtd->timer[timer][4];
+//	indicated_usage = gtd->timer[timer][0] / gtd->timer[timer][1] * gtd->timer[timer][4];
+
+	indicated_usage = gtd->timer[timer][0];
 
 	tintin_printf2(ses, "%-30s%8lld       %8lld      %8.2f     %8.3f",
 		timer_table[timer].name,
@@ -1084,7 +1147,7 @@ void open_timer(int timer)
 
 	if (gtd->timer[timer][2] == 0)
 	{
-		gtd->timer[timer][2] = current_time ;
+		gtd->timer[timer][2] = current_time;
 	}
 	else
 	{

Datei-Diff unterdrückt, da er zu groß ist
+ 480 - 527
src/utf8.c


+ 3 - 3
src/utils.c

@@ -471,7 +471,7 @@ void socket_printf(struct session *ses, size_t length, char *format, ...)
 	}
 }
 
-void telnet_printf(struct session *ses, size_t length, char *format, ...)
+void telnet_printf(struct session *ses, int length, char *format, ...)
 {
 	size_t size;
 
@@ -482,7 +482,7 @@ void telnet_printf(struct session *ses, size_t length, char *format, ...)
 	size = vsprintf(buf, format, args);
 	va_end(args);
 
-	if (size != length && HAS_BIT(ses->telopts, TELOPT_FLAG_DEBUG))
+	if (length != -1 && size != length && HAS_BIT(ses->telopts, TELOPT_FLAG_DEBUG))
 	{
 		tintin_printf(ses, "DEBUG TELNET: telnet_printf size difference: %d vs %d", size, length);
 	}
@@ -491,7 +491,7 @@ void telnet_printf(struct session *ses, size_t length, char *format, ...)
 	{
 		SET_BIT(ses->telopts, TELOPT_FLAG_TELNET);
 
-		write_line_mud(ses, buf, length);
+		write_line_mud(ses, buf, size);
 
 		DEL_BIT(ses->telopts, TELOPT_FLAG_TELNET);
 	}

+ 326 - 41
src/variable.c

@@ -69,7 +69,7 @@ DO_COMMAND(do_variable)
 	}
 	else
 	{
-		if (is_math(ses, arg1))
+		if (!valid_variable(ses, arg1))
 		{
 			show_message(ses, LIST_VARIABLE, "#VARIABLE: INVALID VARIALBE NAME {%s}.", arg1);
 			return ses;
@@ -115,7 +115,17 @@ DO_COMMAND(do_local)
 	}
 	else if (*arg1 && *arg == 0)
 	{
-		node = search_nest_node(root, arg1);
+		root = search_nest_base_ses(ses, arg1);
+
+		if (root)
+		{
+			node = search_nest_node_ses(ses, arg1);
+		}
+		else
+		{
+			root = local_list(ses);
+			node = NULL;
+		}
 
 		if (node)
 		{
@@ -132,7 +142,7 @@ DO_COMMAND(do_local)
 
 		arg = sub_arg_in_braces(ses, arg, str, GET_ALL, SUB_VAR|SUB_FUN);
 
-		node = set_nest_node(root, arg1, "%s", str);
+		node = set_nest_node_ses(ses, arg1, "%s", str);
 
 		while (*arg)
 		{
@@ -149,6 +159,8 @@ DO_COMMAND(do_local)
 		show_message(ses, LIST_VARIABLE, "#OK. LOCAL VARIABLE {%s} HAS BEEN SET TO {%s}.", arg1, str);
 
 		str_free(str);
+
+		SET_BIT(gtd->flags, TINTIN_FLAG_LOCAL);
 	}
 	return ses;
 }
@@ -176,11 +188,52 @@ DO_COMMAND(do_unvariable)
 	return ses;
 }
 
+DO_COMMAND(do_cat)
+{
+	char arg1[BUFFER_SIZE], *str;
+	struct listnode *node;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_NST, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0 || *arg == 0)
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: CAT {<VARIABLE>} {<ARGUMENT>}");
+	}
+	else
+	{
+		str = str_alloc(UMAX(strlen(arg), BUFFER_SIZE));
+
+		if ((node = search_nest_node_ses(ses, arg1)) == NULL)
+		{
+			arg = sub_arg_in_braces(ses, arg, str, GET_ALL, SUB_VAR|SUB_FUN);
+
+			node = set_nest_node(ses->list[LIST_VARIABLE], arg1, "%s", str);
+		}
+
+		while (*arg)
+		{
+			arg = sub_arg_in_braces(ses, arg, str, GET_ALL, SUB_VAR|SUB_FUN);
+
+			check_all_events(ses, SUB_ARG, 1, 2, "VARIABLE UPDATE %s", arg1, arg1, str);
+
+			if (*str)
+			{
+				str_cat(&node->arg2, str);
+			}
+		}
+
+		check_all_events(ses, SUB_ARG, 1, 1, "VARIABLE UPDATED %s", arg1, arg1, str);
+
+		show_message(ses, LIST_VARIABLE, "#CAT: VARIABLE {%s} HAS BEEN SET TO {%s}.", arg1, node->arg2);
+
+		str_free(str);
+	}
+	return ses;
+}
 
 DO_COMMAND(do_replace)
 {
-	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE], buf[BUFFER_SIZE], tmp[BUFFER_SIZE], *pti, *ptm;
-	struct listroot *root;
+	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE], arg3[BUFFER_SIZE], tmp[BUFFER_SIZE], *pti, *ptm, *str;
 	struct listnode *node;
 
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_NST, SUB_VAR|SUB_FUN);
@@ -194,18 +247,11 @@ DO_COMMAND(do_replace)
 		return ses;
 	}
 
-	root = local_list(ses);
-
-	if ((node = search_nest_node(root, arg1)) == NULL)
+	if ((node = search_nest_node_ses(ses, arg1)) == NULL)
 	{
-		root = ses->list[LIST_VARIABLE];
+		show_error(ses, LIST_VARIABLE, "#REPLACE: VARIABLE {%s} NOT FOUND.", arg1);
 
-		if ((node = search_nest_node(root, arg1)) == NULL)
-		{
-			show_error(ses, LIST_VARIABLE, "#REPLACE: VARIABLE {%s} NOT FOUND.", arg1);
-
-			return ses;
-		}
+		return ses;
 	}
 
 	if (tintin_regexp(ses, NULL, node->arg2, arg2, 0, REGEX_FLAG_CMD) == FALSE)
@@ -215,7 +261,7 @@ DO_COMMAND(do_replace)
 	else
 	{
 		pti = node->arg2;
-		*buf = 0;
+		str = str_dup("");
 
 		do
 		{
@@ -235,19 +281,36 @@ DO_COMMAND(do_replace)
 
 			substitute(ses, arg3, tmp, SUB_CMD);
 
-			cat_sprintf(buf, "%s%s", pti, tmp);
+			str_cat_printf(&str, "%s%s", pti, tmp);
 
 			pti = ptm + strlen(gtd->cmds[0]);
 		}
 		while (tintin_regexp(ses, NULL, pti, arg2, 0, REGEX_FLAG_CMD));
 
-		strcat(buf, pti);
+		str_cat(&str, pti);
+
+		str_cpy(&node->arg2, str);
 
-		set_nest_node(root, arg1, "%s", buf);
+		str_free(str);
 	}
 	return ses;
 }
 
+int valid_variable(struct session *ses, char *arg)
+{
+	if (*arg == 0)
+	{
+		return FALSE;
+	}
+
+	if (is_math(ses, arg))
+	{
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 /*
 	support routines for #format
 */
@@ -272,7 +335,7 @@ void numbertocharacter(struct session *ses, char *str)
 	{
 		sprintf(str, "%c", (int) get_number(ses, str));
 	}
-	else if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5))
+	else if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC))
 	{
 		sprintf(str, "%c%c", (unsigned int) get_number(ses, str) % 256, (unsigned int) get_number(ses, str) / 256);
 	}
@@ -290,9 +353,16 @@ void charactertonumber(struct session *ses, char *str)
 {
 	int result;
 
-	if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && is_big5(str))
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, str))
 	{
-		result = (unsigned char) str[0] + (unsigned char) str[1] * 256;
+		if (get_euc_size(ses, str) == 4)
+		{
+			result = (unsigned char) str[0] + (unsigned char) str[1] * 256 + (unsigned char) str[2] * 256 * 256 + (unsigned char) str[3] * 256 * 256 * 256;
+		}
+		else
+		{
+			result = (unsigned char) str[0] + (unsigned char) str[1] * 256;
+		}
 	}
 	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(str))
 	{
@@ -307,9 +377,16 @@ void charactertonumber(struct session *ses, char *str)
 
 void charactertohex(struct session *ses, char *str)
 {
-	if (HAS_BIT(ses->charset, CHARSET_FLAG_BIG5) && is_big5(str))
+	if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, str))
 	{
-		sprintf(str, "%u", (unsigned char) str[0] + (unsigned char) str[1] * 256);
+		if (get_euc_size(ses, str) == 4)
+		{
+			sprintf(str, "%u", (unsigned char) str[0] + (unsigned char) str[1] * 256 + (unsigned char) str[2] * 256 * 256 + (unsigned char) str[3] * 256 * 256 * 256);
+		}
+		else
+		{
+			sprintf(str, "%u", (unsigned char) str[0] + (unsigned char) str[1] * 256);
+		}
 	}
 	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(str))
 	{
@@ -335,6 +412,81 @@ void colorstring(struct session *ses, char *str)
 	strcpy(str, result);
 }
 
+int translate_color_names(struct session *ses, char *string, char *result)
+{
+	int cnt;
+
+	*result = 0;
+
+	if (*string == '<')
+	{
+		strcpy(result, string);
+
+		return TRUE;
+	}
+
+	if (*string == '\\')
+	{
+		strcpy(result, string);
+
+		return TRUE;
+	}
+
+	while (*string)
+	{
+
+		if (isalpha(*string))
+		{
+			for (cnt = 0 ; *color_table[cnt].name ; cnt++)
+			{
+				if (!strncmp(color_table[cnt].name, string, color_table[cnt].len))
+				{
+					result += sprintf(result, "%s", color_table[cnt].code);
+
+					break;
+				}
+			}
+
+			if (*color_table[cnt].name == 0)
+			{
+				for (cnt = 0 ; *color_table[cnt].name ; cnt++)
+				{
+					if (!strncasecmp(color_table[cnt].name, string, color_table[cnt].len))
+					{
+						result += sprintf(result, "%s", color_table[cnt].code);
+
+						break;
+					}
+				}
+
+				if (*color_table[cnt].name == 0)
+				{
+					return FALSE;
+				}
+			}
+			string += strlen(color_table[cnt].name);
+		}
+
+		switch (*string)
+		{
+			case ' ':
+			case ';':
+			case ',':
+			case '{':
+			case '}':
+				string++;
+				break;
+
+			case 0:
+				return TRUE;
+
+			default:
+				return FALSE;
+		}
+	}
+	return TRUE;
+}
+
 int get_color_names(struct session *ses, char *string, char *result)
 {
 	int cnt;
@@ -413,20 +565,24 @@ int get_color_names(struct session *ses, char *string, char *result)
 	return TRUE;
 }
 
-void headerstring(char *str)
+void headerstring(struct session *ses, char *str)
 {
-	char buf[BUFFER_SIZE];
+	char buf[BUFFER_SIZE], fill[BUFFER_SIZE];
+	int len, max;
 
-	if ((int) strlen(str) > gtd->screen->cols - 2)
+	len = string_raw_str_len(ses, str, 0, BUFFER_SIZE);
+	max =  get_scroll_cols(ses);
+
+	if (len > max - 2)
 	{
-		str[gtd->screen->cols - 2] = 0;
-	}
+		str[max] = 0;
 
-	memset(buf, '#', gtd->screen->cols);
+		return;
+	}
 
-	memcpy(&buf[(gtd->screen->cols - strlen(str)) / 2], str, strlen(str));
+	memset(fill, '#', max);
 
-	buf[gtd->screen->cols] = 0;
+	sprintf(buf, "%.*s%s%.*s%s", (max - len) / 2, fill, str, (max - len) / 2, fill, (max - len) % 2 ? "#" : "");
 
 	strcpy(str, buf);
 }
@@ -521,6 +677,131 @@ void thousandgroupingstring(struct session *ses, char *str)
 	strcpy(str, result + cnt2 + 1);
 }
 
+void chronosgroupingstring(struct session *ses, char *str)
+{
+	char *sign = "-";
+	long long val = (long long) get_number(ses, str);
+	int days, hours, minutes, seconds;
+
+	if (val < 0)
+	{
+		val *= -1;
+	}
+	else
+	{
+		sign = "";
+	}
+
+	seconds = val % 60;
+	val /= 60;
+
+	minutes = val % 60;
+	val /= 60;
+
+	hours = val % 24;
+	val /= 24;
+
+	days = val;
+
+	if (days)
+	{
+		sprintf(str, "%s%d:%02d:%02d:%02d", sign, days, hours, minutes, seconds);
+	}
+	else if (hours)
+	{
+		sprintf(str, "%s%d:%02d:%02d", sign, hours, minutes, seconds);
+	}
+	else
+	{
+		sprintf(str, "%s%d:%02d", sign, minutes, seconds);
+	}
+}
+
+void metricgroupingstring(struct session *ses, char *str)
+{
+	char big[]   = {' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', '?', '?', '?', '?', '?', '?', '?', '?'};
+	char small[] = {' ', 'm', 'u', 'n', 'p', 'f', 'a', 'z', 'y', '?', '?', '?', '?', '?', '?', '?', '?'};
+	char tmp[NUMBER_SIZE];
+	long double val = get_number(ses, str);
+	int index = 0;
+
+	if (val >= 1000)
+	{
+                while (val >= 1000)
+                {
+                        val = val / 1000;
+                        index++;
+                }
+                if (val >= 100)
+                {
+                	sprintf(tmp, " %Lf", val);
+		}
+		else
+		{
+                	sprintf(tmp, "%Lf", val);
+		}
+                sprintf(str, "%.4s%c", tmp, big[index]);
+	}
+	else if (val > 0 && val < 0.01)
+	{
+		while (val < 0.01)
+		{
+			val = val * 1000;
+			index++;
+		}
+		sprintf(tmp, "%Lf", val);
+		sprintf(str, "%.4s%c", tmp, small[index]);
+	}
+	else if (val >= 0)
+	{
+		if (val >= 100)
+		{
+			sprintf(tmp, " %Lf", val);
+		}
+		else
+		{
+			sprintf(tmp, "%Lf", val);
+		}
+		sprintf(str, "%.4s%c", tmp, big[index]);
+	}
+	else if (val <= -0.01 && val > -1000)
+	{
+		if (val <= -100)
+		{
+			sprintf(tmp, " %Lf", val);
+		}
+		else
+		{
+			sprintf(tmp, "%Lf", val);
+		} 
+		sprintf(str, "%.5s%c", tmp, small[index]);
+	}
+	else if (val <= -1000)
+	{
+                while (val <= -100)
+                {
+                        if (val <= -10000)
+                        {
+                                val = (long double) ((long long) val / 100LL * 100LL);
+                        }
+                        val = val / 1000;
+                        index++;
+                }
+                sprintf(tmp, "%Lf", val);
+                sprintf(str, "%.5s%c", tmp, big[index]);
+	}
+
+	else if (val < 0 /*&& val > -0.01*/)
+	{
+		while (val > -0.01)
+		{
+			val = val * 1000;
+			index++;
+		}
+		sprintf(tmp, "%Lf", val);
+		sprintf(str, "%.5s%c", tmp, small[index]);
+	}
+}
 
 void stripspaces(char *str)
 {
@@ -741,7 +1022,13 @@ int string_raw_str_len(struct session *ses, char *str, int raw_start, int raw_en
 			continue;
 		}
 
-		if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&str[raw_cnt]))
+		if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_EUC)  && is_euc_head(ses, &str[raw_cnt]))
+		{
+			raw_cnt += get_euc_width(ses, &str[raw_cnt], &width);
+
+			ret_cnt += width;
+		}
+		else if (HAS_BIT(gtd->ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&str[raw_cnt]))
 		{
 			raw_cnt += get_utf8_width(&str[raw_cnt], &width);
 
@@ -964,7 +1251,7 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						break;
 
 					case 'h':
-						headerstring(arglist[i]);
+						headerstring(ses, arglist[i]);
 						break;
 
 					case 'l':
@@ -1012,7 +1299,7 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						break;
 
 					case 'C':
-						sprintf(arglist[i], "%d", gtd->screen->cols);
+						chronosgroupingstring(ses, arglist[i]);
 						break;
 
 					case 'D':
@@ -1038,19 +1325,17 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						sprintf(arglist[i], "%d", stringlength(ses, arglist[i]));
 						break;
 
-					// undocumented
 					case 'M':
-						timeval_t  = (time_t) *arglist[i] ? atoll(arglist[i]) : gtd->time;
-						timeval_tm = *localtime(&timeval_t);
-						strftime(arglist[i], BUFFER_SIZE, "%m", &timeval_tm);
+						metricgroupingstring(ses, arglist[i]);
 						break;
 
 					case 'R':
+						tintin_printf2(ses, "\e[1;31m#echo/#format %%R please use #screen {get} {rows} to get screen height.");
 						sprintf(arglist[i], "%d", gtd->screen->rows);
 						break;
 
 					case 'S':
-						sprintf(arglist[i], "%s", ses->name);
+						sprintf(arglist[i], "%d", spellcheck_count(ses, arglist[i]));
 						break;
 
 					case 'T':
@@ -1111,7 +1396,7 @@ DO_COMMAND(do_format)
 
 	format_string(ses, format, arg, result);
 
-	set_nest_node(ses->list[LIST_VARIABLE], destvar, "%s", result);
+	set_nest_node_ses(ses, destvar, "%s", result);
 
 	show_message(ses, LIST_VARIABLE, "#OK. VARIABLE {%s} HAS BEEN SET TO {%s}.", destvar, result);
 

+ 86 - 9
src/vt102.c

@@ -1,7 +1,7 @@
 /******************************************************************************
 *   This file is part of TinTin++                                             *
 *                                                                             *
-*   Copyright 2004-2019 Igor van den Hoven                                    *
+*   Copyright 2004-2020 Igor van den Hoven                                    *
 *                                                                             *
 *   TinTin++ is free software; you can redistribute it and/or modify          *
 *   it under the terms of the GNU General Public License as published by      *
@@ -13,13 +13,12 @@
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU General Public License for more details.                              *
 *                                                                             *
-*                                                                             *
 *   You should have received a copy of the GNU General Public License         *
 *   along with TinTin++.  If not, see https://www.gnu.org/licenses.           *
 ******************************************************************************/
 
 /******************************************************************************
-*                (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t                 *
+*                               T I N T I N + +                               *
 *                                                                             *
 *                      coded by Igor van den Hoven 2004                       *
 ******************************************************************************/
@@ -56,7 +55,11 @@ void save_pos(struct session *ses)
 	{
 		syserr_printf(ses, "sav_lev++ above 1000.");
 	}
-	print_stdout("\e[?25l");
+
+	if (!HAS_BIT(gtd->flags, TINTIN_FLAG_HIDDENCURSOR))
+	{
+		print_stdout("\e[?25l");
+	}
 
 	pop_call();
 	return;
@@ -82,7 +85,11 @@ void restore_pos(struct session *ses)
 	{
 		goto_pos(ses, gtd->screen->sav_row[gtd->screen->sav_lev], gtd->screen->sav_col[gtd->screen->sav_lev]);
 	}
-	print_stdout("\e[?25h");
+
+	if (!HAS_BIT(gtd->flags, TINTIN_FLAG_HIDDENCURSOR))
+	{
+		print_stdout("\e[?25h");
+	}
 
 	SET_BIT(gtd->flags, TINTIN_FLAG_FLUSH);
 }
@@ -178,7 +185,7 @@ void reset_scroll_region(struct session *ses)
 
 int skip_vt102_codes(char *str)
 {
-	int skip;
+	int skip = 0;
 
 	push_call("skip_vt102_codes(%p)",str);
 
@@ -187,8 +194,8 @@ int skip_vt102_codes(char *str)
 		case   5:   /* ENQ */
 		case   7:   /* BEL */
 		case   8:   /* BS  */
-	/*	case   9: *//* HT  */
-	/*	case  10: *//* LF  */
+	//	case   9:      HT
+	//	case  10:      LF
 		case  11:   /* VT  */
 		case  12:   /* FF  */
 		case  13:   /* CR  */
@@ -198,13 +205,26 @@ int skip_vt102_codes(char *str)
 		case  19:   /* DC3 */
 		case  24:   /* CAN */
 		case  26:   /* SUB */
-		case 127:   /* DEL */
 			pop_call();
 			return 1;
 
 		case  27:   /* ESC */
 			break;
 
+		case  28: // HTML_OPEN
+			for (skip = 1 ; str[skip] ; skip++)
+			{
+				if (str[skip] == 30) // HTML_CLOSE
+				{
+					return skip + 1;
+				}
+			}
+			return 0;
+
+		case 127:   /* DEL */
+			pop_call();
+			return 1;
+
 		default:
 			pop_call();
 			return 0;
@@ -319,6 +339,63 @@ int find_color_code(char *str)
 	return 0;
 }
 
+int find_secure_color_code(char *str)
+{
+	int skip, valid = 1;
+
+	switch (str[0])
+	{
+		case  27:   /* ESC */
+			break;
+
+		default:
+			return 0;
+	}
+
+	switch (str[1])
+	{
+		case '[':
+			break;
+
+		default:
+			return 0;
+	}
+
+	for (skip = 2 ; str[skip] != 0 ; skip++)
+	{
+		switch (str[skip])
+		{
+			case 'm':
+				if (valid)
+				{
+					return skip + 1;
+				}
+				return 0;
+
+			case ';':
+				valid = 0;
+				break;
+
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				valid = 1;
+				break;
+
+			default:
+				return 0;
+		}
+	}
+	return 0;
+}
+
 
 int skip_vt102_codes_non_graph(char *str)
 {

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.