Scandum 2 年之前
父節點
當前提交
316d2700bb
共有 50 個文件被更改,包括 1389 次插入858 次删除
  1. 3 3
      SCRIPTS
  2. 26 47
      TODO
  3. 52 26
      docs/help.html
  4. 20 12
      docs/tutorial.html
  5. 46 0
      mods/igr.mods
  6. 55 40
      src/banner.c
  7. 1 1
      src/base.c
  8. 14 14
      src/buffer.c
  9. 85 31
      src/class.c
  10. 1 1
      src/command.c
  11. 39 46
      src/config.c
  12. 57 8
      src/cursor.c
  13. 4 4
      src/daemon.c
  14. 15 10
      src/data.c
  15. 11 7
      src/draw.c
  16. 1 1
      src/edit.c
  17. 2 2
      src/event.c
  18. 14 12
      src/files.c
  19. 54 26
      src/help.c
  20. 2 26
      src/history.c
  21. 10 4
      src/input.c
  22. 23 23
      src/line.c
  23. 94 16
      src/list.c
  24. 30 9
      src/log.c
  25. 190 153
      src/mapper.c
  26. 19 19
      src/math.c
  27. 13 0
      src/misc.c
  28. 17 1
      src/net.c
  29. 5 0
      src/parse.c
  30. 31 27
      src/path.c
  31. 9 1
      src/port.c
  32. 15 15
      src/regex.c
  33. 15 19
      src/scan.c
  34. 25 33
      src/screen.c
  35. 21 19
      src/session.c
  36. 47 6
      src/show.c
  37. 3 3
      src/split.c
  38. 2 1
      src/ssl.c
  39. 7 8
      src/substitute.c
  40. 6 6
      src/system.c
  41. 4 2
      src/tables.c
  42. 20 20
      src/telnet.h
  43. 17 44
      src/telopt_client.c
  44. 51 27
      src/telopt_server.c
  45. 20 18
      src/tintin.h
  46. 7 7
      src/tokenize.c
  47. 142 33
      src/trigger.c
  48. 8 10
      src/update.c
  49. 5 4
      src/utf8.c
  50. 31 13
      src/variable.c

+ 3 - 3
SCRIPTS

@@ -52,9 +52,9 @@
 	{
 		#switch {1d4}
 		{
-			#case {1} {cheer}
-			#case {2} {greet all}
-			#case {3} {smile}
+			#case {1} {cheer};
+			#case {2} {greet all};
+			#case {3} {smile};
 			#case {4} {laugh self}
 		}
 	}

+ 26 - 47
TODO

@@ -1,32 +1,33 @@
-#info mem
-#INFO MEMORY: STACK SIZE: 1800045
-#INFO MEMORY: STACK  MAX: 64
-#INFO MEMORY: STACK  CAP: 45
-#INFO MEMORY: STACK  LEN: 3
+- Problem with restoring true color subs.
+#sub bla {<F00AA00>bla}
+#high blo <118>
+#showme <158>test bli bla blo test
 
-#ERROR: index 386297 len = 1/0 max = 5706 flags = 6 (e)
-#INFO MEMORY: ALLOC SIZE: 17963354
-#INFO MEMORY: ALLOC USED: 13862779
-#INFO MEMORY: ALLOC  MAX: 1048576
-#INFO MEMORY: ALLOC  LEN: 547640
+- make #regex be able to use \n
 
-#INFO MEMORY: FREED SIZE: 1626853344
-#INFO MEMORY: FREED  MAX: 1048576
-#INFO MEMORY: FREED  LEN: 546481
+- split screen scrollback setting in #screen
 
-#INFO MEMORY: DEBUG SIZE: 1024
-#INFO MEMORY: DEBUG  MAX: 48
-#INFO MEMORY: DEBUG  LEN: 4
+- Make #draw scroll grid table work properly.
 
-- #var foo {x;{a;b;c};y;z}
-  #list foo create {$foo}
-  #list foo simplify
+- Make sure #config compact also applies to the log file.
 
-  #list explode has an issue with ';'
+- look into handling VARIABLE UPDATE event better
 
-- finish ? : support in #math
+- catch event to handle #map global exceptions
+
+- switch to pcre2
+
+- Add routine to escape 0x00 in port.c RECEIVED DATA event.
+
+- allow a full telnet disable in port.c
+
+- Add message when a substitution exceeds available memory.
 
-- multiline triggers
+- config loglevel medium option
+
+- look into GMCP larger than buffer size
+
+- finish ? : support in #math
 
 - finish create_node()
 
@@ -50,8 +51,6 @@
 
 - look into a #debug flag for #class
 
-- switch to pcre2
-
 - Another nice thing would be if there was some flag to make a trigger match newlines as space.
 
 - fix up named delays and undelay
@@ -72,17 +71,14 @@
   - add shadow session support with access to all events.
 
   - set_line_screen debug: col = -5 (64) from draw_text(%p,%d,%p,%p,%p)
+  - set_line_screen stack call triggered on android
 
   - regex101 like regex tester
 
   - check: #var bla { x};#draw scroll box 1 1 3 40 $bla
 
-  - set_line_screen stack call triggered on android
-
   - input spell checking, #cursor display ?
 
-  - look into named actions as a 4th argument
-
   - Get discworld / aardwolf mxp to work for @sentix
 
   - look into default input color
@@ -137,18 +133,17 @@
   - multiple global exit rooms and noglobal flags.
   - auto align routine that inserts void rooms where needed
   - look into writing script to drag rooms + void with mouse
-  - pancake mode to display rooms at all levels and annotations
   - Store the map filename to differentiate between maps.
   - #map list {<exits>} breaks on rooms that have e mapped to eu.
   - finish landmarks
   - map sandbox mode support (flags to disable saving?)
   - add ghosting to fix #map flag nofollow exit cmd issues?
   - Request: 'stop' #map center command, so i could see how the 'user' moves, rather map moves around him? 
-  - #map uninsert <vnum>
   - make map spacing easier
   - event for failed #map move.
   - look into #send triggering follow map.
   - add {roomdata} search to #map list
+  - change 'C' to 'S' in map file
 
   - Make actions with a priority of 0. trigger always
 
@@ -171,14 +166,10 @@
 
   - look into discord api / arachnos
 
-  - better color syntax highlighting configuration.
-
-  - See about adding SESSIONS to the list table.
+  - Add SESSIONS to the list table.
 
   - Add debugger to syntax highlighter, substitution highlighing, 256 color support, and variable expansion.
 
-  - fix readmud in net.c to not move the cursor on inactive sessions.
-
   - add packets patched counter
 
   - reportable_sounds
@@ -189,18 +180,10 @@
 
   - see if #break 2 is possible, maybe #continue 2 as well.
 
-  - http://tintin.sourceforge.net/board/viewtopic.php?t=2339 (map area data)
-
   - IPv6 for chat
 
-  - http://tintin.sourceforge.net/board/viewtopic.php?p=9109 (vt102 strip \e[2J\e[H )
-
-  - http://tintin.sourceforge.net/board/viewtopic.php?p=8766#8766 (global verbose toggle?)
-
   - See about adding ~/ handling for file names.
 
-  - Look into adding basic EUC-KR support.
-
 --------------------------------------------------------------------------------
 
 * LOW PRIORITY
@@ -211,12 +194,8 @@
 
   - multi-line buffer searches / captures / deletes
 
-  - multi-line triggers (use nested actions?)
-
   - add color based auto unwrap routine.
 
-  - Start of line anchors aren't working in #replace.
-
   - add #history filter option to filter out 1 letter commands.
 
   - Look into config option to change the working directory

+ 52 - 26
docs/help.html

@@ -832,7 +832,7 @@ Command</span><span style='color:#AAA'>: #delay </span><span style='color:#FFF'>
          BOTTOM      draw on the bottom side if possible.
          BOXED       draw a box along the square.
          BUMPED      precede the draw with an enter.
-         CALIGN      center text.
+         CALIGN      both LALIGN and RALIGN to center text.
          CIRCLED     circle the corners.
          CONVERT     draw text with meta conversion.
          CROSSED     cross the corners.
@@ -891,7 +891,7 @@ Command</span><span style='color:#AAA'>: #delay </span><span style='color:#FFF'>
          All draw types take an optional text argument as long as a valid
          square with enough space has been defined. Text is automatically
          word wrapped and text formatting can be customized with the
-         CALIGN, LALIGN, RALIGN, and UALIGN options.
+         BALIGN, TALIGN, LALIGN, RALIGN, and UALIGN options.
 
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #draw Blue box 1 1 3 20 {Hello world!}
 
@@ -1183,9 +1183,9 @@ Command</span><span style='color:#AAA'>: #delay </span><span style='color:#FFF'>
          </span><span style='color:#FFF'>RECEIVED INPUT CHARACTER
 </span><span style='color:#AAA'>           %0 character  %1 unicode index  %2 size  %3 width
 
-         NO SESSION ACTIVE      %0 raw text %1 size
-         SEND OUTPUT            %0 raw text %1 size
-         SENT OUTPUT            %0 raw text %1 size
+         </span><span style='color:#FFF'>NO SESSION ACTIVE</span><span style='color:#AAA'>      %0 raw text %1 size
+         </span><span style='color:#FFF'>SEND OUTPUT</span><span style='color:#AAA'>            %0 raw text %1 size
+         </span><span style='color:#FFF'>SENT OUTPUT</span><span style='color:#AAA'>            %0 raw text %1 size
 
          </span><span style='color:#5F5'>MAP EVENTS
 
@@ -1237,6 +1237,8 @@ Command</span><span style='color:#AAA'>: #delay </span><span style='color:#FFF'>
 </span><span style='color:#AAA'>         </span><span style='color:#FFF'>BUFFER UPDATE</span><span style='color:#AAA'>, </span><span style='color:#FFF'>DISPLAY UPDATE
 </span><span style='color:#AAA'>           These events have no additional arguments.
 
+         </span><span style='color:#FFF'>PROCESSED LINE         </span><span style='color:#AAA'>%0 raw text %1 plain text %2 prompt (0 or 1)
+
          </span><span style='color:#FFF'>RECEIVED LINE          </span><span style='color:#AAA'>%0 raw text %1 plain text
          </span><span style='color:#FFF'>RECEIVED OUTPUT        </span><span style='color:#AAA'>%0 raw text %1 plain text
          </span><span style='color:#FFF'>RECEIVED PROMPT        </span><span style='color:#AAA'>%0 raw text %1 plain text
@@ -1250,6 +1252,7 @@ Command</span><span style='color:#AAA'>: #delay </span><span style='color:#FFF'>
          </span><span style='color:#FFF'>PORT DISCONNECTION     </span><span style='color:#AAA'>%0 name %1 ip %2 port
          </span><span style='color:#FFF'>PORT LOG MESSAGE       </span><span style='color:#AAA'>%0 name %1 ip %2 port %3 data %4 plain data
          </span><span style='color:#FFF'>PORT RECEIVED MESSAGE  </span><span style='color:#AAA'>%0 name %1 ip %2 port %3 data %4 plain data
+         </span><span style='color:#FFF'>PORT RECEIVED DATA     </span><span style='color:#AAA'>%0 name %1 ip %2 port %3 data %4 size
 
          </span><span style='color:#5F5'>SCAN EVENTS
 
@@ -1302,6 +1305,8 @@ Command</span><span style='color:#AAA'>: #delay </span><span style='color:#FFF'>
          </span><span style='color:#FFF'>UNKNOWN COMMAND        </span><span style='color:#AAA'>%0 raw text
          </span><span style='color:#FFF'>SIGUSR                 </span><span style='color:#AAA'>%0 signal
 
+         </span><span style='color:#FFF'>REFORMAT &lt;MESSAGE&gt;     </span><span style='color:#AAA'>Use #return to change MESSAGE
+
          </span><span style='color:#5F5'>TELNET EVENTS
 
 </span><span style='color:#AAA'>         </span><span style='color:#FFF'>IAC &lt;EVENT&gt;
@@ -1458,7 +1463,7 @@ Command</span><span style='color:#AAA'>: #delay </span><span style='color:#FFF'>
 
 </span><span style='color:#0AA'>      ####################################################################
       #</span><span style='color:#AAA'>                                                                  </span><span style='color:#0AA'>#
-      #</span><span style='color:#AAA'>                    T I N T I N + +   2.02.31                     </span><span style='color:#0AA'>#
+      #</span><span style='color:#AAA'>                    T I N T I N + +   2.02.40                     </span><span style='color:#0AA'>#
       #</span><span style='color:#AAA'>                                                                  </span><span style='color:#0AA'>#
       #</span><span style='color:#AAA'>      Code by Peter Unold, Bill Reis, and Igor van den Hoven      </span><span style='color:#0AA'>#
       #</span><span style='color:#AAA'>                                                                  </span><span style='color:#0AA'>#
@@ -1567,11 +1572,12 @@ Command</span><span style='color:#AAA'>: #delay </span><span style='color:#FFF'>
 
 </span><span style='color:#FF5'>         HISTORY
 
-</span><span style='color:#FFF'>Command</span><span style='color:#AAA'>: #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>delete</span><span style='color:#FFF'>}</span><span style='color:#AAA'>                 Delete the last command.
-         #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>insert</span><span style='color:#FFF'>}    {</span><span style='color:#AAA'>command</span><span style='color:#FFF'>}</span><span style='color:#AAA'>    Insert a command.
-         #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>list</span><span style='color:#FFF'>}</span><span style='color:#AAA'>                   Display the entire command history.
-         #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>read</span><span style='color:#FFF'>}      {</span><span style='color:#AAA'>filename</span><span style='color:#FFF'>}</span><span style='color:#AAA'>   Read a command history from file.
-         #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>write</span><span style='color:#FFF'>}     {</span><span style='color:#AAA'>filename</span><span style='color:#FFF'>}</span><span style='color:#AAA'>   Write a command history to file.
+</span><span style='color:#FFF'>Command</span><span style='color:#AAA'>: #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>delete</span><span style='color:#FFF'>}</span><span style='color:#AAA'>                    Delete the last command.
+         #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>get</span><span style='color:#FFF'>}    {</span><span style='color:#AAA'>variable</span><span style='color:#FFF'>} {</span><span style='color:#AAA'>range</span><span style='color:#FFF'>}</span><span style='color:#AAA'> Store list in variable.
+         #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>insert</span><span style='color:#FFF'>} {</span><span style='color:#AAA'>command</span><span style='color:#FFF'>}</span><span style='color:#AAA'>          Insert a command.
+         #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>list</span><span style='color:#FFF'>}     </span><span style='color:#AAA'>                 Display the command history.
+         #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>read</span><span style='color:#FFF'>}   {</span><span style='color:#AAA'>filename</span><span style='color:#FFF'>}</span><span style='color:#AAA'>         Read a command history from file.
+         #history </span><span style='color:#FFF'>{</span><span style='color:#AAA'>write</span><span style='color:#FFF'>}  {</span><span style='color:#AAA'>filename</span><span style='color:#FFF'>}</span><span style='color:#AAA'>         Write a command history to file.
 
          Without an argument all available options are shown.
 
@@ -1601,7 +1607,7 @@ Command</span><span style='color:#AAA'>: #delay </span><span style='color:#FFF'>
 
 </span><span style='color:#FF5'>         IF
 
-</span><span style='color:#FFF'>Command</span><span style='color:#AAA'>: #if </span><span style='color:#FFF'>{</span><span style='color:#AAA'>conditional</span><span style='color:#FFF'>} {</span><span style='color:#AAA'>commands if true</span><span style='color:#FFF'>}
+</span><span style='color:#FFF'>Command</span><span style='color:#AAA'>: #if </span><span style='color:#FFF'>{</span><span style='color:#AAA'>conditional</span><span style='color:#FFF'>} {</span><span style='color:#AAA'>commands if true</span><span style='color:#FFF'>} {</span><span style='color:#AAA'>commands if false</span><span style='color:#FFF'>}
 
 </span><span style='color:#AAA'>         The #if command works similar to an if statement in other languages,
          and is based on the way C handles its conditional statements.
@@ -1613,13 +1619,16 @@ Command</span><span style='color:#AAA'>: #delay </span><span style='color:#FFF'>
          executed. See the 'math' helpfile for more information.
 
          To handle the case where an if statement is false it can be followed
-         by the #else command.
+         by the #else command. Alternatively, the else can be provided as the
+         third argument.
 
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #action {%0 gives you %1 gold coins.} {#if {%1 &gt; 5000} {thank %0}}
          If someone gives you more than 5000 coins, thank them.
 
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #alias {k} {#if {&quot;%0&quot; == &quot;&quot;} {kill &dollar;target};#else {kill %0}}
 
+</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #if {&quot;%0&quot; == &quot;{bli|bla}&quot;} {#showme %0 is either bli or bla.}
+
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#CASE'>case</a>, <a href='#DEFAULT'>default</a>, <a href='#ELSE'>else</a>, <a href='#ELSEIF'>elseif</a>, <a href='#MATH'>math</a>, <a href='#SWITCH'>switch</a> and <a href='#REGEXP'>regexp</a>.
 <a name='IGNORE'></a>
 
@@ -2056,7 +2065,7 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
 </span><span style='color:#AAA'>         </span><span style='color:#FFF'>#line background &lt;argument&gt;
 </span><span style='color:#AAA'>           Prevent new session activation.
 
-         </span><span style='color:#FFF'>#line capture &lt;variable&gt; &lt;argument&gt;.
+         </span><span style='color:#FFF'>#line capture &lt;variable&gt; &lt;argument&gt;
 </span><span style='color:#AAA'>           Argument is executed and output stored in &lt;variable&gt;.
 
          </span><span style='color:#FFF'>#line convert &lt;argument&gt;
@@ -2120,6 +2129,7 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
 </span><span style='color:#AAA'>         #list {var} {add} &lt;items&gt;              Add &lt;items&gt; to the list
          #list {var} {clear}                    Empty the given list
          #list {var} {collapse} &lt;separator&gt;     Turn list into a variable
+         #list {var} {copy} &lt;variable&gt;          Copy variable to the list
          #list {var} {create} &lt;items&gt;           Create a list using &lt;items&gt;
          #list {var} {delete} &lt;index&gt; [amount]  Delete the item at &lt;index&gt;,
                                                 the [amount] is optional.
@@ -2140,6 +2150,7 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
          #list {var} {size} &lt;variable&gt;          Copy list size to {variable}
          #list {var} {sort} [items]             Sort list alphabetically, if
                                                 an item is given it's added.
+         #list {var} {swap} &lt;index&gt; &lt;index&gt;     Swap two items
          #list {var} {tokenize} &lt;string&gt;        Create a character list
 
          The index should be between +1 and the list's size. You can also give
@@ -2533,6 +2544,9 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
 </span><span style='color:#AAA'>           Start logging to the given file, if the file already exists it won't
            be overwritten and data will be appended to the end.
 
+         </span><span style='color:#FFF'>#log make &lt;directory&gt;
+</span><span style='color:#AAA'>           Create the given directory.
+
          </span><span style='color:#FFF'>#log move &lt;filename_1&gt; &lt;filename_2&gt;
 </span><span style='color:#AAA'>           Move filename_1 to filename_2. This can be any file and doesn't need
            to be a log file.
@@ -3237,6 +3251,9 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
          Links can be created using the MSLP protocol which will generate link
          specific events when clicked.
 
+         In order to copy/paste, most terminals require that you press the shift
+         key during selection.
+
 
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#BUTTON'>button</a>, <a href='#DRAW'>draw</a>, <a href='#EVENT'>event</a> and <a href='#MSLP'>MSLP</a>.
 <a name='MSDP'></a>
@@ -3302,7 +3319,9 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
          you need to use &bsol;e]68;2;&bsol;a, and they instead trigger the SECURE LINK
          event.
 
-</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #sub {%* tells %*} {&bsol;e]68;2;EXEC;#cursor set tell %1 &bsol;a&bsol;e[4m%0&bsol;e[24m}
+         To creae a link that is not undelined, use &bsol;e]4;24m text &bsol;e]24m.
+
+</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #sub {%* tells %*} {&bsol;e]68;2;EXEC;#cursor set tell %1 &bsol;a&bsol;e[4;24m%0&bsol;e[24m}
 </span><span style='color:#FFF'>       </span><span style='color:#AAA'>  #event {PRESSED SECURE LINK EXEC MOUSE BUTTON ONE} {%4}
 
          This would make you start a reply when clicking on a tell.
@@ -3444,7 +3463,7 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
          is used.
 
 </span><span style='color:#FFF'>TinTin++ Description                                      POSIX
-      %a </span><span style='color:#AAA'>Match zero or more characters including newlines ([^&bsol;n]*?)
+      %a </span><span style='color:#AAA'>Match zero or more characters including newlines ([^&bsol;0]*?)
 </span><span style='color:#FFF'>      %A </span><span style='color:#AAA'>Match zero or more newlines                      ([&bsol;n]*?)
 </span><span style='color:#FFF'>      %c </span><span style='color:#AAA'>Match zero or more ansi color codes              ((?:&bsol;e&bsol;[[0-9;]*m)*?)
 </span><span style='color:#FFF'>      %d </span><span style='color:#AAA'>Match zero or more digits                        ([0-9]*?)
@@ -3452,6 +3471,7 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
 </span><span style='color:#FFF'>      %i </span><span style='color:#AAA'>Matches become case insensitive                  (?i)
 </span><span style='color:#FFF'>      %I </span><span style='color:#AAA'>Matches become case sensitive (default)          (?-i)
 </span><span style='color:#FFF'>      %s </span><span style='color:#AAA'>Match zero or more spaces                        ([&bsol;r&bsol;n&bsol;t ]*?)
+</span><span style='color:#FFF'>      %S </span><span style='color:#AAA'>Match zero or more non-spaces                    ([^&bsol;r&bsol;n&bsol;t ]*?)
 </span><span style='color:#FFF'>      %w </span><span style='color:#AAA'>Match zero or more word characters               ([A-Za-z0-9_]*?)
 </span><span style='color:#FFF'>      %W </span><span style='color:#AAA'>Match zero or more non-word characters           ([^A-Za-z0-9_]*?)
 </span><span style='color:#FFF'>      %? </span><span style='color:#AAA'>Match zero or one character                      (.??)
@@ -3791,10 +3811,10 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
 
 </span><span style='color:#FFF'>Command</span><span style='color:#AAA'>: #</span><span style='color:#FFF'>[</span><span style='color:#AAA'>number</span><span style='color:#FFF'>] {</span><span style='color:#AAA'>commands</span><span style='color:#FFF'>}
 
-        Sometimes you want to repeat the same command multiple times. This is
+</span><span style='color:#AAA'>        Sometimes you want to repeat the same command multiple times. This is
         the easiest way to accomplish that.
 
-Example</span><span style='color:#AAA'>: #10 {buy bread}
+</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #10 {buy bread}
 
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#MATHEMATICS'>mathematics</a> and <a href='#STATEMENTS'>statements</a>.
 <a name='REPLACE'></a>
@@ -4294,7 +4314,7 @@ Example</span><span style='color:#AAA'>: #10 {buy bread}
          Replace generic dark blue color codes with bright blue ones.
 
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #sub {%1massacres%2} {&lt;018&gt;%1&lt;118&gt;MASSACRES&lt;018&gt;%2}
-         Replaces all occurrences of 'massacres' with 'MASSACRES' in red.
+         Replace a line containing 'massacres' with 'MASSACRES' in red.
 
 </span><span style='color:#FFF'>Comment</span><span style='color:#AAA'>: See '#help action', for more information about triggers.
 
@@ -4562,23 +4582,25 @@ Example</span><span style='color:#AAA'>: #10 {buy bread}
 
 </span><span style='color:#5F5'>         Multi-line triggers
 
-</span><span style='color:#AAA'>         If an action contains the &bsol;n sequence it will be turned into a
-         multi-line trigger. A multi-line action is executed on incoming blocks
-         of text from the MUD, and they will not trigger if the regular
-         expression spans more than one block. You can visualize incoming
-         blocks by using #event {RECEIVED OUTPUT} {#echo &lt;058&gt;%+80h BLOCK}
+</span><span style='color:#AAA'>         If an action or substitution contains the &bsol;n sequence it will be
+         turned into a multi-line trigger. A multi-line trigger is executed on
+         incoming blocks of text from the MUD, and they will not trigger if the
+         regular expression spans more than one block. You can visualize
+         incoming blocks by using the following event:
+
+         #event {RECEIVED OUTPUT} {#echo &lt;058&gt;%+80h BLOCK}
 
          Since the %* expression does not capture the &bsol;n sequence it is required
          to use %a to capture multiple lines. To capture the start of the block
          use &bsol;A and for the end use &bsol;Z. You can use ^ and &dollar; to capture the
          start and end of a line.
 
-         Multi-line actions trigger before regular actions. Multiple
+         Multi-line triggers trigger before regular triggers. Multiple
          multi-line actions can trigger per block, and each multi-line action
          can trigger multiple times per block. Packet fragmentation is not
          currently handled.
 
-         Multi-line actions are experimental and subject to change.
+         Multi-line triggers are experimental and subject to change.
 
 </span><span style='color:#5F5'>         Input triggers
 
@@ -4650,6 +4672,10 @@ Example</span><span style='color:#AAA'>: #10 {buy bread}
 
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #show {Targets starting with the letter A: &dollar;targets[A%*]
 
+              To disable using regular expressions start the match with '='.
+
+</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #show {A target literally defined as A%*: &dollar;targets[&bsol;A%*]
+
          To see the internal index of a variable use &amp;&lt;variable name&gt;. To see
          the size of a table you would use: &amp;targets[] or &amp;targets[%*]. A non
          existent nested variable will report itself as 0.

+ 20 - 12
docs/tutorial.html

@@ -612,7 +612,7 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
 
 </span><span style='color:#0AA'>      ####################################################################
       #</span><span style='color:#AAA'>                                                                  </span><span style='color:#0AA'>#
-      #</span><span style='color:#AAA'>                    T I N T I N + +   2.02.31                     </span><span style='color:#0AA'>#
+      #</span><span style='color:#AAA'>                    T I N T I N + +   2.02.40                     </span><span style='color:#0AA'>#
       #</span><span style='color:#AAA'>                                                                  </span><span style='color:#0AA'>#
       #</span><span style='color:#AAA'>      Code by Peter Unold, Bill Reis, and Igor van den Hoven      </span><span style='color:#0AA'>#
       #</span><span style='color:#AAA'>                                                                  </span><span style='color:#0AA'>#
@@ -1209,6 +1209,9 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
          Links can be created using the MSLP protocol which will generate link
          specific events when clicked.
 
+         In order to copy/paste, most terminals require that you press the shift
+         key during selection.
+
 
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='help.html#BUTTON'>button</a>, <a href='help.html#DRAW'>draw</a>, <a href='help.html#EVENT'>event</a> and <a href='#MSLP'>MSLP</a>.
 <a name='MSDP'></a>
@@ -1274,7 +1277,9 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
          you need to use &bsol;e]68;2;&bsol;a, and they instead trigger the SECURE LINK
          event.
 
-</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #sub {%* tells %*} {&bsol;e]68;2;EXEC;#cursor set tell %1 &bsol;a&bsol;e[4m%0&bsol;e[24m}
+         To creae a link that is not undelined, use &bsol;e]4;24m text &bsol;e]24m.
+
+</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #sub {%* tells %*} {&bsol;e]68;2;EXEC;#cursor set tell %1 &bsol;a&bsol;e[4;24m%0&bsol;e[24m}
 </span><span style='color:#FFF'>       </span><span style='color:#AAA'>  #event {PRESSED SECURE LINK EXEC MOUSE BUTTON ONE} {%4}
 
          This would make you start a reply when clicking on a tell.
@@ -1323,7 +1328,7 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
          is used.
 
 </span><span style='color:#FFF'>TinTin++ Description                                      POSIX
-      %a </span><span style='color:#AAA'>Match zero or more characters including newlines ([^&bsol;n]*?)
+      %a </span><span style='color:#AAA'>Match zero or more characters including newlines ([^&bsol;0]*?)
 </span><span style='color:#FFF'>      %A </span><span style='color:#AAA'>Match zero or more newlines                      ([&bsol;n]*?)
 </span><span style='color:#FFF'>      %c </span><span style='color:#AAA'>Match zero or more ansi color codes              ((?:&bsol;e&bsol;[[0-9;]*m)*?)
 </span><span style='color:#FFF'>      %d </span><span style='color:#AAA'>Match zero or more digits                        ([0-9]*?)
@@ -1331,6 +1336,7 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
 </span><span style='color:#FFF'>      %i </span><span style='color:#AAA'>Matches become case insensitive                  (?i)
 </span><span style='color:#FFF'>      %I </span><span style='color:#AAA'>Matches become case sensitive (default)          (?-i)
 </span><span style='color:#FFF'>      %s </span><span style='color:#AAA'>Match zero or more spaces                        ([&bsol;r&bsol;n&bsol;t ]*?)
+</span><span style='color:#FFF'>      %S </span><span style='color:#AAA'>Match zero or more non-spaces                    ([^&bsol;r&bsol;n&bsol;t ]*?)
 </span><span style='color:#FFF'>      %w </span><span style='color:#AAA'>Match zero or more word characters               ([A-Za-z0-9_]*?)
 </span><span style='color:#FFF'>      %W </span><span style='color:#AAA'>Match zero or more non-word characters           ([^A-Za-z0-9_]*?)
 </span><span style='color:#FFF'>      %? </span><span style='color:#AAA'>Match zero or one character                      (.??)
@@ -1494,10 +1500,10 @@ Example</span><span style='color:#AAA'>: #high </span><span style='color:#FFF'>{
 
 </span><span style='color:#FFF'>Command</span><span style='color:#AAA'>: #</span><span style='color:#FFF'>[</span><span style='color:#AAA'>number</span><span style='color:#FFF'>] {</span><span style='color:#AAA'>commands</span><span style='color:#FFF'>}
 
-        Sometimes you want to repeat the same command multiple times. This is
+</span><span style='color:#AAA'>        Sometimes you want to repeat the same command multiple times. This is
         the easiest way to accomplish that.
 
-Example</span><span style='color:#AAA'>: #10 {buy bread}
+</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #10 {buy bread}
 
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#MATHEMATICS'>mathematics</a> and <a href='#STATEMENTS'>statements</a>.
 <a name='SCREEN_READER'></a>
@@ -1792,23 +1798,25 @@ Example</span><span style='color:#AAA'>: #10 {buy bread}
 
 </span><span style='color:#5F5'>         Multi-line triggers
 
-</span><span style='color:#AAA'>         If an action contains the &bsol;n sequence it will be turned into a
-         multi-line trigger. A multi-line action is executed on incoming blocks
-         of text from the MUD, and they will not trigger if the regular
-         expression spans more than one block. You can visualize incoming
-         blocks by using #event {RECEIVED OUTPUT} {#echo &lt;058&gt;%+80h BLOCK}
+</span><span style='color:#AAA'>         If an action or substitution contains the &bsol;n sequence it will be
+         turned into a multi-line trigger. A multi-line trigger is executed on
+         incoming blocks of text from the MUD, and they will not trigger if the
+         regular expression spans more than one block. You can visualize
+         incoming blocks by using the following event:
+
+         #event {RECEIVED OUTPUT} {#echo &lt;058&gt;%+80h BLOCK}
 
          Since the %* expression does not capture the &bsol;n sequence it is required
          to use %a to capture multiple lines. To capture the start of the block
          use &bsol;A and for the end use &bsol;Z. You can use ^ and &dollar; to capture the
          start and end of a line.
 
-         Multi-line actions trigger before regular actions. Multiple
+         Multi-line triggers trigger before regular triggers. Multiple
          multi-line actions can trigger per block, and each multi-line action
          can trigger multiple times per block. Packet fragmentation is not
          currently handled.
 
-         Multi-line actions are experimental and subject to change.
+         Multi-line triggers are experimental and subject to change.
 
 </span><span style='color:#5F5'>         Input triggers
 

+ 46 - 0
mods/igr.mods

@@ -1,3 +1,49 @@
+Mar 2023        2.02.32
+------------------------------------------------------------------------------
+data.c          Changed the symbol for the strict equality assignment/lookup
+                from \ to = due to compatibility issues. You'll get a warning
+                if a variable or table key begins with a \.
+
+net.c           Added the PROCESSED LINE event. Like RECEIVED LINE, but it
+                triggers after line triggers have been processed.
+
+variable.c      Added support to use \A in #replace and #substitute to only
+                match the start of a line. When using ^ the behavior is more
+                ambiguous, matching at the start of each line and at the end
+                of each preceding match.
+
+net.c           Added the PACKET PATCH event which triggers when a broken
+                packet is detected. Has some utility for trouble shooting.
+
+list.c          Added #list <list> copy <variable> to copy a variable to the
+                given list. Will copy tables as well.
+
+log.c           Added #log make <directory> to create directories. #log remove
+                <directory> can be used to remove one.
+
+trigger.c       Added support for multi-line substitutions.
+
+show.c          Added a REFORMAT event which can be used to alter system
+                messages. Must use #return to set the new message format.
+
+help.c          Added a CATCH HELP event.
+
+                #event {CATCH HELP NOP} {#line ignore #showme bla}
+
+class.c         #class clear/close/kill/list/load/save/size/write no longer
+                create a class if none exists.
+
+split.c         When using #split 0 1 0 -80 it will correctly use the negative
+                argument on subsequent screen resizes.
+
+list.c          Added #list swap <index1> <index2> to swap two list items.
+
+cursor.c        You can paste multi-line triggers by first pressing ctrl-v,
+                next paste the data, followed by pressing ctrl-enter.
+
+mapper.c        Added #map flag autolink <on|off>. Default is on. Set to off
+                to disable automatically linking overlapping rooms.
+
 Jan 2023        2.02.31
 ------------------------------------------------------------------------------
 cursor.c        #cursor {get} will escape braces and other special characters.

+ 55 - 40
src/banner.c

@@ -103,6 +103,23 @@ void banner_expires(struct session *ses, char *name, char *arg, char *arg1)
 
 void banner_init(struct session *ses, char *arg1)
 {
+	banner_create(ses, "Armageddon", arg1);
+
+	banner_desc(ses, "Armageddon",
+		"Armageddon MUD is set in the brutal post-apocalyptic world of Zalanthas.\n"
+		"Emphasizing roleplay over combat, the game offers a rich, immersive experience.\n"
+		"Zalanthas is ruled by sorcerer-kings and their powerful Templarate, dominating\n"
+		"the twin cities of Allanak and Tuluk. Magic not sanctioned by these rulers is\n"
+		"feared and often met with fatal consequences. Survival is a constant struggle\n"
+		"in this harsh environment, and death is permanent. Armageddon MUD delivers a\n"
+		"distinctive roleplaying adventure, with a dedicated community of players and\n"
+		"staff actively shaping the ongoing Zalanthan narrative for over three decades.", arg1);
+
+	banner_website(ses, "Armageddon", "https://armageddon.org", arg1);
+	banner_address(ses, "Armageddon", "arm armageddon.org 4050", arg1);
+	banner_expires(ses, "Armageddon", "2030", arg1);
+
+
 	banner_create(ses, "Lost Souls", arg1);
 
 	banner_desc(ses, "Lost Souls",
@@ -118,7 +135,7 @@ void banner_init(struct session *ses, char *arg1)
 
 	banner_website(ses, "Lost Souls", "https://lostsouls.org", arg1);
 	banner_address(ses, "Lost Souls", "ls lostsouls.org 23", arg1);
-	banner_expires(ses, "Lost Souls", "2028", arg1);
+	banner_expires(ses, "Lost Souls", "2029", arg1);
 
 
 	banner_create(ses, "Kallisti MUD", arg1);
@@ -136,9 +153,10 @@ void banner_init(struct session *ses, char *arg1)
 
 	banner_website(ses, "Kallisti MUD", "https://www.KallistiMUD.com", arg1);
 	banner_address(ses, "Kallisti MUD", "LoK kallistimud.com 4000", arg1);
-	banner_expires(ses, "Kallisti MUD", "2028", arg1);
+	banner_expires(ses, "Kallisti MUD", "2029", arg1);
 	banner_flag(ses, "Kallisti MUD", BANNER_FLAG_DUPLICATE);
 
+
 	banner_create(ses, "Legends of Kallisti", arg1);
 
 	banner_desc(ses, "Legends of Kallisti",
@@ -154,7 +172,7 @@ void banner_init(struct session *ses, char *arg1)
 
 	banner_website(ses, "Legends of Kallisti", "https://legendsofkallisti.com", arg1);
 	banner_address(ses, "Legends of Kallisti", "LoK legendsofkallisti.com 4000", arg1);
-	banner_expires(ses, "Legends of Kallisti", "2028", arg1);
+	banner_expires(ses, "Legends of Kallisti", "2029", arg1);
 
 
 	banner_create(ses, "3Kingdoms", arg1);
@@ -172,7 +190,7 @@ void banner_init(struct session *ses, char *arg1)
 
 	banner_website(ses, "3Kingdoms", "http://3k.org", arg1);
 	banner_address(ses, "3Kingdoms", "3K 3k.org 3000", arg1);
-	banner_expires(ses, "3Kingdoms", "2028", arg1);
+	banner_expires(ses, "3Kingdoms", "2029", arg1);
 
 
 	banner_create(ses, "RetroMUD", arg1);
@@ -187,23 +205,6 @@ void banner_init(struct session *ses, char *arg1)
 	banner_expires(ses, "RetroMUD", "2032", arg1);
 
 
-	banner_create(ses, "Realm of Utopian Dreams (RUD)", arg1);
-
-	banner_desc(ses, "Realm of Utopian Dreams (RUD)",
-		"RUD is a unique ROM-based high-fantasy MUD with character choices for many play\n"
-		"styles from hack-n-slash to roleplay. Each of the races and classes offer\n"
-		"unique spells and skills, unlocked through a tiered remort system with\n"
-		"customization through religion memberships and epic advancements. RUD started\n"
-		"in 1996, always seeking new adventurers to become the next hero, build a home,\n"
-		"start a shop, and eventually become Nobles of the Realm! New players to RUD can\n"
-		"be just as successful as 20+ year veterans. We run quests, plots, and annual\n"
-		"festivals in an ever evolving world. Come build Lantarea with us!", arg1);
-
-	banner_website(ses, "Realm of Utopian Dreams (RUD)", "http://rudmud.com", arg1);
-	banner_address(ses, "Realm of Utopian Dreams (RUD)", "rud rudmud.com 1701", arg1);
-	banner_expires(ses, "Realm of Utopian Dreams (RUD)", "2028", arg1);
-
-
 	banner_create(ses, "Alter Aeon", arg1);
 
 	banner_desc(ses, "Alter Aeon",
@@ -217,9 +218,26 @@ void banner_init(struct session *ses, char *arg1)
 
 	banner_website(ses, "Alter Aeon", "https://www.alteraeon.com", arg1);
 	banner_address(ses, "Alter Aeon", "aa alteraeon.com 3000", arg1);
-	banner_expires(ses, "Alter Aeon", "2028", arg1);
+	banner_expires(ses, "Alter Aeon", "2029", arg1);
+
+
+	banner_create(ses, "New Worlds Ateraan", arg1);
+
+	banner_desc(ses, "New Worlds Ateraan",
+		"Ateraan is a world of Intensive Roleplaying offering many unique and powerful\n"
+		"guilds, races, politics, religion, justice, economy, and a storyline that is\n"
+		"dominantly player controlled and based on a novel. The game is based on a\n"
+		"Kingdom with knights, merchants, mages, and thieves, and a fierce southern\n"
+		"state that has warriors, shaman, slaves, and servants. Ships rule the seas and\n"
+		"caravans travel the lands. With 100's of players and features like invasions,\n"
+		"ship creation, house building, clans, theaters, leatherball fields, and massive\n"
+		"events, the game is incredibly robust and diverse.", arg1);
 
+	banner_website(ses, "New Worlds Ateraan", "http://www.ateraan.com", arg1);
+	banner_address(ses, "New Worlds Ateraan", "nwa ateraan.com 4002", arg1);
+	banner_expires(ses, "New Worlds Ateraan", "2029", arg1);
 
+/*
 	banner_create(ses, "Threshold RPG", arg1);
 
 	banner_desc(ses, "Threshold RPG",
@@ -234,6 +252,21 @@ void banner_init(struct session *ses, char *arg1)
 	banner_address(ses, "Threshold RPG", "thresh thresholdrpg.com 3333", arg1);
 	banner_expires(ses, "Threshold RPG", "2028", arg1);
 
+	banner_create(ses, "Realm of Utopian Dreams (RUD)", arg1);
+
+	banner_desc(ses, "Realm of Utopian Dreams (RUD)",
+		"RUD is a unique ROM-based high-fantasy MUD with character choices for many play\n"
+		"styles from hack-n-slash to roleplay. Each of the races and classes offer\n"
+		"unique spells and skills, unlocked through a tiered remort system with\n"
+		"customization through religion memberships and epic advancements. RUD started\n"
+		"in 1996, always seeking new adventurers to become the next hero, build a home,\n"
+		"start a shop, and eventually become Nobles of the Realm! New players to RUD can\n"
+		"be just as successful as 20+ year veterans. We run quests, plots, and annual\n"
+		"festivals in an ever evolving world. Come build Lantarea with us!", arg1);
+
+	banner_website(ses, "Realm of Utopian Dreams (RUD)", "http://rudmud.com", arg1);
+	banner_address(ses, "Realm of Utopian Dreams (RUD)", "rud rudmud.com 1701", arg1);
+	banner_expires(ses, "Realm of Utopian Dreams (RUD)", "2028", arg1);
 
 	banner_create(ses, "Primal Darkness", arg1);
 	
@@ -251,24 +284,6 @@ void banner_init(struct session *ses, char *arg1)
 	banner_address(ses, "Primal Darkness", "pd mud.primaldarkness.com 5000", arg1);
 	banner_expires(ses, "Primal Darkness", "2028", arg1);
 
-
-	banner_create(ses, "New Worlds Ateraan", arg1);
-
-	banner_desc(ses, "New Worlds Ateraan",
-		"Ateraan is a world of Intensive Roleplaying offering many unique and powerful\n"
-		"guilds, races, politics, religion, justice, economy, and a storyline that is\n"
-		"dominantly player controlled and based on a novel. The game is based on a\n"
-		"Kingdom with knights, merchants, mages, and thieves, and a fierce southern\n"
-		"state that has warriors, shaman, slaves, and servants. Ships rule the seas and\n"
-		"caravans travel the lands. With 100's of players and features like invasions,\n"
-		"ship creation, house building, clans, theaters, leatherball fields, and massive\n"
-		"events, the game is incredibly robust and diverse.", arg1);
-
-	banner_website(ses, "New Worlds Ateraan", "http://www.ateraan.com", arg1);
-	banner_address(ses, "New Worlds Ateraan", "nwa ateraan.com 4002", arg1);
-	banner_expires(ses, "New Worlds Ateraan", "2028", arg1);
-
-/*
 	banner_create(ses, "Carrion Fields", arg1);
 
 	banner_desc(ses, "Carrion Fields",

+ 1 - 1
src/base.c

@@ -148,7 +148,7 @@ int str_to_base64(char *in, char *out, size_t size)
 
 	pto = out;
 
-	for (cnt = 0 ; cnt + 3 < size ; cnt += 3)
+	for (cnt = 0 ; cnt + 3 <= size ; cnt += 3)
 	{
 		*pto++ = base64_table[(unsigned char) in[cnt + 0] % 64];
 		*pto++ = base64_table[(unsigned char) in[cnt + 0] / 64 + (unsigned char) in[cnt + 1] % 16 * 4];

+ 14 - 14
src/buffer.c

@@ -712,7 +712,7 @@ DO_BUFFER(buffer_clear)
 
 	if (min > max)
 	{
-		show_error(ses, LIST_COMMAND, "#ERROR: #BUFFER CLEAR {%d} {%d} LOWER BOUND EXCEEDS UPPER BOUND.", min, max);
+		show_error(ses, LIST_COMMAND, "#ERROR: #BUFFER CLEAR {%d} {%d}: LOWER BOUND EXCEEDS UPPER BOUND.", min, max);
 
 		return;
 	}
@@ -931,7 +931,7 @@ DO_BUFFER(buffer_find)
 
 	if (*arg1 == 0)
 	{
-		show_error(gtd->ses, LIST_COMMAND, "#BUFFER, FIND WHAT?");
+		show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #BUFFER FIND [PAGE] <SEARCH TEXT>");
 
 		return;
 	}
@@ -944,7 +944,7 @@ DO_BUFFER(buffer_find)
 
 		if (*arg2 == 0)
 		{
-			show_error(gtd->ses, LIST_COMMAND, "#BUFFER, FIND OCCURANCE %d OF WHAT?", page);
+			show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #BUFFER FIND {%d} <SEARCH TEXT>", page);
 
 			return;
 		}
@@ -1001,7 +1001,7 @@ DO_BUFFER(buffer_find)
 
 	if (scroll_cnt < 0 || scroll_cnt >= ses->scroll->used)
 	{
-		show_error(gtd->ses, LIST_COMMAND, "#BUFFER FIND, NO MATCHES FOUND.");
+		show_error(gtd->ses, LIST_COMMAND, "#BUFFER FIND: NO MATCHES FOUND.");
 
 		return;
 	}
@@ -1063,7 +1063,7 @@ DO_BUFFER(buffer_get)
 
 	if (min > max)
 	{
-		show_error(ses, LIST_COMMAND, "#ERROR: #BUFFER GET {%s} {%d} {%d} LOWER BOUND EXCEEDS UPPER BOUND.", arg1, min, max);
+		show_error(ses, LIST_COMMAND, "#ERROR: #BUFFER GET {%s} {%d} {%d}: LOWER BOUND EXCEEDS UPPER BOUND.", arg1, min, max);
 
 		return;
 	}
@@ -1079,7 +1079,7 @@ DO_BUFFER(buffer_get)
 		add_nest_node_ses(ses, arg1, "{%d}{%s}", ++cnt, arg2);
 	}
 
-	show_message(ses, LIST_COMMAND, "#BUFFER GET: %d LINE%s SAVED TO {%s}.", cnt, cnt > 1 ? "S" : "", arg1);
+	show_message(ses, LIST_COMMAND, "#BUFFER GET: %d LINES SAVED TO {%s}.", cnt, arg1);
 
 	return;
 }
@@ -1127,13 +1127,13 @@ DO_BUFFER(buffer_write)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #BUFFER WRITE <FILENAME>]");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #BUFFER WRITE <FILENAME>");
 	}
 	else
 	{
 		if ((fp = fopen(arg1, "w")))
 		{
-			show_message(ses, LIST_COMMAND, "#OK: WRITING BUFFER TO '%s'.", arg1);
+			show_message(ses, LIST_COMMAND, "#BUFFER WRITE: %d LINES WRITTEN TO {%s}.", ses->scroll->used - 1, arg1);
 
 			logheader(ses, fp, ses->log->mode | LOG_FLAG_OVERWRITE);
 
@@ -1158,7 +1158,7 @@ DO_BUFFER(buffer_write)
 		}
 		else
 		{
-			show_error(ses, LIST_COMMAND, "#ERROR: #BUFFER WRITE {%s} - FAILED TO OPEN FILE.", arg1);
+			show_error(ses, LIST_COMMAND, "#ERROR: #BUFFER WRITE {%s}: COULDN'T OPEN FILE.", arg1);
 		}
 	}
 	return;
@@ -1200,7 +1200,7 @@ DO_BUFFER(buffer_info)
 		add_nest_node_ses(ses, arg2, "{USED}{%d}", ses->scroll->used);
 		add_nest_node_ses(ses, arg2, "{WRAP}{%d}", ses->scroll->wrap);
 
-		show_message(ses, LIST_COMMAND, "#BUFFER INFO: SAVED TO {%s}", arg2);
+		show_message(ses, LIST_COMMAND, "#BUFFER INFO: DATA SAVED TO {%s}", arg2);
 	}
 	else
 	{
@@ -1222,7 +1222,7 @@ DO_COMMAND(do_grep)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: GREP [#] <SEARCH TEXT>");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #GREP [PAGE] <SEARCH TEXT>");
 
 		return ses;
 	}
@@ -1235,7 +1235,7 @@ DO_COMMAND(do_grep)
 
 		if (*arg2 == 0)
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #grep {%s} <SEARCH TEXT>", arg1);
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #GREP {%s} <SEARCH TEXT>", arg1);
 
 			return ses;
 		}
@@ -1286,7 +1286,7 @@ DO_COMMAND(do_grep)
 
 		if (grep_cnt <= grep_min)
 		{
-			show_error(ses, LIST_COMMAND, "#NO MATCHES FOUND.");
+			show_error(ses, LIST_COMMAND, "#GREP: #NO MATCHES FOUND.");
 
 			gtd->level->grep--;
 
@@ -1344,7 +1344,7 @@ DO_COMMAND(do_grep)
 
 		if (grep_cnt == 0)
 		{
-			show_error(ses, LIST_COMMAND, "#NO MATCHES FOUND.");
+			show_error(ses, LIST_COMMAND, "#GREP: #NO MATCHES FOUND.");
 
 			gtd->level->grep--;
 

+ 85 - 31
src/class.c

@@ -46,29 +46,30 @@ struct class_type
 {
 	char                  * name;
 	CLASS                 * fun;
+	char                  * desc;
 };
 
 struct class_type class_table[] =
 {
-	{    "ASSIGN",            class_assign           },
-	{    "CLEAR",             class_clear            },
-	{    "CLOSE",             class_close            },
-	{    "KILL",              class_kill             },
-	{    "LIST",              class_list             },
-	{    "LOAD",              class_load             },
-	{    "OPEN",              class_open             },
-	{    "READ",              class_read             },
-	{    "SAVE",              class_save             },
-	{    "SIZE",              class_size             },
-	{    "WRITE",             class_write            },
-	{    "",                  NULL                   },
+	{    "ASSIGN",            class_assign,   "Execute command with given class opened."        },
+	{    "CLEAR",             class_clear,    "Delete triggers associated with given class."    },
+	{    "CLOSE",             class_close,    "Close given class."                              },
+	{    "KILL",              class_kill,     "Clear, close, and remove given class."           },
+	{    "LIST",              class_list,     "List triggers associated with given class."      },
+	{    "LOAD",              class_load,     "Load saved data of given class."                 },
+	{    "OPEN",              class_open,     "Open given class."                               },
+	{    "READ",              class_read,     "Read file with given class opened."              },
+	{    "SAVE",              class_save,     "Save given class to memory."                     },
+	{    "SIZE",              class_size,     "Store size of given class to a variable."        },
+	{    "WRITE",             class_write,    "Write given class to file."                      },
+	{    "",                  NULL,           NULL                                              }
 };
 
 int count_class(struct session *ses, struct listnode *group);
 
 DO_COMMAND(do_class)
 {
-	int i;
+	int i, cnt;
 	struct listroot *root;
 	struct listnode *node;
 
@@ -91,7 +92,9 @@ DO_COMMAND(do_class)
 	}
 	else if (*arg2 == 0)
 	{
-		class_list(ses, NULL, arg1, arg2);
+		node = search_node_list(ses->list[LIST_CLASS], arg1);
+
+		class_list(ses, node, arg1, arg2);
 	}
 	else
 	{
@@ -105,27 +108,36 @@ DO_COMMAND(do_class)
 
 		if (*class_table[i].name == 0)
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: CLASS {name} {OPEN|CLEAR|CLOSE|READ|SIZE|WRITE|KILL}.", arg1, capitalize(arg2));
+			tintin_header(ses, 80, " CLASS OPTIONS ");
+
+			for (cnt = 0 ; *class_table[cnt].fun != NULL ; cnt++)
+			{
+				if (*class_table[cnt].desc)
+				{
+					tintin_printf2(ses, "  [%-13s] %s", class_table[cnt].name, class_table[cnt].desc);
+				}
+			}
+			tintin_header(ses, 80, "");
 		}
 		else
 		{
 			node = search_node_list(ses->list[LIST_CLASS], arg1);
 
-			if (node == NULL)
-			{
-				show_info(ses, LIST_CLASS, "#INFO: CLASS {%s} CREATED", arg1);
-
-				check_all_events(ses, EVENT_FLAG_CLASS, 0, 1, "CLASS CREATED", arg1);
-				check_all_events(ses, EVENT_FLAG_CLASS, 1, 1, "CLASS CREATED %s", arg1, arg1);
-
-				node = update_node_list(ses->list[LIST_CLASS], arg1, "", arg3, "");
-			}
 			class_table[i].fun(ses, node, arg1, arg3);
 		}
 	}
 	return ses;
 }
 
+struct listnode *create_class(struct session *ses, char *arg1, char *arg3)
+{
+	show_info(ses, LIST_CLASS, "#INFO: CLASS {%s} CREATED", arg1);
+
+	check_all_events(ses, EVENT_FLAG_CLASS, 0, 1, "CLASS CREATED", arg1);
+	check_all_events(ses, EVENT_FLAG_CLASS, 1, 1, "CLASS CREATED %s", arg1, arg1);
+
+	return update_node_list(ses->list[LIST_CLASS], arg1, "", arg3, "");
+}
 
 int count_class(struct session *ses, struct listnode *group)
 {
@@ -151,6 +163,10 @@ int count_class(struct session *ses, struct listnode *group)
 
 DO_CLASS(class_assign)
 {
+	if (node == NULL)
+	{
+		node = create_class(ses, arg1, arg2);
+	}
 	char *tmp = ses->group;
 
 	ses->group = strdup(arg1);
@@ -168,6 +184,13 @@ DO_CLASS(class_clear)
 {
 	int type, index;
 
+	if (node == NULL)
+	{
+		show_message(ses, LIST_CLASS, "#CLASS {%s} DOES NOT EXIST.", arg1);
+
+		return ses;
+	}
+
 	check_all_events(ses, EVENT_FLAG_CLASS, 0, 1, "CLASS CLEAR", ses->group);
 	check_all_events(ses, EVENT_FLAG_CLASS, 1, 1, "CLASS CLEAR %s", ses->group, ses->group);
 
@@ -199,11 +222,9 @@ DO_CLASS(class_clear)
 
 DO_CLASS(class_close)
 {
-	node = search_node_list(ses->list[LIST_CLASS], arg1);
-
 	if (node == NULL)
 	{
-		show_message(ses, LIST_CLASS, "#CLASS {%s} NO LONGER EXIST.", arg1);
+		show_message(ses, LIST_CLASS, "#CLASS {%s} DOES NOT EXIST.", arg1);
 	}
 	else
 	{
@@ -248,7 +269,7 @@ DO_CLASS(class_list)
 {
 	int i, j;
 
-	if (search_node_list(ses->list[LIST_CLASS], arg1))
+	if (node)
 	{
 		tintin_header(ses, 80, " %s ", arg1);
 
@@ -285,6 +306,13 @@ DO_CLASS(class_kill)
 {
 	int group;
 
+	if (node == NULL)
+	{
+		show_message(ses, LIST_CLASS, "#CLASS {%s} DOES NOT EXIST.", arg1);
+
+		return ses;
+	}
+
 	class_clear(ses, node, arg1, arg2);
 
 	group = search_index_list(ses->list[LIST_CLASS], arg1, NULL);
@@ -303,6 +331,13 @@ DO_CLASS(class_load)
 {
 	FILE *file;
 
+	if (node == NULL)
+	{
+		show_error(ses, LIST_CLASS, "#CLASS {%s} DOES NOT EXIST.", arg1);
+
+		return ses;
+	}
+
 	if (node->data == NULL)
 	{
 		show_error(ses, LIST_CLASS, "#CLASS {%s} DOES NOT HAVE ANY DATA SAVED.", arg1);
@@ -328,6 +363,11 @@ DO_CLASS(class_open)
 {
 	int count;
 
+	if (node == NULL)
+	{
+		node = create_class(ses, arg1, arg2);
+	}
+
 	if (!strcmp(ses->group, arg1))
 	{
 		show_message(ses, LIST_CLASS, "#CLASS {%s} IS ALREADY OPENED AND ACTIVATED.", arg1);
@@ -372,6 +412,13 @@ DO_CLASS(class_save)
 	size_t len;
 	int list, index;
 
+	if (node == NULL)
+	{
+		show_error(ses, LIST_CLASS, "#ERROR: #CLASS {%s} DOES NOT EXIST.", arg1);
+
+		return ses;
+	}
+
 	file = open_memstream(&node->data, (size_t *) &len);
 
 	fprintf(file, "%cCLASS {%s} OPEN\n\n", gtd->tintin_char, arg1);
@@ -407,12 +454,12 @@ DO_CLASS(class_size)
 {
 	if (*arg1 == 0 || *arg2 == 0)
 	{
-		show_error(ses, LIST_CLASS, "#SYNTAX: #CLASS {<class name>} SIZE {<variable>}.");
+		show_error(ses, LIST_CLASS, "#SYNTAX: #CLASS <NAME> SIZE <VARIABLE>");
 		
 		return ses;
 	}
 
-	set_nest_node_ses(ses, arg2, "%d", count_class(ses, node));
+	set_nest_node_ses(ses, arg2, "%d", node ? count_class(ses, node) : 0);
 
 	return ses;
 }
@@ -423,9 +470,16 @@ DO_CLASS(class_write)
 	FILE *file;
 	int list, index;
 
+	if (node == NULL)
+	{
+		show_error(ses, LIST_CLASS, "#ERROR: #CLASS {%s} DOES NOT EXIST.", arg1);
+
+		return ses;
+	}
+
 	if (*arg2 == 0 || (file = fopen(arg2, "w")) == NULL)
 	{
-		show_error(ses, LIST_CLASS, "#ERROR: #CLASS WRITE {%s} - COULDN'T OPEN FILE TO WRITE.", arg2);
+		show_error(ses, LIST_CLASS, "#ERROR: #CLASS WRITE {%s}: COULDN'T OPEN FILE.", arg2);
 		
 		return ses;
 	}

+ 1 - 1
src/command.c

@@ -322,7 +322,7 @@ struct command_type command_table[] =
 	{    "pathdir",           do_pathdir,           3, TOKEN_TYPE_COMMAND },
 	{    "port",              do_port,              2, TOKEN_TYPE_COMMAND },
 	{    "prompt",            do_prompt,            2, TOKEN_TYPE_COMMAND },
-	{    "read",              do_read,              1, TOKEN_TYPE_COMMAND },
+	{    "read",              do_read,              2, TOKEN_TYPE_COMMAND },
 	{    "regexp",            do_regexp,            3, TOKEN_TYPE_REGEX   },
 	{    "replace",           do_replace,           3, TOKEN_TYPE_COMMAND },
 	{    "return",            do_nop,               0, TOKEN_TYPE_RETURN  },

+ 39 - 46
src/config.c

@@ -391,16 +391,9 @@ DO_CONFIG(config_autotab)
 {
 	if (*arg2)
 	{
-		if (!is_number(arg2))
-		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {AUTO TAB} <NUMBER>");
-
-			return NULL;
-		}
-
-		if (atoi(arg2) < 1 || atoi(arg2) > 999999)
+		if (!is_number(arg2) || atoi(arg2) < 1 || atoi(arg2) > 999999)
 		{
-			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG BUFFER: PROVIDE A NUMBER BETWEEN 1 and 999999");
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG {%s} {%s}: PROVIDE A NUMBER BETWEEN 1 and 999999.", config_table[index].name, arg2);
 
 			return NULL;
 		}
@@ -419,7 +412,7 @@ DO_CONFIG(config_buffersize)
 	{
 		if (!is_number(arg2))
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {BUFFER SIZE} <NUMBER>");
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <NUMBER>", config_table[index].name);
 
 			return NULL;
 		}
@@ -434,7 +427,7 @@ DO_CONFIG(config_buffersize)
 				break;
 
 			default:
-				show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG BUFFER: SIZE MUST BE 100, 1000, 10000, 100000, or 1000000.");
+				show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG {BUFFER SIZE}: SIZE MUST BE 100, 1000, 10000, 100000, or 1000000.");
 				return NULL;
 		}
 		init_buffer(ses, atoi(arg2));
@@ -482,7 +475,7 @@ DO_CONFIG(config_charset)
 
 			if (*charset_table[index].name == 0)
 			{
-				show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {CHARSET} <AUTO|ASCII|BIG-5|BIG5TOUTF8|CP437TOUTF8|CP949|CP949TOUTF8|CP1251TOUTF8|GBK-1|GBK1TOUTF8|ISO1TOUTF8|ISO2TOUTF8|KOI8TOUTF8|UTF-8>");
+				show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {CHARSET} {AUTO|ASCII|BIG-5|BIG5TOUTF8|CP437TOUTF8|CP949|CP949TOUTF8|CP1251TOUTF8|GBK-1|GBK1TOUTF8|ISO1TOUTF8|ISO2TOUTF8|KOI8TOUTF8|UTF-8}");
 
 				return NULL;
 			}
@@ -503,7 +496,7 @@ DO_CONFIG(config_charset)
 
 	if (*charset_table[index].name == 0)
 	{
-		show_error(ses, LIST_CONFIG, "#CONFIG CHARSET: INVALID FLAG: %d", ses->charset);
+		show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG {CHARSET}: INVALID FLAG: %d.", ses->charset);
 
 		return NULL;
 	}
@@ -527,7 +520,7 @@ DO_CONFIG(config_childlock)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -564,7 +557,7 @@ DO_CONFIG(config_colormode)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF|ANSI|256|TRUE>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF|ANSI|256|TRUE}", config_table[index].name);
 
 			return NULL;
 		}
@@ -588,7 +581,7 @@ DO_CONFIG(config_colorpatch)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -625,7 +618,7 @@ DO_CONFIG(config_commandecho)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -649,7 +642,7 @@ DO_CONFIG(config_compact)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -665,13 +658,13 @@ DO_CONFIG(config_connectretry)
 	{
 		if (!is_number(arg2))
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {CONNECT RETRY} <NUMBER>");
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <NUMBER>", config_table[index].name);
 
 			return NULL;
 		}
 		else if (atof(arg2) < 0 || atof(arg2) > 10000)
 		{
-			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG CONNECT RETRY: PROVIDE A NUMBER BETWEEN 0.0 and 10000.0");
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG {CONNECT RETRY}: PROVIDE A NUMBER BETWEEN 0.0 and 10000.");
 
 			return NULL;
 		}
@@ -697,7 +690,7 @@ DO_CONFIG(config_convertmeta)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -721,7 +714,7 @@ DO_CONFIG(config_debugtelnet)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -737,14 +730,14 @@ DO_CONFIG(config_historysize)
 	{
 		if (!is_number(arg2))
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {HISTORY SIZE} <NUMBER>");
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <NUMBER>", config_table[index].name);
 
 			return NULL;
 		}
 
 		if (atoi(arg2) < 0 || atoi(arg2) > 9999)
 		{
-			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG HISTORY: PROVIDE A NUMBER BETWEEN 0 and 9999");
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG {%s}: PROVIDE A NUMBER BETWEEN 0 and 9999.", config_table[index].name);
 
 			return NULL;
 		}
@@ -770,7 +763,7 @@ DO_CONFIG(config_hibernate)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -794,7 +787,7 @@ DO_CONFIG(config_inheritance)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -819,7 +812,7 @@ DO_CONFIG(config_loglevel)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <LOW|HIGH>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {LOW|HIGH}", config_table[index].name);
 
 			return NULL;
 		}
@@ -854,7 +847,7 @@ DO_CONFIG(config_logmode)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG LOG <HTML|PLAIN|RAW>");
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {HTML|PLAIN|RAW}", config_table[index].name);
 
 			return NULL;
 		}
@@ -879,7 +872,7 @@ DO_CONFIG(config_mccp)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -938,7 +931,7 @@ DO_CONFIG(config_mousetracking)
 			}
 			else
 			{
-				show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF|DEBUG|INFO|PIXELS>", config_table[index].name);
+				show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF|DEBUG|INFO|PIXELS}", config_table[index].name);
 
 				return NULL;
 			}
@@ -991,13 +984,13 @@ DO_CONFIG(config_packetpatch)
 		}
 		else if (!is_number(arg2))
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {PACKET PATCH} <NUMBER>");
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <NUMBER>", config_table[index].name);
 
 			return NULL;
 		}
 		else if (atof(arg2) < 0 || atof(arg2) > 10)
 		{
-			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG PACKET PATCH: PROVIDE A NUMBER BETWEEN 0.00 and 10.00");
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG {PACKET PATCH}: PROVIDE A NUMBER BETWEEN 0.00 and 10.");
 
 			return NULL;
 		}
@@ -1045,7 +1038,7 @@ DO_CONFIG(config_randomseed)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <AUTO|NUMBER>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {AUTO|<NUMBER>}", config_table[index].name);
 
 			return NULL;
 		}
@@ -1061,7 +1054,7 @@ DO_CONFIG(config_repeatchar)
 	{
 		if (!ispunct((int) arg2[0]))
 		{
-			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG REPEAT CHAR: INVALID CHARACTER {%c}", arg2[0]);
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG {%s}: INVALID CHARACTER {%c}.", config_table[index].name, arg2[0]);
 
 			return NULL;
 		}
@@ -1090,7 +1083,7 @@ DO_CONFIG(config_repeatenter)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -1115,7 +1108,7 @@ DO_CONFIG(config_screenreader)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -1142,7 +1135,7 @@ DO_CONFIG(config_scrolllock)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -1166,7 +1159,7 @@ DO_CONFIG(config_speedwalk)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -1186,13 +1179,13 @@ DO_CONFIG(config_tabwidth)
 		}
 		else if (!is_number(arg2))
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {TAB WIDTH} <NUMBER>");
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <NUMBER>", config_table[index].name);
 
 			return NULL;
 		}
 		else if (atof(arg2) < 1 || atof(arg2) > 16)
 		{
-			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG TAB WIDTH: PROVIDE A NUMBER BETWEEN 1 and 16");
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG {TAB WIDTH}: PROVIDE A NUMBER BETWEEN 1 and 16.");
 
 			return NULL;
 		}
@@ -1234,7 +1227,7 @@ DO_CONFIG(config_telnet)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF|DEBUG|INFO>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF|DEBUG|INFO}", config_table[index].name);
 
 			return NULL;
 		}
@@ -1251,7 +1244,7 @@ DO_CONFIG(config_tintinchar)
 	{
 		if (!ispunct((int) arg2[0]))
 		{
-			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG TINTIN CHAR: INVALID CHARACTER {%c}", arg2[0]);
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG {%s}: INVALID CHARACTER {%c}.", config_table[index].name, arg2[0]);
 
 			return NULL;
 		}
@@ -1277,7 +1270,7 @@ DO_CONFIG(config_verbatim)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -1293,7 +1286,7 @@ DO_CONFIG(config_verbatimchar)
 	{
 		if (!ispunct((int) arg2[0]))
 		{
-			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG VERBATIM CHAR: INVALID CHARACTER {%c}", arg2[0]);
+			show_error(ses, LIST_CONFIG, "#ERROR: #CONFIG {%s}: INVALID CHARACTER {%c}.", config_table[index].name, arg2[0]);
 
 			return NULL;
 		}
@@ -1319,7 +1312,7 @@ DO_CONFIG(config_verbose)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}
@@ -1343,7 +1336,7 @@ DO_CONFIG(config_wordwrap)
 		}
 		else
 		{
-			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} <ON|OFF>", config_table[index].name);
+			show_error(ses, LIST_CONFIG, "#SYNTAX: #CONFIG {%s} {ON|OFF}", config_table[index].name);
 
 			return NULL;
 		}

+ 57 - 8
src/cursor.c

@@ -1033,7 +1033,7 @@ DO_CURSOR(cursor_echo)
 	}
 	else
 	{
-		show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {ECHO} {ON|OFF}.");
+		show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {ECHO} {ON|OFF}");
 	}
 }
 
@@ -1137,6 +1137,55 @@ DO_CURSOR(cursor_enter)
 	return;
 }
 
+DO_CURSOR(cursor_escape_enter)
+{
+	if (*gtd->ses->input->buf != 0)
+	{
+		int nest = 0;
+		char *pti = gtd->ses->input->buf;
+		char *pto = gtd->ses->input->buf;
+
+		while (*pti)
+		{
+			switch (*pti)
+			{
+				case '{':
+					nest++;
+					*pto++ = *pti++;
+					break;
+				case '}':
+					nest--;
+					*pto++ = *pti++;
+					break;
+				case '\\':
+					if (pti[1] == 'n')
+					{
+						if (pti[2] == '#' && nest == 0)
+						{
+							*pto++ = ';';
+						}
+						pti += 2;
+					}
+					else if (pti[1])
+					{
+						*pto++ = *pti++;
+						*pto++ = *pti++;
+					}
+					else
+					{
+						*pto++ = *pti++;
+					}
+					break;
+				default:
+					*pto++ = *pti++;
+					break;
+			}
+		}
+		*pto = 0;
+	}
+	cursor_enter(ses, "");
+}
+	
 DO_CURSOR(cursor_soft_enter)
 {
 	if (!inputline_editor())
@@ -1236,11 +1285,11 @@ DO_CURSOR(cursor_flag)
 		}
 		else
 		{
-			show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {FLAG} {EOL} {CR|LF|CRLF|CRNUL|OFF}.");
+			show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {FLAG} {EOL} {CR|LF|CRLF|CRNUL|OFF}");
 			return;
 		}
 
-		show_message(gtd->ses, LIST_COMMAND, "#CURSOR FLAG EOL HAS BEEN SET TO: %s",
+		show_message(gtd->ses, LIST_COMMAND, "#CURSOR FLAG EOL HAS BEEN SET TO: %s.",
 			HAS_BIT(ses->telopts, TELOPT_FLAG_CR|TELOPT_FLAG_LF) == 0 ? "OFF" :
 			HAS_BIT(ses->telopts, TELOPT_FLAG_CR|TELOPT_FLAG_NUL) == (TELOPT_FLAG_CR|TELOPT_FLAG_NUL) ? "CRNUL" :
 			HAS_BIT(ses->telopts, TELOPT_FLAG_CR|TELOPT_FLAG_LF) == TELOPT_FLAG_CR ? "CR" :
@@ -1265,7 +1314,7 @@ DO_CURSOR(cursor_flag)
 		}
 		else
 		{
-			show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {FLAG} {ECHO} {ON|OFF}.");
+			show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {FLAG} {ECHO} {ON|OFF}");
 		}
 		return;
 	}
@@ -1286,12 +1335,12 @@ DO_CURSOR(cursor_flag)
 		}
 		else
 		{
-			show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {FLAG} {INSERT} {ON|OFF}.");
+			show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {FLAG} {INSERT} {ON|OFF}");
 		}
 		return;
 	}
 
-	show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {FLAG} {ECHO|EOL|INSERT} {ON|OFF}.");
+	show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {FLAG} {ECHO|EOL|INSERT} {ON|OFF}");
 }
 
 DO_CURSOR(cursor_get)
@@ -1302,7 +1351,7 @@ DO_CURSOR(cursor_get)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #CURSOR GET {variable}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #CURSOR GET <VARIABLE>");
 	}
 	else
 	{
@@ -1593,7 +1642,7 @@ DO_CURSOR(cursor_insert)
 	}
 	else
 	{
-		show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {INSERT} {ON|OFF}.");
+		show_error(gtd->ses, LIST_COMMAND, "#SYNTAX: #CURSOR {INSERT} {ON|OFF}");
 	}
 }
 

+ 4 - 4
src/daemon.c

@@ -180,11 +180,11 @@ DO_DAEMON(daemon_attach)
 
 		if (*arg1)
 		{
-			show_message(ses, LIST_COMMAND, "#DAEMON ATTACH: UNABLE TO FIND DAEMON FILE {%s} IN {%s}.", arg1, filename);
+			show_message(ses, LIST_COMMAND, "#ERROR: #DAEMON ATTACH: UNABLE TO FIND DAEMON FILE {%s} IN {%s}.", arg1, filename);
 		}
 		else
 		{
-			show_message(ses, LIST_COMMAND, "#DAEMON ATTACH: NO AVAILABLE DAEMON FILES FOUND IN {%s}.", filename);
+			show_message(ses, LIST_COMMAND, "#ERROR: #DAEMON ATTACH: NO AVAILABLE DAEMON FILES FOUND IN {%s}.", filename);
 		}
 
 		return;
@@ -351,7 +351,7 @@ DO_DAEMON(daemon_detach)
 		}
 		else
 		{
-			show_error(gtd->ses, LIST_COMMAND, "#DAEMON DETACH: ALREADY FULLY DETACHED.");
+			show_error(gtd->ses, LIST_COMMAND, "#ERROR: #DAEMON DETACH: ALREADY FULLY DETACHED.");
 		}
 		return;
 	}
@@ -410,7 +410,7 @@ DO_DAEMON(daemon_detach)
 
 	if (strlen(filename) >= sizeof(addr_un.sun_path))
 	{
-		tintin_printf(ses, "#DAEMON DETACH: FILE NAME LENGTH OF {%s} EXCEEDS MAXIMUM OF %d.", filename, sizeof(addr_un.sun_path));
+		show_error(ses, LIST_COMMAND, "#ERROR: #DAEMON DETACH: FILE NAME LENGTH OF {%s} EXCEEDS MAXIMUM OF %d.", filename, sizeof(addr_un.sun_path));
 
 		return;
 	}

+ 15 - 10
src/data.c

@@ -164,6 +164,11 @@ struct listnode *create_node_list(struct listroot *root, char *arg1, char *arg2,
 	}
 
 	if (HAS_BIT(root->flags, LIST_FLAG_NEST) && *arg1 == '\\')
+	{
+		show_error(root->ses, LIST_COMMAND, "#WARNING: VARIABLE {%s}. THE \\ CHARACTER NEEDS TO BE REPLACED WITH = FOR A STRICT EQUALITY MATCH.", arg1);
+		arg1++;
+	}
+	else if (HAS_BIT(root->flags, LIST_FLAG_NEST) && *arg1 == '=')
 	{
 		arg1++;
 	}
@@ -652,7 +657,7 @@ int bsearch_alnum_list(struct listroot *root, char *text, int seek)
 				}
 				break;
 
-			case '\\':
+			case '/':
 				text++;
 				break;
 		}
@@ -982,7 +987,7 @@ int delete_node_with_wild(struct session *ses, int type, char *text)
 	{
 		node = root->list[index];
 
-		show_message(ses, type, "#OK. {%s} IS NO LONGER %s %s.", node->arg1, (*list_table[type].name == 'A' || *list_table[type].name == 'E') ? "AN" : "A", list_table[type].name);
+		show_message(ses, type, "#OK: {%s} IS NO LONGER %s %s.", node->arg1, (*list_table[type].name == 'A' || *list_table[type].name == 'E') ? "AN" : "A", list_table[type].name);
 
 		delete_index_list(root, index);
 
@@ -993,7 +998,7 @@ int delete_node_with_wild(struct session *ses, int type, char *text)
 	{
 		if (match(ses, root->list[index]->arg1, arg1, SUB_VAR|SUB_FUN))
 		{
-			show_message(ses, type, "#OK. {%s} IS NO LONGER %s %s.", root->list[index]->arg1, is_vowel(list_table[type].name) ? "AN" : "A", list_table[type].name);
+			show_message(ses, type, "#OK: {%s} IS NO LONGER %s %s.", root->list[index]->arg1, is_vowel(list_table[type].name) ? "AN" : "A", list_table[type].name);
 
 			delete_index_list(root, index);
 
@@ -1068,7 +1073,7 @@ DO_COMMAND(do_kill)
 
 	if (index == LIST_MAX)
 	{
-		show_error(ses, LIST_COMMAND, "#ERROR: #KILL {%s} {%s} - NO MATCH FOUND.", arg1, arg2);
+		show_error(ses, LIST_COMMAND, "#ERROR: #KILL {%s} {%s}: NO MATCH FOUND.", arg1, arg2);
 	}
 	return ses;
 }
@@ -1134,7 +1139,7 @@ DO_COMMAND(do_message)
 
 		if (found == FALSE)
 		{
-			show_error(ses, LIST_COMMAND, "#ERROR: #MESSAGE {%s} - NO MATCH FOUND.", arg1);
+			show_error(ses, LIST_COMMAND, "#ERROR: #MESSAGE {%s}: NO MATCH FOUND.", arg1);
 		}
 	}
 	return ses;
@@ -1213,7 +1218,7 @@ DO_COMMAND(do_ignore)
 
 		if (found == FALSE)
 		{
-			show_error(ses, LIST_COMMAND, "#ERROR: #IGNORE {%s} - NO MATCH FOUND.", arg1);
+			show_error(ses, LIST_COMMAND, "#ERROR: #IGNORE {%s}: NO MATCH FOUND.", arg1);
 		}
 	}
 	return ses;
@@ -1285,7 +1290,7 @@ DO_COMMAND(do_debug)
 
 		if (found == FALSE)
 		{
-			show_error(ses, LIST_COMMAND, "#DEBUG {%s} - NO MATCH FOUND.", arg1);
+			show_error(ses, LIST_COMMAND, "#DEBUG {%s}: NO MATCH FOUND.", arg1);
 		}
 	}
 	return ses;
@@ -1373,7 +1378,7 @@ DO_COMMAND(do_info)
 				}
 				else
 				{
-					show_error(ses, LIST_COMMAND, "#SYNTAX: #INFO {%s} [ON|OFF|LIST|SAVE|SYSTEM]", arg1);
+					show_error(ses, LIST_COMMAND, "#SYNTAX: #INFO {%s} [ON|OFF|LIST|SAVE]", arg1);
 				}
 				return ses;
 			}
@@ -1580,7 +1585,7 @@ DO_COMMAND(do_info)
 					{
 						str_ptr = gtd->memory->list[index];
 
-						if (str_ptr->max != NAME_SIZE + 1 && strlen(get_str_str(str_ptr)) != str_ptr->len)
+						if (!HAS_BIT(str_ptr->flags, STR_FLAG_FREE) && str_ptr->max != NAME_SIZE + 1 && strlen(get_str_str(str_ptr)) != str_ptr->len)
 						{
 							tintin_printf2(ses, "#ERROR: index %d len = %d/%d max = %d flags = %d (%s)", index, strlen(get_str_str(str_ptr)), str_ptr->len, str_ptr->max, str_ptr->flags, get_str_str(str_ptr));
 						}
@@ -1647,7 +1652,7 @@ DO_COMMAND(do_info)
 					if (is_abbrev(arg2, "SAVE"))
 					{
 						set_nest_node_ses(ses, "info[OUTPUT]", "{RAWBUF}{%s}", gtd->mud_output_buf);
-						add_nest_node_ses(ses, "info[OUTPUT]", "{RAWLEN}{%s}", gtd->mud_output_len);
+						add_nest_node_ses(ses, "info[OUTPUT]", "{RAWLEN}{%d}", gtd->mud_output_len);
 						add_nest_node_ses(ses, "info[OUTPUT]", "{STRBUF}{%s}", gtd->mud_output_strip_buf);
 						add_nest_node_ses(ses, "info[OUTPUT]", "{STRLEN}{%d}", gtd->mud_output_strip_len);
 						add_nest_node_ses(ses, "info[OUTPUT]", "{LINE}{%s}",   gtd->mud_output_line);

+ 11 - 7
src/draw.c

@@ -457,7 +457,7 @@ DO_COMMAND(do_draw)
 
 			if (!is_math(ses, arg1) || !is_math(ses, arg2) || !is_math(ses, arg3) || !is_math(ses, arg4))
 			{
-				show_error(ses, LIST_COMMAND, "#ERROR: #DRAW NON-NUMERIC SQUARE: %s {%s %s %s %s}",
+				show_error(ses, LIST_COMMAND, "#ERROR: #DRAW: NON-NUMERIC SQUARE: %s {%s %s %s %s}",
 					draw_table[index].name,
 					is_math(ses, arg1) ? ntos(top_row) : arg1,
 					is_math(ses, arg2) ? ntos(top_col) : arg2,
@@ -508,7 +508,7 @@ DO_COMMAND(do_draw)
 
 			if (top_row > bot_row || top_col > bot_col)
 			{
-				show_error(ses, LIST_COMMAND, "#ERROR: #DRAW INVALID SQUARE: %s {%d %d %d %d} ROWS: %d COLS: %d", draw_table[index].name, top_row, top_col, bot_row, bot_col, 1 + bot_row - top_row, 1 + bot_col - top_col);
+				show_error(ses, LIST_COMMAND, "#ERROR: #DRAW: INVALID SQUARE: %s {%d %d %d %d} ROWS: %d COLS: %d", draw_table[index].name, top_row, top_col, bot_row, bot_col, 1 + bot_row - top_row, 1 + bot_col - top_col);
 
 				return ses;
 			}
@@ -1510,10 +1510,14 @@ DO_DRAW(draw_buffer)
 		line = ses->scroll->line;
 	}
 
-	for (cnt = rows ; cnt >= 0 && line - cnt >= 0 ; cnt--)
+	for (cnt = rows ; cnt >= 0 ; cnt--)
 	{
-		str_cat_printf(&buf, "{%s}", ses->scroll->buffer[line - cnt]->str);
+		if (line - cnt >= 0)
+		{
+			str_cat_printf(&buf, "{%s}", ses->scroll->buffer[line - cnt]->str);
+		}
 	}
+	tintin_printf2(gtd->ses, "[%d] (%d) (%s)", line, rows, buf);
 
 	draw_box(ses, top_row, top_col, bot_row, bot_col, rows, cols, flags, box_color, txt_color, buf, arg1, arg2, arg3);
 
@@ -1806,7 +1810,7 @@ DO_DRAW(draw_hbar)
 
 	if (bar <= 0)
 	{
-		show_error(ses, LIST_COMMAND, "#DRAW BAR %d %d %d %d: DRAWING WIDTH (%d) MUST BE GREATER THAN 0.", top_row, top_col, bot_row, bot_col, bar);
+		show_error(ses, LIST_COMMAND, "#ERROR: #DRAW BAR %d %d %d %d: DRAWING WIDTH (%d) MUST BE GREATER THAN 0.", top_row, top_col, bot_row, bot_col, bar);
 
 		return;
 	}
@@ -1836,7 +1840,7 @@ DO_DRAW(draw_hbar)
 
 	if (max <= 0)
 	{
-		show_error(ses, LIST_COMMAND, "#DRAW BAR {%s;%s;%s}: MAX (%Lg) MUST BE GREATER THAN 0.", arg1, arg2, arg3, max);
+		show_error(ses, LIST_COMMAND, "#ERROR: #DRAW BAR {%s;%s;%s}: MAX (%Lg) MUST BE GREATER THAN 0.", arg1, arg2, arg3, max);
 
 		return;
 	}
@@ -1932,7 +1936,7 @@ DO_DRAW(draw_rain)
 
 	if (!valid_variable(ses, arg1))
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #DRAW {<COLOR>} RAIN %d %d %d %d {<VARIABLE>} {[SPAWN]} {[FADE]} {[LEGEND]}.", top_row, top_col, bot_row, bot_col);
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #DRAW <COLOR> RAIN %d %d %d %d <VARIABLE> [SPAWN] [FADE] [LEGEND]", top_row, top_col, bot_row, bot_col);
 
 		return;
 	}

+ 1 - 1
src/edit.c

@@ -180,7 +180,7 @@ DO_EDIT(edit_read)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #EDIT READ {<FILENAME>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #EDIT READ <FILENAME>");
 
 		return ses;
 	}

+ 2 - 2
src/event.c

@@ -128,7 +128,7 @@ DO_COMMAND(do_event)
 
 			if (symbol == 0)
 			{
-				show_message(ses, LIST_ALIAS, "#EVENT: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+				show_message(ses, LIST_EVENT, "#EVENT: NO MATCHES FOUND FOR {%s}.", arg1);
 			}
 		}
 	}
@@ -157,7 +157,7 @@ DO_COMMAND(do_event)
 				return ses;
 			}
 		}
-		show_error(ses, LIST_EVENT, "#EVENT {%s} IS NOT AN EXISTING EVENT.", arg1);
+		show_error(ses, LIST_EVENT, "#ERROR: #EVENT {%s} IS NOT AN EXISTING EVENT.", arg1);
 	}
 	return ses;
 }

+ 14 - 12
src/files.c

@@ -33,13 +33,14 @@ DO_COMMAND(do_read)
 {
 	FILE *file;
 
-	sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+	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 ((file = fopen(arg1, "r")) == NULL)
 	{
 		check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", arg1, "FILE NOT FOUND.");
 
-		tintin_printf(ses, "#READ {%s} - FILE NOT FOUND.", arg1);
+		tintin_printf(ses, "#READ {%s}: FILE NOT FOUND.", arg1);
 
 		return ses;
 	}
@@ -63,7 +64,7 @@ struct session *read_file(struct session *ses, FILE *file, char *filename)
 	{
 		check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", filename, "INVALID START OF FILE");
 
-		tintin_printf(ses, "#ERROR: #READ {%s} - INVALID START OF FILE '%c'.", filename, temp[0]);
+		tintin_printf(ses, "#ERROR: #READ {%s}: INVALID START OF FILE '%c'.", filename, temp[0]);
 
 		return ses;
 	}
@@ -88,7 +89,7 @@ struct session *read_file(struct session *ses, FILE *file, char *filename)
 	{
 		check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", filename, "FAILED TO ALLOCATE MEMORY");
 
-		tintin_printf(ses, "#ERROR: #READ {%s} - FAILED TO ALLOCATE %d BYTES OF MEMORY.", filename, size + 2);
+		tintin_printf(ses, "#ERROR: #READ {%s}: FAILED TO ALLOCATE %d BYTES OF MEMORY.", filename, size + 2);
 
 		return ses;
 	}
@@ -98,7 +99,7 @@ struct session *read_file(struct session *ses, FILE *file, char *filename)
 	{
 		check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", filename, "FREAD FAILURE");
 		
-		tintin_printf(ses, "#ERROR: #READ {%s} - FREAD FAILURE.", filename);
+		tintin_printf(ses, "#ERROR: #READ {%s}: FREAD FAILURE.", filename);
 
 		return ses;
 	}
@@ -209,7 +210,7 @@ struct session *read_file(struct session *ses, FILE *file, char *filename)
 						{
 							if (*pti == gtd->tintin_char)
 							{
-								show_error(ses, LIST_COMMAND, "#WARNING: #READ {%s} MISSING SEMICOLON ON LINE %d.", filename, lnc);
+								show_error(ses, LIST_COMMAND, "#WARNING: #READ {%s}: MISSING SEMICOLON ON LINE %d.", filename, lnc);
 							}
 						}
 
@@ -297,7 +298,7 @@ struct session *read_file(struct session *ses, FILE *file, char *filename)
 	{
 		check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", filename, "MISSING BRACE OPEN OR CLOSE");
 
-		tintin_printf(ses, "#ERROR: #READ {%s} - MISSING %d '%c' BETWEEN LINE %d AND %d.", filename, abs(lvl), lvl < 0 ? DEFAULT_OPEN : DEFAULT_CLOSE, fix == 0 ? 1 : ok, fix == 0 ? lnc + 1 : fix);
+		tintin_printf(ses, "#ERROR: #READ {%s}: MISSING %d '%c' BETWEEN LINE %d AND %d.", filename, abs(lvl), lvl < 0 ? DEFAULT_OPEN : DEFAULT_CLOSE, fix == 0 ? 1 : ok, fix == 0 ? lnc + 1 : fix);
 
 		free(bufi);
 		free(bufo);
@@ -309,7 +310,7 @@ struct session *read_file(struct session *ses, FILE *file, char *filename)
 	{
 		check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "READ ERROR", filename, "MISSING COMMENT OPEN OR CLOSE");
 
-		tintin_printf(ses, "#ERROR: #READ {%s} - MISSING %d '%s'", filename, abs(com), com < 0 ? "/*" : "*/");
+		tintin_printf(ses, "#ERROR: #READ {%s}: MISSING %d '%s'", filename, abs(com), com < 0 ? "/*" : "*/");
 
 		free(bufi);
 		free(bufo);
@@ -324,7 +325,7 @@ struct session *read_file(struct session *ses, FILE *file, char *filename)
 		gtd->level->verbose++;
 		gtd->level->debug++;
 
-		show_error(ses, LIST_COMMAND, "\e[1;5;31mWARNING: SETTING THE COMMAND CHARACTER TO '%c' BECAUSE IT'S THE FIRST CHARACTER IN THE FILE.", bufo[0]);
+		show_error(ses, LIST_COMMAND, "#WARNING: #READ {%s}: SETTING THE COMMAND CHARACTER TO '%c' BECAUSE IT'S THE FIRST CHARACTER IN THE FILE.", filename, bufo[0]);
 
 		gtd->level->debug--;
 		gtd->level->verbose--;
@@ -359,7 +360,7 @@ struct session *read_file(struct session *ses, FILE *file, char *filename)
 
 		if (pto - bufi >= BUFFER_SIZE)
 		{
-			show_debug(ses, LIST_COMMAND, "#WARNING: #READ {%s} - POSSIBLE BUFFER OVERFLOW AT COMMAND: %.30s", filename, bufi);
+			show_debug(ses, LIST_COMMAND, "#WARNING: #READ {%s}: POSSIBLE BUFFER OVERFLOW AT COMMAND: %.30s", filename, bufi);
 		}
 
 		if (bufi[0])
@@ -421,7 +422,7 @@ DO_COMMAND(do_write)
 	{
 		check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "WRITE ERROR", arg1, "INVALID FILE NAME");
 
-		tintin_printf2(ses, "#SYNTAX: #WRITE {<filename>} {[FORCE]}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #WRITE <FILENAME> [FORCE]");
 
 		return ses;
 	}
@@ -429,6 +430,7 @@ DO_COMMAND(do_write)
 	if (is_suffix(arg1, ".map") && !is_abbrev(arg2, "FORCE"))
 	{
 		check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "WRITE ERROR", arg1, "INVALID FILE EXTENSION");
+
 		tintin_printf2(ses, "#WRITE {%s}: USE {FORCE} TO OVERWRITE .map FILES.", arg1);
 
 		return ses;
@@ -438,7 +440,7 @@ DO_COMMAND(do_write)
 	{
 		check_all_events(ses, EVENT_FLAG_SYSTEM, 0, 2, "WRITE ERROR", arg1, "FAILED TO OPEN");
 
-		tintin_printf(ses, "#ERROR: #WRITE: COULDN'T OPEN {%s} TO WRITE.", arg1);
+		show_error(ses, LIST_COMMAND, "#ERROR: #WRITE: COULDN'T OPEN {%s} TO WRITE.", arg1);
 
 		return ses;
 	}

+ 54 - 26
src/help.c

@@ -462,8 +462,10 @@ DO_COMMAND(do_help)
 		{
 			if (is_abbrev(arg1, help_table[cnt].name))
 			{
-				print_lines(ses, SUB_COL, "", /*COLOR_HELP_DIM,*/ "%s<088>\n", help_table[cnt].text);
-
+				if (!check_all_events(ses, EVENT_FLAG_CATCH, 1, 0, "CATCH HELP %s", help_table[cnt].name))
+				{
+					print_lines(ses, SUB_COL, "", /*COLOR_HELP_DIM,*/ "%s<088>\n", help_table[cnt].text);
+				}
 				if (*help_table[cnt].also)
 				{
 					print_lines(ses, SUB_COL, "", "%s<088>\n\n", help_related(ses, cnt, 0));
@@ -489,7 +491,7 @@ DO_COMMAND(do_help)
 
 		if (found == FALSE)
 		{
-			tintin_printf2(ses, "No help found for '%s'", arg1);
+			show_message(ses, LIST_COMMAND, "#HELP: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	return ses;
@@ -1283,7 +1285,7 @@ struct help_type help_table[] =
 		"<278>         BOTTOM      draw on the bottom side if possible.\n"
 		"<278>         BOXED       draw a box along the square.\n"
 		"<278>         BUMPED      precede the draw with an enter.\n"
-		"<278>         CALIGN      center text.\n"
+		"<278>         CALIGN      both LALIGN and RALIGN to center text.\n"
 		"<278>         CIRCLED     circle the corners.\n"
 		"<278>         CONVERT     draw text with meta conversion.\n"
 		"<278>         CROSSED     cross the corners.\n"
@@ -1342,7 +1344,7 @@ struct help_type help_table[] =
 		"<278>         All draw types take an optional text argument as long as a valid\n"
 		"<278>         square with enough space has been defined. Text is automatically\n"
 		"<278>         word wrapped and text formatting can be customized with the\n"
-		"<278>         CALIGN, LALIGN, RALIGN, and UALIGN options.\n"
+		"<278>         BALIGN, TALIGN, LALIGN, RALIGN, and UALIGN options.\n"
 		"\n"
 		"<178>Example<278>: #draw Blue box 1 1 3 20 {Hello world!}\n"
 		,
@@ -1701,6 +1703,8 @@ struct help_type help_table[] =
 		"<278>         <178>BUFFER UPDATE<278>, <178>DISPLAY UPDATE\n"
 		"<278>           These events have no additional arguments.\n"
 		"\n"
+		"<278>         <178>PROCESSED LINE         <278>%0 raw text %1 plain text %2 prompt (0 or 1)\n"
+		"\n"
 		"<278>         <178>RECEIVED LINE          <278>%0 raw text %1 plain text\n"
 		"<278>         <178>RECEIVED OUTPUT        <278>%0 raw text %1 plain text\n"
 		"<278>         <178>RECEIVED PROMPT        <278>%0 raw text %1 plain text\n"
@@ -1714,6 +1718,7 @@ struct help_type help_table[] =
 		"<278>         <178>PORT DISCONNECTION     <278>%0 name %1 ip %2 port\n"
 		"<278>         <178>PORT LOG MESSAGE       <278>%0 name %1 ip %2 port %3 data %4 plain data\n"
 		"<278>         <178>PORT RECEIVED MESSAGE  <278>%0 name %1 ip %2 port %3 data %4 plain data\n"
+		"<278>         <178>PORT RECEIVED DATA     <278>%0 name %1 ip %2 port %3 data %4 size\n"
 		"\n"
 		"<278>         <128>SCAN EVENTS\n"
 		"\n"
@@ -1766,6 +1771,8 @@ struct help_type help_table[] =
 		"<278>         <178>UNKNOWN COMMAND        <278>%0 raw text\n"
 		"<278>         <178>SIGUSR                 <278>%0 signal\n"
 		"\n"
+		"<278>         <178>REFORMAT <MESSAGE>     <278>Use #return to change MESSAGE\n"
+		"\n"
 		"<278>         <128>TELNET EVENTS\n"
 		"\n"
 		"<278>         <178>IAC <EVENT>\n"
@@ -2035,11 +2042,12 @@ struct help_type help_table[] =
 	{
 		"HISTORY",
 		TOKEN_TYPE_COMMAND,
-		"<178>Command<278>: #history <178>{<278>delete<178>}<278>                 Delete the last command.\n"
-		"<278>         #history <178>{<278>insert<178>}    {<278>command<178>}<278>    Insert a command.\n"
-		"<278>         #history <178>{<278>list<178>}<278>                   Display the entire command history.\n"
-		"<278>         #history <178>{<278>read<178>}      {<278>filename<178>}<278>   Read a command history from file.\n"
-		"<278>         #history <178>{<278>write<178>}     {<278>filename<178>}<278>   Write a command history to file.\n"
+		"<178>Command<278>: #history <178>{<278>delete<178>}<278>                    Delete the last command.\n"
+		"<278>         #history <178>{<278>get<178>}    {<278>variable<178>} {<278>range<178>}<278> Store list in variable.\n"
+		"<278>         #history <178>{<278>insert<178>} {<278>command<178>}<278>          Insert a command.\n"
+		"<278>         #history <178>{<278>list<178>}     <278>                 Display the command history.\n"
+		"<278>         #history <178>{<278>read<178>}   {<278>filename<178>}<278>         Read a command history from file.\n"
+		"<278>         #history <178>{<278>write<178>}  {<278>filename<178>}<278>         Write a command history to file.\n"
 		"\n"
 		"<278>         Without an argument all available options are shown.\n"
 		"\n"
@@ -2069,7 +2077,7 @@ struct help_type help_table[] =
 	{
 		"IF",
 		TOKEN_TYPE_COMMAND,
-		"<178>Command<278>: #if <178>{<278>conditional<178>} {<278>commands if true<178>}\n"
+		"<178>Command<278>: #if <178>{<278>conditional<178>} {<278>commands if true<178>} {<278>commands if false<178>}\n"
 		"\n"
 		"<278>         The #if command works similar to an if statement in other languages,\n"
 		"<278>         and is based on the way C handles its conditional statements.\n"
@@ -2081,12 +2089,15 @@ struct help_type help_table[] =
 		"<278>         executed. See the 'math' helpfile for more information.\n"
 		"\n"
 		"<278>         To handle the case where an if statement is false it can be followed\n"
-		"<278>         by the #else command.\n"
+		"<278>         by the #else command. Alternatively, the else can be provided as the\n"
+		"<278>         third argument.\n"
 		"\n"
 		"<178>Example<278>: #action {%0 gives you %1 gold coins.} {#if {%1 > 5000} {thank %0}}\n"
 		"<278>         If someone gives you more than 5000 coins, thank them.\n"
 		"\n"
-		"<178>Example<278>: #alias {k} {#if {\"%0\" == \"\"} {kill $target};#else {kill %0}}\n",
+		"<178>Example<278>: #alias {k} {#if {\"%0\" == \"\"} {kill $target};#else {kill %0}}\n"
+		"\n"
+		"<178>Example<278>: #if {\"%0\" == \"{bli|bla}\"} {#showme %0 is either bli or bla.}\n",
 		
 		"case default else elseif math switch regexp"
 	},
@@ -2525,7 +2536,7 @@ struct help_type help_table[] =
 		"<278>         <178>#line background <argument>\n"
 		"<278>           Prevent new session activation.\n"
 		"\n"
-		"<278>         <178>#line capture <variable> <argument.\n"
+		"<278>         <178>#line capture <variable> <argument>\n"
 		"<278>           Argument is executed and output stored in <variable>.\n"
 		"\n"
 		"<278>         <178>#line convert <argument>\n"
@@ -2589,6 +2600,7 @@ struct help_type help_table[] =
 		"<278>         #list {var} {add} <items>              Add <items> to the list\n"
 		"<278>         #list {var} {clear}                    Empty the given list\n"
 		"<278>         #list {var} {collapse} <separator>     Turn list into a variable\n"
+		"<278>         #list {var} {copy} <variable>          Copy variable to the list\n"
 		"<278>         #list {var} {create} <items>           Create a list using <items>\n"
 		"<278>         #list {var} {delete} <index> [amount]  Delete the item at <index>,\n"
 		"<278>                                                the [amount] is optional.\n"
@@ -2609,6 +2621,7 @@ struct help_type help_table[] =
 		"<278>         #list {var} {size} <variable>          Copy list size to {variable}\n"
 		"<278>         #list {var} {sort} [items]             Sort list alphabetically, if\n"
 		"<278>                                                an item is given it's added.\n"
+		"<278>         #list {var} {swap} <index> <index>     Swap two items\n"
 		"<278>         #list {var} {tokenize} <string>        Create a character list\n"
 		"\n"
 		"<278>         The index should be between +1 and the list's size. You can also give\n"
@@ -3005,6 +3018,9 @@ struct help_type help_table[] =
 		"<278>           Start logging to the given file, if the file already exists it won't\n"
 		"<278>           be overwritten and data will be appended to the end.\n"
 		"\n"
+		"<278>         <178>#log make <directory>\n"
+		"<278>           Create the given directory.\n"
+		"\n"
 		"<278>         <178>#log move <filename_1> <filename_2>\n"
 		"<278>           Move filename_1 to filename_2. This can be any file and doesn't need\n"
 		"<278>           to be a log file.\n"
@@ -3727,6 +3743,9 @@ struct help_type help_table[] =
 		"\n"
 		"<278>         Links can be created using the MSLP protocol which will generate link\n"
 		"<278>         specific events when clicked.\n"
+		"\n"
+		"<278>         In order to copy/paste, most terminals require that you press the shift\n"
+		"<278>         key during selection.\n"
 		"\n",
 		
 		"button draw event MSLP"
@@ -3795,7 +3814,9 @@ struct help_type help_table[] =
 		"<278>         you need to use \\e]68;2;\\a, and they instead trigger the SECURE LINK\n"
 		"<278>         event.\n"
 		"\n"
-		"<178>Example<278>: #sub {%* tells %*} {\\e]68;2;EXEC;#cursor set tell %1 \\a\\e[4m%0\\e[24m}\n"
+		"<278>         To creae a link that is not undelined, use \\e]4;24m text \\e]24m.\n"
+		"\n"
+		"<178>Example<278>: #sub {%* tells %*} {\\e]68;2;EXEC;#cursor set tell %1 \\a\\e[4;24m%0\\e[24m}\n"
 		"<178>       <278>  #event {PRESSED SECURE LINK EXEC MOUSE BUTTON ONE} {%4}\n"
 		"\n"
 		"<278>         This would make you start a reply when clicking on a tell.\n"
@@ -3938,7 +3959,7 @@ struct help_type help_table[] =
 		"<278>         is used.\n"
 		"\n"
 		"<178>TinTin++ <178>Description                                      POSIX\n"
-		"<178>      %a <278>Match zero or more characters including newlines ([^\\n]*?)\n"       
+		"<178>      %a <278>Match zero or more characters including newlines ([^\\0]*?)\n"       
 		"<178>      %A <278>Match zero or more newlines                      ([\\n]*?)\n"
 		"<178>      %c <278>Match zero or more ansi color codes              ((?:\\e\\[[0-9;]*m)*?)\n"
 		"<178>      %d <278>Match zero or more digits                        ([0-9]*?)\n"
@@ -3946,6 +3967,7 @@ struct help_type help_table[] =
 		"<178>      %i <278>Matches become case insensitive                  (?i)\n"
 		"<178>      %I <278>Matches become case sensitive (default)          (?-i)\n"
 		"<178>      %s <278>Match zero or more spaces                        ([\\r\\n\\t ]*?)\n"
+		"<178>      %S <278>Match zero or more non-spaces                    ([^\\r\\n\\t ]*?)\n"
 		"<178>      %w <278>Match zero or more word characters               ([A-Za-z0-9_]*?)\n"
 		"<178>      %W <278>Match zero or more non-word characters           ([^A-Za-z0-9_]*?)\n"
 		"<178>      %? <278>Match zero or one character                      (.\?\?)\n"
@@ -4289,8 +4311,8 @@ struct help_type help_table[] =
 		TOKEN_TYPE_STRING,
 		"<178>Command<278>: #<178>[<078>number<178>] {<278>commands<178>}\n"
 		"\n"
-		"        Sometimes you want to repeat the same command multiple times. This is\n"
-		"        the easiest way to accomplish that.\n"
+		"<278>        Sometimes you want to repeat the same command multiple times. This is\n"
+		"<278>        the easiest way to accomplish that.\n"
 		"\n"
 		"<178>Example<278>: #10 {buy bread}\n",
 		
@@ -4798,7 +4820,7 @@ struct help_type help_table[] =
 		"<278>         Replace generic dark blue color codes with bright blue ones.\n"
 		"\n"
 		"<178>Example<278>: #sub {%1massacres%2} {<<888>018>%1<<888>118>MASSACRES<<888>018>%2}\n"
-		"<278>         Replaces all occurrences of 'massacres' with 'MASSACRES' in red.\n"
+		"<278>         Replace a line containing 'massacres' with 'MASSACRES' in red.\n"
 		"\n"
 		"<178>Comment<278>: See '#help action', for more information about triggers.\n"
 		"\n"
@@ -5066,23 +5088,25 @@ struct help_type help_table[] =
 		"\n"
 		"<128>         Multi-line triggers\n"
 		"\n"
-		"<278>         If an action contains the \\n sequence it will be turned into a\n"
-		"<278>         multi-line trigger. A multi-line action is executed on incoming blocks\n"
-		"<278>         of text from the MUD, and they will not trigger if the regular\n"
-		"<278>         expression spans more than one block. You can visualize incoming\n"
-		"<278>         blocks by using #event {RECEIVED OUTPUT} {#echo <<888>058>%+80h BLOCK}\n"
+		"<278>         If an action or substitution contains the \\n sequence it will be\n"
+		"<278>         turned into a multi-line trigger. A multi-line trigger is executed on\n"
+		"<278>         incoming blocks of text from the MUD, and they will not trigger if the\n"
+		"<278>         regular expression spans more than one block. You can visualize\n"
+		"<278>         incoming blocks by using the following event:\n"
+		"\n"
+		"<278>         #event {RECEIVED OUTPUT} {#echo <<888>058>%+80h BLOCK}\n"
 		"\n"
 		"<278>         Since the %* expression does not capture the \\n sequence it is required\n"
 		"<278>         to use %a to capture multiple lines. To capture the start of the block\n"
 		"<278>         use \\A and for the end use \\Z. You can use ^ and $ to capture the\n"
 		"<278>         start and end of a line.\n"
 		"\n"
-		"<278>         Multi-line actions trigger before regular actions. Multiple\n"
+		"<278>         Multi-line triggers trigger before regular triggers. Multiple\n"
 		"<278>         multi-line actions can trigger per block, and each multi-line action\n"
 		"<278>         can trigger multiple times per block. Packet fragmentation is not\n"
 		"<278>         currently handled.\n"
 		"\n"
-		"<278>         Multi-line actions are experimental and subject to change.\n"
+		"<278>         Multi-line triggers are experimental and subject to change.\n"
 		"\n"
 		"<128>         Input triggers\n"
 		"\n"
@@ -5155,6 +5179,10 @@ struct help_type help_table[] =
 		"\n"
 		"<178>Example<278>: #show {Targets starting with the letter A: $targets[A%*]\n"
 		"\n"
+		"              To disable using regular expressions start the match with '='.\n"
+		"\n"
+		"<178>Example<278>: #show {A target literally defined as A%*: $targets[\\A%*]\n"
+		"\n"
 		"<278>         To see the internal index of a variable use &<variable name>. To see\n"
 		"<278>         the size of a table you would use: &targets[] or &targets[%*]. A non\n"
 		"<278>         existent nested variable will report itself as 0.\n"

+ 2 - 26
src/history.c

@@ -127,15 +127,6 @@ struct session *repeat_history(struct session *ses, char *line)
 	return ses;
 }
 
-DO_HISTORY(history_character)
-{
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-
-	gtd->repeat_char = *arg1;
-
-	show_message(ses, LIST_HISTORY, "#HISTORY CHARACTER SET TO {%c}.", gtd->repeat_char);
-}
-
 DO_HISTORY(history_delete)
 {
 	if (ses->list[LIST_HISTORY]->used)
@@ -251,7 +242,7 @@ DO_HISTORY(history_read)
 
 	if (file == NULL)
 	{
-		show_message(ses, LIST_HISTORY, "#HISTORY: COULDN'T OPEN FILE {%s} TO READ.", arg1);
+		show_message(ses, LIST_HISTORY, "#HISTORY READ {%s}: COULDN'T OPEN FILE.", arg1);
 
 		return;
 	}
@@ -277,21 +268,6 @@ DO_HISTORY(history_read)
 	return;
 }
 
-DO_HISTORY(history_size)
-{
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-
-	if (atoi(arg1) < 1 || atoi(arg1) > 100000)
-	{
-		show_error(ses, LIST_COMMAND, "#ERROR: #HISTORY SIZE: PROVIDE A NUMBER BETWEEN 1 and 100,000");
-	}
-	else
-	{
-		gtd->history_size = atoi(arg1);
-	}
-	return;
-}
-
 DO_HISTORY(history_write)
 {
 	struct listroot *root = ses->list[LIST_HISTORY];
@@ -304,7 +280,7 @@ DO_HISTORY(history_write)
 
 	if (file == NULL)
 	{
-		tintin_printf2(ses, "#HISTORY: COULDN'T OPEN FILE {%s} TO WRITE.", arg1);
+		tintin_printf2(ses, "#HISTORY WRITE {%s}: COULDN'T OPEN FILE.", arg1);
 
 		return;
 	}

+ 10 - 4
src/input.c

@@ -1058,12 +1058,18 @@ void input_printf(char *format, ...)
 	}
 
 	va_start(args, format);
-	vasprintf(&buf, format, args);
-	va_end(args);
 
-	print_stdout(0, 0, "%s", buf);
+	if (vasprintf(&buf, format, args) == -1)
+	{
+		syserr_printf(gtd->ses, "input_printf(%s): vasprintf:", format);
+	}
+	else
+	{
+		print_stdout(0, 0, "%s", buf);
 
-	free(buf);
+		free(buf);
+	}
+	va_end(args);
 
 	return;
 }

+ 23 - 23
src/line.c

@@ -108,7 +108,7 @@ DO_LINE(line_background)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {BACKGROUND} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {BACKGROUND} <COMMAND>");
 
 		return ses;
 	}
@@ -130,7 +130,7 @@ DO_LINE(line_benchmark)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {BENCHMARK} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {BENCHMARK} <COMMAND>");
 
 		return ses;
 	}
@@ -170,7 +170,7 @@ DO_LINE(line_capture)
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE CAPTURE {VARIABLE} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE CAPTURE <VARIABLE> <COMMAND>");
 	}
 	return ses;
 }
@@ -181,7 +181,7 @@ DO_LINE(line_convert)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {CONVERT} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {CONVERT} <COMMAND>");
 
 		return ses;
 	}
@@ -203,7 +203,7 @@ DO_LINE(line_debug)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {DEBUG} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {DEBUG} <COMMAND>");
 
 		return ses;
 	}
@@ -252,7 +252,7 @@ DO_LINE(line_ignore)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {IGNORE} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {IGNORE} <COMMAND>");
 		
 		return ses;
 	}
@@ -272,7 +272,7 @@ DO_LINE(line_local)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {LOCAL} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {LOCAL} <COMMAND>");
 
 		return ses;
 	}
@@ -329,7 +329,7 @@ DO_LINE(line_log)
 			}
 			else
 			{
-				show_error(ses, LIST_COMMAND, "#ERROR: #LINE LOG {%s} - COULDN'T OPEN FILE.", arg1);
+				show_error(ses, LIST_COMMAND, "#ERROR: #LINE LOG {%s}: COULDN'T OPEN FILE.", arg1);
 			}
 		}
 	}
@@ -355,12 +355,12 @@ DO_LINE(line_log)
 		}
 		else
 		{
-			show_error(ses, LIST_COMMAND, "#ERROR: #LINE LOG {%s} - COULDN'T OPEN FILE.", arg1);
+			show_error(ses, LIST_COMMAND, "#ERROR: #LINE LOG {%s}: COULDN'T OPEN FILE.", arg1);
 		}
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {LOG} {filename} [string]");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {LOG} <FILENAME> [TEXT]");
 	}
 	return ses;
 }
@@ -401,7 +401,7 @@ DO_LINE(line_logverbatim)
 			}
 			else
 			{
-				show_error(ses, LIST_COMMAND, "#ERROR: #LINE LOGVERBATIM {%s} - COULDN'T OPEN FILE.", arg1);
+				show_error(ses, LIST_COMMAND, "#ERROR: #LINE LOGVERBATIM {%s}: COULDN'T OPEN FILE.", arg1);
 			}
 		}
 	}
@@ -427,12 +427,12 @@ DO_LINE(line_logverbatim)
 		}
 		else
 		{
-			show_error(ses, LIST_COMMAND, "#ERROR: #LINE LOGVERBATIM {%s} - COULDN'T OPEN FILE.", arg1);
+			show_error(ses, LIST_COMMAND, "#ERROR: #LINE LOGVERBATIM {%s}: COULDN'T OPEN FILE.", arg1);
 		}
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {LOGVERBATIM} {filename} {string}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {LOGVERBATIM} <FILENAME> <TEXT>");
 	}
 	return ses;
 }
@@ -469,7 +469,7 @@ DO_LINE(line_logmode)
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {LOGMODE} {HTML|PLAIN|RAW|STAMP} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {LOGMODE} {HTML|PLAIN|RAW|STAMP} <COMMAND>");
 
 		return ses;
 	}
@@ -489,7 +489,7 @@ DO_LINE(line_msdp)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {MSDP} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {MSDP} <COMMAND>");
 
 		return ses;
 	}
@@ -508,7 +508,7 @@ DO_LINE(line_json)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {JSON} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {JSON} <VARIABLE> <COMMAND>");
 
 		return ses;
 	}
@@ -556,7 +556,7 @@ DO_LINE(line_multishot)
 
 	if (!is_math(ses, arg1) || *arg2 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {MULTISHOT} {number} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {MULTISHOT} <NUMBER> <COMMAND>");
 		
 		return ses;
 	}
@@ -580,7 +580,7 @@ DO_LINE(line_oneshot)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {ONESHOT} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {ONESHOT} <COMMAND>");
 		
 		return ses;
 	}
@@ -602,7 +602,7 @@ DO_LINE(line_quiet)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {QUIET} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {QUIET} <COMMAND>");
 		
 		return ses;
 	}
@@ -622,7 +622,7 @@ DO_LINE(line_strip)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {STRIP} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {STRIP} <COMMAND>");
 
 		return ses;
 	}
@@ -645,7 +645,7 @@ DO_LINE(line_substitute)
 	{
 		for (i = 0 ; *substitution_table[i].name ; i++)
 		{
-			tintin_printf2(ses, "#SYNTAX: #LINE {SUBSTITUTE} {%s} {command}.", substitution_table[i].name);
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {SUBSTITUTE} {%s} <COMMAND>", substitution_table[i].name);
 		}
 		return ses;
 	}
@@ -683,7 +683,7 @@ DO_LINE(line_verbatim)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {VERBATIM} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {VERBATIM} <COMMAND>");
 		
 		return ses;
 	}
@@ -703,7 +703,7 @@ DO_LINE(line_verbose)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {VERBOSE} {command}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #LINE {VERBOSE} <COMMAND>");
 		
 		return ses;
 	}

+ 94 - 16
src/list.c

@@ -32,6 +32,7 @@
 extern DO_ARRAY(array_add);
 extern DO_ARRAY(array_clear);
 extern DO_ARRAY(array_collapse);
+extern DO_ARRAY(array_copy);
 extern DO_ARRAY(array_create);
 extern DO_ARRAY(array_delete);
 extern DO_ARRAY(array_explode);
@@ -49,6 +50,7 @@ extern DO_ARRAY(array_shuffle);
 extern DO_ARRAY(array_simplify);
 extern DO_ARRAY(array_size);
 extern DO_ARRAY(array_sort);
+extern DO_ARRAY(array_swap);
 extern DO_ARRAY(array_tokenize);
 
 typedef struct session *ARRAY(struct session *ses, struct listnode *list, char *arg, char *var, char *arg1, char *arg2);
@@ -66,6 +68,7 @@ struct array_type array_table[] =
 	{     "CLEAR",            array_clear,       "Clear a list"                            },
 	{     "CLR",              array_clear,       NULL                                      },
 	{     "COLLAPSE",         array_collapse,    "Collapse the list into a variable"       },
+	{     "COPY",             array_copy,        "Copy a list to a list"                   },
 	{     "CREATE",           array_create,      "Create a list with given items"          },
 	{     "DELETE",           array_delete,      "Delete a list item with given index"     },
 	{     "EXPLODE",          array_explode,     "Explode the variable into a list"        },
@@ -86,6 +89,7 @@ struct array_type array_table[] =
 	{     "SIZE",             array_size,        NULL                                      },
 	{     "SORT",             array_sort,        "Sort a list table alphabetically"        },
 	{     "SRT",              array_sort,        NULL                                      },
+	{     "SWAP",             array_swap,        "Swap two list items"                     },
 	{     "TOKENIZE",         array_tokenize,    "Create a list with given characters"     },
 	{     "",                 NULL,              ""                                        }
 };
@@ -115,7 +119,7 @@ DO_COMMAND(do_list)
 	}
 	else if (*arg2 == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {variable} {option} {argument}");
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST <VARIABLE> <OPTION> <ARGUMENT>");
 	}
 	else
 	{
@@ -254,6 +258,47 @@ DO_ARRAY(array_collapse)
 	return ses;
 }
 
+DO_ARRAY(array_copy)
+{
+	struct listnode *from;
+
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+
+	if (*arg1 == 0)
+	{
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {%s} COPY <VARIABLE>.", var);
+
+		return ses;
+	}
+
+	if ((from = search_nest_node_ses(ses, arg1)) == NULL)
+	{
+		show_error(ses, LIST_VARIABLE, "#LIST COPY: VARIABLE {%s} NOT FOUND.", arg1);
+		
+		return ses;
+	}
+
+	if (!strcmp(var, arg1))
+	{
+		return ses;
+	}
+
+	str_cpy(&list->arg2, from->arg2);
+	str_cpy(&list->arg3, from->arg3);
+	str_cpy(&list->arg4, from->arg4);
+
+	if (list->root)
+	{
+		free_list(list->root);
+
+		list->root = NULL;
+	}
+
+	copy_nest_node(ses->list[LIST_VARIABLE], list, from);
+
+	return ses;
+}
+
 DO_ARRAY(array_create)
 {
 	char *buf, *str;
@@ -329,7 +374,7 @@ DO_ARRAY(array_delete)
 
 		if (index == -1)
 		{
-			show_error(ses, LIST_VARIABLE, "#LIST {%s} DELETE: INVALID INDEX: {%s}.", var, arg1);
+			show_error(ses, LIST_VARIABLE, "#LIST {%s} DELETE: INVALID INDEX {%s}.", var, arg1);
 
 			return ses;
 		}
@@ -351,7 +396,7 @@ DO_ARRAY(array_delete)
 	}
 	else
 	{
-		show_error(ses, LIST_VARIABLE, "#LIST DELETE: {%s} is not a list.", var);
+		show_error(ses, LIST_VARIABLE, "#LIST DELETE: VARIABLE {%s} IS NOT A LIST.", var);
 	}
 	return ses;
 }
@@ -366,7 +411,7 @@ DO_ARRAY(array_explode)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {%s} EXPLODE {<SEPARATOR>}.", var);
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {%s} EXPLODE <SEPARATOR>", var);
 
 		return ses;
 	}
@@ -375,7 +420,7 @@ DO_ARRAY(array_explode)
 	{
 		if (*arg2 == 0)
 		{
-			show_error(ses, LIST_VARIABLE, "#LIST {%s} EXPLODE: VARIABLE %s IS ALREADY A LIST.", var, var);
+			show_error(ses, LIST_VARIABLE, "#LIST {%s} EXPLODE: VARIABLE {%s} IS ALREADY A LIST.", var, var);
 
 			return ses;
 		}
@@ -422,7 +467,7 @@ DO_ARRAY(array_filter)
 
 	if (*arg1 == 0 && *arg2 == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {variable} FILTER {keep} {remove}");
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST <VARIABLE> FILTER <KEEP> [REMOVE]");
 
 		return ses;
 	}
@@ -478,7 +523,7 @@ DO_ARRAY(array_find)
 
 	if (*arg2 == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {variable} FIND {string} {variable}");
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST <VARIABLE> FIND <TEXT> <VARIABLE>");
 
 		return ses;
 	}
@@ -510,7 +555,7 @@ DO_ARRAY(array_get)
 
 	if (*arg2 == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {variable} GET {index} {variable}");
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST <VARIABLE> GET <INDEX> <VARIABLE>");
 		
 		return ses;
 	}
@@ -604,7 +649,7 @@ DO_ARRAY(array_insert)
 
 	if (toi == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#LIST INSERT: INVALID INDEX: {%s}.", arg1);
+		show_error(ses, LIST_VARIABLE, "#LIST INSERT: INVALID INDEX {%s}.", arg1);
 
 		return ses;
 	}
@@ -725,7 +770,7 @@ DO_ARRAY(array_refine)
 
 	if (*arg1 == 0 && *arg2 == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {variable} REFINE {keep} {remove}");
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST <VARIABLE> REFINE <KEEP> [REMOVE]");
 
 		return ses;
 	}
@@ -833,7 +878,7 @@ DO_ARRAY(array_simplify)
 	}
 	else
 	{
-		show_error(ses, LIST_VARIABLE, "#LIST SIMPLIFY: {%s} is not a list.", list->arg1);
+		show_error(ses, LIST_VARIABLE, "#LIST SIMPLIFY: VARIABLE {%s} IS NOT A LIST.", list->arg1);
 	}
 
 	return ses;
@@ -845,7 +890,7 @@ DO_ARRAY(array_size)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {variable} SIZE {variable}");
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST <VARIABLE> SIZE <VARIABLE>");
 		
 		return ses;
 	}
@@ -874,16 +919,18 @@ DO_ARRAY(array_set)
 
 		if (index == -1)
 		{
-			show_error(ses, LIST_VARIABLE, "#LIST {%s} SET: Invalid index: %s", var, arg1);
+			show_error(ses, LIST_VARIABLE, "#LIST {%s} SET: INVALID INDEX {%s}.", var, arg1);
 
 			return ses;
 		}
-		str_cpy(&list->root->list[index]->arg2, arg2);
+		set_nest_node(list->root, list->root->list[index]->arg1, "%s", arg2);
+
+//		str_cpy(&list->root->list[index]->arg2, arg2);
 
 		return ses;
 	}
 
-	show_error(ses, LIST_VARIABLE, "#LIST SET: {%s} is not a list.", var);
+	show_error(ses, LIST_VARIABLE, "#LIST SET: VARIABLE {%s} IS NOT A LIST.", var);
 
 	return ses;
 }
@@ -922,7 +969,7 @@ DO_ARRAY(array_sort)
 	{
 		if (*list->root->list[0]->arg2 == 0)
 		{
-			show_error(ses, LIST_COMMAND, "#ERROR: #LIST {%s} ORDER: LIST IS NOT INDEXED.", var);
+			show_error(ses, LIST_COMMAND, "#ERROR: #LIST {%s} SORT: LIST IS NOT INDEXED.", var);
 
 			return ses;
 		}
@@ -981,6 +1028,37 @@ DO_ARRAY(array_sort)
 	return ses;
 }
 
+DO_ARRAY(array_swap)
+{
+	char *swap;
+	int index1, index2;
+	struct listroot *toor;
+
+	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 (list->root)
+	{
+		index1 = get_list_index(ses, list->root, arg1);
+		index2 = get_list_index(ses, list->root, arg2);
+
+		if (index1 == -1 || index2 == -1)
+		{
+			show_error(ses, LIST_VARIABLE, "#LIST {%s} SWAP: INVALID INDEX {%s} {%s}.", var, arg1, arg2);
+
+			return ses;
+		}
+		swap = list->root->list[index1]->arg2; list->root->list[index1]->arg2 = list->root->list[index2]->arg2; list->root->list[index2]->arg2 = swap;
+		toor = list->root->list[index1]->root; list->root->list[index1]->root = list->root->list[index2]->root; list->root->list[index2]->root = toor;
+
+		return ses;
+	}
+
+	show_error(ses, LIST_VARIABLE, "#LIST SWAP: VARIABLE {%s} IS NOT A LIST.", var);
+
+	return ses;
+}
+
 DO_ARRAY(array_tokenize)
 {
 	char *buf;

+ 30 - 9
src/log.c

@@ -29,6 +29,7 @@
 
 DO_LOG(log_append);
 DO_LOG(log_info);
+DO_LOG(log_make);
 DO_LOG(log_move);
 DO_LOG(log_overwrite);
 DO_LOG(log_off);
@@ -48,10 +49,11 @@ struct log_type log_table[] =
 {
 	{    "APPEND",            log_append,          "Start logging, appending to given file."        },
 	{    "INFO",              log_info,            "Some logging related info."                     },
+	{    "MAKE",              log_make,            "Make the given directory."                      },
 	{    "MOVE",              log_move,            "Move the given file."                           },
 	{    "OFF",               log_off,             "Stop logging."                                  },
 	{    "OVERWRITE",         log_overwrite,       "Start logging, overwriting the given file."     },
-	{    "REMOVE",            log_remove,          "Remove the given file."                         },
+	{    "REMOVE",            log_remove,          "Remove the given file or directory."            },
 	{    "TIMESTAMP",         log_timestamp,       "Timestamp prepended to each log line."          },
 	{    "",                  NULL,                ""                                               }
 };
@@ -123,7 +125,7 @@ DO_LOG(log_append)
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#ERROR: #LOG {%s} {%s} - COULDN'T OPEN FILE.", arg1, arg2);
+		show_error(ses, LIST_COMMAND, "#ERROR: #LOG {%s} {%s}: COULDN'T OPEN FILE.", arg1, arg2);
 	}
 }
 
@@ -136,6 +138,25 @@ DO_LOG(log_info)
 	tintin_printf2(ses, "#LOG INFO: NEXT  = %s", ses->log->next_file ? ses->log->next_name : "");
 }
 
+DO_LOG(log_make)
+{
+	if (mkdir(arg2, 0755))
+	{
+		if (errno != EEXIST)
+		{
+			show_error(ses, LIST_COMMAND, "#ERROR: #LOG MAKE: FAILED TO CREATE DIRECTORY {%s} (%s).", arg2, strerror(errno));
+		}
+		else
+		{
+			show_message(ses, LIST_COMMAND, "#LOG MAKE: DIRECTORY {%s} ALREADY EXISTS.", arg2);
+		}
+	}
+	else
+	{
+		show_message(ses, LIST_COMMAND, "#LOG MAKE: CREATED DIRECTORY {%s}.", arg2);
+	}
+}
+
 DO_LOG(log_move)
 {
 	char *arg3;
@@ -149,11 +170,11 @@ DO_LOG(log_move)
 
 	if (result == 0)
 	{
-		show_message(ses, LIST_COMMAND, "#LOG MOVE: FILE '%s' MOVED TO '%s'.", arg2, arg3);
+		show_message(ses, LIST_COMMAND, "#LOG MOVE: FILE {%s} MOVED TO {%s}.", arg2, arg3);
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#LOG MOVE: COULDN'T MOVE FILE '%s' TO '%s'.", arg2, arg3);
+		show_error(ses, LIST_COMMAND, "#LOG MOVE: COULDN'T MOVE FILE {%s} TO {%s}.", arg2, arg3);
 	}
 }
 
@@ -172,11 +193,11 @@ DO_LOG(log_overwrite)
 
 		logheader(ses, ses->log->file, ses->log->mode);
 
-		show_message(ses, LIST_COMMAND, "#LOG: LOGGING OUTPUT TO '%s'", arg2);
+		show_message(ses, LIST_COMMAND, "#LOG: LOGGING OUTPUT TO {%s}", arg2);
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#ERROR: #LOG {%s} {%s} - COULDN'T OPEN FILE.", arg1, arg2);
+		show_error(ses, LIST_COMMAND, "#ERROR: #LOG {%s} {%s}: COULDN'T OPEN FILE.", arg1, arg2);
 	}
 }
 
@@ -203,11 +224,11 @@ DO_LOG(log_remove)
 
 	if (result == 0)
 	{
-		show_message(ses, LIST_COMMAND, "#LOG REMOVE: FILE '%s' REMOVED.", arg2);
+		show_message(ses, LIST_COMMAND, "#LOG REMOVE: FILE {%s} REMOVED.", arg2);
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#LOG REMOVE: COULDN'T REMOVE FILE '%s'.", arg2);
+		show_error(ses, LIST_COMMAND, "#LOG REMOVE: COULDN'T REMOVE FILE {%s}.", arg2);
 	}
 }
 
@@ -216,7 +237,7 @@ DO_LOG(log_timestamp)
 	RESTRING(ses->log->stamp_strf, arg2);
 	ses->log->stamp_time = 0;
 
-	show_message(ses, LIST_COMMAND, "#LOG TIMESTAMP: FORMAT SET TO '%s'.", arg2);
+	show_message(ses, LIST_COMMAND, "#LOG TIMESTAMP: FORMAT SET TO {%s}.", arg2);
 }
 
 void init_log(struct session *ses)

File diff suppressed because it is too large
+ 190 - 153
src/mapper.c


+ 19 - 19
src/math.c

@@ -115,7 +115,7 @@ DO_COMMAND(do_math)
 
 	if (*arg1 == 0 || *arg2 == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #MATH {variable} {expression}.");
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #MATH <VARIABLE> <EXPRESSION>");
 	}
 	else
 	{
@@ -473,7 +473,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						{
 							if (debug)
 							{
-								show_debug(ses, LIST_VARIABLE, "MATH EXP: { FOUND INSIDE A NUMBER");
+								show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: \x7B FOUND INSIDE A NUMBER");
 							}
 							pop_call();
 							return FALSE;
@@ -488,7 +488,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						{
 							if (debug)
 							{
-								show_debug(ses, LIST_VARIABLE, "MATH EXP: \" FOUND INSIDE A NUMBER");
+								show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: \" FOUND INSIDE A NUMBER");
 							}
 							pop_call();
 							return FALSE;
@@ -503,7 +503,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						{
 							if (debug)
 							{
-								show_debug(ses, LIST_VARIABLE, "#MATH EXP: PARANTESES FOUND INSIDE A NUMBER");
+								show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: PARANTESES FOUND INSIDE A NUMBER");
 							}
 							pop_call();
 							return FALSE;
@@ -523,7 +523,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 					case ':':
 						if (debug && wonky == 0)
 						{
-							show_error(gtd->ses, LIST_COMMAND, "\e[1;31m#WARNING: COMPUTING {%s}. THE : TIME OPERATOR IN #MATH WILL BE REMOVED IN FUTURE RELEASES.", str);
+							show_error(gtd->ses, LIST_COMMAND, "#WARNING: COMPUTING {%s}. THE : TIME OPERATOR IN #MATH WILL BE REMOVED IN FUTURE RELEASES.", str);
 						}
 						*pta++ = *pti++;
 						break;
@@ -556,7 +556,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 							{
 								if (debug)
 								{
-									show_debug(ses, LIST_VARIABLE, "#MATH EXP: MORE THAN ONE POINT FOUND INSIDE A NUMBER");
+									show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: MORE THAN ONE POINT FOUND INSIDE A NUMBER");
 								}
 								precision = 0;
 								pop_call();
@@ -587,7 +587,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						{
 							if (debug)
 							{
-								show_debug(ses, LIST_VARIABLE, "#MATH EXP: EXPRESSION STARTED WITH AN OPERATOR.");
+								show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: EXPRESSION STARTED WITH AN OPERATOR.");
 							}
 							pop_call();
 							return FALSE;
@@ -608,7 +608,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 
 							if (debug)
 							{
-								show_debug(ses, LIST_VARIABLE, "#MATH EXP {%s}: FOUND OPERATOR %s WHILE EXPECTING A VALUE.", str, buf3);
+								show_debug(ses, LIST_VARIABLE, "#DEBUG MATH {%s}: FOUND OPERATOR %s WHILE EXPECTING A VALUE.", str, buf3);
 							}
 							pop_call();
 							return FALSE;
@@ -630,7 +630,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 
 							if (debug)
 							{
-								show_debug(ses, LIST_VARIABLE, "#MATH EXP {%s}: INVALID NUMBER %s.", str, buf3);
+								show_debug(ses, LIST_VARIABLE, "#DEBUG MATH {%s}: INVALID NUMBER %s.", str, buf3);
 							}
 
 							pop_call();
@@ -709,7 +709,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 
 						if (debug)
 						{
-							show_debug(ses, LIST_VARIABLE, "#MATH EXP {%s}: INVALID NUMBER %s.", str, buf3);
+							show_debug(ses, LIST_VARIABLE, "#DEBUG MATH {%s}: INVALID NUMBER %s.", str, buf3);
 						}
 						pop_call();
 						return FALSE;
@@ -783,7 +783,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 						{
 							if (debug)
 							{
-								show_debug(ses, LIST_VARIABLE, "#MATH EXP: UNKNOWN OPERATOR: %c%c", pti[0], pti[1]);
+								show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: UNKNOWN OPERATOR: %c%c", pti[0], pti[1]);
 							}
 							pop_call();
 							return FALSE;
@@ -956,7 +956,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 							default:
 								if (debug)
 								{
-									show_debug(ses, LIST_VARIABLE, "#MATH EXP: UNKNOWN OPERATOR: %c%c", pti[-1], pti[0]);
+									show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: UNKNOWN OPERATOR: %c%c", pti[-1], pti[0]);
 								}
 								pop_call();
 								return FALSE;
@@ -966,7 +966,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 					default:
 						if (debug)
 						{
-							show_debug(ses, LIST_VARIABLE, "#MATH EXP: UNKNOWN OPERATOR: %c", *pti);
+							show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: UNKNOWN OPERATOR: %c", *pti);
 						}
 						pop_call();
 						return FALSE;
@@ -979,7 +979,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 	{
 		if (debug)
 		{
-			show_debug(ses, LIST_VARIABLE, "#MATH EXP: UNMATCHED PARENTHESES, LEVEL: %d", level);
+			show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: UNMATCHED PARENTHESES, LEVEL: %d", level);
 		}
 		pop_call();
 		return FALSE;
@@ -1070,7 +1070,7 @@ void mathexp_compute(struct session *ses, struct math_node *node)
 		case EXP_OP_DICE:
 			if (node->next->val <= 0)
 			{
-				show_debug(ses, LIST_VARIABLE, "#MATHEXP: INVALID DICE: %lld", (long long) node->next->val);
+				show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: INVALID DICE: %lld", (long long) node->next->val);
 				value = 0;
 			}
 			else
@@ -1094,7 +1094,7 @@ void mathexp_compute(struct session *ses, struct math_node *node)
 		case EXP_OP_DIVIDE:
 			if (node->next->val == 0)
 			{
-				show_debug(ses, LIST_VARIABLE, "#MATH ERROR: DIVISION ZERO.");
+				show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: DIVISION BY ZERO.");
 				value = 0;
 				precision = 0;
 			}
@@ -1196,7 +1196,7 @@ void mathexp_compute(struct session *ses, struct math_node *node)
 			break;
 
 		default:
-			show_debug(ses, LIST_VARIABLE, "#MATH COMPUTE EXP: UNKNOWN OPERATOR: %c%c%c", (int) node->val % 128, (int) node->val % 16384 / 128, (int) node->val % 2097152 / 16384);
+			show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: UNKNOWN OPERATOR: %c%c%c", (int) node->val % 128, (int) node->val % 16384 / 128, (int) node->val % 2097152 / 16384);
 			value = 0;
 			break;
 	}
@@ -1526,7 +1526,7 @@ long double tincmp(struct math_node *left, struct math_node *right)
 {
 	if (left->type != right->type)
 	{
-		show_debug(NULL, LIST_VARIABLE, "#MATH COMPUTE: COMPARING STRING WITH A NUMBER.");
+		show_debug(gtd->ses, LIST_VARIABLE, "#DEBUG MATH: COMPARING STRING WITH A NUMBER.");
 
 		return 0;
 	}
@@ -1545,7 +1545,7 @@ long double tineval(struct session *ses, struct math_node *left, struct math_nod
 {
 	if (left->type != right->type)
 	{
-		show_debug(ses, LIST_VARIABLE, "#MATH COMPUTE: COMPARING %s WITH %s.", left->type == EXP_NUMBER ? "NUMBER" : "STRING", right->type == EXP_NUMBER ? "NUMBER" : "STRING");
+		show_debug(ses, LIST_VARIABLE, "#DEBUG MATH: COMPARING %s WITH %s.", left->type == EXP_NUMBER ? "NUMBER" : "STRING", right->type == EXP_NUMBER ? "NUMBER" : "STRING");
 
 		return 0;
 	}

+ 13 - 0
src/misc.c

@@ -151,6 +151,19 @@ DO_COMMAND(do_test)
 {
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 
+	if (!strcmp(arg1, "pointers"))
+	{
+		char data = 'X';
+		char *test = &data;
+		unsigned long int index = (unsigned long int) test;
+
+		printf("data %c index %lu test %c\n", data, index, *test);
+
+		test = 0;
+
+		printf("data %c index %lu test %c\n", data, index, test[index]);
+	}
+
 	if (!strcmp(arg1, "gmcp"))
 	{
 		execute(ses, "%s", "#event {IAC SB GMCP} {#var {%0} {%1};#line debug #var {%0}}");

+ 17 - 1
src/net.c

@@ -322,7 +322,7 @@ void write_line_mud(struct session *ses, char *line, int size)
 	{
 		char buf[BUFFER_SIZE];
 
-		size = utf8_to_all(ses, line, buf);
+		size = utf8_to_all(ses, line, buf, size);
 
 		memcpy(line, buf, size);
 
@@ -540,6 +540,7 @@ void readmud(struct session *ses)
 								str_cat(&ses->more_output, line);
 								ses->check_output = gtd->utime + (ses->packet_patch ? ses->packet_patch : 500000ULL);
 
+								check_all_events(ses, EVENT_FLAG_OUTPUT, 0, 0, "PACKET PATCH");
 								break;
 							}
 						}
@@ -547,6 +548,8 @@ void readmud(struct session *ses)
 						{
 							str_cat(&ses->more_output, line);
 							ses->check_output = gtd->utime + (ses->packet_patch ? ses->packet_patch : 500000ULL);
+
+							check_all_events(ses, EVENT_FLAG_OUTPUT, 0, 0, "PACKET PATCH");
 							break;
 						}
 					}
@@ -693,6 +696,19 @@ void process_mud_output(struct session *ses, char *linebuf, int prompt)
 		return;
 	}
 
+	if (HAS_BIT(ses->event_flags, EVENT_FLAG_OUTPUT|EVENT_FLAG_CATCH))
+	{
+		strip_vt102_codes(linebuf, line);
+
+		check_all_events(ses, SUB_SEC|EVENT_FLAG_OUTPUT, 0, 3, "PROCESSED LINE", linebuf, line, ntos(prompt));
+
+		if (check_all_events(ses, SUB_SEC|EVENT_FLAG_CATCH, 0, 3, "CATCH PROCESSED LINE", linebuf, line, ntos(prompt)))
+		{
+			pop_call();
+			return;
+		}
+	}
+
 	add_line_buffer(ses, linebuf, prompt);
 
 	if (ses == gtd->ses)

+ 5 - 0
src/parse.c

@@ -1181,6 +1181,11 @@ void check_one_line_multi(struct session *ses, char *original, char *stripped)
 	{
 		check_all_actions_multi(ses, original, stripped, buf);
 	}
+
+	if (!HAS_BIT(ses->list[LIST_SUBSTITUTE]->flags, LIST_FLAG_IGNORE))
+	{
+		check_all_substitutions_multi(ses, original, stripped);
+	}
 }
 
 void check_one_line(struct session *ses, char *line)

+ 31 - 27
src/path.c

@@ -102,7 +102,7 @@ DO_PATH(path_start)
 {
 	if (HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
 	{
-		show_message(ses, LIST_COMMAND, "#PATH START: ERROR: YOU ARE ALREADY MAPPING A PATH.");
+		show_message(ses, LIST_COMMAND, "#ERROR: #PATH START: YOU ARE ALREADY MAPPING A PATH.");
 	}
 	else
 	{
@@ -300,7 +300,7 @@ DO_PATH(path_get)
 	}
 	else if (*arg2 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH GET <INFO|LENGTH|MAPPING|POSITION|RUNNING> <VARIABLE NAME>");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH GET {INFO|LENGTH|MAPPING|POSITION|RUNNING} <VARIABLE>");
 	}
 	else if (is_abbrev(arg1, "INFO"))
 	{
@@ -316,7 +316,7 @@ DO_PATH(path_get)
 			(root->list[root->update]->val64 - gtd->utime) < 10000 ? 0.01 :
 			(root->list[root->update]->val64 - gtd->utime) / 1000000.0);
 
-		show_message(ses, LIST_COMMAND, "#PATH GET: PATH INFO SAVED TO {%s}", arg2);
+		show_message(ses, LIST_COMMAND, "#PATH GET: PATH INFO SAVED TO {%s}.", arg2);
 	}
 	else if (is_abbrev(arg1, "LENGTH"))
 	{
@@ -324,7 +324,7 @@ DO_PATH(path_get)
 
 		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_COMMAND, "#PATH GET: PATH LENGTH {%s} SAVED TO {%s}", result, arg2);
+		show_message(ses, LIST_COMMAND, "#PATH GET: PATH LENGTH {%s} SAVED TO {%s}.", result, arg2);
 	}
 	else if (is_abbrev(arg1, "MAPPING"))
 	{
@@ -332,7 +332,7 @@ DO_PATH(path_get)
 
 		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_COMMAND, "#PATH GET: PATH MAPPING {%s} SAVED TO {%s}", result, arg2);
+		show_message(ses, LIST_COMMAND, "#PATH GET: PATH MAPPING {%s} SAVED TO {%s}.", result, arg2);
 	}
 	else if (is_abbrev(arg1, "POSITION"))
 	{
@@ -340,7 +340,7 @@ DO_PATH(path_get)
 
 		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_COMMAND, "#PATH GET: PATH POSITION {%s} SAVED TO {%s}", result, arg2);
+		show_message(ses, LIST_COMMAND, "#PATH GET: PATH POSITION {%s} SAVED TO {%s}.", result, arg2);
 	}
 	else if (is_abbrev(arg1, "RUNNING"))
 	{
@@ -350,11 +350,11 @@ DO_PATH(path_get)
 			(root->list[root->update]->val64 - gtd->utime) < 10000 ? 0.01 :
 			(root->list[root->update]->val64 - gtd->utime) / 1000000.0);
 
-		show_message(ses, LIST_COMMAND, "#PATH GET: PATH RUNNING {%s} SAVED TO {%s}", result, arg2);
+		show_message(ses, LIST_COMMAND, "#PATH GET: PATH RUNNING {%s} SAVED TO {%s}.", result, arg2);
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH GET <INFO|LENGTH|MAPPING|POSITION|RUNNING> <VARIABLE NAME>");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH GET {INFO|LENGTH|MAPPING|POSITION|RUNNING} <VARIABLE>");
 	}
 }
 
@@ -373,7 +373,7 @@ DO_PATH(path_save)
 	}
 	else if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH SAVE <BACKWARD|BOTH|FORWARD> <VARIABLE NAME>");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH SAVE {BACKWARD|BOTH|FORWARD} <VARIABLE>");
 	}
 	else if (*arg2 == 0)
 	{
@@ -396,7 +396,7 @@ DO_PATH(path_save)
 		}
 		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_COMMAND, "#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"))
 	{
@@ -427,7 +427,7 @@ DO_PATH(path_save)
 		}
 		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_COMMAND, "#PATH SAVE: PATH SAVED TO {%s}", arg2);
+		show_message(ses, LIST_COMMAND, "#PATH SAVE: PATH SAVED TO {%s}.", arg2);
 	}
 	else if (is_abbrev(arg1, "LENGTH"))
 	{
@@ -435,7 +435,7 @@ DO_PATH(path_save)
 
 		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_COMMAND, "#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"))
 	{
@@ -443,11 +443,11 @@ DO_PATH(path_save)
 
 		set_nest_node_ses(ses, arg2, "%s", result);
 
-		show_message(ses, LIST_COMMAND, "#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_COMMAND, "#SYNTAX: #PATH SAVE <BACKWARD|FORWARD> <VARIABLE NAME>");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH SAVE {BACKWARD|FORWARD} <VARIABLE>");
 	}
 }
 
@@ -500,16 +500,12 @@ DO_PATH(path_delete)
 
 		delete_index_list(root, root->used - 1);
 
-		if (root->update >= root->used)
-		{
-			root->update--;
-		}
+		root->update = URANGE(0, root->update, root->used);
 	}
 	else
 	{
 		tintin_printf(ses, "#PATH DELETE: NO MOVES LEFT.");
 	}
-
 }
 
 DO_PATH(path_insert)
@@ -522,7 +518,7 @@ DO_PATH(path_insert)
 
 	if (*arg1 == 0 && *arg2 == 0)
 	{
-		show_message(ses, LIST_COMMAND, "#PATH INSERT: ERROR: YOU MUST GIVE A COMMAND TO INSERT");
+		show_message(ses, LIST_COMMAND, "#ERROR: #PATH INSERT: YOU MUST GIVE A COMMAND TO INSERT.");
 	}
 	else
 	{
@@ -625,7 +621,7 @@ DO_PATH(path_walk)
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH WALK {FORWARD|BACKWARD}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH WALK {FORWARD|BACKWARD}");
 	}
 }
 
@@ -638,7 +634,7 @@ DO_PATH(path_swap)
 
 	if (root->used == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#PATH SWAP: ERROR: PATH IS EMPTY.");
+		show_error(ses, LIST_COMMAND, "#ERROR: #PATH SWAP: PATH IS EMPTY.");
 
 		return;
 	}
@@ -869,6 +865,10 @@ DO_PATH(path_goto)
 			show_message(ses, LIST_COMMAND, "#PATH GOTO: POSITION SET TO %d.", root->update + 1);
 		}
 	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH GOTO {START|END|<POSITION>}");
+	}
 }
 
 
@@ -913,6 +913,10 @@ DO_PATH(path_move)
 
 		show_message(ses, LIST_COMMAND, "#PATH MOVE: POSITION MOVED FROM %d TO %d.", last + 1, root->update + 1);
 	}
+	else
+	{
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #PATH MOVE <BACKWARD|FORWARD>");
+	}
 }
 
 DO_PATH(path_undo)
@@ -921,21 +925,21 @@ DO_PATH(path_undo)
 
 	if (root->used == 0)
 	{
-		show_message(ses, LIST_COMMAND, "#PATH UNDO: ERROR: PATH IS EMPTY.");
+		show_message(ses, LIST_COMMAND, "#ERROR: #PATH UNDO: PATH IS EMPTY.");
 
 		return;
 	}
 
 	if (root->update != root->used)
 	{
-		show_message(ses, LIST_COMMAND, "#PATH UNDO: ERROR: YOUR POSITION IS NOT AT END OF PATH.");
+		show_message(ses, LIST_COMMAND, "#ERROR: #PATH UNDO: YOUR POSITION IS NOT AT END OF PATH.");
 	
 		return;
 	}
 
 	if (!HAS_BIT(ses->flags, SES_FLAG_PATHMAPPING))
 	{
-		show_message(ses, LIST_COMMAND, "#PATH UNDO: ERROR: YOU ARE NOT CURRENTLY MAPPING A PATH.");
+		show_message(ses, LIST_COMMAND, "#ERROR: #PATH UNDO: YOU ARE NOT CURRENTLY MAPPING A PATH.");
 
 		return;
 	}
@@ -1013,7 +1017,7 @@ DO_COMMAND(do_pathdir)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_PATHDIR]) == FALSE)
 		{
-			show_message(ses, LIST_PATHDIR, "#NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_PATHDIR, "#PATHDIR: #NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
@@ -1042,7 +1046,7 @@ DO_COMMAND(do_pathdir)
 
 		node->val32[0] = atoi(arg3);
 
-		show_message(ses, LIST_PATHDIR, "#OK: DIRECTION {%s} WILL BE REVERSED AS {%s} @ {%d}.", arg1, arg2, atoi(arg3));
+		show_message(ses, LIST_PATHDIR, "#PATHDIR: DIRECTION {%s} WILL BE REVERSED AS {%s} @ {%d}.", arg1, arg2, atoi(arg3));
 	}
 	return ses;
 }

+ 9 - 1
src/port.c

@@ -65,7 +65,7 @@ DO_COMMAND(do_port)
 
 		if (port_table[cnt].fun != port_initialize && ses->port == NULL)
 		{
-			tintin_printf(ses, "#PORT: You must initialize a port first.");
+			tintin_printf(ses, "#PORT: YOU MUST INITIALIZE A PORT FIRST.");
 
 			return ses;
 		}
@@ -552,6 +552,14 @@ int process_port_input(struct session *ses, struct port_data *buddy)
 
 		input[size] = 0;
 
+		check_all_events(ses, SUB_SEC|EVENT_FLAG_PORT, 0, 5, "PORT RECEIVED DATA", buddy->name, buddy->ip, ntos(buddy->port), input, ntos(size));
+
+		if (check_all_events(ses, SUB_SEC|EVENT_FLAG_CATCH, 0, 5, "CATCH PORT RECEIVED DATA", buddy->name, buddy->ip, ntos(buddy->port), input, ntos(size)))
+		{
+			pop_call();
+			return 0;
+		}
+
 		echo = buddy->intop;
 
 		buddy->intop += server_translate_telopts(ses, buddy, (unsigned char *) input, size, (unsigned char *) buddy->inbuf, buddy->intop);

+ 15 - 15
src/regex.c

@@ -65,7 +65,7 @@ DO_COMMAND(do_regexp)
 
 	if (*arg3 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "SYNTAX: #REGEXP {string} {expression} {true} {false}.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #REGEXP <TEXT> <EXPRESSION> <TRUE> [FALSE]");
 	}
 	else
 	{
@@ -90,14 +90,14 @@ DO_COMMAND(do_regexp)
 	return ses;
 }
 
-int regexp_compare(struct session *ses, pcre *nodepcre, char *str, char *exp, int option, int flag)
+int regexp_compare(struct session *ses, pcre *nodepcre, char *str, char *exp, int comp_option, int flag)
 {
 	pcre *regex;
 	int i, j, matches;
 
 	if (nodepcre == NULL)
 	{
-		regex = regexp_compile(ses, exp, option);
+		regex = regexp_compile(ses, exp, comp_option);
 	}
 	else
 	{
@@ -189,17 +189,17 @@ int regexp_compare(struct session *ses, pcre *nodepcre, char *str, char *exp, in
 	return TRUE;
 }
 
-pcre *regexp_compile(struct session *ses, char *exp, int option)
+pcre *regexp_compile(struct session *ses, char *exp, int comp_option)
 {
 	const char *error;
 	int i;
 /*
 	if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8))
 	{
-		option |= PCRE_UTF8|PCRE_NO_UTF8_CHECK;
+		comp_option |= PCRE_UTF8|PCRE_NO_UTF8_CHECK;
 	}
 */
-	return pcre_compile(exp, option, &error, &i, NULL);
+	return pcre_compile(exp, comp_option, &error, &i, NULL);
 }
 
 
@@ -210,7 +210,7 @@ pcre *regexp_compile(struct session *ses, char *exp, int option)
 * in the text represented by the wildcards on success.                        *
 ******************************************************************************/
 
-int check_one_regexp(struct session *ses, struct listnode *node, char *line, char *original, int option)
+int check_one_regexp(struct session *ses, struct listnode *node, char *line, char *original, int comp_option)
 {
 	char *exp, *str;
 
@@ -237,7 +237,7 @@ int check_one_regexp(struct session *ses, struct listnode *node, char *line, cha
 		str = line;
 	}	
 
-	return tintin_regexp(ses, node->regex, str, exp, option, REGEX_FLAG_ARG);
+	return tintin_regexp(ses, node->regex, str, exp, comp_option, REGEX_FLAG_ARG);
 }
 
 /*
@@ -473,7 +473,7 @@ int tintin_regexp_check(struct session *ses, char *exp)
 //    3.4 If FIX is set the gtd->args index is used and valid tinexp is assumed
 //    3.4 return TRUE
 
-int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int option, int flag)
+int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int comp_option, int flag)
 {
 	char out[BUFFER_SIZE], *pti, *pto;
 	int i, arg = 1, var = 1, fix = 0;
@@ -518,7 +518,7 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 			case '\\':
 				if (pti[1] == 'n')
 				{
-					SET_BIT(option, PCRE_MULTILINE);
+					SET_BIT(comp_option, PCRE_MULTILINE);
 				}
 				else if (pti[1] == 0)
 				{
@@ -822,10 +822,10 @@ int tintin_regexp(struct session *ses, pcre *nodepcre, char *str, char *exp, int
 	}
 	*pto = 0;
 
-	return regexp_compare(ses, nodepcre, str, out, option, flag + fix);
+	return regexp_compare(ses, nodepcre, str, out, comp_option, flag + fix);
 }
 
-pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *exp, int option)
+pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *exp, int comp_option)
 {
 	char out[BUFFER_SIZE], *pti, *pto;
 
@@ -880,7 +880,7 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 				}
 				else if (pti[1] == 'n')
 				{
-					SET_BIT(option, PCRE_MULTILINE);
+					SET_BIT(comp_option, PCRE_MULTILINE);
 					SET_BIT(node->flags, NODE_FLAG_MULTI);
 				}
 				else if (pti[1] == 0)
@@ -1182,9 +1182,9 @@ pcre *tintin_regexp_compile(struct session *ses, struct listnode *node, char *ex
 
 	if (HAS_BIT(node->flags, NODE_FLAG_COLOR) && *exp != '~')
 	{
-		show_error(ses, LIST_COMMAND, "\e[1;31mWARNING: REGEX MATCHES ESCAPE CODES BUT DOES NOT START WITH A '~' (%s)", exp);
+		show_error(ses, LIST_COMMAND, "#WARNING: REGEX {%s} MATCHES ESCAPE CODES BUT DOES NOT START WITH A '~'.", exp);
 	}
-	return regexp_compile(ses, out, option);
+	return regexp_compile(ses, out, comp_option);
 }
 
 void tintin_macro_compile(char *input, char *output)

+ 15 - 19
src/scan.c

@@ -105,7 +105,7 @@ DO_COMMAND(do_scan)
 		{
 			if (*arg1 == 0)
 			{
-				show_error(ses, LIST_COMMAND, "#SYNTAX: #SCAN {%s} {<FILENAME>}", scan_table[cnt].name);
+				show_error(ses, LIST_COMMAND, "#SYNTAX: #SCAN {%s} <FILENAME>", scan_table[cnt].name);
 
 				return ses;
 			}
@@ -137,7 +137,7 @@ DO_COMMAND(do_scan)
 		return ses;
 	}
 
-	show_error(ses, LIST_COMMAND, "\e[1;31mTHE SCAN COMMAND HAS CHANGED, EXECUTING #SCAN {TXT} {%s} INSTEAD.", cmd);
+	show_error(ses, LIST_COMMAND, "#WARNING: THE SCAN COMMAND HAS CHANGED, EXECUTING #SCAN {TXT} {%s} INSTEAD.", cmd);
 
 	ses = command(ses, do_scan, "{TXT} {%s}", cmd);
 
@@ -254,21 +254,19 @@ DO_SCAN(scan_csv)
 
 		for (i = 1 ; i < 100 ; i++)
 		{
-			arg = get_arg_in_quotes(ses, arg, arg2, FALSE);
-
-			RESTRING(gtd->vars[i], arg2);
+			gtd->varc = i;
 
 			if (*arg == 0)
 			{
-				while (++i < 100)
+				while (i < 100)
 				{
-					if (*gtd->vars[i])
-					{
-						RESTRING(gtd->vars[i], "");
-					}
+					*gtd->vars[i++] = 0;
 				}
 				break;
 			}
+			arg = get_arg_in_quotes(ses, arg, arg2, FALSE);
+
+			RESTRING(gtd->vars[i], arg2);
 		}
 
 		if (header == FALSE)
@@ -313,7 +311,7 @@ DO_SCAN(scan_dir)
 
 	if (*arg2 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "SYNTAX: #SCAN DIR {%s} <VARIABLE NAME>", arg1);
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCAN DIR {%s} <VARIABLE>", arg1);
 
 		return ses;
 	}
@@ -522,21 +520,19 @@ DO_SCAN(scan_tsv)
 
 		for (i = 1 ; i < 100 ; i++)
 		{
-			arg = get_arg_stop_tabs(ses, arg, arg2, FALSE);
-
-			RESTRING(gtd->vars[i], arg2);
+			gtd->varc = i;
 
 			if (*arg == 0)
 			{
-				while (++i < 100)
+				while (i < 100)
 				{
-					if (*gtd->vars[i])
-					{
-						RESTRING(gtd->vars[i], "");
-					}
+					*gtd->vars[i++] = 0;
 				}
 				break;
 			}
+			arg = get_arg_stop_tabs(ses, arg, arg2, FALSE);
+
+			RESTRING(gtd->vars[i], arg2);
 		}
 
 		if (header == FALSE)

+ 25 - 33
src/screen.c

@@ -441,7 +441,7 @@ DO_SCREEN(screen_clear)
 
 		if (bot_col == 0)
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN CLEAR SQUARE {<ROW>} {<COL>} {<ROW>} {<COL>}");
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN CLEAR SQUARE <ROW> <COL> <ROW> <COL>");
 		}
 		else
 		{
@@ -587,7 +587,7 @@ DO_SCREEN(screen_fill)
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN FILL {TOP|BOT|LEFT|RIGHT|SCROLL|SPLIT|DEFAULT} {arg}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN FILL {TOP|BOT|LEFT|RIGHT|SCROLL|SPLIT|DEFAULT} <ARGUMENT>");
 	}
 }
 
@@ -616,16 +616,16 @@ DO_SCREEN(screen_get)
 {
 	if (*arg1 == 0 || *arg2 == 0)
 	{
-		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {FOCUS} {<VAR>}");
-		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {ROWS|COLS|HEIGHT|WIDTH} {<VAR>}");
+		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {FOCUS} <VAR>");
+		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {ROWS|COLS|HEIGHT|WIDTH} <VAR>");
 		
 		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {CHAR_HEIGHT|CHAR_WIDTH}");
-		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {SPLIT_TOP_BAR|SPLIT_BOT_BAR|SPLIT_LEFT_BAR|SPLIT_RIGHT_BAR} {<VAR>}");
-		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {SCROLL_TOP_ROW|SCROLL_TOP_COL|SCROLL_BOT_ROW|SCROLL_BOT_COL} {<VAR>}");
-		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {INPUT_TOP_ROW|INPUT_TOP_COL|INPUT_BOT_ROW|INPUT_BOT_COL} {<VAR>}");
-		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {SCROLL_ROWS|SCROLL_COLS|INPUT_ROWS|INPUT_COLS} {<VAR>}");
-		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {INPUT_NAME} {<VAR>}");
-		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {CUR_ROW|CUR_COL} {<VAR>}");
+		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {SPLIT_TOP_BAR|SPLIT_BOT_BAR|SPLIT_LEFT_BAR|SPLIT_RIGHT_BAR} <VAR>");
+		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {SCROLL_TOP_ROW|SCROLL_TOP_COL|SCROLL_BOT_ROW|SCROLL_BOT_COL} <VAR>");
+		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {INPUT_TOP_ROW|INPUT_TOP_COL|INPUT_BOT_ROW|INPUT_BOT_COL} <VAR>");
+		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {SCROLL_ROWS|SCROLL_COLS|INPUT_ROWS|INPUT_COLS} <VAR>");
+		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {INPUT_NAME} <VAR>");
+		tintin_printf2(ses, "#SYNTAX: #SCREEN {GET} {CUR_ROW|CUR_COL} <VAR>");
 
 		return;
 	}
@@ -867,7 +867,7 @@ DO_SCREEN(screen_rescale)
 		}
 		else
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {HORIZONTALLY} {[WIDTH]}");
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {HORIZONTALLY} [WIDTH]");
 		}
 	}
 	else if (is_abbrev(arg1, "VERTICALLY"))
@@ -878,7 +878,7 @@ DO_SCREEN(screen_rescale)
 		}
 		else
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {VERTICALLY} {[HEIGHT]}");
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {VERTICALLY} [HEIGHT]");
 		}
 	}
 	else if (*arg1 == 0 || is_math(ses, arg1))
@@ -894,9 +894,9 @@ DO_SCREEN(screen_rescale)
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {[HEIGHT]} {[WIDTH]}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {VERTICALLY} {<HEIGHT>}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {HORIZONTALLY} {<WIDTH>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} [HEIGHT] [WIDTH]");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {VERTICALLY} <HEIGHT>");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {RESCALE} {HORIZONTALLY} <WIDTH>");
 	}
 }
 
@@ -948,7 +948,7 @@ DO_SCREEN(screen_scrollregion)
 
 	if ((*arg1 && !is_math(ses, arg1)) || (*arg2 && !is_math(ses, arg2)))
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN SCROLLREGION {TOP ROW} {TOP COL} {BOT ROW} {BOT COL}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN SCROLLREGION <TOP ROW> <TOP COL> <BOT ROW> <BOT COL>");
 
 		return;
 	}
@@ -961,7 +961,7 @@ DO_SCREEN(screen_scrollregion)
 
 	if ((*arg1 && !is_math(ses, arg1)) || (*arg2 && !is_math(ses, arg2)))
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN SCROLL {TOP ROW} {TOP COL} {BOT ROW} {BOT COL}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN SCROLL <TOP ROW> <TOP COL> <BOT ROW> <BOT COL>");
 
 		return;
 	}
@@ -1002,7 +1002,7 @@ DO_SCREEN(screen_inputregion)
 
 	if (*arg1 == 0 || *arg2 == 0 || !is_math(ses, arg1) || !is_math(ses, arg2))
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN INPUTREGION {TOP ROW} {TOP COL} {BOT ROW} {BOT COL}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN INPUTREGION <TOP ROW> <TOP COL> <BOT ROW> <BOT COL>");
 
 		return;
 	}
@@ -1015,7 +1015,7 @@ DO_SCREEN(screen_inputregion)
 
 	if ((*arg1 && !is_math(ses, arg1)) || (*arg2 && !is_math(ses, arg2)))
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN INPUT {TOP ROW} {TOP COL} {BOT ROW} {BOT COL}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN INPUT <TOP ROW> <TOP COL> <BOT ROW> <BOT COL>");
 
 		return;
 	}
@@ -1076,7 +1076,7 @@ DO_SCREEN(screen_resize)
 		}
 		else
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SIZE} {HORIZONTALLY} {[COLS]}");
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SIZE} {HORIZONTALLY} [COLS]");
 		}
 	}
 	else if (is_abbrev(arg1, "VERTICALLY"))
@@ -1087,7 +1087,7 @@ DO_SCREEN(screen_resize)
 		}
 		else
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SIZE} {VERTICALLY} {[ROWS]}");
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SIZE} {VERTICALLY} [ROWS]");
 		}
 	}
 	else if (*arg1 != 0 || *arg2 != 0)
@@ -1111,9 +1111,9 @@ DO_SCREEN(screen_resize)
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SIZE} {[ROWS]} {[COLS]}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SIZE} {VERTICALLY} {<ROWS>}");
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SIZE} {HORIZONTALLY} {<COLS>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SIZE} [ROWS] [COLS]");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SIZE} {VERTICALLY} <ROWS>");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SIZE} {HORIZONTALLY} <COLS>");
 	}
 }
 
@@ -1173,7 +1173,7 @@ DO_SCREEN(screen_set)
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SET} {COLS|ROWS|LABEL|NAME|TITLE} {<ARG>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCREEN {SET} {COLS|ROWS|LABEL|NAME|TITLE} <ARG>");
 	}
 }
 
@@ -1440,16 +1440,9 @@ void screen_csit(struct session *ses, char *arg1, char *arg2, char *arg3)
 	}
 
 	print_stdout(0, 0, "\e[%s%s%s%s%st", arg1, *num1 ? XT_S : XT_V, *num1 && *num1 != ' ' ? num1 : "", *num2 ? XT_S : XT_V, *num2 && *num2 != ' ' ? num2 : "");
-
-//	convert_meta(buf, debug, FALSE);
-
-//	tintin_printf2(gtd->ses, "\e[1;32m[%s] num1 (%s) num2 (%s) %s %s", num1, num2, debug, buf);
 }
 
 
-
-
-
 /*
 	save cursor, goto top row, delete (bot - top) rows, restore cursor
 */
@@ -1570,7 +1563,6 @@ void erase_right_region(struct session *ses)
 	}
 }
 
-
 void erase_square(struct session *ses, int top_row, int top_col, int bot_row, int bot_col)
 {
 	int row;

+ 21 - 19
src/session.c

@@ -145,7 +145,7 @@ DO_COMMAND(do_session)
 			return activate_session(sesptr);
 		}
 
-		tintin_puts2(ses, "#THAT SESSION IS NOT DEFINED.");
+		tintin_printf2(ses, "#SESSION {%s} IS NOT DEFINED.", arg1);
 	}
 	else
 	{
@@ -168,7 +168,7 @@ DO_COMMAND(do_snoop)
 
 		if (sesptr == NULL)
 		{
-			show_error(ses, LIST_COMMAND, "#SNOOP: THERE'S NO SESSION NAMED {%s}", arg1);
+			show_error(ses, LIST_COMMAND, "#SNOOP: THERE'S NO SESSION NAMED {%s}.", arg1);
 			
 			return ses;
 		}
@@ -182,23 +182,23 @@ DO_COMMAND(do_snoop)
 	{
 		if (HAS_BIT(sesptr->flags, SES_FLAG_SNOOP))
 		{
-			show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SESSION '%s'", sesptr->name);
+			show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SESSION '%s'.", sesptr->name);
 		}
 		else
 		{
-			show_message(ses, LIST_COMMAND, "#SNOOP: SNOOPING SESSION '%s'", sesptr->name);
+			show_message(ses, LIST_COMMAND, "#SNOOP: SNOOPING SESSION '%s'.", sesptr->name);
 		}
 		TOG_BIT(sesptr->flags, SES_FLAG_SNOOP);
 	}
 	else if (is_abbrev(arg2, "ON"))
 	{
-		show_message(ses, LIST_COMMAND, "#SNOOP: SNOOPING SESSION '%s'", sesptr->name);
+		show_message(ses, LIST_COMMAND, "#SNOOP: SNOOPING SESSION '%s'.", sesptr->name);
 
 		SET_BIT(sesptr->flags, SES_FLAG_SNOOP);
 	}
 	else if (is_abbrev(arg2, "OFF"))
 	{
-		show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SESSION '%s'", sesptr->name);
+		show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SESSION '%s'.", sesptr->name);
 		DEL_BIT(sesptr->flags, SES_FLAG_SNOOP);
 	}
 	else if (is_abbrev(arg2, "SCROLL"))
@@ -209,33 +209,33 @@ DO_COMMAND(do_snoop)
 		{
 			if (HAS_BIT(sesptr->flags, SES_FLAG_SNOOPSCROLL))
 			{
-				show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SCROLL REGION OF SESSION '%s'", sesptr->name);
+				show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SCROLL REGION OF SESSION '%s'.", sesptr->name);
 			}
 			else
 			{
-				show_message(ses, LIST_COMMAND, "#SNOOP: SNOOPING SCROLL REGION OF SESSION '%s'", sesptr->name);
+				show_message(ses, LIST_COMMAND, "#SNOOP: SNOOPING SCROLL REGION OF SESSION '%s'.", sesptr->name);
 			}
 			TOG_BIT(sesptr->flags, SES_FLAG_SNOOPSCROLL);
 		}
 		else if (is_abbrev(arg2, "ON"))
 		{
-			show_message(ses, LIST_COMMAND, "#SNOOP: SNOOPING SCROLL REGION OF SESSION '%s'", sesptr->name);
+			show_message(ses, LIST_COMMAND, "#SNOOP: SNOOPING SCROLL REGION OF SESSION '%s'.", sesptr->name);
 
 			SET_BIT(sesptr->flags, SES_FLAG_SNOOPSCROLL);
 		}
 		else if (is_abbrev(arg2, "OFF"))
 		{
-			show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SCROLL REGION OF SESSION '%s'", sesptr->name);
+			show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SCROLL REGION OF SESSION '%s'.", sesptr->name);
 			DEL_BIT(sesptr->flags, SES_FLAG_SNOOPSCROLL);
 		}
 		else
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #SNOOP {session} {SCROLL} {ON|OFF}");
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SNOOP <SESSION> {SCROLL} {ON|OFF}");
 		}
 	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SNOOP {session} {ON|OFF|SCROLL}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SNOOP <SESSION> {ON|OFF|SCROLL}");
 	}
 	return ses;
 }
@@ -376,7 +376,7 @@ struct session *new_session(struct session *ses, char *name, char *arg, int desc
 	{
 		if (*host == 0)
 		{
-			tintin_puts2(ses, "#HEY! SPECIFY AN ADDRESS WILL YOU?");
+			show_error(ses, LIST_COMMAND, "#ERROR: #SESSION {%s} {%s}: YOU MUST SPECIFY AN ADDRESS.", name, host);
 
 			pop_call();
 			return ses;
@@ -662,16 +662,18 @@ void cleanup_session(struct session *ses)
 
 	if (ses->socket)
 	{
+		if (HAS_BIT(ses->flags, SES_FLAG_CONNECTED) && !HAS_BIT(ses->flags, SES_FLAG_LINKLOST))
+		{
+			if (shutdown(ses->socket, SHUT_RDWR) == -1)
+			{
+				syserr_printf(ses, "cleanup_session: shutdown");
+			}
+		}
+
 		if (close(ses->socket) == -1)
 		{
 			syserr_printf(ses, "cleanup_session: close");
 		}
-/*		else
-		{
-			int status;
-
-			wait(&status);
-		}*/
 
 		// the PID is stored in the session's port.
 

+ 47 - 6
src/show.c

@@ -104,6 +104,8 @@ DO_COMMAND(do_echo)
 		arg += strlen(arg);
 	}
 
+	prompt = is_suffix(arg1, "\\") && !is_suffix(arg1, "\\\\");
+
 	substitute(ses, arg1, arg1, SUB_COL|SUB_ESC);
 
 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
@@ -116,8 +118,6 @@ DO_COMMAND(do_echo)
 		return ses;
 	}
 
-	prompt = is_suffix(arg1, "\\") && !is_suffix(arg1, "\\\\");
-
 	str_cpy_printf(&out, "%s%s%s", COLOR_TEXT, arg1, COLOR_TEXT);
 
 	tintin_puts3(ses, out, prompt);
@@ -157,6 +157,11 @@ void show_message(struct session *ses, int index, char *format, ...)
 
 	display:
 
+	if (check_all_events(ses, EVENT_FLAG_SYSTEM, 1, 0, "REFORMAT %s", format))
+	{
+		format = get_variable_def(ses, "result", format);
+	}
+
 	va_start(args, format);
 
 	if (vasprintf(&buffer, format, args) == -1)
@@ -211,6 +216,11 @@ void show_error(struct session *ses, int index, char *format, ...)
 
 	push_call("show_error(%p,%p,%p)",ses,index,format);
 
+	if (check_all_events(ses, EVENT_FLAG_SYSTEM, 1, 0, "REFORMAT %s", format))
+	{
+		format = get_variable_def(ses, "result", format);
+	}
+
 	va_start(args, format);
 	if (vasprintf(&buffer, format, args) == -1)
 	{
@@ -278,8 +288,19 @@ void show_debug(struct session *ses, int index, char *format, ...)
 		return;
 	}
 
+	if (check_all_events(ses, EVENT_FLAG_SYSTEM, 1, 0, "REFORMAT %s", format))
+	{
+		format = get_variable_def(ses, "result", format);
+	}
+
 	va_start(args, format);
-	vasprintf(&buffer, format, args);
+
+	if (vasprintf(&buffer, format, args) == -1)
+	{
+		syserr_printf(ses, "show_debug(%s): vasprintf:", format);
+
+		buffer = strdup("vasprintf error");
+	}
 	va_end(args);
 
 	if (gtd->level->debug || HAS_BIT(root->flags, LIST_FLAG_DEBUG))
@@ -430,8 +451,18 @@ void tintin_header(struct session *ses, int width, char *format, ...)
 		return;
 	}
 
+	if (check_all_events(ses, EVENT_FLAG_SYSTEM, 1, 0, "REFORMAT %s", format))
+	{
+		format = get_variable_def(ses, "result", format);
+	}
+
 	va_start(args, format);
-	vasprintf(&title, format, args);
+	if (vasprintf(&title, format, args) == -1)
+	{
+		syserr_printf(ses, "tintin_header(%s): vasprintf:", format);
+
+		buffer = strdup("vasprintf error");
+	}
 	va_end(args);
 
 	if ((int) strlen(title) > cols - 2)
@@ -469,6 +500,11 @@ void tintin_printf(struct session *ses, char *format, ...)
 
 	push_call("tintin_printf(%p,%p,...)",ses,format);
 
+	if (check_all_events(ses, EVENT_FLAG_SYSTEM, 1, 0, "REFORMAT %s", format))
+	{
+		format = get_variable_def(ses, "result", format);
+	}
+
 	buffer = str_alloc_stack(0);
 
 	va_start(args, format);
@@ -488,6 +524,11 @@ void tintin_printf2(struct session *ses, char *format, ...)
 
 	push_call("tintin_printf2(%p,%p,...)",ses,format);
 
+	if (check_all_events(ses, EVENT_FLAG_SYSTEM, 1, 0, "REFORMAT %s", format))
+	{
+		format = get_variable_def(ses, "result", format);
+	}
+
 	va_start(args, format);
 	if (vasprintf(&buffer, format, args) == -1)
 	{
@@ -511,12 +552,12 @@ void tintin_printf3(struct session *ses, char *format, ...)
 	char *buffer;
 	va_list args;
 
-	push_call("tintin_printf2(%p,%p,...)",ses,format);
+	push_call("tintin_printf3(%p,%p,...)",ses,format);
 
 	va_start(args, format);
 	if (vasprintf(&buffer, format, args) == -1)
 	{
-		syserr_printf(ses, "tintin_printf2: vasprintf:");
+		syserr_printf(ses, "tintin_printf3: vasprintf:");
 
 		pop_call();
 		return;

+ 3 - 3
src/split.c

@@ -56,7 +56,7 @@ DO_COMMAND(do_split)
 
 		if ((*arg1 && !is_math(ses, arg1)) || (*arg2 && !is_math(ses, arg2)))
 		{
-			show_error(ses, LIST_COMMAND, "#SYNTAX: #SPLIT {TOP BAR} {BOT BAR} {LEFT BAR} {RIGHT BAR}");
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SPLIT [TOP BAR] [BOTTOM BAR] [LEFT BAR] [RIGHT BAR] [INPUT BAR]");
 
 			return ses;
 		}
@@ -162,12 +162,12 @@ void init_split(struct session *ses, int top_row, int top_col, int bot_row, int
 	ses->split->top_col = URANGE(1, ses->split->top_col, gtd->screen->cols - 2);
 	ses->split->bot_row = URANGE(ses->split->top_row + 1,  ses->split->bot_row, gtd->screen->rows - 1);
 	ses->split->bot_col = URANGE(ses->split->top_col + 1, ses->split->bot_col, gtd->screen->cols);
-
+/*
 	ses->split->sav_top_row = ses->split->top_row - 1;
 	ses->split->sav_top_col = ses->split->top_col - 1;
 	ses->split->sav_bot_row = gtd->screen->rows - ses->split->bot_row - 1;
 	ses->split->sav_bot_col = gtd->screen->cols - ses->split->bot_col;
-
+*/
 	ses->wrap = ses->split->bot_col - (ses->split->top_col - 1);
 
 	scroll_region(ses, ses->split->top_row, ses->split->bot_row);

+ 2 - 1
src/ssl.c

@@ -45,7 +45,7 @@ DO_COMMAND(do_ssl)
 
 	if (*arg1 == 0 || *arg == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SSL {name} {host} {port}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SSL <NAME> <HOST> <PORT>");
 	}
 	else
 	{
@@ -70,6 +70,7 @@ gnutls_session_t ssl_negotiate(struct session *ses)
 	gnutls_set_default_priority(ssl_ses);
 	gnutls_credentials_set(ssl_ses, GNUTLS_CRD_CERTIFICATE, ssl_cred);
 	gnutls_transport_set_ptr(ssl_ses, (gnutls_transport_ptr_t) (long int) ses->socket);
+	gnutls_server_name_set(ssl_ses, GNUTLS_NAME_DNS, ses->session_host, strlen(ses->session_host));
 
 	do 
 	{

+ 7 - 8
src/substitute.c

@@ -1201,22 +1201,21 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 
 					for (i = 1 ; i < 100 ; i++)
 					{
-						pte = get_arg_in_braces(ses, pte, temp, GET_ALL);
-
-						RESTRING(gtd->vars[i], temp);
-
-						gtd->varc = i + 1;
+						gtd->varc = i;
 
 						if (*pte == 0)
 						{
-							while (++i < 100)
+							while (i < 100)
 							{
-								*gtd->vars[i] = 0;
+								*gtd->vars[i++] = 0;
 							}
-
 							break;
 						}
 
+						pte = get_arg_in_braces(ses, pte, temp, GET_ALL);
+
+						RESTRING(gtd->vars[i], temp);
+
 						if (*pte == COMMAND_SEPARATOR)
 						{
 							pte++;

+ 6 - 6
src/system.c

@@ -54,7 +54,7 @@ DO_COMMAND(do_run)
 
 	if (*arg1 == 0 || *arg2 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #RUN {NAME} {SYSTEM SHELL COMMAND}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #RUN <NAME> <SHELL COMMAND>");
 		
 		return ses;
 	}
@@ -104,7 +104,7 @@ DO_COMMAND(do_script)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SCRIPT: ONE ARGUMENT REQUIRED.");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SCRIPT <VARIABLE> <SHELL COMMAND>");
 	}
 	else if (*arg2 == 0)
 	{
@@ -282,12 +282,12 @@ DO_COMMAND(do_system)
 
 	if (*arg1 == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SYSTEM {COMMAND}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SYSTEM <COMMAND>");
 		
 		return ses;
 	}
 
-	show_message(ses, LIST_COMMAND, "#OK: EXECUTING '%s'", arg1);
+	show_message(ses, LIST_COMMAND, "#SYSTEM: EXECUTING {%s}.", arg1);
 
 	if (!HAS_BIT(gtd->ses->flags, SES_FLAG_READMUD) && IS_SPLIT(gtd->ses))
 	{
@@ -322,7 +322,7 @@ DO_COMMAND(do_textin)
 
 	if ((fp = fopen(arg1, "r")) == NULL)
 	{
-		show_error(ses, LIST_COMMAND, "#ERROR: #TEXTIN {%s} - FILE NOT FOUND.", arg1);
+		show_error(ses, LIST_COMMAND, "#ERROR: #TEXTIN {%s}: FILE NOT FOUND.", arg1);
 		
 		return ses;
 	}
@@ -345,7 +345,7 @@ DO_COMMAND(do_textin)
 	}
 	fclose(fp);
 
-	show_message(ses, LIST_COMMAND, "#TEXTIN {%s} - FILE READ.", arg1);
+	show_message(ses, LIST_COMMAND, "#TEXTIN {%s}: FILE READ.", arg1);
 
 	return ses;
 }

+ 4 - 2
src/tables.c

@@ -690,6 +690,7 @@ struct cursor_type cursor_table[] =
 	{     "RESET MACRO",        "",                                               "",            CURSOR_FLAG_GET_ALL,     cursor_macro,                 "RESET"     }, // obsolete
 	{     "SET",                "Insert given string at cursor",                  "",            CURSOR_FLAG_GET_ONE,     cursor_set,                   ""          },
 	{     "SOFT ENTER",         "Create a new line in edit mode.",                "\e[13;2u",    CURSOR_FLAG_GET_ALL,     cursor_soft_enter,            ""          },
+	{     "ESCAPE ENTER",       "Process multi-line input.",                      "\e[13;5u",    CURSOR_FLAG_GET_ALL,     cursor_escape_enter,          ""          },
 	{     "SUSPEND",            "Suspend program, return with fg",                "",           CURSOR_FLAG_GET_ALL,     cursor_suspend,               ""          },
 	{     "TAB",                "<LIST|SCROLLBACK> <BACKWARD|FORWARD>",           "",            CURSOR_FLAG_GET_ONE,     cursor_tab,                   ""          },
 	{     "TAB L S BACKWARD",   "",                                               "\e[Z",        CURSOR_FLAG_GET_ALL,     cursor_tab,                   "L S B"     }, // shift tab
@@ -817,6 +818,7 @@ struct event_type event_table[] =
 	{    "MONTH",                                  0, EVENT_FLAG_TIME,     "TIME",      "month or given month"       },
 	{    "MOVED ",                                 0, EVENT_FLAG_MOUSE,    "MOUSE",     "mouse is moved"             },
 	{    "NO SESSION ACTIVE",                      0, EVENT_FLAG_INPUT,    "INPUT",     "input on startup session"   },
+	{    "PACKET PATCH",                           0, EVENT_FLAG_OUTPUT,   "OUTPUT",    "broken packet detected"     },
 	{    "PORT CONNECTION",                        0, EVENT_FLAG_PORT,     "PORT",      "socket connects"            },
 	{    "PORT DISCONNECTION",                     0, EVENT_FLAG_PORT,     "PORT",      "socket disconnects"         },
 	{    "PORT INITIALIZED",                       0, EVENT_FLAG_PORT,     "PORT",      "port is initialized"        },
@@ -826,6 +828,7 @@ struct event_type event_table[] =
 	{    "PORT UNINITIALIZED",                     0, EVENT_FLAG_PORT,     "PORT",      "port is uninitialized"      },
 	{    "PRESSED ",                               0, EVENT_FLAG_MOUSE,    "MOUSE",     "mouse button is pressed"    },
 	{    "PROCESSED KEYPRESS",                     0, EVENT_FLAG_INPUT,    "INPUT",     "after a regular keypress"   },
+	{    "PROCESSED LINE",                         0, EVENT_FLAG_OUTPUT,   "OUTPUT",    "after triggers have fired"  },
 	{    "PROGRAM START",                          0, EVENT_FLAG_SYSTEM,   "SYSTEM",    "main session starts"        },
 	{    "PROGRAM TERMINATION",                    0, EVENT_FLAG_SYSTEM,   "SYSTEM",    "main session exits"         },
 	{    "READ ERROR",                             0, EVENT_FLAG_SYSTEM,   "SYSTEM",    "the read command fails"     },
@@ -837,6 +840,7 @@ struct event_type event_table[] =
 	{    "RECEIVED LINE",                          0, EVENT_FLAG_OUTPUT,   "OUTPUT",    "a new line is received"     },
 	{    "RECEIVED OUTPUT",                        0, EVENT_FLAG_OUTPUT,   "OUTPUT",    "bulk output is received"    },
 	{    "RECEIVED PROMPT",                        0, EVENT_FLAG_OUTPUT,   "OUTPUT",    "a prompt is received"       },
+	{    "REFORMAT ",                              0, EVENT_FLAG_SYSTEM,   "SYSTEM",    "system format message"      },
 	{    "RELEASED ",                              0, EVENT_FLAG_MOUSE,    "MOUSE",     "mouse button is released"   },
 	{    "SCAN CSV HEADER",                        0, EVENT_FLAG_SCAN,     "SCAN",      "scanning a csv file"        },
 	{    "SCAN CSV LINE",                          0, EVENT_FLAG_SCAN,     "SCAN",      "scanning a csv file"        },
@@ -924,13 +928,11 @@ struct path_type path_table[] =
 
 struct history_type history_table[] =
 {
-//	{    "CHARACTER",         history_character,   "Set the character used for repeating commands." },
 	{    "DELETE",            history_delete,      "Delete last command history entry."             },
 	{    "GET",               history_get,         "Store in given variable a given index or range." },
 	{    "INSERT",            history_insert,      "Insert a new command history entry."            },
 	{    "LIST",              history_list,        "Display command history list."                  },
 	{    "READ",              history_read,        "Read a command history list from file."         },
-//	{    "SIZE",              history_size,        "The size of the command history."               },
 	{    "WRITE",             history_write,       "Write a command history list to file."          },
 	{    "",                  NULL,                ""                                               }
 };

+ 20 - 20
src/telnet.h

@@ -29,26 +29,26 @@
 	telnet protocol.
 */
 
-#define     IAC     255
-#define     DONT    254
-#define     DO      253
-#define     WONT    252
-#define     WILL    251
-#define     SB      250
-#define     GA      249   /* Used for prompt marking */
-#define     EL      248
-#define     EC      247
-#define     AYT     246
-#define     AO      245
-#define     IP      244
-#define     BREAK   243
-#define     DM      242
-#define     NOP     241
-#define     SE      240
-#define     EOR     239   /* Used for prompt marking */
-#define     ABORT   238
-#define     SUSP    237
-#define     xEOF    236
+#define     IAC     255 // FF
+#define     DONT    254 // FE
+#define     DO      253 // FD
+#define     WONT    252 // FC
+#define     WILL    251 // FB
+#define     SB      250 // FA
+#define     GA      249 // F9  Used for prompt marking
+#define     EL      248 // F8
+#define     EC      247 // F7
+#define     AYT     246 // F6
+#define     AO      245 // F5
+#define     IP      244 // F4
+#define     BREAK   243 // F3
+#define     DM      242 // F2
+#define     NOP     241 // F1
+#define     SE      240 // F0
+#define     EOR     239 // EF  Used for prompt marking
+#define     ABORT   238 // EE
+#define     SUSP    237 // ED
+#define     xEOF    236 // EC
 
 /*
 	telnet options

+ 17 - 44
src/telopt_client.c

@@ -629,21 +629,7 @@ int client_recv_sb_ttype(struct session *ses, int cplen, unsigned char *cpsrc)
 	if (HAS_BIT(ses->telopts, TELOPT_FLAG_MTTS))
 	{
 		char mtts[BUFFER_SIZE];
-/*
-		sprintf(mtts, "MTTS %d", 0
-			+ (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, TINTIN_FLAG_MOUSETRACKING) ? 16 + 1024 : 0)
-			+ (HAS_BIT(ses->config_flags, CONFIG_FLAG_SCREENREADER) ? 64 : 0)
-			+ (ses->color > 256 ? 256 : 0)
-			+ 512
-#ifdef HAVE_GNUTLS_H
-			+ 2048
-#endif
-			);
-*/
+
 		sprintf(mtts, "MTTS %d", get_mtts_val(ses));
 
 		telnet_printf(ses, 6 + strlen(mtts), "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 0, mtts, IAC, SE);
@@ -1462,7 +1448,6 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 	NEW-ENVIRON
 */
 
-
 char *get_charset_mnes(struct session *ses)
 {
 	int index;
@@ -1490,8 +1475,6 @@ int client_recv_sb_new_environ(struct session *ses, int cplen, unsigned char *sr
 		return cplen + 1;
 	}
 
-//	client_telopt_debug(ses, "RCVD IAC SB NEW-ENVIRON %d %d", src[3], src[4]);
-
 	switch (src[3])
 	{
 		case ENV_IS:
@@ -1510,6 +1493,13 @@ int client_recv_sb_new_environ(struct session *ses, int cplen, unsigned char *sr
 
 	i = 4;
 
+	if (src[3] == ENV_SEND && src[4] == IAC)
+	{
+		strcpy(sub2, "VAR");
+
+		goto shortcut;
+	}
+
 	while (i < cplen && src[i] != IAC)
 	{
 		switch (src[i])
@@ -1533,6 +1523,9 @@ int client_recv_sb_new_environ(struct session *ses, int cplen, unsigned char *sr
 			case ENV_VAR:
 			case ENV_USR:
 				i++;
+
+				shortcut:
+
 				pto = buf;
 
 				while (i < cplen && src[i] >= 4 && src[i] != IAC)
@@ -1543,7 +1536,7 @@ int client_recv_sb_new_environ(struct session *ses, int cplen, unsigned char *sr
 
 				substitute(ses, buf, var, SUB_SEC);
 
-				if (src[3] == ENV_SEND)
+				if (src[3] == ENV_SEND && !strcmp(sub2, "VAR"))
 				{
 					client_telopt_debug(ses, "RCVD IAC SB NEW-ENVIRON SEND %s %s", sub2, var);
 
@@ -1555,51 +1548,31 @@ int client_recv_sb_new_environ(struct session *ses, int cplen, unsigned char *sr
 
 						if (!check_all_events(ses, EVENT_FLAG_CATCH, 1, 4, "CATCH IAC SB NEW-ENVIRON SEND %s", var, sub1, sub2, var, ""))
 						{
-							if (!strcmp(var, ""))
-							{
-								if (!strcmp(sub2, "VAR"))
-								{
-									client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR %s VAL %s", "CHARSET", get_charset(ses));
-									client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR %s VAL %s", "CLIENT_NAME", CLIENT_NAME);
-									client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR %s VAL %s", "CLIENT_VERSION", CLIENT_VERSION);
-									client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR MTTS VAL %d", get_mtts_val(ses));
-									client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR TERMINAL_TYPE VAL %s", gtd->system->term);
-
-									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%d" "%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->system->term,
-										IAC, SE);
-								}
-							}
-							else if (!strcmp(var, "CHARSET"))
+							if (*var == 0 || !strcmp(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_mnes(ses), IAC, SE);
 
 								client_telopt_debug(ses, "SENT IAC SB NEW-ENVIRON IS VAR %s VAL %s", "CHARSET", get_charset(ses));
 							}
-							else if (!strcmp(var, "CLIENT_NAME"))
+							if (*var == 0 || !strcmp(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 (!strcmp(var, "CLIENT_VERSION"))
+							if (*var == 0 || !strcmp(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 (!strcmp(var, "MTTS") || *var == 0)
+							if (*var == 0 || !strcmp(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 (!strcmp(var, "TERMINAL_TYPE"))
+							if (*var == 0 || !strcmp(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->system->term, IAC, SE);
 

+ 51 - 27
src/telopt_server.c

@@ -96,6 +96,21 @@ struct iac_type iac_server_table [] =
 	{ 0, NULL,                                                                  NULL}
 };
 
+void server_telopt_debug(struct session *ses, char *format, ...)
+{
+	char buf[BUFFER_SIZE];
+	va_list args;
+
+	if (HAS_BIT(ses->telopts, TELOPT_FLAG_DEBUG))
+	{
+		va_start(args, format);
+		vsprintf(buf, format, args);
+		va_end(args);
+
+		tintin_puts(ses, buf);
+	}
+}
+
 /*
 	Call this to announce support for telopts marked as such in tables.c
 */
@@ -105,17 +120,21 @@ void announce_support(struct session *ses, struct port_data *buddy)
 	int i;
 
 	push_call("announce_support(%p,%p)",ses,buddy);
-	
+
 	for (i = 0 ; i < 255 ; i++)
 	{
 		if (telopt_table[i].flags)
 		{
 			if (HAS_BIT(telopt_table[i].flags, ANNOUNCE_WILL))
 			{
+				server_telopt_debug(ses, "SENT IAC WILL %s", telopt_table[i].name);
+
 				port_telnet_printf(ses, buddy, 3, "%c%c%c", IAC, WILL, i);
 			}
 			if (HAS_BIT(telopt_table[i].flags, ANNOUNCE_DO))
 			{
+				server_telopt_debug(ses, "SENT IAC DO %s", telopt_table[i].name);
+
 				port_telnet_printf(ses, buddy, 3, "%c%c%c", IAC, DO, i);
 			}
 		}
@@ -276,6 +295,12 @@ int server_translate_telopts(struct session *ses, struct port_data *buddy, unsig
 		switch (*pti)
 		{
 			case IAC:
+				if (!HAS_BIT(ses->config_flags, CONFIG_FLAG_TELNET))
+				{
+					*pto++ = *pti++;
+					srclen--;
+					break;
+				}
 				skip = 2;
 
 				debug_telopts(ses, buddy, pti, srclen);
@@ -400,12 +425,12 @@ void telopt_debug(struct session *ses, char *format, ...)
 
 void debug_telopts(struct session *ses, struct port_data *buddy, unsigned char *src, int srclen)
 {
-	if (srclen > 1 && TELOPT_DEBUG)
+	if (srclen > 1)
 	{
 		switch(src[1])
 		{
 			case IAC:
-				tintin_printf2(ses, "RCVD IAC IAC");
+				server_telopt_debug(ses, "RCVD IAC IAC");
 				break;
 
 			case DO:
@@ -419,39 +444,39 @@ void debug_telopts(struct session *ses, struct port_data *buddy, unsigned char *
 					{
 						if (skip_sb(ses, buddy, src, srclen) == srclen + 1)
 						{
-							tintin_printf2(ses, "RCVD IAC SB %s ?", TELOPT(src[2]));
+							server_telopt_debug(ses, "RCVD IAC SB %s ?", TELOPT(src[2]));
 						}
 						else
 						{
-							tintin_printf2(ses, "RCVD IAC SB %s IAC SE", TELOPT(src[2]));
+							server_telopt_debug(ses, "RCVD IAC SB %s IAC SE", TELOPT(src[2]));
 						}
 					}
 					else
 					{
-						tintin_printf2(ses, "RCVD IAC %s %s", TELCMD(src[1]), TELOPT(src[2]));
+						server_telopt_debug(ses, "RCVD IAC %s %s", TELCMD(src[1]), TELOPT(src[2]));
 					}
 				}
 				else
 				{
-					tintin_printf2(ses, "RCVD IAC %s ?", TELCMD(src[1]));
+					server_telopt_debug(ses, "RCVD IAC %s ?", TELCMD(src[1]));
 				}
 				break;
 
 			default:
 				if (TELCMD_OK(src[1]))
 				{
-					tintin_printf2(ses, "RCVD IAC %s", TELCMD(src[1]));
+					server_telopt_debug(ses, "RCVD IAC %s", TELCMD(src[1]));
 				}
 				else
 				{
-					tintin_printf2(ses, "RCVD IAC %d", src[1]);
+					server_telopt_debug(ses, "RCVD IAC %d", src[1]);
 				}
 				break;
 		}
 	}
 	else
 	{
-		tintin_printf2(ses, "RCVD IAC ?");
+		server_telopt_debug(ses, "RCVD IAC ?");
 	}
 }
 
@@ -544,7 +569,7 @@ int process_sb_ttype_is(struct session *ses, struct port_data *buddy, unsigned c
 
 				if (TELOPT_DEBUG)
 				{
-					tintin_printf2(ses, "INFO IAC SB TTYPE RCVD VAL %s.", val);
+					server_telopt_debug(ses, "INFO IAC SB TTYPE RCVD VAL %s.", val);
 				}
 
 				if (*buddy->ttype == 0)
@@ -611,10 +636,7 @@ int process_sb_naws(struct session *ses, struct port_data *buddy, unsigned char
 		}
 	}
 
-	if (TELOPT_DEBUG)
-	{
-		tintin_printf2(ses, "INFO IAC SB NAWS RCVD ROWS %d COLS %d", buddy->rows, buddy->cols);
-	}
+	server_telopt_debug(ses, "INFO IAC SB NAWS RCVD ROWS %d COLS %d", buddy->rows, buddy->cols);
 
 	return skip_sb(ses, buddy, src, srclen);
 }
@@ -627,6 +649,8 @@ int process_will_new_environ(struct session *ses, struct port_data *buddy, unsig
 {
 	port_socket_printf(ses, buddy, "%c%c%c%c%c%s%c%c", IAC, SB, TELOPT_NEW_ENVIRON, ENV_SEND, ENV_VAR, "SYSTEMTYPE", IAC, SE);
 
+	port_socket_printf(ses, buddy, "%c%c%c%c%c%c", IAC, SB, TELOPT_NEW_ENVIRON, ENV_SEND, IAC, SE);
+
 	return 3;
 }
 
@@ -662,7 +686,7 @@ int process_sb_new_environ(struct session *ses, struct port_data *buddy, unsigne
 
 				if (src[i] != ENV_VAL)
 				{
-					tintin_printf2(ses, "INFO IAC SB NEW-ENVIRON RCVD %d VAR %s", src[3], var);
+					server_telopt_debug(ses, "INFO IAC SB NEW-ENVIRON RCVD %s KEY %s VAL (EMPTY)", src[3] == ENV_VAR ? "VAR" : "USERVAR", var);
 				}
 				break;
 
@@ -676,10 +700,7 @@ int process_sb_new_environ(struct session *ses, struct port_data *buddy, unsigne
 				}
 				*pto = 0;
 
-				if (TELOPT_DEBUG)
-				{
-					tintin_printf2(ses, "INFO IAC SB NEW-ENVIRON RCVD %d VAR %s VAL %s", src[3], var, val);
-				}
+				server_telopt_debug(ses, "INFO IAC SB NEW-ENVIRON RCVD %s KEY %s VAL %s", src[3] == ENV_VAR ? "VAR" : "USERVAR", var, val);
 
 				if (src[3] == ENV_IS)
 				{
@@ -757,13 +778,10 @@ int process_sb_charset(struct session *ses, struct port_data *buddy, unsigned ch
 		}
 		*pto = 0;
 
-		if (TELOPT_DEBUG)
-		{
-			tintin_printf2(ses, "INFO IAC SB CHARSET RCVD %d VAL %s", src[3], val);
-		}
-
 		if (src[3] == CHARSET_ACCEPTED)
 		{
+			server_telopt_debug(ses, "INFO IAC SB CHARSET RCVD ACCEPED VAL %s", val);
+
 			if (!strcasecmp(val, "UTF-8"))
 			{
 				SET_BIT(buddy->comm_flags, COMM_FLAG_UTF8);
@@ -771,11 +789,17 @@ int process_sb_charset(struct session *ses, struct port_data *buddy, unsigned ch
 		}
 		else if (src[3] == CHARSET_REJECTED)
 		{
+			server_telopt_debug(ses, "INFO IAC SB CHARSET RCVD REJECTED VAL %s", val);
+
 			if (!strcasecmp(val, "UTF-8"))
 			{
 				DEL_BIT(buddy->comm_flags, COMM_FLAG_UTF8);
 			}
 		}
+		else
+		{
+			server_telopt_debug(ses, "INFO IAC SB CHARSET RCVD %d VAL %s", src[3], val);
+		}
 		i++;
 	}
 	return i + 1;
@@ -806,7 +830,7 @@ int process_do_msdp(struct session *ses, struct port_data *buddy, unsigned char
 		buddy->msdp_data[index]->value = strdup("");
 	}
 
-	tintin_printf2(ses, "INFO MSDP INITIALIZED");
+	server_telopt_debug(ses, "INFO MSDP INITIALIZED");
 
 	// Easiest to handle variable initialization here.
 
@@ -905,7 +929,7 @@ int process_do_gmcp(struct session *ses, struct port_data *buddy, unsigned char
 	{
 		return 3;
 	}
-	tintin_printf2(ses, "INFO MSDP OVER GMCP INITIALIZED");
+	server_telopt_debug(ses, "INFO MSDP OVER GMCP INITIALIZED");
 
 	SET_BIT(buddy->comm_flags, COMM_FLAG_GMCP);
 

+ 20 - 18
src/tintin.h

@@ -212,7 +212,7 @@
 
 
 #define CLIENT_NAME              "TinTin++"
-#define CLIENT_VERSION           "2.02.31 "
+#define CLIENT_VERSION           "2.02.40 "
 
 
 #define XT_E                            0x27
@@ -651,7 +651,7 @@ enum operators
 #define SES_FLAG_BUFFERUPDATE         BV01
 #define SES_FLAG_CLOSED               BV02
 #define SES_FLAG_CONNECTED            BV03
-#define SES_FLAG_GAG                  BV04 // unused
+#define SES_FLAG_LINKLOST             BV04
 #define SES_FLAG_PATHMAPPING          BV05
 #define SES_FLAG_PRINTBUFFER          BV06
 #define SES_FLAG_PRINTLINE            BV07
@@ -774,6 +774,7 @@ enum operators
 #define MAP_FLAG_READ                 BV18
 #define MAP_FLAG_PANCAKE              BV19
 #define MAP_FLAG_FAST                 BV20
+#define MAP_FLAG_AUTOLINK             BV21
 
 #define MAP_SEARCH_NAME                0
 #define MAP_SEARCH_EXITS               1
@@ -2013,6 +2014,7 @@ extern DO_CURSOR(cursor_echo);
 extern DO_CURSOR(cursor_end);
 extern DO_CURSOR(cursor_enter);
 extern DO_CURSOR(cursor_enter_finish);
+extern DO_CURSOR(cursor_escape_enter);
 extern DO_CURSOR(cursor_flag);
 extern DO_CURSOR(cursor_get);
 extern DO_CURSOR(cursor_history_find);
@@ -2305,12 +2307,10 @@ extern struct session *repeat_history(struct session *ses, char *line);
 extern int write_history(struct session *ses, char *filename);
 extern int read_history(struct session *ses, char *filename);
 
-DO_HISTORY(history_character);
 DO_HISTORY(history_delete);
 DO_HISTORY(history_get);
 DO_HISTORY(history_insert);
 DO_HISTORY(history_list);
-DO_HISTORY(history_size);
 DO_HISTORY(history_read);
 DO_HISTORY(history_write);
 
@@ -2937,6 +2937,7 @@ extern void check_all_gags(struct session *ses, char *original, char *line);
 extern void check_all_highlights(struct session *ses, char *original, char *line);
 extern  int check_all_prompts(struct session *ses, char *original, char *line);
 extern void check_all_substitutions(struct session *ses, char *original, char *line);
+extern void check_all_substitutions_multi(struct session *ses, char *original, char *line);
 
 #endif
 
@@ -2998,7 +2999,7 @@ extern int get_utf8_index(char *str, int *index);
 extern int unicode_to_utf8(int index, char *out);
 extern int utf8_strlen(char *str, int *width);
 
-extern int utf8_to_all(struct session *ses, char *in, char *out);
+extern int utf8_to_all(struct session *ses, char *in, char *out, int size);
 extern int all_to_utf8(struct session *ses, char *in, char *out);
 extern int cp1251_to_utf8(char *input, char *output);
 extern int utf8_to_cp1251(char *input, char *output);
@@ -3017,7 +3018,7 @@ 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);
+extern int utf8_to_gbk1(char *input, char *output, int length);
 extern int is_cp949(char *str);
 extern int cp949_to_utf8(char *input, char *output);
 extern int utf8_to_cp949(char *input, char *output);
@@ -3029,18 +3030,19 @@ extern int utf8_to_cp949(char *input, char *output);
 
 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);
-extern void justify_string(struct session *ses, char *in, char *out, int align, int cut);
-extern void format_string(struct session *ses, char *format, char *arg, char *out);
+extern char *get_variable_def(struct session *ses, char *var, char *def);
+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);
+extern void  justify_string(struct session *ses, char *in, char *out, int align, int cut);
+extern void  format_string(struct session *ses, char *format, char *arg, char *out);
 extern struct listnode *search_variable(struct session *ses, char *variable);
 extern struct listnode *get_variable(struct session *ses, char *variable, char *result);
 extern struct listnode *set_variable(struct session *ses, char *variable, char *format, ...);

+ 7 - 7
src/tokenize.c

@@ -402,7 +402,7 @@ char *addregextoken(struct scriptroot *root, int lvl, int type, int cmd, char *s
 
 	if (*arg3 == 0)
 	{
-		show_error(root->ses, LIST_COMMAND, "#SYNTAX: #REGEXP {string} {expression} {true} {false}");
+		show_error(root->ses, LIST_COMMAND, "#SYNTAX: #REGEXP <TEXT> <EXPRESSION> <TRUE> [FALSE]");
 	}
 	addtoken(root, lvl, type, cmd, arg1);
 
@@ -634,7 +634,7 @@ void tokenize_script(struct scriptroot *root, int lvl, char *str)
 
 						if (*str == 0 || *str == COMMAND_SEPARATOR)
 						{
-							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #FOREACH {list} {variable} {commands}");
+							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #FOREACH <LIST> <VARIABLE> <COMMANDS>");
 						}
 
 						str = get_arg_in_braces(root->ses, str, line, GET_ALL);
@@ -649,7 +649,7 @@ void tokenize_script(struct scriptroot *root, int lvl, char *str)
 
 						if (*str == 0 || *str == COMMAND_SEPARATOR)
 						{
-							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #IF {conditional} {true} {false}");
+							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #IF <CONDITIONAL> <TRUE> [FALSE]");
 						}
 
 						str = get_arg_in_braces(root->ses, str, line, GET_ALL);
@@ -673,7 +673,7 @@ void tokenize_script(struct scriptroot *root, int lvl, char *str)
 
 						if (*str == 0 || *str == COMMAND_SEPARATOR)
 						{
-							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #LOOP {start} {finish} {commands}");
+							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #LOOP <START> <END> <COMMANDS>");
 						}
 
 						str = get_arg_in_braces(root->ses, str, line, GET_ALL);
@@ -687,7 +687,7 @@ void tokenize_script(struct scriptroot *root, int lvl, char *str)
 
 						if (*str == 0 || *str == COMMAND_SEPARATOR)
 						{
-							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #PARSE {string} {variable} {commands}");
+							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #PARSE <TEXT> <VARIABLE> <COMMANDS>");
 						}
 
 						str = get_arg_in_braces(root->ses, str, line, GET_ALL);
@@ -721,7 +721,7 @@ void tokenize_script(struct scriptroot *root, int lvl, char *str)
 
 						if (*str == 0 || *str == COMMAND_SEPARATOR)
 						{
-							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #SWITCH {conditional} {arguments}");
+							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #SWITCH <CONDITIONAL> <ARGUMENTS>");
 						}
 
 						str = get_arg_in_braces(root->ses, str, line, GET_ALL);
@@ -736,7 +736,7 @@ void tokenize_script(struct scriptroot *root, int lvl, char *str)
 
 						if (*str == 0 || *str == COMMAND_SEPARATOR)
 						{
-							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #WHILE {conditional} {commands}");
+							show_error(root->ses, LIST_COMMAND, "#SYNTAX: #WHILE <CONDITIONAL> <COMMANDS>");
 							deltoken(root, root->prev);
 							break;
 						}

+ 142 - 33
src/trigger.c

@@ -41,19 +41,19 @@ DO_COMMAND(do_action)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_ACTION]) == FALSE)
 		{
-			show_message(ses, LIST_ACTION, "#ACTION: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_ACTION, "#ACTION: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
 	{
 		if (*arg3 && (atof(arg3) < 1 || atof(arg3) >= 10))
 		{
-			show_error(ses, LIST_ACTION, "\e[1;31m#WARNING: #ACTION {%s} {..} {%s} SHOULD HAVE A PRIORITY BETWEEN 1.000 and 9.999.", arg1, arg3);
+			show_error(ses, LIST_ACTION, "#WARNING: #ACTION {%s} {..} {%s} SHOULD HAVE A PRIORITY BETWEEN 1.000 and 9.999.", arg1, arg3);
 		}
 
 		update_node_list(ses->list[LIST_ACTION], arg1, arg2, arg3, "");
 
-		show_message(ses, LIST_ACTION, "#OK. #ACTION {%s} NOW TRIGGERS {%s} @ {%s}.", arg1, arg2, arg3);
+		show_message(ses, LIST_ACTION, "#OK: #ACTION {%s} NOW TRIGGERS {%s} @ {%s}.", arg1, arg2, arg3);
 	}
 	return ses;
 }
@@ -103,7 +103,7 @@ void check_all_actions_multi(struct session *ses, char *original, char *stripped
 {
 	struct listroot *root = ses->list[LIST_ACTION];
 	struct listnode *node;
-	char *pto, *pts;
+	char *pto, *pts, *ptm;
 
 	for (root->multi_update = 0 ; root->multi_update < root->used ; root->multi_update++)
 	{
@@ -152,6 +152,13 @@ void check_all_actions_multi(struct session *ses, char *original, char *stripped
 				}
 			}
 			script_driver(ses, LIST_ACTION, buf);
+
+			ptm = node->arg1 + (*node->arg1 == '~');
+
+			if (ptm[0] == '\\' && ptm[1] == 'A')
+			{
+				break;
+			}
 		}
 	}
 }
@@ -178,7 +185,7 @@ DO_COMMAND(do_alias)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_ALIAS]) == FALSE)
 		{
-			show_message(ses, LIST_ALIAS, "#ALIAS: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_ALIAS, "#ALIAS: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
@@ -250,21 +257,20 @@ int check_all_aliases(struct session *ses, char *input)
 
 				for (i = 1 ; i < 100 ; i++)
 				{
-					arg = get_arg_in_braces(ses, arg, buf, GET_ONE);
-
-					RESTRING(gtd->vars[i], buf);
-
-					gtd->varc = i + 1;
+					gtd->varc = i;
 
 					if (*arg == 0)
 					{
-						while (++i < 100)
+						while (i < 100)
 						{
-							*gtd->vars[i] = 0;
+							*gtd->vars[i++] = 0;
 						}
 						break;
 					}
 
+					arg = get_arg_in_braces(ses, arg, buf, GET_ONE);
+
+					RESTRING(gtd->vars[i], buf);
 				}
 			}
 
@@ -306,7 +312,7 @@ DO_COMMAND(do_button)
 	struct listnode *node;
 	int index;
 
-	arg = get_arg_in_braces(ses, arg, arg1, GET_ALL);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
 	arg = get_arg_in_braces(ses, arg, arg3, GET_ALL);
 
@@ -318,7 +324,7 @@ DO_COMMAND(do_button)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_BUTTON]) == FALSE)
 		{
-			show_message(ses, LIST_BUTTON, "#BUTTON: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_BUTTON, "#BUTTON: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
@@ -328,7 +334,7 @@ DO_COMMAND(do_button)
 
 		node = update_node_list(ses->list[LIST_BUTTON], arg1, arg2, arg3, "");
 
-		show_message(ses, LIST_BUTTON, "#OK. BUTTON {%s} NOW TRIGGERS {%s} @ {%s}.", arg1, arg2, arg3);
+		show_message(ses, LIST_BUTTON, "#OK: BUTTON {%s} NOW TRIGGERS {%s} @ {%s}.", arg1, arg2, arg3);
 
 		arg = arg1;
 
@@ -468,7 +474,7 @@ DO_COMMAND(do_delay)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_DELAY]) == FALSE)
 		{
-			show_message(ses, LIST_DELAY, "#DELAY: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_DELAY, "#DELAY: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
@@ -483,7 +489,7 @@ DO_COMMAND(do_delay)
 
 			create_node_list(ses->list[LIST_DELAY], time, arg2, arg1, arg3);
 
-			show_message(ses, LIST_DELAY, "#OK, IN {%s} SECONDS {%s} IS EXECUTED.", arg1, arg2);
+			show_message(ses, LIST_DELAY, "#DELAY: IN {%s} SECONDS {%s} IS EXECUTED.", arg1, arg2);
 
 		}
 		else
@@ -498,7 +504,7 @@ DO_COMMAND(do_delay)
 
 			node->shots = 1;
 
-			show_message(ses, LIST_TICKER, "#ONESHOT: #TICK {%s} WILL EXECUTE {%s} IN {%s} SECONDS.", arg1, arg2, time);
+			show_message(ses, LIST_TICKER, "#ONESHOT: #TICKER {%s} WILL EXECUTE {%s} IN {%s} SECONDS.", arg1, arg2, time);
 		}
 	}
 	return ses;
@@ -544,14 +550,14 @@ DO_COMMAND(do_function)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_FUNCTION]) == FALSE)
 		{
-			show_message(ses, LIST_FUNCTION, "#FUNCTION: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_FUNCTION, "#FUNCTION: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
 	{
 		update_node_list(ses->list[LIST_FUNCTION], arg1, arg2, "", "");
 
-		show_message(ses, LIST_FUNCTION, "#OK. FUNCTION {%s} NOW TRIGGERS {%s}.", arg1, arg2);
+		show_message(ses, LIST_FUNCTION, "#OK: FUNCTION {%s} NOW TRIGGERS {%s}.", arg1, arg2);
 	}
 	return ses;
 }
@@ -585,7 +591,7 @@ DO_COMMAND(do_gag)
 	{
 		update_node_list(ses->list[LIST_GAG], arg1, "", "", "");
 
-		show_message(ses, LIST_GAG, "#OK. {%s} IS NOW GAGGED.", arg1);
+		show_message(ses, LIST_GAG, "#OK: {%s} IS NOW GAGGED.", arg1);
 	}
 	return ses;
 }
@@ -647,7 +653,7 @@ DO_COMMAND(do_highlight)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_HIGHLIGHT]) == FALSE)
 		{
-			show_message(ses, LIST_HIGHLIGHT, "#HIGHLIGHT: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_HIGHLIGHT, "#HIGHLIGHT: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
@@ -661,7 +667,7 @@ DO_COMMAND(do_highlight)
 		{
 			update_node_list(ses->list[LIST_HIGHLIGHT], arg1, arg2, arg3, "");
 
-			show_message(ses, LIST_HIGHLIGHT, "#OK. {%s} NOW HIGHLIGHTS {%s} @ {%s}.", arg1, arg2, arg3);
+			show_message(ses, LIST_HIGHLIGHT, "#OK: {%s} NOW HIGHLIGHTS {%s} @ {%s}.", arg1, arg2, arg3);
 		}
 	}
 	return ses;
@@ -773,7 +779,7 @@ DO_COMMAND(do_macro)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_MACRO]) == FALSE)
 		{
-			show_message(ses, LIST_MACRO, "#MACRO: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_MACRO, "#MACRO: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
@@ -782,7 +788,7 @@ DO_COMMAND(do_macro)
 
 		update_node_list(ses->list[LIST_MACRO], arg1, arg2, "", arg3);
 
-		show_message(ses, LIST_MACRO, "#OK. MACRO {%s} NOW TRIGGERS {%s}.", arg1, arg2);
+		show_message(ses, LIST_MACRO, "#OK: MACRO {%s} NOW TRIGGERS {%s}.", arg1, arg2);
 	}
 	return ses;
 }
@@ -818,7 +824,7 @@ DO_COMMAND(do_prompt)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_PROMPT]) == FALSE)
 		{
-			show_message(ses, LIST_PROMPT, "#PROMPT: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_PROMPT, "#PROMPT: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
@@ -831,7 +837,7 @@ DO_COMMAND(do_prompt)
 
 		update_node_list(ses->list[LIST_PROMPT], arg1, arg2, arg3, arg4);
 
-		show_message(ses, LIST_PROMPT, "#OK. {%s} NOW PROMPTS {%s} @ {%s} {%s}.", arg1, arg2, arg3, arg4);
+		show_message(ses, LIST_PROMPT, "#OK: {%s} NOW PROMPTS {%s} @ {%s} {%s}.", arg1, arg2, arg3, arg4);
 	}
 	return ses;
 }
@@ -915,14 +921,14 @@ DO_COMMAND(do_substitute)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_SUBSTITUTE]) == FALSE)
 		{
-			show_message(ses, LIST_SUBSTITUTE, "#SUBSTITUTE: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_SUBSTITUTE, "#SUBSTITUTE: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
 	{
 		update_node_list(ses->list[LIST_SUBSTITUTE], arg1, arg2, arg3, "");
 
-		show_message(ses, LIST_SUBSTITUTE, "#OK. {%s} IS NOW SUBSTITUTED AS {%s} @ {%s}.", arg1, arg2, arg3);
+		show_message(ses, LIST_SUBSTITUTE, "#OK: {%s} IS NOW SUBSTITUTED AS {%s} @ {%s}.", arg1, arg2, arg3);
 	}
 	return ses;
 }
@@ -953,6 +959,11 @@ void check_all_substitutions(struct session *ses, char *original, char *line)
 	{
 		node = root->list[root->update];
 
+		if (HAS_BIT(node->flags, NODE_FLAG_MULTI))
+		{
+			continue;
+		}
+
 		if (check_one_regexp(ses, node, line, original, 0))
 		{
 			pto = original;
@@ -996,8 +1007,106 @@ void check_all_substitutions(struct session *ses, char *original, char *line)
 				pto = ptm + len;
 
 				show_debug(ses, LIST_SUBSTITUTE, COLOR_DEBUG "#DEBUG SUBSTITUTE " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "} {" COLOR_STRING "%s" COLOR_BRACE "}", node->arg1, match);
+
+				ptm = node->arg1 + (*node->arg1 == '~');
+
+				if (ptm[0] == '\\' && ptm[1] == 'A')
+				{
+					break;
+				}
+			}
+			while (*pto && check_one_regexp(ses, node, ptl, pto, PCRE_NOTBOL));
+
+			if (node->shots && --node->shots == 0)
+			{
+				delete_node_list(ses, LIST_SUBSTITUTE, node);
+			}
+			strcpy(ptr, pto);
+
+			strcpy(original, result);
+
+			strip_vt102_codes(original, line);
+		}
+	}
+	pop_call();
+	return;
+}
+
+void check_all_substitutions_multi(struct session *ses, char *original, char *line)
+{
+	char *match, *subst, *result, *temp, *ptl, *ptm, *pto, *ptr;
+	struct listroot *root = ses->list[LIST_SUBSTITUTE];
+	struct listnode *node;
+	int len;
+
+	push_call("check_all_substitutions(%p,%p,%p)",ses,original,line);
+
+	match  = str_alloc_stack(0);
+	subst  = str_alloc_stack(0);
+	result = str_alloc_stack(0);
+	temp   = str_alloc_stack(0);
+
+	for (root->multi_update = 0 ; root->multi_update < root->used ; root->multi_update++)
+	{
+		node = root->list[root->multi_update];
+
+		if (!HAS_BIT(node->flags, NODE_FLAG_MULTI))
+		{
+			continue;
+		}
+
+		if (check_one_regexp(ses, node, line, original, 0))
+		{
+			pto = original;
+			ptl = line;
+			ptr = result;
+
+			*result = *gtd->color_reset = 0;
+
+			do
+			{
+				if (*gtd->vars[0] == 0)
+				{
+					break;
+				}
+
+				strcpy(match, gtd->vars[0]);
+
+				substitute(ses, node->arg2, temp, SUB_ARG);
+
+				if (*node->arg1 == '~')
+				{
+					ptm = strstr(pto, match);
+
+					len = strlen(match);
+				}
+				else
+				{
+					ptm = strip_vt102_strstr(pto, match, &len);
+
+					ptl = strstr(ptl, match) + strlen(match);
+				}
+
+				*ptm = 0;
+
+				get_color_codes(gtd->color_reset, pto, gtd->color_reset, GET_ALL);
+
+				substitute(ses, temp, subst, SUB_VAR|SUB_FUN|SUB_COL|SUB_ESC);
+
+				ptr += sprintf(ptr, "%s%s", pto, subst);
+
+				pto = ptm + len;
+
+				show_debug(ses, LIST_SUBSTITUTE, COLOR_DEBUG "#DEBUG SUBSTITUTE " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "} {" COLOR_STRING "%s" COLOR_BRACE "}", node->arg1, match);
+
+				ptm = node->arg1 + (*node->arg1 == '~');
+
+				if (ptm[0] == '\\' && ptm[1] == 'A')
+				{
+					break;
+				}
 			}
-			while (*pto && check_one_regexp(ses, node, ptl, pto, 0));
+			while (*pto && check_one_regexp(ses, node, ptl, pto, PCRE_NOTBOL));
 
 			if (node->shots && --node->shots == 0)
 			{
@@ -1034,7 +1143,7 @@ DO_COMMAND(do_tab)
 	{
 		update_node_list(ses->list[LIST_TAB], arg1, "", "", "");
 
-		show_message(ses, LIST_TAB, "#OK. {%s} IS NOW A TAB.", arg1);
+		show_message(ses, LIST_TAB, "#OK: {%s} IS NOW A TAB.", arg1);
 	}
 	return ses;
 }
@@ -1087,14 +1196,14 @@ DO_COMMAND(do_tick)
 	{
 		if (show_node_with_wild(ses, arg1, ses->list[LIST_TICKER]) == FALSE) 
 		{
-			show_message(ses, LIST_TICKER, "#TICK, NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_TICKER, "#TICKER: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
 	{
 		update_node_list(ses->list[LIST_TICKER], arg1, arg2, time, arg3);
 
-		show_message(ses, LIST_TICKER, "#OK. #TICK {%s} NOW EXECUTES {%s} EVERY {%s} SECONDS.", arg1, arg2, time);
+		show_message(ses, LIST_TICKER, "#OK: #TICKER {%s} NOW EXECUTES {%s} EVERY {%s} SECONDS.", arg1, arg2, time);
 	}
 	return ses;
 }

+ 8 - 10
src/update.c

@@ -403,14 +403,6 @@ void update_sessions(void)
 					if (rv < 0)
 					{
 						break; // bug report after removal.
-
-						syserr_printf(ses, "update_sessions: select:");
-
-						cleanup_session(ses);
-
-						gtd->mud_output_len = 0;
-
-						break;
 					}
 
 					if (rv == 0)
@@ -424,6 +416,8 @@ void update_sessions(void)
 						{
 							readmud(ses);
 
+							SET_BIT(ses->flags, SES_FLAG_LINKLOST);
+
 							cleanup_session(ses);
 
 							gtd->mud_output_len = 0;
@@ -436,6 +430,8 @@ void update_sessions(void)
 					{
 						FD_CLR(ses->socket, &read_fd);
 
+						SET_BIT(ses->flags, SES_FLAG_LINKLOST);
+
 						cleanup_session(ses);
 
 						gtd->mud_output_len = 0;
@@ -481,8 +477,10 @@ void update_sessions(void)
 				}
 				else
 				{
+/*
 					if (HAS_BIT(ses->flags, SES_FLAG_SNOOPSCROLL))
 					{
+
 						if (HAS_BIT(ses->scroll->flags, SCROLL_FLAG_RESIZE))
 						{
 							buffer_refresh(ses, "", "", "");
@@ -492,9 +490,9 @@ void update_sessions(void)
 							print_scroll_region(ses);
 						}
 					}
+*/
 					buffer_end(ses, "", "", "");
 				}
-
 				DEL_BIT(ses->flags, SES_FLAG_PRINTBUFFER);
 			}
 
@@ -897,7 +895,7 @@ void tick_update(void)
 			{
 				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->val64);
+				show_info(ses, LIST_TICKER, "#INFO TICKER {%s} INITIALIZED WITH TIMESTAMP {%lld}", node->arg1, node->val64);
 
 				if (node->val64 < gtd->utime_next_tick)
 				{

+ 5 - 4
src/utf8.c

@@ -473,7 +473,7 @@ int utf8_strlen(char *str, int *str_len)
 	return raw_len;
 }
 
-int utf8_to_all(struct session *ses, char *in, char *out)
+int utf8_to_all(struct session *ses, char *in, char *out, int size)
 {
 	switch (HAS_BIT(ses->charset, CHARSET_FLAG_ALL_TOUTF8))
 	{
@@ -487,7 +487,7 @@ int utf8_to_all(struct session *ses, char *in, char *out)
 			return utf8_to_cp949(in, out);
 
 		case CHARSET_FLAG_GBK1TOUTF8:
-			return utf8_to_gbk1(in, out);
+			return utf8_to_gbk1(in, out, size);
 
 		case CHARSET_FLAG_FANSITOUTF8:
 			return sprintf(out, "%s", in);
@@ -1343,7 +1343,7 @@ int is_gbk1(char *str)
 	return ptu[0] > 128 && ptu[0] < 255 && ptu[1] > 64 && ptu[1] < 255;
 }
 
-int utf8_to_gbk1(char *input, char *output)
+int utf8_to_gbk1(char *input, char *output, int length)
 {
 	char *pti, *pto;
 	int size, index, result;
@@ -1351,7 +1351,8 @@ int utf8_to_gbk1(char *input, char *output)
 	pti = input;
 	pto = output;
 
-	while (*pti)
+//	while (*pti)
+	while (length--)
 	{
 		size = get_utf8_index(pti, &index);
 

+ 31 - 13
src/variable.c

@@ -64,7 +64,7 @@ DO_COMMAND(do_variable)
 		}
 		else if (show_node_with_wild(ses, arg1, ses->list[LIST_VARIABLE]) == FALSE)
 		{
-			show_message(ses, LIST_VARIABLE, "#VARIABLE: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_VARIABLE, "#VARIABLE: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
@@ -93,7 +93,7 @@ DO_COMMAND(do_variable)
 
 		show_nest_node(node, &str, 1);
 
-		show_message(ses, LIST_VARIABLE, "#OK. VARIABLE {%s} HAS BEEN SET TO {%s}.", arg1, str);
+		show_message(ses, LIST_VARIABLE, "#OK: VARIABLE {%s} HAS BEEN SET TO {%s}.", arg1, str);
 	}
 	return ses;
 }
@@ -106,7 +106,7 @@ DO_COMMAND(do_unvariable)
 	{
 		if (delete_nest_node(ses->list[LIST_VARIABLE], arg1))
 		{
-			show_message(ses, LIST_VARIABLE, "#OK. {%s} IS NO LONGER A VARIABLE.", arg1);
+			show_message(ses, LIST_VARIABLE, "#OK: {%s} IS NO LONGER A VARIABLE.", arg1);
 		}
 		else
 		{
@@ -157,7 +157,7 @@ DO_COMMAND(do_local)
 		}
 		else if (show_node_with_wild(ses, arg1, root) == FALSE)
 		{
-			show_message(ses, LIST_VARIABLE, "#LOCAL: NO MATCH(ES) FOUND FOR {%s}.", arg1);
+			show_message(ses, LIST_VARIABLE, "#LOCAL: NO MATCHES FOUND FOR {%s}.", arg1);
 		}
 	}
 	else
@@ -184,7 +184,7 @@ DO_COMMAND(do_local)
 
 		show_nest_node(node, &str, 1);
 
-		show_message(ses, LIST_VARIABLE, "#OK. LOCAL VARIABLE {%s} HAS BEEN SET TO {%s}.", arg1, str);
+		show_message(ses, LIST_VARIABLE, "#OK: LOCAL VARIABLE {%s} HAS BEEN SET TO {%s}.", arg1, str);
 	}
 	return ses;
 }
@@ -241,7 +241,7 @@ DO_COMMAND(do_cat)
 
 	if (*arg1 == 0 || *arg == 0)
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: CAT {<VARIABLE>} {<ARGUMENT>}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #CAT <VARIABLE> <ARGUMENT>");
 	}
 	else
 	{
@@ -280,7 +280,7 @@ DO_COMMAND(do_replace)
 
 	if (*arg1 == 0 || *arg2 == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #REPLACE {VARIABLE} {OLD TEXT} {NEW TEXT}");
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #REPLACE <VARIABLE> <OLD TEXT> <NEW TEXT>");
 
 		return ses;
 	}
@@ -320,6 +320,11 @@ DO_COMMAND(do_replace)
 			str_cat_printf(&str, "%s%s", pti, tmp);
 
 			pti = ptm;
+
+			if (arg2[0] == '\\' && arg2[1] == 'A')
+			{
+				break;
+			}
 		}
 		while (tintin_regexp(ses, NULL, pti, arg2, 0, REGEX_FLAG_CMD));
 
@@ -331,6 +336,19 @@ DO_COMMAND(do_replace)
 	return ses;
 }
 
+char *get_variable_def(struct session *ses, char *var, char *def)
+{
+	struct listnode *node;
+
+	node = search_nest_node_ses(ses, var);
+
+	if (node)
+	{
+		return node->arg2;
+	}
+	return def;
+}
+
 int valid_variable(struct session *ses, char *arg)
 {
 	if (*arg == 0)
@@ -350,7 +368,7 @@ int valid_variable(struct session *ses, char *arg)
 
 	if (is_digit(*arg))
 	{
-		show_error(ses, LIST_COMMAND, "\e[1;31m#WARNING: VALIDATE {%s}: VARIABLES SHOULD NOT START WITH A NUMBER.", arg);
+		show_error(ses, LIST_COMMAND, "#WARNING: VALIDATE {%s}: VARIABLES SHOULD NOT START WITH A NUMBER.", arg);
 	}
 
 	return TRUE;
@@ -379,7 +397,7 @@ void stringtobase(char *str, char *base)
 			break;
 
 		default:
-			tintin_printf2(gtd->ses, "#FORMAT: Unknown base conversion {%s}.", base);
+			tintin_printf2(gtd->ses, "#FORMAT: UNKNOWN BASE CONVERSION {%s}.", base);
 			break;
 	}
 	free(buf);
@@ -407,7 +425,7 @@ void basetostring(char *str, char *base)
 			break;
 
 		default:
-			tintin_printf2(gtd->ses, "#FORMAT: Unknown base conversion {%s}.", base);
+			tintin_printf2(gtd->ses, "#FORMAT: UNKNOWN BASE CONVERSION {%s}.", base);
 			break;
 	}
 	pop_call();
@@ -433,7 +451,7 @@ void stringtobasez(char *str, char *base)
 			break;
 
 		default:
-			tintin_printf2(gtd->ses, "#FORMAT: Unknown base conversion {%s}.", base);
+			tintin_printf2(gtd->ses, "#FORMAT: UNKNOWN BASE CONVERSION {%s}.", base);
 			break;
 	}
 	free(buf);
@@ -461,7 +479,7 @@ void basetostringz(char *str, char *base)
 			break;
 
 		default:
-			tintin_printf2(gtd->ses, "#FORMAT: Unknown base conversion {%s}.", base);
+			tintin_printf2(gtd->ses, "#FORMAT: UNKNOWN BASE CONVERSION {%s}.", base);
 			break;
 	}
 	pop_call();
@@ -1645,7 +1663,7 @@ DO_COMMAND(do_format)
 
 	if (*argvar == 0)
 	{
-		show_error(ses, LIST_VARIABLE, "#SYNTAX: #format {variable} {format} {arg1} {arg2}");
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #FORMAT <VARIABLE> <FORMAT> [ARG1] [ARG2] .. [ARG29] [ARG30]");
 
 		return ses;
 	}

Some files were not shown because too many files changed in this diff