Scandum vor 3 Jahren
Ursprung
Commit
503f80e9ec
49 geänderte Dateien mit 1985 neuen und 885 gelöschten Zeilen
  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
 
@@ -11,20 +25,12 @@
 
   - input spell checking, #cursor display ?
 
-  - cp949toutf8 is missing some characters
-
   - 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
 
   - look into default input color
 
-  - Add RECEIVED INPUT CHARACTER event / filter mouse input sequences.
-
   - add ctrl-r support for scrollback
 
   - Add #event {SESSION CONNECTED} {5.1} option.
@@ -61,7 +67,9 @@
 
   - 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.
 
@@ -69,14 +77,14 @@
 
   - 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
   - Store the map filename to differentiate between maps.
   - #map list {<exits>} breaks on rooms that have e mapped to eu.
   - finish landmarks
   - map sandbox mode support (flags to disable saving?)
   - add ghosting to fix #map flag nofollow exit cmd issues?
-  - Request: 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? 
   - #map uninsert <vnum>
   - make map spacing easier
@@ -92,8 +100,6 @@
     #draw button
     #draw titanic
 
-  - proper vt100 skip detection for improper color codes.
-
   - 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.
@@ -245,23 +251,14 @@
 
   - 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.
 
-  - Room creation event
-
   - Add better table support for data fields.
 
   - auto adjust max vnum size of 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.
 
   - Display hidden exits differently.
@@ -278,7 +275,7 @@
 
 * STUFF FOR A RAINY DAY
 
-- Fix up ipv6 support in chat.
+- Fix up IPv6 support in port/chat.
 
 - Look into packet defragmentation for chat.
 
@@ -297,8 +294,6 @@
 
 - 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

+ 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='#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='#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>
@@ -49,9 +49,6 @@ a:active {color:#b06}
          variables are substituted from the message and can be used in the
          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
          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.}
 
-         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
          #show to get triggered use: #line ignore #show {text}
 
          Actions are ordered alphabetically and only one action can trigger at
          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
-         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.
 
 </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.
 
          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.
 
          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
          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'>
-         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:#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:#AAA'>
          Moves you to the end of your scrollback buffer and displays the page.
          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'>
          Moves the buffer to the given string which can contain a regular
          expression. Optionally you can provide the number of matches to skip,
          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:#AAA'>
          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
          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:#AAA'>
          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:#AAA'>
          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
          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:#AAA'>
          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:#FFF'>#class {&lt;name&gt;} {write} {&lt;filename&gt;}
          </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
          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'>
          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>.
 <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:#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>.
 <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'>
 
          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 DISCONNECTED   %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:#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'>      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'>#
@@ -1585,7 +1644,7 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</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.
 
          &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
 
-         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.
 
 </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.
 
-</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
@@ -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
          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 environ will show the environment variables.
          #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 memory will show information about the memory 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} {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} {insert} {index} {string}  Insert {string} at given index
          #list {var} {filter} {keep} {remove}   Filter with keep / remove regex
          #list {var} {find} {regex} {variable}  Return the found index
          #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} {shuffle}                  Shuffle the list
          #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} {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
          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
          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>.
 <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
          while executing scripts you can use '#debug all on'. To stop seeing
          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'>
          Optimization
 </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
            from your current location to the destination is calculated.
            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:#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:#AAA'>  When you enter movement commands the map will no longer
            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:#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
            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:#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:#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
-           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:#AAA'>  Exact opposite of the insert command.
@@ -2829,7 +2935,7 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          -               3            integer subtraction
          &lt;&lt;              4            bitwise shift
          &gt;&gt;              4            bitwise shift
-         ..              4            bitwise ellipsis
+         ..              4            integer range
          &gt;               5            logical greater than
          &gt;=              5            logical greater than or equal
          &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
          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
          match like %* or %S the match is stored at the last used index
          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
          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.
 
-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
          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,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}
 
          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
          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.
 
@@ -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'>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>
 
 </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
          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[].
-         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
          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%*]
 
          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
           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,
           and #case commands. Instead of " " you can use an extra set of
@@ -123,19 +123,20 @@ COORDINATES
 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
 -----
-          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
           size is the maximum size, minus the value. This type of argument
           is used by the #split command.
 
+
 MATH
 ----
           Operators       Priority     Function
@@ -172,6 +173,7 @@ MATH
 M,K,m,u   These four metric suffixes are allowed for numbers.
 
 { }       Braces can be used in #math to perform string operations.
+
 {a} > {b} This checks if the string "a" is greater than "b".
 
 ,         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
          <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
 ----

+ 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
                 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.
 
-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
                 \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,
                 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
                 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.
 
-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
                 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.
                 #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
                 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
                 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
                 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.
 
 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
                 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
                 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
                 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
                 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.
 
@@ -712,7 +785,7 @@ draw.c          Added TUBED drawing option.
 
 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}
 
 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.
 
-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.
 

+ 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_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);
@@ -124,7 +124,7 @@ void banner_init(struct session *ses, char *arg1)
 
 	banner_website(ses, "Legends of Kallisti", "http://www.KallistiMUD.com", 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);
@@ -142,9 +142,20 @@ void banner_init(struct session *ses, char *arg1)
 
 	banner_website(ses, "3Kingdoms", "http://3k.org", arg1);
 	banner_address(ses, "3Kingdoms", "3K 3k.org 3000", arg1);
-	banner_expires(ses, "3Kingdoms", "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_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_expires(ses, "New World Ateraan", "2026", arg1);
 
-
 	banner_create(ses, "Realm of Utopian Dreams (RUD)", arg1);
 
 	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_address(ses, "Carrion Fields", "cf carrionfields.net 4449", 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()
@@ -496,25 +496,6 @@ DO_COMMAND(do_banner)
 		"\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, 
 		1700000000, 
@@ -609,23 +590,6 @@ DO_COMMAND(do_banner)
 		"\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 *
 		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)
 	{
-		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)
@@ -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 *));
 
+		RESTRING(ses->scroll->buffer[cnt]->str, "");
+
 		ses->scroll->used -= purge;
 		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;
 	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);
 
@@ -664,16 +666,72 @@ DO_COMMAND(do_buffer)
 
 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]);
 	}
 
-	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)
@@ -860,8 +918,11 @@ DO_BUFFER(buffer_lock)
 
 DO_BUFFER(buffer_find)
 {
+	char *arg3;
 	int scroll_cnt, grep_cnt, grep_max, page;
 
+	arg3 = str_alloc_stack(0);
+
 	check_buffer(ses);
 
 	grep_cnt = grep_max = scroll_cnt = 0;
@@ -890,14 +951,16 @@ DO_BUFFER(buffer_find)
 	}
 	else
 	{
-		page = 1;
+		page = -1;
 
 		strcpy(arg2, arg1);
 	}
 
+	arg = sub_arg_in_braces(ses, arg, arg3, GET_ONE, SUB_VAR|SUB_FUN);
+
 	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))
 			{
@@ -917,7 +980,7 @@ DO_BUFFER(buffer_find)
 	}
 	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))
 			{
@@ -932,7 +995,6 @@ DO_BUFFER(buffer_find)
 				{
 					break;
 				}
-
 			}
 		}
 	}
@@ -944,10 +1006,16 @@ DO_BUFFER(buffer_find)
 		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;
 }
 
@@ -974,7 +1042,7 @@ DO_BUFFER(buffer_get)
 	{
 		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);
 
@@ -991,7 +1059,7 @@ DO_BUFFER(buffer_get)
 	{
 		max = ses->scroll->used + max;
 	}
-	max = URANGE(0, max, ses->scroll->used - 1);
+	max = URANGE(1, max, ses->scroll->used - 1);
 
 	if (min > max)
 	{
@@ -1006,12 +1074,12 @@ DO_BUFFER(buffer_get)
 
 	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;
 }
@@ -1069,7 +1137,7 @@ DO_BUFFER(buffer_write)
 
 			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))
 				{
@@ -1249,7 +1317,7 @@ DO_COMMAND(do_grep)
 	}
 	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))
 			{

+ 2 - 3
src/chat.c

@@ -453,7 +453,7 @@ DO_CHAT(chat_call)
 
 		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]);
 
@@ -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;
 
 	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_port);
 extern DO_COMMAND(do_prompt);
-extern DO_COMMAND(do_replace);
 extern DO_COMMAND(do_run);
 extern DO_COMMAND(do_scan);
 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);
 		}
 	}
-	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);
@@ -368,7 +366,7 @@ DO_CONFIG(config_connectretry)
 		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;
 }
@@ -525,7 +523,7 @@ DO_CONFIG(config_logmode)
 			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;
 }
@@ -692,7 +690,7 @@ DO_CONFIG(config_packetpatch)
 	}
 	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;
 }

+ 2 - 9
src/cursor.c

@@ -1185,13 +1185,6 @@ DO_CURSOR(cursor_enter_finish)
 
 	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))
 	{
 		cursor_redraw_line(gtd->ses, "");
@@ -2698,7 +2691,7 @@ DO_CURSOR(cursor_tab)
 
 		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);
 
@@ -2720,7 +2713,7 @@ DO_CURSOR(cursor_tab)
 
 		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);
 

+ 3 - 3
src/daemon.c

@@ -190,7 +190,7 @@ DO_DAEMON(daemon_attach)
 		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)
 	{
@@ -547,7 +547,7 @@ DO_DAEMON(daemon_kill)
 				{
 					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);
 
@@ -617,7 +617,7 @@ DO_DAEMON(daemon_list)
 				{
 					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);
 				}

+ 48 - 2
src/data.c

@@ -983,6 +983,11 @@ DO_COMMAND(do_kill)
 	{
 		for (index = 0 ; index < LIST_MAX ; index++)
 		{
+			if (index == LIST_PATHDIR)
+			{
+				continue;
+			}
+
 			if (!HAS_BIT(ses->list[index]->flags, LIST_FLAG_HIDE))
 			{
 				kill_list(ses->list[index]);
@@ -1330,6 +1335,28 @@ DO_COMMAND(do_info)
 
 		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:
 				if (is_abbrev(arg1, "BIG5TOUTF8"))
 				{
@@ -1405,7 +1432,26 @@ DO_COMMAND(do_info)
 				break;
 
 			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)
 					{
@@ -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, "{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
 					{

+ 135 - 57
src/draw.c

@@ -84,7 +84,6 @@ extern DO_DRAW(draw_bot_side);
 extern DO_DRAW(draw_box);
 extern DO_DRAW(draw_buffer);
 extern DO_DRAW(draw_corner);
-extern DO_DRAW(draw_horizontal_line);
 extern DO_DRAW(draw_left_side);
 extern DO_DRAW(draw_line);
 extern DO_DRAW(draw_map);
@@ -156,6 +155,8 @@ DO_COMMAND(do_draw)
 		return ses;
 	}
 
+	arg4 = str_alloc_stack(0);
+
 	box_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, 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_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)
 			{
@@ -549,9 +559,11 @@ DO_COMMAND(do_draw)
 
 // 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)
 {
-	char *buf, *out, *tmp;
+	char *buf, *out, *tmp, *swap;
 	int inside;
 	int height, bor_height, tot_height, max_height;
 	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);
 
-		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)
@@ -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);
 
+	if (HAS_BIT(flags, DRAW_FLAG_HUGE))
+	{
+		arg = tmp;
+	}
 	pop_call();
 	return;
 }
@@ -952,28 +980,28 @@ char *get_draw_corner(long long flags, char *str)
 {
 	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);
 	}
-	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))
 			{
@@ -1255,11 +1283,7 @@ char *get_draw_corner(long long flags, char *str)
 	}
 	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");
 		}
@@ -1267,6 +1291,14 @@ char *get_draw_corner(long long flags, char *str)
 		{
 			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
 		{
 			strcpy(draw_buf[draw_cnt], "+");
@@ -1281,7 +1313,14 @@ char *draw_horizontal(long long flags, char *str)
 
 	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))
 	{
@@ -1309,9 +1348,16 @@ char *draw_vertical(long long flags, char *str)
 {
 	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))
 	{
@@ -1493,7 +1539,8 @@ DO_DRAW(draw_corner)
 
 DO_DRAW(draw_line_horizontal)
 {
-	int col, corner;
+	int col, corner, ins_len;
+	char *line;
 
 	if (!HAS_BIT(flags, DRAW_FLAG_VER))
 	{
@@ -1510,7 +1557,13 @@ DO_DRAW(draw_line_horizontal)
 
 	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))
 	{
@@ -1543,15 +1596,40 @@ DO_DRAW(draw_line_horizontal)
 		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))
 	{
-		tintin_printf2(ses, "%*s%s%s", top_col - 1, "", box_color, arg);
+		tintin_printf2(ses, "%*s%s", top_col - 1, "", line);
 	}
 	else
 	{
 		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 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
 	{
@@ -1948,7 +2026,7 @@ DO_DRAW(draw_rain)
 				{
 					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);
 
@@ -2004,7 +2082,7 @@ DO_DRAW(draw_rain)
 
 				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
 				{
@@ -2015,7 +2093,7 @@ DO_DRAW(draw_rain)
 						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);
@@ -2052,13 +2130,13 @@ DO_DRAW(draw_rain)
 				}
 				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);
 					print_stdout(0, 0, "%s", arg3);
 				}
 				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);
 					print_stdout(0, 0, "%s", arg3);
 				}
@@ -2379,7 +2457,8 @@ DO_DRAW(draw_text)
 
 	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;
 
@@ -2396,7 +2475,7 @@ DO_DRAW(draw_text)
 	{
 		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);
 
@@ -2405,33 +2484,32 @@ DO_DRAW(draw_text)
 				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++;
 		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--;
 		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--;
 	}
 
-	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--;
 	}
 
@@ -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, "│"));
 	}
 
-	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, "│"));
 	}

+ 1 - 1
src/files.c

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

+ 2 - 2
src/gui.h

@@ -311,7 +311,7 @@ char *tt_gui = "#line quiet #port init gui 0\n"
 "		#return {}\n"
 "	};\n"
 "	#local temp {};\n"
-"	#format temp %+64z %0;\n"
+"	#format temp %+64z {%0};\n"
 "	#return $temp\n"
 "}\n"
 "\n"
@@ -322,7 +322,7 @@ char *tt_gui = "#line quiet #port init gui 0\n"
 "		#return {}\n"
 "	};\n"
 "	#local temp {};\n"
-"	#format temp %+64Z %0;\n"
+"	#format temp %+64Z {%0};\n"
 "	#return $temp\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"
 		"         command part of the action.\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"
 		"         enable #config {convert meta} on to display meta characters.\n"
 		"\n"
@@ -314,19 +311,16 @@ struct help_type help_table[] =
 		"\n"
 		"<178>Example<278>: #action {%1 tells you '%2'} {tell %1 I'm afk.}\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"
 		"         #show to get triggered use: #line ignore #show {text}\n"
 		"\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"
 		"         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"
-		"         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"
 		"\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"
 		"\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"
 		"\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"
 		"         buffer.\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"
-		"         <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"
-		"         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"
 		"         <178>#buffer {down} [lines]\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"
 		"         <178>#buffer {end}\n"
 		"<278>\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"
 		"\n"
-		"         <178>#buffer {find} {[number]} {<string>}\n"
+		"         <178>#buffer {find} {[number]} {<string>} {[variable]}\n"
 		"<278>\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"
 		"         allowing you to jump further back in the buffer.\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"
 		"<278>\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"
 		"         bound is given the lines between the two bounds are stored as a list.\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"
 		"<278>\n"
 		"         Display buffer info, optionally save the data to a variable.\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"
 		"<278>\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"
 		"         move you to the end of your scrollback buffer and display the page.\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"
 		"<278>\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"
 		"         <178>#class {<name>} {write} {<filename>}\n"
 		"         <278>  Will write all triggers of the given class to file.\n"
-		"         The {kill} option will delete all triggers of the given class.\n"
 		"\n"
 		"         Keep in mind that the kill and read option are very fast allowing\n"
 		"         them to be used to enable and disable classes.\n"
@@ -797,14 +825,42 @@ struct help_type help_table[] =
 		TOKEN_TYPE_STRING,
 		"<278>\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"
-		"         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"
-		"         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"
 	},
 	{
@@ -922,7 +978,10 @@ struct help_type help_table[] =
 		"         <278>  Kills all daemons or daemons with matching name.\n"
 		"\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"
 	},
@@ -1464,8 +1523,8 @@ struct help_type help_table[] =
 		"         <128>SESSION EVENTS<278>\n"
 		"\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 DISCONNECTED   %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"
 		"         All commands can be separated with a ';'.\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"
 		"\n"
 		"         \\say Hello ;) -- Lines starting with a '\\' aren't parsed by TinTin++.\n"
@@ -1909,12 +1968,6 @@ struct help_type help_table[] =
 		"\n"
 		"<178>Example<278>: #alias greet say Greetings, most honorable %1\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"
 		"\n"
 		"<178>Example<278>: #alias ws <178>{<278>wake;stand<178>}<278>\n"
@@ -1929,7 +1982,7 @@ struct help_type help_table[] =
 		"\n"
 		"         Or by using the send command.\n"
 		"\n"
-		"<178>Example<278>: #send put %1 in %2\n"
+		"<178>Example<278>: #alias put #send put %1 in %2\n"
 		"\n"
 		"\n"
 		"<128>         Action\n"
@@ -2100,9 +2153,12 @@ struct help_type help_table[] =
 		"         triggers/variables associated with that list. With the SAVE option\n"
 		"         this data is written to the info variable.\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 environ will show the environment variables.\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 memory will show information about the memory stack.\n"
 		"         #info stack will show the low level debugging stack.\n"
@@ -2256,16 +2312,17 @@ struct help_type help_table[] =
 		"\n"
 		"         #list {var} {add} {item}               Add {item} to the list\n"
 		"         #list {var} {clear}                    Empty the given list\n"
-		"         #list {var} {collapse}                 Turn list into a variable\n"
-		"         #list {var} {create} {item}            Create a list using {items}\n"
-		"         #list {var} {delete} {index} {number}  Delete the item at {index},\n"
-		"                                                the {number} is optional.\n"
-		"         #list {var} {explode}                  Turn list into a character list\n"
+		"         #list {var} {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} {insert} {index} {string}  Insert {string} at given index\n"
 		"         #list {var} {filter} {keep} {remove}   Filter with keep / remove regex\n"
 		"         #list {var} {find} {regex} {variable}  Return the found index\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} {shuffle}                  Shuffle the list\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} {tokenize} {string}        Create a character list\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"
 		"         the second last, etc.\n"
 		"\n"
@@ -2284,10 +2341,11 @@ struct help_type help_table[] =
 		"         The add and create options allow using multiple items, as well\n"
 		"         as semicolon separated items.\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"
-		"         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"
 	},
@@ -2504,6 +2562,47 @@ struct help_type help_table[] =
 		"         while executing scripts you can use '#debug all on'. To stop seeing\n"
 		"         debug information use '#debug all off'.\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"
 		"<278>\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>  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 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"
 		"         <178>#map flag asciigraphics\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"
 		"         <278>  When you enter movement commands the map will no longer\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"
 		"         <178>#map flag static\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>  must match.\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"
 		"         <178>#map insert <direction> [roomflag]\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"
 		"         <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>  room. Useful if you walked into a non existant direction.\n"
+		"         <278>  room. Useful if you walked into a non-existent direction.\n"
 		"\n"
 		"         <178>#map uninsert <direction>\n"
 		"         <278>  Exact opposite of the insert command.\n"
@@ -3114,7 +3220,7 @@ struct help_type help_table[] =
 		"         -               3            integer subtraction\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 or equal\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"
 		"         stored in the %1 variable which can be used in the action body.\n"
 		"\n"
-		"Example: %1 says 'Tickle me'} {tickle %1}\n"
+		"Example: #act {%1 says 'Tickle me'} {tickle %1}\n"
 		"\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"
 		"         incremented by one.\n"
 		"\n"
-		"Example: %3 says '%*'} {#if {\"%4\" == \"Tickle me\"} {tickle %3}}\n"
+		"Example: #act {%3 says '%*'} {#if {\"%4\" == \"Tickle me\"} {tickle %3}}\n"
 		"\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"
@@ -3526,7 +3632,7 @@ struct help_type help_table[] =
 		"\n"
 		"         You can group alternatives and ranges within a PCRE using brackets.\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"
 		"         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"
@@ -3551,7 +3657,7 @@ struct help_type help_table[] =
 		"    {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"
-		"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"
 		"\n"
 		"         The example only triggers if someone provides a number between 1 and\n"
@@ -4051,10 +4157,10 @@ struct help_type help_table[] =
 		"\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"
-		"         already existant session, a number, or the keywords '+' and '-'.\n"
+		"         already existing session, a number, or the keywords '+' and '-'.\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"
 		"         Without an argument #session shows the currently defined sessions.\n"
 		"\n"
@@ -4308,6 +4414,78 @@ struct help_type help_table[] =
 
 		"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",
 		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"
 		"         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"
-		"         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"
 		"         Nested variables are also known as tables, table generally being used\n"
 		"         to refer to several variables nested within one specific variable.\n"
 		"\n"
+		"         It's possible to use regular expressions.\n"
+		"\n"
 		"<178>Example<278>: #show {Targets starting with the letter A: $targets[A%*]\n"
 		"\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)
 {
 	struct listroot *root;
+	int last;
 
 	root = ses->list[LIST_HISTORY];
 
@@ -82,8 +83,15 @@ void add_line_history(struct session *ses, char *line)
 		return;
 	}
 
+	last = root->used;
+
 	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)
 	{
 		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];
 				}
 
-				sprintf(buf, "%.*s", size, gtd->macro_buf);
+				snprintf(buf, BUFFER_SIZE, "%.*s", size, gtd->macro_buf);
 
 				inputline_insert(buf, -1);
 
@@ -923,6 +923,16 @@ void echo_command(struct session *ses, char *line)
 		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))
 	{
 		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_find);
 extern DO_ARRAY(array_get);
-extern DO_ARRAY(array_index);
+extern DO_ARRAY(array_indexate);
 extern DO_ARRAY(array_insert);
+extern DO_ARRAY(array_numerate);
 extern DO_ARRAY(array_order);
 extern DO_ARRAY(array_reverse);
 extern DO_ARRAY(array_set);
@@ -60,19 +61,20 @@ struct array_type
 
 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"                            },
 	{     "CLR",              array_clear,       NULL                                      },
 	{     "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"     },
 	{     "EXPLODE",          array_explode,     "Explode the variable into a list"        },
 	{     "FILTER",           array_filter,      "Filter a list with given regex"          },
 	{     "FIND",             array_find,        "Find a list item with given regex"       },
 	{     "FND",              array_find,        NULL                                      },
 	{     "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"           },
 	{     "LENGTH",           array_size,        NULL                                      },
 	{     "REVERSE",          array_reverse,     "Sort a list table in reverse order"      },
@@ -177,7 +179,7 @@ DO_ARRAY(array_add)
 	char *str;
 	int index;
 
-	if (!list->root)
+	if (list->root == NULL)
 	{
 		list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 	}
@@ -228,12 +230,19 @@ DO_ARRAY(array_collapse)
 {
 	int index;
 
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN|SUB_ESC);
+
 	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);
 		}
 		free_list(list->root);
@@ -309,7 +318,7 @@ DO_ARRAY(array_delete)
 
 		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;
 		}
@@ -326,63 +335,65 @@ DO_ARRAY(array_delete)
 	}
 	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;
 }
 
 DO_ARRAY(array_explode)
 {
-	char *pti;
+	char *str;
 	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;
 }
 
@@ -490,7 +501,7 @@ DO_ARRAY(array_get)
 	return ses;
 }
 
-DO_ARRAY(array_index)
+DO_ARRAY(array_indexate)
 {
 	int cnt;
 
@@ -498,18 +509,35 @@ DO_ARRAY(array_index)
 
 	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;
 	}
 
-	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, "");
 
 		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;
 		}
@@ -522,7 +550,7 @@ DO_ARRAY(array_index)
 			}
 			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;
 			}
 		}
@@ -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, arg2, GET_ALL, SUB_VAR|SUB_FUN);
 
-	if (!list->root)
+	if (list->root == NULL)
 	{
 		list->root = init_list(ses, LIST_VARIABLE, LIST_SIZE);
 	}
@@ -546,7 +574,7 @@ DO_ARRAY(array_insert)
 
 	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;
 	}
@@ -568,6 +596,25 @@ DO_ARRAY(array_insert)
 	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)
 {
 	int cnt, val, len;
@@ -838,9 +885,14 @@ DO_ARRAY(array_sort)
 
 DO_ARRAY(array_tokenize)
 {
+	char *buf;
 	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)
 	{
@@ -849,24 +901,46 @@ DO_ARRAY(array_tokenize)
 
 	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;
 }

+ 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: 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: 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)
 	{
-		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)
 			{
@@ -253,6 +253,7 @@ int main(int argc, char **argv)
 					printf("  -g  Enable the startup user interface.\n");
 					printf("  -G  Don't show the greeting screen.\n");
 					printf("  -h  This help section.\n");
+					printf("  -H  Nohup compatible mode.\n");
 					printf("  -M  Matrix Digital Rain.\n");
 					printf("  -r  Read given file.\n");
 					printf("  -R  Reattach to daemonized process.\n");
@@ -264,6 +265,10 @@ int main(int argc, char **argv)
 
 					exit(1);
 
+				case 'H':
+					SET_BIT(greeting, STARTUP_FLAG_NOHUP);
+					break;
+
 				case 'M':
 				case 'G':
 					SET_BIT(greeting, STARTUP_FLAG_NOGREETING);
@@ -288,7 +293,6 @@ int main(int argc, char **argv)
 
 	init_tintin(greeting);
 
-
 	RESTRING(gtd->vars[1], argv[0]);
 
 	if (argc > 1)
@@ -297,7 +301,7 @@ int main(int argc, char **argv)
 
 		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)
 			{
@@ -397,6 +401,8 @@ int main(int argc, char **argv)
 		}
 	}
 
+	gtd->varc = UMIN(argc + 1, 100);
+
 	if (argv[optind] != NULL)
 	{
 		if (!strncasecmp(argv[optind], "telnet://", 9))
@@ -600,11 +606,16 @@ void init_tintin(int greeting)
 
 	gtd->level->input--;
 
-	if (HAS_BIT(greeting,  STARTUP_FLAG_VERBOSE))
+	if (HAS_BIT(greeting, STARTUP_FLAG_VERBOSE))
 	{
 		gtd->level->verbose--;
 	}
 
+	if (HAS_BIT(greeting, STARTUP_FLAG_NOHUP))
+	{
+		SET_BIT(gtd->flags, TINTIN_FLAG_NOHUP);
+	}
+
 	init_terminal(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 : "");
 
-	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);
 	}
@@ -734,7 +745,7 @@ void syserr_printf(struct session *ses, char *fmt, ...)
 
 		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)

Datei-Diff unterdrückt, da er zu groß ist
+ 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");
 	}
+/*
+	sscanf(name, "%[^.]..%[^.]", strmin, strmax);
+
+	*min = get_number(ses, strmin);
+	*max = get_number(ses, strmax);
+*/
 
 	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;
 	*max = *max > 0 ? *max - 1 : size + *max;
@@ -518,7 +524,7 @@ int mathexp_tokenize(struct session *ses, char *str, int seed, int debug)
 					case ':':
 						if (debug && wonky == 0)
 						{
-							show_error(gtd->ses, LIST_COMMAND, "\e[1;31m#WARNING: 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++;
 						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)
 {
-	int integer64 = 0;
 	long double value = 0;
-	unsigned long long min = 0, max = 0;
-	unsigned long long value64 = 0;
 
 	switch ((int) node->val)
 	{
 		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;
 
 		case EXP_OP_DICE:
@@ -1220,14 +1218,7 @@ void mathexp_compute(struct session *ses, struct math_node *node)
 	node->priority = EXP_PR_VAR;
 	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)

+ 11 - 0
src/memory.c

@@ -172,6 +172,17 @@ int str_fix(char *original)
 	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)
 {
 	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);
 
+	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"))
 	{
-		printf("%d", ~1 + 2);
+		tintin_printf(ses, "len: %d", strip_color_strlen(ses, arg));
+//		tintin_printf(ses, "<118>\ufffd", arg2);
 	}
 
 	if (!strcmp(arg1, "rain"))

+ 144 - 30
src/nest.c

@@ -27,15 +27,9 @@
 
 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)
@@ -168,6 +162,62 @@ struct listnode *search_nest_node(struct listroot *root, char *variable)
 	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)
 {
 	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)
 {
-//	char arg1[BUFFER_SIZE], arg2[BUFFER_SIZE];
-
 	char *arg1, *arg2;
 
 	arg1 = str_mim(arg);
@@ -277,6 +325,57 @@ int delete_nest_node(struct listroot *root, char *variable)
 	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.
 
 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;
 }
 
-
 struct listnode *get_nest_node_key(struct listroot *root, char *variable, char **result, int def)
 {
 	struct listnode *node;
@@ -726,7 +824,6 @@ struct listnode *get_nest_node_val(struct listroot *root, char *variable, char *
 	return NULL;
 }
 
-
 int get_nest_index(struct listroot *root, char *variable, char **result, int def)
 {
 	struct listnode *node;
@@ -921,13 +1018,8 @@ struct listnode *set_nest_node_ses(struct session *ses, char *arg1, char *format
 		}
 		else
 		{
-                      root = ses->list[LIST_VARIABLE];
+			root = ses->list[LIST_VARIABLE];
 		}
-		node = NULL;
-	}
-	else
-	{
-		node = search_nest_node(root, arg1);
 	}
 
 	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))
 	{
-		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);
+
+		if (strcmp(arg1, name))
+		{
+			check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", arg1, name, arg2, arg1);
+		}
 	}
 	free(arg2);
 
@@ -987,12 +1084,15 @@ struct listnode *add_nest_node_ses(struct session *ses, char *arg1, char *format
 {
 	struct listnode *node;
 	struct listroot *root;
-	char *arg, *arg2, name[BUFFER_SIZE];
+	char *arg, *arg2, *name;
 	va_list args;
 
 	push_call("add_nest_node_ses(%p,%s,%p,...)",ses,arg1,format);
 
+	name = str_alloc_stack(0);
+
 	va_start(args, format);
+
 	if (vasprintf(&arg2, format, args) == -1)
 	{
 		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)
 	{
 		root = ses->list[LIST_VARIABLE];
-		node = NULL;
-	}
-	else
-	{
-		node = search_nest_node(root, arg1);
 	}
 
 	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);
-
 /*
 	if (node && node->root)
 	{
@@ -1039,7 +1133,6 @@ struct listnode *add_nest_node_ses(struct session *ses, char *arg1, char *format
 		node->root = NULL;
 	}
 */
-
 	if (*space_out(arg2) == DEFAULT_OPEN)
 	{
 		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))
 	{
-		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", 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);
 
@@ -1139,6 +1237,11 @@ struct listnode *set_nest_node(struct listroot *root, char *arg1, char *format,
 	}
 	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, "", "");
 	}
 
@@ -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);
 
 		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);
@@ -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))
 	{
+		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);
+
+		if (strcmp(arg1, name))
+		{
+			check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", arg1, name, arg2, arg1);
+		}
 	}
 
 	free(arg2);

+ 2 - 0
src/net.c

@@ -158,6 +158,8 @@ int connect_mud(struct session *ses, char *host, char *port)
 		return -1;
 	}
 
+//	ses->connect_error = connect(sock, address->ai_addr, address->ai_addrlen);
+
 	if (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;
 	int skip, nest = 1;
 
-	pti = space_out(string);
+	pti = HAS_BIT(flag, GET_SPC) ? string : space_out(string);
 	pto = result;
 
 	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;
 	int skip, nest = 0;
 
-	pti = space_out(string);
+	pti = HAS_BIT(flag, GET_SPC) ? string : space_out(string);
 	pto = result;
 
 	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))
 	{
-		pti += sprintf(result, "%.*s", get_utf8_size(pti), pti);
+		pti += snprintf(result, 6, "%.*s", get_utf8_size(pti), pti);
 	}
 	else
 	{

+ 1 - 1
src/path.c

@@ -377,7 +377,7 @@ DO_PATH(path_save)
 	}
 	else if (*arg2 == 0)
 	{
-		sprintf(result, "BOTH {%s}", arg1);
+		snprintf(result, STRING_SIZE, "BOTH {%s}", arg1);
 
 		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);
 
-	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);
 
@@ -479,11 +479,11 @@ void port_printf(struct session *ses, char *format, ...)
 	vsnprintf(buf, len, format, 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);
 
-	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);
 
@@ -505,11 +505,11 @@ void port_log_printf(struct session *ses, struct port_data *buddy, char *format,
 	vsnprintf(buf, len, format, 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);
 
-	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);
 

+ 24 - 0
src/regex.c

@@ -125,35 +125,59 @@ int regexp_compare(struct session *ses, pcre *nodepcre, char *str, char *exp, in
 	switch (flag)
 	{
 		case REGEX_FLAG_CMD:
+			for (i = matches ; i < gtd->cmdc ; i++)
+			{
+				gtd->cmds[i] = restring(gtd->cmds[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->cmdc = matches;
 			break;
 
 		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++)
 			{
 				j = gtd->args[i];
 
 				gtd->cmds[j] = restringf(gtd->cmds[j], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 			}
+			gtd->cmdc = matches;
 			break;
 
 		case REGEX_FLAG_ARG:
+			for (i = matches ; i < gtd->varc ; i++)
+			{
+				gtd->vars[i] = restring(gtd->vars[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->varc = matches;
 			break;
 
 		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++)
 			{
 				j = gtd->args[i];
 
 				gtd->vars[j] = restringf(gtd->vars[j], "%.*s", match[i*2+1] - match[i*2], &str[match[i*2]]);
 			}
+			gtd->varc = matches;
 			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[4], ntos(cnt));
 
+	gtd->cmdc = 5;
+
 	str_sub = str_alloc_stack(strlen(arg) * 2);
 
 	substitute(ses, arg2, str_sub, SUB_CMD);
@@ -573,7 +575,7 @@ DO_SCAN(scan_txt)
 {
 	char line[STRING_SIZE];
 
-	while (fgets(line, BUFFER_SIZE - 1, fp))
+	while (fgets(line, BUFFER_SIZE / 2, fp))
 	{
 		arg = strchr(line, '\r');
 
@@ -591,6 +593,7 @@ DO_SCAN(scan_txt)
 				*arg = 0;
 			}
 		}
+		line[BUFFER_SIZE / 2] = 0;
 
 		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)
 					{
-						sprintf(val, "%.*s", (int) (pts - ptl), ptl);
+						snprintf(val, BUFFER_SIZE, "%.*s", (int) (pts - ptl), ptl);
 					}
 					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)
 		{
-			sprintf(val, "%.*s", (int) (pts - ptl), ptl);
+			snprintf(val, BUFFER_SIZE, "%.*s", (int) (pts - ptl), ptl);
 		}
 		return opt ? opt : 1;
 	}
 
-	sprintf(val, "%.*s", (int) (pts - ptw), ptw);
+	snprintf(val, BUFFER_SIZE, "%.*s", (int) (pts - ptw), ptw);
 
 	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));
 	init_log(newses);
-	ses->log->mode        = gts->log->mode;
+	newses->log->mode     = gts->log->mode;
 
 	newses->input         = calloc(1, sizeof(struct input_data));
 	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);
 	}
 
-	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)
 	{
@@ -554,7 +564,9 @@ struct session *connect_session(struct session *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:
 
@@ -584,18 +596,11 @@ struct session *connect_session(struct session *ses)
 
 		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();
 		return ses;
 	}
 
-	if (ses->connect_retry > gtd->utime)
+	if (ses->connect_retry > utime())
 	{
 		fd_set readfds;
 
@@ -606,7 +611,7 @@ struct session *connect_session(struct session *ses)
 		{
 			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);
 			}
@@ -617,8 +622,6 @@ struct session *connect_session(struct session *ses)
 
 	if (ses->connect_error)
 	{
-		to.tv_sec = 0;
-
 		tintin_printf(ses, "#SESSION '%s' FAILED TO CONNECT.", ses->name);
 	}
 
@@ -644,6 +647,7 @@ void cleanup_session(struct session *ses)
 		pop_call();
 		return;
 	}
+	SET_BIT(ses->flags, SES_FLAG_CLOSED);
 
 	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_mccp3(ses);
 
@@ -691,12 +693,15 @@ void cleanup_session(struct session *ses)
 	{
 		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);
 		}
 
-		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)
 	{

+ 39 - 30
src/show.c

@@ -84,7 +84,7 @@ DO_COMMAND(do_echo)
 
 	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, "\\\\");
 
@@ -168,8 +168,8 @@ void show_message(struct session *ses, int index, char *format, ...)
 
 			if (vasprintf(&buffer, format, args) == -1)
 			{
-				syserr_printf(ses, "print_lines: vasprintf2:");
-				
+				syserr_printf(ses, "show_message: vasprintf2:");
+
 				pop_call();
 				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, ...)
 {
 	struct listroot *root;
-	char buf[STRING_SIZE];
+	char *buffer;
 	va_list args;
 
 	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);
-
-	vsprintf(buf, format, args);
-
+	vasprintf(&buffer, format, args);
 	va_end(args);
 
 	if (gtd->level->debug || HAS_BIT(root->flags, LIST_FLAG_DEBUG))
 	{
 		gtd->level->verbose++;
 
-		tintin_puts2(ses, buf);
+		tintin_puts2(ses, buffer);
 
 		gtd->level->verbose--;
 
-		pop_call();
-		return;
+		goto end;
 	}
 
 	if (HAS_BIT(root->flags, LIST_FLAG_LOG))
 	{
 		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();
 	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, ...)
 {
 	struct listroot *root;
-	char buf[STRING_SIZE];
+	char *buffer;
 	va_list args;
 
 	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();
 		return;
 	}
+	buffer = str_alloc_stack(0);
 
 	va_start(args, format);
-
-	vsprintf(buf, format, args);
-
+	vsprintf(buffer, format, args);
 	va_end(args);
 
 	gtd->level->verbose++;
 
-	tintin_puts(ses, buf);
+	tintin_puts(ses, buffer);
 
 	gtd->level->verbose--;
 
@@ -383,19 +383,14 @@ void show_lines(struct session *ses, char *str)
 	return;
 }
 
-
 void tintin_header(struct session *ses, int width, char *format, ...)
 {
-	char arg[BUFFER_SIZE], buf[BUFFER_SIZE];
+	char *title, *buffer;
 	va_list args;
 	int cols;
 
 	push_call("tintin_header(%p,%p)",ses,format);
 
-	va_start(args, format);
-	vsprintf(arg, format, args);
-	va_end(args);
-
 	if (width)
 	{
 		cols = UMIN(width, get_scroll_cols(ses));
@@ -411,25 +406,32 @@ void tintin_header(struct session *ses, int width, char *format, ...)
 		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))
 	{
-		memset(buf, ' ', cols);
+		memset(buffer, ' ', cols);
 	}
 	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();
 	return;
@@ -462,14 +464,21 @@ void tintin_printf2(struct session *ses, char *format, ...)
 
 void tintin_printf(struct session *ses, char *format, ...)
 {
-	char buffer[BUFFER_SIZE];
+	char *buffer;
 	va_list args;
 
+	push_call("tintin_printf(%p,%p,...)",ses,format);
+
+	buffer = str_alloc_stack(0);
+
 	va_start(args, format);
 	vsprintf(buffer, format, args);
 	va_end(args);
 
 	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
 	{
-		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, "#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);
 
-	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;
 }
@@ -441,7 +441,7 @@ char *lalign(struct session *ses, char *in, char *out, int width)
 
 	width = UMAX(0, width - width_in);
 
-	sprintf(out, "%s%*s", in, width, "");
+	snprintf(out, BUFFER_SIZE, "%s%*s", in, width, "");
 
 	return out;
 }
@@ -466,7 +466,7 @@ char *ralign(struct session *ses, char *in, char *out, int width)
 
 	width = UMAX(0, width - width_in);
 
-	sprintf(out, "%*s%s", width, "", in);
+	snprintf(out, BUFFER_SIZE, "%*s%s", width, "", in);
 
 	return out;
 }

+ 38 - 39
src/substitute.c

@@ -862,6 +862,9 @@ char *lit_color_code(struct session *ses, char *pti, int mod)
 	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)
 {
 	char buf[6];
@@ -1023,16 +1026,13 @@ int color_gradient(char *pti, int min, int max)
 	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)
 {
 	struct listnode *node;
 	struct listroot *root;
 	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 };
 	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);
 
 	pti = string;
-	pto = (string == result) ? buffer : result;
+	pto = out = (string == result) ? buffer : result;
 
 	DEL_BIT(flags_neol, SUB_EOL|SUB_LNF);
 
@@ -1983,7 +1983,6 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 				}
 				break;
 
-
 			case '\\':
 				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);
 							continue;
 
+						case ';':
 						case '$':
 						case '&':
 						case '*':
@@ -2144,6 +2144,11 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 				}
 				break;
 
+			case '\n':
+				old[0] = 0;
+				*pto++ = *pti++;
+				break;
+
 			case ASCII_ESC:
 				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++;
 					}
 				}
+				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
 				{
 					*pto++ = *pti++;
@@ -2193,39 +2224,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 				break;	
 
 			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;
 		}
 	}

+ 18 - 19
src/tables.c

@@ -80,6 +80,7 @@ struct charset_type charset_table[] =
 
 	{    "BIG5TOUTF8",    "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_BIG5TOUTF8 },
 	{    "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 },
 	{    "FANSITOUTF8",   "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_FANSITOUTF8 },
 	{    "GBK1TOUTF8",    "utf-8",       CHARSET_FLAG_UTF8|CHARSET_FLAG_GBK1TOUTF8 },
@@ -1182,7 +1183,7 @@ struct buffer_type buffer_table[] =
 	{    "CLEAR",             buffer_clear,        "Clear buffer."                                  },
 	{    "DOWN",              buffer_down,         "Scroll down one page."                          },
 	{    "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." },
 	{    "HOME",              buffer_home,         "Scroll up to the start of 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╚═╝" },
 	{ "\"", 41, "██╗██╗\n2██║██║\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╚═╝ ╚═╝" },
-	{ "'",  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╚═╝ " },
 	{ "*",  47, "▄  █  ▄\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 ╚════╝ " },
 	{ "1",  53, "  ▄██╗  \n ████║  \n ╚═██║  \n   ██║  \n ██████╗\n ╚═════╝" },
@@ -1772,14 +1774,12 @@ struct stamp_type huge_stamp_table[] =
 	{ "7",  53, "███████╗\n╚════██║\n    ██╔╝\n   ██╔╝ \n   ██║  \n   ╚═╝  " },
 	{ "8",  53, " █████╗ \n██╔══██╗\n╚█████╔╝\n██╔══██╗\n╚█████╔╝\n ╚════╝ " },
 	{ "9",  53, " █████╗ \n██╔══██╗\n╚██████║\n ╚═══██║\n █████╔╝\n ╚════╝ " },
-
 	{ ":",  53, "        \n   ██╗  \n   ╚═╝  \n        \n   ██╗  \n   ╚═╝  " },
-// ;
-// <
-// =
-// >
-// ?
-
+	{ ";",  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 ╚╝╚═══╝ " },
 	{ "A",  53, " █████╗ \n██╔══██╗\n███████║\n██╔══██║\n██║  ██║\n╚═╝  ╚═╝" },
@@ -1808,13 +1808,12 @@ struct stamp_type huge_stamp_table[] =
 	{ "X",  53, "██╗  ██╗\n╚██╗██╔╝\n ╚███╔╝ \n ██╔██╗ \n██╔╝ ██╗\n╚═╝  ╚═╝" },
 	{ "Y",  59, "██╗   ██╗\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" },
 	{ "_",  53, "        \n        \n        \n        \n███████╗\n╚══════╝" },
-// `
+	{ "`",  23, "██╗\n╚█║\n ╚╝\n   \n   \n   " },
 
 	{ "i",  23, "██╗\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);
 
-		client_telopt_debug(ses, "SENT IAC DO ECHO (SKIPPED)");
+		client_telopt_debug(ses, "SENT IAC DO ECHO");
 	}
 	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 *pto;
-	int i, j, accept;
+	int i, j, accept, found = 0, request = 0;
 
 	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]);
 
-	i = 5;
+	if (src[3] == CHARSET_REQUEST)
+	{
+		i = 5;
+		request = 1;
+	}
+	else
+	{
+		i = 4;
+	}
 
 	while (i < cplen && src[i] != SE)
 	{
 		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 = 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);
 				}
-				else if (!strcmp(var, "FANSI"))
+				else if (!strcmp(var, "FANSI") || !strcmp(var, "CP437"))
 				{
 					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 (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++;
 	}
 
+	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");
 
 	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);
 }
 
+
 int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 {
 	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;
 
 			case '{':
+				if (state[nest])
+				{
+					pto += sprintf(pto, "{%d}", state[nest]++);
+				}
 				if (nest != 0)
 				{
 					*pto++ = '{';
@@ -1729,7 +1745,7 @@ int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 				}
 				i++;
 				state[++nest] = 1;
-				pto += sprintf(pto, "{%d}", state[nest]);
+//				pto += sprintf(pto, "{%d}", state[nest]);
 				break;
 
 			case ']':
@@ -1747,14 +1763,18 @@ int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 
 			case ',':
 				i++;
-				if (state[nest])
+/*				if (state[nest])
 				{
 					pto += sprintf(pto, "{%d}", ++state[nest]);
-				}
+				}*/
 				break;
 
 			case '"':
 				i++;
+				if (state[nest])
+				{
+					pto += sprintf(pto, "{%d}", state[nest]++);
+				}
 				if (nest)
 				{
 					*pto++ = '{';
@@ -1818,6 +1838,10 @@ int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 				break;
 
 			default:
+				if (state[nest])
+				{
+					pto += sprintf(pto, "{%d}", state[nest]++);
+				}
 				if (nest)
 				{
 					*pto++ = '{';
@@ -1914,6 +1938,15 @@ int client_recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
 	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
 */

+ 15 - 1
src/terminal.c

@@ -35,6 +35,11 @@ void init_terminal(struct session *ses)
 {
 	struct termios io;
 
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_NOHUP))
+	{
+		return;
+	}
+
 	if (tcgetattr(0, &gtd->old_terminal))
 	{
 		syserr_fatal(-1, "init_terminal: tcgetattr 1");
@@ -85,6 +90,11 @@ void init_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 (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[?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)

+ 20 - 6
src/tintin.h

@@ -124,6 +124,7 @@
 #define GET_ALL                          1 // stop at semicolon
 #define GET_NST                          2 // nest square brackets
 #define GET_VBT                          4 // ignore semicolon for verbatim mode
+#define GET_SPC                          8 // don't strip spaces
 
 #define TEL_N                            0
 #define TEL_Y                            1
@@ -196,6 +197,8 @@
 
 #define HISTORY_FILE         "history.txt"
 
+#define MALLOC_SIZE                1000000
+#define STRING_SIZE                  80000
 #define BUFFER_SIZE                  40000
 #define INPUT_SIZE                   10000
 #define PATH_SIZE                     4096
@@ -207,10 +210,9 @@
 #define CHAR_SIZE                        5
 #define LIST_SIZE                        2
 
-#define STRING_SIZE        2 * BUFFER_SIZE
 
 #define CLIENT_NAME              "TinTin++"
-#define CLIENT_VERSION           "2.02.12 "
+#define CLIENT_VERSION           "2.02.20 "
 
 
 #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_PROCESSINPUT      BV03
 #define TINTIN_FLAG_INHERITANCE       BV04
@@ -615,6 +617,7 @@ enum operators
 #define TINTIN_FLAG_LOCAL             BV12
 #define TINTIN_FLAG_PRESERVEMACRO     BV13
 #define TINTIN_FLAG_WINCHUPDATE       BV14
+#define TINTIN_FLAG_NOHUP             BV15 // fixes tcsetattr crashes with nohup
 
 #define CONFIG_FLAG_AUTOPATCH         BV01
 #define CONFIG_FLAG_AUTOPROMPT        BV02
@@ -681,7 +684,7 @@ enum operators
 #define LIST_FLAG_NEST                BV13
 #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_LINEFEED             BV01
@@ -754,12 +757,13 @@ enum operators
 #define MAP_FLAG_BLOCKGRAPHICS        BV10
 #define MAP_FLAG_RESIZE               BV11
 #define MAP_FLAG_SYNC                 BV12
-#define MAP_FLAG_ASCIILENGTH          BV13 // For debugging but might be useful
+#define MAP_FLAG_ASCIILENGTH          BV13
 #define MAP_FLAG_TERRAIN              BV14
 #define MAP_FLAG_UPDATETERRAIN        BV15
 #define MAP_FLAG_DOUBLED              BV16
 #define MAP_FLAG_QUIET                BV17
 #define MAP_FLAG_READ                 BV18
+#define MAP_FLAG_PANCAKE              BV19
 
 #define MAP_SEARCH_NAME                0
 #define MAP_SEARCH_EXITS               1
@@ -829,6 +833,7 @@ enum operators
 #define STARTUP_FLAG_ARGUMENT            8
 #define STARTUP_FLAG_NOTITLE            16
 #define STARTUP_FLAG_VERBOSE            32
+#define STARTUP_FLAG_NOHUP              64
 
 #define WRAP_FLAG_NONE                   0
 #define WRAP_FLAG_DISPLAY             BV01
@@ -1143,6 +1148,8 @@ struct tintin_data
 	char                  * vars[100];
 	char                  * cmds[100];
 	int                     args[100];
+	int                     varc;
+	int                     cmdc;
 	char                    color_reset[COLOR_SIZE];
 };
 
@@ -1436,7 +1443,7 @@ struct map_data
 	int                     global_vnum;
 	struct exit_data      * global_exit;
 	int                     version;
-	short                   display_stamp;
+	unsigned short          display_stamp;
 	int                     nofollow;
 	char                    legend[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_max(char *str);
 extern  int str_fix(char *str);
+extern  int str_fix_len(char *str, int len);
+
 
 extern char *str_alloc(int len);
 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 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_path(struct listroot *root, char *variable, char *path);
 extern struct listnode *search_nest_node_ses(struct session *ses, char *variable);
 extern int search_nest_index(struct listroot *root, char *variable);
 extern struct listroot *update_nest_root(struct listroot *root, char *arg);
 extern void update_nest_node(struct listroot *root, char *arg);
 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_val(struct listroot *root, char *variable, char **result);
 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__
 #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_write_compressed(struct session *ses, char *txt, int length);
 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__
 #define __VARIABLE_H__
 
+extern DO_COMMAND(do_replace);
+
 extern  int valid_variable(struct session *ses, char *arg);
 extern  int string_raw_str_len(struct session *ses, char *str, int start, int end);
 extern  int string_str_raw_len(struct session *ses, char *str, int start, int end);

+ 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)
 {
-	char *str, arg[BUFFER_SIZE];
+	char *str, *arg;
+
+	arg = malloc(MALLOC_SIZE);
+
+	if (arg == NULL)
+	{
+		arg = malloc(BUFFER_SIZE);
+	}
 
 	str = token->data->cpy;
 
@@ -255,6 +262,8 @@ void resetforeachtoken(struct session *ses, struct scriptnode *token)
 
 	RESTRING(token->data->str, arg);
 
+	free(arg);
+
 	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))
 	{
-		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))
 	{
-		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
 	{
@@ -1071,6 +1080,10 @@ char *write_script(struct session *ses, struct scriptroot *root)
 	{
 		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:
 				cat_sprintf(buf, "%s%s", indent(token->lvl), token->str);
 				break;
@@ -1155,6 +1168,10 @@ char *view_script(struct session *ses, struct scriptroot *root)
 	{
 		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:
 				cat_sprintf(buf, "%s" COLOR_STRING "%s", indent(token->lvl), token->str);
 				break;
@@ -1195,7 +1212,7 @@ char *view_script(struct session *ses, struct scriptroot *root)
 				break;
 
 			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;
 
 			case TOKEN_TYPE_END:

+ 3 - 1
src/trigger.c

@@ -581,7 +581,7 @@ DO_COMMAND(do_highlight)
 	{
 		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.");
 		}
 		else
@@ -995,6 +995,8 @@ DO_COMMAND(do_tick)
 		get_number_string(ses, arg3, time);
 	}
 
+	// store creation time for #info tickers save
+
 	sprintf(arg3, "%lld", ++gtd->utime);
 
 	if (*arg1 == 0)

+ 7 - 2
src/update.c

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

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


+ 1 - 1
src/utils.c

@@ -403,7 +403,7 @@ char *ftos(float 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')
 		{

+ 122 - 24
src/variable.c

@@ -41,7 +41,9 @@ DO_COMMAND(do_variable)
 	}
 	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)
 		{
@@ -53,11 +55,11 @@ DO_COMMAND(do_variable)
 
 				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
 			{
-				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)
@@ -69,7 +71,7 @@ DO_COMMAND(do_variable)
 	{
 		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;
 		}
@@ -108,7 +110,11 @@ DO_COMMAND(do_unvariable)
 		}
 		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);
 	}
@@ -618,7 +624,7 @@ void headerstring(struct session *ses, char *str, char *columns)
 		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);
 
@@ -805,11 +811,11 @@ void metricgroupingstring(struct session *ses, char *str)
                 }
                 if (val >= 100)
                 {
-                	sprintf(tmp, " %Lf", val);
+                	snprintf(tmp, NUMBER_SIZE, " %Lf", val);
 		}
 		else
 		{
-                	sprintf(tmp, "%Lf", val);
+                	snprintf(tmp, NUMBER_SIZE, "%Lf", val);
 		}
                 sprintf(str, "%.4s%c", tmp, big[index]);
 	}
@@ -820,18 +826,18 @@ void metricgroupingstring(struct session *ses, char *str)
 			val = val * 1000;
 			index++;
 		}
-		sprintf(tmp, "%Lf", val);
+		snprintf(tmp, NUMBER_SIZE, "%Lf", val);
 		sprintf(str, "%.4s%c", tmp, small[index]);
 	}
 	else if (val >= 0)
 	{
 		if (val >= 100)
 		{
-			sprintf(tmp, " %Lf", val);
+			snprintf(tmp, NUMBER_SIZE, " %Lf", val);
 		}
 		else
 		{
-			sprintf(tmp, "%Lf", val);
+			snprintf(tmp, NUMBER_SIZE, "%Lf", val);
 		}
 		sprintf(str, "%.4s%c", tmp, big[index]);
 	}
@@ -839,11 +845,11 @@ void metricgroupingstring(struct session *ses, char *str)
 	{
 		if (val <= -100)
 		{
-			sprintf(tmp, " %Lf", val);
+			snprintf(tmp, NUMBER_SIZE, " %Lf", val);
 		}
 		else
 		{
-			sprintf(tmp, "%Lf", val);
+			snprintf(tmp, NUMBER_SIZE, "%Lf", val);
 		} 
 		sprintf(str, "%.5s%c", tmp, small[index]);
 	}
@@ -858,7 +864,7 @@ void metricgroupingstring(struct session *ses, char *str)
                         val = val / 1000;
                         index++;
                 }
-                sprintf(tmp, "%Lf", val);
+                snprintf(tmp, NUMBER_SIZE, "%Lf", val);
                 sprintf(str, "%.5s%c", tmp, big[index]);
 	}
 
@@ -869,7 +875,7 @@ void metricgroupingstring(struct session *ses, char *str)
 			val = val * 1000;
 			index++;
 		}
-		sprintf(tmp, "%Lf", val);
+		snprintf(tmp, NUMBER_SIZE, "%Lf", val);
 		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);
 	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)
 	{
@@ -957,7 +963,7 @@ void wrapstring(struct session *ses, char *str, char *wrap)
 		{
 			*pte++ = 0;
 
-			substitute(ses, pts, arg1, SUB_BRA);
+			substitute(ses, pts, arg1, SUB_SEC);
 
 			cat_sprintf(str, "{%d}{%s}", ++cnt, arg1);
 
@@ -968,7 +974,7 @@ void wrapstring(struct session *ses, char *str, char *wrap)
 			pte++;
 		}
 	}
-	substitute(ses, pts, arg1, SUB_BRA);
+	substitute(ses, pts, arg1, SUB_SEC);
 
 	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;
 }
 
+// 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
 
 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)
 								{
 									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)));
 								}
 								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",
-										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)));
 								}
 							}
@@ -1465,8 +1557,14 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						break;
 
 					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;
 
 					case 'D':
@@ -1510,7 +1608,7 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						break;
 
 					case 'U':
-						sprintf(arglist[i], "%lld", ++gtd->utime);
+						sprintf(arglist[i], "%lld", utime());
 						break;
 
 					case 'X':

+ 4 - 2
src/vt102.c

@@ -105,7 +105,9 @@ void goto_pos(struct session *ses, int row, int col)
 
 		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_col = col;
@@ -1443,7 +1445,7 @@ int catch_vt102_codes(struct session *ses, unsigned char *str, int cplen)
 						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);
 

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