Scandum 4 năm trước cách đây
mục cha
commit
e8686858ef
35 tập tin đã thay đổi với 1503 bổ sung1154 xóa
  1. 12 1
      NEWS
  2. 13 104
      TODO
  3. 109 88
      docs/help.html
  4. 32 20
      docs/syntax.txt
  5. 43 1
      mods/igr.mods
  6. 7 1
      src/buffer.c
  7. 9 2
      src/cursor.c
  8. 263 199
      src/data.c
  9. 10 7
      src/dict.c
  10. 22 5
      src/draw.c
  11. 7 0
      src/edit.c
  12. 29 26
      src/gui.h
  13. 114 96
      src/help.c
  14. 5 5
      src/history.c
  15. 33 5
      src/main.c
  16. 44 17
      src/mapper.c
  17. 377 347
      src/math.c
  18. 1 12
      src/misc.c
  19. 16 4
      src/nest.c
  20. 1 1
      src/port.c
  21. 3 3
      src/regex.c
  22. 18 7
      src/scan.c
  23. 21 1
      src/screen.c
  24. 50 25
      src/session.c
  25. 3 2
      src/split.c
  26. 64 1
      src/string.c
  27. 42 18
      src/substitute.c
  28. 4 2
      src/tables.c
  29. 2 0
      src/text.c
  30. 18 18
      src/tintin.h
  31. 2 0
      src/tokenize.c
  32. 2 2
      src/trigger.c
  33. 29 0
      src/update.c
  34. 83 129
      src/utils.c
  35. 15 5
      src/variable.c

+ 12 - 1
NEWS

@@ -1,6 +1,17 @@
 Due to continuous improvements old tintin scripts aren't always compatible
 with new versions. This document tries to list most compatibility conflicts.
 
+TinTin++ 2.02.11
+----------------
+
+01) %w now translates to {[a-zA-Z0-9_]*} instead of {[a-zA-Z]*} so it matches
+    the behavior of \w. Same for %W.
+
+02) The : time operator in #math should no longer be used since that is
+    going to be changed to c style ? : ternary operator support.
+
+03) #math and #if no longer recognize .1 as a number, you need to use 0.1.
+
 TinTin++ 2.02.04
 ----------------
 
@@ -32,7 +43,7 @@ TinTin++ 2.02.00
 03) $variable() *variable() and &variable() are reserved until further notice,
     use ${variable}( for proper behavior.
 
-04) %+ should no longer be used in regular expressions.
+04) %+ now has different behavior when followed by a number.
 
     You can replace '%+' with '%+1..a', see #help regex for more information.
 

+ 13 - 104
TODO

@@ -1,83 +1,27 @@
-  - double check packet patch code, gagging patched line? Echo command?
-
   - look into large variable handling, foreach, once again
 
-
-  - output from the other session remains in the input line of the new session sometimes.
-
-  - dev/test/mouse/windowmap.tin has issues with ; in chat window
-    caused by %w in format, add SUB_BRA to just substitute braces.
-
-  - #echo {%g} {10684.3609}
+  - tab window, add tab ordering and garbage collection
 
 ----------------
+  - ãã is messing up
 
-  - backspace rewrites the input line, which can confuse screen readers.
-
-  - precision is poorly handled in #math (sprintf(temp, "%.12Lf", value);) needs to be improved.
+  - Add RECEIVED INPUT CHARACTER event / filter mouse input sequences.
 
-  - Add #event {SESSION CONNECTED} {5.1} option.
+  - handle signals in the mainloop.
 
-  - allow using #snoop to print to the foreground in provided square?
+  - add ctrl-r support for scrollback
 
-  - #split 0 1;#screen inputregion -2 1 -2 -1;#showme bug -1
-
-  - look into using $HOME/.config/tintin
-  - look into checking XDG: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
-
-  - Display directory on failed #read.
+  - Add #event {SESSION CONNECTED} {5.1} option.
 
   - Add the option to use + ? . and * as variable exit fields. 
 
   - TT++ HANDBOOK for meticulous organized details.
 
-  - improve #draw bar.▪▪ ◼◼ ■■ ◆◆ ▬▬ ●● ⣿⣿⣿⣷
-    dual bar using reverse color and ██ ▀▀ ▄▄
-    128 bit flags
-    text inside bar.    
-
-  - store creation epoch in arg4 for #tick, add example script to display.
-
-  - get rid of SORT_DELAY due to one shot code.
-
-  - add #path load option to #path create
-
-  - double check script crashes on screen resize
-
-  - Add catch events for SIGUSR1 and SIGUSR2
-  - See about turning window close into a program termination event.
-
-  - check out hibbmap.dat in dev/test, mudlet to tt++ conversion
-
-  - add #history filter option to filter out 1 letter commands.
-
-  - no longer works?
-
-          #event {SESSION ACTIVATED} {
-                #BUFFER end;
-                };
-
-  - Possible crash with launching 2 sessions reading a large variable.
-
-  - https://tintin.sourceforge.io/forum/viewtopic.php?f=3&t=2818 (likely a
-    telnet compatibility issue)
+  - vertical bar drawing
 
+  - error: cursor_check_line_modified1: 
   - https://tintin.sourceforge.io/forum/viewtopic.php?f=10&t=2811 (possible
-    issue with iniating input buffer for a new session, possibly prompt
-    related somehow)
-
-  - possible issue with sessions randomly getting activated.
-
-  - possible wordwrapping on raw string length instead of stripped length
-    in some edge case.
-
-  - Look into WSL sound handling
-
-  - CP949.TXT full table support
-
-  - Look into escaped color code handling in zmp, possibly msdp
-
-  - See about making @$var{} work. Maybe allow @{$var}{}
+    issue with initiating input buffer for a new session)
 
   - finish BUFFER_SIZE replacement.
 
@@ -85,34 +29,16 @@
 
   - update msdp scripts with #line msdp feature
 
-  - refresh input on session switch
-
-  - swap arg1, arg2, arg for do_port
-
-  - https://tintin.sourceforge.io/forum/viewtopic.php?f=5&t=2776
-    Need special casing for non capturing {?!} regex handling.
-
   - #cursor get word option, maybe get the yank buffer as well.
 
-  - Look into config option to change the working directory
-
   - Add a way to set env variables either 1) for the current process, and/or 2) as a parameter to #system (i.e. after fork in the child process).
 
-  - Add #info input, mainly to save the cursor position
-
 * STUFF THAT IS PROBABLY GONNA GET DONE
 
-  - #path get position saves as 1 if no path is loaded. 
-
   - Finish port proxy support: resizing, input, security
 
   - look into transparent drawing
 
-  - if error pcre.h, need export C_INCLUDE_PATH= where it locate
-
-  - The #else command could follow a #foreach so as to create "for-else" loops
-    in case a loop is not broken.
-
   - Add #line gag 2 +2 -2 support.
 
   - Work on VT2020 protocol (mouse click)
@@ -120,8 +46,6 @@
   - $var[%*][%*] support.
   - make list sorting move nests as well
 
-  - #line convert utf-8 support.
-
   - better #draw font support
 
   - case insensitive tabbing (partial start with rewrite)
@@ -131,18 +55,6 @@
 #0  0x00005555555a5418 in tunnel_void (ses=0x555558a97b00, from=8004, room=1931506785, dir=1684955508) at mapper.c:4220
 #1  0x00005555555a599d in searchgrid_find (ses=0x555558a97b00, from=153, search=0x555558d8f850) at mapper.c:4336
 #2  0x00005555555afb44 in map_list (ses=0x555558a97b00, arg=0x5555572e1b95 "{} {} {} {$roomarea} {variable} {area_mapped}",
-    arg1=0x555555d2d910 "", arg2=0x555555d23cb0 "") at mapper.c:6791
-#3  0x0000555555596562 in do_map (ses=0x555558a97b00, arg=0x5555572e1b95 "{} {} {} {$roomarea} {variable} {area_mapped}",
-    arg1=0x555555d2d910 "", arg2=0x555555d23cb0 "", arg3=0x0, arg4=0x0) at mapper.c:133
-#4  0x00005555555be82a in parse_script (root=0x555557f71960, lvl=0, token=0x55555c6ba230, shift=0x55555767ab10) at tokenize.c:812
-#5  0x00005555555bfded in script_driver (ses=0x555558a97b00, list=1,
-    str=0x555555d19ff0 "#map {get} {ROOMVNUM} {roomvnum};#map {get} {ROOMAREA} {roomarea};#map {get} {ROOMNAME} {roomname};#map {get} {ROOMDESC} {roomdesc};#format {roomname_len}\t{%L} {$roomname};#format {roomvnum_len}\t{%L} "...) at tokenize.c:1242
-#6  0x0000555555581ee1 in parse_input (ses=0x555558a97b00, input=0x55555767c099 "") at parse.c:226
-#7  0x00005555555bef73 in parse_script (root=0x555557680c80, lvl=0, token=0x555557f719d0, shift=0x555557f719d0) at tokenize.c:1011
-#8  0x00005555555bfded in script_driver (ses=0x555558a97b00, list=6, str=0x555558d95d10 "mapupdate") at tokenize.c:1242
-#9  0x00005555555865af in delay_update () at update.c:938
-#10 0x000055555558434d in mainloop () at update.c:159
-#11 0x000055555557df19 in main (argc=2, argv=0x7fffffffe4c8) at main.c:416
 
   - #map list {<exits>} breaks on rooms that have e mapped to eu.
   - finish landmarks
@@ -203,21 +115,14 @@
 
   - add packets patched counter
 
-  - Fix arrow key up history recall overwriting the prompt (partial redesign)
-    Auto prompt fixing on overwrite.
-
   - reportable_sounds
 
-  - https://tintin.sourceforge.io/forum/viewtopic.php?f=4&t=2597 #add #screen support
-
   - TELNET documentation.
 
   - Add JSON support to #scan
 
   - see if #break 2 is possible, maybe #continue 2 as well.
 
-  - http://tintin.sourceforge.net/board/viewtopic.php?p=9625 (map undo issue) (not a big issue)
-
   - http://tintin.sourceforge.net/board/viewtopic.php?t=2339 (map area data)
 
   - IPv6 for chat
@@ -244,6 +149,10 @@
 
   - Start of line anchors aren't working in #replace.
 
+  - add #history filter option to filter out 1 letter commands.
+
+  - Look into config option to change the working directory
+
 --------------------------------------------------------------------------------
 
 * ROADMAP

+ 109 - 88
docs/help.html

@@ -721,6 +721,7 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
          You can further prefix the option as following:
 
          ASCII      will draw in ASCII mode.
+         BALIGN     will bottom align text.
          BLANKED    will blank the lines and corners.
          BOTTOM     will draw on the bottom side if possible.
          BOXED      will draw a box along the square.
@@ -761,7 +762,7 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
          The following types are available.
 
          [HORIZONTAL] </span><span style='color:#FFF'>BAR</span><span style='color:#AAA'> {&lt;MIN&gt;;&lt;MAX&gt;;[COLOR]}
-          will draw a bar.
+          will draw a bar, use two 256 color codes for a color gradient.
          [ASCII|UNICODE|HUGE] </span><span style='color:#FFF'>BOX</span><span style='color:#AAA'> {[TEXT1]} {[TEXT2]}
            will draw a box.
          [BOXED|FOREGROUND] </span><span style='color:#FFF'>BUFFER
@@ -805,7 +806,7 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
 
 </span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #echo {The current date is %t.} {%Y-%m-%d %H:%M:%S}
          #echo {[%38s][%-38s]} {Hello World} {Hello World}
-         #echo {{this is %s on the top row} {-1}} {printed}
+         #echo {{this is %s on the top row} {1}} {printed}
 
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#BUFFER'>buffer</a>, <a href='#FORMAT'>format</a>, <a href='#GREP'>grep</a> and <a href='#SHOWME'>showme</a>.
 <a name='EDIT'></a>
@@ -1013,6 +1014,7 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
          &bsol;x7B  send the '{' character.
          &bsol;x7D  send the '}' character.
          &bsol;u    print a 16 bit unicode character, &bsol;uFFFD for example.
+         &bsol;u{}  print a 8-21 bit unicode character, &bsol;u{2AF21} for example.
          &bsol;U    print a 21 bit unicode character, &bsol;U02AF21 for example.
          &bsol;v    send a vertical tab
 
@@ -1137,6 +1139,38 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
          </span><span style='color:#FFF'>PORT LOG MESSAGE       </span><span style='color:#AAA'>%0 name %1 ip %2 port %3 data %4 plain data
          </span><span style='color:#FFF'>PORT RECEIVED MESSAGE  </span><span style='color:#AAA'>%0 name %1 ip %2 port %3 data %4 plain data
 
+         </span><span style='color:#5F5'>SCAN EVENTS</span><span style='color:#AAA'>
+
+         SCAN CSV HEADER        %0 all args %1 arg1 %2 arg2 .. %99 arg99
+         SCAN CSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99
+         SCAN TSV HEADER        %0 all args %1 arg1 %2 arg3 .. %99 arg99
+         SCAN TSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99
+
+         </span><span style='color:#5F5'>SCREEN EVENTS</span><span style='color:#AAA'>
+
+         </span><span style='color:#FFF'>SCREEN FOCUS
+         </span><span style='color:#AAA'>  %0 focus (0 or 1)
+
+         SCREEN LOCATION        %0 rows %1 cols  %2 height %3 width
+
+         </span><span style='color:#FFF'>SCREEN MOUSE LOCATION
+         </span><span style='color:#AAA'>  %0 row  %1 col  %2 -row  %3 -col  %4 pix row  %5 pix col
+           %6 -pix row  %7 -pix col  %8 location
+
+         SCREEN RESIZE          %0 rows %1 cols %2 height %3 width
+         SCREEN SPLIT           %0 top row %1 top col %2 bot row %3 bot col
+         SCREEN UNSPLIT         %0 top row %1 top col %2 bot row %3 bot col
+
+         </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 DEACTIVATED    %0 name
+         SESSION DISCONNECTED   %0 name %1 host %2 ip %3 port
+         SESSION TIMED OUT      %0 name %1 host %2 ip %3 port
+
+
          </span><span style='color:#5F5'>SYSTEM EVENTS</span><span style='color:#AAA'>
 
          DAEMON ATTACHED        %0 file %1 pid
@@ -1152,6 +1186,7 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
          SYSTEM CRASH           %0 message
          SYSTEM ERROR           %0 name %1 system msg %2 error %3 error msg
          UNKNOWN COMMAND        %0 raw text
+         SIGUSR                 %0 signal
 
          </span><span style='color:#5F5'>TELNET EVENTS
 
@@ -1174,37 +1209,6 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
          </span><span style='color:#AAA'>  %0 year  %1 month  %2 day of week  %3 day of month  %4 hour
            %5 minute  %6 second
 
-         </span><span style='color:#5F5'>SCAN EVENTS</span><span style='color:#AAA'>
-
-         SCAN CSV HEADER        %0 all args %1 arg1 %2 arg2 .. %99 arg99
-         SCAN CSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99
-         SCAN TSV HEADER        %0 all args %1 arg1 %2 arg3 .. %99 arg99
-         SCAN TSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99
-
-         </span><span style='color:#5F5'>SCREEN EVENTS</span><span style='color:#AAA'>
-
-         </span><span style='color:#FFF'>SCREEN FOCUS
-         </span><span style='color:#AAA'>  %0 focus (0 or 1)
-
-         SCREEN LOCATION        %0 rows %1 cols  %2 height %3 width
-
-         </span><span style='color:#FFF'>SCREEN MOUSE LOCATION
-         </span><span style='color:#AAA'>  %0 row  %1 col  %2 -row  %3 -col  %4 pix row  %5 pix col
-           %6 -pix row  %7 -pix col  %8 location
-
-         SCREEN RESIZE          %0 rows %1 cols %2 height %3 width
-         SCREEN SPLIT           %0 top row %1 top col %2 bot row %3 bot col
-         SCREEN UNSPLIT         %0 top row %1 top col %2 bot row %3 bot col
-
-         </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 DEACTIVATED    %0 name
-         SESSION DISCONNECTED   %0 name %1 host %2 ip %3 port
-         SESSION TIMED OUT      %0 name %1 host %2 ip %3 port
-
          </span><span style='color:#5F5'>VARIABLE EVENTS</span><span style='color:#AAA'>
 
          VARIABLE UPDATE &lt;VAR&gt;  %0 name %1 new value %2 path
@@ -1271,7 +1275,6 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
                                          optional {{string}{width}} syntax
          #format {test} {%x}      {hex}  print corresponding charset character
          #format {test} {%A}     {char}  store corresponding character value
-         #format {test} {%C}   {number}  store number in chronological notation
          #format {test} {%D}      {hex}  convert hex to decimal in {test}
          #format {hash} {%H}   {string}  store a 64 bit string hash in {hash}
          #format {test} {%L}   {string}  store the string length in {test}
@@ -1340,7 +1343,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.06b                    </span><span style='color:#0AA'>#
+      #</span><span style='color:#AAA'>                    T I N T I N + +   2.02.11b                    </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'>#
@@ -1478,23 +1481,28 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
 
 </span><span style='color:#5F5'>         IF
 
-</span><span style='color:#FFF'>Command</span><span style='color:#AAA'>: #if </span><span style='color:#FFF'>{</span><span style='color:#AAA'>conditional</span><span style='color:#FFF'>} {</span><span style='color:#AAA'>commands if true</span><span style='color:#FFF'>} {</span><span style='color:#AAA'>commands if false</span><span style='color:#FFF'>}</span><span style='color:#AAA'>
+</span><span style='color:#FFF'>Command</span><span style='color:#AAA'>: #if </span><span style='color:#FFF'>{</span><span style='color:#AAA'>conditional</span><span style='color:#FFF'>} {</span><span style='color:#AAA'>commands if true</span><span style='color:#FFF'>}</span><span style='color:#AAA'>
 
-         The 'if' command is one of the most powerful commands added since
-         TINTIN III. It works similar to an 'if' statement in other languages,
-         and is strictly based on the way C handles its conditional statements.
-         When an 'if' command is encountered, the conditional statement is
+         The #if command is one of the most powerful commands added since
+         TINTIN III. It works similar to an if statement in other languages,
+         and is based on the way C handles its conditional statements.
+         When an #if command is encountered, the conditional statement is
          evaluated, and if TRUE (any non-zero result) the commands are executed.
 
-         The 'if' statement is only evaluated if it is read, so you must nest
-         the 'if' statement inside another statement (most likely an 'action'
+         The if statement is only evaluated if it is read, so you must nest
+         the if statement inside another statement (most likely an #action
          command). The conditional is evaluated exactly the same as in the
-         'math' command only instead of storing the result, the result is used
+         #math command, only instead of storing the result, the result is used
          to determine whether to execute the commands.
 
-</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #action {%0 gives you %1 gold coins.} {#if {%1&gt;5000} {thank %0}}
+         To handle the case where an if statement is false it can be followed
+         by the #else command.
+
+</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #action {%0 gives you %1 gold coins.} {#if {%1 &gt; 5000} {thank %0}}
          If someone gives you more than 5000 coins, thank them.
 
+</span><span style='color:#FFF'>Example</span><span style='color:#AAA'>: #alias {k} {#if {&quot;%0&quot; == &quot;&quot;} {kill &dollar;target};#else {kill %0}}
+
 </span><span style='color:#FFF'>Comment</span><span style='color:#AAA'>: See '#help math', for more information.
 
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#CASE'>case</a>, <a href='#DEFAULT'>default</a>, <a href='#ELSE'>else</a>, <a href='#ELSEIF'>elseif</a>, <a href='#SWITCH'>switch</a> and <a href='#REGEXP'>regexp</a>.
@@ -1515,11 +1523,9 @@ Related</span><span style='color:#AAA'>: <a href='#PORT'>port</a>
 
 </span><span style='color:#5F5'>         INDEX
 
-         INDEX
-</span><span style='color:#AAA'>
          On this page you'll find an introduction to using TinTin++. Additional
          information can be found in the individual help sections.
-</span><span style='color:#5F5'>
+
          Starting and Ending
 </span><span style='color:#AAA'>
          The syntax for starting TinTin++ is: ./tt++ [command file]
@@ -1787,12 +1793,16 @@ Command</span><span style='color:#AAA'>: #alias </span><span style='color:#FFF'>
 
          By providing the name of a list and the LIST option it shows all
          triggers/variables associated with that list. With the SAVE option
-         This data is written to the info variable.
+         this data is written to the info variable.
 
          #info 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 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.
-         #info session will show some session information.
+         #info session will show information on the session.
+         #info sessions will show information on all sessions.
          #info system will show some system information.
          #info unicode will show information on the provided character.
 
@@ -2345,7 +2355,8 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          </span><span style='color:#AAA'>  Sets displaying center of the map viewer, default is 0 0 0.
 
          </span><span style='color:#FFF'>#map color &lt;field&gt; [value]
-         </span><span style='color:#AAA'>  Sets the map color for the given color field.
+         </span><span style='color:#AAA'>  Sets the map color for the given color field. Use #map color reset
+           to restore colors to default.
 
          </span><span style='color:#FFF'>#map create &lt;size&gt;
          </span><span style='color:#AAA'>  Creates a new map and room 1. The default size is 50000 rooms.
@@ -2754,31 +2765,33 @@ Terminal -&gt; Window Settings -&gt; Emulation.
          ------------------------------------------------
          !               0            logical not
          ~               0            bitwise not
-         *               1            integer multiply
-         **              1            integer power
-         /               1            integer divide
-         //              1            integer sqrt // 2 or cbrt // 3
-         %               1            integer modulo
-         d               1            integer random dice roll
-         +               2            integer addition
-         -               2            integer subtraction
-         &lt;&lt;              3            bitwise shift
-         &gt;&gt;              3            bitwise shift
-         ..              3            bitwise ellipsis
-         &gt;               4            logical greater than
-         &gt;=              4            logical greater than or equal
-         &lt;               4            logical less than
-         &lt;=              4            logical less than or equal
-         ==              5            logical equal (can use regex)
-         ===             5            logical equal (never regex)
-         !=              5            logical not equal (can use regex)
-         !==             5            logical not equal (never regex)
-          &amp;              6            bitwise and
-          ^              7            bitwise xor
-          |              8            bitwise or
-         &amp;&amp;              9            logical and
-         ^^             10            logical xor
-         ||             11            logical or
+         d               1            integer random dice
+         *               2            integer multiply
+         **              2            integer power
+         /               2            integer divide
+         //              2            integer sqrt // 2 or cbrt // 3
+         %               2            integer modulo
+         +               3            integer addition
+         -               3            integer subtraction
+         &lt;&lt;              4            bitwise shift
+         &gt;&gt;              4            bitwise shift
+         ..              4            bitwise ellipsis
+         &gt;               5            logical greater than
+         &gt;=              5            logical greater than or equal
+         &lt;               5            logical less than
+         &lt;=              5            logical less than or equal
+         ==              6            logical equal (can use regex)
+         ===             6            logical equal (never regex)
+         !=              6            logical not equal (can use regex)
+         !==             6            logical not equal (never regex)
+          &amp;              7            bitwise and
+          ^              8            bitwise xor
+          |              9            bitwise or
+         &amp;&amp;             10            logical and
+         ^^             11            logical xor
+         ||             12            logical or
+         ?              13            logical ternary if (unfinished code)
+         :              14            logical ternary else 
 
          True is any non-zero number, and False is zero.  Parentheses () have
          highest precedence, so inside the () is always evaluated first.
@@ -2973,7 +2986,7 @@ Related</span><span style='color:#AAA'>: <a href='#EVENT'>event</a> and <a href=
 
          This would make you start a reply when clicking on a tell.
 
-Website: https://tintin.mudhalla.net/protocols/mslp
+</span><span style='color:#FFF'>Website</span><span style='color:#AAA'>: https://tintin.mudhalla.net/protocols/mslp
 
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#EVENT'>event</a> and <a href='#PORT'>port</a>.
 <a name='NOP'></a>
@@ -3321,7 +3334,7 @@ Example: #action {~&bsol;e[1;37m%1} {#var roomname %1}
          Prompt is a feature for split window mode, which will capture a line
          received from the server and display it on the status bar of your
          split screen terminal. You would define &lt;text&gt; and &lt;new text&gt; the
-         same way as with a substitution.
+         same way as you would with #substitute.
 
          The row number is optional and useful if you use a non standard split
          mode. A positive row number draws #row lines from the top while a
@@ -3332,8 +3345,8 @@ Example: #action {~&bsol;e[1;37m%1} {#var roomname %1}
          The col number is optional and can be used to set the column index.
          A positive col number draws the given number of columns from the left,
          while a negative col number draws from the right. If you leave the
-         column argument empty tintin will clear the row before printing at
-         the start of the row.
+         col number empty tintin will clear the row before printing at the
+         start of the row.
 
          The #show command takes a row and col argument as well so it's also
          possible to place text on your split lines using #show.
@@ -3389,19 +3402,22 @@ Example: #action {~&bsol;e[1;37m%1} {#var roomname %1}
 
          Of the following the (lazy) match is available at %1-%99 + 1
 
-      %a match zero to any number of characters including newlines.
-      %A match zero to any number of newlines.
       %d match zero to any number of digits.
       %D match zero to any number of non digits.
-      %p match zero to any number of printable characters.
-      %P match zero to any number of non printable characters.
       %s match zero to any number of spaces.
       %S match zero to any number of non spaces.
-      %u match zero to any number of unicode characters.
-      %U match zero to any number of non unicode characters.
       %w match zero to any number of word characters.
       %W match zero to any number of non word characters.
 
+      Experimental (subject to change) matches are:
+
+      %a match zero to any number of characters including newlines.
+      %A match zero to any number of newlines.
+      %p match zero to any number of printable characters.
+      %P match zero to any number of non printable characters.
+      %u match zero to any number of unicode characters.
+      %U match zero to any number of non unicode characters.
+
       If you want to match 1 digit use %+1d, if you want to match between 3
       and 5 spaces use %+3..5s, if you want to match 0 or more word
       characters use %+0..w, etc.
@@ -3423,7 +3439,7 @@ Example: #action {~&bsol;e[1;37m%1} {#var roomname %1}
 
 </span><span style='color:#FFF'>Comment</span><span style='color:#AAA'>: Like an alias or function #regex has its own scope.
 
-</span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#REPLACE'>replace</a>
+</span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#PCRE'>pcre</a> and <a href='#REPLACE'>replace</a>.
 <a name='REPEAT'></a>
 
 </span><span style='color:#5F5'>         REPEAT
@@ -3638,7 +3654,7 @@ easiest way to accomplish that.
          treats the generated echos as commands if no variable is provided.
 
          This is useful for running php, perl, ruby, and python scripts. You
-         can run these scrips either from file or from within tintin if the
+         can run these scripts either from file or from within tintin if the
          scripting language allows this.
 
          If you provide a variable the output of the script is stored as a list.
@@ -3747,7 +3763,7 @@ session one will remain the active session.
 
 </span><span style='color:#5F5'>         SNOOP
 
-</span><span style='color:#FFF'>Command</span><span style='color:#AAA'>: #snoop </span><span style='color:#FFF'>{</span><span style='color:#AAA'>session name</span><span style='color:#FFF'>} {</span><span style='color:#AAA'>on</span><span style='color:#FFF'>|</span><span style='color:#AAA'>off</span><span style='color:#FFF'>}</span><span style='color:#AAA'>
+</span><span style='color:#FFF'>Command</span><span style='color:#AAA'>: #snoop </span><span style='color:#FFF'>{</span><span style='color:#AAA'>session name</span><span style='color:#FFF'>} {</span><span style='color:#AAA'>on</span><span style='color:#FFF'>|</span><span style='color:#AAA'>off</span><span style='color:#FFF'>|</span><span style='color:#AAA'>scroll</span><span style='color:#FFF'>}</span><span style='color:#AAA'>
 
          If there are multiple sessions active, this command allows you to
          monitor what is going on in the sessions that are not currently active.
@@ -3756,6 +3772,11 @@ session one will remain the active session.
 
          You can toggle off snoop mode by executing #snoop a second time.
 
+         By using the scroll argument you will snoop the session's scroll
+         region which will overwrite the display of whichever session is active.
+         You can change the size and location of a session's scroll region by
+         using the #split and #screen scrollregion commands.
+
 </span><span style='color:#FFF'>Related</span><span style='color:#AAA'>: <a href='#ALL'>all</a>, <a href='#PORT'>port</a>, <a href='#RUN'>run</a>, <a href='#SESSION'>session</a>, <a href='#SESSIONNAME'>sessionname</a>, <a href='#SSL'>ssl</a> and <a href='#ZAP'>zap</a>.
 <a name='SPEEDWALK'></a>
 

+ 32 - 20
docs/syntax.txt

@@ -4,9 +4,10 @@ CHARACTERS
 
 #         The hashtag is the default character for starting a command and is
           subsequently known as the command character or tintin character.
+
           When loading a command file the command character is set to the
           first character in the file. The character can also be redefined
-          using #config.
+          using #config. It's adviced not to redefine the command character.
 
 ;         The semi-colon is used as the command separator and can be used to
           separate two commands. Multiple commands can be strung together as
@@ -18,8 +19,9 @@ CHARACTERS
           easily be escaped and must always be used in pairs.
 
 " "       Quote characters are used for strings in the #math, #if, #switch,
-          and #case commands. It is however suggested to use an extra
-          set of braces { } to define strings.
+          and #case commands. Instead of " " you can use an extra set of
+          braces { } to define strings, this is suggested if you are
+          expecting input to contain " characters.
 
 !         The exclamation sign is used to repeat commands, see #help history.
           The character can be redefined using #config.
@@ -29,7 +31,6 @@ CHARACTERS
           #config.
 
 
-
 SUBSTITUTIONS
 -------------
 
@@ -49,14 +50,20 @@ $         The dollar sign is used to retrieve the value of a variable.
           associative array. Associative arrays are also known as tables and
           maps. Regex can be used within brackets to match multiple variables.
 
-&0- &99   The ampersand sign followed by a number is used for arguments in the
-          regex command.
+          Variables are ordered alphanumerically.
+
++ -       The plus and minus sign is used to access variables by their index,
+          with the first variable having index 1, and the last variable
+          having index -1.
 
 %0- %99   The percent sign followed by a number is used for arguments by the
           following triggers:
 
           alias, action, button, delay, event, function, substitute, and tick.
 
+&0- &99   The ampersand sign followed by a number is used for arguments in the
+          regex and replace commands.
+
 <000>     Three alphanumeric characters encapsulated by the less- and greater-
           than signs are used for 4 and 8 bit color codes.
 
@@ -73,8 +80,10 @@ $         The dollar sign is used to retrieve the value of a variable.
 \         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, or being
-          displayed on the screen.
+          displayed on the screen. Escapes try to mimic escapes in PCRE when
+          possible.
 
+\a        07  bell character.
 \t        08  horizontal tab character.
 \n        10  line feed character.
 \v        11  vertical tab character.
@@ -83,42 +92,49 @@ $         The dollar sign is used to retrieve the value of a variable.
 \c            6 bit control character, \ca for ctrl-a.
 \x            8 bit character using 2 hexadecimal numbers.
 \u            16 bit unicode character, \uFFFD for example.
+\U            21 bit unicode character, \U02AF21 for example.
 
           All variables and functions can be escaped by doubling the sign,
           like $$variable_name or @@function_name. To escape a variable
           twice use $$$var_name. One escape is removed each time tintin
           needs to substitute a variable or function.
 
+          All trigger arguments can be escaped by double the ampersand,
+          like %%1. One escape is removed each time tintin substitutes
+          trigger arguments.
+
           All command arguments can be escaped by doubling the ampersand,
           like &&1. One escape is removed each time tintin substitutes
           command arguments.
 
-          All trigger arguments can be escaped by double the ampersand,
-          like %%1. One escape is removed each time tintin substitutes
-          trigger arguments.
 
 COORDINATES
 -----------
 
           When the 0,0 coordinate is in the upper left corner TinTin++ uses
           a y,x / rows,cols notation, starting at 1,1. Subsequently -1,-1
-          will indicate the bottom right corner.
+          will indicate the bottom right corner. This type of argument is
+          used by the #showme command.
 
           When the 0,0 coordinate is in the bottom left corner tintin uses
-          a standard x,y notation.
+          a standard x,y notation. This type of argument is used by the
+          #map jump command.
 
 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.
+          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, 
 
 PANES
 -----
           A panes 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.
+          size is the maximum size, minus the value. This type of argument
+          is used by the #split command.
 
 MATH
 ----
@@ -129,7 +145,7 @@ MATH
           *               1            integer multiply
           **              1            integer power
           /               1            integer divide
-          //              1            integer sqrt // 2 or cbrt // 3
+          //              1            integer root
           %               1            integer modulo
           d               1            integer random dice roll
           +               2            integer addition
@@ -151,11 +167,7 @@ MATH
           ^^             10            logical xor
           ||             11            logical or
 
-:         The colon can be used in #math to indicate a time value.
-1:30      This means 1 minute and 30 seconds and equals 90 seconds.
-2:1:30    This means 2 hours, 1 minute and 30 seconds.
-6:2:1:30  This means 6 days, 2 hours, 1 minute and 30 seconds.
-          #math time 6:2:1:30 equals 525690
+? :       The ? : symbols can be used for simple ternary operations.
 
 M,K,m,u   These four metric suffixes are allowed for numbers.
 

+ 43 - 1
mods/igr.mods

@@ -1,4 +1,46 @@
-Nov 2020        2.02.06
+Mar 2021        2.02.11
+------------------------------------------------------------------------------
+draw.c          #draw BALIGN will push text to the bottom if there aren't
+                enough lines to fill the entire square.
+
+main.c          Synchronized terminal resize handling which might fix some odd
+                crashes.
+
+variable.c      Getting rid of #format %C for chronological formatting since
+                I'm abandoning chronological notation support. While it was
+                a cute feature it's more important to support the ? : ternary
+                operation. This frees up the %C argument for a more complex
+                column operation.
+
+mapper.c        Added support to use #map list 1..6 to provide a vnum range.
+
+substitute.c    Added \u{} which supports between 2 and 6 hexadecimals.
+
+data.c          Added the #info input and #info input save option.
+
+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]
+
+main.c          Added SIGHUB event which should trigger when the terminal is
+                closed with TinTin++ still running inside. You can use
+                #event {SIGHUB} {#end} to have tintin close normally.
+
+session.c       Added #snoop {session} scroll {on|off} to snoop a session's
+                scroll region, which will be printed on top of the active
+                session's screen.
+
+math.c          Made #math / #if about 3x faster.
+
+math.c          Updated // to support powers other than 2 and 3.
+
+regex.c         Changed %w to match any letter, number, or undescore instead
+                of just letters, to make it match \w. Same for %W.
+
+Nov 2020        2.02.10
 
 ------------------------------------------------------------------------------
 

+ 7 - 1
src/buffer.c

@@ -160,6 +160,12 @@ void add_line_buffer(struct session *ses, char *line, int prompt)
 
 	push_call("add_line_buffer(%p,%s,%d)",ses,line,prompt);
 
+	if (HAS_BIT(ses->flags, SES_FLAG_SNOOPSCROLL))
+	{
+		SET_BIT(gtd->flags, TINTIN_FLAG_SESSIONUPDATE);
+		SET_BIT(ses->flags, SES_FLAG_PRINTLINE);
+	}
+
 	if (gtd->level->scroll)
 	{
 		pop_call();
@@ -438,7 +444,7 @@ int show_buffer(struct session *ses)
 {
 	int scroll_size, scroll_cnt, scroll_tmp, scroll_add, scroll_cut, start, end, row;
 
-	if (ses != gtd->ses)
+	if (ses != gtd->ses && !HAS_BIT(ses->flags, SES_FLAG_SNOOPSCROLL))
 	{
 		return TRUE;
 	}

+ 9 - 2
src/cursor.c

@@ -908,8 +908,15 @@ DO_CURSOR(cursor_delete)
 		}
 	}
 
-	cursor_redraw_line(ses, "");
-
+	if (gtd->ses->input->raw_len == gtd->ses->input->raw_pos)
+	{
+		input_printf("\e[1X");
+		cursor_check_line(ses, arg);
+	}
+	else
+	{
+		cursor_redraw_line(ses, "");
+	}
 	modified_input();
 }
 

+ 263 - 199
src/data.c

@@ -422,10 +422,6 @@ struct listnode *search_node_list(struct listroot *root, char *text)
 			index = bsearch_alnum_list(root, text, 0);
 			break;
 
-		case SORT_DELAY:
-			index = bsearch_priority_list(root, text, text, 0);
-			break;
-
 		default:
 			index = nsearch_list(root, text);
 			break;
@@ -452,9 +448,6 @@ int search_index_list(struct listroot *root, char *text, char *priority)
 		case SORT_ALNUM:
 			return bsearch_alnum_list(root, text, 0);
 
-		case SORT_DELAY:
-			return bsearch_priority_list(root, text, text, 0);
-
 		case SORT_PRIORITY:
 			if (priority)
 			{
@@ -483,9 +476,6 @@ int locate_index_list(struct listroot *root, char *text, char *priority)
 		case SORT_ALNUM:
 			return bsearch_alnum_list(root, text, SEEK_REPLACE);
 
-		case SORT_DELAY:
-			return bsearch_priority_list(root, text, text, SEEK_REPLACE);
-
 		case SORT_PRIORITY:
 			return bsearch_priority_list(root, text, priority, SEEK_REPLACE);
 
@@ -524,7 +514,7 @@ int bsearch_alpha_list(struct listroot *root, char *text, int seek)
 
 	if (seek)
 	{
-		return bot += strcmp(text, root->list[bot]->arg1) > 0;
+		return bot + (strcmp(text, root->list[bot]->arg1) > 0);
 	}
 
 	return -1;
@@ -826,10 +816,6 @@ int show_node_with_wild(struct session *ses, char *text, struct listroot *root)
 			index = bsearch_alnum_list(root, text, 0);
 			break;
 
-		case SORT_DELAY:
-			index = bsearch_priority_list(root, text, text, 0);
-			break;
-
 		default:
 			index = nsearch_list(root, text);
 			break;
@@ -936,10 +922,6 @@ int delete_node_with_wild(struct session *ses, int type, char *text)
 			index = bsearch_alnum_list(root, arg1, 0);
 			break;
 
-		case SORT_DELAY:
-			index = bsearch_priority_list(root, arg1, arg1, 0);
-			break;
-
 		default:
 			index = nsearch_list(root, arg1);
 			break;
@@ -1258,14 +1240,14 @@ DO_COMMAND(do_info)
 		{
 			if (!HAS_BIT(ses->list[index]->flags, LIST_FLAG_HIDE))
 			{
-				tintin_printf2(ses, "%-15s %5d   IGNORE %3s   MESSAGE %3s   INFO %3s   DEBUG %3s %3s",
+				tintin_printf2(ses, "%-15s %5d   IGNORE %3s   MESSAGE %3s   INFO %3s   DEBUG %3s%s",
 					list_table[index].name_multi,
 					ses->list[index]->used,
 					HAS_BIT(ses->list[index]->flags, LIST_FLAG_IGNORE)  ?  "ON" : "OFF",
 					HAS_BIT(ses->list[index]->flags, LIST_FLAG_MESSAGE) ?  "ON" : "OFF",
 					HAS_BIT(ses->list[index]->flags, LIST_FLAG_INFO)    ?  "ON" : "OFF",
 					HAS_BIT(ses->list[index]->flags, LIST_FLAG_DEBUG)   ?  "ON" : "OFF",
-					HAS_BIT(ses->list[index]->flags, LIST_FLAG_LOG)     ? "LOG" : "   ");
+					HAS_BIT(ses->list[index]->flags, LIST_FLAG_LOG)     ? " LOG" : "");
 			}
 		}
 		tintin_header(ses, 80, "");
@@ -1329,240 +1311,322 @@ DO_COMMAND(do_info)
 			}
 			show_message(ses, LIST_COMMAND, "#OK: #INFO STATUS FOR %s HAS BEEN SET TO: %s.", list_table[index].name_multi, HAS_BIT(ses->list[index]->flags, LIST_FLAG_INFO) ? "ON" : "OFF");
 
+			if (strcasecmp(arg1, "ALL"))
+			{
+				return ses;
+			}
 			found = TRUE;
 		}
 
-		if (found == FALSE)
+		if (found)
 		{
-			if (is_abbrev(arg1, "CPU"))
-			{
-				show_cpu(ses);
-			}
-			else if (is_abbrev(arg1, "MCCP"))
-			{
-				if (ses->mccp2)
-				{
-					tintin_printf2(ses, "#INFO MCCP2: TOTAL IN: %9u TOTAL OUT: %9u PERCENT: %3d", ses->mccp2->total_in, ses->mccp2->total_out, ses->mccp2->total_out ? 100 * ses->mccp2->total_in / ses->mccp2->total_out : 0);
-				}
-				if (ses->mccp3)
-				{
-					tintin_printf2(ses, "#INFO MCCP3: TOTAL IN: %9u TOTAL OUT: %9u PERCENT: %3d", ses->mccp3->total_in, ses->mccp3->total_out, ses->mccp3->total_in ? 100 * ses->mccp3->total_out / ses->mccp3->total_in : 0);
-				}
-			}
-			else if (is_abbrev(arg1, "MEMORY"))
-			{
-				struct str_data *str_ptr;
-
-				long long quan, used, max;
+			return ses;
+		}
 
-				max  = 0;
-				quan = 0;
-				used = 0;
+		*gtd->is_result = 0;
 
-				for (index = 0 ; index < gtd->memory->stack_cap ; index++)
+		switch (*arg1 % 32)
+		{
+			case CTRL_C:
+				if (is_abbrev(arg1, "CPU"))
 				{
-					max++;
-					quan += gtd->memory->stack[index]->max;
-					used += gtd->memory->stack[index]->len;
+					show_cpu(ses);
 				}
+				break;
 
-				tintin_printf2(ses, "#INFO MEMORY: STACK SIZE: %d", quan);
+			case CTRL_E:
+				if (is_abbrev(arg1, "ENVIRON"))
+				{
+					char **env, *sep;
 
-				tintin_printf2(ses, "#INFO MEMORY: STACK  MAX: %d", gtd->memory->stack_max);
-				tintin_printf2(ses, "#INFO MEMORY: STACK  CAP: %d", gtd->memory->stack_cap);
-				tintin_printf2(ses, "#INFO MEMORY: STACK  LEN: %d", gtd->memory->stack_len);
+					if (is_abbrev(arg2, "SAVE"))
+					{
+						set_nest_node_ses(ses, "info[ENVIRON]", "");
 
-				tintin_printf2(ses, "");
+						for (env = environ ; *env ; env++)
+						{
+							sep = strchr(*env, '=');
 
-				max  = 0;
-				quan = 0;
-				used = 0;
+							*sep = 0;
 
-				for (index = 0 ; index < gtd->memory->list_len ; index++)
-				{
-					str_ptr = gtd->memory->list[index];
+							add_nest_node_ses(ses, "info[ENVIRON]", "{%s}{%s}", *env, sep + 1);
 
-					if (str_ptr->max != NAME_SIZE + 1 && strlen(get_str_str(str_ptr)) != str_ptr->len)
+							*sep = '=';
+						}
+					}
+					else
 					{
-						tintin_printf2(ses, "#ERROR: index %d len = %d/%d max = %d flags = %d (%s)", index, strlen(get_str_str(str_ptr)), str_ptr->len, str_ptr->max, str_ptr->flags, get_str_str(str_ptr));
+						for (env = environ ; *env ; env++)
+						{
+							tintin_printf2(ses, "%s", *env);
+						}
 					}
+				}
+				break;
 
-					if (!HAS_BIT(str_ptr->flags, STR_FLAG_FREE))
+			case CTRL_I:
+				if (is_abbrev(arg1, "INPUT"))
+				{
+					if (is_abbrev(arg2, "SAVE"))
 					{
-						max++;
-						quan += str_ptr->max;
-						used += str_ptr->len;
+						set_nest_node_ses(ses, "info[INPUT]", "{BUFFER}{%s}", gtd->ses->input->buf);
+						add_nest_node_ses(ses, "info[INPUT]", "{CUT}{%s}", gtd->ses->input->cut);
+						add_nest_node_ses(ses, "info[INPUT]", "{COL}{%d}", inputline_cur_col());
+						add_nest_node_ses(ses, "info[INPUT]", "{HEIGHT}{%d}", inputline_max_row());
+						add_nest_node_ses(ses, "info[INPUT]", "{LENGTH}{%d}", inputline_cur_str_len());
+						add_nest_node_ses(ses, "info[INPUT]", "{NAME}{%s}", gtd->ses->input->line_name);
+						add_nest_node_ses(ses, "info[INPUT]", "{OFFSET}{%d}", inputline_cur_off());
+						add_nest_node_ses(ses, "info[INPUT]", "{ROW}{%d}", inputline_cur_row());
+						add_nest_node_ses(ses, "info[INPUT]", "{WIDTH}{%d}", inputline_max_str_len());
+					}
+					else
+					{
+						tintin_printf2(ses, "#INFO INPUT: BUFFER: %s", gtd->ses->input->buf);
+						tintin_printf2(ses, "#INFO INPUT: CUT: %s", gtd->ses->input->cut);
+						tintin_printf2(ses, "#INFO INPUT: COL: %d", inputline_cur_col());
+						tintin_printf2(ses, "#INFO INPUT: HEIGHT: %d", inputline_max_row());
+						tintin_printf2(ses, "#INFO INPUT: LENGTH: %d", inputline_cur_str_len());
+						tintin_printf2(ses, "#INFO INPUT: NAME: %s", gtd->ses->input->line_name);
+						tintin_printf2(ses, "#INFO INPUT: OFFSET: %d", inputline_cur_off());
+						tintin_printf2(ses, "#INFO INPUT: ROW: %d", inputline_cur_row());
+						tintin_printf2(ses, "#INFO INPUT: WIDTH: %d", inputline_max_str_len());
 					}
 				}
+				break;
 
-				tintin_printf2(ses, "#INFO MEMORY: ALLOC SIZE: %d", quan);
-				tintin_printf2(ses, "#INFO MEMORY: ALLOC USED: %d", used);
-
-				tintin_printf2(ses, "#INFO MEMORY: ALLOC  MAX: %d", gtd->memory->list_max);
-				tintin_printf2(ses, "#INFO MEMORY: ALLOC  LEN: %d", gtd->memory->list_len);
-
-				tintin_printf2(ses, "");
+			case CTRL_M:
+				if (is_abbrev(arg1, "MCCP"))
+				{
+					if (ses->mccp2)
+					{
+						tintin_printf2(ses, "#INFO MCCP2: TOTAL IN: %9u TOTAL OUT: %9u PERCENT: %3d", ses->mccp2->total_in, ses->mccp2->total_out, ses->mccp2->total_out ? 100 * ses->mccp2->total_in / ses->mccp2->total_out : 0);
+					}
+					if (ses->mccp3)
+					{
+						tintin_printf2(ses, "#INFO MCCP3: TOTAL IN: %9u TOTAL OUT: %9u PERCENT: %3d", ses->mccp3->total_in, ses->mccp3->total_out, ses->mccp3->total_in ? 100 * ses->mccp3->total_out / ses->mccp3->total_in : 0);
+					}
+				}
+				else if (is_abbrev(arg1, "MEMORY"))
+				{
+					struct str_data *str_ptr;
 
-				quan = 0;
-				used = 0;
+					long long quan, used, max;
 
-				for (index = 0 ; index < gtd->memory->free_len ; index++)
-				{
-					str_ptr = gtd->memory->list[gtd->memory->free[index]];
+					max  = 0;
+					quan = 0;
+					used = 0;
 
-					if (HAS_BIT(str_ptr->flags, STR_FLAG_FREE))
+					for (index = 0 ; index < gtd->memory->stack_cap ; index++)
 					{
-						quan += str_ptr->max;
-						used += str_ptr->len;
+						max++;
+						quan += gtd->memory->stack[index]->max;
+						used += gtd->memory->stack[index]->len;
 					}
-					else
+
+					tintin_printf2(ses, "#INFO MEMORY: STACK SIZE: %d", quan);
+
+					tintin_printf2(ses, "#INFO MEMORY: STACK  MAX: %d", gtd->memory->stack_max);
+					tintin_printf2(ses, "#INFO MEMORY: STACK  CAP: %d", gtd->memory->stack_cap);
+					tintin_printf2(ses, "#INFO MEMORY: STACK  LEN: %d", gtd->memory->stack_len);
+
+					tintin_printf2(ses, "");
+
+					max  = 0;
+					quan = 0;
+					used = 0;
+
+					for (index = 0 ; index < gtd->memory->list_len ; index++)
 					{
-						tintin_printf2(ses, "error: found freed memory not marked as free.");
+						str_ptr = gtd->memory->list[index];
+
+						if (str_ptr->max != NAME_SIZE + 1 && strlen(get_str_str(str_ptr)) != str_ptr->len)
+						{
+							tintin_printf2(ses, "#ERROR: index %d len = %d/%d max = %d flags = %d (%s)", index, strlen(get_str_str(str_ptr)), str_ptr->len, str_ptr->max, str_ptr->flags, get_str_str(str_ptr));
+						}
+
+						if (!HAS_BIT(str_ptr->flags, STR_FLAG_FREE))
+						{
+							max++;
+							quan += str_ptr->max;
+							used += str_ptr->len;
+						}
 					}
-				}
 
-				tintin_printf2(ses, "#INFO MEMORY: FREED SIZE: %d", quan);
+					tintin_printf2(ses, "#INFO MEMORY: ALLOC SIZE: %d", quan);
+					tintin_printf2(ses, "#INFO MEMORY: ALLOC USED: %d", used);
 
-				tintin_printf2(ses, "#INFO MEMORY: FREED  MAX: %d", gtd->memory->free_max);
-				tintin_printf2(ses, "#INFO MEMORY: FREED  LEN: %d", gtd->memory->free_len);
+					tintin_printf2(ses, "#INFO MEMORY: ALLOC  MAX: %d", gtd->memory->list_max);
+					tintin_printf2(ses, "#INFO MEMORY: ALLOC  LEN: %d", gtd->memory->list_len);
 
-				tintin_printf2(ses, "");
+					tintin_printf2(ses, "");
 
-				quan = 0;
-				used = 0;
+					quan = 0;
+					used = 0;
 
-				for (index = 0 ; index < gtd->memory->debug_len ; index++)
-				{
-					quan += NAME_SIZE;
-				}
+					for (index = 0 ; index < gtd->memory->free_len ; index++)
+					{
+						str_ptr = gtd->memory->list[gtd->memory->free[index]];
+
+						if (HAS_BIT(str_ptr->flags, STR_FLAG_FREE))
+						{
+							quan += str_ptr->max;
+							used += str_ptr->len;
+						}
+						else
+						{
+							tintin_printf2(ses, "error: found freed memory not marked as free.");
+						}
+					}
 
-				tintin_printf2(ses, "#INFO MEMORY: DEBUG SIZE: %d", quan);
+					tintin_printf2(ses, "#INFO MEMORY: FREED SIZE: %d", quan);
 
-				tintin_printf2(ses, "#INFO MEMORY: DEBUG  MAX: %d", gtd->memory->debug_max);
-				tintin_printf2(ses, "#INFO MEMORY: DEBUG  LEN: %d", gtd->memory->debug_len);
-			}
-			else if (is_abbrev(arg1, "SESSION"))
-			{
-				if (is_abbrev(arg2, "SAVE"))
-				{
-					sprintf(name, "info[SESSION]");
+					tintin_printf2(ses, "#INFO MEMORY: FREED  MAX: %d", gtd->memory->free_max);
+					tintin_printf2(ses, "#INFO MEMORY: FREED  LEN: %d", gtd->memory->free_len);
 
-					set_nest_node_ses(ses, name, "{NAME}{%s}", ses->name);
-					add_nest_node_ses(ses, name, "{ACTIVE}{%d}", gtd->ses == ses);
-					add_nest_node_ses(ses, name, "{CLASS}{%s}", ses->group);
-					add_nest_node_ses(ses, name, "{CREATED}{%d}", ses->created);
-					add_nest_node_ses(ses, name, "{HOST} {%s}", ses->session_host);
-					add_nest_node_ses(ses, name, "{IP} {%s}", ses->session_ip);
-					add_nest_node_ses(ses, name, "{PORT} {%s}", ses->session_port);
+					tintin_printf2(ses, "");
 
-					show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[SESSION]}");
-				}
-				else
-				{
-					tintin_printf2(ses, "{NAME}{%s}", ses->name);
-					tintin_printf2(ses, "{ACTIVE}{%d}", gtd->ses == ses);
-					tintin_printf2(ses, "{CLASS}{%s}", ses->group);
-					tintin_printf2(ses, "{CREATED}{%d}", ses->created);
-					tintin_printf2(ses, "{HOST} {%s}", ses->session_host);
-					tintin_printf2(ses, "{IP} {%s}", ses->session_ip);
-					tintin_printf2(ses, "{PORT} {%s}", ses->session_port);
+					quan = 0;
+					used = 0;
+
+					for (index = 0 ; index < gtd->memory->debug_len ; index++)
+					{
+						quan += NAME_SIZE;
+					}
+
+					tintin_printf2(ses, "#INFO MEMORY: DEBUG SIZE: %d", quan);
+
+					tintin_printf2(ses, "#INFO MEMORY: DEBUG  MAX: %d", gtd->memory->debug_max);
+					tintin_printf2(ses, "#INFO MEMORY: DEBUG  LEN: %d", gtd->memory->debug_len);
 				}
-			}
-			else if (is_abbrev(arg1, "SESSIONS"))
-			{
-				struct session *sesptr;
+				break;
 
-				if (is_abbrev(arg2, "SAVE"))
+			case CTRL_S:
+				if (is_abbrev(arg1, "SESSION"))
 				{
-					set_nest_node_ses(ses, "info[SESSIONS]", "");
-
-					for (sesptr = gts ; sesptr ; sesptr = sesptr->next)
+					if (is_abbrev(arg2, "SAVE"))
+					{
+						set_nest_node_ses(ses, "info[SESSION]", "{NAME}{%s}", ses->name);
+						add_nest_node_ses(ses, "info[SESSION]", "{ACTIVE}{%d}", gtd->ses == ses);
+						add_nest_node_ses(ses, "info[SESSION]", "{CLASS}{%s}", ses->group);
+						add_nest_node_ses(ses, "info[SESSION]", "{CREATED}{%d}", ses->created);
+						add_nest_node_ses(ses, "info[SESSION]", "{HOST} {%s}", ses->session_host);
+						add_nest_node_ses(ses, "info[SESSION]", "{IP} {%s}", ses->session_ip);
+						add_nest_node_ses(ses, "info[SESSION]", "{PORT} {%s}", ses->session_port);
+
+						show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[SESSION]}");
+					}
+					else
 					{
-						sprintf(name, "info[SESSIONS][%s]", sesptr->name);
-
-						add_nest_node_ses(ses, name, "{NAME}{%s}", sesptr->name);
-						add_nest_node_ses(ses, name, "{ACTIVE}{%d}", gtd->ses == sesptr);
-						add_nest_node_ses(ses, name, "{CLASS}{%s}", sesptr->group);
-						add_nest_node_ses(ses, name, "{CREATED}{%d}", sesptr->created);
-						add_nest_node_ses(ses, name, "{HOST} {%s}", sesptr->session_host);
-						add_nest_node_ses(ses, name, "{IP} {%s}", sesptr->session_ip);
-						add_nest_node_ses(ses, name, "{PORT} {%s}", sesptr->session_port);
+						tintin_printf2(ses, "{NAME}{%s}", ses->name);
+						tintin_printf2(ses, "{ACTIVE}{%d}", gtd->ses == ses);
+						tintin_printf2(ses, "{CLASS}{%s}", ses->group);
+						tintin_printf2(ses, "{CREATED}{%d}", ses->created);
+						tintin_printf2(ses, "{HOST} {%s}", ses->session_host);
+						tintin_printf2(ses, "{IP} {%s}", ses->session_ip);
+						tintin_printf2(ses, "{PORT} {%s}", ses->session_port);
 					}
-					show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[SESSION]}");
 				}
-				else
+				else if (is_abbrev(arg1, "SESSIONS"))
 				{
-					for (sesptr = gts ; sesptr ; sesptr = sesptr->next)
+					struct session *sesptr;
+
+					if (is_abbrev(arg2, "SAVE"))
 					{
-						tintin_printf2(ses, "{%s}{NAME}{%s}", sesptr->name, sesptr->name);
-						tintin_printf2(ses, "{%s}{ACTIVE}{%d}", sesptr->name, gtd->ses == sesptr);
-						tintin_printf2(ses, "{%s}{CLASS}{%s}", sesptr->name, sesptr->group);
-						tintin_printf2(ses, "{%s}{CREATED}{%d}", sesptr->name, sesptr->created);
-						tintin_printf2(ses, "{%s}{HOST} {%s}", sesptr->name, sesptr->session_host);
-						tintin_printf2(ses, "{%s}{IP} {%s}", sesptr->name, sesptr->session_ip);
-						tintin_printf2(ses, "{%s}{PORT} {%s}", sesptr->name, sesptr->session_port);
+						set_nest_node_ses(ses, "info[SESSIONS]", "");
+
+						for (sesptr = gts ; sesptr ; sesptr = sesptr->next)
+						{
+							sprintf(name, "info[SESSIONS][%s]", sesptr->name);
+
+							add_nest_node_ses(ses, name, "{NAME}{%s}", sesptr->name);
+							add_nest_node_ses(ses, name, "{ACTIVE}{%d}", gtd->ses == sesptr);
+							add_nest_node_ses(ses, name, "{CLASS}{%s}", sesptr->group);
+							add_nest_node_ses(ses, name, "{CREATED}{%d}", sesptr->created);
+							add_nest_node_ses(ses, name, "{HOST} {%s}", sesptr->session_host);
+							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]}");
+					}
+					else
+					{
+						for (sesptr = gts ; sesptr ; sesptr = sesptr->next)
+						{
+							tintin_printf2(ses, "{%s}{NAME}{%s}", sesptr->name, sesptr->name);
+							tintin_printf2(ses, "{%s}{ACTIVE}{%d}", sesptr->name, gtd->ses == sesptr);
+							tintin_printf2(ses, "{%s}{CLASS}{%s}", sesptr->name, sesptr->group);
+							tintin_printf2(ses, "{%s}{CREATED}{%d}", sesptr->name, sesptr->created);
+							tintin_printf2(ses, "{%s}{HOST} {%s}", sesptr->name, sesptr->session_host);
+							tintin_printf2(ses, "{%s}{IP} {%s}", sesptr->name, sesptr->session_ip);
+							tintin_printf2(ses, "{%s}{PORT} {%s}", sesptr->name, sesptr->session_port);
+						}
 					}
 				}
-			}
-			else if (is_abbrev(arg1, "STACK"))
-			{
-				dump_stack();
-			}
-			else if (is_abbrev(arg1, "SYSTEM"))
-			{
-				char cwd[PATH_MAX];
-
-				if (getcwd(cwd, PATH_MAX) == NULL)
+				else if (is_abbrev(arg1, "STACK"))
 				{
-					syserr_printf(ses, "do_info: getcwd:");
+					dump_stack();
 				}
-
-				if (is_abbrev(arg2, "SAVE"))
+				else if (is_abbrev(arg1, "SYSTEM"))
 				{
-					sprintf(name, "info[SYSTEM]");
+					char cwd[PATH_MAX];
+
+					if (getcwd(cwd, PATH_MAX) == NULL)
+					{
+						syserr_printf(ses, "do_info: getcwd:");
+
+						cwd[0] = 0;
+					}
+
+					if (is_abbrev(arg2, "SAVE"))
+					{
+						sprintf(name, "info[SYSTEM]");
 
-					set_nest_node_ses(ses, name, "{CLIENT_NAME}{%s}{CLIENT_VERSION}{%s}", CLIENT_NAME, CLIENT_VERSION);
-//					add_nest_node_ses(ses, name, "{CLIENT}{{NAME}{%s}{VERSION}{%s}}", CLIENT_NAME, CLIENT_VERSION);
-					add_nest_node_ses(ses, name, "{CWD}{%s}{EXEC}{%s}{HOME}{%s}{LANG}{%s}{OS}{%s}{TERM}{%s}{TINTIN}{%s}", cwd, gtd->system->exec, gtd->system->home, gtd->system->lang, gtd->system->os, gtd->system->term, gtd->system->tt_dir);
-					add_nest_node_ses(ses, name, "{DETACH_FILE}{%s}{ATTACH_FILE}{%s}", gtd->detach_port > 0 ? gtd->detach_file : "", gtd->attach_sock > 0 ? gtd->attach_file : "");
+						set_nest_node_ses(ses, name, "{CLIENT_NAME}{%s}{CLIENT_VERSION}{%s}", CLIENT_NAME, CLIENT_VERSION);
+						add_nest_node_ses(ses, name, "{CWD}{%s}{EXEC}{%s}{HOME}{%s}{LANG}{%s}{OS}{%s}{PID}{%d}{TERM}{%s}{TINTIN}{%s}", cwd, gtd->system->exec, gtd->system->home, gtd->system->lang, gtd->system->os, getpid(), gtd->system->term, gtd->system->tt_dir);
+						add_nest_node_ses(ses, name, "{DETACH_FILE}{%s}{ATTACH_FILE}{%s}", gtd->detach_port > 0 ? gtd->detach_file : "", gtd->attach_sock > 0 ? gtd->attach_file : "");
 
-					show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[SYSTEM]}");
+						show_message(ses, LIST_COMMAND, "#INFO: DATA WRITTEN TO {info[SYSTEM]}");
+					}
+					else
+					{
+						tintin_printf2(ses, "#INFO SYSTEM: CLIENT_NAME    = %s", CLIENT_NAME);
+						tintin_printf2(ses, "#INFO SYSTEM: CLIENT_VERSION = %s", CLIENT_VERSION);
+						tintin_printf2(ses, "#INFO SYSTEM: CWD            = %s", cwd);
+						tintin_printf2(ses, "#INFO SYSTEM: EXEC           = %s", gtd->system->exec);
+						tintin_printf2(ses, "#INFO SYSTEM: HOME           = %s", gtd->system->home);
+						tintin_printf2(ses, "#INFO SYSTEM: LANG           = %s", gtd->system->lang);
+						tintin_printf2(ses, "#INFO SYSTEM: OS             = %s", gtd->system->os);
+						tintin_printf2(ses, "#INFO SYSTEM: PID            = %d", getpid());
+						tintin_printf2(ses, "#INFO SYSTEM: TERM           = %s", gtd->system->term);
+						tintin_printf2(ses, "#INFO SYSTEM: TINTIN         = %s", gtd->system->tt_dir);
+						tintin_printf2(ses, "#INFO SYSTEM: DETACH_PORT    = %d", gtd->detach_port);
+						tintin_printf2(ses, "#INFO SYSTEM: DETACH_FILE    = %s", gtd->detach_port ? gtd->detach_file : "");
+						tintin_printf2(ses, "#INFO SYSTEM: ATTACH_SOCK    = %d", gtd->attach_sock);
+						tintin_printf2(ses, "#INFO SYSTEM: ATTACH_FILE    = %s", gtd->attach_sock ? gtd->attach_file : "");
+					}
 				}
-				else
+				break;
+
+			case CTRL_U:
+				if (is_abbrev(arg1, "UNICODE"))
 				{
-					tintin_printf2(ses, "#INFO SYSTEM: CLIENT_NAME    = %s", CLIENT_NAME);
-					tintin_printf2(ses, "#INFO SYSTEM: CLIENT_VERSION = %s", CLIENT_VERSION);
-					tintin_printf2(ses, "#INFO SYSTEM: CWD            = %s", cwd);
-					tintin_printf2(ses, "#INFO SYSTEM: EXEC           = %s", gtd->system->exec);
-					tintin_printf2(ses, "#INFO SYSTEM: HOME           = %s", gtd->system->home);
-					tintin_printf2(ses, "#INFO SYSTEM: LANG           = %s", gtd->system->lang);
-					tintin_printf2(ses, "#INFO SYSTEM: OS             = %s", gtd->system->os);
-					tintin_printf2(ses, "#INFO SYSTEM: TERM           = %s", gtd->system->term);
-					tintin_printf2(ses, "#INFO SYSTEM: TINTIN         = %s", gtd->system->tt_dir);
-					tintin_printf2(ses, "#INFO SYSTEM: DETACH_PORT    = %d", gtd->detach_port);
-					tintin_printf2(ses, "#INFO SYSTEM: DETACH_FILE    = %s", gtd->detach_port ? gtd->detach_file : "");
-					tintin_printf2(ses, "#INFO SYSTEM: ATTACH_SOCK    = %d", gtd->attach_sock);
-					tintin_printf2(ses, "#INFO SYSTEM: ATTACH_FILE    = %s", gtd->attach_sock ? gtd->attach_file : "");
-				}
-			}
-			else if (is_abbrev(arg1, "UNICODE"))
-			{
-				int size, width, index;
+					int size, width, index;
 
-				size = get_utf8_size(arg2);
-				get_utf8_width(arg2, &width);
-				get_utf8_index(arg2, &index);
+					size = get_utf8_size(arg2);
+					get_utf8_width(arg2, &width);
+					get_utf8_index(arg2, &index);
 
-				tintin_printf2(ses, "#INFO UNICODE: %s:  is_utf8_head  = %d (%s)", arg2, is_utf8_head(arg2), is_utf8_head(arg2) ? "true" : "false");
-				tintin_printf2(ses, "#INFO UNICODE: %s: get_utf8_size  = %d", arg2, size);
-				tintin_printf2(ses, "#INFO UNICODE: %s: get_utf8_width = %d", arg2, width);
-				tintin_printf2(ses, "#INFO UNICODE: %s: get_utf8_index = %d (decimal)", arg2, index);
-				tintin_printf2(ses, "#INFO UNICODE: %s: get_utf8_index = %x (hexadecimal)", arg2, index);
-			}
-			else
-			{
-				show_error(ses, LIST_COMMAND, "#INFO {%s} - NO MATCH FOUND.", arg1);
-			}
+					tintin_printf2(ses, "#INFO UNICODE: %s:  is_utf8_head  = %d (%s)", arg2, is_utf8_head(arg2), is_utf8_head(arg2) ? "true" : "false");
+					tintin_printf2(ses, "#INFO UNICODE: %s: get_utf8_size  = %d", arg2, size);
+					tintin_printf2(ses, "#INFO UNICODE: %s: get_utf8_width = %d", arg2, width);
+					tintin_printf2(ses, "#INFO UNICODE: %s: get_utf8_index = %d (decimal)", arg2, index);
+					tintin_printf2(ses, "#INFO UNICODE: %s: get_utf8_index = %x (hexadecimal)", arg2, index);
+				}
+				break;
+		}
+		if (*gtd->is_result == 0)
+		{
+			show_error(ses, LIST_COMMAND, "#INFO {%s} - NO MATCH FOUND.", arg1);
 		}
 	}
 	return ses;

+ 10 - 7
src/dict.c

@@ -29,8 +29,8 @@
 
 struct dictionary_data
 {
-	unsigned int  * wordindex[26];
-	int             listsize[26];
+	unsigned int * wordindex[26];
+	unsigned int   listsize[26];
 };
 
 struct dictionary_data *dictionary;
@@ -83,11 +83,12 @@ void dictionary_init()
 		}
 		while (*pta);
 	}
-
-//	for (hash = 0 ; hash < 26 ; hash++)
-//	{
-//		printf("hash %2d = %d\n", hash, dictionary->listsize[hash]);
-//	}
+/*
+	for (hash = 0 ; hash < 26 ; hash++)
+	{
+		printf("hash %c = %d\n", 'A' + hash, dictionary->listsize[hash]);
+	}
+*/
 }
 
 int dictionary_search(int hash, char *key)
@@ -190,6 +191,8 @@ DO_COMMAND(do_dictionary)
 	{
 		show_message(ses, LIST_COMMAND, "#SYNTAX: #DICTIONARY {WORD}");
 
+		wordlist[0][0] = 0;
+
 		return ses;
 	}
 

+ 22 - 5
src/draw.c

@@ -70,6 +70,7 @@ static int  draw_cnt;
 #define DRAW_FLAG_RALIGN              BV38
 #define DRAW_FLAG_TALIGN              BV39
 #define DRAW_FLAG_UALIGN              BV40
+#define DRAW_FLAG_BALIGN              BV41
 
 #define DRAW_FLAG_APPENDIX            DRAW_FLAG_CIRCLED|DRAW_FLAG_CORNERED|DRAW_FLAG_CROSSED|DRAW_FLAG_JEWELED|DRAW_FLAG_PRUNED|DRAW_FLAG_ROUNDED|DRAW_FLAG_TEED
 
@@ -198,7 +199,11 @@ DO_COMMAND(do_draw)
 				}
 				continue;
 			case CTRL_B:
-				if (is_abbrev(arg1, "BLANKED"))
+				if (is_abbrev(arg1, "BALIGN"))
+				{
+					SET_BIT(flags, DRAW_FLAG_BALIGN);
+				}
+				else if (is_abbrev(arg1, "BLANKED"))
 				{
 					SET_BIT(flags, DRAW_FLAG_BLANKED);
 				}
@@ -1714,7 +1719,7 @@ DO_DRAW(draw_hbar)
 
 	if (bar <= 0)
 	{
-		show_error(ses, LIST_COMMAND, "#DRAW HBAR %d %d %d %d: DRAWING WIDTH (%d) MUST BE GREATER THAN 0.", top_row, top_col, bot_row, bot_col, bar);
+		show_error(ses, LIST_COMMAND, "#DRAW BAR %d %d %d %d: DRAWING WIDTH (%d) MUST BE GREATER THAN 0.", top_row, top_col, bot_row, bot_col, bar);
 
 		return;
 	}
@@ -1732,9 +1737,11 @@ DO_DRAW(draw_hbar)
 
 	nest = buf;
 	nest = get_arg_in_braces(ses, nest, arg1, GET_ALL);
-	if (*nest == COMMAND_SEPARATOR) nest++;
+	if (*nest == COMMAND_SEPARATOR)
+		nest++;
 	nest = get_arg_in_braces(ses, nest, arg2, GET_ALL);
-	if (*nest == COMMAND_SEPARATOR) nest++;
+	if (*nest == COMMAND_SEPARATOR)
+		nest++;
 	nest = get_arg_in_braces(ses, nest, arg3, GET_ALL);
 
 	min = get_number(ses, arg1);
@@ -1742,7 +1749,7 @@ DO_DRAW(draw_hbar)
 
 	if (max <= 0)
 	{
-		show_error(ses, LIST_COMMAND, "#DRAW HBAR {%d;%d;%s}: MAX MUST BE GREATER THAN 0.", min, max, arg3);
+		show_error(ses, LIST_COMMAND, "#DRAW BAR {%s;%s;%s}: MAX (%Lg) MUST BE GREATER THAN 0.", arg1, arg2, arg3, max);
 
 		return;
 	}
@@ -2441,6 +2448,16 @@ DO_DRAW(draw_text)
 
 	txt = buf1;
 
+	if (HAS_BIT(flags, DRAW_FLAG_BALIGN))
+	{
+		str_fix(buf1);
+
+		while (height < rows)
+		{
+			str_ins(&buf1, 0, "\n");
+			height++;
+		}
+	}
 
 	if (HAS_BIT(flags, DRAW_FLAG_TALIGN))
 	{

+ 7 - 0
src/edit.c

@@ -269,6 +269,13 @@ DO_EDIT(edit_suspend)
 {
 	struct edit_data *edit = gtd->ses->input->edit;
 
+	if (!HAS_BIT(gtd->ses->input->flags, INPUT_FLAG_EDIT))
+	{
+		tintin_printf2(gtd->ses, "edit_suspend: not currently editing");
+
+		return ses;
+	}
+
 	DEL_BIT(gtd->ses->input->flags, INPUT_FLAG_EDIT);
 
 	str_cpy(&edit->line[edit->update]->str, gtd->ses->input->buf);

+ 29 - 26
src/gui.h

@@ -613,33 +613,36 @@ char *tt_gui = "#line quiet #port init gui 0\n"
 "\n"
 "	gui_reload;\n"
 "\n"
-"	#if {$gui[cols] > 75}\n"
+"	#line verbose\n"
 "	{\n"
-"		#draw Silver huge traced scroll tile 1 1 6 73 { TINTIN++};\n"
-"		#draw Silver calign scroll tile 1 1 2 75 {}{$info[SYSTEM][CLIENT_VERSION]};\n"
-"		#draw Silver calign scroll tile 1 1 3 75 {}{Code by Peter Unold, Bill Reiss, and Igor van den Hoven}\n"
-"	};\n"
-"	#elseif {$gui[cols] > 40}\n"
-"	{\n"
-"		#draw Silver huge traced scroll tile 1 1 6 40 { TT++};\n"
-"		#draw Silver calign scroll tile 1 1 2 40 {}{$info[SYSTEM][CLIENT_VERSION]};\n"
-"		#draw Silver calign scroll tile 1 1 4 40 {}{Code by Peter Unold, Bill Reiss,}{}{and Igor van den Hoven}\n"
-"	};\n"
-"	#elseif {$gui[cols] > 18}\n"
-"	{\n"
-"		#draw Silver calign scroll tile 1 1 14 $gui[cols] {T I N T I N + +}{}{$info[SYSTEM][CLIENT_VERSION]}{}{Code by}{}{Peter Unold}{}{Bill Reiss}{}{and}{}{Igor van den Hoven};\n"
-"	};\n"
-"	#elseif {$gui[cols] > 8}\n"
-"	{\n"
-"		#draw Silver huge traced scroll tile 1 1 6 9 {T};\n"
-"		#draw Silver huge traced scroll tile 1 1 6 9 {T};\n"
-"		#draw Silver huge traced scroll tile 1 1 6 9 {+};\n"
-"		#draw Silver huge traced scroll tile 1 1 6 9 {+};\n"
-"		#draw Silver calign scroll tile 1 1 1 9 {$info[SYSTEM][CLIENT_VERSION]}\n"
-"	};\n"
-"	#elseif {$gui[cols] > 1}\n"
-"	{\n"
-"		#draw Silver calign scroll tile 1 1 8 $gui[cols] {T}{i}{n}{T}{i}{n}{+}{+}\n"
+"		#if {$gui[cols] > 75}\n"
+"		{\n"
+"			#draw Silver huge traced scroll tile 1 1 6 73 { TINTIN++};\n"
+"			#draw Silver calign scroll tile 1 1 2 75 {}{$info[SYSTEM][CLIENT_VERSION]};\n"
+"			#draw Silver calign scroll tile 1 1 3 75 {}{Code by Peter Unold, Bill Reiss, and Igor van den Hoven}\n"
+"		};\n"
+"		#elseif {$gui[cols] > 40}\n"
+"		{\n"
+"			#draw Silver huge traced scroll tile 1 1 6 40 { TT++};\n"
+"			#draw Silver calign scroll tile 1 1 2 40 {}{$info[SYSTEM][CLIENT_VERSION]};\n"
+"			#draw Silver calign scroll tile 1 1 4 40 {}{Code by Peter Unold, Bill Reiss,}{}{and Igor van den Hoven}\n"
+"		};\n"
+"		#elseif {$gui[cols] > 18}\n"
+"		{\n"
+"			#draw Silver calign scroll tile 1 1 14 $gui[cols] {T I N T I N + +}{}{$info[SYSTEM][CLIENT_VERSION]}{}{Code by}{}{Peter Unold}{}{Bill Reiss}{}{and}{}{Igor van den Hoven};\n"
+"		};\n"
+"		#elseif {$gui[cols] > 8}\n"
+"		{\n"
+"			#draw Silver huge traced scroll tile 1 1 6 9 {T};\n"
+"			#draw Silver huge traced scroll tile 1 1 6 9 {T};\n"
+"			#draw Silver huge traced scroll tile 1 1 6 9 {+};\n"
+"			#draw Silver huge traced scroll tile 1 1 6 9 {+};\n"
+"			#draw Silver calign scroll tile 1 1 1 9 {$info[SYSTEM][CLIENT_VERSION]}\n"
+"		};\n"
+"		#elseif {$gui[cols] > 1}\n"
+"		{\n"
+"			#draw Silver calign scroll tile 1 1 8 $gui[cols] {T}{i}{n}{T}{i}{n}{+}{+}\n"
+"		}\n"
 "	}\n"
 "}\n"
 "\n"

+ 114 - 96
src/help.c

@@ -127,20 +127,20 @@ DO_COMMAND(do_help)
 
 			if (HAS_BIT(gtd->flags, TINTIN_FLAG_MOUSETRACKING))
 			{
-				sprintf(tmp, "%.*s\e]68;6;;%s\a\e[4m%s%s\e[24m", 15 - (int) strlen(help_table[cnt].name), "                ", help_table[cnt].name, color, help_table[cnt].name);
+				sprintf(tmp, "%.*s\e]68;6;;%s\a\e[4m%s%s\e[24m", 16 - (int) strlen(help_table[cnt].name), "                ", help_table[cnt].name, color, help_table[cnt].name);
 			}
 			else
 			{
-				sprintf(tmp, "%s%15s", color, help_table[cnt].name);
+				sprintf(tmp, "%s%16s", color, help_table[cnt].name);
 			}
 
-			if (strip_vt102_strlen(ses, buf) + 15 > ses->wrap)
+			if (strip_vt102_strlen(ses, buf) + 16 > ses->wrap)
 			{
 				print_lines(ses, SUB_COL, "<088>%s<088>\n", buf);
 
 				*buf = 0;
 			}
-			cat_sprintf(buf, "%s ", tmp);
+			cat_sprintf(buf, "%s", tmp);
 		}
 
 		if (*buf)
@@ -292,12 +292,6 @@ DO_COMMAND(do_help)
 	return ses;
 }
 
-
-
-/*
-	This help table is a mess, but I got better things to do - Igor
-*/
-
 struct help_type help_table[] =
 {
 	{
@@ -986,6 +980,7 @@ struct help_type help_table[] =
 		"         You can further prefix the option as following:\n"
 		"\n"
 		"         ASCII      will draw in ASCII mode.\n"
+		"         BALIGN     will bottom align text.\n"
 		"         BLANKED    will blank the lines and corners.\n"
 		"         BOTTOM     will draw on the bottom side if possible.\n"
 		"         BOXED      will draw a box along the square.\n"
@@ -1026,7 +1021,7 @@ struct help_type help_table[] =
 		"         The following types are available.\n"
 		"\n"
 		"         <278>[HORIZONTAL] <178>BAR<278> {<MIN>;<MAX>;[COLOR]}\n"
-		"         <278> will draw a bar.\n"
+		"         <278> will draw a bar, use two 256 color codes for a color gradient.\n"
 		"         <278>[ASCII|UNICODE|HUGE] <178>BOX<278> {[TEXT1]} {[TEXT2]}\n"
 		"         <278>  will draw a box.\n"
 		"         <278>[BOXED|FOREGROUND] <178>BUFFER\n"
@@ -1071,7 +1066,7 @@ struct help_type help_table[] =
 		"\n"
 		"<178>Example<278>: #echo {The current date is %t.} {%Y-%m-%d %H:%M:%S}\n"
 		"         #echo {[%38s][%-38s]} {Hello World} {Hello World}\n"
-		"         #echo {{this is %s on the top row} {-1}} {printed}\n",
+		"         #echo {{this is %s on the top row} {1}} {printed}\n",
 		
 		"buffer format grep showme"
 	},
@@ -1290,6 +1285,7 @@ struct help_type help_table[] =
 		"         \\x7B  send the '{' character.\n"
 		"         \\x7D  send the '}' character.\n"
 		"         \\u    print a 16 bit unicode character, \\uFFFD for example.\n"
+		"         \\u{}  print a 8-21 bit unicode character, \\u{2AF21} for example.\n"
 		"         \\U    print a 21 bit unicode character, \\U02AF21 for example.\n"
 		"         \\v    send a vertical tab\n"
 		"\n"
@@ -1416,6 +1412,38 @@ struct help_type help_table[] =
 		"         <178>PORT LOG MESSAGE       <278>%0 name %1 ip %2 port %3 data %4 plain data\n"
 		"         <178>PORT RECEIVED MESSAGE  <278>%0 name %1 ip %2 port %3 data %4 plain data\n"
 		"\n"
+		"         <128>SCAN EVENTS<278>\n"
+		"\n"
+		"         SCAN CSV HEADER        %0 all args %1 arg1 %2 arg2 .. %99 arg99\n"
+		"         SCAN CSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
+		"         SCAN TSV HEADER        %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
+		"         SCAN TSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
+		"\n"
+		"         <128>SCREEN EVENTS<278>\n"
+		"\n"
+		"         <178>SCREEN FOCUS\n"
+		"         <278>  %0 focus (0 or 1)\n"
+		"\n"
+		"         SCREEN LOCATION        %0 rows %1 cols  %2 height %3 width\n"
+		"\n"
+		"         <178>SCREEN MOUSE LOCATION\n"
+		"         <278>  %0 row  %1 col  %2 -row  %3 -col  %4 pix row  %5 pix col\n"
+		"         <278>  %6 -pix row  %7 -pix col  %8 location\n"
+		"\n"
+		"         SCREEN RESIZE          %0 rows %1 cols %2 height %3 width\n"
+		"         SCREEN SPLIT           %0 top row %1 top col %2 bot row %3 bot col\n"
+		"         SCREEN UNSPLIT         %0 top row %1 top col %2 bot row %3 bot col\n"
+		"\n"
+		"         <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 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"
+		"\n"
+		"\n"
 		"         <128>SYSTEM EVENTS<278>\n"
 		"\n"
 		"         DAEMON ATTACHED        %0 file %1 pid\n"
@@ -1431,7 +1459,7 @@ struct help_type help_table[] =
 		"         SYSTEM CRASH           %0 message\n"
 		"         SYSTEM ERROR           %0 name %1 system msg %2 error %3 error msg\n"
 		"         UNKNOWN COMMAND        %0 raw text\n"
-
+		"         SIGUSR                 %0 signal\n"
 		"\n"
 		"         <128>TELNET EVENTS\n"
 		"\n"
@@ -1454,37 +1482,7 @@ struct help_type help_table[] =
 		"         <278>  %0 year  %1 month  %2 day of week  %3 day of month  %4 hour\n"
 		"         <278>  %5 minute  %6 second\n"
 		"\n"
-		"         <128>SCAN EVENTS<278>\n"
-		"\n"
-		"         SCAN CSV HEADER        %0 all args %1 arg1 %2 arg2 .. %99 arg99\n"
-		"         SCAN CSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
-		"         SCAN TSV HEADER        %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
-		"         SCAN TSV LINE          %0 all args %1 arg1 %2 arg3 .. %99 arg99\n"
-		"\n"
-		"         <128>SCREEN EVENTS<278>\n"
-		"\n"
-		"         <178>SCREEN FOCUS\n"
-		"         <278>  %0 focus (0 or 1)\n"
-		"\n"
-		"         SCREEN LOCATION        %0 rows %1 cols  %2 height %3 width\n"
-		"\n"
-		"         <178>SCREEN MOUSE LOCATION\n"
-		"         <278>  %0 row  %1 col  %2 -row  %3 -col  %4 pix row  %5 pix col\n"
-		"         <278>  %6 -pix row  %7 -pix col  %8 location\n"
-		"\n"
-		"         SCREEN RESIZE          %0 rows %1 cols %2 height %3 width\n"
-		"         SCREEN SPLIT           %0 top row %1 top col %2 bot row %3 bot col\n"
-		"         SCREEN UNSPLIT         %0 top row %1 top col %2 bot row %3 bot col\n"
-		"\n"
-		"         <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 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"
-		"\n"
+
 		"         <128>VARIABLE EVENTS<278>\n"
 		"\n"
 		"         VARIABLE UPDATE <VAR>  %0 name %1 new value %2 path\n"
@@ -1551,7 +1549,6 @@ struct help_type help_table[] =
 		"                                         optional {{string}{width}} syntax\n"
 		"         #format {test} {%x}      {hex}  print corresponding charset character\n"
 		"         #format {test} {%A}     {char}  store corresponding character value\n"
-		"         #format {test} {%C}   {number}  store number in chronological notation\n"
 		"         #format {test} {%D}      {hex}  convert hex to decimal in {test}\n"
 		"         #format {hash} {%H}   {string}  store a 64 bit string hash in {hash}\n"
 		"         #format {test} {%L}   {string}  store the string length in {test}\n"
@@ -1762,23 +1759,28 @@ struct help_type help_table[] =
 	{
 		"IF",
 		TOKEN_TYPE_COMMAND,
-		"<178>Command<278>: #if <178>{<278>conditional<178>} {<278>commands if true<178>} {<278>commands if false<178>}<278>\n"
+		"<178>Command<278>: #if <178>{<278>conditional<178>} {<278>commands if true<178>}<278>\n"
 		"\n"
-		"         The 'if' command is one of the most powerful commands added since\n"
-		"         TINTIN III. It works similar to an 'if' statement in other languages,\n"
-		"         and is strictly based on the way C handles its conditional statements.\n"
-		"         When an 'if' command is encountered, the conditional statement is\n"
+		"         The #if command is one of the most powerful commands added since\n"
+		"         TINTIN III. It works similar to an if statement in other languages,\n"
+		"         and is based on the way C handles its conditional statements.\n"
+		"         When an #if command is encountered, the conditional statement is\n"
 		"         evaluated, and if TRUE (any non-zero result) the commands are executed.\n"
 		"\n"
-		"         The 'if' statement is only evaluated if it is read, so you must nest\n"
-		"         the 'if' statement inside another statement (most likely an 'action'\n"
+		"         The if statement is only evaluated if it is read, so you must nest\n"
+		"         the if statement inside another statement (most likely an #action\n"
 		"         command). The conditional is evaluated exactly the same as in the\n"
-		"         'math' command only instead of storing the result, the result is used\n"
+		"         #math command, only instead of storing the result, the result is used\n"
 		"         to determine whether to execute the commands.\n"
 		"\n"
-		"<178>Example<278>: #action {%0 gives you %1 gold coins.} {#if {%1>5000} {thank %0}}\n"
+		"         To handle the case where an if statement is false it can be followed\n"
+		"         by the #else command.\n"
+		"\n"
+		"<178>Example<278>: #action {%0 gives you %1 gold coins.} {#if {%1 > 5000} {thank %0}}\n"
 		"         If someone gives you more than 5000 coins, thank them.\n"
 		"\n"
+		"<178>Example<278>: #alias {k} {#if {\"%0\" == \"\"} {kill $target};#else {kill %0}}\n"
+		"\n"
 		"<178>Comment<278>: See '#help math', for more information.\n",
 		
 		"case default else elseif switch regexp"
@@ -2069,12 +2071,16 @@ struct help_type help_table[] =
 		"\n"
 		"         By providing the name of a list and the LIST option it shows all\n"
 		"         triggers/variables associated with that list. With the SAVE option\n"
-		"         This data is written to the info variable.\n"
+		"         this data is written to the info variable.\n"
 		"\n"
 		"         #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 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"
-		"         #info session will show some session information.\n"
+		"         #info session will show information on the session.\n"
+		"         #info sessions will show information on all sessions.\n"
 		"         #info system will show some system information.\n"
 		"         #info unicode will show information on the provided character.\n",
 
@@ -2632,7 +2638,8 @@ struct help_type help_table[] =
 		"         <278>  Sets displaying center of the map viewer, default is 0 0 0.\n"
 		"\n"
 		"         <178>#map color <field> [value]\n"
-		"         <278>  Sets the map color for the given color field.\n"
+		"         <278>  Sets the map color for the given color field. Use #map color reset\n"
+		"         <278>  to restore colors to default.\n"
 		"\n"
 		"         <178>#map create <size>\n"
 		"         <278>  Creates a new map and room 1. The default size is 50000 rooms.\n"
@@ -3043,31 +3050,33 @@ struct help_type help_table[] =
 		"         ------------------------------------------------\n"
 		"         !               0            logical not\n"
 		"         ~               0            bitwise not\n"
-		"         *               1            integer multiply\n"
-		"         **              1            integer power\n"
-		"         /               1            integer divide\n"
-		"         //              1            integer sqrt // 2 or cbrt // 3\n"
-		"         %               1            integer modulo\n"
-		"         d               1            integer random dice roll\n"
-		"         +               2            integer addition\n"
-		"         -               2            integer subtraction\n"
-		"         <<              3            bitwise shift\n"
-		"         >>              3            bitwise shift\n"
-		"         ..              3            bitwise ellipsis\n"
-		"         >               4            logical greater than\n"
-		"         >=              4            logical greater than or equal\n"
-		"         <               4            logical less than\n"
-		"         <=              4            logical less than or equal\n"
-		"         ==              5            logical equal (can use regex)\n"
-		"         ===             5            logical equal (never regex)\n"
-		"         !=              5            logical not equal (can use regex)\n"
-		"         !==             5            logical not equal (never regex)\n"
-		"          &              6            bitwise and\n"
-		"          ^              7            bitwise xor\n"
-		"          |              8            bitwise or\n"
-		"         &&              9            logical and\n"
-		"         ^^             10            logical xor\n"
-		"         ||             11            logical or\n"
+		"         d               1            integer random dice\n"
+		"         *               2            integer multiply\n"
+		"         **              2            integer power\n"
+		"         /               2            integer divide\n"
+		"         //              2            integer sqrt // 2 or cbrt // 3\n"
+		"         %               2            integer modulo\n"
+		"         +               3            integer addition\n"
+		"         -               3            integer subtraction\n"
+		"         <<              4            bitwise shift\n"
+		"         >>              4            bitwise shift\n"
+		"         ..              4            bitwise ellipsis\n"
+		"         >               5            logical greater than\n"
+		"         >=              5            logical greater than or equal\n"
+		"         <               5            logical less than\n"
+		"         <=              5            logical less than or equal\n"
+		"         ==              6            logical equal (can use regex)\n"
+		"         ===             6            logical equal (never regex)\n"
+		"         !=              6            logical not equal (can use regex)\n"
+		"         !==             6            logical not equal (never regex)\n"
+		"          &              7            bitwise and\n"
+		"          ^              8            bitwise xor\n"
+		"          |              9            bitwise or\n"
+		"         &&             10            logical and\n"
+		"         ^^             11            logical xor\n"
+		"         ||             12            logical or\n"
+		"         ?              13            logical ternary if (unfinished code)\n"
+		"         :              14            logical ternary else \n"
 		"\n"
 		"         True is any non-zero number, and False is zero.  Parentheses () have\n"
 		"         highest precedence, so inside the () is always evaluated first.\n"
@@ -3282,7 +3291,7 @@ struct help_type help_table[] =
 		"\n"
 		"         This would make you start a reply when clicking on a tell.\n"
 		"\n"
-		"Website: https://tintin.mudhalla.net/protocols/mslp\n",
+		"<178>Website<278>: https://tintin.mudhalla.net/protocols/mslp\n",
 
 		"event port"
 	},
@@ -3633,7 +3642,7 @@ struct help_type help_table[] =
 		"         Prompt is a feature for split window mode, which will capture a line\n"
 		"         received from the server and display it on the status bar of your\n"
 		"         split screen terminal. You would define <text> and <new text> the\n"
-		"         same way as with a substitution.\n"
+		"         same way as you would with #substitute.\n"
 		"\n"
 		"         The row number is optional and useful if you use a non standard split\n"
 		"         mode. A positive row number draws #row lines from the top while a\n"
@@ -3644,8 +3653,8 @@ struct help_type help_table[] =
 		"         The col number is optional and can be used to set the column index.\n"
 		"         A positive col number draws the given number of columns from the left,\n"
 		"         while a negative col number draws from the right. If you leave the\n"
-		"         column argument empty tintin will clear the row before printing at\n"
-		"         the start of the row.\n"
+		"         col number empty tintin will clear the row before printing at the\n"
+		"         start of the row.\n"
 		"\n"
 		"         The #show command takes a row and col argument as well so it's also\n"
 		"         possible to place text on your split lines using #show.\n"
@@ -3701,19 +3710,22 @@ struct help_type help_table[] =
 		"\n"
 		"         Of the following the (lazy) match is available at %1-%99 + 1\n"
 		"\n"
-		"      %a match zero to any number of characters including newlines.\n"
-		"      %A match zero to any number of newlines.\n"
 		"      %d match zero to any number of digits.\n"
 		"      %D match zero to any number of non digits.\n"
-		"      %p match zero to any number of printable characters.\n"
-		"      %P match zero to any number of non printable characters.\n"
 		"      %s match zero to any number of spaces.\n"
 		"      %S match zero to any number of non spaces.\n"
-		"      %u match zero to any number of unicode characters.\n"
-		"      %U match zero to any number of non unicode characters.\n"
 		"      %w match zero to any number of word characters.\n"
 		"      %W match zero to any number of non word characters.\n"
 		"\n"
+		"      Experimental (subject to change) matches are:\n"
+		"\n"
+		"      %a match zero to any number of characters including newlines.\n"
+		"      %A match zero to any number of newlines.\n"
+		"      %p match zero to any number of printable characters.\n"
+		"      %P match zero to any number of non printable characters.\n"
+		"      %u match zero to any number of unicode characters.\n"
+		"      %U match zero to any number of non unicode characters.\n"
+		"\n"
 		"      If you want to match 1 digit use %+1d, if you want to match between 3\n"
 		"      and 5 spaces use %+3..5s, if you want to match 0 or more word\n"
 		"      characters use %+0..w, etc.\n"
@@ -3735,7 +3747,7 @@ struct help_type help_table[] =
 		"\n"
 		"<178>Comment<278>: Like an alias or function #regex has its own scope.\n",
 
-		"replace"
+		"pcre replace"
 	},
 
 	{
@@ -3954,7 +3966,7 @@ struct help_type help_table[] =
 		"         treats the generated echos as commands if no variable is provided.\n"
 		"\n"
 		"         This is useful for running php, perl, ruby, and python scripts. You\n"
-		"         can run these scrips either from file or from within tintin if the\n"
+		"         can run these scripts either from file or from within tintin if the\n"
 		"         scripting language allows this.\n"
 		"\n"
 		"         If you provide a variable the output of the script is stored as a list.\n"
@@ -4067,14 +4079,19 @@ struct help_type help_table[] =
 	{
 		"SNOOP",
 		TOKEN_TYPE_COMMAND,
-		"<178>Command<278>: #snoop <178>{<278>session name<178>} <178>{<278>on<178>|<278>off<178>}<278>\n"
+		"<178>Command<278>: #snoop <178>{<278>session name<178>} <178>{<278>on<178>|<278>off<178>|<278>scroll<178>}<278>\n"
 		"\n"
 		"         If there are multiple sessions active, this command allows you to\n"
 		"         monitor what is going on in the sessions that are not currently active.\n"
 		"         The line of text from other sessions will be prefixed by the session's\n"
 		"         name.\n"
 		"\n"
-		"         You can toggle off snoop mode by executing #snoop a second time.\n",
+		"         You can toggle off snoop mode by executing #snoop a second time.\n"
+		"\n"
+		"         By using the scroll argument you will snoop the session's scroll\n"
+		"         region which will overwrite the display of whichever session is active.\n"
+		"         You can change the size and location of a session's scroll region by\n"
+		"         using the #split and #screen scrollregion commands.\n",
 
 		"all port run session sessionname ssl zap"
 	},
@@ -4483,3 +4500,4 @@ struct help_type help_table[] =
 		""
 	}
 };
+

+ 5 - 5
src/history.c

@@ -119,7 +119,7 @@ struct session *repeat_history(struct session *ses, char *line)
 
 DO_HISTORY(history_character)
 {
-	arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 
 	gtd->repeat_char = *arg1;
 
@@ -217,7 +217,7 @@ DO_HISTORY(history_get)
 
 DO_HISTORY(history_list)
 {
-	arg = get_arg_in_braces(ses, arg, arg1, GET_ALL);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
 
 	if (*arg1 == 0)
 	{
@@ -235,7 +235,7 @@ DO_HISTORY(history_read)
 	struct listroot *root = ses->list[LIST_HISTORY];
 	FILE *file;
 
-	arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 
 	file = fopen(arg1, "r");
 
@@ -269,7 +269,7 @@ DO_HISTORY(history_read)
 
 DO_HISTORY(history_size)
 {
-	arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 
 	if (atoi(arg1) < 1 || atoi(arg1) > 100000)
 	{
@@ -288,7 +288,7 @@ DO_HISTORY(history_write)
 	FILE *file;
 	int i;
 
-	arg = get_arg_in_braces(ses, arg, arg1, GET_ONE);
+	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 
 	file = fopen(arg1, "w");
 

+ 33 - 5
src/main.c

@@ -63,14 +63,22 @@ void ttou_handler(int signal)
 	syserr_printf(gtd->ses, "ttou_handler");
 }
 
+void usr_handler(int signal)
+{
+	check_all_events(gts, EVENT_FLAG_SYSTEM, 0, 1, "SIGUSR", signal == SIGUSR1 ? "1" : "2");
+}
+
 /*
 	when the screen size changes, take note of it
 */
 
 void winch_handler(int signal)
 {
-	struct session *ses;
+	SET_BIT(gtd->flags, TINTIN_FLAG_WINCHUPDATE);
 
+	gtd->time_session = gtd->time;
+/*
+	struct session *ses;
 	init_terminal_size(gts);
 
 	for (ses = gts->next ; ses ; ses = ses->next)
@@ -84,6 +92,7 @@ void winch_handler(int signal)
 	}
 
 	winch_daemon();
+*/
 }
 
 
@@ -99,7 +108,7 @@ void child_handler(int signal)
 
 //	syserr_fatal(signal, "child_handler");
 }
-
+/*
 void interrupt_handler(int signal)
 {
 	if (gtd->ses->connect_retry > utime())
@@ -108,7 +117,7 @@ void interrupt_handler(int signal)
 	}
 	else if (HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_SGA) && !HAS_BIT(gtd->ses->telopts, TELOPT_FLAG_ECHO))
 	{
-		socket_printf(gtd->ses, 1, "%c", 4);
+		socket_printf(gtd->ses, 1, "\004");
 	}
 	else if (gtd->attach_sock)
 	{
@@ -121,7 +130,7 @@ void interrupt_handler(int signal)
 		cursor_delete_or_exit(gtd->ses, "");
 	}
 }
-
+*/
 void suspend_handler(int signal)
 {
 	show_message(gtd->ses, LIST_COMMAND, "#SIGNAL: SIGTSTP");
@@ -219,6 +228,16 @@ int main(int argc, char **argv)
 		syserr_fatal(-1, "signal SIGTTOU");
 	}
 */
+	if (signal(SIGUSR1, usr_handler) == BADSIG)
+	{
+		syserr_fatal(-1, "signal SIGUSR1");
+	}
+
+	if (signal(SIGUSR2, usr_handler) == BADSIG)
+	{
+		syserr_fatal(-1, "signal SIGUSR1");
+	}
+
 	if (signal(SIGWINCH, winch_handler) == BADSIG)
 	{
 		syserr_fatal(-1, "signal SIGWINCH");
@@ -533,8 +552,8 @@ void init_tintin(int greeting)
 
 	gtd->banner_list    = init_list(gts, LIST_CONFIG, 32);
 
-	gts->split          = calloc(1, sizeof(struct split_data));
 	gts->scroll         = calloc(1, sizeof(struct scroll_data));
+	gts->split          = calloc(1, sizeof(struct split_data));
 	gts->input          = calloc(1, sizeof(struct input_data));
 
 	init_local(gts);
@@ -787,6 +806,15 @@ void syserr_fatal(int signal, char *msg)
 		exit(-1);
 	}
 
+	if (signal == SIGHUP)
+	{
+		crashed--;
+
+		check_all_events(gts, SUB_SEC|EVENT_FLAG_SYSTEM, 0, 0, "SIGHUB");
+
+		crashed++;
+	}
+
 	if (signal <= 0)
 	{
 		sprintf(errstr, "(error %d: %s", errno, strerror(errno));

+ 44 - 17
src/mapper.c

@@ -3569,16 +3569,23 @@ void map_search_compile(struct session *ses, char *arg, char *var)
 
 	arg = sub_arg_in_braces(ses, tmp, buf, GET_ALL, SUB_VAR|SUB_FUN); // name
 
+	ses->map->search->min = ses->map->search->max = ses->map->search->vnum = 0;
+
 	if (is_math(ses, buf))
 	{
-		ses->map->search->vnum = (int) get_number(ses, buf);
-	}
-	else
-	{
-		ses->map->search->vnum = 0;
+		if (strstr(buf, ".."))
+		{
+			get_ellipsis(ses, ses->map->size, buf, &ses->map->search->min, &ses->map->search->max);
+			ses->map->search->min++;
+			ses->map->search->max++;
+		}
+		else
+		{
+			ses->map->search->vnum = (int) get_number(ses, buf);
+		}
 	}
 
-	if (ses->map->search->vnum)
+	if (ses->map->search->vnum || ses->map->search->min || ses->map->search->max)
 	{
 		pop_call();
 		return;
@@ -3859,6 +3866,11 @@ int match_room(struct session *ses, int vnum, struct search_data *search)
 		return room->vnum == search->vnum;
 	}
 
+	if (search->min || search->max)
+	{
+		return room->vnum >= search->min && room->vnum <= search->max;
+	}
+
 	if (search->id)
 	{
 		return !strcmp(room->id, search->id);
@@ -3995,7 +4007,9 @@ int find_location(struct session *ses, char *arg)
 		return find_exit(ses, ses->map->in_room, arg)->vnum;
 	}
 
-	if (is_math(ses, arg))
+	sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
+
+	if (is_math(ses, arg1))
 	{
 		arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 		arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
@@ -4223,8 +4237,11 @@ int check_global(struct session *ses, int room)
 
 int tunnel_void(struct session *ses, int from, int room, int dir)
 {
+	push_call("tunnel_void(%p,%d,%d,%d)",ses,from,room,dir);
+
 	if (!HAS_BIT(ses->map->room_list[room]->flags, ROOM_FLAG_VOID))
 	{
+		pop_call();
 		return room;
 	}
 
@@ -4234,19 +4251,28 @@ int tunnel_void(struct session *ses, int from, int room, int dir)
 
 		if (exit)
 		{
+			pop_call();
 			return tunnel_void(ses, room, exit->vnum, exit->dir);
 		}
+		pop_call();
 		return room;
 	}
 
 	if (ses->map->room_list[room]->f_exit->vnum != from)
 	{
+		pop_call();
 		return tunnel_void(ses, room, ses->map->room_list[room]->f_exit->vnum, ses->map->room_list[room]->f_exit->dir);
 	}
-	else
+
+	if (ses->map->room_list[room]->l_exit->vnum != from)
 	{
+		pop_call();
 		return tunnel_void(ses, room, ses->map->room_list[room]->l_exit->vnum, ses->map->room_list[room]->l_exit->dir);
 	}
+	show_error(ses, LIST_COMMAND, "\e[1;31mtunnel_void(%p,%d,%d,%d) NO VALID EXITS FOUND.",ses,from,room,dir);
+
+	pop_call();
+	return room;
 }
 
 // shortest_path() utilities
@@ -4946,8 +4972,6 @@ void map_mouse_handler(struct session *ses, char *arg1, char *arg2, int row, int
 				  "w", "RL", "RC", "RR",  "e", "e",
 				 "sw",  "d",  "s", "16", "se", "18" };
 
-//		tintin_printf2(ses, "\e[1;32mdebug: y=%d x=%d mod(y)=%d mod(x)=%d", y / 3, y / 6, y % 3, x % 6);
-
 		strcpy(exit, grid[URANGE(0, y % 3 * 6 + x % 6, 17)]);
 
 		y /= 3;
@@ -4986,8 +5010,6 @@ void map_mouse_handler(struct session *ses, char *arg1, char *arg2, int row, int
 				break;
 		}
 
-//		tintin_printf2(ses, "\e[1;32mdebug: y=%d x=%d y_mod=%d x_mod=%d y+x=%d y=%d x=%d", y, x, y_mod, x_mod, 5*y_mod+x_mod, y, x);
-
 		max_y = 2 + (rows + 2) / 2;
 		max_x = 2 + (cols + 4) / 5;
 	}
@@ -5030,7 +5052,7 @@ void map_mouse_handler(struct session *ses, char *arg1, char *arg2, int row, int
 
 	if (arg1 && arg2)
 	{
-		check_all_events(ses, EVENT_FLAG_MAP, 2, 6, "MAP REGION %s %s", arg1, arg2, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), ntos(vnum), exit);
+		check_all_events(ses, EVENT_FLAG_MOUSE, 2, 6, "MAP REGION %s %s", arg1, arg2, ntos(row), ntos(col), ntos(rev_row), ntos(rev_col), ntos(vnum), exit);
 	}
 
 	if (vnum)
@@ -5080,6 +5102,13 @@ DO_MAP(map_at)
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 	arg = sub_arg_in_braces(ses, arg, arg2, GET_ALL, SUB_NONE);
 
+	if (ses->map->at_room)
+	{
+		show_error(ses, LIST_COMMAND, "#MAP AT: Nested #map at call from room {%d}.", ses->map->in_room);
+
+		return;
+	}
+
 	new_room = find_room(ses, arg1);
 
 	ses->map->at_room = ses->map->in_room;
@@ -5095,6 +5124,8 @@ DO_MAP(map_at)
 		{
 			show_message(ses, LIST_COMMAND, "#MAP AT: Couldn't find room or exit {%s}.", arg1);
 
+			ses->map->at_room = 0;
+
 			return;
 		}
 	}
@@ -7216,12 +7247,8 @@ DO_MAP(map_move)
 {
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
 
-//	tintin_printf2(ses, "debug: %s vs %s", arg, arg1);
-
 	arg = substitute_speedwalk(ses, arg1, arg2);
 
-//	tintin_printf2(ses, "debug: %s vs %s", arg, arg2);
-
 	ses->map->nofollow++;
 
 	while (*arg)

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 377 - 347
src/math.c


+ 1 - 12
src/misc.c

@@ -278,22 +278,11 @@ struct interval_type cp949_to_unicode_table[] =
 
 DO_COMMAND(do_test)
 {
-	int i;
-
 	arg = sub_arg_in_braces(ses, arg, arg1, GET_ONE, SUB_VAR|SUB_FUN);
 
 	if (!strcmp(arg1, "bla"))
 	{
-		int size = sizeof(cp949_to_unicode_table) / sizeof(struct interval_type);
-
-		printf("size: %d\n", size);
-
-		for (i = 0 ; i < size ; i++)
-		{
-			execute(ses, "#variable keys[%d] %d", cp949_to_unicode_table[i].head, cp949_to_unicode_table[i].tail);
-//			command(ses, do_line, "log cp949_to_unicode_keys %d,", cp949_to_unicode_table[i].head);
-//			command(ses, do_line, "log cp949_to_unicode_vals %d,", cp949_to_unicode_table[i].tail);
-		}
+		printf("%d", ~1 + 2);
 	}
 
 	if (!strcmp(arg1, "rain"))

+ 16 - 4
src/nest.c

@@ -326,7 +326,8 @@ int get_nest_size(struct listroot *root, char *variable)
 
 					if (root->used)
 					{
-						range = get_ellipsis(root, name, &min, &max);
+						range = get_ellipsis(root->ses, root->used, name, &min, &max);
+
 
 						return range + 1;
 					}
@@ -407,7 +408,7 @@ int get_nest_size_index(struct listroot *root, char *variable, char **result)
 
 					if (root->used)
 					{
-						range = get_ellipsis(root, name, &min, &max);
+						range = get_ellipsis(root->ses, root->used, name, &min, &max);
 
 						return range + 1;
 					}
@@ -493,7 +494,9 @@ int get_nest_size_key(struct listroot *root, char *variable, char **result)
 
 					if (root->used)
 					{
-						range = get_ellipsis(root, name, &min, &max);
+						range = get_ellipsis(root->ses, root->used, name, &min, &max);
+
+//						tintin_printf2(root->ses, "debug: %d %d %d\n", min, max, range);
 
 						if (min < max)
 						{
@@ -598,7 +601,7 @@ int get_nest_size_val(struct listroot *root, char *variable, char **result)
 
 					if (root->used)
 					{
-						range = get_ellipsis(root, name, &min, &max);
+						range = get_ellipsis(root->ses, root->used, name, &min, &max);
 
 						if (min < max)
 						{
@@ -974,7 +977,10 @@ 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);
+
 		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);
 	}
 	free(arg2);
 
@@ -1063,7 +1069,10 @@ 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);
+
 		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);
 	}
 	free(arg2);
 
@@ -1147,7 +1156,10 @@ struct listnode *set_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);
+		check_all_events(root->ses, EVENT_FLAG_VARIABLE, 1, 3, "VARIABLE UPDATED %s", arg1, name, arg2, arg1);
 	}
 
 	free(arg2);

+ 1 - 1
src/port.c

@@ -74,7 +74,7 @@ DO_COMMAND(do_port)
 
 		arg = sub_arg_in_braces(ses, arg, arg2, port_table[cnt].rval, SUB_VAR|SUB_FUN);
 
-		ses = port_table[cnt].fun(ses, arg1, arg2, arg);
+		ses = port_table[cnt].fun(ses, arg, arg1, arg2);
 
 		return ses;
 	}

+ 3 - 3
src/regex.c

@@ -308,10 +308,10 @@ int get_regex_range(char *in, char *out, int *var, int *arg)
 				pto += sprintf(pto, "([\\x00-\\x7F\\xFF]");
 				break;
 			case 'w':
-				pto += sprintf(pto, "([a-zA-Z]");
+				pto += sprintf(pto, "([a-zA-Z0-9_]");
 				break;
 			case 'W':
-				pto += sprintf(pto, "([^a-zA-Z]");
+				pto += sprintf(pto, "([^a-zA-Z0-9_]");
 				break;
 
 			default:
@@ -1230,7 +1230,7 @@ void tintin_macro_compile(char *input, char *output)
 					case 'U':
 						if (pti[2] && pti[3] && pti[4] && pti[5] && pti[6] && pti[7])
 						{
-							pto += unicode_21_bit(&pti[2], pto);
+							pto += unicode_21_bit(pti + 2, pto);
 							pti += 8;
 						}
 						else

+ 18 - 7
src/scan.c

@@ -62,7 +62,7 @@ struct scan_type scan_table[] =
 {
 	{       "ABORT",            scan_abort,   SCAN_FLAG_NONE,                "Abort a scan currently in progress."},
 	{       "CSV",              scan_csv,     SCAN_FLAG_FILE|SCAN_FLAG_SCAN, "Scan a comma separated value file." },
-	{       "DIR",              scan_dir,     SCAN_FLAG_FILE,                "Scan a directory to a variable."    },
+	{       "DIR",              scan_dir,     SCAN_FLAG_NONE,                "Scan a directory to a variable."    },
 	{       "FILE",             scan_file,    SCAN_FLAG_FILE,                "Scan a file all at once."           },
 	{       "FORWARD",          scan_forward, SCAN_FLAG_FILE,                "Scan a file and send each line."    },
 	{       "TSV",              scan_tsv,     SCAN_FLAG_FILE|SCAN_FLAG_SCAN, "Scan a tab separated value file."   },
@@ -303,7 +303,7 @@ DO_SCAN(scan_csv)
 
 DO_SCAN(scan_dir)
 {
-	char filename[PATH_SIZE];
+	char cwd[PATH_SIZE], filename[PATH_SIZE * 2];
 	struct dirent **dirlist;
 	struct stat info;
 	int size, index;
@@ -319,14 +319,25 @@ DO_SCAN(scan_dir)
 
 	set_nest_node_ses(ses, arg2, "");
 
+	if (*arg1 == 0)
+	{
+		if (getcwd(cwd, PATH_MAX) == NULL)
+		{
+			syserr_printf(ses, "scan_dir: getcwd:");
+
+			cwd[0] = 0;
+		}
+		arg1 = cwd;
+	}
+
 	size = scandir(arg1, &dirlist, 0, NULL);
 
 	if (size == -1)
 	{
 		if (stat(arg1, &info) == -1)
 		{
-			syserr_printf(ses, "scan_dir: stat:");
-			
+			syserr_printf(ses, "scan_dir: stat(%s): error:", arg1);
+
 			return ses;
 		}
 
@@ -353,9 +364,9 @@ DO_SCAN(scan_dir)
 
 		if (stat(filename, &info) == -1)
 		{
-			syserr_printf(ses, "scan_dir: stat:");
-			
-			return ses;
+			syserr_printf(ses, "scan_dir: stat(%s): error:", filename);
+
+			continue;
 		}
 
 		add_nest_node_ses(ses, arg2, "{%s}{{FILE}{%d}{MODE}{%u}{SIZE}{%u}{TIME}{%lld}}",

+ 21 - 1
src/screen.c

@@ -1702,7 +1702,7 @@ void fill_split_region(struct session *ses, char *arg)
 
 DO_SCREEN(screen_info)
 {
-	int lvl;
+	int lvl, cnt, max;
 
 	if (is_abbrev(arg1, "SAVE"))
 	{
@@ -1780,7 +1780,27 @@ DO_SCREEN(screen_info)
 		tintin_printf2(ses, "SPLIT mode detected.");
 	}
 
+	for (cnt = max = 0 ; cnt < gtd->screen->max_row ; cnt++)
+	{
+		max += str_len(gtd->screen->line[cnt]->str);
+	}
+	tintin_printf2(ses, "gtd->screen->line[%4d]:  %4d", gtd->screen->max_row, max);
+
+	for (cnt = max = 0 ; cnt < gtd->screen->max_row ; cnt++)
+	{
+		max += str_len(gtd->screen->grid[cnt]->str);
+	}
+	tintin_printf2(ses, "gtd->screen->grid[%4d]:  %4d", gtd->screen->max_row, max);
+/*
+	char buf[BUFFER_SIZE];
+
+	for (cnt = 0 ; cnt < 12 && cnt < gtd->screen->max_row ; cnt++)
+	{
+		convert_meta(gtd->screen->grid[cnt]->str, buf, 0);
 
+		tintin_printf2(ses, "%2d, %s", cnt, buf);
+	}
+*/
 	return;
 }
 

+ 50 - 25
src/session.c

@@ -72,7 +72,15 @@ DO_COMMAND(do_session)
 
 		for (sesptr = gts->next ; sesptr ; sesptr = sesptr->next)
 		{
-			show_session(ses, sesptr);
+			tintin_printf2(ses, "%-10s %18s:%-5s %5s %6s %10s %7s %5s",
+				sesptr->name,
+				sesptr->session_host,
+				sesptr->session_port,
+				sesptr == gtd->ses ? "(ats)" : "",
+				sesptr->ssl ? "(ssl)" : sesptr->port ? "(port)" : HAS_BIT(sesptr->flags, SES_FLAG_RUN) ? " (run)" : "",
+				sesptr->mccp2 && sesptr->mccp3 ? "(mccp 2+3)" : sesptr->mccp2 ? "(mccp 2)" : sesptr->mccp3 ? "(mccp 3)" : "",
+				HAS_BIT(sesptr->flags, SES_FLAG_SNOOP|SES_FLAG_SNOOPSCROLL) ? "(snoop)" : "",
+				sesptr->logfile ? "(log)" : "");
 		}
 	}
 	else if (*arg1 && *arg == 0)
@@ -126,6 +134,13 @@ DO_COMMAND(do_session)
 			}
 		}
 
+		sesptr = find_session(arg1);
+
+		if (sesptr)
+		{
+			return activate_session(sesptr);
+		}
+
 		tintin_puts2(ses, "#THAT SESSION IS NOT DEFINED.");
 	}
 	else
@@ -180,12 +195,43 @@ DO_COMMAND(do_snoop)
 	else if (is_abbrev(arg2, "OFF"))
 	{
 		show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SESSION '%s'", sesptr->name);
-
 		DEL_BIT(sesptr->flags, SES_FLAG_SNOOP);
 	}
+	else if (is_abbrev(arg2, "SCROLL"))
+	{
+		arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
+
+		if (*arg2 == 0)
+		{
+			if (HAS_BIT(sesptr->flags, SES_FLAG_SNOOPSCROLL))
+			{
+				show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SCROLL REGION OF SESSION '%s'", sesptr->name);
+			}
+			else
+			{
+				show_message(ses, LIST_COMMAND, "#SNOOP: SNOOPING SCROLL REGION OF SESSION '%s'", sesptr->name);
+			}
+			TOG_BIT(sesptr->flags, SES_FLAG_SNOOPSCROLL);
+		}
+		else if (is_abbrev(arg2, "ON"))
+		{
+			show_message(ses, LIST_COMMAND, "#SNOOP: SNOOPING SCROLL REGION OF SESSION '%s'", sesptr->name);
+
+			SET_BIT(sesptr->flags, SES_FLAG_SNOOPSCROLL);
+		}
+		else if (is_abbrev(arg2, "OFF"))
+		{
+			show_message(ses, LIST_COMMAND, "#SNOOP: NO LONGER SNOOPING SCROLL REGION OF SESSION '%s'", sesptr->name);
+			DEL_BIT(sesptr->flags, SES_FLAG_SNOOPSCROLL);
+		}
+		else
+		{
+			show_error(ses, LIST_COMMAND, "#SYNTAX: #SNOOP {session} {SCROLL} {ON|OFF}");
+		}
+	}
 	else
 	{
-		show_error(ses, LIST_COMMAND, "#SYNTAX: #SNOOP {session} {ON|OFF}");
+		show_error(ses, LIST_COMMAND, "#SYNTAX: #SNOOP {session} {ON|OFF|SCROLL}");
 	}
 	return ses;
 }
@@ -244,27 +290,6 @@ DO_COMMAND(do_zap)
 	return ses;
 }
 
-
-void show_session(struct session *ses, struct session *ptr)
-{
-	char temp[BUFFER_SIZE];
-
-	sprintf(temp, "%-10s %18s:%-5s %5s %6s",
-		ptr->name,
-		ptr->session_host,
-		ptr->session_port,
-		ptr == gtd->ses ? "(ats)" : "",
-		ptr->ssl ? "(ssl)" : ptr->port ? "(port)" : HAS_BIT(ptr->flags, SES_FLAG_RUN) ? " (run)" : "");
-
-	cat_sprintf(temp, " %10s", ptr->mccp2 && ptr->mccp3 ? "(mccp 2+3)" : ptr->mccp2 ? "(mccp 2)" : ptr->mccp3 ? "(mccp 3)" : "");
-
-	cat_sprintf(temp, " %7s", HAS_BIT(ptr->flags, SES_FLAG_SNOOP) ? "(snoop)" : "");
-
-	cat_sprintf(temp, " %5s", ptr->logfile ? "(log)" : "");
-
-	tintin_puts2(ses, temp);
-}
-
 struct session *find_session(char *name)
 {
 	struct session *ses;
@@ -433,7 +458,7 @@ struct session *new_session(struct session *ses, char *name, char *arg, int desc
 
 	newses->wrap          = gts->wrap;
 
-        newses->scroll        = calloc(1, sizeof(struct scroll_data));
+	newses->scroll        = calloc(1, sizeof(struct scroll_data));
 	init_buffer(newses, gts->scroll->size);
 
 	newses->input         = calloc(1, sizeof(struct input_data));

+ 3 - 2
src/split.c

@@ -247,6 +247,7 @@ void dirty_screen(struct session *ses)
 	if (IS_SPLIT(ses) && ses == gtd->ses)
 	{
 		init_pos(ses, ses->input->top_row, ses->input->top_col);
+		cursor_redraw_input(ses, "");
 	}
 
 	pop_call();
@@ -302,7 +303,7 @@ void split_show(struct session *ses, char *prompt, char *row_str, char *col_str)
 		return;
 	}
 
-	if (row != gtd->screen->rows && inside_scroll_region(ses, row, col))
+	if (row > ses->input->top_row && inside_scroll_region(ses, row, col))
 	{
 		show_error(ses, LIST_PROMPT, "#ERROR: PROMPT ROW IS INSIDE THE SCROLLING REGION: {%s} {%s} [%d].", prompt, row_str, row);
 
@@ -331,7 +332,7 @@ void split_show(struct session *ses, char *prompt, char *row_str, char *col_str)
 
 	save_pos(ses);
 
-	if (row == gtd->screen->rows)
+	if (row == ses->input->top_row)
 	{
 		gtd->ses->input->str_off = width + 1;
 

+ 64 - 1
src/string.c

@@ -255,6 +255,65 @@ int raw_len_str_min(struct session *ses, char *str, int start, int end)
 	return ret_cnt;
 }
 
+// like str_min but also grab tailing colors
+
+int raw_len_str_opt(struct session *ses, char *str, int start, int end)
+{
+	int raw_cnt, str_cnt, ret_cnt, width, tmp_cnt, raw_len;
+
+	raw_cnt = 0;
+	str_cnt = 0;
+	ret_cnt = 0;
+	raw_len = strlen(str);
+
+	while (raw_cnt < raw_len)
+	{
+		tmp_cnt = skip_vt102_codes(&str[raw_cnt]);
+
+		if (tmp_cnt)
+		{
+			raw_cnt += tmp_cnt;
+
+			if (str_cnt >= start)
+			{
+				ret_cnt += tmp_cnt;
+			}
+			continue;
+		}
+
+		if (str_cnt >= end)
+		{
+			break;
+		}
+
+		if (HAS_BIT(ses->charset, CHARSET_FLAG_UTF8) && is_utf8_head(&str[raw_cnt]))
+		{    
+			tmp_cnt = get_utf8_width(&str[raw_cnt], &width);
+
+			if (str_cnt >= start)
+			{
+				ret_cnt += tmp_cnt;
+			}
+			raw_cnt += tmp_cnt;
+		}
+		else
+		{
+			if (str_cnt >= start)
+			{
+				ret_cnt++;
+			}
+			raw_cnt++;
+			width = 1;
+		}
+		if (end >= 0 && str_cnt + width > end)
+		{
+			break;
+		}
+		str_cnt += width;
+	}
+	return ret_cnt;
+}
+
 int raw_len_raw(struct session *ses, char *str, int start, int end)
 {
 	if (end == -1)
@@ -290,8 +349,10 @@ char *str_ins_str(struct session *ses, char **str, char *ins, int str_start, int
 	ins_raw_len = raw_len_str(ses, ins, 0, str_end - str_start);
 
 	raw_start = raw_len_str_min(ses, *str, 0, str_start);
+
 	raw_len   = str_len(*str);
-	raw_end   = raw_len_str_min(ses, *str, 0, str_end);
+//	raw_end   = raw_len_str_min(ses, *str, 0, str_end);
+	raw_end   = raw_len_str_opt(ses, *str, 0, str_end);
 
 	tmp = (*str)[raw_end];
 
@@ -301,6 +362,8 @@ char *str_ins_str(struct session *ses, char **str, char *ins, int str_start, int
 
 	(*str)[raw_end] = tmp;
 
+//	*old = 0;
+
 	col_len = strlen(old);
 
 	str_resize(str, ins_raw_len + col_len + 1);

+ 42 - 18
src/substitute.c

@@ -1991,25 +1991,49 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 						case 'x':
 							if (pti[1] && pti[2])
 							{
-								if (pti[1] == '0' && pti[2] == '0' && pti[3] == 0)
-								{
-									pti += 2;
-									DEL_BIT(flags, SUB_EOL);
-									DEL_BIT(flags, SUB_LNF);
-								}
-								else
-								{
-									pti++;
-									*pto++ = hex_number_8bit(pti);
-									pti++;
-								}
+								*pto++ = hex_number_8bit(pti + 1);
+								pti += 2;
 							}
 							break;
 
 						case 'u':
-							if (pti[1] && pti[2] && pti[3] && pti[4])
+							if (pti[1] == '{')
+							{
+								if (pti[2] && pti[3] && pti[4])
+								{
+									if (pti[4] == '}')
+									{
+										pto += unicode_8_bit(pti + 2, pto);
+										pti += 4;
+									}
+									else if (pti[5] == '}')
+									{
+										pto += unicode_12_bit(pti + 2, pto);
+										pti += 5;
+									}
+									else if (pti[5] && pti[6])
+									{
+										if (pti[6] == '}')
+										{
+											pto += unicode_16_bit(pti + 2, pto);
+											pti += 6;
+										}
+										else if (pti[7] == '}')
+										{
+											pto += unicode_20_bit(pti + 2, pto);
+											pti += 7;
+										}
+										else if (pti[7] && pti[8] == '}')
+										{
+											pto += unicode_21_bit(pti + 2, pto);
+											pti += 8;
+										}
+									}
+								}
+							}
+							else if (pti[1] && pti[2] && pti[3] && pti[4])
 							{
-								pto += unicode_16_bit(&pti[1], pto);
+								pto += unicode_16_bit(pti + 1, pto);
 								pti += 4;
 							}
 							break;
@@ -2017,7 +2041,7 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 						case 'U':
 							if (pti[1] && pti[2] && pti[3] && pti[4] && pti[5] && pti[6])
 							{
-								pto += unicode_21_bit(&pti[1], pto);
+								pto += unicode_21_bit(pti + 1, pto);
 								pti += 6;
 							}
 							break;
@@ -2029,14 +2053,14 @@ int substitute(struct session *ses, char *string, char *result, int flags)
 						case '0':
 							if (pti[1] == 0)
 							{
+								*pto++ = 0;
 								DEL_BIT(flags, SUB_EOL);
 								DEL_BIT(flags, SUB_LNF);
 							}
 							else if (pti[1] && pti[2])
 							{
-								pti++;
-								*pto++ = oct_number(pti);
-								pti++;
+								*pto++ = oct_number(pti + 1);
+								pti += 2;
 							}
 							break;
 

+ 4 - 2
src/tables.c

@@ -36,7 +36,7 @@ struct list_type list_table[LIST_MAX] =
 	{    "CLASS",             "CLASSES",            SORT_PRIORITY,    2, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_INHERIT                                 },
 	{    "COMMAND",           "COMMANDS",           SORT_APPEND,      1, 0, 0, LIST_FLAG_MESSAGE                                                                  },
 	{    "CONFIG",            "CONFIGS",            SORT_ALPHA,       2, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
-	{    "DELAY",             "DELAYS",             SORT_STABLE,       2, 2, 3, LIST_FLAG_MESSAGE|LIST_FLAG_READ                                                   },
+	{    "DELAY",             "DELAYS",             SORT_STABLE,      2, 2, 3, LIST_FLAG_MESSAGE|LIST_FLAG_READ                                                   },
 	{    "EVENT",             "EVENTS",             SORT_ALPHA,       2, 2, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
 	{    "FUNCTION",          "FUNCTIONS",          SORT_ALPHA,       2, 2, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
 	{    "GAG",               "GAGS",               SORT_ALPHA,       1, 0, 0, LIST_FLAG_MESSAGE|LIST_FLAG_READ|LIST_FLAG_WRITE|LIST_FLAG_CLASS|LIST_FLAG_INHERIT },
@@ -1084,6 +1084,8 @@ struct event_type event_table[] =
 	{    "SESSION DISCONNECTED",                   0, EVENT_FLAG_SESSION,  "SESSION",   "a session disconnects"      },
 	{    "SESSION TIMED OUT",                      0, EVENT_FLAG_SESSION,  "SESSION",   "a session doesn't connect"  },
 	{    "SHORT-CLICKED",                          0, EVENT_FLAG_MOUSE,    "MOUSE",     "mouse is short-clicked"     },
+	{    "SIGHUB",                                 0, EVENT_FLAG_SYSTEM,   "SYSTEM",    "Terminal has closed"        },
+	{    "SIGUSR",                                 0, EVENT_FLAG_SYSTEM,   "SYSTEM",    "Raised SIGUSR1 or SIGUSR2"  },
 	{    "SWIPED",                                 0, EVENT_FLAG_MOUSE,    "MOUSE",     "mouse swipe"                },
 	{    "SYSTEM CRASH",                           0, EVENT_FLAG_SYSTEM,   "SYSTEM",    "system crash"               },
 	{    "SYSTEM ERROR",                           0, EVENT_FLAG_SYSTEM,   "SYSTEM",    "system errors"              },
@@ -1157,7 +1159,7 @@ struct line_type line_table[] =
 
 struct log_type log_table[] =
 {
-	{    "APPEND",            log_append,          "Start logging, appending to give file."         },
+	{    "APPEND",            log_append,          "Start logging, appending to given file."        },
 	{    "INFO",              log_info,            "Some logging related info."                     },
 	{    "OFF",               log_off,             "Stop logging."                                  },
 	{    "OVERWRITE",         log_overwrite,       "Start logging, overwriting the given file."     },

+ 2 - 0
src/text.c

@@ -220,6 +220,8 @@ int word_wrap(struct session *ses, char *textin, char *textout, int flags, int *
 		{
 			cur_height++;
 
+//			printf("cur_col %d tab %d wrap %d pti %d\n", ses->cur_col, tab, wrap, *pti);
+
 			if (HAS_BIT(ses->config_flags, CONFIG_FLAG_WORDWRAP))
 			{
 				if (ses->cur_col - cur_space >= 15 || wrap <= 20 || !SCROLL(ses))

+ 18 - 18
src/tintin.h

@@ -134,7 +134,6 @@
 #define SORT_ALNUM                       2
 #define SORT_STABLE                      3
 #define SORT_APPEND                      4
-#define SORT_DELAY                       5
 
 #define SEEK_MATCH                       0
 #define SEEK_REPLACE                     1
@@ -208,7 +207,7 @@
 #define STRING_SIZE        2 * BUFFER_SIZE
 
 #define CLIENT_NAME              "TinTin++"
-#define CLIENT_VERSION           "2.02.10 "
+#define CLIENT_VERSION           "2.02.11 "
 
 
 #define XT_E                            0x27
@@ -381,6 +380,7 @@ enum operators
 #define BV38 (1LL << 37)
 #define BV39 (1LL << 38)
 #define BV40 (1LL << 39)
+#define BV41 (1LL << 40)
 
 
 #define BUFFER_FLAG_GREP                  BV01
@@ -609,7 +609,7 @@ enum operators
 #define TINTIN_FLAG_HIDDENCURSOR      BV11
 #define TINTIN_FLAG_LOCAL             BV12
 #define TINTIN_FLAG_PRESERVEMACRO     BV13
-
+#define TINTIN_FLAG_WINCHUPDATE       BV14
 
 #define CONFIG_FLAG_AUTOPATCH         BV01
 #define CONFIG_FLAG_AUTOPROMPT        BV02
@@ -642,8 +642,9 @@ enum operators
 #define SES_FLAG_SCANABORT            BV10
 #define SES_FLAG_SCROLLSPLIT          BV11
 #define SES_FLAG_SNOOP                BV12
-#define SES_FLAG_SPLIT                BV13
-#define SES_FLAG_UPDATEVTMAP          BV14
+#define SES_FLAG_SNOOPSCROLL          BV13
+#define SES_FLAG_SPLIT                BV14
+#define SES_FLAG_UPDATEVTMAP          BV15
 
 
 #define TELOPT_FLAG_TELNET            BV01
@@ -1289,7 +1290,6 @@ struct split_data
 	int                     bot_col;
 };
 
-
 struct scroll_data
 {
 	struct buffer_data   ** buffer;
@@ -1475,6 +1475,8 @@ struct exit_data
 struct search_data
 {
 	int                     vnum;
+	int                     min;
+	int                     max;
 	unsigned short          stamp;
 	char                  * arg;
 	pcre                  * name;
@@ -1602,7 +1604,7 @@ struct window_data
 #define DO_LOG(log)                        void log (struct session *ses, char *arg, char *arg1, char *arg2)
 #define DO_MAP(map)                        void map (struct session *ses, char *arg, char *arg1, char *arg2)
 #define DO_PATH(path)                     void path (struct session *ses, char *arg)
-#define DO_PORT(port)          struct session *port (struct session *ses, char *arg1, char *arg2, char *arg)
+#define DO_PORT(port)          struct session *port (struct session *ses, char *arg, char *arg1, char *arg2)
 
 
 /*
@@ -1624,7 +1626,7 @@ typedef struct session *LINE    (struct session *ses, char *arg, char *arg1, cha
 typedef void            MAP     (struct session *ses, char *arg, char *arg1, char *arg2);
 typedef void            MSDP    (struct session *ses, struct port_data *buddy, int index);
 typedef void            PATH    (struct session *ses, char *arg);
-typedef struct session *PORT    (struct session *ses, char *arg1, char *arg2, char *arg);
+typedef struct session *PORT    (struct session *ses, char *arg, char *arg1, char *arg2);
 
 
 /*
@@ -1964,6 +1966,9 @@ extern DO_COMMAND(do_cursor);
 
 int inputline_cur_row(void);
 int inputline_cur_col(void);
+int inputline_cur_off(void);
+int inputline_cur_str_len(void);
+int inputline_max_str_len(void);
 int inputline_max_row(void);
 int inputline_editor(void);
 int inputline_rows(struct session *ses);
@@ -2110,23 +2115,16 @@ extern DO_MAP(map_write);
 #ifndef __TT_MATH_H__
 #define __TT_MATH_H__
 
+extern long double mathexp(struct session *ses, char *str, char *result, int seed);
 extern int is_math(struct session *ses, char *str);
-extern int get_ellipsis(struct listroot *root, char *name, int *min, int *max);
+extern int get_ellipsis(struct session *ses, unsigned int size, char *name, int *min, int *max);
 extern long double get_number(struct session *ses, char *str);
 extern unsigned long long get_ulong(struct session *ses, char *str);
 extern long double get_double(struct session *ses, char *str);
 extern void get_number_string(struct session *ses, char *str, char *result);
 extern long double mathswitch(struct session *ses, char *left, char *right);
-extern void mathexp(struct session *ses, char *str, char *result, int seed);
-extern int mathexp_tokenize(struct session *ses, char *str, int seed, int debug);
-extern void mathexp_level(struct session *ses, struct link_data *node);
-extern void mathexp_compute(struct session *ses, struct link_data *node);
-extern long double tinternary(char *arg1, char *arg2);
 extern long double tintoi(char *str);
 extern unsigned long long tintou(char *str);
-extern long double tincmp(char *left, char *right);
-extern long double tineval(struct session *ses, char *left, char *right);
-extern long double tindice(struct session *ses, char *left, char *right);
 
 
 #endif
@@ -2697,7 +2695,6 @@ extern DO_COMMAND(do_snoop);
 extern DO_COMMAND(do_zap);
 
 extern struct session *session_command(char *arg, struct session *ses);
-extern void show_session(struct session *ses, struct session *ptr);
 extern struct session *find_session(char *name);
 extern struct session *newactive_session(void);
 extern struct session *activate_session(struct session *ses);
@@ -2955,7 +2952,10 @@ extern unsigned long long hex_number_64bit(char *str);
 extern unsigned int hex_number_32bit(char *str);
 extern int hex_number_8bit(char *str);
 extern int oct_number(char *str);
+extern int unicode_8_bit(char *str, char *out);
+extern int unicode_12_bit(char *str, char *out);
 extern int unicode_16_bit(char *str, char *out);
+extern int unicode_20_bit(char *str, char *out);
 extern int unicode_21_bit(char *str, char *out);
 extern unsigned long long utime(void);
 extern time_t get_time(struct session *ses, char *str);

+ 2 - 0
src/tokenize.c

@@ -289,6 +289,8 @@ void handleswitchtoken(struct session *ses, struct scriptnode *token)
 {
 	char arg[BUFFER_SIZE];
 
+//	substitute(ses, token->str, arg, SUB_VAR|SUB_FUN);
+
 	mathexp(ses, token->str, arg, 0);
 
 	RESTRING(token->data->str, arg);

+ 2 - 2
src/trigger.c

@@ -244,7 +244,7 @@ DO_COMMAND(do_button)
 	struct listnode *node;
 	int index;
 
-	arg = sub_arg_in_braces(ses, arg, arg1, GET_ALL, SUB_VAR|SUB_FUN);
+	arg = get_arg_in_braces(ses, arg, arg1, GET_ALL);
 	arg = get_arg_in_braces(ses, arg, arg2, GET_ALL);
 	arg = get_arg_in_braces(ses, arg, arg3, GET_ALL);
 
@@ -272,7 +272,7 @@ DO_COMMAND(do_button)
 
 		for (index = 0 ; index < 4 ; index++)
 		{
-			arg = get_arg_in_braces(ses, arg, arg2, GET_ONE);
+			arg = sub_arg_in_braces(ses, arg, arg2, GET_ONE, SUB_VAR|SUB_FUN);
 
 			node->val16[index] = (short) get_number(ses, arg2);
 

+ 29 - 0
src/update.c

@@ -374,6 +374,24 @@ void update_sessions(void)
 		sleep = 0;
 	}
 
+	if (HAS_BIT(gtd->flags, TINTIN_FLAG_WINCHUPDATE))
+	{
+		DEL_BIT(gtd->flags, TINTIN_FLAG_WINCHUPDATE);
+
+		init_terminal_size(gts);
+
+		for (ses = gts->next ; ses ; ses = ses->next)
+		{
+			init_terminal_size(ses);
+
+			if (HAS_BIT(ses->telopts, TELOPT_FLAG_NAWS))
+			{
+				SET_BIT(ses->telopts, TELOPT_FLAG_UPDATENAWS);
+			}
+		}
+		winch_daemon();
+	}
+
 	if (gts->next)
 	{
 		FD_ZERO(&read_fd);
@@ -474,6 +492,17 @@ void update_sessions(void)
 				}
 				else
 				{
+					if (HAS_BIT(ses->flags, SES_FLAG_SNOOPSCROLL))
+					{
+						if (HAS_BIT(ses->scroll->flags, SCROLL_FLAG_RESIZE))
+						{
+							buffer_refresh(ses, "", "", "");
+						}
+						else
+						{
+							print_scroll_region(ses);
+						}
+					}
 					buffer_end(ses, "", "", "");
 				}
 

+ 83 - 129
src/utils.c

@@ -83,30 +83,14 @@ int hex_number_8bit(char *str)
 {
 	int value = 0;
 
-	if (str)
+	if (*str)
 	{
-		if (is_digit(*str))
-		{
-			value += 16 * (*str - '0');
-		}
-		else
-		{
-			value += 16 * (toupper((int) *str) - 'A' + 10);
-		}
-		str++;
+		value += 16 * hex_digit(str++);
 	}
 
-	if (str)
+	if (*str)
 	{
-		if (is_digit(*str))
-		{
-			value += *str - '0';
-		}
-		else
-		{
-			value += toupper((int) *str) - 'A' + 10;
-		}
-		str++;
+		value += hex_digit(str++);
 	}
 
 	return value;
@@ -116,7 +100,7 @@ int oct_number(char *str)
 {
 	int value = 0;
 
-	if (str)
+	if (*str)
 	{
 		if (is_digit(*str))
 		{
@@ -125,7 +109,7 @@ int oct_number(char *str)
 		str++;
 	}
 
-	if (str)
+	if (*str)
 	{
 		if (is_digit(*str))
 		{
@@ -137,64 +121,63 @@ int oct_number(char *str)
 	return value;
 }
 
-int unicode_16_bit(char *str, char *out)
+int unicode_8_bit(char *str, char *out)
 {
 	int val = 0;
 	unsigned char *pto = (unsigned char *) out;
 
-	val += 4096 * hex_digit(str);
+	val += 16 * hex_digit(str++);
+	val += hex_digit(str++);
 
-/*
-	if (is_digit(*str))
+	if (val < 128)
 	{
-		val += 4096 * (*str - '0');
+		*pto++ = val;
+		*pto++ = 0;
+		return 1;
 	}
 	else
 	{
-		val += 4096 * (toupper((int) *str) - 'A' + 10);
+		*pto++ = 192 + val / 64;
+		*pto++ = 128 + val % 64;
+		*pto++ = 0;
+		return 2;
 	}
-*/
-	str++;
+}
 
-	val += 256 * hex_digit(str);
+int unicode_12_bit(char *str, char *out)
+{
+	int val = 0;
+	unsigned char *pto = (unsigned char *) out;
 
-/*
-	if (is_digit(*str))
-	{
-		val += 256 * (*str - '0');
-	}
-	else
-	{
-		val += 256 * (toupper((int) *str) - 'A' + 10);
-	}
-*/
-	str++;
+	val += 256 * hex_digit(str++);
+	val += 16 * hex_digit(str++);
+	val += hex_digit(str++);
 
-	val += 16 * hex_digit(str);
-/*
-	if (is_digit(*str))
+	if (val < 128)
 	{
-		val += 16 * (*str - '0');
+		*pto++ = val;
+		*pto++ = 0;
+		return 1;
 	}
 	else
 	{
-		val += 16 * (toupper((int) *str) - 'A' + 10);
+		*pto++ = 192 + val / 64;
+		*pto++ = 128 + val % 64;
+		*pto++ = 0;
+		return 2;
 	}
-*/
-	str++;
+}
 
-	val += hex_digit(str);
-/*
-	if (is_digit(*str))
-	{
-		val += (*str - '0');
-	}
-	else
-	{
-		val += (toupper((int) *str) - 'A' + 10);
-	}
-*/
-	str++;
+
+int unicode_16_bit(char *str, char *out)
+{
+	int val = 0;
+	unsigned char *pto = (unsigned char *) out;
+
+	val += 4096 * hex_digit(str++);
+	val += 256 * hex_digit(str++);
+	val += 16 * hex_digit(str++);
+	val += hex_digit(str++);
 
 	if (val < 128)
 	{
@@ -219,88 +202,59 @@ int unicode_16_bit(char *str, char *out)
 	}
 }
 
-int unicode_21_bit(char *str, char *out)
+int unicode_20_bit(char *str, char *out)
 {
 	int val = 0;
 	unsigned char *pto = (unsigned char *) out;
 
-	if (str)
-	{
-		if (is_digit(*str))
-		{
-			val += 1048576 * (*str - '0');
-		}
-		else
-		{
-			val += 1048576 * (toupper((int) *str) - 'A' + 10);
-		}
-		str++;
-	}
+	val += 65536 * hex_digit(str++);
+	val += 4096 * hex_digit(str++);
+	val += 256 * hex_digit(str++);
+	val += 16 * hex_digit(str++);
+	val += hex_digit(str++);
 
-	if (str)
+	if (val < 128)
 	{
-		if (is_digit(*str))
-		{
-			val += 65536 * (*str - '0');
-		}
-		else
-		{
-			val += 65536 * (toupper((int) *str) - 'A' + 10);
-		}
-		str++;
+		*pto++ = val;
+		return 1;
 	}
-
-	if (str)
+	else if (val < 4096)
 	{
-		if (is_digit(*str))
-		{
-			val += 4096 * (*str - '0');
-		}
-		else
-		{
-			val += 4096 * (toupper((int) *str) - 'A' + 10);
-		}
-		str++;
+		*pto++ = 192 + val / 64;
+		*pto++ = 128 + val % 64;
+		*pto++ = 0;
+		return 2;
 	}
-
-	if (str)
+	else if (val < 65536)
 	{
-		if (is_digit(*str))
-		{
-			val += 256 * (*str - '0');
-		}
-		else
-		{
-			val += 256 * (toupper((int) *str) - 'A' + 10);
-		}
-		str++;
+		*pto++ = 224 + val / 4096;
+		*pto++ = 128 + val / 64 % 64;
+		*pto++ = 128 + val % 64;
+		*pto++ = 0;
+		return 3;
 	}
-
-	if (str)
+	else
 	{
-		if (is_digit(*str))
-		{
-			val += 16 * (*str - '0');
-		}
-		else
-		{
-			val += 16 * (toupper((int) *str) - 'A' + 10);
-		}
-		str++;
+		*pto++ = 240 + val / 262144;
+		*pto++ = 128 + val / 4096 % 64;
+		*pto++ = 128 + val / 64 % 64;
+	        *pto++ = 128 + val % 64;
+		*pto++ = 0;
+	        return 4;
 	}
+}
 
-	if (str)
-	{
-		if (is_digit(*str))
-		{
-			val += (*str - '0');
-		}
-		else
-		{
-			val += (toupper((int) *str) - 'A' + 10);
-		}
-		str++;
-	}
+int unicode_21_bit(char *str, char *out)
+{
+	int val = 0;
+	unsigned char *pto = (unsigned char *) out;
+
+	val += 1048576 * hex_digit(str++);
+	val += 65536 * hex_digit(str++);
+	val += 4096 * hex_digit(str++);
+	val += 256 * hex_digit(str++);
+	val += 16 * hex_digit(str++);
+	val += hex_digit(str++);
 
 	if (val < 128)
 	{

+ 15 - 5
src/variable.c

@@ -695,7 +695,7 @@ void thousandgroupingstring(struct session *ses, char *str)
 
 	get_number_string(ses, str, strold);
 
-	cnt1 = strlen(strold);
+	cnt1 = strlen(strold) - 1;
 	cnt2 = BUFFER_SIZE / 2;
 	cnt4 = strchr(strold, '.') ? 1 : 0;
 
@@ -724,6 +724,7 @@ void thousandgroupingstring(struct session *ses, char *str)
 	return;
 }
 
+/*
 void chronosgroupingstring(struct session *ses, char *str)
 {
 	char *sign = "-";
@@ -763,6 +764,7 @@ void chronosgroupingstring(struct session *ses, char *str)
 		sprintf(str, "%s%d:%02d", sign, minutes, seconds);
 	}
 }
+*/
 
 void metricgroupingstring(struct session *ses, char *str)
 {
@@ -885,7 +887,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_LIT|SUB_ESC);
+	arg = sub_arg_in_braces(ses, str, arg1, GET_ALL, SUB_COL|SUB_ESC);
 
 	if (*arg == COMMAND_SEPARATOR)
 	{
@@ -1440,7 +1443,8 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						break;
 
 					case 'C':
-						chronosgroupingstring(ses, arglist[i]);
+						tintin_printf2(ses, "\e[1;31m#echo/#format %%C please use #screen {get} {cols} to get screen width.");
+//						chronosgroupingstring(ses, arglist[i]);
 						break;
 
 					case 'D':
@@ -1465,8 +1469,14 @@ void format_string(struct session *ses, char *format, char *arg, char *out)
 						break;
 
 					case 'R':
-						tintin_printf2(ses, "\e[1;31m#echo/#format %%R please use #screen {get} {rows} to get screen height.");
-						sprintf(arglist[i], "%d", gtd->screen->rows);
+						if (*arglist[i] == 0)
+						{
+							sprintf(arglist[i], "%d", gtd->screen->rows);
+						}
+						else
+						{
+							sprintf(arglist[i], "%d", get_row_index_arg(ses, arglist[i]));
+						}
 						break;
 
 					case 'S':

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác