Scandum 3 роки тому
батько
коміт
503f80e9ec
49 змінених файлів з 1985 додано та 885 видалено
  1. 23 28
      TODO
  2. 251 71
      docs/help.html
  3. 11 10
      docs/syntax.txt
  4. 86 13
      mods/igr.mods
  5. 15 51
      src/banner.c
  6. 88 20
      src/buffer.c
  7. 2 3
      src/chat.c
  8. 0 1
      src/command.c
  9. 12 14
      src/config.c
  10. 2 9
      src/cursor.c
  11. 3 3
      src/daemon.c
  12. 48 2
      src/data.c
  13. 135 57
      src/draw.c
  14. 1 1
      src/files.c
  15. 2 2
      src/gui.h
  16. 249 69
      src/help.c
  17. 8 0
      src/history.c
  18. 11 1
      src/input.c
  19. 138 64
      src/list.c
  20. 1 1
      src/log.c
  21. 17 6
      src/main.c
  22. 292 224
      src/mapper.c
  23. 11 20
      src/math.c
  24. 11 0
      src/memory.c
  25. 15 1
      src/misc.c
  26. 144 30
      src/nest.c
  27. 2 0
      src/net.c
  28. 3 3
      src/parse.c
  29. 1 1
      src/path.c
  30. 5 5
      src/port.c
  31. 24 0
      src/regex.c
  32. 4 1
      src/scan.c
  33. 3 3
      src/screen.c
  34. 23 18
      src/session.c
  35. 39 30
      src/show.c
  36. 1 1
      src/split.c
  37. 3 3
      src/string.c
  38. 38 39
      src/substitute.c
  39. 18 19
      src/tables.c
  40. 52 19
      src/telopt_client.c
  41. 15 1
      src/terminal.c
  42. 20 6
      src/tintin.h
  43. 21 4
      src/tokenize.c
  44. 3 1
      src/trigger.c
  45. 7 2
      src/update.c
  46. 0 1
      src/utf8.c
  47. 1 1
      src/utils.c
  48. 122 24
      src/variable.c
  49. 4 2
      src/vt102.c

+ 23 - 28
TODO

@@ -1,9 +1,23 @@
-  - look into large variable handling, foreach, once again
+- make #draw talign+balign do a vertical calign
+             lalign+ralign do a horizontal calign
+
+- make -g mode not write to disk
+
+- look into #draw boxed map with unicode mode.
+
+- Display room symbol in #map info
+- Add nohup mode
+
+ne room has #map set roomsymbol {<faa>/},
+e room has #map set roomsymbol {<faa>------}
+se room has #map set roomsymbol {<faa>\\}
 
 
 ----------------
 ----------------
-  - GMCP: MG.room.info { "exits": [ ] } gets parsed into {exits} {{1}{}}
+  - add shadow session support with access to all events.
 
 
-  - #map info save option
+  - set_line_screen debug: col = -5 (64) from draw_text(%p,%d,%p,%p,%p)
+
+  - regex101 like regex tester
 
 
   - check: #var bla { x};#draw scroll box 1 1 3 40 $bla
   - check: #var bla { x};#draw scroll box 1 1 3 40 $bla
 
 
@@ -11,20 +25,12 @@
 
 
   - input spell checking, #cursor display ?
   - input spell checking, #cursor display ?
 
 
-  - cp949toutf8 is missing some characters
-
   - look into named actions as a 4th argument
   - look into named actions as a 4th argument
 
 
-  - look into adding #line quiet support to #buffer
-
-  - pruned tile handling
-
   - Get discworld / aardwolf mxp to work for @sentix
   - Get discworld / aardwolf mxp to work for @sentix
 
 
   - look into default input color
   - look into default input color
 
 
-  - Add RECEIVED INPUT CHARACTER event / filter mouse input sequences.
-
   - add ctrl-r support for scrollback
   - add ctrl-r support for scrollback
 
 
   - Add #event {SESSION CONNECTED} {5.1} option.
   - Add #event {SESSION CONNECTED} {5.1} option.
@@ -61,7 +67,9 @@
 
 
   - Add #line gag 2 +2 -2 support.
   - Add #line gag 2 +2 -2 support.
 
 
-  - Work on VT2020 protocol (mouse click)
+  - VT2020
+
+  - mouse enter/leave events on move. hoover/long-press.
 
 
   - $var[%*][%*] support.
   - $var[%*][%*] support.
 
 
@@ -69,14 +77,14 @@
 
 
   - tab completion on directory structure.
   - tab completion on directory structure.
 
 
+  - 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
   - pancake mode to display rooms at all levels and annotations
   - Store the map filename to differentiate between maps.
   - Store the map filename to differentiate between maps.
   - #map list {<exits>} breaks on rooms that have e mapped to eu.
   - #map list {<exits>} breaks on rooms that have e mapped to eu.
   - finish landmarks
   - finish landmarks
   - map sandbox mode support (flags to disable saving?)
   - map sandbox mode support (flags to disable saving?)
   - add ghosting to fix #map flag nofollow exit cmd issues?
   - add ghosting to fix #map flag nofollow exit cmd issues?
-  - Request: aura around the character position. Something similar to terrain dense narrow. Could improve visibility of the character position on big resolutions. Furthermor config to allow modification of the character terrain. 
-  - vt map doesn't utilize all available space.
   - Request: 'stop' #map center command, so i could see how the 'user' moves, rather map moves around him? 
   - Request: 'stop' #map center command, so i could see how the 'user' moves, rather map moves around him? 
   - #map uninsert <vnum>
   - #map uninsert <vnum>
   - make map spacing easier
   - make map spacing easier
@@ -92,8 +100,6 @@
     #draw button
     #draw button
     #draw titanic
     #draw titanic
 
 
-  - proper vt100 skip detection for improper color codes.
-
   - Make { match both a { and \x7B as it's an annoying issue to debug.
   - Make { match both a { and \x7B as it's an annoying issue to debug.
 
 
   - Add VT100 filter to make outside sources respect terminal size constraints, also needed to run bash in vsplit mode.
   - Add VT100 filter to make outside sources respect terminal size constraints, also needed to run bash in vsplit mode.
@@ -245,23 +251,14 @@
 
 
   - add option to show party members on the map
   - add option to show party members on the map
 
 
-  - add option to add a delay to each exit for #map run
-
   - map where command that shows relative coordinates.
   - map where command that shows relative coordinates.
 
 
-  - Room creation event
-
   - Add better table support for data fields.
   - Add better table support for data fields.
 
 
   - auto adjust max vnum size of mapper.
   - auto adjust max vnum size of mapper.
 
 
   - global flag to only show the same area with the mapper.
   - global flag to only show the same area with the mapper.
 
 
-  - map color setting for hidden exits.
-
-  - Create global exits, (aka portals) like recall, that can be used
-    with #map run.
-
   - Add something to indicate a non standard exit on the ASCII mapper.
   - Add something to indicate a non standard exit on the ASCII mapper.
 
 
   - Display hidden exits differently.
   - Display hidden exits differently.
@@ -278,7 +275,7 @@
 
 
 * STUFF FOR A RAINY DAY
 * STUFF FOR A RAINY DAY
 
 
-- Fix up ipv6 support in chat.
+- Fix up IPv6 support in port/chat.
 
 
 - Look into packet defragmentation for chat.
 - Look into packet defragmentation for chat.
 
 
@@ -297,8 +294,6 @@
 
 
 - Keep last input visible with repeat enter enabled.
 - Keep last input visible with repeat enter enabled.
 
 
-- would be nice to have "#map list" show the variable being filtered on, instead of defaulting to roomname ie, I'm currently writing a script to save "#map list {roomnote} {{\w+}}" into a variable, then map get roomnote for each item in the list and display it...would be nice if when listing for a roomnote, it showed the roomnote
-
 -------------------------------------------------------------------------------
 -------------------------------------------------------------------------------
 
 
 * STUFF THAT WON'T BE IMPLEMENTED
 * STUFF THAT WON'T BE IMPLEMENTED

+ 251 - 71
docs/help.html

@@ -33,9 +33,9 @@ a:active {color:#b06}
  <a href='#RUN'>            RUN</a> <a href='#SCAN'>           SCAN</a> <a href='#SCREEN'>         SCREEN</a> <a href='#SCREEN READER'>  SCREEN READER</a> <a href='#SCRIPT'>         SCRIPT</a>
  <a href='#RUN'>            RUN</a> <a href='#SCAN'>           SCAN</a> <a href='#SCREEN'>         SCREEN</a> <a href='#SCREEN READER'>  SCREEN READER</a> <a href='#SCRIPT'>         SCRIPT</a>
  <a href='#SEND'>           SEND</a> <a href='#SESSION'>        SESSION</a> <a href='#SESSIONNAME'>    SESSIONNAME</a> <a href='#SHOWME'>         SHOWME</a> <a href='#SNOOP'>          SNOOP</a>
  <a href='#SEND'>           SEND</a> <a href='#SESSION'>        SESSION</a> <a href='#SESSIONNAME'>    SESSIONNAME</a> <a href='#SHOWME'>         SHOWME</a> <a href='#SNOOP'>          SNOOP</a>
  <a href='#SPEEDWALK'>      SPEEDWALK</a> <a href='#SPLIT'>          SPLIT</a> <a href='#SSL'>            SSL</a> <a href='#STATEMENTS'>     STATEMENTS</a> <a href='#SUBSTITUTE'>     SUBSTITUTE</a>
  <a href='#SPEEDWALK'>      SPEEDWALK</a> <a href='#SPLIT'>          SPLIT</a> <a href='#SSL'>            SSL</a> <a href='#STATEMENTS'>     STATEMENTS</a> <a href='#SUBSTITUTE'>     SUBSTITUTE</a>
- <a href='#SUSPEND'>        SUSPEND</a> <a href='#SWITCH'>         SWITCH</a> <a href='#SYSTEM'>         SYSTEM</a> <a href='#TAB'>            TAB</a> <a href='#TEXTIN'>         TEXTIN</a>
- <a href='#TICKER'>         TICKER</a> <a href='#TIME'>           TIME</a> <a href='#VARIABLE'>       VARIABLE</a> <a href='#WHILE'>          WHILE</a> <a href='#WRITE'>          WRITE</a>
- <a href='#ZAP'>            ZAP</a>
+ <a href='#SUBSTITUTIONS'>  SUBSTITUTIONS</a> <a href='#SUSPEND'>        SUSPEND</a> <a href='#SWITCH'>         SWITCH</a> <a href='#SYSTEM'>         SYSTEM</a> <a href='#TAB'>            TAB</a>
+ <a href='#TEXTIN'>         TEXTIN</a> <a href='#TICKER'>         TICKER</a> <a href='#TIME'>           TIME</a> <a href='#VARIABLE'>       VARIABLE</a> <a href='#WHILE'>          WHILE</a>
+ <a href='#WRITE'>          WRITE</a> <a href='#ZAP'>            ZAP</a>
 
 
 
 
 <a name='ACTION'></a>
 <a name='ACTION'></a>
@@ -49,9 +49,6 @@ a:active {color:#b06}
          variables are substituted from the message and can be used in the
          variables are substituted from the message and can be used in the
          command part of the action.
          command part of the action.
 
 
-         The priority part is optional and determines the priority of the
-         action, it defaults to 5.
-
          If the message starts with a ~ color codes must be matched. You can
          If the message starts with a ~ color codes must be matched. You can
          enable #config {convert meta} on to display meta characters.
          enable #config {convert meta} on to display meta characters.
 
 
@@ -59,19 +56,16 @@ a:active {color:#b06}
 
 
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #action {%1 tells you '%2'} {tell %1 I'm afk.}
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #action {%1 tells you '%2'} {tell %1 I'm afk.}
 
 
-         Actions can be triggered by the show command and certain system
-         messages.
-
          Actions can be triggered by the #show command. If you don't want a
          Actions can be triggered by the #show command. If you don't want a
          #show to get triggered use: #line ignore #show {text}
          #show to get triggered use: #line ignore #show {text}
 
 
          Actions are ordered alphabetically and only one action can trigger at
          Actions are ordered alphabetically and only one action can trigger at
          a time. To change the order you can assign a priority, which defaults
          a time. To change the order you can assign a priority, which defaults
          to 5, with a lower number indicating a higher priority. The priority
          to 5, with a lower number indicating a higher priority. The priority
-         can be a floating point number.
+         can be a floating point number and should be between 1 and 9.
 
 
-         To remove action with %* as the message, use #unaction {%%*} or
-         #unaction {%*}. Alternatively you could wrap the action inside a
+         To remove an action with %* as the message, use #unaction {%%*} or
+         #unaction {&bsol;%*}. Alternatively you could wrap the action inside a
          class, and kill that class when you no longer need the action.
          class, and kill that class when you no longer need the action.
 
 
 </span><span style='color:#FFF'>Comment</span><span style='color:#AAA'>: You can remove an action with the #unaction command.
 </span><span style='color:#FFF'>Comment</span><span style='color:#AAA'>: You can remove an action with the #unaction command.
@@ -112,7 +106,7 @@ a:active {color:#b06}
          can be a floating point number.
          can be a floating point number.
 
 
          To remove an alias with %* as the name, use #unalias {%%*} or #unalias
          To remove an alias with %* as the name, use #unalias {%%*} or #unalias
-         {%*}. Alternatively you can wrap the alias inside a class, and kill
+         {&bsol;%*}. Alternatively you can wrap the alias inside a class, and kill
          that class when you no longer need the alias.
          that class when you no longer need the alias.
 
 
          For more information on pattern matching see the section on PCRE.
          For more information on pattern matching see the section on PCRE.
@@ -192,34 +186,45 @@ a:active {color:#b06}
          The buffer command has various options to manipulate your scrollback
          The buffer command has various options to manipulate your scrollback
          buffer.
          buffer.
 
 
-         </span><span style='color:#FFF'>#buffer {home}
-</span><span style='color:#AAA'>
-         Moves you to the top of your scrollback buffer and displays the page.
-         Enables scroll lock mode. Most useful when used in a #macro.
+         The size of the scrollback buffer can be configured using #config
+         buffer_size &lt;size&gt;. The size must be either 100, 1000, 10000, 100000
+         or 1000000 lines.
 
 
-         </span><span style='color:#FFF'>#buffer {up} [lines]
+         While scrolling through the scrollback buffer incoming text is not
+         displayed, this can be disabled using #config scroll_lock off. The
+         scroll lock is automatically disabled when manual input is received,
+         subsequently #buffer up and down only work properly when used in a
+         macro or mouse event.
+
+         </span><span style='color:#FFF'>#buffer {clear} {[lower bound]} {[upper bound]}
 </span><span style='color:#AAA'>
 </span><span style='color:#AAA'>
-         Moves your scrollback buffer up one page and displays the page.
-         Enables scroll lock mode. Most useful when used in a #macro. You
-         can use #buffer {up} {1} to move the scrollback buffer up 1 line.
+         Without an argument this will clear the entire scrollback buffer.
+         Otherwise it will clear the given range.
+
+         Positive numbers are measured from the start of the scrollback buffer,
+         negative numbers from the end.
 
 
          </span><span style='color:#FFF'>#buffer {down} [lines]
          </span><span style='color:#FFF'>#buffer {down} [lines]
 </span><span style='color:#AAA'>
 </span><span style='color:#AAA'>
-         Moves your scrollback buffer down one page and displays the page.
-         Enables scroll lock mode unless at the end. Most useful when used in
-         a #macro.
+         Moves your scrollback buffer down one page and displays the page. If
+         a line number is provided it will scroll down the given number of
+         lines.
 
 
          </span><span style='color:#FFF'>#buffer {end}
          </span><span style='color:#FFF'>#buffer {end}
 </span><span style='color:#AAA'>
 </span><span style='color:#AAA'>
          Moves you to the end of your scrollback buffer and displays the page.
          Moves you to the end of your scrollback buffer and displays the page.
          Disables scroll lock mode. Most useful when used in a #macro.
          Disables scroll lock mode. Most useful when used in a #macro.
 
 
-         </span><span style='color:#FFF'>#buffer {find} {[number]} {&lt;string&gt;}
+         </span><span style='color:#FFF'>#buffer {find} {[number]} {&lt;string&gt;} {[variable]}
 </span><span style='color:#AAA'>
 </span><span style='color:#AAA'>
          Moves the buffer to the given string which can contain a regular
          Moves the buffer to the given string which can contain a regular
          expression. Optionally you can provide the number of matches to skip,
          expression. Optionally you can provide the number of matches to skip,
          allowing you to jump further back in the buffer.
          allowing you to jump further back in the buffer.
 
 
+         A positive number searches from the start of the buffer, a negative
+         number from the end. If you provide a variable the location will be
+         stored and no jump takes place.
+
          </span><span style='color:#FFF'>#buffer {get} {&lt;variable&gt;} {&lt;lower bound&gt;} {[upper bound]}
          </span><span style='color:#FFF'>#buffer {get} {&lt;variable&gt;} {&lt;lower bound&gt;} {[upper bound]}
 </span><span style='color:#AAA'>
 </span><span style='color:#AAA'>
          Allows you to store one or several lines from your scrollback buffer
          Allows you to store one or several lines from your scrollback buffer
@@ -228,10 +233,23 @@ a:active {color:#b06}
          omitted the given line is stored as a standard variable. If an upper
          omitted the given line is stored as a standard variable. If an upper
          bound is given the lines between the two bounds are stored as a list.
          bound is given the lines between the two bounds are stored as a list.
 
 
+         Positive numbers are measured from the start of the scrollback buffer,
+         negative numbers from the end.
+
+         </span><span style='color:#FFF'>#buffer {home}
+</span><span style='color:#AAA'>
+         Moves you to the top of your scrollback buffer and displays the page.
+         Enables scroll lock mode. Most useful when used in a #macro.
+
          </span><span style='color:#FFF'>#buffer {info} {[save]} {[variable]}
          </span><span style='color:#FFF'>#buffer {info} {[save]} {[variable]}
 </span><span style='color:#AAA'>
 </span><span style='color:#AAA'>
          Display buffer info, optionally save the data to a variable.
          Display buffer info, optionally save the data to a variable.
 
 
+         </span><span style='color:#FFF'>#buffer {jump} {&lt;location&gt;}
+</span><span style='color:#AAA'>
+         Moves the buffer to the given location. A positive number jumps from
+         the start of the buffer, a negative number from the end.
+
          </span><span style='color:#FFF'>#buffer {lock} {on|off}
          </span><span style='color:#FFF'>#buffer {lock} {on|off}
 </span><span style='color:#AAA'>
 </span><span style='color:#AAA'>
          Toggles the lock on the scrollback buffer. When locked, newly incoming
          Toggles the lock on the scrollback buffer. When locked, newly incoming
@@ -239,6 +257,17 @@ a:active {color:#b06}
          several buffer commands will re-enable the lock. When unlocking it'll
          several buffer commands will re-enable the lock. When unlocking it'll
          move you to the end of your scrollback buffer and display the page.
          move you to the end of your scrollback buffer and display the page.
 
 
+         </span><span style='color:#FFF'>#buffer {refresh}
+</span><span style='color:#AAA'>
+         Marks the buffer as needing to be refreshed, only useful while in
+         vertical split mode.
+
+         </span><span style='color:#FFF'>#buffer {up} [lines]
+</span><span style='color:#AAA'>
+         Moves your scrollback buffer up one page and displays the page.
+         Enables scroll lock mode. Most useful when used in a #macro. You
+         can use #buffer {up} {1} to move the scrollback buffer up 1 line.
+
          </span><span style='color:#FFF'>#buffer {write} {&lt;filename&gt;}
          </span><span style='color:#FFF'>#buffer {write} {&lt;filename&gt;}
 </span><span style='color:#AAA'>
 </span><span style='color:#AAA'>
          Writes the scrollback buffer to the given file.
          Writes the scrollback buffer to the given file.
@@ -463,7 +492,6 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
          </span><span style='color:#AAA'>  Will store the size of the class in a variable.
          </span><span style='color:#AAA'>  Will store the size of the class in a variable.
          </span><span style='color:#FFF'>#class {&lt;name&gt;} {write} {&lt;filename&gt;}
          </span><span style='color:#FFF'>#class {&lt;name&gt;} {write} {&lt;filename&gt;}
          </span><span style='color:#AAA'>  Will write all triggers of the given class to file.
          </span><span style='color:#AAA'>  Will write all triggers of the given class to file.
-         The {kill} option will delete all triggers of the given class.
 
 
          Keep in mind that the kill and read option are very fast allowing
          Keep in mind that the kill and read option are very fast allowing
          them to be used to enable and disable classes.
          them to be used to enable and disable classes.
@@ -539,13 +567,41 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
 
 
 </span><span style='color:#AAA'>
 </span><span style='color:#AAA'>
          When the 0,0 coordinate is in the upper left corner TinTin++ uses
          When the 0,0 coordinate is in the upper left corner TinTin++ uses
-         a y,x / rows,cols notation. When the 0,0 coordinate is in the
-         bottom left corner tintin uses a x,y / cols/rows notation.
+         a y,x / row,col notation, starting at 1,1. Subsequently -1,-1
+         will indicate the bottom right corner. This type of argument is
+         used by the #showme command.
 
 
-         When a square is defined this is done by specifying the upper left
-         and bottom right corner of the square using four coordinates.
+         When the 0,0 coordinate is in the bottom left corner tintin uses
+         a standard x,y notation. This type of argument is used by the
+         #map jump command.
 
 
-         The vast majority of tintin commands use row,col notation.
+         The vast majority of tintin commands use y,x / row,col notation,
+         primarily because that is the notation used by the VT100 standard
+         used for terminal emulation.
+
+         </span><span style='color:#5F5'>Squares
+</span><span style='color:#AAA'>
+         A square argument takes 2 coordinates. The first coordinate defines
+         the upper left corner, the last coordinate defines the bottom
+         right corner. The upper left corner of the terminal is defined as
+         1,1 and the bottom right corner as -1,-1. This type of argument is
+         used by #draw, #button and #map offset.
+
+         </span><span style='color:#5F5'>Panes
+</span><span style='color:#AAA'>
+         A pane argument takes 4 size values, which are: top pane, bottom
+         pane, left pane, right pane. When a negative value is provided the
+         size is the maximum size, minus the value. This type of argument
+         is used by the #split command.
+
+         </span><span style='color:#5F5'>Ranges
+</span><span style='color:#AAA'>
+         A range argument takes 2 values known as the upper bound and lower
+         bound. The upper bound (first value) defines the start of the
+         range, the lower bound (second value) the end. The first index of
+         a range is defined as 1. When a negative value is provides the last
+         index is defined as -1. This type of argument is used by #buffer
+         and #variable.
 
 
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#CHARACTERS'>characters</a>, <a href='#COLORS'>colors</a>, <a href='#ESCAPE'>escape</a>, <a href='#MATHEMATICS'>mathematics</a> and <a href='#PCRE'>pcre</a>.
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#CHARACTERS'>characters</a>, <a href='#COLORS'>colors</a>, <a href='#ESCAPE'>escape</a>, <a href='#MATHEMATICS'>mathematics</a> and <a href='#PCRE'>pcre</a>.
 <a name='CONFIG'></a>
 <a name='CONFIG'></a>
@@ -666,6 +722,9 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
          </span><span style='color:#FFF'>#daemon list [name]
          </span><span style='color:#FFF'>#daemon list [name]
          </span><span style='color:#AAA'>  List all daemons or daemons with matching name.
          </span><span style='color:#AAA'>  List all daemons or daemons with matching name.
 
 
+         You can launch tintin and attach the first daemonized instance using
+         tt++ -R. To attach a named instance use tt++ -R&lt;name&gt;.
+
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#SCRIPT'>script</a>, <a href='#SYSTEM'>system</a> and <a href='#RUN'>run</a>.
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#SCRIPT'>script</a>, <a href='#SYSTEM'>system</a> and <a href='#RUN'>run</a>.
 <a name='DEBUG'></a>
 <a name='DEBUG'></a>
 
 
@@ -1191,8 +1250,8 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
          </span><span style='color:#5F5'>SESSION EVENTS</span><span style='color:#AAA'>
          </span><span style='color:#5F5'>SESSION EVENTS</span><span style='color:#AAA'>
 
 
          SESSION ACTIVATED      %0 name
          SESSION ACTIVATED      %0 name
-         SESSION CONNECTED      %0 name %1 host %2 ip %3 port
-         SESSION CREATED        %0 name %1 host %2 ip %3 port
+         SESSION CONNECTED      %0 name %1 host %2 ip %3 port %4 file
+         SESSION CREATED        %0 name %1 host %2 ip %3 port %4 file
          SESSION DEACTIVATED    %0 name
          SESSION DEACTIVATED    %0 name
          SESSION DISCONNECTED   %0 name %1 host %2 ip %3 port
          SESSION DISCONNECTED   %0 name %1 host %2 ip %3 port
          SESSION TIMED OUT      %0 name %1 host %2 ip %3 port
          SESSION TIMED OUT      %0 name %1 host %2 ip %3 port
@@ -1370,7 +1429,7 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
 
 
 </span><span style='color:#0AA'>      ####################################################################
 </span><span style='color:#0AA'>      ####################################################################
       #</span><span style='color:#AAA'>                                                                  </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.12                     </span><span style='color:#0AA'>#
+      #</span><span style='color:#AAA'>                    T I N T I N + +   2.02.13b                    </span><span style='color:#0AA'>#
       #</span><span style='color:#AAA'>                                                                  </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'>      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'>#
       #</span><span style='color:#AAA'>                                                                  </span><span style='color:#0AA'>#
@@ -1585,7 +1644,7 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
 
 
          All commands can be separated with a ';'.
          All commands can be separated with a ';'.
 
 
-         n;l green;s;say Dan Dare is back! -- do these 4 commands
+         n;l dragon;s;say Dan Dare is back! -- do these 4 commands
          There are 3 ways ';'s can be overruled.
          There are 3 ways ';'s can be overruled.
 
 
          &bsol;say Hello ;) -- Lines starting with a '&bsol;' aren't parsed by TinTin++.
          &bsol;say Hello ;) -- Lines starting with a '&bsol;' aren't parsed by TinTin++.
@@ -1631,12 +1690,6 @@ Command</span><span style='color:#AAA'>: #alias </span><span style='color:#FFF'>
 
 
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #alias greet say Greetings, most honorable %1
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #alias greet say Greetings, most honorable %1
 
 
-         If there are no variables on the right-side of the alias definition,
-         any arguments following the aliases-command will be appended to the
-         command string.
-
-</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #alias ff cast 'fireball' -- 'ff bob' equals: cast 'fireball' bob
-
          If you want an alias to execute more commands, you must use braces.
          If you want an alias to execute more commands, you must use braces.
 
 
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #alias ws </span><span style='color:#FFF'>{</span><span style='color:#AAA'>wake;stand</span><span style='color:#FFF'>}</span><span style='color:#AAA'>
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #alias ws </span><span style='color:#FFF'>{</span><span style='color:#AAA'>wake;stand</span><span style='color:#FFF'>}</span><span style='color:#AAA'>
@@ -1651,7 +1704,7 @@ Command</span><span style='color:#AAA'>: #alias </span><span style='color:#FFF'>
 
 
          Or by using the send command.
          Or by using the send command.
 
 
-</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #send put %1 in %2
+</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #alias put #send put %1 in %2
 
 
 
 
 </span><span style='color:#5F5'>         Action
 </span><span style='color:#5F5'>         Action
@@ -1822,9 +1875,12 @@ Command</span><span style='color:#AAA'>: #alias </span><span style='color:#FFF'>
          triggers/variables associated with that list. With the SAVE option
          triggers/variables associated with that list. With the SAVE option
          this data is written to the info variable.
          this data is written to the info variable.
 
 
+         #info arguments will show matched trigger arguments.
+         #info big5toutf will show the big5 to utf8 translation table.
          #info cpu will show information about tintin's cpu usage.
          #info cpu will show information about tintin's cpu usage.
          #info environ will show the environment variables.
          #info environ will show the environment variables.
          #info input will show information about the input line.
          #info input will show information about the input line.
+         #info matches will show matched command arguments.
          #info mccp will show information about data compression.
          #info mccp will show information about data compression.
          #info memory will show information about the memory stack.
          #info memory will show information about the memory stack.
          #info stack will show the low level debugging stack.
          #info stack will show the low level debugging stack.
@@ -1977,16 +2033,17 @@ Terminal -&gt; Window Settings -&gt; Emulation.
 
 
          #list {var} {add} {item}               Add {item} to the list
          #list {var} {add} {item}               Add {item} to the list
          #list {var} {clear}                    Empty the given list
          #list {var} {clear}                    Empty the given list
-         #list {var} {collapse}                 Turn list into a variable
-         #list {var} {create} {item}            Create a list using {items}
-         #list {var} {delete} {index} {number}  Delete the item at {index},
-                                                the {number} is optional.
-         #list {var} {explode}                  Turn list into a character list
+         #list {var} {collapse} {separator}     Turn list into a variable
+         #list {var} {create} {items}           Create a list using {items}
+         #list {var} {delete} {index} {amount}  Delete the item at {index},
+                                                the {amount} is optional.
+         #list {var} {explode} {separator}      Turn variable into a list
          #list {var} {indexate}                 Index a list table for sorting
          #list {var} {indexate}                 Index a list table for sorting
          #list {var} {insert} {index} {string}  Insert {string} at given index
          #list {var} {insert} {index} {string}  Insert {string} at given index
          #list {var} {filter} {keep} {remove}   Filter with keep / remove regex
          #list {var} {filter} {keep} {remove}   Filter with keep / remove regex
          #list {var} {find} {regex} {variable}  Return the found index
          #list {var} {find} {regex} {variable}  Return the found index
          #list {var} {get} {index} {variable}   Copy an item to {variable}
          #list {var} {get} {index} {variable}   Copy an item to {variable}
+         #list {var} {numerate}                 Turn a table into a list
          #list {var} {order} {string}           Insert item in numerical order
          #list {var} {order} {string}           Insert item in numerical order
          #list {var} {shuffle}                  Shuffle the list
          #list {var} {shuffle}                  Shuffle the list
          #list {var} {set} {index} {string}     Change the item at {index}
          #list {var} {set} {index} {string}     Change the item at {index}
@@ -1995,7 +2052,7 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          #list {var} {sort} {string}            Insert item in alphabetic order
          #list {var} {sort} {string}            Insert item in alphabetic order
          #list {var} {tokenize} {string}        Create a character list
          #list {var} {tokenize} {string}        Create a character list
 
 
-         The index should be between 1 and the list's length. You can also give
+         The index should be between +1 and the list's size. You can also give
          a negative value, in which case -1 equals the last item in the list, -2
          a negative value, in which case -1 equals the last item in the list, -2
          the second last, etc.
          the second last, etc.
 
 
@@ -2005,10 +2062,11 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          The add and create options allow using multiple items, as well
          The add and create options allow using multiple items, as well
          as semicolon separated items.
          as semicolon separated items.
 
 
-         A length of 0 is returned for an empty or non existant list.
+         A size of 0 is returned for an empty or non-existent list. You can
+         directly access the size of a list using &amp;var[].
 
 
-         You can directly access elements in a list variable using &dollar;var[1],
-         &dollar;var[2], &dollar;var[-1], etc.
+         You can directly access elements in a list variable using &dollar;var[+1],
+         &dollar;var[+2], &dollar;var[-1], etc.
 
 
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#BREAK'>break</a>, <a href='#CONTINUE'>continue</a>, <a href='#FOREACH'>foreach</a>, <a href='#LOOP'>loop</a>, <a href='#PARSE'>parse</a>, <a href='#REPEAT'>repeat</a>, <a href='#RETURN'>return</a> and <a href='#WHILE'>while</a>.
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#BREAK'>break</a>, <a href='#CONTINUE'>continue</a>, <a href='#FOREACH'>foreach</a>, <a href='#LOOP'>loop</a>, <a href='#PARSE'>parse</a>, <a href='#REPEAT'>repeat</a>, <a href='#RETURN'>return</a> and <a href='#WHILE'>while</a>.
 <a name='LISTS'></a>
 <a name='LISTS'></a>
@@ -2223,6 +2281,47 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          If you want to get a better look at what goes on behind the scenes
          If you want to get a better look at what goes on behind the scenes
          while executing scripts you can use '#debug all on'. To stop seeing
          while executing scripts you can use '#debug all on'. To stop seeing
          debug information use '#debug all off'.
          debug information use '#debug all off'.
+</span><span style='color:#5F5'>
+         List Tables
+</span><span style='color:#AAA'>
+         List tables are also known as databases and the #list command has
+         several options to manipulate them.
+
+         For these options to work properly all tables need to have identical
+         keys. Here is an example list table.
+
+         #var {friendlist}
+         {
+             {1}{{name}{bob} {age}{54}}
+             {2}{{name}{bubba} {age}{21}}
+             {3}{{name}{pamela} {age}{36}}
+         }
+
+         To sort the list table by age you would use:
+
+         #list friendlist indexate age
+         #list friendlist order
+
+         To remove everyone whose name starts with a 'b' you would use:
+
+         #list friendlist indexate name
+         #list friendlist filter {} {b%*}
+
+         The filter option only supports regular expressions. To filter
+         using mathematics you would loop through the list backwards:
+
+         #loop &amp;friendlist[] 1 index
+         {
+             #if {&dollar;friendlist[+&dollar;index][age] &lt; 30}
+             {
+                 #list friendlist delete &dollar;index
+             }
+         }
+
+         To add an item to a list table there are two options:
+
+         #list friendlist add {{{name}{hobo} {age}{42}}}
+         #list friendlist insert -1 {{name}{hobo} {age}{42}}
 </span><span style='color:#5F5'>
 </span><span style='color:#5F5'>
          Optimization
          Optimization
 </span><span style='color:#AAA'>
 </span><span style='color:#AAA'>
@@ -2451,12 +2550,16 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          </span><span style='color:#AAA'>  searches for the given room name. If found the shortest path
          </span><span style='color:#AAA'>  searches for the given room name. If found the shortest path
            from your current location to the destination is calculated.
            from your current location to the destination is calculated.
            The route is stored in #path and can subsequently be used with
            The route is stored in #path and can subsequently be used with
-           the various #path commands. If &lt;exits&gt; is provided all exits
-           must be matched, if &lt;roomdesc&gt;, &lt;roomarea&gt; or &lt;roomnote&gt; or
-           &lt;roomterrain&gt; or &lt;roomflag&gt; is provided these are matched as
-           well against the room to be found.
-           These options are also available to the at, delete, goto
-           link, list and run commands.
+           the various #path commands. If #map flag nofollow is set it
+           will store the exit commands instead of the exit names.
+
+           If &lt;exits&gt; is provided all exits must be matched, if
+           &lt;roomdesc&gt;, &lt;roomarea&gt; or &lt;roomnote&gt; or &lt;roomterrain&gt; or
+           &lt;roomflag&gt; is provided these are matched as well against the
+           room to be found.
+
+           These search options are also available for the at, delete,
+           goto, link, list and run commands.
 
 
          </span><span style='color:#FFF'>#map flag asciigraphics
          </span><span style='color:#FFF'>#map flag asciigraphics
          </span><span style='color:#AAA'>  Takes up more space but draws a more detailed
          </span><span style='color:#AAA'>  Takes up more space but draws a more detailed
@@ -2468,7 +2571,9 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          </span><span style='color:#FFF'>#map flag nofollow
          </span><span style='color:#FFF'>#map flag nofollow
          </span><span style='color:#AAA'>  When you enter movement commands the map will no longer
          </span><span style='color:#AAA'>  When you enter movement commands the map will no longer
            automatically follow along. Useful for MSDP and GMCP
            automatically follow along. Useful for MSDP and GMCP
-           automapping scripts.
+           automapping scripts. When you use #map find in nofollow
+           mode it will store the exit command instead of the exit
+           name into the path.
 
 
          </span><span style='color:#FFF'>#map flag static
          </span><span style='color:#FFF'>#map flag static
          </span><span style='color:#AAA'>  Will make the map static so new rooms are no longer
          </span><span style='color:#AAA'>  Will make the map static so new rooms are no longer
@@ -2506,8 +2611,9 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          </span><span style='color:#AAA'>  Takes you to the given room name, if you provide exits those
          </span><span style='color:#AAA'>  Takes you to the given room name, if you provide exits those
            must match.
            must match.
 
 
-         </span><span style='color:#FFF'>#map info
-         </span><span style='color:#AAA'>  Gives information about the map and room you are in.
+         </span><span style='color:#FFF'>#map info [save]
+         </span><span style='color:#AAA'>  Gives information about the map and room you are in. If the save
+           argument is given the map data is saved to the info[map] variable.
 
 
          </span><span style='color:#FFF'>#map insert &lt;direction&gt; [roomflag]
          </span><span style='color:#FFF'>#map insert &lt;direction&gt; [roomflag]
          </span><span style='color:#AAA'>  Insert a room in the given direction. Most useful for inserting
          </span><span style='color:#AAA'>  Insert a room in the given direction. Most useful for inserting
@@ -2663,7 +2769,7 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          </span><span style='color:#FFF'>#map undo
          </span><span style='color:#FFF'>#map undo
          </span><span style='color:#AAA'>  Will undo your last move. If this created a room or a link
          </span><span style='color:#AAA'>  Will undo your last move. If this created a room or a link
            they will be deleted, otherwise you'll simply move back a
            they will be deleted, otherwise you'll simply move back a
-           room. Useful if you walked into a non existant direction.
+           room. Useful if you walked into a non-existent direction.
 
 
          </span><span style='color:#FFF'>#map uninsert &lt;direction&gt;
          </span><span style='color:#FFF'>#map uninsert &lt;direction&gt;
          </span><span style='color:#AAA'>  Exact opposite of the insert command.
          </span><span style='color:#AAA'>  Exact opposite of the insert command.
@@ -2829,7 +2935,7 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          -               3            integer subtraction
          -               3            integer subtraction
          &lt;&lt;              4            bitwise shift
          &lt;&lt;              4            bitwise shift
          &gt;&gt;              4            bitwise shift
          &gt;&gt;              4            bitwise shift
-         ..              4            bitwise ellipsis
+         ..              4            integer range
          &gt;               5            logical greater than
          &gt;               5            logical greater than
          &gt;=              5            logical greater than or equal
          &gt;=              5            logical greater than or equal
          &lt;               5            logical less than
          &lt;               5            logical less than
@@ -3189,13 +3295,13 @@ mind that { } is replaced with ( ) automatically unless %!{ } is used.
          If you use %1 in an action to perform a match the matched string is
          If you use %1 in an action to perform a match the matched string is
          stored in the %1 variable which can be used in the action body.
          stored in the %1 variable which can be used in the action body.
 
 
-Example: %1 says 'Tickle me'} {tickle %1}
+Example: #act {%1 says 'Tickle me'} {tickle %1}
 
 
          If you use %2 the match is stored in %2, etc. If you use an unnumbered
          If you use %2 the match is stored in %2, etc. If you use an unnumbered
          match like %* or %S the match is stored at the last used index
          match like %* or %S the match is stored at the last used index
          incremented by one.
          incremented by one.
 
 
-Example: %3 says '%*'} {#if {&quot;%4&quot; == &quot;Tickle me&quot;} {tickle %3}}
+Example: #act {%3 says '%*'} {#if {&quot;%4&quot; == &quot;Tickle me&quot;} {tickle %3}}
 
 
          The maximum variable index is 99. If you begin an action with %* the
          The maximum variable index is 99. If you begin an action with %* the
          match is stored in %1. You should never use %0 in the trigger part of
          match is stored in %1. You should never use %0 in the trigger part of
@@ -3220,7 +3326,7 @@ Example: #act {%* raises {his|her|its} eyebrows.} {say 42..}
 
 
          You can group alternatives and ranges within a PCRE using brackets.
          You can group alternatives and ranges within a PCRE using brackets.
 
 
-Example: #act {%* says 'Who is number {[1-9]}} {say &dollar;number[%2] is number %2}
+Example: #act {%* says 'Who is number {[1-9]}?} {say &dollar;number[%2] is number %2}
 
 
          The example only triggers if someone provides a number between 1 and
          The example only triggers if someone provides a number between 1 and
          9. Any other character will cause the action to not trigger.
          9. Any other character will cause the action to not trigger.
@@ -3245,7 +3351,7 @@ Example: #act {%* says 'Set password to {[^0-9]*}&dollar;} {say The password mus
     {n,} repeat at least n times, n must be a number.
     {n,} repeat at least n times, n must be a number.
    {n,o} repeat between n and o times, n and o must be a number.
    {n,o} repeat between n and o times, n and o must be a number.
 
 
-Example: #act {%* says 'Who is number {[1-9][0-9]{0,2}} {Say &dollar;number[%2] is
+Example: #act {%* says 'Who is number {[1-9][0-9]{0,2}}?} {Say &dollar;number[%2] is
            number %2}
            number %2}
 
 
          The example only triggers if someone provides a number between 1 and
          The example only triggers if someone provides a number between 1 and
@@ -3737,10 +3843,10 @@ easiest way to accomplish that.
 
 
          Starts a telnet session with the given name, host, port, and optional
          Starts a telnet session with the given name, host, port, and optional
          file name. The name can be anything you want, except the name of an
          file name. The name can be anything you want, except the name of an
-         already existant session, a number, or the keywords '+' and '-'.
+         already existing session, a number, or the keywords '+' and '-'.
 
 
-         If a file name is given the file is only read if the session succesfully
-         connects.
+         If a file name is given the file is only read if the session
+         succesfully connects.
 
 
          Without an argument #session shows the currently defined sessions.
          Without an argument #session shows the currently defined sessions.
 
 
@@ -3992,6 +4098,78 @@ Related</span><span style='color:#AAA'>: <a href='#ALL'>all</a>, <a href='#PORT'
 </span><span style='color:#FFF'>Comment</span><span style='color:#AAA'>: You can remove a substitution with the #unsubstitute command.
 </span><span style='color:#FFF'>Comment</span><span style='color:#AAA'>: You can remove a substitution with the #unsubstitute command.
 
 
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#ACTION'>action</a>, <a href='#GAG'>gag</a>, <a href='#HIGHLIGHT'>highlight</a> and <a href='#PROMPT'>prompt</a>.
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#ACTION'>action</a>, <a href='#GAG'>gag</a>, <a href='#HIGHLIGHT'>highlight</a> and <a href='#PROMPT'>prompt</a>.
+<a name='SUBSTITUTIONS'></a>
+
+</span><span style='color:#5F5'>         SUBSTITUTIONS
+
+</span><span style='color:#AAA'>          TinTin++ will perform various types of substitions as detailed below.
+
+</span><span style='color:#5F5'>          Variables
+
+</span><span style='color:#FFF'>&dollar; &amp; * @</span><span style='color:#AAA'>   All variable and function names must begin with an alphabetic
+          character, followed by any combination of alphanumeric characters and
+          underscores.
+
+</span><span style='color:#FFF'>&dollar;</span><span style='color:#AAA'>         The dollar sign is used to retrieve the value of a variable.
+
+</span><span style='color:#FFF'>&amp;</span><span style='color:#AAA'>         The ampersand sign is used to retrieve the index of a variable.
+
+</span><span style='color:#FFF'>*</span><span style='color:#AAA'>         The astrix sign is used to retrieve the name of a variable.
+
+</span><span style='color:#FFF'>@</span><span style='color:#AAA'>         The at sign is used for functions.
+
+</span><span style='color:#FFF'>[ ]</span><span style='color:#AAA'>       Brackets are used for nested variables which function as an
+          associative array. Associative arrays are also known as tables and
+          maps. Regex can be used within brackets to match multiple variables.
+
+</span><span style='color:#FFF'>+ -</span><span style='color:#AAA'>       The plus and minus signs are used to access variables by their index,
+          with the first variable having index +1, and the last variable
+          having index -1. Variables are ordered alphanumerically.
+
+          All variables and functions can be escaped by doubling the sign,
+          like &dollar;&dollar;variable_name or @@function_name. To escape a variable
+          twice use &dollar;&dollar;&dollar;var_name. One escape is removed each time tintin
+          needs to substitute a variable or function.
+
+</span><span style='color:#5F5'>          Arguments
+
+</span><span style='color:#FFF'>%0 - %99</span><span style='color:#AAA'>  The percent sign followed by a number is used for arguments by the
+          following triggers:
+
+          alias, action, button, event, function, prompt, and substitute.
+
+</span><span style='color:#FFF'>&amp;0 - &amp;99</span><span style='color:#AAA'>  The ampersand sign followed by a number is used for arguments in the
+          regex and replace commands.
+
+          All trigger and command arguments can be escaped by doubling the
+          sign like %%1 or &amp;&amp;1. One escape is removed each time tintin
+          substitutes trigger or command arguments. To escape three times
+          triple the sign like %%%1, etc.
+
+</span><span style='color:#5F5'>          Colors
+
+</span><span style='color:#FFF'>&lt;000&gt;</span><span style='color:#AAA'>     Three alphanumeric characters encapsulated by the less- and greater-
+          than signs are used for 4 and 8 bit color codes.
+
+</span><span style='color:#FFF'>&lt;0000&gt;</span><span style='color:#AAA'>    Either a B (background) or F (foreground) followed by three
+          hexadecimal characters encapsulated by &lt; &gt; signs are used for 12
+          bit color codes. Requires truecolor capable terminal.
+
+</span><span style='color:#FFF'>&lt;0000000&gt;</span><span style='color:#AAA'> Either a B (background) or F (foreground) followed by six
+          hexadecimal characters encapsulated by &lt; &gt; signs are used for 24
+          bit color codes. Requires truecolor capable terminal.
+
+          More information is available at #help color.
+
+</span><span style='color:#5F5'>          Escapes
+
+</span><span style='color:#FFF'>&bsol; </span><span style='color:#AAA'>        The back slash is used to escape a character. All available options
+          are listed at #help escape. Escapes are typically escaped when text
+          leaves the client, by being send to a server, the shell, being
+          displayed on the screen, or being processed as part of a regex.
+          Escapes try to mimic escapes in PCRE when possible.
+
+</span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#CHARACTERS'>characters</a>, <a href='#COLORS'>colors</a>, <a href='#ESCAPES'>escapes</a>, <a href='#INFO'>info</a> and <a href='#PCRE'>pcre</a>.
 <a name='SUSPEND'></a>
 <a name='SUSPEND'></a>
 
 
 </span><span style='color:#5F5'>         SUSPEND
 </span><span style='color:#5F5'>         SUSPEND
@@ -4156,12 +4334,14 @@ Related</span><span style='color:#AAA'>: <a href='#ALL'>all</a>, <a href='#PORT'
          You can see the first nest of a variable using &dollar;variable[+1] and the
          You can see the first nest of a variable using &dollar;variable[+1] and the
          last nest using &dollar;variable[-1]. Using &dollar;variable[-2] will report the
          last nest using &dollar;variable[-1]. Using &dollar;variable[-2] will report the
          second last variable, and so on. To show all indices use *variable[].
          second last variable, and so on. To show all indices use *variable[].
-         To show all values use &dollar;variable[%*] or a less generic regex. To show
-         all values from index 2 through 4 use &dollar;variable[+2..4].
+         To show all values use &dollar;variable[]. To show all values from index 2
+         through 4 use &dollar;variable[+2..4].
 
 
          Nested variables are also known as tables, table generally being used
          Nested variables are also known as tables, table generally being used
          to refer to several variables nested within one specific variable.
          to refer to several variables nested within one specific variable.
 
 
+         It's possible to use regular expressions.
+
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #show {Targets starting with the letter A: &dollar;targets[A%*]
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #show {Targets starting with the letter A: &dollar;targets[A%*]
 
 
          To see the internal index of a variable use &amp;&lt;variable name&gt;. To see
          To see the internal index of a variable use &amp;&lt;variable name&gt;. To see

+ 11 - 10
docs/syntax.txt

@@ -16,7 +16,7 @@ CHARACTERS
 
 
 { }       Curly brackets aka braces are used for seperating multi word command
 { }       Curly brackets aka braces are used for seperating multi word command
           arguments, nesting commands, and nesting variables. Braces cannot
           arguments, nesting commands, and nesting variables. Braces cannot
-          easily be escaped and must always be used in pairs.
+          be escaped using \{ \}, to escape { } use \x7B \x7D
 
 
 " "       Quote characters are used for strings in the #math, #if, #switch,
 " "       Quote characters are used for strings in the #math, #if, #switch,
           and #case commands. Instead of " " you can use an extra set of
           and #case commands. Instead of " " you can use an extra set of
@@ -123,19 +123,20 @@ COORDINATES
 SQUARES
 SQUARES
 -------
 -------
 
 
-          A square argument takes 4 coordinates. The first two coordinates
-          define the upper left corner, the last two coordinates define the
-          bottom right corner. The upper left corner of the terminal is
-          defined as 1,1 and the bottom right corner as -1,-1. This type
-          of argument is use by the #draw, #button, #map offset, 
+          A square argument takes 2 coordinates. The first coordinate defines
+          the upper left corner, the last two coordinates define the bottom
+          right corner. The upper left corner of the terminal is defines as
+          1,1 and the bottom right corner as -1,-1. This type of argument is
+          used by #draw, #button and #map offset.
 
 
 PANES
 PANES
 -----
 -----
-          A panes argument takes 4 size values, which are: top pane, bottom
+          A pane argument takes 4 size values, which are: top pane, bottom
           pane, left pane, right pane. When a negative value is provided the
           pane, left pane, right pane. When a negative value is provided the
           size is the maximum size, minus the value. This type of argument
           size is the maximum size, minus the value. This type of argument
           is used by the #split command.
           is used by the #split command.
 
 
+
 MATH
 MATH
 ----
 ----
           Operators       Priority     Function
           Operators       Priority     Function
@@ -172,6 +173,7 @@ MATH
 M,K,m,u   These four metric suffixes are allowed for numbers.
 M,K,m,u   These four metric suffixes are allowed for numbers.
 
 
 { }       Braces can be used in #math to perform string operations.
 { }       Braces can be used in #math to perform string operations.
+
 {a} > {b} This checks if the string "a" is greater than "b".
 {a} > {b} This checks if the string "a" is greater than "b".
 
 
 ,         Commas in numbers are ignored, as well as spaces and tabs.
 ,         Commas in numbers are ignored, as well as spaces and tabs.
@@ -289,9 +291,8 @@ Example: #showme <cfa>Lime     <fac>Pink     <fca>Orange
          For 12 bit truecolor use <F000> to <FFFF> for foreground colors and
          For 12 bit truecolor use <F000> to <FFFF> for foreground colors and
          <B000> to <BFFF> for background colors.
          <B000> to <BFFF> for background colors.
 
 
-         For 24 bit truecolor use \e[38;2;R;G;Bm where R G B are red/green/blue
-         intensities between 0 and 255. For example: \e[37;2;50;100;150m. Use
-         \e[48;2;R;G;Bm for background colors.
+         For 24 bit truecolor use <F000000> to <FFFFFFF> for foreground
+         colors and <B000000> to <BFFFFFF> for background colors.
 
 
 HELP
 HELP
 ----
 ----

+ 86 - 13
mods/igr.mods

@@ -1,4 +1,77 @@
-May 2022        2.02.12
+Sep 2021        2.02.20
+------------------------------------------------------------------------------
+main.c          Added -H startup option to run tintin in a nohup compatible
+                mode.
+
+list.c          Changed #list explode to support:
+
+                #list {var} {explode} {separator}
+
+                The separator can be {;} or {\n} or anything else. Variables
+                and escape codes in the separator are substituted. This turns
+                a variable into a list using the given separator.
+
+list.c          Updated #list collapse to support:
+
+                #list {var} {collapse} {separator}
+
+                The separator can be {;} or {\n} or anything else. Variables
+                and escape codes in the separator are substituted. This turns
+                a list into a variable.
+
+buffer.c        Updated #buffer find to support:
+
+                #buffer {find} {[number]} {<string>} {[variable]}
+
+                The number must now be -1, -2, etc to search starting from the
+                end of the buffer, and 1, 2, etc to search from the start.
+
+buffer.c        Updated #buffer clear to support:
+
+                #buffer {clear} {[lower bound]} {[upper bound]}
+
+tokenize.c      Increased the buffer size of #foreach to 1 million bytes, can
+                be adjusted in tintin.h by changing MALLOC_SIZE.
+
+data.c          Added #info arguments [save] option that will display all
+                arguments / matches of the most recently fired trigger.
+
+data.c          Added #info matches [save] option that will display all
+                matches of the most recently fired command like #regex.
+
+data.c          #kill all will no longer remove pathdirs.
+
+telopt_client.c Fixed an incorrect rejection response for the charset telopt.
+
+mapper.c        Fixed #map goto jumping to the room you're currently in on a
+                failed search.
+
+telopt_client.c Fixed handling of empty arrays for gmcp.
+
+mapper.c        Added #map flag pancake which will display rooms on the map
+                above and below you while enabled. You can use #map color
+                room <aaa><fff> to display rooms with a white to black color
+                gradient in pancake mode. To easily view color gradients use:
+
+                #loop 6 0 i #draw scroll bar 1 1 1 40 {$i;6;<aaa><fff>}
+
+session.c       The SESSION CREATED and SESSION CONNECTED will store the file
+                name, if provided, in the %4 argument.
+
+draw.c          #draw line now takes a text argument which can be further
+                manipulated with the CALIGN and RALIGN flags, LALIGN is the
+                default.
+
+session.c       The SESSION CONNECTED event now triggers after the SESSION
+                CREATED event.
+
+session.c       Changed GAG SESSION DISCONNECTED to GAG SESSION DESTROYED.
+
+mapper.c        Added the #map info save option.
+
+utf8.c          Updated the cp949 to utf8 table with additional characters.
+
+May 2021        2.02.12
 ------------------------------------------------------------------------------
 ------------------------------------------------------------------------------
 input.c         Added RECEIVED INPUT CHARACTER event that triggers on each
 input.c         Added RECEIVED INPUT CHARACTER event that triggers on each
                 character added to the input buffer. %0 holds the character,
                 character added to the input buffer. %0 holds the character,
@@ -33,12 +106,12 @@ data.c          Added the #info tokenizer option to have a peek at the stack
 
 
 path.c          #path load now automatically adds reverse directions.
 path.c          #path load now automatically adds reverse directions.
 
 
-map.c           Added support for #map list {roomflag} {!void} etc.
+mapper.c        Added support for #map list {roomflag} {!void} etc.
 
 
 line.c          Added #line substitute braces which will turn { and } into
 line.c          Added #line substitute braces which will turn { and } into
                 \x7B and \x7D.
                 \x7B and \x7D.
 
 
-map.c           Added #map list {{distance}{5}} to list all rooms within a
+mapper.c        Added #map list {{distance}{5}} to list all rooms within a
                 weighted distance of 5. Keep in mind this goes by weight,
                 weighted distance of 5. Keep in mind this goes by weight,
                 which starts at 1.0 and increases by 2.0 per room by default.
                 which starts at 1.0 and increases by 2.0 per room by default.
 
 
@@ -48,10 +121,10 @@ cursor.c        Added #cursor flag eol {cr|lf|crlf|crnul|off} to change the
 log.c           Changed logging to where it logs data before it's wrapped by
 log.c           Changed logging to where it logs data before it's wrapped by
                 the client.
                 the client.
 
 
-map.c           Added support for roomsymbol lengths up to 5 characters in
+mapper.c        Added support for roomsymbol lengths up to 5 characters in
                 unicode, and 6 characters in ascii graphics mode.
                 unicode, and 6 characters in ascii graphics mode.
 
 
-map.c           Added the option to #map list/find to use * as a roomexit.
+mapper.c        Added the option to #map list/find to use * as a roomexit.
                 This will make the search ignore non-pathdir exits without
                 This will make the search ignore non-pathdir exits without
                 an exit direction.
                 an exit direction.
 
 
@@ -79,8 +152,8 @@ data.c          Added the #info environ and #info environ save option.
 
 
 main.c          Added SIGUSR event. %0 will contain either 1 or 2.
 main.c          Added SIGUSR event. %0 will contain either 1 or 2.
                 #event {SIGUSR} {#showme RECEIVED SIGUSR %0}
                 #event {SIGUSR} {#showme RECEIVED SIGUSR %0}
-		#info system save
-		#system {kill -USR1 $info[SYSTEM][PID]
+                #info system save
+                #system {kill -USR1 $info[SYSTEM][PID]
 
 
 main.c          Added SIGHUB event which should trigger when the terminal is
 main.c          Added SIGHUB event which should trigger when the terminal is
                 closed with TinTin++ still running inside. You can use
                 closed with TinTin++ still running inside. You can use
@@ -114,7 +187,7 @@ draw.c          Added color gradient support to #draw bar. To use it you
                 field and based on the min;max value it will pick a color
                 field and based on the min;max value it will pick a color
                 somewhere in between.
                 somewhere in between.
 
 
-		#loop 1 9 i #draw <DDF> scroll bar 1 1 1 40 {$i;9;<faa><afa>}
+                #loop 1 9 i #draw <DDF> scroll bar 1 1 1 40 {$i;9;<faa><afa>}
 
 
 draw.c          Added the BAR drawing type. Currently only horizontal bars
 draw.c          Added the BAR drawing type. Currently only horizontal bars
                 are supported, but for forward compatibility you should draw
                 are supported, but for forward compatibility you should draw
@@ -460,7 +533,7 @@ input.c         ctrl-v will now correctly capture ctrl-alt-f and similar
                 Example added to the SCRIPTS file.
                 Example added to the SCRIPTS file.
 
 
 event.c         Added GAG SESSION CONNECTED, GAG SESSION CREATED,
 event.c         Added GAG SESSION CONNECTED, GAG SESSION CREATED,
-                GAG SESSION DISCONNECTED, and GAG SESSION TIMED OUT events.
+                GAG SESSION DESTROYED, and GAG SESSION TIMED OUT events.
 
 
 mapper.c        Added #map roomflag fog. Works like #map roomflag hide but
 mapper.c        Added #map roomflag fog. Works like #map roomflag hide but
                 the flag is auto cleared on entrance.
                 the flag is auto cleared on entrance.
@@ -494,7 +567,7 @@ screen.c        Added basic link support. To create a basic link surround a
                 keyword with the \e[4m keyword \e[24m tags. When clicked with
                 keyword with the \e[4m keyword \e[24m tags. When clicked with
                 mouse support enabled it'll spawn a LINK mouse event.
                 mouse support enabled it'll spawn a LINK mouse event.
 
 
-map.c           Added #map exitflag <dir> <option> GET <variable> support.
+mapper.c        Added #map exitflag <dir> <option> GET <variable> support.
 
 
 event.c         The %4 argument of mouse clicks once again reports the word
 event.c         The %4 argument of mouse clicks once again reports the word
                 that has been clicked, with %5 reporting the line. Under
                 that has been clicked, with %5 reporting the line. Under
@@ -563,7 +636,7 @@ main.c          Configurations and pathdirs are automatically assigned to
                 the CONFIG and PATHDIR class on startup. Somewhat
                 the CONFIG and PATHDIR class on startup. Somewhat
                 experimental as there may be unforseen complications.
                 experimental as there may be unforseen complications.
 
 
-list.c          Added #list <list> collapse and explode
+list.c          Added #list <list> collapse.
 
 
 mapper.c        Updated #map {} output.
 mapper.c        Updated #map {} output.
 
 
@@ -712,7 +785,7 @@ draw.c          Added TUBED drawing option.
 
 
 scan.c          Added #scan FILE {commands} option. See #help scan.
 scan.c          Added #scan FILE {commands} option. See #help scan.
 
 
-map.c           Can provide multiple ; separated commands to #map move at
+mapper.c        Can provide multiple ; separated commands to #map move at
                 once. For example: #map move {n;e;s;w}
                 once. For example: #map move {n;e;s;w}
 
 
 main.c          Added support for starting tintin with telnet://host:port
 main.c          Added support for starting tintin with telnet://host:port
@@ -1721,7 +1794,7 @@ highlight.c    Added option to use escape codes in the #highlight color field.
 
 
 class.c        Added #class {<class>} list {<list>} option.
 class.c        Added #class {<class>} list {<list>} option.
 
 
-map.c          Added #map list {roomflag} option.
+mapper.c       Added #map list {roomflag} option.
 
 
 list.c         Fixed bug with using #list clear on a nested variable.
 list.c         Fixed bug with using #list clear on a nested variable.
 
 

+ 15 - 51
src/banner.c

@@ -106,7 +106,7 @@ void banner_init(struct session *ses, char *arg1)
 
 
 	banner_website(ses, "Lost Souls", "http://lostsouls.org", arg1);
 	banner_website(ses, "Lost Souls", "http://lostsouls.org", arg1);
 	banner_address(ses, "Lost Souls", "ls lostsouls.org 23", arg1);
 	banner_address(ses, "Lost Souls", "ls lostsouls.org 23", arg1);
-	banner_expires(ses, "Lost Souls", "2026", arg1);
+	banner_expires(ses, "Lost Souls", "2027", arg1);
 
 
 
 
 	banner_create(ses, "Legends of Kallisti", arg1);
 	banner_create(ses, "Legends of Kallisti", arg1);
@@ -124,7 +124,7 @@ void banner_init(struct session *ses, char *arg1)
 
 
 	banner_website(ses, "Legends of Kallisti", "http://www.KallistiMUD.com", arg1);
 	banner_website(ses, "Legends of Kallisti", "http://www.KallistiMUD.com", arg1);
 	banner_address(ses, "Legends of Kallisti", "LoK kallistimud.com 4000", arg1);
 	banner_address(ses, "Legends of Kallisti", "LoK kallistimud.com 4000", arg1);
-	banner_expires(ses, "Legends of Kallisti", "2026", arg1);
+	banner_expires(ses, "Legends of Kallisti", "2027", arg1);
 
 
 
 
 	banner_create(ses, "3Kingdoms", arg1);
 	banner_create(ses, "3Kingdoms", arg1);
@@ -142,9 +142,20 @@ void banner_init(struct session *ses, char *arg1)
 
 
 	banner_website(ses, "3Kingdoms", "http://3k.org", arg1);
 	banner_website(ses, "3Kingdoms", "http://3k.org", arg1);
 	banner_address(ses, "3Kingdoms", "3K 3k.org 3000", arg1);
 	banner_address(ses, "3Kingdoms", "3K 3k.org 3000", arg1);
-	banner_expires(ses, "3Kingdoms", "2026", arg1);
+	banner_expires(ses, "3Kingdoms", "2027", arg1);
 
 
+	banner_create(ses, "RetroMUD", arg1);
+
+	banner_desc(ses, "RetroMUD",
+		"RetroMUD features over 100 levels of play, a huge array of character advancement\n"
+		"options, and dozens of quests across six different worlds. It's like six games\n"
+		"in one.", arg1);
 
 
+	banner_website(ses, "RetroMUD", "http://www.retromud.org", arg1);
+	banner_address(ses, "RetroMUD", "rm 96.126.116.118 3000", arg1);
+	banner_expires(ses, "RetroMUD", "2027", arg1);
+
+/*
 	banner_create(ses, "New World Ateraan", arg1);
 	banner_create(ses, "New World Ateraan", arg1);
 
 
 	banner_desc(ses, "New World Ateraan",
 	banner_desc(ses, "New World Ateraan",
@@ -161,7 +172,6 @@ void banner_init(struct session *ses, char *arg1)
 	banner_address(ses, "New World Ateraan", "nwa ateraan.com 4002", arg1);
 	banner_address(ses, "New World Ateraan", "nwa ateraan.com 4002", arg1);
 	banner_expires(ses, "New World Ateraan", "2026", arg1);
 	banner_expires(ses, "New World Ateraan", "2026", arg1);
 
 
-
 	banner_create(ses, "Realm of Utopian Dreams (RUD)", arg1);
 	banner_create(ses, "Realm of Utopian Dreams (RUD)", arg1);
 
 
 	banner_desc(ses, "Realm of Utopian Dreams (RUD)",
 	banner_desc(ses, "Realm of Utopian Dreams (RUD)",
@@ -194,17 +204,7 @@ void banner_init(struct session *ses, char *arg1)
 	banner_website(ses, "Carrion Fields", "http://carrionfields.net", arg1);
 	banner_website(ses, "Carrion Fields", "http://carrionfields.net", arg1);
 	banner_address(ses, "Carrion Fields", "cf carrionfields.net 4449", arg1);
 	banner_address(ses, "Carrion Fields", "cf carrionfields.net 4449", arg1);
 	banner_expires(ses, "Carrion Fields", "2026", arg1);
 	banner_expires(ses, "Carrion Fields", "2026", arg1);
-
-	banner_create(ses, "RetroMUD", arg1);
-
-	banner_desc(ses, "RetroMUD",
-		"RetroMUD features over 100 levels of play, a huge array of character advancement\n"
-		"options, and dozens of quests across six different worlds. It's like six games\n"
-		"in one.", arg1);
-
-	banner_website(ses, "RetroMUD", "http://www.retromud.org", arg1);
-	banner_address(ses, "RetroMUD", "rm 96.126.116.118 3000", arg1);
-	banner_expires(ses, "RetroMUD", "2027", arg1);
+*/
 }
 }
 
 
 int total_banners()
 int total_banners()
@@ -496,25 +496,6 @@ DO_COMMAND(do_banner)
 		"\n"
 		"\n"
 	},
 	},
 
 
-	{
-		1400000000, 
-		1700000000, 
-		100,
-		"\n"
-		"<138>               Carrion Fields  -  http://carrionfields.net\n"
-		"\n"
-		"<078>Adventure, politics and bloody war await you in this life of swords, sorcery,\n"
-		"<078>deception, and honor.  We have 17 customizable classes with which to explore a\n"
-		"<078>massively rich world of over 270 areas.  RP is mandatory, but help is always\n"
-		"<078>available on the newbie channel.  Intuitive game mechanics provide a fun and\n"
-		"<078>fulfilling PK environment.  Carrion Fields is 100% free to play and free of\n"
-		"<078>paid perks as well.  By what name do you wish to be mourned?\n"
-		"\n"
-                "<178>To connect to Carrion Fields enter: #session cf carrionfields.net 4449\n"
-                "\n"
-		                
-	},
-
 	{
 	{
 		1400000000, 
 		1400000000, 
 		1700000000, 
 		1700000000, 
@@ -609,23 +590,6 @@ DO_COMMAND(do_banner)
 		"\n"
 		"\n"
 	},
 	},
 
 
-	{
-		1291140000,
-		1354280000,
-		100,
-		"\n"
-		"<138>                   Primordiax - http://www.primordiax.com\n"
-		"\n"
-		"<078>Primordiax is an in-character enforced MUD where roleplaying is heavily\n"
-		"<078>encouraged. The exclusive design of Primordiax makes it extremely\n"
-		"<078>accessible to new players without losing the intrigue and complexity that\n"
-		"<078>continues to attract veterans of the genre. Primordiax offers a classic\n"
-		"<078>gaming experience with a highly unique class system and an open skill tree.\n"
-		"\n"
-		"<178>To connect to Primordiax enter: #session prim primordiax.com 3000\n"
-		"\n"
-	},
-
 	{
 	{
 		1291140000, * 30 Nov 2010 *
 		1291140000, * 30 Nov 2010 *
 		1354280000, * 30 Nov 2012 *
 		1354280000, * 30 Nov 2012 *

+ 88 - 20
src/buffer.c

@@ -272,7 +272,7 @@ void add_line_buffer(struct session *ses, char *line, int prompt)
 
 
 	if (HAS_BIT(ses->flags, SES_FLAG_SNOOP) && ses != gtd->ses)
 	if (HAS_BIT(ses->flags, SES_FLAG_SNOOP) && ses != gtd->ses)
 	{
 	{
-		tintin_printf2(gtd->ses, "%s[%s%s] %s", COLOR_TEXT, ses->name, ses->scroll->input, COLOR_TEXT);
+		tintin_printf2(gtd->ses, "%s[%s] %s%s", COLOR_TEXT, ses->name, ses->scroll->input, COLOR_TEXT);
 	}
 	}
 
 
 	if (ses->proxy)
 	if (ses->proxy)
@@ -326,6 +326,8 @@ void add_line_buffer(struct session *ses, char *line, int prompt)
 		}
 		}
 		memmove(&ses->scroll->buffer[0], &ses->scroll->buffer[purge], (ses->scroll->size - purge) * sizeof(struct buffer_data *));
 		memmove(&ses->scroll->buffer[0], &ses->scroll->buffer[purge], (ses->scroll->size - purge) * sizeof(struct buffer_data *));
 
 
+		RESTRING(ses->scroll->buffer[cnt]->str, "");
+
 		ses->scroll->used -= purge;
 		ses->scroll->used -= purge;
 		ses->scroll->line = URANGE(-1, ses->scroll->line - purge, ses->scroll->used - 1);
 		ses->scroll->line = URANGE(-1, ses->scroll->line - purge, ses->scroll->used - 1);
 	}
 	}
@@ -349,7 +351,7 @@ void buffer_print(struct session *ses, int index, int start, int end)
 {
 {
 	struct buffer_data *buffer;
 	struct buffer_data *buffer;
 	char *pti, temp[STRING_SIZE];
 	char *pti, temp[STRING_SIZE];
-	int col, swap, str_len, col_len, height, width;
+	int col, swap, str_len, col_len, height, width = 0;
 
 
 	push_call("buffer_print(%p,%d,%d,%d)",ses,index,start,end);
 	push_call("buffer_print(%p,%d,%d,%d)",ses,index,start,end);
 
 
@@ -664,16 +666,72 @@ DO_COMMAND(do_buffer)
 
 
 DO_BUFFER(buffer_clear)
 DO_BUFFER(buffer_clear)
 {
 {
-	int cnt;
+	int cnt, min, max, range;
+
+	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 (*arg1 == 0)
+	{
+		for (cnt = 1 ; cnt < ses->scroll->used ; cnt++)
+		{
+			free(ses->scroll->buffer[cnt]->str);
+			free(ses->scroll->buffer[cnt]);
+		}
+
+		ses->scroll->used = 1;
+		ses->scroll->line = -1;
+
+		return;
+	}
+
+	min = get_number(ses, arg1);
+
+	if (min < 0)
+	{
+		min = ses->scroll->used + min;
+	}
+
+	min = URANGE(1, min, ses->scroll->used - 1);
+
+	if (*arg2 == 0)
+	{
+		max = min;
+	}
+	else
+	{
+		max = get_number(ses, arg2);
+
+		if (max < 0)
+		{
+			max = ses->scroll->used + max;
+		}
+
+		max = URANGE(1, max, ses->scroll->used - 1);
+	}
+
+	if (min > max)
+	{
+		show_error(ses, LIST_COMMAND, "#ERROR: #BUFFER CLEAR {%d} {%d} LOWER BOUND EXCEEDS UPPER BOUND.", min, max);
+
+		return;
+	}
+
+	range = max - min + 1;
 
 
-	for (cnt = 1 ; cnt < ses->scroll->used ; cnt++)
+	for (cnt = min ; cnt <= max ; cnt++)
 	{
 	{
 		free(ses->scroll->buffer[cnt]->str);
 		free(ses->scroll->buffer[cnt]->str);
 		free(ses->scroll->buffer[cnt]);
 		free(ses->scroll->buffer[cnt]);
 	}
 	}
 
 
-	ses->scroll->used  = 1;
-	ses->scroll->line = - 1;
+	for (max++ ; max < ses->scroll->used ; min++, max++)
+	{
+		ses->scroll->buffer[min] = ses->scroll->buffer[max];
+	}
+
+	ses->scroll->used -= range;
+	ses->scroll->line = URANGE(-1, ses->scroll->line - range, ses->scroll->used - 1);
 }
 }
 
 
 DO_BUFFER(buffer_up)
 DO_BUFFER(buffer_up)
@@ -860,8 +918,11 @@ DO_BUFFER(buffer_lock)
 
 
 DO_BUFFER(buffer_find)
 DO_BUFFER(buffer_find)
 {
 {
+	char *arg3;
 	int scroll_cnt, grep_cnt, grep_max, page;
 	int scroll_cnt, grep_cnt, grep_max, page;
 
 
+	arg3 = str_alloc_stack(0);
+
 	check_buffer(ses);
 	check_buffer(ses);
 
 
 	grep_cnt = grep_max = scroll_cnt = 0;
 	grep_cnt = grep_max = scroll_cnt = 0;
@@ -890,14 +951,16 @@ DO_BUFFER(buffer_find)
 	}
 	}
 	else
 	else
 	{
 	{
-		page = 1;
+		page = -1;
 
 
 		strcpy(arg2, arg1);
 		strcpy(arg2, arg1);
 	}
 	}
 
 
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+
 	if (page > 0)
 	if (page > 0)
 	{
 	{
-		for (scroll_cnt = ses->scroll->used - 1; scroll_cnt >= 0 ; scroll_cnt--)
+		for (scroll_cnt = 1 ; scroll_cnt < ses->scroll->used ; scroll_cnt++)
 		{
 		{
 			if (HAS_BIT(ses->scroll->buffer[scroll_cnt]->flags, BUFFER_FLAG_GREP))
 			if (HAS_BIT(ses->scroll->buffer[scroll_cnt]->flags, BUFFER_FLAG_GREP))
 			{
 			{
@@ -917,7 +980,7 @@ DO_BUFFER(buffer_find)
 	}
 	}
 	else
 	else
 	{
 	{
-		for (scroll_cnt = 0 ; scroll_cnt < ses->scroll->used ; scroll_cnt++)
+		for (scroll_cnt = ses->scroll->used - 1 ; scroll_cnt > 0 ; scroll_cnt--)
 		{
 		{
 			if (HAS_BIT(ses->scroll->buffer[scroll_cnt]->flags, BUFFER_FLAG_GREP))
 			if (HAS_BIT(ses->scroll->buffer[scroll_cnt]->flags, BUFFER_FLAG_GREP))
 			{
 			{
@@ -932,7 +995,6 @@ DO_BUFFER(buffer_find)
 				{
 				{
 					break;
 					break;
 				}
 				}
-
 			}
 			}
 		}
 		}
 	}
 	}
@@ -944,10 +1006,16 @@ DO_BUFFER(buffer_find)
 		return;
 		return;
 	}
 	}
 
 
-	ses->scroll->line = scroll_cnt;
-
-	buffer_down(ses, ntos(get_scroll_rows(ses) - 1), arg1, arg2);
+	if (*arg3)
+	{
+		set_nest_node_ses(ses, arg3, "%d", scroll_cnt);
+	}
+	else
+	{
+		ses->scroll->line = scroll_cnt;
 
 
+		buffer_down(ses, ntos(get_scroll_rows(ses) - 1), arg1, arg2);
+	}
 	return;
 	return;
 }
 }
 
 
@@ -974,7 +1042,7 @@ DO_BUFFER(buffer_get)
 	{
 	{
 		min = ses->scroll->used + min;
 		min = ses->scroll->used + min;
 	}
 	}
-	min = URANGE(0, min, ses->scroll->used - 1);
+	min = URANGE(1, min, ses->scroll->used - 1);
 
 
 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
 
 
@@ -991,7 +1059,7 @@ DO_BUFFER(buffer_get)
 	{
 	{
 		max = ses->scroll->used + max;
 		max = ses->scroll->used + max;
 	}
 	}
-	max = URANGE(0, max, ses->scroll->used - 1);
+	max = URANGE(1, max, ses->scroll->used - 1);
 
 
 	if (min > max)
 	if (min > max)
 	{
 	{
@@ -1006,12 +1074,12 @@ DO_BUFFER(buffer_get)
 
 
 	while (min <= max)
 	while (min <= max)
 	{
 	{
-		sprintf(arg2, "%s[%d]", arg1, ++cnt);
+		substitute(ses, ses->scroll->buffer[min++]->str, arg2, SUB_SEC);
 
 
-		set_nest_node_ses(ses, arg2, "%s", ses->scroll->buffer[min++]->str);
+		add_nest_node_ses(ses, arg1, "{%d}{%s}", ++cnt, arg2);
 	}
 	}
 
 
-	show_message(ses, LIST_COMMAND, "#BUFFER GET: %d LINES SAVED TO {%s}.", cnt, arg1);
+	show_message(ses, LIST_COMMAND, "#BUFFER GET: %d LINE%s SAVED TO {%s}.", cnt, cnt > 1 ? "S" : "", arg1);
 
 
 	return;
 	return;
 }
 }
@@ -1069,7 +1137,7 @@ DO_BUFFER(buffer_write)
 
 
 			logheader(ses, fp, ses->log->mode + LOG_FLAG_OVERWRITE);
 			logheader(ses, fp, ses->log->mode + LOG_FLAG_OVERWRITE);
 
 
-			for (cnt = 0 ; cnt < ses->scroll->used ; cnt++)
+			for (cnt = 1 ; cnt < ses->scroll->used ; cnt++)
 			{
 			{
 				if (HAS_BIT(ses->log->mode, LOG_FLAG_PLAIN))
 				if (HAS_BIT(ses->log->mode, LOG_FLAG_PLAIN))
 				{
 				{
@@ -1249,7 +1317,7 @@ DO_COMMAND(do_grep)
 	}
 	}
 	else
 	else
 	{
 	{
-		for (scroll_cnt = 0 ; scroll_cnt < ses->scroll->used ; scroll_cnt++)
+		for (scroll_cnt = 1 ; scroll_cnt < ses->scroll->used ; scroll_cnt++)
 		{
 		{
 			if (HAS_BIT(ses->scroll->buffer[scroll_cnt]->flags, BUFFER_FLAG_GREP))
 			if (HAS_BIT(ses->scroll->buffer[scroll_cnt]->flags, BUFFER_FLAG_GREP))
 			{
 			{

+ 2 - 3
src/chat.c

@@ -453,7 +453,7 @@ DO_CHAT(chat_call)
 
 
 		return;
 		return;
 	}
 	}
-	sprintf(buf[i], "{%s} {%s}", arg1, arg2);
+	snprintf(buf[i], 200, "{%s} {%s}", arg1, arg2);
 
 
 	pthread_create(&thread, NULL, threaded_chat_call, (void *) buf[i]);
 	pthread_create(&thread, NULL, threaded_chat_call, (void *) buf[i]);
 
 
@@ -952,9 +952,8 @@ void get_chat_commands(struct chat_data *buddy, char *buf, int len)
 }
 }
 
 
 
 
-void chat_name_change(struct chat_data *buddy, char *txt)
+void chat_name_change(struct chat_data *buddy, char *name)
 {
 {
-	char name[BUFFER_SIZE];
 	struct chat_data *node;
 	struct chat_data *node;
 
 
 	if (strlen(name) > 20)
 	if (strlen(name) > 20)

+ 0 - 1
src/command.c

@@ -187,7 +187,6 @@ extern DO_COMMAND(do_message);
 extern DO_COMMAND(do_path);
 extern DO_COMMAND(do_path);
 extern DO_COMMAND(do_port);
 extern DO_COMMAND(do_port);
 extern DO_COMMAND(do_prompt);
 extern DO_COMMAND(do_prompt);
-extern DO_COMMAND(do_replace);
 extern DO_COMMAND(do_run);
 extern DO_COMMAND(do_run);
 extern DO_COMMAND(do_scan);
 extern DO_COMMAND(do_scan);
 extern DO_COMMAND(do_script);
 extern DO_COMMAND(do_script);

+ 12 - 14
src/config.c

@@ -204,22 +204,20 @@ DO_CONFIG(config_charset)
 			SET_BIT(ses->charset, charset_table[index].flags);
 			SET_BIT(ses->charset, charset_table[index].flags);
 		}
 		}
 	}
 	}
-	else
+
+	for (index = 0 ; *charset_table[index].name ; index++)
 	{
 	{
-		for (index = 0 ; *charset_table[index].name ; index++)
+		if (ses->charset == charset_table[index].flags)
 		{
 		{
-			if (ses->charset == charset_table[index].flags)
-			{
-				break;
-			}
+			break;
 		}
 		}
+	}
 
 
-		if (*charset_table[index].name == 0)
-		{
-			show_error(ses, LIST_CONFIG, "#CONFIG CHARSET: INVALID FLAG: %d", ses->charset);
+	if (*charset_table[index].name == 0)
+	{
+		show_error(ses, LIST_CONFIG, "#CONFIG CHARSET: INVALID FLAG: %d", ses->charset);
 
 
-			return NULL;
-		}
+		return NULL;
 	}
 	}
 
 
 	strcpy(arg2, charset_table[index].name);
 	strcpy(arg2, charset_table[index].name);
@@ -368,7 +366,7 @@ DO_CONFIG(config_connectretry)
 		gts->connect_retry = atoll(arg2) * 1000000LL;
 		gts->connect_retry = atoll(arg2) * 1000000LL;
 	}
 	}
 
 
-	sprintf(arg2, "%.1Lf", (long double) gts->connect_retry / 1000000);
+	snprintf(arg2, BUFFER_SIZE, "%.1Lf", (long double) gts->connect_retry / 1000000);
 
 
 	return ses;
 	return ses;
 }
 }
@@ -525,7 +523,7 @@ DO_CONFIG(config_logmode)
 			return NULL;
 			return NULL;
 		}
 		}
 	}
 	}
-	strcpy(arg2, HAS_BIT(ses->log->mode, LOG_FLAG_HTML) ? "HTML" : HAS_BIT(ses->log->mode, LOG_FLAG_PLAIN) ? "PLAIN" : "RAW");
+	strcpy(arg2, HAS_BIT(ses->log->mode, LOG_FLAG_HTML) ? "HTML" : HAS_BIT(ses->log->mode, LOG_FLAG_PLAIN) ? "PLAIN" : HAS_BIT(ses->log->mode, LOG_FLAG_RAW) ? "RAW" : "UNSET" );
 
 
 	return ses;
 	return ses;
 }
 }
@@ -692,7 +690,7 @@ DO_CONFIG(config_packetpatch)
 	}
 	}
 	else
 	else
 	{
 	{
-		sprintf(arg2, "%4.2Lf", (long double) ses->packet_patch / 1000000);
+		snprintf(arg2, BUFFER_SIZE, "%4.2Lf", (long double) ses->packet_patch / 1000000);
 	}
 	}
 	return ses;
 	return ses;
 }
 }

+ 2 - 9
src/cursor.c

@@ -1185,13 +1185,6 @@ DO_CURSOR(cursor_enter_finish)
 
 
 	DEL_BIT(gtd->ses->input->flags, INPUT_FLAG_EDIT);
 	DEL_BIT(gtd->ses->input->flags, INPUT_FLAG_EDIT);
 
 
-	if (ses == gtd->ses && gtd->ses->scroll->line != -1)
-	{
-		cursor_check_line(gtd->ses, "");
-
-		buffer_end(gtd->ses, "", "", "");
-	}
-
 	if (ses == gtd->ses && HAS_BIT(gtd->ses->flags, SES_FLAG_SPLIT))
 	if (ses == gtd->ses && HAS_BIT(gtd->ses->flags, SES_FLAG_SPLIT))
 	{
 	{
 		cursor_redraw_line(gtd->ses, "");
 		cursor_redraw_line(gtd->ses, "");
@@ -2698,7 +2691,7 @@ DO_CURSOR(cursor_tab)
 
 
 		if (!HAS_BIT(flag, TAB_FLAG_COMPLETE) && inputline_editor())
 		if (!HAS_BIT(flag, TAB_FLAG_COMPLETE) && inputline_editor())
 		{
 		{
-			sprintf(arg1, "%*s", gtd->ses->tab_width, "");
+			snprintf(arg1, BUFFER_SIZE, "%*s", gtd->ses->tab_width, "");
 
 
 			inputline_insert(arg1, -1);
 			inputline_insert(arg1, -1);
 
 
@@ -2720,7 +2713,7 @@ DO_CURSOR(cursor_tab)
 
 
 		if (!HAS_BIT(flag, TAB_FLAG_COMPLETE) && inputline_editor())
 		if (!HAS_BIT(flag, TAB_FLAG_COMPLETE) && inputline_editor())
 		{
 		{
-			sprintf(arg1, "%*s", gtd->ses->tab_width, "");
+			snprintf(arg1, BUFFER_SIZE, "%*s", gtd->ses->tab_width, "");
 			
 			
 			inputline_insert(arg1, 0);
 			inputline_insert(arg1, 0);
 
 

+ 3 - 3
src/daemon.c

@@ -190,7 +190,7 @@ DO_DAEMON(daemon_attach)
 		return;
 		return;
 	}
 	}
 
 
-	sprintf(sock_file, "%.*s/%.*s.s", PATH_SIZE, filename, NAME_SIZE, arg2);
+	sprintf(sock_file, "%.*s/%.*s.s", PATH_SIZE - 1, filename, NAME_SIZE, arg2);
 
 
 	if (access(sock_file, F_OK) == -1)
 	if (access(sock_file, F_OK) == -1)
 	{
 	{
@@ -547,7 +547,7 @@ DO_DAEMON(daemon_kill)
 				{
 				{
 					pid = atoi(arg + 1);
 					pid = atoi(arg + 1);
 
 
-					sprintf(sock_file, "%.*s/%.*s.s", PATH_SIZE, filename, NAME_SIZE, arg2);
+					sprintf(sock_file, "%.*s/%.*s.s", PATH_SIZE - 1, filename, NAME_SIZE, arg2);
 
 
 					show_message(ses, LIST_COMMAND, "#DAEMON {%s} KILLED.", sock_file, pid);
 					show_message(ses, LIST_COMMAND, "#DAEMON {%s} KILLED.", sock_file, pid);
 
 
@@ -617,7 +617,7 @@ DO_DAEMON(daemon_list)
 				{
 				{
 					pid = atoi(arg + 1);
 					pid = atoi(arg + 1);
 
 
-					sprintf(sock_file, "%.*s/%.*s.s", PATH_SIZE, filename, NAME_SIZE, arg2);
+					sprintf(sock_file, "%.*s/%.*s.s", PATH_SIZE - 1, filename, NAME_SIZE, arg2);
 
 
 					tintin_printf2(ses, "%-40s [%6d]", sock_file, pid);
 					tintin_printf2(ses, "%-40s [%6d]", sock_file, pid);
 				}
 				}

+ 48 - 2
src/data.c

@@ -983,6 +983,11 @@ DO_COMMAND(do_kill)
 	{
 	{
 		for (index = 0 ; index < LIST_MAX ; index++)
 		for (index = 0 ; index < LIST_MAX ; index++)
 		{
 		{
+			if (index == LIST_PATHDIR)
+			{
+				continue;
+			}
+
 			if (!HAS_BIT(ses->list[index]->flags, LIST_FLAG_HIDE))
 			if (!HAS_BIT(ses->list[index]->flags, LIST_FLAG_HIDE))
 			{
 			{
 				kill_list(ses->list[index]);
 				kill_list(ses->list[index]);
@@ -1330,6 +1335,28 @@ DO_COMMAND(do_info)
 
 
 		switch (*arg1 % 32)
 		switch (*arg1 % 32)
 		{
 		{
+			case CTRL_A:
+				if (is_abbrev(arg1, "ARGUMENTS"))
+				{
+					if (is_abbrev(arg2, "SAVE"))
+					{
+						set_nest_node_ses(ses, "info[ARGUMENTS]", "");
+
+						for (index = 0 ; index < gtd->varc ; index++)
+						{
+							add_nest_node_ses(ses, "info[ARGUMENTS]", "{%d}{%s}", index, gtd->vars[index]);
+						}
+					}
+					else
+					{
+						for (index = 0 ; index < gtd->varc ; index++)
+						{
+							tintin_printf2(ses, "#INFO ARGUMENTS: %2d: %s", index, gtd->vars[index]);
+						}
+					}
+				}
+				break;
+
 			case CTRL_B:
 			case CTRL_B:
 				if (is_abbrev(arg1, "BIG5TOUTF8"))
 				if (is_abbrev(arg1, "BIG5TOUTF8"))
 				{
 				{
@@ -1405,7 +1432,26 @@ DO_COMMAND(do_info)
 				break;
 				break;
 
 
 			case CTRL_M:
 			case CTRL_M:
-				if (is_abbrev(arg1, "MCCP"))
+				if (is_abbrev(arg1, "MATCHES"))
+				{
+					if (is_abbrev(arg2, "SAVE"))
+					{
+						set_nest_node_ses(ses, "info[MATCHES]", "");
+
+						for (index = 0 ; index < gtd->cmdc ; index++)
+						{
+							add_nest_node_ses(ses, "info[MATCHES]", "{%d}{%s}", index, gtd->cmds[index]);
+						}
+					}
+					else
+					{
+						for (index = 0 ; index < gtd->cmdc ; index++)
+						{
+							tintin_printf2(ses, "#INFO MATCHES: %2d: %s", index, gtd->cmds[index]);
+						}
+					}
+				}
+				else if (is_abbrev(arg1, "MCCP"))
 				{
 				{
 					if (ses->mccp2)
 					if (ses->mccp2)
 					{
 					{
@@ -1556,7 +1602,7 @@ DO_COMMAND(do_info)
 							add_nest_node_ses(ses, name, "{IP} {%s}", sesptr->session_ip);
 							add_nest_node_ses(ses, name, "{IP} {%s}", sesptr->session_ip);
 							add_nest_node_ses(ses, name, "{PORT} {%s}", sesptr->session_port);
 							add_nest_node_ses(ses, name, "{PORT} {%s}", sesptr->session_port);
 						}
 						}
-						show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[SESSION]}");
+						show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[SESSIONS]}");
 					}
 					}
 					else
 					else
 					{
 					{

+ 135 - 57
src/draw.c

@@ -84,7 +84,6 @@ extern DO_DRAW(draw_bot_side);
 extern DO_DRAW(draw_box);
 extern DO_DRAW(draw_box);
 extern DO_DRAW(draw_buffer);
 extern DO_DRAW(draw_buffer);
 extern DO_DRAW(draw_corner);
 extern DO_DRAW(draw_corner);
-extern DO_DRAW(draw_horizontal_line);
 extern DO_DRAW(draw_left_side);
 extern DO_DRAW(draw_left_side);
 extern DO_DRAW(draw_line);
 extern DO_DRAW(draw_line);
 extern DO_DRAW(draw_map);
 extern DO_DRAW(draw_map);
@@ -156,6 +155,8 @@ DO_COMMAND(do_draw)
 		return ses;
 		return ses;
 	}
 	}
 
 
+	arg4 = str_alloc_stack(0);
+
 	box_color = str_alloc_stack(0);
 	box_color = str_alloc_stack(0);
 	txt_color = str_alloc_stack(0);
 	txt_color = str_alloc_stack(0);
 
 
@@ -446,17 +447,26 @@ DO_COMMAND(do_draw)
 		{
 		{
 			arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
 			arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
 			arg = get_arg_in_braces(ses, arg, arg2, GET_ONE);
 			arg = get_arg_in_braces(ses, arg, arg2, GET_ONE);
-//			arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
-//			arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+			arg = get_arg_in_braces(ses, arg, arg3, GET_ONE);
+			arg = get_arg_in_braces(ses, arg, arg4, GET_ONE);
 
 
 			top_row = get_row_index_arg(ses, arg1);
 			top_row = get_row_index_arg(ses, arg1);
 			top_col = get_col_index_arg(ses, arg2);
 			top_col = get_col_index_arg(ses, arg2);
+			bot_row = get_row_index_arg(ses, arg3);
+			bot_col = get_col_index_arg(ses, arg4);
 
 
-			arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
-			arg = get_arg_in_braces(ses, arg, arg2, GET_ONE);
+			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}",
+					draw_table[index].name,
+					is_math(ses, arg1) ? ntos(top_row) : arg1,
+					is_math(ses, arg2) ? ntos(top_col) : arg2,
+					is_math(ses, arg3) ? ntos(bot_row) : arg3,
+					is_math(ses, arg4) ? ntos(bot_col) : arg4);
+
+				return ses;
+			}
 
 
-			bot_row = get_row_index_arg(ses, arg1);
-			bot_col = get_col_index_arg(ses, arg2);
 
 
 			if (top_row == 0 && top_col == 0)
 			if (top_row == 0 && top_col == 0)
 			{
 			{
@@ -549,9 +559,11 @@ DO_COMMAND(do_draw)
 
 
 // utilities
 // utilities
 
 
+void string_to_stamp(struct session *ses, long long flags, char *in, char *out);
+
 void scale_drawing(struct session *ses, int *top_row, int *top_col, int *bot_row, int *bot_col, int *rows, int *cols, int index, long long flags, char *arg)
 void scale_drawing(struct session *ses, int *top_row, int *top_col, int *bot_row, int *bot_col, int *rows, int *cols, int index, long long flags, char *arg)
 {
 {
-	char *buf, *out, *tmp;
+	char *buf, *out, *tmp, *swap;
 	int inside;
 	int inside;
 	int height, bor_height, tot_height, max_height;
 	int height, bor_height, tot_height, max_height;
 	int width, bor_width, tot_width, max_width;
 	int width, bor_width, tot_width, max_width;
@@ -605,7 +617,19 @@ void scale_drawing(struct session *ses, int *top_row, int *top_col, int *bot_row
 	{
 	{
 		ualign(ses, arg, tmp, max_width - bor_width);
 		ualign(ses, arg, tmp, max_width - bor_width);
 
 
-		arg = tmp;
+		strcpy(arg, tmp);
+	}
+
+	if (HAS_BIT(flags, DRAW_FLAG_HUGE))
+	{
+		string_to_stamp(ses, flags, arg, tmp);
+
+		swap = arg; arg = tmp; tmp = swap;
+
+		if (*arg)
+		{
+			arg[strlen(arg) - 1] = 0;
+		}
 	}
 	}
 
 
 	while (*arg)
 	while (*arg)
@@ -680,6 +704,10 @@ void scale_drawing(struct session *ses, int *top_row, int *top_col, int *bot_row
 	}
 	}
 //	tintin_printf2(ses, "debug4: cols %d top_col: %d bot_col %d", *cols, *top_col, *bot_col);
 //	tintin_printf2(ses, "debug4: cols %d top_col: %d bot_col %d", *cols, *top_col, *bot_col);
 
 
+	if (HAS_BIT(flags, DRAW_FLAG_HUGE))
+	{
+		arg = tmp;
+	}
 	pop_call();
 	pop_call();
 	return;
 	return;
 }
 }
@@ -952,28 +980,28 @@ char *get_draw_corner(long long flags, char *str)
 {
 {
 	draw_cnt = (draw_cnt + 1) % 100;
 	draw_cnt = (draw_cnt + 1) % 100;
 
 
-	if (HAS_BIT(flags, DRAW_FLAG_BLANKED))
-	{
-		strcpy(draw_buf[draw_cnt], " ");
-	}
-	else if (HAS_BIT(flags, DRAW_FLAG_NUMBERED))
+	if (HAS_BIT(flags, DRAW_FLAG_NUMBERED))
 	{
 	{
 		sprintf(draw_buf[draw_cnt], "%d", draw_cnt % 10);
 		sprintf(draw_buf[draw_cnt], "%d", draw_cnt % 10);
 	}
 	}
-	else if (HAS_BIT(flags, DRAW_FLAG_UTF8))
+	else if (HAS_BIT(flags, DRAW_FLAG_PRUNED))
 	{
 	{
-		if (HAS_BIT(flags, DRAW_FLAG_PRUNED))
+		if (HAS_BIT(flags, DRAW_FLAG_SCROLL))
 		{
 		{
-			if (HAS_BIT(flags, DRAW_FLAG_SCROLL))
-			{
-				strcpy(draw_buf[draw_cnt], " ");
-			}
-			else
-			{
-				strcpy(draw_buf[draw_cnt], "\e[C");
-			}
+			strcpy(draw_buf[draw_cnt], " ");
+		}
+		else
+		{
+			strcpy(draw_buf[draw_cnt], "\e[C");
 		}
 		}
-		else if (HAS_BIT(flags, DRAW_FLAG_CIRCLED))
+	}
+	else if (HAS_BIT(flags, DRAW_FLAG_BLANKED))
+	{
+		strcpy(draw_buf[draw_cnt], " ");
+	}
+	else if (HAS_BIT(flags, DRAW_FLAG_UTF8))
+	{
+		if (HAS_BIT(flags, DRAW_FLAG_CIRCLED))
 		{
 		{
 			if (HAS_BIT(flags, DRAW_FLAG_CROSSED))
 			if (HAS_BIT(flags, DRAW_FLAG_CROSSED))
 			{
 			{
@@ -1255,11 +1283,7 @@ char *get_draw_corner(long long flags, char *str)
 	}
 	}
 	else
 	else
 	{
 	{
-		if (HAS_BIT(flags, DRAW_FLAG_PRUNED))
-		{
-			strcpy(draw_buf[draw_cnt], "\e[C");
-		}
-		else if (HAS_BIT(flags, DRAW_FLAG_CIRCLED) || HAS_BIT(flags, DRAW_FLAG_ROUNDED))
+		if (HAS_BIT(flags, DRAW_FLAG_CIRCLED) || HAS_BIT(flags, DRAW_FLAG_ROUNDED))
 		{
 		{
 			strcpy(draw_buf[draw_cnt], "o");
 			strcpy(draw_buf[draw_cnt], "o");
 		}
 		}
@@ -1267,6 +1291,14 @@ char *get_draw_corner(long long flags, char *str)
 		{
 		{
 			strcpy(draw_buf[draw_cnt], "+");
 			strcpy(draw_buf[draw_cnt], "+");
 		}
 		}
+		else if (HAS_BIT(flags, DRAW_FLAG_VER))
+		{
+			strcpy(draw_buf[draw_cnt], "|");
+		}
+		else if (HAS_BIT(flags, DRAW_FLAG_HOR))
+		{
+			strcpy(draw_buf[draw_cnt], "-");
+		}
 		else
 		else
 		{
 		{
 			strcpy(draw_buf[draw_cnt], "+");
 			strcpy(draw_buf[draw_cnt], "+");
@@ -1281,7 +1313,14 @@ char *draw_horizontal(long long flags, char *str)
 
 
 	if (HAS_BIT(flags, DRAW_FLAG_BLANKED))
 	if (HAS_BIT(flags, DRAW_FLAG_BLANKED))
 	{
 	{
-		strcpy(draw_buf[draw_cnt], " ");
+		if (HAS_BIT(flags, DRAW_FLAG_PRUNED) && !HAS_BIT(flags, DRAW_FLAG_SCROLL))
+		{
+			strcpy(draw_buf[draw_cnt], "\e[C");
+		}
+		else
+		{
+			strcpy(draw_buf[draw_cnt], " ");
+		}
 	}
 	}
 	else if (HAS_BIT(flags, DRAW_FLAG_NUMBERED))
 	else if (HAS_BIT(flags, DRAW_FLAG_NUMBERED))
 	{
 	{
@@ -1309,9 +1348,16 @@ char *draw_vertical(long long flags, char *str)
 {
 {
 	draw_cnt = (draw_cnt + 1) % 100;
 	draw_cnt = (draw_cnt + 1) % 100;
 
 
-	if (HAS_BIT(flags, DRAW_FLAG_BLANKED))
+	if (!HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_HOR|DRAW_FLAG_VER|DRAW_FLAG_LEFT|DRAW_FLAG_RIGHT) || HAS_BIT(flags, DRAW_FLAG_BLANKED))
 	{
 	{
-		strcpy(draw_buf[draw_cnt], " ");
+		if (HAS_BIT(flags, DRAW_FLAG_PRUNED) && !HAS_BIT(flags, DRAW_FLAG_SCROLL))
+		{
+			strcpy(draw_buf[draw_cnt], "\e[C");
+		}
+		else
+		{
+			strcpy(draw_buf[draw_cnt], " ");
+		}
 	}
 	}
 	else if (HAS_BIT(flags, DRAW_FLAG_NUMBERED))
 	else if (HAS_BIT(flags, DRAW_FLAG_NUMBERED))
 	{
 	{
@@ -1493,7 +1539,8 @@ DO_DRAW(draw_corner)
 
 
 DO_DRAW(draw_line_horizontal)
 DO_DRAW(draw_line_horizontal)
 {
 {
-	int col, corner;
+	int col, corner, ins_len;
+	char *line;
 
 
 	if (!HAS_BIT(flags, DRAW_FLAG_VER))
 	if (!HAS_BIT(flags, DRAW_FLAG_VER))
 	{
 	{
@@ -1510,7 +1557,13 @@ DO_DRAW(draw_line_horizontal)
 
 
 	corner = flags;
 	corner = flags;
 
 
-	arg = arg1;
+	sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_COL|SUB_LIT|SUB_ESC|SUB_VAR|SUB_FUN);
+
+	arg = arg2;
+
+	line = arg1;
+
+	arg1 += sprintf(arg1, "%s", box_color);
 
 
 	if (HAS_BIT(flags, DRAW_FLAG_LEFT))
 	if (HAS_BIT(flags, DRAW_FLAG_LEFT))
 	{
 	{
@@ -1543,15 +1596,40 @@ DO_DRAW(draw_line_horizontal)
 		arg1 += sprintf(arg1, "%s", draw_horizontal(flags, "─"));
 		arg1 += sprintf(arg1, "%s", draw_horizontal(flags, "─"));
 	}
 	}
 
 
+	if (*arg)
+	{
+		str_fix(line);
+
+		if (*txt_color)
+		{
+			str_cpy_printf(&arg, "%s%s", txt_color, arg);
+		}
+
+		ins_len = UMIN(cols, strip_vt102_strlen(ses, arg));
+
+		if (HAS_BIT(flags, DRAW_FLAG_RALIGN))
+		{
+			str_ins_str(ses, &line, arg, cols - ins_len, cols);
+		}
+		else if (HAS_BIT(flags, DRAW_FLAG_CALIGN))
+		{
+			str_ins_str(ses, &line, arg, cols / 2 - ins_len / 2, -1);
+		}
+		else
+		{
+			str_ins_str(ses, &line, arg, 0, ins_len);
+		}
+	}
+
 	if (HAS_BIT(flags, DRAW_FLAG_SCROLL))
 	if (HAS_BIT(flags, DRAW_FLAG_SCROLL))
 	{
 	{
-		tintin_printf2(ses, "%*s%s%s", top_col - 1, "", box_color, arg);
+		tintin_printf2(ses, "%*s%s", top_col - 1, "", line);
 	}
 	}
 	else
 	else
 	{
 	{
 		goto_pos(ses, top_row, top_col);
 		goto_pos(ses, top_row, top_col);
 
 
-		print_stdout(top_row, top_col, "%s%s", box_color, arg);
+		print_stdout(top_row, top_col, "%s", line);
 	}
 	}
 }
 }
 
 
@@ -1800,7 +1878,7 @@ DO_DRAW(draw_hbar)
 			case 7: ptb += sprintf(ptb, "▉"); break;
 			case 7: ptb += sprintf(ptb, "▉"); break;
 			case 8: ptb += sprintf(ptb, "█"); break;
 			case 8: ptb += sprintf(ptb, "█"); break;
 		}
 		}
-		ptb += sprintf(ptb, "%*s%s}", (bar - cnt) / 8, "", box_color);
+		ptb += snprintf(ptb, BUFFER_SIZE, "%*s%s}", (bar - cnt) / 8, "", box_color);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -1948,7 +2026,7 @@ DO_DRAW(draw_rain)
 				{
 				{
 					rand = generate_rand(ses) % max;
 					rand = generate_rand(ses) % max;
 
 
-					sprintf(arg2, "%s%.*s", lit_color_code(ses, code, 10), utfs[rand], rain[rand]);
+					snprintf(arg2, BUFFER_SIZE, "%s%.*s", lit_color_code(ses, code, 10), utfs[rand], rain[rand]);
 
 
 					substitute(ses, arg2, arg3, SUB_COL);
 					substitute(ses, arg2, arg3, SUB_COL);
 
 
@@ -2004,7 +2082,7 @@ DO_DRAW(draw_rain)
 
 
 				if (cnt == node->root->list[col]->val16[1])
 				if (cnt == node->root->list[col]->val16[1])
 				{
 				{
-					sprintf(arg2, "%s%.*s", lit_color_code(ses, code, 5), size, &node->root->list[col]->arg2[row]);
+					snprintf(arg2, BUFFER_SIZE, "%s%.*s", lit_color_code(ses, code, 5), size, &node->root->list[col]->arg2[row]);
 				}
 				}
 				else
 				else
 				{
 				{
@@ -2015,7 +2093,7 @@ DO_DRAW(draw_rain)
 						continue;
 						continue;
 					}
 					}
 
 
-					sprintf(arg2, "%s%.*s", fuzzy_color_code(ses, code), size, &node->root->list[col]->arg2[row]);
+					snprintf(arg2, BUFFER_SIZE, "%s%.*s", fuzzy_color_code(ses, code), size, &node->root->list[col]->arg2[row]);
 				}
 				}
 
 
 				substitute(ses, arg2, arg3, SUB_COL);
 				substitute(ses, arg2, arg3, SUB_COL);
@@ -2052,13 +2130,13 @@ DO_DRAW(draw_rain)
 				}
 				}
 				else if (node->root->list[col]->val16[0] - cnt < 0)
 				else if (node->root->list[col]->val16[0] - cnt < 0)
 				{
 				{
-					sprintf(arg2, "%s%.*s", fuzzy_color_code(ses, code), size, &node->root->list[col]->arg2[row]);
+					snprintf(arg2, BUFFER_SIZE, "%s%.*s", fuzzy_color_code(ses, code), size, &node->root->list[col]->arg2[row]);
 					substitute(ses, arg2, arg3, SUB_COL);
 					substitute(ses, arg2, arg3, SUB_COL);
 					print_stdout(0, 0, "%s", arg3);
 					print_stdout(0, 0, "%s", arg3);
 				}
 				}
 				else
 				else
 				{
 				{
-					sprintf(arg2, "%s%.*s", dim_color_code(ses, code, node->root->list[col]->val16[0] - cnt), size, &node->root->list[col]->arg2[row]);
+					snprintf(arg2, BUFFER_SIZE, "%s%.*s", dim_color_code(ses, code, node->root->list[col]->val16[0] - cnt), size, &node->root->list[col]->arg2[row]);
 					substitute(ses, arg2, arg3, SUB_COL);
 					substitute(ses, arg2, arg3, SUB_COL);
 					print_stdout(0, 0, "%s", arg3);
 					print_stdout(0, 0, "%s", arg3);
 				}
 				}
@@ -2379,7 +2457,8 @@ DO_DRAW(draw_text)
 
 
 	txt = buf2;
 	txt = buf2;
 
 
-	substitute(ses, arg, buf3, SUB_VAR|SUB_FUN);
+	substitute(ses, arg, buf1, SUB_VAR|SUB_FUN);
+	substitute(ses, buf1, buf3, SUB_ESC|SUB_COL|SUB_LIT);
 
 
 	arg = buf3;
 	arg = buf3;
 
 
@@ -2396,7 +2475,7 @@ DO_DRAW(draw_text)
 	{
 	{
 		while (*arg)
 		while (*arg)
 		{
 		{
-			arg = sub_arg_in_braces(ses, arg, buf1, GET_ALL, SUB_COL|SUB_LIT|SUB_ESC|SUB_VAR|SUB_FUN);
+			arg = get_arg_in_braces(ses, arg, buf1, GET_ALL);
 
 
 			txt += sprintf(txt, "%s\n", buf1);
 			txt += sprintf(txt, "%s\n", buf1);
 
 
@@ -2405,33 +2484,32 @@ DO_DRAW(draw_text)
 				arg++;
 				arg++;
 			}
 			}
 		}
 		}
-		string_to_font(ses, flags, buf2, buf2);
+
+		if (HAS_BIT(flags, DRAW_FLAG_FAT|DRAW_FLAG_CURSIVE|DRAW_FLAG_SANSSERIF))
+		{
+			string_to_font(ses, flags, buf2, buf2);
+		}
 	}
 	}
 
 
-	if (HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_TOP|DRAW_FLAG_PRUNED))
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_TOP|DRAW_FLAG_BLANKED|DRAW_FLAG_PRUNED))
 	{
 	{
 		top_row++;
 		top_row++;
 		rows--;
 		rows--;
 	}
 	}
 
 
-	if (HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_BOT|DRAW_FLAG_PRUNED))
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_BOT|DRAW_FLAG_BLANKED|DRAW_FLAG_PRUNED))
 	{
 	{
 		bot_row--;
 		bot_row--;
 		rows--;
 		rows--;
 	}
 	}
 
 
-	if (HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_LEFT|DRAW_FLAG_PRUNED))
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_LEFT|DRAW_FLAG_BLANKED|DRAW_FLAG_PRUNED))
 	{
 	{
-		strcpy(side1, " ");
 		cols--;
 		cols--;
 	}
 	}
 
 
-	if (HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_RIGHT|DRAW_FLAG_PRUNED))
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_RIGHT|DRAW_FLAG_BLANKED|DRAW_FLAG_PRUNED))
 	{
 	{
-		if (!HAS_BIT(flags, DRAW_FLAG_GRID) || HAS_BIT(flags, DRAW_FLAG_RIGHT))
-		{
-			strcpy(side2, " ");
-		}
 		cols--;
 		cols--;
 	}
 	}
 
 
@@ -2484,12 +2562,12 @@ DO_DRAW(draw_text)
 		}
 		}
 	}
 	}
 
 
-	if (HAS_BIT(flags, DRAW_FLAG_LEFT))
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_LEFT|DRAW_FLAG_BLANKED|DRAW_FLAG_PRUNED))
 	{
 	{
 		strcpy(side1, draw_vertical(flags, "│"));
 		strcpy(side1, draw_vertical(flags, "│"));
 	}
 	}
 
 
-	if (HAS_BIT(flags, DRAW_FLAG_RIGHT))
+	if (HAS_BIT(flags, DRAW_FLAG_BOXED|DRAW_FLAG_RIGHT|DRAW_FLAG_BLANKED|DRAW_FLAG_PRUNED))
 	{
 	{
 		strcpy(side2, draw_vertical(flags, "│"));
 		strcpy(side2, draw_vertical(flags, "│"));
 	}
 	}

+ 1 - 1
src/files.c

@@ -465,7 +465,7 @@ DO_COMMAND(do_write)
 		{
 		{
 			node = root->list[j];
 			node = root->list[j];
 
 
-			if (node->shots)//HAS_BIT(node->flags, NODE_FLAG_ONESHOT))
+			if (node->shots)
 			{
 			{
 				continue;
 				continue;
 			}
 			}

+ 2 - 2
src/gui.h

@@ -311,7 +311,7 @@ char *tt_gui = "#line quiet #port init gui 0\n"
 "		#return {}\n"
 "		#return {}\n"
 "	};\n"
 "	};\n"
 "	#local temp {};\n"
 "	#local temp {};\n"
-"	#format temp %+64z %0;\n"
+"	#format temp %+64z {%0};\n"
 "	#return $temp\n"
 "	#return $temp\n"
 "}\n"
 "}\n"
 "\n"
 "\n"
@@ -322,7 +322,7 @@ char *tt_gui = "#line quiet #port init gui 0\n"
 "		#return {}\n"
 "		#return {}\n"
 "	};\n"
 "	};\n"
 "	#local temp {};\n"
 "	#local temp {};\n"
-"	#format temp %+64Z %0;\n"
+"	#format temp %+64Z {%0};\n"
 "	#return $temp\n"
 "	#return $temp\n"
 "}\n"
 "}\n"
 "\n"
 "\n"

+ 249 - 69
src/help.c

@@ -304,9 +304,6 @@ struct help_type help_table[] =
 		"         variables are substituted from the message and can be used in the\n"
 		"         variables are substituted from the message and can be used in the\n"
 		"         command part of the action.\n"
 		"         command part of the action.\n"
 		"\n"
 		"\n"
-		"         The priority part is optional and determines the priority of the\n"
-		"         action, it defaults to 5.\n"
-		"\n"
 		"         If the message starts with a ~ color codes must be matched. You can\n"
 		"         If the message starts with a ~ color codes must be matched. You can\n"
 		"         enable #config {convert meta} on to display meta characters.\n"
 		"         enable #config {convert meta} on to display meta characters.\n"
 		"\n"
 		"\n"
@@ -314,19 +311,16 @@ struct help_type help_table[] =
 		"\n"
 		"\n"
 		"<178>Example<278>: #action {%1 tells you '%2'} {tell %1 I'm afk.}\n"
 		"<178>Example<278>: #action {%1 tells you '%2'} {tell %1 I'm afk.}\n"
 		"\n"
 		"\n"
-		"         Actions can be triggered by the show command and certain system\n"
-		"         messages.\n"
-		"\n"
 		"         Actions can be triggered by the #show command. If you don't want a\n"
 		"         Actions can be triggered by the #show command. If you don't want a\n"
 		"         #show to get triggered use: #line ignore #show {text}\n"
 		"         #show to get triggered use: #line ignore #show {text}\n"
 		"\n"
 		"\n"
 		"         Actions are ordered alphabetically and only one action can trigger at\n"
 		"         Actions are ordered alphabetically and only one action can trigger at\n"
 		"         a time. To change the order you can assign a priority, which defaults\n"
 		"         a time. To change the order you can assign a priority, which defaults\n"
 		"         to 5, with a lower number indicating a higher priority. The priority\n"
 		"         to 5, with a lower number indicating a higher priority. The priority\n"
-		"         can be a floating point number.\n"
+		"         can be a floating point number and should be between 1 and 9.\n"
 		"\n"
 		"\n"
-		"         To remove action with %* as the message, use #unaction {%%*} or\n"
-		"         #unaction {\%*}. Alternatively you could wrap the action inside a\n"
+		"         To remove an action with %* as the message, use #unaction {%%*} or\n"
+		"         #unaction {\\%*}. Alternatively you could wrap the action inside a\n"
 		"         class, and kill that class when you no longer need the action.\n"
 		"         class, and kill that class when you no longer need the action.\n"
 		"\n"
 		"\n"
 		"<178>Comment<278>: You can remove an action with the #unaction command.\n",
 		"<178>Comment<278>: You can remove an action with the #unaction command.\n",
@@ -367,7 +361,7 @@ struct help_type help_table[] =
 		"         can be a floating point number.\n"
 		"         can be a floating point number.\n"
 		"\n"
 		"\n"
 		"         To remove an alias with %* as the name, use #unalias {%%*} or #unalias\n"
 		"         To remove an alias with %* as the name, use #unalias {%%*} or #unalias\n"
-		"         {\%*}. Alternatively you can wrap the alias inside a class, and kill\n"
+		"         {\\%*}. Alternatively you can wrap the alias inside a class, and kill\n"
 		"         that class when you no longer need the alias.\n"
 		"         that class when you no longer need the alias.\n"
 		"\n"
 		"\n"
 		"         For more information on pattern matching see the section on PCRE.\n"
 		"         For more information on pattern matching see the section on PCRE.\n"
@@ -447,34 +441,45 @@ struct help_type help_table[] =
 		"         The buffer command has various options to manipulate your scrollback\n"
 		"         The buffer command has various options to manipulate your scrollback\n"
 		"         buffer.\n"
 		"         buffer.\n"
 		"\n"
 		"\n"
-		"         <178>#buffer {home}\n"
-		"<278>\n"
-		"         Moves you to the top of your scrollback buffer and displays the page.\n"
-		"         Enables scroll lock mode. Most useful when used in a #macro.\n"
+		"         The size of the scrollback buffer can be configured using #config\n"
+		"         buffer_size <size>. The size must be either 100, 1000, 10000, 100000\n"
+		"         or 1000000 lines.\n"
 		"\n"
 		"\n"
-		"         <178>#buffer {up} [lines]\n"
+		"         While scrolling through the scrollback buffer incoming text is not\n"
+		"         displayed, this can be disabled using #config scroll_lock off. The\n"
+		"         scroll lock is automatically disabled when manual input is received,\n"
+		"         subsequently #buffer up and down only work properly when used in a\n"
+		"         macro or mouse event.\n"
+		"\n"
+		"         <178>#buffer {clear} {[lower bound]} {[upper bound]}\n"
 		"<278>\n"
 		"<278>\n"
-		"         Moves your scrollback buffer up one page and displays the page.\n"
-		"         Enables scroll lock mode. Most useful when used in a #macro. You\n"
-		"         can use #buffer {up} {1} to move the scrollback buffer up 1 line.\n"
+		"         Without an argument this will clear the entire scrollback buffer.\n"
+		"         Otherwise it will clear the given range.\n"
+		"\n"
+		"         Positive numbers are measured from the start of the scrollback buffer,\n"
+		"         negative numbers from the end.\n"
 		"\n"
 		"\n"
 		"         <178>#buffer {down} [lines]\n"
 		"         <178>#buffer {down} [lines]\n"
 		"<278>\n"
 		"<278>\n"
-		"         Moves your scrollback buffer down one page and displays the page.\n"
-		"         Enables scroll lock mode unless at the end. Most useful when used in\n"
-		"         a #macro.\n"
+		"         Moves your scrollback buffer down one page and displays the page. If\n"
+		"         a line number is provided it will scroll down the given number of\n"
+		"         lines.\n"
 		"\n"
 		"\n"
 		"         <178>#buffer {end}\n"
 		"         <178>#buffer {end}\n"
 		"<278>\n"
 		"<278>\n"
 		"         Moves you to the end of your scrollback buffer and displays the page.\n"
 		"         Moves you to the end of your scrollback buffer and displays the page.\n"
 		"         Disables scroll lock mode. Most useful when used in a #macro.\n"
 		"         Disables scroll lock mode. Most useful when used in a #macro.\n"
 		"\n"
 		"\n"
-		"         <178>#buffer {find} {[number]} {<string>}\n"
+		"         <178>#buffer {find} {[number]} {<string>} {[variable]}\n"
 		"<278>\n"
 		"<278>\n"
 		"         Moves the buffer to the given string which can contain a regular\n"
 		"         Moves the buffer to the given string which can contain a regular\n"
 		"         expression. Optionally you can provide the number of matches to skip,\n"
 		"         expression. Optionally you can provide the number of matches to skip,\n"
 		"         allowing you to jump further back in the buffer.\n"
 		"         allowing you to jump further back in the buffer.\n"
 		"\n"
 		"\n"
+		"         A positive number searches from the start of the buffer, a negative\n"
+		"         number from the end. If you provide a variable the location will be\n"
+		"         stored and no jump takes place.\n"
+		"\n"
 		"         <178>#buffer {get} {<variable>} {<lower bound>} {[upper bound]}\n"
 		"         <178>#buffer {get} {<variable>} {<lower bound>} {[upper bound]}\n"
 		"<278>\n"
 		"<278>\n"
 		"         Allows you to store one or several lines from your scrollback buffer\n"
 		"         Allows you to store one or several lines from your scrollback buffer\n"
@@ -483,10 +488,23 @@ struct help_type help_table[] =
 		"         omitted the given line is stored as a standard variable. If an upper\n"
 		"         omitted the given line is stored as a standard variable. If an upper\n"
 		"         bound is given the lines between the two bounds are stored as a list.\n"
 		"         bound is given the lines between the two bounds are stored as a list.\n"
 		"\n"
 		"\n"
+		"         Positive numbers are measured from the start of the scrollback buffer,\n"
+		"         negative numbers from the end.\n"
+		"\n"
+		"         <178>#buffer {home}\n"
+		"<278>\n"
+		"         Moves you to the top of your scrollback buffer and displays the page.\n"
+		"         Enables scroll lock mode. Most useful when used in a #macro.\n"
+		"\n"
 		"         <178>#buffer {info} {[save]} {[variable]}\n"
 		"         <178>#buffer {info} {[save]} {[variable]}\n"
 		"<278>\n"
 		"<278>\n"
 		"         Display buffer info, optionally save the data to a variable.\n"
 		"         Display buffer info, optionally save the data to a variable.\n"
 		"\n"
 		"\n"
+		"         <178>#buffer {jump} {<location>}\n"
+		"<278>\n"
+		"         Moves the buffer to the given location. A positive number jumps from\n"
+		"         the start of the buffer, a negative number from the end.\n"
+		"\n"
 		"         <178>#buffer {lock} {on|off}\n"
 		"         <178>#buffer {lock} {on|off}\n"
 		"<278>\n"
 		"<278>\n"
 		"         Toggles the lock on the scrollback buffer. When locked, newly incoming\n"
 		"         Toggles the lock on the scrollback buffer. When locked, newly incoming\n"
@@ -494,6 +512,17 @@ struct help_type help_table[] =
 		"         several buffer commands will re-enable the lock. When unlocking it'll\n"
 		"         several buffer commands will re-enable the lock. When unlocking it'll\n"
 		"         move you to the end of your scrollback buffer and display the page.\n"
 		"         move you to the end of your scrollback buffer and display the page.\n"
 		"\n"
 		"\n"
+		"         <178>#buffer {refresh}\n"
+		"<278>\n"
+		"         Marks the buffer as needing to be refreshed, only useful while in\n"
+		"         vertical split mode.\n"
+		"\n"
+		"         <178>#buffer {up} [lines]\n"
+		"<278>\n"
+		"         Moves your scrollback buffer up one page and displays the page.\n"
+		"         Enables scroll lock mode. Most useful when used in a #macro. You\n"
+		"         can use #buffer {up} {1} to move the scrollback buffer up 1 line.\n"
+		"\n"
 		"         <178>#buffer {write} {<filename>}\n"
 		"         <178>#buffer {write} {<filename>}\n"
 		"<278>\n"
 		"<278>\n"
 		"         Writes the scrollback buffer to the given file.\n"
 		"         Writes the scrollback buffer to the given file.\n"
@@ -720,7 +749,6 @@ struct help_type help_table[] =
 		"         <278>  Will store the size of the class in a variable.\n"
 		"         <278>  Will store the size of the class in a variable.\n"
 		"         <178>#class {<name>} {write} {<filename>}\n"
 		"         <178>#class {<name>} {write} {<filename>}\n"
 		"         <278>  Will write all triggers of the given class to file.\n"
 		"         <278>  Will write all triggers of the given class to file.\n"
-		"         The {kill} option will delete all triggers of the given class.\n"
 		"\n"
 		"\n"
 		"         Keep in mind that the kill and read option are very fast allowing\n"
 		"         Keep in mind that the kill and read option are very fast allowing\n"
 		"         them to be used to enable and disable classes.\n"
 		"         them to be used to enable and disable classes.\n"
@@ -797,14 +825,42 @@ struct help_type help_table[] =
 		TOKEN_TYPE_STRING,
 		TOKEN_TYPE_STRING,
 		"<278>\n"
 		"<278>\n"
 		"         When the 0,0 coordinate is in the upper left corner TinTin++ uses\n"
 		"         When the 0,0 coordinate is in the upper left corner TinTin++ uses\n"
-		"         a y,x / rows,cols notation. When the 0,0 coordinate is in the\n"
-		"         bottom left corner tintin uses a x,y / cols/rows notation.\n"
+		"         a y,x / row,col notation, starting at 1,1. Subsequently -1,-1\n"
+		"         will indicate the bottom right corner. This type of argument is\n"
+		"         used by the #showme command.\n"
 		"\n"
 		"\n"
-		"         When a square is defined this is done by specifying the upper left\n"
-		"         and bottom right corner of the square using four coordinates.\n"
+		"         When the 0,0 coordinate is in the bottom left corner tintin uses\n"
+		"         a standard x,y notation. This type of argument is used by the\n"
+		"         #map jump command.\n"
 		"\n"
 		"\n"
-		"         The vast majority of tintin commands use row,col notation.\n",
-		
+		"         The vast majority of tintin commands use y,x / row,col notation,\n"
+		"         primarily because that is the notation used by the VT100 standard\n"
+		"         used for terminal emulation.\n"
+		"\n"
+		"         <128>Squares\n"
+		"<278>\n"
+		"         A square argument takes 2 coordinates. The first coordinate defines\n"
+		"         the upper left corner, the last coordinate defines the bottom\n"
+		"         right corner. The upper left corner of the terminal is defined as\n"
+		"         1,1 and the bottom right corner as -1,-1. This type of argument is\n"
+		"         used by #draw, #button and #map offset.\n"
+		"\n"
+		"         <128>Panes\n"
+		"<278>\n"
+		"         A pane argument takes 4 size values, which are: top pane, bottom\n"
+		"         pane, left pane, right pane. When a negative value is provided the\n"
+		"         size is the maximum size, minus the value. This type of argument\n"
+		"         is used by the #split command.\n"
+		"\n"
+		"         <128>Ranges\n"
+		"<278>\n"
+		"         A range argument takes 2 values known as the upper bound and lower\n"
+		"         bound. The upper bound (first value) defines the start of the\n"
+		"         range, the lower bound (second value) the end. The first index of\n"
+		"         a range is defined as 1. When a negative value is provides the last\n"
+		"         index is defined as -1. This type of argument is used by #buffer\n"
+		"         and #variable.\n",
+
 		"characters colors escape mathematics pcre"
 		"characters colors escape mathematics pcre"
 	},
 	},
 	{
 	{
@@ -922,7 +978,10 @@ struct help_type help_table[] =
 		"         <278>  Kills all daemons or daemons with matching name.\n"
 		"         <278>  Kills all daemons or daemons with matching name.\n"
 		"\n"
 		"\n"
 		"         <178>#daemon list [name]\n"
 		"         <178>#daemon list [name]\n"
-		"         <278>  List all daemons or daemons with matching name.\n",
+		"         <278>  List all daemons or daemons with matching name.\n"
+		"\n"
+		"         <278>You can launch tintin and attach the first daemonized instance using\n"
+		"         tt++ -R. To attach a named instance use tt++ -R<name>.\n",
 		
 		
 		"script system run"
 		"script system run"
 	},
 	},
@@ -1464,8 +1523,8 @@ struct help_type help_table[] =
 		"         <128>SESSION EVENTS<278>\n"
 		"         <128>SESSION EVENTS<278>\n"
 		"\n"
 		"\n"
 		"         SESSION ACTIVATED      %0 name\n"
 		"         SESSION ACTIVATED      %0 name\n"
-		"         SESSION CONNECTED      %0 name %1 host %2 ip %3 port\n"
-		"         SESSION CREATED        %0 name %1 host %2 ip %3 port\n"
+		"         SESSION CONNECTED      %0 name %1 host %2 ip %3 port %4 file\n"
+		"         SESSION CREATED        %0 name %1 host %2 ip %3 port %4 file\n"
 		"         SESSION DEACTIVATED    %0 name\n"
 		"         SESSION DEACTIVATED    %0 name\n"
 		"         SESSION DISCONNECTED   %0 name %1 host %2 ip %3 port\n"
 		"         SESSION DISCONNECTED   %0 name %1 host %2 ip %3 port\n"
 		"         SESSION TIMED OUT      %0 name %1 host %2 ip %3 port\n"
 		"         SESSION TIMED OUT      %0 name %1 host %2 ip %3 port\n"
@@ -1863,7 +1922,7 @@ struct help_type help_table[] =
 		"\n"
 		"\n"
 		"         All commands can be separated with a ';'.\n"
 		"         All commands can be separated with a ';'.\n"
 		"\n"
 		"\n"
-		"         n;l green;s;say Dan Dare is back! -- do these 4 commands\n"
+		"         n;l dragon;s;say Dan Dare is back! -- do these 4 commands\n"
 		"         There are 3 ways ';'s can be overruled.\n"
 		"         There are 3 ways ';'s can be overruled.\n"
 		"\n"
 		"\n"
 		"         \\say Hello ;) -- Lines starting with a '\\' aren't parsed by TinTin++.\n"
 		"         \\say Hello ;) -- Lines starting with a '\\' aren't parsed by TinTin++.\n"
@@ -1909,12 +1968,6 @@ struct help_type help_table[] =
 		"\n"
 		"\n"
 		"<178>Example<278>: #alias greet say Greetings, most honorable %1\n"
 		"<178>Example<278>: #alias greet say Greetings, most honorable %1\n"
 		"\n"
 		"\n"
-		"         If there are no variables on the right-side of the alias definition,\n"
-		"         any arguments following the aliases-command will be appended to the\n"
-		"         command string.\n"
-		"\n"
-		"<178>Example<278>: #alias ff cast 'fireball' -- 'ff bob' equals: cast 'fireball' bob\n"
-		"\n"
 		"         If you want an alias to execute more commands, you must use braces.\n"
 		"         If you want an alias to execute more commands, you must use braces.\n"
 		"\n"
 		"\n"
 		"<178>Example<278>: #alias ws <178>{<278>wake;stand<178>}<278>\n"
 		"<178>Example<278>: #alias ws <178>{<278>wake;stand<178>}<278>\n"
@@ -1929,7 +1982,7 @@ struct help_type help_table[] =
 		"\n"
 		"\n"
 		"         Or by using the send command.\n"
 		"         Or by using the send command.\n"
 		"\n"
 		"\n"
-		"<178>Example<278>: #send put %1 in %2\n"
+		"<178>Example<278>: #alias put #send put %1 in %2\n"
 		"\n"
 		"\n"
 		"\n"
 		"\n"
 		"<128>         Action\n"
 		"<128>         Action\n"
@@ -2100,9 +2153,12 @@ struct help_type help_table[] =
 		"         triggers/variables associated with that list. With the SAVE option\n"
 		"         triggers/variables associated with that list. With the SAVE option\n"
 		"         this data is written to the info variable.\n"
 		"         this data is written to the info variable.\n"
 		"\n"
 		"\n"
+		"         #info arguments will show matched trigger arguments.\n"
+		"         #info big5toutf will show the big5 to utf8 translation table.\n"
 		"         #info cpu will show information about tintin's cpu usage.\n"
 		"         #info cpu will show information about tintin's cpu usage.\n"
 		"         #info environ will show the environment variables.\n"
 		"         #info environ will show the environment variables.\n"
 		"         #info input will show information about the input line.\n"
 		"         #info input will show information about the input line.\n"
+		"         #info matches will show matched command arguments.\n"
 		"         #info mccp will show information about data compression.\n"
 		"         #info mccp will show information about data compression.\n"
 		"         #info memory will show information about the memory stack.\n"
 		"         #info memory will show information about the memory stack.\n"
 		"         #info stack will show the low level debugging stack.\n"
 		"         #info stack will show the low level debugging stack.\n"
@@ -2256,16 +2312,17 @@ struct help_type help_table[] =
 		"\n"
 		"\n"
 		"         #list {var} {add} {item}               Add {item} to the list\n"
 		"         #list {var} {add} {item}               Add {item} to the list\n"
 		"         #list {var} {clear}                    Empty the given list\n"
 		"         #list {var} {clear}                    Empty the given list\n"
-		"         #list {var} {collapse}                 Turn list into a variable\n"
-		"         #list {var} {create} {item}            Create a list using {items}\n"
-		"         #list {var} {delete} {index} {number}  Delete the item at {index},\n"
-		"                                                the {number} is optional.\n"
-		"         #list {var} {explode}                  Turn list into a character list\n"
+		"         #list {var} {collapse} {separator}     Turn list into a variable\n"
+		"         #list {var} {create} {items}           Create a list using {items}\n"
+		"         #list {var} {delete} {index} {amount}  Delete the item at {index},\n"
+		"                                                the {amount} is optional.\n"
+		"         #list {var} {explode} {separator}      Turn variable into a list\n"
 		"         #list {var} {indexate}                 Index a list table for sorting\n"
 		"         #list {var} {indexate}                 Index a list table for sorting\n"
 		"         #list {var} {insert} {index} {string}  Insert {string} at given index\n"
 		"         #list {var} {insert} {index} {string}  Insert {string} at given index\n"
 		"         #list {var} {filter} {keep} {remove}   Filter with keep / remove regex\n"
 		"         #list {var} {filter} {keep} {remove}   Filter with keep / remove regex\n"
 		"         #list {var} {find} {regex} {variable}  Return the found index\n"
 		"         #list {var} {find} {regex} {variable}  Return the found index\n"
 		"         #list {var} {get} {index} {variable}   Copy an item to {variable}\n"
 		"         #list {var} {get} {index} {variable}   Copy an item to {variable}\n"
+		"         #list {var} {numerate}                 Turn a table into a list\n"
 		"         #list {var} {order} {string}           Insert item in numerical order\n"
 		"         #list {var} {order} {string}           Insert item in numerical order\n"
 		"         #list {var} {shuffle}                  Shuffle the list\n"
 		"         #list {var} {shuffle}                  Shuffle the list\n"
 		"         #list {var} {set} {index} {string}     Change the item at {index}\n"
 		"         #list {var} {set} {index} {string}     Change the item at {index}\n"
@@ -2274,7 +2331,7 @@ struct help_type help_table[] =
 		"         #list {var} {sort} {string}            Insert item in alphabetic order\n"
 		"         #list {var} {sort} {string}            Insert item in alphabetic order\n"
 		"         #list {var} {tokenize} {string}        Create a character list\n"
 		"         #list {var} {tokenize} {string}        Create a character list\n"
 		"\n"
 		"\n"
-		"         The index should be between 1 and the list's length. You can also give\n"
+		"         The index should be between +1 and the list's size. You can also give\n"
 		"         a negative value, in which case -1 equals the last item in the list, -2\n"
 		"         a negative value, in which case -1 equals the last item in the list, -2\n"
 		"         the second last, etc.\n"
 		"         the second last, etc.\n"
 		"\n"
 		"\n"
@@ -2284,10 +2341,11 @@ struct help_type help_table[] =
 		"         The add and create options allow using multiple items, as well\n"
 		"         The add and create options allow using multiple items, as well\n"
 		"         as semicolon separated items.\n"
 		"         as semicolon separated items.\n"
 		"\n"
 		"\n"
-		"         A length of 0 is returned for an empty or non existant list.\n"
+		"         A size of 0 is returned for an empty or non-existent list. You can\n"
+		"         directly access the size of a list using &var[].\n"
 		"\n"
 		"\n"
-		"         You can directly access elements in a list variable using $var[1],\n"
-		"         $var[2], $var[-1], etc.\n",
+		"         You can directly access elements in a list variable using $var[+1],\n"
+		"         $var[+2], $var[-1], etc.\n",
 
 
 		"break continue foreach loop parse repeat return while"
 		"break continue foreach loop parse repeat return while"
 	},
 	},
@@ -2504,6 +2562,47 @@ struct help_type help_table[] =
 		"         while executing scripts you can use '#debug all on'. To stop seeing\n"
 		"         while executing scripts you can use '#debug all on'. To stop seeing\n"
 		"         debug information use '#debug all off'.\n"
 		"         debug information use '#debug all off'.\n"
 		"<128>\n"
 		"<128>\n"
+		"         List Tables\n"
+		"<278>\n"
+                "         List tables are also known as databases and the #list command has\n"
+                "         several options to manipulate them.\n"
+                "\n"
+                "         For these options to work properly all tables need to have identical\n"
+                "         keys. Here is an example list table.\n"
+                "\n"
+                "         #var {friendlist}\n"
+                "         {\n"
+                "             {1}{{name}{bob} {age}{54}}\n"
+                "             {2}{{name}{bubba} {age}{21}}\n"
+                "             {3}{{name}{pamela} {age}{36}}\n"
+                "         }\n"
+                "\n"
+                "         To sort the list table by age you would use:\n"
+                "\n"
+                "         #list friendlist indexate age\n"
+                "         #list friendlist order\n"
+                "\n"
+                "         To remove everyone whose name starts with a 'b' you would use:\n"
+                "\n"
+                "         #list friendlist indexate name\n"
+                "         #list friendlist filter {} {b%*}\n"
+                "\n"
+                "         The filter option only supports regular expressions. To filter\n"
+                "         using mathematics you would loop through the list backwards:\n"
+                "\n"
+                "         #loop &friendlist[] 1 index\n"
+                "         {\n"
+                "             #if {$friendlist[+$index][age] < 30}\n"
+                "             {\n"
+                "                 #list friendlist delete $index\n"
+                "             }\n"
+                "         }\n"
+                "\n"
+                "         To add an item to a list table there are two options:\n"
+                "\n"
+                "         #list friendlist add {{{name}{hobo} {age}{42}}}\n"
+                "         #list friendlist insert -1 {{name}{hobo} {age}{42}}\n"
+		"<128>\n"
 		"         Optimization\n"
 		"         Optimization\n"
 		"<278>\n"
 		"<278>\n"
 		"         TinTin++ tables are exceptionally fast while they remain under 100\n"
 		"         TinTin++ tables are exceptionally fast while they remain under 100\n"
@@ -2734,12 +2833,16 @@ struct help_type help_table[] =
 		"         <278>  searches for the given room name. If found the shortest path\n"
 		"         <278>  searches for the given room name. If found the shortest path\n"
 		"         <278>  from your current location to the destination is calculated.\n"
 		"         <278>  from your current location to the destination is calculated.\n"
 		"         <278>  The route is stored in #path and can subsequently be used with\n"
 		"         <278>  The route is stored in #path and can subsequently be used with\n"
-		"         <278>  the various #path commands. If <exits> is provided all exits\n"
-		"         <278>  must be matched, if <roomdesc>, <roomarea> or <roomnote> or\n"
-		"         <278>  <roomterrain> or <roomflag> is provided these are matched as\n"
-		"         <278>  well against the room to be found.\n"
-		"         <278>  These options are also available to the at, delete, goto\n"
-		"         <278>  link, list and run commands.\n"
+		"         <278>  the various #path commands. If #map flag nofollow is set it\n"
+		"         <278>  will store the exit commands instead of the exit names.\n"
+		"\n"
+		"         <278>  If <exits> is provided all exits must be matched, if\n"
+		"         <278>  <roomdesc>, <roomarea> or <roomnote> or <roomterrain> or\n"
+		"         <278>  <roomflag> is provided these are matched as well against the\n"
+		"         <278>  room to be found.\n"
+		"\n"
+		"         <278>  These search options are also available for the at, delete,\n"
+		"         <278>  goto, link, list and run commands.\n"
 		"\n"
 		"\n"
 		"         <178>#map flag asciigraphics\n"
 		"         <178>#map flag asciigraphics\n"
 		"         <278>  Takes up more space but draws a more detailed\n"
 		"         <278>  Takes up more space but draws a more detailed\n"
@@ -2751,7 +2854,9 @@ struct help_type help_table[] =
 		"         <178>#map flag nofollow\n"
 		"         <178>#map flag nofollow\n"
 		"         <278>  When you enter movement commands the map will no longer\n"
 		"         <278>  When you enter movement commands the map will no longer\n"
 		"         <278>  automatically follow along. Useful for MSDP and GMCP\n"
 		"         <278>  automatically follow along. Useful for MSDP and GMCP\n"
-		"         <278>  automapping scripts.\n"
+		"         <278>  automapping scripts. When you use #map find in nofollow\n"
+		"         <278>  mode it will store the exit command instead of the exit\n"
+		"         <278>  name into the path.\n"
 		"\n"
 		"\n"
 		"         <178>#map flag static\n"
 		"         <178>#map flag static\n"
 		"         <278>  Will make the map static so new rooms are no longer\n"
 		"         <278>  Will make the map static so new rooms are no longer\n"
@@ -2789,8 +2894,9 @@ struct help_type help_table[] =
 		"         <278>  Takes you to the given room name, if you provide exits those\n"
 		"         <278>  Takes you to the given room name, if you provide exits those\n"
 		"         <278>  must match.\n"
 		"         <278>  must match.\n"
 		"\n"
 		"\n"
-		"         <178>#map info\n"
-		"         <278>  Gives information about the map and room you are in.\n"
+		"         <178>#map info [save]\n"
+		"         <278>  Gives information about the map and room you are in. If the save\n"
+		"         <278>  argument is given the map data is saved to the info[map] variable.\n"
 		"\n"
 		"\n"
 		"         <178>#map insert <direction> [roomflag]\n"
 		"         <178>#map insert <direction> [roomflag]\n"
 		"         <278>  Insert a room in the given direction. Most useful for inserting\n"
 		"         <278>  Insert a room in the given direction. Most useful for inserting\n"
@@ -2946,7 +3052,7 @@ struct help_type help_table[] =
 		"         <178>#map undo\n"
 		"         <178>#map undo\n"
 		"         <278>  Will undo your last move. If this created a room or a link\n"
 		"         <278>  Will undo your last move. If this created a room or a link\n"
 		"         <278>  they will be deleted, otherwise you'll simply move back a\n"
 		"         <278>  they will be deleted, otherwise you'll simply move back a\n"
-		"         <278>  room. Useful if you walked into a non existant direction.\n"
+		"         <278>  room. Useful if you walked into a non-existent direction.\n"
 		"\n"
 		"\n"
 		"         <178>#map uninsert <direction>\n"
 		"         <178>#map uninsert <direction>\n"
 		"         <278>  Exact opposite of the insert command.\n"
 		"         <278>  Exact opposite of the insert command.\n"
@@ -3114,7 +3220,7 @@ struct help_type help_table[] =
 		"         -               3            integer subtraction\n"
 		"         -               3            integer subtraction\n"
 		"         <<              4            bitwise shift\n"
 		"         <<              4            bitwise shift\n"
 		"         >>              4            bitwise shift\n"
 		"         >>              4            bitwise shift\n"
-		"         ..              4            bitwise ellipsis\n"
+		"         ..              4            integer range\n"
 		"         >               5            logical greater than\n"
 		"         >               5            logical greater than\n"
 		"         >=              5            logical greater than or equal\n"
 		"         >=              5            logical greater than or equal\n"
 		"         <               5            logical less than\n"
 		"         <               5            logical less than\n"
@@ -3495,13 +3601,13 @@ struct help_type help_table[] =
 		"         If you use %1 in an action to perform a match the matched string is\n"
 		"         If you use %1 in an action to perform a match the matched string is\n"
 		"         stored in the %1 variable which can be used in the action body.\n"
 		"         stored in the %1 variable which can be used in the action body.\n"
 		"\n"
 		"\n"
-		"Example: %1 says 'Tickle me'} {tickle %1}\n"
+		"Example: #act {%1 says 'Tickle me'} {tickle %1}\n"
 		"\n"
 		"\n"
 		"         If you use %2 the match is stored in %2, etc. If you use an unnumbered\n"
 		"         If you use %2 the match is stored in %2, etc. If you use an unnumbered\n"
 		"         match like %* or %S the match is stored at the last used index\n"
 		"         match like %* or %S the match is stored at the last used index\n"
 		"         incremented by one.\n"
 		"         incremented by one.\n"
 		"\n"
 		"\n"
-		"Example: %3 says '%*'} {#if {\"%4\" == \"Tickle me\"} {tickle %3}}\n"
+		"Example: #act {%3 says '%*'} {#if {\"%4\" == \"Tickle me\"} {tickle %3}}\n"
 		"\n"
 		"\n"
 		"         The maximum variable index is 99. If you begin an action with %* the\n"
 		"         The maximum variable index is 99. If you begin an action with %* the\n"
 		"         match is stored in %1. You should never use %0 in the trigger part of\n"
 		"         match is stored in %1. You should never use %0 in the trigger part of\n"
@@ -3526,7 +3632,7 @@ struct help_type help_table[] =
 		"\n"
 		"\n"
 		"         You can group alternatives and ranges within a PCRE using brackets.\n"
 		"         You can group alternatives and ranges within a PCRE using brackets.\n"
 		"\n"
 		"\n"
-		"Example: #act {%* says 'Who is number {[1-9]}} {say $number[%2] is number %2}\n"
+		"Example: #act {%* says 'Who is number {[1-9]}?} {say $number[%2] is number %2}\n"
 		"\n"
 		"\n"
 		"         The example only triggers if someone provides a number between 1 and\n"
 		"         The example only triggers if someone provides a number between 1 and\n"
 		"         9. Any other character will cause the action to not trigger.\n"
 		"         9. Any other character will cause the action to not trigger.\n"
@@ -3551,7 +3657,7 @@ struct help_type help_table[] =
 		"    {n,} repeat at least n times, n must be a number.\n"
 		"    {n,} repeat at least n times, n must be a number.\n"
 		"   {n,o} repeat between n and o times, n and o must be a number.\n"
 		"   {n,o} repeat between n and o times, n and o must be a number.\n"
 		"\n"
 		"\n"
-		"Example: #act {%* says 'Who is number {[1-9][0-9]{0,2}} {Say $number[%2] is\n"
+		"Example: #act {%* says 'Who is number {[1-9][0-9]{0,2}}?} {Say $number[%2] is\n"
 		"           number %2}\n"
 		"           number %2}\n"
 		"\n"
 		"\n"
 		"         The example only triggers if someone provides a number between 1 and\n"
 		"         The example only triggers if someone provides a number between 1 and\n"
@@ -4051,10 +4157,10 @@ struct help_type help_table[] =
 		"\n"
 		"\n"
 		"         Starts a telnet session with the given name, host, port, and optional\n"
 		"         Starts a telnet session with the given name, host, port, and optional\n"
 		"         file name. The name can be anything you want, except the name of an\n"
 		"         file name. The name can be anything you want, except the name of an\n"
-		"         already existant session, a number, or the keywords '+' and '-'.\n"
+		"         already existing session, a number, or the keywords '+' and '-'.\n"
 		"\n"
 		"\n"
-		"         If a file name is given the file is only read if the session succesfully\n"
-		"         connects.\n"
+		"         If a file name is given the file is only read if the session\n"
+		"         succesfully connects.\n"
 		"\n"
 		"\n"
 		"         Without an argument #session shows the currently defined sessions.\n"
 		"         Without an argument #session shows the currently defined sessions.\n"
 		"\n"
 		"\n"
@@ -4308,6 +4414,78 @@ struct help_type help_table[] =
 
 
 		"action gag highlight prompt"
 		"action gag highlight prompt"
 	},
 	},
+	{
+		"SUBSTITUTIONS",
+		TOKEN_TYPE_STRING,
+		"<278>          TinTin++ will perform various types of substitions as detailed below.\n"
+		"\n"
+		"<128>          Variables\n"
+		"\n"
+		"<178>$ & * @<278>   All variable and function names must begin with an alphabetic\n"
+		"          character, followed by any combination of alphanumeric characters and\n"
+		"          underscores.\n"
+		"\n"
+		"<178>$<278>         The dollar sign is used to retrieve the value of a variable.\n"
+		"\n"
+		"<178>&<278>         The ampersand sign is used to retrieve the index of a variable.\n"
+		"\n"
+		"<178>*<278>         The astrix sign is used to retrieve the name of a variable.\n"
+		"\n"
+		"<178>@<278>         The at sign is used for functions.\n"
+		"\n"
+		"<178>[ ]<278>       Brackets are used for nested variables which function as an\n"
+		"          associative array. Associative arrays are also known as tables and\n"
+		"          maps. Regex can be used within brackets to match multiple variables.\n"
+		"\n"
+		"<178>+ -<278>       The plus and minus signs are used to access variables by their index,\n"
+		"          with the first variable having index +1, and the last variable\n"
+		"          having index -1. Variables are ordered alphanumerically.\n"
+		"\n"
+		"          All variables and functions can be escaped by doubling the sign,\n"
+		"          like $$variable_name or @@function_name. To escape a variable\n"
+		"          twice use $$$var_name. One escape is removed each time tintin\n"
+		"          needs to substitute a variable or function.\n"
+		"\n"
+		"<128>          Arguments\n"
+		"\n"
+		"<178>\%0 - \%99<278>  The percent sign followed by a number is used for arguments by the\n"
+		"          following triggers:\n"
+		"\n"
+		"          alias, action, button, event, function, prompt, and substitute.\n"
+		"\n"
+		"<178>&0 - &99<278>  The ampersand sign followed by a number is used for arguments in the\n"
+		"          regex and replace commands.\n"
+		"\n"
+		"          All trigger and command arguments can be escaped by doubling the\n"
+		"          sign like \%\%1 or &&1. One escape is removed each time tintin\n"
+		"          substitutes trigger or command arguments. To escape three times\n"
+		"          triple the sign like \%\%\%1, etc.\n"
+		"\n"
+		"<128>          Colors\n"
+		"\n"
+		"<178><<888>000><278>     Three alphanumeric characters encapsulated by the less- and greater-\n"
+		"          than signs are used for 4 and 8 bit color codes.\n"
+		"\n"
+		"<178><<888>0000><278>    Either a B (background) or F (foreground) followed by three\n"
+		"          hexadecimal characters encapsulated by < > signs are used for 12\n"
+		"          bit color codes. Requires truecolor capable terminal.\n"
+		"\n"
+		"<178><<888>0000000><278> Either a B (background) or F (foreground) followed by six\n"
+		"          hexadecimal characters encapsulated by < > signs are used for 24\n"
+		"          bit color codes. Requires truecolor capable terminal.\n"
+		"\n"
+		"          More information is available at #help color.\n"
+		"\n"
+		"<128>          Escapes\n"
+		"\n"
+		"<178>\\ <278>        The back slash is used to escape a character. All available options\n"
+		"          are listed at #help escape. Escapes are typically escaped when text\n"
+		"          leaves the client, by being send to a server, the shell, being\n"
+		"          displayed on the screen, or being processed as part of a regex.\n"
+		"          Escapes try to mimic escapes in PCRE when possible.\n",
+
+		"characters colors escapes info pcre",
+	},
 	{
 	{
 		"SUSPEND",
 		"SUSPEND",
 		TOKEN_TYPE_STRING,
 		TOKEN_TYPE_STRING,
@@ -4471,12 +4649,14 @@ struct help_type help_table[] =
 		"         You can see the first nest of a variable using $variable[+1] and the\n"
 		"         You can see the first nest of a variable using $variable[+1] and the\n"
 		"         last nest using $variable[-1]. Using $variable[-2] will report the\n"
 		"         last nest using $variable[-1]. Using $variable[-2] will report the\n"
 		"         second last variable, and so on. To show all indices use *variable[].\n"
 		"         second last variable, and so on. To show all indices use *variable[].\n"
-		"         To show all values use $variable[%*] or a less generic regex. To show\n"
-		"         all values from index 2 through 4 use $variable[+2..4].\n"
+		"         To show all values use $variable[]. To show all values from index 2\n"
+		"         through 4 use $variable[+2..4].\n"
 		"\n"
 		"\n"
 		"         Nested variables are also known as tables, table generally being used\n"
 		"         Nested variables are also known as tables, table generally being used\n"
 		"         to refer to several variables nested within one specific variable.\n"
 		"         to refer to several variables nested within one specific variable.\n"
 		"\n"
 		"\n"
+		"         It's possible to use regular expressions.\n"
+		"\n"
 		"<178>Example<278>: #show {Targets starting with the letter A: $targets[A%*]\n"
 		"<178>Example<278>: #show {Targets starting with the letter A: $targets[A%*]\n"
 		"\n"
 		"\n"
 		"         To see the internal index of a variable use &<variable name>. To see\n"
 		"         To see the internal index of a variable use &<variable name>. To see\n"

+ 8 - 0
src/history.c

@@ -67,6 +67,7 @@ DO_COMMAND(do_history)
 void add_line_history(struct session *ses, char *line)
 void add_line_history(struct session *ses, char *line)
 {
 {
 	struct listroot *root;
 	struct listroot *root;
+	int last;
 
 
 	root = ses->list[LIST_HISTORY];
 	root = ses->list[LIST_HISTORY];
 
 
@@ -82,8 +83,15 @@ void add_line_history(struct session *ses, char *line)
 		return;
 		return;
 	}
 	}
 
 
+	last = root->used;
+
 	update_node_list(ses->list[LIST_HISTORY], line, "", "", "");
 	update_node_list(ses->list[LIST_HISTORY], line, "", "", "");
 
 
+	if (last < root->used)
+	{
+		SET_BIT(gtd->flags, TINTIN_FLAG_HISTORYUPDATE);
+	}
+
 	while (root->used > gtd->history_size)
 	while (root->used > gtd->history_size)
 	{
 	{
 		delete_index_list(ses->list[LIST_HISTORY], 0);
 		delete_index_list(ses->list[LIST_HISTORY], 0);

+ 11 - 1
src/input.c

@@ -292,7 +292,7 @@ void read_line(char *input, int len)
 					index = (int) gtd->macro_buf[0];
 					index = (int) gtd->macro_buf[0];
 				}
 				}
 
 
-				sprintf(buf, "%.*s", size, gtd->macro_buf);
+				snprintf(buf, BUFFER_SIZE, "%.*s", size, gtd->macro_buf);
 
 
 				inputline_insert(buf, -1);
 				inputline_insert(buf, -1);
 
 
@@ -923,6 +923,16 @@ void echo_command(struct session *ses, char *line)
 		strcpy(output, "");
 		strcpy(output, "");
 	}
 	}
 
 
+	if (ses->scroll->line != -1)
+	{
+		buffer_end(gtd->ses, "", "", "");
+
+		if (!HAS_BIT(ses->flags, SES_FLAG_SPLIT))
+		{
+			printf("%s\n", line);
+		}
+	}
+
 	if (!HAS_BIT(ses->flags, SES_FLAG_SPLIT))
 	if (!HAS_BIT(ses->flags, SES_FLAG_SPLIT))
 	{
 	{
 		add_line_buffer(ses, line, -1);
 		add_line_buffer(ses, line, -1);

+ 138 - 64
src/list.c

@@ -38,8 +38,9 @@ extern DO_ARRAY(array_explode);
 extern DO_ARRAY(array_filter);
 extern DO_ARRAY(array_filter);
 extern DO_ARRAY(array_find);
 extern DO_ARRAY(array_find);
 extern DO_ARRAY(array_get);
 extern DO_ARRAY(array_get);
-extern DO_ARRAY(array_index);
+extern DO_ARRAY(array_indexate);
 extern DO_ARRAY(array_insert);
 extern DO_ARRAY(array_insert);
+extern DO_ARRAY(array_numerate);
 extern DO_ARRAY(array_order);
 extern DO_ARRAY(array_order);
 extern DO_ARRAY(array_reverse);
 extern DO_ARRAY(array_reverse);
 extern DO_ARRAY(array_set);
 extern DO_ARRAY(array_set);
@@ -60,19 +61,20 @@ struct array_type
 
 
 struct array_type array_table[] =
 struct array_type array_table[] =
 {
 {
-	{     "ADD",              array_add,         "Add an item to a list table"             },
+	{     "ADD",              array_add,         "Add items to a list"                     },
 	{     "CLEAR",            array_clear,       "Clear a list"                            },
 	{     "CLEAR",            array_clear,       "Clear a list"                            },
 	{     "CLR",              array_clear,       NULL                                      },
 	{     "CLR",              array_clear,       NULL                                      },
 	{     "COLLAPSE",         array_collapse,    "Collapse the list into a variable"       },
 	{     "COLLAPSE",         array_collapse,    "Collapse the list into a variable"       },
-	{     "CREATE",           array_create,      "Create a list table with given items"    },
+	{     "CREATE",           array_create,      "Create a list with given items"          },
 	{     "DELETE",           array_delete,      "Delete a list item with given index"     },
 	{     "DELETE",           array_delete,      "Delete a list item with given index"     },
 	{     "EXPLODE",          array_explode,     "Explode the variable into a list"        },
 	{     "EXPLODE",          array_explode,     "Explode the variable into a list"        },
 	{     "FILTER",           array_filter,      "Filter a list with given regex"          },
 	{     "FILTER",           array_filter,      "Filter a list with given regex"          },
 	{     "FIND",             array_find,        "Find a list item with given regex"       },
 	{     "FIND",             array_find,        "Find a list item with given regex"       },
 	{     "FND",              array_find,        NULL                                      },
 	{     "FND",              array_find,        NULL                                      },
 	{     "GET",              array_get,         "Retrieve a list item with given index"   },
 	{     "GET",              array_get,         "Retrieve a list item with given index"   },
-	{     "INDEXATE",         array_index,       "Indexate a list table for sorting"       },
-	{     "INSERT",           array_insert,      "Insert a list item at given index"       },
+	{     "INDEXATE",         array_indexate,    "Indexate a list table for sorting"       },
+	{     "INSERT",           array_insert,      "Insert an item at given index"           },
+	{     "NUMERATE",         array_numerate,    "Turn a table into a list"                },
 	{     "ORDER",            array_order,       "Sort a list table numerically"           },
 	{     "ORDER",            array_order,       "Sort a list table numerically"           },
 	{     "LENGTH",           array_size,        NULL                                      },
 	{     "LENGTH",           array_size,        NULL                                      },
 	{     "REVERSE",          array_reverse,     "Sort a list table in reverse order"      },
 	{     "REVERSE",          array_reverse,     "Sort a list table in reverse order"      },
@@ -177,7 +179,7 @@ DO_ARRAY(array_add)
 	char *str;
 	char *str;
 	int index;
 	int index;
 
 
-	if (!list->root)
+	if (list->root == NULL)
 	{
 	{
 		list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 		list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 	}
 	}
@@ -228,12 +230,19 @@ DO_ARRAY(array_collapse)
 {
 {
 	int index;
 	int index;
 
 
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN|SUB_ESC);
+
 	if (list->root)
 	if (list->root)
 	{
 	{
-		str_cpy(&list->arg2, "");
+		if (list->root->used)
+		{
+			str_cpy(&list->arg2, list->root->list[0]->arg2);
+		}
 
 
-		for (index = 0 ; index < list->root->used ; index++)
+		for (index = 1 ; index < list->root->used ; index++)
 		{
 		{
+			str_cat(&list->arg2, arg1);
+
 			str_cat(&list->arg2, list->root->list[index]->arg2);
 			str_cat(&list->arg2, list->root->list[index]->arg2);
 		}
 		}
 		free_list(list->root);
 		free_list(list->root);
@@ -309,7 +318,7 @@ DO_ARRAY(array_delete)
 
 
 		if (index == -1)
 		if (index == -1)
 		{
 		{
-			show_error(ses, LIST_VARIABLE, "#LIST {%s} DEL: Invalid index: %s", var, arg1);
+			show_error(ses, LIST_VARIABLE, "#LIST {%s} DELETE: INVALID INDEX: {%s}.", var, arg1);
 
 
 			return ses;
 			return ses;
 		}
 		}
@@ -326,63 +335,65 @@ DO_ARRAY(array_delete)
 	}
 	}
 	else
 	else
 	{
 	{
-		show_error(ses, LIST_VARIABLE, "#LIST DEL: {%s} is not a list.", var);
+		show_error(ses, LIST_VARIABLE, "#LIST DELETE: {%s} is not a list.", var);
 	}
 	}
 	return ses;
 	return ses;
 }
 }
 
 
 DO_ARRAY(array_explode)
 DO_ARRAY(array_explode)
 {
 {
-	char *pti;
+	char *str;
 	int index = 1;
 	int index = 1;
 
 
-	if (list->root)
-	{
-		array_collapse(ses, list, arg, var, arg1, arg2);
-	}
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN|SUB_ESC);
+	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
 
 
-	list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
+	if (*arg1 == 0)
+	{
+		show_error(ses, LIST_VARIABLE, "#SYNTAX: #LIST {%s} EXPLODE {<SEPARATOR>}.", var);
 
 
-	pti = list->arg2;
+		return ses;
+	}
 
 
-	while (*pti)
+	if (list->root)
 	{
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
-		{
-			pti += sprintf(arg2, "%.*s", get_euc_size(ses, pti), pti);
-		}
-		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
-		{
-			pti += sprintf(arg2, "%.*s", get_utf8_size(pti), pti);
-		}
-		else
+		if (*arg2 == 0)
 		{
 		{
-			pti += sprintf(arg2, "%c", *pti);
+			show_error(ses, LIST_VARIABLE, "#LIST {%s} EXPLODE: VARIABLE %s IS ALREADY A LIST.", var, var);
+
+			return ses;
 		}
 		}
+		free_list(list->root);
 
 
-		set_nest_node(list->root, ntos(index++), "%s", arg2);
+		list->root = NULL;
 	}
 	}
-	sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+	list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 
 
-	pti = arg1;
+	if (*arg2)
+	{
+		str_cpy(&list->arg2, arg2);
+	}
+	arg = list->arg2;
 
 
-	while (*pti)
+	while (1)
 	{
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, pti))
-		{
-			pti += sprintf(arg2, "%.*s", get_euc_size(ses, pti), pti);
-		}
-		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
-		{
-			pti += sprintf(arg2, "%.*s", get_utf8_size(pti), pti);
-		}
-		else
+		str = strstr(arg, arg1);
+
+		if (str == NULL)
 		{
 		{
-			pti += sprintf(arg2, "%c", *pti);
+			if (*arg)
+			{
+				set_nest_node(list->root, ntos(index++), "%s", arg);
+			}
+			break;
 		}
 		}
+		*str = 0;
 
 
-		set_nest_node(list->root, ntos(index++), "%s", arg2);
+		set_nest_node(list->root, ntos(index++), "%s", arg);
+
+		arg = str + strlen(arg1);
 	}
 	}
+
 	return ses;
 	return ses;
 }
 }
 
 
@@ -490,7 +501,7 @@ DO_ARRAY(array_get)
 	return ses;
 	return ses;
 }
 }
 
 
-DO_ARRAY(array_index)
+DO_ARRAY(array_indexate)
 {
 {
 	int cnt;
 	int cnt;
 
 
@@ -498,18 +509,35 @@ DO_ARRAY(array_index)
 
 
 	if (list->root == NULL || list->root->list[0]->root == NULL)
 	if (list->root == NULL || list->root->list[0]->root == NULL)
 	{
 	{
-		show_error(ses, LIST_COMMAND, "#ERROR: #LIST {%s} INDEX: NOT AN INDEXABLE LIST TABLE.", var);
+		show_error(ses, LIST_COMMAND, "#ERROR: #LIST {%s} INDEXATE: NOT AN INDEXABLE LIST TABLE.", var);
 
 
 		return ses;
 		return ses;
 	}
 	}
 
 
-	if (list->root->used > 1)
+	if (*arg1 == 0)
+	{
+		for (cnt = 0 ; cnt < list->root->used ; cnt++)
+		{
+			if (list->root->list[cnt]->root)
+			{
+				str_cpy(&list->root->list[cnt]->arg2, list->root->list[cnt]->arg1);
+			}
+			else
+			{
+				show_error(ses, LIST_COMMAND, "#ERROR: #LIST {%s} INDEXATE: FAILED TO POPULATE INDEX {%s}.", var, list->root->list[cnt]->arg1);
+				break;
+			}
+		}
+		return ses;
+	}
+
+	if (list->root->used)
 	{
 	{
 		int index = search_index_list(list->root->list[0]->root, arg1, "");
 		int index = search_index_list(list->root->list[0]->root, arg1, "");
 
 
 		if (index == -1)
 		if (index == -1)
 		{
 		{
-			show_error(ses, LIST_COMMAND, "#ERROR: #LIST {%s} INDEX {%s}: FAILED TO FIND NEST.", var, arg1);
+			show_error(ses, LIST_COMMAND, "#ERROR: #LIST {%s} INDEXATE: FAILED TO FIND NEST {%s}.", var, arg1);
 
 
 			return ses;
 			return ses;
 		}
 		}
@@ -522,7 +550,7 @@ DO_ARRAY(array_index)
 			}
 			}
 			else
 			else
 			{
 			{
-				show_error(ses, LIST_COMMAND, "#ERROR: #LIST {%s} INDEX: FAILED TO POPULATE INDEX {%s}.", var, list->root->list[cnt]->arg1);
+				show_error(ses, LIST_COMMAND, "#ERROR: #LIST {%s} INDEXATE: FAILED TO POPULATE INDEX {%s}.", var, list->root->list[cnt]->arg1);
 				break;
 				break;
 			}
 			}
 		}
 		}
@@ -537,7 +565,7 @@ DO_ARRAY(array_insert)
 	arg = 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_ALL, SUB_VAR|SUB_FUN);
 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_VAR|SUB_FUN);
 
 
-	if (!list->root)
+	if (list->root == NULL)
 	{
 	{
 		list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 		list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 	}
 	}
@@ -546,7 +574,7 @@ DO_ARRAY(array_insert)
 
 
 	if (toi == 0)
 	if (toi == 0)
 	{
 	{
-		show_error(ses, LIST_VARIABLE, "#LIST INS: Invalid index: %s", arg1);
+		show_error(ses, LIST_VARIABLE, "#LIST INSERT: INVALID INDEX: {%s}.", arg1);
 
 
 		return ses;
 		return ses;
 	}
 	}
@@ -568,6 +596,25 @@ DO_ARRAY(array_insert)
 	return ses;
 	return ses;
 }
 }
 
 
+DO_ARRAY(array_numerate)
+{
+	int index;
+
+	if (list->root == NULL)
+	{
+		show_error(ses, LIST_VARIABLE, "#LIST {%s} NUMERATE: VARIABLE IS NOT A TABLE.", var);
+
+		return ses;
+	}
+
+	for (index = 0 ; index < list->root->used ; index++)
+	{
+		str_cpy_printf(&list->root->list[index]->arg1, "%d", index + 1);
+	}
+
+	return ses;
+}
+
 DO_ARRAY(array_order)
 DO_ARRAY(array_order)
 {
 {
 	int cnt, val, len;
 	int cnt, val, len;
@@ -838,9 +885,14 @@ DO_ARRAY(array_sort)
 
 
 DO_ARRAY(array_tokenize)
 DO_ARRAY(array_tokenize)
 {
 {
+	char *buf;
 	int index = 1, i;
 	int index = 1, i;
 
 
-	sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+	buf = str_alloc_stack(0);
+
+	substitute(ses, arg, buf, SUB_VAR|SUB_FUN);
+
+	arg = buf;
 
 
 	if (list->root)
 	if (list->root)
 	{
 	{
@@ -849,24 +901,46 @@ DO_ARRAY(array_tokenize)
 
 
 	list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 	list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 
 
-	i = 0;
-
-	while (arg1[i] != 0)
+	while (*arg)
 	{
 	{
-		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, &arg1[i]))
-		{
-			i += sprintf(arg2, "%.*s", get_euc_size(ses, &arg1[i]), &arg1[i]);
-		}
-		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&arg1[i]))
+		arg = get_arg_in_braces(ses, arg, arg1, GET_ALL);
+
+		i = 0;
+
+		while (arg1[i] != 0)
 		{
 		{
-			i += sprintf(arg2, "%.*s", get_utf8_size(&arg1[i]), &arg1[i]);
+			if (arg1[i] == '{')
+			{
+				strcpy(arg2, "\\x7B"); i++;
+			}
+			else if (arg1[i] == '}')
+			{
+				strcpy(arg2, "\\x7D"); i++;
+			}
+			else if (arg1[i] == '\\' && arg1[i+1] == 'x' && is_hex(arg1[i+2]) && is_hex(arg1[i+3]))
+			{
+				i += sprintf(arg2, "%.4s", &arg1[i]);
+			}
+			else if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, &arg1[i]))
+			{
+				i += sprintf(arg2, "%.*s", get_euc_size(ses, &arg1[i]), &arg1[i]);
+			}
+			else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&arg1[i]))
+			{
+				i += sprintf(arg2, "%.*s", get_utf8_size(&arg1[i]), &arg1[i]);
+			}
+			else
+			{
+				i += sprintf(arg2, "%c", arg1[i]);
+			}
+
+			set_nest_node(list->root, ntos(index++), "%s", arg2);
 		}
 		}
-		else
+
+		if (*arg == COMMAND_SEPARATOR)
 		{
 		{
-			i += sprintf(arg2, "%c", arg1[i]);
+			arg++;
 		}
 		}
-
-		set_nest_node(list->root, ntos(index++), "%s", arg2);
 	}
 	}
 	return ses;
 	return ses;
 }
 }

+ 1 - 1
src/log.c

@@ -131,7 +131,7 @@ DO_LOG(log_info)
 {
 {
 	tintin_printf2(ses, "#LOG INFO: FILE  = %s", ses->log->file ? ses->log->name : "");
 	tintin_printf2(ses, "#LOG INFO: FILE  = %s", ses->log->file ? ses->log->name : "");
 	tintin_printf2(ses, "#LOG INFO: LEVEL = %s", HAS_BIT(ses->log->mode, LOG_FLAG_LOW) ? "LOW" : "HIGH");
 	tintin_printf2(ses, "#LOG INFO: LEVEL = %s", HAS_BIT(ses->log->mode, LOG_FLAG_LOW) ? "LOW" : "HIGH");
-	tintin_printf2(ses, "#LOG INFO: MODE  = %s", HAS_BIT(ses->log->mode, LOG_FLAG_HTML) ? "HTML" : HAS_BIT(ses->log->mode, LOG_FLAG_PLAIN) ? "PLAIN" : "RAW");
+	tintin_printf2(ses, "#LOG INFO: MODE  = %s", HAS_BIT(ses->log->mode, LOG_FLAG_HTML) ? "HTML" : HAS_BIT(ses->log->mode, LOG_FLAG_PLAIN) ? "PLAIN" : HAS_BIT(ses->log->mode, LOG_FLAG_RAW) ? "RAW" : "UNSET");
 	tintin_printf2(ses, "#LOG INFO: LINE  = %s", ses->log->line_file ? ses->log->line_name : "");
 	tintin_printf2(ses, "#LOG INFO: LINE  = %s", ses->log->line_file ? ses->log->line_name : "");
 	tintin_printf2(ses, "#LOG INFO: NEXT  = %s", ses->log->next_file ? ses->log->next_name : "");
 	tintin_printf2(ses, "#LOG INFO: NEXT  = %s", ses->log->next_file ? ses->log->next_name : "");
 }
 }

+ 17 - 6
src/main.c

@@ -241,7 +241,7 @@ int main(int argc, char **argv)
 
 
 	if (argc > 1)
 	if (argc > 1)
 	{
 	{
-		while ((c = getopt(argc, argv, "a: e: g G h M:: r: R:: s t: T v V")) != EOF)
+		while ((c = getopt(argc, argv, "a: e: g G h H M:: r: R:: s t: T v V")) != EOF)
 		{
 		{
 			switch (c)
 			switch (c)
 			{
 			{
@@ -253,6 +253,7 @@ int main(int argc, char **argv)
 					printf("  -g  Enable the startup user interface.\n");
 					printf("  -g  Enable the startup user interface.\n");
 					printf("  -G  Don't show the greeting screen.\n");
 					printf("  -G  Don't show the greeting screen.\n");
 					printf("  -h  This help section.\n");
 					printf("  -h  This help section.\n");
+					printf("  -H  Nohup compatible mode.\n");
 					printf("  -M  Matrix Digital Rain.\n");
 					printf("  -M  Matrix Digital Rain.\n");
 					printf("  -r  Read given file.\n");
 					printf("  -r  Read given file.\n");
 					printf("  -R  Reattach to daemonized process.\n");
 					printf("  -R  Reattach to daemonized process.\n");
@@ -264,6 +265,10 @@ int main(int argc, char **argv)
 
 
 					exit(1);
 					exit(1);
 
 
+				case 'H':
+					SET_BIT(greeting, STARTUP_FLAG_NOHUP);
+					break;
+
 				case 'M':
 				case 'M':
 				case 'G':
 				case 'G':
 					SET_BIT(greeting, STARTUP_FLAG_NOGREETING);
 					SET_BIT(greeting, STARTUP_FLAG_NOGREETING);
@@ -288,7 +293,6 @@ int main(int argc, char **argv)
 
 
 	init_tintin(greeting);
 	init_tintin(greeting);
 
 
-
 	RESTRING(gtd->vars[1], argv[0]);
 	RESTRING(gtd->vars[1], argv[0]);
 
 
 	if (argc > 1)
 	if (argc > 1)
@@ -297,7 +301,7 @@ int main(int argc, char **argv)
 
 
 		RESTRING(gtd->vars[2], argv[1]);
 		RESTRING(gtd->vars[2], argv[1]);
 
 
-		while ((c = getopt(argc, argv, "a: e: g G h M:: r: R:: s t: T v")) != EOF)
+		while ((c = getopt(argc, argv, "a: e: g G h H M:: r: R:: s t: T v")) != EOF)
 		{
 		{
 			switch (c)
 			switch (c)
 			{
 			{
@@ -397,6 +401,8 @@ int main(int argc, char **argv)
 		}
 		}
 	}
 	}
 
 
+	gtd->varc = UMIN(argc + 1, 100);
+
 	if (argv[optind] != NULL)
 	if (argv[optind] != NULL)
 	{
 	{
 		if (!strncasecmp(argv[optind], "telnet://", 9))
 		if (!strncasecmp(argv[optind], "telnet://", 9))
@@ -600,11 +606,16 @@ void init_tintin(int greeting)
 
 
 	gtd->level->input--;
 	gtd->level->input--;
 
 
-	if (HAS_BIT(greeting,  STARTUP_FLAG_VERBOSE))
+	if (HAS_BIT(greeting, STARTUP_FLAG_VERBOSE))
 	{
 	{
 		gtd->level->verbose--;
 		gtd->level->verbose--;
 	}
 	}
 
 
+	if (HAS_BIT(greeting, STARTUP_FLAG_NOHUP))
+	{
+		SET_BIT(gtd->flags, TINTIN_FLAG_NOHUP);
+	}
+
 	init_terminal(gts);
 	init_terminal(gts);
 
 
 	reset_screen(gts);
 	reset_screen(gts);
@@ -678,7 +689,7 @@ void quitmsg(char *message)
 
 
 	check_all_events(gts, EVENT_FLAG_SYSTEM, 0, 1, "PROGRAM TERMINATION", message ? message : "");
 	check_all_events(gts, EVENT_FLAG_SYSTEM, 0, 1, "PROGRAM TERMINATION", message ? message : "");
 
 
-	if (gtd->history_size)
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_HISTORYUPDATE) && gtd->history_size)
 	{
 	{
 		command(gts, do_history, "write %s/%s", gtd->system->tt_dir, HISTORY_FILE);
 		command(gts, do_history, "write %s/%s", gtd->system->tt_dir, HISTORY_FILE);
 	}
 	}
@@ -734,7 +745,7 @@ void syserr_printf(struct session *ses, char *fmt, ...)
 
 
 		if (ses && ses != gts)
 		if (ses && ses != gts)
 		{
 		{
-			tintin_printf2(ses, "#SYSTEM ERROR: %s %s (%d: %s)\e[0m", name, buf, errno, errstr);		
+			tintin_printf2(ses, "#SYSTEM ERROR: %s %s (%d: %s)\e[0m", name, buf, errno, errstr);
 		}
 		}
 
 
 		if (ses && gtd->ses != ses && gtd->ses != gts)
 		if (ses && gtd->ses != ses && gtd->ses != gts)

Різницю між файлами не показано, бо вона завелика
+ 292 - 224
src/mapper.c


+ 11 - 20
src/math.c

@@ -167,11 +167,17 @@ int get_ellipsis(struct session *ses, unsigned int size, char *name, int *min, i
 	{
 	{
 		strcpy(name + len, "-1");
 		strcpy(name + len, "-1");
 	}
 	}
+/*
+	sscanf(name, "%[^.]..%[^.]", strmin, strmax);
+
+	*min = get_number(ses, strmin);
+	*max = get_number(ses, strmax);
+*/
 
 
 	range = get_ulong(ses, name);
 	range = get_ulong(ses, name);
 
 
-	*min = (int) (HAS_BIT(range, 0x00000000FFFFFFFFLL));
-	*max = (int) (HAS_BIT(range, 0xFFFFFFFF00000000ULL) >> 32ULL);
+	*min = (int) (range / 1000000000 - 100000000);
+	*max = (int) (range % 1000000000 - 100000000);
 
 
 	*min = *min > 0 ? *min - 1 : size + *min;
 	*min = *min > 0 ? *min - 1 : size + *min;
 	*max = *max > 0 ? *max - 1 : size + *max;
 	*max = *max > 0 ? *max - 1 : size + *max;
@@ -518,7 +524,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 					case ':':
 					case ':':
 						if (debug && wonky == 0)
 						if (debug && wonky == 0)
 						{
 						{
-							show_error(gtd->ses, LIST_COMMAND, "\e[1;31m#WARNING: THE : TIME OPERATOR IN #MATH WILL BE REMOVED IN FUTURE RELEASES.");
+							show_error(gtd->ses, LIST_COMMAND, "\e[1;31m#WARNING: COMPUTING {%s}. THE : TIME OPERATOR IN #MATH WILL BE REMOVED IN FUTURE RELEASES.", str);
 						}
 						}
 						*pta++ = *pti++;
 						*pta++ = *pti++;
 						break;
 						break;
@@ -1054,20 +1060,12 @@ void mathexp_level(struct session *ses, struct math_node *node)
 
 
 void mathexp_compute(struct session *ses, struct math_node *node)
 void mathexp_compute(struct session *ses, struct math_node *node)
 {
 {
-	int integer64 = 0;
 	long double value = 0;
 	long double value = 0;
-	unsigned long long min = 0, max = 0;
-	unsigned long long value64 = 0;
 
 
 	switch ((int) node->val)
 	switch ((int) node->val)
 	{
 	{
 		case EXP_OP_ELLIPSIS:
 		case EXP_OP_ELLIPSIS:
-			integer64 = 1;
-
-                        SET_BIT(max, (unsigned int) node->next->val);
-                        max = max << 32ULL;
-                        SET_BIT(min, (unsigned int) node->prev->val);
-                        value64 = max | min;
+			value = ((long long) node->prev->val + 100000000LL) * 1000000000LL + ((long long) node->next->val + 100000000LL);
                         break;
                         break;
 
 
 		case EXP_OP_DICE:
 		case EXP_OP_DICE:
@@ -1220,14 +1218,7 @@ void mathexp_compute(struct session *ses, struct math_node *node)
 	node->priority = EXP_PR_VAR;
 	node->priority = EXP_PR_VAR;
 	node->type = EXP_NUMBER;
 	node->type = EXP_NUMBER;
 
 
-	if (integer64)
-	{
-		node->val = (long double) value64;
-	}
-	else
-	{
-		node->val = value;
-	}
+	node->val = value;
 }
 }
 
 
 long double tinternary(struct math_node *left, struct math_node *right)
 long double tinternary(struct math_node *left, struct math_node *right)

+ 11 - 0
src/memory.c

@@ -172,6 +172,17 @@ int str_fix(char *original)
 	return str_ptr->len;
 	return str_ptr->len;
 }
 }
 
 
+int str_fix_len(char *original, int len)
+{
+	struct str_data *str_ptr = get_str_ptr(original);
+
+	str_ptr->len = len;
+
+	original[len] = 0;
+
+	return str_ptr->len;
+}
+
 int str_len(char *str)
 int str_len(char *str)
 {
 {
 	return get_str_ptr(str)->len;
 	return get_str_ptr(str)->len;

+ 15 - 1
src/misc.c

@@ -151,9 +151,23 @@ DO_COMMAND(do_test)
 {
 {
 	arg = 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);
 
 
+	if (!strcmp(arg1, "gmcp"))
+	{
+		execute(ses, "%s", "#event {IAC SB GMCP} {#var {%0} {%1};#line debug #var {%0}}");
+
+		test_gmcp(ses, "Char.Defences.List [ { \"name\": \"boar tattoo\", \"desc\": \"boar tattoo\" }, { \"name\": \"moss tattoo\", \"desc\": \"moss tattoo\" } ]");
+
+		test_gmcp(ses, "MG.room.info { \"exits\": [ ], \"short\": \"Dschungel.\", \"id\": \"1\"}");
+
+		test_gmcp(ses, "char.combat_immunity {\"combat_immunity\": \"a tame dragon is immune to your attack\"}");
+
+		test_gmcp(ses, "room.info { \"num\": 5922, \"name\": \"At the entrance of the park\", \"zone\": \"zoo\", \"terrain\": \"city\", \"exits\": { \"e\": 5920, \"s\": 5916, \"w\": 12611 }, \"coord\": { \"id\": 0, \"x\": 37, \"y\": 19, \"cont\": 0 } }");
+	}
+
 	if (!strcmp(arg1, "bla"))
 	if (!strcmp(arg1, "bla"))
 	{
 	{
-		printf("%d", ~1 + 2);
+		tintin_printf(ses, "len: %d", strip_color_strlen(ses, arg));
+//		tintin_printf(ses, "<118>\ufffd", arg2);
 	}
 	}
 
 
 	if (!strcmp(arg1, "rain"))
 	if (!strcmp(arg1, "rain"))

+ 144 - 30
src/nest.c

@@ -27,15 +27,9 @@
 
 
 struct listroot *search_nest_root(struct listroot *root, char *arg)
 struct listroot *search_nest_root(struct listroot *root, char *arg)
 {
 {
-	struct listnode *node;
-
-	node = search_node_list(root, arg);
+	struct listnode *node = search_node_list(root, arg);
 
 
-	if (node == NULL || node->root == NULL)
-	{
-		return NULL;
-	}
-	return node->root;
+	return node ? node->root : NULL;
 }
 }
 
 
 struct listroot *search_nest_base_ses(struct session *ses, char *arg)
 struct listroot *search_nest_base_ses(struct session *ses, char *arg)
@@ -168,6 +162,62 @@ struct listnode *search_nest_node(struct listroot *root, char *variable)
 	return NULL;
 	return NULL;
 }
 }
 
 
+// same as search_nest_node, but store the path in *path
+
+struct listnode *search_nest_node_path(struct listroot *root, char *variable, char *path)
+{
+	struct listnode *node;
+	char name[BUFFER_SIZE], *arg;
+
+	arg = get_arg_to_brackets(root->ses, variable, name);
+
+	node = search_node_list(root, name);
+
+	if (node)
+	{
+		path += sprintf(path, "%s", node->arg1);
+
+		if (*arg == 0)
+		{
+			return node;
+		}
+	}
+	root = node ? node->root : NULL;
+
+	if (root)
+	{
+		arg = get_arg_in_brackets(root->ses, arg, name);
+	}
+
+	while (root && *arg)
+	{
+		node = search_node_list(root, name);
+
+		if (node)
+		{
+			path += sprintf(path, "[%s]", node->arg1);
+		}
+		root = node ? node->root : NULL;
+
+		if (root)
+		{
+			arg = get_arg_in_brackets(root->ses, arg, name);
+		}
+	}
+
+	if (root)
+	{
+		node = search_node_list(root, name);
+
+		if (node)
+		{
+			path += sprintf(path, "[%s]", node->arg1);
+		}
+		return node;
+	}
+	return NULL;
+}
+
 int search_nest_index(struct listroot *root, char *variable)
 int search_nest_index(struct listroot *root, char *variable)
 {
 {
 	char name[BUFFER_SIZE], *arg;
 	char name[BUFFER_SIZE], *arg;
@@ -213,8 +263,6 @@ struct listroot *update_nest_root(struct listroot *root, char *arg)
 
 
 void update_nest_node(struct listroot *root, char *arg)
 void update_nest_node(struct listroot *root, char *arg)
 {
 {
-//	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
-
 	char *arg1, *arg2;
 	char *arg1, *arg2;
 
 
 	arg1 = str_mim(arg);
 	arg1 = str_mim(arg);
@@ -277,6 +325,57 @@ int delete_nest_node(struct listroot *root, char *variable)
 	return FALSE;
 	return FALSE;
 }
 }
 
 
+int delete_nest_node_with_wild(struct listroot *root, char *variable)
+{
+	char name[BUFFER_SIZE], *arg, *ptv;
+	int index, found;
+
+	arg = get_arg_to_brackets(root->ses, variable, name);
+
+	ptv = arg;
+
+	while (root && *arg)
+	{
+		root = search_nest_root(root, name);
+
+		if (root)
+		{
+			arg = get_arg_in_brackets(root->ses, arg, name);
+
+			if (*arg != 0)
+			{
+				ptv = arg;
+			}
+		}
+	}
+
+	found = FALSE;
+
+	if (root)
+	{
+		for (index = root->used - 1 ; index >= 0 ; index--)
+		{
+			if (match(root->ses, root->list[index]->arg1, name, SUB_VAR|SUB_FUN))
+			{
+				if (*ptv)
+				{
+					show_message(root->ses, LIST_VARIABLE, "#OK. {%.*s[%s]} IS NO LONGER A VARIABLE.", ptv - variable, variable, root->list[index]->arg1);
+				}
+				else
+				{
+					show_message(root->ses, LIST_VARIABLE, "#OK. {%s} IS NO LONGER A VARIABLE.", root->list[index]->arg1);
+				}
+				
+				delete_index_list(root, index);
+
+				found = TRUE;
+			}
+		}
+	}
+	return found;
+}
+
+
 // Return the number of indices of a node.
 // Return the number of indices of a node.
 
 
 int get_nest_size(struct listroot *root, char *variable)
 int get_nest_size(struct listroot *root, char *variable)
@@ -649,7 +748,6 @@ int get_nest_size_val(struct listroot *root, char *variable, char **result)
 	return 0;
 	return 0;
 }
 }
 
 
-
 struct listnode *get_nest_node_key(struct listroot *root, char *variable, char **result, int def)
 struct listnode *get_nest_node_key(struct listroot *root, char *variable, char **result, int def)
 {
 {
 	struct listnode *node;
 	struct listnode *node;
@@ -726,7 +824,6 @@ struct listnode *get_nest_node_val(struct listroot *root, char *variable, char *
 	return NULL;
 	return NULL;
 }
 }
 
 
-
 int get_nest_index(struct listroot *root, char *variable, char **result, int def)
 int get_nest_index(struct listroot *root, char *variable, char **result, int def)
 {
 {
 	struct listnode *node;
 	struct listnode *node;
@@ -921,13 +1018,8 @@ struct listnode *set_nest_node_ses(struct session *ses, char *arg1, char *format
 		}
 		}
 		else
 		else
 		{
 		{
-                      root = ses->list[LIST_VARIABLE];
+			root = ses->list[LIST_VARIABLE];
 		}
 		}
-		node = NULL;
-	}
-	else
-	{
-		node = search_nest_node(root, arg1);
 	}
 	}
 
 
 	while (*arg)
 	while (*arg)
@@ -971,9 +1063,14 @@ struct listnode *set_nest_node_ses(struct session *ses, char *arg1, char *format
 
 
 	if (HAS_BIT(root->ses->event_flags, EVENT_FLAG_VARIABLE))
 	if (HAS_BIT(root->ses->event_flags, EVENT_FLAG_VARIABLE))
 	{
 	{
-		arg = get_arg_to_brackets(ses, arg1, name);
+		arg = get_arg_to_brackets(root->ses, arg1, name);
 
 
 		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", name, name, arg2, arg1);
 		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", name, name, arg2, arg1);
+
+		if (strcmp(arg1, name))
+		{
+			check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", arg1, name, arg2, arg1);
+		}
 	}
 	}
 	free(arg2);
 	free(arg2);
 
 
@@ -987,12 +1084,15 @@ struct listnode *add_nest_node_ses(struct session *ses, char *arg1, char *format
 {
 {
 	struct listnode *node;
 	struct listnode *node;
 	struct listroot *root;
 	struct listroot *root;
-	char *arg, *arg2, name[BUFFER_SIZE];
+	char *arg, *arg2, *name;
 	va_list args;
 	va_list args;
 
 
 	push_call("add_nest_node_ses(%p,%s,%p,...)",ses,arg1,format);
 	push_call("add_nest_node_ses(%p,%s,%p,...)",ses,arg1,format);
 
 
+	name = str_alloc_stack(0);
+
 	va_start(args, format);
 	va_start(args, format);
+
 	if (vasprintf(&arg2, format, args) == -1)
 	if (vasprintf(&arg2, format, args) == -1)
 	{
 	{
 		syserr_printf(ses, "add_nest_node_ses: vasprintf");
 		syserr_printf(ses, "add_nest_node_ses: vasprintf");
@@ -1012,11 +1112,6 @@ struct listnode *add_nest_node_ses(struct session *ses, char *arg1, char *format
 	if (root == NULL)
 	if (root == NULL)
 	{
 	{
 		root = ses->list[LIST_VARIABLE];
 		root = ses->list[LIST_VARIABLE];
-		node = NULL;
-	}
-	else
-	{
-		node = search_nest_node(root, arg1);
 	}
 	}
 
 
 	while (*arg)
 	while (*arg)
@@ -1030,7 +1125,6 @@ struct listnode *add_nest_node_ses(struct session *ses, char *arg1, char *format
 	}
 	}
 
 
 	node = search_node_list(root, name);
 	node = search_node_list(root, name);
-
 /*
 /*
 	if (node && node->root)
 	if (node && node->root)
 	{
 	{
@@ -1039,7 +1133,6 @@ struct listnode *add_nest_node_ses(struct session *ses, char *arg1, char *format
 		node->root = NULL;
 		node->root = NULL;
 	}
 	}
 */
 */
-
 	if (*space_out(arg2) == DEFAULT_OPEN)
 	if (*space_out(arg2) == DEFAULT_OPEN)
 	{
 	{
 		update_nest_node(update_nest_root(root, name), arg2);
 		update_nest_node(update_nest_root(root, name), arg2);
@@ -1062,10 +1155,15 @@ struct listnode *add_nest_node_ses(struct session *ses, char *arg1, char *format
 
 
 	if (HAS_BIT(root->ses->event_flags, EVENT_FLAG_VARIABLE))
 	if (HAS_BIT(root->ses->event_flags, EVENT_FLAG_VARIABLE))
 	{
 	{
-		arg = get_arg_to_brackets(ses, arg1, name);
+		arg = get_arg_to_brackets(root->ses, arg1, name);
 
 
 		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", name, name, arg2, arg1);
 		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", name, name, arg2, arg1);
-		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", arg1, name, arg2, arg1);
+
+		if (strcmp(arg1, name))
+		{
+			check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", arg1, name, arg2, arg1);
+		}
+
 	}
 	}
 	free(arg2);
 	free(arg2);
 
 
@@ -1139,6 +1237,11 @@ struct listnode *set_nest_node(struct listroot *root, char *arg1, char *format,
 	}
 	}
 	else
 	else
 	{
 	{
+		if (*name == '-' || *name == '+')
+		{
+			get_number_string(root->ses, name, name);
+//			printf("debug: set_nest_node - or +\n");
+		}
 		node = update_node_list(root, name, arg2, "", "");
 		node = update_node_list(root, name, arg2, "", "");
 	}
 	}
 
 
@@ -1152,7 +1255,11 @@ struct listnode *set_nest_node(struct listroot *root, char *arg1, char *format,
 		arg = get_arg_to_brackets(root->ses, arg1, name);
 		arg = get_arg_to_brackets(root->ses, arg1, name);
 
 
 		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", name, name, arg2, arg1);
 		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", name, name, arg2, arg1);
-		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", arg1, name, arg2, arg1);
+
+		if (strcmp(arg1, name))
+		{
+			check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", arg1, name, arg2, arg1);
+		}
 	}
 	}
 
 
 	free(arg2);
 	free(arg2);
@@ -1242,7 +1349,14 @@ struct listnode *add_nest_node(struct listroot *root, char *arg1, char *format,
 
 
 	if (HAS_BIT(root->ses->event_flags, EVENT_FLAG_VARIABLE))
 	if (HAS_BIT(root->ses->event_flags, EVENT_FLAG_VARIABLE))
 	{
 	{
+		arg = get_arg_to_brackets(root->ses, arg1, name);
+
 		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", name, name, arg2, arg1);
 		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", name, name, arg2, arg1);
+
+		if (strcmp(arg1, name))
+		{
+			check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", arg1, name, arg2, arg1);
+		}
 	}
 	}
 
 
 	free(arg2);
 	free(arg2);

+ 2 - 0
src/net.c

@@ -158,6 +158,8 @@ int connect_mud(struct session *ses, char *host, char *port)
 		return -1;
 		return -1;
 	}
 	}
 
 
+//	ses->connect_error = connect(sock, address->ai_addr, address->ai_addrlen);
+
 	if (ses->connect_error)
 	if (ses->connect_error)
 	{
 	{
 //		ses->connect_error = wait_on_connect(ses, sock, ses->connect_error);
 //		ses->connect_error = wait_on_connect(ses, sock, ses->connect_error);

+ 3 - 3
src/parse.c

@@ -609,7 +609,7 @@ char *get_arg_in_braces(struct session *ses, char *string, char *result, int fla
 	char *pti, *pto;
 	char *pti, *pto;
 	int skip, nest = 1;
 	int skip, nest = 1;
 
 
-	pti = space_out(string);
+	pti = HAS_BIT(flag, GET_SPC) ? string : space_out(string);
 	pto = result;
 	pto = result;
 
 
 	if (*pti != DEFAULT_OPEN)
 	if (*pti != DEFAULT_OPEN)
@@ -708,7 +708,7 @@ char *get_arg_with_spaces(struct session *ses, char *string, char *result, int f
 	char *pto, *pti;
 	char *pto, *pti;
 	int skip, nest = 0;
 	int skip, nest = 0;
 
 
-	pti = space_out(string);
+	pti = HAS_BIT(flag, GET_SPC) ? string : space_out(string);
 	pto = result;
 	pto = result;
 
 
 	while (*pti)
 	while (*pti)
@@ -1098,7 +1098,7 @@ char *get_char(struct session *ses, char *string, char *result)
 	}
 	}
 	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
 	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(pti))
 	{
 	{
-		pti += sprintf(result, "%.*s", get_utf8_size(pti), pti);
+		pti += snprintf(result, 6, "%.*s", get_utf8_size(pti), pti);
 	}
 	}
 	else
 	else
 	{
 	{

+ 1 - 1
src/path.c

@@ -377,7 +377,7 @@ DO_PATH(path_save)
 	}
 	}
 	else if (*arg2 == 0)
 	else if (*arg2 == 0)
 	{
 	{
-		sprintf(result, "BOTH {%s}", arg1);
+		snprintf(result, STRING_SIZE, "BOTH {%s}", arg1);
 
 
 		path_save(ses, result);
 		path_save(ses, result);
 	}
 	}

+ 5 - 5
src/port.c

@@ -117,7 +117,7 @@ DO_PORT(port_initialize)
 
 
 	tintin_printf(ses, "#TRYING TO LAUNCH '%s' ON PORT '%s'.", arg1, arg2);
 	tintin_printf(ses, "#TRYING TO LAUNCH '%s' ON PORT '%s'.", arg1, arg2);
 
 
-	sprintf(temp, "{localhost} {%d} {%.*s}", atoi(arg2), PATH_SIZE, file);
+	snprintf(temp, BUFFER_SIZE, "{localhost} {%d} {%.*s}", atoi(arg2), PATH_SIZE, file);
 
 
 	port = atoi(arg2);
 	port = atoi(arg2);
 
 
@@ -479,11 +479,11 @@ void port_printf(struct session *ses, char *format, ...)
 	vsnprintf(buf, len, format, args);
 	vsnprintf(buf, len, format, args);
 	va_end(args);
 	va_end(args);
 
 
-	sprintf(tmp, "%s%s", ses->port->prefix, buf);
+	snprintf(tmp, BUFFER_SIZE, "%s%s", ses->port->prefix, buf);
 
 
 	strip_vt102_codes_non_graph(tmp, buf);
 	strip_vt102_codes_non_graph(tmp, buf);
 
 
-	sprintf(tmp, "%s%s\e[0m", ses->port->color, buf);
+	snprintf(tmp, BUFFER_SIZE, "%s%s\e[0m", ses->port->color, buf);
 
 
 	check_all_events(ses, SUB_SEC|EVENT_FLAG_PORT, 0, 2, "PORT MESSAGE", tmp, buf);
 	check_all_events(ses, SUB_SEC|EVENT_FLAG_PORT, 0, 2, "PORT MESSAGE", tmp, buf);
 
 
@@ -505,11 +505,11 @@ void port_log_printf(struct session *ses, struct port_data *buddy, char *format,
 	vsnprintf(buf, len, format, args);
 	vsnprintf(buf, len, format, args);
 	va_end(args);
 	va_end(args);
 
 
-	sprintf(tmp, "%s%s@%s %s", ses->port->prefix, buddy->name, buddy->ip, buf);
+	snprintf(tmp, BUFFER_SIZE, "%s%s@%s %s", ses->port->prefix, buddy->name, buddy->ip, buf);
 
 
 	strip_vt102_codes_non_graph(tmp, buf);
 	strip_vt102_codes_non_graph(tmp, buf);
 
 
-	sprintf(tmp, "%s%s\e[0m", ses->port->color, buf);
+	snprintf(tmp, BUFFER_SIZE, "%s%s\e[0m", ses->port->color, buf);
 
 
 	check_all_events(ses, EVENT_FLAG_PORT, 0, 5, "PORT LOG MESSAGE", buddy->name, buddy->ip, ntos(buddy->fd), tmp, buf);
 	check_all_events(ses, EVENT_FLAG_PORT, 0, 5, "PORT LOG MESSAGE", buddy->name, buddy->ip, ntos(buddy->fd), tmp, buf);
 
 

+ 24 - 0
src/regex.c

@@ -125,35 +125,59 @@ int regexp_compare(struct session *ses, pcre *nodepcre, char *str, char *exp, in
 	switch (flag)
 	switch (flag)
 	{
 	{
 		case REGEX_FLAG_CMD:
 		case REGEX_FLAG_CMD:
+			for (i = matches ; i < gtd->cmdc ; i++)
+			{
+				gtd->cmds[i] = restring(gtd->cmds[i], "");
+			}
+
 			for (i = 0 ; i < matches ; i++)
 			for (i = 0 ; i < matches ; i++)
 			{
 			{
 				gtd->cmds[i] = restringf(gtd->cmds[i], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 				gtd->cmds[i] = restringf(gtd->cmds[i], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 			}
 			}
+			gtd->cmdc = matches;
 			break;
 			break;
 
 
 		case REGEX_FLAG_CMD + REGEX_FLAG_FIX:
 		case REGEX_FLAG_CMD + REGEX_FLAG_FIX:
+			for (i = matches ; i < gtd->cmdc ; i++)
+			{
+				gtd->cmds[i] = restring(gtd->cmds[i], "");
+			}
+
 			for (i = 0 ; i < matches ; i++)
 			for (i = 0 ; i < matches ; i++)
 			{
 			{
 				j = gtd->args[i];
 				j = gtd->args[i];
 
 
 				gtd->cmds[j] = restringf(gtd->cmds[j], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 				gtd->cmds[j] = restringf(gtd->cmds[j], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 			}
 			}
+			gtd->cmdc = matches;
 			break;
 			break;
 
 
 		case REGEX_FLAG_ARG:
 		case REGEX_FLAG_ARG:
+			for (i = matches ; i < gtd->varc ; i++)
+			{
+				gtd->vars[i] = restring(gtd->vars[i], "");
+			}
+
 			for (i = 0 ; i < matches ; i++)
 			for (i = 0 ; i < matches ; i++)
 			{
 			{
 				gtd->vars[i] = restringf(gtd->vars[i], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 				gtd->vars[i] = restringf(gtd->vars[i], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 			}
 			}
+			gtd->varc = matches;
 			break;
 			break;
 
 
 		case REGEX_FLAG_ARG + REGEX_FLAG_FIX:
 		case REGEX_FLAG_ARG + REGEX_FLAG_FIX:
+			for (i = matches ; i < gtd->varc ; i++)
+			{
+				gtd->vars[i] = restring(gtd->vars[i], "");
+			}
+
 			for (i = 0 ; i < matches ; i++)
 			for (i = 0 ; i < matches ; i++)
 			{
 			{
 				j = gtd->args[i];
 				j = gtd->args[i];
 
 
 				gtd->vars[j] = restringf(gtd->vars[j], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 				gtd->vars[j] = restringf(gtd->vars[j], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 			}
 			}
+			gtd->varc = matches;
 			break;
 			break;
 	}
 	}
 
 

+ 4 - 1
src/scan.c

@@ -414,6 +414,8 @@ DO_SCAN(scan_file)
 	RESTRING(gtd->cmds[3], ntos(strlen(str_rip)));
 	RESTRING(gtd->cmds[3], ntos(strlen(str_rip)));
 	RESTRING(gtd->cmds[4], ntos(cnt));
 	RESTRING(gtd->cmds[4], ntos(cnt));
 
 
+	gtd->cmdc = 5;
+
 	str_sub = str_alloc_stack(strlen(arg) * 2);
 	str_sub = str_alloc_stack(strlen(arg) * 2);
 
 
 	substitute(ses, arg2, str_sub, SUB_CMD);
 	substitute(ses, arg2, str_sub, SUB_CMD);
@@ -573,7 +575,7 @@ DO_SCAN(scan_txt)
 {
 {
 	char line[STRING_SIZE];
 	char line[STRING_SIZE];
 
 
-	while (fgets(line, BUFFER_SIZE - 1, fp))
+	while (fgets(line, BUFFER_SIZE / 2, fp))
 	{
 	{
 		arg = strchr(line, '\r');
 		arg = strchr(line, '\r');
 
 
@@ -591,6 +593,7 @@ DO_SCAN(scan_txt)
 				*arg = 0;
 				*arg = 0;
 			}
 			}
 		}
 		}
+		line[BUFFER_SIZE / 2] = 0;
 
 
 		process_mud_output(ses, line, FALSE);
 		process_mud_output(ses, line, FALSE);
 
 

+ 3 - 3
src/screen.c

@@ -2088,7 +2088,7 @@ int get_link_screen(struct session *ses, char *var, char *val, int flags, int ro
 				{
 				{
 					if (*val == 0)
 					if (*val == 0)
 					{
 					{
-						sprintf(val, "%.*s", (int) (pts - ptl), ptl);
+						snprintf(val, BUFFER_SIZE, "%.*s", (int) (pts - ptl), ptl);
 					}
 					}
 					return opt ? opt : 1;
 					return opt ? opt : 1;
 				}
 				}
@@ -2118,12 +2118,12 @@ int get_link_screen(struct session *ses, char *var, char *val, int flags, int ro
 	{
 	{
 		if (*val == 0)
 		if (*val == 0)
 		{
 		{
-			sprintf(val, "%.*s", (int) (pts - ptl), ptl);
+			snprintf(val, BUFFER_SIZE, "%.*s", (int) (pts - ptl), ptl);
 		}
 		}
 		return opt ? opt : 1;
 		return opt ? opt : 1;
 	}
 	}
 
 
-	sprintf(val, "%.*s", (int) (pts - ptw), ptw);
+	snprintf(val, BUFFER_SIZE, "%.*s", (int) (pts - ptw), ptw);
 
 
 	return FALSE;
 	return FALSE;
 }
 }

+ 23 - 18
src/session.c

@@ -456,7 +456,7 @@ struct session *new_session(struct session *ses, char *name, char *arg, int desc
 
 
 	newses->log           = calloc(1, sizeof(struct log_data));
 	newses->log           = calloc(1, sizeof(struct log_data));
 	init_log(newses);
 	init_log(newses);
-	ses->log->mode        = gts->log->mode;
+	newses->log->mode     = gts->log->mode;
 
 
 	newses->input         = calloc(1, sizeof(struct input_data));
 	newses->input         = calloc(1, sizeof(struct input_data));
 	init_input(newses, 0, 0, 0, 0);
 	init_input(newses, 0, 0, 0, 0);
@@ -536,7 +536,17 @@ struct session *new_session(struct session *ses, char *name, char *arg, int desc
 		newses = command(newses, do_read, "%s", file);
 		newses = command(newses, do_read, "%s", file);
 	}
 	}
 
 
-	check_all_events(newses, EVENT_FLAG_SESSION, 0, 4, "SESSION CREATED", newses->name, newses->session_host, newses->session_ip, newses->session_port);
+	check_all_events(newses, EVENT_FLAG_SESSION, 0, 5, "SESSION CREATED", newses->name, newses->session_host, newses->session_ip, newses->session_port, file);
+
+	if (HAS_BIT(newses->flags, SES_FLAG_CONNECTED) && !HAS_BIT(newses->flags, SES_FLAG_RUN))
+	{
+		if (!check_all_events(newses, EVENT_FLAG_GAG, 0, 4, "GAG SESSION CONNECTED", newses->name, newses->session_host, newses->session_ip, newses->session_port))
+		{
+			tintin_printf(newses, "\n#SESSION '%s' CONNECTED TO '%s' PORT '%s'", newses->name, newses->session_host, newses->session_port);
+		}
+
+		check_all_events(newses, EVENT_FLAG_SESSION, 0, 5, "SESSION CONNECTED", newses->name, newses->session_host, newses->session_ip, newses->session_port, file);
+	}
 
 
 	if (gtd->level->background == 0)
 	if (gtd->level->background == 0)
 	{
 	{
@@ -554,7 +564,9 @@ struct session *connect_session(struct session *ses)
 
 
 	push_call("connect_session(%p)",ses);
 	push_call("connect_session(%p)",ses);
 
 
-	ses->connect_retry = ++gtd->time + gts->connect_retry;
+	ses->connect_retry = ++gtd->utime + gts->connect_retry;
+
+	to.tv_sec = 0;
 
 
 	reconnect:
 	reconnect:
 
 
@@ -584,18 +596,11 @@ struct session *connect_session(struct session *ses)
 
 
 		SET_BIT(ses->flags, SES_FLAG_CONNECTED);
 		SET_BIT(ses->flags, SES_FLAG_CONNECTED);
 
 
-		if (!check_all_events(ses, EVENT_FLAG_GAG, 0, 4, "GAG SESSION CONNECTED", ses->name, ses->session_host, ses->session_ip, ses->session_port))
-		{
-			tintin_printf(ses, "\n#SESSION '%s' CONNECTED TO '%s' PORT '%s'", ses->name, ses->session_host, ses->session_port);
-		}
-
-		check_all_events(ses, EVENT_FLAG_SESSION, 0, 4, "SESSION CONNECTED", ses->name, ses->session_host, ses->session_ip, ses->session_port);
-
 		pop_call();
 		pop_call();
 		return ses;
 		return ses;
 	}
 	}
 
 
-	if (ses->connect_retry > gtd->utime)
+	if (ses->connect_retry > utime())
 	{
 	{
 		fd_set readfds;
 		fd_set readfds;
 
 
@@ -606,7 +611,7 @@ struct session *connect_session(struct session *ses)
 		{
 		{
 			if (to.tv_sec == 0)
 			if (to.tv_sec == 0)
 			{
 			{
-				to.tv_sec = 1;
+				to.tv_sec = 2;
 
 
 				tintin_printf(ses, "#SESSION '%s' FAILED TO CONNECT. RETRYING FOR %d SECONDS.", ses->name, (ses->connect_retry - gtd->utime) / 1000000);
 				tintin_printf(ses, "#SESSION '%s' FAILED TO CONNECT. RETRYING FOR %d SECONDS.", ses->name, (ses->connect_retry - gtd->utime) / 1000000);
 			}
 			}
@@ -617,8 +622,6 @@ struct session *connect_session(struct session *ses)
 
 
 	if (ses->connect_error)
 	if (ses->connect_error)
 	{
 	{
-		to.tv_sec = 0;
-
 		tintin_printf(ses, "#SESSION '%s' FAILED TO CONNECT.", ses->name);
 		tintin_printf(ses, "#SESSION '%s' FAILED TO CONNECT.", ses->name);
 	}
 	}
 
 
@@ -644,6 +647,7 @@ void cleanup_session(struct session *ses)
 		pop_call();
 		pop_call();
 		return;
 		return;
 	}
 	}
+	SET_BIT(ses->flags, SES_FLAG_CLOSED);
 
 
 	if (ses == gtd->update)
 	if (ses == gtd->update)
 	{
 	{
@@ -682,8 +686,6 @@ void cleanup_session(struct session *ses)
 
 
 	}
 	}
 
 
-	SET_BIT(ses->flags, SES_FLAG_CLOSED);
-
 	client_end_mccp2(ses);
 	client_end_mccp2(ses);
 	client_end_mccp3(ses);
 	client_end_mccp3(ses);
 
 
@@ -691,12 +693,15 @@ void cleanup_session(struct session *ses)
 	{
 	{
 		DEL_BIT(ses->flags, SES_FLAG_CONNECTED);
 		DEL_BIT(ses->flags, SES_FLAG_CONNECTED);
 
 
-		if (!check_all_events(ses, EVENT_FLAG_GAG, 0, 4, "GAG SESSION DISCONNECTED", ses->name, ses->session_host, ses->session_ip, ses->session_port))
+		if (!check_all_events(ses, EVENT_FLAG_GAG, 0, 4, "GAG SESSION DESTROYED", ses->name, ses->session_host, ses->session_ip, ses->session_port))
 		{
 		{
 			tintin_printf(gtd->ses, "#SESSION '%s' DIED.", ses->name);
 			tintin_printf(gtd->ses, "#SESSION '%s' DIED.", ses->name);
 		}
 		}
 
 
-		check_all_events(ses, EVENT_FLAG_SESSION, 0, 4, "SESSION DISCONNECTED", ses->name, ses->session_host, ses->session_ip, ses->session_port);
+		if (!HAS_BIT(ses->flags, SES_FLAG_RUN))
+		{
+			check_all_events(ses, EVENT_FLAG_SESSION, 0, 4, "SESSION DISCONNECTED", ses->name, ses->session_host, ses->session_ip, ses->session_port);
+		}
 	}
 	}
 	else if (ses->port)
 	else if (ses->port)
 	{
 	{

+ 39 - 30
src/show.c

@@ -84,7 +84,7 @@ DO_COMMAND(do_echo)
 
 
 	format_string(ses, arg1, arg, result);
 	format_string(ses, arg1, arg, result);
 
 
-	arg = get_arg_in_braces(ses, result, arg1, GET_ALL);
+	arg = get_arg_in_braces(ses, result, arg1, GET_ALL|GET_SPC);
 
 
 	prompt = is_suffix(arg1, "\\") && !is_suffix(arg1, "\\\\");
 	prompt = is_suffix(arg1, "\\") && !is_suffix(arg1, "\\\\");
 
 
@@ -168,8 +168,8 @@ void show_message(struct session *ses, int index, char *format, ...)
 
 
 			if (vasprintf(&buffer, format, args) == -1)
 			if (vasprintf(&buffer, format, args) == -1)
 			{
 			{
-				syserr_printf(ses, "print_lines: vasprintf2:");
-				
+				syserr_printf(ses, "show_message: vasprintf2:");
+
 				pop_call();
 				pop_call();
 				return;
 				return;
 			}
 			}
@@ -247,7 +247,7 @@ void show_error(struct session *ses, int index, char *format, ...)
 void show_debug(struct session *ses, int index, char *format, ...)
 void show_debug(struct session *ses, int index, char *format, ...)
 {
 {
 	struct listroot *root;
 	struct listroot *root;
-	char buf[STRING_SIZE];
+	char *buffer;
 	va_list args;
 	va_list args;
 
 
 	push_call("show_debug(%p,%p,%p)",ses,index,format);
 	push_call("show_debug(%p,%p,%p)",ses,index,format);
@@ -261,30 +261,31 @@ void show_debug(struct session *ses, int index, char *format, ...)
 	}
 	}
 
 
 	va_start(args, format);
 	va_start(args, format);
-
-	vsprintf(buf, format, args);
-
+	vasprintf(&buffer, format, args);
 	va_end(args);
 	va_end(args);
 
 
 	if (gtd->level->debug || HAS_BIT(root->flags, LIST_FLAG_DEBUG))
 	if (gtd->level->debug || HAS_BIT(root->flags, LIST_FLAG_DEBUG))
 	{
 	{
 		gtd->level->verbose++;
 		gtd->level->verbose++;
 
 
-		tintin_puts2(ses, buf);
+		tintin_puts2(ses, buffer);
 
 
 		gtd->level->verbose--;
 		gtd->level->verbose--;
 
 
-		pop_call();
-		return;
+		goto end;
 	}
 	}
 
 
 	if (HAS_BIT(root->flags, LIST_FLAG_LOG))
 	if (HAS_BIT(root->flags, LIST_FLAG_LOG))
 	{
 	{
 		if (ses->log->file)
 		if (ses->log->file)
 		{
 		{
-			logit(ses, buf, ses->log->file, LOG_FLAG_LINEFEED);
+			logit(ses, buffer, ses->log->file, LOG_FLAG_LINEFEED);
 		}
 		}
 	}
 	}
+	end:
+	
+	free(buffer);
+
 	pop_call();
 	pop_call();
 	return;
 	return;
 }
 }
@@ -292,7 +293,7 @@ void show_debug(struct session *ses, int index, char *format, ...)
 void show_info(struct session *ses, int index, char *format, ...)
 void show_info(struct session *ses, int index, char *format, ...)
 {
 {
 	struct listroot *root;
 	struct listroot *root;
-	char buf[STRING_SIZE];
+	char *buffer;
 	va_list args;
 	va_list args;
 
 
 	push_call("show_info(%p,%p,%p)",ses,index,format);
 	push_call("show_info(%p,%p,%p)",ses,index,format);
@@ -304,16 +305,15 @@ void show_info(struct session *ses, int index, char *format, ...)
 		pop_call();
 		pop_call();
 		return;
 		return;
 	}
 	}
+	buffer = str_alloc_stack(0);
 
 
 	va_start(args, format);
 	va_start(args, format);
-
-	vsprintf(buf, format, args);
-
+	vsprintf(buffer, format, args);
 	va_end(args);
 	va_end(args);
 
 
 	gtd->level->verbose++;
 	gtd->level->verbose++;
 
 
-	tintin_puts(ses, buf);
+	tintin_puts(ses, buffer);
 
 
 	gtd->level->verbose--;
 	gtd->level->verbose--;
 
 
@@ -383,19 +383,14 @@ void show_lines(struct session *ses, char *str)
 	return;
 	return;
 }
 }
 
 
-
 void tintin_header(struct session *ses, int width, char *format, ...)
 void tintin_header(struct session *ses, int width, char *format, ...)
 {
 {
-	char arg[BUFFER_SIZE], buf[BUFFER_SIZE];
+	char *title, *buffer;
 	va_list args;
 	va_list args;
 	int cols;
 	int cols;
 
 
 	push_call("tintin_header(%p,%p)",ses,format);
 	push_call("tintin_header(%p,%p)",ses,format);
 
 
-	va_start(args, format);
-	vsprintf(arg, format, args);
-	va_end(args);
-
 	if (width)
 	if (width)
 	{
 	{
 		cols = UMIN(width, get_scroll_cols(ses));
 		cols = UMIN(width, get_scroll_cols(ses));
@@ -411,25 +406,32 @@ void tintin_header(struct session *ses, int width, char *format, ...)
 		return;
 		return;
 	}
 	}
 
 
-	if ((int) strlen(arg) > cols - 2)
+	va_start(args, format);
+	vasprintf(&title, format, args);
+	va_end(args);
+
+	if ((int) strlen(title) > cols - 2)
 	{
 	{
-		arg[cols - 2] = 0;
+		title[cols - 2] = 0;
 	}
 	}
 
 
+	buffer = calloc(1, cols + 1);
+
 	if (HAS_BIT(ses->config_flags, CONFIG_FLAG_SCREENREADER))
 	if (HAS_BIT(ses->config_flags, CONFIG_FLAG_SCREENREADER))
 	{
 	{
-		memset(buf, ' ', cols);
+		memset(buffer, ' ', cols);
 	}
 	}
 	else
 	else
 	{
 	{
-		memset(buf, '#', cols);
+		memset(buffer, '#', cols);
 	}
 	}
 
 
-	memcpy(&buf[(cols - strlen(arg)) / 2], arg, strlen(arg));
+	memcpy(&buffer[(cols - strlen(title)) / 2], title, strlen(title));
 
 
-	buf[cols] = 0;
+	tintin_puts2(ses, buffer);
 
 
-	tintin_puts2(ses, buf);
+	free(title);
+	free(buffer);
 
 
 	pop_call();
 	pop_call();
 	return;
 	return;
@@ -462,14 +464,21 @@ void tintin_printf2(struct session *ses, char *format, ...)
 
 
 void tintin_printf(struct session *ses, char *format, ...)
 void tintin_printf(struct session *ses, char *format, ...)
 {
 {
-	char buffer[BUFFER_SIZE];
+	char *buffer;
 	va_list args;
 	va_list args;
 
 
+	push_call("tintin_printf(%p,%p,...)",ses,format);
+
+	buffer = str_alloc_stack(0);
+
 	va_start(args, format);
 	va_start(args, format);
 	vsprintf(buffer, format, args);
 	vsprintf(buffer, format, args);
 	va_end(args);
 	va_end(args);
 
 
 	tintin_puts(ses, buffer);
 	tintin_puts(ses, buffer);
+
+	pop_call();
+	return;
 }
 }
 
 
 /*
 /*

+ 1 - 1
src/split.c

@@ -323,7 +323,7 @@ void split_show(struct session *ses, char *prompt, char *row_str, char *col_str)
 	}
 	}
 	else
 	else
 	{
 	{
-		sprintf(buf1, "%.*s", raw_len_str(ses, prompt, 0, gtd->screen->cols - col), prompt);
+		snprintf(buf1, BUFFER_SIZE, "%.*s", raw_len_str(ses, prompt, 0, gtd->screen->cols - col), prompt);
 
 
 		show_debug(ses, LIST_PROMPT, "#DEBUG PROMPT {%s}", prompt);
 		show_debug(ses, LIST_PROMPT, "#DEBUG PROMPT {%s}", prompt);
 		show_debug(ses, LIST_PROMPT, "#PROMPT WIDTH %d WITH OFFSET %d LONGER THAN ROW SIZE %d.", width, col, gtd->screen->cols);
 		show_debug(ses, LIST_PROMPT, "#PROMPT WIDTH %d WITH OFFSET %d LONGER THAN ROW SIZE %d.", width, col, gtd->screen->cols);

+ 3 - 3
src/string.c

@@ -416,7 +416,7 @@ char *calign(struct session *ses, char *in, char *out, int width)
 
 
 	width = UMAX(0, width - width_in);
 	width = UMAX(0, width - width_in);
 
 
-	sprintf(out, "%*s%s%*s", width / 2, "", in, width - width / 2, "");
+	snprintf(out, BUFFER_SIZE, "%*s%s%*s", width / 2, "", in, width - width / 2, "");
 
 
 	return out;
 	return out;
 }
 }
@@ -441,7 +441,7 @@ char *lalign(struct session *ses, char *in, char *out, int width)
 
 
 	width = UMAX(0, width - width_in);
 	width = UMAX(0, width - width_in);
 
 
-	sprintf(out, "%s%*s", in, width, "");
+	snprintf(out, BUFFER_SIZE, "%s%*s", in, width, "");
 
 
 	return out;
 	return out;
 }
 }
@@ -466,7 +466,7 @@ char *ralign(struct session *ses, char *in, char *out, int width)
 
 
 	width = UMAX(0, width - width_in);
 	width = UMAX(0, width - width_in);
 
 
-	sprintf(out, "%*s%s", width, "", in);
+	snprintf(out, BUFFER_SIZE, "%*s%s", width, "", in);
 
 
 	return out;
 	return out;
 }
 }

+ 38 - 39
src/substitute.c

@@ -862,6 +862,9 @@ char *lit_color_code(struct session *ses, char *pti, int mod)
 	return "";
 	return "";
 }
 }
 
 
+// color gradient <faa><afa>
+// faa fba fca fda fea ffa efa dfa cfa bfa afa
+
 int color_gradient(char *pti, int min, int max)
 int color_gradient(char *pti, int min, int max)
 {
 {
 	char buf[6];
 	char buf[6];
@@ -1023,16 +1026,13 @@ int color_gradient(char *pti, int min, int max)
 	return 0;
 	return 0;
 }
 }
 
 
-// color gradient <faa><afa>
-// faa fba fca fda fea ffa efa dfa cfa bfa afa
-
 
 
 int substitute(struct session *ses, char *string, char *result, int flags)
 int substitute(struct session *ses, char *string, char *result, int flags)
 {
 {
 	struct listnode *node;
 	struct listnode *node;
 	struct listroot *root;
 	struct listroot *root;
 	struct session *sesptr;
 	struct session *sesptr;
-	char *temp, *buf, *buffer, *pti, *pto, *ptt, *str;
+	char *temp, *buf, *buffer, *pti, *pto, *out, *ptt, *str;
 	char *pte, old[10] = { 0 };
 	char *pte, old[10] = { 0 };
 	int i, skip, cnt, escape = FALSE, flags_neol = flags;
 	int i, skip, cnt, escape = FALSE, flags_neol = flags;
 
 
@@ -1043,7 +1043,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 	buffer = str_alloc_stack(0);
 	buffer = str_alloc_stack(0);
 
 
 	pti = string;
 	pti = string;
-	pto = (string == result) ? buffer : result;
+	pto = out = (string == result) ? buffer : result;
 
 
 	DEL_BIT(flags_neol, SUB_EOL|SUB_LNF);
 	DEL_BIT(flags_neol, SUB_EOL|SUB_LNF);
 
 
@@ -1983,7 +1983,6 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 				}
 				}
 				break;
 				break;
 
 
-
 			case '\\':
 			case '\\':
 				if (HAS_BIT(flags, SUB_ESC))
 				if (HAS_BIT(flags, SUB_ESC))
 				{
 				{
@@ -2102,6 +2101,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 							DEL_BIT(flags, SUB_LNF);
 							DEL_BIT(flags, SUB_LNF);
 							continue;
 							continue;
 
 
+						case ';':
 						case '$':
 						case '$':
 						case '&':
 						case '&':
 						case '*':
 						case '*':
@@ -2144,6 +2144,11 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 				}
 				}
 				break;
 				break;
 
 
+			case '\n':
+				old[0] = 0;
+				*pto++ = *pti++;
+				break;
+
 			case ASCII_ESC:
 			case ASCII_ESC:
 				if (HAS_BIT(flags, SUB_COL) && ses->color == 0)
 				if (HAS_BIT(flags, SUB_COL) && ses->color == 0)
 				{
 				{
@@ -2158,6 +2163,32 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 						*pto++ = *pti++;
 						*pto++ = *pti++;
 					}
 					}
 				}
 				}
+				else if (HAS_BIT(flags, SUB_SEC) && !HAS_BIT(flags, SUB_ARG))
+				{
+					skip = find_secure_color_code(pti);
+
+					if (skip)
+					{
+						pto += sprintf(pto, "%.*s", skip, pti);
+						pti += skip;
+					}
+					else
+					{
+						*pto++ = *pti++;
+					}
+				}
+				else
+				{
+					*pto++ = *pti++;
+				}
+				break;
+
+			case COMMAND_SEPARATOR:
+				if (HAS_BIT(flags, SUB_SEC) && !HAS_BIT(flags, SUB_ARG))
+				{
+					*pto++ = '\\';
+					*pto++ = *pti++;
+				}
 				else
 				else
 				{
 				{
 					*pto++ = *pti++;
 					*pto++ = *pti++;
@@ -2193,39 +2224,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 				break;	
 				break;	
 
 
 			default:
 			default:
-				if (HAS_BIT(flags, SUB_SEC) && !HAS_BIT(flags, SUB_ARG))
-				{
-					switch (*pti)
-					{
-						case '\e':
-							skip = find_secure_color_code(pti);
-
-							if (skip)
-							{
-								pto += sprintf(pto, "%.*s", skip, pti);
-								pti += skip;
-							}
-							else
-							{
-								*pto++ = *pti++;
-							}
-							break;
-
-						case COMMAND_SEPARATOR:
-							*pto++ = '\\';
-							*pto++ = COMMAND_SEPARATOR;
-							break;
-
-						default:
-							*pto++ = *pti;
-							break;
-					}
-					pti++;
-				}
-				else
-				{
-					*pto++ = *pti++;
-				}
+				*pto++ = *pti++;
 				break;
 				break;
 		}
 		}
 	}
 	}

+ 18 - 19
src/tables.c

@@ -80,6 +80,7 @@ struct charset_type charset_table[] =
 
 
 	{    "BIG5TOUTF8",    "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5TOUTF8 },
 	{    "BIG5TOUTF8",    "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5TOUTF8 },
 	{    "CP1251TOUTF8",  "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_CP1251TOUTF8 },
 	{    "CP1251TOUTF8",  "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_CP1251TOUTF8 },
+	{    "CP437TOUTF8",   "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_FANSITOUTF8 },
 	{    "CP949TOUTF8",   "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_CP949TOUTF8 },
 	{    "CP949TOUTF8",   "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_CP949TOUTF8 },
 	{    "FANSITOUTF8",   "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_FANSITOUTF8 },
 	{    "FANSITOUTF8",   "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_FANSITOUTF8 },
 	{    "GBK1TOUTF8",    "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_GBK1TOUTF8 },
 	{    "GBK1TOUTF8",    "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_GBK1TOUTF8 },
@@ -1182,7 +1183,7 @@ struct buffer_type buffer_table[] =
 	{    "CLEAR",             buffer_clear,        "Clear buffer."                                  },
 	{    "CLEAR",             buffer_clear,        "Clear buffer."                                  },
 	{    "DOWN",              buffer_down,         "Scroll down one page."                          },
 	{    "DOWN",              buffer_down,         "Scroll down one page."                          },
 	{    "END",               buffer_end,          "Scroll down to the end of the buffer."          },
 	{    "END",               buffer_end,          "Scroll down to the end of the buffer."          },
-	{    "FIND",              buffer_find,         "Move to the given string in the buffer."        },
+	{    "FIND",              buffer_find,         "Jump to the given string."                      },
 	{    "GET",               buffer_get,          "Store in given variable a given line or range." },
 	{    "GET",               buffer_get,          "Store in given variable a given line or range." },
 	{    "HOME",              buffer_home,         "Scroll up to the start of the buffer."          },
 	{    "HOME",              buffer_home,         "Scroll up to the start of the buffer."          },
 	{    "INFO",              buffer_info,         "Display statistics about the buffer."           },
 	{    "INFO",              buffer_info,         "Display statistics about the buffer."           },
@@ -1750,17 +1751,18 @@ struct stamp_type huge_stamp_table[] =
 	{ "!",  23, "██╗\n██║\n██║\n╚═╝\n██╗\n╚═╝" },
 	{ "!",  23, "██╗\n██║\n██║\n╚═╝\n██╗\n╚═╝" },
 	{ "\"", 41, "██╗██╗\n2██║██║\n╚═╝╚═╝\n   \n   \n   " },
 	{ "\"", 41, "██╗██╗\n2██║██║\n╚═╝╚═╝\n   \n   \n   " },
 	{ "#",  59, " ██╗ ██╗ \n████████╗\n╚██╔═██╔╝\n████████╗\n╚██╔═██╔╝\n ╚═╝ ╚═╝ " },
 	{ "#",  59, " ██╗ ██╗ \n████████╗\n╚██╔═██╔╝\n████████╗\n╚██╔═██╔╝\n ╚═╝ ╚═╝ " },
-	{ "&",  59, "  ████╗  \n ██╔═██╗ \n ╚████╔╝ \n██╔══██═╗\n╚█████╔█║\n ╚════╝╚╝" },
+	{ "$",  53, "▄▄███▄▄ \n██╔█══╝ \n███████╗\n╚══█═██║\n███████║\n╚═▀▀▀══╝" },
 	{ "%",  47, "██╗ ██╗\n╚═╝██╔╝\n  ██╔╝ \n ██╔╝  \n██╔╝██╗\n╚═╝ ╚═╝" },
 	{ "%",  47, "██╗ ██╗\n╚═╝██╔╝\n  ██╔╝ \n ██╔╝  \n██╔╝██╗\n╚═╝ ╚═╝" },
-	{ "'",  23, "╗██╗\n██║\n╚═╝\n   \n   \n   " },
+	{ "&",  59, "  ████╗  \n ██╔═██╗ \n ╚████╔╝ \n██╔══██═╗\n╚█████╔█║\n ╚════╝╚╝" },
+	{ "'",  23, "██╗\n██║\n╚═╝\n   \n   \n   " },
 	{ "(",  29, " ██╗\n██╔╝\n██║ \n██║ \n╚██╗\n ╚═╝" },
 	{ "(",  29, " ██╗\n██╔╝\n██║ \n██║ \n╚██╗\n ╚═╝" },
 	{ ")",  29, "██╗ \n╚██╗\n ██║\n ██║\n██╔╝\n╚═╝ " },
 	{ ")",  29, "██╗ \n╚██╗\n ██║\n ██║\n██╔╝\n╚═╝ " },
 	{ "*",  47, "▄  █  ▄\n █▄█▄█ \n  ▐█▌  \n █▀█▀█ \n▀  █  ▀\n       " },
 	{ "*",  47, "▄  █  ▄\n █▄█▄█ \n  ▐█▌  \n █▀█▀█ \n▀  █  ▀\n       " },
 	{ "+",  59, "   ██╗   \n   ██║   \n████████╗\n╚══██╔══╝\n   ██║   \n   ╚═╝   " },
 	{ "+",  59, "   ██╗   \n   ██║   \n████████╗\n╚══██╔══╝\n   ██║   \n   ╚═╝   " },
-// ,
-// -
-// .
-// /
+	{ ",",  23, "   \n   \n   \n██╗\n╚█║\n ╚╝" },
+	{ "-",  53, "        \n        \n███████╗\n╚══════╝\n        \n        " },
+	{ ".",  23, "   \n   \n   \n   \n██╗\n╚═╝" },
+	{ "/",  47, "    ██╗\n   ██╔╝\n  ██╔╝ \n ██╔╝  \n██╔╝   \n╚═╝    " },
 
 
 	{ "0",  53, " █████╗ \n██╔══██╗\n██║  ██║\n██║  ██║\n╚█████╔╝\n ╚════╝ " },
 	{ "0",  53, " █████╗ \n██╔══██╗\n██║  ██║\n██║  ██║\n╚█████╔╝\n ╚════╝ " },
 	{ "1",  53, "  ▄██╗  \n ████║  \n ╚═██║  \n   ██║  \n ██████╗\n ╚═════╝" },
 	{ "1",  53, "  ▄██╗  \n ████║  \n ╚═██║  \n   ██║  \n ██████╗\n ╚═════╝" },
@@ -1772,14 +1774,12 @@ struct stamp_type huge_stamp_table[] =
 	{ "7",  53, "███████╗\n╚════██║\n    ██╔╝\n   ██╔╝ \n   ██║  \n   ╚═╝  " },
 	{ "7",  53, "███████╗\n╚════██║\n    ██╔╝\n   ██╔╝ \n   ██║  \n   ╚═╝  " },
 	{ "8",  53, " █████╗ \n██╔══██╗\n╚█████╔╝\n██╔══██╗\n╚█████╔╝\n ╚════╝ " },
 	{ "8",  53, " █████╗ \n██╔══██╗\n╚█████╔╝\n██╔══██╗\n╚█████╔╝\n ╚════╝ " },
 	{ "9",  53, " █████╗ \n██╔══██╗\n╚██████║\n ╚═══██║\n █████╔╝\n ╚════╝ " },
 	{ "9",  53, " █████╗ \n██╔══██╗\n╚██████║\n ╚═══██║\n █████╔╝\n ╚════╝ " },
-
 	{ ":",  53, "        \n   ██╗  \n   ╚═╝  \n        \n   ██╗  \n   ╚═╝  " },
 	{ ":",  53, "        \n   ██╗  \n   ╚═╝  \n        \n   ██╗  \n   ╚═╝  " },
-// ;
-// <
-// =
-// >
-// ?
-
+	{ ";",  53, "        \n   ██╗  \n   ╚═╝  \n   ██╗  \n   ╚█║  \n    ╚╝  " },
+	{ "<",  47, "   ██╗ \n  ██╔╝ \n ██╔╝  \n ╚██╗  \n  ╚██╗ \n   ╚═╝ " },
+	{ "=",  53, "        \n███████╗\n╚══════╝\n███████╗\n╚══════╝\n        " },
+	{ ">",  47, " ██╗   \n ╚██╗  \n  ╚██╗ \n  ██╔╝ \n ██╔╝  \n ╚═╝   " },
+        { "?",  47, "██████╗\n╚═══██║\n ▄███╔╝\n ▀▀══╝ \n ██╗   \n ╚═╝   " },
 
 
 	{ "@",  59, " ██████╗ \n██╔═══██╗\n██║██╗██║\n██║██║██║\n╚█║████╔╝\n ╚╝╚═══╝ " },
 	{ "@",  59, " ██████╗ \n██╔═══██╗\n██║██╗██║\n██║██║██║\n╚█║████╔╝\n ╚╝╚═══╝ " },
 	{ "A",  53, " █████╗ \n██╔══██╗\n███████║\n██╔══██║\n██║  ██║\n╚═╝  ╚═╝" },
 	{ "A",  53, " █████╗ \n██╔══██╗\n███████║\n██╔══██║\n██║  ██║\n╚═╝  ╚═╝" },
@@ -1808,13 +1808,12 @@ struct stamp_type huge_stamp_table[] =
 	{ "X",  53, "██╗  ██╗\n╚██╗██╔╝\n ╚███╔╝ \n ██╔██╗ \n██╔╝ ██╗\n╚═╝  ╚═╝" },
 	{ "X",  53, "██╗  ██╗\n╚██╗██╔╝\n ╚███╔╝ \n ██╔██╗ \n██╔╝ ██╗\n╚═╝  ╚═╝" },
 	{ "Y",  59, "██╗   ██╗\n╚██╗ ██╔╝\n ╚████╔╝ \n  ╚██╔╝  \n   ██║   \n   ╚═╝   " },
 	{ "Y",  59, "██╗   ██╗\n╚██╗ ██╔╝\n ╚████╔╝ \n  ╚██╔╝  \n   ██║   \n   ╚═╝   " },
 	{ "Z",  53, "███████╗\n╚══███╔╝\n  ███╔╝ \n ███╔╝  \n███████╗\n╚══════╝" },
 	{ "Z",  53, "███████╗\n╚══███╔╝\n  ███╔╝ \n ███╔╝  \n███████╗\n╚══════╝" },
-
-// [
-
-// ]
+	{ "[",  41, " ███╗ \n ██╔╝ \n ██║  \n ██║  \n ███╗ \n ╚══╝ " },
+	{ "\\", 41, "██╗    \n╚██╗   \n ╚██╗  \n  ╚██╗ \n   ╚██╗\n    ╚═╝" },
+	{ "]",  41, " ███╗ \n ╚██║ \n  ██║ \n  ██║ \n ███║ \n ╚══╝ " },
 	{ "^",  41, " ███╗ \n██╔██╗\n╚═╝╚═╝\n      \n      \n" },
 	{ "^",  41, " ███╗ \n██╔██╗\n╚═╝╚═╝\n      \n      \n" },
 	{ "_",  53, "        \n        \n        \n        \n███████╗\n╚══════╝" },
 	{ "_",  53, "        \n        \n        \n        \n███████╗\n╚══════╝" },
-// `
+	{ "`",  23, "██╗\n╚█║\n ╚╝\n   \n   \n   " },
 
 
 	{ "i",  23, "██╗\n╚═╝\n██╗\n██║\n██║\n╚═╝" },
 	{ "i",  23, "██╗\n╚═╝\n██╗\n██║\n██║\n╚═╝" },
 	{ "n",  47, "       \n       \n██▟███╗\n██║ ██║\n██║ ██║\n╚═╝ ╚═╝" },
 	{ "n",  47, "       \n       \n██▟███╗\n██║ ██║\n██║ ██║\n╚═╝ ╚═╝" },

+ 52 - 19
src/telopt_client.c

@@ -782,7 +782,7 @@ int client_recv_will_echo(struct session *ses, int cplen, unsigned char *cpsrc)
 
 
 		telnet_printf(ses, 3, "%c%c%c", IAC, DO, TELOPT_ECHO);
 		telnet_printf(ses, 3, "%c%c%c", IAC, DO, TELOPT_ECHO);
 
 
-		client_telopt_debug(ses, "SENT IAC DO ECHO (SKIPPED)");
+		client_telopt_debug(ses, "SENT IAC DO ECHO");
 	}
 	}
 	else
 	else
 	{
 	{
@@ -1300,7 +1300,7 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 {
 {
 	char buf[BUFFER_SIZE], var[BUFFER_SIZE];
 	char buf[BUFFER_SIZE], var[BUFFER_SIZE];
 	char *pto;
 	char *pto;
-	int i, j, accept;
+	int i, j, accept, found = 0, request = 0;
 
 
 	if (client_skip_sb(ses, cplen, src) > cplen)
 	if (client_skip_sb(ses, cplen, src) > cplen)
 	{
 	{
@@ -1309,14 +1309,26 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 
 
 //	client_telopt_debug(ses, "RCVD IAC SB CHARSET %d %d", src[3], src[4]);
 //	client_telopt_debug(ses, "RCVD IAC SB CHARSET %d %d", src[3], src[4]);
 
 
-	i = 5;
+	if (src[3] == CHARSET_REQUEST)
+	{
+		i = 5;
+		request = 1;
+	}
+	else
+	{
+		i = 4;
+	}
 
 
 	while (i < cplen && src[i] != SE)
 	while (i < cplen && src[i] != SE)
 	{
 	{
 		pto = buf;
 		pto = buf;
 
 
-		while (i < cplen && src[i] != src[4] && src[i] != IAC)
+		while (i < cplen && src[i] != IAC)
 		{
 		{
+			if (request && src[i] == src[4])
+			{
+				break;
+			}
 			*pto++ = src[i++];
 			*pto++ = src[i++];
 		}
 		}
 		*pto = 0;
 		*pto = 0;
@@ -1366,7 +1378,7 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 				{
 				{
 					accept = HAS_BIT(ses->charset, CHARSET_FLAG_CP949) || HAS_BIT(ses->charset, CHARSET_FLAG_CP949TOUTF8);
 					accept = HAS_BIT(ses->charset, CHARSET_FLAG_CP949) || HAS_BIT(ses->charset, CHARSET_FLAG_CP949TOUTF8);
 				}
 				}
-				else if (!strcmp(var, "FANSI"))
+				else if (!strcmp(var, "FANSI") || !strcmp(var, "CP437"))
 				{
 				{
 					accept = HAS_BIT(ses->charset, CHARSET_FLAG_FANSITOUTF8);
 					accept = HAS_BIT(ses->charset, CHARSET_FLAG_FANSITOUTF8);
 				}
 				}
@@ -1385,20 +1397,13 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 
 
 				if (!check_all_events(ses, EVENT_FLAG_CATCH, 2, 2, "CATCH IAC SB CHARSET %s %s", buf, var, buf, var))
 				if (!check_all_events(ses, EVENT_FLAG_CATCH, 2, 2, "CATCH IAC SB CHARSET %s %s", buf, var, buf, var))
 				{
 				{
-					if (accept >= 0)
+					if (accept > 0 && found == 0)
 					{
 					{
-						if (accept)
-						{
-							telnet_printf(ses, -1, "%c%c%c%c %s%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_ACCEPTED, var, IAC, SE);
+						telnet_printf(ses, -1, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_ACCEPTED, var, IAC, SE);
 							
 							
-							client_telopt_debug(ses, "SENT IAC SB CHARSET ACCEPTED %s", var);
-						}
-						else
-						{
-							telnet_printf(ses, -1, "%c%c%c%c %s%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_REJECTED, var, IAC, SE);
+						client_telopt_debug(ses, "SENT IAC SB CHARSET ACCEPTED %s", var);
 
 
-							client_telopt_debug(ses, "SENT IAC SB CHARSET REJECTED %s", var);
-						}
+						found = 1;
 					}
 					}
 				}
 				}
 			}
 			}
@@ -1406,6 +1411,12 @@ int client_recv_sb_charset(struct session *ses, int cplen, unsigned char *src)
 		i++;
 		i++;
 	}
 	}
 
 
+	if (request == 1 && found == 0)
+	{
+		telnet_printf(ses, -1, "%c%c%c%c%c%c", IAC, SB, TELOPT_CHARSET, CHARSET_REJECTED, IAC, SE);
+
+		client_telopt_debug(ses, "SENT IAC SB CHARSET REJECTED");
+	}
 	client_telopt_debug(ses, "RCVD IAC SB CHARSET IAC SE");
 	client_telopt_debug(ses, "RCVD IAC SB CHARSET IAC SE");
 
 
 	check_all_events(ses, EVENT_FLAG_TELNET, 0, 0, "IAC SB CHARSET IAC SE");
 	check_all_events(ses, EVENT_FLAG_TELNET, 0, 0, "IAC SB CHARSET IAC SE");
@@ -1653,6 +1664,7 @@ int client_recv_sb_zmp(struct session *ses, int cplen, unsigned char *src)
 	return UMIN(i + 1, cplen);
 	return UMIN(i + 1, cplen);
 }
 }
 
 
+
 int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 {
 {
 	char mod[BUFFER_SIZE], val[BUFFER_SIZE], json[BUFFER_SIZE], *pto;
 	char mod[BUFFER_SIZE], val[BUFFER_SIZE], json[BUFFER_SIZE], *pto;
@@ -1705,6 +1717,10 @@ int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 				break;
 				break;
 
 
 			case '{':
 			case '{':
+				if (state[nest])
+				{
+					pto += sprintf(pto, "{%d}", state[nest]++);
+				}
 				if (nest != 0)
 				if (nest != 0)
 				{
 				{
 					*pto++ = '{';
 					*pto++ = '{';
@@ -1729,7 +1745,7 @@ int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 				}
 				}
 				i++;
 				i++;
 				state[++nest] = 1;
 				state[++nest] = 1;
-				pto += sprintf(pto, "{%d}", state[nest]);
+//				pto += sprintf(pto, "{%d}", state[nest]);
 				break;
 				break;
 
 
 			case ']':
 			case ']':
@@ -1747,14 +1763,18 @@ int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 
 
 			case ',':
 			case ',':
 				i++;
 				i++;
-				if (state[nest])
+/*				if (state[nest])
 				{
 				{
 					pto += sprintf(pto, "{%d}", ++state[nest]);
 					pto += sprintf(pto, "{%d}", ++state[nest]);
-				}
+				}*/
 				break;
 				break;
 
 
 			case '"':
 			case '"':
 				i++;
 				i++;
+				if (state[nest])
+				{
+					pto += sprintf(pto, "{%d}", state[nest]++);
+				}
 				if (nest)
 				if (nest)
 				{
 				{
 					*pto++ = '{';
 					*pto++ = '{';
@@ -1818,6 +1838,10 @@ int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 				break;
 				break;
 
 
 			default:
 			default:
+				if (state[nest])
+				{
+					pto += sprintf(pto, "{%d}", state[nest]++);
+				}
 				if (nest)
 				if (nest)
 				{
 				{
 					*pto++ = '{';
 					*pto++ = '{';
@@ -1914,6 +1938,15 @@ int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 	return UMIN(i + 1, cplen);
 	return UMIN(i + 1, cplen);
 }
 }
 
 
+void test_gmcp(struct session *ses, char *buf)
+{
+	char mod[BUFFER_SIZE];
+
+	int cplen = sprintf(mod, "%c%c%c%s%c%c", IAC, SB, TELOPT_GMCP, buf, IAC, SE);
+
+	client_recv_sb_gmcp(ses, cplen, (unsigned char *) mod);
+}
+
 /*
 /*
 	MCCP2
 	MCCP2
 */
 */

+ 15 - 1
src/terminal.c

@@ -35,6 +35,11 @@ void init_terminal(struct session *ses)
 {
 {
 	struct termios io;
 	struct termios io;
 
 
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_NOHUP))
+	{
+		return;
+	}
+
 	if (tcgetattr(0, &gtd->old_terminal))
 	if (tcgetattr(0, &gtd->old_terminal))
 	{
 	{
 		syserr_fatal(-1, "init_terminal: tcgetattr 1");
 		syserr_fatal(-1, "init_terminal: tcgetattr 1");
@@ -85,6 +90,11 @@ void init_terminal(struct session *ses)
 
 
 void reset_terminal(struct session *ses)
 void reset_terminal(struct session *ses)
 {
 {
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_NOHUP))
+	{
+		return;
+	}
+	
 	if (gtd->detach_port == 0)
 	if (gtd->detach_port == 0)
 	{
 	{
 		if (tcsetattr(0, TCSANOW, &gtd->old_terminal))
 		if (tcsetattr(0, TCSANOW, &gtd->old_terminal))
@@ -97,7 +107,7 @@ void reset_terminal(struct session *ses)
 	{
 	{
 		print_stdout(0, 0, "\e[?1000l\e[?1002l\e[?1006l");
 		print_stdout(0, 0, "\e[?1000l\e[?1002l\e[?1006l");
 	}
 	}
-	print_stdout(0, 0, "\e[?25h\e[23t\e[?1004l\e[>4n\e[?47l\e[r\e[0#t");
+	print_stdout(0, 0, "\e[?25h\e[23t\e[?1004l\e[>4n\e[>4;0m\e[?47l\e[r\e[0#t");
 }
 }
 
 
 
 
@@ -160,6 +170,10 @@ void init_terminal_size(struct session *ses)
 				}
 				}
 			}
 			}
 		}
 		}
+		else
+		{
+			init_screen(SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT * 16, SCREEN_WIDTH * 10);
+		}
 	}
 	}
 
 
 	if (ses->scroll)
 	if (ses->scroll)

+ 20 - 6
src/tintin.h

@@ -124,6 +124,7 @@
 #define GET_ALL                          1 // stop at semicolon
 #define GET_ALL                          1 // stop at semicolon
 #define GET_NST                          2 // nest square brackets
 #define GET_NST                          2 // nest square brackets
 #define GET_VBT                          4 // ignore semicolon for verbatim mode
 #define GET_VBT                          4 // ignore semicolon for verbatim mode
+#define GET_SPC                          8 // don't strip spaces
 
 
 #define TEL_N                            0
 #define TEL_N                            0
 #define TEL_Y                            1
 #define TEL_Y                            1
@@ -196,6 +197,8 @@
 
 
 #define HISTORY_FILE         "history.txt"
 #define HISTORY_FILE         "history.txt"
 
 
+#define MALLOC_SIZE                1000000
+#define STRING_SIZE                  80000
 #define BUFFER_SIZE                  40000
 #define BUFFER_SIZE                  40000
 #define INPUT_SIZE                   10000
 #define INPUT_SIZE                   10000
 #define PATH_SIZE                     4096
 #define PATH_SIZE                     4096
@@ -207,10 +210,9 @@
 #define CHAR_SIZE                        5
 #define CHAR_SIZE                        5
 #define LIST_SIZE                        2
 #define LIST_SIZE                        2
 
 
-#define STRING_SIZE        2 * BUFFER_SIZE
 
 
 #define CLIENT_NAME              "TinTin++"
 #define CLIENT_NAME              "TinTin++"
-#define CLIENT_VERSION           "2.02.12 "
+#define CLIENT_VERSION           "2.02.20 "
 
 
 
 
 #define XT_E                            0x27
 #define XT_E                            0x27
@@ -601,7 +603,7 @@ enum operators
 
 
 
 
 
 
-#define TINTIN_FLAG_GETNUMBER         BV01 // UNUSED
+#define TINTIN_FLAG_HISTORYUPDATE     BV01
 #define TINTIN_FLAG_SESSIONUPDATE     BV02
 #define TINTIN_FLAG_SESSIONUPDATE     BV02
 #define TINTIN_FLAG_PROCESSINPUT      BV03
 #define TINTIN_FLAG_PROCESSINPUT      BV03
 #define TINTIN_FLAG_INHERITANCE       BV04
 #define TINTIN_FLAG_INHERITANCE       BV04
@@ -615,6 +617,7 @@ enum operators
 #define TINTIN_FLAG_LOCAL             BV12
 #define TINTIN_FLAG_LOCAL             BV12
 #define TINTIN_FLAG_PRESERVEMACRO     BV13
 #define TINTIN_FLAG_PRESERVEMACRO     BV13
 #define TINTIN_FLAG_WINCHUPDATE       BV14
 #define TINTIN_FLAG_WINCHUPDATE       BV14
+#define TINTIN_FLAG_NOHUP             BV15 // fixes tcsetattr crashes with nohup
 
 
 #define CONFIG_FLAG_AUTOPATCH         BV01
 #define CONFIG_FLAG_AUTOPATCH         BV01
 #define CONFIG_FLAG_AUTOPROMPT        BV02
 #define CONFIG_FLAG_AUTOPROMPT        BV02
@@ -681,7 +684,7 @@ enum operators
 #define LIST_FLAG_NEST                BV13
 #define LIST_FLAG_NEST                BV13
 #define LIST_FLAG_DEFAULT             LIST_FLAG_MESSAGE
 #define LIST_FLAG_DEFAULT             LIST_FLAG_MESSAGE
 
 
-#define NODE_FLAG_ONESHOT             BV01
+#define NODE_FLAG_ONESHOT             BV01 // unused
 
 
 #define LOG_FLAG_NONE                    0
 #define LOG_FLAG_NONE                    0
 #define LOG_FLAG_LINEFEED             BV01
 #define LOG_FLAG_LINEFEED             BV01
@@ -754,12 +757,13 @@ enum operators
 #define MAP_FLAG_BLOCKGRAPHICS        BV10
 #define MAP_FLAG_BLOCKGRAPHICS        BV10
 #define MAP_FLAG_RESIZE               BV11
 #define MAP_FLAG_RESIZE               BV11
 #define MAP_FLAG_SYNC                 BV12
 #define MAP_FLAG_SYNC                 BV12
-#define MAP_FLAG_ASCIILENGTH          BV13 // For debugging but might be useful
+#define MAP_FLAG_ASCIILENGTH          BV13
 #define MAP_FLAG_TERRAIN              BV14
 #define MAP_FLAG_TERRAIN              BV14
 #define MAP_FLAG_UPDATETERRAIN        BV15
 #define MAP_FLAG_UPDATETERRAIN        BV15
 #define MAP_FLAG_DOUBLED              BV16
 #define MAP_FLAG_DOUBLED              BV16
 #define MAP_FLAG_QUIET                BV17
 #define MAP_FLAG_QUIET                BV17
 #define MAP_FLAG_READ                 BV18
 #define MAP_FLAG_READ                 BV18
+#define MAP_FLAG_PANCAKE              BV19
 
 
 #define MAP_SEARCH_NAME                0
 #define MAP_SEARCH_NAME                0
 #define MAP_SEARCH_EXITS               1
 #define MAP_SEARCH_EXITS               1
@@ -829,6 +833,7 @@ enum operators
 #define STARTUP_FLAG_ARGUMENT            8
 #define STARTUP_FLAG_ARGUMENT            8
 #define STARTUP_FLAG_NOTITLE            16
 #define STARTUP_FLAG_NOTITLE            16
 #define STARTUP_FLAG_VERBOSE            32
 #define STARTUP_FLAG_VERBOSE            32
+#define STARTUP_FLAG_NOHUP              64
 
 
 #define WRAP_FLAG_NONE                   0
 #define WRAP_FLAG_NONE                   0
 #define WRAP_FLAG_DISPLAY             BV01
 #define WRAP_FLAG_DISPLAY             BV01
@@ -1143,6 +1148,8 @@ struct tintin_data
 	char                  * vars[100];
 	char                  * vars[100];
 	char                  * cmds[100];
 	char                  * cmds[100];
 	int                     args[100];
 	int                     args[100];
+	int                     varc;
+	int                     cmdc;
 	char                    color_reset[COLOR_SIZE];
 	char                    color_reset[COLOR_SIZE];
 };
 };
 
 
@@ -1436,7 +1443,7 @@ struct map_data
 	int                     global_vnum;
 	int                     global_vnum;
 	struct exit_data      * global_exit;
 	struct exit_data      * global_exit;
 	int                     version;
 	int                     version;
-	short                   display_stamp;
+	unsigned short          display_stamp;
 	int                     nofollow;
 	int                     nofollow;
 	char                    legend[LEGEND_MAX][LEGEND_SIZE];
 	char                    legend[LEGEND_MAX][LEGEND_SIZE];
 	char                    legend_raw[LEGEND_MAX][LEGEND_SIZE];
 	char                    legend_raw[LEGEND_MAX][LEGEND_SIZE];
@@ -2422,6 +2429,8 @@ extern char *get_str_str(struct str_data *str_ptr);
 extern  int str_len(char *str);
 extern  int str_len(char *str);
 extern  int str_max(char *str);
 extern  int str_max(char *str);
 extern  int str_fix(char *str);
 extern  int str_fix(char *str);
+extern  int str_fix_len(char *str, int len);
+
 
 
 extern char *str_alloc(int len);
 extern char *str_alloc(int len);
 extern void  str_free(char *ptr);
 extern void  str_free(char *ptr);
@@ -2497,11 +2506,13 @@ extern struct listroot *search_nest_base_ses(struct session *ses, char *arg);
 extern struct listroot *search_nest_root(struct listroot *root, char *arg);
 extern struct listroot *search_nest_root(struct listroot *root, char *arg);
 extern struct listnode *search_base_node(struct listroot *root, char *variable);
 extern struct listnode *search_base_node(struct listroot *root, char *variable);
 extern struct listnode *search_nest_node(struct listroot *root, char *variable);
 extern struct listnode *search_nest_node(struct listroot *root, char *variable);
+extern struct listnode *search_nest_node_path(struct listroot *root, char *variable, char *path);
 extern struct listnode *search_nest_node_ses(struct session *ses, char *variable);
 extern struct listnode *search_nest_node_ses(struct session *ses, char *variable);
 extern int search_nest_index(struct listroot *root, char *variable);
 extern int search_nest_index(struct listroot *root, char *variable);
 extern struct listroot *update_nest_root(struct listroot *root, char *arg);
 extern struct listroot *update_nest_root(struct listroot *root, char *arg);
 extern void update_nest_node(struct listroot *root, char *arg);
 extern void update_nest_node(struct listroot *root, char *arg);
 extern int delete_nest_node(struct listroot *root, char *variable);
 extern int delete_nest_node(struct listroot *root, char *variable);
+extern int delete_nest_node_with_wild(struct listroot *root, char *variable);
 extern int get_nest_size_key(struct listroot *root, char *variable, char **result);
 extern int get_nest_size_key(struct listroot *root, char *variable, char **result);
 extern int get_nest_size_val(struct listroot *root, char *variable, char **result);
 extern int get_nest_size_val(struct listroot *root, char *variable, char **result);
 extern struct listnode *get_nest_node_key(struct listroot *root, char *variable, char **result, int def);
 extern struct listnode *get_nest_node_key(struct listroot *root, char *variable, char **result, int def);
@@ -2851,6 +2862,7 @@ extern struct map_legend_group_type map_legend_group_table[];
 #ifndef __TELOPT_H__
 #ifndef __TELOPT_H__
 #define __TELOPT_H__
 #define __TELOPT_H__
 
 
+extern void test_gmcp(struct session *ses, char *buf);
 extern  int client_translate_telopts(struct session *ses, unsigned char *src, int cplen);
 extern  int client_translate_telopts(struct session *ses, unsigned char *src, int cplen);
 extern  int client_write_compressed(struct session *ses, char *txt, int length);
 extern  int client_write_compressed(struct session *ses, char *txt, int length);
 extern  int client_send_sb_naws(struct session *ses, int cplen, unsigned char *cpsrc);
 extern  int client_send_sb_naws(struct session *ses, int cplen, unsigned char *cpsrc);
@@ -3030,6 +3042,8 @@ extern int utf8_to_cp949(char *input, char *output);
 #ifndef __VARIABLE_H__
 #ifndef __VARIABLE_H__
 #define __VARIABLE_H__
 #define __VARIABLE_H__
 
 
+extern DO_COMMAND(do_replace);
+
 extern  int valid_variable(struct session *ses, char *arg);
 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_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 string_str_raw_len(struct session *ses, char *str, int start, int end);

+ 21 - 4
src/tokenize.c

@@ -247,7 +247,14 @@ char *addforeachtoken(struct scriptroot *root, int lvl, int opr, int cmd, char *
 
 
 void resetforeachtoken(struct session *ses, struct scriptnode *token)
 void resetforeachtoken(struct session *ses, struct scriptnode *token)
 {
 {
-	char *str, arg[BUFFER_SIZE];
+	char *str, *arg;
+
+	arg = malloc(MALLOC_SIZE);
+
+	if (arg == NULL)
+	{
+		arg = malloc(BUFFER_SIZE);
+	}
 
 
 	str = token->data->cpy;
 	str = token->data->cpy;
 
 
@@ -255,6 +262,8 @@ void resetforeachtoken(struct session *ses, struct scriptnode *token)
 
 
 	RESTRING(token->data->str, arg);
 	RESTRING(token->data->str, arg);
 
 
+	free(arg);
+
 	token->data->arg = token->data->str;
 	token->data->arg = token->data->str;
 }
 }
 
 
@@ -359,11 +368,11 @@ char *get_arg_parse(struct session *ses, struct scriptnode *token)
 
 
 	if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, token->data->arg))
 	if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, token->data->arg))
 	{
 	{
-		token->data->arg += sprintf(buf, "%.*s", get_euc_size(ses, token->data->arg), token->data->arg);
+		token->data->arg += snprintf(buf, 5, "%.*s", get_euc_size(ses, token->data->arg), token->data->arg);
 	}
 	}
 	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(token->data->arg))
 	else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(token->data->arg))
 	{
 	{
-		token->data->arg += sprintf(buf, "%.*s", get_utf8_size(token->data->arg), token->data->arg);
+		token->data->arg += snprintf(buf, 5, "%.*s", get_utf8_size(token->data->arg), token->data->arg);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -1071,6 +1080,10 @@ char *write_script(struct session *ses, struct scriptroot *root)
 	{
 	{
 		switch (token->type)
 		switch (token->type)
 		{
 		{
+			case TOKEN_TYPE_REPEAT:
+				cat_sprintf(buf, "%s%c%s", indent(token->lvl + 1), gtd->repeat_char, token->str);
+				break;
+
 			case TOKEN_TYPE_STRING:
 			case TOKEN_TYPE_STRING:
 				cat_sprintf(buf, "%s%s", indent(token->lvl), token->str);
 				cat_sprintf(buf, "%s%s", indent(token->lvl), token->str);
 				break;
 				break;
@@ -1155,6 +1168,10 @@ char *view_script(struct session *ses, struct scriptroot *root)
 	{
 	{
 		switch (token->type)
 		switch (token->type)
 		{
 		{
+			case TOKEN_TYPE_REPEAT:
+				cat_sprintf(buf, "%s" COLOR_REPEAT "%c" COLOR_STRING "%s", indent(token->lvl + 1), gtd->repeat_char, token->str);
+				break;
+
 			case TOKEN_TYPE_STRING:
 			case TOKEN_TYPE_STRING:
 				cat_sprintf(buf, "%s" COLOR_STRING "%s", indent(token->lvl), token->str);
 				cat_sprintf(buf, "%s" COLOR_STRING "%s", indent(token->lvl), token->str);
 				break;
 				break;
@@ -1195,7 +1212,7 @@ char *view_script(struct session *ses, struct scriptroot *root)
 				break;
 				break;
 
 
 			case TOKEN_TYPE_REGEX:
 			case TOKEN_TYPE_REGEX:
-				cat_sprintf(buf, "%s" COLOR_TINTIN "%c" COLOR_STATEMENT "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "} {" COLOR_STRING "%s" COLOR_BRACE "}\n%s{\n%s" COLOR_STRING "%s\n" COLOR_BRACE "%s}", indent(token->lvl), gtd->tintin_char, command_table[token->cmd].name, token->str, token->regex->str, indent(token->lvl), indent(token->lvl + 1), token->regex->bod, indent(token->lvl));
+				cat_sprintf(buf, "%s" COLOR_TINTIN "%c" COLOR_STATEMENT "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "} {" COLOR_STRING "%s" COLOR_BRACE "}\n%s" COLOR_BRACE "{\n%s" COLOR_STRING "%s\n" COLOR_BRACE "%s}", indent(token->lvl), gtd->tintin_char, command_table[token->cmd].name, token->str, token->regex->str, indent(token->lvl), indent(token->lvl + 1), token->regex->bod, indent(token->lvl));
 				break;
 				break;
 
 
 			case TOKEN_TYPE_END:
 			case TOKEN_TYPE_END:

+ 3 - 1
src/trigger.c

@@ -581,7 +581,7 @@ DO_COMMAND(do_highlight)
 	{
 	{
 		if (!is_color_name(arg2))
 		if (!is_color_name(arg2))
 		{
 		{
-			tintin_printf2(ses, "#HIGHLIGHT: VALID COLORS ARE:\n");
+			tintin_printf2(ses, "#HIGHLIGHT: INVALID COLOR {%s}. VALID COLORS ARE:\n", arg2);
 			tintin_printf2(ses, "reset, bold, light, faint, dim, dark, underscore, blink, reverse, black, red, green, yellow, blue, magenta, cyan, white, b black, b red, b green, b yellow, b blue, b magenta, b cyan, b white, azure, ebony, jade, lime, orange, pink, silver, tan, violet.");
 			tintin_printf2(ses, "reset, bold, light, faint, dim, dark, underscore, blink, reverse, black, red, green, yellow, blue, magenta, cyan, white, b black, b red, b green, b yellow, b blue, b magenta, b cyan, b white, azure, ebony, jade, lime, orange, pink, silver, tan, violet.");
 		}
 		}
 		else
 		else
@@ -995,6 +995,8 @@ DO_COMMAND(do_tick)
 		get_number_string(ses, arg3, time);
 		get_number_string(ses, arg3, time);
 	}
 	}
 
 
+	// store creation time for #info tickers save
+
 	sprintf(arg3, "%lld", ++gtd->utime);
 	sprintf(arg3, "%lld", ++gtd->utime);
 
 
 	if (*arg1 == 0)
 	if (*arg1 == 0)

+ 7 - 2
src/update.c

@@ -311,7 +311,7 @@ void update_input(void)
 	static struct timeval timeout;
 	static struct timeval timeout;
 	static unsigned char sleep;
 	static unsigned char sleep;
 
 
-	if (gtd->time_input + 60 < gtd->time)
+	if (gtd->time_input < gtd->time)
 	{
 	{
 		if (sleep < 10)
 		if (sleep < 10)
 		{
 		{
@@ -321,6 +321,11 @@ void update_input(void)
 		sleep = 0;
 		sleep = 0;
 	}
 	}
 
 
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_NOHUP))
+	{
+		return;
+	}
+
 	if (gtd->detach_port)
 	if (gtd->detach_port)
 	{
 	{
 		return;
 		return;
@@ -341,7 +346,7 @@ void update_input(void)
 			break;
 			break;
 		}
 		}
 
 
-		gtd->time_input = gtd->time;
+		gtd->time_input = gtd->time + 60;
 
 
 		process_input();
 		process_input();
 
 

Різницю між файлами не показано, бо вона завелика
+ 0 - 1
src/utf8.c


+ 1 - 1
src/utils.c

@@ -403,7 +403,7 @@ char *ftos(float number)
 
 
 	sprintf(outbuf[cnt], "%f", number);
 	sprintf(outbuf[cnt], "%f", number);
 
 
-	for (len = strlen(outbuf[cnt]) - 1 ; len ; len--)
+	for (len = strlen(outbuf[cnt]) - 1 ; len > 0 ; len--)
 	{
 	{
 		if (outbuf[cnt][len] == '0')
 		if (outbuf[cnt][len] == '0')
 		{
 		{

+ 122 - 24
src/variable.c

@@ -41,7 +41,9 @@ DO_COMMAND(do_variable)
 	}
 	}
 	else if (*arg == 0)
 	else if (*arg == 0)
 	{
 	{
-		node = search_nest_node(root, arg1);
+		char *path = str_alloc_stack(0);
+
+		node = search_nest_node_path(root, arg1, path);
 
 
 		if (node)
 		if (node)
 		{
 		{
@@ -53,11 +55,11 @@ DO_COMMAND(do_variable)
 
 
 				view_nest_node(node, &str_result, 0, TRUE, TRUE);
 				view_nest_node(node, &str_result, 0, TRUE, TRUE);
 
 
-				print_lines(ses, SUB_NONE, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n{\n" COLOR_STRING "%s" COLOR_BRACE "}" COLOR_RESET "\n", gtd->tintin_char, list_table[LIST_VARIABLE].name, node->arg1, str_result);
+				print_lines(ses, SUB_NONE, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "}\n" COLOR_BRACE "{\n" COLOR_STRING "%s" COLOR_BRACE "}" COLOR_RESET "\n", gtd->tintin_char, list_table[LIST_VARIABLE].name, path, str_result);
 			}
 			}
 			else
 			else
 			{
 			{
-				tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "} {" COLOR_STRING "%s" COLOR_BRACE "}" COLOR_RESET "\n", gtd->tintin_char, list_table[LIST_VARIABLE].name, node->arg1, node->arg2);
+				tintin_printf2(ses, COLOR_TINTIN "%c" COLOR_COMMAND "%s " COLOR_BRACE "{" COLOR_STRING "%s" COLOR_BRACE "} {" COLOR_STRING "%s" COLOR_BRACE "}" COLOR_RESET "\n", gtd->tintin_char, list_table[LIST_VARIABLE].name, path, node->arg2);
 			}
 			}
 		}
 		}
 		else if (show_node_with_wild(ses, arg1, ses->list[LIST_VARIABLE]) == FALSE)
 		else if (show_node_with_wild(ses, arg1, ses->list[LIST_VARIABLE]) == FALSE)
@@ -69,7 +71,7 @@ DO_COMMAND(do_variable)
 	{
 	{
 		if (!valid_variable(ses, arg1))
 		if (!valid_variable(ses, arg1))
 		{
 		{
-			show_error(ses, LIST_VARIABLE, "#VARIABLE: INVALID VARIALBE NAME {%s}.", arg1);
+			show_error(ses, LIST_VARIABLE, "#VARIABLE: INVALID VARIABLE NAME {%s}.", arg1);
 
 
 			return ses;
 			return ses;
 		}
 		}
@@ -108,7 +110,11 @@ DO_COMMAND(do_unvariable)
 		}
 		}
 		else
 		else
 		{
 		{
-			delete_node_with_wild(ses, LIST_VARIABLE, arg1);
+			if (delete_nest_node_with_wild(ses->list[LIST_VARIABLE], arg1) == FALSE)
+			{
+				show_message(ses, LIST_VARIABLE, "#UNVARIABLE: NO MATCHES FOUND FOR {%s}.", arg1);
+			}
+//			delete_node_with_wild(ses, LIST_VARIABLE, arg1);
 		}
 		}
 		arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
 		arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
 	}
 	}
@@ -618,7 +624,7 @@ void headerstring(struct session *ses, char *str, char *columns)
 		memset(fill, '#', max);
 		memset(fill, '#', max);
 	}
 	}
 
 
-	sprintf(buf, "%.*s%s%.*s%s", (max - len) / 2, fill, str, (max - len) / 2, fill, (max - len) % 2 ? "#" : "");
+	snprintf(buf, BUFFER_SIZE, "%.*s%s%.*s%s", (max - len) / 2, fill, str, (max - len) / 2, fill, (max - len) % 2 ? "#" : "");
 
 
 	strcpy(str, buf);
 	strcpy(str, buf);
 
 
@@ -805,11 +811,11 @@ void metricgroupingstring(struct session *ses, char *str)
                 }
                 }
                 if (val >= 100)
                 if (val >= 100)
                 {
                 {
-                	sprintf(tmp, " %Lf", val);
+                	snprintf(tmp, NUMBER_SIZE, " %Lf", val);
 		}
 		}
 		else
 		else
 		{
 		{
-                	sprintf(tmp, "%Lf", val);
+                	snprintf(tmp, NUMBER_SIZE, "%Lf", val);
 		}
 		}
                 sprintf(str, "%.4s%c", tmp, big[index]);
                 sprintf(str, "%.4s%c", tmp, big[index]);
 	}
 	}
@@ -820,18 +826,18 @@ void metricgroupingstring(struct session *ses, char *str)
 			val = val * 1000;
 			val = val * 1000;
 			index++;
 			index++;
 		}
 		}
-		sprintf(tmp, "%Lf", val);
+		snprintf(tmp, NUMBER_SIZE, "%Lf", val);
 		sprintf(str, "%.4s%c", tmp, small[index]);
 		sprintf(str, "%.4s%c", tmp, small[index]);
 	}
 	}
 	else if (val >= 0)
 	else if (val >= 0)
 	{
 	{
 		if (val >= 100)
 		if (val >= 100)
 		{
 		{
-			sprintf(tmp, " %Lf", val);
+			snprintf(tmp, NUMBER_SIZE, " %Lf", val);
 		}
 		}
 		else
 		else
 		{
 		{
-			sprintf(tmp, "%Lf", val);
+			snprintf(tmp, NUMBER_SIZE, "%Lf", val);
 		}
 		}
 		sprintf(str, "%.4s%c", tmp, big[index]);
 		sprintf(str, "%.4s%c", tmp, big[index]);
 	}
 	}
@@ -839,11 +845,11 @@ void metricgroupingstring(struct session *ses, char *str)
 	{
 	{
 		if (val <= -100)
 		if (val <= -100)
 		{
 		{
-			sprintf(tmp, " %Lf", val);
+			snprintf(tmp, NUMBER_SIZE, " %Lf", val);
 		}
 		}
 		else
 		else
 		{
 		{
-			sprintf(tmp, "%Lf", val);
+			snprintf(tmp, NUMBER_SIZE, "%Lf", val);
 		} 
 		} 
 		sprintf(str, "%.5s%c", tmp, small[index]);
 		sprintf(str, "%.5s%c", tmp, small[index]);
 	}
 	}
@@ -858,7 +864,7 @@ void metricgroupingstring(struct session *ses, char *str)
                         val = val / 1000;
                         val = val / 1000;
                         index++;
                         index++;
                 }
                 }
-                sprintf(tmp, "%Lf", val);
+                snprintf(tmp, NUMBER_SIZE, "%Lf", val);
                 sprintf(str, "%.5s%c", tmp, big[index]);
                 sprintf(str, "%.5s%c", tmp, big[index]);
 	}
 	}
 
 
@@ -869,7 +875,7 @@ void metricgroupingstring(struct session *ses, char *str)
 			val = val * 1000;
 			val = val * 1000;
 			index++;
 			index++;
 		}
 		}
-		sprintf(tmp, "%Lf", val);
+		snprintf(tmp, NUMBER_SIZE, "%Lf", val);
 		sprintf(str, "%.5s%c", tmp, small[index]);
 		sprintf(str, "%.5s%c", tmp, small[index]);
 	}
 	}
 }
 }
@@ -909,8 +915,8 @@ void wrapstring(struct session *ses, char *str, char *wrap)
 	arg1 = str_alloc_stack(0);
 	arg1 = str_alloc_stack(0);
 	arg2 = str_alloc_stack(0);
 	arg2 = str_alloc_stack(0);
 
 
-//	arg = sub_arg_in_braces(ses, str, arg1, GET_ALL, SUB_COL|SUB_LIT|SUB_ESC);
-	arg = sub_arg_in_braces(ses, str, arg1, GET_ALL, SUB_COL|SUB_ESC);
+	arg = sub_arg_in_braces(ses, str, arg1, GET_ALL, SUB_COL|SUB_LIT|SUB_ESC);
+//	arg = sub_arg_in_braces(ses, str, arg1, GET_ALL, SUB_COL|SUB_ESC);
 
 
 	if (*arg == COMMAND_SEPARATOR)
 	if (*arg == COMMAND_SEPARATOR)
 	{
 	{
@@ -957,7 +963,7 @@ void wrapstring(struct session *ses, char *str, char *wrap)
 		{
 		{
 			*pte++ = 0;
 			*pte++ = 0;
 
 
-			substitute(ses, pts, arg1, SUB_BRA);
+			substitute(ses, pts, arg1, SUB_SEC);
 
 
 			cat_sprintf(str, "{%d}{%s}", ++cnt, arg1);
 			cat_sprintf(str, "{%d}{%s}", ++cnt, arg1);
 
 
@@ -968,7 +974,7 @@ void wrapstring(struct session *ses, char *str, char *wrap)
 			pte++;
 			pte++;
 		}
 		}
 	}
 	}
-	substitute(ses, pts, arg1, SUB_BRA);
+	substitute(ses, pts, arg1, SUB_SEC);
 
 
 	cat_sprintf(str, "{%d}{%s}", ++cnt, arg1);
 	cat_sprintf(str, "{%d}{%s}", ++cnt, arg1);
 
 
@@ -1080,6 +1086,86 @@ int string_str_raw_len(struct session *ses, char *str, int start, int end)
 	return ret_cnt;
 	return ret_cnt;
 }
 }
 
 
+// stripped range stripped return
+
+int string_str_str_len(struct session *ses, char *str, int start, int end)
+{
+	int raw_cnt, str_cnt, ret_cnt, tmp_cnt, tot_len, width, col_len, skip;
+
+	raw_cnt = str_cnt = ret_cnt = 0;
+
+	tot_len = strlen(str);
+
+	while (raw_cnt < tot_len)
+	{
+		skip = skip_vt102_codes(&str[raw_cnt]);
+
+		if (skip)
+		{
+			raw_cnt += skip;
+
+			continue;
+		}
+
+		col_len = is_color_code(&str[raw_cnt]);
+
+		if (col_len)
+		{
+			raw_cnt += col_len;
+
+			continue;
+		}
+
+		if (str_cnt >= end)
+		{
+			break;
+		}
+
+		if (str[raw_cnt] == '\\')
+		{
+			raw_cnt++;
+			
+			if (str[raw_cnt] == '\\')
+			{
+				ret_cnt += (str_cnt >= start) ? 1 : 0;
+				raw_cnt++;
+				str_cnt++;
+			}
+			continue;
+		}
+
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_EUC) && is_euc_head(ses, &str[raw_cnt]))
+		{
+			tmp_cnt = get_euc_width(ses, &str[raw_cnt], &width);
+
+			if (str_cnt >= start)
+			{
+				ret_cnt += width;
+			}
+			raw_cnt += tmp_cnt;
+			str_cnt += width;
+		}
+		else if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&str[raw_cnt]))
+		{
+			tmp_cnt = get_utf8_width(&str[raw_cnt], &width, NULL);
+
+			if (str_cnt >= start)
+			{
+				ret_cnt += width;
+			}
+			raw_cnt += tmp_cnt;
+			str_cnt += width;
+		}
+		else
+		{
+			ret_cnt += (str_cnt >= start) ? 1 : 0;
+			raw_cnt++;
+			str_cnt++;
+		}
+	}
+	return ret_cnt;
+}
+
 // raw range stripped return
 // raw range stripped return
 
 
 int string_raw_str_len(struct session *ses, char *str, int raw_start, int raw_end)
 int string_raw_str_len(struct session *ses, char *str, int raw_start, int raw_end)
@@ -1353,13 +1439,19 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 								if (atoi(arg1) < 0)
 								if (atoi(arg1) < 0)
 								{
 								{
 									sprintf(argformat, "%%%d.%d",
 									sprintf(argformat, "%%%d.%d",
-										atoi(arg1) - ((int) strlen(arglist[i]) - string_raw_str_len(ses, arglist[i], 0, -1)),
+										atoi(arg1) - (string_str_raw_len(ses, arglist[i], 0, atoi(arg2)) - string_str_str_len(ses, arglist[i], 0, atoi(arg2))),
+//										atoi(arg1) - ((int) strlen(arglist[i]) - string_str_raw_len(ses, arglist[i], 0, -1)),
 										string_str_raw_len(ses, arglist[i], 0, atoi(arg2)));
 										string_str_raw_len(ses, arglist[i], 0, atoi(arg2)));
 								}
 								}
 								else
 								else
 								{
 								{
+/*									printf("debug: %%%d.%d\n",
+										atoi(arg1) + string_str_raw_len(ses, arglist[i], 0, atoi(arg2)) - string_str_str_len(ses, arglist[i], 0, atoi(arg2)),
+										string_str_raw_len(ses, arglist[i], 0, atoi(arg2)));
+*/
 									sprintf(argformat, "%%%d.%d",
 									sprintf(argformat, "%%%d.%d",
-										atoi(arg1) + ((int) strlen(arglist[i]) - string_raw_str_len(ses, arglist[i], 0, -1)),
+										atoi(arg1) + string_str_raw_len(ses, arglist[i], 0, atoi(arg2)) - string_str_str_len(ses, arglist[i], 0, atoi(arg2)),
+//										atoi(arg1) + ((int) strlen(arglist[i]) - string_raw_str_len(ses, arglist[i], 0, -1)),
 										string_str_raw_len(ses, arglist[i], 0, atoi(arg2)));
 										string_str_raw_len(ses, arglist[i], 0, atoi(arg2)));
 								}
 								}
 							}
 							}
@@ -1465,8 +1557,14 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						break;
 						break;
 
 
 					case 'C':
 					case 'C':
-						tintin_printf2(ses, "\e[1;31m#echo/#format %%C please use #screen {get} {cols} to get screen width.");
-//						chronosgroupingstring(ses, arglist[i]);
+						if (*arglist[i] == 0)
+						{
+							sprintf(arglist[i], "%d", gtd->screen->cols);
+						}
+						else
+						{
+							sprintf(arglist[i], "%d", get_col_index_arg(ses, arglist[i]));
+						}
 						break;
 						break;
 
 
 					case 'D':
 					case 'D':
@@ -1510,7 +1608,7 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						break;
 						break;
 
 
 					case 'U':
 					case 'U':
-						sprintf(arglist[i], "%lld", ++gtd->utime);
+						sprintf(arglist[i], "%lld", utime());
 						break;
 						break;
 
 
 					case 'X':
 					case 'X':

+ 4 - 2
src/vt102.c

@@ -105,7 +105,9 @@ void goto_pos(struct session *ses, int row, int col)
 
 
 		return;
 		return;
 	}
 	}
-	print_stdout(0, 0, "\e[%d;%dH", row, col);
+	// Add \r to capture a stray \e in broken packets
+
+	print_stdout(0, 0, "\r\e[%d;%dH", row, col);
 
 
 	ses->cur_row = row;
 	ses->cur_row = row;
 	ses->cur_col = col;
 	ses->cur_col = col;
@@ -1443,7 +1445,7 @@ int catch_vt102_codes(struct session *ses, unsigned char *str, int cplen)
 						skip++;
 						skip++;
 					}
 					}
 	
 	
-					sprintf(osc, "%.*s", skip - 2, str + 2);
+					snprintf(osc, BUFFER_SIZE, "%.*s", skip - 2, str + 2);
 
 
 					check_all_events(ses, SUB_SEC|EVENT_FLAG_VT100, 0, 1, "VT100 OSC", osc);
 					check_all_events(ses, SUB_SEC|EVENT_FLAG_VT100, 0, 1, "VT100 OSC", osc);
 
 

Деякі файли не було показано, через те що забагато файлів було змінено