Ticol Manual - Copyright (C) M Shaw 2010-2023

      _______          __         ___  ___ ____                                 
     /_  __(_)______  / / _  __  <  / |_  /_  /                                 
      / / / / __/ _ \/ / | |/ /  / / / __/ / /                                  
     /_/ /_/\__/\___/_/  |___/  /_(_)____//_/                                   

See any associated README.TXT for latest updates and amendments

Use: help <command> or 'exit' to exit the interpreter

Type the following:for help on a particular command, e.g.

    help contents              help print
    help debugging             help load
    help faq                   help run
    help flow control          help tutorial
    help licence               help tcl
    help math                  help troubleshooting
    help syntax                help ticol

    http://www.kerys.co.uk/ticol

Alphabetic Index of Built-In Ticol Commands

Code used to generate this list:

    foreach x [commands] { puts $x }                                            
    puts "[lcount [commands]] commands"                                         

Count the commands using:

    puts [lcount [commands]]                                                    

Redirect to a file in Windows using:

    ticol.exe ; "foreach x [commands] {puts $x}" /na > cmdlist.txt              

 .
 $
 ${}
 ::
 !
 !=
 %
 &
 &&
 &=
 *
 **
 *=
 +
 ++
 +=
 -
 --
 -=
 /
 /=
 <
 <<
 <=
 <=>
 ==
 >
 >=
 >>
 ?
 @
 K
 ^
 ^=
 add
 addressof
 addressofb
 addslash
 after
 alias
 all
 any
 append
 apply
 args
 arity
 array
 array_set
 asc
 assert
 at_exit
 atof
 autoexec
 base64
 baseconv
 beep
 big
 bigtext
 binary
 bintoint
 bool
 box
 bit
 break
 calc
 call
 carray
 catch
 cd
 cd..
 ceil
 cgi
 chain
 char
 charcount
 chdir
 chomp
 chr
 clear
 clearprocs
 clipboard
 clock
 close
 clreol
 cls
 cmdcount
 cmp
 comma
 commands
 concat
 console
 content
 const
 continue
 convbase
 copyfile
 cpu
 cscript
 ctime_to_double
 cvn
 date
 date_to_ctime
 day_of_week
 debug
 dec
 decr
 decrypt
 defined
 dict
 die
 dim
 diskfree
 diskstat
 disktype
 div
 do while
 do until
 doevents
 double
 double_to_ctime
 dummy
 dump
 dumpmem
 dumpvariant
 echo
 elevate
 empty
 encrypt
 enum
 eof
 eq
 eqi
 eqn
 error
 escape
 eval
 event
 exec
 exit
 expand
 explain
 expr
 extract
 false
 fib
 file
 filesize
 filter
 find
 float
 flush
 fmod
 for
 foreach
 format
 fpart
 fraction
 fromhex
 funct
 functions
 ge
 gei
 get_folder
 get_win_error
 getcwd
 getenv
 get_fileopen
 get_filesave
 getkey
 gets
 getsid
 glob
 global
 gosub
 goto
 goto_block
 gotoxy
 gt
 gti
 halt
 help
 hextoint
 highlight
 hostname
 html
 http
 if
 in
 incr
 index
 info
 inkey
 input
 input_csv
 inputbox
 inrange
 inspect
 instr
 int
 integer_to_words (plugin)
 integral
 inttobin
 inttohex
 inttooct
 ip_to_long
 is_admin
 is_command
 is_const
 is_date
 is_dir
 is_dst
 is_elevated
 is_file
 is_leapyear
 is_list
 is_mod
 is_null
 is_numeric
 is_pointer
 is_proc
 is_set
 item
 join
 json_to_list
 lappend
 lassign
 lastchar
 lcount
 le
 left
 lei
 len
 let
 level
 lib
 lindex
 link_to_path
 linkedlist
 list
 list_to_array
 llength
 lmap
 load
 logo
 long
 long_to_ip
 loop
 lrand
 lrange
 ls
 lsearch
 lset
 lsort
 lt
 lti
 makestr
 man
 map
 max
 md
 md5
 md5_file
 mean
 median
 mem_used
 memset
 mid
 mids
 min
 mkdir
 mkdirs
 mkn
 mktemp
 mod
 mode
 movefile
 msgbox
 mul
 ne
 nei
 new
 newline
 ni
 normal_path
 now
 octtoint
 ofaddress
 ofaddressb
 on
 on_error
 open
 option
 pause
 pid
 pipe
 play_wav
 precision
 printf
 proc
 procs
 put
 puts
 pwd
 randomstr
 range
 read
 readfile
 ren
 rename
 replacechar
 resume
 return
 rewind
 right
 rinstr
 rnd
 rot13
 round
 run
 sc
 scan
 sci
 screen
 set
 setat
 setb
 setc
 setenv
 setl
 setr
 setresult
 shell
 short
 signed
 sizeof
 sleep
 slice
 sockping
 source
 spawn
 split
 srand
 stack
 static
 stop
 stop_wav
 store
 strchr
 string
 stripto
 striptor
 strptr
 strsplit
 strstr
 strstrr
 strto
 strtok
 strtor
 struct
 sub
 subst
 swap
 switch
 sysfree
 tab
 tell
 test
 textcolor
 ticks
 time
 timer
 tohex
 tokenise
 trace
 trimprefix
 trimsuffix
 true
 trunc
 try
 type
 undef
 unescape
 unlink
 unset
 unsigned
 unwrap
 uplevel
 upvar
 urldecode
 urlencode
 username
 variable
 varname
 varray
 vars
 volume
 vsplit
 vwait
 waitkey
 walk
 wallpaper
 watch
 wchar_length
 wchar_to_string
 while
 wide
 win_error
 wordcount
 words_to_integer (plugin)
 wrap
 writefile
 |
 |=
 ||
 ~

See also: commands by function, Ticol, Tcl, faq

Commands Grouped by Functionality

    Data Types and Type Manipulation
    Database
    Date and Time
    Debugging and Introspection
    DLL and Struct Interface
    Encryption, Compression, Randomness and MD5
    File and Disk and I/O
    Flow Control
    Event Handling (Threaded compile of Ticol)
    HTML and Internet
    Information and Environment
    Languages
    List Handling
    Logic
    Macro
    Math
    Memory
    Network
    Program Execution
    String
    User interaction and Display
    Windows System Interaction

 Data Types and Type Manipulation                                               

 Relational: > < >= <= == != 
 array
 array_to_list
 array_set
 base64
 bit
 carray
 chain
 const
 convbase
 ctime_to_double
 cvn
 dict
 dim
 enum
 eq
 eqi
 eqn
 escape
 fromhex
 ge
 gei
 global
 gt
 gti
 hextoint
 inttobin
 inttohex
 inttooct
 is_const
 is_mod
 is_null
 is_numeric
 is_proc
 is_set
 json_to_list
 le
 lei
 let
 lei
 let
 list_to_array
 linkedlist (Linked list plugin)
 long
 long_to_ip
 le
 lt
 lti
 map
 mkn
 ne
 nei
 new
 octtoint
 rtf (RTF plugin)
 sc
 set
 short
 signed
 sizeof
 stack
 static
 subst
 swap
 tohex
 type
 unset
 unsigned
 variable
 varname
 varray
 vsplit
 wchar_length
 wchar_to_string
 wide

 Database                                                                       

 db    (Ticol DB plugin)
 sql   (SQLite plugin)
 xbase (DBase 3/4 plugin)

 Date and Time                                                                  

 clock
 ctime_to_double
 date
 date_to_ctime
 day_of_week
 double_to_ctime
 is_date
 is_dst
 is_leapyear
 ticks
 time
 timer
 now

 Debugging and Introspection                                                    

 args
 array walk
 assert
 catch
 cmdcount
 debug
 dummy
 dump
 dumpvariant
 empty
 halt
 info
 inspect
 mem_used
 resume
 sc
 time
 trace
 try
 type
 vars
 walk
 watch

 DLL and Struct Interface                                                       

 ->  Dereference a struct field
 =>  Set (assign) a struct field value
 =>> Dereference then evaluate
 addressof
 addressofb
 class
 new
 ofaddress
 ofaddressb
 setb
 strptr
 struct

 Encryption, Compression, Randomness and MD5                                    

 crc (CRC plugin)
 decrypt
 encrypt
 file_crc (CRC plugin)
 md5
 md5_file
 randomstr
 rle (RLE plugin 
 rnd
 rot13 (Misc plugin 
 srand
 zip (ZIP plugin 

 File and Disk and I/O                                                          

 addslash
 cd
 cd..
 chdir
 close
 copyfile
 diskfree
 diskstat
 disktype
 eof
 file
 filesize
 flush
 getcwd
 get_fileopen
 get_filesave
 glob
 ini (INI Plugin)
 input_csv
 is_dir
 is_file
 link_to_path
 ls
 md
 mkdir
 mkdirs
 mktemp
 movefile
 normal_path
 open
 pwd
 read
 readfile
 ren
 rename
 rewind
 tell
 unlink
 volume
 writefile

 Flow Control                                                                   

 {*}
 after
 arity
 at_exit
 break
 call
 catch
 clear
 clearprocs
 continue
 defined
 die
 do while
 do until
 doevents
 error
 exit
 expand
 for
 foreach
 funct
 gosub
 goto
 goto_block
 halt
 if
 is_proc
 level
 loop
 on
 on_error
 proc
 procs
 resume
 return
 sleep
 stop
 sub
 switch
 try
 undef
 uplevel
 upvar
 vwait
 while

 Event Handling (Threaded compile of Ticol)                                     

 after
 vwait
 every (script)
 at (script)

 HTML and Internet                                                              

 cgi (Plugin)
 html (Plugin)
 http
 urldecode
 urlencode

 Information and Environment                                                    

 cpu
 info
 is_admin
 is_command
 is_elevated
 is_pointer
 $tcl_date
 $tcl_config
 $tcl_cli
 $tcl_threaded
 $tcl_version
 $tcl_precision
 $env
 $argc
 $argv
 $win64

 Languages                                                                      

 bf (Plugin)
 asm (Plugin)
 cscript (Plugin)

 List Handling                                                                  

 apply
 array_to_list
 in
 is_list
 lappend
 lassign
 lcount
 lindex
 linkedlist (Plugin)
 lappend
 lassign
 lcount
 list
 list_to_array
 llength
 lmap
 lrand
 lrange
 lsearch
 lset
 lsort
 unwrap
 ni

 Logic                                                                          

 Logical and/or &&, ||
 Bitwise and/or &, |
 Logical not !
 Bitwise not ~
 Combined: &=, |=
 all
 any
 bool
 false ($::false)
 true ($::true)
 xor

 Macro                                                                          

 #echo
 #else
 #endif
 #exec
 #exit
 #if
 #ifdef
 #ifndef
 #undef

 Math                                                                           

 Bitshift: << >> 
 Increment: ++
 Decrement: --
 Combinational: +=, -=, *=, /=, ^=
 atof
 baseconv
 big (big number plugin)
 binary
 bintoint
 calc
 ceil
 dec
 decr
 div
 double
 eval
 explain
 expr
 fib
 float
 fmod
 fpart
 fraction
 incr
 inrange
 int
 integer_to_words (plugin)
 integral
 Math operators: + - * / ** ^ %
 max
 mean
 median
 min
 mod
 mode
 mul
 pipe
 precision
 round
 sci
 trunc
 unescape
 words_to_integer (plugin)

 Memory                                                                         

 free
 malloc
 memcpy
 memset
 store

 Network                                                                        

 arp            (ARP plugin)
 cidr_match     (Misc plugin)
 dns            (DNS plugin)
 ftp            (FTP plugin)
 hostname
 ip_from_host   (Ping plugin)
 ip_match       (Misc plugin)
 ip_to_long
 is_mac         (Misc plugin)
 ping           (Ping plugin)
 rarp           (ARP plugin)
 sendmail       (SMTP mail plugin)
 sockping       (Socket ping plugin)

 Program Execution                                                              

 alias
 args
 autoexec
 cmdcount
 commands
 dump
 functions
 lib
 load
 option
 run
 source

 String                                                                         

 <=>
 add
 append
 asc
 char
 chr
 chomp
 cmp
 comma
 concat
 content
 extract
 filter
 index
 instr
 item
 join
 lastchar
 left
 makestr
 memset
 mid
 mids
 range
 replacechar
 right
 rinstr
 scan
 setat
 setc
 setl
 setr
 slice
 split
 strchr
 string
 stripto
 striptor
 strsplit
 strstr
 strstrr
 strto
 strtok
 strtor
 tab
 trimprefix
 trimsuffix
 wordcount
 wrap

 User interaction and Display                                                   

 beep
 box
 bigtext
 clreol
 cls
 echo
 find
 format
 get_folder
 get_fileopen
 get_filesave
 getkey
 gets
 gotoxy
 help
 inkey
 input
 inputbox
 logo
 man
 msgbox
 newline
 pause
 printf
 puts
 rainbow
 screen
 textcolor
 waitkey

 Windows System Interaction                                                     

 clipboard
 console
 elevate
 event
 exec
 get_win_error
 getenv
 getsid
 info
 mem_used
 pid
 play_wav
 reg (registry plugin)
 setenv
 shell
 spawn
 stop_wav
 sysfree
 username
 wallpaper
 win_error

See also: command list, Ticol, Tcl, faq

:: Double-colon (Scope symbol)

The double-colon symbol operates in a similar way to that found in C/C++

    ::varname 

    represents a variable in global (root) scope

    $::varname

    represents a dereferenced variable in global (root) scope

Ticol Tcl does not support operational namespaces but can emulate them by
allowing for the inclusion of pseudo-namespace prefixes such as...

    namespace::varname

    or

    $namespace::varname

Example:

In Tcl, unlike other programming languages, variables do not "inherit"
into procedures from higher-scope. Globals are visible if addressed using
a double colon prefix...

    set s "Hello world"                                                         
    proc foo {} {                                                               
       set s "Goodbye cruel"  # Declare a conflicting variable 's'              
       puts $::s              # Reference the global variable 's'               
    }                                                                           

Result:

    Hello world                                                                 

For partial/embedded substitution, a variable name prefix or any component
of a variable name may be isolated and forced by wrapping in curly braces. 
So to replace a variable placeholder which references a pseudo-namespace
you would use, for example (where q holds the pseudo-namespace value)

    ${q}::varname                                                               

This causes $q to be dereferenced in preference to $q::varname

Invalid single colons may currently be used as part of a variable name, but
these require wrapping the whole variable name in braces as follows...

    set foo:a 23     # This is not valid as a scope-delimted name               
    puts ${foo:a}    # Single colon requires full bracing                       
    puts $foo:a      # This will throw a trappable error                        

See also: variables, dereference, set, namespace, global

Ticol Tcl Architecture

This is a simplified block diagram of the unthreaded Ticol Tcl architecture
from high-level command inputs to lower-level execution

    +--+------------------------------------------------------------+           
    | 1| ticol.exe binary          |                                |           
    +--+---------------------------+--------------------------------+           
    | 2| Command Line Interpreter (CLI) or EXE Command Line         |           
    +--+---------------------------+--------------------------------+           
    | 3| Tcl script                | Automatic TCX script decoding  |           
    |  |                           |--------------------------------+           
    |  |                           | Obfuscation Decoder/Encoder    |           
    +--+---------------------------+--------------------------------+           
    | 4| Macro Preprocessor (MPP) script preprocessing/optimisation |           
    |  | [calc] expression simplification+optimisation              |           
    |  | Substitution of constants                                  |           
    |  | Macro generation+substitution, line numbers, syntax checks |           
    +--+---------------------------+--------------------------------+           
    | 5| Internal command registration                              |           
    +--+---------------------------+--------------------------------+           
    | 6| Tcl Parser                | Dynamic error checking         |           
    |  |                           |                                |           
    +--+---------------------------+--------------------------------+           
    | 7| [eval] variable substitution, command execution, trace etc.|           
    +--+---------------------------+--------------------------------+           
    | 8| [expr] or [calc] natural expression handling/ordering      |           
    +--+------------------------------------------------------------+           
    | 9| Registered commands, [proc] registration, [undef]          |           
    |  | Registered [lib] commands [lib calldll] plugin etc.        |           
    +--+---------------------------+--------------------------------+           
    |10| [proc]                    | Registered Tcl procs           |           
    +--+---------------------------+--------------------------------+           
    |11| Output,trace, errors, debugger                             |           
    +--+------------------------------------------------------------+           

The CLI or ticol.exe binary command line loads plaintext or 
obfuscated scripts with various command arguments. These are automatically
decoded using relevant credentials

Scripts are processed before execution by the Macro Preprocessor (MPP).
The resulting script content may register [proc] instances and interact
with any registered command once executed. The MPP may also execute
Tcl commands as part of the preprocessor operation

The Macro PreProcessor (MPP) performs a number of tasks, including
stripping out comments while preserving line numbers; processing the
internal macro language and optimising natural [calc] expressions into
native Tcl format, as well as performing basic sanity checks on the script
syntax including brace/quote parity checks and substitution of constants
such as \n or 0xfe. The MPP performs a lot of the "heavy lifting" to 
speed up interpreter execution.

All commands are evaluated internally by the internal 'eval()' function
which may also be called directly using [eval]. [eval] parses the script
content and breaks it up into commands and arguments, executes commands
and performs variable substitution. This is also where the debugger hooks
and tracing is performed and this is the core of the interpreter

[proc]s are registered or unregistered on-the-fly according to when 
commands are encountered. Nested procs are permitted and may be generated
dynamically

The [lib] command can load exported Tcl commands dynamically into the root
namespace where they may be called the same as any other command 
(See: lib interface)

See also: Ticol, Tcl, lib, Ticol, lib interface

Expand - Argument Expansion

 {*}    - Argument Expansion Operator
 expand - Argument Expansion Command

    command {*}{word ?word...?}
    string [expand command string...]

Expands a well-formed Tcl list into an argument array for a single
Tcl command. Note that [eval] is required to process multiple commands

The {*} operator operates only on well-formed Tcl lists, not Tcl script
blocks

Inline Tcl scripts are generally not well-suited to calling via this 
method unless carefully constructed. Semi-colon separated scripts are
not suitable for use as well-formed Tcl lists with expand. Instead, 
put such scripts within a [sub] or [proc] and resolve the expansion
reference to the name of this subroutine.

{*} makes each item in a well-formed Tcl list an individual argument of
the current command. Under normal circumstances, passing a list to a
command passes only one argument (i.e. the complete list). {*} causes
the list to be broken into separate arguments.

Backslash substitutions are performed as is normal for a list and
individual internal words may be surrounded by either braces or
double-quote characters. Its words are then added as individual
arguments to the command being substituted

Variables are not resolved as expansion is resolved before variables
are processed. [subst] may be used to force variable resolution on the 
list before passing to {*}

{*} is typically required with chained commands where the outer command
requires separate arguments but the inner one returns a string

The [dummy] command is useful for tracing results of {*} and to test or
debug an argument list.

    set s "puts {Hello world}"                                                  
    # This is will not work as {*} is processed before $s                       
    {*}$s                                                                       
    # This will work as expected. {*} is delayed till after $s                  
    expand $s                                                                   

Be extremely cautious not to accidentally use an unquoted "{*}" string
within your code where you do not intend it to expand arguments. e.g.
don't use, e.g. ...

    puts [unescape "[string repeat 80 {*}]\\highlight0\\cf0\\par "]             

as this will attempt to expand within the nested call to [string repeat]
and will lead to near impossible to debug errors

Supplying a whitespace character (tab or space) after {*} is unlawful

For instance:

    cmd a {*}{b [c]} d {*}{$e f {g h}}                                          

is equivalent to supplying:

    cmd a b {[c]} d {$e} f {g h}                                                

And results in the effective arguments:

    a
    b
    {[c]}
    d
    {$e}
    f
    g h

    being passed to a command

Example:

    set a "puts {Hello world}" # Set var to a command with args                 
    $a                         # Error: Bad attempt to dereference              
    {*}$a                      # Correct method of dereferencing                
    eval $a                    # This works just as well, however               
    eval {$a}                  # Results in an error                            

Results

    puts' is not recognized as an internal or external command ...              
    Hello world                                                                 
    Hello world                                                                 
    puts' is not recognized as an internal or external command ...              

Example:

    # Unset all variables 'a' to 'z' (supplying individual arguments)           
    unset {*}[split [range a-z]] -nocomplain                                    
    # This works just as well                                                   
    expand "unset [split [range a-z] ""] -nocomplain"                           
    # So does this                                                              
    eval "unset [split [range a-z]] -nocomplain"                                

Example:

    # Lambda like call with argument passed via [lmap]                          
    set square {lmap {x {expr $x * $x}}}                                        
    # {*} will break string into separate arguments                             
    puts [{*}$square 42]                                                        
    # This also works just as well                                              
    puts [eval "$square 42"]                                                    

Result:

    1764                                                                        
    1764                                                                        

Example:

    set e {info exists}                                                         
    # {*} will break string into separate arguments                             
    puts [{*}$e a]                                                              
    assert [{*}$e a] {== $_ 0} -v -line #__LINE__                               
    set a Hello                                                                 
    puts [{*}$e a]                                                              
    assert [{*}$e a] {== $_ 1} -v -line #__LINE__                               
    # This also works just as well                                              
    puts [expand "$e a"]                                                        

Results:

    0                                                                           
    1                                                                           
    1                                                                           

Example:

    set args [list range foobar 2 4]                                            
    puts [string {*}$args]                                                      
    # This works just as well                                                   
    puts [expand "string $args"]                                                
    # And so does this...                                                       
    puts [eval "string $args"]                                                  

Example:

Set script literal via 'command list' [...list...]
and execute variable contents

    set q "puts {1 2 3 4}"    # Or set q {puts {1 2 3 4}}                       
    {*}$q                                                                       

Results:

    1 2 3 4                                                                     

Example:

    # [mean] expects a series of arguments not a single Tcl list                
    # We can expand using {*}                                                   
    set l "234.34 23.11 37.333 3828.22"                                         
    puts [mean {*}$l]                                                           

Results:

    1030.750749999999900                                                        

See:
    http://wiki.tcl.tk/17158
    http://wiki.tcl.tk/19707

{*} and [expand] Differences
----------------------------
The [expand] command will supply a complete, pre-formed and fully-bounded
list to the interpreter. Thus it is more reliable. The {*} command has to
operate during the parsing of the entire script block so it is more
complex and problematic to detect the full bound of the relevant list
which is to be decoded. The possibility that the presented list may be
less well-formed has to be handled by {*}.

See also: subst, dummy

semi-colon

A semi-colon is used as a punctuator between groups of statements
It marks the absolute end of a command list and acts equivalent to a 
newline character. 

Example:

    set a 0;puts "hello";incr a;puts "a is $a"                                  
                                                                                
    # Is equivalent to                                                          
                                                                                
    set a 0                                                                     
    puts "hello"                                                                
    incr a                                                                      
    puts "a is $a"                                                              

Result:

    hello                                                                       
    a is 1                                                                      

Statements which require to pass multiple commands as a single argument
must be braced, thus: 

    some-command {arg1; arg2; arg3}                                             

otherwise the command string will be interpreted as:

    some-command arg1                                                           
    arg2                                                                        
    arg3                                                                        

This should be particularly noted with commands which take a script
element as an argument such as [after]

Command Line Use:

A semi-colon may also be used as the first argument to ticol.exe to specify
a "dummy" script file which is never executed. If specified then any 
remaining arguments will be evaluated by the Ticol Command line Interpreter
(CLI) within the same interpreter instance

Example Command Line:

    ticol.exe ; "set a 0;puts hello;incr a;puts \"a is $a\""                    

Result:

    hello                                                                       
    a is 1                                                                      

Example:

    ticol.exe ; "puts \"Hello\tworld\""                                         

Result:

    Hello world                                                                 

See also: syntax, Tcl

addslash

    addslash varname -var -separator <char> -unix
    string [addslash string]

Conditionally append a path separation character (slash) to a path string
The backslash character in the string will be escaped

Unix forward slash./ separators may be forced using the -unix argument
This will permit Unix paths and web URLs to be processed

Alternately, any character separator can be specified using the -separator
argument. It is recommended to specify only Windows \\ or Unix /.

Examples:

    puts [addslash C:]                                                          
    puts [addslash .]                                                           
    puts [addslash \\path\\to\\file]                                            

Results:

    C:\\                                                                        
    .\\                                                                         
    \\path\\to\\file\\                                                          

A common task is to take user input for an ambiguous path or filespec 
and to normalise this into a full path

Example:

    # If user enters "." or a path such as c:\foo then we normalise             
    # Note that we must 'escape' this new path if it contains '\' chars         
    # We store the normalised path for later use with say, [ls]                 
                                                                                
    if {eq [file extension $filespec] ""} {                                     
       set filespec [escape [file normalize $filespec]]                         
       set filespec [addslash $filespec]                                        
       set path $filespec                                                       
       append filespec "*.tcl"                                                  
    }                                                                           

See also: file, info

ofaddress, addressof (Tcl Variable Address)

    integer [addressof variable]
    string [ofaddress pointer-variable|address-literal-value -string]

[addressof] returns the address of a simple Tcl variable object (not the
variable's data buffer) and stores it as a string record of that address

Complex abstract data types such as structs or arrays are not supported.
The internal address of an array variable can't be returned as these are
stored as nodes in a hash table.

[ofaddress] performs the inverse operation. The two functions are provides
for use in conjunction with calldll and calldll_cdecl

Both commands are for use with [calldll*] only, and it is strongly advised
that Ticol variables are not manipulated using these commands

    [addressof]                [addressofb],[set],[$]  # Get address            
    |                          |                                                
    v                          v                                                
    +---------------------+    +---------------------+                          
    | Tcl Variable Object | -> | Variable Data Block |                          
    +---------------------+    +---------------------+                          
    |                          |                                                
    v                          v                                                
    [ofaddress]                [ofaddressb]            # Dereference            

Where a variable is passed to [ofaddress], the address stored in the
internal buffer is dereferenced and the result is returned. Where an
integer literal is passed to [ofaddress], the value is assumed to be
an address and it is dereferenced back to a buffer address 

By comparison with C/C++, [addressofb] returns the equivalent of LPSTR*
or LPPSTR (char**)

If a string value is being dereferenced the '-string' argument is
required, otherwise the string may be truncated.

Note that you cannot evaluate [ofaddress "literal-string"]
Such a value must be an integer pointer variable address value returned
by [addressof]

To return a string from pointer value set by [struct setb var] use: 

    [ofaddress $var]

To return string as a char array use:

    [ofaddress var]

When using [calldll*] the address of a Tcl variable buffer/string pointer
by value may be passed by using [addressofb variable] to pass the literal
string address (LPSTR). strptr is an alias for addressofb

For Ticol structs, member values refer to an offset in the struct data
block:

    struct s {a 5 b 10 c 20}                                                    

                                                                                
    Tcl var   Address                 Struct byte offset                        
    -----------------------------------------------------                       
    s      => 32542464 (0x1f08f00)   0                                          
    s.a    => 32542464 (0x1f08f00)   0                                          
    s.b    => 32542469 (0x1f08f05)   5                                          
    s.c    => 32542479 (0x1f08f0f)  15                                          

In the above example, both $s and $s.a is 32542464, $s.b is 32542469 etc.

[addressof] and [ofaddress] are useful for assigning values to structs 
where the size of the value to be stored is unknown. In such cases we
can store the variable address provided the variable is not reassigned.

See also: setb, ofaddressb, calldll, calldll_cdecl, struct

addressofb, strptr (Binary Buffer Address)

    integer [addressofb variable]
    string [strptr variable]

Inverse of [ofaddressb]

[addressofb] and [ofaddressb] are 'unsafe' and not recommended

Returns the address of the binary string buffer of a variable/string
[addressofb] (or [strptr]) returns the equivalent of LPSTR or (char*)
The command performs a similar function to VarPtr() in Visual BASIC

[addressofb] is for use with [calldll*] only, and it is strongly advised
that Ticol variables are not manipulated using this command

    [addressof]                [addressofb],[set],[$]                           
    |                          |                                                
    v                          v                                                
    +---------------------+    +-----------------------+                        
    | Tcl Variable Object | -> | Variable's Data Block |                        
    +---------------------+    +-----------------------+                        
    |                          |                                                
    v                          v                                                
    [ofaddress]                [ofaddressb]                                     

The [addressof] command, unlike [addressofb], returns the address of a
Ticol variable as a numeric string which can be dereferenced back using
ofaddress

However, this does not point to the variable's string buffer and is not,
therefore suitable to pass via a [calldll*] to a DLL. Instead, 
[addressofb] should be used to return the true string buffer address

Example:

    set a Hello                                                                 
                                                                                
    [addressof]                [addressofb],[set],[$]                           
    | 34602704                 | 34600096                                       
    v                          v                                                
    +---------------------+    +-----------------------+                        
    | a                   | -> | Hello                 |                        
    +---------------------+    +-----------------------+                        
    |                          |                                                
    v                          v                                                
    [ofaddress 34602704]       [ofaddressb 34600096]                            
     Hello                      Hello                                           

[addressofb] may be used to pass the address of a Tcl variable's string 
buffer to an external DLL where the contents will be recovered and used
in your program. Care should be taken to avoid buffer overflows. It is
preferable to use a struct when passing data to external DLLs

[strptr] is a friendly alias for [addressofb]

    Retrieve 2 byte short value:                 [ofaddressb var 2]
    Retrieve 4 byte long value:                  [ofaddressb var 4]
    Retrieve 8 byte int64 value:                 [ofaddressb var 8]
    Retrieve 256 byte struct 's':                [ofaddressb s 256]

Example:

    set timeout 0                                                               
    set datalen 64                                                              
    set buf [makestr 16]    # Create 16 byte buffer as Tcl variable             
    # Call ping32.dll, passing the binary address of the Tcl buffer             
    # data block, which will receive the return data                            
    calldll ping32 PingAPI google.com [addressofb buf]:4 datalen:2\             
       timeout:4 1000                                                           
    puts $buf                                                                   

Result:

    173.194.78.105                                                              

See also ofaddressb

See also: addressof, ofaddress, ofaddressb, set, setb, calldll

ofaddressb (Binary Buffer from Address)
 
    string [ofaddressb struct-variable ?byte-width?]

Inverse of [addressofb]

[addressofb] and [ofaddressb] are 'unsafe' and not recommended

Return a binary interpretation of data pointed to in a struct field by
the struct-variable. Normally a string interpretation of the memory 
location is returned

[ofaddressb] is for use with [calldll*] only, and it is strongly advised
that Ticol variables are not manipulated using this command

    [addressof]                [addressofb],[set],[$]                           
    |                          |                                                
    v                          v                                                
    +---------------------+    +-----------------------+                        
    | Tcl Variable Object | -> | Variable's Data Block |                        
    +---------------------+    +-----------------------+                        
    |                          |                                                
    v                          v                                                
    [ofaddress]                [ofaddressb]                                     

This will usually be unprintable without using [tohex]. What is often
required is to retrieve a literal numeric value written to memory by 
[struct setb] which is the inverse function. [ofaddress] could be used but
that will simply map 'n' bytes from the given memory location

On Windows Intel platforms the data is stored in inverted 'little endian'
format (byte order is reversed)

A byte-width can be specified but this will be rounded to 1,2,4 or 8 bits
You must correctly supply the bit-width interpretation of the data to
match the struct storage field definition. The default bit-width is 
4 (a DWORD or 32-bit long)

Example:   (where field2 is defined as 8 bytes width)

    struct s {field 8}           # Single-field struct                          
    struct setb s.field 123      # Set member field to 123 binary (0x7b)        
    puts [ofaddressb s.field 8]  # Retrieve and display                         

Dump:

008f3440  00 00 00 00 00 00 00 00 - 00 00 00 7B 00 00 00 00 ...........{....    
                                             -- -- -- --                        
                                             00 00 00 7B                        

Result:

    123                                                                         

Note: Literal integer values greater than the range of DWORD must be
retrieved back using [ofaddressb <varname> 8]

Example:

    struct setb foo.a 1234567890123456789 -value                                
    puts [ofaddressb foo.a 8]   # Byte width of 8 is required                   

Result:

    1234567890123456789                                                         

See also: addressof, ofaddress, struct, struct setb, setb, calldll,
          calldll_cdecl

after, every (experimental script)

    after milliseconds command ?arg? ?arg...?
    after milliseconds {script}
    after milliseconds {run scriptfile}
    after milliseconds {source scriptfile}

Threaded Ticol versions *:

    after info
    after count
    after cancel ?id?

Run a command after a given number of milliseconds

For unthreaded releases of Ticol, processing will halt until the specified
time-interval has elapsed. [after] is implemented as a simple time-delay
(see notes below)

For experimental, threaded releases of Ticol; [after] is event-driven and
will run asynchronously with main scripts with separate [after] scripts
interleaved

The threaded version of Ticol multitasks at script rather than command-
level. [after] and [run] lock mutually so [after] cannot launch either
[after] or [run] whilst a script is in progress from either of these 
commands

Interaction with scripts launched using [after] should be controlled using
[vwait]. A continuously running script will typically block an [after]
script. Consider rewriting scripts which need to run continually as an 
[every] proc which can be polled at intervals rather than run in a main 
script loop (see below)

Extreme care should be taken to avoid scripts clashing, particularly
with respect to common variables or procedures. Pseudo namespace variables
may be used outside procs. Procs will have their own temporary namespace

A proc return value can be passed to any variable which may then be used
as a [vwait] control flag, thus:

    after 2500 {set vret [draw_logo yellow $cursor_yloc]}                       
    vwait vret                                                                  

[vwait] is 'blocking' and will suspend the current script until the named
variable changes

IMPORTANT [after] scripts do not interact with the CLI as these run under
a separate thread. There is no concept of 'input focus'

Note:

    Be careful to brace [after] arguments and not use:

       after 4000 set vret [foo]                                                

    for example, the unbraced [foo] proc will be called before [after]
    and thus polled immediately by the interpreter

Threaded versions can be detected via the const '::tcl_threaded' e.g.

    if {$tcl_threaded} {                                                        
       # Call some [after] code                                                 
    }                                                                           

Arguments will be concatenated with a space. Formatting and grouping may
be maintained by wrapping arguments in braces

Multiple commands may be specified by using an intervening semi-colon (;)

An [every] command may be defined using a simple 4-line script (see below)
An [at] command can be defined using an 11-line procedure

Unthreaded Compile Example:

    # Unthreaded Ticol version example                                          
    # Instructions are executed sequentially after a simple delay               
    after 1000 box 3 3 20 10 - blue                                             
    after 1000 load demo.tcl                                                    
    after 1000 run                                                              
    after 1000 puts {"hello world"}                                             
    after 1000 {puts "hello there...";} {puts "How are you?"}                   
    after 1000 goto 10                                                          
    after 2000 ping 127.0.0.1                                                   

Threaded Version Example (requires threaded compile of Ticol):

    proc every {ms code} {                                                      
       source $code                                                             
       after $ms every $ms $code    # Respawn                                   
    }                                                                           
                                                                                
    set done 0                                                                  
    set i 0                                                                     
    proc tick {} {                                                              
       ++ ::i                                                                   
       if {> $::i 4} {set ::done 1}                                             
    }                                                                           
    every 1000 tick                 # Tick once per second                      
    vwait done                                                                  
    puts "Finished ticking at $i"                                               

Results:

    Finished ticking at 5                                                       

Threaded Version Example (A simple 'DOS TSR-like' clock)

    # When run from the CLI the CLI will remain interactive                     
    proc every {ms code} {                                                      
       source $code                                                             
       after $ms every $ms $code    # Respawn                                   
    }                                                                           
                                                                                
    every 1000 {                                                                
       set x [screen curx]                                                      
       set y [screen cury]                                                      
       gotoxy 72 1                                                              
       textcolor white blue;                                                    
       puts [time] -nonewline                                                   
       textcolor                                                                
       gotoxy $x $y                                                             
    }                                                                           

Threaded Compile Example (A useful Tcl [at] scheduler)

    # Be careful to avoid calling the Windows 'at' command                      
    if {! [defined at]} {                                                       
       proc at {time args} {                                                    
          global at_flag                                                        
          if {[>= [date $time] [fraction [date now]]] } {                       
             set dt [int [* 86400000 [- [date $time] \                          
                [fraction [date now]]]]]                                        
          } else {                                                              
             set dt [int [* 86400000 [- [+ [date $time] 1] \                    
                [fraction [date now]]]]]                                        
          }                                                                     
          after $dt $args                                                       
          return [/ $dt 1000]        # Convert ms to seconds                    
       }                                                                        
    }                                                                           
    at 23:59 {puts "The witching hour is nigh!"}                                

* NOTE: Threaded Ticol is currently experimental alpha and unlikely
  to reach final release quality

See also: vwait, every, time

every (Script) - Experimental Threaded Ticol Versions Only

    every ms {script}
    every cancel
    every reset
    every trace

[every] is a script which can poll code at regular intervals. Because
each thread is transient (it is launched then exits) it can't be 
managed using [after cancel]. Instead, [every cancel] will cancel all
threads. [every reset] must be called before making another script call
to [every]. This is a crude experimental script

Note that the script payload will be run in procedure scope so global
variables must be prefixed with '::'

    set _clock_running 0                    # Variable return for vwait         
    set _every::cancel_flag 0                # Module flag variable             
    set _every::every_trace 0                # Module flag variable             
                                                                                
    undef every -nocomplain                                                     
    proc every {ms args} {                                                      
                                                                                
       if {$::_every::every_trace} {                                            
          puts "'[after list]' [after count]"                                   
       }                                                                        
                                                                                
       if {eq $ms "cancel"} {                                                   
          set ::_every::cancel_flag 1                                           
          after cancel                    # Crude: Kills all                    
       } elseif {eq $ms "reset"} {                                              
          set ::_every::cancel_flag 0                                           
       } elseif {eq $ms "trace"} {                                              
          set ::_every::every_trace 1                                           
       } elseif {! $::_every::cancel_flag} {                                    
          eval $args                                                            
          after $ms every $ms $args                                             
       }                                                                        
    }                                                                           

Example:

A simple title bar clock:

    every 1000 {                                                                
       console set_title [time]                                                 
       set _clock_running 1                # Returns for [vwait]                
    }                                                                           

See also: after, vwait

Using Logical and/or Within Flow-Control Statements

Using with: 'option expression off' (forcing non-expression syntax):

    && ||     Will perform logical operations on arguments
              Arguments will be evaluated as true or false
    & |       Will perform bitwise operations on arguments,
              the result of which may be boolean true/false

    0b10 | 0b01 == 3    # i.e. true
    0b10 & 0b01 == 0    # i.e. false

Ticol does not perform lazy evaluation with these expression. Tcl always
evaluates every [] statement within a complex expression. The use of
[any] or [all] is recommended as this not only simplifies nested
expressions but performs lazy evaluation where there is more than one 
clause. See: any, all

Note that sub-expression which are to be tested using logical and/or 
should be bracketed if possible, e.g. ((22/7==3) && (12>1))

Example:

    if {[expr ($k != 4) && ($k != 6) &&  ($k != 9) && ($k != 11)]} { ...        

Due to the structure of the Tcl language, compound expressions with [if] 
command which use 'and' or 'or' may become complicated. You can either
pass to an [expr] statement or wrap multiple bracketed statements as follows:

    if { [&& [statement1] [statement2] } { ...                                  
    if { [||  [statement1] [statement2] } { ...                                 
    if { [expr [statement1] || [statement2]] } { ...                            
    if { [expr [statement1] && [statement2]] } { ...                            

Or you can use [option expression on] which will enable expressions for
flow control structures. 

Commands presented in [] format are evaluated before the outer expression
statement...

    if { [statement1] || [statement2] } { ...                                   
    if { [statement1] && [statement2] } { ...                                   
                                                                                
    option expression off                                                       
    for {set i 0} {< $i $adapter_count} {incr i} {                              
       set addr [info ip_address $i]                                            
       if { [&& [ne $addr "0.0.0.0"] [ne $addr "127.0,0.1"]] } {                
          set adapter $i                                                        
          break                                                                 
       }                                                                        
    }                                                                           

Example:

    # if $op is * and (a xor b) is -ve then option a else option b ...          
    if {[eq $op *] && [^ [eq $a "-"] [eq $b "-"]] } {                           

You may also nest 'if' statements to achieve the same effect as 'and'

    option expression off                                                       
    for {set i 0} {< $i $adapter_count} {incr i} {                              
       if { ne [info ip_address $i] "0.0.0.0"} {                                
          if { ne [info ip_address $i] "127.0,0.1"} {                           
             set adapter $i                                                     
             break                                                              
          }                                                                     
       }                                                                        
    }                                                                           

[if.. else] statements may be used to replicate 'or' constructs

--

Using with: 'option expression on' or with [expr]

Logical operators &&, || (and, or) are evaluated left-to-right
Complex expressions using logical combination operators should be properly
bracketed. e.g. 'if {(22/7)==3 && (12>1)} {puts yes} else {puts no}'

Expressions may combine individual values, bitwise to yield a testable
result: expr "1 || 2 || 4"

You may still use bracketed 'eval' type expressions

See also: any, all, if

ANSI Text Display Plugin

    To load, use:    lib ticol_ansi ?-verbose? ?-nocomplain?

Displays ANSI encoded text which would usually require the ANSI.SYS driver
and which is not available in some recent versions of Windows

Syntax:

    ansi <text>               # Display ANSI or plain text                      
    ansi <text> -slow         # Slow down output (useful for animations)        
    ansi <text> -veryslow     # Slowest output (useful for animations)          
    ansi <text> -mono         # Monochrome only (disable colour codes)          
    ansi <text> -width N      # Wrap to console width of N characters           
    ansi <text> -80           # Force 80 column line-wrap in > 80 col mode      
                              # 80 column displays will wrap at column 80       
    ansi <text> -step         # Single-step output by CSI code                  
    ansi <text> -trace        # Show translated CSI code to the Title bar       
    ansi <text> -noansi       # Don't apply the detected ANSI codes             
    ansi <test> -trans        # List translated codes as detected               
    ansi <test> -codes        # Output found CSI codes. 1 per line              
                              # Add -text to include plaintext                  
    ansi <text> -nocrlf       # Suppress CRLFs in the source text               
    ansi <text> -fix          # Fix with single line drawing characters         
    ansi <text> -fix2         # Fix with double line drawing characters         
    ansi <text> -unescape     # Unescape the string if supplied 'escaped'       
    ansi varname -var ?args?  # Supply a variable name instead of string        

[ansi] allows rich text display of classic ANSI formatted text of the kind
which was popular in the 1990s. This can be useful for rendering splash
screens or displaying rich content in text mode

[ansi] will not be as fast as a dedicated ANSI.SYS driver written in 
assembler but performance is still reasonable

Not all existing ANSI files will display well. Some ANSI drivers will work
by forcing 80 column mode for systems such as DOS which commonly used 
80 x 25 resolution. This is incompatible with modern console use
but both 40 and 80 column mode may be forced using the '-40' and '-80' 
arguments. Some existing ANSI art files may contain NULL characters and
thus cannot be displayed unless the NULL characters are removed.

Problems may occur displaying old files which relied on console terminal
behaviour or fixed console attributes of a legacy ANSY.SYS driver. Such
files will rely on console line-wrapping for correct display

Windows code pages tend to be missing some line-drawing characters, and
common ones, 221 and 222 may be replaced by 25% block shade (219) using
the argument '-fix'

The 'bell' character (0x07) is filtered out to prevent ANSI-'bombs'
which are comprised of files full of this character

Example:

    set s [readfile xmas.ans] # Read a legacy ANSI text file                    
    ansi $s                   # Display the content in ANSI format              
    pause -q                  # Pause with no prompt                            

Handled CSI Codes
-----------------
A small subset of common and safe ANSI codes are handled, which will
enable most legacy 16 colour content to be displayed: The Windows console
prior to Windows 10 does not support the blink or other attributes.

Keyboard redefinition codes are not supported

The ANSI Control Sequence Introducer (CSI) is comprised of character 27
(ESC) followed by an open square bracket. Various parameters may then
follow, separated by semi-colon characters. A final operation type
character terminates the instruction sequence. These being as follows:

    CSI  m  Screen display (attributes and colour). Intensity (bold) only       
    CSI  A  Cursor up (CUP)                                                     
    CSI  B  Cursor down (CUD)                                                   
    CSI  C  Cursor forward (CUF)                                                
    CSI  D  Cursor backward (CUB)                                               
    CSI  E  Newline (CNL)                                                       
    CSI  F  Previous line (CPL)                                                 
    CSI  G  Cursor horizontal absolute (CHA)                                    
    CSI  H  Set cursor position (CUP)                                           
    CSI  f  As per: CSI H                                                       
    CSI 2J  Clear screen                                                        
    CSI  K  Clear to end of line                                                
    CSI  U  Cursor up (CUU)                                                     
    CSI  s  Store cursor position                                               
    CSI  u  Restore cursor position                                             

A Control Sequence Introducer (CSI) string should be defined in Ticol as:

    set CSI "[chr 27][chr 91]"                                                  

Don't be tempted to use an escaped literal square bracket in place of 
[chr 91] or you will need to use [escape] to pass arguments and 
the -unescape flag with [ansi]

For more information on CSI visit:
    https://en.wikipedia.org/wiki/ANSI_escape_code

Sample ANSI Text
----------------
The following code displays "Hello" in multi-colour and backspaces over
the word 'Coder' to finally print the string '"Hello World'

    set CSI "[chr 27][chr 91]"                                                  
    ansi "${CSI}1;34mH${CSI}33me${CSI}31m${CSI}36ml${CSI}\                      
    0;33ml${CSI}1;32mo ${CSI}1;37m${CSI}sCoder${CSI}uWorld\n"                   

Creating A Simple ANSI File Viewer
----------------------------------
A simple ANSI file viewer which can be run from the Windows command line
can be created as follows:

    option expression off                                                       
    set filename ""                                                             
    set arg ""                                                                  
    lib ticol_ansi                                                              
    if {> argc 1} {                                                             
       set filename [item argv(1) ""]                                           
    }                                                                           
    if {eq [file type $filename] ""} {                                          
       append filename ".ans"                                                   
    }                                                                           
    if {! [file exists $filename]} {                                            
       die "File: $filename not found"                                          
    }                                                                           
    set s [readfile $filename]                                                  
    # Using [item] is far easier than checking for valid argc counts            
    ansi $s $arg [item argv(2)] [item argv(3)] [item argv(4)]                   
    lib ticol_ansi -unload                                                      
    pause -q                                                                    

Save the code above as ansi.tcl and use the supplied batch script:
converter, tcl2bat.tcl, to convert using the command:  

    ticol tcl2bat ansi

You can then run the batch file with optional arguments:

    ansi xmas -slow -fix

or run directly as:

    ticol.exe ansi xmas -slow -fix

Troubleshooting
---------------
Mangled displays: Check your terminal console is set to 80 columns
Go to the console menu, select properties, then 'Layout' tab.
Ensure you have set BOTH 'Screen Buffer Size' width and 'Window Size'
width to 80 columns or the correct number of columns intended by the ANSI
file

The most useful troubleshooting arguments will probably be '-trace -step'
which will single-step CSI processing and show the codes in the
console title bar

The 'blink' character attribute is not supported in the Windows console

See also: plugins, readfile, item

ANSI (American National Standard for Information) - Unicode Support

Ticol is an ANSI only language. That is, extended/international characters,
often called "Unicode", "Wide" or "Multi-byte" characters are not supported

Non-Roman languages such as Chinese are not directly supported but data
might be handled via external DLLs and using the [calldll] plugin

Only the standard 8-bit ANSI character set from 0..255, including many 
international accented characters, is supported

This is by design. Ticol is a personal hobby project with no requirement
for non-Roman character sets

Many other applications similarly lack Unicode/MBCS support
e.g. Unix du.exe for Windows cannot handle Unicode path names

Ticol may be used to "drive" Unicode-aware command-line application such as
RoboCopy or XCOPY.

See also: Ticol, why, ANSI plugin

alias

   bool [alias alias-name existing-command ?-nocomplain?]
   bool [alias alias-name existing-proc ?-nocomplain?]

Create an alias of a proc or command. The command or proc being aliased
must exist and the alias must not exist

An alias can be undefined using [undef] or [rename] as with any command

Take extreme care when creating aliases to procedures and subsequently
renaming or deleting the original reference. The procedure code will 
only be removed after the last reference is removed. Also, when testing
for removed references, take care to ensure that proc [unknown] is
not being called as this could confuse your checks.

Example:

    alias fred puts                                                             
    fred "Hello world"                                                          

Results:

    Hello world                                                                 

See also: undef, proc, commands

append
 
            append variable value ?value? ... 
    string [append variable value ?value? ...  -return]

Append a value or series of values to a variable. If the variable does not 
exist it will be created and any arguments appended to it

Append provides an efficient means of concatenating values into variables
However, [store add] is much faster

[append] will append to any simple variable or array element only

To concatenate strings without passing to a variable see: [concat]

As the string size expands and to aid speed and efficiency, [append]
returns no string unless the -return argument is used

Example:

    option expression off                                                       
    set var 0                                                                   
    for {set i 1} {<= $i 10} {incr i} {                                         
       append var "," $i                                                        
    }                                                                           
    puts $var                                                                   

Result:

    0,1,2,3,4,5,6,7,8,9,10                                                      

See also: concat, store, set, string

apply

    string [apply funct ?arg1 arg2 ...?]

Apply an anonymous function

The command [apply] applies the function 'func' to the arguments 
arg1 arg2 ... and returns the result. 

In mathematics apply is a function that applies functions to arguments.
It is key to programming languages derived from lambda calculus, such as
LISP and also in functional languages

The function 'func' is a two element list {args body} or a three
element list {args body namespace} (as if the list command had been 
used). Note that namespaces are only emulated in Ticol
The first element args specifies the formal arguments to func. 

The invocation of apply adds a call frame to Ticol's evaluation stack
and acts in equivalent manner to [proc]. So the [info level] value will
be +1 higher than the calling frame

Empty function args and an empty arglist as {} are acceptable

The specification of the formal arguments args is shared with the proc
command. See: proc

Example:

This shows how to make a simple general command that applies a
transformation to each element of a list

    proc lmap {lambdaExpression list} {                                         
       set result {}                                                            
       foreach item $list {                                                     
          lappend result [apply $lambda $item]                                  
       }                                                                        
       return $result                                                           
    }                                                                           
                                                                                
    lmap {x {return [string length $x]:$x}} {a bb ccc dddd}                     
    lmap {x {expr {$x**2 + 3*$x - 2}}} {-4 -3 -2 -1 0 1 2 3 4}                  

Results:

    1:a 2:bb 3:ccc 4:dddd                                                       
    2 -2 -4 -4 -2 2 8 16 26                                                     

Example:

    puts [apply {{x y} {expr hypot($x,$y)}} 3 4]                                

Result:

    5.0                                                                         

Example:

    # Empty lambas are allowed                                                  
    # Show callframe level                                                      
    puts [apply {{} {return [info level]}} {}]                                  

Result:

    1                                                                           

See:
    http://wiki.tcl.tk/4884
    http://wiki.tcl.tk/5892
    https://en.wikipedia.org/wiki/Apply
    https://www.tcl.tk/man/tcl/TclCmd/apply.htm

See also: proc, lassign, K, uplevel, lmap, map, lambda

argv and argc

    ::argc      Is always defined. 
    ::argv      Will be defined only if there are command arguments
    ::argv0     Will always be defined but may be empty

If a script is launched via ticol.exe's command-line then argc will always
be at least 1 and argv(0) will contain the full path and name of the script
file. This applies to both TCL and TCX filetypes. The argv() array starts
at zero and with the second Windows argument (i.e. the Tcl script name)

    # Windows/C++ argv[] index                                                  
    #   0         1         2   3                                               
    ticol.exe myscript.tcl foo bar                                              
    # Ticol argv index                                                          
    #  N/A        0         1   2                                               

If there are no command-line arguments and [source] has not been used to set
argv(), then argv will not be set, argc will be 0. 

Use [is_set], [array item], or ::argc to check if argv exists before
processing $argv()

Examples:

    ticol.exe argv.tcl 1 2 3                                                    

Result:

    --------------------------------------------                                
    hash_table object:0x3c4088 size:250                                         
    --------------------------------------------                                
    Bucket(1) Node: 0x20a3f10 chained nodes:1                                   
       1: 0x20a3f10 key:'0' => 'argv.tcl'                                       
    Bucket(69) Node: 0x20a3fd0 chained nodes:1                                  
       1: 0x20a3fd0 key:'3' => '3'                                              
    Bucket(94) Node: 0x20a3f50 chained nodes:1                                  
       1: 0x20a3f50 key:'1' => '1'                                              
    Bucket(135) Node: 0x20a3f90 chained nodes:1                                 
       1: 0x20a3f90 key:'2' => '2'                                              

Caution!
--------
                                                                                
 If a Ticol filetype (tcl/tcx) is associated with the Windows shell then        
 this behaviour will differ unless the association definition has been          
 modified to include command line arguments. Simply associating TCL or TCX      
 files with Ticol.exe is insufficient to pass command line arguments.           
 For this reason, take extreme care in launching Ticol scripts via Windows      
 file associations                                                              

Example:

    Command:  argv.tcx 1 2 3
    argv.tcx: array walk argv

Result:  (argv higher than 0 is not passed via the command-line)

    ---------------------------------------------------------------             
    hash_table object:0x5c4088 size:250                                         
    ---------------------------------------------------------------             
    Bucket(1) Node: 0x2113f40 chained nodes:1                                   
       1: 0x2113f40 key:'0' => 'C:\\VC5\\MyProjects\\ticol\\Re...'              

Example:

    if {is_set argv} {                                                          
       array foreach argv val ss {                                              
          puts "argv($ss)='$val'"                                               
       }                                                                        
    }                                                                           

Safe Dereferencing
------------------
[array item] offers the safest means of dereferencing argv() and offers
a default return value

    puts [array item argv 0 ""]                    # Default is ""              
    puts [array item argv 1 "argv 1 is not set"]   # Default is a message       

See also: globals, array item

args  (proc variable or command)

    proc <name> {args} {...          # Variable
    list [args argument-list...]     # Command

$args proc variable
------------------
$args is a procedure variable which acts analogously to the C++ ellipsis
pseudo variable placeholder. Where specified,'args' will contain all
subsequent arguments similar to the "C" ellipsis (...)

Example:

    proc foo {args} {                                                           
       puts "\$args=='$args'"                                                   
    }                                                                           
                                                                                
    foo 1 2 3 4                                                                 

Result:

    $args=='1 2 3 4'                                                            

[args] command
--------------
See: proc args     for additional information

The [args] command echoes its arguments as a Tcl list.

Example: (Using the {*} expand operator)

    puts [args "hello world" 1 2 3]                                             

Result:

    {hello world} 1 2 3                                                         

See also: proc, expand, debugging, argv

Procedure Argument List Variable: 'args' ($args)

    proc {argument-list ... args} { ...

The special variable 'args' may be defined as the last argument of a
procedure. When defined this variable will receive a list of zero or more
arguments which are supplied when the proc is called. It is similar in
function to the C/C++ ellipsis function argument "..."

If the special argument, 'args', is not specified as the final argument of
a proc then any excess arguments supplied when the proc is called will be
discarded and ignored

When including several scripts it is recommended to ensure procs from
other scripts are cleaned-up before defining using [undef], e.g. 

    (See also args for the command [args])

    undef foo -nocomplain                                                       

Example:

    proc foo {a args} {        # Will include unlimited trailing args           
       puts "foo a is '$a'"                                                     
       puts "foo args is '$args'"                                               
    }                                                                           
                                                                                
    proc bar {a} {             # Will allow only one arg                        
       puts "bar a is '$a'"                                                     
    }                                                                           
                                                                                
    foo Hello                                                                   
    foo Hello World                                                             
    foo Hello World How {Are You} Today                                         
    newline                                                                     
                                                                                
    bar Hello                                                                   
    bar Hello World                                                             
    bar Hello World How {Are You} Today                                         
    newline                                                                     

Results:

    foo a is 'Hello'                                                            
    foo args is ''                                                              
    foo a is 'Hello'                                                            
    foo args is 'World'                                                         
    foo a is 'Hello'                                                            
    foo args is 'World How {Are You} Today'                                     
                                                                                
    bar a is 'Hello'                                                            
    bar a is 'Hello'                                                            
    bar a is 'Hello'                                                            

Example:

    proc foo {x y} { ...                                                        

Example:

    proc demo {first {second "two"} args} { ...                                 

Results:

    See: proc for more information

Procs can also be called in many ways, directly by name or via [call],
[gosub], [@>] or [$]

Example:

    proc foo {} {puts Hello}                                                    
    foo                                                                         
    call foo                                                                    
    gosub foo                                                                   
    @> foo                                                                      
    set p foo                                                                   
    $foo                                                                        

Results:

    Hello                                                                       
    Hello                                                                       
    Hello                                                                       
    Hello                                                                       
    Hello                                                                       

See also: proc, args, varname, call, gosub

Function Arity
 
Arity is the number of arguments presented to a given function. There are a
variety of functions available either within expr or funct as follows:

The command [arity] will return the arity of a built-in function

Function Arity 0
----------------
srand                      Initialise the random number generator
rand                       Return double random value from 0 to 1
                           The rand() and srand() functions are not 
                           cryptographically secure, and should not be used
                           to generate one-time passwords or session keys
                           Link to the Windows API using [calldll] instead

Function Arity 1
----------------
abs      value             Return the absolute value of a number
acos     value             Return the arc cosine of value (arccos)
asin     value             Return the arc sin of value (arcsin)
atan     value             Return the arc tangent of value (arctan)
bool     value             Evaluates a string and returns 1 or 0
ceil     value             Round to the next-highest integer value
cos/cosh value             Return the cosine of value
decr     value             Decrement value
degrees  value             Convert radians to degrees
double   value             Cast value to double
exp      value             Return the exponent of value
fib      value             Return the Fibonacci of value
fix      value             Return integer value rounded up
floor    value             Round to the next-lowest integer value
fraction value             Return the fraction part of a real/double
incr     value             Increment value
int      value             Cast value to integer
integral value             Return the integer part of a real/double
log      value             Return the logarithm of value
log10    value             Return the log10 of value
round    value (0 places)  Round value to 0 places using standard rounding
sgn      value             Return the sign as an integer (1, 0, -1)
sin/sinh value             Return the sine of value
sqr      value             Return the square of value
sqrt     value             Return the square-root of value
srand    seed              Seed the random number generator
tan/tanh value             Return the tangent of value
wide     value             Force a value to 64 bit signed integer

Function Arity 2
----------------
div      a b               Functional division of a / b
fmod     a b               Return the remainder of a / b
hypot    a b               Return the hypotenuse of a and b
max      a b               Return the maximum of a and b
min      a b               Return the minimum of a and b
mul      a b               Functional multiplication of a * b
pow      a b               Return a to the power of b
rnd      a b               Return a random number between a and b inclusive
round    a b (b places)    Round a to b decimal places

You can use the [functions] command to get a list of supported [expr] functions
You can use the [funct] command to export and access an expression function

See also: expression functions, arity, functions, funct, expr, eval

arity

    integer [arity functname]

Return the arity count of an inbuilt function as an integer value
If the function is not found then -1 is returned

Example:

    puts [arity rand]    # No arguments                                         
    puts [arity abs]     # One argument                                         
    puts [arity max]     # Two arguments (funct)                                
    puts [arity fred]    # No such command                                      

Results

    0                                                                           
    1                                                                           
    2                                                                           
    -1                                                                          

For an arity list of each available function. See: function arity

See also: function arity, expression functions, functions, funct,
          expr, eval

ARP Address Resolution Protocol and RARP (Reverse ARP) Plugin

Load using: lib ticol_arp?.dll?

   arp ipv4-address ?-list?   # ARP lookup of IPV4 address
   arp table                  # Return the ARP table as a Tcl list
   rarp mac-address ?-list?   # Reverse ARP lookup. Look up in ARP table

Address Resolution Protocol (ARP) queries will query the local ARP table. 
Results will depend on the content of the table

[arp] returns a string value and may return an empty string for interfaces
which return no MAC address. No mac address is returned for addresses which
cross NAT firewalls or for loopback/dummy interfaces. Such addresses must
be on the same LAN

[arp table] returns a Tcl list with one {ip-address mac-address status}
list per interface found

[rarp] can only return results for "known" IPV4 addresses which have been
discovered and placed in the local ARP table cache. Your workstation must 
have been in conversation with these addresses, (e.g. via ping)

Lookups which return empty {} string are not fatal errors. The return should
be tested to see if the return value is empty, which indicates the IP or MAC
address was not found

For exceptions such as malformed IP or MAC addresses an error exception is
raised with an internal ARP library error code or a Windows system error code

The -list option will return a list which includes the error code as the
2nd item.

    [arp] Error Codes                                                           
    -----------------------------------------------------------------           
    0       Success                                                             
    1       Success but empty MAC address returned                              
    2       Failed to load iphlpapi.dll                                         
    3       Failed to link to lib function SendARP                              
    4       MakeMacAddress failed                                               
    5       Bad PMAC buffer                                                     
    6       Bad IP address                                                      
    7       Unhandled error                                                     
    *       SendArp Windows API error code                                      
    31      ERROR_GEN_FAILURE (Network error/disconnected interface)            
    50      ERROR_NOT_SUPPORTED                                                 
    67      ERROR_BAD_NET_NAME                                                  
    87      ERROR_INVALID_PARAMETER                                             
    111     ERROR_BUFFER_OVERFLOW                                               
    1168    ERROR_NOT_FOUND                                                     
    1784    ERROR_INVALID_USER_BUFFER                                           
                                                                                
    [rarp] Error Codes                                                          
    -----------------------------------------------------------------           
    0       Success (match found)                                               
    1       Failure (no match)                                                  
    2       Bad MAC address                                                     
    3       Bad MAC format                                                      
    4       Unknown/unhandled error                                             

Example:

    puts [arp table]                                                            

Result:

    {224.0.0.2 00-00-00-00-00-00 Static} {224.0.0.22 00-00-00-00-00-00 St       
    atic} {224.0.0.252 00-00-00-00-00-00 Static}                                

Example:

    set t [arp table]                                                           
    foreach l $t {                  # foreach list item                         
       foreach {i m s} $l {        # IP, MAC, Status                            
          puts "$i\t$m\t$s"                                                     
       }                                                                        
    }                                                                           

Result:

    224.0.0.2       00-00-00-00-00-00       Static                              
    224.0.0.22      00-00-00-00-00-00       Static                              
    224.0.0.252     00-00-00-00-00-00       Static                              

Examples:

    arp 127.0.0.1       # Loopback adapter                                      
    arp 8.8.8.8         # Google DNS                                            
    arp 192.168.1.1     # Class C local IPV4 address                            
    rarp 3c46d8cc5cce   # Must be in the ARP table (ping first)                 
    rarp beefdeadbeef   # Must be in the ARP table (ping first)                 

Results:

    {}                  # Empty string as loopback adapter not in cache         
    {}                  # Empty string as 8.8,8.8 is an internet address        
    3c46d8cc5cce        # MAC address of workstation on local LAN               
    192.168.1.1         # Reverse lookup from MAC to IP                         
    {}                  # No such MAC address in the ARP table                  

Examples:

    arp 1.2.3.4 -list   # No such address?                                      
    rarp pqrs -list     # Bad MAC address                                       

Result:

    {} 31               # Empty return + network error                          

See also: ping, dns, dhcp, plugins

array - Tcl Array Commands

Various commands for manipulating standard Tcl arrays. Arrays are also
addressed and dereferenced using the dollar ($) sign in the same way
as for languages such as PHP. See a topic below for more information:

 arrays                              Information about standard Tcl arrays      
 array append     a b ?n?            Append array b to array a for ?n? rows     
 array blank      arrayVar s ?c? ?n? Blank an integer-indexed array             
 array create     arrayVar ?n?       Create an empty array                      
 array exists     arrayVar           Return boolean true if array exists        
 array find       arrayVar string    Return index/subscript or empty string     
 array foreach    arrayVar v s code  Iterator                                   
 array get        arrayVar           Return an array as a key + value list      
 array is_set     arrayVar           Boolean 1 if an array exists else 0        
 array item       arrayVar subscript Array indexing with default option         
 array list       arrayVar           Exports an array to an unsorted list       
 array names      arrayVar           Returns a list of subscript values         
 array set        arrayVar list      Set array using a list of parameters       
 array setint     arrayVar list...   Set integer-indexed array using lists      
 array size       arrayVar           Return the size of array if it exists      
 array sort       arrayVar           Exports the array to a sorted Tcl list     
 array statistics arrayVar           Show statistics for a given array          
 array swap       arrayVar s1 s2     Show swap 2 array elements                 
 array trim       arrayVar ?mask?    Trim L/H and R/H end of array values       
 array unset      arrayVar           Delete array & all allocated elements      
 array walk       arrayVar           Walk the array hash-table                  

string_to_array string arrayvar     Convert a CRLF delmited string to a Tcl array

Example:

    set a(0) Hello                                                              
    puts $a(0)                                                                  

Example:

    set a(0) Hello                                                              
    puts [array item a 0]                                                       

Example:

    set a(0) Hello                                                              
    puts [array get a 0]                                                        

Example:

    set a(0) Hello                                                              
    puts [set a(0)]                                                             

See:  help array <subcommand>

e.g.: help array list

For "C"-like integer indexed, non-associative arrays see: help carray

See also: arrays, variant arrays, array foreach, passing variables by name
          varname, carray, string_to_array

array append

    integer [array append array1 array2 ?count?]

Append one integer-indexed array onto the end of another. Optionally, this
can be restricted to one or more elements of the 2nd array. The returned
integer is the new count of the appended-to array

Integer-indexed arrays are returned by a number of Ticol command such as
[split]

Example:

  split $qbf " " -array a       # Splits into an integer-indexed array          
  split "Now is the time for all men to come to the party" " " -array b         
  set r [array append a b]      # Append 2nd array on to the 1st                
  assert [array size a] {== $_ 22} -v -line #__LINE__                           
  puts "New size is $r"                                                         

Result:

    New size is 22                                                              

This is an equivalent [proc] solution

    option expression off                                                       
    proc array_append {_a _q} {                                                 
          upvar $_a a                                                           
          upvar $_q b                                                           
          set q [array size a]                                                  
          set r [array size b]                                                  
          for {set i 0} {< $i $r} {++ i} {                                      
             set a([+ $q $i]) $b($i)                                            
          }                                                                     
          return $_a                                                            
    }                                                                           

See also: arrays, array foreach, array size, carray

array blank

     integer [array blank arrayname size ?blankchar? ?-start N?]

Blank (or create) an integer-indexed array of the given size

[array blank] is useful for quickly creating and initialising very
large arrays

If 'blankchar' is specified then each element will be intialised to this
value. The array index will start at 0 unless otherwise specified by
the '-start N' argument. 

An integer count of elements successfully set is returned

Example:

    array blank foo 4 0 -start 3                                                
    loop i 3 7 {                                                                
       puts "foo($i) == '$foo($i)'"                                             
    }                                                                           

Result:

    foo(3) == '0'                                                               
    foo(4) == '0'                                                               
    foo(5) == '0'                                                               
    foo(6) == '0'                                                               

See also: array, array set, array walk, carray

array foreach

    array foreach arrayName iteratorVar ?subscriptVar? {code}

Iterate an array, applying a block of code with each element's value
passed into valVar and, optionally, the array's subscript value passed
into subscriptVar. The array element value precedes subscript due to
subscript being an optional parameter

[array foreach] is functionally equivalent to a combination of [foreach]
and [array get] but is more efficient

    # Emulate [array foreach] using [array get] and [foreach]                   
    foreach {x y} [array get env] {                                             
       printf "%-20.20s\t=%-45.45s\r\n" $x $y                                   
    }                                                                           

One common alternative is to get the names and then order them

Tcl arrays are associative and intrinsically unordered. Values cannot be
retrieved from an array in the same order that they were set. Arrays will
be retrieved in random (unsorted) order. Integer-indexed arrays should
be used where a sorted order is required

Don't wrap a pair of value and subscript variables in braces

Example:

    set i 0                                                                     
    array foreach env val ss {                                                  
       printf "$i:\t%-20.20s=%-45.45s\n" $ss $val                               
       incr i                                                                   
    }                                                                           

Result: (prints out environment table)

    1:      ProgramFiles        =C:\Program Files (x86)                         
    2:      USERPROFILE         =C:\Users\Admin                                 

Example:

    ticol.exe ; "set i 0; array foreach env x {puts $i:$x;[incr i]}"            

Result:

    Prints out environment                                                      

See also: foreach, arrays, array, carray

array create

    array create arrayName ?size?

Alias for [dim arrayName size]

Creates a Tcl associative array with the default pre-allocation of 1
If 'size' is specified then the array will have memory preallocated to that 
size. This will prevent a need to reorganise the storage table on the fly

It is not mandatory to use [array create]. Arrays will be created on first
use as with standard variables, for example, the statement:

    set a(0) Hello                                                              

 will create array 'a' and array element '0'

[array set] will be the most common means of creating arrays with pre-
determined contents, e.g.

    array set colours {                                                         
       red   1                                                                  
       green 2                                                                  
       blue  3                                                                  
       white 4                                                                  
    }                                                                           

 will create array 'colours' with 4 elements

Example:

    array create names 10                                                       
    array walk names                                                            

Results:

    -----------------------------------------------------------------           
    hash_table object:0x35ec650 size:10                                         
    -----------------------------------------------------------------           
                                                                                
    Hash Table Statistics:                                                      
    Root size:10 Occupation:0 (0.00%) Max depth:0 Nodes:0                       
    -----------------------------------------------------------------           

See also: array, arrays, dim, carray

array_to_list

    array_to_list arrayvar ?listvar? ?-values?

Converts an array to a well-formed but unsorted Tcl list containing pairs
of keys (subscripts) and matching values. Optionally this list can be
passed into an output variable. Both the array and optional list variables
must be passed by name

If the -values argument is used then only values are returned

Use [lsort] to sort the resulting list

Example:

    set q [lsort [array_to_list env]]                                           
                                                                                
    puts "Returned [lcount q] item(s)"                                          
    newline                                                                     
                                                                                
    set i 1                                                                     
    foreach pair $q {                                                           
       foreach {l r} $pair {                                                    
          puts "$i\t$l\t=$r"                                                    
       }                                                                        
       ++ i                                                                     
    }                                                                           

Result:

    1       ALLUSERSPROFILE =C:\ProgramData                                     
    2       APPDATA =C:\Users\Admin\AppData\Roaming                             
    3       COMPUTERNAME    =FIDO                                               
    4       ComSpec =C:\Windows\system32\cmd.exe                                
    5       CommonProgramFiles      =C:\Program Files (x86)\Common Files        
    6       CommonProgramFiles(x86) =C:\Program Files (x86)\Common Files        
    7       CommonProgramW6432      =C:\Program Files\Common Files              
    ...                                                                         

See also: list_to_array, lsort, array, list, carray

array is_set
 
 array is_set arrayVar

Returns boolean 1 ($true) if an array exists, otherwise 0 ($false)

Example:

    set a(0) Hello                                                              
    puts [bool [array is_set foo]]                                              
    puts [bool [array is_set a]]                                                

Result:

    false                                                                       
    true                                                                        

See also: is_set, carray

array exists

Return a boolean indicating if an array exists

    array exists arrayName 
    array exists arrayName(element) 

When used with the name of an array variable this tests only if the array
variable name has been set. This does not guarantee any elements in the
array are assigned. To check the size of an array use [array size arrayName]

An array element represents a single variable bound to an array and should
be tested using [array exists arrayName(element)]

You may also use [info exists varname] to test for an array element

In all cases the variable to be tested must NOT be prefixed by a dollar $
sign

Case will be ignored when searching for the special system arrays 'env' and
'argv'

Examples:

    array exists argv                                                           
    array exists env                                                            
                                                                                
    array exists argv(0)                                                        
    array exists env(TEMP)                                                      
                                                                                
    info exists argv(0)                                                         
    info exists env(TEMP)                                                       

See also: is_set, is_empty, arrays, array, array size, info exists, carray

array find (Reverse array lookup)

    array find arrayName valuestring ?sequence? ?-nocase? ?-length N?

Searches an array by looking up a value to return the index/subscript
of the first matching item with full string match, or an empty string if
the item does not exist. The match is case-sensitive and will match the
entire string. It will not match a substring

[array find] is not a standard Tcl command and is much less efficient
than subscript indexing, but should be adequate for small arrays of less
than a few thousand elements

Example:

    split $qbf " " -array a       # Split into integer-indexed array            
    set i [array find a "fox"]                                                  
    puts "fox is in element '$i'"                                               

Results:

    fox is in element '3'                                                       

The '-length' argument allows comparison on a given number of characters

Example:

     puts [array find env "x86" -nocase -length 3]                              

Result:

    PROCESSOR_ARCHITECTURE                                                      

Example:

    array set fruits {                                                          
          # Subscript/key                                                       
                  # Value                                                       
          blue    berry                                                         
          green   apple                                                         
          orange  orange                                                        
          red     cherry                                                        
          yellow  lemon                                                         
    }                                                                           
    set i [array find fruits cherry]                                            
    puts "cherry is in element '$i'"                                            

Results:

    cherry is in element 'red'                                                  

Name to Integer Lookups
-----------------------
String to integer lookups using an array indexed on integer values can be
done using [array find]

    array months {                                                              
       1 January                                                                
       2 February                                                               
       # ... etc.                                                               
    } -const                                                                    
    # e.g. where argv(1) is a month name we return an integer                   
    set month [array find months $argv(1) -nocase -length 3]                    

However, consideration should be given to inverting the lookup array so
as to render the use of [array find] unnecessary. Note however that
[array item] will fail "safe" if the item is not found whereas $() 
reference does not unless the [array set -nocase] option is used

    array months {                                                              
       Jan 1                                                                    
       Feb 2                                                                    
       # ... etc.                                                               
    } -const -nocase                                                            
    # e.g. For "May" we return 5                                                
    set month $month([string left $argv(1) 3])                                  
    # or                                                                        
    # set month [array item months [string left $argv(1) 3] ""]                 

Sequential Search
-----------------
Tcl arrays are not ordered vectors and Tcl arrays are stored in a
randomised hash table. This can make sequential searching for multiple
identical items problematic if the array is not integer indexed.

A sequence number may be given to find the Nth matching item in an array
A sequence value < 1 is ignored and returns the first matching item
A sequence value >= 1 returns the Nth matching item
Sequence numbers are inefficient but allow loop searching for N matching
items within an array. Note that arrays are stored unordered and case
significant searches may return items in a different sequence to case
insignificant ones. Case and case-insignificant searches should not be
mixed within the same sequential search.

    # findstr is set with some string to search in array 'a'                    
    option expression off                                                       
    set found ""                                                                
    set sequence 1                                                              
                                                                                
    do {                                                                        
       set found [array find a $findstr $sequence]                              
       puts "'$found'\t'[array item a $found {<not found>}]'"                   
       ++ sequence                                                              
    } while {ne $found ""}                                                      

See also: arrays, carray

array get

    list   [array get arrayname]
    string [array get arrayName ?mode? ?pattern?]
    string [array get arrayName ?pattern? ?mode?]

Return an array as a key + value list. [array get arrayName] will return
the entire array as a list of pairs of keys+values

Where ?mode? is any of:

    -glob         Wildcard string matching (the default)                        
    -exact        Exact string matching (-glob is the default)                  
    -all          Return all items                                              

and optionally:

    -pair         Return array subscript+value pairs                            
    -nocase       Ignore case when making comparisons                           

Note that [array get] is less efficient than direct array addressing as
Tcl associative arrays are hashed and meant to be indexed efficiently by
the precise key value (subscript)

Case will be ignored automatically for arrays set using -nocase or for the
special global arrays env() and argv()

Example:

    array get env u*                                                            

Result:

    USERPROFILE C:\Users\Admin USERNAME Admin USERDOMAIN SNOOPY                 

Example:  (Prints out the environment table)

   foreach {x y} [array get env] {puts $x=$y}                                   

Result:

    ProgramFiles=C:\Program Files (x86)                                         
    USERPROFILE=C:\Users\Admin                                                  
    OS=Windows_NT                                                               
    ComSpec=C:\Windows\system32\cmd.exe                                         
    PROCESSOR_ARCHITEW6432=AMD64                                                
    SystemDrive=C:                                                              

Example:

    foreach x [lsort [array get env -nocase -pair]] {                           
      puts [lindex $x 0]=[lindex $x 1]                                          
    }                                                                           

Result:

    ALLUSERSPROFILE=C:\ProgramData                                              
    APPDATA=C:\Users\Admin\AppData\Roaming                                      
    HOMEDRIVE=C:                                                                
    HOMEPATH=\Users\Admin                                                       
    LOCALAPPDATA=C:\Users\Admin\AppData\Local                                   
    USERNAME=Admin                                                              
    WINDIR=C:\Windows                                                           

Example:

    foreach {x y} [array get env proc* -nocase] {puts $x=$y}                    

Result:

  PROCESSOR_ARCHITEW6432=AMD64                                                  
  PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 37 Stepping 5, GenuineIntel       
  PROCESSOR_REVISION=2505                                                       
  PROCESSOR_LEVEL=6                                                             
  PROCESSOR_ARCHITECTURE=x86                                                    

Example:

    puts [wrap [array get env -glob c*] 60]                                     

Results:

    ComSpec C:\\Windows\\system32\\cmd.exe COMPUTERNAME LENOVO C                
    ommonProgramFiles(x86) {C:\\Program Files (x86)\\Common File                
    s} CommonProgramFiles {C:\\Program Files (x86)\\Common Files                
    } CommonProgramW6432 {C:\\Program Files\\Common Files} COMMP                
    ath {C:\\Program Files\\Lenovo\\Communications Utility}                     

See also: array names, arrays, array, info exists, carray

array list

    array list arrayName

Exports the array values to an unsorted Tcl list

Note that this does not include the array subscripts. Ticol lists are
slow and inefficient, particularly when used to store large numbers of
items (> 1000 or more). For small lists the difference in performance
is insignificant

Example:

    # Export and iterate each list value item                                   
    foreach {x} [array list env] { puts $x }                                    

Result:

    C:\Program Files                                                            
    C:\Users\Admin\AppData\Local                                                
    C:\Users\Public                                                             
    C:\Users\Admin\AppData\Roaming                                              
    C:\ProgramData                                                              
    $P$G                                                                        

See also: arrays, array names, array item, carray

array item

    array item arrayName subScript ?default value? ?-nocase? ?-nocomplain?

Resolve an array index with optional default return value. This is helpful
since associative arrays may be 'sparse' and there may therefore be no
guarantee that any element exists at all

[array item] is not a standard Tcl command and it offers a safe lookup of
arrays. The standard, 'inline' method of referencing arrays will return a
fatal error where the array element does not exist. [array item] will 
instead return an empty string where the array element does not exist
The referenced array variable must exist

[array item] may be combined with [array exists array subscript -nocase] to 
safely handle ambiguous/case-insensitive array lookups

If the element is not found and a default value given then the default value
will be returned. This permits safe/simple array lookups with defaults 

A fatal error is raised only where the referenced array does not exist
unless '-nocomplain' is passed. In which case the default will be returned

The variable name argument must not be dereferenced using the $ symbol

Correct:

    puts [array item env temp -nocase]                                          

Wrong:

    puts [array item $env temp -nocase] # $env returns the tag "<array>"        

Example:

    array set smtp_code {        # Array of SMTP error codes                    
       221 "Server ready"                                                       
       250 "Send success"                                                       
       252 "Cannot verify user, but it will deliver"                            
    }                                                                           
    puts [array item smtp_code 250 "Unknown error"]                             
    puts [array item smtp_code 251 "Unknown error"]                             

Result:

    Server ready                                                                
    Unknown error                                                               

See also: array, arrays, carray

array names

    array names arrayName ?mode? ?pattern?

Where ?mode? is any of:

    -glob         Wildcard string matching (the default)                        
    -exact        Exact string matching (-glob is the default)                  
    -nocase       Ignore case when making comparisons                           
    -all          Return all items                                              

Returns a list of array subscript values (array element name identifiers)
Case will be ignored automatically for arrays set using -nocase or for the 
special global arrays env and argv

Example:

    puts [array names env u*]                                                   

Result:

    USERPROFILE  USERNAME  USERDOMAIN                                           

Example:

    foreach {x} [array names env t* -nocase] {puts "$x\t$env($x)"}              

Result:

    TEMP    C:\Users\Admin\AppData\Local\Temp                                   
    TMP     C:\Users\Admin\AppData\Local\Temp                                   

Example:

    foreach {x} [array names env -exact windir] {puts "$x=$env($x)"}            

Result:

    windir=C:\Windows                                                           

See: array get    for similar examples

See also: arrays, carray

array set

    integer [array set arrayName list  ?-const? ?-nocase?]

Set and initialise an array using a list of parameters. This may be a single
Tcl list or series of arguments. [array set] cannot be used to append items 
to an existing array. The array must not already exist

The standard Tcl command is extended to add constant arrays (-const) and 
case-insignificance lookup attributes (-nocase). Arrays which are constant
cannot be reassigned-to but can be unset

Arrays which are set using -nocase will be much less efficient but can be
indexed by subscript values in a case-insensitive manner. Scripts should be
designed to use large arrays in a case-sensitive manner. For small arrays 
the performance impact will be negligible

A subst is performed on array pairs before assignment. Variables are 
substituted and escaped characters are translated. Commands are not 
executed

Use: [set array(subscript) value] - to set individual array elements

[array create] can be used to create an array without assigning any 
elements and [array create size] can be used to pre-allocate an array of a
fixed size to avoid time-consuming dynamic reallocation

[array set] returns an integer count of the number of array elements
created

Example:

    array set colours {                                                         
       red   1                                                                  
       green 2                                                                  
       blue  3                                                                  
       white 4                                                                  
    }                                                                           
    array set colours {red 1 green 2 blue 3 white 4}                            
                                                                                
    array walk colours                                                          
    array foreach colours a b { printf "%s\t%s\n" $b $a}                        

Result:

    ---------------------------------------------                               
    hash_table::walk() Object:0x1df43f0 size:250                                
    ---------------------------------------------                               
    Bucket(138) Node: 0x1e08840 chained nodes:1                                 
            1: 0x1e08840 key:'green' value:'2'                                  
    Bucket(140) Node: 0x1e08870 chained nodes:1                                 
            1: 0x1e08870 key:'blue' value:'3'                                   
    Bucket(181) Node: 0x1e088a0 chained nodes:1                                 
            1: 0x1e088a0 key:'white' value:'4'                                  
    Bucket(238) Node: 0x1e08810 chained nodes:1                                 
            1: 0x1e08810 key:'red' value:'1'                                    
                                                                                
    green   2                                                                   
    blue    3                                                                   
    white   4                                                                   
    red     1                                                                   

Example: (Calling commands and passing the result into [array set]

    set p [expr 4*atan(1)]     # An accurate representation of pi               
    set bp [expr 22/7.0]       # An inaccurate representation of pi             
                                                                                
    array set mathstuff {                                                       
       pie $p                                                                   
       badpie $bp                                                               
    }                                                                           
                                                                                
    array foreach mathstuff v ss { puts "Item: $ss\tValue '$v'" }               

Result:

    Item: badpie    Value '3.142857142857143'                                   
    Item: pie       Value '3.141592653589792'                                   

See also: set, array, arrays

array setint

    array setint arrayvar {list} ?{list} ...? ?-base N? ?-const? ?-nocase?

Set an integer-indexed array using a standard Tcl list of values. 

More than one list may be passed, in which case, each subsequent list of
items will be appended to the array. If an array does not exist one will
be created.

By specifying the array base, singular or groups of items may also be
replaced within an existing array.

The default array subscript base starts at 0

The '-const' argument allows a const array to be created.
Constant arrays require the use of [array unset varname -const] to unset
 
The '-nocase' argument allows an array to be created which is case-
insensitive with respect to subscript references (See: arrays)

Example:

    array setint a {one two three} {four five six}                              
    loop i 0 [array size a] {                                                   
       puts "array a: $i -> '$a($i)'"                                           
    }                                                                           
    newline                                                                     
    array setint a {THREE FOUR} -base 2                                         
    loop i 0 [array size a] {                                                   
       puts "array a: $i -> '$a($i)'"                                           
    }                                                                           

Results:

    array a: 0 -> 'one'                                                         
    array a: 1 -> 'two'                                                         
    array a: 2 -> 'three'                                                       
    array a: 3 -> 'four'                                                        
    array a: 4 -> 'five'                                                        
    array a: 5 -> 'six'                                                         
                                                                                
    array a: 0 -> 'one'                                                         
    array a: 1 -> 'two'                                                         
    array a: 2 -> 'THREE'                                                       
    array a: 3 -> 'FOUR'                                                        
    array a: 4 -> 'five'                                                        
    array a: 5 -> 'six'                                                         

See also: array, arrays, array set, array unset, is_const, carray

array size

    array size arrayName    # Return the size of an array if it exists

If the array is not assigned (does not exist) then an empty string is
returned

If the array exists (has been declared) but with no elements assigned then
zero is returned

Example:

    # a                               # Not declared                            
    set b {}                          # Not an array                            
    dim c 1                           # Create empty array using [dim]          
    set d(1) 0                        # Assigned array and element              
    array set e {1 one 2 two 3 three} # Multi-element set                       
                                                                                
    puts "Undeclared a  '[array size a]'"                                       
    puts "Non-array b   '[array size b]'"                                       
    puts "Empty-array c '[array size c]'"                                       
    puts "Declared d    '[array size d]'"                                       
    puts "Declared e    '[array size e]'"                                       

Results:

    Undeclared a  ''                                                            
    Non-array b   ''                                                            
    Empty-array c '0'                                                           
    Declared d    '1'                                                           
    Declared e    '3'                                                           

See also: array, array unset, carray

array list

    array list arrayName    # Exports the array to an unsorted Tcl list

Note that the list contents will be in random, not original, order

Example:

    foreach x $qbf { set q($x) $x }                                             

Result:

    the brown jumps lazy fox quick over The dog                                 

Example:

    foreach x [array list env] { puts $x }                                      

Result:

    D:\WINDOWS                                                                  
    D:\WINDOWS\system32\cmd.exe                                                 

See also: arrays, array sort, array, carray

array sort

  list    [array sort arrayname ?newvar? ?-reverse? ?-names? ?-numeric?]
  integer [array sort arrayname ?newvar? ?-reverse? ?-array name?\
     ?-names? ?-numeric?]

Creates a sorted Tcl list of array values. Note that the subscript (key)
values are not returned and the internal array hashtable structure cannot
actually be sorted, only the returned output

The default sorting is to sort alphabetically (A..Z). To sort numeric data
use the -numeric argument. This will sort on the basis of 64 bit integers.
Float sorting is not supported

By default a sorted Tcl list is returned. Alternately, another integer-
indexed array can be created, in which case the new array element count
is returned.

The default sort order is normal (A-Z)

Sorting in Integer Sequence
---------------------------
[array sort] can also output an integer sorted array indexed at base 0 by
specifying the '-array' argument

Example:

    option expression on                                                        
    for {set i 0} {$i < 2000} {incr i} {                                        
       set a($i) [randomstr 20]                                                 
    }                                                                           
    puts [llength [array sort a]]                                               

Result:

    2000                                                                        

Example:

    option expression on                                                        
    for {set i 0} {$i < 2000} {incr i} {                                        
       set a($i) [randomstr 20]                                                 
    }                                                                           
    puts [array sort a b]                                                       

Result:

    2000                                                                        

Example:

    option expression off                                                       
    array sort env -array out                                                   
    set s [array size out]                                                      
    for {set i 0} {[< $i $s]} {incr i} {                                        
       printf "%4i:\t%-40.40s\r\n" $i $out($i)                                  
    }                                                                           

Results

    Outputs values in the ENV table                                             

Example:

  # Using a temporary and [swap] to swap between sorted and unsorted arrays     
  # [swap] moves no data, it simply renames the two variables                   
  option expression off                                                         
  array sort foo -array out                                                     
  swap out foo               # Swap $foo and $out                               
  set s [array size foo]                                                        
  for {set i 0} {[< $i $s]} {incr i} {                                          
    printf "%4i:\t%-40.40s\r\n" $i $foo($i)                                     
  }                                                                             

Sort by Array Names (Subscripts/Keys)
-------------------------------------
If -names is specified together with -array and an array value then the
array will contain an exported, integer-indexed list of array names
(subscript keys) rather than array values. The resulting integer-indexed
array can then be used to index the original array in that sorted order

Example:

    array set unsorted {                                                        
       The     orange                                                           
       quick   yellow                                                           
       brown   blue                                                             
       fox     black                                                            
       jumps   magenta                                                          
       over    white                                                            
       the     cyan                                                             
       lazy    green                                                            
       dog     red                                                              
    }                                                                           
                                                                                
    array sort unsorted -array sorted -names  # Output to array 'sorted'        
    set size [array size sorted]              # Grab the array size             
    option expression off                                                       
                                                                                
    for {set i 0} {[< $i $size]} {incr i} {   # Iterate numerically             
        printf "%4i:\tref: %s =>\t%-40.40s\r\n" $i $sorted($i) \                
        $unsorted($sorted($i))                                                  
    }                                                                           

Results:

    0:   ref: brown =>   blue                                                   
    1:   ref: dog =>     red                                                    
    2:   ref: fox =>     black                                                  
    3:   ref: jumps =>   magenta                                                
    4:   ref: lazy =>    green                                                  
    5:   ref: over =>    white                                                  
    6:   ref: quick =>   yellow                                                 
    7:   ref: The =>     orange                                                 
    8:   ref: the =>     cyan                                                   

[array sort] is not a standard Tcl command

See also: arrays, array list, array, swap, Tcl lists, carray

array statistics

    array statistics arrayname   

Show statistics for a given array. This shows the total number of root 
entries in the array's hash-table as well as the total number of buckets 
(nodes) including the root entries. 

Additionally, a summary is shown of the first 10 most relevant top-level 
bucket allocation counts. Ideally the average search depth for a given root 
entry should not be much more than about 2 or 3 but as the allocation is
largely random and this is therefore impossible to achieve completely. 
Ticol enhances the usual Tcl statistics table by showing the total bucket
count per-category in brackets.

Example:

    option echo on                                                              
    option expression on                                                        
    for {set i 0} {$i < 2000} {incr i} {                                        
       set a($i) [randomstr 20]                                                 
    }                                                                           
    array statistics a                                                          

Result:

    2000 entries in table, 1000 buckets                                         
    number of buckets with 0 entries: 0     (0)                                 
    number of buckets with 1 entries: 270   (270)                               
    number of buckets with 2 entries: 269   (538)                               
    number of buckets with 3 entries: 173   (519)                               
    number of buckets with 4 entries: 94    (376)                               
    number of buckets with 5 entries: 36    (180)                               
    number of buckets with 6 entries: 16    (96)                                
    number of buckets with 7 entries: 3     (21)                                
    number of buckets with 8 entries: 0     (0)                                 
    number of buckets with 9 entries: 0     (0)                                 
    number of buckets with 10 or more entries: 0    (0)                         
    average search distance per entry: 2.32                                     

Array hash-table performance can be configured using ticol.ini settings
but it is strongly recommended to keep to the defaults

See: ticol.ini

See also: arrays, array, array walk, carray

array swap

    bool [array swap arrayVar subscript1 subscript2]

Swap the contents of two array elements

Example:

    set a(0) world                                                              
    set a(1) Hello                                                              
    puts "$a(0) $a(1)"                                                          
    array swap a 0 1                                                            
    puts "$a(0) $a(1)"                                                          

Results:

    world hello                                                                 
    Hello world                                                                 

See also: array, array sort, array item

array_to_string, string_to_array

    string [array_to_string arrayname ?-join string? \
        ?-start element? ?-stop element? ?-stopstr string? ?-nocase?]

    integer [string_to_array string arrayname ?-start elementnum? \
        ?-chars? ?-asc? ?-append? ?-stopchar char?]

[array_to_string] converts an array which may be, and is most useful with,
a byte array, to a string. See [split] for the inverse command.

[string_to_array] will convert either:

a multi-line string (default) to a series of character array elements
with a base 0 index containing each line.

    or:

if '-chars' is used then will split the string into characters with one
character in each array element. When used in character mode with '-chars'
this behaviour can also be modified to store either ASCII characters
(default) or the ASCII integer value by using '-asc' option

If the array exists then unless -append is used an error will be raised to
prevent accidental overwriting. When using -append mode a starting 
index value in the existing array may be specified.

[string_to_array] returns a count of the number of lines appended or the
number of characters appended if used in -chars mode.

'-stopchar <char>' will halt processing at the given character. This is
useful for trimming whitespace or other characters from the stored string

[string_to_array] can't have both -start and -append. -append will 
override any -start setting and will, instead, append to the end of any
existing array. -start will assign elements from the starting subscript
value onwards

Example:

Byte-array to string conversion

    array set a {                                                               
       0 H                                                                      
       1 e                                                                      
       2 l                                                                      
       3 l                                                                      
       4 o                                                                      
    }                                                                           
    puts [array_to_string a]                                                    

Result:

    Hello                                                                       

Example:

String to byte-array conversion with append and stop filter

    array set a {                                                               
       0 H                                                                      
       1 e                                                                      
       2 l                                                                      
       3 l                                                                      
       4 o                                                                      
       5 " "                                                                    
    }                                                                           
    set s "world! 12345"                                                        
    string_to_array $s a 6 -append -chars -stopchar " "                         
    loop i 0 [array size a] {                                                   
        puts "$i:\t$a($i)"                                                      
    }                                                                           

Result:

    0:      H                                                                   
    1:      e                                                                   
    2:      l                                                                   
    3:      l                                                                   
    4:      o                                                                   
    5:                                                                          
    6:      w                                                                   
    7:      o                                                                   
    8:      r                                                                   
    9:      l                                                                   
    10:     d                                                                   
    11:     !                                                                   

See also: array, arrays, carray

array trim

    array trim arrayVar ?maskchars? ?-left|-right?

Trims the left and/or right hand ends of all array values

Example:

    # Create a ragged, CRLF-delimited string with leading/trailing spaces       
    option expression off                                                       
    set s "  The quick brown                                                    
         fox jumped                                                             
             over the                                                           
      lazy dog"                                                                 
                                                                                
    puts $s                                                                     
    puts [string repeat - 30]                                                   
    split $s "\r\n" -array a                                                    
    array trim a                                                                
    for {set i 0} {< $i 4} {++ i} {  # Print in array order                     
       puts '$a($i)'                                                            
    }                                                                           

Result:

      The quick brown                                                           
           fox jumped                                                           
                     over the                                                   
           lazy dog                                                             
    ------------------------------                                              
    'The quick brown'                                                           
    'fox jumped'                                                                
    'over the'                                                                  
    'lazy dog'                                                                  

See also: array, trim, string repeat, carray

array unset

    integer [array unset ?::?arrayName ?::??arrayName?... ?-nocomplain? ?-const?]
    integer [array unset ?::??arrayElement(X)?... ?-nocomplain?]

Delete an array and all elements or individual elements.
Returns an integer count of items successfully deleted
The dollar $ prefix must not be passed to [array unset]
Const arrays must be deleted using the -const argument
If -nocomplain is used with multiple arguments then execution will continue
with the remainder if an error is encountered

    array unset arrayName                                                       

If the array is in global scope then use the '::' prefix (omitting the $) 
as:   

    array unset ::arrayName                                                     

Array object types may be mixed, either whole array names or individual 
array elements.

    array unset a b(1) c d e(index) -nocomplain                                 

It is an error to attempt to unset a non-existent or const array
variable but the -nocomplain argument may be used to ignore this behaviour
If -nocomplain is not passed then processing will halt at the first error

See also: unset, arrays, array, clear, vars, carray

array walk

    array walk arrayName ?count? ? ?-integer? ?-bare?

Walk (iterate) the array hash-table (not a standard Tcl command)
If the 'count' argument is omitted the entire array is iterated

If -integer is used then an array will be output in integer order starting
at array element "0"

If -bare is used then no structural debug information is included and
only the array contents are output

   --------------------------------------------------                           
   hash_table::walk() Object:0x973290 size:500                                  
   --------------------------------------------------                           
   Table(1) Node: 0x9d8500 chained nodes:1                                      
     0x9d8500 depth:1 key:'75' value:'WdqUPVnSLNLTMfeeEqTO'                     
   Table(5) Node: 0x9d8a80 chained nodes:1                                      
     0x9d8a80 depth:1 key:'50' value:'gPkhMdxxowGitxhibQgd'                     
   Table(10) Node: 0x9d8d90 chained nodes:1                                     
     0x9d8d90 depth:1 key:'76' value:'KMtJygZvZouAvRBUPVqg'                     
                                                                                
   Root size:500 Root occupation:89 (17.80%) Max depth:2 Occupied nodes:100     
   --------------------------------------------------                           

[array walk] is not a standard Tcl command

See also: arrays, array, array statistics, carray

Array Notes

Ticol supports subscripted, associative Tcl arrays. This offers arrays with
either traditional numeric subscripts or subscripts indexed by a case-
sensitive string. The subscript may be a string-literal or a Tcl variable
name prefixed by a dollar $ sign or $:: global scope indicator to access
global variables within procedures. Numeric subscripts are still supported
but the subscript will simply be a string value which may be intermixed
with non-integer subscript values within the same array

Some languages such as PERL call associative arrays 'hashes' which
reflects the underlying hash-table data structure used for storage. Ticol
also uses dynamic hash tables to store array data.

Example
-------

    set a(0) Hello           # Integer subscript                                
    set a(place) world       # String subscript                                 
    puts "$a(0) $a(place)"   # Prints 'Hello world'                             

Variable Promotion
------------------
Ticol Tcl allows the promotion of simple variables to array type using
[set]. This is not a standard Tcl feature and is provided to simplify
the working of the [static] command. No checks are made when promoting
(overwriting) a variable from a simple type to a more complex array type.

    set a Hello     # Set 'a' as a standard variable                            
    puts $a                                                                     
    set a(0) Hi     # Reset and promote to array type, discarding contents      
    puts $a         # Will show <array> type placeholder                        
    puts $a(0)                                                                  

Result:

    Hello                                                                       
    <array>                                                                     
    Hi                                                                          

See: array

Multi-Dimensional Arrays (Arrays of Arrays)
-------------------------------------------
There are no multi-dimensional arrays in standard Tcl but they may be
emulated by various means. This usually involves compound subscript values
using embedded commas which, when combined, form a unique hash key e.g. 
'arrayvar(x,y)' where the key value is "x,y"

These arrays allow 'tree-like' structures to be created using arrays
Such structures will typically require recursive code to iterate

1 and 2 dimensional C-like arrays are provided via the ticol_carray plugin

See the Tcl websites for a discussion

Example:

    # Emulated multi-dimensional array                                          
    set a(0,1) "Item one"                                                       
    set a(0,2) "Item two"                                                       
    set a(1,1) "Item three"                                                     
    set a(1,2) "Item four"                                                      

Ticol, however, supports enhanced, nested, arrays of arrays via nested
dollar addressing. So, ${$a(1)(0)} will reference array element 1 of a(),
subscript item 0. Array elements must exit. If the array is sparse and 
not all array elements in a range exist then use [array item] to address
safely

Example:

    # Will iterate any number of child arrays belonging to array 'a'            
    # First, init the top-level array, a                                        
    # This is as simple as naming the array in the element                      
    # If tested, these elements will return "<array>"                           
    # [is_array] may also be used to test, selectively                          
    set a(0) b                                                                  
    set a(1) c                                                                  
                                                                                
    # Init child array, b                                                       
    set b(0) $qbf                                                               
    set b(1) "b element 1"                                                      
                                                                                
    # Init child array, c                                                       
    set c(0) "Hello world"                                                      
    set c(1) "c element 1"                                                      
                                                                                
    loop i 0 [array size a] {                                                   
       loop j 0 [array size $a($i)] {                                           
          puts "a($a($i)($j)) -> '${$a($i)($j)}'"                               
       }                                                                        
       newline                                                                  
    }                                                                           

Interaction of array 'trees' can also be achieve using recursion as follows:
Note that Ticol uses the '@' character for [uplevel] rather than '#'

    const QUOTE [chr 34]                # [chr 34] could be used inline         
    proc delete_array_recursive {_q} {                                          
       upvar $_q q                                                              
       if {is_array q} {                                                        
          loop i 0 [array size q] {     # Check element for child array         
             if {[uplevel @0 "is_array $::QUOTE$q($i)$::QUOTE"]} {              
                delete_array_recursive ::$q($i)                                 
             }                                                                  
          }                                                                     
          uplevel @0 "array unset $_q"  # Use [uplevel 1 array unset]           
       }                                                                        
    }                                                                           
    # Assumes parent array 'a' in this example                                  
    delete_array_recursive a                                                    

Case (In)Sensitive Subscripts
-----------------------------
Array subscripts are case-sensitive with the exception of the $argv and
$env system arrays. Subscripts are referenced case-insensitively so that
$env(TEMP) and $env(temp) will both return the correct array element.
Large arrays cannot be efficiently traversed when addressed case-
insensitively. The internal hash table has to be traversed in a linear
fashion in such cases (See: array index)

Some Tcl commands have been extended to include case-insensitive lookups.
These lookups are far less efficient than case sensitive ones

An array can be defined as case-insensitive using [array set] with the
-nocase argument

Example:

    array set a {q Hello} -nocase                                               
    puts $a(q)        # Reference using lower case                              
    puts $a(Q)        # Reference using upper case                              

Result:

    Hello             # Same result for either upper or lower case              
    Hello                                                                       

Spaces in Subscripts
--------------------
Whitespace is allowed in a subscript value but should be wrapped in double
quotes

Example:

    set foo("a b c") zzz                                                        
    set bar("d e f") "Hello world"                                              

Declaration and Use
-------------------
Arrays can be declared using set only one level at a time. Multiple, 
simultaneous, nested arrays may not be declared (e.g. set a(b(c(1))) hello)
You can use [array set] to initialise multiple array elements

    set a(1) Hello          # A traditional literal numeric subscript           
    puts $a(1)              # Example dereference                               

                                                                                
    Hello                   # Result                                            

                                                                                
    set a(one) Hello        # An associative array with literal subscript       
    puts $a(one)            # Example dereference                               

                                                                                
    Hello                   # Result                                            

                                                                                
    set i 0                 # Create an index variable                          
    set a($i) Hello         # An associative array with variable subscript      
    puts $a($i)             # Example dereference                               

                                                                                
    Hello                   # Result                                            

                                                                                
    array set days {        # Create a const array of 7 elements                
       1 Monday                                                                 
       2 Tuesday                                                                
       3 Wednesday                                                              
       4 Thursday                                                               
       5 Friday                                                                 
       6 Saturday                                                               
       7 Sunday                                                                 
    } -const                                                                    

Arrays are referenced by dollar prefix, the same as any other Tcl variable
Note that the array syntax prohibits any whitespace between the variable
name and the open parenthesis: 

   Correct:   "$varname($index)"
   Incorrect: "$varname ($index)" (looks for non-array variable '$varname')

Arrays as Command Arguments
---------------------------
Not all Ticol commands accept arrays as output arguments. Some do, such as
[setat]. Check with the section for each command

Most commands won't accept nested/complex array expressions as output
arguments (i.e. as the name of target variables to be written). 

Most will accept arrays and complex array expressions as input arguments
as long as the array is resolved before the call

Sorting
-------
Associative arrays cannot be sorted internally, they are, however, rapidly
accessible by the hashed key (subscript) value so sorted indexes are 
typically unnecessary.. See [array sort] for sorting workarounds

Storage and Efficiency
----------------------
All arrays are stored in a dynamic hash-table as associative arrays. Due to 
this internal storage method, arrays are not contiguous in memory and thus 
cannot be natively sorted. To sort an array, output it to a sorted list

You can create an array with a fixed table allocation and avoid the 
performance costs of resizing by declaring to a high enough initial size. 
Use array create arrayName size, e.g. array create big 10000

Arrays indexed by numeric value can be indexed in pseudo-sorted order by 
use of a numeric subscript index value and a [for] loop or returned as a
list

Array storage is expanded dynamically from a minimum reserve allocation of
1 element

Any valid object which can be stored in a standard Tcl variable may be 
stored in an array element

Storing Types
-------------
The empty string "" is stored in an array element as an empty Tcl list {}

Arrays of lists and other types are possible but it should be understood 
that a Tcl array is not a contiguous binary array and is unsuitable for 
passing forward to an external C/C++ DLL

Arrays of Lists
---------------
Since Tcl lists are strings, Tcl arrays may have a list as the array value

    array set economists {                                                      
       1 {HH002 "Henry Hazlitt" 002 87}                                         
       2 {WB001 "Walter Block" 072 77}                                          
       3 {PB001 "Phillip Bagus" 033 22}                                         
       4 {MR001 "Murray Rothbard" 023 19}                                       
    } -const                                                                    

And this would be equally valid since the subscript key is unique

    array set economists {                                                      
       HH002 {1 "Henry Hazlitt" 002 87}                                         
       WB001 {2 "Walter Block" 072 77}                                          
       PB001 {3 "Phillip Bagus" 033 22}                                         
       MR001 {4 "Murray Rothbard" 023 19}                                       
    } -const                                                                    

Passing Arrays to Procedures
----------------------------
Arrays can only be passed via a proc body by reference (by name) and using
a subsequent upvar command. To pass an array by value you must convert to
a list

This is by design. Arrays are intended to be passed by reference (by name)

[array item] is the most convenient way to handle the double-dereference of
unknown array names passed to a proc

Example:

    array set colours {                                                         
       1 red                                                                    
       2 green                                                                  
       3 blue                                                                   
    } -const                                                                    
                                                                                
    proc list_by_key {arrayName} {                                              
       upvar $arrayName                                                         
       foreach key [lsort [array names $arrayName]] {                           
          puts "Key: '$key [array item $arrayName $key]'"                       
       }                                                                        
    }                                                                           
                                                                                
    list_by_key colours                                                         

Nested Array References
-----------------------
Nested array references are permissible. The outer variable must follow Tcl
syntax and NOT include a dollar prefix when calling [set]

    set c 1                # Create a subscript variable                        
    set b($c) hi           # Create an array variable                           
    set a($b($c)) Hello    # Set a nested associative array                     
    puts $a($b($c))        # Example nested dereference                         

    Hello                  # Result                                             

Each nested level must first be declared either using set or the [array]
command. Attempting to set an un-dereferenced intermediate array (i.e.
missing the $ symbol) will trigger an invalid argument error. 

(e.g. set a(b(c))) hello) -> error in 'b(c)')

Advanced Arrays
---------------
Ticol cannot create complex arrays such as arrays of struct, arrays of
stack etc. The array variable, can however, point to the name of a
complex variable type or a standard Tcl variable which holds a list

Array Debugging
---------------
The statistics for an array are inspected using: 

[array statistics arrayname]

and the array can be inspected in detail using [array walk]

See also: array, array create, array exists, array list, array size,
          array sort, array statistics, array unset, array walk,
          dictionaries, carray, string_to_array

asc
 
    integer [asc character|string]
    integer [asc "character"|"string"]

The inverse of chr. Returns the ASCII (ANSI) numeric value of a string

Special chars such as []${} etc. must be escaped (See: escape sequences)
Strings with escaped characters must be wrapped in double-quotes

Examples:

    puts [asc "A"]      # Double-quoted argument                                
    puts [asc {A}]      # Braced argument                                       
    puts [asc A]        # Raw argument                                          
    puts [asc "\["]     # Special character [ must be escaped and quoted        

Result:

    65                                                                          
    65                                                                          
    65                                                                          
    91                                                                          

A procedural equivalent would be something like:

    undef asc -nocomplain          # Undefine the built-in command              
    proc asc { char } {                                                         
       scan $char %c value                                                      
       return $value                                                            
    }                                                                           
    puts [asc A]                                                                
    puts [asc a]                                                                

Result:

    65                                                                          
    97                                                                          

See also: chr, string, left, right, mid, index

assert

    assert lvalue ?{rvalue-expression}? ?-line line? ?-v? ?-var name?\
          ?-crlf? ?-trace? ?-unescape? ?-debug?

Asserts an expression using a substitution variable in the expression
This expression is in the current scope context.Failure raises a 
trappable error exception which can be handled by [catch] or [try/catch]

A temporary variable is created in the current scope which receives the
result of the first argument. The default name is $_ . You can change this
to any name using the ?-var name? option as long as the variable does not
already exist in the current scope. The substitution variable represents
the evaluated expression and may be used in the test expression

If you supply a line number this will be displayed in the output

No output is issued if the assertion is true unless the '-v' (verbose)
argument is specified. Failures are always displayed. Thus, [assert] 
statements may be left within active code. See: NDEBUG (below)

By default, [assert] will not escape the lvalue input. You can enable
this by using the '-unescape' argument which will unescape backslash
escape sequences. Nor does [assert] subst its lvalue input so variables
which are wrapped in braces and [subst] or [eval] must be called directly
if required

The expression rvalue argument is optional must be supplied in braced
format. The default rvalue is {== $_ 1} which resolves to TRUE

If you want a CRLF emitting after the test expression has been executed
use the -crlf argument. This option should come before '-line #__LINE__'

The -trace argument will allow you to inspect the lvalue and rvalue
being compared. This can be useful when debugging unexpected results from
escaped strings for example.

After debugging is complete all [assert] statements can be disabled
or re-enabled globally by using the following Macro definition.

    #define NDEBUG           # Disable all [assert] commands                    
    #undef NDEBUG            # Re-enable all [assert] commands                  

Once the macro variable NDEBUG is defined [assert] statements are skipped
and have almost no execution overhead within a script

Float values should incorporate an appropriate level of rounding using
[expr ... round(n)] or [round n]

-debug shows the argument list as it is passed to [assert] to aid 
introspection and error checking.

Resetting NDEBUG with #undef NDEBUG will have no effect on the rest of 
the script. The entire script will be affected by the first instance of
#define NDEBUG which is found since this has effect only at macro 
preprocess time and these events are out-of-order with sequential Tcl
command processing

It may be convenient to write [assert] as a one-line command

For more information, see: macro preprocessor

Examples
--------

     # Note that the default placeholder for 'this' is $_                       
     assert 1 {eq $_ 1}                                                         
     assert 1                                                                   

Result:

    # PASS - Program continues without interruption (no output)                 

Example:

     assert 1 {eq $_ 1} -line 10 -v                                             
     assert 1 -line 10 -v                                                       

Result:

    # Program continues without interruption and displays confirmation          

    assert: PASS 'eq $_ 1'                                                      

Example:

     assert 0 {eq $_ 1} -v -line #__LINE__  # Default internal variable         
     assert 0 -v -line #__LINE__                                                

Result:

    assert: Line 10: Assertion failed: '0' != 'eq $_ 1'                         

    # Program execution halts                                                   

Example:

    assert 1 {eq $x 1} -var x                                                   

Result:

    # Test passes OK. No output                                                 

Example:

    assert 1 {eq $x 1} -v -var x -line #__LINE__                                
    assert 0 {eq $x 1} -v -var x -line #__LINE__                                

Result:

    assert: Line 1: PASS 'eq $x 1'                                              
    assert: Line 2: Assertion failed: '0' != 'eq $x 1'                          

    # Program execution halts                                                   

Example:

    #define ndebug                                                              
    # The following statement would normally fail but is now ignored            
    assert 1 {eq $_ 0}                                                          

Result:

    # Assertion is ignored. Execution continues without assert                  

Example:

    assert [round $pi 5] {== [round $_ 5] 3.14159} -v -line #__LINE__           

Result:

    assert: Line 1: PASS '== [round $_ 5] 3.14159'                              

See also: debugging

Associative arrays

Older languages like C, C++, BASIC, and Java support arrays in which the
array subscript index value is an integer and where that subscript index 
represents some offset into a single memory block. Tcl, in common with
many modern scripting languages such as Perl, Python or PHP supports
'associative' arrays in which the index value is an arbitrary string value
or string representation of a number. In associative arrays there is no
counted binary offset from one location in a block of memory to the next. 
The underlying data structure in the case of Ticol is the "hash table"
Associative arrays held in a hash-table are inherently unordered

Associative array syntax places the subscript within parentheses as with
other languages. There is no need to declare an array before assigning.
You can assign any valid variable name as an array a long as it is not
already assigned and in use

If you intend to assign a very large array you can use [dim] to pre-
allocate the underlying hash-table to speed-up assignment later on

Example:

    set name(first) "Gordon"            # 'first' is a string literal           
    set name(last)  "Bennett"            # 'last' is a string literal           
                                                                                
    puts "Full name: $name(first) $name(last)"                                  

Result:

    Full name: Gordon Bennett                                                   

As with all Tcl variables, you dereference a variable by prefixing with
a dollar symbol $ but cases which refer to a variable name without
dereferencing do not require a dollar prefix  e.g. puts $name(first)

Some functions, such as [unset] require the literal name of the array, 
e.g. [unset name], [unset name(first)]

The system environment is provided via a pre-allocated const array 'env'
e.g. 
                                                                                
    puts $env("PATH")                                                           

Ticol command line arguments are provided via the const array 'argv' with
integer 'argc' representing the argument count.
e.g. puts $argv(0)

Double-dereferencing such as $$array(subscript) can be handled

For detailed information, see: arrays

See also: array, arrays, array find, env, argv, dim, dump, array unset
          double dereference

autoexec
 
    autoexec ?on|off?

This option interacts with 'proc unknown'. It controls the passing of
unrecognised/unknown commands to the underlying operating system or the
calling of a special procedure. Autoexec is ON by default, allowing Ticol to
be used as a Windows command-shell

If autoexec is 'on' then unknown commands are passed to Windows IF and only
IF a proc called "unknown" isn't defined. C/C++ style escape characters such
as \r or \n are also disabled at the console to ensure correct operation
with Windows

If autoexec is 'off' and a proc called "unknown" is not defined, then the 
command will return an error. Turning the option 'off' will re-read any INI
configuration settings for BackslashEscape for the CLI (see: help ini)

The command 'autoexec' with no arguments will return the current status 
as 'on' or 'off'

This is also configured in ticol.ini [config] as AutoExecCommand=<boolean>

The autoexec option is Ticol specific and not standard Tcl. 

The autoexec option does not relate to the use of an autoexec.tcl file

For more information, see: unknown

See also: autoexec.tcl, option, ini, commands, exec, spawn, cli, 
          debugging, macro, unknown

autoexec.tcl
 
    autoexec.tcl - The Ticol startup script

If a file called autoexec.tcl is found in the same folder as ticol.exe at
startup then this will be executed automatically unless configured
otherwise or set to do so via the command line. This is the Ticol
equivalent to the DOS/Win AUTOEXEC.BAT file. 

Due to inherent risks in running a script automatically, the autoexec.tcl
file may not be obfuscated, thus preventing potentially damaging commands
from being hidden. Any obfuscated autoexec.tcl file will not be processed

autoexec.tcl will only be run from the exe path location

You may disable the execution of autoexec.tcl using either the command line 
option /NA or via the ticol.ini file setting:

    [config]                                                                    
    AutoExecScript=FALSE                                                        

An autoexec.tcl file may be used to initialise common variables or define
a set of common procedures before an interactive CLI session or running
another script

If autoexec.tcl ran successfully and returned code ok then variable
$::autoexec_ran will be set to $::true (1), otherwise $::false (0)

autoexec.tcl is never run in CGI mode. Programmers must rely on ticol.ini
or the active script for configuration

Note that autoexec.tcl may NOT be encrypted/obfuscated. This is by design

autoexec.tcl has nothing to do with the [option autoexec] setting
for the Ticol Command Line Interpreter (CLI)

See also: configuration, obfuscation, autoexec, at_exit

at_exit

    proc at_exit {} {?script?}
    sub at_exit {?script?}

[at_exit] is an optional sub or user-defined procedure which is called when
a Tcl script exits. [at_exit] takes no arguments or than the main body 
script. Any arguments passed to [at_exit] will be ignored

The script body is optional and may be empty

[at_exit] is called only via running scripts and never from the CLI. It is
not called unless the script exits the Ticol binary via control statements
or by conclusion of a script. Exiting Ticol using CTRL+BREAK or by typing
'exit' at the CLI prompt will bypass any defined [at_exit] script.

The [upvar] command is required within an [at_exit] procedure to access 
variables defined elsewhere in a script as with any other procedure or sub

[at_exit] is intended for use as a cleanup routine, to close DLL and open
file handles safely, avoid memory leaks, file or database corruption etc.

[at_exit] is not called when Ticol is exited abruptly using CTRL+BREAK

Where a [stop] command is encountered within [at_exit], execution will be
returned to the CLI. Where an exit command is met within [at_exit], Ticol
will exit

Where neither is given the default behaviour will apply (exit if run from 
Windows or stop and return to the CLI if run from the CLI)

Unlike C++, multiple [at_exit] routines are not provided for. There is no
need to register multiple [at_exit] routines. One routine can call all
desired shutdown code, including other procedures

Example:

    undef at_exit -nocomplain                                                   
                                                                                
    proc at_exit {} {                                                           
       puts "Inside proc 'at_exit'"                                             
       upvar handle                                                             
       upvar pointer                                                            
       puts "Handle is $handle"                                                 
       puts "Pointer is $pointer"                                               
       puts "Exiting Ticol"                                                     
       return 0                                                                 
    }                                                                           
                                                                                
    puts "In main()"                                                            
    set handle 10                                                               
    set pointer 2                                                               
    puts "Exiting script"                                                       
    stop                                                                        

Result:

    In main()                                                                   
    Exiting script                                                              
    Inside proc 'at_exit'                                                       
    Handle is 10                                                                
    Pointer is 2                                                                
    Exiting Ticol                                                               

Example:

    proc at_exit {} {                                                           
       # If undefined then has no effect on the program exit                    
       textcolor lightred                                                       
       puts "* Stopped *"                                                       
       textcolor                                                                
       # exit          # Exit to O/S if desired                                 
       # stop          # Return to CLI if desired                               
       return 23       # Return ERRORLEVEL value to the O/S                     
                       # Else exit to original start environment                
    }                                                                           

Example:

    ticol.exe "proc at_exit {} {puts Done}"; test.tcl                           

Example:

    ticol.exe "sub at_exit {puts Done}"; test.tcl                               

See also: on_error, upvar, run, cli, exit, stop, return, autoexec.tcl

on_error

    proc on_error {} {?script?}
    sub on_error {?script?}

[on_error] is an optional sub or user-defined procedure which is called when
a Tcl script encounters a non-caught trappable error. [on_error] takes no 
arguments or  than the main body script. [on_error] is not called where 
errors are caught or trapped by [catch] or [try]..[catch].

Any arguments passed to [on_error] will be ignored

The script body is optional and may be empty

[on_error] may be used to cleanup or take remedial action in the event of an
error occurring such as unloading a Ticol plugin module or logging an event.

Errors generates within the [on_error] script are blocked from recursively
generating futher errors. The preferable course of action is to perform any
necessary cleanup then call [die]

Note that variables such as errorMsg are global and should be addressed 
within the [on_error] script via the '::' scope prefix.

See also: error, at_exit, die, sub, proc

atof, ftoa

    double [atof <scientific-number-format-string>]                             
    string [ftoa value]                                                         

Converts a string containing a number in "scientific notation" format
e.g. "2e+10" into a double value. This will convert only within the range
of an 8 byte double representation

Scientific notation numbers are not implemented within [expr] in order
to increase efficiency of the interpreter and since their use is likely
to be quite low.

This is a temporary/intermediate fix for the lack of "scientific" number
handling within Ticol. [atof] may be embedded within [expr] expressions

The inverse of [atof] is [ftoa] which will convert a floating point number
in non "scientific" notation back to such. [format %e] may also be used to
convert from standard to "scientific notation" format

Be aware that double precision numbers may not precisely represent certain
values

Similar functionality is provided by the ticol_sci plugin via [sci] and [dec]

Examples:

    puts [atof 2e10]                                                            
    puts [atof 2e-5]                                                            
    puts [atof 32e+4]                                                           
    puts [ftoa 320000.000000]                                                   
    puts [atof 32.345e-4]                                                       
    puts [atof 0.001234e+6]                                                     
    set q "0.001234e+6"                                                         
    puts [expr [atof $q]*$pi]                                                   
    puts [ftoa [expr [atof $q]*$pi]]                                            
                                                                                
    # Division and cast using scientific notation                               
    set a 2.2e1                                                                 
    set b 7                                                                     
    puts [/ [double [atof $a]] $b]                                              

Results:

    20000000000.000000                                                          
    0.000020                                                                    
    320000.000000                                                               
    3.200000e+005                                                               
    0.003235                                                                    
    1234.000000                                                                 
    3876.725334529804500                                                        
    3.876725e+003                                                               
    3.142857142857143                                                           

    https://en.wikipedia.org/wiki/Scientific_notation

See also: sci, dec, math

base64

    string [base64 string -encode | -decode ?-wrap width?]
    string [base64 varName -var -encode | -decode ?-wrap width?]

Base64 encode or decode a string or variable according to the argument
parameter given. -var will specify that a variable rather than string
constant has been passed.

Where a -wrap value is given this will control line-wrapping on the 
returned base64 code. The default is no line-wrapping (0)

The minimum -wrap width is 4 characters

Example:

    set a [base64 "Hello world!" -encode]                                       
    puts $a                                                                     
    puts [base64 $a -decode]                                                    

Result:

    SGVsbG8gd29ybGQh                                                            
    Hello world!                                                                

Example:

    puts [base64 $qbf -encode]                                                  
    newline                                                                     
    puts [base64 $qbf -encode -wrap 10]                                         

Result:

    VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==                
                                                                                
    VGhlIHF1aWNr                                                                
    IGJyb3duIGZv                                                                
    eCBqdW1wcyBv                                                                
    dmVyIHRoZSBs                                                                
    YXp5IGRvZw==                                                                

See also: encrypt, decrypt, mkn, cvn

Tips for Writing Big Programs

Ticol Tcl is intended for small scripts relating to administrative use. 
It was written by the author to be used for general housekeeping tasks
as a simple alternative to PowerShell and batch script

Here are some tips for developing larger programs:

Make full use of [proc]

    Divide large scripts into many small procedures except where
    performance is critical and inline code required

Use file-level modularity

    Break big scripts into smaller files and call using [source scriptname]
    Be aware that each call to [source scriptname] will evoke the macro
    preprocessor (MPP) which has a time cost
    Arguments to external files are permissible via [source scriptname]

Use the Macro Pre Processor

    For conditional execution, where appropriate

Make full use of plugin libraries with [lib]

    A range of functionality is available via Ticol plugin DLLs

See also: performance, faq, eval

bit

Currently, only one subcommand implemented [bit invert]

    integer [bit invert value ?-8|16|32|64?]

Bitwise invert a value, optionally according to a given mask width

The inversion mirrors or flips the bit pattern left-to-right

    0x0001 -> 0x1000

The inversion is performed against the specified mask-width with
a mask of 64 being the default

Example:

    puts [inttobin [bit invert 1]]                                              
    puts [inttobin [bit invert 1 -8]]                                           
    puts [bit invert 0x0180 -16]                                                
    puts [inttobin [bit invert 0x0180 -16]]                                     
    puts [inttobin [bit invert 0b1100000000 -16]]                               

Result:

    0b1000000000000000000000000000000000000000000000000000000000000000          
    0b10000000                                                                  
    384                                                                         
    0b110000000        # 384 decimal                                            
    0b110000000        # 384 decimal                                            

See also: << >> ~

calc - Auto-optimising alternative to [expr]

    calc  <expression>  ?-explain?
    calc {<expression>} ?-explain?
    calc "<expression>" ?-explain?
    calc <arg> <arg...> ?-explain?

Inline Tcl commands are faster and more efficient than using [expr].
The Macro PreProcessor will expand [calc] natural 'infix' expressions
into native Tcl commands using Polish 'prefix'-style notation

[calc] is identical to [expr] and may be used in place of [expr]. 
It accepts the same expression format argument(s). However, unless the 
command-line argument /NEO (No Expression Optimisation) or the 
[option preprocessor off] directive is used, then the expression will
be expanded and rewritten by the Macro PreProcessor (MPP) into native Tcl
format commands before running. If /NEO is used then [calc] will be 
interpreted exactly as for [expr]

The '-explain' argument is should be used only at the Ticol CLI rather
than load/macro-preprocess runtime. It must also be the last argument in
any test expression. Since constants are optimised-out, the resulting
expression may be a single value. A separate command, [explain] can be
used to view a fully-expanded expression.

Example:

    # Example using -explain from the CLI                                       
    puts [calc 1+2/3.0-4*4]                                                     
    puts [calc 1+2/3.0-4*4 -explain]                                            
    puts [explain 1+2/3.0-4*4]                                                  

Result:

    -14.333333333333332                                                         
    [- [+ 1 [/ 2 3.0]] [* 4 4]]                                                 
    [- [+ 1 [/ 2 3.0]] [* 4 4]]                                                 

[calc] supports the same range of math operators as [expr] 
(excludes ++ and --)

[calc] requires a bracketed declaration - Use [calc ...] not calc ...

The translated command strings are typically around 50% faster than [expr]
as [expr] has an error and syntax-checking overhead as well finally calling 
[eval] with the results. [calc] hugely speeds up performance by
pre-processing this task before run time

Ticol expressions are BEDMAS-optimised unless brackets are used to enforce
ordering. Function calls are resolved to the [funct] command.

If the command-line argument /NEO is used then [calc] will behave in exactly
the same way as [expr]; This makes [calc] much more flexible as well as 
significantly improving perform performance on math tasks. In addition, 
this enables the '-explain' argument. If problems are encountered then use 
/NEO to disable [calc] translation and it will be interpreted as [expr]

If the result of [calc] is a string value and if [option expression] is 
set to OFF and this value is passed to [eval] then the result may be 
misinterpreted as a command. The fix is to ensure that you use 
[option expression on] when handling string returns when chaining the
result to [eval]

You may examine the translated result using the /ECHO command line argument
if manual checks are required on the translated expression 

Example:

    set f 3                                                                     
    puts [calc {$f+2-($f==2)}]                                                  

Translation (requires ticol.exe /ECHO to output):

    puts [- [+ $f 2] [== $f 2]]                                                 

Result:

    5                                                                           

Example:

    option PreProcessor off          # Turn off MPP for the command line        
    set s [calc 1/2.0+3*4 -explain]  # Get the translated Tcl code              
    puts $s                          # Display the Tcl code                     
    puts [eval $s]                   # Evaluate it and get the result           
    option PreProcessor on           # Turn the MPP back on again               

Result: (Expanded Tcl commands)

    [+ [/ 1 2.0] [* 3 4]]                                                       
    12.5                                                                        

[calc] will also attempt to simplify constant-only expressions by
evaluation and then substitution. Disable using the /NEO command argument

Example:

    puts [calc {[rnd 2 4] > 3  ? {return Higher} : {return "Same/lower"}}]      

Interpreted as:

    puts [? [> [rnd 2 4] 3] {return Higher} {return "Same or Lower"}]           

Result:

    Either Higher or lower                                                      

Example: (save as script.tcl)

    # script.tcl                                                                
    set s [calc "1 / 2.0 + 3 * 4"]                                              
    puts $s                                                                     

Run as: ticol.exe script.tcl /echo /neo

    set s [calc "1 / 2.0 + 3 * 4"]                                              
    puts $s                                                                     

Run as: ticol.exe script.tcl /echo

    set s [12.5]                                                                
    puts $s                                                                     
    ticol.exe script.tcl                                                        

Note: (/NEO command-line argument)

The /NEO (No Expression Optimisation) argument also applies when creating
protected TCX files. Any TCX sub-file which needs to be run using the 
/NEO argument must be obfuscated using /NEO
 
Note: (Excess {} braces)

Do not wrap the entire expression to be passed to [calc] in braces or 
[calc] will be unable to interpret it. Braces prevent evaluation. 
Spurious error messages will result.

Example: (WRONG - expression is 'masked' by braces)

    puts "a and b are: '[calc {{$a eq $b} ? {return equal}\                     
     : {return different}}]"                                                    

Example: (CORRECT)

    puts "a and b are: '[calc {$a eq $b} ? {return equal}\                      
     : {return different}]"                                                     

Note: (/NES argument)

The /NES (No Expression Simplification) will disable automatic 
simplification and optimisation of expressions generated by [calc]

Note that where [calc] const expressions are pre-evaluated and generate
an error then this error will always be fatal and the preprocessing phase
will halt. This feature can be disabled using /NES on the command-line

Note also, that constants or variables with embedded # characters must
be enclosed in double-quotes

Example:

   puts [calc acos(10)]  # Will evaluate to -1.#IND00000000000 (invalid)        

Note that the literal hash (#) comment character can be used safely
within a quoted string

See also: explain, expr, Macro PreProcessor, /NEO

Handling Byte Arrays

Integer-indexed arrays may be used to store byte arrays as integer values
These may be converted to string using the following procedure:
Buffered [store] is used for rapid I/O. No pad characters are added.

Any zero (NULL) character encountered will truncate the string

    proc array_to_string_asc {arr} {                                            
       #################################################                        
       # Convert an integer byte-array to ASCII string                          
       #################################################                        
       upvar $arr a                                                             
       set j [array size a]                                                     
       for {set i 0} {< $i $j} {++ i} {                                         
          store add [chr $a($i)]                                                
       }                                                                        
       return [store get]                                                       
    }                                                                           
    set a(0) 72                                                                 
    set a(1) 101                                                                
    set a(2) 108                                                                
    set a(3) 108                                                                
    set a(4) 111                                                                
    puts [array_to_string_asc a]                                                

Result:

    Hello                                                                       

See also: arrays, array_to_string, string_to_array

Callbacks

Ticol Tcl procs can perform 'callbacks' to other Tcl procs where the
name of a callback proc is passes via the initial command reference

Example:

    option expression off                                                       
                                                                                
    proc test_callback {cbp} {                                                  
       puts "Enter test_callback -> '$cbp'"                                     
       for {set j 0} {[< $j 10000]} {++ j} {                                    
          if {[&& [> $j 0] [is_mod $j 1000]]} {                                 
             $cbp $j  # Call the callback proc                                  
          }                                                                     
          sleep 1                                                               
       }                                                                        
    }                                                                           
                                                                                
    proc callback {x} {                                                         
       puts "In callback $x"                                                    
    }                                                                           
                                                                                
    test_callback callback                                                      

Results:

    Enter test_callback -> 'callback'                                           
    In callback 1000                                                            
    In callback 2000                                                            
    In callback 3000                                                            
    In callback 4000                                                            
    ...                                                                         

See also: proc

Variable Casting

    signed char [char value]              # 8 bit signed                        
    unsigned char [uchar value]           # 8 bit unsigned                      
    signed short [short value]            # 16 bit signed                       
    unsigned ushort [ushort value]        # 16 bit unsigned                     
    signed long [long value]              # 32 bit signed                       
    unsigned long [ulong value]           # 32 bit unsigned                     
    signed integer [int value]            # 64 bit signed                       
    signed integer [signed value]         # 64 bit signed                       
    unsigned integer [uint value]         # 64 bit unsigned                     
    unsigned integer [unsigned value]     # 64 bit unsigned                     
    double [double value]                 # 8 byte FP                           

Tcl is effectively a typeless language. It handles all variables internally
as string values and, in most cases these are converted by Ticol to 64-bit
integers or 8-byte floating-point doubles when necessary for arithmetic
purposes

All Ticol integers are stored as 64-bit signed integers, internally 
(See: 'data type ranges' for data range values)

Conversion and type detection is automatic. There may be cases, such as 
marshalling data for DLL interfaces, or certain math routines, where you
may wish to coerce the type manually. The built-in Ticol casts are more
efficient than casts written in Tcl script

Non-decimal constants such as 0x1234 or 0o1234 are usually translated into
decimal format before execution by the Macro PreProcessor (MPP). If the
MPP is disabled using the /NP command-line option then translation is done
on-the-fly.

Binary conversion [bintoint], [inttobin] converts to unsigned integer

The work of unnecessary runtime conversion is therefore avoided by the MPP,
but this might cause unexpected side-effects if a script relies on the
original notation. If in any doubt, wrap such values in double-quotes,
this will prevent MPP translation

You may use the functions above to cast the string values to an appropriate
type. This may involve the truncation of the value with loss of precision.

[char] differs from [chr] in that [char] truncates any value to a valid
char integer range whereas [chr] accepts only a fixed range of argument
values (0..255) outside of which an error will be generated

[signed] and [unsigned] will be coerced from 64-bit signed or unsigned
integers with consequent 'wrap around'. Overflows will encounter truncation
e.g. unsigned short values over 65535 will be truncated at 0xFFFF and the
remainder will 'wrap around'

Appending ".0" to an integer will have the same math effect as a [double]
cast

Example:

    # Same effect from both concatenation of ".0" and [double] cast             
    [12345].0       -> 12345.0                                                  
    [double 12345]  -> 12345.000000000000000                                    

Numeric type conversions are built-in to Ticol. A cast will accept any kind
of numeric input, decimal integer, decimal real, hexadecimal, octal or
binary

You will still have to specify byte widths when marshalling data for export
via a DLL interface via [calldll*]

Note again, that non-decimal number types such as hex (0xNN) which are not
protected within quoted strings will be converted to decimal by the Macro 
Pre-Processor (MPP) before any script is run. This is designed to speed up
execution

 Example:                           Result                Comments              
 --------------------------------------------------------------------------     
 puts [int $pi]                     3                     Cast a constant       
 puts [char 240]                    -16                   Cast a char           
 puts [uchar 12345]                 57                    Masked to 0xFF        
 puts [chr 12345]                   Overflow              Invalid for [chr]     
 puts [char 0b1111000]              120                   Cast binary (0bX)     
 puts [uchar 0b1111000]             120                   Cast binary (0bX)     
 puts [double 0b111100011011]       3867.000000000000000  Cast binary (0bX)     
 puts [short 0o123]                 83                    Cast octal  (0oX)     
 puts [short 0x123ffff]             -1                    Truncated at 0x7FFF   
 puts [ushort 0x123ffff]            65535                 Truncated at 0xFFFF   
 puts [long 44073709551615]         -1244839937           Mask 0x7FFFFFFF       
 puts [ulong 4294967297]            3050127359            Mask 0xFFFFFFFF       
 puts [int 0x123ffff]               19136511              Valid integer         
 puts [double 0x123ffff]            19136511.000000000000 Valid double          
 puts -1                            -1                    64-bit integer        
 puts [unsigned -1]                 18446744073709551615  64-bit integer        
 puts [signed 18446744073709551615] -1                    64-bit integer        
 puts [signed 0xFFFFFFFFFFFFFFFF]   -1                    64-bit integer        

Code Example:

    option expression off                                                       
    for {set i 0} {< $i 64} {incr i} {                                          
        puts "1 << $i == [unsigned [<< 1 $i]]"                                  
    }                                                                           

See also: data type ranges, fpart, integral, asc, calldll, chr, error

catch

    # With option expression off                                                
    errorcode [catch {sourcecode} resultVariable ?-quiet?]                      
                                                                                
    # With option expression on                                                 
    errorcode [catch [sourcecode] resultVariable ?-quiet?]                      

[catch] intercepts error returns from the source code expression which is
passed to it and prevents the script from halting, instead returning a
boolean value. If there is an error condition then 'variable' is set to the
error message. [catch] returns a success code and the intercepted error
code as a return value. 

The '-quiet' argument will suppress local error messages and the caller
must rely on using other methods such as $errorMsg to interpret failures

The [catch] command operates at the level of Tcl error return values and
does not provide any additional O/S CPU exception handling.
[catch] returns 0 on SUCCESS and a non-zero value on error
The non-zero value is a Tcl error code. See: error code

The [catch] expression must be wrapped in braces and, if a command, the
command must not be wrapped in square brackets or catch will evaluate the
command return rather than the command itself and may fail. This behaviour
is dependent on the [option expression] setting

If the expression is successful then 'resultVariable' is set to the return
value of the expression. It is important that the catch expression is
wrapped in curved braces not square brackets as square bracketed
expressions will be evaluated before the catch test

Don't wrap an assignment within a [catch] statement unless you want to
test the assignment itself. For example...

    # Where 'nosuchvar' does not exist, s is not set                            
    set s Hello                                                                 
    catch {set s [string left $nosuchvar 1]}                                    
    assert $s {ne $_ Hello} -v   # This will fail to clear s                    
    # Use instead...                                                            
    option expression off                                                       
    catch {string left $nosuchvar 1} s                                          

Effective use of [catch] requires 'option echo off' as [catch] will handle
the display of error messages rather than direct console output

Within flow-control statements with: [option expression on] catch must be 
wrapped in square brackets to be evaluated otherwise it will be treated as
an expression. Catch is most useful when combined with [if] (see below)

See: error values for a list of returned error codes

Example:

    option expression off                                                       
    if {[catch {set h1 [xbase open user.dbf]}]} {                               
       die "Failed to open database address.dbf"                                
    }                                                                           

Example:

    option echo off                                                             
    set file "nosuchfile.txt"                                                   
    # Note [catch] is wrapped in square brackets for [if]                       
    if { [catch { open $file r } err] } {  # Any value > 0                      
       puts stderr "Could not open $file for reading\n$err"                     
       exit 1                                                                   
    }                                                                           

Result: 

    Could not open nosuch.txt for reading                                       
    Exception raised from catch. Error code 1                                   

Example:

    option echo off                                                             
    stack create s 1000                                                         
    stack unset s                                                               
    catch {puts "Stack empty? [bool [stack is_empty s]]" } err                  
    puts stderr "Stack error\n$err"                                             
    exit 1                                                                      

Result:

    Stack error                                                                 
    stack is_empty: Stack 's' not initialised                                   

Example:

    set line #__LINE__                                                          
    if {[! [catch                                                               
       {puts 'pwd: [pwd]'}   # Must be wrapped in braces                        
    ]]} {                                                                       
       die "Failed to trap bad puts command at line: $line"                     
    }                                                                           

Result:

    # No error occurs                                                           

Global variables are also set by internal error-handlers as follows:

    errorCode   Tcl error code returned to the CLI
    errorLine   May be set by the script using #__LINE__
    errorMsg    Console message generated by an internal error-handler

See also: try, cmdcount, trace, error, run, time, debugging, flow control
          stop, exit, errorCode, errorLine, errorMsg, errorInfo,
          error values

chain

    string [chain varname ?defaultvalue? ?-depth N?]

[chain] will follow a chained set of standard variable references until
the terminating case is met. It performs the equivalent of multiple $$
or [set] dereferences and follows a chain until the terminal case is found

[chain] handles any $-resolvable variable including arrays and struct
elements

The command is useful if the dereference depth is unknown, thus precluding
the use of a fixed number of $ or [set] commands

The command accepts an optional default value. If specified, this will be
returned if the chain of references fails.

The optional '-depth N' argument can be used to restrict the search depth
A value of -depth 0 will perform a full search, 1 will search to the 1st
level below, 2 the 2nd and so on...

[chain] won't know if a terminal case string is a reference to a broken
variable name or a string literal

Example:

    set a Hello                      # Terminal node                            
    set b(23) a                      # b(23) points to a                        
    set c b(23)                      # c points to b(23)                        
    puts [chain c]                   # Chain through: c->b(23)->a->Hello        
    unset a                          # Break the chained reference              
    puts [chain c]                                                              
    puts [chain foobar {Not found}]  # No such reference, 'foobar'              
    catch {                                                                     
        puts [chain foobar]          # Will raise a catchable error             
    }                                                                           

Results:

    Hello                            # The chain is fully-dereferenced          
    a                                # Chain search halts at 'a'                
    Not found                        # A default value is returned              
    <Error is raised>                # A trappable error                        

See also: double dereference, dereference, item, try, catch

ceil

    integer [ceil double-value]

Returns a double, rounded up to the nearest integer value

Example:

    puts [ceil 7.6]                                                             
    puts [ceil 3.2]                                                             

Results:

    8                                                                           
    4                                                                           

See also: floor, math, integer, double

clear

    integer [clear ?-list?]

Clear (unset) all user-level variables and consts

System-level consts such as $env are unaffected. 

Although consts cannot be changed during the life of a script, the entire 
variable table may be cleared before running a new script. This prevents
conflicts where scripts are loaded from the CLI.

By default, [clear] returns the count of cleared variables
Note that certain variables such as error* are recreated each
time the Command Line Interface reloads

Adding the '-list' argument will return a list of cleared variables
instead of a count of variables cleared

Ticol also maintains a small number of reserved system variables which
are protected from deletion by [clear] or [run]

The [run] command will also clear the user-level variable table

The loaded script may be cleared using the command [load -clear]

An alternative is to use: 

    unset * -glob -nocomplain  # -const is required to delete consts            

Example:

    puts [clear -list]                                                          

Result:

    errorMsg errorLine errorInfo errorCode                                      

See also: clearprocs, reserved variables, load, unset, set, vars

charcount

    integer [charcount string mask-chars ?-nocase?]

Count the number of instances of each character in the mask in the
input string. 

Case may be ignored using the -nocase argument

Where $qbf is a standard test variable:
    "The quick brown fox jumps over the lazy dog" ...

Each successive mask character is counted only once. i.e. the string is
iterated against the mask and each string char is matched only once

Thus [charcount $qbf Tt] and [charcount $qbf TTtt] both return 2
This applies also to -nocase where both [charcount $qbf Tt -nocase] 
and [charcount $qbf t -nocase] return 2

[charcount] is useful for checking the number of backslash escapes and
estimating a shrinkage factor when a string is unescaped

Escaped backslash characters - "\\" are counted as one character,
otherwise each backslash is counted as one character and any non-backslash
mask character which follows is counted as one character

    Example                                            Result

    puts [charcount $qbf a]                            # 1                      
    puts [charcount $qbf r]                            # 2                      
    puts [charcount $qbf t]                            # 1                      
    puts [charcount $qbf tT]                           # 2                      
    puts [charcount $qbf t -nocase]                    # 2                      
    puts [charcount $qbf tT -nocase]                   # 2                      
    puts [charcount $qbf aeiou]                        # 11                     
    puts [charcount $qbf fox]                          # 6 x o and f            
    puts [charcount "\[puts \"hello\"\]" "\\"]         # 4 x '\\'               
    puts [charcount "c:\\windows\\system32\\" "\\"]    # 3 x '\\'               
    puts [charcount "c:\\windows\\system32\\" "ws\\"]  # 8 x w, s and \\        

See also: chartok, char, string

chartok

    integer [chartok string charmask array ?-nocase?]

[chartok] 'tokenises' (breaks apart) a string as delimited by characters
specified in the 'charmask' argument and returns the individual components
in a base 0 integer-indexed array. A count of the number of tokenised items
in the array is returned

The mask argument may comprise of one or more characters by which the
string will be broken-up

The mask characters are not included in the output. Typically a string will
be split by spaces, tabs or CRLF pairs

If the 'array' argument exists it will be overwritten without warning

If the -nocase option is used then case will be ignored when comparing the 
'charmask' argument.

Example:

    option expression off                                                       
    set count [chartok $qbf " " a]                                              
    for {set i 0} {< $i $count} {++ i} {puts "$i $a($i)"}                       

Result:

    0 The                                                                       
    1 quick                                                                     
    2 brown                                                                     
    3 fox                                                                       
    4 jumps                                                                     
    5 over                                                                      
    6 the                                                                       
    7 lazy                                                                      
    8 dog                                                                       

Example:

    set s "Live-long and+prosper"                                               
    set count [chartok $s "+- " a]                                              
    for {set i 0} {< $i $count} {++ i} {puts $a($i)}                            

Result:

    Live                                                                        
    long                                                                        
    and                                                                         
    prosper                                                                     

See also: array, arrays, charcount, split, strto, strtor

clearprocs

    integer [clearprocs ?filter? ?-nocomplain?]

Clear all user-defined procedures from the current workspace
If a 'glob' name filter is not given then * is assumed (all procs)

User procedures are all defined at scope level 0 (root/global)

[clearprocs] returns a count of the number of procs which have been
successfully erased. This may be used to cross-check with a list produced
by [procs]

[clearprocs] will terminate any [after] script which is proc dependent

Example:

    option echo on                                                              
    procs                                                                       
    puts [clearprocs]                                                           

Result:

    cube rainbow square testmean                                                
    4                                                                           

See also: clear, glob

Command Line Arguments

  Syntax: ticol.exe <scriptname>?.tcl? ?/argument...?
  Syntax: ticol.exe ; "tcl script commands"
 
  The following command line switch options are accepted:

    /ECHO              Echo preprocessed source code and exit                   
                       Invalid for encrypted files                              
    /EXPR:<expression> Evaluate a Tcl expression (calls [expr expression])      
                       Expression results can be piped back to scripts etc.     
    /CRLF              Issue a CRLF after /EXPR (default is OFF)                
    /LIB:libname?.dll? Load a Ticol plugin library DLL at startup               
    /C?:OPT? ?outfile? Protect (encrypt) a Ticol source file                    
                       where OPT is one of the following options:               
                                                                                
                          <No option> Automatic encryption/decryption           
                          USER        Lock to a Windows user name               
                          VERSION     Lock to a Ticol version                   
                          MAC         Lock to a network card address            
                          WINVER      Lock to a Windows version x.xx            
                          HOST        Lock to a Windows host name               
                          IP          Lock to a valid IP address                
                          PASSWORD    Require a user-specified password         
                                                                                
                       Decryption is automatic except for /C:password           
                       If <outfile> is omitted then infile.tcx is created       
    /64                Base64 encode TCX output produced with /C (above)        
    /PW:<password>     A password phrase relevant to the above /C cases         
                       This may include any field including host or IP address  
                       which will be used to encrypt the script                 
                       The MD5 value is displayed on successful encryption      
    /I                 Info. Show encryption information for a TCX file         
    /LOG               Enable startup logging to ticol.log                      
    /MD5 filename      Display the MD5 value of a file (or: /MD5:filename)      
    /NA                No autoexec.tcl Don't load/execute autoexec.tcl          
    /NB                No console I/O buffering                                 
    /NE                Disable the exit command, disable CTRL+BREAK             
    /NP                No Macro PreProcessor                                    
    /NOESC             No MPP processing of escape sequences                    
    /NEO               No Expression Optimisation of [calc] during preproc      
    /NES               No Expression Simplification of [calc] during preproc    
    /NX                No CLI autoexecute unknown commands (autoexec off)       
    /NMX               No MPP #exec allowed (see also INI->PreprocessorExec)    
    /SC                Enforce Tcl standard comments                            
    /G                 Show performance analysis graph at end of run            
    /PAUSE             Pause after execution (keep console window open)         
    /BP                Enable breakpoints and single-step debugging             
                       This also enables the [halt] debugging command and       
                       stack trace                                              
    /ST                Enable Stack Trace only. Will be displayed only if       
                       an exception occurs. Not valid for plugin DLL libs       
    /DEBUG             Enter immediate single-step debugging mode               
                       Enables breakpoints (/BP), stacktraces and issues a      
                       [halt] instruction to start debugging immediately on     
                       running a script                                         
    /Q                 Quiet mode. Don't show load/run/PreProcessor errors      
    /VER               Echo the version                                         

Use: ticol.exe /? to get details of command line arguments for the current
version of Ticol

Note that under some versions of Windows, the maximum command line length 
is 8191 characters (8Kb-1)

Expressions are accepted if the first argument is specified as a semi-
colon. Where this format is used, the entire expression must be wrapped
at the command-line level, in double-quotes otherwise the following error
may be encountered:

    Error: 1 too many [ brackets found

Braces may be used within Windows CMD line arguments to delineate Tcl 
strings

Examples:

    ticol.exe ; "puts [{hello world}]"                                          
    ticol.exe ; "puts [cd {\\\\server\\share\\path}]"                           

See also: argc, argv, command line interpreter, CLI

Command Line Interpreter (CLI)

    void [cli]
 
This is an interactive shell within which Tcl commands may be directly
executed. From here you can also run programs or interact with the O/S
shell. The command [cli] launches another instance in the same thread
and is launched automatically on running ticol.exe without a script
name. The [exit] command will exit any [cli] instance.

The CLI is a single-line editor with a maximum line length of 4096
characters. Multi-line freeform editing is not supported but multiple
commands may be separated by a semi-colon.

Command history is retrieved using the up/down cursor keys control the
command history both at the CLI and within most text input routines

Macro-preprocessing such as #ifdef...#endif won't apply to 
commands entered directly within the CLI. The [at_exit] command also not
enabled from the CLI

Scripts may be run using:  run script?.tcl? or evaluated using: eval script

The CLI returns the status error code in square brackets, followed by any
applicable return value if 'option echo' is set to ON

    [x:status] <return-value>

The primary Ticol status codes are:

    0     OK/Success                                                            
    1     Error condition. The [error] command may give more information        
    2     [return] statement encountered                                        
    3     [break] statement encountered                                         
    4     [continue] statement encountered                                      
    5     [exit] statement encountered                                          
    6     [stop] or abort condition (stop command, wrong arguments etc.)        
    7     [goto] command encountered                                            
    8     A command or proc is already defined                                  
    9     [gosub] statement encountered                                         

Commands results are not echoed by default. To enable command console echo
use the command:    option echo on

- or configure the ticol.ini file and add an entry:

    [Config]                                                                    
    ConsoleEcho=TRUE                                                            

Example:

    set a "Hello"                                                               
    set $a                                                                      

Result (at the CLI):

    [0:ok] Hello                                                                

Security
--------
For obvious security reasons, 3rd party users should not be given access to
the Ticol CLI e.g. via CGI, remote command scripting or other mechanisms.
Direct CLI scripting (direct argument interpretation) is blocked in CGI
mode; e.g. /EXPR:<string> and the [cli] command which provides a command-
line interpreter...

    http://127.0.0.1:8800/cgi-bin/ticol.exe?/expr%2022/7.0                      

The [cli] command should be undefined in all secure scenarios using the
ticol.ini -> CommandUndefList="command..."

See also: security considerations, ticol.ini, command line arguments, 
          command line editing, command line history, exec, autoexec, 
          debugging, macro

Command Line History

The Ticol CLI stores input history. You can retrieve this using the up
and down cursor arrow keys.

See also: command line editing, command line editing

clipboard
 
    clipboard get
    clipboard set string ?-append?
    clipboard clear
    clipboard isempty
    clipboard printscreen
    clipboard gettext

Copy text to or from the clipboard. Optionally, clear the clipboard or check
to see if it is empty. clipboard printscreen will capture the current window
to the clipboard as an image

[clipboard get] will retrieve ANSI text from a selected area in the console
window. You can append to the clipboard using the -append argument

[clipboard set] will place ANSI text into the clipboard

[clipboard clear] empty the clipboard. 

It is recommended that you test for a non-empty clipboard and clear it prior
to copying text to it. Note that the clipboard supports multiple data formats

[clipboard printscreen] will grab the current visible console window as a 
graphic image equivalent to a PrtScrn operation

[clipboard gettext] will retrieve 80 x 25 characters of ANSI text from
cursor location 1,1 onwards in the current console. If the screen is scrolled
after a [cls] command then only the first 2,000 ANSI characters will be
returned. Select-zones are not supported by this command

Other than clipboard printscreen, only text mode operations are supported

See also: Single line commands, Ticol

Command Line Prompt String

    void [prompt {braced-argument-list} ?-reset?]

Configures the Ticol CLI prompt. The option '-reset' may be used to reset
to the internal default of "$u@$h$s$k$g". The argument list must be
enclosed in braces. Any tokens which are not recognised as prompt symbols
will be treated as string literals.

Example:

    prompt {Hello $u it is $d at $t $g}                                         
    prompt -reset                                                               

Result:

    Hello Admin it is 19-11-2021 at 12:49:38 >                                  
    Admin@Snoopy>                                                               

The command line prompt can also be configured via the TICOL.INI file using
a subset of the DOS/Windows PROMPT characters as follows

    [Config]                                                                    
    Prompt=<string>                                                             

Where the default internal value is Prompt=$u@$h$g
Which will return:   user@host>

Example:

    [Config]                                                                    
    Prompt=$u@$h$s$k$g   ; Show user@hostname,space,command-count               

Result:

    Admin@Snoopy 132>                                                           

Where: the string contains literal text and any of the following tokens:

    $c          An opening curved parenthesis (                                 
    $g          A greater-than symbol >                                         
    $h          The current hostname (if any)                                   
    $f          A closing curved parenthesis )                                  
    $k          Command count (See: info cmdcount)                              
    $l          A less-than symbol <                                            
    $p          The current path                                                
    $n          The current drive letter only, e.g. C                           
    $s          A single space character                                        
    $u          The current username (if any)                                   
    $_          A newline                                                       

See also: cli, ini file, info cmdcount

Colour Constants

The following colour constants are defined. You may also use numeric values:

    0   0x00    black                                                           
    1   0x01    darkblue                                                        
    2   0x02    darkgreen                                                       
    3   0x03    darkcyan                                                        
    4   0x04    darkred                                                         
    5   0x05    darkmagenta                                                     
    6   0x06    darkyellow (orange)                                             
    7   0x07    grey (gray)                                                     
    8   0x08    darkgrey (darkgray)                                             
    9   0x09    blue                                                            
    10  0x0a    green                                                           
    11  0x0b    cyan                                                            
    12  0x0c    red                                                             
    13  0x0d    magenta                                                         
    14  0x0e    yellow                                                          
    15  0x0f    white                                                           

Both 'grey' (UK) and 'gray' (US) spellings are accepted

See the example file: colors.tcl

Most modern versions of Windows will display "darkyellow" as a brown/khaki
colour where orange would be more useful. You can change darkyellow to 
orange using the following REG file. Save as "console-orange.reg" and
merge into the current session then log out and back in again to enable

More recent versions of Windows will reset this colour at each boot

    ---------------------------------------------------------------------       
    Windows Registry Editor Version 5.00                                        
                                                                                
    ; Colour table with DarkYellow == Orange 0x00c0c0c0 (BGR)                   
                                                                                
    [HKEY_CURRENT_USER\Console]                                                 
    "ColorTable06"=dword:00508dfc                                               
    ---------------------------------------------------------------------       

See also: textcolor, textbackground

Conditional Operator - ?:

Command syntax:

    bool [? {test-expression} {true-expression} ?:? {false-expression}]

or: (using expr syntax):
 
    bool ( test-expression ? true-expression : false-expression )

Ternary (3 argument) or 'functional' if

The first operand is evaluated and all side effects such as expression calls
are completed before continuing to evaluate

If the first operand evaluates to true (a nonzero value), the second operand 
is evaluated. If the first operand evaluates to false (0), the third operand 
is evaluated. The result of the conditional operator is the result of 
whichever operand is evaluated the second or the third.

Whichever of the operand expressions applies will be evaluated using 'expr'
and this may include other Tcl commands. To return literal strings use 
'return string'

Only one of the last two operands is evaluated in a conditional expression

Note: Nested functional if commands should be avoided as these can lead
to difficult-to-debug problems, bracket parity issues and possible
stack overflow conditions in recursive code. Where a nested functional if
must be used it is strongly recommended that each level is bracketed

Arguments for the command version should be properly braced, particularly
if the expressions contain commands wrapped in square brackets

The functional if may be faster than an if command for simple expressions

To return a string or other literal from the command form [?], use the 
[return] command. This is not necessary in the expression form

The expression form may be deprecated in future versions of Ticol as the 
command form is easy to include within expressions

Example:

    puts [expr "rnd(1,2) ==1 ? One : Two"]                                      

Example:

    set i 0; while {[< $i 5]} {puts [? [rnd 0 1] {return true}\                 
     {return false}]; incr i}                                                   

Result:

    false                                                                       
    true                                                                        
    true                                                                        
    false                                                                       
    true                                                                        

Example:

    # Command form [?] requires []                                              
    puts "[? [rnd 0 1] {return true} {return false}]"                           

Result:

    (true or false)                                                             

Command Version of [?]
----------------------
Infix functional [expr ?] if expressions may be rewritten using the
command version [? arg true-expr | false-expr]

    proc factorial {n} {? $n<2 1 : {? $n>=20 0 : {expr $n * \                   
       [factorial [-- n]]}}}                                                    

[?] Sub Expressions and Nesting Caveats
---------------------------------------
Infix [expr ?] sub expression should call [expr] separately to evaluate
sub expression. Calls to [expr] must have braced arguments for [expr]

    # Contains 3 levels of call to [expr], each with braced arguments           
    # restrict to factorial 20 to avoid 64-bit integer overflow                 
    proc factorial n {expr {$n<2 ? 1: expr {$n>20 ? $n*($n-1) :\                
       expr {$n * [factorial [-- n]]}}}}                                        

See also: flow control, if, else, while, for, foreach

Conversion Routines - mkn, cvn

    string  [mkn <numeric-value>]  # Make a compressed number
    integer [cvn <mkn-string>]     # Reconvert a compressed number

These commands are similar to Visual BASIC's Mki/Cvi, Mkf, Cvf functions
[mkn] converts a decimal numeric string of any length into a compressed 
4-bit encoding format which is universal to all decimal-based number types

Large amounts of numeric data may be compressed or numeric data may be
efficiently compressed for storage in say a database field

This format does not embed null characters within the string and encodes a
precise string representation rather than a binary translation. It encodes
to precisely half the original string length

The size of the numeric string is limited only by available memory

The encoded character ranges is: "0-9 + - . <space>"

Encoding pattern is as follows:

 ------------------------------------------------------------------             
 ASCII Value Input                                                              
             Encoding Symbol                                                    
                         Encodes                                                
                                Comment                                         
 ------------------------------------------------------------------             
 48..57      0x1..0xa    '0..9' Decimal digit                                   
 43          0xb 11      '+'    Positive sign                                   
 44          0xc 12      ','    Comma (thousands separator)                     
 45          0xd 13      '-'    Negative sign                                   
 46          0xe 14      '.'    Decimal point                                   
 32          0xf 15      ' '    Space                                           
  0          0x0         '\0'   Reserved for C/C++ NULL terminator              
 ------------------------------------------------------------------             

Examples:

    puts [mkn 616]                                                              
    puts [mkn $pi]                                                              
    puts [mkn +3.14159]                                                         
    puts [cvn [mkn 3452345]]                                                    
    puts [cvn [mkn +3.14159]]                                                   

Results (may include extended characters not reproducible here):

    rp                                                                          
    N%&úvFÜèC@                                                                  
    ¦ÔRj                                                                        
    3452345                                                                     
    +3.14159                                                                    

Using [mkn], [cvn] to store float/double values in struct fields
----------------------------------------------------------------
Since [struct setb] stores integers only you must either store 
floats in string format or, if space is at a premium, you can encode
them using [mkn] to create a compressed floating point number 
representation which can be stored and then retrieved using [cvn]

Example:

    struct s {a 16 b 16}                                                        
    struct setb s.a 1234.5678]        # Will truncate and store integer         
    struct set s.b [mkn 1234.5678]    # Will store full float value             
    puts "s.a is: '[ofaddressb s.a]'"                                           
    puts "s.b is: '$s.b' -> '[cvn $s.b]'"                                       

Results:

    1234                                                                        
    1234.5678                                                                   

Struct dump:

 struct 's' level 0 (0x1f389f0) data @32737744 (0x1f389d0)                      
 |      32 byte(s), 2 member(s)                                                 
 + 1:'->s.a'    => 32737744 (0x1f389d0) 16 byte(s)                              
 + 2:'->s.b'    => 32737760 (0x1f389e0) 16 byte(s)                              
                                                                                
Address   0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F ASCII                 
-------- -----------------------  ----------------------- ----------------      
01f389d0 D2 04 00 00 00 00 00 00  00 00 00 00 00 00 00 00 ................      
01f389e0 23 45 E6 78 90 00 00 00  00 00 00 00 00 00 00 00 #E.x............      

See also: base64, fromhex, tohex, inttohex, md5, crc, encrypt, decrypt

baseconv, convbase

    integer [baseconv number base ?-ucase?]
    integer [convbase number base]

[baseconv] can convert from base 10 to any base (2..16), [convbase] can
convert positive integers from an arbitrary base  (2..16) to base 10. 

No type prefix or zero padding is added to the result. If you wish to use
the resulting value via casting or other commands you may need to prefix
with a type identifier such as "0x", "0b" or "0o".

[baseconv] output may include alpha characters depending on the selected
base. By default these will be lower-case unless the '-ucase' argument is
passed

CAUTION: Do not prefix convbase inputs with a number-type prefix such as 0x
or the Macro PreProcessor will pre-convert to decimal before it is passed to
the command

Example:

    convbase 499602D2 16                                                        
    convbase 1010 2                                                             

Result

    1234567890  (Base 10)                                                       
    10          (Base 10)                                                       

Example:

    baseconv 10 2                                                               

Result

    1010                                                                        

The resulting numeric values are not prefixed by a type flag e.g. 0x, 0o or 0b

See also: inttobin, inttohex, inttooct

beep

    beep ?frequency? ?duration?
 
Play a note. The default frequency is 700Hz and the default duration is
200ms

Example:

    option expression off                                                       
    for {set i 300} {< $i 900} {incr i 100} {beep $i 400}                       

Examples:

    beep 600                                                                    
    beep - 2000                                                                 

Note that best performance is on systems which support the 8254 timer chip. 
This is emulated on Windows 7 and later via the sound card, and not at all
on some versions of Windows. Performance may be very with such emulated
sound output. 

Support for the Beep API was dropped in Windows Vista and Windows XP 64-Bit

See: 

https://msdn.microsoft.com/en-gb/library/windows/desktop/ms679277%28v=vs.85%29.aspx

See also: playwav 

Using Ticol With Batch Files
 
It is possible to emulate the Linux 'shebang' system and create a batch-
script which can automatically execute Ticol Tcl code within the same file

The following batch script can be used as a stub-wrapper for Tcl code

  ::set - {                                                                     
  @echo off                                                                     
  REM ##################################################################        
  REM Name:       magic.bat                                                     
  REM Purpose:    Command-line chaining test for Ticol Tcl                      
  REM Notes:      Run as: magic.bat                                             
  REM             The initial enclosing [set] command is ignored by Tcl         
  REM ##################################################################        
  REM Check filetype using BATCH script                                         
  if "%~x0" equ "" (                                                            
     set SCRIPT=%0.bat                                                          
  )                                                                             
  if "%~x0" neq "" (                                                            
     set SCRIPT=%0%~x0                                                          
  )                                                                             
  ticol.exe %SCRIPT% %1 %2 %3 %4 %5 %6 %7 %8 %9 /na                             
  exit /b                                                                       
  }                                                                             
  # start Tcl Code ####################################################         
  textcolor magenta                                                             
  puts "Hello from Ticol Tcl"                                                   
  textcolor                                                                     
  newline                                                                       

See also:   tcl2bat, Ticol, md5 batch example, autoexec.tcl, autoexec
            exec, spawn, shell

in, ni

    bool [in list value ?-glob|-nocase?]
    bool [ni list value ?-glob|-nocase?]

Return a boolean indicating whether an element is in or not in a well-
formed single-level Tcl list. Multi-level lists are unsupported.

[in] and [ni] are list rather than string matching commands

[lsearch] is more suited to complex list-searching

A 'glob' style wildcard or case-insensitive search may be used.
These options cannot be combined and -glob is case-significant

    Example                       Result
                                                                                
    # qbf: The quick brown fox jumps over the lazy dog                          
    puts [in $qbf fox]            # 1                                           
    puts [in $qbf FOX -nocase]    # 1                                           
    puts [in $qbf f?x -glob]      # 1                                           
    puts [in $qbf f*x -glob]      # 1                                           
    puts [in $qbf fred]           # 0                                           
    puts [ni $qbf fox]            # 0                                           
    puts [ni $qbf fred]           # 1                                           

See also: list, lsearch, lindex, llength, instr, string substr

Ticol is a Tcl interpreter, not a compiler!

Ticol is a Tcl interpreter. This means that Tcl scripts are evaluated 
on-the-fly and are not translated into machine instructions for the CPU

Interpreters are necessarily slower in terms of execution than any compiled 
language. Some other languages compile to an intermediate form (p-code) 
which is designed to run on a virtual machine (virtual CPU) and these can 
perform very fast and in some cases out-perform native compiled object code.
However these languages are not interpreters in the truest sense. This means 
Ticol will be relatively slow and should not be used for tasks where 
performance is required

For this reason it is vital to optimise Tcl scripts. One of the ways this is
achieved is to use a Macro PreProcessor (MPP) to pre-process scripts and 
perform any necessary static translations, e.g. on non-decimal const numbers
such as 0x123 and convert these to decimal values. C-like escape sequences 
such as \r or \n are also converted to literal characters.

Ticol includes a number of non-standard Tcl commands which are designed to
facilitate optimal performance. See: performance

The command [calc] is an optimisable alternative to [expr]. This command
will be optimised into simpler Tcl commands by the Macro PreProcessor unless
disabled. If disabled it will be interpreted identically to [expr]

See: Macro PreProcessor for more information

See:
    https://en.wikipedia.org/wiki/Tcl

See also: calc, Tcl, Ticol 

interp

    string [interp ?script?]

Launches a new instance of the Ticol interpreter with a separate CLI
and preserving the original interpreter. At present only a rudimentary
implementation of [interp] is offered.

Currently this runs in the same thread as the original intepreter and
the original interpreter is 'suspended' during execution. The child
interpreter will inherit the configuration of the parent. In a future
version of Ticol, [interp] will run in a separate thread.

To exit this interpreter instance and return to the original interpreter
instance, use the [exit] command. If [exit] or [return] is not present
in the script body then the child CLI will be entered

Example:

    set a 23                                                                    
    assert $a {== $_ 23} -v -line #__LINE__                                     
    interp {                                                                    
       puts "Enter child interpreter @"#__LINE__                                
       set a 24                                                                 
       assert $a {== $_ 24} -v -line #__LINE__                                  
       exit                                                                     
    }                                                                           
    assert $a {== $_ 23} -v -line #__LINE__                                     

[interp] may be used to return a result as with [proc]

Example:

    set r [interp {                                                             
       return [* 4 [funct atan 1]]                                              
    }]                                                                          
    puts "interp result was $r"                                                 

Result:

    interp result was 3.141592653589792                                         

See also: cli, spawn, exec, commands, interpreter

binary (Experimental binary command)

    bool [binary set varname escapedString ?-length x?]                         
    string [binary set varname ?-length?]                                       
    string [binary tohex ?-var? varname]                                        
    bool [binary fromhex string varname]                                        
    string [binary base64 string|varname -var ?-encode? | ?-decode var?]        
    integer [binary length varname]                                             
    binary memcpy tcl-var source-address length                                 
    bool [binary update varname]                                                

Ticol uses uncounted ASCIIZ string which limits flexibility for use with
binary strings which contain embedded NULL characters

Ticol supports a few rudimentary binary operations. Note that where 
'varname' is specified as an argument this should NOT be dereferenced as 
dereferenced strings are not handled as binary and will be truncated at 
the first NULL character.

Binary strings cannot be returned from commands directly, so binary
commands which return binary strings cannot be chained together (nested)

If an argument variable does not exist then it will be created. If it
exists and has content it will be overwitten. Useful for handling binary
registry values. [binary] is not a full implementation of the Tcl [binary]
command

[binary] commands can not be used on non-binary variables

    binary set varname escapedString ?-length x?                                

       Set a standard Tcl variable with binary content

    binary tohex string|varname -var                                            

       Return a hex interpretation of a binary variable or string
       If a variable is passed, use the -var argument

    binary fromhex string varname                                               

       Convert a hex string to a Ticol binary variable
       The contents of the variable will be overwritten
       If the variable does not exist it will be created

    binary base64 string|varname -?var? ?-encode? | ?-decode varname?           

       Returns the contents of a binary variable or string, base64-
       encoded or decoded. -encode is the default mode for this command
       If a variable is passed, use the -var argument

       If -decode is specified then the argument must be
       followed by the name of a new output variable. The output
       from -decode will/may be a binary string with embedded NULLs

       Where a variable name is specified the return value is a boolean
       Otherwise (for -encode with no varname), the return value is 
       the encoded ASCIIZ string in base64 format

    binary length varname                                                       

       Return an integer value representing the stored length
       The variable must be a binary one which has been set using
       the [binary] command. Standard variables will return a binary
       length of 0

    binary memcpy varname sourceAddress length                                  

       Copies data from memory address to a Tcl variable
       The Tcl variable will have memory allocated to hold the source
       The variable is marked as 'binary' and will have a length value
       The resulting binary string is "safe" and is null-terminated
       It may safely be printed using [puts]

    binary update varname                                                       

       Updates a Ticol variable and forces one with an existing value
       to be recognised as a binary type

Notes:

The Ticol Macro PreProcessor (MPP) will not allow \x00 or \x22 entries
as these would cause strings to malfunction, but these are internally
handled by [binary]. Remaining values are unescaped by the MPP before
the script is loaded

Example:

    binary set a \xfe\xffhello\x21\x00\x22world\x02\xff\xfe -length 18          
    puts "'$a!'"                                                                
    puts "normal: length=[string length $a]"                                    
    puts "binary: length=[binary length a]"                                     
    puts "tohex:  '[binary tohex a]'"                                           
    puts "base64: '[binary base64 a]'"                                          

Results:

    '. Hello'                                                                   
    normal: length=8                                                            
    binary: length=18                                                           
    tohex:  'feff68656c6c6f210022776f726c6402fffe'                              
    base64: '/v9oZWxsbyEAIndvcmxkAv/+'                                          

Example:

    set a 23                                                                    
    binary memcpy b [addressofb a] 2                                            
    puts "\$b=='$b'"                                                            
    puts "binary length=[binary length b]"                                      
    puts "string length=[string length b]"                                      

Result:

    $b=='23'                                                                    
    binary length=2                                                             
    string length=2                                                             

See also: set, tohex, encrypt, base64, tohex, fromhex

bintoint, inttobin

    integer [bintoint binary-value]
    string  [inttobin value  ?-full? ?-noprefix?]

[bintoint] converts a binary value to a decimal

[inttobin] converts a decimal value to a binary string with the prefix
"0b". Conversion of binary to decimal integer is automatic in Ticol
Values are calculated as 64-bit integers

Values given to inttobin may be decimal integers, hexadecimal, octal or binary

The optional ?-full? arg includes leading left-hand zeroes for the full,
64-bit internal representation

[format] and [printf] may be used to force full width formatting

CAUTION
-------
The Macro PreProcessor (MPP) will automatically convert octal values in
the form 0bNN into decimal. Be cautious when passing unquoted binary
values with "0b" prefix to [bintoint]

Examples:

    option echo on                                                              
    inttobin 9223372036854775808 -full                                          
    inttobin 666 -full                                                          
    inttobin 666 -noprefix                                                      
    inttobin 666                                                                
    inttobin 0                                                                  
    printf "0b%08s\r\n" [inttobin 0 -noprefix]                                  

Results:

    0b1000000000000000000000000000000000000000000000000000000000000000          
    0b0000000000000000000000000000000000000000000000000000001010011010          
    0000000000000000000000000000000000000000000000000000001010011010            
    0b1010011010                                                                
    0b0                                                                         
    0b00000000                                                                  

See also: big number, inttohex, inttoct

any, all

    bool [any bool ?bool...?]
    bool [all bool ?bool...?]

The [any] and [all] commands offer efficient and compact methods of testing
multiple boolean or pseudo-boolean results without requiring either the
use of [expr] or deep command nesting necessary with multiple [||] commands
Both forms perform lazy evaluation. [expr] with natural expressions may be
used but this is less efficient than using the native Tcl command form.

    [any] will return $true (1) if any clause is true
    [all] will return $true (1) if and only if all clauses are true

To test say four different variables, a, b, c, d and would require the
following using Tcl format expressions:

Example:

    option expression off                                                       
    # We assume a b c d and e are already set                                   
    if { || $a [|| $b [|| $c [|| $d $e]]] } {puts "At least one variable is set"}
    if { && $a [&& $b [&& $c [&& $d $e]]] } {puts "All variables are set"}      

Can be replaced with the following, which is easier to read and less prone
to hard-to-trace bracket parity errors:

    option expression off                                                       
    # We assume a b c d and e are already set                                   
    if {any $a $b $c $d $e} {puts "At least one variable is set"}               
    if {all $a $b $c $d $e} {puts "All variables are set"}                      
    if {all [any $a $b $c] [any $d $e $f]} {puts "Pass"}                        
    if {any [all $a $b $c] [all $d $e $f]} {puts "a,b,c XOR d,e,f"}             

Example:

    puts [any 0 1 0]                                                            
    puts [any 0 0 0]                                                            
    puts [any 1 1 1]                                                            
    newline                                                                     
    puts [all 1 1 1]                                                            
    puts [all 1 0 1]                                                            
    puts [all 0 0 0]                                                            
    newline                                                                     
    set a [rnd 0 1]                                                             
    set b [rnd 0 1]                                                             
    if {all $a $b} {puts "True"} else {puts "False"}                            

Results:

    1                                                                           
    0                                                                           
    1                                                                           
                                                                                
    1                                                                           
    0                                                                           
    0                                                                           
                                                                                
    <Prints True or False depending on [rnd]>                                   

See also: bool, &&, ||, if, flow control

bool (boolean values)
 
    string [bool value]     # Command
    $true                   # Global constant ($::true in proc scope)
    $false                  # Global constant ($::false in proc scope)

[bool] returns the string "true" or "false" depending on the input numeric
value. A non-zero value returns true, a zero value, false
If the string "true" or "false" is passed to [bool] then 1 or 0 is
returned with the string being evaluated for a match

Constants are defined as $true and $false, but these reside in global 
scope and must be referred within procs using the scope prefix $::true and
$::false

Raw boolean words 'true' or 'false' may be defined by the Macro 
PreProcessor (MPP) as follows. These substitutions are unaffected by
scoping issues

    # Define macro symbols for 'true' and 'false'                               
    #define true 1                                                              
    #define false 0                                                             
    assert true {== $_ 1}                                                       
    assert false {== $_ 0}                                                      
                                                                                
    # String comparisons for macro values *must* be dquoted and not braced      
    # this prevents the MPP performing a substitution, braces do not            
    assert [bool true] {eq $_ "true"} -v -line #__LINE__                        
    # Otherwise ...                                                             
    assert [bool true] {eq $_ true} -v -line #__LINE__                          
    # Would translate to...                                                     
    assert [bool 1] {eq $_ 1} -v -line #__LINE__                                
    # In which case, [bool 1] would return the string "true"                    

Example input mappings for [bool]:

    Input value   Result                                                        
    --------------------                                                        
    1             True                                                          
    3             True                                                          
    -3            True                                                          
    0             False                                                         
    x             False                                                         
    True          1                                                             
    TRUE          1                                                             
    False         0                                                             
    FALSE         0                                                             

true and false are defined as constants 1 and 0 respectively

Examples:

    puts [bool 1]                                                               
    puts [bool true]                                                            
    puts [bool "true"]                                                          
    puts [bool {true}]                                                          
    puts [bool false]                                                           
    puts [bool [> [funct rnd 1 10] 5]]                                          

Results:

    true                                                                        
    1                                                                           
    1                                                                           
    1                                                                           
    0                                                                           
    The string "true" or "false" depending on [rnd] output                      

See also: math, string

sbool (Signed Boolean)

    signed-boolean-integer [sbool value]

Returns -1 (true) or 0 (false)

Converting BASIC code to Tcl can be problematic as expressions may include
boolean results which behave differently to Tcl. This can be because BASIC
usually uses the signed integer -1 (0xFFFF) as a 'true' value whereas Tcl
uses the positive integer, 1. 

[sbool] takes a value, not an expression. Use [expr] or [eval] to evaluate

If combined with bitwise operations translated from BASIC, unexpected
results may occur.

Example:

    # BASIC code example:  (7 AND -8 < -5)                                      
    # WRONG because [< -8 -5] returns 1 not -1, which breaks [&]                
    puts [& 7 [< -8 -5]]                                                        
    # CORRECT because [< -8 -5] will now be cast to -1 (0xFFFFFFFFFFFFFFFF)     
    puts [& 7 [sbool [< -8 -5]]]                                                

Result:

    1                # Wrong                                                    
    7                # Correct                                                  

See also: math, bool

box
 
Draw a graphic box

    box x y w h ?forecolour | -? ?backcolour? ?-single? ?-solid? ?-nofill?

Color may be a symbolic name such as 'blue' or a numeric value which
corresponds with standard Windows console values (e.g. blue, cyan and
these may optionally be prefixed with 'light', e.g. 'lightred')
Both 'grey' and 'gray' are accepted.

-single will draw a single-line border (the default is double line)
-solid will remove the border pattern and draw a solid box of 'backcolour'
-nofill will draw only the box outline in 'forecolour'

'backcolour' is the box 'fill' colour
'forecolour' may be ignored when specifying 'backcolour' by using a -

Displaying the box will change the cursor position. Use gotoxy to
reset. gotoxy may also be used to draw within the box area

Screen colours are reset back to defaults after a call to [box]

If width w or height h is zero then a vertical bar or horizontal line
will be drawn

Examples:

    box 3 4 30 10 - lightcyan -solid                                            
    box [rnd 1 20] [rnd 1 20] [rnd 5 30] [rnd 1 20] - lightcyan                 

See also: msgbox, cls, gotoxy

Braces {} (Grouping) and Brace Style
 
Braces group Tcl commands including string characters. Braces may be nested
Typically each call to a Tcl command unwraps one level of braces

In some cases, double-quotes may be used to group words together. Note that
single-quotes do not group, so ...

    puts 'Hello world'                                                          

    is not a valid group/string, whereas ... 

    puts "Hello world"                                                          

    or ...

    puts {Hello world}                                                          

 ... is valid, as braces may also delimit a string and can be useful where
there would otherwise be nested double-quotes.

Braces delay evaluation and may be used strategically to prevent 
evaluation at a specific level. Evaluation may be forced using [subst]
Each command-call removes one set of braces and evaluates the result

Because braces delay evaluation for one level they will prevent variables
from being replaced unless [subst] is called on the group. Thus later 
evaluations which are dependent on such variables will fail

Nested levels of braces, used outside of quoted strings may be used to
prevent interpretation within the current context, i.e. to delay
interpretation to a later evaluation in a sequence

Braced Variables
----------------
Braces may also be used to isolate concurrent variables within strings and
to prevent misinterpretation of variables adjacent to literal string
content e.g. in "$variablestring" as "${variable}string" we want to isolate
'variable' from 'string' and dereference $variable but not 'string'. 

This is easier in languages which have a dollar suffix but Tcl has a dollar
prefix which requires a more complex solution to strings 'butting up'
against each other. When used in this context, whitespace is significant
and may be interpreted as part of a variable name. Therefore care should
be taken to avoid including unnecessary whitespace in braced variable 
references.

Braces which fully-enclose variables name within strings do not delay
evaluation and may also be used to distinguish variable names from string
content which follows immediately-after

e.g. 

    set will_be_evaluated_ok "Hello "                                           
    puts ${will_be_evaluated_ok}string    # Normal dereference                  
    set q will_be_evaluated_ok                                                  
    puts ${$q}string                      # Double dereference                  
    puts $[$q }string                     # Will look for var 'q '              

Result:

    Hello string                                                                
    Hello string                                                                
    # Variable 'q ' not found                                                   

Braces may be used within lists to delineate groupings

Example:

    set a "Goodbye "                                                            
    set b "cruel "                                                              
    puts "$a${b}world"                                                          

Result:

    Goodbye cruel world                                                         

Brace Style
-----------
Tcl requires K&R style brace placement as the line end forms part of the
command syntax. Failure to adhere to this style will cause fatal errors

    proc foo x {           # Correct K&R Tcl style                              
       ...                                                                      
    }                                                                           
                                                                                
    proc foo x             # Incorrect Allman/BSD C/C++ style                   
    {                                                                           
       ...                                                                      
    }                                                                           

See:
    https://en.wikipedia.org/wiki/Indent_style
    http://wiki.tcl.tk/708

See also: Tcl commands, double dereference, dereference, flow control,
          subst, eval, math, Tcl

call
 
    call proc ?argument...?
    call command ?argument...?
    call "<tcl script>"

Allows the calling of a procedure, Ticol command or scriptlet

Similar to the function of CALL in legacy Visual BASIC. Call allows a
proc to be called directly bypassing expression-evaluation, [trace] 
and /G graph statistics.

A command can be called. In this case the normal evaluation chain is
followed including [trace] and /G graphing

Finally, a text script containing Ticol Tcl commands may be called

Note that Ticol treats numeric labels within square brackets as 
literal numbers e.g. [123]. This is by design. [call] may be used
to call procs with all numeric names.

Proc Example:

    proc 123 {} {puts "I am in bad proc 123"}                                   
    [123]               # Not called as [123] is evaluated as a number          
    call 123            # Proc [123] is called successfully                     

Result:

    I am in bad proc 123                                                        

Command Examples:

    option echo on                                                              
    call expr "sqrt(99)"                                                        

Result:   (Submitted via the CLI prompt)

    [0] 9.949874371066199                                                       

Script Example:

    call "textcolor magenta; puts {Hello } -nonewline ;\                        
     textcolor red; puts world; textcolor"                                      

Result:   (Submitted via the CLI prompt)

    Hello world                                                                 

See also: puts, eval, expr, foreach, sub, gosub, goto, dummy

External DLL Link Plugin - calldll*, calldll*_cdecl
 
    To load, use:    lib ticol_calldll ?-verbose? ?-nocomplain?

Syntax:

    calldll* dllname?.dll? function-name|@ordinal arg arg...
    calldll* handle function-name|@ordinal arg arg...

    See also: dll_open
    See also: dll_close

[calldll*], where * is one of the suffix types listed below, can safely
call an external ANSI DLL routine.

This command is still experimental and calling-conventions may change

Both __stdcall and __cdecl non-COM DLLs are supported thus offering
connectivity with a wide range of other applications including the 
Microsoft Visual C++ runtime (msvcrt40.dll), SQLite3, AutoIT and
other popular DLLs which use standard interface methods

Calling DLLs is not a trivial task an the marshalling of variables both
outward and return can be quite complex and difficult to debug. The correct
call-type (standard __stdcall or __Cdecl) must be used to match the DLL

The DLL may be called either by name or by a handle returned from dll_open
Additionally, an exported DLL function may be called by ordinal number by 
prefixing the ordinal with a @ symbol. e.g. 

    # Must be run from an elevated console prompt or session                    
    puts "Is user an admin?: "[bool [calldll "Shell32" "@680"]]                 

Handling Partial and Default DLL Arguments
------------------------------------------
All arguments must be supplied, even if optional for the DLL interface
Pass default NULL arguments as 0. Failure to supply sufficient arguments,
even if defaults exist, will result in a [calldll] exception

e.g. for C++ export prototype in foo.dll

    void __stdcall bar(LPSTR, long* = NULL);

Pass:

    calldll foo.dll bar "Hello" 0      # Equivalent to passing NULL             
    set r "000000000000000000000"      # Ensure buffer is sufficient            
    calldll foo.dll bar "Hello" r      # And pass 'r; by reference              
    struct r {s 21}                    # Create a small struct                  
    calldll foo.dll bar "Hello" r      # And pass by reference                  

Quoting
-------
Quotes around the dll and function name are optional, as is the DLL suffix
It is usually recommended that calldll calls be wrapped inside a procedure

Byte Widths
-----------
Byte widths may be specified in arguments as 'var:<width>' e.g. 'data:4'
If a variable or string contains a ":" character (e.g. in a Windows path), 
a byte-width MUST be specified. This feature is not ideal and will be 
improved in a future version. At present [calldll] is fairly primitive
and satisfied only the basic design requirements.

Passing by Value or by Reference
--------------------------------
Variables are passed by reference if a variable name is used and the
variable exists, if prefixed by a $ then they are passed by value.
If you pass a value which could be interpreted as a variable name then 
ensure you [unset] that name before calling [calldll] as detection is
automatic. 

Passing a Tcl variable by reference does not guarantee sufficient 
allocated memory space to hold any return. Either the called DLL must
allocate using malloc() or you should allocate a sufficient buffer 
by using [makestr] or assigning a suitably large value using [set]
Alternatively, you may pass the address of a single-member struct
which has sufficient static buffer space in the member field.

Literals, e.g. "Hello" or 23, are passed by value for example

Passing by Name or Reference
----------------------------
Where a variable is passed by name rather than value, the argument is
passed to the DLL by reference. String variables will be passed to the
DLL as char* which will allow the original string data block to be modified
This should be used with extreme caution and the variable must be created
with sufficient space allocated to handle any return. Use [makestr] for 
example, to do this.

    # 'funct()' below is an exported __stdcall function in 'dllname.dll'        
    set var 10                                                                  
    # Passed as char* but DLL should not modify the string contents             
    calldll dllname funct $var             # Pass a variable by value           
    # Passed as char* and DLL may modify carefully                              
    calldll dllname funct var              # Pass by Tcl name (reference)       
    # Passed as char* and DLL may modify carefully                              
    calldll dllname funct [addressofb var] # Pass by reference                  
    # Passed as char* and DLL may modify carefully                              
    calldll dllname funct [strptr var]     # Pass by reference                  

Note: The Microsoft Visual C++ runtime (msvcrt40.dll) is compiled as a
__cdecl DLL and calldll*_cdecl must be used to call it.

Passing Binary Addresses
------------------------
Passing the binary address of a variable buffer/string pointer by value may
be forced by using [addressofb variable] to pass the literal buffer address
(LPSTR in C++). [strptr] is an alias for [addressofb]. This is most useful
for struct field address offsets. In most cases [calldll*] will auto-
detect the correct type of cast to apply.

Passing C/C++ Pointer Arrays (String Arrays)
--------------------------------------------
Pointer array such as char** are not directly supported by can be emulated
using [struct] to store an array of 4-byte addresses. An example is given
elsewhere. See: calldll examples

Return types and calls
----------------------

    Return Type         Use                                                     
    ----------------------------------------------------------------------      
    long, short, void:  calldll dllname|handle funct ?var...?                   
                        You may also recast a long value to string pointer      
    <type*>             You may also cast any accessible type                   
    double, float:      calldlldbl dllname|handle funct ?var...?                
    string, bstr:       calldllstr dllname|handle funct ?var...?                
    variant             calldllvariant dllname|handle funct ?var...?            
                        Return variant must be released using [free]            
                                                                                
    long, short, void:  calldll_cdecl dllname|handle funct ?var...?             
    double, float:      calldlldbl_cdecl dllname|handle funct ?var...?          
    string, bstr:       calldllstr_cdecl dllname|handle funct ?var...?          
    variant             calldllvariant_cdecl dllname|handle funct ?var...?      
                        Return variant must be released using [free]            
                                                                                
    Return Error Codes                                                          
    -------------------                                                         
    0      No error                                                             
           DLL Name only (file exists)                                          
           DLL Name + Funct Name (file and funct exist)                         
           DLL Name + all parameters (success)                                  
    1      No DLL filename was given                                            
    2      Failed to get DLL handle (DLL not found/doesn't exist)               
    3      Can't get proc address to given function in existing DLL             
    4      Unhandled data type was passed via the VARIANT to CallDLL*           
    5      Invalid ordinal number was passed for given DLL                      
    6      An invalid handle was passed                                         

Examples
--------
Example: 

  calldll_cdecl msvcrt40.dll printf "Hi from printf %i %f\n" 123 4.567          

Result:

  Hi from printf 123 4.567000                                                   

Example:

  # Must be run from an elevated console prompt or session                      
  puts "Shell32 is user an admin: [bool [calldll shell32 @680]]"                

Result:

  true                                                                          

Example: 

  # Must be run from an elevated console prompt or session                      
  puts "AutoIt is_admin: [bool [calldll AutoItX3.dll AU3_is_admin]]"            

Result:  

  true                                                                          

Notes on Calling MSVC Print Functions From ticol.dll
----------------------------------------------------
[calldll] cannot call MSVC (msvcrt40.dll) console print functions
After 256 calls they will return -1 (EOF)
Console output requires Windows API functions and which are 
exported as lib functions WriteF() and WriteLn()

Notes on DLLs Returning Static Structs
--------------------------------------
If a DLL returns a pointer to a static struct or object which requires
persistent memory read, for example, msvcrt40.dll function asctime() 
will return a pointer to 'static struct tm', it is vital that
[dll_load] is used to load and keep the DLL in memory while that data
structure is referenced or copied. You may call the DLL using the handle
returned by [dll_open]

Example:

    lib ticol_calldll                                                           
    set handle [dll_open msvcrt40.dll]                                          
    set t 1470404087                                                            
    set tm_ptr [calldll_cdecl $handle localtime t]                              
    set s [calldllstr_cdecl $handle asctime tm_ptr]                             
    dll_close $handle                                                           
    puts $s                                                                     

Result:

    Fri Aug 05 14:34:47 2016                                                    

Data Structures
---------------
External API routines can often use structs. Ticol provides binary-
compatible structs which can interface with both Ticol and external DLLs
The return from a C++ struct* may be passed to a local Tcl struct using
[struct copy]

Variant returns, including 1D and 2D string arrays are handled

See: structs

See also: calldllstr, calldll examples, dll_open, dll_close, struct,
          addressof, addressofb, strptr, ofaddress, struct copy

calldll examples

Note: All examples require the statement:    lib ticol_calldll

--

set t 1470404087                                # Fri Aug 05 14:34:47 2016      
set ret [calldllstr_cdecl msvcrt40.dll ctime t] # Convert time_t to string      
puts "Result is ctime(FIXED-DATE): -"[string trim ret [chr 10]]-                

--

calldll user32 SetWindowTextA [calldll Kernel32 GetConsoleWindow] \             
"Ticol Tcl"                                                                     

--

puts "Is user an admin?: [calldll Shell32 @680]"                                

--

puts "is_admin: [bool [calldll AutoItX3.dll AU3_is_admin]]"                     

                                                                                
true                                                                            

--

calldll_cdecl msvcrt40.dll printf "Hi from printf %i %f\n" 123 4.567            

                                                                                
Hi from printf 123 4.567000                                                     

--

calldll mslib145 MapNetworkDrive "\\srv01\c[chr 36]" "q:" "" "" errcode 1 0     

--

puts "proc mem: "[comma [calldll "mslib145.dll" "GetProcessMemoryUsed" \        
 [pid]]]                                                                        

--

proc ipfromhost {hostname} {                                                    
    return [calldllstr ping32 GetIPFromHostName $hostname]                      
}                                                                               

--

puts "calldlldbl_cdecl->"[calldlldbl_cdecl msvcrt40.dll sqrt $pi]               

--

Example passing the address of a buffer by value to call a DLL:

set timeout 0                                                                   
set datalen 64                                                                  
set buf [makestr 16]                                                            
calldll ping32 PingAPI google.com [addressofb buf]:4 datalen:2 timeout:4\       
 1000                                                                           

                                                                                
Returns the string: 173.194.78.105 in variable 'buf'                            

--
Example querying Windows OS version

    struct OsVersionInfo {                                                      
       {OSVSize         4}         # long                                       
       {dwVerMajor      4}         # long                                       
       {dwVerMinor      4}         # long                                       
       {dwBuildNumber   4}         # long                                       
       {PlatformID      4}         # long                                       
       {szCSDVersion    128}       # char* x 128 bytes                          
    }                                                                           
                                                                                
    setb OSVSize [struct size OsVersionInfo] 4 -value                           
                                                                                
    puts "GetVersionEx=[calldll kernel32 GetVersionExA OsVersionInfo]"          
                                                                                
    puts "OSVSize:       [ofaddressb OSVSize 4]"                                
    puts "dwVerMajor:    [ofaddressb dwVerMajor 4]"                             
    puts "dwVerMinor:    [ofaddressb dwVerMinor 4]"                             
    puts "dwBuildNumber: [ofaddressb dwBuildNumber 4]"                          
    puts "PlatformID:    [ofaddressb PlatformID 4]"                             
    puts "szCSDVersion:  [ofaddress szCSDVersion]"                              
    dump OsVersionInfo                                                          

--

    # Requires the author's vbsqlite3.dll port                                  
    set QueryString "SELECT t1.ArtistName,CDs.Title,CDs.date, CDs.CDID\         
       FROM Artists t1, CDs\                                                    
       Where t1.ArtistID = CDs.ArtistID\                                        
       ORDER BY ArtistName, CDs.Date ASC "                                      
                                                                                
    set dbfile ".\\test.db"                                                     
    set h [dll_open "vbsqlite3.dll"]                                            
    set r [calldll $h sqlite3_open [strptr dbfile] db]                          
    set vr [calldllvariant $h sqlite_get_table_basic $db:4 $QueryString]        
    set rows [varray size $vr]                                                  
    set cols [varray size $vr 2]                                                
    set dims [calldll $h "GetArrayDimensions" $vr]                              
                                                                                
    puts "Table has $rows rows and $cols columns and $dims dimensions"          
                                                                                
    calldll $h sqlite3_close $db                                                
    dll_close h                                                                 

--
                                                                                
    # Call dll by handle                                                        
    # Requires the author's VB Toolbox DLL                                      
    option expression off                                                       
    set handle [dll_open mslib145.dll]                                          
    calldll $handle LoadIPHLPAPI                                                
    set adapters [calldll $handle GetAdapterCount]                              
    puts "Querying $adapters adapter(s)..."                                     
    option expression off                                                       
    for {set i 0} {< $i $adapters} {++ i} {                                     
       set addr [calldllstr $handle GetIPAddress $i]                            
       set name [calldllstr $handle GetAdapterDescription $i]                   
       puts  "$i: [left $name 40] -> $addr"                                     
    }                                                                           
    dll_close $handle                                                           

--
Calling a DLL or EXE with exports which requires a pointer array
Pointer arrays (char*[]) are not supported by Ticol but can be
emulated using [struct] as follows

N.B. Calling ticol.exe internal exports is not recommended!

    # Run under ticol.exe or ticol.dll                                          
    if {[info is_dll]} {                                                        
        set filename ticol.dll                                                  
    } else {                                                                    
        set filename ticol.exe                                                  
    }                                                                           
    lib ticol_calldll                                                           
    # Open a binary which contains valid exports...                             
    set h [dll_open $filename]                                                  
    # Get the global interpreter handle (internal workings)                     
    set i [calldll_cdecl $h picolThis]                                          
    # Create a pesudo-array of 2 x 4-byte "pointers" in a struct                
    # as successive memory addresses                                            
    struct foo {                                                                
        a1 4                                                                    
        a2 4                                                                    
    }                                                                           
    set arg1 "eval"                                                             
    # A simple test which is interpreted via [eval]                             
    set arg2 {textcolor magenta;puts [expr 22/7.0];textcolor}                   
    # Map the var's binary string into the struct address space                 
    struct setb foo.a1 [strptr arg1]                                            
    struct setb foo.a2 [strptr arg2]                                            
    # Call the DLL (must be __cdecl)                                            
    set s [calldll_cdecl $h picolCommandEval $i 2 foo 0]                        
    puts "Tcl result code is $s"                                                

--
ReactOS
-------
[calldll] may not work reliably with the ReactOS operating system

See also: calldllstr, calldll, calldll_cdecl, dll_open, dll_close,
          single line commands

calldllstr, calldllstr_cdecl

    To load, use:    lib ticol_calldll ?-verbose? ?-nocomplain?

Syntax:

    calldllstr dllname?.dll? function-name|@ordinal arg arg...
    calldllstr_cdecl dllname?.dll? function-name|@ordinal arg arg...

Call a DLL which returns a string result, exported as a Tcl string

If there is a need to dereference or address a returned pointer, e.g. to 
release memory allocated by the DLL call then you must use [calldll] instead
and use the returned integer pointer address value. When a Tcl string is 
returned then the original address will be lost

Static references to strings returned via [calldll] will return a reference
to some part of an external variable. When [calldllstr] is used the original
string (pointer address) reference is lost. No attempt should be made 
therefore, to release values returned by [calldllstr] other than using
standard Tcl methods such as [unset]

[calldllstr_cdecl] is used to call DLLs compiled with the __cdecl DLL
calling convention. Use [calldll_cdecl] to return an address value

If memory wasn't allocated by the DLL call then any return address 
returned by [calldll] when returning a string may be discarded. 
A null address will return 0

Example:

# Uses shell32 StrChrA to locate a substring by character match                 
# Shows differences between addressof and addressofb                            
# and use of ofaddressb to dereference                                          
                                                                                
set s "Hello World"                                                             
puts "addressof s  [addressof s]"         # Note how these addresses differ     
puts "addressofb s [addressofb s]"        # This is the var string address      
puts "calldll: H is '[calldllstr shell32 StrChrA [addressofb s] [asc H]]'"      
set ptr [calldllstr shell32 StrChrA [addressofb s] [asc W]]                     
puts "StrChrA($s,W) offset should be 'World' -> '[ofaddressb $ptr']"            

See also: calldll

carray plugin

    To load, use:    lib ticol_carray ?-verbose? ?-nocomplain?

Syntax:

    handle  [carray create ?size_x? ?size_y? ?size_z?]                          
    integer [carray add handle lval ?rval?]                                     
             carray delete handle index                                         
    integer [carray count handle]                                               
    string  [carray item handle index ?default-value? ?-rvalue? ?-list?]        
    string  [carray get handle index ?-rvalue?]                                 
    string  [carray get_2d handle x y ?-rvalue? ?-tuple?]                       
             carray foreach handle lvalue ?rvalue? {code}                       
    string  [carray set handle index]                                           
             carray set handle index lvalue ?rvalue?                            
             carray set_2d handle x y lvalue ?rvalue?                           
             carray sort handle ?-reverse? ?-rvalue? ?-numeric?                 
             carray walk handle                                                 
    bool    [carray unset handle ?-nocomplain?]                                 

Emulates "C"-like fixed arrays rather than associative arrays. Fixed 
arrays are indexed using 'base zero' integer index values and may be
sorted, Carrays are never sparse. Deletions are infilled by rearranging
the array. Sorting will rearrange the element order

[carray] can optionally act as an integer-indexed tuple and store an
"RValue" as well as the required "LValue".

    IndexValue -> 'LValue' + ?RValue?

Multi-dimensional arrays are not supported. As with early versions of
FORTRAN, they can be emulated using integer arithmetic to calculate an
offset within the 1-dimensional array.

 create  # Can pre-allocate memory to a given number of elements                
         # [carray create]        Create a dynamic array                        
         # [carray create x]      Create a 1 dimensional fixed array            
         # [carray create x y]    Create a 2 dimensional fixed array            
         # [carray create x y z]  Create a 3 dimensional fixed array            
         # Address 2D arrays using set_2d and get_2d                            
 add     # Adds an lvalue and, optionally, a corresponding rvalue               
         # An Rvalue only may be added by specifying the Lvalue as {}           
         # The updated record count is return on success, otherwise 0           
 delete  # Removes an item by index (both Lvalue and any Rvalue)                
         # The array is re-packed and indexed items will be re-ordered          
 count   # Returns a count of items in the array                                
 foreach # Offers a means of iterating each element of the array in             
         # ascending integer storage order. [foreach] is affected by            
         # the internally-sorted order of the array                             
         # The variables are placeholders for lvalue and rvalues                
         # with rvalue being optional                                           
         # [foreach] works only with 1 dimensional arrays                       
         # use iteration and direct addressing for 2d and 3d arrays             
 get     # Retrieve an array item by integer subscript. The element must        
         # exist, otherwise an error will be returned. Optionally, an           
         # RValue may be returned instead. [carray get] fails on error          
 get_2d  # Get an lvalue and optionally an rvalue in a 2D array                 
 get_3d  # Get an lvalue and optionally an rvalue in a 3D array                 
 item    # Index the array by integer subscript, returning an LValue and        
         # optional Rvalue. A default value may be specified                    
         # If the element does not exist either "" or an optional default       
         # value will be returned. [carray item] returns default on error       
 set     # Either set an element's lvalue (optionally also rvalue) at a         
         # specific index position. Called with no value arguments this         
         # command will echo the lvalue at the index position                   
 set_2d  # Set an lvalue and optionally an rvalue in a 2D array                 
 set_3d  # Set an lvalue and optionally an rvalue in a 3D array                 
 sort    # Can sort the array in normal or reverse order. Note that fixed       
         # arrays are not as efficient at sorting as associative arrays         
         # and it may be impractical and slow to sort very large arrays         
         # The default sort is 'string', a numeric sort can be forced by        
         # using the -numeric argument. 64-bit Ticol integers are correctly     
         # handled by this option                                               
         # Record numbers may no longer match after a sort is performed         
 walk    # Is useful for debugging. It will iterate the array elements          
 unset   # Destroys the array and renders the handle unusable. Using            
         # the handle after a call to [unset] will result in an error           

Example:

    set h [carray create]                                                       
    carray add $h Quick Brown                                                   
    carray add $h Fox Jumps                                                     
    carray add $h Lazy Dog                                                      
    carray add $h LValue                                                        
    carray add $h {} RValue                                                     
                                                                                
    carray sort $h                                                              
    carray foreach $h lv rv {               # foreach iterator example          
       puts "Lvalue:'$lv'\tRvalue:'$rv'"                                        
    }                                                                           
    carray sort $h -reverse                                                     
                                                                                
    set count [carray count $h]                                                 
    puts "We have $count item(s)"                                               
    for {set i 0} {[< $i $count]} {++ i} {   # Loop example                     
       puts "Item $i: '[carray item $h $i]'\t'\                                 
       [carray item $h $i -rvalue]'"                                            
    }                                                                           

Results:

    Lvalue:''       Rvalue:'RVOnly'                                             
    Lvalue:'Fox'    Rvalue:'Jumps'                                              
    Lvalue:'Hello'  Rvalue:'World'                                              
    Lvalue:'LVonly' Rvalue:''                                                   
    Lvalue:'Lazy'   Rvalue:'Dog'                                                
    Lvalue:'Quick'  Rvalue:'Brown'                                              
                                                                                
    Item 0: 'Quick' 'Brown'                                                     
    Item 1: 'Lazy'  'Dog'                                                       
    Item 2: 'LVonly' ''                                                         
    Item 3: 'Hello' 'World'                                                     
    Item 4: 'Fox'   'Jumps'                                                     
    Item 5: ''      'RVOnly'                                                    

Advantages and Disadvantages
----------------------------
Unlike associative arrays, "C" arrays can have an inherently ordered
(potentially sorted) state can can only be indexed by integer value
rather than by a string. They are, on average, slower to insert/add new
records and sorting a large array may take a long time and searching a
record on an unsorted array will may be much longer than a hashtable-
based associative array. A Tcl array could be used to index a carray.

Deleting All Nodes
------------------
carrays are not 'sparse' so they will reorganise and resize downwards on
deletion, and any intermediate records will be removed. Thus record
ordering will change on the deletion of an intermediate node

As the count and record numbers may change after each call to 
[carray delete] the following method should be used to iterate through and
delete all nodes:

    set i 0                                                                     
    while {[> [carray count $h] 0]} {                                           
       carray delete $h 0   # Delete the first element                          
    }                                                                           

Deleting in reverse order will also work

    for {set i [- [carray count $h] 1]} {>= $i 0} {-- i} {                      
       carray delete $h $i                                                      
    }                                                                           

A naive [for] loop which iterates the count as follows won't work as the
array will be resized downwards after each deletion:

    # Won't work as [carray count] changes after each [carray delete]           
    for {set i 0} {[< $i [carray count $h]]} {++ i} {                           
       carray delete $h $i                                                      
    }                                                                           

This method also will not work...

    set count [carray count $h]                                                 
    for {set i 0} {[< $i $count]} {++ i} {                                      
       carray delete $h $i                                                      
    }                                                                           

Emulating Multi-Dimensional Arrays
----------------------------------
A linear array can be manipulated as a multi-dimensional 
(N-dimensional) array with some trivial mathematics

A proc may be defined to handle N-dimensional indices and resolve these
into a 1-dimensional index value. [carray create] must be called with 
fixed array dimensions

2D and 3D addressing commands are built-in from build date 19/Dec/2021

    lib ticol_carray                                                            
    set dim_x 5                                                                 
    set dim_y 3                                                                 
    # Proc [in] x and y should be array-base 0 values                           
    proc array_set {handle x y value {rvalue}} {                                
        carray set $handle  [+ [* $y $::dim_x] $x] $value $rvalue               
    }                                                                           
    proc array_get {handle x y} {                                               
        return [carray get $handle [+ [* $y $::dim_x] $x]]                      
    }                                                                           
    set h [carray create [* $dim_x $dim_y]]                                     
    array_set $h 0 0 "0,0" Alpha element                                        
    array_set $h 1 0 "4,2" Omega element                                        
    puts "1 0 -> '[array_get $h 1 0]'"                                          
    lib ticol_carray -unload                                                    

See the 2d_carray.tcl and 3d_carray.tcl examples

https://www.geeksforgeeks.org/emulating-a-2-d-array-using-1-d-array/

See also: plugins, arrays, dictionaries

Handling Sparse Integer-Indexed Arrays

Tcl associative arrays are naturally "sparse" and have no innate connected 
ordering. Where ordering is required, some sort of ordering array subscript
such as an integer subscript is required.

    # The phrase "Hello world" is produced by implicit ordering by subcript     
    set a(1) "Hello"                                                            
    set a(2) "world"                                                            

Where more control is required over ordering then the "carray" plugin 
should be used. This allows "C"-like arrays which are indexed by an 
integer address value and where both Lvalue and Rvalues may be stored.
A command based indexing syntax is provided rather than bracketed array
references. One, two and three dimensional carrays may be sparse in that
unoccupied elements which have been declared are set to NULL.

Iterating large sparse arrays may be problematic, performance wise. It may
be necessary to use a Tcl array to index the sparse array in order to avoid
say iterating a sparse array of say a million elements

CGI Web Plugin

    To load, use:    lib ticol_cgi ?varname?

A rudimentary command to process CGI server POST input as a CGI
(Common Gateway Interface) application and splits into an array variable

The default variable name is _POST but this can be changed by passing an
alternate name as an argument

Ticol can be used to generate a modest CGI 'back-end' system including
database using SQLite or XBase. The language is not, however, recommended
for commercial or industrial CGI use

[get] will set and populate an array variable called _POST()

Syntax:

    integer [cgi]                        # Get/set the '_POST()' array          
    integer [cgi get ?-var name?]        # Get a CGI variable                   
    string [cgi arg name ?-nocase?]      # Match QUERY_STRING                   
    bool [cgi argfound name ?-nocase?]   # Test QUERY_STRING                    
    string [cgi post name ?-nocase?]     # Match _POST value                    
    bool [cgi postfound name ?-nocase?]  # Test _POST value                     

Where 'name' is an optional alternative to the default var, '_POST'
If 'var' is omitted then $_POST() will be used from the last [get] call

Errors:

To avoid fatal errors within a CGI script, all errors other than a fatal
exception are non-fatal and will return code 'OK' an error code as follows

    >0    SUCCESS Count of items returned                                       
     0    No items returned from POST                                           
    -1    Failed to initialise library                                          
    -2    Incorrect argument count                                              
    -3    CONTENT_LENGTH was not set                                            
    -4    CONTENT_LENGTH <= 0                                                   
    -5    CONTENT_TYPE was not set                                              
    -6    REQUEST_METHOD not set to POST                                        
    -7    Unknown/unhandled error                                               

Example:

Front-end form

    <!-- Web form -->                                                           
    <html>                                                                      
    <head>                                                                      
    <title>Test form</title>                                                    
    </head>                                                                     
    <body><pre>                                                                 
    <form name="A" method="POST" action="/cgi-bin/ticol.exe?cgi_post.tcl"       
    enctype="multipart/form-data" encoding="multipart/form-data"><p>            
    Enter your name: <input type="text" name="name" size="30"><p>               
    Enter some text: <input type="text" name="text" size="40"><p>               
    <input type="submit" value="Submit" name="button1">&nbsp;                   
    <input type="reset" value="Reset" name="button2"><p>                        
    </form></pre></body>                                                        
    </html>                                                                     

Tcl back-end script

    option expression off                                                       
    puts "Content-type: text/html\r\n\r\n"   # Send HTTP header                 
    lib ticol_cgi -nocomplain                # Load the plugin dll              
    set s [cgi]                              # Call & set $_POST() array var    
    if {> $s 0} {                            # Test for -ve error return        
       puts "<font color=\"blue\"><strong>"                                     
       array foreach _POST val subscript {   # Iterate array $_POST()           
          puts "<br>Result \$_POST($subscript)='$val'<br>"                      
       }                                                                        
       puts "</strong></font>"                                                  
    }                                                                           
    flush                                    # Flush stdout                     

Result:

    <Depends on data typed into the input form>                                 

See example file: lib_cgi_post.tcl and cgi_post.tcl (non lib version)

See also: security, cgi setup, plugins, dll interface, flush, plugins

CPU Information Plugin

    To load, use:    lib ticol_cpu ?-verbose? ?-nocomplain?

Shows various low-level CPU information by directly accessing the CPU

Syntax:

  string  [cpu name]           # The full, descriptive name of the CPU          
  string  [cpu vendor]         # The CPU vendor, e.g. "GenuineIntel"            
  integer [cpu bitness]        # The underlying O/S bitness, (32 or 64)         
  integer [cpu cores]          # Estimate the number of available cores         
  bool    [cpu hyperthreading] # Hyperthreading supported. Returns 1 or 0       
  bool    [cpu hypervisor]     # Running on hypervisor. Returns 1 or 0          
  integer [cpu eax]            # eax register return as integer                 
  integer [cpu ebx]            # ebx register return as integer                 
  integer [cpu ecx]            # ecx register return as integer                 
  integer [cpu edx]            # edx register return as integer                 

Not all VMs will show hypervisor enabled. Some VMs will emulate the CPU 
entirely, others will use the underlying CPU but may disable some features

The number of cores is an estimate and may be incorrect. The operating
system may disable cores as it wishes

Examples:

    lib ticol_cpu                                                               
    puts [cpu name]                                                             
    puts [cpu vendor]                                                           
    puts [cpu bitness]                                                          
    puts [cpu cores]                                                            
    puts [cpu hypervisor]                                                       
    puts "Virtual Machine Extensions " -nonewline                               
    if {& [cpu edx] 0x2} {      # Test edx bit 2                                
       puts "present"                                                           
    } else {                                                                    
       puts "not present"                                                       
    }                                                                           

Results:

    Intel(R) Core(TM) i5 CPU       M 540  @ 2.53GHz                             
    GenuineIntel                                                                
    64                                                                          
    4                                                                           
    FALSE                                                                       
    Virtual Machine Extensions present                                          

See also: plugins

concat

    string [concat string ?string ...?]

Efficiently concatenate one or more strings and return the result

[concat] treats each argument as a list and concatenates them into a single
list. It permits any number of arguments. The arguments are unchanged

[concat] is much faster at accumulating strings than using chained [set]
operations as the original source string is not repeatedly passed via the
Ticol interpreter and parsed

To concatenate items to a variable see [append] or [store]

Example:

    puts [concat The quick brown fox jumps over the lazy dog]                   

Result

    The quick brown fox jumps over the lazy dog

Example:

    concat a b {c d e} {f {g h}}                                                

Result:

    a b c d e f {g h}                                                           

See also: append, store, setat, cmp, set, variable, performance

setc

    integer [setc varName string ?length?]

Insert a source string into a variable's contents, centrally padded,
optionally for 'length' characters. The target variable is not truncated

Any value for 'length' < 0 will be ignored. Any value for 'length' greater
than the source string length or variable length will be ignored

The number of characters copied from the source string will be returned

If varName is empty then the request will be ignored and 0 will be returned

Use [setl] and [setr] to align left and right

Example:

    set s [makestr 30]                                                          
    set r [setc s Hello]                                                        
    puts '$s'                                                                   
    puts $r                                                                     

Result:

    '            Hello             '                                            
    5                                                                           

Example:

    set s ""                                                                    
    set r [setc s Hello]                                                        
    puts '$s'                                                                   
    puts $r                                                                     

Result:

    ''                                                                          
    0                                                                           

See also: setl, setr, string

chdir
 
    cd    path
    chdir path

Change the current directory. Does not support wildcard * chdir prefix

Both forward and backslash characters are accepted

Where cd is called from the command-line as a script expression, remember
to escape all backslash characters and any reserved Tcl character such as
the dollar ($) sign

UNC paths are accepted but can have no action relative to a current, fixed
drive-letter:

Examples:

    cd..                                                                        
    cd ..                                                                       
    cd \\rootdir                                                                
    cd \\rootdir\\subdir                                                        
    cd \\\\server\\share\\path     # Will have no action relative to path       
    cd \\\\server\\c\$\\path       # Note that $ must be escaped                
                                                                                
    cd /rootdir                                                                 
    cd /rootdir/subdir                                                          
    cd //server/share/path         # Will have no action relative to path       
    cd //server/c\$/path           # Note that $ must be escaped                

See also: mkdir, mkdirs, getcwd, file, pwd

chr
 
    string [chr n ?added-offset-value?]

The inverse of asc. Returns character number 'n' as a string

The argument 'n' must be an integer value, not a character

[chr] also permits a value to be added. This is useful where an integer is
converted into an ASCII letter e.g. 0 -> 'a', 1 -> 'b' and avoids an 
additional and time-consuming call to [+] to calculate the ASCII value

Example:

    puts [chr 65]                                                               

Result:    

    A                                                                           

Example:

    set i 2                                                                     
    puts [chr $i 97]   # Add offset: character 2 + 97                           

Result:

    c                                                                           

A procedural equivalent might look something like this:

    undef chr -nocomplain          # Undefine the built-in command              
    proc chr {i} {                                                              
       return [format "%c" $i]                                                  
    }                                                                           
    puts [chr 65]                                                               
    puts [chr 97]                                                               

Result

    A                                                                           
    a                                                                           

See also: asc, string, left, right, mid, index

chomp

    string [chomp var ?-leave?]
    string [chomp var N ?-leave?]

[chomp] takes only a variable name as its argument and is a non-standard
command to efficiently slice-up large strings. 

If an argument length is not specified, then [chomp] removes and returns
characters up to and excluding the next whitespace character as one of
"\r\n\t ".

If an argument length, 'N' is specified, [chomp] removes and returns 'N'
characters from the left-hand side of a string. The remainder of the
characters not removed are left in the variable

The source variable will have the returned characters removed and they
are thus 'eaten' or 'chomped' away unless the '-leave' argument is used

[chomp] is faster and more memory-efficient with very large strings than
compounding [set] and [mids] or [set] and [string left] etc.

If argument N is larger than the string length then an whole string is
returned. If the source variable is empty then an empty string is
returned

    chomp var N        # Removing N characters is equivalent to                 
    mids $var [+ N 1]  # this command using [mids]                              

Example:

    set s $qbf                                                                  
    chomp s 1          # Discards 1 character and retains 2nd char onwards      
    puts $s                                                                     
    set s $qbf                                                                  
    puts [mids $s 2]   # Returns 2nd character onwards                          

Results:

    he quick brown fox jumps over the lazy dog                                  
    he quick brown fox jumps over the lazy dog                                  

See also: mids, left, string, string left

clock
 
Standard Tcl time/date routines

Returns dates or times in the Unix epoch (01/01/1970)

    integer [clock clicks|seconds|milliseconds]
    string  [clock format seconds|now ?-format "%Y-%m-%dT%H:%M:%S"?]

Where seconds must be a positive integer

See also: clock scan, clock split

The -format option controls what format the return will be in
The contents of the string argument to format has similar contents as the 
format statement Additionally, there are several more %* descriptors which
can be used to describe the output.

    %a      Abbreviated weekday name (Mon, Tue, etc.)                           
    %A      Full weekday name (Monday, Tuesday, etc.)                           
    %b      Abbreviated month name (Jan, Feb, etc.)                             
    %B      Full month name (January, February, etc.)                           
    %d      Day of month                                                        
    %j      Julian day of year                                                  
    %m      Month number (01-12)                                                
    %y      Year in century                                                     
    %Y      Year with 4 digits                                                  
    %H      Hour (00-23)                                                        
    %I      Hour (00-12)                                                        
    %M      Minutes (00-59)                                                     
    %S      Seconds(00-59)                                                      
    %p      PM or AM                                                            
    %D      Date as %m/%d/%y                                                    
    %r      Time as %I:%M:%S %p                                                 
    %R      Time as %H:%M                                                       
    %T      Time as %H:%M:%S                                                    
    %Z      Time Zone Name                                                      

The default -format mask is "%a %b %d %H:%M:%S %Y"

Examples:

    Command:                                      Result
                                                                                
    clock clicks                                  # 80579                       
    clock seconds                                 # 1425214130                  
    clock format now -format "%Y %m %d"           # 2015 03 01                  
    clock format 9999 -format "%Y %m %d %H:%M:%S" # 1970 01 01 02:46:39         

Example:

    puts [clock format [double_to_ctime [now]] -format "%Y/%m/%d %H:%M:%S"      

Result:

    2015/04/08 12:15:41                                                         

Example:

    puts "[clock milliseconds] v's [clock seconds]"                             

Result:

    1512702231579 v's 1512702231                                                

See: clock, clock scan, clock scan workaround

See also: clock format, clock scan, clock scan workaround, clock split,
          time, date, ticks, double_to_ctime, ctime_to_double,
          date_to_ctime, now, vars, is_dst

clock format

    string [clock format seconds|now ?-format "%Y-%m-%dT%H:%M:%S"?]
 
Format codes for the clock -format string are as follows. These are
standard C/C++ formatting codes with a couple of extra Tcl codes

  %a Abbreviated weekday name                                                   
  %A Full weekday name                                                          
  %b Abbreviated month name                                                     
  %B Full month name                                                            
  %c Date and time representation appropriate for locale                        
  %D Day of month as a decimal number with no 0 prefix (1..31)                  
  %d Day of month as decimal number (01..31)                                    
  %H Hour in 24-hour format (00 .. 23)                                          
  %I Hour in 12-hour format (01 .. 12)                                          
  %j Day of year as decimal number (001..366)                                   
  %m Month as decimal number (01 .. 12)                                         
  %M Minute as decimal number (00 .. 59)                                        
  %N Month of the year as a 1 or 2 digit number (1..12)                         
  %p Current locale's A.M./P.M. indicator for 12-hour clock                     
  %o Day of month ordinal. E.g. "st", "nd", "rd", "th"                          
  %S Second as decimal number (00 .. 59)                                        
  %U Week of year as decimal number, Sunday as first day of week (00..51)       
  %w Weekday as decimal number (0 .. 6; Sunday is 0)                            
  %W Week of year as decimal number, Monday as first day of week (00..51)       
  %x Date representation for current locale                                     
  %X Time representation for current locale                                     
  %y Year without century, as decimal number (00 .. 99)                         
  %Y Year with century, as decimal number                                       
  %z Time-zone name or abbreviation; no characters if time zone is unknown      
  %Z Ditto                                                                      
  %% Percent sign                                                               

Example:

    puts [clock format 0 -format "%A, %D%o %B %Y"]                              

Result:

    Thursday, 1st January 1970                                                  

Example:

    puts [clock format now -format "%A, %D%o %B %Y %H:%I:%S"]                   

Result

    Wednesday, 4th October 2017 20:04:37                                        

Example:

    puts [clock format [date_to_ctime 1972 04 04] -format %D%N%y]               

Result:

    4472                                                                        

See also: clock, clock split, clock scan, clock scan workaround,
          time, date

clock scan

    clock scan <date time>

[clock scan] parses the date/time argument and returns the number of
seconds since the start of the Unix epoch which may be converted back
to a date using various methods such as [date]

[clock scan] accepts input in the following common formats

    US date format - 'MMM DD ?HH:MM:SS?'                                        
    US date format - 'MMM DD YY ?HH:MM:SS?'                                     
    US date format - 'MMM DD YYYY ?HH:MM:SS?'                                   
    US date format - 'MMM DD, YY ?HH:MM:SS?'                                    
    US date format - 'MMM DD, YYYY ?HH:MM:SS?'                                  
    US date format - 'MMMM DD ?HH:MM:SS?'                                       
    US date format - 'MMMM DD YY ?HH:MM:SS?'                                    
    US date format - 'MMMM DD YYYY ?HH:MM:SS?'                                  
    US date format - 'MMMM DD, YY ?HH:MM:SS?'                                   
    US date format - 'MMMM DD, YYYY ?HH:MM:SS?'                                 
    US date format - 'DD MMM ?HH:MM:SS?'                                        
    US date format - 'DD MMM YY?HH:MM:SS?'                                      
    US date format - 'DD MMM YYYY?HH:MM:SS?'                                    
    US date format - 'DD MMMM ?HH:MM:SS?'                                       
    US date format - 'DD MMMM YY ?HH:MM:SS?'                                    
    US date format - 'DD MMMM YYYY ?HH:MM:SS?'                                  
    US date format - 'MM/DD ?HH:MM:SS?'                                         
    US date format - 'MM/DD/YY ?HH:MM:SS?'                                      
    US date format - 'MM/DD/YYYY ?HH:MM:SS?'                                    
    US date format - 'DD/MM ?HH:MM:SS?'                                         
    US date format - 'DD/MM/YY ?HH:MM:SS?'                                      
    US date format - 'DD/MM/YYYY ?HH:MM:SS?'                                    

Where:

    MM    is a 2-digit month value                                              
    MMM   is a 3 character month name                                           
    MMMM  is a full month name                                                  
    DD    is a 1 or 2 digit day                                                 
    YY    is a 2 digit year > 100 AD, where > 50 -> 19xx and <= 50 -> 20xx      
    YYYY  is a 4 digit year                                                     

The output is always limited to the start of the Unix time epoch
 - 1970/Jan/01 00:00:00 (0)

[clock scan] can be emulated for various date formats using [scan] and 
[date],

Example:

    # Scan an ISO date into [date] format                                       
    scan 20161122 %4i%2i%2i y m d                                               
    puts "$y/$m/$d"                                                             

Result:

    2016/11/22                                                                  

Checks are made only on each individual parameter. No check is made for
the validity of the entire date string.

See: clock scan workaround

Example:

    set a [clock scan "Jan 12, 1992"]                                           
    set b [clock scan "Jan 12, 1997"]                                           
    puts $a                                                                     
    puts $b                                                                     
    puts "Difference is [- $b $a]"                                              

Results:

    695174400                                                                   
    853027200                                                                   
    Difference is 157852800 second(s)                                           

The result of [clock scan] can be converted from a "C" integer time value
to double using [ctime_to_double]. This value can then be manipulated as
an ISO date string using [date]

Example:

    set b [clock scan "Jan 12, 1997, 23:59:12"]                                 
    puts [date [ctime_to_double $b]]                                            

Result:

    1997/01/12 23:59:12                                                         

                                                                                
    set q [yyyy-mm-dd "2018-01-05 23:59:01"]        # "2018-01-05 00:00:00"     
    puts $q                                                                     
    puts [date [ctime_to_double $q]]                                            

                                                                                
    1515196741                                                                  

                                                                                
    Error Codes                                                                 
    -----------                                                                 
    1   Bad slash separator count                                               
    2   Day value < 1 || > 31                                                   
    3   Month value < 1 || > 12                                                 
    4   Missing value field                                                     
    5   Resulting date is invalid                                               
    6   Unhandled error                                                         

See also: clock scan workaround, clock, clock_split, date, time, scan, is_dst

Clock Scan Workaround

[clock scan] is not fully-implemented as the date inputs are too widely
varying to be practical. Using the popular Tcl example code we can
demonstrate an easy workaround using [scan] and [date]

 # We replace: set bookSeconds [clock scan $halBirthBook]                       
 set h 00                                                                       
 set i 00                                                                       
 set s 00                                                                       
 set halBirthBook "Jan 12, 1997 01:02:03"                                       
 set count [scan $halBirthBook "%s %s %s %02d:%02d:%02d" m d y h i s]           
 puts "Scanned $count item(s)"                                                  
 set d [string trim d ,]                                                        
 puts "date string $y/$m/$d $h:$i:$s"                                           
 set bookSeconds [date $y/$m/$d $h:$i:$s -ctime]                                
 puts "bookSeconds=$bookSeconds"                                                
 newline                                                                        
                                                                                
 # We replace: set movieSeconds [clock scan $halBirthMovie]                     
 set h 00                                                                       
 set i 00                                                                       
 set s 00                                                                       
 set halBirthMovie "Jan 12, 1992 23:59:01"                                      
 set count [scan $halBirthMovie "%3s %s %s %02d:%02d:%02d" m d y h i s]         
 puts "Scanned $count item(s)"                                                  
 set d [string trim d ,]                                                        
 puts "date string $y/$m/$d $h:$i:$s"                                           
 set movieSeconds [date $y/$m/$d $h:$i:$s -ctime]                               
 puts "movieSeconds=$movieSeconds"                                              
 newline                                                                        
                                                                                
 set diffseconds [expr $bookSeconds - $movieSeconds]                            
 puts "The book and movie versions of '2001, A Space Odyssey' had a"            
 puts "discrepancy of $diffseconds second(s) [/ $diffseconds 86400] day(s)\     
  in how"                                                                       
 puts "soon we would have sentient computers like the HAL 9000"                 

Based on example from:

    https://www.tcl.tk/man/tcl8.5/tutorial/Tcl41.html

See also: clock, clock scan, clock split, date, time, scan

clock split

    [clock split seconds ?days|-? ?hours|-? ?minutes|-? ?seconds|-?]

Splits a number of seconds into variables containing an absolute number of
days, hours, minutes or seconds

Each variable argument is optional but at least one variable argument must
be present. Arguments must be in order of days, hours, minutes and seconds.
Arguments may be skipped by using a "-" placeholder

Variable arguments must not exist

Example:

    puts [clock split 3601 d h m s]                                             
    puts "$d day(s) $h hour(s) $m minute(s) $s second(s)"                       

Result:

    4                                                                           
    0 day(s) 1 hour(s) 0 minute(s) 1 second(s)                                  

Example:

    puts [clock split 86461 d h m s]                                            
    puts "$d day(s) $h hour(s) $m minute(s) $s second(s)"                       

Result:

    4                                                                           
    1 day(s) 0 hour(s) 1 minute(s) 1 second(s)                                  

Example:

    puts [clock split [now -ctime] d h m s]                                     
    puts "$d day(s) $h hour(s) $m minute(s) $s second(s)"                       

Result:

    4                                                                           
    17522 day(s) 12 hour(s) 42 minute(s) 49 second(s)                           

See also: clock, clock scan, time, date

close, file close
 
    close handle
    file close handle

Close a file-handle previously returned from the open command
Handle must be > 0 and a valid stream handle

[puts] may be used to write to the file

Example:

    set fp [open "input.txt" w+]                                                
    puts $fp "test"                                                             
    close $fp                                                                   

See also: open, gets, file, readfile, writefile

clreol

Clear text from the current cursor position to the end of the line

Example

    cls                                                                         
    gotoxy 1 1                                                                  
    puts $qbf         # Output some text which we will clear                    
    gotoxy 1 1                                                                  
    puts "Hello" -nonewline                                                     
    clreol            # Clear the previous text                                 
    newline                                                                     

cls

    cls ?character|{}? ?background-colour?

Clears the console window, optionally to a given character or colour
The default screen-clear character is a space. A character value is 
required in order for the background colour to have effect

Example:

    cls [chr 176] darkcyan                                                      

See also: console

cmdcount (Command Count)

    integer [cmdcount ?-reset?]
    integer [cmdcount script|command ?var?]

Similar to [catch], [cmdcount] will either show the current count of
commands executed or will execute a command or script and return a count
of the number of commands which were executed. Along with the [time]
command this may be useful for profiling and optimising Tcl scripts

The '-reset' argument can be used to reset cmdcount

Example:

    puts [cmdcount]                # Show the current command count             

Result:

    1                                                                           

Example:

    puts [cmdcount {puts "Hello"}] # Command cost of running this command       

Result:

    1                                                                           

Example:

    puts [cmdcount {run test.tcl}]                                              

Result:

    2786                                                                        

See also: debugging, time, trace, catch

cmp (Compare)
 
    bool [cmp string1 string2 ?start-pos? ?len? ?-nocase?]

Efficient compare function. Avoids the need to extract string segments for
comparison by comparing directly within the string.

Locates string2 within string1 dependent on string1 start position and
comparison length

The start position is indexed at a base 1 offset in string 'string1'. 
i.e. 1 indexes the first character, 2, the second etc.

A case-insensitive comparison can be performed using -nocase

[sc] (string compare) is a similar command but which returns an index value

Example:  (Detect C style opening comment)

    option expression off                                                       
    # Variable 'data' is read from a file of 'count' characters in length       
    for {set i 1} {< $i $count} {incr i} {                                      
       if { cmp data "/*" $i 2 } {                                              
          incr i 2                                                              
          set in_comment $true                                                  
       }                                                                        
    }                                                                           

See also: cat, eq, ne, efficiency

Coding Standards

The following coding standards have been used when developing Ticol Tcl

Methodology
-----------
A 'test-driven' methodology has been used. Code modules are developed
against small test routines, not all of which form the release package
test scripts

Language
--------
The following C/C++ language standard library functions have been avoided
at all cost and alternative 'safe' versions written:

    Avoided         Alternative used    Explanation
    ---------------------------------------------------------------
    gets            getstr              Buffer size specification
    sprintf         _snprintf           Buffer size specification
    strcpy          strcpyn             Null termination guarantee
    strncpy         strcpyn             Null termination guarantee
    strcat          strcatn             Null termination guarantee
    strncat         strcatn             Null termination guarantee

Leak tests have been performed on modules where feasible

See also: ticol, why

comma

    string [comma string ?separator-char? ?-round N?]
 
Comma-formats a numeric value. Optionally, the separator character may be
customised and a rounding value may be specified.

Example:

    puts [comma 1234567890]                                                     
    puts [comma 1234567890 +]                                                   
    puts [comma 123456.7890 / -round 2]                                         
    puts [comma 123.4567890 -round 2]                                           

Result:

    1,234,567,890                                                               
    1+234+567+890                                                               
    123/456.7890                                                                
    123.45                                                                      

Example:

    puts [comma 1234567890 " "]                                                 

Result:

    1 234 567 890                                                               

Example:

    puts "[comma [/ [filesize "ticol.exe"] 1024]] Kb"                           

Result:

    214 Kb                                                                      

See also: format

commands
 
    list [commands ?filter?]

Returns a list of all known/registered commands and procs
A filter may be used to select by filter prefix match

Example:

    commands sc                                                                 

Result:

    scan screen                                                                 

[is_command] may be used to test for the presence of a defined command or
proc. A count of commands registered may be obtained using:

    puts [lcount [commands]]                                                    

Use: option echo on to display directly to the console

See also: Tcl commands, commands by function, info, is_command, 
          vars, help, exit

Using Ticol at the Command Line

    ticol.exe   filename[.tcl] [arg ...] [/switches]
    ticol.exe   /expr:<expression> [/switches]
    ticol.exe ; [statement ...] [/switches]

Running Tcl scripts from the Windows command-line:

If the first argument to ticol.exe is a Tcl script then this will be loaded
and executed after running any autoexec.tcl file (unless /NA is specified)
Any subsequent arguments can be accessed from the argv() array 
See: argv
 
Using Ticol as a command-line expression-evaluator from Windows:

You can call Ticol with Tcl commands if you prefix them with a semi-colon 
character as the first command-line argument and quote the remainder when
necessary. This avoids Ticol attempting to run a Tcl script file

    ticol.exe ; "puts [+ 1 1]" ;                                                

Embedded double quotes may be escaped in Windows using \"  e.g.

    ticol.exe ; "puts [expr \"22/7.0\"]" ;                                      

or you may use the /EXPR command-line argument. This will evaluate
and return the result by calling [expr]

    ticol.exe ; "/expr:22/7.0"                                                  

The semi-colon variant can handle multiple arguments in the same
interpreter context:

Example:

    ticol.exe ; "set a 4" "printf \"Pi is %3.2f\r\n\" \                         
    [calc round(4*atan(1),2)]" "puts [expr $a*3]" /na                           

Result:

    Pi is 3.14                                                                  
    12                                                                          

Examples:

    ticol ; "load test.tcl; run"                                                
    ticol.exe ; "puts [expr \"22/7.0\"]" ; "puts \"hello world\"" ; "for \      
       {set i 0} {< $i 3} {++ i} {puts $i}"                                     

Result:

    <runs script test.tcl and then returns to the O/S>                          
    3.142857142857143                                                           
    hello world                                                                 
    0                                                                           
    1                                                                           
    2                                                                           

Expressions with embedded spaces, commands etc. must be wrapped in
double-quotes. Embedded double-quotes may be escaped

    ticol "/expr:[cube 3]" /na          # Quoted works OK                       
    ticol  /expr:[cube 3]  /na          # Unquoted does not                     
                                                                                
    ticol "/expr:\"Hello world\"" /na   # puts not required here                

Calling from Windows Batch Script FOR Command
---------------------------------------------
The Windows FOR command will require usebackq and backtick wrappers to use
double-quotes around any Tcl /EXPR command which contains spaces

for /F "tokens=* usebackq" %%i in (`ticol.exe "/expr:round(22/7.0,4)"`) do (    
   set RESULT=%%i                                                               
)                                                                               
echo Ticol expression result for 22/7.0 is %RESULT%                             
                                                                                
for /F "tokens=* usebackq" %%i in (`ticol.exe "/expr:4*atan(1)"`) do (          
   set RESULT=%%i                                                               
)                                                                               
echo Ticol expression result for 4*atan(1) is %RESULT%                          

/EXPR may also be used to call external __stdcall or __cdecl DLLs ...

for /F "tokens=* usebackq" %%i in (`ticol /expr:"[calldlldbl_cdecl \            
 msvcrt40.dll sqrt 10.0]" /na`) do (                                            
   set RESULT=%%i                                                               
)                                                                               
echo Result for [calldlldbl_cdecl msvcrt40.dll sqrt 10.0] is %RESULT%           

The Windows FOR command requires no CRLF to be issued. You can add a CRLF
to the output by adding the /CRLF argument to ticol.exe

See also: command line, command line arguments, Ticol, commands

Tcl Commands

    string [command ?arg...?]

A Tcl command is some alpha string which is most often contained within
square brackets

If a known/registered command string is met outside of a quoted
string it will be executed without the need to be wrapped in square
brackets. Thus, in 

    puts "Hello world"                                                          

the command, [puts] does not need to be wrapped in []

Unless evaluation is delayed by wrapping in braces, commands are
evaluated recursively within strings before the string or command-
sequence is evaluated

Thus...

    puts "Hello [chr [+ 80 7]][chr 111][chr 114][chr 108][chr 100]"             

will display

    Hello world                                                                 

The innermost [+ 80 7] command is resolved first, then the result,
[chr 87], followed by [chr 111] ...

The command may have optional arguments. Most Tcl commands take arguments,
whereas a few do not. Thus [stop] takes no arguments whereas [puts] does.
Arguments act as modifiers of a command similar to 'attributes' in 
certain languages such as C#

Tcl commands use "Polish" type prefix notation where an operator or
command appears first, followed by any arguments. Flow-control commands
may accept either Tcl command or natural expression syntax. This behaviour
can be controlled using the [option expression] command. Explicitly
called commands which are wrapped in [] do not require the use of 
[option expression off]

The command name can be supplied via a variable which will be resolved
before the command is executed (if braces are not used to delay
evaluation)

    set p puts                                                                  
    set q Hello                                                                 
    set r world                                                                 
    $p "$q $r"                                                                  

Result:

    Hello world                                                                 

Example:

    set p puts                                                                  
    set q randomstr                                                             
    set r 10                                                                    
    $p [$q $r]                                                                  

Result:

    UaMLNJYcxl                                                                  

See also: braces, option expression

Comments
 
Standard Tcl single-line comments are prefixed by the # character 

Example:

    # This is a comment                                                         

Standard Tcl single-line comments are both supported and are enhanced,
since a semi-colon is not required before a # comment. This is because
single-line comments are implemented by the macro preprocesor and are
removed from the script before being interpreted by the Tcl interpreter

You can add semi-colons before a # comment if you wish, these will be
ignored:

Example:

    puts "A standard Tcl comment with ; follows...";  # ...here                 
    puts "An enhanced Ticol Tcl comment follows..."   # ...here                 

A # comment start symbol is valid only outside of quoted strings. Hash
symbols within quoted strings, even multiline-ones, are not treated as
comment start markers and may be used as literal characters. 

Outside of quoted strings a literal # character may be represented by
being 'escaped' as \#.

Unquoted escape sequences should not be used for [puts] or other print
commands.

Example:

    puts "This string contains a literal # character"                           

A literal has character can be also represented using [asc] and [chr]
as follows:

Example:

    puts [asc \#]         # Prints 35 (unquoted not recommended)                
    puts [asc "#"]        # Safely quoted (recommended)                         
    puts [chr 35]                                                               

Result:

    35                                                                          
    35                                                                          
    #                                                                           

It is strongly-recommended that, as a matter of style, a # comment
character is followed by a space. This will avoid accidental and
difficult to trace clashes with the Ticol macro commands 
(e.g. #exec, #if, #exit)

The Tcl semi-colon+hash end of line syntax is supported but not required

    # This is a comment                                                         
    puts "Hello World"         # This is a valid Ticol comment                  
    puts "Goodbye"            ;# This also a valid comment                      
    puts "Quoted # char"      ;# This is a valid Tcl comment too                

Any information after a comment character is completely removed by the
Ticol Macro PreProcessor. To disable this use the /NP (no preprocesor)
command-line argument. Note that this also disables #ifdef etc. macros
and these must therefore be removed from the source file

A # comment should not appear after a line continuation character '\'

Example:

    "The quick brown\          # This comment is illegal because of \           
     fox jumps over\           # line-continuation. So is this ...              
     the lazy dog"                                                              

Long Comments
-------------
Long comments are supported in two forms via the Macro PreProcessor. 
#ifdef... #endif statements and C/C++ style /* ... */ long comment tags. 
C/C++ style comments are the most efficient and both styles are processed
by the  Macro PreProcessor (MPP) before the script is executed, both from
the command-line or CLI during [load] or [run] operations.

Long comments are valid within braces and outside of quoted strings.
If it is desired to place literal comments within strings, such strings
must be wrapped in double quotes rather than braces.

Example:

    # Comment sequence visible as a string literal...                           
    puts "This string contains a /* comment */ sequence"                        
    # Comment within braced string acts as a comment...                         
    puts {This string contains/* comment */ characters}                         

Results:

    This string contains a /* comment */ sequence                               
    This string contains characters                                             

Example Tcl comments using macro variables and #ifdef commands

    #ifdef comment                                                              
       set a 22                                                                 
       set b 7.0                                                                
       puts [/ $a $b]                                                           
    #endif                                                                      

To revert a long macro comment either delete or prefix again with a 2nd 
# character ...

    ##ifdef comment   # This ifdef statement is commented-out                   
    #ifdef comment    # This ifdef statement is active                          

And (perhaps heinously, for some Tcl enthusiasts) C/C++ style comments
are supported ...

    /*                                                                          
       This is a comment, this code is ignored                                  
       So is this invalid expression [expr 1/0]                                 
       Some may not like this, but it is convenient and useful                  
    */                                                                          

C/C++ long comments are not valid inside quoted strings and are
treated as string literals within them. Thus...

    set a "Brave /* new */ world"                                               

    will result in the string ...

    Brave /* new */ world                                                       

C/C++ long comments may be ignored by prefixing with a standard Tcl # 
comment ...

    # /*                                                                        
       puts "This code will be executed as it is not commented"                 
    # */                                                                        

Selective enabling of comment blocks during debugging can be facilitated
as in C++

    /*                                                                          
       puts "This code will not be executed"                                    
    # */                                                                        

C++ single-line style comments may be defined using this dummy proc:

    proc // args {                                                              
       # C++ style comment - do nothing. Discard all args on this line          
    }                                                                           

Be careful of Unix-style wildcard filename/path values within unquoted
strings. It is good practise to always wrap strings in double quotes

    puts [ftp $hostname ls /tools/ * -u admin -p xxxxx]                         

Should be:

    puts "[ftp $hostname ls /tools/*.zip -u admin -p xxxxx]"                    

Since the unquoted '/*' in '/*.zip' will be interpreted as a long comment
start. Quoting will prevent this.

Mixed Long and Short Comments
-----------------------------
Mixed long and short comments should appear on separate lines and
apart from other statements, particularly quoted strings.

    # /*                                                                        
       puts "This code uncommented until the leading # is removed"              
    # */                                                                        

Dangerous Comment Examples
--------------------------
These are examples of a dangerous comments because '#if' and '#else' are 
reserved Macro Preprocessor keywords. Commenting out a Tcl [if] or [else]
statement will inadvertently create a valid Macro directive

    #if     This line is present the MPP will execute the '#if' clause          
    #else   This comment is also dangerous for the same reason                  
    #exit   This comment will cause the Macro Preprocessor to exit              
    #echo   "--x--"  This is bad if you intended to comment out [echo]          

See also: command line, cli, Ticol

Compatibility with Standard Tcl

Ticol Tcl was designed to be flexible and have a reasonable degree of 
compatibility with standard Tcl. Standard Tcl has changed over time and has
incorporated many new features. Ticol was written for personal use by the 
author and incorporates a number of changes which were desired by the author

Some of these changes reduce compatibility with standard Tcl.

One of the main design issues was a desire to improve performance by use of
a Macro PreProcessor (MPP) to optimise source code before execution. It was
decided that the # character would be an absolute comment delimiter and
therefore cannot be uses in commands such as [uplevel]. C/C++ like comments
are also available as these suited the author's preference

The Macro PreProcessor is non-Tcl-standard and is more C-like. Although 
basic it can offer some advantages when developing Tcl code

Tests showed that standard Tcl command such as [+] or [-] were much faster 
than the expression-handler. Perhaps because Ticol doesn't make use of any
compiler-like optimisations when interpreting the script and it leans on
the basic Picol kernel interpreter design. For this reason, a new command
[calc] was added which enables the MPP to expand expressions into inline 
Tcl code. If the MPP is disabled then [calc] will be interpreted as [expr]

Some advanced Tcl features are not implemented either fully or at all.

Arrays are preferred by the author to lists and dictionaries and have been
implemented as hashtables. Performance of arrays is quite fast with Ticol
being able to handle a million array items with no particular problems and
with excellent access speed

Ticol has its own (DLL) plugin library support which is not supported by
standard Tcl. One of these plugins offers the ability to interface into 
external DLL libraries (either __stdcall or __Cdecl). To support this 
interface Ticol also offers a simple binary struct which enables 
API/external DLL calls and struct returns. Variants are also supported
The plugin interface C++ API code is available on request so you can 
extend Ticol by adding new commands

Ticol also offers many non-standard commands which can be used to improve
performance and flexibility, e.g. [funct] will allow you to call any [expr]
function directly. [funct] is used by the MPP when it expands [calc] 
expressions

Other commands include: [at_exit], [base64], [cat], [chr]. [encrypt],
[ctime_to_double], [decimal_to_roman], [dump], [elevate], [inputbox]
[json_to_list], [msgbox], [shell], [spawn], [win_error], [write]

Standard Tcl may require some modifications before it will run on Ticol

Tk is not supported

See also: Non Standard Tcl Commands, Ticol, FAQ, Performance, Plugins, Tcl

Ticol DLL Stub Program (ticol_stub.exe)

This is a Windows console program used to test interactivity with the
DLL version of the Ticol Tcl interpreter (ticol.dll)

The stub program is a very simple console app which dynamically links
to ticol.dll and runs Tcl commands. These commands may be used to 
run a script file via either [run] or [source]

The syntax is as follows:

    ticol_stub.exe <options> tcl commands...                                    
    where <options> are one or more of                                          
    /Q or -Q          Quiet mode                                                
    /BP or -BP        Enable breakpoints                                        
    /ST or -ST        Enable stack trace                                        
    /RA or -RA        Enable run autoexec.tcl                                   
    /DEBUG or -DEBUG  Run in debug mode                                         

Examples:

    ticol_stub.exe ?-ra? ?-bp? ?-st?                                            
    ticol_stub "puts \"hello world\""                                           
    ticol_stub puts [expr 22/7.0]                                               
    ticol_stub -q run dbview locos                                              
    ticol_stub -q run tests.tcl                                                 
    ticol_stub -q run ansishow ansi -fix                                        
    ticol_stub -q source hanoi.tcl 17                                           
    ticol_stub -q source run_asm.tcl maze                                       
    ticol_stub -q run test_lib_asm                                              
    ticol_stub help puts                                                        
    ticol_stub source dbview.bat albums.dbf                                     

Note that this stub application is for testing only and should not
be used for high security purposes or in a production environment

The test stub exe must not be exposed as a CGI back end application as it
accepts direct Tcl commands and could compromise a system.

See also: ticol.dll, ticol.ini

console

  console attribute              # Return the attribute at the cursor pos       
  console borders on|off         # Enable the console window frame border       
  console center                 # Centre the console on the screen             
  console close                  # Closes the console and exits Ticol           
  console curx                   # Return the cursor x position in columns      
  console cury                   # Return the cursor y position in rows         
  console get_attributes         # Get attrbutes as a base64 block              
  console get_text               # Grab the current console as plain text       
  console get_title              # Retrieve the console title (ANSI only)       
  console height                 # Return the console height in rows            
  console hidden                 # Returns a boolean 1 if hidden, 0 if not      
  console hide                   # Hides the console                            
  console hidecursor             # Hide the console cursor                      
  console maximize               # Maximize the console window                  
  console minimize               # Minimize the console window                  
  console move x y ?w? ?h?       # Move or resize the console window (pix)      
  console noborders              # Disable the console window border            
  console scroll x y w h ?n?     # Scroll a console area by 'n' lines           
  console set_attributes string  # Set attributes saved by get_attributes       
  console set_title string       # Set the console title (ANSI only)            
  console setfocus               # Set focus on the console window              
  console show                   # Shows the console                            
  console showcursor             # Show the console cursor                      
  console size w h               # Resize the console columns and rows          
  console width                  # Return the console width in columns          

Show or hide the console window or check if the console is already hidden.
Note: if you hide the console window you will lose control of the Ticol
CLI

The command 'console hide' should be run only from a running script or you
may lose control of the console. Hiding the console may be useful for CGI
web scripts

Setfocus has limited use and may be blocked by recent versions of Windows

[console scroll] accepts an optional lines argument. Currently 1 (default)
scrolls the contents of the area up and -1 scrolls the window content
downwards

[console set_title] will only have effect during script operation. The CLI
will reset the console title on script exit. The console may only be set
to ANSI (ASCII) string values. Paths should be unescaped for set_title
Both [console get_title] and [console set_title] will return a boolean
indicating success or failure. [console set_title] does not unescape
input strings, you must use [unescape]

Usage:

   console hide                                                                 
   console show                                                                 
   console setfocus                                                             

Example:

   console set_title "Ticol Demo"                                               
   puts "Hiding the console..."                                                 
   console hide                                                                 
   puts "Console hidden? [bool [console hidden]]" # Not visible until show      
   sleep 3000                                                                   
   console show                                                                 
   puts "I should be back now"                                                  

Result:

   The console window will disappear for 3 seconds then reappear

Example:

    # Smooth scroll an area down by 1 line, 5 times                             
    for {set i 0} {< $i 5} {++ i}  {                                            
       console scroll 26 3  50 4                                                
       sleep 80                                                                 
    }                                                                           

                                                                                
    # Smooth scroll an area up by 1 line, 5 times                               
    for {set i 0} {< $i 5} {++ i}  {                                            
       console scroll 26 3  50 4 -1                                             
       sleep 80                                                                 
    }                                                                           

See also: screen, screen capture, CLI, box, gotoxy, console colours

Console Screen Capture and Restore

A console screen can be saved and restored as follows
Note that the console must not be scrolled by restoring text before
any attributes are restored

    # Save the text                                                             
    writefile console.txt [base64 [escape [console get_text]] \                 
        -encode -wrap 100]                                                      
    # Save the attributes                                                       
    writefile console_attribs.txt [console get_attributes \                     
        -wrap 100]                                                              
                                                                                
    cls                                                                         
    pause                                                                       
                                                                                
    # Restore                                                                   
    set console_text [readfile console.txt]                                     
    set console_attribs [readfile console_attribs.txt]                          
    string_to_array [base64 $console_text -decode] a                            
    # Restore the text                                                          
    loop i 0 [array size a] {                                                   
        printf $a($i)        # printf will not add CRLFs                        
    }                                                                           
    # Restore the attributes                                                    
    console set_attributes $console_attribs                                     

See also: console, console get_text, console get_attributes

Console Colours

The standard console colours can be customised. This may be necessary as 
some colours in the standard Windows console palette are not represented
well. These also may not match those interpretations on very old PC system
For example, orange appears as khaki brown for some reason on some
versions of Windows.

Example: (Set console "DarkYellow" to be orange)

Save the following file as: console-orange.reg
To load it, double click and accept the prompts

    ---------------------------------------------------------------             
    Windows Registry Editor Version 5.00                                        
                                                                                
    ; Colour table with DarkYellow == Orange 0x00c0c0c0 (BGR format)            
                                                                                
    [HKEY_CURRENT_USER\Console]                                                 
    "ColorTable06"=dword:00508dfc                                               
                                                                                
    ---------------------------------------------------------------             

Note that the file may only be valid for the current session and unless the
defaults are changed the colours will revert after the next reboot

Colour changes made via the registry have no effect until a new console
window is opened

See also: console, big graph

const

    const varName value

Define a simple constant variable. You cannot assign to a constant

The use of [const] is preferable to macro #define

Const values will typically be created in global scope and can be accessed
from within procedures using the $::constname scope prefix. Or they may be
declared using upvar to create a local const instance

Constants can be deleted using [unset]

Const arrays can be set using [array set arrayname -const]

Example:

    const ONE_GIGABYTE 1073741824.0                                             
    puts $ONE_GIGABYTE                                                          
    puts [bool [is_const ONE_GIGABYTE]]                                         
    proc foo {} {                                                               
       puts "foo thinks 1Gb is: $::ONE_GIGABYTE" # :: scope operator used       
    }                                                                           
    foo                                                                         
    unset ONE_GIGABYTE -const                                                   
    puts [bool [is_set ONE_GIGABYTE]]                                           

Results:

    1073741824.0                                                                
    True                                                                        
    foo thinks 1Gb is: 1073741824.0                                             
    False                                                                       

See also: enum, set, array set

content

    string [content string left-anchor right-anchor]

Extracts and returns the portion of a string bounded by the left and right
anchor characters, not including the anchor character.

Example:

    puts [content "abc(12345)def" ( )]    # Returns '12345'                     

Note that to pass braces or whitespace characters as anchor characters you
must either enclose these in double quotes, or specify as [chr] values

    puts [content "abc{12345}def" "{" "}"]             # Returns '12345'        
    puts [content "abc{12345}def" [chr 123] [chr 125]] # Returns '12345'        

See also: string

copyfile

    integer [copyfile sourcespec target ?-y?]

Copy a file from sourcefile to targetfile. Wildcards are supported
Path targets or specified filename targets are supported

Paths accept \ or / separators. Where a trailing -y is used from the CLI
then a trailing / separator must be used to signify a path as the backslash
will be interpreted as a line-continuation escape character (\).

Where backslashes are used then take note that the Macro PreProcessor (MPP)
will require these to be escaped as double backslashes (\\) e.g. "c:\\path"

Where wildcard+forward-slash combinations are present, be sure to wrap
any filename in double-quotes to avoid /* being interpreted as a comment.

The count of successful copies is returned else 0 on failure

CAUTION: Supports only ANSI filenames, not Unicode/MBCS

Example:

    copyfile ticol.exe c:\temp\ticol.exe -y                                     
    copyfile ticol.exe c:/temp/new          Copy to target file /temp/new       
    copyfile c:\\tcl\\*.tcl c:\\temp        Existing directory                  
    copyfile c:\\tcl\\*.tcl c:/temp/new     Will fail as '/new' is a file       
    copyfile c:\\tcl\\*.tcl c:/temp/new/    Force create '/new/' directory      

See also: movefile, file

Copyright and Licensing

 ---------------------------------------------------------------------------    
  Ticol Copyright and Licence                                                   
 ---------------------------------------------------------------------------    
  Copyright (C) to M Shaw 2010-2023                                             
  All rights reserved.                                                          
  Portions Copyright (c) 2007-2016, Salvatore Sanfilippo                        
  All rights reserved.                                                          
                                                                                
  Redistribution and use in source and binary forms, with or without            
  modification, are permitted provided that the following conditions            
  are met:                                                                      
                                                                                
  1 Redistributions of source code must retain the above copyright notice,      
    this list of conditions and the following disclaimer.                       
  2 Redistributions in binary form must reproduce the above copyright           
    notice, this list of conditions and the following disclaimer in the         
    documentation and/or other materials provided with the distribution         
  3 This product and its associated documentation may not be sold or            
    resold for profit                                                           
  4 This licence does not extend to the Ticol Tcl manual                        
    The Ticol Tcl manual, associated documentation and sample scripts           
    are not included in this licence but are covered separately by the          
    standard international author copyright for literary works                  
      Copyright (C) M Shaw (2015-2023)                                          
      All rights are reserved                                                   
                                                                                
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS           
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED     
  TO,THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR     
  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR              
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,         
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,           
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR            
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF        
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING          
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS            
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                  
                                                                                
---------------------------------------------------------------------------     

 Licence Summary
 ---------------
 As with the Info-ZIP licence, this modified BSD licence has additional
 clauses added to protect the author:

   Clause 3 is to avoid the author having to encounter the downstream
   cost of commercial support

   Clause 4 is to ensure that the literary rights to the documentation
   are fully protected to ensure that it is not edited, repackaged or
   sold by a 3rd party

 A modified licence may be issues on request and only by agreement with
 the author

 This project was initially derived from a micro-project called "Picol",
 by Salvatore  Antirez Sanfilippo, which implemented a Tcl interpreter
 in 500 lines. 

 This project is closed source and the source code is not distributed
 There is no obligation under the BSD licence to distribute source code

 The original 500-line Picol source is freely available from
 http://oldblog.antirez.com/post/picol.html

 This documentation (manual), in whatever format distributed, is an
 original creative work by the author, It is licensed separately from
 the program binaries and protected by standard international literary
 copyright. It may be distributed with the program free of charge,
 but may not be sold, resold, modified or incorporated into other works
   Copyright (C) to M Shaw (2015-2023)
   All rights reserved

 To the best of the author's knowledge, no GPL code is used in this project
 
 Portions of certain plugin code may contain code attributable to other
 authors including but not limited to the following code: 
 For more information, see any accompanying readme.txt for a full list

  ticol_zip plugin, portions
   Copyright (c) 1990-1999 Info-ZIP.  All rights reserved
   The plugin library is not supplied by Info-ZIP

  ticol_sqlite plugin, portions
   Copyright attributed to SQLite (Public Domain)

 Acknowledgements are made for the following code

  MD5 Algorithm: RSA Data Security, Inc. MD5 Message-Digest Algorithm
   Copyright (C) 1991-2, RSA Data Security, Inc. 1991. 
   All rights reserved

  Mersenne Twister Random Number Generator
   Copyright(C) 1997-2002, Makoto Matsumoto, Takuji Nishimura

  TCP/IP specific extensions in Windows Sockets 2
   Portions Copyright(C) 1980,1983,1988,1993 
   The Regents of the University of California

 The Picol project source code has been replaced with around 100,000
 lines of C++ code. This source code is not available but the program
 binaries will be made available for both private and commercial use as
 freeware. Should any disputes arise the author reserves the right to
 withdraw the Ticol program from further public distribution

 Please read the LICENCE.TXT or README.TXT file for full licence terms

 In summary:

 No warranty of fitness for use or any other type of warranty is offered.
 By using Ticol you agree to use it at your own risk and indemnify the
 author against all liabilities arising as per the licence agreement. 
 Ticol must not be used in a commercial environment without adequate and
 thorough testing

  You may freely use Ticol whether for private or commercial use
  You may give individual copies of Ticol to friends or colleagues
  You may not sell Ticol or any accompanying documentation
  You have no right whatsoever to technical support or fixes
  You agree to satisfy yourself of any fitness for use regarding Ticol
  You accept this as software offered free of charge and in good faith
  You agree that this software is not warrantied in terms of fitness for
    any purpose whatsoever
  You accept that this is a personal/hobby/educational project and you hold
    no commercial or professional expectations of it either in terms of 
    reliability, fitness for use, performance or levels of support
  You agree to use Ticol entirely at your own risk and indemnify the author
    from any and all liabilities arising from its use
  You agree not to use this product in any mission critical systems where
    life or financial loss is at risk
  You must include the BSD copyright notice/licence terms with any copies
    you pass on to a 3rd party

See: Ticol, why, frequently asked questions

ctime (Unix time)

C or "Unix" time is defined as a signed integer representing the number
of seconds elapsed since 00:00:00 hours on January the 1st 1970

Dates prior to this cannot be represented
Dates after 03:14:07 UTC on 19 January 2038 cannot be represented

The Visual BASIC method is to use a double value to represent time

Example:

    puts [clock seconds]                                                        

Result:

    1449684929                                                                  

See:
    https://en.wikipedia.org/wiki/Year_2038_problem

Convert to a string format using the [clock format] command

See also: vbtime, date_to_ctime, double_to_ctime, ctime_to_double, 
          clock, time, now

ctime_to_double, double_to_ctime
 
    double  [ctime_to_double seconds]
    integer [double_to_ctime double]

[ctime_to_double] converts the standard internal Tcl "C" time format from
seconds in the Unix epoch of 01/Jan/1970 to an industry-standard or Visual
BASIC double value.  [double_to_ctime] performs the inverse operation

Example:

    puts [clock format [double_to_ctime [now]] -format "%Y/%m/%d %H:%M:"        

Example:

    puts [double_to_ctime [now]]                                                
    puts [clock seconds]                                                        

Result:

    1449684929                                                                  
    1449684930                                                                  

See also: date, time, is_date ,date_to_ctime, clock, clock format, now, 
          ctime

cube, cuberoot

[cube] and [cuberoot] are not implmented internally but can easly be 
implemented as Tcl procedures. The following examples may be used.

Note that when dealing with double/float precision numbers, accuracy 
can be an issue and rounding may be required (due to binary to decimal)
conversion.

Prefix notation is slightly faster than using [expr] for math.

proc cube {x} {                                                                 
        return [round [* $x [* $x $x]] 5]                                       
}                                                                               

                                                                                
proc cuberoot {x} {                                                             
        set start 0                                                             
        set end $x                                                              
        set mid [/ [+ $start $end] 2.0]                                         
        while {!= [round [* $mid [* $mid $mid]] 4] $x} {                        
                set mid [/ [+ $start $end] 2.0]                                 
                if {< [* $mid [* $mid $mid]] $x} {                              
                        set start $mid                                          
                } elseif {> [* $mid [* $mid $mid]] $x} {                        
                        set end $mid                                            
                } else {                                                        
                        return [int [round $mid 5]]                             
                }                                                               
        }                                                                       
        return [round $mid 5]                                                   
}                                                                               

See also: math functions

VB (Visual BASIC) Time

Visual BASIC stores Date values as an IEEE 64-bit (8-byte) floating point
number, identical to a VB Double. Digits to the left of the decimal point,
when converted to decimal, are interpreted as a date between 1 January 100
and 31 December 9999. 

Values to the right of the decimal indicate a time between 0:00:00 and 
23:59:59 and is significant to 5 decimal places at 1 second resolution

The date section of the Date data type, is a count of the number of days 
that have passed since 1 Jan 100, offset by 657434. That is, 1 Jan 100 is 
denoted by a value of -657434; 2 Jan 100 is denoted -657433; 31 Dec 1899 is
1; 1 Jan 2000 is 36526 etc.

The time section of the date (after the decimal point) is the fraction of a
day, expressed as a time. For example, 1.5 indicates the date 31 Dec 1899 
and half a day, i.e. 12:00:00. So, an hour is denoted by an additional 
4.16666666666667E-02, a minute by 6.94444444444444E-04, and a second 
by 1.15740740740741E-05

The Ticol [date], [dateserial] and [time] commands understand Visual BASIC
format doubles representing date values

See also: date, dateserial, time, ctime, now, date_to_ctime

is_*

See:
                                                                                
    is_active procname    # Test if a proc is currently active                  
    is_admin              # Test if user has administrative rights              
    is_array              # Test if a variable name is an array                 
    is_command string     # Is a string a registered command                    
    is_const string       # Is a string a constant variable                     
    is_date string        # Is a string a valid formatted date or time          
    is_dir path           # Is a string a valid directory                       
    is_dst                # Test if we are in Daylight Savings Time             
    is_elevated           # Test if we are running with elevated rights         
    is_empty var          # Test if a variable is both set and empty            
    is_file name          # Test a file                                         
    is_leapyear string    # Is a date value a leap year                         
    is_list string        # Test if a string is a well-formed Tcl list          
    is_mod x              # Optimised modular division test                     
    is_numeric x          # Is a string a valid number                          
    is_pointer x          # Is a variable a pointer (struct member)             
    is_proc name          # Test if a string is a defined proc                  
    is_set var            # Test for a valid/existing variable                  
    array exists a        # Tests if an array exists                            
    array is_set a        # Tests if an array element is set                    
                                                                                
    # C++ Ctype Emulation    (Applies to whole strings)                         
    is_alpha              # Test if all A..Z or a..z                            
    is_alnum              # Test if all A..Z or a..z or 0..9                    
    is_digit              # Test if all 0..9                                    
    is_lower              # Test if all chars are lower case alpha              
    is_punct              # Test if all chars are punctuators                   
    is_space              # Test if all chars are space or tab                  
    is_upper              # Test if all chars are upper case alpha              
    is_xdigit             # Test if all chars are A..F, a..f or 0..9            

All the above commands return type "bool" (0 | 1)

See also: array exists, array is_set

is_array

    bool [is_array varname]

Test if a variable name is an array variable

    dim a 10             # Empty array                                          
    set b Hello          # Simple variable                                      
    array c {a 10}       # Array                                                
    struct set d {a 10}  # Struct                                               
    puts [is_array a]                                                           
    puts [is_array b]                                                           
    puts [is_array c]                                                           
    puts [is_array d]                                                           
    puts [is_array e]    # e does not exist                                     

Results:

    1                                                                           
    0                                                                           
    1                                                                           
    0                                                                           
    0                                                                           

See also: is_*, is_set, array exists, varname

is_active

    integer [is_active procname]

Test to see if a proc or a proc's parent proc is active
[is_active] returns the following values

    0   If neither are active
    1   If the current proc is active
    2   If the current proc and a proc's parent is active

The integer return from [is_active] may be interpreted as a boolean in
most situations. [is_active] may be used to test before calling [undef]
or [rename] safely

Example:

    proc foo {} {                                                               
       puts [is_active foo]                                                     
    }                                                                           
    foo                                                                         

Result:

    1                                                                           

See also: proc, rename, undef, is_*

is_dst

    bool [is_dst ?-bias? ?-offset? ?-standardname? ?-daylightname?]

[is_dst] returns a boolean (1 or 0) indicating whether we are currently
in Daylight Savings Time. The actual time offset is not returned

The '-bias' argument will return the local geographic time zone bias from
UTC (GMT) in minutes. 

The '-offset' argument will return the local Daylight-Savings-Time (DST)
offset in minutes

The '-name' argument will return the name of the time zone

Example:

    # US Central time (-6hrs) will return -360 (minutes)                        
    puts [is_dst -offset]                                                       

Result:

    -360                                                                        

Example:

    # UK summer time (+1hr) will return 60 (minutes)                            
    puts [is_dst -offset]                                                       

Result:

    60                                                                          

Example:

    puts [is_dst -standardname]                                                 

Result:

    GMT Standard Time                                                           

Example:

    puts [is_dst -daylightname]                                                 

Result:

    GMT Daylight Time                                                           

See also: is_*, time, date, clock, ctime_to_double, double_to_ctime

is_date

    bool [is_date double]
    bool [is_date date|time]
    bool [is_date date]
    bool [is_date time]
    bool [is_date "date time"]
    bool [is_date "time date"]

Performs basic sanity checks in a date-interpreted double value or string(s)

Examples:

    puts [is_date 0]                        # True                              
    puts [is_date -1]                       # True                              
    puts [is_date 42657.0430902778]         # True                              
    puts [is_date 2016/Oct/14]              # True                              
    puts [is_date 2016/10/14]               # True                              
    puts [is_date 00:00:59]                 # True                              
    puts [is_date 2016/10/14 01:02:03]      # True                              
    puts [is_date 01:02:03 2016/10/14]      # True                              
    puts [is_date "2016/10/14 01:02:03"]    # True                              
    puts [is_date {2016/10/14 01:02:03}]    # True                              
    puts [is_date 9999999.0430902778]       # False (out of range)              
    puts [is_date "00:00:59 2016/oct/39"]   # False (invalid day)               

See also: is_*, date, dateserial, vbtime

is_file

    bool [is_file filename]

Currently checks for the existence of a file and returns a boolean value
indicating whether the file exists or not

Examples:

    puts [is_file ticol.exe]                                                    
    puts [is_file nosuch.txt]                                                   

Results:

    1                                                                           
    0                                                                           

See also: file, is_*

is_leapyear

    bool [is_leapyear year]

Confirms whether a year value is a leapyear or not. 

Valid from 1 AD to 9999 AD

Returns a boolean (integer) 1 or 0

The date of the 1582 Pope Gregory declaration is used as the calculation
threshold

Common algorithms which use leap years to calculate are not valid for
years in the Julian calendar before 1752 in the British Empire and US.
The year 1700 was a leap year in the Julian calendar, but not in the
Gregorian calendar.

Example:

    puts [is_leapyear 0001]                                                     
    puts [is_leapyear 0004]     # Is a leap year                                
    puts [is_leapyear 4]        # Is a leap year                                
    puts [is_leapyear 1896]     # Is a leap year                                
    puts [is_leapyear 2001]                                                     
    puts [is_leapyear 2012]     # Is a leap year                                
    puts [is_leapyear 9996]     # Is a leap year                                
    puts [is_leapyear 9999]                                                     

Results:

    0                                                                           
    1                                                                           
    1                                                                           
    1                                                                           
    0                                                                           
    1                                                                           
    1                                                                           
    0                                                                           

See also: is_date, is_*

is_list

    bool [is_list string]

Test if a string is a valid Tcl list

Example:

    puts [is_list {}]                                                           
    puts [is_list ""]                                                           
    puts [is_list a]                                                            
    puts [is_list {a b]                                                         

Results:

    0                                                                           
    0                                                                           
    1                                                                           
    1                                                                           

See also: is_*, list, lindex, lcount

is_static

    bool [is_static varname]

Tests if a given variable is a protected static variable
Static variables are protected from deletion using [unset] and require the
use of [static varname -unset] to remove

Static variables are intended to persist for the life of a script but there
may be certain circumstances where a variable needs to be tested for and 
selectively removed

See also: static, is_*

date

 date ?-separator char?         # Return the current date as YYYY-MM-DD         
                                # Use [date -separator {}] for YYYYMMDD         
 date -year                     # Return the current year as YYYY               
 date -month                    # Return the current month as M                 
 date -day                      # Return the current day as D                   
 date -yday                     # Return the current day of the year as D       
 date double ?-separator char?  # Returns YYYY-MM-DD + optional separator       
 date double ?-list?            # Return each field as a Tcl list               
 date double ?-notime?          # Suppress display of time value                
 time double                    # Use [time] to suppress date value             
 date double ?-longmonth?       # Display 3-char month names e.g. "Jan"         
 date double -format <string>   # Return specific date using PHP format         
 date now                       # Returns current date as a double value        
 date now -format <string>      # Return current date using PHP format          
 date YYYY/MM/DD  ?hh:mm??:ss?  # Returns a double representing the date        
 date YYYY-MM-DD  ?hh:mm??:ss?  # Do                                            
 date YYYY/MMM/DD ?hh:mm??:ss?  # Do                                            
 date YYYY-MMM-DD ?hh:mm??:ss?  # Do                                            
 date DD/MM/YYYY  ?hh:mm??:ss?  # Do                                            
 date DD-MM-YYYY  ?hh:mm??:ss?  # Do                                            
 date DD-MMM-YYYY ?hh:mm??:ss?  # Do                                            
 date DD-MMM-YYYY ?hh:mm??:ss?  # Do                                            
 date <date-string> -ctime      # Return the time as a C-long integer           
 dateserial YYYY MM DD hh mm ss # Use [dateserial] for dates as Tcl lists       

Shows the current system date in ISO YYYY-MM-DD format
ISO date format is preferred
Returns a VB6-compatible date value as double
Or, returns a formatted date/time string

Date Ranges

Numeric inputs range from -693958.000000 (0000/01/01 AD) to 
2958465.000000 (9999/12/31 AD). Units are in days and fractions of
a day as per Visual BASIC 6.0

US Date Formats

The US date formats of MM-DD-YYYY and MM-DDD-YYYY are not supported

Comma Date Formats

The date format Jan, 1, 2016 and similar formats are not supported

Input Locales

Automatic/Windows system input locales are not supported

The epoch starts the same as for VB6 at 30/Dec/1899 as value 0.0
Negative values extend as far back as January, 1st 0 AD

1/1/70 00:00:00 (Unix epoch in VB) is 25569.0 as a double
February 5, 2036 (Unix epoch end) is 49711.00001157407 
(00:00:01 6th Feb 2036)

Valid Range

Dates are valid in the range from 0 AD to 9999/12/31 AD 
Dates older than the positive VB6 date epoch are negative

Year values must be 4-digit. Leading zeroes are required if a date is 
< 4 digits. e.g. 325 AD will be year "0325" not "325"

Values outside the of range 0/00/00 to 9999/12/31 (-693959.0 to 2958465.0)
will raise an error

Similarly: puts [date [time [date now] -ctime]] is invalid because
[date] cannot take a C time value as an argument. In such cases we would
use [ctime_to_double] to convert the result from [time]:

     puts [date [ctime_to_double [time [date now] -ctime]]]                     

Short Year Values

Short year values such as 99 for 1999 are not supported
4-digit years are mandatory otherwise 0.0 will be returned

Date Arithmetic

Since date values are "real" (floating point double) values they may be
added, subtracted, multiplied or divided with caution and providing the
number range is adhered to when reconverting back to date format using 
[date]

Year/month/day values can be compared with now by using [int] to 
strip off the fractional (time) part of the number

Example:

    if {[eq [int [date now]] [date $year-$month-$day]]} {...}                   

Exercise caution when adding dates on or before the zero epoch date of
30/dec/1899 as time values will wrap-around. You may need to use [fpart]
or [fraction] to split-off the fractional part of a date, save it and
then re-add after arithmetic. This is because dates < 30/dec/1899 are 
represented negative floating point and changing the sign may invert
the fractional part

When manipulating for whole days you will usually need to strip off the 
fraction parts of double values to avoid rounding up with 1 day error
You may use [fpart] to strip-off and save the decimal fraction part

Example:

    # Increment year from 325 AD to 1776 AD, keeping the time                   
    # An alternate method is to return a list and increment item 0              
    set d [date 0325/jan/01 09:30:00]          # Save full date                 
    set t [fpart $d]                           # Split off and save time        
    set i [integral $d]                        # Split and save the date        
    puts "Stored: $d [date $d] and time as $t" # Display saved values           
    set r [+ [* 1452 365] $i]                  # Add 1452 365 day years         
    puts "Result: '${r}.$t' -> [date ${r}.$t]" # Concatenate and display        

Result:

    Stored: -575255.3958333334 0325/01/01 09:30:00 and time as 3958333333       
    Result: '-45275.3958333333' -> 1776/01/01 09:30:00                          

Example:

    # Date subtraction in epoch range > 0.0 (30/Dec/1899)                       
    # Days to 2019/01/10                                                        
    puts "[int [- [date 2019/01/10] [date now]]] days"                          
    # Days to Xmas 2016                                                         
    puts "[int [- [date 2016/12/25] [date now]]] days"                          

Results:

    813 days                                                                    
    67 days                                                                     

Example:

    # Add 8.5 years to today and display in YYYY/MMM/DD format                  
    puts [date [+ [date now] [* 8.5 365.25]] -longmonth]                        

Time Arithmetic
---------------
Time values should be first converted using [date]. Next, any arithmetic
should be performed, and the result converted back using [time]

    puts [date 11:58]                                                           
    puts [date 5:30]                                                            
    # Add 5.5 hours to 11:58                                                    
    puts [time [+ [date 11:58] [date 5:30]]]                                    
    # Same action using [now]                                                   
    puts [time [+ [now] [date 5:30]]]                                           
    # Same action, giving a full date                                           
    puts [date [+ [now] [date 5:30]]]                                           

Results:

    0.4986111111                                                                
    0.2291666667                                                                
    17:28:00                                                                    
    17:29:58                                                                    
    2017/04/14 17:30:58                                                         

PHP Date Formats
----------------
The [date now -format <string>] and [date <value> -format <string] options
permit the construction of date strings using PHP style character tags
such as "r" or "H:i:s". There is a maximum output string length of 
64 characters, any excess will be truncated. Any unhandled characters will
be passed as literals e.g. the colon ":" or hyphen "-" characters

All tags other than U and b are implemented

Example:

    puts [date now -format l]                                                   
    puts [date [+ [date now] 2.5] -format r]                                    
    puts [date $dateval -format "D, jS F Y H:i:s"]                              

Results

    Thursday                                                                    
    Wed, 20 Oct 2021 03:37:04 +0100                                             
    Sun, 17 Oct 2021 15:37:24 +0100                                             

Exception Errors
----------------
Accessing a date value outside the valid range will trigger an exception
error. Other date errors will return 0.0

Notes - Converting from 1 second interval cdates to decimal day fractions

Multiply each second by 0.000011574074074 or [/ 1 86400.0]

Example:

    # 1 second past the Unix epoch end                                          
    puts [+ 49711 [/ 1 86400.0]]                                                
    # 1 second past the Unix epoch end                                          
    puts [date [+ 49711 [/ 1 86400.0]]]                                         

Result:

    49711.000011574077                                                          
    2036/02/08 00:00:01                                                         

Visual BASIC Anomalies
----------------------
In order to accommodate two digit year values, VB5 interprets 
Cdbl(Fix(Cdate("0000/jan/1"))) as 36892.0; which is 01/01/2000

Dates with a year of < 100 AD are interpreted as being in the year 2000
Ticol does no such 2-digit year interpretation adjustment. Post Y2K there 
is now no valid reason to use two-digit year dates within scripts. 
4-digit years are mandatory in Ticol for all dates after 999 AD

More Examples                           Result

puts [date]                             2016-10-18         # ISO date           
puts [date -year]                       2016               # Current year       
puts [date -month]                      10                 # Current month      
puts [date -day]                        18                 # Current day        
puts [date -yday]                       ?                  # Day of year        
puts [date now]                         42661.785891       # "now" keyword      
puts [date [date now]]                  2016/10/18 18:51:41                     
puts [date 0]                           1899/12/30         # Same as VB6        
puts [date 1]                           1899/12/31         # Same as VB6        
puts [date 2]                           1900/01/01         # Same as VB6        
puts [date 0000/1/1]                    -693958            # VB6=01/01/2000     
puts [date -693958.000000]              0000/01/01         # Jan, 1st 0 AD      
puts [date 0001/1/1]                    -693593            # VB6=01/01/2001     
puts [date -693593.000000]              0001/01/01         # Jan, 1st 1 AD      
puts [date 42661.785891]                2016/10/18 18:51:40                     
puts [date 42661.785891 -separator -]   2016-10-18 18:51:40                     
puts [date 42661.785891 -separator " "] 2016 10 18 18:51:40                     
puts [date 42661.785891 -separator _]   2016_10_18 18:51:40                     
puts [date 42661.785891 -longmonth]     2016/Oct/18 18:51:41                    
puts [date 2016-10-18]                  42661              # YYYY-MM-DD         
puts [date 2016/10/18]                  42661              # YYYY/MM/DD         
puts [date 18-10-2016]                  42661              # DD-MM-YYYY         
puts [date 18/10/2016]                  42661              # DD/MM/YYYY         
puts [date 0325/01/01]                  -575255            # -ve (325 AD)       
puts [date 1400/01/10]                  -182610            # Same as VB6        
puts [date 1700/01/10]                  -73037             # Same as VB6        
puts [date 1970/01/01]                  25569              # Unix epoch         
puts [date 2036/02/06]                  49712              # Unix epoch end     
puts [date 9999/02/06]                  2958101            # Same as VB6        
puts [date 2016-10/18]                  0.000000           # ERROR              
puts [dateserial 2016 10 18 18 51 40]   42661.785880       # For lists          
puts [date 2016/10/18 18:51:40]         42661.785880       # 2 arguments        
puts [date 18:51:40 2016/10/18]         42661.785880       # Reverse is OK      
puts [date "2016/10/18 18:51:40"]       42661.785880       # Quoted is OK       
puts [date {2016/10/18 18:51:40}]       42661.785880       # Braced is OK       
puts [date {18:51:40 2016/10/18}]       42661.785880       # Reverse is OK      

See also: time, dateserial, is_date, ticks, clock, ctime, vbtime, 
          date_to_ctime, fpart, clock scan workaround, clock scan,
          day_of_week, format

dateserial

    double [dateserial ?year|-? ?month|-? ?day|-? ?hour|-? ?minute|-?\
            ?second|-?]
    double [dateserial ?tcl-list?]

Calculates a date value and returns a double from individual arguments
Each arguments is optional with the defaults being 1 1 1 0 0 0 
(1st January, 1 AD 00:00:00). Optional arguments are omitted by specifying
a hyphen character '-', using empty "" or {} arguments

[dateserial] may also be called with these items as a space-separated
Tcl list, as produced by [date double -list]

All arguments must be integer values. Values must be given in order but
may be omitted with a '-' used as a placeholder

Example:

    puts [dateserial - - - 00 05 36]                                            
    puts [dateserial "" "" "" 00 05 36]                                         
    puts [dateserial {} {} {} 00 05 36]                                         
    puts [date [dateserial - - - 00 05 36]]                                     

Result:

    -693958.00388888887                                                         
    -693958.00388888887                                                         
    -693958.00388888887                                                         
    0000/01/01 00:05:36                                                         

Example:

    puts [dateserial 1899 12 30 0 0 0]                                          
    puts [date [dateserial 1899 12 30 0 0 0]]                                   

Result:

    0.000000000000000                                                           
    1899/12/30 00:00:00                                                         

Example:

    puts [date now]                                  # Returns a double         
    puts [date [date now] -list]                     # Returns date as list     
    puts [dateserial [date [date now] -list]]        # Returns a double         
    puts [date [dateserial [date [date now] -list]]] # Returns full date        

Result:

    42663.971261574072                                                          
    2016 10 20 23 18 37                                                         
    42663.971261574072                                                          
    2016/10/20 23:18:37                                                         

Example:

    puts [fraction [dateserial - - - 2 4 6]]         # Extracts .nnnn           
    puts [time [fraction [dateserial - - - 2 4 6]]]  # Extracts .nnnn           

Result:

    -0.086180555517785                                                          
    02:04:06                                                                    

See also: date, time, vbtime, format

date_to_ctime

    integer [date_to_ctime year ?month? ?day? ?hour? ?minute? ?second?]

Convert string-formatted date values into a C/Unix time value as a number
of seconds since January 1st 1970. Passing a date value earlier than this
will raise an error.

The inverse command is [ctime_to_double]

Example:

    puts [date_to_ctime 1972 apr 04]     # Flying Scotsman Day                  

Result:

    71193600                                                                    

Example:

    puts [date [ctime_to_double [date_to_ctime 1972 04 04]]]                    
    puts [clock format [date_to_ctime 1972 04 04] -format %D%N%y]               

Result:

    1972/04/04 00:00:00                                                         
    4472                                                                        

See also: ctime_to_double, ctime, time, date, dateserial, vbtime

Data Type Ranges
 
The following data type ranges may be coerced using data type casts

The internal storage format for numbers is string. Calculations are
performed on integers by converting to 64-bit values and real numbers by
converting to 64-bit (8 byte) doubles. However, not all routines will
handle such values. Some which link to the underlying C runtime 
may be limited to 32 bit signed long values.

By default, signed values are used internally. Some commands such as 
bitwise operations [bit] may use unsigned values. See the documentation
for each command

Type casts may  be forced using various cast commands (See: variable 
casting)

 Type Bits Sign     Example Comma-Range                                         
 --------------------------------------------------------------------------     
 char    8 signed   -128 to 127                                                 
 char    8 unsigned  0 to 255                                                   
 short  16 signed   -32,768 to 32,767                                           
 short  16 unsigned  0 to 65535                                                 
 long   32 signed   -2,147,483,648 to 2,147,483,647                             
 long   32 unsigned  0 to 4294967295                                            
 int    64 signed   -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807     
 int    64 unsigned  0 to 18,446,744,073,709,551,615                            
 double 64 signed    1.7E +/- 308 (15 digits of precision)                      
                                                                                
                    Example Integer Range                                       
                    -------------------------------------------------------     
 char               -128 to 127                                                 
 unsigned char       0 to 255                                                   
 short              -32768 to 32767                                             
 unsigned short      0 to 65535                                                 
 long               -2147483648 to 2147483647                                   
 unsigned long       0 to 4294967295                                            
 int                -9223372036854775808 to 9223372036854775807                 
 int unsigned        0 to 18446744073709551615                                  
 double              1.7E +/- 308 (15 digits of precision)                      

Note that [scan] with %d, %ld etc. will not truncate excess range values
down to handled ranges

Command and Function Argument Limits
------------------------------------
Although the default internal integer range is 64-bit signed integer, most
commands are based on code exported from the underlying C library and are 
thus limited to signed 32 bit values unless otherwise stated

For example, the [rnd] command takes two signed long values. Calling with a
Ticol 64-bit integer outside this range will return an invalid value.

Useful Definitions
------------------
                                                                                
    # Refer within source without a $ prefix e.g. CHAR_MIN                      
    # The MPP will detect within substrings e.g. MYCHAR_MIN                     
    # const or enum is preferable                                               
    #define CHAR_MIN  -128                                                      
    #define CHAR_MAX   127                                                      
    #define UCHAR_MIN  0                                                        
    #define UCHAR_MAX  255                                                      
    #define SHORT_MIN  -32768                                                   
    #define SHORT_MAX   32767                                                   
    #define USHORT_MIN  0                                                       
    #define USHORT_MAX  65535                                                   
    #define LONG_MIN  -2147483648                                               
    #define LONG_MAX   2147483647                                               
    #define ULONG_MIN  0                                                        
    #define ULONG_MAX  4294967295                                               
    #define INT_MIN   -9223372036854775808                                      
    #define INT_MAX    9223372036854775807                                      
    #define UNSIGNED_MIN 0                                                      
    #define UNSIGNED_MAX 18446744073709551615                                   

or

    # Refer within source code using a $ prefix e.g. $CHAR_MIN                  
    # Detection and parsing is more precise but slower                          
    # const or enum is preferable to #define                                    
    const CHAR_MIN  -128                                                        
    const CHAR_MAX   127                                                        
    const UCHAR_MIN  0                                                          
    const UCHAR_MAX  255                                                        
    const SHORT_MIN  -32768                                                     
    const SHORT_MAX   32767                                                     
    const USHORT_MIN  0                                                         
    const USHORT_MAX  65535                                                     
    const LONG_MIN  -2147483648                                                 
    const LONG_MAX   2147483647                                                 
    const ULONG_MIN  0                                                          
    const ULONG_MAX  4294967295                                                 
    const INT_MIN   -9223372036854775808                                        
    const INT_MAX    9223372036854775807                                        
    const UNSIGNED_MIN 0                                                        
    const UNSIGNED_MAX 18446744073709551615                                     

Within the C language these values are defined within file "limits.h"

See also: variable casting, cast, numeric types, vbtime

Data Structures (Abstract Data Types)

Ticol offers the following data structures and ADTs

    strings                     natively                                        
    buffered strings            natively                                        
    integers                    as string or 64-bit internal representation     
    double/floats/real          as string or internal double representation     
    binary values               as string                                       
    hexadecimal values          as string                                       
    octal values                as string                                       
    big integers                plugin                                          
    Roman numbers               plugin                                          
    compressed numbers          natively via mkn, cvn                           
    Scientific numbers          native and plugin                               
    associative arrays          natively                                        
    lists                       as string                                       
    binary lists                plugin                                          
    stacks                      natively                                        
    structs                     natively                                        
    dictionaries                <feature incomplete>                            
    line addressed strings      plugin                                          
    date/time                   natively including VB-like dates and C dates    
    dynamic memory blocks       natively                                        
    xbase/dbase databases       plugin                                          
    Ticol databases             plugin and similar to DBF format                
    sqlite databases            plugin                                          
    C-like arrays (1, 2 or 3D)  plugin with command (v's $) addressing          
    Variant array               plugin for DLL interfacing                      

Memory block access is available via malloc/free which (in theory at least)
could offer the ability to create other complex ADTs.

Ticol does not offer ...

    bit-limited types - char, short etc. (all integers are 64-bit)              
    complex or nested binary structs                                            
    classes (other than very simple emulation)                                  
    objects/inheritance                                                         
    trees                                                                       
    counted/binary strings (other than in a very limited manner)                

See also: data type ranges

day_of_week

    integer [day_of_week day month year]

Returns the day of the week for a given day in a given month and for a
given year. All arguments are required. Valid for range 1000 AD to 9999 AD

The values are all 'base 1' e.g. day 1..31, month 1..12, year 1..2017+
The return value is 'base 0' index where 0==Sunday, 1==Monday etc...

[day_of_week] can be used to construct a Gregorian calendar

Calculations are based on the introduction of the Gregorian calendar in
1582 not 1752

Common algorithms which use leap years to calculate are not valid for
years in the Julian calendar before 1752 in the British Empire and US.
The year 1700 was a leap year in the Julian calendar, but not in the
Gregorian calendar.

Example:

    # 23rd December 2017 is a Saturday (day 6)                                  
    puts [day_of_week 23 12 2017]                                               
    puts [day_of_week 4 4 1972]                                                 
    puts [day_of_week 21 2 1804]                                                

Result:

    6     # Saturday                                                            
    2     # Flying Scotsman Day was a Tuesday                                   
    2     # The first steam locomotive ran on a Tuesday                         

[day_of_week] can be used to create an extended-range Gregorian 
calendar. See calendar.tcl example file

Example:

    JANUARY                1172                                                 
    Sun Mon Tue Wed Thu Fri Sat                                                 
                             1                                                  
     2   3   4   5   6   7   8                                                  
     9  10  11  12  13  14  15                                                  
    16  17  18  19  20  21  22                                                  
    23  24  25  26  27  28  29                                                  
    30  31                                                                      

                                                                                
    # 1st January is a Saturday (Gregorian)                                     
    # Validation Reference:                                                     
    # https://moodle.lse.ac.uk/calendar/view.php?\                              
       view=month&course=1&time=-25182489600                                    

                                                                                
    JANUARY                14001                                                
    Sun Mon Tue Wed Thu Fri Sat                                                 
         1   2   3   4   5   6                                                  
     7   8   9  10  11  12  13                                                  
    14  15  16  17  18  19  20                                                  
    21  22  23  24  25  26  27                                                  
    28  29  30  31                                                              

                                                                                
    # Validation reference:                                                     
    # http://elearning2.msbm-uwi.org/calendar/view.php?\                        
       view=month&course=1&time=379661749200                                    

References:
    https://calendar.zoznam.sk/nameday-enathor.php

See also: date, time, clock

debug
 
    debug ?on|off|print? ?-quiet?

Turn on/off debug tracing or view the current setting or print to the
console.

[debug] with no arguments issues a verbose status unless -quiet is used.
In all cases a boolean 1 or 0 is returned indicating the current debug
status 

[debug -quiet] can be used to programmatically detect debugging status
and act accordingly

    if { [debug -quiet] } {                                                     
       debug off                                                                
    }                                                                           

[debug print] or [debug print string] is a command which prints to the
console unless a ...

    #define ndebug                                                              

    statement is issued. In which case all [debug print] statements
are disabled in the script, regardless of where the #define occurs

Because this is an interpreter, not a compiler and because the MPP runs
before runtime interpretation the '#define ndebug' statement can appear
anywhere and will have an effect on the whole file

If selective output is required then do not define 'ndebug', instead
define a different macro variable and wrap statements in #ifdef 

Example:

    debug print "Hello from debug"                                              

Result:

    Hello from debug                                                            

Example:

    debug print "This statement will not appear"                                
    #define ndebug                                                              
    debug print "Nor will this"                                                 

Result:

    <no output>                                                                 

Example:

    #define debug_print                                                         
                                                                                
    #ifdef debug_print                                                          
    debug print "I will appear"                                                 
    #else                                                                       
    debug print "I won't appear"                                                
    #endif                                                                      

Result:

    I will appear                                                               

See also: trace, debugging, breakpoints, halt, time, debugging, commands

Useful Debug Routines

Two useful routines are offered to aid testing and debugging, [check] and
[checkex]. [checkex] is an extended form of [check] which allows for more
complex expressions in the test rather than just simple equivalence

[assert] offers a built in alternative

    # Use:  set line #__LINE__ at parent level                                  
    if {! [defined checkex] } {                                                 
       proc checkex {x expr} {                                                  
          upvar line                                                            
          set NAN "-1.[calc [chr 35]]IND00000000000"                            
          set q {$x}                                                            
          puts "Line $line: " -nonewline                                        
          puts "'$q'=='$x': '$expr'"                                            
          if { [eval $expr] } {                                                 
             # puts "Pass"                                                      
          } else {                                                              
             textcolor red                                                      
             puts "checkex assertion failed: '$q' -> '$x' at line $line"        
             textcolor                                                          
             stop                                                               
          }                                                                     
       }                                                                        
    }                                                                           
                                                                                
    if {! [defined check] } {                                                   
       proc check {x y} {                                                       
          if {!= $x $y} {                                                       
             puts "Error in test at after line $::line"                         
             puts "'$x' != '$y'"                                                
             stop                                                               
          }                                                                     
       }                                                                        
    }                                                                           

Examples:

    check [calc "ceil(2.8)"] 3                                                  
    checkex [comma 1234567890] {eq $x "1,234,567,890"}                          

Results:

    line 3348: x:[3.000000000000000] == y:[3]                                   
    Line 3348: '$x'=='1,234,567,890': 'eq $x "1,234,567,890"'                   

See also: assert, debug, faq, debugging

default (Placeholder)

    See: proc     for default proc arguments
    See: switch   for default switch clause

defined
 
    bool [defined command|proc|function ?-command? ?-proc? ?-funct?]

Returns a boolean 1 or 0 indicating whether the given command or procedure 
is defined. The [defined] command is useful in allowing scripts to be re-
run after a stop event without encountering a 'procedure already defined'
error.

Defined procs and built-in commands (which have not been undefined) share
the same calling space.

Valid flag arguments:

 <none>    Test for either a command or defined proc
 -command  Test only for a built-in command (which has not been undefined)
 -proc     Test only for a registered proc
 -funct    Test only for a math function which can be called by [funct]

Example:

    puts [defined puts]                                                         
    puts [defined foo]                                                          

Result:

    1                                                                           
    0                                                                           

The default is to detect any command or proc but not a function as 
functions cannot be called directly and present a minimal need to be
detected

Example:

    if {! [defined ipfromhost]} {                                               
       proc ipfromhost {hostname} {                                             
          return [calldllstr ping32 GetIPFromHostName $hostname]                
       }                                                                        
    }                                                                           

Example:

    puts [bool [defined puts]]                                                  
    proc bar {} {}                                                              
    puts [bool [defined bar -proc]]                                             
    puts [bool [defined foo]]                                                   
    puts [bool [defined abs -funct]]                                            
    puts [bool [defined quux -command]]                                         

Result:

    true                                                                        
    true                                                                        
    false                                                                       
    true                                                                        
    false                                                                       

See also: undef, commands, is_set, funct, proc

dict (Dictionaries)

Simplified Tcl Dictionaries. (Incomplete Feature)

Note: Ticol dictionaries are not currently 'order-preserving'

 dict append var key ?string?        Append (concatenate) string values         
 dict clear var                      Empty a dictionary but retain type         
 dict create ?key value?...          Create dict, Reference is returned         
 dict exists value key ?key?         Test if a key exists in a dict arg         
 dict for {key val} dictValue body   Dictionary iterator                        
 dict get dictValue key ?key...?     Get a dictionary item(s)                   
 dict info                           Show dictionary information                
 dict item dictValue key ?default?   Return a single key match or default       
 dict keys dictValue ?glob?          Return a list of dictionary keys           
 dict set var key ?key...? value     Add/update a dict item by key value        
 dict lappend var key ?value?        Append items as well-formed Tcl list       
 dict map                            Iterate a dictionary against a script      
 dict merge ?dictValue?              Merge dictionary items                     
 dict remove dictValue ?key ...?     Remove items from a dictionary arg         
 dict replace dictValue ?key value?  Replace items by key search                
 dict set var key ?key...? value     Set key values in a dictionary             
 dict size                           Return the number of element pairs         
 dict unset                          Unset individual key entries               
 dict values dictValue ?glob?        Return a list of dictionary values         
 dict with var {script}              Set variables to dict key values           
 dict walk                           Diagnostic iteration of a dictionary       
 set dictVar                         Return a dictionary as a Tcl list          
 $dictVar                            Ditto                                      
 unset dictVar                       Delete a dictionary variable               

A dictionary is a series of pairs of keys and values which are may be
stored either in a Tcl list or in a dictionary object. Nested dictionaries
are permissible in the same manner as nested Tcl lists. Each nested 
dictionary level will consist of a matched key+value pair.

Dictionaries may be populated from arrays, text files or data files for
example

Dictionary string formatting follows the same Tcl list rules in that empty
items are represented as {} and strings containing whitespace are wrapped
in braces.

Dictionary data is stored internally as a hash table for fast retrieval
and can handle very large dictionaries. Dictionaries may be exchanged 
between Tcl list strings and back using standard dereference commands $
and [set] with conversion back to dictionary via [dict create] and 
[dict set]

Dereferencing a Ticol dictionary with $ returns a Tcl list string, not
another dictionary object. This means dereferencing the contents of a
dictionary can be used with other related commands such as list-processing
It also means that dereferencing is inefficient with large dictionaries
and dereferencing an entire dictionary should be avoided

If dictionary variable assigned using [set] is dereferenced using $ or
[set], the output will be a well-formed Tcl list.

Dictionary variables are deleted using [unset], not [dict unset]

Example:

    set mydict [dict create 4470 {Great Northern} 4471 "Sir Frederic Banbury"]  
    puts "Type='[type mydict]'"                                                 
                                                                                
    dict lappend mydict 4472 "Flying Scotsman"                                  
    dict lappend mydict 4473 "Solario"                                          
    dict lappend mydict 4474 "Victor Wild"                                      
    dict lappend mydict 4475 "Flying Fox"                                       
    dict lappend mydict 4476 "Royal Lancer"                                     
                                                                                
    set k [dict keys $mydict]                                                   
    foreach item [lsort $k] {                                                   
       puts [format "%-10s\t%s" $item [string trim [dict get $mydict $item] \   
       [chr 34]]]                                                               
    }                                                                           
    unset mydict                                                                

Results:

    Type='dictionary'                                                           
    4470            Great Northern                                              
    4471            Sir Frederic Banbury                                        
    4472            Flying Scotsman                                             
    4473            Solario                                                     
    4474            Victor Wild                                                 
    4475            Flying Fox                                                  
    4476            Royal Lancer                                                

See also: dict append, dict create, dict exists, dict info, dict map, 
    dict remove, dict rename, dict set, dict size, dict walk, list, 
    arrays

$dictionary, [set dictionary] (Dictionary Dereference)
 
    string $dictionaryVariable
    string [set dictionaryVariable]

Dictionary data is stored internally as a hash table for fast retrieval
and can handle very large dictionaries. Dictionaries may be exchanged 
between Tcl list strings and back using standard dereference commands $
and [set] with conversion back to dictionary via [dict create] and 
[dict set]

Dereferencing a Ticol dictionary with $ returns a Tcl list string, not
another dictionary object. This means dereferencing the contents of a
dictionary can be used with other related commands such as list-processing
It also means that dereferencing is inefficient with large dictionaries
and dereferencing an entire dictionary should be avoided

If dictionary variable assigned using [set] is dereferenced using $ or
[set], the output will be a well-formed Tcl list.

Double-dereferencing is not yet implemented for dictionaries. Note that
the dictionary variable is simply a handle to a dictionary object.

Example:

    set dict [dict create 4470 {Flying Fox} 4472 {Flying Scotsman}]             
    puts "Type dict   '[type dict]'"                                            
    puts "Type \$dict '[type $dict]'"                                           
    puts $dict                                                                  
    set a dict                                                                  
    puts [set $a]       # [set] will double-dereference ok                      
    puts $$a            # Double-dereferencing is not implemented               

Result:

    dictionary                                                                  
    string                                                                      
    4470 {Flying Fox} 4472 {Flying Scotsman}                                    
    4470 {Flying Fox} 4472 {Flying Scotsman}                                    
    <dict>                                                                      

See also: set, dict

dict append

Note: Ticol dictionaries are not currently 'order-preserving'
 
    dictionary [dict append dictionaryVariable key ?string ...?]

Append new key+value pairs to an existing dictionary as specified by
the dictionaryVariable argument. The return value is a dictionary. 

[dict append] appends the values to an existing key by string concatenation
rather than using a list operation (see [dict lappend])

[dict append] takes a dictionary variable as its argument. This 
variable need not exist. If it does not exist it will be created.

A dictionary value is returned which may be echoed using $ or [set]

Example:

    set mydict [dict create 1 The]                                              
    dict append mydict 1 " quick brown"     # 1 {The quick brown}               
    dict append mydict 1 " "                # 1 {The quick brown }              
    dict append mydict 1 fox                # 1 {The quick brown fox}           
    puts $mydict                            # 1 {The quick brown fox}           

Result:

    1 {The quick brown fox}                                                     

See also: dict, dict lappend

dict clear

    bool [dict clear dictionaryVar ?dictionaryVar? ... ?-nocomplain?]

Clears one or more dictionaries of all keys and values but retains the
data structure and type information ready for repopulation

Errors are fatal and will halt the script unless the -nocomplain argument
is used

This is not a standard Tcl command

Example:

    set mydict [dict create a b c d]                                            
    puts $mydict                                                                
    assert [type mydict] {eq $_ dictionary} -v                                  
    dict clear mydict                                                           
    assert [type mydict] {eq $_ dictionary} -v                                  
    assert $mydict {eq $_ {}} -v                                                

Result:

    assert: PASS 'eq $_ dictionary'                                             
    a b c d                                                                     
    assert: PASS 'eq $_ dictionary'                                             
    assert: PASS 'eq $_ {}'                                                     

See also: dict, dict unset, unset

dict create

Note: Ticol dictionaries are not currently 'order-preserving'

    dictionary [dict create ?key value ...?]

[dict create] takes a series of key+value pairs, supplied as a well-formed
Tcl list and returns a dictionary value. The returned dictionary may be
assigned to a variable using [set]. One or more of the value arguments may
be null '{}' if desired but null should be avoided for key values

If the set dictionary variable is dereferenced using $ the output will be
a well-formed Tcl list. Although this may show no transformation in the
data, the dictionary data is stored internally as a hash table for 
efficient retrieval

[dict create] does not take a variable name as an argument. The return
value is a dictionary list which must be set into a variable. The
resulting variable created by [set] will be of type 'dictionary'

Example:

    # This example uses the expand {*} operator to expand [split] return        
    set s {4470 {Flying Fox} 2553 Manna 2555 Centenary 60100 Spearmint}         
    set d [dict create {*}[split $s]]                                           
    puts "Type d is: [type d]"                                                  
    # Dereference dictionary and return a list                                  
    puts $d                                                                     

Result:

    Type of d is: dictionary                                                    
    4470 {Flying Fox} 2555 Centenary 2553 Manna 60100 Spearmint                 

See also: dict, dict exists, dict map

dict exists

    bool [dict exists dictionaryValue key ?key ...?]

[dict exists] returns a boolean value (1 or 0) indicating whether the key or
series of keys within a set of nested dictionaries) exists in the given 
dictionary value. This returns a true (1) value if a [dict get] on that 
series will succeed. 

Example:

    dict set a the quick brown fox jumps over the lazy dog                      
    bool [dict exists $a the]                                                   
    bool [dict exists $a The]                                                   
    bool [dict exists $a the quick]                                             
    bool [dict exists $a the quick brown fox jumps over the lazy]               
    bool [dict exists $a the quick brown fox jumps over the lazy dog]           

Results

    true                                                                        
    false                                                                       
    true                                                                        
    true                                                                        
    false                                                                       

See also: dict

dict for

    <null string> [dict for {keyVar valueVar} dictValue script]

Iterates each key+value pair within a dictionary value string, mapping
each key+value pair to the specified variable names and applying
the script with those variables for each iteration. The result of the
command is an empty (null) string.

[dict for] allows the contents of the keys and values to be changed
However, it is not recommended that keys be changed

The script cannot unset the original source dictionary which it is
currently iterating as this is passed by value, it can, however,
change the variables in the local scope of the script and in 
the dictionary which is returned

The keyVar and valueVar variables are created in the current scope and
will remain after [dict for] has exited

Example:

    dict for {key val} {a b c d e f} {                                          
       puts "$key $val"                                                         
    }                                                                           

Result;

    a b                                                                         
    c d                                                                         
    e f                                                                         

See also: dict, dict set, for, foreach

dict info

    void [dict info dictionaryVariable]

Displays diagnostic information about the underlying dictionary hash
table

    Dictionary 'mydict' at address 0x2056e20 7 item(s)                          
    7 entries in table, 100 bucket(s) (7.00% use)                               
    Buckets with  0 or more entries: 93     (Total: 0)                          
    Buckets with  1 or more entries: 7      (Total: 7)                          
    Buckets with  2 or more entries: 0      (Total: 0)                          
    Buckets with  3 or more entries: 0      (Total: 0)                          
    Buckets with  4 or more entries: 0      (Total: 0)                          
    Buckets with  5 or more entries: 0      (Total: 0)                          
    Buckets with  6 or more entries: 0      (Total: 0)                          
    Buckets with  7 or more entries: 0      (Total: 0)                          
    Buckets with  8 or more entries: 0      (Total: 0)                          
    Buckets with  9 or more entries: 0      (Total: 0)                          
    Buckets with 10 or more entries: 0      (Total: 0)                          
    Mean search distance per entry: 1.00                                        

See also: dict, dict walk, array walk

dict get

    dictionary [dict get dictionaryValue key ?key ...?]

Returns a value matching a given key or keys
Multiple keys may be supplied to search within a nested dictionary
A failure to find a match to a given key will raise an error
Use [dict exists] to check for the presence of keys before retrieving

Example (non-nested):

    # Non-nested dictionary                                                     
    set mydict [dict create 4470 {Great Northern} 4473 Solario]                 
    puts [dict get $mydict 4470]                                                
    puts [dict get $mydict 4473]                                                

Result:

    Great Northern                                                              
    Solario                                                                     

Example (nested):

    # Nested dictionary                                                         
    # Locomotive, BR number, LNER number, Name                                  
    dict set a4 60014 2509 Silver Link                                          
    dict set a4 60015 2510 Quicksilver                                          
    puts [dict get $a4 60014 2509]                                              
    puts [dict get $a4 60015 2510]                                              
    puts [dict get $a4 60103 4472]                                              

Result:

    Silver Link                                                                 
    Quicksilver                                                                 
    <error>                                                                     

See also: dict, dict item

dict item

    string [dict item dictValue key ?default? ?-nocase? ?-glob?]

Retrieve a single dictionary item from a dictionary value which matches a
single key value.

If the key is not found then an empty string will be returned unless an
alternate default value is specified, in which case this default will be
returned instead.

The default comparison will be made by case-sensitive, full-string match
unless the -nocase and/or -glob arguments are used.

-glob will treat the key value as a glob wildcard match template
-nocase will perform either a standard or glob search as case-insensitive

If a wildcard search using -glob is performed then there may be other
matching keys present in the dictionary. [item] will return the first
matching key only. Use [dict keys] or [dict values] to collect multiple
matching values

[item] is a non-standard Tcl command and it matches the behaviour of
[item], [array item] etc. as found elsewhere in Ticol 

Example:

    set mydict [dict create The quick brown fox]                                
    puts [dict item $mydict the]                 # No match                     
    puts [dict item $mydict The]                 # Direct match                 
    puts [dict item $mydict the -nocase]         # Case-insignificant match     
    puts [dict item $mydict t?e]                 # No match                     
    puts [dict item $mydict T?e -glob]           # Wildcard match               
    puts [dict item $mydict t?e -glob -nocase]   # Wildcard match, nocase       
    unset mydict                                                                

Result:

    ''                                                                          
    'quick'                                                                     
    'quick'                                                                     
    ''                                                                          
    'quick'                                                                     
    'quick'                                                                     

 See also: dict, dict get, dict keys, dict values

dict keys, dict values

Note: Ticol dictionaries are not currently 'order-preserving'

    dictionary [dict keys dictionaryValue ?globPattern?]
    dictionary [dict values dictionaryValue ?globPattern?]

Return either a list of all or selected keys in a dictionary value or a 
list of all values stored in a dictionary value. Note that [dict keys] and
[dict values] both take a dictionary value not a variable

Example:
                                                                                
    set mydict [dict create foo 18 bar 19 moo 20 par 20]                        
    puts [dict keys $mydict]                                                    
    puts [dict keys $mydict ?oo]                                                
    puts [dict keys $mydict *ar]                                                
    puts [dict values $mydict]                                                  
    puts [dict values $mydict 1?]                                               

Result:
                                                                                
    moo foo par bar                                                             
    moo foo                                                                     
    par bar                                                                     
    18 19                                                                       
    18 19 20 20                                                                 

See also: dict, dict get

dict lappend

Note: Ticol dictionaries are not currently 'order-preserving'

    dictionary [dict lappend dictionaryVariable key ?value ...?]

Appends one or more values to a dictionary key value in Tcl list format

[dict lappend] takes a dictionary variable as its argument. This 
variable need not exist. If it does not exist it will be created.

A dictionary value is returned which may be echoed using $ or [set]

Example:

    set mydict [dict create keyVal foo]                                         
    dict lappend mydict keyVal {some marmalade} {I like marmalade}              
    dict lappend mydict keyVal bar                                              
    puts $mydict                                                                

Result:

    keyVal foo {some marmalade} {I like marmalade} bar                          

See also: dict, dict map, lappend, append

dict map

Note: Ticol dictionaries are not currently 'order-preserving'

    dictionary [dict map {keyVariable valueVariable} dictionaryValue body]

[dict map] transforms a literal dictionary string via a pair of variables
which represent the key/value pairs within the passed dictionary string
and which will (possibly) be transformed by the body script.

For each iteration of a key value in the dictionary, 'body' is called 
with the specified variables substituted to the values of the dictionary 
key pairs. The script may modify the key pair values as desired and the
changes are written back to the returned dictionary. 

The returned dictionary represents the transformation applied to each
key+value pair by the script argument. The return value is cast to a
dictionary type

It is not recommended to change the key value within the script as this
will rename the original key within the dictionary

ActiveState Tcl's behaviour is to clear all values to null '{}' unless
explicitly set by the script argument. Ticol leaves values unchanged
unless specifically set or cleared by the script as this makes more sense
If you wish a value to be cleared you must do it explicitly by using
set valueVariable "" or valueVariable val {}.

A key value which is changed will replace the original dictionary key
rather than creating a new key

Example:

    set i 1                                                                     
    set dict [dict map {key var} {1 {Blink Bonny} 2 {Sir Nigel Gresley}\        
     3 {Flying Scotsman}} {                                                     
       if {[expr $key == 1]} {                                                  
          set var "Solario"                                                     
       }                                                                        
    }]                                                                          
    puts "The dict is now: '$dict'"                                             

Results:

    The dict is now: '3 {Flying Scotsman} 1 Solario 2 {Sir Nigel Gresley}'      

See also: dict, map

dict merge

    dictionary [dict merge ?dictionaryValue ...?]

[dict merge] merges together one or more dictionary values to return a new
dictionary variable. Where key collisions occur the result will be based
on the last key+value pair presented

Example:

    # Create and merge three dictionaries                                       
    set dict1 [dict create 2550 *Unknown* 4498 {Sir Nigel Gresley}]             
    set dict2 [dict create 4470 {Flying Fox} 4472 {Flying Scotsman}]            
    set dict3 [dict create 2553 Manna 2555 Centenary 2550 {Blink Bonny}]        
    set dict [dict merge $dict1 $dict2 $dict3]                                  
    dict for {key val} $dict {                                                  
       puts "$key  $val"                                                        
    }                                                                           
    unset dict1 dict2 dict3 dict                                                

Result:

    2553  Manna                                                                 
    2550  Blink Bonny                                                           
    4470  Flying Fox                                                            
    4472  Flying Scotsman                                                       
    4498  Sir Nigel Gresley                                                     
    2555  Centenary                                                             

See also: dict, dict set

dict remove

    dictionary [dict remove dictionaryValue ?key ...?]

Removes a key+value pair from a dictionary by matching a key entry to
return a new dictionary value

Be careful to supply a dereferenced dictionary variable or literal
dictionary list and not a variable name. Also note that the original 
dictionary will remain unchanged

Example:

    set dict1 [dict create 4470 {Flying Fox} 4472 {Flying Scotsman}]            
    set dict2 [dict remove $dict1 4470]                                         
    puts $dict1                                                                 
    puts $dict2                                                                 

Result:

    4470 {Flying Fox} 4472 {Flying Scotsman}                                    
    4472 {Flying Scotsman}                                                      

See also: dict replace, dict unset

    dictionary [dict replace dictionaryValue ?key value ...?]

Replaces one or more key+value pairs in a specified dictionary value and
returns a new dictionary with the amended key+pair combinatins. Where
collisions exist between successive keys, the result will be that produced
by the last key entry supplied.

Be careful to supply a dereferenced dictionary variable or literal
dictionary list and not a variable name. Also note that the original 
dictionary will remain unchanged

Example:

    set dict1 [dict create 4470 FOOBAR 4472 {Flying Scotsman}]                  
    set dict2 [dict replace $dict1 4470 {Flying Fox}]                           
    puts $dict1                                                                 
    puts $dict2                                                                 

Result:

    4470 FOOBAR 4472 {Flying Scotsman}                                          
    4470 {Flying Fox} 4472 {Flying Scotsman}                                    

See also: dict replace, dict unset

dict set

Note: Ticol dictionaries are not currently 'order-preserving'

    dictionary [dict set dictionaryVariable key ?key ...? value]

[dict set] creates a new dictionary by assigning one or more key arguments
to a single value. Multiple key arguments will create a nested dictionary

A dictionary variable argument must be specified and this variable must not
already exist.

Example:
                                                                                
    # Non-nested example                                                        
    dict set mydict 4470 {Great Northern}                                       
    dict set mydict 4471 "Sir Frederic Banbury"                                 
    puts $mydict                                                                
    unset mydict                                                                

Result:

    4470 {Great Northern} 4471 {Sir Frederic Banbury}                           

Nested dictionary keys must access the keys by nesting the first
keys innermost within any nested sequence

Example:

    # Multiple, nested keys mapping to a singular value, 'f'                    
    dict set mydict a b c d e f                                                 
    puts "Dictionary is:    '$mydict'"                                          
    puts "Nested retrieval: '[dict get [dict get [dict get \                    
    [dict get $mydict a] b] c] d]"                                              
    unset mydict                                                                

                                                                                
    Dictionary is:    'a {b {c {d {e f}}}}'                                     
    Nested retrieval: 'e f                                                      

See also: dict, dict create, dict exists, dict map

dict size

    integer [dict size dictionaryValue]

Return the size (count) of key+value pairs in a dictionary

Note that this command takes a VALUE (not a dictionary variable as its 
argument

Example:

    puts [dict size {a b c d}]                                                  

Result:

    2                                                                           

See also: dict

dict unset

CAUTION: Don't use [dict unset] to unset (delete) a dictionary variable!        
         Use [unset dictvar] instead                                            

    dictionary [dict unset dictionaryVariable key ?key ...?]

Remove one or more dictionary items by matching to a given key value. 
A (possibly) updated dictionary is returned. [dict unset] does not unset a
dictionary variable.

Example:

    set mydict [dict create 4470 {Flying Fox} 4472 {Flying Scotsman}\           
     2553 Manna 2555 Centenary]                                                 
    puts [dict size mydict]                                                     
    dict unset mydict 2553 2555                                                 
    puts [dict size mydict]                                                     
    puts [dict get $mydict]                                                     
    unset mydict                                                                

Result:

    4                                                                           
    2                                                                           
    4470 {Flying Fox} 4472 {Flying Scotsman}                                    

See also: dict, unset, dict rename, dict remove

dict walk

    void [dict walk dictionaryVar ?-bare?]

Iterates the underlying hash table object for a specified dictionary 
variable. Useful for debugging dictionaries.

The '-bare' argument changes output to a key+values only output with no
structural debug information

    ------------------------------------------------------                      
    hash_table object:0x2216100 size:100                                        
    ------------------------------------------------------                      
    Bucket(6) Node: 0x2217b00 chained nodes:1                                   
       1: 0x2217b00 key:'4475' => 'Flying Fox'                                  
    Bucket(26) Node: 0x2217810 chained nodes:1                                  
       1: 0x2217810 key:'4476' => 'Royal Lancer'                                
    Bucket(32) Node: 0x2216500 chained nodes:1                                  
       1: 0x2216500 key:'4470' => 'Great Northern'                              
    Bucket(37) Node: 0x22177d0 chained nodes:1                                  
       1: 0x22177d0 key:'4472' => 'Flying Scotsman'                             
    Bucket(53) Node: 0x2217160 chained nodes:1                                  
       1: 0x2217160 key:'4474' => 'Victor Wild'                                 
    Bucket(64) Node: 0x2217990 chained nodes:1                                  
       1: 0x2217990 key:'4473' => 'Solario'                                     
    Bucket(87) Node: 0x2216cb0 chained nodes:1                                  
       1: 0x2216cb0 key:'4471' => 'Sir Frederic Banbury'                        
    Hash Table Statistics:                                                      
    Root size:100 Occupation:7 (7.00%) Max depth:0 Nodes:7                      
    ------------------------------------------------------                      

-bare style output...

    60014 2509 {Silver Link}                                                    
    60015 2510 Quicksilver                                                      
    60016 2511 {Silver King}                                                    
    60017 2512 {Silver Fox}                                                     

See also: dict, array walk, dict info

dict with

    <result> [dict with dictionaryVariable ?key ...? body -unset]

[dict with] sets a series of variables within the current scope to one
or more of the key values found in the supplied dictionary variable.
The dictionary variable will be a well-formed Tcl list

Once these are set the script body will be called and the result will be
the result of that script evaluation

This could be say the 'value' variable of [dict for]

If -unset is used then [dict with] will unset the variables it set

Specifying keys is not yet implemented. Nested dictionary handling is
not yet implemented

Example:

    # List Railway Locomotive Numbers                                           
    # Set fields individually to create a single-level nested list              
    #         Dictionary name                                                   
    #         | Unique key                                                      
    #         | |   Key+value pair                                              
    #         | |   |      |                                                    
    #         v v   v      v                                                    
    dict set a4 1 name Silver Link                                              
    dict set a4 1 br 60014                                                      
    dict set a4 1 lner 2509                                                     
                                                                                
    dict set a4 2 name Quicksilver                                              
    dict set a4 2 br 60015                                                      
    dict set a4 2 lner 2510                                                     
                                                                                
    dict for {key val} $a4 {                                                    
        dict with val {                                                         
            printf "%2s-> Name: %-10s\tBR Number: %5s \                         
LNER Number: %4i\n" $key $name $br $lner                                        
        }                                                                       
    }                                                                           

Result:

    1-> Name: Silver Link  BR Number: 60014 LNER Number: 2509                   
    2-> Name: Quicksilver  BR Number: 60015 LNER Number: 2510                   

See also: dict, dict for, dict set

Windows Dialogs

The following Windows dialogs are available:

    inputbox       Accept user input, optionally with password masking
    get_fileopen   Browse for a file to open
    get_filesave   Browse for a file to save
    get_folder     Browse for a folder name
    msgbox         Get user input or display a message from a popup dialog

There is no Tk GUI support in Ticol, unless an external DLL is called or the
Win32 API called via the calldll* plugin

See also: inputbox, get_fileopen, get_filesave, get_folder, msgbox, gets

die
 
    die ?message? ?-code return-code?

A PHP-like command to exit the current program, optionally displaying a 
message or return code. If running in the CLI, control is returned to the
CLI, otherwise  the program will exit to the operating system.

Example:

    die "Something went wrong"                                                  
    die "Exiting to system" -code 10                                            

See also: exit, stop, at_exit, return

Differences between Ticol and Standard Tcl
 
Ticol is intended to be a subset of standard Tcl with a few differences

The following aspects differ from ActiveState Tcl:

  $$           Double/multiple $ dereferencing is supported

  ${...}       Complex, nested dereferencing with braces is supported
               Thus, braces have extra meaning when associated with $

  $this        Ticol offers $this in [switch] and [goto_block]

  $env         $env (and other) consts are provided. $env has case-
               insensitive subscript reference

  any          A simplified method of handling multiple || clauses

  all          A simplified method of handling multiple && clauses

string replace $a($i) "{" "[chr 92]{" -noescape
               A standard variable may be promoted to an array by 
               assignment but not vice-versa. Arrays may also be const or
               case-insensitive. Arrays use efficient hashtables and
               are associative, also supporting integer subscripts

               "C" like arrays are available as a plugin

  autoexec.tcl An optional startup, autoexec.tcl script is supported

  big math     Big number integer support available as a plugin and with
               some floating point support (fdiv, fmod)

  binary       Binary values are handled as 0bNNNNN... with options

  calc         [calc] interacts with the Macro Preprocesor (MPP) which will
               transform [calc] commands to more-efficient raw Tcl. If the 
               MPP is disabled then [calc] will be interpreted as [expr]

  calldll      An external plugin lib which offers DLL interfacing similar
               to Visual BASIC, dotNet etc.

  chain        Follows a chain of multiple nested dereferences (see $$)

  commands     Many additional commands which are implemented at binary
               level for speed. This includes some useful BASIC-like 
               commands. To facilitate [calc] macro optimisation a command
               will accept a reduced calc expression as [number]. e.g. [10]

  comments     Comments require no ; prefix as ;#
               Comments also include "C" style /* ... */ long-comments

  console echo To make the CLI more useful, Ticol turns console echo off
               by default. This may be enabled using:   option echo on

  date         A wide variety of date commands are available

  Debugger     A single-step debugger and error stack trace

  do           Ticol implements a do..while loop as well as [loop]

  encryption   Source code obfuscation with various locking schemes are
               available in Ticol Tcl

  eval         External scripts are linked and evaluated using [eval]

  expand       [expand] is an alternative to {*} in some cases

  explain      Can 'explain' or expand natural expression as Tcl braced
               commands

  expr         Expressions can sometimes be handled differently, due to
               less flexibility in the Ticol expression handler

               e.g. Spaces required between commands when evaluating
               strings in [expr]
               ActiveState Tcl:  if {""==""}    {puts PASS}
               Ticol Tcl:        option expression on
                                 if {"" == ""}  {puts PASS}

               Sometimes Ticol is more flexible:

               ActiveState Tcl:
               set op ==
               set foo 1
               if "$foo $op 1" {puts YES}  # can't read "foo": no such var

               Ticol Tcl:
               option expression on
               set op ==
               set foo 1
               if "$foo $op 1" {puts YES}  # Passes (echoes YES)
               if {"$foo$op 1"} {puts YES} # Passes (echoes YES)
               if {"$foo${op}1"} {puts YES}# Passes (echoes YES)

  gosub        [proc] without arguments or call a [goto_block] label

  goto         Block-limited gotos (purely for entertainment purposes)
               with localised scope and jump out of flow-control direct
               to a label

  if           The keyword 'then' is not supported. It serves no useful
               purpose and processing it slows down the interpreter

  item         The [item] keyword allows safe dereference calls to objects
               which may not exist and offers default return values
               Supported for all object types

  lib          External plugin modules (as DLLs) are loaded using [lib]
               The standard Tcl plugin method is different and incompatible
               Linked exports are automatically unregistered on unloading
               a lib. Aliases are also checked.

  loop         A simple and efficient equivalent to [for]

  namespaces   Real namespaces are not supported. They are emulated using
               name-scope prefixes such as 'fake_scope::var'

  macros       A full "C"-like Macro PreProcessor with load-time 
               optimisation and extended commands
               Nested #ifdef is not currently supported

  math         All integer math is calculated using 64-bit integers
               Hex, octal and binary consts are handled transparently in
               math.:e.g. expr 0b1010*3 => 30, expr 0x0f*10 => 150
               The new Tcl 9.x 0oNN octal format specifier is supported
               Math expressions are optimised by the Macro PreProcessor 
               unless /NEO and /NES command line arguments are used

  memory       Memory allocation and free for use with [calldll] which
               exposes the standard "C" malloc, free and memcpy

  obfuscation  Script encryption (obfuscation). Ticol scripts may be
               obfuscated using symmetric encryption and may also be
               execution-locked by various means to user, workstation
               or by password etc.

  on           Similar to BASIC's ON GOSUB

  option       Certain functionality is controlled via the [option] command
               particularly, the 'option expression' option

  Performance  Performance analysis, profiling and graphing

  plugins      Around 50 plugins are available to extend the language

  proc         Ticol procs may not be integers or real numbers
               A proc name may, however, contain numeric digits
               ActiveState Tcl allows numeric values as proc names
               A proc name may also consist of only one or more spaces

               Procs defined with numeric prefixes can be called only
               directly using [call procname]

               Proc default args cannot contain math or other symbols
               unless wrapped in double-quotes. It is good practice to wrap
               strings within double-quotes in any case

  set          A standard variable may be promoted to an array by 
               assignment using [set] and no warning is issued, This helps
               facilitate [static] variables in procedures. e.g.

               set a Hello
               set a(10) 23  # Variable a is now type <array>
               puts $a       # Returns "<array>" 

  speed        Ticol Tcl is purely an interpreter. Even though the MPP will
               do some optimisation it will always be slower than 
               ActiveState Tcl or compiled code

  stacks       Stacks are built-in to the main interpreter core

  static       Ticol offers native static variables for procs, and
               this can be extended to include structs

  structs      Structs are built-in. Ticol provides for binary struct
               emulation in order to interface with external DLLs. Structs
               may also be used with care with native Tcl code

  sub          [sub] offers argument-less procedures

  time         Optionally returns the current time as [time]
               Ticol also has an extensive [date] command

  uplevel      The absolute level parameter is specified by a @ symbol 
               not # as # is reserved by the MPP for comments
               e.g. uplevel @1 command   # Executes at absolute level 0

  upvar        The absolute level parameter is specified by a @ symbol
               not # as # is reserved by the MPP for comments
               [upvar] also assumes the local variable name (optional)
               e.g. upvar @1 s   # Executes at absolute level 0

               Also, [upvar] optionally assumes the alias, which makes
               far more sense than making it mandatory
               e.g. upvar s      # Not upvar s s

  variants     Some variant handling for interfacing with external DLLs
               These are not recommended for use with native Tcl code

  variable     [variable] is implemented an alias for [set] because Ticol
               does not support real namespaces
               Variable names allow underscores, dots and colon characters
               Variable names can embed "::" as a pseudo namespace
               e.g. variable foo::bar

  variable     More restrictive than standard. Allows only the following
               characters names "a-zA-Z0-9_.:& \t"

  Windows      Basic Windows dialogs including file open, file save, 
               folder-browse, input box and message box

  xbase/dbase  xbase database support as a plugin as well as an extended
               database support which offers large fields/records

The Macro-PreProcessor (MPP) extends the standard Tcl comment character. 
This adds trailing comments but reserves the # symbol. The MPP s designed
to optimise and speed up interpretive scripts but can be disabled using
the /NP command argument (not recommended). The MPP consumes no run-time
resources as pre-processing is performed at load time

Macro #__LINE__ , #__DATE__ #__TIME__, #__FILE__, #__USER__ and #__HOST__
debugging constants are available. Macro PreProcessor #define, #ifdef, 
#ifndef, #undef processing controls

Ticol has no Tk/GUI support unless you call an external dll via the calldll
plugin to facilitate graphics output via the Windows API

See also: non standard

See also: ticol, gui, arrays, calc, calldll, do, encryption, global, goto,
          lib, math, option, struct, time, uplevel, upvar, variant

dim

    dim arrayname size

Dimensions an array according to size. The underlying hash table is
created and required memory is allocated. No array elements are assigned
and the element count is set to zero. See also [array create]

A size value of 0 is acceptable

[is_set] will return $true (1)
[array exists] will return $true (1)
[array count] will return 0

[dim] is not necessary to create array elements as array allocation is
dynamic, but it may speed up execution when allocating a very large number
of elements within a loop as the array hash table is expanded dynamically
and geometrically as elements are added. This introduces a time cost
overhead to critical loops

Example:

    dim names 100                                                               
    puts [array count names]                                                    
    array walk names                                                            

Results:

    0                                                                           
    -----------------------------------------------------------------           
    hash_table object:0x35ec650 size:10                                         
    -----------------------------------------------------------------           
                                                                                
    Hash Table Statistics:                                                      
    Root size:10 Occupation:0 (0.00%) Max depth:0 Nodes:0                       
    -----------------------------------------------------------------           

See also: array, arrays, array set

disktype
 
    integer [disktype x:]

Returns a numeric value indicating the disk type as follows:

    0 DRIVE_UNKNOWN                                                             
    1 DRIVE_NO_ROOT_DIR                                                         
    2 DRIVE_REMOVABLE                                                           
    3 DRIVE_FIXED                                                               
    4 DRIVE_REMOTE                                                              
    5 DRIVE_CDROM                                                               
    6 DRIVE_RAMDISK                                                             

Example:

    array set status {                                                          
       0 "Unknown"                                                              
       1 "No root directory"                                                    
       2 "Removable"                                                            
       3 "Fixed"                                                                
       4 "Remote"                                                               
       5 "CDROM"                                                                
       6 "RAMDisk"                                                              
    } -const                                                                    
                                                                                
    puts "[array item status [disktype c:] Unknown"                             

Result: (where c: is a hard drive)

    Fixed                                                                       

Note that a trailing path is allowed. e.g. [disktype "c:\\temp"]

See also: diskfree, diskstat, info

dll_close

    dll_close dllname handle

Release a DLL previously opened by [dll_open]. Accepts either an integer
literal or a variable. Returns a boolean success flag.

See: calldll

Example:

    set handle [dll_open "vbsqlite3.dll"]                                       
    dll_close $handle                                                           

Result:

    1

See also: dll_open, calldll

dll_open

    integer [dll_open dllname]

Load a DLL into persistent memory. This may be necessary to process DLL 
calls where there is a need for a persistent handle such as SQLite
queries

[calldll] will accept a handle to an open DLL in place of a DLL name

See: calldll

Example:

    set handle [dll_open "vbsqlite3.dll"]                                       

Result:

    13631488                                                                    

See also: dll_close, calldll

Unary commands: incr, decr, ++, --
 
    integer [incr value|variable ?increment? ?-unsigned? ?-width 8|16|32?]
    integer [decr value|variable ?increment? ?-unsigned? ?-width 8|16|32?]
    integer [++   value|variable ?increment? ?-unsigned? ?-width 8|16|32?]
    integer [--   value|variable ?increment? ?-unsigned? ?-width 8|16|32?]

The above commands increase or decrease a value, simple or array variable
by a specified amount. No other variable type is accepted.

The increment or decrement is by 1 unless increment is specified
If specified the increment may be +ve or -ve and greater than 1
The optional increment argument may be any signed 64-bit integer

Real numbers (1.2345) will be rounded to integer before applying the 
operator so:  [++ -1.23]  gives 0,  and [++ 1.23] gives 2

The command is extended with the addition of the following arguments:

    -unsigned      Return an unsigned 64-bit integer
    -width N       Return the value (truncated if nesc.) to 8,16 or 32 bits
    -width 8       Unsigned char (8 bits)
    -width 16      Unsigned word (16 bits)
    -width 32      Unsigned long (32 bits)

Example:

    set line #__LINE__                                                          
    ++ line 2                                                                   
    puts "Line is $line"                                                        

Example:

    set a(23)                                                                   
    puts [++ a(23)]                                                             
    puts $a(23)                                                                 

Result:

    24                                                                          
    24                                                                          

Unary increment/decrement is not implemented within [expr]. This is due to
potential conflicts with expressions such as "3 - -7"
Embed [++] [incr] or [--] [decr] commands within the expression as follows...

Example:

    set a 1;puts [expr "[++ $a]*2"]        # Result 4                           

Example:

    set a 1;puts [expr "[++ $a 5]*2"]    # Result 12                            

Where the incrementing operator is used in an expression as an infix
operator it should be wrapped in parentheses if an increment value is given

Example:

    set a 1;puts [expr "($a++ 10)*2"]    # Result 22                            

See also: math, expr, eval

diskfree
 
    integer [diskfree x:]

Shows the free disk space for a given drive/path. [diskfree] works with
UNC path. You can use comma to format the result

Example:

    puts "[comma [diskfree d:]] byte(s)"                                        

Result:

    135,071,793,152 byte(s)                                                     

Example:

Show free space on UNC path for workstation, "snoopy" C:

    puts "[format %.3f [/ [diskfree \\snoopy\c$] [* [* 1024.0 1024] 1024]]] Gb" 

Result:

    7.89 Gb                                                                     

See also: disktype, diskstat, info, file

diskstat
 
    integer [diskstat x:]
    string [diskstat x: -name]

Check the status of a disk drive, including network drives and return a 
status code

The -name argument will cause either a drive letter or network share name
to be returned depending on the connection type instead of a status code

Useful in conjunction with [disktype]

The following codes are returned where -name is omitted:

    0   Absent/error (invalid spec/removable drive absent/no such drive)
    1   Drive is present and available
    2   Networked drive present but disconnected state
    3   Networked drive is absent

See also: diskfree, disktype, info, file

do while, do until (Flow Control)

    do while {condition-statement} {code-block}                                 
    do {code-block} while {condition-statement}                                 
    do {code-block} {condition-statement}                                       
    do until {condition-statement} {code-block}                                 
    do {code-block} until {condition-statement}                                 

A flexible-syntax [do] loop

The following additional commands interact with [do]

    break             # Is passed back to any enclosing structure               
    stop              # Halt operation, return to the CLI if loaded             
    exit              # Exit the script and return to Windows                   
    goto              # Where used with an enclosing [goto_block]               
    continue          # Resume execution at the head of the loop                

Ticol offers both standard Tcl while loops as well as do...while loops

Well-formed single statements need not be braced but where braces are
present and over more than one line the open brace must be at the end of the
line not the start of a line (see flow control). Statements may be combined
on a line using the semi-colon separator

Examples:

    do {puts [rnd 1 10] } while 1                                               
                                                                                
    do {puts [rnd 1 10] } while { 1 }                                           
                                                                                
    do {puts [rnd 1 10] } while {                                               
       1                                                                        
    }                                                                           
                                                                                
    set i 0                                                                     
    do {                       # Correct brace layout (Tcl)                     
       puts "i is $i"                                                           
       if {$i == 2} { return }                                                  
       incr i                                                                   
    } while { $i < 10}                                                          
                                                                                
    do while {1} {puts [rnd 1 10] }                                             
                                                                                
    do {puts [rnd 1 10] } {1}                                                   
                                                                                
    set i 0 ; do {puts "i is $i"; if {== $i 2} { return } ; incr i } \          
       while { $i < 10}                                                         
                                                                                
    do while {[< $i 10]} {                                                      
       puts "do-while i is $i"                                                  
       ++ i                                                                     
    }                                                                           
                                                                                
    set i 0                                                                     
    do {                                                                        
       puts "do {} i is $i"                                                     
       ++ i                                                                     
    } {[< $i 10]}                                                               
                                                                                
    set i 0                                                                     
    do {                                                                        
       puts "do-while i is $i"                                                  
       ++ i                                                                     
    } while {[< $i 10]}                                                         

See also: flow control, while, for, loop, if, switch, goto, time

$ dereference

    $varname                                                                    
    [set varname]                                                               

Dereferences a variable to return the contents (see: double dereference)

Very early versions of Tcl lacked the '$' syntax and, instead, used the
[set] command to dereference variables. In some cases [set] may still be
useful. Variable names may be braced as ${var} to isolate from strings

Because braces delay evaluation, the use of braces to isolate variable
names can be problematic, especially with nested sub-variables: 
e.g. ${name_$var}. Such cases will require the use of intermediate
variables since translation would require the order of nested brace
evaluation to be reversed for a $ operation. Nested, braced dereferences
are allowed (e.g. ${var1${var2}} )

Strings which contain non-variable embedded dollars must be escaped.
If it is not possible to manually escape then such strings may be 
escaped using the [escape] command

Example:

    set var {"\\path\\to\\badfile.$$$"}  # Simulate read from [ls]              
    puts [escape $var]                   # Prevent problems                     

Unlike standard Tcl, Ticol supports multi-level dereferences ($$var)
as well as complex, nested dereferences using braces.
See: nested dereferences

Note that you cannot dereference a struct member using $ or [set] as 
this merely returns the member offset address within the struct

Example:

    set s 23                                                                    
    puts "a is '$s'"                                                            
    puts "a is '[set s]'"                                                       

Result:

    s is 23
    s is 23

Example:

    set a(1) 23                                                                 
    puts "a(1) is '$a(1)'"                                                      
    puts "a(1) is '[set a(1)]'"                                                 

Result:

    a(1) is '23'
    a(1) is '23'

Example:

    set a "Hello "                                                              
    puts ${a}world                                                              

Result:

    Hello world

Double-Dereferencing
--------------------
Double/multiple level dereferences are allowed in Ticol Tcl using either
nested [set] or multiple $ prefix statements. (See: double dereference)

Examples:

    set a 23                                                                    
    set b a                                                                     
    set c b                                                                     
    set d c                                                                     
    puts $$$$d                      # 4 levels of dereference                   
    puts [set $$$d]                 # 4 levels of dereference                   
    puts [set [set $$d]]            # 4 levels of dereference                   
    puts [set [set [set $d]]]       # 4 levels of dereference                   
    puts [set [set [set [set d]]]]  # 4 levels of dereference                   

Result:

    23                                                                          
    23                                                                          
    23                                                                          
    23                                                                          
    23                                                                          

See also: set, double dereference, nested dereference, variables, arrays

Domain Error (Math)

Some math functions have a restricted set of valid inputs or 'domain'
submitting values outside these limits will result in a 'Domain Error'

The following functions have limited domains

    Function      Range Limit
    --------------------------------------
    acos         -1 to +1
    asin         -1 to +1
    log          Positive numbers only
    log10        Positive numbers only
    sqrt         Positive numbers only

Example:

    puts [expr acos(23)]                                                        
    puts [funct acos 23]                                                        

Result:

    acos(23): Domain error                                                      
    acos(23): Domain error                                                      

    http://www.cplusplus.com/reference/stdexcept/domain_error/

See also: math functions

Double Quotes

In common with most programming languages, double quotes in Tcl are used
to delineate and group words together into strings. They are not mandatory
unless an intended string value contains whitespace. Although braces are 
preferable as delimiters, double quotes strongly recommended to be used in
all cases where a string is intended and braces can't be used.

Braces may also be used to perform this task since braces can group
characters into a single argument for a command. Since nested double-
quotes as not permitted, braces may be used to group string sequences
within double-quoted strings

Example:

    set "I am a {string within} a string"
    set "I am a {delayed $var within} a string"

Example:

    puts "Hello world"                                                          
    puts {Hello world}                                                          

It should also be noted that while unescaped double-quotes are not 
allowed within a string, adjacent strings will concatenate, thus in the
following example \\r\\n isn't embedded but is, instead, concatenated

Example:

    store add s "[unescape "\\r\\n"]\r\n"

Single-quote characters (') do not group and therefore cannot be used to
group characters into a string argument

The empty string or empty list may be represented by pairs of double
quotes or braces

Example:

    set s ""                                                                    
    set s {}                                                                    

Numeric hex or other literals for which it is desired that the MPP not 
translate into decimal should be quoted. This will affect the interaction
with string comparison commands such as eq, ne. Numeric comparison 
commands will perform automatic decimal conversion

Examples:

    set a 0xABCDEF                                                              
    set b "0xABCDEF"                                                            
    set c 0o12345                                                               
    set d "0o12345"                                                             
    set e 0b1010101                                                             
    set f "0b1010101"                                                           
                                                                                
    puts $a                                                                     
    puts $b                                                                     
    puts [int $b]                                                               
    puts $c                                                                     
    puts $d                                                                     
    puts [int $d]                                                               
    puts $e                                                                     
    puts $f                                                                     
    puts [int $f]                                                               

Result:

    11259375                                                                    
    0xABCDEF                                                                    
    11259375                                                                    
    5349                                                                        
    0o12345                                                                     
    5349                                                                        
    85                                                                          
    0b1010101                                                                   
    85                                                                          

It is not allowed to embed unescaped double-quotes within a quoted-string
even if quote parity is maintained. In standard Tcl this is an error

Example:

    option echo on                                                              
    set q Hello"world"      # This is actually two adjacent strings             

Result

    Hello"world             # But the embedded quote is visible                 

However, a workaround is to wrap the unescaped string in spaces or to
escape the string and then use [unescape]. For example, when addressing
an array with a subscript which has spaces...

    set q {a("with spaces")}                                                    
    vars q*                                                                     
    assert $q {eq $_ {a("with spaces")}} -v                                     
    assert $q {eq $_ [unescape "a(\"with spaces\")"]} -v                        

See also: quoting hell, braces, single quotes, dereference, 
          minimal escaping style

Quoting Hell

Tcl requires careful attention to using quoted versus braced strings. 
Usually the braced form is preferable but there may be a few cases where
quoted strings are preferable. In Ticol Tcl double-quoted strings (") are
preferable where a string contains long-comment /* */ tags.

Tcl may look like C/C++ but it is not and the handling of quoted strings
differs since, in essence, all but special types are handled as strings

The need for nested quotes may require a careful rethink of your source
code since nested double quotes are not allowed. Quotes are permitted only
at one level of nesting or the macro preprocessor will complain!

Quotes are often wholly unnecessary. A string is still a string even if
not enclosed in either quotes or braces. A means of enclosure becomes
necessary when whitespace is involved, however, even in this case, if
the this is an escaped space character then delimiters are unnecessary

The single quote - ' - is not a string delimiter

Example:
                                                                                
    puts Hello                # Hello   is a string (undelimited)               
    puts "Hello"              # Hello   is a string                             
    puts {Hello}              # Hello   is a string                             
    puts "Hello world"        # Hello world   is a string                       
    puts {Hello world}        # Hello world   is a string                       
    puts Hello\ world         # Hello world   is a string (undelimited)         
    puts "Hello\ world"       # Not valid at the CLI, valid only in scripts     
                              # unless option escape is set to on               
    set a(Hello\ world) 23    # Array subscript: Hello world  is a string       
    set a("Hello\ world") 23  # Array subscript: Hello world  is a string       
                                                                                
    puts 'Hello'              # 'Hello' is a string, but not delimited by '     
    puts 'Hello world'        # Not a properly formed string                    
    puts [Hello world]        # Not a properly formed string                    

Links
-----
Recommended reading:

    https://wiki.tcl-lang.org/page/Quoting+hell
    https://wiki.tcl-lang.org/page/Tcl+Quoting

See also: quoting, minimal escaping style

Minimal Escaping Style

This is a code style which avoids unnecessary Tcl escaping syntax wherever
possible and is highly recommended. For example, while it is strongly 
recommended to properly brace arguments for flow-control commands such 
as [if] or [while], these braces are optional and the recommendation is
merely to reduce mistakes.

In Ticol, much of the work of translating backslash-escaped characters
is performed during macro preprocessing which means its behaviour
differs slightly

Curly braces prevent proper and timely variable substitution

   "In some languages such as C, Javascript, Lisp, and Python, double
    quotes indicate a certain type of value: a string. In Tcl all values
    are already strings, so double quotes do not have that function.
    Instead, they provide an environment in which whitespace characters
    are ordinary characters rather than word delimiters. Braces do the
    same, but additionally inhibit the standard substitutions"
    (https://wiki.tcl-lang.org)

General Rules
-------------
Don't use quotes where braces could be used. 
Don't use braces where no backslash substitutions are needed

Examples
--------
                                                                                
    puts Hello\ world     # Valid (escaped spaces are ok)                       
    puts Hello\t world    # Not valid, will raise an error                      
    puts {Hello\tworld}   # Valid grouping using braces                         
    puts "Hello\tworld"   # Valid grouping using dquotes                        

Links
-----
    https://wiki.tcl-lang.org/page/Tcl+Minimal+Escaping+Style
    https://web.archive.org/web/20081009203437/\http://www.tclscripting.com/articles/jan06/article3.html

See also: quoting hell

$$ double-dereference (multi-dereference)

    $$var                                                                       
    set $var                                                                    

Ticol allows for a non-standard double-dereference similar to PERL on
simple variables and arrays elements. This avoids the need for clumsy
[set] statements and is much faster, avoiding a slow command-call

Multiple levels of dollar-dereferencing are supported e.g. $$$$var
Deeply-nested levels can also be handled using [set] in the approved
manner for example:

    puts [set $var]                                                             
    puts [set [set [set [set $var]]]]                                           

Complex, braced variable expressions such as '${var}::name' are 
supported by multiple dereferences. 

Example:

    # Prints out '23'                                                           
    set prefix::name 23       # set 'var prefix::name' to value '23'            
    set p prefix              # p points to 'prefix'                            
    puts ${${p}::name}        # Double-dereference via $p                       
    puts [set ${p}::name]     # Same functionality                              
    puts [set [set p]::name]  # Same functionality                              

[set] can also be used to handle very complex nested dereferences and
can be combined with the use of one or more dollar $ dereferences

Note that struct member fields must be dereferenced using [struct item]
and not the dereference dollar prefix $

An alternative to double/multiple dereferences is to use [chain] which will
automatically resolve down to a root case.

More examples...

Example:

    set a 23                                                                    
    set myvar a                                                                 
    puts "Double dereference using \$\$ is:    '$$myvar'"                       
    puts "Double dereference using \[set\] is: '[set $myvar]'"                  

Result:

    Double dereference using $$ is:    '23'                                     
    Double dereference using [set] is: '23'                                     

Example:

    # d -> c -> b -> a == 23                                                    
    set a 23; set b a; set c b; set d c                                         
    puts $$$$d                      # x4 levels of dereference                  
    puts [set $$$d]                 # x4 levels of dereference                  
    puts [set [set $$d]]            # x4 levels of dereference                  
    puts [set [set [set $d]]]       # x4 levels of dereference                  
    puts [set [set [set [set d]]]]  # x4 levels of dereference                  

Result:

    23                                                                          
    23                                                                          
    23                                                                          
    23                                                                          
    23                                                                          

Example:

    set a(1) one                                                                
    set b a(1)                                                                  
    set c(1) b                                                                  
    set d c(1)                                                                  
    puts "\$\$\$\$d resolves to '$$$$d'"                                        
    puts "\$\$\$\$d resolves to '[set [set [set [set d]]]]'"                    

Results:

    $$$$d resolves to 'one'                                                     
    $$$$d resolves to 'one'                                                     

Array double-dereferences, where the array name prefix is contained
in a variable must be resolved as follows. This method is useful when
addressing array names returned from procedures etc.

    set a(0) "Hello"                                                            
    set q a                                                                     
    set i 0                                                                     
    puts "Array element $i: '${${q}($i)}'"        # Correct syntax              

    #                          ^                                                
    #   Resolve first          ${q}                                             
    #   Resolve second       ${____($i)}                                        

Result:

    Hello                                                                       

This breaks down as follows...

    a)    ${${q}($i)}                                                           
    b)    Resolve innermost reference: ${q} -> which gives 'a'                  
    c)    Resolve outermost array:     ${a($i)} -> Hello                        

Complex array elements containing whitespace can be referenced using
quoted-escaped strings and the [unescape] command as follows: Whilst
variables can be declared which have complex whitespace, literal strings
require escaping.

Example:

    set a("with spaces") "Hello world!"                                         
    #          ^ Array definition (escapes not required)                        
    set b [unescape "a(\"with spaces\")"]  # Escaped string                     
    #                         ^ Literal string (must be escaped)                
    set c b                                                                     
    puts "Triple dereference: '$$$c'"                                           

Result:

    Triple dereference: Hello world!                                            

Structs
-------
Structs can slightly more complex to multi-level dereference, but this
depends on the circumstances:

Simple example:

    struct create st {a 10}             # Create struct s with 1 field          
    set s Hello                         # Create a separate variable            
    struct set st.a s                   # Point s.a to variable s               
    puts $st.a                          # Single-dereference                    
    puts ${$st.a}                       # Double-dereference                    
    #        ^                          # Dereferences to 's'                   
    #    ${s}                           # Dereferences to 'Hello'               

Result:

    s                                                                           
    Hello                                                                       

Alternate Simple Example:

    struct create st {a 10}             # Create struct s with 1 field          
    set s hello                         # Create a separate variable            
    struct set st.a s                   # Point s.a to variable s               
    puts $st.a                          # Single-dereference gives s            
    puts $$st.a                         # Double-dereference gives $s           

Result:

    s                                                                           
    hello                                                                       

Complex example:

    struct create s {a 10 b 20}         # Create struct s with 2 fields         
    struct set s.a Hi                   # Set field a to "Hi"                   
    struct set s.b There                # Set field b to "there"                
    set q s                             # Create 1 level of indirection         
    set r q                             # Create another level in r             
    puts "s.a '${${$r}.a}' "            # Dereference 3 times                   
    puts "s.a '${${$r}.b}' "            # (2 indirection and 1 final)           

Result:

  Hi                                                                            
  there                                                                         

How this resolves (from the innermost braces outwards) ...

    #   Expression:        ${${$r}.a}                                           
    #                          ^                                                
    #   Resolve first          $r -> q                                          
    #   Resolve second       ${q} -> s                                          
    #   Resolve third        $s.a -> "Hi"                                       
    #                                                                           
    #   Note that $s.a is equivalent to ${s.a}                                  

Dictionaries
------------
Double/indirect dereferencing for dictionary types is not yet implemented
For such variables, use [set $varname]

Example:

    set dict [dict create 4470 {Flying Fox}]                                    
    set a dict                                                                  
    puts $$a             # Will return "<dict>"                                 
    puts [set $a]        # Will correctly dereference dict ok                   

Other Data Types
----------------
All other data types such as linkedlist, stack, variant etc. use simple
handles or variables which can be multi-dereferenced as such

Useful resources:

    http://phaseit.net/claird/comp.lang.tcl/tcl_deref.html
    https://psg.com/~joem/tcl/faq.html#DoubleIndirect

See also: chain, dereference, nested dereference, set, variables

Complex Nested Dereferences

Complex nested dereferences are supported for arrays and standard variables
These may also be braced to isolate variable names from strings. This is an
enhancement on standard Tcl

Braces are resolved within variable expressions in the opposite order to
braces within standard Tcl expressions. That is, they are evaluated
recursively, from the innermost set of braces outwards. Where braces are
used within a variable expression they must follow immediately after the
dollar sign. Braces must wrap the whole variable expression

Examples:

    $s                # A simple variable dereference                           
    $a(text)          # Array with subscript literal value 'text'               
    $a($ss)           # Array with subscript variable '$ss'                     
    ${s}text          # Variable 's' isolated from remainder of text            
    ${${s}text}       # Double dereference of 's' including 'text'              
    ${a($ss)}text     # Array 'a' with subscript '$ss', isolated from text      
    abc${a(1)}def     # Array 'a(1)' embedded within the string "abcdef"        
    $a(${i}wo)        # Where $i == 't', this references subscript 'two'        
    $$$c              # Triple dereference (c->b->a->result)                    
    $$${c}            # Ditto (equivalent)                                      
    $${${c}}          # Ditto (equivalent)                                      
    ${${${c}}}        # Ditto (equivalent)                                      
    ${$p$q$r}         # Where p, q, r == v, a, r and var == "x" then "x"        
    ${${p}${q}${r}}   # Ditto (equivalent)                                      
    ${q}(${i}wo)      # Where q == a and i == t then "a(two)"                   
    ${${q}(${i}wo)}   # Where q == a and i == t and a(two) == "x" then x        
    ${${$q}.a}        # Where q => r => struct s with member field a            
    ${::x}abc         # A global variable called x embedded against 'abc'       
    ${::{$x}.a}       # Where x == s; refer to global struct member s.a         
    ${::foo::{$x}.a}  # Where x == s; refer to namespace struct ::foo::s.a      

Invalid Expressions:

    ${q}(text)        # Where q == a. Array 'a' will return "<array>text"       
                      # Should be ${${q}(text)}                                 
    {$a(1)}           # Will not resolve due to protective braces/missing $     
                      # Should be ${$a(1)}                                      
    {$p$q$r}          # Will not resolve due to protective braces/missing $     
                      # Should be ${$p$q$r}, however ...                        
                      # puts [eval $[subst {$p$q$r}]]    ... will resolve       
    $::{x}abc         # A global variable requires :: inside the braces         

Dereferencing [struct] Inside [proc]
------------------------------------
Dereferencing an [upvar] struct inside [proc] can be problematic due to
indirection in the struct name which requires a complex, nested
dereference.

If desired, [set] or [->] can be used as an alternative to resolve the
final dereference where ${${argvar}.field}  would be required

Example:

    struct s {a 10}                                                             
                                                                                
    proc foo {x} {                                                              
       upvar $x             # Dereferencing, x gives the struct name 's'        
       puts "-> ${x}.a"     # This requires x.a to be braced                    
       puts "-> ${${x}.a}"  # This requires $s.a to be braced                   
    }                                                                           
                                                                                
    struct set s.a Hello                                                        
    foo s                                                                       

Results:

    s.a                                                                         
    Hello                                                                       

See also: chain, dereference

dump

Dump loaded source-code or known variables

    dump ?-echo? ?-crlf?     # Display syntax-highlighted source code           
    dump variable            # Analyse a variable                               

Dump with no arguments will display any preprocessed source code loaded
within the CLI by either [run] or [load] as long as it is not encrypted.
Code which has been encrypted (obfuscated) using ticol.exe /C cannot be
listed

Source code is highlighted using the following convention

    blue        Inbuilt Tcl commands                                            
    magenta     Procs defined within the script                                 
    yellow      Strings (embedded commands not highlighted)                     
    green       Dereferenced variables                                          
    darkyellow  Numeric values                                                  
    grey        Variables, arguments and lib commands                           
    white       Square, curved and curly braces                                 
    -           Comments are removed by the MPP                                 

Example: (procs will only be highlighted once known)

    proc foo {x} {                                                              
        puts "Hello from [info procname] "$x  # Alert user                      
        ++ x                                                                    
    }                                                                           
    puts [foo 12]       # Prints 13                                             

Line numbers will no longer match the original source-code but #__LINE__
tags will correctly reference the original source. Trailing whitespace is
removed but indenting is preserved.

See: load

You can return the source code as a single string to be captured by a Tcl 
variable by using the -echo argument. Unless the Macro PreProcessor has
been disabled the returned code will be preprocessed and will have had
empty lines removed with comments and macro #ifdef..#else..#endif blocks
stripped

Example:

  load msgbox          # Load msgbox.tcl to the CLI                             
  set a [dump -echo]   # Echo the source into a variable                        
  puts $a              # Display the source                                     

Result:

  Loaded msgbox.tcl OK 105 byte(s)                                              
                                                                                
  set r [msgbox [join [split $qbf " "] "\n" 768] "Choose" 36]                   
  msgbox "You chose [map {6 Yes 7 No} $r]"                                      

--

    dump ?variable?                                                             

[dump] with a variable name displays an analysis of a variable. When used with
structs full member details will be shown, this includes the offset into the 
struct and the member (field) size in bytes

 struct 's' - at 9384672 (0x8f32e0), 146 byte(s), 5 member(s)                   
 + member 1: 's->s_a' => 0x8f32e0, 4 byte(s)                                    
 + member 2: 's->s_b' => 0x8f32e4, 30 byte(s)                                   
 + member 3: 's->s_c' => 0x8f3302, 100 byte(s)                                  
 + member 4: 's->s_d' => 0x8f3366, 4 byte(s)                                    
 + member 5: 's->s_e' => 0x8f336a, 8 byte(s)                                    
 -------- -----------------------   ----------------------- -- ASCII -------    
 008f32e0 48 65 6C 6C F0 FF 00 00 - 6F 00 33 32 31 41 42 43 Hell....o.321ABC    
 008f32f0 44 45 46 47 48 49 4A 4B - 4C 4D 4E 4F 50 51 52 53 DEFGHIJKLMNOPQRS    
 008f3300 54 55 54 68 65 20 71 75 - 69 63 6B 20 62 72 6F 77 TUThe quick brow    
 008f3310 6E 20 66 6F 78 20 6A 75 - 6D 70 73 20 6F 76 65 72 n fox jumps over    
 008f3320 20 74 68 65 20 6C 61 7A - 79 20 64 6F 67 00 00 00  the lazy dog...    
 008f3330 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 ................    

[dump], when used with upvar arrays or standard variables will also show the 
parent linked-list chain. This may be useful to understand how variables work

 array 'b' level 3 (0x200bde0) data @33603120 (0x200be30) 7 byte(s)             
 (Array data not shown. Use array walk arrayname)                               
                                                                                
 Address   0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F ASCII                
 -------- -----------------------  ----------------------- ----------------     
 020cbe30 3C 61 72 72 61 79 3E 00  20 27 00 78 69 74 20 6C <array>. '.xit l     
                                                                                
 + Parents of 3::'b' (0x20cbde0 / 34389472):                                    
 + Value: '<array>'                                                             
 |                                                                              
 | Lvl::Variable Name           This       -> Parent      Type  Const           
 | ------------------------------------------------------------------           
 +   2::b                       0x020cbe40 -> 0x020cb890  array  False          
 +   Value: '<array>'                                                           
 +   1::b                       0x020cb890 -> 0x020c9540  array  False          
 +   Value: '<array>'                                                           
 +   0::b                       0x020c9540 -> 0x00000000  array  False          
 +   Value: '<array>'                                                           

See also: inspect, vars, walk, calldll, dumpvariant, array walk, load, run

dumpvariant

    dumpvariant address
 
Defined in lib plugin: ticol_variant.dll

Displays information relating to a variant object which has been returned 
from a calldll*variant operation. This operation requires a valid variant
address

Example:

    set v [vsplit $qbf]      # Split to Variant (SAFEARRAY) array               
    dumpvariant v                                                               
    varray unset v                                                              

Result:

    DUMP: Variant 'v' at 36012656 (0x2258270)                                   
      Type is 8204 (0x200c) BYVAL                                               
      Variant is an array of type 0xc                                           
      Type is 12 byte Variant VT_VARIANT                                        

See also: dump, inspect, walk, vars, varray get, array walk

dumpmem

    void [dumpmem address ?count?]

Debugging routine to dump memory contents at the specified location
Attempting to access a forbidden location will result in a trappable error

Example:

    lib ticol_calldll                       # Load plugin                       
    set h [dll_open msvcrt40.dll]           # Load the MSVC runtime             
    set t 0                                 # Set a value (integer)             
    set ret [calldll_cdecl $h localtime t]  # Call 'localtime'                  
    dumpmem $ret 32                         # Dump 32 bytes of the result       
    dll_close $h                            # Release the MSVC runtime          

To dump the binary contents of a Tcl variable use [addressofb] as follows

    set s "Hello world"                                                         
    dumpmem [addressofb s] 16                                                   

See also: dump, dumpvariant, walk, array walk, vars, info, introspection

Ticol Editor and Recommended IDE
 
Notepad++ is the recommended editor:
    https://notepad-plus-plus.org

You can add the TCL syntax by copying the output of:     puts [commands]

Next, open Notepad++ and go to:

    Settings->Style Configuration->Tcl (from list)                              
    ->Instruction Word (from list)                                              
                                                                                
    and then paste the list of functions into the                               
    "User defined keywords" area                                                

Executing Ticol Scripts from Notepad++
--------------------------------------
* Click the "Run" menu option on the Notepad++ main menu
* Select "Run..."
* Click the elipsis button (...) to browse to the location of ticol.exe
  and select it
* Add a space to the resulting command line and append the following,
  wrapped in double-quotes:  "$(FULL_CURRENT_PATH)"
* You should now have a command something like
    C:\path\to\ticol.exe "$(FULL_CURRENT_PATH)"
* Click "Save..."
* Enter an appropriate name, e.g. "Ticol Tcl"
* If desired select a shortcut such as CTRL+F1
* Click "OK" to save
* You should now be able to run the current script using your shortcut or
  the menu
* You may need to add /NA to the command string to prevent autoexec.tcl
  from running, e.g.
    C:\path\to\ticol.exe "$(FULL_CURRENT_PATH)" /na
* You may also need to add /PAUSE to halt the screen before closing
    C:\path\to\ticol.exe "$(FULL_CURRENT_PATH)" /na /pause
* Note that command-line editing is non-existent via the menus, if you
  wish to change the command line you will need to delete the shortcut and
  recreate from scratch

See also: Ticol, ticol.ini

echo

    echo value|varname ?-var? ?-hex?                                            

Print a string without backslash escape translation other than performed by
the Macro PreProcessor when the script is loaded. Echo is marginally more
efficient than either [puts] or [printf] and is useful for debugging string
translation, escaping etc.

The -hex argument will output hex character pairs

Additionally, [echo] with -var can output a string which contains Tcl code
without the $ dereference causing the contained code to be executed. 

[echo] is equivalent to:  puts [set value] except that a string value, not a 
variable is required as the only argument

[echo] returns the untranslated string intact, and also does not append an 
additional carriage return/linefeed pair at the end. Use [newline] or
puts "" after a call to [echo] if you wish to generate a new line

Examples:

    echo $env(programfiles)                                                     
    puts [set env(programfiles)]                                                
    puts $env(programfiles)                                                     

Results:

    C:\Program Files (x86)                                                      
    C:\Program Files (x86)                                                      

See also: puts, printf

elevate (Windows 2000 or Higher)
 
    elevate ?scriptname? ?command-line-arg...?

Raise the current Ticol interpreter elevation level if running on Windows
XP, 7 or higher by spawning a new instance of the Ticol interpreter

Elevating executables (other than ticol.exe) is not supported

You will be prompted for UAC if necessary. If the console or launching
process is already elevated then the [elevate] command will be ignored

Examples:

    # With no script loaded ########################                            
                                                                                
    # Elevate the current session                                               
    elevate                                                                     
    # Elevate session and does not run autoexec.tcl                             
    elevate /na                                                                 
                                                                                
    # With script loaded ###########################                            
                                                                                
    # Elevate the loaded script and run it                                      
    elevate                                                                     
    # Elevate and run the loaded script, doesn't run autoexec                   
    elevate /na                                                                 
    # Elevate the script 'foo.tcl'                                              
    elevate foo.tcl                                                             
    # Elevate the script 'foo.tcl' (filetype is optional)                       
    elevate foo                                                                 
    # Elevate the script 'foo.tcl' with no autoexec.tcl                         
    elevate foo /na                                                             
    # Elevate the script 'foo.tcl' with 3 arguments                             
    elevate foo arg1 arg2 arg3                                                  
    # Elevate the script 'foo.tcl' with 3 arguments, no autoexec                
    elevate foo arg1 arg2 arg3 /na                                              

[elevate] can elevate any script which has been pre-loaded using [load]
or will elevate specified scripts together with their optional arguments.
[elevate] can also be used to run a script in elevated mode from the
Ticol CLI

Prompts depend on the current UAC setting to determine wither or not
user-interaction may be required to grant elevated permission. Ticol 
cannot override this,

[elevate] returns a boolean indicating whether the session is/was elevated
successfully

To exit an elevated Ticol CLI with no active script:

    type exit and press ENTER

Elevation will work differently between Windows XP and 7 and this also 
depends on the service pack level for Windows XP. To kill an elevated 
instance of Ticol you will need to run pskill.exe from an elevated command 
prompt. Elevation may block 64-bit console applications from running 
properly on a 64-bit system.

Use [is_elevated] to test whether or not a script is elevated

The following snippet of code is usually all that is required to elevate
and continue running a script...

    if {! [is_elevated]} {                                                      
       elevate                 # Elevate and re-call with any args              
       exit                    # We continue, when elevated (not here)          
    }                                                                           

As the spawned, elevated instance of Ticol will close automatically on
exit you may wish to use this snippet to prevent the script from closing

    option expression off                                                       
    if {is_elevated} {                                                          
       pause                                                                    
    }                                                                           

Example:

    puts [elevate]                                                              

Result:

    1                                                                           

Example:

    if( ! [is_elevated] } {                                                     
       elevate                                                                  
    } else {                                                                    
       puts "[escape [info script] is running elevated"                         
       # ...                                                                    
       pause                                                                    
    }                                                                           

Result:

    # If UAC is accepted then will display                                      
    <path-to-script> is running elevated                                        

Example:

    load test           # Contains [elevate] command as shown above             
    run                                                                         

Result:

    # The script is run in elevated mode                                        

Example:

    load tests           # No [elevate] command present                         
    elevate              # Elevate and run                                      

Result:

    # The script is run in elevated mode                                        

Chaining $argv to an elevated script
------------------------------------
The expansion operator can be used to pass a concatenated $argv() array

    if {! [is_elevated] } {                                                     
       set cmdline ""                                                           
       if { [array is_set argv] } {                                             
          for {set i 0} {< $i $argc} {++ i} {                                   
             append cmdline "$argv($i) "                                        
          }                                                                     
       }                                                                        
       elevate {*}$cmdline                                                      
    } else {                                                                    
       newline                                                                  
       textcolor green                                                          
       puts "* [escape [info script]] is now elevated!\r\n"                     
       textcolor                                                                
       # ... further commands ...                                               
    }                                                                           

See also: is_elevated, is_admin, spawn, exec, shell

Elevate Example Script (Windows 2000 or Higher)

The following script will create a combined Tcl and Windows batch script
which, when run will elevate a Windows executable (with optional arguments)
A script created by tcl2bat.bat is insufficient to perform this task

Use as (example):

    elevate.bat "explorer.exe /e /select,c:\windows"
    elevate.bat "notepad %temp%\hello.txt"
    elevate.bat "ticol.exe /na"

Script:

    ::set - {                                                                   
    @echo off                                                                   
    REM ####################################################################    
    REM File:      elevate.bat                                                  
    REM Purpose:   Command-line self-chaining batch script for Ticol Tcl        
    REM Language:  Windows BATCH script and Ticol Tcl v1.26+                    
    REM ####################################################################    
    set SCRIPT=%~dp0%~nx0                                                       
    ticol.exe %SCRIPT% /na %1 %2 %3 %4 %5 %6 %7 %8 %9                           
    exit /b 2> nul                                                              
    REM Windows NT4.0 does not support exit /b so we must goto end              
    goto end                                                                    
    REM End Batch ##########################################################    
    }                                                                           
                                                                                
    option expression off                          # Use non-expr style         
    if {! [is_elevated] } {                                                     
       set cmd ""                                                               
       if { [array is_set argv] } {                                             
          for {set i 0} {< $i $argc} {++ i} {                                   
             append cmd "$argv($i) "                                            
          }                                                                     
       }                                                                        
       elevate {*}$cmd                                                          
    } else {                                                                    
       set cmd ""                                                               
       if { [array is_set argv] } {               # Array to list               
          for {set i 2} {< $i $argc} {++ i} {                                   
             set arg_v [array item argv $i {}]                                  
             if {! [eq $arg_v ""]} {                                            
                append cmd " $arg_v"                                            
             }                                                                  
          }                                                                     
       }                                                                        
       # We MUST unescape any outgoing path otherwise path supplied             
       # to Windows will be incorrect                                           
       exec {*}[unescape $cmd]                                                  
    }                                                                           
    /* # End Tcl / EOF #                                                        
    :END                                                                        
    REM */                                                                      

See also: elevate, is_elevated, escape, exec, spawn, array, append,
          expand, {*}

Executable-Only Code Obfuscation (ticol.exe filename /c)
 
* For the ticol_tcx.dll plugin see: tcx plugin

Code obfuscation/encryption or code-protection is available. This encodes
a Ticol source file to prevent inspection and tampering. You should only
run protected and unprotected Ticol scripts from a source you trust.

Code obfuscation is designed to deter casual tampering, not a determined
or professional hacker. The encryption mechanism used is not industrial
strength but nor is it trivial. However, Tcl naturally exposes portions
of any script so parts of a loaded script may still be visible. TCX 
encoding merely protects the source script from casual tampering.

This option can apply only to a single Ticol file module. TCX files can be
run either from the command-line or from the Ticol CLI console

Where an output isn't specified when encoding, the filename will be used
with the filetype-suffix ".tcx". When running .tcx files the filetype must
be given

The code is obfuscated and randomised. e.g. (hex dump)

000000 2C 15 72 09 1F 46 1B 40 - 78 77 66 0C 0F 7F 7F 0A ,.r..F.@xwf..+       
000010 29 7A 3F 4E 62 6A 2A 33 - 7B 05 6E 0F 41 77 16 3A )z?Nbj*3{.n.Aw.:       
000020 7C 2D 6E 13 68 6F 4C 15 - 7B 77 5F 40 58 28 13 7D |-n.hoL.{w_@X(.}       
000030 29 21 47 60 72 22 76 71 - 1A 1E 75 2A 39 34 47 4A )!G`r"vq..u*94GJ       

Advantages of Obfuscation
-------------------------
Scripts may be emailed or transmitted securely. The contents are tamper-
proofed which means their safety can be verified if the source is known
and trusted and if checked using MD5 signatures. Source code and IP is
not divulged to third-parties. MD5 signatures should always be used to
check

Macro PreProcessing, other than processing backslash escape sequences is
not performed in advance. These are not processed as the MPP will be run
a 2nd time on the TCX file and strings with resolved \n sequences would be
invalid. Advanced processing enables advance checking of basic syntax
before runtime.

Comments are removed in TCX files, leading to smaller files

Disadvantages of Obfuscation
----------------------------
Obfuscated scripts could hide malicious code if run from untrusted sources
You should never run a tcx script from an untrusted source nor if the MD5
is not verified. Preferably reserve protected code for internal use only.
Treat protected Ticol scripts the same as unverified EXE files

Use the sample MD5 check BAT file as a specimen for checking against a
list of known scripts

Caution: The macro, "#exec" can cause malicious scripts to be executed not
only when scripts are run but when they are encoded using /C since the 
Macro PreProcessor will be invoked when encoding unless /NP is used

You should only run (including obfuscate) scripts you trust and preferably
only ones you generate and lock-down yourself

[info body] can reveal [proc] source information at runtime

Caveats on Code Protection
--------------------------
Although the encryption type can be divulged, protected scripts cannot
be decrypted back to the full, original source. The /ECHO command is
disabled. This is by design. You MUST retain unencrypted copied of any
encrypted source code

When running in breakpoint/debug mode the individual commands are shown
at each breakpoint but the script summary is unavailable for TCX files. 
Likewise, the commands [commands], [functions] and [procs] will reveal
the names of loaded code but not the code itself

As the command [info body] is necessary for some scripts to run it is
not blocked and will reveal code snippets for procedures only. The main
code and its full structure are not revealed

Security Verification
---------------------
The validity of any decoded TCX block or file should be tested by checking
the code's MD5 value to ensure it matches what is expected.Use an external
MD5 checking application or Ticol's /MD5 argument to generate a checkable
MD5 signature.

Example:

    ticol.exe /md5 test.tcx   # Generate an MD5 for an existing file            
    ticol.exe foo.tcl /C      # Generate foo.tcx encoded file                   
    ticol.exe foo.tcl /C /64  # Generate TCX block file (foo.t64) and MD5       

Result:

    AC6960741A575B1C0928EEE817D72272                                            
    * Encrypt: Successfully created obfuscated output file                      
        C:\tcl\foo.tcx                                                          
        MD5 is: 0823aceda778c586e1ee072c2f7d9919                                
    * Encrypt: Successfully created obfuscated output file                      
        C:\tcl\foo.t64                                                          
        MD5 is: 14701fd2324ee68888b1238808ad3ae8                                

See TESTMD5.BAT for a specimen script to automatically check a TCX file
before executing

Encryption Syntax
-----------------
ticol.exe tclfile.tcl ?output-file? /C?:<option>? ?/PW:<password>?

The command-line option /C takes the following arguments
Case is ignored for all options other than /C:PASSWORD

 /C:PASSWORD     Requires /PW:<password> to be successfully encrypted           
 /PW             Values are optional for the remainder and if omitted,          
                 Local workstation/user details, Tcl version will be used       
                                                                                
 /C              Automatic encryption. No run restrictions. Can be run on       
                 any workstation and by any user. Source cannot be viewed       
                 or edited                                                      
 /C:VERSION      Encrypt to be run only with a specific version of Ticol        
 /C:PASSWORD     Uses a password specified using /PW:<password>                 
                 Will not execute unless password is specified                  
 /C:MAC          Encrypt on the basis of a MAC address which must be            
                 present on the executing system                                
 /C:IP           Lock to an IP address specified by /PW                         
                 All valid addresses are checked on decryption                  
 /C:USER         Encrypt to be run by a current Windows username                
 /C:HOST         Encrypt to be run on a specific workstation name               
 /C:WINVER       Encrypt to be run on a specific windows version (x.xx)         
                 Controlling WinVer execution is better done in real time       
 /64             Base64 encode TCX output when used with /C (above)             
                 and save as a text file with a '.t64' filetype                 
 /I              Will display the encryption mode info for a protected file     
 /MD5:<filename> Produce an MD5 string for any file. This may be used by        
                 an external script to verify a Ticol file which is about       
                 to be executed                                                 

Operation Syntax
----------------
No arguments are required to run a protected file except /C:password

    ticol.exe filename.tcx               # Run a default-encrypted file         
    ticol.exe filename.tcx /pw:secret    # Run a password protected file        
    ticol.exe filename.tcx /i            # Show obfuscation info                
    ticol.exe /md5:filename.tcx          # Show a tcx file's MD5                

The encryption type is automatically detected. The only mode for which a
password can be specified is that encrypted using /C:PASSWORD mode

tcx files may be associated with ticol.exe to run as executable files. e.g.

    C:\>hello.tcx                                                               
    Hello World                                                                 

    in such cases command-line arguments my not be passed properly

Code Protection Examples
------------------------
                                                                                
 ticol.exe foo.tcl /c                      # Obfuscated (no run restrictions)   
 ticol.exe foo.tcl /c:mac /pw:01020365A690 # Run only on MAC 01020365A690       
 ticol.exe foo.tcl /c:user /pw:admin       # Run only for user 'admin'          
 ticol.exe foo.tcl /c:host /pw:server1     # Run only on host PC server1        
 ticol.exe foo.tcl /c:version /pw:1.27     # Run only on Ticol v1.27+           
 ticol.exe foo.tcl /c:password /pw:secret  # Run only with this password        

Result:

Creates 'foo.tcx' which can be run using the command:    

    ticol.exe foo.tcx                                                           

The MD5 signature of the new file will be displayed if successful. This
should be stored if necessary for use with script validation

Creating Base64 Blocks for the [tcx] Command
--------------------------------------------
Adding the /64 argument to /C will output to a file in base 64-encoded
format suitable for use with the [tcx] command provided by the 
ticol_tcx.dll plugin. See also: tcx plugin

CGI Mode
--------
TCX scripts may be used as web-server CGI applications. The embedded
TCX block may be wrapped to a fixed line length and delimited with
newline characters if desired. These will be ignored

TCX blocks embedded in a TCL script may be executed using [tcx run]
or decoded using [tcx decode] and then run using [eval]

Example:

    set s "\                                                                    
    bOevxqW17U\                                                                 
    eCvogqO3Ju\                                                                 
    4c5Asi/FpO\                                                                 
    UdXWNsHPtH\                                                                 
    QfHX6HdMut\                                                                 
    Fr4JT82PtF\                                                                 
    oOA+Ax53YI\                                                                 
    wheQsssDIq\                                                                 
    A3g="                                                                       

Security
--------
Cryptographic features in this program are not cryptographically secure 
and should not be relied on to secure confidential information

Obfuscation or Encryption?
--------------------------
You can decide which term is more appropriate
    http://csharptest.net/865/obfuscation-is-not-encryption/index.html

See also: tcx plugin, run, load, Ticol, md5, md5_file

Pseudo-Assembler Interpreter Plugin (TicASM)

Load using: lib ticol_asm?.dll?

    string [asm string ?-debug?]

This plugin allows you to interpret 'assembler-like' ASM scripts
The assembler interpreter is reasonably fast. There is a Tcl script
version of this interpreter (asm.tcl) which is about an order of
magnitude or two slower than the Ticol plugin DLL version

The language is a pseudo-assembler it has an assembler-like language
and works very much like a normal assembler but is not bound by CPU
limitations, and registers are able to operate using float/double
values as well as integer. It has less functionality than a real
assembler and it is important to note that some of the constructs
and paradigms differ from those of a real assembler. In particular, 
dereferencing [VAR] or [REG] differs

Performance
-----------
A minimalist language can perform quite efficiently. A benchmark fractal
script takes 320ms for a compiled EXE, 1605ms for a Ticol interpreted
script and only 400ms for a TicASM interpreted script. Which shows that
the processing overhead is minimal compared to native machine code

  Advantages
  ----------
* A useful prototyping and experimental tool
* May prove useful as a learning tool
* ASM code can run much faster than Ticol TCL
* Errors trace opcodes and line numbers for debugging
* Would probably not be too difficult to write a compiler for TicASM
* External DLLs may be linked directly from ASM
* The Ticol Tcl interpreter can be called from ASM
* A small number of CRT functions are emulated in CALL
* External __cdecl DLLs may be called e.g. msvcrt40.dll

  Disadvantages
  -------------
* Experimental, not for production use
* Assembler is hard, code is bulky and extremely verbose
* There is no sophisticated debugger for this version
* Can't compile this language to machine code
* Difficult to debug and write error-free code
* Lack of full API/library support although Ticol libs can be called
* An 'assembler-like' language, not a true assembler
* Opcodes differ from those of a real physical CPU
* Paradigms differ in some subtle ways from 'real' ASM

The following opcodes are supported:

    ADD AND CALL CMP DB DEC DUMP END FADD FCMP FDIV FMOD FMUL
    FPOP FPUSH FSUB HALT INC INT JG JGE JL JLE JMP JNZ JZ MOV
    MUL NOP NOT OR ORG POP PUSH RET SHL SHR SUB TRNC XOR

The language can be extended in the future with no runtime overhead

There are 4 64-bit/float/real registers: AX, BX, CX, DX,
a data index pointer EDP, which references the memory block, and 
instruction pointer EIP. EIP can't be modified but EIP can be 
addressed the same as other registers

Memory is allocated via a 'tape' of 64Kb similar to 'Brainfuck'
and is indexed by EDP. A separate execution stack of 32kb is kept
for PUSH, POP, CALL and RET (CALL pushes the return address to
the stack, RET will POP it off and resume at that address)

The data stack works in reverse order to normal. Additional memory
is used by adding to EDP rather than subtracting

Comments are supported, prefixed by a semi-colon and C++ style long
/*...*/ comments are supported

A __LINE__ macro will substituted into the source code line as an integer

ORG is supported and it understands ORG <hex>h, decimal and 0xNN
The J* instructions currently understand only labels so ORG is ignored

There are no cast modifiers to opcodes (qword, dword, byte ptr) etc.
Float values may be truncated (rounded), to integer if necessary using
the TRNC instruction. Otherwise a register will store an 8 byte double
value where a floating point number is detected. Future versions of
the language may have more sophisticated number type casting. Double/
float values can be stored only in literals, registers or as BCD strings

    mov ax, 123456      ; AX stores 64-bit integer                              
    mov ax, 123.456     ; AX stores 8-byte double                               

Literal integer values may be presented as Hex (0xNN), 
Octal (0oNN) or binary (0bNN), e.g.

    mov ax, 23                                                                  
    mov ax, 0x17                                                                
    mov ax, 0o27                                                                
    mov ax, 0b10111                                                             

Three standard Tcl/C++ escape characters are understood in literal
strings used by DB, these are \t (TAB), \r (0x0d), \n (0x0a)

    e.g. db ? "Hello\tworld!\r\n"

Loading
-------
The source is 'compiled' to a bytecode memory image at load time
Basic checks are made on syntax etc. No further interpretation is
performed at runtime other than variable lookup

Returning Values to Tcl
-----------------------
Returns can be made to Ticol Tcl via a BCD string indexed via the AX
register. To avoid 'accidental' returns It is suggested that AX is cleared
on exit using xor ax,ax, and the first byte of the data table is set to
NULL using: db ? "" as the first entry

    lib ticol_asm                                                               
    buf:               ; Declare variable buf                                   
    mov buf, edp       ; Set buf address                                        
    db ?, "12345"      ; Store a value at buf address                           
    mov ax, buf        ; Return address of buf in AX                            

Calling Tcl Commands
--------------------
Tcl routines can be called from TicASM using 'call eval'
A buffer of sufficient size to handle the result must be declared and
the buffer plus length arguments pushed in right-to-left order on the
stack before calling

    lib ticol_asm                                                               
    asm {                                                                       
       expr:          ; Define a variable                                       
       buf:           ; Define a variable                                       
       mov expr, edp                                                            
       db ? "puts [/ 22 7.0]"                                                   
       mov buf, edp                                                             
       db 256, ""                                                               
                                                                                
       push 256       ; Push the buffer size                                    
       push buf       ; Push the buffer address                                 
       push expr      ; Push the expression address                             
       call eval      ; Call the Ticol Tcl [eval] command                       
                                                                                
       mov ax, buf    ; Print out the result: '3.142857142857143'               
       call 9                                                                   
    }                                                                           
    lib ticol_asm -unload                                                       

Calling External DLLs
---------------------
External DLLs may be called. Subject to the limits of the interface
e.g. MSVCRT40.DLL has been tested. (See: asm instruction set)

Precision
---------
The underlying data type is IEEE double. Integer precision is limited by
double (float) representation to 52 bits or 16 digits. This is less than
that offered by 64-bit integers but represents a compromise. No exceptions
are generated where precision is lost. Code should check/guard against

Performance
-----------
For comparison. A standard Mandelbrot fractal test which takes around 250
to 300 milliseconds as a compiled EXE takes about 420 milliseconds to run
in TicASM as an ASM script. The ASM.TCL script will run the same ASM 
script in about 13 minutes The corresponding Tcl version of the same
code runs in 1760 ms which means that for many tasks ticol_asm.dll could
be much faster

    Standard Mandelbrot Test

    Approx     Implementation Type
    Time/ms
        300    Compiled EXE
        420    Ticol ASM Plugin running fractal.asm
      1,760    Ticol interpreting fractal.tcl
    780,000    Ticol asm.tcl interpreting fractal.asm

The specification is experimental and subject to revision and improvement

If curly-braces are avoided in the source then the entire source, including
double-quotes may be braced and set within a Tcl variable and included
within a Tcl script, the return from which can be returned to Tcl

Comments and whitespace affect only load-time not run-time performance

Example Calls:

    lib ticol_asm                # Load the interpreter plugin                  
    set script [readfile fractal.asm]                                           
    asm $script                                                                 
    lib ticol_asm -unload                                                       

                                                                                
    lib ticol_asm                # Load the interpreter plugin                  
    asm {                        ; We are now using ASM syntax                  
        msg:                                                                    
        mov msg, edp             ; Point var 'msg' to current EDP location      
        db ? "Hello world\n"     ; Initialise buffer space                      
        mov ax, msg              ; Point AX to the address in 'msg'             
        call 9                   ; Call print function                          
    }                                                                           
    lib ticol_asm -unload        # Unload the interpreter plugin                

Example Code:

    lib ticol_asm                # Load the ASM interpreter plugin              
                                                                                
    set r [                      # Note square bracket here                     
        asm {                    ; Approximation to PI                          
            buf:                 ; Define a variable                            
                                                                                
            mov buf, edp         ; Set buffer address                           
            db 21, ""            ; Define buffer storage                        
                                                                                
            mov ax, 22           ; AX = 22                                      
            mov bx 7             ; BX = 7                                       
            fdiv ax, bx          ; Float divide AX / BX                         
            mov [buf], ax        ; Copy AX to string buffer                     
            mov ax, buf          ; Index AX to buffer                           
            end                  ; and return                                   
       }                                                                        
    ]                            # Note square bracket here                     
                                                                                
    assert $r {== $_ 3.142857} -v -line #__LINE__                               
                                                                                
    lib ticol_asm -unload ; Unload the interpreter plugin                       

Example Code:

    lib ticol_asm                # Load the interpreter plugin                  
    asm {                                                                       
        org 100h                 ; Example only, not required                   
                                 ; as we won't JMP < 0x100                      
        msg:                     ; Declare a variable/label                     
        buf:                     ; Declare a variable/label                     
                                                                                
            db ? ""              ; Ensure AX is not printable on return         
            mov msg, edp         ; msg points to this string (EDP)              
            db ? "Loop: "        ; Define a string (6 bytes+null)               
            mov buf, edp         ; buf points to EDP                            
            db 21, 0             ; 21 byte buffer to store number               
            mov ax, 4            ; Initialise AX                                
            mov bx, 0            ; Initialise BX to counter                     
                                                                                
        L1:                      ; Declare a label                              
            push ax              ; Save AX                                      
            mov ax, msg          ; Point AX to our message                      
            call 9               ; Print string                                 
            inc bx               ; Increment BX                                 
            mov [buf], bx        ; Store BX value in our buffer                 
            mov ax, buf          ; Point AX to our printable buffer             
            call 9               ; Call print address at AX                     
            mov ax, 10           ; CR (newline)                                 
            call 2               ; Call print char                              
            pop ax               ; Restore AX                                   
            dec ax               ; Decrement AX                                 
            jnz L1               ; If not zero then loop L1                     
            xor ax, ax           ; Clear AX                                     
            end                  ; Finish                                       
    }                                                                           
    lib ticol_asm -unload        # Unload the interpreter plugin                

Result:

    Loop: 1                                                                     
    Loop: 2                                                                     
    Loop: 3                                                                     
    Loop: 4                                                                     
                                                                                
    Execution took 1 ms                                                         

Development
-----------
TicASM is in a relatively early state of development
There is potential to write a Tcl to ASM transcoder
There is also potential to write a true assembler for the language

For more information, see the separate documentation

See also: asm instruction set, plugins, brainfuck

TicASM Instruction Set (Plugin)

See: Ticol Assembler

The opcodes are similar to those in a normal CPU based assembler but the
goal is not the compilation of object code but fast execution of an 
interpreted script language at as close a speed as possible to that of a
compiled executable

Things to note:

* This is not a real assembler. Syntax will differ
* Registers may contain either float (real) numbers or 64-bit integers
* Variables and labels are interchangeable (same object type)
* Variables and labels must be declared and are declared by first
  reference in the source with a colon suffix. e.g. var:
* Variables are integer-only and cannot contain float (real) numbers
* Registers and memory storage may contain strings and real numbers
* A variable may point to a location with allocated memory which can be
  dereferenced to retrieve    or set the contents of that location
* Variables should be initialised to zero using, say, MOV VAR, 0
* An allocated memory location may contain either a integer or float 
  number, or an ASCIIZ string
* Dereferencing a register will retrieve or set a single byte at
  the address location referenced by the register. Dereferencing a 
  variable will fetch a BCD-translated number (integer or float)
  [REG]                  Fetch a single char
  [VAR]                  Fetch an integer or float value (if translatable)
* Both source and target operands determine whether the result will be
  restricted to integer. e.g. MOV VAR, [VAR] is restricted by target type,
  MOV [VAR], VAR is restricted by source type
* Memory is allocated using DB
  As with C++, a 1 byte is added to the requested storage size, which will
  contain a NULL byte terminator character (0x0). This should be allowed-
  for if incrementing memory address pointer locations
* A variable may  be incremented, decremented or set to point to any valid
  memory location. You are responsible for overflow checking
* Fixed, automatic and fill modes are supported by DB
  DB 100, "x"          ; Allocate 100 bytes, repeat fill with char "x"
  DB 100, "foo"        ; Allocate 100 bytes, repeat fill with string "foo"
  DB 3, "foo"          ; Allocate 3 bytes, fill exactly with string "foo"
  DB 100, ""           ; Allocate 100 bytes, repeat fill with NULL chars
  DB ?, "Hello"        ; Allocate 6 bytes and fill exactly with "Hello\0"
  DB AX, "x"           ; As for DB 100,"x"
  A buffer can't be allocated to a size larger than a fill string with
  zero fill repeat. In such cases, allocate to NULL ("") and copy later
* A DB fill of NULL bytes is achieved using a null string "" argument
* After memory is allocated using DB, the EDP register will be
  incremented to the next location after the NULL terminator character
* Items are stored in memory locations as ASCIIZ strings
* CALL can only call a label value or a preset library function at present,
  not an arbitrary integer address within the source
* ORG may be set but currently has no usage
* OP [VAR], [VAR] performing location-to-location byte transfers is not
  supported. Use intermediate registers to facilitate memory content
  transfers. Character strings must be copied one character at a time using
  [REG] to dereference the character.  (Alternately use: call strcpy)

    mov ax, [var1]    ; Copy a BCD double or integer                            
    mov [var2], ax                                                              
                      ; Copy x1 char from var1 to var2                          
    mov ax, var1      ; Point AX to source location                             
    mov bx, var2      ; Point bx to target location                             
    mov cx, [ax]      ; Fetch x1 byte from source to CX                         
    mov [bx], cx      ; Write x1 byte from CX to target                         

  [VAR]            Dereference the integer variable address VAR to access
                   the contents in allocated memory
  [REG]            Retrieve a single byte from the address location indexed
                   by REG
* Literal values may be decimal, hexadecimal (0xNN), octal (0oNN), or
  binary (0bNN). The ORG instruction understands <HEX>h
* TRNC (truncate) is an approximate rounding which will truncate a float
  value to N decimal places. TRNC is useful with FCMP to bound a value to
  some known, comparable range
* Comma separators between operands are not mandatory, 'MUL AX 12' is ok
* The underlying stack type is a 64-bit integer stack
* Float values can be stored on the stack using FPUSH, FPOP
* FPUSH *must* be followed by FPOP unless the result is to be discarded
  in which case you can POP into a discarded register

The following opcodes are supported:

  Instruction        Description
  -----------------------------------------------------------------------
  ;                  Comment delimiter
  Add:               Integer addition of two values (See FADD for float)
  ADD REG1, REG2     Integer add register 2 to register 1
  ADD REG, N         Add integer value N to register
  And:               Bitwise AND two integer values
  AND REG1, REG2     Bitwise AND register 1 with register 2
  AND REG, N         Bitwise AND register with value N
  Call Label         Execution jumps to the specified label
                     The return address is pushed onto the stack
                     Calling functions should preserve the return address
  CALL <function>    Call an emulated "C" runtime function
  CALL dll_open      Open (load) a standard __cdecl DLL
  CALL dll_run       Execute a function call in a DLL loaded by dll_open
  CALL dll_close     Close a DLL loaded by dll_open
  CALL N             Call emulated O/S function
  Db:                Define bytes (allocate memory) as follows:  
  DB ? <string>      Allocate string length bytes and copy string
  DN N <string>      Allocate N bytes, copy as much of string as will fit
                     If string is shorter then the string is repeated
  DB REG, <string>   Allocate REG bytes. Behaviour as for DB, N
  DEC REG            Integer decrement the contents of register REG
  DUMP               Display register and variable contents etc.
  END                Immediately end script processing
  Fadd:              Add two float values
  FADD REG1, REG2    Float add registers 2 to register 1
  FADD REG, N        Add value N to register contents
  FADD REG, VAR      Add the integer contents of VAR to register REG
  Fcmp:              Compare two float values. FCMP is an alias for FSUB
  FCMP REG1, REG2    Compare register 1 with register 1 (corrupting 1)
  Fdiv:              Float divide A by B, giving A
  FDIV REG1, REG2    Float divide REG1 by REG2, giving REG1
  FDIV REG, N        Float divide REG by N, giving REG
  FDIV REG, VAR      Divide register REG by the integer contents of VAR
  Fmod:              Modular float divide two values and return the result
  FMOD REG1, REG2    Modular float divide register 1 by register 2
  FMOD REG, N        Modular float divide register by value N
  FMOD REG, VAR      Modular float divide register by integer variable
  Fmul:              Float multiply A by B, giving A
  FMUL REG1, REG2    Float multiply register 1 by register 2
  FMUL REG, N        Float multiply register by value N giving register
  FMUL REG, VAR      Multiply the integer contents of VAR to register REG
  FPOP REG           POP stack to REG as float. Must be pushed with FPUSH
  FPOP VAR           POP stack to VAR as float. Must be pushed with FPUSH
  FPUSH REG          PUSH float REG to stack. Must be popped with FPOP
  FPUSH N            PUSH float N to stack. Must be popped with FPOP
  FPUSH VAR          PUSH float VAR to stack. Must be popped with FPOP
  Fsub:              Subtract two values
  FSUB REG1, REG2    Subtract value of register 2 from register 1
  FSUB REG, N        Subtract value N from register
  FSUB REG, VAR      Subtract an integer variable from register
  HALT               Pause execution
  INC REG            Integer increment a register value
  INT N              Emulated/simplified O/S interrupts (See below)
  J* label:          Jump operations can jump only to a label not an integer
  JG LABEL           Jump to label if last op was > than zero
  JGE LABEL          Jump to label if last op was >= than zero
  JL LABEL           Jump to label if last op was < than zero
  JLE LABEL          Jump to label if last op was <= than zero
  JMP LABEL          Unconditionally jump to label
  JNZ LABEL          Jump to label if last op was == zero (Z flag set)
  JZ LABEL           Jump to label if last op was != zero (Z flag not set)
  Mov:               Move data from one location to another
  MOV REG, REG       Move contents of the right hand register to the left
  MOV REG, N         Move the float or integer value N to register
  MOV REG, VAR       Move the integer contents of variable to register
  MOV REG, [VAR]     Move float or integer value from variable to register
  MOV VAR, REG       Move the contents of register to variable as integer
  MOV VAR, N         Move value N to variable as integer
  MOV VAR1, VAR2     Move value from variable 1 to variable 2
  MOV VAR1, [VAR2]   Move value at variable 2 to variable 1
  MOV [VAR], REG     Move value of register to variable at location VAR
  MOV [VAR], N       Move value N to location at VAR
  MOV [VAR1], VAR2   Move value from variable VAR2 to location at VAR1
  MOV [VAR1], [VAR2] Not supported. See mov [var], var; or mov var, [var]
                     May be implemented later as a byte copy
  MUL A, B           Integer multiply A by B
  MUL REG1, REG2     Integer multiply register 1 by register 2
  NOP                No operation (ignored)
  NOT REG            Bitwise invert integer in register
  NOT VAR            Bitwise invert integer value
  Or:                Bitwise OR two integers
  OR REG1, REG2      Bitwise OR integer register 1 with register 2
  OR REG, N          Bitwise OR integer register with value N
  OR REG, VAR        Bitwise OR integer register with integer value
  ORG N              Set the code origin (once only per script)
  POP REG            Retrieve value from stack and assign to register
  PUSH REG           Push register value to the stack. Leave REG unchanged
  RET                Return from CALL. Pop the return address from the stack
  Shl:               Integer bit shift left two integers
  SHL REG1, REG2     Integer bit shift left register 1 by register 2
  SHL REG, N         Integer bit shift left register by integer value N
  SHL REG, VAR       Integer bit shift left register by integer variable
  Shr:               Integer bit shift right two integers
  SHR REG1, REG2     Integer bit shift right register 1 by register 2
  SHR REG, VAR       Integer bit shift right register by integer variable
  Trnc:              Truncate value A at B digits after the decimal point
  TRNC REG1, REG2    Truncate float value in register 1 by register 2
  TRNC REG, N        Truncate float value in register by value N
  TRNC REG, VAR      Truncate float value in register by integer variable
  Sub:               Integer subtract 2 items
  SUB REG1, REG2     Integer subtract register 2 from register 1
  SUB REG, N         Integer subtract value N from register
  SUB REG, VAR       Integer subtract value in variable from register
  Xor:               Integer XOR two items
  XOR REG1, REG2     Integer xor register 2 with register 1
  XOR REG, N         Integer register 1 with value N
  XOR REG, VAR       Integer register with value in variable

Math opcodes currently operate only on registers not variables

The emulated O/S layer is in very early stages of development and may be
changed substantially to bring it more into line with real API code

Abstract Data Types (ADTs)
--------------------------
Arrays, structs, stacks, heaps, queues etc. must be emulated in the same
way as they would using any assembler. It is not anticipated that TicASM
will be used for particularly complex programs which feature such ADTs

Emulated "C" (CRT/C Runtime) Functions
--------------------------------------
A small number of essential C runtime functions are emulated and can be
called directly using CALL. The function prototypes are the same as
for the C functions except that strchr, and strstr return an integer
character position not a char* offset. For features not linked directly
it is possible to call MSVCRT.DLL via calldll_*

Unless stated, any required argument(s) should be pushed onto the stack in
__stdcall reverse order (right to left). These functions will clean up the
stack. A return value (if any) will be in register AX

    dtoa             Convert double (float/real) to string 
                     Buffer should be at least 21 bytes
                     dtoa(integer buffer buflen)
                     fpush: buflen, push: buffer double

    flush            Flush stdout, stdin and stderr
                     No arguments, no return

    free             Free memory
                     AX: Address value returned by malloc
                     No return

    eval             Call Tcl [eval] to run a Tcl command / script
                     Returns string result in a buffer
                     Defaults of 0 (NULL) may be passed for buffer/length
                     integer eval(argument-string buffer buflen)
                     push: argument-string buffer buffer-length
                     AX: Returns Tcl error code

    getvar           Get a simple Tcl variable (arrays not supported)
                     string getvar(varname buffer length)
                     push: length buffer varname
                     AX: Returns buffer address (NULL on error)

    gotoxy           Locate the cursor at X, Y
                     void gotoxy(x y)
                     push: y x
                     No return

    itoa             Convert integer to string
                     Buffer should be at least 21 bytes
                     itoa(integer buffer buflen)
                     push: buflen buffer integer

    malloc           Allocate memory
                     AX: Bytes to allocate
                     AX: Returns memory handle/address (0 on failure)

    memset           Erase (set) a block of memory
                     void memset(address char length)
                     AX: Memory address to write 
                     BX: Char value to set (integer)
                     CX: Length of block to write
                     AX: Return from malloc

    printf           Print an ASCIIZ string
                     integer printf(string ...)
                     AX: Returns index to string

    setvar           Set a Tcl variable
                     varname must be a declared memory location
                     integer setvar(varname value)
                     push: value varname
                     AX: Returns Tcl error code

    strchr           Index presence of a character in a string
                     String must be a declared memory location
                     integer strchr(string char)
                     push: char string
                     AX: Returns base 1 integer index

    strcmp           Compare one string to another (case-sensitive)
    strcmpi          Compare one string to another (case-insensitive)
                     Strings must be a declared memory location
                     integer strcmp(string compare)
                     push: compare string
                     Returns standard "C" integer
                     0 equal, >0 greater-than, <0 less-than

    strcpy           Safe copy bytes from one location to another
                     Source and target must be declared memory locations
                     The new target string length is returned
                     integer strcpy(target source length)
                     push:  length source target

    strlen           Return the length of an ASCIIZ string
                     string must be a declared memory location
                     integer strlen(string)
                     push: string
                     AX: Returns integer string length

    strstr           Index presence of a substring in a string
                     string must be a declared memory location
                     integer strstr(string substring)
                     push: substring string
                     AX: Returns base 1 integer index

    unlink           Delete a file
                     string must be a declared memory location
                     filename must be a declared memory location
                     integer unlink(filename)
                     push: filename
                     AX: Returns boolean success

External DLL Linkage
--------------------
Primary arguments are passed in registers (AX..DX), 
Optional arguments are pushed onto the stack
The functions perform internal cleanup of the stack (__stdcall)

    call dll_open       ; Load an external __cdecl DLL                          
                        ; DLL name in AX, handle returned in AX                 
    call dll_close      ; Close the handle of an open DLL                       
                        ; DLL handle in AX, boolean success in AX               
    call dll_run        ; Execute a DLL function which returns a long/int       
                        ; Result may be a string address                        
                        ; AX Handle                                             
                        ; BX Function name                                      
                        ; CX Arg count (not including fn name)                  
                        ; <stack> args in reverse order (R to L)                
                        ; AX integer return                                     
    call dll_run_double ; Execute a DLL function which returns a double         
                        ; As for dll_run: AX double return                      
    call dll_run_string ; Execute a DLL function which returns a string         

Emulated O/S Functions
----------------------
These require the argument to passed in register AX

    CALL 2         ; Print single byte
    CALL 3         ; Set the console colour (0..15)
    CALL 8         ; Print numeric literal
    CALL 9         ; Print ASCIIZ string 

Emulated MS/DOS / Windows Interrupts
------------------------------------
These expose "C" interfaces and require the argument to passed in
register AX. Unless specified, any return is in register AX
More features may be added in due course. Some functions are non-standard

    int 0x21, <ax> ; (Hex 21, Decimal 33)                                       
    int 0x21, 0x2  ; Print single byte indexed by AX                            
    int 0x21, 0x9  ; Print ASCIIZ string indexed by AX                          
    int 0x21, 0x0f ; Open file for sequential read or write                     
                   ; Filename indexed by BX, mode string CX                     
                   ; Standard "C" file modes ("rwa+")                           
                   ; Returns a "C" FILE* handle in AX cast to integer           
    int 0x21, 0x10 ; Close file opened with 0xf.                                
                   ; Pass file handle in BX                                     
                   ; Returns boolean in AX                                      
    int 0x21, 0x14 ; Sequential read file                                       
                   ; Handle in BX, buffer address CX, length DX                 
                   ; String buffer will not have NULL termination added         
                   ; Byte count returned in AX                                  
    int 0x21, 0x15 ; Sequential write file                                      
                   ; Handle in BX, string address CX, length DX                 
                   ; If DX == 0 then whole string will be written               
                   ; Byte count returned in AX                                  
    int 0x21, 0x2a ; Get current date as an ASCIIZ string (return in AX)        
    int 0x21, 0x2b ; Get current time as an ASCIIZ string (return in AX)        
    int 0x21, 0x2c ; Get the clock time in seconds (return in AX)               
    int 0x21, 0x30 ; Get clock time in milliseconds (return in AX)              
    int 0x21, 0x31 ; Get pseudo-random number (PRNG) (return in AX)             

The TicASM spec is experimental and subject to revision

See: ticol assembler

Embedding Ticol Tcl in C++ Programs (ticol.dll)

There are 2 potential methods of embedding/linking Ticol Tcl into other
language.

1) The use of a C++ static LIB file
2) Using a DLL file

At some future point a mechanism for producing compilable C++ source
code for embedded Ticol Tcl projects may be released, until then it is 
possible to call via the available 'ticol.dll' version of the interpreter.
The interface prototypes are not yet finalised and may be subject to 
revision.

The static LIB version is working but not yet publicly released since it
requires extensive documentation.

Example Calling ticol.dll
-------------------------
How to call the DLL version of the Ticol interpreter from C++ code...

// Prototype signature                                                          
LPSTR __stdcall ticol_command(LPSTR s,                                          
        const long opt_breakpoint=FALSE,                                        
        const long opt_stacktrace=FALSE,                                        
        const long opt_run_autoexec=FALSE,                                      
        const long opt_debug_mode=FALSE,                                        
        long* result=NULL)                                                      

                                                                                
// Example C++ calling ticol.dll (A __stdcall DLL)                              
// Ensure stack is sufficient for recursion (try 30,000-100,000)                
// Loading the DLL dynamically is straightforward                               
                                                                                
#pragma comment(linker, "/stack:0x300000")                                      
#include <windows.h>                                                            
#include <stdio.h>                                                              
                                                                                
int main(int argc, char** argv)                                                 
{                                                                               
  LPSTR ptr=NULL;                                                               
  long breakpoint=0;                                                            
  long stacktrace=0;                                                            
  long autoexec=0;                                                              
  long debug=0;                                                                 
  long err=0;                                                                   
                                                                                
  // Embedded Tcl source code:                                                  
  // DO NOT indent this multi-line literal!                                     
  // Some compilers may also restrict the size of string literals               
  // e.g. MSVC 5.0                                                              
                                                                                
    LPSTR script="\                                                             
textcolor magenta\r\n\                                                          
puts \"Hello world\"\r\n\                                                       
textcolor\r\n\                                                                  
";                                                                              
                                                                                
  // Declare funct*                                                             
  LPSTR (__stdcall* ticol_command)(LPSTR s,long,long,long,long,long*);          
                                                                                
  HINSTANCE hAPI=LoadLibraryA("ticol.dll");             // Load the DLL         
  if(hAPI)                                              // Check handle         
  {                                                                             
    ticol_command=(LPSTR (__stdcall*)(LPSTR,long,long,long,long,long*)) \       
        GetProcAddress(hAPI,"ticol_command");           // Load the export      
    if(ticol_command)                                   // If successful        
    {                                                                           
      printf("test: Loaded DLL ticol.dll->ticol_command()\r\n");                
                                                                                
      // Call Tcl ...                                                           
      ptr=ticol_command(script,breakpoint,stacktrace,autoexec,debug,&err);      
      printf("test: ticol_command: Returned result:'%s' errorcode:%li\r\n"\     
       ,(ptr?ptr:"NULL"),err);                                                  
    }                                                                           
    else                                                                        
    {                                                                           
      printf("test: Failed to ticol_command in ticol.dll\r\n");                 
      return 1;                                                                 
    }                                                                           
  }                                                                             
  else                                                                          
  {                                                                             
    printf("test: Failed to load ticol.dll\r\n");                               
    return 2;                                                                   
  }                                                                             
  return 0;                                                                     
}                                                                               

A Windows test interface is also provided to test ticol.dll 
(See dll interface)

See also: dll interface, plugins, Ticol

emulating C++ arrays

 Emulating C/C++ Arrays When Calling External DLLs

When using [calldll] to link to external DLLs it may be necessary to pass
a C/C++ "pointer array", i.e. an array of pointers rather than a fixed 
size array of strings. This sort of array is not natively supported by 
Ticol Tcl. However, it can be emulated using Ticol structs providing that
the called DLL treats the passed array as 'readonly'

    # Create an array of 4-byte "pointers" in a struct as successive            
    # addresses with individual elements in, say,, e1..e4                       
                                                                                
    struct a {                                                                  
        e1 4                                                                    
        e2 4                                                                    
        e3 4                                                                    
        e4 4                                                                    
    }                                                                           
                                                                                
    # Create string variables which will be mapped-to by the pointer array      
                                                                                
    set a1 "The"                                                                
    set a2 "quick"                                                              
    set a3 "brown"                                                              
    set a4 "fox"                                                                
                                                                                
    # Connect the pointer array elements to the string variables by             
    # storing the binary representation of the internal pointer address         
    # using [strptr]                                                            
    [struct setb] will set a binary representation rather than ASCII            
                                                                                
    struct setb a.e1 [strptr a1]                                                
    struct setb a.e2 [strptr a2]                                                
    struct setb a.e3 [strptr a3]                                                
    struct setb a.e4 [strptr a4]                                                
                                                                                
    # Call 'someFunct' in the DLL, passing the array size and the array         
    # N.B. the DLL must NOT reallocate the array or expand string contents      
                                                                                
    set r [calldll my.dll someFunct 4 a]                                        

See also: calldll, arrays, carray, struct, struct setb, strptr

encrypt

    string [encrypt string password]                                            
    string [encrypt var password -var]                                          
    string [decrypt string password]                                            
    string [decrypt var password -var]                                          

Encrypts either a string or variable (if the -var argument is used)

Encrypt a string or variable to a base64 string using moderate-strength
symmetric encryption. Encryption will result in string size increase due
to base64 encoding

[encrypt] should not be used to store highly-vulnerable information such
as credit cards, personal or bank details etc. For high-strength secure
encryption call an external DLL or API which offers AES or similar
asymmetric encryption

[decrypt] decrypts a string or variable from a base64 string which was
encoded using [encrypt]. Decoding will result in a string size reduction

No error is raised where the password is incorrect. The caller should
check any decrypted string and test the result as appropriate.

If you require stronger encryption use [calldll*] to call an external
encryption library

Example:

    puts [encrypt "Hello world!" secret]                                        
    puts [decrypt [encrypt "Hello world!" secret] secret]                       
    set s "Hello world!"                                                        
    puts [encrypt s secret -var]                                                
    puts [decrypt [encrypt s secret -var] secret]                               

Result

    N4w8n7XnePSvob9G                                                            
    Hello world!                                                                
    N4w8n7XnePSvob9G                                                            
    Hello world!                                                                

Security
--------
Cryptographic features in this program are not cryptographically secure
External routines should be called for security cryptography

Properties
----------
The following techincal information may be relevant to anyone writing C
code to link the library

ticol.dll is compiled with the following properties:(* is default)

    Stack 0x30000
    Processor: Blend*
    User lib: single threaded*
    Calling convention: __stdcall
    Struct byte alignment: 8 bytes*

See also: tcx, base64, mkn, cvn

Embedded TCX Plugin

    Load using: lib ticol_tcx?.dll?

    string  [tcx encode <script> ?-password password?]
    string  [tcx decode "base64-block" -password password]
    string  [tcx run "base64-block" ?-password password?]
    integer [tcx writefile filename <script> ?-64?]

The ticol_tcx plugin allows blocks of obfuscated Ticol Tcl code to be
embedded within windows batch or other Ticol Tcl scripts

Generate encoded blocks using: ticol.exe <filename> /c /64 or by using
[tcx encode] to generate a base64 encoded block of data.

Extreme caution should be used when executing such code from unknown or
untrusted sources. The use of [md5] is recommended to check the code has
not been modified. (See: md5)

[tcx run] can run any base64 encoded TCX block which has been created 
using 'ticol.exe /c /64' or using [tcx encode]

Arguments are not directly currently supported via [tcx run]
but you can set argc and argv consts as follows:

    # Example setting argc and argv to pass to a [tcx] block                    
    # This example is for Towers of Hanoi (hanoi.tcl) with 15 hoops             
    unset argc -const -nocomplain                                               
    unset argv -const -nocomplain                                               
    set argc 2       # 2 arguments to be passed                                 
    set argv(0) ""   # 1st argument will be the script name (not required)      
    set argv(1) 15   # 2nd argument will be the ring count                      

Decrypt failure will return an error code (2) and should be handled using
[try] or [catch]

A companion utility called base64.exe can be used to convert TCX binary
code into base 64 format text for use with [tcx] or the /64 argument can
be used with the /C argument. Base64 encoded TCX code can only be executed
by the [tcx] command, and not by ticol.exe directly unless re-saved as
a binary file.

Example:

    # Will read 'filename.tcl' and write 'filename.t64'                         
    ticol.exe filename /c /64                                                   

Example:

    # Uses default encryption                                                   
    # Encode using ticol.exe filename.tcl /c /64                                
    # Or using base64.exe filename.tcx to encode                                
    set code "ibuhXiuUJYyD6Uubn7G4L/wRkK/BtOkaHi5IFuULSaPU/3YskM139se           
    IuesC7KEhQjprZY83PyZAqzgwFmlfnIkpMMmSMvs="                                  
    # Load the plugin                                                           
    lib ticol_tcx                                                               
    # Execute the code (call [eval] internally)                                 
    tcx run $code                                                               

Decoded script:

    textcolor yellow                                                            
    puts "Hello World"                                                          
    textcolor                                                                   
    ;                                                                           

Results:

    Hello world                                                                 

Note that Ticol's string processor doesn't care how the quoted base64 
string is formatted. Line-endings are ignored and this 'ragged-line' example
would work just as well.

    set code "ibuhXiuUJ                                                         
    YyD6Uubn                                                                    
    7G4L/wRkK/BtOkaHi5IFuULSaPU/3Y                                              
    skM139se                                                                    
    Iues                                                                        
    C7KEhQjprZY83PyZAqzgw                                                       
    FmlfnIkpMMmSMvs="                                                           

Code validation can be performed using [md5]

Example:

    # Encode using ticol.exe filename.tcl /c which will also generate the       
    # MD5 value, and then: base64.exe filename.tcx                              
    set code "<some-block-of-tcx-code-generated-via-the-above-method>"          
                                                                                
    if {ne [md5 $code] 2e65ca46a5e2e605e5e675d935033c1a} {                      
       textcolor red                                                            
       die "Error: MD5 validation failed at line "#__LINE__                     
       textcolor                                                                
    }                                                                           
                                                                                
    # Otherwise, continue and evaluate                                          
    tcx run $code                                                               

Error Handling
--------------
The -nocomplain argument can suppress error messages. To control program
flow during errors, use [try..catch] as follows

    try {                                                                       
        lib foo.dll                                                             
    } catch {                                                                   
        puts "Can't load plugin foo.dll"                                        
    }                                                                           

Security
--------
Cryptographic features in this program are not cryptographically secure
TCX encoding is intended for obfuscation only

See also: tcx, base64

enum

    enum var ?var? ...
    enum var = integer ?var = integer? ...
    enum {var|var = integer ?var|var = integer? ...}

A simple implementation of enumerated integer constants. You cannot assign 
to an enum. Enums will have visibility scope at the level where they are
defined and each member will be treated as a separate const variable

Enums are assigned values in sequence starting at zero to guarantee they
are unique. Names may not have already been assigned to a const or variable
Member names must be unique

Assignment is optionally permitted in any number format which resolves to a 
signed integer. The enum increment sequence will continue to increment by
+1 from that number in the next constant. The assignment operator '=' must
be separated by a space or tab character

[enum] avoids more convoluted methods of declaring a unique series of
variables

[enum] const values will typically be created in global scope and can be 
accessed from within procedures using the $::constname scope prefix

Enumerated values cannot be assigned but they can be [unset] and re-
declared. You must call as [unset varname -const]

Example:

    enum zero one two three                                                     

Result:

    integer  three                3                                             
    integer  two                  2                                             
    integer  one                  1                                             
    integer  zero                 0                                             

Example:

    enum zero one six = 6 seven                                                 

Result:

    integer  seven                7                                             
    integer  six                  6                                             
    integer  one                  1                                             
    integer  zero                 0                                             

Example:

    enum {quick brown = 3 fox}                                                  

Result:

    integer  quick                0                                             
    integer  brown                3                                             
    integer  fox                  4                                             

Example:    (For Windows MessageBox API)

    enum {                                                                      
        MB_OK                                                                   
        MB_OKCANCEL                                                             
        MB_ABORTRETRYIGNORE                                                     
        MB_YESNOCANCEL                                                          
        MB_YESNO                                                                
        MB_RETRYCANCEL                                                          
        MB_CANCELTRYCONTINUE                                                    
        MB_ICONSTOP = 0x10                                                      
        MB_ICONERROR = 0x10                                                     
        MB_ICONHAND = 0x10                                                      
        MB_ICONQUESTION = 0x20                                                  
        MB_ICONWARNING = 0x30                                                   
        MB_ICONINFORMATION = 0x40                                               
        MB_ICONASTERISK = 0x40                                                  
        MB_DEFBUTTON1 = 0                                                       
        MB_DEFBUTTON2 = 0x100                                                   
        MB_DEFBUTTON3 = 0x200                                                   
        MB_DEFBUTTON4 = 0x300                                                   
        MB_APPLMODAL = 0                                                        
        MB_SYSTEMMODAL = 0x1000                                                 
        MB_TASKMODAL = 0x2000                                                   
        MB_HELP = 0x4000                                                        
    }                                                                           

Example:

    enum zero one two                                                           
    unset zero -const                                                           
    unset one -const                                                            
    unset tw* -const -nocomplain                                                

See also: const, set, unset, vars, walk

error
 
    error "message" ?info? ?returnCode? ?line?

Raises an error condition

The [error] command may be used in conjunction with catch to display/set
error strings message is an informative error message. info gives more
information about the error. code is intended to be a numeric error code.

Line is optional and non-standard and may report the line number by using
the #__LINE__ macro together with a variable

Example:

    option expression off                                                       
    proc Div {a b} {                                                            
       set line #__LINE__                                                       
       if {== $b 0} {                                                           
          error "Error generated" "Info for error" 1 [- $line 1]                
       } else {                                                                 
          return [/ $a $b]                                                      
       }                                                                        
    }                                                                           
                                                                                
    Div 3 0                                                                     

See also: on_error, preset globals, error codes, try, catch, return

Ticol Errors

Some common Tcl/Ticol errors;

Example: 

    my_proc [[string left [getenv PROMPT] 4]]                                   

Result:

    eval: No such command or procedure '$P$G'                                   

Fix:

Remove the outer layer of square brackets from the expression, thus:

    checkex [string left [getenv PROMPT] 4]                                     

Reason:

The outer brackets will try to evaluate the string which is
returned and it will be executed as a Ticol or Windows command

See also: error, debugging

Error Codes

Ticol error code return values are as follows
These are returned by [catch] but may be passed as option arguments to
other commands such as [return]

    0   Success                                                                 
    1   Error                                                                   
    2   return statement met                                                    
    3   break statement met                                                     
    4   continue statement met                                                  
    5   exit statement met                                                      
    6   stop statement met                                                      
    7   goto statement met                                                      
    8   value already defined                                                   

See also: error, catch, return

Chaining Tcl Commands

Many commands are able to be chained together to form command sequences

Example:

  set r [msgbox [join [split $qbf " "] "\n" 768] "Choose" 36]                   
  msgbox "You chose [map {6 Yes 7 No} $r]"                                      

See also: K combinator

source

  string [source filename.tcl|filename.tcx ?arg ...? ?-password <xxx>?]

Evaluate a Ticol tcl or obfuscated tcx script file, retaining the currently
loaded script and active variables. Use [eval] to evaluate an expression

[source scriptname] will execute the named script.

Note: [source] will perform a time-consuming Macro PreProcess on the
script each time it loads. Do use [source] within fast loops

The filetype for normal TCL files is not enforced for [source] but TCX
files will be detected by the filetype 'TCX' and is thus mandatory for
obfuscated TCX files

TCX files which do not require a password may be used if the password is
passed using the -password argument. Care should be taken when placing
high security passwords into Ticol scripts. Such passwords could be
stored in encrypted form and locked to a local machine etc. Alternatively
use a scriptable external app such as pwsave.exe which can retrieve
a stored password locked to a local machine and user account.

[run] will force [option escape] to ON, [source] leaves this setting
unchanged. Entering the CLI will force this option OFF so using [source]
from the CLI will require correctly setting [option escape] in a script

All arguments passed to [source] are passed to the referenced script except
the -password argument will be replaced with "******"

Example:

    source hanoi.tcx 17                                                         
    source pwprotected.tcx -password foobar                                     

Result

    Loads, decrypts and executes hanoi.tcx setting argv(1) to 17

Tcl script files may be called/chained from a script by using [source], 
with execution returning to the original script. Note that variables for a 
chained script exist in the same namespace (true namespaces are not 
supported) so care should be taken to avoid clashes in variable names in
the chained script.

[source filename] will not reset the interpreter command count

Example

    puts "Calling child script"                                                 
    source child.tcl                                                            
    puts "Parent script is still here"                                          

Note that where a script which calls other TCL files is encrypted as a TCX
file, this does not automatically alter those TCL links to TCX format. 
If you wish to 'chain' only to TCX files you must write the script to call
TCX versions of a file before encoding

Note that chaining scripts may accumulate uncleared variables. A script
should clean-up its own global variables before exit if it is likely to
be called by another script. Local proc variables are cleared 
automatically. (See: clear, unset)

A script file may also be executed by reading the contents to a variable
and then calling [eval] with the loaded variable contents

Exiting a Child Script
----------------------
The spawned child script may be exited prematurely by [return -code exit]
[return] or [return -code ok] at root level will not cause a child script
to exit and return to the parent

Caution: Mutual Script Loading Via [source]
-------------------------------------------
If two files mutually call each other via [source] then this may lead to
rapid memory consumption and memory exhaustion since memory from each 
script instance will be preserved upon loading the next

See also: eval, load, run, dump. spawn, exec, tcx

eval

    string [eval math-expression]

Evaluate a Tcl expression

For simple commands it is more efficient to call directly than via [eval]
(e.g. [+ $x $y] [min $x $y] v's [eval "+ $x $y"] [eval "min $x $y])

Standard Tcl expressions are in 'prefix' format whereas expr expressions are
in  'infix' format

Example:

    puts [eval "> 10 2"]                                                        

Result:

    true                                                                        

Freeform/unquoted number formats understood by eval are as follows:

    decimal values  NNNN                                                        
    hexadecimal     prefixed by 0xNNNN                                          
    octal           prefixed by 0oNNNN                                          
    binary          prefixed by 0bNNNN                                          

See also: expr, bool, math, ticol, clear, unset, tcx

Event Log Handling Plugin

To load, use:    lib ticol_event ?-verbose? ?-nocomplain?

    bool [event is_source name]
    bool [event add name ?0..4?]
    bool [event remove name]
    bool [event log name string type ?postscript?]

[event] offers basic Windows event logging commands.

[event add] requires Windows UAC elevation and adds an SZ_EXPAND registry
event entry under: 

"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"

and this registers ticol_event.dll as the string table source for event
messages. Only 4 basic message types are supported

The following Macro defines are useful. Note that these can only be used
outside quoted strings

    #define EVENTLOG_SUCCESS 0                                                  
    #define EVENTLOG_ERROR_TYPE 1                                               
    #define EVENTLOG_WARNING_TYPE 2                                             
    #define EVENTLOG_INFORMATION_TYPE 4                                         

Note 
----
If an event source is removed then the Windows Event Log application will
no longer be able to query the string table of the linked Ticol plugin DLL. 
You will see an error:

    The description for Event ID N from source XXXX cannot be found. 

The event source should be left registered as long as ticol_event.dll is
installed on the system and you need to query related events

Example:

    #define EVENTLOG_INFORMATION_TYPE 4                                         
    lib ticol_event                                                             
                                                                                
    if {[is_elevated]} {                                                        
       if {event add ticol EVENTLOG_INFORMATION_TYPE} {                         
          puts "Added an event to the event log"                                
       } else {                                                                 
          puts "Failed to add an event"                                         
       }                                                                        
       puts [bool [event is_source ticol]                                       
    }                                                                           

Results: (If elevated)

    True                                                                        

See also: is_elevated, elevate, plugins

Exception Handling

    The following commands are useful in handling exceptions:

    catch
    try / catch
    error

The following variables are pre-defined and set on an error condition
arising

    Name             Type   Comments
    ::errorCode      Var    Tcl error code sent to a the console
    ::errorLine      Var    May be preset using the #__LINE__ macro
    ::errorMsg       Var    Error message generated by a Ticol error
                            Fatal exceptions generate no errorMsg variable
    ::errorInfo      Var    Additional information

See also; try, catch, error

exec, shell

    exec program ?arg? ?arg...?                                                 
    @program ?arg? ?arg...?                                                     
    @ program ?arg? ?arg...?                                                    
    shell                                                                       

Run an external program from the Ticol shell. Ticol can be used as a
Windows command shell.

If autoexec is "on" then unrecognised commands will be shell-executed

Prefixing a command with '@' can be useful to override conflicts with
built-in commands. 

[shell] launches an instance of CMD.EXE. Type exit to exit and return
to Ticol

To grab the output text from a spawned application, see the [spawn] 
command

CTRL+C is available for external applications, if quit using CTRL+C then
execution will be returned to the Ticol CLI shell

Examples:

    @type ticol.man                   # Call the Windows command shell          
    exec dir *.tcl /s                 # Use [exec] to spawn the command         
    ftp localhost                     # Call the program directly               
    xcopy *.* \\server\share\folder   # Call xcopy with arguments               

See also: autoexec, spawn, exit

Examples

Various example scripts are included with the standard Ticol distribution

This includes a series of test files

See also: Tcl, Ticol, faq

exit

    exit ?errorlevel-value?

Exits a Ticol Tcl program. If the program was launched from a Windows
console then execution will return to the console. If launched from the
Ticol CLI then execution will return to the CLI

The errorlevel value will be returned to the operating system

CTRL+C is disabled in Ticol. If you wish to quit the CLI prompt, either
press CTRL+BREAK or type 'exit' at the CLI prompt

See also: stop, at_exit, return

explain

    string [explain expression]

Expand or 'explain' a natural expression in terms of the Polish Notation
(PN) interpretation of an expression as used with [calc]. The expression
handler, [expr] does not translate into intermediate Polish notation but
[calc] does. [explain] may be used to check the internal interpretation
of any expression

The result of [explain] may be evaluated using [eval]

Example:

    puts [explain 1+2*3+4]                                                      
    puts [eval [explain 1+2*3+4]]                                               

Result:

    [+ [+ 1 [* 2 3]] 4]                                                         
    11                                                                          

See also: calc, expr

expr

    string [expr expression items]
    string [expr {expression-group}]
    string [expr "expression-string"]
    string [expr arg arg...]

Evaluates a mathematical expression held in a Tcl string or variable

Expressions within 'expr' allow 'infix' format whereas native Tcl 'eval' 
commands are in 'prefix' notation as [operator var1 ?var2?], 

Individual [expr] arguments may be grouped by double-quotes or braces but
will otherwise be concatenated into a single argument. It is strongly
recommended that the argument to [expr] be wrapped in braces or double-
quotes or complex expressions, (particularly multi-line) will be mis-
evaluated

[calc] is recommended as an alternative to expr for expressions not
generated at run-time. [calc] will reduce the natural-expression to
'Polish notation' format which runs faster than iterating over the
expression interpreter

Examples:

    puts [expr 2*3+2]           # Internal precedence                           
    puts [expr $b*(3+2)]        # Natural expression with brackets              
    puts [expr 2*[+ 3 2]]       # Infix sub-command                             
    puts [expr "2*[+ 3 2]"]     # Infix sub-command (quoted)                    
    puts [expr "2*[- $a 2]"]    # Infix sub-command (quoted)                    
    puts [expr "2*[decr 3 2]"]  # Infix sub-command (quoted)                    
    puts [expr {2*[+ 3 2]}]     # Infix sub-command (braced)                    
    puts [expr "2*[incr 3 2]"]  # Infix sub-command (quoted)                    

Result:

    8                                                                           
    10                                                                          
    10                                                                          
    10                                                                          
    2                                                                           
    2                                                                           
    10                                                                          
    10                                                                          

Expression operators include the following:

    + - * /  ^                  # Add, sub, mul, div, power                     
    == != > < >= <=             # Relational math operators                     
    &= |= ^= += -= *= /=        # Combined math operators                       
    << >>                       # Bit-shifting operators                        
    ** ? ()                     # Exponent, long-if, brackets                   
    & |                         # Bitwise and/or                                
    &&, ||                      # Logical and/or                                

The following are not supported. Use embedded [command] versions

    ++ --                       # Increment, decrement                          
    and or                      # Keywords (use symbols instead)                
    mul div add sub             # Keywords (use symbols instead)                

Note particularly that:

    ++ and -- are not supported other than as separate [++], [--] commands

Unary increment/decrement is not implemented within [expr]. This is due to
potential conflicts with expressions such as "3 - -7"
Embed [++] [incr] or [--] [decr] commands within the expression.
    
You may intermingle expression functions, procedures, bracketed commands,
constants, variable names etc. within the expression string. Standard 
BEDMAS operator precedence applies unless curved parentheses are used to
group

A wide range of arithmetic functions are supported. See "functions" for
more information. Curved brackets may be used to prioritise sub-expressions
Parity checks are made on bracket pairing outside of quoted strings.

Number evaluation follows Tcl rules. All numbers are evaluated as integer
unless a decimal point is specified. Division will therefore be truncated
to integer unless a decimal point is given for the divisor

Freeform expressions are also permitted but it is recommended that 
expression-strings are braced or enclosed in double-quotes

Performance
-----------
Because it is a generalised expression-handler with significant syntax-
checking overheads and which calls the underlying Tcl routines, [expr] may 
be far slower than decomposing an expression into component operations with
Tcl 'prefix' syntax. See [calc] for an optimised equivalent to [expr]

Example:

    set src [expr ($x & $y) % 3]     # Freeform expression syntax               
    set src [expr {22/7.0}]          # Braced expression syntax                 
    set src [expr "22/7.0"]          # Quoted expression syntax                 
    set src [% [& $x $y] 3]          # Decomposed commands (~50% faster)        

The Macro PreProcessor (MPP) may be used to optimise expressions into native
Tcl commands (see [calc] which is a substitute command which will auto-
optimise expressions into simpler Tcl commands)

Embedded Commands
------------------
Tcl commands may be embedded within expressions. These will be evaluated
first but do not affect overall operator precedence - e.g.

    Use: puts [expr 22/7.0+[incr 2]] -> 6.142857142857142                       
    Not: puts [expr 22/7.0+incr 2]                                              
    Use: puts [expr [incr 10]]                                                  
    Not: puts [expr "incr 10"]                                                  

Whitespace
----------
Whitespace is handled between bracketed expression calls but the expression 
should ideally be wrapped in double quotes or curly braces to ensure safe
grouping

Examples: 

    expr 22 / 7.0                                                               
    expr 22/7.0                                                                 
    expr "22/7.0"                                                               

Returns:

    3.142857                                                                    

Whitespace is permitted it is recommended that it is avoided

Number Formats
--------------
The following 64-bit encoded number formats are understood by [expr]:

    decimal      any base 10 number   12345                                     
    hexadecimal  prefixed by 0xNNNN   0x3039                                    
    octal        prefixed by 0oNNNN   0o30071                                   
    binary       prefixed by 0bNNNN   0b11000000111001                          

Transiting Array Brackets
-------------------------
An [expr] statement does not transit expression array () brackets. 
While array indices may contain expressions those expressions are not 
resolved unless they are in Tcl "command" format e.g. [* 2 3] or if the
[expr] command is used

Thus: [expr $a(22/7*2)] would be one of:

    [expr $a([expr (22/7)*2])]                                                  
    [expr $a([* [22/7] 2])]                                                     

Note that [calc] will attempt to resolve all sub-expressions, including
array parentheses expressions

Incorrect Use
-------------
Tcl commands which are not part of the [expr] syntax cannot be embedded
without square brackets. Commands [++] and [--] are not allowed as these
conflict with arithmetic addition/subtraction followed by sign value

Example:

    set a 1;puts [expr "set b; ++ $a"]        # ;separators are not valid       
    set a 1;set b [expr "[++ $a]"]; puts '$b' # Valid                           

Caution
-------
Because Tcl does not distinguish between data types you should avoid
passing unchecked string data into [expr], particularly string data which
may contain valid Tcl commands. This is because the arguments will be 
evaluated

Example:

    set s "@format c: /y"                                                       
    expr "$s == {}"            # This could be bad!                             

instead, embed string comparison commands such as [eq] or [string equal]
as follows...

    set s "@format c: /y"                                                       
    expr [eq $s {}]                                                             

    https://en.wikipedia.org/wiki/Polish_notation

Stored, Nested [expr]
---------------------
If [expr] is both stored within a variable which is resolved later using
[expr] *and* the stored string contains [expr], then you must not only
brace the [expr] argument but include square brackets as follows:

    set a {[expr {1 + 2 * 3}]}   # Correct                                      
    # set a {expr {1 + 2 * 3}}   # Wrong                                        
    puts "Stored a is: '$a'"                                                    
    puts [expr {$a}]                                                            

Result:

    Stored a is: '[expr 1 +( 2 * 3)]'                                           
    7                                                                           

See also: calc, eval, bool, functions, math, arrays, inttohex, inttooct,
          inttobin

factorial

This is example code which calculates a factorial

This sample may overflow into negative or zero values without further 
error checking.

    option expression off                                                       
    proc factorial {val} {                                                      
        puts "Current level: [info level] - val: $val"                          
        set lvl [info level]                                                    
        if {== $lvl $val} {                                                     
            return $val                                                         
        }                                                                       
        return [expr {($val-$lvl) * [factorial $val]}]                          
    }                                                                           
    puts [factorial 20]                                                         

Results:

    2432902008176640000                                                         

See also: fib, math

fib (Fibonacci)
 
    integer [fib n]

Compute Fibonacci series value from 0 to 93
Accessing a value over 93 will return an overflow error
[fib] uses a rapid, iterative method of calculation internally

Example:

    option expression on                                                        
    for {set j 0} {$j < 81} {incr j} {puts "$j [fib $j]"}                       

Result:

    0 0                                                                         
    1 1                                                                         
    2 1                                                                         
    3 2                                                                         
    4 3                                                                         
    5 5                                                                         
    6 8                                                                         
    7 13                                                                        
    8 21                                                                        
    9 34                                                                        
    10 55                                                                       
    11 89                                                                       
    12 144                                                                      
    13 233                                                                      
    14 377                                                                      
    . . .                                                                       

See also: math, big

file
 
Enhanced Tcl 'file' command

Offers a variety of stream-based file manipulation commands as well as
O/S-like commands. These are similar to those offered by the "C" language

Many of the sub-commands can be used independently without the 'file' prefix

For I/O see commands such as [puts], [format], [read]

file attributes filename    # Return a Tcl list of common attributes            
file close fp               # Close an open file                                
file delete filename        # Delete a file (alias for unlink)                  
file dirname name           # Return the directory name of a filename/path      
file eof fp                 # Tests file-read for end of file condition         
file executable filename    # Tests if a file is executable, return 1 or 0      
file exepath                # Return the path of the Ticol executable           
file exists filename        # Test if a file exists                             
file extension filename     # Get the filename-extension/filetype as ".typ"     
file flush fp               # Flush file output to a stream                     
file gets fp ?var? ?bytes?  # Read a line of text data from an open file        
                            # Returns string or count of bytes if var given     
file isdirectory filename   # Test if a directory exists (returns a boolean)    
file isfile filename        # Test if a file exists (returns a boolean)         
file isroot path            # Test if an unescaped path is root, X:\ or \       
file md5 filename           # Generate an MD5 value for a file                  
file mkdir filename         # Create a directory                                
file name filename          # Return the filename+ext only (non standard)       
                            # "C:\path\to\my.txt" gives "my.txt"                
file normalize filename     # Get the full, normalised path of a filename       
                            # e.g. win.ini in c:\windows\system32 gives         
                            # c:\windows\system32\win.ini                       
file open filename mode     # Open a file for reading or writing                
                            # Uses C++ fopen() modes e.g. "rb", wb", "rt"       
                            # Returns a file handle                             
file pathcount filename     # Return count of path segments (\\ or /)           
file pathstrip filename n   # Strips N path segments from the given path        
file readable filename      # Check a file for read access & return 1 or 0      
                            # Does not check file read/write locking            
file rename oldname newname # Rename a file. *CAUTION* - Ticol is ANSI only     
                            # and does not support Unicode filename             
file resolve path           # Resolve an ambiguous path                         
file rewind fp              # Rewind the file pointer of an open-file           
file runtype filename ?-v?  # Tests executable run type                         
                            # Returns code: Optional -v verbose flag            
                            # -1  unknown  Unknown type/non-executable          
                            #  0  32bit    32-bit Windows exe                   
                            #  1  dos      DOS executable                       
                            #  2  wow      Windows on Windows64 exe (wow)       
                            #  3  pif      PIF file                             
                            #  4  posix    Posix binary                         
                            #  5  os216    OS/2 16-bit exe                      
                            #  6  64bit    64-bit Windows exe                   
file seek fp offset         # Set/place the file pointer of an open-file        
file size filename          # Get the size of a filename (64-bit)               
file split filename         # Split a path into components in list format       
                            # {drive: path filename}                            
file stat filename var      # Retrieve 'stat' data. See: file stat              
file tell fp                # Get the file-pointer location of an open-file     
file type filename          # Return file, directory, link or unknown           
file unlink filename        # Delete a file (alias for delete)                  
file volumes                # Return a Tcl list of valid volumes                
file writeable filename     # Check file/dir for write access. Returns bool     

Examples:
--------
All examples are with: option echo on

Example:

    file attributes autoexec.tcl                                                

Result:

-archive 1 -hidden 0 -system 0 -readonly 0 -longname autoexec.tcl \             
-shortname {}                                                                   

Example:

    file readable autoexec.tcl                                                  

Result:

    1                                                                           

Example:

    file type autoexec.tcl                                                      

Result:

    file                                                                        

Example:

    file volumes                                                                

Result:

    C:/ D:/ E:/ F:/ G:/ I:/ J:/                                                 

See also: info, exists, filesize, mkdir, mkdirs, unlink, dir, input,
          readfile, writefile, normal_path, unc paths

file exists
 
    bool [file exists filespec]

Check if a file exists or not. [file exists] also supports wildcards

Example:

    puts [file exists ticol.exe]                                                
    puts [file exists *.tcl]                                                    

Result:

    1                                                                           
    1                                                                           

See also: file, readfile, writefile

file pathcount

    integer [file pathcount filename]

Returns a count of the individual path segments (including any filename)

[pathcount] accepts backslash or Unix-style forward slash characters

If using backslash characters then [pathcount] requires an 'escaped' path
Use [escape] to escape a path string delimited by single backslash
characters

Examples:

    puts [file pathcount c:\\]                                                  
    puts [file pathcount c:/]                                                   
    puts [file pathcount c:\\file.txt]                                          
    puts [file pathcount c:/file.txt]                                           
    puts [file pathcount c:\\path\\to\\some\\file.txt]                          
    puts [file pathcount [escape c:\path\to\some\file.txt]]                     
    puts [file pathcount c:/path/to/some/file.txt]                              

Results:

    1                                                                           
    1                                                                           
    2                                                                           
    2                                                                           
    5                                                                           
    5                                                                           
    5                                                                           

See also: file

file pathstrip

    string [file pathstrip pathstring n ?-unix?]

Strips 'n' path segments from the specified path.

If 'n' is zero then no segments are removed
If 'n' is greater than the number of path segments then an empty string is
returned

Both Windows and Unix style path separators are accepted as input, but if
Windows backslash characters are used they must be 'escaped' (use [escape])

A Windows style double backslash-escaped path will be returned unless the
-unix argument is used. Any trailing path separators are removed.

Note that path strings should always be wrapped in double quotes

Examples:

    puts [file pathstrip "c:\\windows\\system32\\drivers\\etc\\" 2]             
    puts [file pathstrip "c:\\windows\\system32\\drivers\\etc\\" 2 -unix]       
    puts [file pathstrip "c:/program files/microsoft shared/"  1]               

Results:

    c:\\windows\\system32                                                       
    c:/windows/system32                                                         
    c:\\program files                                                           

See also: file, file pathcount, escape, unescape

file stat

    bool [file stat filename arrayvar]

Fills an array with _stat information using the C++ '_stat()' function
The array variable referenced in the argument must not exist. A boolean
True (1) is returned if the file exists, False (0) if it doesn't

Example:

    file stat ticol.exe a                                                       
                                                                                
    array foreach a val ss {                                                    
       puts "\$a($ss)\t$val"                                                    
    }                                                                           

Results:

    $a(mode)        33279         # Protection                                  
    $a(ino)         0             # inode number (always 0)                     
    $a(dev)         2             # ID of device containing file (A==0)         
    $a(atime)       1481501018    # C time of last access                       
    $a(mtime)       1481501108    # C time of last modification                 
    $a(gid)         0             # Group ID of owner (always 0)                
    $a(size)        190976        # Total size, in bytes                        
    $a(nlink)       1             # Number of links (always 1)                  
    $a(rdev)        2l            # device ID (if special file)                 
    $a(ctime)       1481501018    # time of last status change                  
    $a(drive)       C             # Drive letter of 'dev' (upper case)          
    $a(uid)         0             # User ID of owner (always 0)                 

References:

    https://msdn.microsoft.com/en-us/library/14h5k7ff.aspx
    https://stackoverflow.com/questions/6094665/how-does-stat-under-windows-exactly-works

See also: file

file gets

    string [file gets ?fp? ?varname? ?-length bytes?]
    
Read a line of text data from an open file

Returns a string value if no variable specified, else returns the count of
bytes successfully read if a var argument was given. If an EOL byte sequence
is encountered before 'bytes' have been read then a short count will be
returned. If 'var' does not exist it will be created in the current scope

[file gets var] will always clear var to {} if it exists

Example:

    set fp [file open [unescape $argv(1)]]                                      
    set r [file gets $fp s -length 100]                                         
    puts "s is '$s"                                                             
    file close $fp                                                              

Result:

    <Text from input file up to 100 chars or first CRLF sequence>               

See also: readfile, file

extract

   string [extract string maskchars ?default? ?-nocase? ?-offset N?]
   string [extract varname maskchars ?default? -var ?-nocase? ?-offset N?]

Extract strings matching 'maskchars' from the left hand end of a string
Can be useful for stripping and parsing number sequences from large
strings. The first 4 arguments are positional

Very large strings in a variable may be passed by name using the -var arg
To simplify use with math commands, a default value may be returned

Using -nocase will match on characters regardless of case

If -offset is used with an integer value then the search will begin at 'n'
characters into the string where 0 is the first character 1 is the 2nd etc.
The offset value shifts to the starting point to the right by N chars

Note that the mask string must include a whitespace character if multiple
words are to be found in any search string. Omitting a space can be useful
in using [extract] to search and extract single words. Using '-offset N'
with a loop variable it is possible to iterate through all word tokens in
a string

If the search mask is braced it will be interpreted as a literal string
and can therefore not contain embedded commands. Any complex search
mask relying on commands should be generated and passed via a variable

Example:

    set s "123.45Hello World"                                                   
    puts [* [extract s "0123456789." "0" -var] $pi]                             
    set s "No numbers in here"                                                  
    puts [* [extract s "0123456789." "1" -var] $pi]                             
    puts [extract qbf [range a-z] -var -nocase -offset 4]                       
    puts [extract qbf "[range a-z] " -var -nocase -offset 35]                   

Result:

    387.829613085659960                                                         
    3.141592653589793                                                           
    quick                                                                       
    lazy dog                                                                    

See also: string, filter

filter

    string [filter string charmask ?-exclude? ?-nocase?]
    string [filter varname charmask ?-exclude? ?-var? ?-nocase?]

Filter characters in a string, either including (default) or excluding
characters from string 'charmask'. The first 3 arguments are positional

Large variables which have a heavy copy cost may be passed by name using
the -var argument

Using -nocase will match on characters regardless of case

Example:

    # Snippet of Brainfuck code                                                 
    set s "+++++ +++++        Initialize counter (cell #0) to 10"               
    set s [filter $s "+"]                                                       
    puts '$s'                                                                   

Result:

    '++++++++++'                                                                

See also: string, extract

filesize
 
    integer [filesize filename]

Returns the size of a given file in bytes as a 64-bit value

See also: file, unlink

find (Help)
 
    find string
    find "string with spaces"

Search the Ticol manual for topics which contain a given search string

Since the macro preprocessor (MPP) processes all Ticol command-line 
arguments including both 'help' and 'find', this will affect the ability
to search for the # character (which is interpreted as a comment)

See: help option PreProcessor

Example:

    user@host> find upvar                                                       

Result:

Help topics containing keyword 'upvar'

arrays                   subsequent upvar command. To pass an array by value    
at_exit                  The upvar command is required to access variables d    
combinatory logic        upvar 1 $_stack stack                                  
comparing ticol with php Standard (downwards) variable inheritance  No (upva    
const                    declared using upvar to create a local const instan    
contents                 upvar              Reference a variable in a higher    
differences              global         Global is an alias for upvar            
dump                     dump, when used with upvar arrays or standard varia    
find                     user@host> find upvar                                  

See also: help

for (Flow Control)

Provides a for iterator similar to that of C/C++

With: option expression on (Standard Tcl):

    Default:

       for {init-eval} {condition-expr} {post-eval} {eval-code}                 

    With INI ForInitAllowsExpression=TRUE

       for {init-expr} {condition-expr} {post-eval} {eval-code}                 
       for {[init-eval]} {condition-expr} {post-eval} {eval-code}               

This uses an [expr] statement as the 3rd (condition) argument and
if configured, can optionally use an [expr] statement for the 2nd (init)
argument. Always ensure you have the correct [option expression] setting

With: option expression off (Non-standard):

    for {init-eval} {condition-eval} {post-eval} {eval-code}                    

    This uses an [eval] statement for the 3rd (condition) argument

The following additional commands are available for use with [for]

    return             # Will exit the script when used in root-level [if]      
    break              # Will be passed back to any enclosing structure         
    stop               # Halt operation, return to the CLI if loaded            
    exit               # Exit the script and return to Windows                  
    goto               # Where used with an enclosing [goto_block]              
    continue           # Resume execution at the head of the [for] loop         

Flow-control for loop

Example with single loop variable

    option expression off                                                       
    for {set i 0} {< $i 10} {incr i} {                                          
       puts "i is $i"                                                           
       ++ j                                                                     
    }                                                                           
                                                                                
    option expression on                                                        
    for {[set i 0]} {$i < 10} {incr i} {                                        
       puts "i is $i"                                                           
       ++ j                                                                     
    }                                                                           

Example - Handling multiple loop variables:

[for] will quite happily accept multiple or complex arguments since each
braced argument is merely a Tcl expression which will be interpreted
using either [expr] or [eval] depending on the [option expression]
setting.

    option expression on                                                        
    for {[set i 0]; [set j 0]} {($i < 10) && ($j < 10)} {incr i; incr j} {      
       puts "i is $i, j is $j"                                                  
       ++ j                                                                     
    }                                                                           
                                                                                
    option expression on                                                        
    for {[set i 0; set j 0]} {$i < 6} { ++ i; ++ j 2} {                         
       puts "i=$i; j=$j"                                                        
    }                                                                           
                                                                                
    option expression off                                                       
    for {[set i 0]; [set j 0]} {[expr ($i < 10) && ($j < 10)]} {[incr i]; \     
       [incr j]} {                                                              
       puts "i is $i, j is $j"                                                  
       ++ j                                                                     
    }                                                                           

Emulating C++ for(;;)
---------------------
The [for] command with null arguments will result in an infinite loop
See also: while

    for {} {} {} {                                                              
        puts "Infinite loop"                                                    
    }                                                                           

See also: loop, while, do, time, flow control

Control+C (break)

The standard interrupt handler CTRL+C is disabled to prevent accidental
or undesired exit from a Ticol script. Script execution can be terminated
using CTRL+Break instead

See also: break, exit, stop

Flow-control: for foreach if else, switch while do goto on loop
 
The following construct flow-control constructs are supported:

    array foreach          # Iterator                                           
    carray foreach         # Iterator (Plugin)                                  
    catch                  # Error handler                                      
    do                     # Iterator                                           
    floop                  # Floating point loop                                
    for                    # Iterator                                           
    foreach                # Iterator                                           
    gosub                  # Subroutine execute                                 
    goto / goto_block      # Selection                                          
    if                     # Selection                                          
    linkedlist foreach     # Iterator (Plugin)                                  
    loop                   # Integer loop iterator                              
    on                     # Selection                                          
    proc                   # Procedure execute                                  
    stack foreach          # Iterator                                           
    switch                 # Selection                                          
    time                   # Iterator                                           
    try/catch              # Error handler                                      
    varray foreach         # Iterator (Plugin)                                  
    while                  # Iterator                                           

Details
-------
                                                                                
    array foreach array element ?subscript? {code-body}                         
    carray foreach handle lvalue ?rvalue? {code-body}                           
    catch {code-body}                                                           
    do {code-body} while {...}                                                  
    floop variable float-start float-limit ?float-increment? {code}             
    for {initiator} {condition-test} {post-action} {code-body}                  
    foreach variable list {code-body}                                           
    foreach varlist1 list1 ?varlistN listN ...? {code-body}                     
    gosub function                                                              
    goto_block {?goto? statement} {...}                                         
    if {condition-test} else {...}                                              
    if {condition-test} elseif {...} ... else {...}                             
    linkedlist foreach handle lvalue ?rvalue? {code-body}                       
    loop variable start limit ?increment? {code-body}                           
    on integer {...} ?{...}...? ?-default {...}?                                
    proc name {?arg...?} {code-body}                                            
    stack foreach varName indexVar {code-body} ?-r?                             
    switch ?options? {arg} {{case script} ... | {default script}}               
    time ?expression? ?iterations? ?-info | -quiet?                             
    try {code-body} catch {code-body}                                           
    varray foreach handle variable {code-body}                                  
    while {condition-test} {code-body}                                          

    Additionally: the following control keywords are available:

    break        # Exit the loop                                                
    continue     # Continue execution from the loop head                        
    default      # (For use with [switch] only)                                 
    die          # Exit to windows with message                                 
    exit         # Halt processing and exit to Windows                          
    goto         # Only valid within a [goto_block]                             
    return       # Return from the loop with optional code etc.                 
    stop         # Halt processing and return to the CLI                        

Behaviour is modified by:     option expression on|off

if/else/elseif keywords must be separated by whitespace. The Tcl syntax 
requires that the opening brace following if, else, elseif, be on the same
line as the command. e.g.  (See: brace style)

    if { condition } {             # Correct layout (K&R/Tcl format)            
          statement                                                             
       } elseif { condition } {                                                 
          statement                                                             
       } else {                                                                 
          statement                                                             
    }                                                                           
                                                                                
    if { condition }               # Incorrect layout (Allman/BSD format)       
    {                                                                           
       statement                                                                
    }                                                                           
    else if { condition } {                                                     
       statement                                                                
    }                                                                           
    else                                                                        
    {                                                                           
       statement                                                                
    }                                                                           
                                                                                
    while { condition } {          # Correct layout (Tcl)                       
       statement                                                                
    }                                                                           
                                                                                
    while condition { statement }  # Correct layout (Tcl)                       
                                                                                
    do {                           # Correct layout (Tcl)                       
                                                                                
       statement                                                                
                                                                                
    } ?while? {condition}                                                       
                                                                                
    do {statement} {condition}     # Correct layout (Tcl)                       
                                                                                
    set i 0                                                                     
    do {                           # Correct layout (Tcl)                       
       puts "i is $i"                                                           
       if {== $i 2} { return }                                                  
       incr i                                                                   
    } while { $i < 10}                                                          
                                                                                
    while 1 {puts [rnd 1 10] }                                                  
                                                                                
    do {puts [rnd 1 10] } while 1                                               

All of the above cases support the break or continue keywords
break exits the block loop structure such as while, for, foreach
continue jumps to the start of any block structure and repeats

Return values set using 'return' can be passed from while or do-while by
chaining from 'set' - e.g.

    set r [do { ...                                                             
       if { $i == 2 } {return string}                                           
                                                                                
    set r [while { ...                                                          
       if { $i == 2 } {return string}                                           

If 'option expression' is set to ON then expr with infix notation will be
used for tests instead of the default of eval with prefix notation

A limited-scope form of goto is supported within a goto_block 
(see goto_block)

See also: for, foreach, array foreach, if, elseif, else, switch, 
          while, do, loop

See also: option expression, foreach, array foreach, if, proc, return, 
          at_exit, linkedlist, carray, stack, loop, try, catch

break

    break

Halt execution within a flow-control block such as [for], [while], [do] or
[goto_block]

If executed outside a flow control block and at level 0 (root) [break]
will exit the current script and have the same effect as [exit]

[break] is not valid within other flow-control structures

Example:

    option expression off                                                       
    for {set i 0} {< $i 10} {incr i} {                                          
       if {== $i 5} {break}                                                     
    }                                                                           

Example:

    break                                                                       

Result

    # Script exits                                                              

See also: flow control, continue, stop, exit, return

continue

    continue

Continue execution of a loop structure from the head of the loop

[continue] skips any body code and executes the increment argument of a
[for] loop. Relevant to [for] [foreach] [while] [do]

Example:

    # print a line for each of the integers from 0 to 10 except 5:              
                                                                                
    option expression on                                                        
    for {set x 0} {$x < 10} {incr x} {                                          
       if {$x == 5} {                                                           
          continue                                                              
       }                                                                        
       puts "x is $x"                                                           
    }                                                                           

Result:

    x is 0                                                                      
    x is 1                                                                      
    x is 2                                                                      
    x is 3                                                                      
    x is 4                                                                      
    x is 6                                                                      
    x is 7                                                                      
    x is 8                                                                      
    x is 9                                                                      

Example:

    option expression off                                                       
    set sum 0                                                                   
    set x 0                                                                     
    while {< $x 10} {                                                           
       incr x            # Pre-increment before [continue]                      
       if {== $x 5} {                                                           
          continue                                                              
       }                                                                        
       puts "x is $x"                                                           
       += sum $x                                                                
    }                                                                           

Result:

    x is 1                                                                      
    x is 2                                                                      
    x is 3                                                                      
    x is 4                                                                      
    x is 6                                                                      
    x is 7                                                                      
    x is 8                                                                      
    x is 9                                                                      
    x is 10                                                                     

See also: flow control, break, stop, exit, return

Handling filenames and paths

Wherever possible, Ticol handles both / and \ path separators in filenames. 
Within complex strings and expressions it may  be preferable to use / as
this does not need to be escaped and avoids problems with 'double-
unescaping'

Literal strings must have the \ character 'escaped' as \\

Example:

    set filename "c:/path/to/file.txt"          # Correct                       
                                                                                
    set filename "c:\\path\\to\\file.txt"       # Correct                       
                                                                                
    set filename "c:\path\to\file.txt"          # Incorrect (\t is a TAB)       

Command Line Use
----------------
When using filenames from the CLI this behaviour is modified to allow
unescaped paths. See: option escape    for more information

See also: escape sequences, escape, unescape, option escape

fpart

    integer [fpart double-value ?precision?]

Return the fractional part of a double value as an integer
The precision part must be >= 1 and <= 10

[fpart] preserves any zeroes after the decimal, stripping the sign, leading
zero (if any) before the decimal point and the decimal point itself 

Example:

    puts [fpart $pi]                                                            
    puts [fpart $pi 2]                                                          
    puts [fpart $pi 6]                                                          

Results:

    1415926535                                                                  
    14                                                                          
    141592                                                                      

Example:

    puts [fpart 0.12]                                                           

Result:

    1200000000                                                                  

Example:

    # Splitting a double value for processing with reassembly                   
    puts "[integral $pi].[fpart $pi 7]"   # Recommended                         
    puts "[integral $pi][fraction $pi]"   # Bad idea!                           
    puts "[integral $pi].[fraction $pi]"  # Bad idea!                           

Result:

    3.1415926            # Correct                                              
    30.141592653589793   # Wrong!                                               
    3.0.141592653589793  # Wrong!                                               

Example:

    puts [fpart [dateserial - - - 2 4 6]]                                       
    puts [time .[fpart [dateserial - - - 2 4 6]]]                               

Results:

    0861805555                                                                  
    02:04:06                                                                    

See also: fraction, integral, vfract

fraction and integral

    double [fraction realValue]
    double [integral realValue]

Returns either the fractional or integer part of a real number (float)

Example:

    fraction [expr atan(1)*4]                                                   

Result:

    0.141593                                                                    

Example:

    integral [expr atan(1)*4]                                                   

Result:

    3.00000                                                                     

Note: Use [fpart] to retrieve the decimal fraction as an integer value

See also: fpart, math, vfract

Introspection and Reflection

Ticol has modest commands for type reflection and introspection, 
as follows:

    args         # C++ like vararg placeholder                                  
    array walk   # Dump a variable's contents                                   
    assert       # Debug assertion                                              
    catch        # Trap errors (see also; try..catch)                           
    cmdcount     # Count of the # of executed commands                          
    dump         # Dump details of a standard variable or struct                
    dump         # Show syntax-highlighted source code                          
    dumpvariant  # Dump the contents of a variant                               
    halt         # Enter the debugger when debugging  active                    
    info         # Various introspective commands                               
    inspect      # Inspect raw Tcl variable information                         
    mem_used     # Returns the current memory used by Ticol                     
    resume       # Resume execution after debugging halt                        
    time         # Time execution calls                                         
    trace        # Display calls and shows results                              
    try .. catch # C++ like try...catch blocks                                  
    type         # Display variable type names                                  
    vars         # List variables for a given context                           
    walk         # Dump the variable table at any callframe level               
    watch        # Set a variable watch for use with breakpoints                
    /echo        # Arg. Show Macro PreProcessed source code                     
    /bp          # Arg. Breakpoint execution stacktrace                         
    /debug       # Arg. Enable debugging options including /bp                  
    /g           # Arg. Performance graph                                       

Variables also exist which reveal information about the system
These can be inspected using the command [vars -const]

    string    exe_name                                                          
    string    tcl_date                                                          
    integer   tcl_cli                                                           
    string    tcl_config                                                        
    integer   tcl_threaded                                                      
    float     tcl_version                                                       
    integer   tcl_precision                                                     
    array     env                                                               
    empty     argv0                                                             
    integer   argc                                                              

See also: debugging

doevents
 
    doevents ?count? ?-sleep?

This is analogous to Visual BASIC's DoEvents command. 

[doevents] yields thread control to Windows and allows processing of the 
Windows message queue. doevents may be combined with sleep to minimise CPU
use during tight loops. [doevents] will poll the Windows message queue

An optional count argument may be added which will cause [doevents]
to poll a given number of times. Additionally, a 'sleep' of 10ms 
may be performed during each loop.

You can use [sleep 0] to achieve a similar effect

Example:

    set i 0                                                                     
    while {$i < 1000000 } {                                                     
       # A tight, CPU-intensive loop                                            
       doevents                                                                 
       incr i                                                                   
       sleep 0                                                                  
    }                                                                           

See also: sleep, performance

flush
 
    flush ?stream?

Flushes output to an open file stream. This may include the symbolic strings
stdout and stderr as well as open file handles

See also: open, close, file, puts, printf, echo, ticol_cgi

. dot (or head) command

    char [. string|var ?-var?]

The 'dot' command returns the first (head) character of a string or
variable. This is far more efficient than [string left $string 1] with
a timing of about 1 or 2 microseconds per call on an i5 upwards

It is equivalent to C/C++ '*string'

The command [asc] will return the first character as an ASCII integer

Example:

    puts [. $qbf]                                                               
    puts [. qbf -var]                                                           
    puts [. qbf]                                                                

Result

    T                                                                           
    T                                                                           
    q                                                                           

See also: pipe, asc, chr, string

foreach (Flow Control)
 
    foreach var list ?var list...? {script}
    foreach {varlist} list ?{varlist} list...? {script}
    foreach var|{var...} list|{vars...} {script}

Iterates the RValue (right hand value) list using LValue (left hand value)
variable(s) as placeholder(s) for the list item(s)

Can be considered equivalent to the pseudocode ('in' is not supported): 

    foreach variable in list {script}

If a literal list of items is given then it should not be wrapped in
braces to ensure grouping. If a braced list of variables is given then
don't enclose in quotes or braces. Rvalue variables should not be braced
as this will prevent evaluation. LValue variables may be braced

The opening brace of the body statement should be on the same line as 
foreach. See the rules about grouping in Tcl in the Tcl documentation

The iterator variable(s) may exist in the current scope, but will be 
reused and if they exist must be of standard type. [foreach] will not
check/reject existing variables before using them as iterators

The iterator variable(s) will remain in the current scope after [foreach]
exits and must be unset. [foreach] will not unset iterators on exit

If a break statement is encountered before the list is exhausted, the tail 
of the list will be returned.

    foreach f [ls "c:\\windows\\*.*" -af -list] {                               
       puts "File: $f"                                                          
    }                                                                           
    unset f                                                                     
                                                                                
    foreach {f g} {one {two three} four five six {seven eight} nine ten} {      
       puts "Items f g: $f, $g"                                                 
    }                                                                           
    unset f g                                                                   
                                                                                
    ls *.* a -la -list                                                          
    foreach {i j k l} $a {           # Lvalue vars may be braced to group       
       puts "$i, $j, $k, $l"                                                    
    }                                                                           
                                                                                
    foreach f {one two three} {puts "$f,"}                                      
    unset f                                                                     
                                                                                
    set args {one two three four}                                               
    set arg [foreach {a b c} $args break]                                       
                                                                                
    foreach s [commands] {puts $s}                                              
    unset s                                                                     
                                                                                
    set l [array list argv]                                                     
    foreach s $l {puts $s}                                                      
    unset s                                                                     

Example:

    set a "1 2"                                                                 
    set b "3 4"                                                                 
    foreach x $a y $b {puts "$x $y"}                                            
    unset x                                                                     

Results:

    1 3                                                                         
    2 4                                                                         

When using with arrays take care to wrap multiple Lvalue variable arguments in
braces

    WRONG:   foreach x y [array get env] {puts $x=$y}
    RIGHT:   foreach {x y} [array get env] {puts $x=$y}

foreach may also be used to split lists into variables using an empty body

    set s "123 456"                                                             
    foreach {p q} $s {}                                                         

    Admin@Lenovo 134> vars                                                      
                                                                                
    Defined variables at level 0                                                
    ----------------------------------------                                    
    integer   p                    123                                          
    integer   q                    456                                          

See also: for, while, time, switch, array foreach, list, ls, ticol

Form Input

The [input] and [gotoxy] commands can be used to create simple form-based
input screens to capture user-input. There is no support for Tk and 
Windows GUI based forms

Example:

    # Prompt for 3 basic input fields                                           
    cls                                                                         
    textcolor white darkmagenta                                                 
    puts " * Ticol Demo Form Input * "                                          
    textcolor                                                                   
                                                                                
    if {< $tcl_version 1.27} {                                                  
       puts "Error: Requires Tcl v1.27 or higher. Found v$tcl_version"          
       die                                                                      
    }                                                                           
                                                                                
    proc at_exit {} {                                                           
       gotoxy 1 15                                                              
    }                                                                           
                                                                                
    box 4 4 74 7                                                                
                                                                                
    gotoxy 6 6                                                                  
    puts "Enter your name:    ________________________________________"         
    gotoxy 6 7                                                                  
    puts "Enter your age:     ___"                                              
    gotoxy 6 8                                                                  
    puts "Enter your address: ________________________________________"         
                                                                                
    gotoxy 6 6                                                                  
    puts "Enter your name:    " -nonewline                                      
    gotoxy 26 6                                                                 
    set name [input 40 -f white -b red]                                         
    textcolor                                                                   
    if {eq $name ""} return                                                     
                                                                                
    gotoxy 6 7                                                                  
    puts "Enter your age:     " -nonewline                                      
    gotoxy 26 7                                                                 
    set age [input 3 -f white -b blue -password]                                
    textcolor                                                                   
    if {eq $age ""} return                                                      
                                                                                
    gotoxy 6 8                                                                  
    puts "Enter your address: " -nonewline                                      
    gotoxy 26 8                                                                 
    set address [input 50 -f black -b yellow]                                   
    textcolor                                                                   
    if {eq $address ""} return                                                  

    # Display our results ...                                                   
    box 4 11 74 5                                                               
                                                                                
    gotoxy 6 12                                                                 
    puts "Your name is:       $name"                                            
    gotoxy 6 13                                                                 
    puts "Your age is:        $age"                                             
    gotoxy 6 14                                                                 
    puts "Your address is:    $address"                                         
                                                                                
    gotoxy 1 17                                                                 

Result:

    +--------------------------------------------------------------------+      
    | Your name is:       Me                                             |      
    | Your age is:        23                                             |      
    | Your address is:    me@example.com                                 |      
    +--------------------------------------------------------------------+      

See also: input, inputwin, msgbox, getkey

format
 
    string [format format-string|literal ?arg? ...]

Standard Tcl format command. The syntax is similar to the C printf command.
format prints no output. You must call puts or printf to display the result.
Note that format and printf do not automatically issue a newline character

Examples:

    const euler 2.71828182845904523                                             
    format %x 123456                                                            
    puts [format "%.10f" [/ $euler $pi]]                                        
    puts [format "%.0i" [expr 1/20.0*100]]%                                     

Results:

    1e240                                                                       
    0.8652559794                                                                
    5                                                                           

[format] and [printf] both include 64-bit number formatting as %I64i and 
%I64u (case-sensitive). Overflows occur at about 19 digits.

Format field numeric specifiers may be passed using a * token

    Example: (interprets as '%-20.2s')
    Result:  printf "'%-*.*s'\n" 20 2 Hello    => 'He          '

See also: format string, set, puts, printf, comma

Format String (printf and format)

The [format] and [printf] commands generate a formatted string in a fashion
similar to the ANSI C sprintf function. A format string indicates how to 
format the result, using % conversion specifiers as in C sprintf, and the
additional arguments, if any, provide values to be substituted into the 
result. The return value from format is a formatted string

A format token sequence is introduced via a percent % character

The following formatting tokens may be used with [format] and [printf]

    %<flags><field-width><precision><size-modifier>conversion-type

    %<+|-| |0><N|*><.N|*><ll|i64>b|c|d|e|E|f|g|i|o|s|u|x|X|%

Positional Specifier
--------------------
In standard Tcl the first position is an optional positional specifier
This is unsupported in Ticol Tcl

Optional Flags
--------------
The section portion of a format string consists of an optional flag
character

    -          Left-justify the converted argument                              
    +          Print number with a sign, even if positive.                      
    space      A space should be added to the beginning of the number           
               if the first character is not a sign                             
    0          Specifies that the number should be padded on the left           
               with zeroes instead of spaces                                    
    0n         Specify 'n' number of leading zeroes (e.g. %05i, see below)      

Optional Field Width
--------------------
The third portion of a conversion specifier is a decimal number giving a 
minimum  field width for this conversion. It is typically used to make 
columns line up in tabular printouts. If the converted argument contains 
fewer characters than the minimum field width then it will be padded so
that it is as wide as the minimum field width. 

Padding normally occurs by adding extra spaces on the left of the converted
argument, but the 0 and - flags may be used to specify padding with zeroes 
on the left or with spaces on the right, respectively. If the minimum field
width is specified as * rather than a number, then the next argument to the
format command determines the minimum field width; it must be an integer
value

    printf "'%*s'\n" 20 Hello         => '        Hello'                        
    printf "'%-*s'\n" 20 Hello        => 'Hello        '                        

Optional Precision Specifier
----------------------------
The fourth portion of a conversion specifier is a precision, which consists
of a period followed by a number

If the precision is specified with * rather than a number then the next 
argument to the format command determines the precision; it must be a 
numeric string. 

Example: (interpreted as '%-20.2s')

    printf "'%-*.*s'\n" 20 2 Hello    => 'He          '                         

Optional Size Modifier
----------------------
The fifth part of a conversion specifier is a size modifier, which must be 
ll, h, or l. If it is ll or I64 then the value will be cast to 'wide'
format

    lli        64-bit signed integer                                            
    llu        64-bit unsigned integer                                          
    I64i       64-bit signed integer                                            
    I64u       64-bit unsigned integer                                          

Mandatory Conversion Types
--------------------------
The sixth part of a format string is a mandatory conversion type specifier

    b          Binary integer                                                   
    c          Character type (Non-Tcl standard)                                
    d          Signed decimal                                                   
    e or E     Scientific notation                                              
    f          Floating point/double                                            
    g          General format                                                   
    i          Signed integer                                                   
    o          Octal                                                            
    s          String                                                           
    u          Unsigned integer                                                 
    x or X     Hexadecimal (lower, upper case)                                  
    %          Literal % symbol (%%)                                            

Differences from ANSI printf
----------------------------
                                                                                
    *          Added Tcl support                                                
    %n         Unsupported as per standard Tcl                                  
    %p         Pointer format. (Unsupported as per standard Tcl, use %x)        

Differences from Standard Tcl
-----------------------------
                                                                                
    %N$        Prefix $ unsupported                                             

See also: format, printf, puts

Memory Allocation and Garbage-Collection

Ticol allocates and frees memory for variables automatically. There is
no need to perform any specific 'garbage collection' operations

Array variables are assigned from a self-managing hashtable which expands
dynamically and is recovered automatically as scope exits

Uncontrolled recursion can also cause a 'stack overflow' memory error
where the application will generate a fatal exception error. Recursive 
procedures should be carefully-designed to avoid such situations arising

Memory returned by external DLL calls or assigned by [malloc] for an
external DLL call should be carefully managed

See also: set, proc, malloc, free, memory

get_win_error

    integer [get_win_error]

Retrieve the last registered Windows error. This should be retrieved as soon
as possible before a new event resets the error value to zero

Use [win_error] to format the error message

See also: win_error

glob

    list [glob ?-file d|f? ?-directory name? ?-nocase? pattern]

A limited implementation of the Tcl [glob] command. The [ls] command
offers more flexibility but [glob] is useful for generating lists of 
files/folders or taking file counts

The glob pattern MUST be wrapped in double-quotes if it contains '/*'

[glob] performs a case-sensitive comparison on search results, this can be
overridden using -nocase

The search pattern may specify only * and ?; character matches [chars], 
\x and {a,b,...} are unsupported. Only one search pattern is supported

The options currently support only -type d (directory) or -type f (file)
arguments b,c,l,p,s are unsupported. (df is the default)
The -directory name may be either a path name or a filespec
The types may be combined using multiple -type arguments

A Tcl list is returned with the path separator format will always be in
Unix style "/"

Examples:

    puts [glob -type f]                        # Find all files                 
    puts [glob -type fd]                       # Find all files and dirs        
    puts [glob -type f -type d]                # Find all files and dirs        
    puts [glob -type f d*]                     # Find all files matching d*     
    puts [glob -directory c:\tools -type f a*] # Find files a* in c:\tools      
    puts [glob "/windows/*.dll"]               # Dquotes required for '/*'      
    puts [lcount [glob -nocase "/windows/system32/r*"]]                         

Example:

    puts [glob -type d "\windows\system32\r*"] # From the CLI                   

Result:

    /windows/system32/ras /windows/system32/Recovery                            
    /windows/system32/restore /windows/system32/ro-RO                           
    /windows/system32/RTCOM /windows/system32/ru-RU                             

Example:

    foreach x [glob -type f "/tools/*"] {puts [file normalize $x]               

Result:

    C:/tools/ask.exe                                                            
    C:/tools/md5.exe                                                            
    C:/tools/printf.exe                                                         
    C:/tools/ticol.exe                                                          

Glob is also a term which refers to wildcard string matching present in
the following commands:

    array get                                                                   
    array names                                                                 
    lsearch                                                                     
    string match                                                                
    switch                                                                      
    unset                                                                       
    vars                                                                        

See also: ls

get_folder

    string [get_folder ?-initdir initdir? ?-flags flags? ?-title string?]

Creates a browse dialog with a user-input edit box

Uses the Windows shell GUI to browse for a folder, and, if required create
a new foldername. If the interface is cancelled then an empty string is
returned. Unicode is not supported. No error codes are returned

[get_folder] accepts Unix style forward-slash '/' path separators but
requires an unescaped string for '-initdir' if Windows backslash path
separators are used. Use [unescape]

A path containing Windows '\' path separators will be returned unless
the user cancels input

Some useful flag values (may have no effect in some Windows releases):

    #define BIF_RETURNONLYFSDIRS    0x00000001                                  
    #define BIF_EDITBOX             0x00000010                                  
    #define BIF_NEWDIALOGSTYLE      0x00000040                                  

See the Windows API guide for full details of folder browse flags

Examples:

    #define BIF_RETURNONLYFSDIRS    0x00000001                                  
    set s [get_folder -flags BIF_RETURNONLYFSDIRS]                              
    set s [get-folder -initdir [unescape "c:\\temp"]                            
    set s [get-folder -initdir [unescape "c:/windows"]                          
    set s [get-folder -initdir [unescape "c:\\"] -title "Select txt files"      

See also: get_fileopen, get_filesave

getkey

    string [getkey ?-integer? ?-lower? ?-upper?]

Wait for a keypress and return the character. If the -integer argument is 
given then an integer value representing the key input is returned

Upper or lower case can be forced using -lower or -upper

Note that PC extended key handling for keys such as F1 will return a 
sequence of two integer values. For more information, search for PC keyboard
handling

Use [inkey] to handle keyboard polling loops

Example:

A useful Yes/No/Quit key-input procedure

    if {! [defined get_key_input]} {                                            
       proc get_key_input {} {                                                  
          while {1} {                                                           
             set k [getkey -lower] # Returns a lower-case keypress              
             if {eq $k "y"} {                                                   
                puts $k                                                         
                return "-r"                                                     
             } elseif {eq $k "n"} {                                             
                puts $k                                                         
                return {}                                                       
             } elseif {eq $k "q"} {                                             
                puts $k                                                         
                return q                                                        
             }                                                                  
             sleep 20                                                           
          }                                                                     
       }                                                                        
    }                                                                           

Example:

Prompt user to copy text to clipboard

    textcolor yellow                                                            
    puts "Copy screen to the clipboard ? Y/N" -nonewline                        
    set k [getkey -lower]                                                       
    if {eq $k "y"} {                                                            
       clipboard gettext            # Grab the screen as text                   
       puts "\\nCaptured screen to clipboard"                                   
    } else {                                                                    
       newline                                                                  
    }                                                                           
    textcolor                                                                   

See also: key input, inkey, gets, input, inputbox

getsid

    integer [getsid <username>]
    integer [getsid <domain\\username>]

Retrieve a Windows SID string for a given username. The domain prefix is
optional. If omitted the current workstation/workgroup will be used

Example:

    puts [getsid administrator]                                                 
    puts [getsid snoopy\\administrator]                                         
    puts [getsid [username]]                                                    

Result:

    S-1-5-21-460261754-543217201-1416368962-500                                 
    S-1-5-21-460261754-543217201-1416368962-500                                 
    S-1-5-21-460261754-123456789-9876543210-500                                 

See also: pid, tid, username

global

    global varName ?varName? ... ?-value value?

Link to an existing variable in global scope or, if this does not exist, 
create a new, simple variable (non struct/array) in global scope with an
alias of the same name in the local scope

[global] is valid only within procedures

[global] is used within procedures to create a global in the root (::global) 
namespace and a local (upvar) copy of the same variable. The value argument 
is optional and if omitted the new variable will be set to empty ""

When the local copy of the global variable is updated the global copy will
also be updated. The local copy is destroyed on exit of the procedure but
the global copy will remain with values set by the procedure

Ticol global differs from standard Tcl in supporting a single 'value' argument
which may be used to initialise all of the specified values to, say, zero

Where a scope prefix is given (e.g. a in 'a::b', this will be emulated
and a local variable created which uses the tail value (e.g. 'b' in this 
example)

Example:

    proc create_global {} {                                                     
      # Create a global var with local upvar copy, set it then change it        
      puts "Calling \[global global_var -value 12345\]"                         
      # Creates var in global scope                                             
      global global_var -value 12345                                            
      puts "Local: \[bool \[is_set global_var\]\]=\                             
        [bool [is_set global_var]]"                                             
      puts "Global:\[bool \[is_set ::global_var\]\]=\                           
        [bool [is_set ::global_var]]"                                           
      puts "Check:  \$::global_var=$::global_var"                               
      # Update the local copy                                                   
      set global_var 11111                                                      
      # Then check the global copy                                              
      puts "Recheck:\$::global_var=$::global_var"                               
    }                                                                           
                                                                                
    create_global                                                               
    vars gl*                                                                    

Result:

    Calling [global global_var -value 12345]                                    
    Local: [bool [is_set global_var]]=true                                      
    Global:[bool [is_set ::global_var]]=true                                    
    Check:  $::global_var=12345                                                 
    Recheck:$::global_var=11111                                                 
                                                                                
    Defined variables matching 'gl' at level 0                                  
    ------------------------------------------                                  
    integer   global_var           11111                                        

Example:

    proc create_global {} {                                                     
       # Create a global var with an emulated scope prefix                      
       global a::b                    # Create '::a::b' and local 'b'           
       set b 12345                    # Local 'b' and '::a::b' are set          
    }                                                                           
                                                                                
    create_global                                                               
    vars gl*                                                                    

Result:

    Lists variable 'a::b' as 12345                                              

See also: global variables, preset globals

Global Variables
 
Variables in global scope, including arrays, may be accessed by the global 
scope prefix '::'. This must appear immediately after any dollar sign, e.g.

    $::globalVar                                                                

Global constants, when accessed within procedures, must be either referred 
to using $:: or declared using upvar which will create a local const instance

See: preset globals - for a discussion of pre-set Ticol global variables

Nested scope levels exist within procedures. This is referred to as the 
given 'level'. This value increases from 0 at the top (global) level to 
higher values as procedure call-frames are added. 

Variable scope within flow-control structures is the same as at the current
procedure level

Example:

    proc getvar {} {                                                            
       set a "I am here"                                                        
       puts "proc local a is:  '$a'"                                            
       puts "proc global a is: '$::a'"                                          
       puts "proc const b is $::b"                                              
    }                                                                           
                                                                                
    set a "Hello"                                                               
    const b 1234                                                                
    puts "a in global scope is $::a"                                            
    getvar                                                                      

Result:

    a in global scope is Hello                                                  
    proc local a is:  'I am here'                                               
    proc global a is: 'Hello'                                                   
    proc const b is 1234                                                        

Example of declaring a global variable and setting as an array/element

    set a ""                                                                    
    proc set_global_array {} {                                                  
       # Set (cast) global non-array variable as array with one element         
       set ::a(1) "One"                                                         
    }                                                                           
    set_global_array                     # Call the proc                        
    puts "\$a(1) set by proc is '$a(1)'" # Print the results                    

Result:

    $a(1) set by proc is 'One'                                                  

See also: preset globals, global, set, let

goto_block, goto

    goto_block, goto, gosub                                                     
                                                                                
    goto_block {                                                                
       goto local_label                                                         
       gosub local_label                                                        
       label {statement}                                                        
       ...                                                                      
    }                                                                           

[goto] is implemented purely for the entertainment of the author
[goto] statements are valid only within the scope of a [goto_block]
statement

The [goto] paradigm is unpopular and, if not used correctly, can lead to
messy and difficult to debug source code; but it may be of interest to
computer science students

A [goto_block] is a simple set of locally-constrained labels and matched,
braced script pairs which works in a manner similar to PHP's goto statement
but which is confined in scope to one or more [goto_block] statements.

If a label's script argument consists of only a single-command there is no
need for braces. In addition to pairs of labels and scripts, a [goto label]
or [gosub label] entry with matching label argument is allowed

It is not possible (nor recommended) to jump into any structured flow
control using [goto]. A [goto_block] cannot span or overlap a flow control
command such as [for] or [while]

No freeform Tcl code is allowed in the [goto_block] outside a properly-
defined block. All Tcl script code must be declared under a block 
associated with a valid label - e.g.

    goto_block {                                                                
       puts "Hello"            # Wrong (freeform code with no label)            
    }                                                                           

    goto_block {                                                                
       init {puts "Hello"}     # Correct (label + code block pair)              
    }                                                                           

[goto_block] labels may be any string (including Tcl command names) but 
they must be unique within the context of each [goto_block]

Unless a [goto] or [gosub] command is encountered, a [goto_block] will
sequentially evaluate each label's script argument in turn, one after the
other. A label's script is executed immediately when encountered with
execution starting at the first valid label in the block. Each successive
label will be evaluated unless otherwise directed by a [goto] or [gosub]
statement

A [break] or [return] within a label's script will exit the [goto_block]
and ignore all remaining labels and code within that block. e.g. 

    goto_block {               # Sequential labels + statements ...             
        label1 { puts Hello }  # 1)                                             
        label2 {}              # 2) Empty arguments are permitted               
        label3 { break }       # 3) Will cause the goto_block to exit           
        label4 { puts world }  # 4) Never executed                              
        label5 { return }      # 5) [goto_block] would exit                     
    }                                                                           

Result:

    Hello                                                                       

Each label entry within a [goto_block] must consist of a label followed by
a code block. The code block must be wrapped in braces if there is more
than one command present. Labels need not be all-numeric and can be any
valid string. Empty label script argument parentheses are allowed

Any required block initialisation code for the [goto_block] MUST appear
within a dummy label entry and not 'inline'. It is suggested that an 
'init' label is used as the first label entry and any setup code executed
within the 'init' code-block.

Label statements are executed sequentially until a [goto] statement is
encountered or after one or more goto statements has been executed

[goto_blocks] may be nested but must appear within the scope of a properly
constructed block statement. However, labels are unique and local to each 
[goto_block] at each level. Nested goto_blocks are probably a bad idea and
could lead to very messy and hard to debug code

Variable $this is set to the label entry on entering a code block

Example:

    goto_block {                                                                
       label1 {                                                                 
          puts "In parent goto_block label1"                                    
          goto_block {                                                          
             label1 {                                                           
                puts "In child goto_block label1"                               
                goto_block {                                                    
                   label1 {                                                     
                      puts "In grandchild goto_block label1"                    
                      goto label2                                               
                   }                                                            
                   label2 {                                                     
                      puts "In grandchild goto_block label2"                    
                   }                                                            
                }                                                               
             }                                                                  
             label2 {                                                           
                puts "In child goto_block label2"                               
             }                                                                  
          }                                                                     
       }                                                                        
    }                                                                           

Result:

    In parent goto_block label1                                                 
    In child goto_block label1                                                  
    In grandchild goto_block label1                                             
    In grandchild goto_block label2                                             
    In child goto_block label2                                                  

[gosub] may be used to call a local label and resume execution at the next
statement after that original [gosub]

[gosub], when used within a label's argument script, behaves normally and
will only call a registered [sub]. This [sub] may be declared within the
[goto_block] or externally (e.g. globally)

Example:

    sub external {                                                              
        puts "In external sub"                                                  
    }                                                                           
    goto_block {                                                                
        gosub label_two           # Call local block                            
        gosub label_one           # Call local block                            
        label_one {                                                             
            puts "In yr label1"                                                 
            gosub external        # Call external [sub]                         
        }                                                                       
        label_two {                                                             
            puts "In yr label2"                                                 
        }                                                                       
    }                                                                           

Results:

    In yr label2        # [gosub] calls directly                                
    In yr label1        # [gosub] calls directly                                
    In external sub     # Called by non-[goto_block] [gosub]                    
    In yr label1        # 'Fall-through' to label1                              
    In external sub     # Called by non-[goto_block] [gosub]                    
    In yr label2        # 'Fall-through' to label2                              

A [return] within a label's script argument will behave as follows:

* If called from [goto] or sequential execution then [return] will
  always exit the [goto_block]

* If called from a [gosub] a will return from the [goto_block] unless
  [return -code ok] is used (default is to return -code return)
  If [return -code ok] is used then execution will continue with the 
  next statement in the [goto_block]

    goto_block {                                                                
       gosub label_one                                                          
       label_one {                                                              
          puts "Hello user"                                                     
          return      # Use 'return -code ok' to continue execution             
       }                                                                        
       label_two {                                                              
          puts "I am never executed"                                            
       }                                                                        
    }                                                                           

If a [break] statement is encountered this will cause the [goto_block] to
exit. A [goto_block] cannot be exited using return, other than using 
[return -code break]

Example:

    goto_block {                                                                
                                                                                
       10 { puts "Going to 30"                                                  
           goto 30                                                              
          }                                                                     
       20 { puts "Going to end"                                                 
           goto end                                                             
          }                                                                     
       30 { puts "Going to 20"                                                  
           goto 20                                                              
          }                                                                     
       end { puts "Exiting" }                                                   
    }                                                                           

Example:

    set a 1; goto_block { 10 {;} 11 {;} 12 {puts $a} 12 {incr a; goto 10} }     

Example:

    goto_block {                                                                
       # goto_block used to comment out a block of code                         
       goto end                                                                 
       puts "This statement won't be executed"                                  
       end                                                                      
    }                                                                           

A BASIC-like ON GOTO can be simulated using the following. This behaves the
same as C++ switch()

    goto_block {                                                                
       init {                # Initialisation must be within a dummy block      
          set var [rnd 1 3]                                                     
          goto $var          # Jump to a [goto] entry                           
       }                                                                        
       1 {                                                                      
          puts "One"                                                            
          break                                                                 
       }                                                                        
       2 {                                                                      
          puts "Two"                                                            
          break                                                                 
       }                                                                        
       3 {                                                                      
          puts "Three"                                                          
          break                                                                 
       }                                                                        
    }                                                                           

Braces are required for the rvalue of each goto sub-block when more than
one command is present and items are separated by whitespace. The following
[goto_block] will jump to the selected label similar to ON GOTO and will 
then execute all subsequent blocks sequentially

A [break] may be introduced after each proc call if each jump case needs
to be 'atomic' ...

Example:

    proc one {} {                                                               
       puts "One"                                                               
    }                                                                           
    proc two {} {                                                               
       puts "Two"                                                               
    }                                                                           
    proc three {} {                                                             
       puts "Three"                                                             
    }                                                                           
    proc four {} {                                                              
       puts "Four"                                                              
    }                                                                           
                                                                                
    goto_block {                                                                
       init {                # Initialisation is in a dummy block               
          set var [rnd 1 4]  # Initialise 'var'                                 
          goto $var          # Jump to a [goto] entry, below ...                
       }                     # .. then continue, sequentially                   
       1 one                 # Call proc [one]                                  
       2 two                 # Call proc [two]                                  
       3 three;break         # Call proc [three] (with break example)           
       4 four                # Call proc [four]                                 
    }                                                                           

Example emulated loop with conditional exit:

    option expression off                                                       
    set s 0                                                                     
    goto_block {                                                                
       start {               # Loop start                                       
          ++ s               # Increment                                        
          if {> $s 9} {      # Test                                             
             goto end        # Exit                                             
          }                                                                     
          goto start         # Loop end                                         
       }                                                                        
       end {                                                                    
          textcolor magenta                                                     
          puts "Puts exited OK at label: end"                                   
          textcolor                                                             
       }                                                                        
    }                                                                           

Example:

'Flip-flops' are memory-stable and can be run infinitely

    set sleep_delay 2000                                                        
    goto_block {                                                                
       10 {puts "in 10";sleep $sleep_delay;goto 20}                             
       20 {puts "in 20";sleep $sleep_delay;goto 10}                             
    }                                                                           

Variable $this is set by [goto_block]

Example:

    proc foo {} {                                                               
       puts "Called by $::this"                                                 
    }                                                                           
    goto_block {                                                                
       one {                                                                    
          foo                                                                   
       }                                                                        
       goto three                                                               
       two {                                                                    
          foo                                                                   
       }                                                                        
       three  {                                                                 
          foo                                                                   
       }                                                                        
    }                                                                           

Result:

    Called by one                                                               
    Called by three                                                             

Used outside of a [goto_block], [goto] will echo its first argument if
[option echo] is on

Infinite Loops

It is possible to make coding 'mistakes' whereby jump errors within a 
[goto_block] may result in infinite loops. Here is a clumsy exmaple...

    option expression off                                                       
    goto_block {                                                                
       # This block never exits...                                              
       init {                                                                   
          set i 0                                                               
       }                                                                        
                                                                                
       10 {                                                                     
          while {< $i 10} {                                                     
             puts "Loop $i of 10"                                               
             if {> $i 5} {goto init}                                            
             ++ i                                                               
          }                                                                     
       }                                                                        
    }                                                                           

Dynamically-Defined Block Labels

Variables may be used to define [goto_block] labels but these are evaluated
only once and on entry into the block. Once control has passed into the 
[goto_block] these can no longer be changed and changes in a label's 
variable will have no effect.

set a 1                                                                         
set b 2                                                                         
set z 0                                                                         
                                                                                
goto_block {                                                                    
    init {                                                                      
        puts "In $this"                                                         
        if {rnd 0 1} {                                                          
            set a 2         # Reset the ordering                                
            set b 3         # Has no effect on labels                           
        }                                                                       
        goto $b$z                                                               
    }                                                                           
    $a$z {                  # Label is fixed at 10                              
        puts "In $this"                                                         
        goto $a$z                                                               
    }                                                                           
    $b$z {                  # Label is fixed at 20                              
        puts "In $this"                                                         
    }                                                                           
}                                                                               

Implementing [break]
--------------------
A root-level immediate break within a [goto_block] can be implemented as
follows

    goto_block {                                                                
        label1 {code}                                                           
        {} break                                                                
        label2 {code}                                                           
    }                                                                           

or 

    goto_block {                                                                
        label1 {code}                                                           
        label2 break                                                            
        label3 {code}                                                           
    }                                                                           

Background Reading
------------------
    https://www.php.net/manual/en/control-structures.goto.php
    https://www.mcherm.com/in-defense-of-the-much-maligned-goto.html
    http://www.davar.net/PROGRAM/EXTRACTS/GO-TO.HTM

See also: gosub, sub, call, do, while, for, foreach, flow control

gosub, sub

The [sub] and [gosub] commands are relatively useless and has been
implemented for entertainment value only. [proc] should be used instead

The [gosub] command is 'syntactic-sugar' and is an optional part of the
Ticol Tcl language. For a discussion of [goto_block] see: goto_block

    sub label {code}
    gosub label        # When used within goto_block as an lvalue
    gosub proc         # When used within a code block as a command

A [sub] subroutine is a procedure ([proc]) which takes no arguments and
(usually) returns no value. [return] commands are ignored and return
values are discarded. Any arguments given to a [gosub] command are ignored.

[gosub] may also be combined with [goto] within a [goto_block]
For use of [gosub] within a [goto_block],

[sub] labels are case-sensitive. [gosub] may also call any proc but will
pass no arguments to it.

The commands [static], [upvar], [uplevel] etc. may be used within a
subroutine exactly as with [proc]. Local variables in [sub] work in exactly
the same way as with [proc]

[gosub] has two different behaviours. Within a script, including script 
blocks of a [goto_block] it will [gosub] (call) a defined subroutine. 
At 'root' level within a [goto_block] it will [gosub] (or, call) a block
label, See [goto_block] and [sub]

When calling a label within a [goto_block] a [return] is optional

[gosub] can also call any defined [proc] but will pass no arguments to it

[gosub] will return the return value of any [sub] or [proc] which is
called although in most cases this would be discarded

Example:

    sub sub1 {                                                                  
       static counter                                                           
       ++ counter                                                               
       puts "In sub1. Counter is $counter"                                      
    }                                                                           
    gosub sub1  # Used here as an lvalue                                        
    gosub sub1  # Used here as an lvalue                                        

Result:

    In sub1. Counter is 1                                                       
    In sub1. Counter is 2                                                       

Example:

    sub sub1 {                                                                  
       static counter1                                                          
       ++ counter1                                                              
       puts "In sub 1. counter1 is $counter1"                                   
       return              # optional, return values are discarded              
    }                                                                           
                                                                                
    sub sub2 {                                                                  
       static counter2                                                          
       ++ counter2                                                              
       puts "In sub 2. counter2 is $counter2"                                   
       gosub sub1          # Call another sub. Scope prefix required            
       return              # optional, return values are discarded              
    }                                                                           
                                                                                
    gosub sub1                                                                  
    gosub sub2                                                                  
    gosub sub1                                                                  
    vars                                                                        

Result:

    In sub 1. counter1 is 1                                                     
    In sub 2. counter2 is 1                                                     
    In sub 1. counter1 is 2                                                     
    In sub 1. counter1 is 3                                                     
                                                                                
    Defined variables at level 0                                                
    --------------------------------                                            
    integer   sub2::counter      1                                              
    integer   sub1::counter      3                                              

CAUTION: [gosub] subroutines may not call each other or call themselves
recursively and block execution or a stack-overflow error may result.

Example:

Stack overflow conditions can be triggered by the following abuses...

    sub sub1 {            # Problem 1                                           
       gosub sub1         # Self-recursion will cause a stack-overflow          
    }                                                                           
    gosub sub1                                                                  

                                                                                
    sub sub2 {            # Problem 2                                           
       gosub sub3         # Mutual calls will cause a stack-overflow            
    }                                                                           
                                                                                
    sub sub3 {                                                                  
       gosub sub2                                                               
    }                                                                           
    gosub sub2                                                                  

The [gosub] prefix is optional outside of [goto_block]. A defined
subroutine may be called directly by referencing its name

Example:

    sub sub1 {                                                                  
       puts "In sub1"                                                           
    }                                                                           
                                                                                
    sub1                   # Call the subroutine without [gosub] prefix         

Result:

    In sub1                                                                     

Using [gosub] within [goto_block] (Block labels as subroutines)
---------------------------------------------------------------
[gosub] is also allowed within a [goto_block], but the [gosub] call must
be at the same scope level as the labels (i.e. not within a label's code
block scope). Execution of defined subroutine blocks may be skipped by
using a dummy (empty) skip label as in the example below.

The label's code-block must return code 'ok' using 'return -code ok'

Note that [gosub], where used in a block's script, will call an 
externally defined [sub], not a block label.

Example:

goto_block {                                                                    
    goto skip       # Skip execution of the blocks below...                     
    one {                                                                       
       textcolor darkyellow                                                     
       puts "* In $this"                                                        
       textcolor                                                                
       return -code ok                                                          
    }                                                                           
    two {                                                                       
       textcolor magenta                                                        
       puts "* In $this"                                                        
       textcolor                                                                
       return -code ok                                                          
    }                                                                           
    skip {}         # Empty/dummy label placeholder                             
    gosub two       # Call block 'two' (gosub keyword required)                 
    gosub one       # Call block 'one' (gosub keyword required)                 
}                                                                               

Results:

    * In two                                                                    
    * In one                                                                    

See also: goto_block,  goto, proc, static, upvar, uplevel, eval, dummy, 

hasp - Ticol Hasp HL Plugin

A HASP HL plugin to query local and remote network HASP licence servers

      To load, use:    lib ticol_hasp ?-verbose? ?-nocomplain?

Prerequisite:

    The HASP HL driver must be installed and running on the target host

Command and Syntax:

    list-of-dongle-ids [hasp keys hostname|ip ?-port value?]
    list-of-fids [hasp modules hostname|ip keyid ?-port value?]
    date-string [hasp expiry hostname|ip keyid fid ?-port value?]
    string [hasp product hostname|ip keyid fid ?-port value?]
    string [hasp type hostname|ip keyid ?-port value?]
    list [hasp data hostname|ip ?-port value?]
    list [hasp keydata hostname|ip ?-port value?]

Note that the HASP HL series driver must be installed to run this plugin
Note also that 'fid' is Feature ID code, 'keyid' must be a single key ID, 
not a list of key IDs

Non networked (HASP HL Time) can only be discovered on the local machine

[hasp keys]    Returns a list of discovered HASP key IDs                        
[hasp keydata] Returns a list of basic information on a unique HASP key         
               basis for all known keys. 5 fields are returned per key          
               Key#, IP, Hostname, Type, Module FID, Module Expiry              
[hasp modules] Returns a list of known module FIDs by key ID                    
[hasp expiry]  Returns the expiry of a given module                             
[hasp product] Returns the product name for a given FID                         
[hasp type]    Returns they key type of a given HASP key ID                     
[hasp data]    Returns all of the known HASP key data from a host               

Example:

    # List basic key information (per key)                                      
    set i 1                                                                     
    foreach x [hasp keydata localhost] {                                        
       puts "$i '[lindex $x 0]' '[lindex $x 1]' '[lindex $x 2]'"                
       ++ i                                                                     
    }                                                                           

Example:

    # Do not use                                                                
    puts [hasp modules localhost [hasp keys localhost]]                         

Examples:

    lib ticol_hasp.dll                                                          
    # Show all connected+known keys on server foo                               
    puts [hasp keys foo]                                                        

                                                                                
    # List known module FIDs on the first known key                             
    lib ticol_hasp.dll                                                          
    puts [hasp modules localhost [lindex [hasp keys localhost] 0]]              

Useful references
-----------------
    https://sentinel.gemalto.com/software-monetization/sentinel-hasp-hl/

See also: plugins, ticol_rlm

Include files

    source filename

There is no #include macro for Ticol. Includes are processed in real-time
by using the [eval] command. As the 'included' file will undergo Macro
Preprocessing it should be arranged to include the file only once near the
start of any script. 

Avoid using [source filename] inside loops unless careful thought is put
into the script design, including handling variable conflicts and chained
error conditions

Example:

    source defs.tcl                                                             
    puts $LONG_MAX                                                              

You can use [eval] to include data to set arrays etc. which has been
defined in a separate file

Example:

    # File. include_data.tcl                                                    
    return {                                                                    
       1 {foo}                                                                  
       2 {bar}                                                                  
       3 {baz}                                                                  
       4 {quux}                                                                 
    }                                                                           

                                                                                
    # Main file which will include the data file                                
    puts "Including include_data.tcl"                                           
    array set data [source include_data.tcl]                                    
    for {set i 1} {<= $i [array size data]} {++ i} {                            
       puts "$data($i)"                                                         
    }                                                                           

Results:

    foo                                                                         
    bar                                                                         
    baz                                                                         
    quux                                                                        

See also: Macro, eval

index

    string [index string N]

Rapid indexing of strings to return a single character. [index] uses base 0
indexing values where 0 represents the first character of a string. Out of
bounds index values will return the null string {}

Example:

    puts -[index $qbf 0]-                                                       
    puts -[index $qbf 1]-                                                       
    puts -[index $qbf 2]-                                                       
    puts -[index $qbf 42]-                                                      

Result

    -T-                                                                         
    -h-                                                                         
    -e-                                                                         
    -g-                                                                         

See also: strstr, instr, string

Tcl Programming Idioms

Idioms are code fragments with a common theme and frequent usage

"A programming idiom expresses a recurring construct in one or more
programming languages. Developers recognize programming idioms by
associating and giving meaning to one or more code fragments" (Wikipedia)

Idioms are often implemented 'longhand' and inefficiently using a number
of separate commands (words). Such idioms may be compressed into a single
command. In the case of Ticol, which is implemented more efficiently as
a distinct command

In essence, they represent one or more ways of doing a common task

Some non-standard idiomatic commands have been included with Ticol. 
These can be written by combining several other commands but these
inbuilt idioms improve speed and efficiency by compressing into a
single command

Removing a matching sequence of characters from the start of a string

    See [chomp], [html chomp_tag]

Extract complete HTML tags from a text string

    See [html chomp_tag]

Align one string into another

    See [setl], [setc], [setr]

Wrap text into a fixed width boundary

    See [wrap]

Convert JSON data to a Tcl list

    See [json_to_list]

Efficiently concatenate strings

    See [concat], [append], [store]

Match one of a series of characters to a location in a string

    See:[instr], [strstr]

Create a repeated list/string sequence

    See [string repeat], [range]

Return the last character of a string

    See [lastchar], [mids]

Split a string along fixed width boundaries

    See [slice]

Return a single character within a string

    See [index], [mids]

Return part of a string

    See [mids]

Return all of a string after a given index position

    See [mids]

Format a number as a comma-separated sequence

    See [comma]

Replace part of a string with a sequence of characters

    See [setat], [mid]

Declare and initialise

    See [static], [let], [dim]

Show Disk Free Space (UNC or local)

    See [diskfree]

References: 
    https://en.wikipedia.org/wiki/Idiom

if..elseif..else (Flow Control)
 
    if {condition} {statement}
    if {condition} {statement} ?else {statement}?
    if {condition} {statement} ?elseif {statement}? ?else {statement}?

The if statement controls conditional branching

The body of an if statement is executed if the value of the expression is 
evaluated as nonzero. The number of elseif statements is unlimited but 
execution will slow down as more cases are added

The opening brace for the statement following if or else MUST be on the same 
line as the if or else verb. 

The standard Tcl keyword 'then' is wholly superfluous and not supported in
order to speed-up execution of the interpreter

Ensure arguments are enclosed in braces

    if {condition} {                                                            
       statements...                                                            
    }                                                                           
                                                                                
    if {condition} {                                                            
       statements...                                                            
    } elseif { condition } {                                                    
       statements...                                                            
    } else {                                                                    
       statements...                                                            
    }                                                                           

Testing Numeric Conditions
--------------------------
The 'condition' clause may be an expression, numeric literal or [eval] 
return. If 'condition' is a numeric expression then test commands are
not required

Example:

    set s 23                                                                    
    if {$s} {puts Yes} else {puts No}                                           
    set s 0                                                                     
    if {$s} {puts Yes} else {puts No}                                           
    if {string length hi} {puts Yes} else {puts No}                             
    if {string length ""} {puts Yes} else {puts No}                             

Results:

    Yes                                                                         
    No                                                                          
    Yes                                                                         
    No                                                                          

Compact if..elseif Form
-----------------------
Line-continuation characters may be used to produce a compact elseif
ladder without braces. Note that the \ backslash is not required on the
last elseif line:

    set failed $false                                                           
    if {eqi $tag area} {set failed $true}\                                      
    elseif {eqi $tag base} {set failed $true}\                                  
    elseif {eqi $tag br} {set failed $true}\                                    
    elseif {eqi $tag col} {set failed $true} # No backslash on last clause      
    if {$failed} {                                                              
       ...                                                                      
    }                                                                           

Subcommands
-----------
The following additional commands are available with [if-elseif-else]

    return             # Will exit the script when used in root-level [if]      
    break              # Will be passed back to any enclosing structure         
    stop               # Halt operation, return to the CLI if loaded            
    exit               # Exit the script and return to Windows                  
    goto               # Where used with an enclosing [goto_block]              

Optimising Very Large if...elseif Ladders
-----------------------------------------
See: optimising if

Expression v's Eval
-------------------
Flow control structures use the settings of 'option expression' to decide
whether to test statements using [expr] or [eval]. When using expression on
bear in mind that the argument must be a valid math expression and NOT a 
command

If 'option expression' is ON, then you MUST use valid math expressions. Math
expressions must use direct or implied logical tests on command results. 
Command calls require square brackets with expression ON in order to return
a boolean value to test

Caution: Don't do this ...

    option expression on                                                        
    if {[$argc > 1]} { ...  # [] transforms into a command call                 

As Ticol will try to dereference argc first and then try to run a command
with that name. Use this form...

    option expression on                                                        
    if {$argc > 1} { ...    # A braced expression is required here              

Example Expression:

    option expression on                                                        
    if {is_set w} {puts yes} else {puts no}      # Not a math expression        
                                                                                
    if {[is_set w]!=0} {puts yes} else {puts no} # Correct math expression      
                                                                                
    if {[is_set w]} {puts yes} else {puts no}    # Correct math expression      

Example Eval:

    option expression on                                                        
    if {[is_set w]!=0} {puts yes} else {puts no} # Wrong for use with eval      
                                                                                
    if {[is_set w]} {puts yes} else {puts no}    # OK for eval                  
                                                                                
    if {is_set w} {puts yes} else {puts no}      # OK for eval, [] optional     

More complex, combined conditional expressions may be formed using [expr]

Example:

    option expression off                                                       
    if {expr [ne $var_1 ""] && [ne $var_2 ""]} { ...                            
                                                                                
    option expression on                                                        
    if {[ne $var_1 ""] && [ne $var_2 ""]} { ...                                 

If Tests for Non-Zero Results
-----------------------------
[if] tests for a non-zero expression or command result so expressions may
sometimes return unusual or unexpected results

    option expression on                                                        
    set i 9                                                                     
    if {$i - 10} {puts yes} else {puts no}                                      
    if {$i - 9} {puts yes} else {puts no}                                       
    if {$i - 8} {puts yes} else {puts no}                                       

Results

    yes                                                                         
    no                                                                          
    yes                                                                         

See also: else, elseif, on, switch, flow control

info

* Note that adapter and TCP/IP information is unsupported in Windows NT 

info adapter_count            # Return the count of known network adapters      
info adapter_id n             # Return adapter GUID                             
info adapter_name n           # Return the adapter name                         
info adapter_type n           # Return the adapter type value                   
info argcount a               # Return the number of non-switch (/) args        
info args procname            # Return the argument list of a procedure         
                              # Cannot be blocked by TCX encryption             
info body procname            # Return the script body of a procedure           
                              # Cannot be blocked by TCX encryption             
info cmdcount                 # Return a count of total command calls           
info commands ?pattern?       # List available commands                         
info domain ?n?               # Show the DNS domain name bound to adapter       
info dhcp_enabled n           # Return boolean 1 if DHCP is enabled else 0      
info dhcp_end n               # Show the DHCP lease expiry date                 
info dhcp_server n            # Show any currently configured DHCP server       
info dhcp_start n             # Show the DHCP lease start date                  
info disks                    # Return a Tcl list containing active disks       
info disksize x:              # Show size of the given disk                     
info disktype x:              # Show the disk type for a given drive/path       
                              #    0 Unknown type                               
                              #    1 No root directory/Unavailable/Error        
                              #    2 Removable                                  
                              #    3 Fixed                                      
                              #    4 Remote                                     
                              #    5 CDROM/DVD                                  
                              #    6 RAMDISK                                    
info encrypted                # Returns 1 if script was obfuscated else 0       
info executablename           # Returns the command line EXE filename           
info exists varname           # Check if a variable exists, returns 1 or 0      
info fmac_address n           # Return a formatted adapter MAC address          
info frame ?number?           # Show the frame number                           
info functions                # Return a list of functions                      
info gateway n                # Return the configured IP gateway address        
info globals ?filter?         # Returns a Tcl list of variables at level 0      
info guid                     # Return a new, unique GUID string                
info hostname                 # Return the configured PC host name              
info ip_address n             # Return the IP address for an adapter            
info is_dll                   # Returns true (1) if running in ticol.dll        
info is_elevated              # Show whether running elevated (Win7+)           
info is_networked             # Show whether networked or not                   
info level ?n?                # Show the call frame level (top level is 0)      
info lib                      # The library path name                           
info locals ?filter?          # Tcl list of variables at the current level      
info mac_address n            # The adapter's MAC address                       
info memtotal                 # The total physical memory in bytes              
info memavail                 # The available physical memory in bytes          
info memtotalpage             # The total swapfile memory in bytes              
info memavailpage             # The available swapfile memory in bytes          
info memtotalvirtual          # The total virtual memory in bytes               
info memavailvirtual          # The available virtual memory in bytes           
info mempercentfree           # Show the % free to 2 decimal places             
info monitorcount             # Show the number of connected monitors           
info nameofexecutable         # Ticol's EXE name and path                       
info netmask n                # The adapter's IP subnet mask                    
info primary_dns n            # The adapter's primary DNS server                
info primary_wins n           # The adapter's primary WINS server               
info procname                 # The currently active proc else ""               
info procs ?filter?           # A list of user-defined procedures               
info script                   # The name of the active script (if any)          
                              # Is (re)set by [source],[load],[run]             
info scriptpath               # The escaped, qualified path of the script       
                              # Is (re)set by [source],[load],[run]             
info secondary_dns n          # The adapter's secondary DNS server              
info secondary_wins n         # The adapter's secondary WINS server             
info tclversion               # The Ticol version. Same as $::tcl_version       
info username                 # Return the currently logged-on user             
                              # List variables. See also: info vars             
info vars ?pattern? ?-const? ?-nocase?                                          
info winver ?-list?           # Windows version information (as a list)         
                              # See: info winver                                

Where a network adapter is given this is a base 0 value
Multiple network information calls are necessarily slow as an API call has
to be repeatedly made to retrieve each attribute.

# Example                       Result           Comment                        
# ----------------------------------------------------------------------------  
puts [info argcount argv]     # 1                Count non-switch '/' args      
puts [info args square]       # x                List procedure arguments       
puts [info body square]       # * $x $x          List procedure body            
puts [bool [info exists pi]]  # true             Check if var pi exists         
puts [info exists commands c] # call calldll...  List all starting with 'c'     
puts [info executablename]    # c:\tcl\ticol.exe Shows the EXE name and path    
puts [info ip_address 0]      # 0.0.0.0          Show IP for adapter #1         
puts [info ip_address 2]      # 192.168.1.23     Show IP for adapter #3         
puts [info hostname]          # WORKSTATION23    Show the host name             
puts [info tclversion]        # 1.06             Ticol-specific version no      
puts [info vars pi -const]    # pi 3.141592...   List vars starting with pi     
puts [info procs f*]          # foo              List procs starting with f     
puts [info script]            # test.tcl         Must be loaded                 
puts [info level -1]          # foo 10           Where called from proc foo     

Example:

    option expression on                                                        
    for {set i 0} {$i < [info adapter_count]} {incr i} {                        
       puts "Adapter $i: [info ip_address $i]"                                  
    }                                                                           

Example:

    # This script file extension                                                
    set filetype [file extension [info script]]                                 
    puts "File ext is: $filetype"                                               

See also: mem_used, commands, dump, set, vars

info args

    string [info args procname]

Returns the argument parameter of a defined proc. The proc must exist

Example:

    proc foo {x {y NULL}} {                                                     
       puts "In proc [info procname] [info args [info procname]]"               
    }                                                                           
                                                                                
    foo                                                                         

Result:

    In proc foo x {y NULL}                                                      

See: info, mem_used

info cmdcount

    integer [info cmdcount]

Return a count of total Ticol command calls as an integer

Example:

    option expression off                                                       
    proc factorial {val} {                                                      
        puts "Current level: [info level] - val: $val"                          
        set lvl [info level]                                                    
        if {== $lvl $val} {                                                     
            return $val                                                         
        }                                                                       
        return [expr {($val-$lvl) * [factorial $val]}]                          
    }                                                                           
                                                                                
   puts "This is how many commands have been executed: [info cmdcount]"         
   puts "Now this is how many commands have been executed: [info cmdcount]"     
                                                                                
   puts "\nThis interpreter is revision level: [info tclversion]"               
   puts "The process id for this program is [pid]"                              
                                                                                
   set count1 [info cmdcount]                                                   
   set fact [factorial 3]                                                       
   set count2 [info cmdcount]                                                   
                                                                                
   puts "The factorial of 3 is $fact"                                           
   puts "Before calling the factorial proc, $count1 commands executed"          
   puts "After calling the factorial proc, $count2 commands executed"           
   puts "It took [expr $count2-$count1] commands to calculate a factorial"      

Results:

   This is how many commands have been executed: 133                            
   Now this is how many commands have been executed: 135                        
                                                                                
   This interpreter is revision level: 1.14                                     
   The process id for this program is 4352                                      
   Current level: 1 - val: 3                                                    
   Current level: 2 - val: 3                                                    
   Current level: 3 - val: 3                                                    
   The factorial of 3 is 18,3,3                                                 
   Before calling the factorial proc, 142 commands executed                     
   After calling the factorial proc, 176 commands executed                      
   It took 34 commands to calculate a factorial                                 

See also: info

info exists

    bool [info exists variableName|arrayName]

The argument for variable or array name must be a name-literal. i.e. it 
cannot be prefixed with a dollar sign as this will cause automatic 
dereference to the variable value. Dollar-subscript values may be required 
for arrays.

Note that the array syntax prohibits whitespace between the variable name
and the open parenthesis: 

Correct:   "$varname($index)"
Incorrect: "$varname ($index)"  (looks for non-array variable $varname)

Use [array exists arrayname] to test for the presence of an array by name

    info exists argv0                                                           
    info exists argv(0)                                                         
                                                                                
    set i 0                                                                     
    info exists argv($i)                                                        
    info exists env(TEMP)                                                       
                                                                                
    array exists argv                                                           
    array exists env                                                            

See also: info, array exists

info level

    info level ?number?

Displays information about the calling frame at a given level. Level 1 in
a proc will be the current level, level 0 the calling frame for that proc.

If 'number' is not specified, info level returns a number indicating the 
current calling level, i.e. the level from which info level was called with
0 being the top level, and each subsequent level incremented by 1

If 'number' is specified, info levels returns a Tcl list containing words
of the command for the level indicated by number. A number value greater
than 0 indicates the level identified by that number. 0 indicates the
current level, -1 indicates one level prior to the current level, 
-2 indicates the level prior to that one etc.

Example:

    proc foo {bar {baz NULL}} {                                                 
       puts "info level 0 is \[[info level 0]\]"                                
    }                                                                           
                                                                                
    foo                                                                         
    foo abc                                                                     
    foo abc def                                                                 

Results:

    info level 0 is [foo]                                                       
    info level 0 is [foo abc]                                                   
    info level 0 is [foo abc def]                                               

See:

    http://wiki.tcl.tk/1720

See also: info

info vars

    list [info vars ?pattern? ?-const? ?-nocase?]

Return a Tcl list of matching variables. A glob pattern may be supplied
and the search will be case-sensitive unless the -nocase argument is
included. A list of constants may be returned by using the -const
argument

  set foo 1                          # Create a standard var                    
  const Example 1                    # Create a const                           
  puts [info vars]                   # All variables regardless of case         
  puts [info vars e*]                # All variables precisely matching e*      
  puts [info vars f??]               # All variables precisely matching e*      
  puts [info vars F?? -nocase]       # All variables matching f?? or F??        
  puts [info vars N* -nocase]        # All variables matching n* or N*          
  puts [info vars -const]            # All constants                            
  puts [info vars e* -const]         # All constants matching e*                
  puts [info vars e* -const -nocase] # All constants matching e* or E*          

Results:

  argc argv argv0 env eof errorCode errorInfo errorLine errorMsg exe _name      
  false logo NAN pi qbf tcl_cli tcl_config tcl_date tcl_precision tcl_thre      
  aded tcl_version true win64 env eof errorCode errorInfo errorLine errorM      
  sg exe_name                                                                   
  foo                                                                           
  foo                                                                           
  NAN                                                                           
  argc argv argv0 env eof exe_name false logo NAN pi qbf tcl_cli tcl _conf      
  ig tcl_date tcl_precision tcl_threaded tcl_version true win64 env eof eu      
  ler exe_name env eof Example exe_name                                         

The [vars] command gives a diagnostic snapshot of current variables and
an abridged view of their type and contents. See: vars

See also: vars, info

info winver

    info winver ?-list? ?-vernum? ?-truever?

Show information about the current Windows version

Argument -list will return full Windows information in Tcl list format as
{Version name} major.minor.build {Service pack} bitness

Argument -vernum will return the full version number as a double value

Argument -truever will show the true version number on later Windows
builds

Example:

    puts [info winver -list]                                                    

Result:

    {Windows XP} 5.01.2600 {Service Pack 3} 32                                  

Example:

    puts [info winver]                                                          

Result:

    Windows XP [Version 5.01.2600] (Service Pack 3) 32 bit                      

Example:

    # Show whether Windows is 64 or 32 bit                                      
    lindex [info winver -list] 3                                                

Result:

    64                                                                          

Example:

    puts [info winver -vernum]                                                  

Result

    5.1                                                                         

See also: info

input

    string [input ?length? ?default-string? ?-f forecolour? \
            ?-b backcolour? ?-password? ?-crlf? ?-var varname?]

Input a fixed length string from the console. This is a simple input
editor with backspace delete only. Full line-editing is not supported.

WARNING: DO NOT EVALUATE USER INPUT WITHOUT INPUT SANITISATION

User input should be protected by being wrapped in {} braces
as the user might input malicious Tcl code. Avoid using [eval]
or [expr] on the result of user-inputs without checks.

The default input length is 30 characters

Optionally, the input area foreground and background colour can be set 
and input characters can be masked with '*' if the -password argument
is used. Backspace editing/deleting is supported. Insert mode is not
supported. The input length cannot be exceeded or a bell alert will
sound

Input is confirmed by pressing the RETURN, ENTER or TAB keys
If ESC is pressed then any input text is discarded and input exits

If '-var varname' is passed then an integer keycode value will be
returned in that variable

Example:

    # Input 20 characters using white on blue text                              
    puts "Enter 20 chars: "                                                     
    set s [input 20 -f white -b blue]                                           
    newline                                                                     
    puts "You input '$s'"                                                       

Example:

    # Input a password, hiding the input                                        
    puts "Enter password of 20 chars: "                                         
    set s [input 20 -password]                                                  
    newline                                                                     
    puts "You input '$s'"                                                       

See also: inkey, getkey, gets, input, input_csv, gotoxy, box, inputbox

inkey
 
    integer [inkey]

Check if there is a key input event pending and retrieve the keypress code
If a key-event is pending the key value as an integer representing the ASCII
code is returned, otherwise 0 is returned

[inkey] does not halt for keyboard input. Use [getkey] to wait for input

Example:

    puts "Press ESC to halt"                                                    
    while {1} {                                                                 
       puts "Looping"                                                           
       sleep 1000                                                               
       if {== [inkey] 27} {     # ESC is 27                                     
          stop                                                                  
       }                                                                        
    }                                                                           

See also: key input, getkey, gets, input, input_csv, gotoxy, box

input_csv

  input-csv filename ?varname | -? ?-max items-max? ?-crlf? ?-array name?\
     ?-r? ?-retries retry-count?

Read lines from a standard-format CSV file and translate to a properly-
formatted Tcl list. The CSV input-file may be contain double-quote wrapped 
strings but all fields must be comma separated

If no variable name is given then [input_csv] returns the list. If a 
variable name is passed then the variable is assigned (even if this is an 
empty list) and a count of the number of list elements is returned

The new variable name is optional but if lines-max is specified and varname
is omitted then supply a hyphen (-) character instead

The optional '-crlf' argument will retain newline pairs in the resulting 
output

'-max' is the maximum number of array items to read from the file

'-retries' is the maximum number of 1000ms file open retries to attempt
The minimum is zero retries and the maximum is 200 (200 x 1000ms)

CSV read and conversion may become slow for files over about 5Mb and 
excessively so for files above 10 or 20Mb+. Consider sequential. line-based
input into smaller Tcl lists instead

Each line (row) of the file can be read to a Tcl array using the -array
argument with a non-existent variable name. Subscript->value assignment 
can be reversed or 'inverted' using the -r argument

No unicode/wide character conversion is performed

Example:

    CSV file:                                                                   
    "Hello world",1234,"ABC",5.678                                              
    "The quick brown fox",0123,"CDE",23.23                                      

    puts [input_csv csvfile.csv - -max 4]                                       

Result:

    {Hello world} 1234 Tcl 5.678                                                

See also: json_to_list, file read

Getting User Input

[input] offers console line-input with text input length limits, backspace
editing and text input area colourisation as well as password character
hiding
 
[inkey] provides polled single key input to enable you to build your own
keyboard-interaction routines

[gets stdin] is a stream-based method of retrieving user input from stdin
It offers limited line-editing facilities and no option to fix the length
of the user-input or prevent line-wrap. It is not recommended for general
user interaction. Use [input] instead

Example:

    puts "Enter 20 chars: "                                                     
    set s [input 20 -f white -b blue]                                           
    newline                                                                     
    puts "You input '$s'"                                                       

Example:

    puts -nonewline "Throw a die. What value did you get? "                     
    flush stdout                                                                
                                                                                
    set top [gets stdin]                                                        
                                                                                
    switch -- $top {                                                            
       1 {set end "st"}                                                         
       2 {set end "nd"}                                                         
       3 {set end "rd"}                                                         
       default {set end "th"}                                                   
    }                                                                           
                                                                                
    puts "You selected the $top$end face"                                       

See also: gets, flush, switch

inttooct, octtoint

    string  [inttooct integer-value]
    integer [octtoint octal-value ?-unsigned?]

Converts to and from from decimal integer and octal format
Conversion from octal to decimal is automatic in all routines
Octal format is defined by an "0o" prefix. e.g. "0o12"

Values given to [inttooct] may be decimal integers, hexadecimal, octal or 
binary values

Tcl v9 octal number identifiers are implemented, which require 0oNN format

CAUTION
-------
The Macro PreProcessor (MPP) will automatically convert octal values in
the form 0oNN into decimal. Be cautious when passing unquoted octal
values with "0o" prefix to [octtoint]

Examples:

    puts [inttooct [expr "0o123+1"]]                                            
    puts [inttooct 0b1101001]                                                   
    puts [inttooct 0x68]                                                        
    puts [octtoint 0o124]       # Unquoted octal will be converted by MPP       
    puts [octtoint "0o124"]     # Quoted octal will be retained                 

Results:

    0o124                                                                       
    0o151                                                                       
    0o150                                                                       
    0                                                                           
    84                                                                          

See:

    http://wiki.tcl.tk/498

See also: intotohex, inttobin, tohex, fromhex, cast

strchr

    integer [strchr string charstring ?-nocase?]

C++ like string search command. Finds the location of the first character
from 'charstring' in 'string' If found a base 1 value is returned, 
otherwise zero is returned.

Example:

    puts [strchr $qbf "k"]                                                      
    puts [strchr "Hello" "x"]                                                   

Result:

    9                                                                           
    0                                                                           

See also: strstr, instr

inrange

    bool [inrange integer_value reference_target ?percent?]

[inrange] tests a signed integer value to see if it is within 
+ or - X percent of the given reference range integer value. 
Integers are standard 64 bit range for Ticol integers

The default percentage is +/- 10%. Alternate ranges may be specified as
an integer value in the range 0% upwards

[inrange] is useful for testing integer values which may deviate by small
amounts within a required reference range. e.g. timing keyboard inputs
in milliseconds

Example:

    set r [rnd 9000 1000]                                                       
    puts [inrange $r 1000]                                                      

Result:

    1                                                                           

See also: data type ranges, inkey, pause, gets, input

inspect

    void [inspect varname ?tag-string?]

[inspect] allows raw variable information to be inspected. Optionally
a tag string may be included, which may be a comment string or line number

Non-printing characters are not displayed

Example:

    set a(0) 23                                                                 
    inspect a(0) Line:#__LINE__                                                 

See also: walk, dump, debugging, troubleshooting

instr, rinstr, strstr, strstrr

    integer [instr haystack needle ?startpos? ?-nocase? ?-var?]
    integer [rinstr haystack needle ?startpos? ?-nocase? ?-var?]
    integer [strstr haystack needle ?startpos? ?-nocase? ?-var?]
    integer [strstrr haystack needle ?startpos? ?-nocase? ?-var?]

VB and C++ - like string search commands. Use either the VB or C++ alias

[instr] and [strstr] search forward from the start of a string
[rinstr] and [strstrr] search backward from the end of the string

Where 'haystack' may be a variable or literal string and where 'needle'
may also be a variable or literal string

The 'startpos' argument is an index base 1 value indicating which character
to start the comparison from. e.g. 1 is the 1st character in the string

Returns a 'base 1' index value of 'needle' into 'haystack'
If 'needle' is  not found in 'haystack' then 0 will be returned.
If 'haystack' is a null "" string then 0 is returned
If 'needle' is a null "" string then 0 is returned

The result may be treated as a boolean - not found being 0 and found non-zero

Example:

    puts [instr $qbf "fox"]                                                     
    puts [instr $qbf "QUICK" -nocase]                                           
    if {[instr $qbf "lazy"]} { puts "True" }                                    

Results:

    17                                                                          
    5                                                                           
    True                                                                        

Example:

    puts [strstr "quick brown fox" "brown"]                                     
    puts [strstr "quick brown fox" "BROWN" -nocase]                             

Result:

    7                                                                           
    7                                                                           

Example:

    option expression on                                                        
    if {instr $qbf fox} {                                                       
       puts "I found the fox!"                                                  
    }                                                                           

Result:

    I found the fox!                                                            

See also: setl, setr, mid, string

inputbox

    inputbox ?prompt? ?title? ?default? ?-password? ?-x X? ?-y Y?\
        ?-w W? ?-h H?

Creates a windows which can receive user input, optionally with input masked
by a password-masking character. Inputs are confirmed by ENTER/OK click or
cancelled using ESC/CANCEL click

    Options                                                                     
    -------                                                                     
    -password        Mask input characters with an asterisk (*)                 
    -x X             Locate at cursor X                                         
    -x Y             Locate at cursor Y                                         
    -w W             Make dialog W wide                                         
    -h H             Make dialog H high                                         

If -x and -y are omitted the dialogue will centre on the screen

Examples:

    puts [inputbox "Enter your name:"]                                          
    puts [inputbox "Enter password:" "secret" -password]                        
    puts -[inputbox "Enter your name" "Rupert"  -w 500  -password]-             
    puts [inputbox "Enter your name:" -x 200 -y 300 -w 500]                     

See also: msgbox, get_filesave, get_filesave, get_folder, input, gets

ip_to_long, long_to_ip

    integer [ip_to_long value]
    string [long_to_ip value]

Convert an IPV4 dotted-quad address to an unsigned long value or convert a
long value to an IPV4 dotted-quad address. The address can be partial, in
which case it will be evaluated from left to right (e.g. "192.168.0")

The caller is responsible for any 32-bit integer bounds checking

Example:

    puts [inttohex [ip_to_long 255.255.255.255]]                                

Result:

    0xffffffff                                                                  

Example:

    # Partial address                                                           
    puts [inttohex [ip_to_long 192.168.0]]                                      
    puts [ip_to_long 192.168.0]                                                 

Result:

    0xc0a80000                                                                  
    3232235520                                                                  

Example:

    puts [long_to_ip 0xffffffff]                                                

Result:

    255.255.255.255                                                             

See also: info

item (array, stack or struct)

    string [array item arrayName subScript ?default? ?-nocase?]
    string [stack item stackName index ?default?]
    string [struct item structName.fieldname ?default?]

The item argument returns a value if it exists. If the referenced object
does not exist then either "" or a default value is returned

See also: item, array, stack, struct

tohex, fromhex
 
    string [tohex   string]
    string [fromhex string]

Converts a string to and from a hex-encoded version

Examples:

    tohex "hello"                                                               
    fromhex 68656C6C6F                                                          
    puts [fromhex [tohex "Hello world"]]                                        

Result:

    68656C6C6F                                                                  
    hello                                                                       
    Hello world                                                                 

See also: inttohex, inttooct, cast

funct
 
    number [funct function-name arg ?arg? ...]

Call an [expr] function directly and separate from the [expr] command

[funct] is more efficient than calling expr where access to a single
function is required. Complex expressions are not supported within the
arguments unless returned by [expr]

Examples:

    puts [funct log10 2]                                                        
    puts [funct round $pi 2]                                                    
    puts [funct rnd 1 10]                                                       
    puts [* [funct atan 1] 4]                                                   

Result:

    0.301029995663981                                                           
    3.140000                                                                    
    6                                                                           
    3.141592653589792                                                           

See: 'functions' and 'expression functions' for the full list of functions
exported from [expr]

See also: functions, expr, eval, function arity

functions
 
    list [functions ?glob-pattern?]

Return a Tcl list of math functions defined for use with the expr command

The search may be constrained by a glob search value. The list may be
iterated with [foreach]. The search case is not relevant as all functions
are pre-defined internally as lower-case.

See: 'function arity' and 'expression functions' for more details about
particular functions and their arguments

Example:

    # Full list of all [expr] and [funct] functions in this version             
    puts [functions]                                                            

Result:

   abs acos asin atan bool ceil decr div cos cosh degrees double exp fib        
   fix floor fmod fraction hypot incr int integral log log10 max min mul        
   pow rand rnd round sgn sin sinh sqr sqrt srand tan tanh wide                 

Example:

    # List all functions starting with the letter 'm'                           
    puts [functions m*]                                                         

Result:

    max min mul                                                                 

Use: option echo on to display directly to the console

See also: expression, procs, funct

BEDMAS/BODMAS/BOMDAS (Math Operator Precedence)

Ticol implements BEDMAS to automatically prioritise math expressions:
 
BEDMAS: Brackets, Exponent, Division, Multiplication, Addition, Subtraction

This is also known by other acronyms such as PEDMAS, BOMDAS etc.

The order of operations used throughout mathematics, science, technology and 
many computer programming languages is expressed as:

    Exponents and roots
    Multiplication and division (of equal preference, thus left-to-right)
    Addition and subtraction (of equal preference, thus left-to-right)

Where an operator has equal precedence, (e.g. * and /) then a left to 
right evaluation is performed

This aspect of mathematics appears to date from well before the 17th
century and frequent internet postings suggest that this comes as a
surprise to many

See:

   http://en.wikipedia.org/wiki/Order_of_operations

See also: math operators, math expressions, expression functions

Math Expressions

Tcl does not natively understand math, although individual commands are
able to process math commands and expressions

The built-in [expr] command can handle complex expressions and applies
standard BEDMAS rules by reprioritising expressions before evaluation. 
During the checking phase any non-decimal consts such as 0xFF or 0o777 are 
automatically converted to their decimal equivalents. Multiplication and
division are given priority over addition and subtraction

Main flow-control commands may take either an [expr] or [eval] argument
which is controlled by the 'option expression' setting. With 'option
expression on' commands such as [if] and [while] will take an [expr]
argument. With 'option expression off' you will need to pass eval results
of individual Tcl commands

e.g. 

    option expression on                                                        
    if {30 / 10 == 3} { ...                                                     
    option expression on                                                        
    if {== [/ 30 10]} { ...                                                     

Exponentiation is supported via the operator '**' and pow() expression
function

Example:

    puts [expr 2**8]                                                            
    puts [expr pow(2,8)]                                                        

Results:

    256                                                                         
    256.000000000000000                                                         

The contents of pre-bracketed expressions and function brackets are also 
evaluated for correct ordering and precedence.

Value interpretation follows Tcl rules. Integers are subjected to integer
division. If you wish float division then specify a decimal point.
e.g. - 22/7 will return 3, 22/7.0 will return 3.142857142857143

For a full list of supported math functions see Expression Functions

Note that commas ',' are undefined within math expressions other than as 
function parameter separators and their inclusion elsewhere may cause 
unpredictable results.

You may also apply your own expression parentheses.

Example: 

    # Call [expr] then external BAT file which calls cscript->eval              
    # abs(1+11*2-1/7.0)/sqrt(2/1.6) is reinterpreted as:                        
    # abs(1+(11*2)-(1/7.0))/sqrt(2/1.6)                                         
                                                                                
    puts [expr abs(1+11*2-1/7.0)/sqrt(2/1.6)]                                   
    20.444050079998078                                                          
                                                                                
    puts [eval.bat abs(1+11*2-1/7.0)/sqr(2/1.6)]                                
    20.44405007999810                                                           

Note: The big math plugin module can be used to extend the number range

See also: math operators, BEDMAS, expression functions

fmod

    float [fmod float float]

Return the remainder after float division

Example:

    puts [fmod 1.34 0.45]                                                       

Result:

    0.440000                                                                    

See also: %, mod, math

Combination Math Operators

Combined operation and assignment commands

    value [+= var|value value]   # Combined addition and assignment
    value [-= var|value value]   # Combined subtraction and assignment
    value [*= var|value value]   # Combined multiplication and assignment
    value [/= var|value value]   # Combined division and assignment
    value [|= var|value value]   # Combined or + assignment
    value [&= var|value value]   # Combined and + assignment
    value [^= var|value value]   # Combined xor + assignment

These operators can take either a variable name or a value (dereferenced
from a variable or as a literal numeric constant)

Unsigned 64 bit operations can be forced using the '-unsigned' argument

They are more efficient than using a longhand form with [set] using

    set var [+ var value]

Examples:

    set a 23                                                                    
    set b 12                                                                    
    puts [+= 23 12]                                                             
    puts [+= $a 12]                                                             
    puts [+= a 12]                                                              
    puts [+= 23 $b]                                                             
    puts [+= $a $b]                                                             
    puts [+= a $b]                                                              

Results:

    35                                                                          
    35                                                                          
    35                                                                          
    35                                                                          
    35                                                                          
    35                                                                          

See also: math operators, optimisation

Math Operators

The following math operators are supported. 

CAUTION: Operations are treated as integer or float unless mixed types are
encountered, in which case float is used. Take care with division; if you
want a float result then one of the operands must be a float value or your
result will be truncated to an integer value:  e.g. Use: [expr "22/7.0"]
not [expr "22/7"]

    +      Addition                                                             
    -      Subtraction                                                          
    *      Multiplication                                                       
    /      Division. At least one argument must be float to return a float      
    %      Modular integer division                                             
    **     Power. Returns x raised to the power of y (See: pow)                 
           Integer overflow is fatal                                            
           Double overflow returns 1.#INF00000000000                            
    fmod   Floating point Modular integer division as a command                 
    fmod() Floating point modular division (as an [expr] function)              

    Combination Operators  (See also: Combination operators)

    +=     Combination add and assign                                           
    -=     Combination sub and assign                                           
    *=     Combination mul and assign                                           
    /=     Combination div and assign                                           
    |=     Combination or and assign                                            
    &=     Combination and and assign                                           
    ^=     Combination xor and assign                                           

    Integer Only:

    ^      Bitwise xor                                                          
    <<     Bitwise shift left (64-bit)                                          
    >>     Bitwise shift right (64-bit)                                         
    |      Bitwise or                                                           
    ||     Logical or                                                           
    &      Bitwise and                                                          
    &&     Logical and                                                          

    The above may be applied as combined operator= symbols, e.g. ^=, &=

    Relational Operators:

    All relational operators may be qualified using an '-unsigned' option
    which will force an unsigned 64 bit integer comparison

    ==     Equivalence                                                          
    !=     Inequality                                                           
    >      Greater than                                                         
    >=     Greater than or equal to                                             
    <      Less than                                                            
    <=     Less than or equal to                                                
    <=>    Relational test (same as PERL operator)                              
           Returns -1 for <, - for equal, +1 for > as per C++ strcmp            
                                                                                
    is_mod Safe modular division test                                           
                                                                                
    ()     Precedence bracketing                                                

Division by zero will return an error as either:

    Integer division by zero (10 / 0)
    Float division by zero (10 / 0)

When used as a command, the math operators such as [add] [sub] etc. can be
used with a variable name as the following method of shortcut assignment...

    set a 1                                                                     
    add a [expr 3*3]                                                            
    add a [* 3 3]                                                               

Result:

    10                                                                          
    19                                                                          

Note: The big math plugin module can be used to extend the number range

See also: incr, decr, ++, --, fmod, is_div, shortcut operators, power,
          BEDMAS, math expressions, is_mod, big math, combination operators

Shortcut Math Operators

    Syntax: <operator> <variable> <value>

    Operator    Mnemonic    Domain                                              
    ------------------------------------------                                  
      +=        Add         Integer, float                                      
      -=        Sub         Integer, float                                      
      *=        Mul         Integer, float                                      
      /=        Div         Integer, float                                      
      &=        BitAnd      Integer                                             
      |=        BitOr       Integer                                             
      ^=        BitXor      Integer                                             

The shortcut math operators help simplify and speed up math expressions.
For example, the longhand: set a [* a 3] may be condensed to:  *= a 3

Examples:

    Set a 1     #  1                                                            
    += a 10     # 11                                                            
    -= a 2      #  9                                                            
    *= a 3      # 27                                                            
    /= a 9      #  3                                                            
    |= a 4      #  7                                                            
    &= a 6      #  6                                                            
    ^= a 2      #  4                                                            
    ^= a 2      #  6                                                            

See also: math operators

<< >> Bit shift (64-bit)

    integer [<< value bits ?bit-width?]     # Bitwise shift left
    integer [>> value bits ?bit-width?]     # Bitwise shift right

Either left or right-shifts a value by N bits. This is a valid operation
for integers only. Negative shift values will raise a catch exception

The expression forms of << and >> do not accept bit-width arguments. 
If you need to specify a bit width then insert the command in either
expression or eval pass as [<< value shift bits] [>> value shift bits]

Examples:

    puts [expr [<< [expr 20+3] [expr 2^4] 32] - 472]                            
    puts [expr 1*[<< 1 1]*3/4.0*5]                                              

Result:

    1000                                                                        
    7.5                                                                         

Note that all Ticol integers are 64-bit and by default these bit operations
work on 64-bit integers. This can be modified by adding an output bit-width
argument of either 8,16 or 32. Any other values will evaluate as 64-bit

Shift does not wrap or suffer from an underlying 32-bit bias for other
bit-widths. All shifts equal-to or in excess of the specified bit width
will return 0

Wrapping does not occur (32-bit wrapping is blocked)

Shift [<<] and [>>] fills vacated bits with 0. All values are unsigned. 
Sign-bits do not therefore, cause 'backfill' with [>>]

Examples:

    puts [inttohex [<< 0xff 32]]                                                
    puts [inttohex [<< 0xff 63]]                                                
    puts [inttohex [<< 0xff 64]]                                                
                                                                                
    loop i 0 40 8 {                                                             
       printf "\[inttohex \[<< 0xff %2i 32\]\]='%20s'\r\n" $i\                  
        [inttohex [<< 0xff $i 32]]                                              
    }                                                                           

Result:

    0xff00000000                                                                
    0x8000000000000000                                                          
    0x0                                                                         

Examples:

    puts [inttohex [<< 0xff  0 32]]       # 0xff                                
    puts [inttohex [<< 0xff  8 32]]       # 0xff00                              
    puts [inttohex [<< 0xff 16 32]]       # 0xff0000                            
    puts [inttohex [<< 0xff 24 32]]       # 0xff000000                          
    puts [inttohex [<< 0xff 32 32]]       # 0x0                                 

See also: math operators, functions

Expression Functions (Built In Functions)
 
The following standard C/C++ compatible functions are available within Tcl
expressions using the [expr] command. 

See: function arity    for more information

    abs(x)                  # Return the absolute value of a number             
    acos(x)                 # Return the arc cosine of value (arccos)           
    asin(x)                 # Return the arc sin of value (arcsin)              
    atan(x)                 # Return the arc tangent of value (arctan)          
    bool(x)                 # Evaluates a string and returns 1 or 0             
    ceil(x)                 # Round to the next-highest integer value           
    cosh(x)                 # Return the cosine of value                        
    decr(x)                 # Decrement value                                   
    div(x,y)                # Functional division of x / y                      
    cos(x)                  # Return the cosine of value (in radians)           
    cosh(x)                 # Return the cosine of value (in radians)           
    degrees(x)              # Convert radians to degrees                        
    double(x)               # Cast value to double                              
    exp(x)                  # Return the exponent of value                      
    fib(x)                  # Return the Fibonacci of value                     
    fix(x)                  # Return integer value rounded up                   
    floor(x)                # Round to the next-lowest integer value            
    fmod(x,y)               # Return the remainder of x / y                     
    fraction(x)             # Return the fraction part of a real/double         
    hypot(x,y)              # Return the hypotenuse of x and y                  
    incr(x)                 # Increment value                                   
    int(x)                  # Cast value to integer                             
    integral(x)             # Return the integer part of a real/double          
    log(x)                  # Return the logarithm of value                     
    log10(x)                # Return the log10 of value                         
    max(x,y)                # Return the maximum of x and y                     
    min(x,y)                # Return the minimum of x and y                     
    mul(x,y)                # Functional multiplication of x * y                
    pow(x,y)                # Return x to the power of y                        
    rand()                  # Return double random value from 0 to 1            
    rnd(x,y)                # Return a random number between a and b inclusive  
21963          
                            # cryptographically secure, and should not be used  
                            # to generate one-time passwords or session keys    
    round(x,y)              # Round value x to y places using standard rounding 
    sgn(x)                  # Return the sign as an integer (1, 0, -1)          
    sin(x)                  # Return the sine of value (in radians)             
    sinh(x)                 # Return the sine of value (in radians)             
    sqr(x)                  # Return the square of value                        
    sqrt(x)                 # Return the square-root of value                   
    srand()                 # Seed the random number generator                  
    tan(x)                  # Return the tangent of value (in radians)          
    tanh(x)                 # Return the tangent of value (in radians)          
    wide(x)                 # Force a value to 64 bit signed integer            
    ?x:y                    # Functional [if] statement (deprecated)            

Available functions may be listed using the 'functions' command
See: function arity for more details about functionality

The functional int differs from the int conversion command. int(x) will 
round to the lowest integer whereas [int x] will simply cast and convert
Int returns the first -ve integer less than or equal to value, whereas Fix
returns the first -ve integer greater than or equal to value.

Functions may be nested within expressions passed to the 'expr' command
and may also be called using the [funct] command using [funct <name>]

Where radians are required (cos, sin etc.) radians = degrees * (pi/180)
or: degrees * 0.017453292519943.

The 'sgn' function returns:

   Greater than zero   1                                                        
   Equal to zero       0                                                        
   Less than zero     -1                                                        

Example:

    expr "int(22/7.0)+int(5.2)"                                                 

Result:

    8                                                                           

Example:

    expr "round(exp(log(10)),0)"                                                

Result:

    10                                                                          

Example:

    expr ([rnd 0 1]?yes:no)                                                     

Result:

    Randomly yes or no                                                          

Examples using [funct]

    puts [funct max 2 10]                                                       
    puts [funct sqrt $pi]                                                       

Result:

    10                                                                          
    1.772453850905516                                                           

See also: function arity, math operators, math expressions, arity, expr,
          sqrt proc, eval, math, funct, functions

sqrt procedure

A square root command is not defined but is available as a math function
Math functions can be called via the [funct] command or a useful code
snippet can be wrapped-up as shown below

This allows a default precision of 3 and the option of passing a precision
parameter to suit

    proc sqrt {x {precision 3}} {                                               
        return [format %.${precision}f [funct sqrt $x]]                         
    }                                                                           
    puts [sqrt 169]                                                             
    puts [sqrt $pi]                                                             
    puts [sqrt $pi 5]                                                           
    puts [sqrt $pi 7]                                                           

Results:

    13.000                                                                      
    1.772                                                                       
    1.77245                                                                     
    1.7724539                                                                   

See also: sqrt function, functions, math

getcwd, pwd
 
    getcwd ?-unix?
    pwd ?-unix?

Returns the current working directory

[getcwd] will return the path as a backslash-escaped string for Tcl
processing [pwd] will print out the current working directory for display
purposes. The -unix argument will force Unix path separators to be returned

Example:

    pwd                                                                         
    getcwd                                                                      
    getcwd -unix                                                                

Results:

    C:\windows\system32                                                         
    C:\\windows\\system32                                                       

See also: mkdir, mkdirs, file, dir

get_fileopen, get_filesave

    get_fileopen filter title initdir ?flags?
    get_filesave filter title initdir ?flags? ?defaultname?

Launches a Windows file select (open) dialogue or a file select (save)
dialogue For get_filesave a default filename may be specified after the
flag value. Unwanted flag values should be specified as 0

Any option which is specified as "-" will be ignored

Example:

    set f [get_filesave *.txt "Save Txt File" "d:\temp"]                        
    set f [get_filesave *.txt "Open Txt File" "d:\temp" 0 fred.txt]             

See also: msgbox, inputbox, get_folder

getenv
 
    string [getenv]

Return an environment variable from Windows

Example:

    puts [getenv ProgramFiles]                                                  

Result:

    C:\Program Files (x86)                                                      

See also: setenv

gets
 
    string [gets ?stdin|filenum|-? ?var|-? ?-length count?]

[gets] has an enhanced syntax compared to standard Tcl. The first two 
arguments are optional and each of those may have a default specified 
using "-". The default stream is stdin and the default variable is none.

Read data from an open file-stream. This may either have been opened by the
open command or the stdin read stream may be used. Optionally, a variable
may be specified to receive the data. This need not be declared in advance.
The default file-stream is 'stdin'. A numeric value is assumed to represent
a length value

Additionally an optional number of bytes to read from the stream may be
specified

If 'count' is specified then CTRL+Z (^Z or EOF) terminates input before
the required number of characters has been read. If count is not specified
then input terminates when ENTER is met

When using [gets] to read from the stdin console it supports up/down cursor 
history, home/end and full inset-mode line editing

Example:

    puts "Enter your name: " -nonewline                                         
    set a [gets]                                                                
    newline                                                                     

Example:

    puts "Enter your name: " -nonewline                                         
    gets a                                                                      
    newline                                                                     

Example:

    puts "Enter your name up to 20 characters: " -nonewline                     
    gets a 20                                                                   
    newline                                                                     

Example:

    puts "Enter your name up to 20 characters: " -nonewline                     
    gets stdin a 20                                                             
    newline                                                                     

Example:

    set fp [open "input.txt" r]                                                 
    gets $fp a 100               # Read 100 chars                               
    close $fp                                                                   

See also: input, inputbox, file, open, close

gotoxy
 
    gotoxy x y

Locate the screen cursor at an x/y location. The values are base 1

If using [puts] with absolute screen coordinates via [gotoxy] you should
use the -nonewline argument if possible to avoid screen scrolling issues

[screen curx] and [screen cury] give the current x/y locations

Example:

    option expression off                                                       
    for {set i 0} {< $i 10} {++ i} {                                            
       gotoxy 5 5                                                               
       puts "I am at 5,5" -nonewline                                            
    }                                                                           

See also: screen, cls, box

Towers of Hanoi

The Towers of Hanoi script is a useful benchmark for speed. A compiled
C++ version of the Tcl algorithm is a couple of orders of magnitude
faster, but this is to be expected as Ticol Tcl is interpreted and
outside the plugin module level, is not intended to be coded for
performance

The following example code, 'hanoi 17' runs in around 4 seconds on a
low-powered i5 under Windows 7.

    option expression off                                                       
    cls                                                                         
    set n 0                                                                     
    set yloc 1                                                                  
                                                                                
    if {! [defined draw_status]} {                                              
       proc draw_status {} {                                                    
          upvar yloc                                                            
          gotoxy 1 $yloc                                                        
          upvar a                                                               
          upvar b                                                               
          upvar c                                                               
          upvar n                                                               
          upvar moves                                                           
          upvar x                                                               
          textcolor white                                                       
          puts "Towers of Hanoi ($n) - Pole Status ([comma $x] moves)\n"        
          textcolor red                                                         
          printf "Pole A(%2i) %-67s\r\n" [stack count a] [stack list a]         
          textcolor yellow                                                      
          printf "Pole B(%2i) %-67s\r\n" [stack count b] [stack list b]         
          textcolor green                                                       
          printf "Pole C(%2i) %-67s\r\n" [stack count c] [stack list c]         
          textcolor                                                             
       }                                                                        
    }                                                                           
    halt                                                                        
                                                                                
    textcolor white blue                                                        
    puts "  Towers of Hanoi  "                                                  
    textcolor                                                                   
    newline                                                                     
                                                                                
    if {[< $argc 2]} {                                                          
       puts "How many rings (2..25)? " -nonewline                               
       gets stdin n                                                             
       if {|| [== $n ""] [== $n 0]} {stop}                                      
    } else {                                                                    
       set n $argv(1)                                                           
    }                                                                           
    set n [min $n 25]                                                           
                                                                                
    stack create a $n                    # Init 3 stacks to given size          
    stack create b $n                                                           
    stack create c $n                                                           
                                                                                
    for {set x 0} {[< $x $n ]} {++ x} {  # Create initial stack of hoops        
       stack push a [+ $x 1]            # 0..n-1                                
    }                                                                           
                                                                                
    set x 1                                                                     
    set shln [<< 1 $n]                   # Precalc (1 << $n), << for n          
                                                                                
    draw_status                                                                 
                                                                                
    set start_secs [clock seconds]                                              
    set start [timer]                                                           
    while {< $x $shln} {                                                        
       set xminus1 [- $x 1]                                                     
       stack push [chr [calc ((($x|$xminus1) + 1) % 3 )] 97] \                  
          [stack pop [chr [calc ($x & $xminus1) % 3] 97]]                       
       is_mod $x 50000 draw_status                                              
       ++ x                                                                     
    }                                                                           
    set end [timer]                                                             
    set end_secs [clock seconds]                                                
                                                                                
    draw_status                                                                 
    newline                                                                     
    puts "Took [- $end $start] millisecond(s) and [comma $x] move(s)"           
    puts "Took [- $end_secs $start_secs] second(s) and [comma $x] move(s)"      
    newline                                                                     
    puts "Done."                                                                
                                                                                
    stack unset a                                                               
    stack unset b                                                               
    stack unset c                                                               

Results:

    Took 3884 millisecond(s) and 131,072 move(s)                                
    Took 4 second(s) and 131,072 move(s)                                        

For comparison. The compiled C++ hanoi.exe 17 gives the following
and can run 'hanoi 23' in about the same time with 8,388,607 moves

    Took 47 ms and 131071 move(s)                                               

Hex Routines
 
   string  [tohex string]
   string  [fromhex value]
   string  [inttohex decimal-variable ?-noprefix? ?-full?]
   integer [hextoint string]

Conversion routines to convert from text and integers, to and from 
hexadecimal

Note that because the Macro PreProcessor will translate hex values if
prefixed with '0x', you should wrap any such values in double quotes 
before calling [hextoint], or you should strip any prefixing '0x',
otherwise inaccurate results will be returned due to double, interpretation
of the hex value

[tohex] Converts a string into hex-encoding, the inverse is [fromhex]

[inttohex] converts a decimal value to base 16, the inverse is [hextoint]

Conversion from hex to decimal integer is automatic in Ticol
The -full argument presents all prefixing zero values. 
Normally these leading zeroes are omitted

If the -noprefix argument is given then the 0x... prefix will be omitted

[printf] and [format] can also be used to generate more control over 
hex formatted output

Example:

    puts [inttohex 12345]                                                       

Result:

    0x3039                                                                      

Examples:

    puts [inttohex 12345]                                                       
    puts [inttohex 12345 -noprefix]                                             
    puts [hextoint "0x3039"]                                                    
    puts [tohex "Hello world"]                                                  
    puts [fromhex 48656C6C6F20776F726C64]                                       

Results:

    0x3039                                                                      
    3039                                                                        
    48656C6C6F20776F726C64                                                      
    Hello world                                                                 

See also:  int, double, data types

Code Highlighting Plugin

Load using: lib ticol_highlight?.dll?

    string [highlight string ?-f bat|asm|html? ?-rtf?]
    string [highlight {} -reset]

[highlight] was created to output the Ticol man file as both HTML and RTF
format files and is designed for highlighting on a black background but
could be used with grey background.

It can create a HTML or RTF-highlighted block of text from Tcl, Windows
Batch, Ticol Assembler or HTML script code using the Ticol man file
markup syntax. 

Arguments
---------
-html              Generate HTML output (default, -html is not required)
-rtf               Generate RTF output
-f asm             Ticol Assembler (TicASM) format
-f bat             Windows Batch Script format
-f html            HTML code format
-f tcl             Tcl format (default, -tcl is not required)
-reset             Reset multiline string highlighting

When using in -html output mode, any < and > characters will be replaced
with &lt; and &gt;

Rich Text (RTF) output can also be generated using a custom {colortbl}
entry. The default output format is HTML.

Call as [highlight {} -reset] to reset multi-line string text highlighting
if necessary

Unescaping Inputs
-----------------
Escaped Tcl source should be passed in 'unescaped' form using [unescape]

RTF Colour Table
----------------
Highlighted code will refer to \cf1 to \cf16 and the following RTF colour
table header can be used and which match the standard DOS/Windows console
colours: This will allow embedded colour codes '\cf1' to \cf16' to map
into this console colour table. Note that "darkyellow" is represented
as 'orange' not 'khaki'.

    # RTF emulation of classic Windows console colours                          
    array set rgb_colour {    # Used to set individual RGB colortbl elements    
    # array {R G B}          cfN  hex     binary name          \cfN             
         0 {0x00 0x00 0x00} # 01 0x00        0b0 black          cf1             
         1 {0x48 0x3D 0x8B} # 02 0x01        0b1 darkblue       cf2             
         2 {0x00 0xC6 0x00} # 03 0x02       0b10 darkgreen      cf3             
         3 {0x00 0x8B 0x8B} # 04 0x03       0b11 darkcyan       cf4             
         4 {0x80 0x00 0x00} # 05 0x04      0b100 darkred        cf5             
         5 {0x8B 0x00 0x8B} # 06 0x05      0b101 darkmagenta    cf6             
         6 {0xD2 0x69 0x1E} # 07 0x06      0b110 darkyellow     cf7             
         7 {0x69 0x69 0x69} # 08 0x07      0b111 darkgrey       cf8             
         8 {0x80 0x80 0x80} # 09 0x08     0b1000 grey           cf9             
         9 {0x00 0x00 0xFF} # 10 0x09     0b1001 blue           cf10            
        10 {0x7C 0xFC 0x00} # 11 0x0a     0b1010 green          cf11            
        11 {0x00 0xFF 0xFF} # 12 0x0b     0b1011 cyan           cf12            
        12 {0xFF 0x00 0x00} # 13 0x0c     0b1100 red            cf13            
        13 {0xFF 0x33 0xFF} # 14 0x0d     0b1101 magenta        cf14            
        14 {0xFF 0xFF 0x00} # 15 0x0e     0b1110 yellow         cf15            
        15 {0xff 0xff 0xff} # 16 0x0f     0b1111 white          cf16            
    }                                                                           
    # Build RGB RTF colour table header #                                       
    option expression off                                                       
    set ct "{\\colortbl ;"                                                      
    for {set i 0} {< $i 16} {++ i} {                                            
       append ct "\\red[lindex $rgb_colour($i) 0]"                              
       append ct "\\green[lindex $rgb_colour($i) 1]"                            
       append ct "\\blue[lindex $rgb_colour($i) 2];"                            
    }                                                                           
    append ct "}"                                                               

Example files are supplied, man_to_rtf.tcl and man_to_html.tcl

Example:

    lib ticol_highlight                                                         
    set s "<font color=\"blue\">"                                               
    puts [highlight [unescape $s] -html]                                        

See also: Plugins, lib, rtf

HTML Plugin

Various HTML manipulation commands

To load, use:    lib ticol_html ?-verbose? ?-nocomplain?

See separate help topics for:

    html chomp_tag             Parse an HTML file
    html header                Return the entire HTTP header
    html header_field          Return an HTTP header field
    html remove_header         Strip off an HTTP/HTML header
    html strip                 Remove all HTML tags from a string

See also: plugins, http

html chomp_tag (Plugin)

To load, use:    lib ticol_html ?-verbose? ?-nocomplain?

    string [html chomp_tag varName ?varOut ?-nobrackets?]

Remove the next HTML tag from a variable, return it and remove that
part of the string (including <> brackets)

An optional array or simple variable (varOut) can receive any pre-tag
prefix text, thus enabling combined HTML and plaintext to be parsed.

Optionally the enclosing brackets can be omitted from any return

Example:

    lib ticol_html                                                              
    set s [http http://localhost:8800]                                          
    puts [html chomp_tag s]                                                     
    puts [html chomp_tag s]                                                     
    puts [html chomp_tag s]                                                     

Result:

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">                     
    <html>                                                                      
    <head>                                                                      

Example:

    lib ticol_html                                                              
    set s "    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\         
    <html><head><title>Hello world</title></head><body>\                        
    Hello World                                                                 
    </body></html>"                                                             
    puts [html chomp_tag s]     # Tag:doctype                                   
    html chomp_tag s            # Tag:html                                      
    html chomp_tag s            # Tag:head                                      
    html chomp_tag s            # Tag:title                                     
    html chomp_tag s t          # Tag:/title. Return the title text value in 't'
    puts $t                                                                     

Example:

    lib ticol_html                                                              
    option expression off                                                       
    set s [http http://localhost:8800]                                          
    do {                                                                        
       set t [html chomp_tag s]                                                 
       puts $t                                                                  
    } while {ne $t ""}                                                          

Results:

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">                     
    <html>                                                                      
    <head>                                                                      
    <title>                                                                     
    </title>                                                                    
    </head>                                                                     
    <body>                                                                      
    ...                                                                         

See also: plugins, html strip, html header_field, html remove_header

html header (Plugin)

To load, use:    lib ticol_html ?-verbose? ?-nocomplain?
 
    string [html header string]
    string [html header varName -var]

Returns the entire header from an HTTP 1.1 block of HTML code

Example:

    set s [http http://localhost:8800]                                          
    puts [html header $s]                                                       

Result:

    Header: HTTP/1.1 200 OK                                                     
    Date: Mon, 31 Jan 2022 17:01:42 GMT                                         
    Server: Apache                                                              
    Last-Modified: Thu, 25 Feb 2021 01:58:24 GMT                                
    ETag: "40f48-a5e-7cb6c213"                                                  
    Accept-Ranges: bytes                                                        
    Content-Length: 2654                                                        
    Vary: Accept-Encoding                                                       
    Content-Type: text/html                                                     

See also: html, html header_field

html header_field (Plugin)

To load, use:    lib ticol_html ?-verbose? ?-nocomplain?
 
    string [html header_field string field-name]
    string [html header_field varName field-name -var]

Return a header field from an HTTP 1.1 block of HTML code. Case is ignored 
during comparison and trailing colons may be omitted for the field name.
Whitespace is trimmed from the return string

The -var argument allows a variable name to be passed instead of a string
literal

If either the string argument or field-name is null then an empty string
is returned

Example:

    # Emulate an HTTP header                                                    
    set x "HTTP/1.1 200 OK\r\n\                                                 
    Date: Fri, 12 Jun 2017 13:26:15 GMT\r\n\                                    
    Server: Apache/2.4.10 (Ubuntu)\r\n\                                         
    X-Powered-By: PHP/5.5.12-2ubuntu4.3\r\n\                                    
    Redirect: http://localhost.com/index.php\r\n\r\n"                           
                                                                                
    puts "Date:        \[[html header_field $x Date:]\]"                        
    puts "Redirect:    \[[html header_field $x rEdIrEcT:]\]"                    
    puts "Server:      \[[html header_field x server -var]\]"                   
    puts "Foo:         \[[html header_field $x foo]\]"                          

Result:

    Date:        [Fri, 12 Jun 2017 13:26:15 GMT]                                
    Redirect:    [http://localhost.com]                                         
    Server:      [Apache/2.4.10 (Ubuntu)]                                       
    Foo          []                                                             

See also: plugins, html strip, html header_field, html remove_header

html remove_header (Plugin)

To load, use:    lib ticol_html ?-verbose? ?-nocomplain?

    string [html remove_header string]

Servers may return a 'header' section as an HTML page prefix. This 
command offers an easy way to remove this

[html remove_header] strips off and removes the HTTP header from an
HTML body return string stripto may be used to remove other junk up to
the first "<" bracket

This command is also useful for removing any HTML prefix from JSON data
which is prefixed by a header. See: json_to_list

See also: plugins, http, html strip, html header_field, html chomp_tag

html strip (Plugin)

To load, use:    lib ticol_html ?-verbose? ?-nocomplain?

    string [html strip string]

Removes all HTML tags from a string

&nbsp; tags are replaced with a single space

Example:

    set s "<b><em>Hello&nbsp;world</em></b>"                                    
    puts [html strip s]                                                         

Result

    Hello world                                                                 

See also: plugins, html remove_header, html header_field, html chomp_tag

http
 
    http ?http://?domain?:port??url? ?port | -? ?var? ?-u username?
            ?-p password??-1? ?-nocomplain? ?-head? ?-t N?

Retrieve a page of HTML text from a remote website via a HTTP 1.1 request
HTTP is supported on any port but HTTPS and FTP etc. are unsupported
For FTP, see ticol_ftp

This is currently a minimal HTTP 1.1 implementation with automatic chunked 
download support which should be sufficient for rudimentary data download
from a server. 

HTTP 1.0 using the ?-1? argument is available, otherwise HTTP 1.1 is used

Binary downloads are currently unsupported but you can easily [spawn]
external programs such as wget.exe for binary content

The default port value is 80. This may be specified either in the URL or via
the port argument. Extra headers cannot be specified.

A URL which contains whitespace must be enclosed in braces or double-
quotes

Optional Arguments:

    -1           Use HTTP 1.0 instead of HTTP 1.1                               
    -u           Specify a username for BASIC Authentication (plaintext)        
    -p           Specify a password for BASIC Authentication (plaintext)        
    -t N         Specify an optional connect timeout in seconds                 
    -head        Issue an HTTP HEAD command instead of HTTP GET                 
    -nocomplain  Don't issue verbose output on errors                           

Positional Arguments:

Example:

    http "http://en.wikipedia.org/wiki/Main_Page" - x                           

Result:

    puts "HTML is: $x"                                                          

Example:

    puts [http http://localhost:8800/cgi-bin/ticol.exe?cgi.tcl]                 

Result:

    <displays Tcl script page contents>                                         

See also: urlencode, urldecode, html remove_header, html header_field

int, double (Type Casting)
 
 Type-casting operators:

    integer [int variable|number]
    double  [double variable|number]

Converts a numeric character to the requested type, by truncation of
necessary. Input can either be a number or a variable name

Bear in mind that Tcl is a "typeless" language where "everything is a 
string" However strings still have an apparent type, for example the
string:

    3.141592653589792

    has "real" number characteristics, whereas ...

    3

   does not. Consequently one can transpose real numbers to integers. Also,
Ticol assumes type and will assume integer values in some cases where this
is undesired. This may affect certain math operations such as division
forcing a truncated integer result. You can ensure a real number (double)
operation by casting integer values to double

Example:

    expr 22/7                                                                   

Result:

    3                                                                           

Examples:

     puts [expr 22/double(7)]     # Expression syntax                           
     puts [/ 22 [funct double 7]] # Using [funct] to call                       
     puts [/ 22 [double 7]]       # [double] direct command                     
     puts [/ 22 7.0]              # Command syntax with double value            

Result:

    3.142857142857143                                                           

[int] and [double] are available as both expression functions and Ticol
commands

See also: vars, type

is_admin
 
    bool [is_admin]

Return $::true (1) if the current Windows user is an administrator

See also: true, false, elevate, is_elevated, is_*

is_command
 
    bool [is_command command|procname]

Returns either $::true (1) or $::false (0) indicating whether a command or
procedure is defined. [is_defined] may also be used to test for definition

    Example                         Result                                      
                                                                                
    is_command puts                 1                                           
    is_command put                  1                                           
    is_command puttt                0                                           

See also: is_defined, commands, if, while. for. foreach, flow control, is_*
          true, false

is_const

    bool [is_const varName]

Returns a boolean $::true (1) or $::false (0) indicating whether a
variable is a constant

Example:

    set sid Hello                                                               
    enum {                                                                      
       fred                                                                     
    }                                                                           
    puts "Is sid const? [bool [is_const sid]]"                                  
    puts "Is fred const? [bool [is_const fred]]"                                

Results:

    False                                                                       
    True                                                                        

See also: bool, true, false, is_pointer, const, set, is_*

is_elevated
 
    bool [is_elevated]

Returns $::true (1) if Ticol is running with elevated privileges
(Windows 7+) or $::false (0) if not

[elevate] will self-call, passing the original command-line arguments

Example:

    if {! [is_elevated]} {                                                      
        elevate                                                                 
    } else {                                                                    
        # Run the script                                                        
    }                                                                           

See also: true, false, elevate, is_admin, is_*

is_empty

    bool [is_empty varname]

Test if a variable is both set and empty. If a variable of either array or
simple type is both set and empty it returns 1. If the variable does not
exist 1 is also returned. 

Be careful to pass the name of the variable rather than its value. 
Empty strings also return 1. [is_set] may be used in conjunction with
[is_empty].

Use [is_null] to test strings for 'emptiness'

    Type             Empty  Not-empty                                           
    ---------------------------------                                           
    unset var        1      1                                                   
    set var          1      0                                                   
    string literal   1      0                                                   
    ---------------------------------                                           

Example:

    set s {}                                                                    
    if {is_empty s} {                                                           
        puts "Empty"                                                            
    } else {                                                                    
        puts "Not empty"                                                        
    }                                                                           
    puts [is_empty {}]                                                          
    puts [is_empty nosuchvar]                                                   
    puts [is_set nosuchvar]                                                     

Result:

    Empty                                                                       
    1                                                                           
    1                                                                           
    0                                                                           

See also: is_set, is_null, is_*

is_dir
 
    bool [is_dir path]

Tests if the given path string is a path or not, returning a boolean
$::true (1) or $::false (0)

See also: true, false, mkdir, mkdirs, unlink, file, is_*

is_pointer

    bool [is_pointer varName]

Returns a boolean $::true (1) or $::false (0) indicating whether a
variable is a pointer variable or not

See also: true, false, is_const, setb, addressofb, ofaddressb, struct,
          is_*

is_proc

    bool [is_proc name]
 
 Test if a command is a user-defined procedure. Returns a boolean 
 $::true (1) or $::false (0)

 Example:

    if {is_proc foo} {                                                          
        undef foo                                                               
    }                                                                           

 See also: true, false, proc, undef, is_*

is_null

    bool [is_null string]

Test if a string is null (empty, "" or {})

Use [is_empty] to test a variable name and [is_set] to test if a variable's
status

Example:

    puts [is_null ""]                                                           
    puts [is_null {}]                                                           
    puts [is_null Hello]                                                        
    puts [is_null $qbf]                                                         

Results:

    1                                                                           
    1                                                                           
    0                                                                           
    0                                                                           

See also: is_set, is_empty

is_mod

    bool [is_mod value divisor ?{command}?]

Fast, division-safe modular division check. If the divisor is zero then 
$false (0) is returned. This avoids extra code required to prevent division
by zero

In loops this can lead to improved performance. A boolean $::true (1) or
$::false (0) is returned. Performance may be enhanced by avoiding [if]
and executing an optional command to evaluate if [is_mod] is true

[is_mod] replaces the longhand expression:

    if {[== [% $x 1000] 0]} {call_proc}                                         

with:

    if {[is_mod $x 5000]} {call_proc}                                           

Calculations are performed on 64-bit integer values

Example:

    [is_mod $x 5000 call_proc]                                                  

Avoid using this...

    if {is_mod $i 1000} {                                                       
       printf "\r  %2.f %%  " [* [/ ${i}.0 $size] 100.0]                        
    }                                                                           

Use this instead, as it is more efficient and avoids calling [if]

    is_mod $i 1000 {                                                            
       printf "\r  %2.f %%  " [* [/ ${i}.0 $size] 100.0]                        
    }                                                                           

See also: is_, is_number, math, is_*

is_numeric

    bool [is_numeric string ?-comma?]

Tests if a string is composed entirely of the following characters and
returns boolean 1 if so, otherwise 0

    +-0123456789,.

This may include comma-formatted numbers where -comma is used

Examples:

    puts [is_numeric hello]                                                     
    puts [is_numeric 10/01/2017]                                                
    puts [is_numeric 23:59]                                                     
    puts [is_numeric 12345]                                                     
    puts [is_numeric +123.45]                                                   
    puts [is_numeric -12,345.67]                                                

Results:

    0                                                                           
    0                                                                           
    0                                                                           
    1                                                                           
    1                                                                           
    1                                                                           

See also: is_*

is_set
 
    bool [is_set variableName ?-nocase?]

Tests to see if a variable exists (is set). Similar to php's 'isset'

This command is useful for testing potentially unset variables with [if]

You cannot do the following with an array variable because the variable
is resolved before [if] is called ...

    if {$_array(nosuch_element)} { # Will raise an unset variable error         

Instead use [is_set]:

    if {is_set _array(nosuch_element)} {                                        

You should pass a variable name not the dereferenced contents of a variable
[is_set] returns a boolean value (1 or 0)

[is_set] may be used on standard Tcl variables such as string variables,
arrays, lists, stacks, structs. For arrays you may also use [array is_set]

If using option expression on then the 1st argument will not be interpreted
as a command. Thus care should be taken with flow-control statement
arguments

Correct use of [is_set]:

    option expression on                                                        
    if { [is_set argv] } {...}                                                  
    option expression off                                                       
    if { is_set argv } {...}                                                    

Incorrect:

    if { is_set $argv } {...}                                                   
    if { [is_set $argv] } {...}                                                 

Array existence should be tested using [array exists]

See also: is_empty, is_null, array exists, is_admin, is_dir, is_elevated,
          set, vars, is_*

item

    string [item varname ?default?]

Safe reference of any variable with optional default value which is returned
if the specified variable is not found. The default default return is ""

[item] avoids fatal errors where a variable does not exist and will,
instead, return a default value    without complaint

Any type of variable may be referenced including simple variable, array,
struct member etc. The variable name only should be given and the name
must not be prefixed with a dollar sign

Examples:

    # Where simple variable q does not exist ...                                
    puts [item q]                                                               
    puts [item q "q was not found"]                                             
    set q foo                                                                   
    puts [item q]                                                               
    # Where array item a(1) does not exist ...                                  
    puts [item a(1)]                                                            
    puts [item a(1) "Array element a(1) was not found"]                         
    set a(1) bar                                                                
    puts [item a(1)]                                                            
    set q 1                                                                     
    puts [item a($q)]                                                           

Results:

    ""                                                                          
    q was not found                                                             
    foo                                                                         
    ""                                                                          
    Array element a(1) was not found                                            
    bar                                                                         

See also: array item, stack item, struct item

join
 
    string [join list ?separator?]

Join elements within a space-separated Tcl list and return a string. The
spaces are removed during the process. The list elements may be separated
by an optional separator character

Example:

    set s "The quick brown fox"                                                 
    set r [join $s "-"]                                                         
    puts $r                                                                     

Result:

    The-quick-brown-fox                                                         

[join] can also be used to 'flatten' a list by one level. The outer-
braces are always removed when an argument is passed to a command but
an additional +1 level of braces is removed

Example:

    set data {1 {2 3} 4 {5 {6 7} 8}}                                            
    puts [join $data]                                                           

Result:

    1 2 3 4 5 {6 7} 8                                                           

See also: split, list

json_to_list (JSON Conversion Plugin)

This is a very simple JSON conversion command

To load, use:    lib ticol_json?.dll? ?-verbose? ?-nocomplain?

    list [json_to_list json_string|var ?-var? ?variable-out?]

Convert a sequence of JSON format data into Tcl list format
Converting JSON using native Tcl code can be quite slow. This function
speeds up conversion significantly compared to a Tcl code implementation

The command was written for a specific data-processing task and may not,
therefore, be feature-complete as far as the JSON specification is
concerned. It contains a subset of the JSON feature set

If -var argument is passed then the first input argument is interpreted as
a variable name. An optional, trailing output variable is supported. 
This may be either a simple variable or an array element

Example:

    lib ticol_json -verbose                                                     
                                                                                
    set json { "name":"John", "age":30, "car":null }                            
    set l [json_to_list $json]                                                  
    puts "Name: [lindex $l 0 1]"                                                
    puts "Age:  [lindex $l 1 1]"                                                
    puts "Car:  [lindex $l 2 1]"                                                
                                                                                
    lib ticol_json -unload                                                      

Results:

    Name: John                                                                  
    Age:  30                                                                    
    Car:  null                                                                  

See also: Plugins, list, list_to_array, unwrap

Line Based String Processing Plugin

Efficiently process lines from CRLF-delimited text. Allows a block of
CRLF-delimited text to be addressed as conveniently as if it was an array

To load, use:    lib ticol_line?.dll? ?-verbose? ?-nocomplain?

    integer [line count string|?varname -var?]
    string  [line <number> string|?varname -var?]
    integer [line <number> string|?varname -var? -array name]

[line count] will return a count of CRLF-delimited lines in a string

[line <number>] will return a particular line number from a CRLF-
delimited string

If the -var qualifier argument is appended then the passed string argument
will be assumed to be the name of a variable. This avoids having to make
copies of large strings internally and should improve performance in loops

If '-array name' is appended then an array will be returned containing
the requested lines of text. The command will return the count of lines
returned

Example:

    lib ticol_line                                                              
    set s [readfile foo.txt]                                                    
    puts [line count $s]                                                        

Example:

    lib ticol_line                                                              
    set s [readfile foo.txt]                                                    
    puts [line 237 $s]                                                          

Example:

    lib ticol_line                                                              
    set s [readfile ovid.txt]                                                   
    puts [line 237 $s 4 -array a]                                               

Result:

    # Returns 4 lines from 237 on in array 'a'                                  

See also: plugins

K Combinator
 
    string [K x ?y? ...]

Functional programming K-combinator. Allows chaining of Tcl expressions
Functionally equivalent to:

    proc K {a b} {return $a}                                                    

It can be used in all situations where you want to deliver a result that is 
not the last. i.e. the first item is returned but the last discarded

The K is a capital letter K (not lower-case k)

For instance, reading a file in one go:

    proc readfile filename {                                                    
       set f [open $filename]                                                   
       set data [read $f]                                                       
       close $f                                                                 
       return $data                                                             
    }                                                                           

can be simplified, without need for the data variable, to:

    proc readfile filename {                                                    
       K [read [set f [open $filename]]] [close $f]                             
    }                                                                           

Swap two variables, say a and b, without any temporary variables:

    set a [K $b [set b $a]]                                                     

Another example, popping a stack:

    proc pop _stack {                                                           
       upvar 1 $_stack stack                                                    
       K [lindex $stack end] [set stack [lrange $stack 0 end-1]]                
    }                                                                           

We can also achieve an efficient implementation of lambda expressions in
Tcl using K:

  proc lambda {arg body} { K [set n [info $body 0]] [proc $n $arg1 $body] }     

For more information see:
    http://wiki.tcl.tk/1923/

See also: list functions

Key Input Routine

Here is an example input procedure for single key input with echo
This could also be implemented as a [sub]

    if {! [defined get_key_input]} {                                            
       option expression off                                                    
       proc get_key_input {} {                                                  
          # Returns a lower-case keypress                                       
          while {1} {                                                           
             set k [getkey -lower]                                              
             if {eq $k "y"} {                                                   
                puts $k                                                         
                return "-r"                                                     
             } elseif {eq $k "n"} {                                             
                puts $k                                                         
                return {}                                                       
             } elseif {eq $k "q"} {                                             
                puts $k                                                         
                return q                                                        
             }                                                                  
             sleep 20                                                           
          }                                                                     
       }                                                                        
    }                                                                           

Example usage

    option expression off                                                       
    set r [get_key_input]                                                       
    if {eq $r "q"} {                                                            
       textcolor red                                                            
       puts "\n * Quit *"                                                       
       textcolor                                                                
       stop                                                                     
    }                                                                           

See also: getkey, input, proc, sub

Lambda Example

Here is an example Lambda function from the http://tcl.tk website

When called from a script the lambda function can be called using 
puts [$inv x]. This script cannot handle lists of arguments

When called from the Ticol CLI you can use just $inv x  - e.g. $inv 5

   proc lambda {args body} {                                                    
      puts "in lambda args:$args body:$body"                                    
      set name [info level 0]       # Should return lambda x {expr 1./$x}       
      puts "Name:$name"                                                         
      commands $name                                                            
      proc $name $args $body                                                    
      set name                                                                  
   }                                                                            
   set inv [lambda x {expr 1./$x}]  # Calls proc lambda                         
                                                                                
   puts [$inv 5]                    # Call with $inv 5 from the Ticol CLI       

Result:

   0.2                                                                          

The [lmap] command can also be used to construct lambda expressions such
as:

    puts [lmap {x {expr {$x * $x}}} {1 2 3 4 5}]                                

For more on lambda expressions see: lmap and apply

See:
    http://wiki.tcl.tk/519

See also: K, apply, lassign, lmap, map

lappend
 
    list [lappend listVar string ?string?...]

Append list elements onto a variable or creates a list from a series of
strings. Strings with spaces are wrapped in double quotes. The variable
referenced in the argument will be created if it does not exist

Where the string to be appended contains reserved Tcl characters such as

    {}{}$\ 

these will be escaped in order to print safely

Creating Nested Lists
---------------------
Don't use [lappend] to add enclosing { and } braces to create sub lists as
this will add superfluous spaces and break parsing. Either append sub
items to a sub-list and [lappend] that or use string routines to wrap in {}

Examples
--------

Example:

    puts [lappend var [list one two] three]                                     
    puts $var                                                                   

Result:

    {one two} three                                                             

Example:

    set s ""                                                                    
    echo [lappend s "{escaped"]  # [echo] is required to see the escapes        

Result:

    \{escaped                                                                   

Example:

    # Will create a new variable                                                
    lappend nosuchvar 1 2 3  # Variable 'nosuchvar' does not exit               
    puts $nosuchvar                                                             

Result:

    1 2 3                                                                       

See also: lindex, list

lassign

    list [lassign list ?varName ...?]

This command treats the value list as a list and assigns successive
elements  from that list to the variables given by the varName arguments
in order. If there are more variable names than list elements, the
remaining variables are set to the empty string. 

If there are more list elements than variables, a list of unassigned
elements is returned, otherwise an empty list {} is returned

Example:

    lassign {a b c} x y z                                                       

Result:

    $x =>'a'                                                                    
    $y =>'b'                                                                    
    $z =>'c'                                                                    

Example:

    lassign {a b c} v w x y z                                                   

Result:

    $v =>'a'                                                                    
    $w =>'b'                                                                    
    $x =>'c'                                                                    
    $y =>'{}'                                                                   
    $z =>'{}'                                                                   

Example: 

    lassign {a b c "The quick" "brown fox" "jumps"} x y z                       

Result

    $x =>'a'                                                                    
    $y =>'b'                                                                    
    $z =>'c'                                                                    
                                                                                
    {The quick} {brown fox} jumps                                               

See also: lappend, list, apply, lmap, map, apply, lambda

lcount

    integer [lcount list]
    integer [lcount varname -var]

Returns an integer count of the number of items in a list.
Non-standard Tcl. See [llength]

Using -var and a variable name avoids a time-consuming variable copy
which may improve performance in tight loops or with very large lists

The argument list must be either a variable or, if a literal string,
enclosed in braces or double-quotes

Example:

    puts [lcount $qbf]      # Count the number of words                         
    puts [llength $qbf]     # Count the number of words (standard)              
    puts [lcount qbf -var]  # Count the number of words                         
    puts [lcount {{a b} c}]                                                     

Result:

    9                                                                           
    9                                                                           
    9                                                                           
    2                                                                           

Example:

See also: list, lindex, lappend, lassign

ldepth (proc)

    integer [ldepth list]

Returns the depth of a list or 0 for an empty or flat list

Procedure definition:

    proc ldepth {list} {                                                        
       if {! [is_list $list]} {                                                 
          return 0                                                              
       }                                                                        
       set flatList [join $list]                                                
       if {[string equal $flatList $list]} {                                    
          return 1                                                              
       }                                                                        
       return [expr {[ldepth $flatList] + 1}]                                   
    }                                                                           

Example:

    set l {}                                                                    
    puts [ldepth $l]                                                            
    set l a                                                                     
    puts [ldepth $l]                                                            
    set l {a b}                                                                 
    puts [ldepth $l]                                                            
    set l {a {b {c d}}}                                                         
    puts [ldepth $l]                                                            

Results:

    0                                                                           
    1                                                                           
    1                                                                           
    3                                                                           

See also: llength, unwrap, list, lappend, lassign, lcount, ldepth, lindex,
          list, list_to_array, llength, lmap, lrand, lrange, lreverse,
          lsearch, lset, lsort

lastchar

    string [lastchar string]

Returns the last character of a string (or list interpreted as a string)
If the string is empty then an empty string "" is returned

[lastchar] is more efficient than convoluted combined commands such as:

    puts [mids $qbf [string length $qbf] 1]                                     

Examples:

    puts [mids $qbf [string length $qbf] 1]                                     
    puts [lastchar $qbf]                                                        
    puts [lastchar Hello]                                                       
    puts [lastchar ""]                                                          

Results:

    g                                                                           
    g                                                                           
    o                                                                           
    ""                                                                          

See also: string, left, right, mids, mid

left, string left
 
    string [left <string> n]
    string [left string var -var]
    string [string left <string> n]
    string [string left var n -var]

Return the leftmost n characters of a string

A common task is to look at the first character of a string. The most
effective way to do this is to use [index] rather than [string left]

If the string contains special Tcl characters []{}$ then call [unescape]
on the string before calling [string left]

Example:

    puts [string left $qbf 1]                                                   
    puts [index $qbf 1]                                                         

Results:

    T                                                                           
    T                                                                           
                                                                                
See also: index, right, mid, lastchar, setc, setl, setr, strto, strtor          
          filter                                                                

len

    integer [len string]

Return the length of a string

This is a convenient alias for [string length]

Example:

    puts [len $qbf]                                                             

Result

    43                                                                          

See also: string, string length, lastchar

let (Assignment command)

let Lvalue  = Rvalue   Evaluate Rvalue(s) as an expression then assign          
let Lvalue := Rvalue   Assign rvalue(s) without evaluating (assignment)         
                       The MPP may simplify/evaluate expressions                
                       Rvalue of [expr] or [command] will still evaluate        
                       If Rvalue is a string then a well-formed Tcl list        
                       will be returned (this may be brace-wrapped)             
let Lvalue ++          Increment lvalue(s) - equivalent to [++ var]             
let Lvalue --          Increment lvalue(s) - equivalent to [-- var]             
let Lvalue += Rvalue   Assign to existing value. Rvalue may be expression       
                       Will cast to double before addition                      
let Lvalue -= Rvalue   Assign to existing value. Rvalue may be expression       
                       Will cast to double before subtraction                   
                                                                                
let variable|array  = expression|variable|array|[command] # Eval with expr      
let variable|array := list|variable|array|[command]       # Assignment only     
let variable|array  = {variable|array}  # Evaluate contents with [expr]         
let variable|array := {variable|array}  # Assignment of values only             

[let] implements a proposed extension to the Tcl language. It acts similarly
to [set] except that it accepts a Tcl expression as the Rvalue

The expression may be another Tcl variable or a complex statement

[let] returns the resulting Lvalue variable's contents if there is only one
lvalue. If several variables are [let] then the last variable's contents
is returned

Whitespace is allowed between individual Rvalue expression arguments where
'=' is used to separate. This will be removed before the result is
evaluated using [expr]

To assign a literal string rather than evaluate it, use the ':=' operator 
instead of '='. This also allows the result of a bracketed [eval] operation
or commands to be assigned. For numeric expressions there will be no 
difference between the two methods since the numeric result will be 
correctly checked by [expr]

Example:

    let x y z := "06/Jun/2015"    # Note: just assign, don't [eval]             

Note that expressions contained within square [] brackets will have already
been evaluated before being presented to [let]. Therefore the result will
be re-evaluated again when using '='. Where this is not desired use direct
assignment ':='

For example let a b c = [info commands] return an invalid result since the
resulting command string will be re-evaluated as an expression. The correct 
command would be: let a b c := [info commands]

Example:

    set x "Hello world"                                                         
    let a b c := $x                                                             
    puts "$a, $b, $c"                                                           

Result:

    Hello world, Hello world, Hello world,                                      

Example:

    let x := [/ 22 7.0]     # Call assign instead of expr                       

Result:

    3.142857142857143       # [] are always resolved                            

Example

    let x := [/ 22 7.0]*2   # Call assign instead of expr                       

Result:

    3.142857142857143*2     # [] are always resolved                            

Example

    let x = [/ 22 7.0]*2    # Call expr and assign the result                   

Result:

    6.285714285714286                                                           

Example:

    let x := "Hello world"  # Will return a Tcl list                            

Result:

    {Hello world}                                                               

Example:

    let x := one "two three" four   # Will return a well-formed Tcl list        

Result:

    one {two three} four                                                        

Example:

    set q 0;let q += [rnd 1 10]; puts $q                                        

Result (some value between 1 and 10):

    2.000000                                                                    

Let is an efficient way of setting variables to a complex expression which 
avoids intermediate steps but will be slower and less-efficient for simple 
assignments of const values

When using the '=' operator, variables, including array variables will be
evaluated before the call to let, and the contents of the variable will 
then be evaluated by let as an expression, thus if you wish to assign the 
value of a variable without interpretation you must wrap the rvalue variable 
in braces thus:

Example:

    let path = {$env(TEMP)} # Prevent [expr] evaluation, pass as a literal      
    let path := $env(TEMP)  # Alternate method, direct assignment               

Result:

    x is correctly set to 'c:\temp'                                             

Example:

    let path = $env(TEMP)                                                       

Result:

    x is incorrectly set to 'temp' after evaluation of c:\temp                  

Expression examples:

Example:

    let q = 4 * atan(1)                                                         
    puts "q is $q"                                                              

Result:

    q is 3.141592653589792                                                      

Example:

    let q = [fib 12]                                                            
    puts "q is $q"                                                              

Result: 

    q is 233                                                                    

Example:

    let x = {rnd(1,5)>2?return Hi:return Lo} # rvalue must be wrapped in {}     

Result:

    Lo                                                                          

See: Tcl language extension proposal:
    http://wiki.tcl.tk/3880

See also: set, unset, vars

level

    integer [level]

Returns the current callframe level

See also: info, proc

lib (Ticol Plugin Module Extension Loader)

Load or reference a library using the following syntax:

    result [lib <libraryname>?.dll? ?-option?]

    integer [lib dllname ?filter|modulename? ?-list? ?-nocomplain? \            
         ?-verbose? ?-replace?]              # Load a library plugin            
    integer [lib dllname -unload]            # Unload a module                  
    string  [lib -modules]                   # List loaded modules              
    string  [lib dllname -commands]          # List commands for a              
                                             # loaded plugin                    
    string  [lib dllname -info]              # List information for a           
                                             # a loaded plugin                  
    bool    [lib -loaded module_name]        # Test if a module is loaded       
    bool    [lib module_name -loaded]        # Test if a module is loaded       
    ticol.exe /lib:dllname ?/lib:dllname...? # Load at startup                  

(For a list of available plugins see: help plugins)

Load a Ticol plugin extension DLL. The DLL must have been written for Ticol
using the Ticol interface standards/ It must provide query interfaces
according to the supplied API definitions. (See the accompanying API
documentation)

If no path is given, [lib] will attempt to load the plugin library from the
current ticol.exe path location. The command returns a count of the number
of modules loaded or unloaded

[lib] will load commands into the current interpreter instance. Once loaded 
these cannot be reloaded by [lib] until they have been undefined using
[undef]. [lib -nocomplain] may be used to ignore any reload errors.

No load status will be printed to the console unless the -verbose arg is
used

Conflicting existing commands can be automatically unregistered using the
-replace argument which will cause existing commands to be unlinked

Once loaded, plugin library functions may be used the same as any other 
internal Ticol command and variable memory assigned is automatically
garbage-collected

[lib -info] Calling separately with the -info arg will return the version
number as x.xx and the build date in ISO format as a Tcl list. This command
does not load the library

To unload a plugin and remove all registered commands use the -unload 
argument. -unload will return a count of modules successful de-registered
An -unload call to a lib which is not loaded will raise an error unless
the -nocomplain option is used

Unloading a [lib] will destroy any aliases to exported [lib] commands

A library may be loaded at startup using:  ticol.exe /lib:dllname
Any number of modules may be loaded at startup

[lib -modules] will return a Tcl list of all registered Ticol module DLLs

Examples:

   lib ticol_zip                      # Load in silent mode                     
   lib ticol_mail.dll -verbose        # Load in verbose mode                    
   lib ticol.dll d* -list             # Load and return modules as a list       
   lib ticol.dll -unload              # Unload ticol.dll                        
   lib ticol_bignum.dll -replace      # Reload                                  
   puts [lib ticol_ping -info]        # Returns "1.02 20160331"                 
   puts [lib -modules]                # "ticol_bignum ticol_zip ticol_reg"      

To unload all loaded plugins use the following code snippet

    foreach x [lib -modules] {lib $x -unload}                                   

An detailed audit of all modules can be performed using the following code

    set file [escape [ls "ticol_*.dll" -list]]                                  
                                                                                
    set l ""                                                                    
    foreach x $file {                                                           
       set cmds [lib $x -list]     # Load with option '-list'                   
       set info [lib $x -info]     # Module info                                
       textcolor yellow                                                         
       puts $x                     # Lib filename                               
       textcolor                                                                
       puts "\t$info"                                                           
       puts "\t$cmds"                                                           
       lib $x -unload              # Unload the module                          
    }                                                                           
    newline                                                                     
    puts "[lcount $file] module(s)"                                             

Example:

    # Find the first loaded module and confirm it is loaded                     
    # Yes, this is not particularly useful!                                     
    lib ticol_cpu                                                               
    puts [bool [lib [lindex [lib -modules 0]] -loaded]]                         
    lib ticol_cpu -unload                                                       
    puts [bool [lib [lindex [lib -modules 0]] -loaded]]                         

Result:

    true                                                                        
    false                                                                       

See: plugins for a list of available Ticol plugin modules

See also: plugins, lib interface, undef, commands

lib interface specifications

Plug-in DLLs can be produced for Ticol which adhere to the following 
specifications:

All functions must be exported as __cdecl
The following exports must be offered:

    long __cdecl _get_dll_version()                                             
    {                                                                           
       return _DLL_VER                  // C++ macro definition                 
    }                                                                           
                                                                                
    LPCSTR __cdecl _get_dll_date()                                              
    {                                                                           
       return _DLL_DATE;                // C++ macro definition                 
    }                                                                           
                                                                                
    LPCSTR* __cdecl _get_function_list()                                        
    {                                                                           
       return _dll_function;            // Returns a C++ array                  
    }                                                                           
                                                                                
    LPCSTR* __cdecl _get_command_list()                                         
    {                                                                           
       return _dll_command;             // Returns a C++ array                  
    }                                                                           
                                                                                
    long __cdecl _get_export_count()                                            
    {                                                                           
       return _export_count;            // Defined from arrays                  
    }                                                                           

Interface functions (as defined above) must be exported by name as __cdecl
with the following Ticol command signature:

int (__cdecl* CmdFunc)(struct Interp* i, int argc, char** argv, void* pd)       

Your functions will use i as the interpreter instance and use the argc/argv
arguments to receive arguments. These arguments may be altered but not
deleted or reassigned. Full details of available library functions are given
in the plugin documentation. For more information see the example C++ source
code

    ticol_init(); // Call before calling any Ticol library functions            

Only locally-allocated memory may be freed. The C++ lib header has 
prototypes for a number of functions exported back from Ticol to the DLL 
for memory housekeeping etc.

Commands are specified as case-sensitive strings which must not contain spaces

Example skeleton stub code:

#define _DLL_VER      100                   // Required (v1.00)                 
#define _DLL_DATE     "20211221"            // Required (21/Dec/2021)           
#define _DLL_COMMENT  "A Demo Plugin"       // Required (Caption)               
                                                                                
LPCSTR _dll_command[]=    {"mytest" };        // Registered command name        
LPCSTR _dll_function[]=    {"CommandTest"};    // The C++ function here         
long _export_count=howbig(_dll_command);    // howbig() is a C++ macro          
                                                                                
long __cdecl lib_cleanup(long)              // Any cleanup goes in here         
{                                                                               
   // Empty (no cleanup required)                                               
}                                                                               
                                                                                
int __cdecl CommandTest(struct Interp *i, int argc, char **argv, void *pd)      
{                                                                               
   ticol_init();                           // MUST be called                    
   print_error("%s: Command not implemented\n",argv[0]);                        
   _ticol_set_result(i,"0");               // Return "0" to Tcl                 
   return TICOL_OK;                        // Return code SUCCESS               
}                                                                               

The associated project DEF file...

   LIBRARY ticol_my.dll                                                         
   DESCRIPTION "Ticol Demo Plugin"                                              
   EXPORTS                                                                      
      CommandTest                                                               

See also: plugins, commands, faq, calldll

lindex
 
    string [lindex list index ?index|end-N?... -blanks]

Index an item from a list. Multiple index selectors may be given, in which
case, each will be selected in turn from left to right. Brace binding is
honoured and quoted strings are treated as distinct objects outside of
braces. Index values are array base indexed at zero

Items are separated by whitespace which is removed unless braced

Indexing very large lists (megabytes and above) can be quite slow. Arrays 
should be used in preference

For single indices, the mnemonic, "end" may be used either with or without
an adjustment value, e.g. end-4

If -blanks is used then empty items will be returned

To return a range of elements from a list, see [lrange]

Example:

    set l [list colour "icon cornflower blue"]                                  
    puts $l                                                                     

Result:

    colour {icon cornflower blue}                                               

Example:

    puts [lindex $l 0]                                                          

Result:

    icon                                                                        

Example:

    puts [lindex $l 1 0]                                                        

Result:

    icon                                                                        

Example:

    puts [lindex $l 1 1]                                                        

Result:

    cornflower                                                                  

See also: list, lrange, llength, lappend

Line Continuation Character
 
A backslash character can be appended to the end of a Tcl source file line
This will cause the line to continue onto the next and the backslash to be
ignored. 

Care should be taken to include formatting spaces when breaking up a string

The next line should start at the beginning of the line and not be indented
within the source-code unless you wish this to be included in the resulting
string

The line-continuation character must not be followed by any other character
or comment such as # on that line or it will be interpreted as a literal
backslash rather than end of line marker

Ticol also handles multi-line strings within a source file. 

Example: (First char of each next line is will become a space)

    .........1.........2.........3...
                                                                                
    puts "The quick brown \                                                     
    fox jumps over \                                                            
    the lazy dog"                                                               

Result:

    The quick brown fox jumps over the lazy dog                                 

Example: (Multi-line string. The text format is retained)

    .........1.........2.........3...
                                                                                
    puts "The quick brown                                                       
     fox jumps                                                                  
     over                                                                       
     the lazy                                                                   
    dog"                                                                        

Result:

    .........1.........2.........3...
                                                                                
    The quick brown                                                             
     fox jumps                                                                  
     over                                                                       
     the lazy                                                                   
    dog                                                                         

Example: (Results in extra spaces)

    .........1.........2.........3...
                                                                                
    puts "The quick brown \                                                     
    fox jumps over \                                                            
    the lazy dog"                                                               

Result:

    The quick brown fox jumps over the lazy dog                                 

Example:

    .........1.........2.........3...
                                                                                
    puts \                                                                      
    "Hello world"                                                               

Result:

    Hello world                                                                 

Where if statements are used with [option expression on] and the expression
is on multiple lines then line break continuation characters must be used,
e.g.

    set r [rnd 1 10]                                                            
    if {    $r    \                                                             
     > \                                                                        
     5                                                                          
    } {                                                                         
       puts Yes                                                                 
    } else {                                                                    
       puts No                                                                  
    }                                                                           

Numbers may be declared using line-continuation providing they are 
wrapped in double quotes, thus:

    set n "12345\                                                               
    6789"                                                                       

See also: escape sequences, macro, #ifdef, #define

Command Line Interpreter (CLI) Line Editing

The CLI implements standard 'insert-mode' line-editing with the following
keystrokes. The command history is reloaded on next running Ticol

  UP CURSOR          Previous command in history
  DOWN CURSOR        Next command in history
  HOME               Move cursor to the start of the line
  END                Move cursor to the end of the line
  ENTER/RETURN       Submit the edited line to the interpreter (command)
  BACKSPACE DELETE   Delete backwards (right to left)
  DEL                Delete forwards (text to right of cursor is deleted)
  CTRL+C             Cancel the current edit and start a new line
  CTRL+M             Same as ENTER/RETURN
  CTRL+S             Halt the interpreter
  CTRL+BREAK         Immediately quit the interpreter (even while running)

The command [exit] will exit the CLI whereas [stop] will not

See also: command line history, CLI

Line Numbers

Absolute line numbers exist only in an original Ticol source script. In Tcl
there is no concept of absolute line numbers within operational code since
code may be broken up into many separate fragments (e.g. procs)

The Macro PreProcessor (MPP) will completely remove comment lines and thus
the original line numbers cannot be traced. Also, procedures use copied
segments of code which have no absolute line numbering.

Line numbers referring back to the original source code can be matched
during debugging by the use of a variable set within the source code at
strategic points using the #__LINE__ macro

Example:

    set line #__LINE__                                                          
                                                                                
    if {! [defined check] } {      # [assert] is the native alternative         
       proc check {x y} {                                                       
       if {!= $x $y} {                                                          
             puts "Error in test at after line $::line"                         
             puts "$x != $y"                                                    
             stop                                                               
          }                                                                     
       }                                                                        
    }                                                                           
                                                                                
    set line #__LINE__                                                          
    check [expr "abs(-100)"] 100    # Should pass                               
    set line #__LINE__                                                          
    check [expr "abs(-100)"] 101    # Should fail                               

Result:

    Error in test at after line 15                                              

Example:

The [assert] command using #__LINE__ to add a trace output

    assert [/ 22 7.0] {> $_ 3} -v -line #__LINE__                               

See also: assert, line continuation character, debugging, performance

llength
 
    integer [llength list]

Return the count of items in a Tcl list or 0 if the list is empty
llength takes a dereferenced list literal as argument, not a variable name

Example:

    set a [list one two three]                                                  
    puts [llength $a]                                                           

Result:

    3                                                                           

See also: list, lappend

link_to_path
 
    string [link_to_path filename]

Resolve a Windows LNK shortcut file to the target path for that link file
The result is an unescaped string. [escape] may be required to use the 
return with other commands

Example:  (This uses an external app: makelink.exe to create the LNK file)

    makelink.exe ticol.exe .                                                    
    puts [link_to_path ticol.exe.lnk]                                           

Result:

    C:\Tcl\ticol.exe                                                            

See also: info file, escape

Linked List Plugin

To load use: lib ticol_list ?-verbose? ?-nocomplain?

    handle [linkedlist create]
    bool   [linkedlist add handle lvalue ?rvalue?]
    string [linkedlist get handle lvalue]
    string [linkedlist item handle integer]
    code   [linkedlist foreach $handle lvalue ?rvalue? {command}]
    bool   [linkedlist in_list handle key]
    bool   [linkedlist insert handle key lvalue ?rvalue?]
    long   [linkedlist count handle]
    void   [linkedlist walk handle]
    bool   [linkedlist unset handle key]
    bool   [linkedlist unset handle]

[linkedlist] offers standard low-level, linked lists. These are not the
same as Ticol built-in Tcl lists (which are represented as ordered strings)
[linkedlist] enables a programmer to create rapid, robust linked lists in
the same manner as offered by many other high-level programming languages

External data structures such as [linkedlist] require a valid handle
variable to access

A list has a start (often called a 'head') and an end (often called a 
'tail'). Items are usually added to the start of the list and the 'tail' 
value represents the most recently added item. Lists are iterated by 
starting at the 'start' node and reading each successive node in the chain
until the 'tail' or last node is met

       the      quick    brown                                                  
    +------+  +------+  +------+  +-----------+                                 
    | head |->|node 1|->|node 2|->|tail (NULL)|                                 
    +------+  +------+  +------+  +-----------+                                 
    |lvalue|  |lvalue|  |lvalue|  |    N/A    |                                 
    +------+  +------+  +------+  +-----------+                                 
    |rvalue|  |rvalue|  |rvalue|  |    N/A    |                                 
    +------+  +------+  +------+  +-----------+                                 

Each node in the linked list can store two variables. Each will store an 
lvalue (left hand value) and may optionally store an rvalue (right hand 
value)

[linkedlist create]  Creates a new list and returns a handle to the created
list. A list is identified and manipulated by its handle. It is a fatal
error to issue a list command with an invalid, freed or absent handle

[linkedlist add] will add a new item to the linked list. Each successive
new item is added at the head of the list. An "rvalue" is optional

Example:

    linkedlist add $handle hello                                                
    linkedlist add $handle pi 3.14159265358979323                               

[linkedlist get] retrieves an 'rvalue' (if any) from a list item. A list
item may either simply be present in the list (as an lvalue) or the
lvalue may have an associated rvalue. (lvalue and rvalue stand for left
hand value and right-hand value respectively)

Example:

    # The lvalue is 'pi', the rvalue is '3.14159265358979323'                   
    linkedlist add $handle pi 3.14159265358979323                               
    puts [linkedlist get $handle pi]                                            

[linkedlist item] Allows for random access of the linked list by integer
value. The first item in the list will be '1' (i.e. a base 1 index)
[linkedlist count] should be used to obtain a count of list items

Example:

    option expression off                                                       
    set handle [linkedlist create]                                              
    set i 1                                                                     
    foreach {x} $qbf {                                                          
       linkedlist insert $handle {} $x $i                                       
       ++ i                                                                     
    }                                                                           
    for {set i 1} {< $i [linkedlist count $handle]} {++ i} {                    
       set lv [linkedlist item $handle $i]          # LValue                    
       set rv [linkedlist item $handle $i -rvalue]  # RValue                    
       puts "$i:\t$lv\t$rv"                                                     
    }                                                                           

Result:

    1:      lazy    8                                                           
    2:      the     7                                                           
    3:      over    6                                                           
    4:      jumps   5                                                           
    5:      fox     4                                                           
    6:      brown   3                                                           
    7:      quick   2                                                           
    8:      The     1                                                           

[linkedlist foreach] allows iteration over the list from start to head,
calling some body of code. An lvalue variable must be supplied. An rvalue
variable is optional. Standard flow-control commands such as [break],
[return] and [stop] are supported and these will return an appropriate
error code (See: error codes, stop, break, return)

Example:

    set handle [linkedlist create]                                              
    set i 1                                                                     
    foreach {x} $qbf {                                                          
       linkedlist add $handle $x $i                                             
       ++ i                                                                     
    }                                                                           
    linkedlist foreach $handle lv rv {                                          
       puts "foreach is:\t'$lv'\t'$rv'"                                         
       if {[eq $lv fox]} {                                                      
          puts "* Break *"                                                      
          break                                                                 
       }                                                                        
    }                                                                           

Results:

    foreach is:     'The'   '1'                                                 
    foreach is:     'quick' '2'                                                 
    foreach is:     'brown' '3'                                                 
    foreach is:     'fox'   '4'                                                 
    * Break *                                                                   

[linkedlist in_list] can be used to test if a given lvalue (key) is present
in a given linked-list structure

Example:

    set handle [linkedlist create]                                              
    set i 1                                                                     
    foreach {x} $qbf {                                                          
       linkedlist insert $handle {} $x $i                                       
       ++ i                                                                     
    }                                                                           
    puts "dog present?: [bool [linkedlist in_list $handle dog]]"                
    puts "cat present?: [bool [linkedlist in_list $handle cat]]"                

Results:

    true                                                                        
    false                                                                       

[linkedlist insert] inserts a new entry AFTER any key value which is found
or at the start of the list if the key value was not found or was supplied
as an empty/null value

Example:

    linkedlist insert $handle quick blue                                        
    linkedlist unset $handle fox                                                

Repeated calls to [linkedlist insert handle {}] can be used to create a
list in reversed-order from the default. Each successive call will add
the new value to the beginning of the list

Example:

    set handle [linkedlist create]                                              
    set i 1                                                                     
    foreach {x} $qbf {                                                          
       linkedlist insert $handle {} $x $i                                       
       ++ i                                                                     
    }                                                                           
    linkedlist walk $handle                                                     

Results:

    List 0x2d60cb0 0 item(s)                                                    
    List::walk 0x2d60f70 VT_001e [dog]=>'9'                                     
    List::walk 0x2d60f20 VT_001e [lazy]=>'8'                                    
    List::walk 0x2d60ed0 VT_001e [the]=>'7'                                     
    List::walk 0x2d60e80 VT_001e [over]=>'6'                                    
    List::walk 0x2d60e30 VT_001e [jumps]=>'5'                                   
    List::walk 0x2d60de0 VT_001e [fox]=>'4'                                     
    List::walk 0x2d60d90 VT_001e [brown]=>'3'                                   
    List::walk 0x2d60d40 VT_001e [quick]=>'2'                                   
    List::walk 0x2d60cf0 VT_001e [The]=>'1'                                     

[linkedlist count] will return a count of the number of items in a list

[linkedlist walk] will perform a debug 'walk' of the linked list, starting
at the beginning and iterating all nodes until the head is met

Cleanup
-------
It is not recommended to [return] from a linked script with ticol_list
loaded and objects created unless you first clean-up using 
[lib ticol_list -unload] and/or specifically call [linkedlist unset]

[linkedlist unset $handle] Destroys the linked list and renders the
handle invalid. Attempts to use a handle after a call to unset will raise
an exception. [linkedlist] objects are not subject to automatic garbage-
collection on proc-exit

[lib unload] destroys all created lists and reclaims their memory

Example:

    set handle [linkedlist create]                                              
    puts "Handle to list is $handle"                                            
    set i 0                                                                     
    foreach {x} $qbf {                                                          
       linkedlist add $handle $x $i                                             
       ++ i                                                                     
    }                                                                           
    linkedlist insert $handle jumps "rapidly"  # Insert after jumps             
    linkedlist insert $handle {} *start*       # Insert at start                
    linkedlist insert $handle fox dog          # Add dog after fox              
    linkedlist unset $handle fox               # Remove fox                     
    linkedlist walk $handle                                                     
    linkedlist unset $handle                                                    

Results:

    List 0x2d20fe0 10 item(s)                                                   
    List::walk 0x2d20cd0 VT_001e [The]=>'0'                                     
    List::walk 0x2d20d20 VT_001e [quick]=>'1'                                   
    List::walk 0x2d20d70 VT_001e [brown]=>'2'                                   
    List::walk 0x2d21150 VT_001e [dog]=>'10'                                    
    List::walk 0x2d20e10 VT_001e [jumps]=>'4'                                   
    List::walk 0x2d21100 VT_001e [rapidly]=>'9'                                 
    List::walk 0x2d20e60 VT_001e [over]=>'5'                                    
    List::walk 0x2d20eb0 VT_001e [the]=>'6'                                     
    List::walk 0x2d20f00 VT_001e [lazy]=>'7'                                    
    List::walk 0x2d20f50 VT_001e [dog]=>'8'                                     

See also: list, array, stack, struct, error codes

list
 
    list {vars...}

Return a Tcl list from a series of variables. List creates an atomic
parameter which can be supplied/chained into other parameters

Example:

    call [dict create d colours [list orange red blue green purple]]            

Ticol supports a range of standard Tcl list commands

Note that a plugin also offers standard linked lists: (See: linked list)

See: list commands    for a full list of Tcl list commands

See also: lappend, lreverse, lcount, lindex, llength, lassign, 
          list_to_array, array_to_list, string, linked list, apply

List Processing Commands

Ticol lists are simple formatted strings, and are quite inefficient.
List processing operations should be kept to relatively short lists
For greater efficiency, use arrays or stacks

See the following separate help topics:

    array_to_list    Convert an array to a list
    in               Test if an item is in a list
    lappend          Append an item to a list
    lassign          Anonymous functions. Apply a command to a list
    lcount           Return the count of elements in a list
    ldepth           Test the depth of a list (proc)
    lindex           Return an item from a list by integer index
    list             Return a well-formed Tcl list
    list_to_array    Convert a list to an array
    llength          Return the length of a list
    lmap             Iteratively assign elements of lists to variables
    lpop             Pop an item from head of a list, optionally splitting the list
    lpush            Push item to head of a list
    lrand            Return a random element from a list
    lrange           Return a range of items from a list
    lreverse         Reverse the order of a list
    lsearch          Search a list for an item
    lset             Set a list element
    lsort            Sort a list
    ltail            Returns the tail of a list held in a strong constant
    ni               Test if an item is not in a list

See also: Tcl lists

list_to_array

    integer [list_to_array <tcl-list> arrayname ?array-base?]

Converts a well-formed Tcl list into an array indexed by integer from 
element base 0.

Example:

    set r [list_to_array $qbf out]                                              
    puts "list_to_array returned $r item(s)"                                    
    set s [array size out]                                                      
    for {set i 0} {[< $i $s]} {incr i} {                                        
       printf "%i:\t%-40.40s\r\n" $i $out($i)                                   
    }                                                                           

Result:

    list_to_array returned 9 item(s)                                            
    0:   The                                                                    
    1:   quick                                                                  
    2:   brown                                                                  
    3:   fox                                                                    
    4:   jumps                                                                  
    5:   over                                                                   
    6:   the                                                                    
    7:   lazy                                                                   
    8:   dog                                                                    

See also: list

lpush, lpop, ltail, lpeek

    list [lpush list value]
    void [lpush variable value -var]
    list [lpop list ?-n X?]
    list [lpop variable -var ?-n X?]
    list [ltail list]
    list [lpeek list]
    list [lpeek variable -var]

These commands offer 'stack-like' push and pop operations which will
operate either on variables or lists held in strings.

Where lists are handled as strings the [lpush] and [lpop] oeprations
may be 'chained' together. This is not recommended when using variables
via the -var argument.

[lpop] will return a list containing one item. This may be a a string of
one or more words, depending whether it was braced or not within the
input list.

When using string lists, [lpop] simply returns the head of the list and
leaves the input list unchanged. i.e. the input string is a constant

The default behaviour for [lpop] is to pop on item from the list, this
can be changed using '-n' to specify how many items to return.

[lpush] will return an updated list when used with a string based list 
input. When used with a variable, the command returns nothing and
the updated list appears within the given variable.

[ltail] is provided so that lists held in string constnats can be
split in conjunction with [lpop]. [ltail] operations may be chained.

[lpeek] will return the head item from a list without removing it

Example:

    # List push sing string literals                                            
    puts [lpush [lpush "" Hello] world]                                         
    # List push using a variable                                                
    set s ""                                                                    
    lpush s Hello -var                                                          
    lpush s world -var                                                          
    puts $s                                                                     
    # Decompose the list into head and tail using a variable                    
    set head [lpop s -var]                                                      
    puts $head                                                                  
    puts $s                                                                     
    # Build multi-word list items...                                            
    set s ""                                                                    
    lpush s {Now is the winter} -var                                            
    lpush s {of our discontent} -var                                            
    puts $s                                                                     
    # Return the tail of a list held in a string constant                       
    puts [ltail $qbf]                                                           

Results:

    Hello world                                                                 
    Hello world                                                                 
    Hello                                                                       
    world                                                                       
    {Now is the winter} {of our discontent}                                     
    quick brown fox jumps over the lazy dog                                     

See also: list commands, list, lindex, lmap, lreverse

lrand

    string [lrand <list> ?-norepeat?]

Returns a random element from a well-formed Tcl list

If -norepeat is used then the same indexed element will not be returned 
more than once in succession. i.e. unless the list comprises a single 
element then a different element will be returned at each call.

[lrand] is concise and avoids the use of a complex and syntactically
error-prone compound statements

Example:

    puts [lrand $qbf]                                                           

Result:

    fox       # (probably)                                                      

See also: list, lrange, rnd, rndseq, randomstr

lrange

    list [lrange <list> start finish|end|end-N]

[lrange] returns a range of list elements from the given list.
The index values are 'base 0' values where 0 is the first element

The end value may be specified using either 'end' for the last item
or 'end-N' for the Nth item before the last

To select single elements from a list or sub-list see [lindex]

Example:

    puts [lrange $qbf 1 3]        # 2nd to 4th items                            

Results:

    quick brown fox                                                             

See also: list, lindex, list commands, lpush, lpop

lremove (command)

    list [lremove list item ?-nocase? ?-all?]
    list [lremove varname item -var ?-nocase? ?-all?]

Removes one or more instance(s) of a single item from a well-formed Tcl
list. The list may be passed directly via a literal or dereferenced 
variable or passed as a variable name using the '-var' option

Only complete list items may be removed. [lremove] does not remove
substring values and is not a string-processing command

Unless '-all' is used, only the first matching reference to 'item' will
be removed. If '-all' is used then all matching instances will be removed

The default is to perform a case-sensitive match. The '-nocase' argument
may be used to perform case-insensitive matching

Example:

    set s $qbf                                                                  
    puts [lremove $s fox]                                                       
    puts [lremove $s the -nocase]                                               

Result:

    The quick brown jumps over the lazy dog                                     
    quick brown fox jumps over lazy dog                                         

A simple verison of [lremove] can be implemeted using [lreplace] as
follows ...

    proc lremove {listVariable value {case {-exact}}} {                         
        upvar 1 $listVariable var                                               
        set idx [lsearch $case $var $value]                                     
        set var [lreplace $var $idx $idx]                                       
    }                                                                           

See also: lremove (proc), lreplace, list, lpush, lpop

lreplace

    list [lreplace list <start> <end> ??list-item? ?list-item?...?]

Replace items within a list over a range where <start> and <end> are
integer index values and where 0 refers to the first item of the list
(base 0 index)

[lreplace] returns a new list with zero or more items replaced by those
specified in the argument(s). The supplied list argument is not modified

It is important to note that <start> and <end> refer to a range
Successive optional elements are inserted into the specified range

Example:

    puts [lreplace {a b c d e} 1 1 foo]                                         
    puts [lreplace {a b c d e} 1 3 foo]                                         
    puts [lreplace {a b c d e} 1 1 foo bar moo par]                             
    puts [lreplace {a b c d e} 1 3 foo bar moo par]                             

Result

    a foo c d e                                                                 
    a foo e                                                                     
    a foo bar moo par c d e                                                     
    a foo bar moo par e                                                         

See also: lremove, list, lappend, lassign, lmap, lreverse, lsearch, lrange,
          lpush, lpop

lremove (proc)

Removes one unique item from a well-formed Tcl list

[lremove] can be implemented as the following proc and which includes the
ability to use '-nocase' to perform a case-insensitive replacement

    proc lremove {listVariable value {case {-exact}}} {                         
        upvar 1 $listVariable var                                               
        set idx [lsearch $case $var $value]                                     
        set var [lreplace $var $idx $idx]                                       
    }                                                                           

Note that [lremove] does not yet support negative index values. The 'value'
argument should be tested to ensure that it is non NULL

A proc would be required to remove all instances of a particular item. This
could be achieved by looping and testing the [lsearch] return as follows...

    proc lremove_all {listVariable value {case {-exact}}} {                     
        upvar 1 $listVariable var                                               
        while {>= [set idx [lsearch $case $var $value]] 0} {                    
            set var [lreplace $var $idx $idx]                                   
        }                                                                       
        return $var                                                             
    }                                                                           

The functionality of both could be combined with an '-all' argument

See also: lsearch, lreplace, list, lpush, lpop

lreverse

  lreverse tcl-list

Reverse a Tcl list. The list must be a well-formed Tcl list or simple
space/tab-separated string

Example:

    puts [lreverse $qbf]                                                        

Result:

    dog lazy the over jumps fox brown quick The                                 

Example:

    set q "Now is the {time for all good men} to come to the aid of \           
    {the party}"                                                                
    puts [lreverse $q]                                                          

Result:

    {the party} of aid the to come to {time for all good men} the is the is Now 

See also: list, lindex, lpush, lpop

Tcl Lists

A Tcl list is a sequence of characters, optionally separated by whitespace
and grouped, where necessary by braces. To all intents and purposes a Tcl
list is a human-readable string. Lists get complex when there are nested
lists and nesting where braces protect space-separated lists. Sub-lists
are likewise braced

    "This is a list"                                                            

Placing this as a sub-list we get:

    "This is a list {This is a list}"                                           

As with Lisp, complex "tree-like" hierarchies can be represented using
lists

See also: list, lindex, lassign

load (CLI Command - Loads a file into CLI interpreter memory)

    # Unencrypted file                                                          
    load filename?.tcl? ?targetVarName? ?-password password?                    
                                                                                
    # Encrypted file                                                            
    load filename.tcx ?-password password?                                      
                                                                                
    # Clear a loaded file                                                       
    load -clear                                                                 

Load to the Console and Run
---------------------------
Loads a Tcl file from the Command Line Interpreter (CLI) shell

[load] with no arguments will display information about a loaded script

Use [run] to run the file once loaded

Use [dump] to display non-encrypted (obfuscated) loaded source code to the
console and optionally to a Tcl variable

Use [load -clear] to erase the script buffer (See also: clear)

The [load] command does not clear the user-level variable table or non-
system level consts (See: run, clear)

[load] with a .tcx file and, if necessary a password via -password will
load and decrypt a protected file.

Load to a Variable
------------------
Optionally, a file may be loaded to a variable instead of the CLI. In this
case any loaded program will be cleared and the preprocessed source loaded
to the given variable. If the variable exists it will be overwritten. You 
can then run this source using [source $varname]. The source is not
returned or echoed by the [load] command

Example:

    load test.tcl code                 # Load 'test.tcl to variable 'code'      

    > Loaded test.tcl OK 1420 byte(s)  # CLI acknowledgement                    

    eval $code                         # Runs the script at the CLI             

See also: run, dump, exit, clear, source, clearprocs, call

logo

    logo fore ?back? ?x? ?y?

Draw the Ticol logo

See also: info, console, screen, puts

lmap

    lmap {lambda-expression} list

Iteratively assigns elements of one or more lists to variables, 
executes the lambda-expression argument, and collects the results into
a new list by calling [apply]. The lambda-expression will be an expression
such as:

    {x {expr {$x * $x}}}  # Given x return x * x                                

Which is equivalent to f(x) -> (x * x)

[lmap] applied to an empty list will return an empty list

[lmap] is defined as a native command in Ticol. This can also be emulated
using the following Tcl code and using [apply]. The native command is more
efficient, performance-wise

    proc lmap {lambdaExpression list} {                                         
      set result {}                                                             
      foreach item $list {                                                      
        lappend result [apply $lambdaExpression $item]                          
      }                                                                         
      return $result                                                            
    }                                                                           

Example:

    puts [lmap {x {expr {$x * $x}}} {1 2 3 4 5}]                                

Result:

    1 4 9 16 25                                                                 

Example:

    puts [lmap {x {return [string length $x]:$x}} {a bb ccc dddd}]              

Result:

    1:a 2:bb 3:ccc 4:dddd                                                       

Example:

    lmap {x {expr {$x**2 + 3*$x - 2}}} {-4 -3 -2 -1 0 1 2 3 4}                  

Result:

    2 -2 -4 -4 -2 2 8 16 26                                                     

See:
    https://wiki.tcl-lang.org/13920
    http://www.tcl.tk/man/tcl/TclCmd/lmap.htm

See also: lambda, list, apply

loop (Flow Control)

    loop limit ?increment? {code}                   # Short form
    loop variable start limit ?increment? {code}    # Long form

A flexible and efficient [for] loop. [loop] similar in implementation to
the TclX [loop] construct.

A short form simply iterates for 'limit' times using an optional increment
value. This increment may be negative but negative increments will work
only with negative limit values.

    loop 10 {puts "Hello"}     # Iterates 10 times                              
    loop 10 2 {puts "Hello"}   # Iterates 5 times                               
    loop -10 -2 {puts "Hello"} # Iterates 5 times                               

It is important to understand that the long form of [loop] iterates from 
'start' to 'limit-1' and for a total of 'limit' times (depending in any 
increment value)

e.g. [loop i 0 5] will iterate 5 times through '0 1 2 3 4'

    loop i 0 10 2 {puts "Hello $i"}                                             

    is equivalent to:

    for {set i 0} {< $i 10} {incr i 2} {   # Loop exits when i == 10            
       puts "Hello $i"                                                          
    }                                                                           

[loop] optionally takes 5 argument with an optional 6th argument to
specify an increment. There is no need to brace all but the last argument

[loop] takes a loop variable name as its 2nd argument, which will be
initiated to the value of the 3rd argument. The final 'code' argument
will be evaluated, incrementing the loop variable by 1 unless an increment
value is specified as the optional 5th argument

The increment argument may be positive or negative but not zero. If
argument 3 is higher than argument 4 and no increment argument is given
then an increment of -1 will be used. An increment value of zero will
raise an error.as this would lead to infinite loops 

The [loop] variable will always be the same as the limit variable on exit
but the loop will execute for 'limit-start' times. So 'loop i 0 10'
will execute 10 times, looping from 0 to 9 inclusive the same as [for]

if 'start' is equal to 'limit' then the loop code body is never executed
and 'limit' is returned in the loop variable

[loop] supports [continue], [break] and [return] which behave the same as
with [while] and [for] constructs

[loop] uses signed 64-bit integers as its loop arguments and can thus
loop to INT_MAX (9223372036854775807)

Example:

    # Automatic use of +1 increment                                             
    loop i 0 5 {                                                                
       puts "i is $i"                                                           
    }                                                                           
    puts "Final i is $i"                                                        
                                                                                
    # Automatic use of -1 increment                                             
    loop i 5 0 {                                                                
       puts "i is $i"                                                           
    }                                                                           
    puts "Final i is $i"                                                        

Results:

    i is 0                                                                      
    i is 1                                                                      
    i is 2                                                                      
    i is 3                                                                      
    i is 4                                                                      
    Final i is 5                                                                
                                                                                
    and                                                                         
                                                                                
    i is 5                                                                      
    i is 4                                                                      
    i is 3                                                                      
    i is 2                                                                      
    i is 1                                                                      
    Final i is 0                                                                

Example:

    option expression off                                                       
    set i 0                                                                     
    proc foo {} {                                                               
       upvar i                                                                  
       loop i 0 10 1 {                                                          
          puts "i is $i"                                                        
          if {== $i 5} {return $i}                                              
       }                                                                        
    }                                                                           
    foo                                                                         
    puts "i is $i"                                                              

Result:

    i is 5                                                                      

Example:

    option expression off                                                       
    loop i 0 10 1 {                                                             
       if {== $i 5} {                                                           
          # Skip 5 and increment past 6                                         
          ++ i                                                                  
          continue                                                              
       }                                                                        
    }                                                                           
    puts "i is $i"                                                              

Result:

    i is 10                                                                     

See:
    https://osr507doc.xinuos.com/en/man/html.TCL/loop.TCL.html

See also: floop, for, while, do, flow control

floop (floating point loop)

    floop variable float-start float-limit ?float-increment? {code}

Implements simple loop constructs as with [loop] but using floating point 
values. [loop] is 64-bit integer only while [floop], which is slower, allows
double value floating point increments and counters.
 
See [loop] for more details on structure and control.

Note that there is no short-form variant of [floop] as offered by [loop]
 
See also loop, for, while, do, flow control

Listing Files

    See: ls

See also: file

ls
 
    ls <filespec> ?-r? ?-ad | -af | -all? ?-la? ?-list ?var?? ?-array var?

List files This is a partial implementation

    -r           Recursive search (includes subfolders)                         
    -ad | -af    Attributes: directory or file                                  
    -all         List all files                                                 
    -la          List all file details                                          
    -lc          Force lower case path names                                    
    -path        Return full path (default is filename only)                    
                 Paths are returned with Unix style '/' separators              
    -list ?var?  Return in list format (variable not mandatory)                 
    -array var   Return the list as an array (variable is mandatory)            
    var          Storage variable is optional (for either -array or -list)      

If var is given then a filespec must also be given
Ordering/sorting is not supported. You can sort the result from -list

Perform a Windows-style directory listing. The result can optionally be
fed into a variable and optionally in Tcl list format using the -list arg

If [option echo] is set to OFF then [puts] or [printf] must be used to
display the results

Forward-slash and backslash path separators are supported

CAUTION: The default is not to return a Tcl list. Use -list to return one
CAUTION: Large folders or whole disk searches have the potential to create
         huge lists which may exhaust available memory. Lists are slow and
         inefficient. Use arrays for potentially large searches

Example:

    option echo off                                                             
    ls *.txt -la -list a               # Return list in var 'a'                 
    foreach {q} [lsort $a] {           # For each file row entry                
       foreach {i j k l} $q {          # Iterators must match return fields     
          puts "'$i' '$j' '$k' '$l'"                                            
       }                                                                        
    }                                                                           

Example:

    option echo on                                                              
    ls c:/temp/newdir                                                           
    ls "c:/temp/newdir/*.tcl"                                                   

Example:

    option echo on                                                              
    ls *.tcl -array a                                                           
    loop i 0 [array size a] {                                                   
       puts $a($i)                                                              
    }                                                                           

Note: where the external Windows DIR command is used this requires backslash
syntax and does not support Unix/Tcl style forward slash path separators

See also: unlink, mkdir, mkdirs, chdir, getcwd

lsearch

    integer [lsearch ?option ...? list pattern]

Where ?options? are implemented as follows:

    -glob        Default. Use a glob style match v's -exact (see: glob)         
    -exact       Make an exact match (case-sensitive) v's -glob matching        
    -nocase      Make a case-insensitive comparison                             
    -inline      Return the value rather than the list index position           
    -start N     Start processing at list index item offset N                   
                 An array base 0 value corresponding to [string index]          
                 where 0 is the first item in the list, 1 the 2nd etc...        
                 This offset will apply to the pre-sorted input list            
                 Values <0 are ignored and 0 assumed                            
    -not         Invert the search. Return items which don't match 'list'       
                 Typically this will return a well-formed Tcl list              
    -all         Return a list of all items matching the search pattern         
                 of the first item. Output may be modified by -inline           
    -sort        Sort the input list before comparing (non-standard)            
    -sorted      Ignored. Ticol does not optimise sorted list searches          
    -increasing  Ignored. Used with -sort only - otherwise ignored              
    -decreasing  Ignored. Used with sort only - otherwise ignored               
    -ascii       Ignored. Ticol is ANSI-only                                    

[lsearch] matches the pattern argument to the list to find a match. If no
match is found then -1 is returned. Nested lists are not supported.

If all matching style options are omitted then the default matching style is
-glob. If more than one matching style is specified, the last matching style
given takes precedence

Sub-list matching is not yet supported

Examples:

lsearch {ba ze fi eo cu pa le ri} fred                  # => -1                 
lsearch {ba ze fi eo cu pa le ri} *                     # => ba                 
lsearch -inline {ba ze fi eo cu pa le ri} *             # => 0                  
                                                                                
lsearch -not {ba ze fi eo cu pa le ri} *                # => -1                 
lsearch -all -not {ba ze fi eo cu pa le ri} *           # => {}                 
lsearch -inline -all -not {ba ze fi eo cu pa le ri} *   # => {}                 
                                                                                
lsearch {ba ze fi eo cu pa le ri} ?o                    # => 3                  
lsearch -inline {ba ze fi eo cu pa le ri} ?o            # => eo                 
                                                                                
lsearch -inline -all {ba ze fi eo cu pa le ri} ?e       # => ze le              
lsearch -inline -glob -all {ba ze fi eo cu pa le ri} ?e # => ze le              
lsearch -inline -all -not {ba ze fi eo cu pa le ri} ?e  # => ba fi eo cu pa ri  
lsearch -all -not {ba ze fi eo cu pa le ri} ?e          # => 0 2 3 4 5 7        
lsearch -glob -all -not {ba ze fi eo cu pa le ri} ?e    # => 0 2 3 4 5 7        
                                                                                
lsearch -exact -nocase {{a b c} {d e f}} {D E F}        # =>  1                 
lsearch -exact -nocase {{a b c} {d e f}} {p q r}        # => -1 # Not found/-1  
lsearch -exact -sorted -inline -start 4 {b z f e c p l r} {p} # => p            
lsearch -exact -sorted -start 4 {b z f e c p l r} {p}   # => 2                  
lsearch -inline -list -exact\                                                   
            -not -start 0 {a "Hi there" x y z} {x}   # => "a {Hi there} y z"    
                                                                                
lsearch -exact -inline -all -not {a c b c a b c} c      # => a b a b            
lsearch -exact -all -not {a c b c a b c} c              # => 0 2 4 5            
lsearch -exact -inline -all {a c b c a b c} c           # => c c c              
lsearch -exact -all {a c b c a b c} c                   # => 1 3 6              
lsearch -exact -all -start 2 {a c b c a b c} c          # => 3 6                

Note that [lsearch] does not 'eval' its arguments...

lsearch -all -inline {$qbf} *                    # => $qbf      (correct)       
lsearch -all -inline {list $qbf} *               # => list $qbf (correct)       
lsearch -all -inline "$qbf" *                    # => quick brown ...           
lsearch -all -nocase -inline $qbf t*             # => The the                   

See also: list, lappend, lindex

lset

    list [lset varname list]
    list [lset varname {} list]
    list [lset varname ?index|end?-N?|{}? replacelist]

Set a list element in a list variable. [lset] takes a string variable
name and assigns the Nth element of that list with a value passed in the
'replacelist' argument. A list is returned

It is permissible for the string variable to be empty or not exist and
the variable will be created if it does not already exist
A replacement list is mandatory even if this is a single empty element 
If the variable exists and it is of string type it will be overwritten
If the variable exists and it is not of string type (e.g. struct or stack)
then an error will occur and execution will halt unless [try/catch] is used
The element index list (3rd+ argument) need not be braced

Each list element must be >= 0 and < the number of elements in each 
addressed sublist. e.g. if a sublist has 3 elements {a b c} then 
valid element addresses range from 0 to 2.

[lset] takes either 3 or 4 or more arguments

Nested lists of unlimited depth (other than memory) are supported

    lset x {j k l}       # Set x with list value {j k l}                        
    lset x {} {j k l}    # Set x with list value {j k l}                        
    lset x 0 j           # Set x list element 0 with value j                    
    lset x 2 j           # Set x list element 2 with value j                    
    lset x end j         # Set x last list element with value j                 
    lset x end-1 j       # Set x last but one list element with value j         
    lset x {2 1} j       # Set x nested list element 2,1 with value j           
    lset x 2 1 j         # As above but with unbraced element list              
    lset x {2 3} j       # Set x nested list element 2,3 with value j           

Examples:                                                                       

    set x [list [list a b c] [list d e f] [list g h i]]                         
    puts [lset x end-1 j]                                                       
    set x [list [list a b c] [list d e f] [list g h i]]                         
    puts [lset x 2 1 j]                                                         
    set x {0 {0 {0 {0 1 2 3 4 5}} 1 2 3} 1 2 3}                                 
    puts [lset x {1 1 1 3} 23]                                                  

Results:

    {a b c} j {g h i}                                                           
    {a b c} {d e f} {g j i}                                                     
    0 {0 {0 {0 1 2 23 4 5}} 1 2 3} 1 2 3                                        

See also: list, lappend, lindex, lsearch, lmap

setl
 
    string [setl target-variable source-variable|source-string]

A Procrustean command. Inserts a source string or variable into the
target, left-aligned. [setl] is based on the BASIC command LSET
Any excess length in the source is stripped to fit.

The standard Tcl [setl list] is not yet implemented

Example:

    set s [makestr 20 *]                                                        
    setl s "hello"                                                              
    puts $s                                                                     

Result:

    hello***************                                                        

Example: (Blank out part of a string)

    set s "0849 666 616"                                                        
    setl s "    "                                                               
    puts "'$s'"                                                                 

Result:

    '     666 616'                                                              

See also: setc, setr, mid, strstr, string

lsort
 
list [lsort list ?-ascending | -descending? ?-nocase? ?-integer? ?-real?]

Sorts a Tcl list in ascending or descending order, optionally ignoring case,
comparing as integer, or comparing as a double value

lsort does not support -step. -index, -stride etc.
Braced list sub-items are supported

lsort will perform a simple sort on a list. e.g.

    foreach x [lsort [array get env]] {puts $x}

Example:

   set ls [lsort  {                                                             
                                                                                
      {{d e m o} 34512}                                                         
      {{c o d e} 54321}                                                         
      {{v e r y} 13254}                                                         
      {{b i g} 12345}                                                           
      {}                                                                        
   } ]                                                                          
                                                                                
   puts "=$ls="                                                                 

Result:

   ={{b i g} 12345} {{c o d e} 54321} {{d e m o} 34512} {{v e r y} 13254}=      

See also: list

makestr
 
    string [makestr length ?char? ?var?]
    string [makestr length -- var]

Creates and allocated a string of given length, optionally filled with a
specified character. If more than one fill character is passed then only
the first character of the fill-string will be used. The default fill
character is a space. Arguments are positional

Don't mix [makestr] with [malloc] for standard Tcl variables. Variables 
created with [makestr] are standard Ticol variables and will be
automatically released

Literal numeric fill characters will be used if passed:

   e.g. [makestr 5 0] => "00000"

Low and high order ASCII characters may be passed using [chr N] as an
argument

Examples:

    makestr 5 [chr 0x21]       # => "!!!!!"                                     
    makestr 5 [chr 42]         # => "*****"                                     
    makestr 5 [char 0b101011]  # => "+++++"                                     

[makestr] does not accept the value [chr 0]
Ticol Tcl does not like NULL (0x0) characters within strings. 
Use [memset] to zeroise the contents of a Tcl string buffer if this
is required.

If variable is specified and the default filler-character of a single space
is required either the dummy argument '--' should be used to skip at this 
position or a space " " should be explicitly specified

Example:

    set buf [makestr 10]                                                        
    puts "-'$buf'-"                                                             

Result:

    -'          '-                                                              

Example:

    # Use of -- to ensure default single space is used                          
    makestr 10 -- buf                                                           
    puts "-'$buf'-"                                                             
    # Alternative method, using an explicit space                               
    makestr 10 " " buf                                                          
    puts "-'$buf'-"                                                             

Result:

    -'          '-                                                              
    -'          '-                                                              

Example:

    set buf [makestr 1024]   # Allocate a string buffer of 1024 spaces          
    memset buf [chr 0] 1024  # Set all characters to [chr 0]                    

See also: chr, asc, memset, calldll

malloc, free

    address [malloc byte-count]
    free address
    free variable -var

[malloc] is provides for integration with external DLLs using [calldll], 
in particular, with [calldllvariant]. The command [calldllvariant] within
plugin ticol_calldll returns a VARIANT which must be releasd using [free]

[malloc] allocates memory using the C malloc() function from the heap
memory manager. An address to a heap-allocated block of memory is returned
This address is freed by a call to [free] using the same address.

The address argument or variable value must be > 0

[malloc] and [free] should be used with extreme care and are intended for
use only with Ticol structs and for interfacing with [calldll]

CAUTION
-------
You are responsible for the safe management of allocated memory blocks

Don't use [malloc] for standard Tcl variables. Used correctly, [malloc]
will be stable and not leak memory. Memory blocks allocated with [malloc]
are NOT automatically freed. You must call [free] on the address returned
by [malloc]. Do not attempt to [free] an address which has not been 
allocated via [malloc]

Value Zeroed After Free
-----------------------
If [free] is passed the name of a variable then the variable will be reset
to "0" (zero) on successful free to prevent the address being re-used. 
In other cases you must take care to set malloced addresses to zero to
as appropriate in order to prevent re-use after [free]

Example:

    set ptr [malloc 10]                                                         
    puts "malloc ptr is $ptr"                                                   
    set source Hello                                                            
    # MUST copy length of string + 1 to copy the NULL char                      
    memcpy $ptr [addressofb source] 6                                           
    puts "ofaddressb ptr =='[ofaddressb $ptr]'"                                 
    free $ptr                                                                   

Result:

    malloc ptr is 33394064                                                      
    ofaddressb ptr =='Hello'                                                    

See also: memcpy, memset, calldll, addressofb, ofaddressb

man - Ticol Manual

    .key
    ..control-code
    .. comment

The Ticol manual is a simple text file which is formatted using a method
which can break the file into easily-accessible sections. Topic key markers
are phrases which must be prefixed by a dot character and the dot must be
the first character on a line. Control codes and comments are prefixed
with two dots.

The format is as follows

    ---------------------------------------------                               
    .. comment and end of section marker                                        
    .topic index key                                                            
    .another optional key for this topic                                        
    .yet another optional key for the same tipic                                
                                                                                
    topic title 

    Manual text...
                                                                                
    ..code                                                                      
                                                                                
    <some Tcl code>
                                                                                
    ..end                                                                       

                                                                                
    ..colour                                                                    
                                                                                
    <some text
                                                                                
    ..end                                                                       
    ..end  (of topic section)                                                   
    ---------------------------------------------                               

Any text following ".." will be ignored

See also: ticol

map, string map
 
    string [map {token-pair-list} variable|string-literal ?-nocase?]
    string [string map {token-pair-list} variable|string-literal ?-nocase?]

Remaps strings using a paired list. The list-pair must have even parity.
If a variable is given for the last argument then the variable contents
will be reassigned. In either case the remapped string is returned.
If there are no valid mappings then the original input string is returned

Mapping is expensive on long strings where no multi-char remaps are found
and where a large input-map is given

Also implemented as [string map]

Examples:

    puts "Result is: [string map {abc 1 ab 2 a 3 1 0} 1abcaababcabababc]"       
    puts "Should be: 01321221"                                                  
                                                                                
    puts "Result is: [string map {1 0 ab 2 a 3 abc 1} 1abcaababcabababc]"       
    puts "Should be: 02c322c222c"                                               
                                                                                
    puts "Result is: [string map {1 0 ab 2 a 3 abc 1} frederick]"               
    puts "Should be: frederick"  # No valid mapping returns original string     
                                                                                
    set s "1&lt;2 2&gt;1 2&ge;2 2&le;2"                                         
    puts "Result is: "[string map -nocase {                                     
      "&lt;"      "<"                                                           
      "&gt;"      ">"                                                           
      "&le;"      "<="                                                          
      "&ge;"      ">="                                                          
    } $s]                                                                       
    puts "Should be: 1<2 2>1 2>=2 2<=2"                                         

See also: map, apply, string, filter

max, min (Command form)
 
    number [max x y ...]
    number [min x y ...]

Return the maxima or minima of two numeric values, either double or
integer. String arguments are not accepted. Either a double or integer
value is returned, depending on the inputs

max() and min() are also available as expression functions. You can also
access these in command-style using [funct max x y] and [funct min x y]

Example:

    puts [max 3 7]                                                              
    puts [max 3.0 7.0]                                                          
    puts [max foo bar]    # Invalid string literals                             
    puts [max $pi 2.0]                                                          

Results:

    7                                                                           
    7.000000000000000                                                           
    0                                                                           
    3.141592653589793                                                           

See also: max function, min function, functions, math

mid
 
    integer [mid varName string|var startpos ?length? ?-var?]

Set the middle of a variable to a portion of a 2nd variable or string literal

If the length parameter is omitted then the entire source string will be
copied to the target (or as much as will fit)

[mid] is not a standard Tcl command and only operates on variables, not strings

Example:

    set q $qbf                                                                  
    puts [mid q cat 17 3]                                                       
    puts $q                                                                     

Results:

    3                                                                           
    The quick brown cat jumps over the lazy dog                                 

See also: mids, left, right, setr, setl, strstr, string

mids (Return a String Midsection)
 
    string [mids string start ?length?]
    string [mids var start ?length? -var]
    string [string mids string start ?length?]
    string [string mids var start ?length? -var]

Return a mid-section of a string

The 'start' position argument is an array 'base 1' index value

The length argument is optional and if omitted then the entire string
from 'start' will be returned

If the 'start' argument is <= 0 then a start position of 1 is assumed and
the whole string will be processed

If the 'start' argument is greater than the string then an empty string,
as "" will be returned.

If -var is passed as the last argument then the value argument will be
interpreted as a variable name instead of a string. The variable must exist

Use [index <string> n] instead of [mids <string> n 1]

[mids] is not a standard Tcl command

Example:

    if {eq [mids $data $i 2] "/*" } {                                           
       incr i 2                                                                 
       set in_comment $true                                                     
    }                                                                           

Example:

    puts [mids "hello world" 7]                                                 

Result:

    world                                                                       

Example:

    puts [mids "hello world" 2 4]                                               

Result:

    ello                                                                        

Example:

    set s "Hello"                                                               
    puts [mids $s 3 2]                                                          

Result:

    ll                                                                          

See also: mid, left, right, setc, setr, setl, strstr, string, index,
          lastchar, strto, strtor, filter

mkdir
 
    bool [mkdir path]
    bool [md    path]

Create a single directory (folder)

Example:

    mkdir foldername                                                            

See: [mkdirs] to create a series of folder names in one command

See also: unlink, file, ls

mkdirs
 
    bool [mkdirs path]

Create a series of directories (folders)
[mkdirs] is not a standard Tcl command

Example:

    mkdirs \\one\two\\three                                                     

See: [mkdir] to create a single folder

See also: unlink, file, ls

mktemp

    string [mktemp ?typ? ?path?]

Returns a temporary filename, optionally specifying the filetype (default 
is TMP) and the path (default is the current path)

If the path does not exist an attempt will be made to create it

Example:

    puts [mktemp]                                                               
    puts [mktemp {} c:/temp]                                                    

md5
 
    string [md5 string|var ?-var?]

Return the 32-character MD5 value of a string
[md5] is not a standard Tcl command

The -var option is binary-compatible

Example:

    puts [md5 $qbf]                                                             
    puts [md5 qbf -var]                                                         

Result:

    9e107d9d372bb6826bd81d3542a419d6                                            
    9e107d9d372bb6826bd81d3542a419d6                                            

Example:   MD5 hash-based random number generator

 set seed $pi                               # Could instead use [clock]         
 set counter 0                              # Global/static counter             
 option expression on                       # Use [expr] for flow-control       
                                                                                
 proc hashrnd {lo hi} {                     # Hash-generated random number      
     upvar seed                             # Not recursive. No level given     
     upvar counter                                                              
     incr counter                                                               
     set r [md5 [+ $counter $seed]]         # Get an MD5 value                  
     set q "0x"                             # Literal 0x prefix                 
     append q [mids r [% $counter 24] 8]    # Slice a section of the MD5        
     return [expr "$q % (($hi-$lo)+1)+$lo"] # Evaluate as hexadecimal           
 }                                                                              
                                                                                
 option expression on                                                           
 for {set i 0} { $i < 10000} {incr i} {                                         
     puts "hashrnd 10..10000:\t[hashrnd 10 10000]"                              
 }                                                                              

See also: md5 batch example, md5_file, protection, MD5 Verification, 
          upvar, append

MD5 Batch Example

This is a simple batch file which will run a Ticol script depending on a
successful MD5 check. You will need to know the MD5 signature in advance and 
if the signature changed then you would need to update the script

    @echo OFF                                                                   
    set FILE=hello.tcx                                                          
    set MD5=C7A37A87B4E6B7D311C0BF430844B94A                                    
                                                                                
    for /F "tokens=*" %%i in ('ticol.exe /md5 %FILE%') do (                     
       set RESULT=%%i                                                           
    )                                                                           
                                                                                
    REM ECHO DEBUG: MD5 result is: %RESULT%                                     
                                                                                
    if /I %MD5% equ %RESULT% (                                                  
       hello.tcx                                                                
    ) else (                                                                    
       echo MD5 failure. Cannot execute %FILE%                                  
       echo MD5 is:                                                             
       ticol.exe /md5:%FILE% /crlf                                              
       echo Should be:                                                          
       echo %MD5%                                                               
    )                                                                           

See also: md5

md5_file
 
    string [md5_file filename]

Return a 32-bit MD5 hex-value for a given filename. Performance is
reasonable even on large ISO images. 

This should match the MD5 of any external MD5 EXE program

Example:

    puts [md5_file ticol.exe]                                                   

Result:

    82bef32153657dc25dc439a3b500a614                                            

See also: md5, protection, MD5 Verification

MD5 Verification
 
The following skeleton batch file may be used to generate a mechanism to
test encrypted Ticol TCX files before execution. This method requires that
the MD5 value be entered into the batch script

    REM ----------------------------------------------------------              
    @echo OFF                                                                   
                                                                                
    set FILE=hello.tcx                                                          
    for /F "tokens=*" %%i in ('ticol.exe /md5 %FILE%') do (                     
          set RESULT=%%i                                                        
    )                                                                           
                                                                                
    REM ECHO DEBUG: MD5 result is: %RESULT%                                     
                                                                                
    if /I 314ebb9f13a413ab4419fc9d5691011d equ %RESULT% (                       
          ticol.exe %FILE%                                                      
    ) else (                                                                    
          echo MD5 failure. Cannot execute %FILE%                               
    )                                                                           
    REM ----------------------------------------------------------              

See also: md5, md5_file, security issues

mean, median, mode

    double [mean value ?value? ...]
    double [median value ?value?...]
    double [mode value ?value?...]

[mean] calculates the arithmetic mean of a series of values and return the
result as a floating point double value. Individual values and the total are
evaluated as floating point doubles with a double return

[median] returns the 'middle' value of the sorted list of numbers after
sorting into ascending order. If 1 value is supplied, this is returned, if
2 are supplied then the lower (floor) number is returned.

[mode] returns the most frequent number. If no number is repeated more
than once then no mode value exists and zero is returned.

(double 64 bit signed   1.7E +/- 308 (15 digits of precision))

Example:

    puts [mean 1.2 19.8 22.7 4343 40.5 19.4 222]                                
    puts [median 3.4 5.4 1.2 2.2 5.1]                                           

Result:

    666.942857142857060                                                         
    3.4                                                                         

[mean] expects a series of arguments not a single Tcl list but e can expand
using the {*} operator to enable list handling

Example:

    set l "234.34 23.11 37.333 3828.22"                                         
    puts [mean {*}$l]                                                           

Results:

    1030.750749999999900                                                        

[mean] is not a standard Tcl command

See also: math functions, big mean

mem_used
 
    integer [mem_used ?pid?]

Show the memory used by the current Ticol process or any other process in
bytes. This corresponds to the data 'working set' in Windows Task Manager
not to 'memory (private)'.

This will be the same amount of memory as displayed in Task manager for 
"mem usage". This can be used to track application memory use for fault-
finding etc. There should be initial memory allocation as routines are 
set-up, but this should not continually increase. The underlying memory-
manager may also fluctuate but the usage trend should remain broadly
stable

Error returns:

On error mem_used returns a negative value indicating one of the following:

    -1   Can't load PSPAPI.DLL                                                  
    -2   Can't get procaddress for "GetProcessMemoryInfo"                       
    -3   Can't open given Process ID (usually insufficient permissions)         

Example:

    set memstart [mem_used]                                                     
    set last 0                                                                  
    set this 0                                                                  
    set iters 0                                                                 
    while {1} {                                                                 
       # command to check ########################                              
       some-valid-tcl-function                                                  
       ###########################################                              
       set this [mem_used]                                                      
       if {!= $last $this} {                                                    
       # Next line ends with a line-continuation character...                   
          puts "Leaked [/ [- $this $memstart] 1024] Kb\tTotal used: \           
       [comma [/ $this 1024]] Kb\t$iters iteration(s)"                          
       }                                                                        
       set last $this                                                           
       incr iters                                                               
    }                                                                           

[mem_used] is not a standard Tcl command

See also: Ticol, debugging

memcpy

    memcpy target-address source-address byte-count ?-t?

[memcpy] calls the standard "C" memcpy() routine to copy a block of
memory from one location to another.

[memcpy] can take a dereferenced struct member name as the target, or
an address of a block of memory allocated by [malloc]. The copied block
may not be null-terminated and therefore may not be safely printable.

[memcpy], [malloc] and [free] should be used with extreme care and are
intended for use only with Ticol structs and for interfacing with [calldll]

[addressofb] can be used to return the binary/byte address of the allocated
buffer in a Ticol variable (similar to StrPtr() in Visual BASIC)

[ofaddresb] is used to dereference a Ticol variable which stores a memory
address

Example:

    set ptr [malloc 10]                                                         
    puts "malloc ptr is $ptr"                                                   
    set source Hello                                                            
    memcpy $ptr [addressofb source] 6                                           
    # MUST copy length of string + 1 to copy the NULL char                      
    puts "ofaddressb ptr =='[ofaddressb $ptr]'"                                 
    free $ptr                                                                   

Result:

    malloc ptr is 33394064                                                      
    ofaddressb ptr =='Hello'                                                    

If the -t argument is used then a null terminator will be substituted
for the last byte copied. Thus x1 byte of the source is discarded

Example:

    set a [malloc 256]             # Allocate a large buffer                    
    set s "hello"                  # Set a standard Tcl variable                
    memcpy $a [addressofb s] 5 -t  # Copy 5 bytes. Make the last byte NULL      
    puts "a is '[ofaddressb $a]'"  # Print the binary data at address a         

Result:

    a is 'hell'                                                                 

[memcpy] is not a standard Tcl command and should be used with extreme
care

See also: malloc, free, memset

memset

    memset varName character length

[memset] may be used to format or zeroise buffers such as those created
using [makestr] when interfacing with external DLLs using calldll*

[set] will copy only the usable part of a string, not the full, allocated
buffer size. Thus using [set] with [makestr] and [chr 0] could create an 
under-sized target buffer string and cause instability with [calldll]

[memset] will clear memory only up to the allocated length of the string
it will not clear/set the internal/terminating null character of any string

Don't use [memset] with [makestr] as the latter is a Tcl command, not 
a heap memory command.

It is allowable to call memset with a calculated address offset

Avoid using memset with Tcl variables other than those used via calldll*
A record should be kept of the return address and that address manipulated
rather than passing data into a Ticol variable object.

Example:

    set buf [makestr 1024 " "]  # Creates a buffer of 1024 spaces               
    memset buf [chr 0] 1024                                                     
    puts "'$buf'"                                                               

Result

    '' # Buffer has been allocated to 1024 chars but set to [chr 0]             

Example:

    set ptr [malloc 200]     # [malloc] returns an address                      
    memset $ptr x 10         # Write 10 char "x"                                
    memset [+ $ptr 5] "q" 2  # Write "qq" to offset ptr + 5                     
    puts "Address is: $ptr"                                                     
    puts "Contents:   '[ofaddressb $ptr]'"                                      

Result:

    Address is: 34791712                                                        
    Contents:   'xxxxxqqxxx'                                                    

[memset] is not a standard Tcl command and should be used with extreme
care

See also: calldll, malloc, free, memcpy, makestr

Troubleshooting and Common Mistakes

Here are some of the most common mistakes when first using Tcl.
Particularly when coming from a C/C++ background:

No Text Output
Command Switch Arguments are Case Sensitive
Using Double Quotes Where Braces Are Preferable
Mismatched Square Brackets in Quoted Strings
Failure to Spot an Error Condition During Debugging
Line Continuation \ Is Not The Same As A Multiline String
Accidental Misuse of # (Hash) Comments With Multiline Strings
Accidental Misuse of # (Hash) Comments With Macros
Very Long Strings in Notepad++
Using [unset] instead of [undef]
Failure to Use [option expression off]
Using 'VB' Style Variable Syntax
Failure to Use [upvar] for [proc] Arguments
Invalid Variable Names
Variable Name Clash Within Procedures when Using [upvar]
Typo Leads to Infinite Loop
Storing [expr] Commands in Strings
Lapsing into [expr] Format Within Flow Control Statements
Failure to clear/delete variables or procs
Failure to Evaluate [proc] Returns
Using [static] where [set] is required
Handling Sparse Integer Indexed Arrays
Incorrect Options Setting with [for] etc.
Can't Use Tcl Variables for [proc] Default Arg Values
Erroneously Declaring a Global as a Local Var
Incorrect [option expression] Setting
Mis-handling of Number Range
Misplaced End of Line Brace Characters
Failure to Separate Commands by Whitespace
Failure to Protect Strings With Double Quotes
Misplaced Double Quotes
Incorrect Delimiting of Command Arguments
Using [else if] Instead of [elseif]
Using [then] Within [if]
Finding Bad PreProcessor Expressions
Malformed CRLFs in Files
Junk Characters
Windows Command Line Breaks Direct Commands
Memory Use
Big Number Library Returns Unexpected Results

No Text Output
--------------
Problem: No output or activity past a certain point in the script
Fix:     Check for a malformed string and malformed escape sequences with
         an accidental string overrun to the end of the file

Find the halt point in the script using line traces to locate the 
statement where the problem occurs and isolate the malformed statement

Try to simplify the string and avoid an excess of \ escape sequences
or embedded double-quotes as it is easy to lose track. Use minimal quoting
style and use braces instead where possible. Break down complex strings
into simpler tasks with smaller strings then reassemble.

Don't embed '#' comment characters within braced strings, and if embedded
within double-quoted strings, be sure to escape using '\#'

Command Switch Arguments are Case Sensitive
-------------------------------------------
Where command options are available, unless otherwise specified, these 
must be passed in lower case. e.g. pause Hello -quiet

Using Double Quotes Where Braces Are Preferable
-----------------------------------------------
Tcl does not support nested quoted strings (nested double quotes)
and since braces perform the same task it is often preferable for
braces to be used unless string substitution or immediate command
execution is required

Note, however, that some escapes such as \# are valid only within quoted
strings. These should be avoided if possible

Forgetting to Include Required Whitespace Between Words
-------------------------------------------------------
Because Tcl consists of a series of commands which are delimited by
<whitespace>[command-name]<whitespace>, if this whitespace is
inadvertently omitted then a syntax error will result. What would
be distinct operators in other languages are commands in Tcl...

Example:

                             # [!] is a command not an operator                 
    if{![expr 2+2]} {...     # C++ like syntax is wrong                         
    if{! [expr 2+2]} {...    # Whitespace is required after '!'                 

Mismatched Square Brackets in Quoted Strings
--------------------------------------------
Square bracket parity is checked outside of quoted strings by the
macro preprocessor (MPP) but not within quoted strings. Since Tcl
allows quoted strings to contain commands which will be evaluated
it is important to check for mismatched [] brackets if anomalous
behaviour occurs.

Failure to Spot an Error Condition During Debugging
---------------------------------------------------
You may not want console error messages displayed and may prefer to 
handle these yourself and display your own. However, if you disable
console echo of fatal errors using [option echo off] you risk failing
to spot the cause of a fatal error and this could be difficult to 
debug. 

If in doubt, always ensure that [option echo on] overrides as close
to the location of any potential fault during debugging. This is 
particularly the case if you spawn external script files using [source]
and which may disable console echo.

Ideally, child scripts should save the current echo setting using 
[option push] and restore it on exit using [option pop]
e.g. [option echo push on], [option echo pop]

Example:

    # Here a fatal error triggers the premature exit of [time] with no          
    # indication of why. 'child_script.tcl' inadvertently unsets                
    # variable 'i', and also turns echo off, thus '++ i' fails                  
    # The only indication is given by the error code from [time -info]          
    set i 0                                                                     
    time [                                                                      
       puts "Loop $i"                                                           
       source child_script.tcl                                                  
       # option echo on # This is required to spot the problem                  
       ++ i                                                                     
    ] 100000 -info                                                              

Line Continuation \ Is Not The Same As A Multiline String
---------------------------------------------------------
Line-continuation characters at the end of a quoted string will cause the
lines to merge and behave as a single line. Where a quoted string is 
defined over several lines without continuation characters, this string
will retain CRLF line endings after each line.

    # String will be "The quick brown fox"                                      
    set s "The \                                                                
    quick \                                                                     
    brown \                                                                     
    fox"                                                                        

Is not the same as 

    # String will be "The \r\nquick\r\n brown\r\n fox"                          
    set s "The                                                                  
    quick                                                                       
    brown                                                                       
    fox"                                                                        

The first example will result in a single line, the second, multiple lines.

Accidental Misuse of # (Hash) Comments With Multiline Strings
-------------------------------------------------------------
Hash characters are not interpreted within quoted strings. Thus a #
comment can't be included at the end of any line within a multiline
string definition. For example, the comments below are invalid:

    set s "The                                                                  
     quick brown            # Foxes aren't that quick                           
     fox jumps                                                                  
     over the                                                                   
     lazy dog"                                                                  

                                                                                
    set s "The\                                                                 
     quick brown\           # Foxes aren't that quick                           
     fox jumps\                                                                 
     over the\                                                                  
     lazy dog"                                                                  

Accidental Misuse of # (Hash) Comments With Macros
--------------------------------------------------
It is good practice to insert a space after a # character. This prevents
inadvertently calling a macro command. For example. If one intended to
comment-out the 'echo' command by prefixing with a # then the macro
preprocessor version would be called. With CGI applications this would
trigger text output before headers were sent and generate a 500 server
error.

Examples:

    #echo "Some text"           # This calls the macro #echo command            
    # echo "Some text"          # Inserting a space is recommended, thus        
    #if                         # This will execute the MPP '#if' clause        
    #else                       # Dangerous for the same reason                 
    #exit                       # Will cause the MPP to exit immediately        

These are examples of a dangerous comments because '#if' and '#else' are 
reserved Macro Preprocessor keywords. Commenting out a Tcl [if] or [else]
statement will create a valid Macro directive

Very Long Strings in Notepad++
------------------------------
It has been noted that some versions of NPP may automatically wrap and
insert CRLFs into very long strings, e.g. very large numbers of say
1000+ digits

The solution to this is to break the string into smaller pieces, wrap in
double quotes and use line-continuation backslash characters at the end
of each intermediate line. These will then be correctly handled by the 
Macro PreProcessor

Example:

assert [big fact 200] {== $_ "7886578673647905035523632139321850622951\         
3597768717326329474253324435944996340334292030428401198462390417721213\         
8919638830257642790242637105061926624952829931113462857270763317237396\         
9889439224456214516642402540332918641312274282948532775242424075739032\         
4032125740557956866022603190417032406235170085879617892222278962370389\         
7374720000000000000000000000000000000000000000000000000"} -v                    

Using [unset] instead of [undef]
--------------------------------
Intending to clear a proc (e.g. foo) but using 'unset foo' where
'undef foo' is required, or intending to clear var foo using 
'undef foo' where 'unset foo' is required

Failure to Use [option expression off]
--------------------------------------
When using non-expression (Tcl command format) arguments to flow-control
The preference can be set up in ticol.ini

Using 'VB' Style Variable Syntax
--------------------------------
Using var$ instead of $var. Tcl requires the latter

Example:

    set i 0                                                                     
    for {[> i$ 10]} { ...   # Wrong                                             
                                                                                
    set i 0                                                                     
    for {[> $i 10]} { ...   # Correct                                           

Failure to Use [upvar] for [proc] Arguments
-------------------------------------------
When passing by name (by reference) rather than by value, you must use
[upvar] to create a reference to the variable. If a [proc] is called by
another [proc] then [upvar] giving no level reference is sufficient as
each [proc] call is one level higher than the next.

Example:

    proc foo x {           # Wrong. Variable x won't be referenced              
       return [* $x 1]                                                          
    }                                                                           
                                                                                
    proc foo x {           # Correct                                            
       upvar x             # [upvar] creates a linkage to the source var        
       return [* $x 1]                                                          
    }                                                                           
                                                                                
    proc foo x {           # Correct (using an alternate local variable)        
       upvar x y           # $y is an alias for $x                              
       return [* $y 1]                                                          
    }                                                                           

Invalid Variable Names
----------------------
Variable names in Ticol are more restricted than in standard Tcl
The reason for this is to speed-up the interpreter core.

It is is possible to create variable names which can't be dereferenced
using the dollar '$' symbol. In such cases it is usually possible to 
dereference using [set] instead.

The dollar parse routine differs from the string parse routine and
commands which accept strings will tolerate a wider range of characters.
Under normal circumstances it is strongly recommended to adhere to the
restricted range of characters available when choosing variable names.

Example:

    set [chr 0x1e] Hello                                                        
    puts [set [chr 0x1e]]  # Will dereference okay                              
    puts ${[chr 0x1e]}     # Will raise an error                                

Results:

    Hello                                                                       
    # (12400) eval: No variable reference to '{[chr 30]}' at level 0            

Variable Name Clash Within Procedures when Using [upvar]
--------------------------------------------------------
Consider the following proc which will pass its argument by name 
(reference)

    proc foo {a} {  # Variable 'a' is created in [proc] scope                   
       upvar $a     # Attempts to declare a second local var 'a'                
       puts $a                                                                  
    }                                                                           
                                                                                
    set a Hello                                                                 
    foo a           # Calling with 'a' means [proc]->'a' resolves to 'a'        

An error will result, pointing out that variable 'a' already exists in
the current scope This occurs because we passed the name 'a' via argument
'a' which then resolves to 'a' when dereferenced

    upvar: 'a' already exists at level 1                                        

which it does, since it was passed via the proc body. Because you can't be
sure what variable name will be passed into the proc later on you must 
avoid such clashes where the inbound name is the same as the local one

You must either explicitly declare the local variable in the [upvar]
statement, e.g. in this case:   upvar $a b     or 'mangle' the names
using underscores e.g.   

    proc foo {_a} {                                                             
       upvar $_a a   # Declares a local var 'a'                                 
       puts $a                                                                  
    }                                                                           

Alternatively, just ensure the variable names for the local reference
are different from the arguments when calling by name

    proc foo {a} {                                                              
       upvar $a b   # Declares a local var 'b'                                  
       puts $b                                                                  
    }                                                                           

Typo Leads to Infinite Loop
---------------------------

    option expression off                                                       
    for {set i 0} {< $i count} {++ i} {                                         

This should be '$count' not 'count' (a difficult bug to spot)

    option expression off                                                       
    for {set i 0} {< $i $count} {++ i} {                                        

Storing [expr] Commands in Strings
----------------------------------
If storing [expr] or [calc] command sequences, do not also store the
command [expr] within the string. Store only the argument tail unless
you wish to use the {*} expansion operator to resolve:

    # Correct                                                                   
    set a {1+2*3}                                                               
    puts [expr $a]                                                              

                                                                                
    # Wrong                                                                     
    set a {expr {1+2*3}}                                                        
    puts [$a]                                                                   

                                                                                
    # Correct                                                                   
    set a {expr {1+2*3}}                                                        
    puts [{*}$a]                                                                

                                                                                
    # Wrong. [expr] args should be braced but '{*}a$' is a command              
    # The subcommand should be wrapped in square brackets                       
    set a {expr {1+2*3}}                                                        
    puts [expr {{*}$a}]                                                         

                                                                                
    # Correct                                                                   
    set a {expr {1+2*3}}                                                        
    puts [expr [{*}$a]]                                                         

This can be debugged in single-step detail by using [option breakpoint on]
and issuing a [halt] instruction

Lapsing into [expr] Format Within Flow Control Statements
---------------------------------------------------------
                                                                                
    # Wrong '!' is a Tcl command                                                
    if {![info exists env(QUERY_STRING)]} {                                     
       set env(QUERY_STRING) ""                                                 
    }                                                                           

                                                                                
    # Correct (a space separator is inserted)                                   
    if {! [info exists env(QUERY_STRING)]} {                                    
       set env(QUERY_STRING) ""                                                 
    }                                                                           

Failure to clear/delete variables or procs
------------------------------------------
A conflicting proc or variable exists somewhere else in the code, possibly
within an external file. It is good practise to clear procs and variables
as soon as you have finished within them, particularly when working in the
global (root) scope. This can be done safely using:

    undef procname -nocomplain
    unset variablename -nocomplain

[clear] will clear all locally created variables

A useful shortcut scriptlet is as follows: This will remove all single
letter variables 'a' to 'z'. The {*} will cause each list element to be
passed to [unset] as a separate argument:

    unset {*}[split [range a-z]] -nocomplain                                    

Failure to Evaluate [proc] Returns
----------------------------------
Example:

    return * $i $i                                                              

Should be:

    return [* $i $i]                                                            

Reason:

[*] is a command which must be evaluated. The first example, instead,
supplied 3 invalid arguments to the [return] command. 
i.e. [return * $i $i]. 

Square brackets cause an advance recursive evaluation, thus, if i was 
set to 3 then [return [* $i $i]] would be interpreted as [return 9]

Using [static] where [set] is required
----------------------------------------
Static is used for declaration with an optional initialiser
It should not be used for assignment after declaration

  # In root-level scope                                                         
  static foo 23                                                                 
  assert $foo {== $_ 23} -v   # PASS                                            
  static foo 11                                                                 
  assert $foo {== $_ 11} -v   # FAIL                                            
  set foo 11                                                                    
  assert $foo {== $_ 11} -v   # PASS                                            

Handling Sparse Integer Indexed Arrays
--------------------------------------
Tcl arrays are associative rather than sequential binary blocks. They have
no implicit order to them, so iterating over a 'sparse' integer sequence
can be problematic. Use of [foreach] may not deliver the required numeric
sequence or ordering.

[array item] can be used to safely iterate over a desired sequence but 
there may be a computational cost to doing this.

This solution is far from idea. [carray] in the ticol_carray plugin offers 
an alternative to Tcl arrays. It may also be useful to built up an index
to large sparse arrays. "C" arrays may be non-sparse, expanded and kept in 
explicit, sorted order. They may therefore be traversed at very low cost.

Example:

    # Sparse array example                                                      
    # Assuming we don't know the contents of array 'a' in advance and that      
    # the array size could be up to 100,000 elements                            
    set a(1) "Hello "                                                           
    set a(999) "Fine "                                                          
    set a(10000) "World "                                                       
    loop i 0 100000 {                                                           
    set q [array item a $i]                                                     
    if {ne $q ""} {                                                             
        puts $q -nonewline                                                      
    }                                                                           

Incorrect Options Setting with [for] etc.
-----------------------------------------
Example: 

    variable not found after [for] loop initiation

Should be:

    for {[set i 0]} ...                                                         

or

    option expression off                                                       
    for {set i 0} ...                                                           

or:

    TICOL.INI -> ForInitAllowsExpression=FALSE                                  

    for {set i 0} ...                                                           

Reason:

    Standard Tcl forces the first (initiator) statement of a [for] loop
    to be a Tcl command (via [eval]). Ticol Tcl allows the first command
    to be an expression if configured in TICOL.INI using:

        ForInitAllowsExpression=<boolean>

Can't Use Tcl Variables for [proc] Default Arg Values
-----------------------------------------------------
String or numeric literals should be used for default values in [proc]
default argument lists. Such default values should be considered as 
string or numeric constants.

Braces will precent these from being resolved when the proc is defined

Example:

Here the global '$::true' will not be resolved to '1'

    proc foo {{x $::true}} {...                                                 

Instead use:

    proc foo {{x 1}} {...                                                       

A future version may offer a workaround for this problem

Erroneously Declaring a Global as a Local Var
---------------------------------------------
The intent is to declare a global variable or global reference but instead a
local is declared due to the inadvertent omission of the '::' global scope
prefix. Often this will be where the global is 'masked' by a local with the 
same name.

Example:

    # Local 'masks' intended global reference in for loop                       
    option expression off                                                       
    set maximum 10                                                              
    proc foo {} {                                                               
        set maximum 0                                                           
        for {set i 0} {< $i $maximum} {++ i} {                                  
            puts $i                                                             
        }                                                                       
    }                                                                           

Incorrect [option expression] Setting
-------------------------------------
Example:

    option expression on                                                        
    if {file exists test.tcl} { ... }                                           

Should be:

    if {[file exists test_enum.tcx]} {                                          

or

    option expression off                                                       
    if {file exists test.tcl} { ... }                                           

Reason:

If option expression is on, either by command or INI config file, then
the arguments to flow-control commands such as [if], [for] etc. will be
interpreted as math expressions. If this is the case any command must be
evaluate (called) by wrapping in square brackets.

--

Example:

    option expression off                                                       
    for {set i 0} {$i < 10} {incr i} { ...}                                     

Should be:

    option expression off                                                       
    for {set i 0} {< $i 10} {incr i} { ...}                                     

or:

    option expression on                                                        
    for {[set i 0]} {< $i 10} {incr i} { ...}                                   

Reason:

With option expression off, arguments to flow-control commands such as 
[for], [while] etc. will not be evaluated in "expression format" but in
[eval] format. The command expression in the first argument will have no
effect

With 'option expression on' '{set i 0}' is not a valid expression

Mis-handling of Number Range
----------------------------
Ticol natively works with 64-bit signed integers but many commands accept
and return only 32-bit signed integers. Care should be taken to appreciate
the limits of specific commands as stated in their definitions here.

Misplaced End of Line Brace Characters
--------------------------------------
C/C++ and C# programmers may inadvertently assume that the following brace
style can be used with Tcl. It cannot. Opening braces mark delayed
expansion wrappers for commands. CRLFs are valid within delayed brace 
wrappers but not within argument tails outside curly braces

Example: (wrong - opening brace must be on same line)

    if {== $i 10}                                                               
    {                                                                           
       puts "i is 10"                                                           
    }                                                                           
    else                                                                        
    {                                                                           
       puts "i is not 10"                                                       
    }                                                                           

Should be: (correct - opening brace on same line)

    if {== $i 10} {                                                             
       puts "i is 10"                                                           
    } else {                                                                    
       puts "i is not 10"                                                       
    }                                                                           

or 

    if {== $i 10} {puts "i is 10"} else {puts "i is not 10"}                    

Reason. The first expression is "C++"-like but illegal. The CRLF marks the
termination of the command [for] and it is prematurely truncated

Failure to Separate Commands by Whitespace
------------------------------------------
Example:

    option expression on                                                        
    for{set i 0}{$i < 10}{incr i}{ ...}                                         

Should be:

    option expression on                                                        
    for {set i 0} {$i < 10} {incr i} { ...}                                     

Reason:

Tcl is a discrete, command-based language. Spaces are required between 
commands and grouped arguments (those wrapped in braces)

Failure to Protect Strings With Double Quotes
---------------------------------------------
Strings need not be wrapped in double-quotes but failure to do so leaves
a path open for frustrating errors. e.g. 

    puts [ftp $hostname ls /tools/* -u admin -p xxxxx]                          

Should be:

    puts "[ftp $hostname ls /tools/*.zip -u admin -p xxxxx]"                    

Since the unquoted '/*' in '/*.zip' will be interpreted as a long comment
start. Quoting will prevent this.

Misplaced Double Quotes
-----------------------
If an extra, unescaped double quote is embedded in a properly quoted string
then this error may be issued:

    "Premature end preprocessing source file"

Possibly with a preprocessor line number. Work backwards from the line
number to check for strings with embedded/unescaped double quotes

Incorrect Delimiting of Command Arguments
-----------------------------------------
Bad example:

    puts The quick brown fox jumps over the lazy dog                            

Should be:

    puts "The quick brown fox jumps over the lazy dog"                          

or, (preferably):

    puts {The quick brown fox jumps over the lazy dog}                          

or:

     puts Thequickbrownfoxjumpsoverthelazydog                                   

Reason:

Arguments to Tcl commands such as [puts] must be either wrapped in double-
quotes (where appropriate) or (preferably) grouped using curly-braces. 
The first example instead supplies 9 independent and invalid arguments to 
[puts]. Arguments  are also implicitly grouped if there is no intervening
whitespace, thus an argument which contains no whitespace need neither be
braced or quoted

Using [else if] Instead of [elseif]
-----------------------------------
Example: (wrong)

    if {[rnd 0 1]} {                                                            
       puts "True"                                                              
    } else if {[== [rnd 2 3] 3] }   # 'else if' is not a valid keyword          
       puts "Three"                                                             
    }                                                                           

Example: (correct with nested construct)

    if {[rnd 0 1]} {                                                            
       puts "True"                                                              
    } else {                                                                    
       if {[== [rnd 2 3] 3] }                                                   
          puts "Three"                                                          
       }                                                                        
    }                                                                           

Example: (correct using 'elseif')

    if {[rnd 0 1]} {                                                            
       puts "True"                                                              
    } elseif {[== [rnd 2 3] 3] }                                                
       puts "Three"                                                             
    }                                                                           

Reason:

'elseif' is not a command but an argument of the command [if]

Using [then] Within [if]
------------------------
Example: (wrong)

    if {[rnd 0 1]} then {                                                       
       puts "True"                                                              
    }                                                                           

Example: (correct)

    if {[rnd 0 1] {                                                             
       puts "True"                                                              
    }                                                                           

Reason:

There is no [then] argument to the Ticol [if] command

Finding Bad PreProcessor Expressions
------------------------------------
Problem:  Preprocessor Bad expression at line xxx, near
          and the script fragment can't be distinguished          
Solution: Place #exit statements at strategic locations and run
          ticol.exe scriptname /echo /na
          until the line is located. Use binary search strategy in
          placing the #exit statements

Malformed CRLFs in Files
------------------------
Problem:  Files loaded into an editor have double-line spacing
Evidence: A hex dump of the file shows line endings to be a sequence of
          0x0d 0x0d 0x0a ("\r\r\n")
Reason:   File I/O is translating "\n" (0x0d) characters 
          to "\r\n" (0x0d 0x0a) pairs
          Thus, passing \r\n will keep the "\r" and substitutes "\r\n"
          for "\n" and generating "\r\r\n"
Fix:      Emit a "\n" character in the appropriate sections of code 
          instead of "\r\n"

Junk Characters
---------------
Be careful when pasting-in code from websites and non-text editors. 
Occasionally, non-visible "junk" characters may find their way into source
code and result in difficult to debug issues. These characters may not be
visible unless certain options are enabled in your editor

Take particular care when pasting-in Unicode/UTF-8 source

Windows Command Line Breaks Direct Commands
-------------------------------------------
Passing a direct command via the Windows command line requires that the
command is wrapped in double-quotes rather than braces. If the command
is braced then Windows will break apart the command string and supply
only the first part. The result will be a brace parity error.

Example:

    ticol.exe ; {puts Hello}   # Too many { characters                          
    # Windows will supply '{ puts'                                              

Instead, direct commands passed from the command-line should be as
follows and should use bracing for embedded strings...

    ticol.exe ; "puts {Hello World}"                                            

Memory Use
----------
Various structures and objects within Ticol may progressively consume
amounts of memory, the allocation of which may be visible within 
Windows Task Manager or another process monitor. Array hash tables 
perform an incremental "smart resize" periodically and are not reduced
until the array is unset. Various aspects of memory may become fragmented
during long and complex loops. Unless memory use is continually escalating
there should be no issues and this is normal behaviour.

Common memory load on first loading Ticol is usually just under 2Mb
During operation this may rise to 10 or 20 Mb particularly if very large
data structures are created

See: 
http://phaseit.net/claird/comp.lang.tcl/fmm.html  Frequently Made Mistakes

Big Number Library Returns Unexpected Results
---------------------------------------------
The big number library is an integer only library with a few exceptions
Attempts to supply real numbers is not error-checked. The caller must
'sanity check' all inputs. Calculations may return zero or unexpected
results

    puts [big pow 2 10]      # Returns 1024                                     
    puts [big pow 2.6 10]    # Also returns 1024 (accepts only integer)         

See also: optimisation, performance

movefile

    bool [movefile sourcefile targetfolder ?-y?]

If target is specified as a foldername, ensure it ends with a trailing '\'
character or it will be assumed to be a new filename

Wildcards are not supported

[movefile] can rename files as well as move them

Forward-slash characters '/' may be used as path separators to avoid 
false escape issues with '\' characters

Where wildcard+forward-slash combinations are present, be sure to wrap
any filename in double-quotes to avoid /* being interpreted as a comment.

CAUTION: Supports only ANSI filenames, not Unicode/MBCS

Examples:

    movefile test.txt test\\        # Move into subfolder 'test'                
    movefile test.txt test/         # Move into subfolder 'test'                
    movefile test.txt test.dat      # Rename file as test.dat                   

See also: copyfile, file

msgbox
 
    integer [msgbox message ?title | -? ?flags?]

Displays a standard Windows messagebox dialogue

A title is optional and may be omitted or specified as blank using the '-"
placeholder in order to supply a flag value with no title.
msgbox returns Windows-standard numeric return-codes

Return values:

    yes    6
    no    7

Flag values:

    0x0 MB_OK                                                                   
    The message box contains one push button: OK                                
    This is the default                                                         
                                                                                
    0x1 MB_OKCANCEL                                                             
    The message box contains two push buttons: OK and Cancel                    
                                                                                
    0x2 MB_ABORTRETRYIGNORE                                                     
    The message box contains three push buttons: Abort, Retry, and Ignore       
                                                                                
    0x3 MB_YESNOCANCEL                                                          
    The message box contains three push buttons: Yes, No, and Cancel            
                                                                                
    0x4 MB_YESNO                                                                
    The message box contains two push buttons: Yes and No                       
                                                                                
    0x5 MB_RETRYCANCEL                                                          
    The message box contains two push buttons: Retry and Cancel                 
                                                                                
    0x6 MB_CANCELTRYCONTINUE                                                    
    The message box contains three push buttons: Cancel, Try Again, Continue    
    Use this message box type instead of MB_ABORTRETRYIGNORE                    
                                                                                
    0x10 MB_ICONSTOP                                                            
    A stop-sign icon appears in the message box                                 
                                                                                
    0x10 MB_ICONERROR                                                           
    A stop-sign icon appears in the message box.                                
                                                                                
    0x10 MB_ICONHAND                                                            
    A stop-sign icon appears in the message box.                                
    To indicate the default button, specify one of the following values         
                                                                                
    0x20 MB_ICONQUESTION                                                        
    A question-mark icon appears in the message box.                            
    The question-mark message icon is no longer recommended because it          
    does not clearly represent a specific type of message and because the       
    phrasing of a message as a question could apply to any message type.        
    In addition, users can confuse the message symbol question mark with        
    Help information. Therefore, do not use this question mark message          
    symbol in your message boxes. The system continues to support its           
    inclusion only for backward compatibility.                                  
                                                                                
    0x30 MB_ICONWARNING                                                         
    An exclamation-point icon appears in the message box.                       
                                                                                
    0x40 MB_ICONINFORMATION                                                     
    An icon consisting of a lowercase letter i in a circle appears in the       
    message box.                                                                
                                                                                
    0x40 MB_ICONASTERISK                                                        
    An icon consisting of a lowercase letter i in a circle appears in the       
    message box.                                                                
                                                                                
    0x0 MB_DEFBUTTON1                                                           
    The first button is the default button.                                     
    MB_DEFBUTTON1 is the default                                                
    unless MB_DEFBUTTON2, MB_DEFBUTTON3, or MB_DEFBUTTON4 is specified.         
                                                                                
    0x100 MB_DEFBUTTON2                                                         
    The second button is the default button.                                    
                                                                                
    0x200 MB_DEFBUTTON3                                                         
    The third button is the default button.                                     
                                                                                
    0x300 MB_DEFBUTTON4                                                         
    The fourth button is the default button.                                    
    To indicate the modality of the dialog box, specify one of the              
    following values.                                                           
                                                                                
    0x0 MB_APPLMODAL                                                            
    The user must respond to the message box before continuing work in the      
    window identified by the hWnd parameter. However, the user can move to      
    the windows of other threads and work in those windows                      
    Depending on the hierarchy of windows in the application, the user may      
    be able to move to other windows within the thread. All child windows       
    of the parent of the message box are automatically disabled, but pop-up     
    windows are not                                                             
    MB_APPLMODAL is the default if neither MB_SYSTEMMODAL nor MB_TASKMODAL      
    is specified                                                                
                                                                                
    0x1000 MB_SYSTEMMODAL                                                       
    Same as MB_APPLMODAL except that the message box has the WS_EX_TOPMOST      
    style. Use system-modal message boxes to notify the user of serious,        
    potentially damaging errors that require immediate attention (for           
    example, running out of memory). This flag has no effect on the user's      
    ability to interact with windows other than those associated with hWnd      
                                                                                
    0x2000 MB_TASKMODAL                                                         
    Same as MB_APPLMODAL except that all the top-level windows belonging to     
    the current thread are disabled if the hWnd parameter is NULL. Use this     
    flag when the calling application or library does not have a window         
    handle available but still needs to prevent input to other windows in       
    the calling thread without suspending other threads.                        
                                                                                
    0x4000 MB_HELP (Not supported)                                              
    Adds a Help button to the message box. When the user clicks the Help        
    button or presses F1, the system sends a WM_HELP message to the owner       

You can use [enum] to set these const values as follows:

    enum {                                                                      
       MB_OK                                                                    
       MB_OKCANCEL                                                              
       MB_ABORTRETRYIGNORE                                                      
       MB_YESNOCANCEL                                                           
       MB_YESNO                                                                 
       MB_RETRYCANCEL                                                           
       MB_CANCELTRYCONTINUE                                                     
       MB_DEFBUTTON1 = 0                                                        
       MB_ICONSTOP = 0x10                                                       
       MB_ICONERROR = 0x10                                                      
       MB_ICONHAND = 0x10                                                       
       MB_ICONQUESTION = 0x20                                                   
       MB_ICONWARNING = 0x30                                                    
       MB_ICONINFORMATION = 0x40                                                
       MB_ICONASTERISK = 0x40                                                   
       MB_DEFBUTTON1 = 0                                                        
       MB_DEFBUTTON2 = 0x100                                                    
       MB_DEFBUTTON3 = 0x200                                                    
       MB_DEFBUTTON4 = 0x300                                                    
       MB_APPLMODAL = 0                                                         
       MB_SYSTEMMODAL = 0x1000                                                  
       MB_TASKMODAL = 0x2000                                                    
       MB_HELP = 0x4000                                                         
    }                                                                           

Example:

    msgbox "Yes or no" "Question" [| 4 32]                                      

Result:

    7                                                                           

Example:

    option autoexec off                                                         
    option escape on                                                            
    msgbox [join [split $qbf " "] "\n"] "Choose" 36                             

Result:

    Messagebox which displays each word on new line with Yes, No buttons        

See also: inputbox, box, get_fileopen, get_filesave, get_folder, enum

Multiple Commands

As with C/C++, multiple commands may be separated using an intervening 
semi-colon (;)

This is particularly useful when calling Ticol from the Windows console

Example:

  set a 22;set b 7.0;set p [expr $a/$b];puts "Pi is $p"                         

Command line example:

  # Run with no autoexec                                                        
  ticol.exe ; "set a 22;set b 7.0;set p [expr $a/$b];puts {Pi is $p}" /na       

See also: syntax, Tcl, Ticol

Namespaces and Scope

Ticol, as a minimal Tcl implementation, does not support full Tcl namespaces,
however they may be emulated by prefixing procedure and variable names with
namespace tag value and double-colon. You will need to use the full,
emulated namespace name of the procedure when calling as a namespace context
cannot be set

Namespace-prefixed variables are useful to help avoid variable name 
collisions where the same name may be inadvertently be chosen in more than
one Tcl script

Care should be taken to avoid misinterpretation of namespaces by adjacent
variables or punctuation in strings. This may require enclosing the tag
value in curly-braces. 

Note that the double-colon 'namespace' scope prefix should not be confused
with global scope use (e.g. '::var' and '$::var')

If you wish to use colons adjacent to variables you must enclose the
variable name in braces ...

Example:

    set s "I am a pretty string"                                                
    puts "${s}::Punctuated::By colons"   # Correct                              
    puts "$s::Punctuated::By colons"     # Incorrect                            

Result:

    I am a pretty string::Punctuated::By colons                                 
    eval: No variable 's::Punctuated::By' in the current scope                  

Here is a demonstration of procedure and variable namespace emulation:

Example:

    set local::a 12345         # We will overwrite this using the proc          
                                                                                
    proc math::div {x y} {     # Example divide procedure                       
       upvar local::a a                                                         
       set a "div was called"                                                   
       if {!= $y 0} {                                                           
          [/ $x $y]                                                             
       } else {                                                                 
          return 0 -code error                                                  
       }                                                                        
    }                                                                           
                                                                                
    set a [math::div 22 7.0]                                                    
    puts $a                                                                     
                                                                                
    puts "\$local::a=$local::a   (Should say \"div was called\")"               

Result:

    3.142857142857143                                                           
    $local::a=div was called   (Should say "div was called")                    

Example:

An example of scoped variable use

    set scope fred                                                              
    set ${scope}::j 0                                                           
    ++ ${scope}::j                                                              
    puts "fred is $fred::j"                                                     

Result:

    fred is 1                                                                   

This example of scoped variable use using pseudo-namespaces is more
complex in that the nested-resolution of an indirect scope variable (fred)
and subsequent dereference is problematic without the use if [set] to
dereference since the outer '$' reference will be evaluated before the
inner reference unless a [command] is used

[set] can echo or 'dereference' a variable as well as set it

Example:

    option expression on                                                        
                                                                                
    set scope fred                                                              
                                                                                
    for {set ${scope}::j 0} {[set ${scope}::j] < 500} {++ ${scope}::j} {        
       set q [set ${scope}::j]                                                  
       if {[is_mod $q 100]} {                                                   
          puts "${scope}::j is $q"                                              
       }                                                                        
    }                                                                           

Results:

    fred::j is 0                                                                
    fred::j is 100                                                              
    fred::j is 200                                                              
    fred::j is 300                                                              
    fred::j is 400                                                              

See also: proc

Multi Line Commands

Providing that a newline is not encountered before an opening brace or
bracket, the contents of a command or expression can be spread across
multiple lines. This is normal for braced multiline expressions but is
also true for commands which are enclosed in square brackets

Example:

    # Single-line command                                                       
    puts [eval "/ 22.0 7.0"]                                                    
                                                                                
    # Multi-line command                                                        
    puts [                                                                      
       eval "/ 22.0 7.0"                                                        
    ]                                                                           

See also: command separator

Networking Commands
 
See the [info] command for various commands for querying the local network
External DLLs such as ping32.dll may be dynamically linked-to and used

See also: info, ping, ftp, dns, arp, dhcp, calldll

new

    new sourceobject newobject

Create a new and empty instance of an object. Source/template objects have
no special designation. Any handled object can be used as the source 
template. Currently, [new] detects and handles only structs

For structs, the total binary struct size, field count, field names
and field widths are copied. Field names will be distinguished (decorated)
with the name prefix of the struct parent variable name

Structs are binary-compatible and may be used with [calldll_*] and passed
to the Windows API and other DLLs. Each Tcl field variable is mapped to
a binary-offset in the struct's data-block

[new] is not implemented for arrays, standard variables or other data
types as it would serve no useful purpose

Manual deletion of objects created using [new] is not required. Garbage
collection is automatic

Example:

     struct s {{field1 10} {field2 20}}  # Create a source struct               
     struct set s.field1 Hello           # Assign some data                     
     struct set s.field2 World                                                  
     new s s_new                         # Copy s as s_new                      
     dump s_new                          # Display debug info                   

Results:  (Displays a blank struct)

var type is 3 v->val is 36811792                                                
                                                                                
 struct 's_new' level 0 (0x231b430) data @36811792 (0x231b410)                  
 ¦ 30 byte(s), 2 member(s)                                                      
 + 1:'s_new->s_new.field1'      => 36811792 (0x231b410) 10 byte(s)              
 + 2:'s_new->s_new.field2'      => 36811802 (0x231b41a) 20 byte(s)              
                                                                                
 Address   0  1  2  3  4  5  6  7    8  9  A  B  C  D  E  F ASCII               
 -------- -----------------------   ----------------------- ----------------    
 0231b410 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 ................    
 0231b420 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 ................    
                                                                                
 * Var 0::'s_new' has no parents                                                

See also: struct, set

newline
 
    newline ?count?

Prints a new line to the console. 

Equivalent to [puts ""] or [printf "\r\n"]

The default is to issue one newline (CRLF pair). An optional argument may 
specify the number of new lines to print

[newline] offers no particular performance advantage but may make code
easier to read

See also: puts

not (Bitwise and Logical)

    integer [!  expression]     # Logical not                                   
    integer [!  string]         # Logical not                                   
    integer [~  expression]     # Bitwise not                                   

The '!' operator returns a logical true/false as 1 or 0. [!] will also
operate on non-numeric strings as a test for an empty string. An empty
string or empty list will return true (1), otherwise false. Standard
number detection rules apply. Anything which is not a valid number will
be interpreted as a string

The bitwise not '~' operator performs a bit-inversion on the expression
and returns the result

Example                             Returns

    puts [expr "!(-12 -3)" ]        0                                           
    puts [expr "! $true" ]          0                                           
    puts [expr "! $false" ]         1                                           
    puts [expr "! -12 -3" ]        -3                                           
    puts [expr "~(-12 -3)" ]       14                                           
    puts [~ 0]                     -1                                           
    puts [~ 1]                     -2                                           
    puts [! Hello]                  0                                           
    puts [! 1.2.3.4]                0                                           
    puts [! ""]                     1                                           
    puts [! {}]                     1                                           

Example:

    set s {}                                                                    
    if {! $s} {                                                                 
       puts "s is empty"                                                        
    }                                                                           

Result:

    s is empty                                                                  

Example:

    set s Hello                                                                 
    if {! $s} {                                                                 
       puts "s is empty"                                                        
    } else {                                                                    
       puts "s is not empty"                                                    
    }                                                                           

Result:

    s is not empty                                                              

Example:

    set ip_address 1.2.3.4                                                      
    if {! $ip_address} {                                                        
       puts "ip_address is empty"                                               
    } else {                                                                    
       puts "\$ip_address is not empty"                                         
    }                                                                           

Result:

    $ip_address is not empty                                                    

See also: expr

Non-Decimal Numbers

Ticol supports several non-decimal const number formats as follows:

Unquoted values (freeform integer):

    Type        Prefix      Range       Example
    ----        ------      -----       -------
    Binary      0bN         0..1        0b01010101
    Octal       0oN         0..7        0o13652104
    Hexadecimal 0xN         0..9,a..f   0xdeadbeef
    Hexadecimal 0XN         0..9,A..F   0xDEADBEEF

Quoted values (escape character):

    Type        Prefix      Range       Example
    ----        ------      -----       -------
    Octal       \0N         0..7        0o13652104
    Hexadecimal \xN         0..9,a..f   0xdeadbeef
    Hexadecimal \XN         0..9,A..F   0xDEADBEEF

The Macro PreProcessor (MPP) will attempt to transcode non-decimal numbers
into decimal equivalents where possible during the preprocessing phase of
script loading or via the CLI. The CLI may not translate all numbers and
if in doubt, [expr] may be used to ensure they are translated

Non-decimal numbers are not translated where embedded within double-quotes
In such cases, explicit conversion using [expr] must be used

The commonly used convention of numbers which are prefixed with zero (0) 
being assumed as octal is not adhered to. Octals require a "0o" prefix

Binary Numbers
--------------
Often useful for bit manipulation/bit-shifting algorithms

    puts 0b110000000    # Not translated within the CLI                         

Also available via the ticol_bignum.dll plugin

Hexadecimal Numbers
-------------------
The most common alternative to decimal format numbers and understood by
most Ticol commands. Prefixed with "0x"

    # Prints ASCII code for the letter 'A' (65)                                 
    puts 0x41           # Not translated within the CLI                         
    puts 0X41           # Not translated within the CLI                         

Octal Numbers
-------------
Less commonly used than hexadecimal numbers (0..7) and prefixed with "0o"

Roman Numbers
-------------
These may be used via the ticol_r2d.dll plugin and due to late-binding
of the lib load are not processed by the MPP but via direct commands
Roman numbers are integer-only and have a small, positive range

    puts [roman_to_decimal mdcclxxvi]                                           
    puts [decimal_to_roman 1776]                                                

Results:

    1776                                                                        
    MDCCLXXVI                                                                   

See also: cli, data type ranges, roman numbers

now

    double [now ?-ctime?]

Returns the current date/time as a VB-compatible 8-byte double value
or, if the -ctime argument is used, as a C/Unix long integer
a
Example:

    puts [now]               # VB data format                                   
    puts [now -ctime]        # C/C++ data format                                

Result:

    42933.993946759263000                                                       
    1500331879                                                                  

[double_to_ctime] can also convert...

Example:

    clock format [now -ctime]                                                   
    clock format [double_to_ctime [now]]                                        

Result:

    Tue Mar 03 22:57:35 2015                                                    
    Tue Mar 03 22:57:36 2015                                                    

See also: double_to_ctime, ctime_to_double, date, time, clock

Number Formats

Number formats understood by various commands including [eval] and [expr]
are as follows:

    Type                Format          Signed?    Bytes Interpretation         
    -------------------------------------------------------------------         
    decimal integer     ?+|-0..9?       signed     8     64-bit                 
    decimal float       ?+|-0..9.NN?    signed     8     64-bit double          
    hexadecimal         0x?0..9|A..F?   unsigned   8     64-bit                 
    octal               0o?0..7?        unsigned   8     64-bit                 
    binary              0b?0|1?         unsigned   8     64-bit                 

All numbers are evaluated as 64-bit wherever possible. Conversion to 
floating point is automatic where necessary for real numbers. Floating point
conversion is handled as an 8 byte double which has a precision of 15 
decimal places.

Tcl v9 octal number identifiers are implemented, which require 0oNN format
See: http://wiki.tcl.tk/498

Real numbers are converted on the following format basis:
  [whitespace] [sign] [digits] [.digits] [ {d | D | e | E }[sign]digits]

All Tcl numbers are stored internally as strings and converted in real time
Casts are possible to coerce and convert between number types and conversion
routines are available

See also: cast, type, math, expr, eval, functions, ticol
          inttooct, intothex, inttobin

on (Flow Control)

    result [on integer {script} ?{script...}? ?-default {script}?]    

A faster equivalent to switch which may be more efficient in tight loops
which tests showed to use only 80% of the time of an [if] ladder
[on] performs slightly faster than [switch]

Similar to BASIC's 'ON..GOTO' or 'ON GOSUB' statement. The integer value
indexes directly into a series of clauses. At least one clause must be
present

[on] is designed to work only with integer values. Other values, e.g.
characters could be resolved into an integer using say [lindex] or
[strchr] / [strstr]. If double values are presented, they will be
truncated to integer

The index value is a base 1 value which indexes into the series of 
arguments with 1 as the first clause, 2 as the 2nd and so-on. 
These clauses should be presented individually as with [if] and not
wrapped in a single set of braces

If the index is < 1 then that index value is ignored

If the index is > the highest script case then an error is generated
unless a '-default' case is included

Example:

    on [rnd 0 10] {                                                             
       puts "Case 1"                                                            
    } {                                                                         
       puts "Case 2"    # Highest valid index is 2                              
    } -default {                                                                
       puts "Case Default"                                                      
    }                                                                           

Result:

    <Randomly prints "Case 1", "Case 2" or "Case Default">                      

Example:

    on 10 {              # Trigger an exception                                 
       puts "Case 1"                                                            
    } {                                                                         
       puts "Case 2"    # Highest valid index is 2                              
    } {                                                                         

Result:

    <A catchable exception is raised>                                           

Example Using Non-Integer Values
--------------------------------

    set ch "c"                                                                  
    on [strchr "abcde" $ch] {                                                   
       puts "Letter a was found"                                                
    } {                                                                         
       puts "Letter b was found"                                                
    } {                                                                         
       puts "Letter c was found"                                                
    } -default {                                                                
       puts "Some other character was found"                                    
    }                                                                           

                                                                                
    Letter c was found                                                          

Incorrect Syntax
----------------
Note that the script cases MUST be presented individually. 
Don't present as follows; i.e. with all clauses wrapped within a
pair of braces...

    on [rnd 0 10] {                                                             
       {                       # This brace is incorrect ...                    
          puts "Case 1"                                                         
       } {                                                                      
          puts "Case 2"                                                         
       } -default {                                                             
          puts "Case Default"                                                   
       }                       # So is this                                     
    }                                                                           

See also: loop, switch, optimising

open, file open
 
    handle [open filename mode]
    handle [file open filename mode]

Returns an integer handle to the open file if successful, otherwise 0
The mode parameter is a string made up of character flags r, w, a and +
and works in the same way as for C/C++. [try...catch] can be used to detect
exceptions opening invalid files

    r   read mode                                                               
    w   write mode                                                              
    a   append mode                                                             
    +   update mode                                                             

Example: 

    set fp [open "filename" r]                                                  
    close fp                                                                    

Example:

    set fp [open "input.txt" w+]                                                
    puts $fp "test"                                                             
    close $fp                                                                   
                                                                                
    set fp [open "input.txt" r]                                                 
    set data [read $fp]                                                         
    puts $data                                                                  
    close $fp                                                                   

Example:

    try {                                                                       
       set fp [open "nosuch.txt" w+]                                            
    } catch {                                                                   
       puts "File not found"                                                    
    }                                                                           

Result:

    File not found                                                              

Example:

    set filename "licence.txt"                                                  
    try {                                                                       
       set fp [open $filename "rb"]                                             
    } catch {                                                                   
       die "Can't open $filename"                                               
    }                                                                           
    set line 0                                                                  
    while {! [file eof $fp]} {                                                  
       file gets $fp s                                                          
       puts $s                                                                  
       ++ line                                                                  
    }                                                                           
    close $fp                                                                   

For the Windows "open file" / "save file" common dialogs, See: get_fileopen
get_filesave

See also: file gets, file eof, close, gets

dll_close
 
    handle [dll_open dllname]
    dll_close handle

Loads a DLL by name and returns a handle to the loaded DLL file. This handle
can then be used to call the calldll* series functions

Note that handle variables should be set to zero after a call to dll_close

Example:

    set handle [dll_open mydll.dll]                                             
    set s [calldllstr handle some_string_function]                              
    dll_close $handle                                                           

See also: dll_open, calldll

Optimising [if]...[elseif]...[else] Ladders

Very large [if..elseif] ladders can be time consuming when high speed
is required. When processing very large [if] ladders within loops
the entire [if] block is copied, split and parsed for each iteration.
This can be avoided. It is possible to take advantage of Tcl's [eval]
command and array speed to make substantial speed gains with this
type of problem

Define each separate [if] clause in an array element and index this
by subscript matching the if conditions, either by number or string

    set some_global_var 0                                                       
                                                                                
    set if_block("foo") {                                                       
       puts "in block foo: $some_global_var"                                    
       ++ some_global_var                                                       
    }                                                                           
                                                                                
    set if_block("bar") {                                                       
       puts "in block bar: $some_global_var"                                    
       -- some_global_var                                                       
    }                                                                           
                                                                                
    set if_block("exit") {                                                      
        puts "* Exiting *"                                                      
        set done 1          # Will cause the main loop to exit                  
    }                                                                           
                                                                                
    array set cmd {         # Create integer to array mappings, N -> foo        
        0 foo               # We could use string to array element mappings     
        1 bar               # or string to integer mappings                     
        3 exit              # Exit                                              
    }                                                                           
                                                                                
    set done 0                                                                  
    while {! $done} {                                                           
       ##################################################################       
       # We will iterate, mapping a random integer, but we could just as        
       # easily map to a sequence of strings parsed from an input file          
       # If strings can be mapped directly then $cmd() is not required          
       ##################################################################       
       set op [rnd 0 3]    # Simulate varying IF ladder conditions              
       set code [array item cmd $op ""]                                         
       if {ne $code ""} {                                                       
          eval $if_block($code)                                                 
       }                                                                        
    }                                                                           

Results:

Will vary depending on the random sequence in this example

    in block bar: 0                                                             
    in block bar: -1                                                            
    in block foo: -2                                                            
    in block bar: -1                                                            
    * Exiting *                                                                 

To test against a range of strings, put the matching strings into an array
and return an integer mapping as follows. Then use [array item] with a
default return value of 0. This can rapidly match a very large range of
cases and efficiently default to 'false' if there is no match

    option expression off                                                       
    array set valid {        # A potentially huge list of strings to check      
       foo 1                                                                    
       bar 1                                                                    
    }                                                                           
                                                                                
    proc check {s} {                                                            
       if {[array item valid $s 0]} {                                           
          puts "We have a match on $s"                                          
       } else {                                                                 
          puts "No match on $s"                                                 
       }                                                                        
    }                                                                           
    check "foo"              # These checks can be rapidly performed in         
    check "moo"              # a loop                                           

Results:

    We have a match on foo                                                      
    No match on moo                                                             

See also: if, on, switch, proc, sub, gosub, goto

option  (Runtime Configuration Options)

Programmatically set various Ticol configuration options at runtime
Command options given after the 1st argument may appear in any order

    option autoexec     ?on|off|push|pop|reset?                                 
    option breakpoint   ?on|off|push|pop|reset?                                 
    option echo         ?on|off|push|pop|reset?                                 
    option escape       ?on|off|push|pop|reset?                                 
    option expression   ?on|off|push|pop|reset?                                 
    option preprocessor ?on|off|push|pop|reset?                                 
    option stacktrace   ?on|off|push|pop|reset?                                 

Note that using Ticol as a manual Windows CLI shell requires that "C"-like 
escape characters be correctly processed. Thus if autoexec is ON then 
C-escapes will not be interpreted but passed-through literally. Thus paths
such as "\first\second\third" will not encounter an embedded \t tab 
character

A script cannot change the current state of the CLI settings. These will
be restored after a [run] command

To use C-like escape sequences at the CLI use the following:

    option autoexec off
    option escapes on

Autoexec should be OFF when running Tcl scripts. Use [exec] or [spawn] to
launch programs safely

In case of strange behaviour always check the [option expression] setting
first

option
------
option with no arguments displays the current full status:

Subcommand    Arg  Comment
-----------------------------------------------------------------------
                                                                                
autoexec       on: Autoexec unknown (disables CLI C-like escapes if ON)         
echo          off: Echo command and error results                               
                   Should always be set to ON during debugging                  
escape        off: C-like escapes. (Disabled in CLI by autoexec)                
expression     on: Flow control uses [expr]                                     
preprocessor   on: Macro preprocessor                                           

option <item>
-------------
[option <item>] returns the current status in a string as one of either
"OFF" or "ON"

push and pop
------------
The push and pop options save or restore the current setting
push may be combined with on or off. e.g. [option echo push off], or
may be used separately, [option echo push], [option echo off]

This is useful for procedures which may need to alter the global setting
temporarily then restore it to its prior condition on exit

Only one save level is currently supported. Any previous push operations
will be discarded if called a second time. Future versions of Ticol will
allow for multiple depth push/pop.

Example:

    option echo push on                                                         
    # Some code...                                                              
    option echo pop                                                             

Combined commands
-----------------
Combined commands are possible which save and set a setting

Example:

    proc foo {} {                                                               
       option expression push on  # Save current state and set to ON            
       # ... some Tcl code here                                                 
       option pop                 # Restore the previous state                  
    }                                                                           

Command       Arg  Comment
------------------------------------------
                                                                                
autoexec       on: Autoexec unknown                                             
echo          off: Echo command and error messages at the console               
escape        off: C-like escapes                                               
expression    off: Flow control uses eval                                       
PreProcessor   on: Macro PreProcessor                                           
                                                                                
stacktrace     on: Debug error stacktrace (when enabled)                        
breakpoint     on  Debug breakpoints (when enabled) via [halt]                  

option <command>
----------------
Returns the current status as either 'on' or 'off'
e.g. 'option echo' -> 'on'

option autoexec
---------------
Turns the automatic execution of Windows programs on or off
If turned off then this enables the Tcl [unknown] feature
If turned on then this will disable "C" escape characters in the CLI

See: help unknown

option echo
-----------
Turns the Command Line Interpreter (CLI) console echo results on or off
This command has no effect within scripts, where there is no local echo

[option echo off] also disables display of fatal error messages
and you must therefore trap and handle all error cases in order to
generate sensible user feedback

The [catch] command requires option echo off for effective use

If you prefer to see error messages within program output as well as
command output, enable option echo on within the INI configuration file

Without [option echo off] all console output could be echoed twice

option escape
-------------
Turns processing "C"-like escape characters such as \t, \n etc. on or off
If turned on will disable autoexec since backslashes can no longer be
safely passed to Windows

[run] will force [option escape] to ON, [source] leaves this setting
unchanged. Entering the CLI will force this option OFF so using [source]
from the CLI will require correctly setting [option escape] in a script
Thus, any scripts which depend on processing Tcl backslash escape
sequences may not work properly if called using [source] from the CLI

See: help escape

option expression (USE CAREFULLY)
---------------------------------
Turn on or off the implicit use of the [expr] command when evaluating 
while, for and if flow-control statements

Caution: When set to "on", [expr] with infix operators will be used instead
of the default, [eval], which uses prefix operators. Incorrectly setting
this option scheme can cause unpredictable behaviour with flow control
commands.

In an interpreter, expression handling is more complex and far-slower
than using native Tcl commands but expressions may be more convenient to
use. Thus a method is offered of switching between the two formats

Strings should be wrapped in double-quotes wherever possible. In particular
strings containing math symbols will be interpreted as a math expression
with option expression on.  

Example:

    # Examples of bad and good expression presentation                          
    option expression on         # Enable 'expression on'                       
    if {file exists test} {      # BAD. This is not an expression               
    if {[file exists test]} {    # CORRECT. Command return is evaluated         

With [option expression on] expression {== $s 123} would be interpreted as
'if {expr "== $a 123"}' and will therefore fail. This behaviour can be 
corrected by using a bracketed argument: if {[== $a 123]} or by toggling
option expression off then on again

[option expression] is less efficient than [eval] (default) due to the
text-processing overheads of the expression-handler. Time-critical code
should use prefix notation and [option expression off]

[option] with the 2nd argument is omitted will display the current status. 

With [option expression on] both infix and prefix expression styles may
be used. The overhead of selecting between the two styles is trivial

This option is also configured in ticol.ini [config] as UseEval=<Boolean>

Infix expression example:

[option expression on]  - Uses infix [expr] for initialiser and test

    option expression on                                                        
    for {[set i 0]} { $i < 10 } {incr i} { puts $i }                            
    for {set i 0} { $i < 10 } {incr i} { puts $i }                              
    while { $i < 10 } { puts $i; incr i }                                       
    if { $i < 10 } { puts $i } elseif { $i > 5 } { break }                      

Prefix expression example:

[option expression off] - (Default): Used prefix [eval] for init and tests

    option expression off                                                       
    # Note that the test expression differs from [option expression off]        
    for {set i 0} {< $i 10} {incr i} {puts $i}                                  
    while {< $i 10} { puts $i; incr i }                                         
    if { < $i 10 } { puts $i } elseif { > $i 5 } { break }                      

Option expression problems:

    option expression off                                                       
    for {[set i 0]} { $i < 10 } {incr i} { puts $i }  # Loop never exits        

                                                                                
    option expression on                                                        
    if {file exists some.tcl} {...   # FAILS ('file' is tested as a var)        
                                                                                
    option expression off                                                       
    if {file exists some.tcl} {...   # WORKS OK ('file' is a command)           
                                                                                
    option expression on                                                        
    if {[file exists some.tcl]} {... # WORKS OK (context is enforced)           

Note that with option expression off, if you use the form below then
[eval] will be called twice. This will slow down performance slightly

    option expression off                                                       
    if {[proc $x]} { ...             # [eval] is called twice                   
                                                                                
    option expression off                                                       
    if {proc $x} { ...               # [eval] is called once                    

See also: flow-control, for, while, if, configuration

option preprocessor
-------------------
Turns on or off the source-code Macro PreProcessor

The default is ON and is also applied to CLI commands

option stacktrace
-----------------
Turns on or off the debugging stacktrace for fatal errors
The default is OFF and this option is also configurable in TICOL.INI

This command is ignored in CGI mode unless forced via ticol.ini

This shows the execution stack-trace when a fatal error is displayed.
The first 256 characters of each command name or parameter will be shown

If option echo is OFF then no stacktrace (or error message) is shown
This can help track down the sequence of errors should a bug occur

Tracing the execution stack will slow down Ticol slightly so should be 
enabled only during script debugging

Example:

Execution is proc level1->proc level2->puts (invalid [puts] argument count)

    Error stack trace:
    puts: Invalid stream handle
          In: puts x y z
          In: level2 x
          In: level1 10

option breakpoint
-----------------
See: option breakpoint

See also: option breakpoint, autoexec

Debugging With option breakpoint, halt, resume

option breakpoint
-----------------
option breakpoint ?on|off?

Enables conditional and non-conditional breakpoint handling. Execution will
halt prior to each command call. When a [halt] instruction is encountered
you have the option to single-step to further commands, view the execution
stack, resume execution (turn off breakpoints) or to quit the script

[option breakpoint on] will also enable the stack trace on errors

Debug mode is enabled using: ticol.exe <script> /debug

This command is ignored in CGI mode unless forced via ticol.ini

Example:

   option stacktrace on                # Enable stacktracing                    
   option breakpoint on                # Enable breakpoints                     
   watch add handle                    # Watch variable $handle                 
   halt                                # Halt and enter the debugger            
                                                                                
   set handle [dll_open "sqlite3.dll"] # Code continues...                      
                                                                                
   resume                              # Resume non-debug execution             

halt
----
The [halt] and [resume] commands may be called conditionally within flow-
control statements. [halt] and [resume] only have effect with the /BP 
command line argument or with [option breakpoint on], otherwise they
are ignored. [halt] will activate the breakpoint debugger

Example:

   if {== $i 10} { halt }              # Halt and debug if i is 10              

Example (at the CLI):

    option breakpoint on                                                        
    set s "1+2*3"                                                               
    halt                                                                        
    puts [expr $s]                                                              
    # Debugger is triggered to single-step execution                            

Enabling breakpoints will also enable stacktrace which provides breakpoint
trace information

Once breakpoints are activated, execution will stop at the first [halt] 
command. You need at least one [halt] command in your script. If breakpoints 
are not enabled any [halt] command within code will be ignored. You may have 
any number of [halt] instructions in a Ticol script, however you should
remove them in any final release due to a small performance cost

When a [resume] command is encountered any previous [halt] instruction will
be cancelled and normal script execution will resume until another [halt] is
encountered

Breakpoints may be enabled immediately in a script by using 
[option breakpoint on] followed by [halt]

The debugger menu will halt with the next pending command shown along with
the current command-count and other options. The [textcolor] command will
be ineffective during debugging

You may also enable breakpoints from the Windows command line using the 
argument: ticol.exe /BP

The following menu options are displayed for breakpoints:

  Info   Shows the current execution stack                                      
  Next   Steps to the next command without showing the stack                    
  Trace  Steps to the next command and shows the execution afterwards           
  Resume Halts debugging and resumes normal execution until another [halt]      
         command is encountered, in which case the debugger is re-entered       
  Vars   Show variables in the current execution level                          
  Watch  Toggle configured watches on/off                                       
  Quit   Exits the current code. Returns to the O/S unless launched from        
         the CLI, in which case control is returned to the CLI                  

Example:

Breakpoint stack item   Near local line 1:                                      
                                                                                
    At: / 10 0                                                                  
    At: if == $y $b                                                             
          error "Error generated by error" "Info for error" 401 [- $line 1]     
        else                                                                    
          return [/ $y $b]                                                      
    At: Div x                                                                   
    At: level2 10                                                               
    At: level1 10                                                               
                                                                                
(142): Breakpoint at [/]:Info Trace Next Watch(OFF) Resume Quit                 

Using Stack Trace to Analyse GPFs
---------------------------------
If they occur, GPFs can be analysed by re-running the script with the 
command-line argument /ST to enable stack-tracing. When the GPF is triggered
the stack place will show the command stack and which command triggered the 
GPF. This may be useful when using [calldll*] commands. /ST will not trace
into plugin libs

The debugger stack will trace the last 100 events

See also: watch, halt, array, arrays, upvar, global, ticol.ini, debugging

See: help PreProcessor

See also: watch, at_exit, cli, autoexec, unknown, autoexec.tcl

Passing Variables to Procedures by Name (by Reference)

Variables may be passed to a procedure by name (often called passing by
reference) within procedures using [upvar]. The variable name is passed
in the proc argument and [upvar] is used to create a reference to the
parent variable. Whilst this passes a variable name it emulates 'passing 
by reference' since the name is a reference to a variable in another stack
frame level

    Pass by value              foo $var   # Dereference and pass the value
    Pass by reference/name     foo var    # Pass the variable name

The proc argument should be a different name tag value to the referenced
variable (see example below)

Passing by name offers more flexibility than passing by value

Example:

    array set colours {                                                         
       1 red                                                                    
       2 green                                                                  
       3 blue                                                                   
       4 white                                                                  
    }                                                                           
                                                                                
    proc list_by_key {var} {                                                    
       puts "list_by_key: Listing $var"                                         
       # Upvar is required as all we have here is a name                        
       upvar $var        # Upvar is required for pass by name                   
       foreach key [lsort [array names $var]] {                                 
          puts "Key: '$key $colours($key)'"                                     
       }                                                                        
    }                                                                           
                                                                                
    puts "Listing colours() sorted by key"                                      
    list_by_key colours    # Pass the variable name 'colours' (reference)       

Result:

    Listing array colours() sorted by key                                       
    list_by_key: Listing colours                                                
    Key: '1 red'                                                                
    Key: '2 green'                                                              
    Key: '3 blue'                                                               
    Key: '4 white'                                                              

See also: pass by value, proc, variables, passing by value

Hash Tables

As Ticol arrays operate via efficient hash tables there wasn't much point
in implementing a separate [hash] command

See: arrays

Passing Variables to Procedures by Value

Variables may be passed to a procedure by value within procedures
The variable is de-referenced in the procedure call by prefixing with a 
dollar sign. i.e. the variable is dereferenced before the [proc] call

Discussion:

Passing by value has limitations. Complex objects such as stacks, structs
or arrays cannot be passed by value to a proc; these must be passed by
name. Simple objects such as strings, integers or real numbers may be
freely passed by value

The value will be passed via the proc argument variable. This argument
variable must still be de-referenced using a dollar sign

Example:

    set s "Hello world"                                                         
    set i 123456789                                                             
    set f $pi                                                                   
                                                                                
    proc print_arg {value} {                                                    
       puts "Argument is: $value"                                               
    }                                                                           
                                                                                
    puts "Print argument passed by value"                                       
    print_arg $s    # Pass the variable by value                                

Result:

    Argument is: Hello world                                                    
    Argument is: 123456789                                                      
    Argument is: 3.14159265358979323                                            

See also: calling by name, pass by reference, proc, variables

Console Redirection

 Redirect to a file in Windows by appending the redirect ">" character
 at the end of the command. Any arguments such as /NA must appear before
 this character

Example

    # List all Ticol commands and redirect into a text file...                  
    # Run this from the Windows console command-line                            
    ticol.exe ; "foreach x [commands] {puts $x}" /na > cmdlist.txt              

See also: spawn, exec, ticol.exe

Returning Struct Variables from Procedures

Ticol structs were written to offer a binary interface with external DLLs
via the calldll_* library. However they may be used internally and within
Ticol procs. Returning values from Ticol procs via structs is possible but
using them in this way isn't as easy as using them in C/C++ etc.

Discussion:

Within procedures, abstract/complex data types such as struct and stack
require a field de-reference using [struct item] to get a reference to
the field variable from the struct variable. Simple types don't require
this

Outside of procedures and where the struct variable is 'in scope' access
to the variable field member requires [ofaddress struct.member] to
retrieve the struct member address and dereference it back to a variable
reference. This is because Ticol structs emulate C/C++ structs by mapping
Tcl variables to address-offsets in a block of memory space. Each field
member variable has a value which stores the memory address of the field.

Where struct members hold a reference (name) of a variable in another 
scope such as root/level 1, it will be necessary to use [set] and the
scoping prefix or [upvar] to manipulate that variable. Where a struct
member field contains a literal variable and is therefore passed by
value, no such complex indirect reference is necessary

Example:

Here, a struct with member field 's.i' is passed in var 's'. s.i contains
the name of a variable in global scope. It is echoed using [set] as the
'$' dereference will be evaluated too late...

    puts "struct s.i: '[set ::[struct item $value ${s}.i]]'"                    

Here, struct 's' member field 's.f' contains a literal double value

    puts "struct s.f: '[struct item $value ${s}.f]'"                            

Variables which reference a struct member in struct.member format will
require the use of curved braces to isolate the struct name so that it
may be dereferenced separately from the trailing field name, i.e.
${struct}.member v's $struct.member

Example Code:

proc ret_val {value} {                                                          
    upvar $value                                                                
    textcolor green                                                             
                                                                                
    if {[eq [type $value] stack]} {                                             
       puts "ret_val: Stack: '[stack item $value 0]'" # Show stack top          
       stack push $value "s1 Stack item return"       # Return stack val        
    } elseif {[eq [type $value] array]} {                                       
       textcolor red                                                            
       puts "ret_val: Array: [array item $value 1]"                             
       array set $value 1 "Ret-array"                 # Return array item       
    } elseif {[eq [type $value] struct]} {                                      
       textcolor cyan                                                           
       puts "ret_val: Struct $value item(s):"                                   
       array set ::[struct item ${value}.a] 1 "a1"                              
       struct set ::${value}.f "f2"                                             
       struct set ::${value}.i "i3"                                             
       # ADTs require a field dereference using [struct item]                   
       stack push ::[struct item ${value}.k] "k4"                               
       # Simple variables do not require field dereference                      
       struct set ::${value}.r "r5"                                             
       struct set ::${value}.s "s6"                                             
    } else {                                                                    
       # Set literal var                                                        
       puts "ret_val: String or number $value is '[set $value]'"                
       set $value "sn4"                                                         
    }                                                                           
    textcolor                                                                   
}                                                                               
                                                                                
struct ts {   # Test struct: holding various data types                         
    a 20      # Array                                                           
    f 40      # Double/Float Literal                                            
    i 20      # Integer Literal                                                 
    k 20      # Stack Reference                                                 
    r 20      # String Reference                                                
    s 256     # String Literal * 256 bytes                                      
}                                                                               
                                                                                
# // ASSIGN //                                                                  
set i 123456789                                                                 
struct set ts.i 123456789   # By value                                          
                                                                                
set f $pi                   # Use const pi                                      
struct set ts.f f           # By reference                                      
                                                                                
set s "Hello I am s"        # Simple var                                        
                                                                                
set r "Hello string"                                                            
struct set ts.r r                                                               
                                                                                
struct set ts.s "Hello literal string"                                          
                                                                                
set a(1) "This is array item 1"                                                 
struct set ts.a a                                                               
                                                                                
stack create stk 200                                                            
stack push stk "Hello stack inbound"                                            
struct set ts.k stk                                                             
                                                                                
# // CALL //                                                                    
ret_val s    # Pass a variable by name                                          
textcolor yellow                                                                
puts "Returned string literal is:\t'$s'"                                        
                                                                                
ret_val r    # Pass a variable by name                                          
textcolor yellow                                                                
puts "Returned string reference is:\t'$r'"                                      
                                                                                
ret_val i    # Pass a variable by name                                          
textcolor yellow                                                                
puts "Returned integer literal is:\t'$i'"                                       
                                                                                
ret_val f    # Pass a variable by name                                          
textcolor yellow                                                                
puts "Returned float literal is:\t'$i'"                                         
                                                                                
ret_val a    # Pass an array by name                                            
 textcolor yellow                                                               
puts "Returned array a(1) is:\t\t'$a(1)'"                                       
                                                                                
ret_val stk  # Pass a stack by name                                             
textcolor yellow                                                                
puts "Returned stack is:\t\t'[stack pop stk]'"                                  
                                                                                
# // Struct Translation and Returns //                                          
newline                                                                         
ret_val ts                                                                      
textcolor yellow                                                                
                                                                                
# Structs with name references require [ofaddress]                              
# Global values which were set can be addressed directly                        
                                                                                
puts "Returned stack is:\t\t'[ofaddress ts.i]'"                                 
puts "a(1) is '$a(1)'"                                                          
puts "a(1) is '[set a(1)]'"                                                     
puts "ts.a '[array item [ofaddress ts.a] 1]'"                                   
puts "ts.f '[ofaddress ts.f]' ($ts.f)"                                          
puts "ts.i '[ofaddress ts.i]' ($ts.i)"                                          
puts "ts.k '[stack pop [ofaddress ts.k]]'"                                      
puts "ts.r '[ofaddress ts.r]' ($ts.r)"                                          
puts "ts.s '[ofaddress ts.s]' ($ts.s)"                                          
                                                                                
# END #                                                                         

See also: pass by value, pass by name

pause

    pause ?string? ?-quiet?
 
Temporarily halt execution with a prompt. The prompt message may be
customised by an argument. The -q argument will suppress prompt output

See also: exit

CGI Application Back-End Configuration

Using Ticol Directly
--------------------
Ticol can be used directly as a CGI application. It will not be as fast
or efficient as PHP or PERL. It will automatically detect if running in
CGI mode by checking for the environment variable HTTP_HOST. 

If no script argument is given then it will try to serve an HTML man
file called 'ticol.htm' if it is found in the same folder

Configuring Apache Webserver
----------------------------
Ticol TCL can be configure either as a direct CGI application where the
embedded URLs are directed to /cgi-bin/ticol.exe?scriptname.tcl where
ticol.exe and ticol.ini plus any scripts are copied to that folder, or 
it is possible to have all files of the type ".tcl" automatically
interpreted by Ticol Tcl if configured as a registered type. 

To do this either 

a) Use a 'shebang' as the first line of each file as follows:

    #!C:/path/to/for/example/cgi-bin/ticol.exe                                  

and the following Apache configuration

    # httpd.conf                                                                
    LoadModule cgi_module modules/mod_cgi.so                                    
    ScriptAlias /cgi-bin/ "cgi-bin/"                                            
    LoadModule actions_module modules/mod_actions.so                            
    AddType application/x-httpd-tcl .tcl                                        
    AddHandler cgi-script .tcl                                                  
    ScriptInterpreterSource Script                                              
    Action application/x-httpd-tcl "cgi-bin/ticol.exe"                          
    # Append index.tcl to DirectoryIndex                                        
    DirectoryIndex index.html index.html.var index.tcl                          

this must come before any other content or comment in the file

or, 

b) use the following configuration to avoid the need for a shebang

    # httpd.conf (requires a registry modification)                             
    LoadModule cgi_module modules/mod_cgi.so                                    
    ScriptAlias /cgi-bin/ "cgi-bin/"                                            
    LoadModule actions_module modules/mod_actions.so                            
    AddType application/x-httpd-tcl .tcl                                        
    AddHandler cgi-script .tcl                                                  
    ScriptInterpreterSource Registry                                            
    Action application/x-httpd-tcl "cgi-bin/ticol.exe"                          
    # Append index.tcl to DirectoryIndex                                        
    DirectoryIndex index.html index.html.var index.tcl                          

Windows Registry Changes:

    ; --------------------------------------------------                        
    Windows Registry Editor Version 5.00                                        
                                                                                
    [HKEY_CLASSES_ROOT\.tcl\Shell\ExecCGI\Command]                              
     @="C:\\path\\to\\example\\cgi-bin\\ticol.exe"                              
    ; --------------------------------------------------                        

The web server must be restarted after making the above changes

Example HTML file:  Save as: "index.tcl"

    puts "Content-type: text/html\r\n\r\n"                                      
    puts "Hello world"                                                          
    die                                                                         

Troubleshooting can be aided by entering the following setting into
ticol.ini which will enable command-logging

    ; ticol.ini configuration for CGI                                           
    [Config]                                                                    
    StartupLog=True        ; Command logging for CGI to ticol.log               

If you install to other than the standard /cgi-bin/ folder you may need
to configure the directory: Consult the Apache manual for full details

    <Directory "C:/path/to/ticol">                                              
       AllowOverride None                                                       
       Options +ExecCGI                                                         
       Order allow,deny                                                         
       Allow from all                                                           
    </Directory>                                                                

Finally, ensure you have an antivirus scanning exception for ticol.exe or
you may notice substantial delays (up to 30 seconds) before page load

Using TCX Files
---------------
To use TCX files as web page scripts, duplicate the registry entry and
httpd.conf entries for .tcl files and name as .tcx

    # httpd.conf (requires a registry modification)                             
    LoadModule cgi_module modules/mod_cgi.so                                    
    ScriptAlias /cgi-bin/ "cgi-bin/"                                            
    LoadModule actions_module modules/mod_actions.so                            
    AddType application/x-httpd-tcx .tcx                                        
    AddHandler cgi-script .tcx                                                  
    ScriptInterpreterSource Registry                                            
    Action application/x-httpd-tcx "cgi-bin/ticol.exe"                          
    # Append index.tcl to DirectoryIndex                                        
    DirectoryIndex index.html index.html.var index.tcl index.tcx                

Windows Registry Changes:

    ; --------------------------------------------------                        
    Windows Registry Editor Version 5.00                                        
                                                                                
    [HKEY_CLASSES_ROOT\.tcx\Shell\ExecCGI\Command]                              
     @="C:\\path\\to\\example\\cgi-bin\\ticol.exe"                              
    ; --------------------------------------------------                        

Ticol.ini File Changes for Webserver CGI
----------------------------------------

    ; File: ticol.ini                                                           
    ; This file should reside in the same folder as ticol.exe                   
    [Config]                                                                    
    StartupLog=False          ; Enables command logging for CGI mode            
    CGIDebug=False            ; Enables CGI mode debugging                      
    AutoExecScript=FALSE      ; Should be set to FALSE for CGI mode             
    PreprocessorVerbose=FALSE ; Should be set to FALSE for CGI mode             
    StackTrace=FALSE          ; Should be set to FALSE for CGI mode             

Using PHP with Ticol
--------------------
PHP can be used within an HTML web page to launch a Ticol Tcl CGI back-end
script with the aid of some PHP as follows:

HTML code:

-- Snip --------------------------------------------------------
                                                                                
<!DOCTYPE html>                                                                 
<html>                                                                          
    <body>                                                                      
        <?php                                                                   
            function print_procedure ($arg) {                                   
                echo exec("ticol.exe test.tcl");                                
            }                                                                   
            $script_name='test.tcl';                                            
            print_procedure($script_name);                                      
        ?>                                                                      
    </body>                                                                     
</html>                                                                         
                                                                                
-- Snip --------------------------------------------------------

Tcl script:

-- Snip --------------------------------------------------------
                                                                                
set a 10                                                                        
set b 20                                                                        
set c [expr $a + $b]                                                            
return c                                                                        
                                                                                
-- Snip --------------------------------------------------------

If the Tcl output spans multiple lines it can be captured into a PHP
array as follows:

PHP Code:

-- Snip --------------------------------------------------------
                                                                                
$array = array();                                                               
function print_procedure ($arg) {                                               
    echo exec("ticol.exe test.tcl",$array);                                     
}                                                                               
                                                                                
-- Snip --------------------------------------------------------

See also: cgi debugging, html, http, Ticol, ticol.ini

CGI Script Debugging

Debugging CGI scripts can be problematic. The main problem with CGI scripts
is that errors may result in a fatal server "500" error and generate no
useful output to aid diagnosis.

However, there are a number of things you can try to locate the source of 
any elusive bug

First of all you should look at the server's error.log and access.log files

Add this code as the first line of any CGI script during development to
guarantee errors are visible and don't trigger a "500" error

    puts "Content-type: text/html\r\n"
or:
    puts "Content-type: text/html\r\n\r\n" -nonewline
or:
    printf "Content-type: text/html\r\n\r\n"

Run the CGI script in console mode using the /CD (CGI Debug) argument. Then
place a halt command near the suspected cause and run the script from the
command-line using the /BP (Breakpoint) argument and single-step execution

Enable the following parameters in TICOL.INI during CGI code development

   [config]                                                                     
   Debug=True                                                                   
   CGIDebug=True                                                                

   these values will create additional output and also send early HTTP
headers back to the browser

See also: cgi setup, cgi

pid, tid  (Process and Thread ID)
 
    integer [pid]
    integer [tid]

Return the interpreter's process ID (PID) or thread ID (TID)

Example:

    puts "PID is [pid]"                                                         
    puts "TID is [tid]"                                                         

Result:

    PID is 616                                                                  
    TID is 33456                                                                

See also: info, username

Piping Commands

PowerShell has a feature which allows commands to be chained together or
"piped". Tcl does this naturally with nested commands but the resultant
nesting of brackets may be cumbersome if nesting is deep.

Given four commands, foo bar moo and par, Tcl would chain these left to
right as follows, where command or proc [foo] is executed first, followed
by [bar] using the results of [foo], next followed by [moo] using the
result of the previous] commands and so on...

    set result [par [moo [bar [foo]]]]                                          

See also: K combinator

Comparing Ticol/Tcl With PHP

Tcl is similar in appearance to PHP, Java, Javascript and other C-like
languages in its use of curly braces and with similar language constructs. 
However, Tcl is a command-based language which has minimal syntax. This
makes it easily extensible

Ticol has extended the Tcl language to add various useful commands.

Here is a brief summary of the differences between standard Tcl and PHP

    Item                                       Tcl         PHP        Ticol     
    -----------------------------------------------------------------------     
    Release date                               1988        1994       2015      
    Easy to learn                              Yes         Yes        Yes       
    Reference variables prefixed with $        Yes         Yes        Yes       
    Variables are always prefixed with a $     No          Yes        No        
    Braced flow-control constructs             Yes         Yes        Yes       
    Intended mainly for web-development        No          Yes        No        
    Complex variable dereferencing             No          No         Yes       
    Can be byte-precompiled                    Yes         Yes        No        
    Literal strings must be quoted always      No          Yes        No        
    C-like for statement                       Yes         Yes        Yes       
    Suitable for web CGI application?          Yes         Yes        Yes       
    Natural expression math format?            Yes         Yes        Yes       
    (Tcl can use prefix notation: "+ 1 1")                                      
    Associative arrays                         Yes         Yes        Yes       
    Standard (downwards) variable inheritance  No (upvar)  Yes        No        
    Static variables                           No          Yes        Yes       
    New flow control constructs possible?      Yes         No         Yes       
    Suitable for desktop application?          Yes (Tk)    No         No        
    Usable as a general IT script language     Yes         No         Yes       
    Usable as an interactive O/S shell?        Yes         No         Yes       
    Webserver CGI use on Windows               Yes         Yes        Yes       

See also: Ticol, Tcl

play_wav, stop_wav
 
    play_wav filename?.wav? ?-loop? ?-var v?
    stop_wav

Plays a Windows WAV file, optionally looping playback. Playback can be
halted after a fixed period by calling sleep, followed by stop_wav

If play_wav is immediately followed by stop_wav or if the script exits
before the WAV has been played then the file will not be audible

[play_wav] should be followed by a [sleep] to allow the WAV file to play
then a [stop_wav] command

Stored base64-encoded data read from a WAV file by a program such as
base64.exe can be played by allocating to a variable and passing via the
'-var v' argument

Example:

    puts "Played WAV? :"[bool [play_wav ".\\sample.wav"]]                       
    sleep 4000            # Allow sufficient time to play                       
    stop_wav              # Halt playback                                       

See also: beep

Integer to English Word Conversion Plugin

To load, use:    lib ticol_numword ?-verbose? ?-nocomplain?

    string [integer_to_words integer integer ?-noand? ?-hyphen? ?-zero?]
    integer [words_to_integer string]

Converts a signed 64-bit integer to an English word representation and
back from words to a 64-bit integer

Arguments:

    -noand     Don't include the word 'and'
    -hyphen    Replace hyphens with spaces
    -zero      Include leading zeroes

Acceptable values range from:

    INT_MIN+1 (-9223372036854775807)
to 
    INT_MAX    (9223372036854775807)

Signed negative numbers will be prefixed with the word, "minus". Zero
values will be represented by the word "zero". If overflow occurs the word
"overflow" is returned without error.

By default, numbers are output in English (UK) format with 'and' separators
between the hundred and tens values. This may be disabled using the '-noand'
argument.

[words_to_integer] will accept either  English numeric and magnitude words,
or numeric values or. The use of 'and' is optional and all instances are
ignored. Where numerals are used, they must include the correct magnitude,
for example: "9 2 thousand" does not represent "92 thousand". Each separate
number group will be substituted by the matching English key word, so
"200 20 two" will be evaluated as 222.

Examples:

    lib ticol_numwords                                                          
    puts [integer_to_words 12345]                                               
    puts [integer_to_words -1234567]                                            
    puts [integer_to_words 9223372036854775807]                                 
    newline                                                                     
    puts [words_to_integer {minus nine hundred and eighty six}]                 
    puts [words_to_integer ninety two thousand one hundred and twenty three]    
    puts [words_to_integer 92 thousand 1 hundred and 23]                        
    puts [words_to_integer [integer_to_words 987654321]]                        
    puts [words_to_integer "1 million 200 20 two thousand 8 hundred and 2"]     
    lib ticol_numwords -unload                                                  

Results:

    twelve thousand three hundred and forty five                                
    minus one million two hundred and thirty four thousand five\                
    hundred and sixty seven                                                     
    nine quintillion two hundred and twenty three quadrillion three\            
    hundred and seventy two trillion thirty six billion eight hundred\          
    and fifty four million seven hundred and seventy five thousand eight\       
    hundred and seven                                                           
                                                                                
    -986                                                                        
    92123                                                                       
    92123                                                                       
    987654321                                                                   
    1222802                                                                     

Where multiple words of the same magnitude are encountered, these will be
added (summed)

Example:

    puts [words_to_integer twenty three twelve]       # Add 23+12               
    puts [words_to_integer one two three four five]   # Add 1+2+3+4+5           
    puts [words_to_integer one hundred twenty three forty two] # Add 123+42     

Result:

    35                                                                          
    15                                                                          
    165                                                                         

QEMU Notes
----------
Some problems were experienced during testing with FP math on early
versions of QEMU emulator and NT4.0. Since it is unlikely that anyone
will be using this combination the problem has not been addressed.
The cause may be in the FPU emulation on some versions of QEMU

See also; plugins

ICMP Ping Plugin

To load, use:    lib ticol_ping ?-verbose? ?-nocomplain?

    string [ping hostname ?timeout-ms? ?-list?]
    string [ping ipv4-address ?timeout-ms? ?-list?]
    string [ip_from_host hostname]

Can ping either a hostname or an IPV4 address. The default timeout period is
4000 ms but this may be specified as an optional 2nd argument. 

By default the IPV4 address of the pinged node is returned but a list may
be returned by appending the -list argument. In which case a list containing
the IPV4 host address and round-trip response time in ms will be returned.

All versions of Windows are supported from Windows NT4/2K to Server 2016
ReactOS is not currently supported

Example:

    set hostname "localhost"                                                    
    set i 1;                                                                    
    while {$i < 11} {                                                           
       set r [ping $hostname -list]                                             
       printf "%3i\t%s\t%i ms\r\n" $i [lindex $r 0] [lindex $r 1]               
       incr i                                                                   
       sleep 1000                                                               
    }                                                                           

The command [ip_from_host] will perform a hostname DNS lookup to return 
the IPV4 address

Example:

    puts [ip_from_host www.google.com]                                          

Result:

    123.234.123.234                                                             

Example:

    puts [ip_from_host www.google.com -list]                                    

Result:

    123.234.123.234 301                                                         

See also: plugins, lib

pipe

    string [pipe <expr> ?, <expr>?...]

[pipe] strings together a series of statements or commands without 
requiring complex or deeply nested brackets. Most Tcl commands will 
'chain' or 'pipe' to to each other but may require nested brackets
A comma is used as the expression (or command) separator. This works
in a similar fashion to PowerShell's | pipe separator. 

Expressions are evaluated or 'piped' left-to-right with no precedence.

Example:

    # Left -> to -> right evaluation                                            
    # 2*3   = 6                                                                 
    # 6/1.5 = 4                                                                 
    # 4*atan(1) = PI                                                            
    puts [pipe 2 , * 3 , / 1.5 , * [funct atan 1]]                              

Result:

    3.141592653589792                                                           

Where a [proc] receives [pipe] arguments or participates in a pipe chain
it should use the following definition

    # Arg 1: arg is the chained accumulator (always the first argument)         
    # Arg 2: op is an optional operator                                         
    # Arg 3: val is an optional trailing value (args may also be used)          
    proc foo {arg} {                                                            
       return [* $arg 23.0]     # Return in Tcl eval order (op arg val)         
    }                                                                           
    proc bar {arg op} {                                                         
       return [$op $arg 5]      # Return in Tcl eval order (op arg val)         
    }                                                                           
    proc moo {arg op val} {                                                     
       return [$op $arg $val]   # Return in Tcl eval order (op arg val)         
    }                                                                           
    # Usage is equivalent to: [moo [bar [foo 2] +] / $pi]                       
    #                                   foo arg                                 
    #                               bar   arg  op                               
    #                          moo     arg        op val                        
    #                                                                           
    puts [moo [bar [foo 2] +] / $pi]                                            
    puts [pipe 2, foo, bar +, moo / $pi]                                        

Result:

    16.233804195373324                                                          
    16.233804195373324                                                          

See also: dot, expr, eval, math

Plugins Extensions for Ticol Tcl

Plugins are external modules which can be loaded to add additional 
functionality to Ticol. The capabilities of Ticol Tcl can be expanded
by the addition of external DLL routines as linkable plugins

A plugin is loaded using:

    integer [lib <plugin-name>?.dll? ?-verbose? ?-nocomplain?]                  

which returns a count of the number of exported commands successfully
loaded into the interpreter or zero on load failure. The return value
may be ignored

Plugin info is returned using 

    string [lib <plugin-name> -info]                                            

See: help lib for more information

A small number of plugins are supplied. Most of these were developed
for personal use and for specific projects so may contain only the
initially required functionality. e.g. ticol_json was designed to 
parse only HASP licensing data.

Some of the plugins are new, experimental or feature-incomplete:

Title         Lib DLL             Description              Help Topic           
-------------------------------------------------------------------------       
ANSI          ticol_ansi.dll      ANSI text output         See:ansi             
ARP           ticol_arp.dll       ARP and RARP commands    See:arp              
ASM           ticol_asm.dll       Pseudo assembler         See:asm              
Big Numbers   ticol_bignum.dll    Arbitrary precision math See:big math         
Big Text      ticol_bigtext.dll   Large Console Text       See:big text         
Brainfuck     ticol_bf.dll        Brainfuck interpreter    See:bf               
Bitmap        ticol_bmp.dll       Bitmap encoding          See:bmp              
Call DLL      ticol_calldll.dll   Call an external DLL     See:calldll          
CArray        ticol_carray.dll    C-Like fixed arrays      See:carray           
CGI           ticol_cgi.dll       Basic CGI interface      See:cgi              
CPU           ticol_cpu.dll       Basic CPU information    See:cpu              
CRC           ticol_crc.dll       CRC calculation          See:crc              
CScript       ticol_cscript.dll   VBScript/CScript         See:cscript          
Database      ticol_db.dll        Random access database   See:db               
DHCP          ticol_dhcp.dll      DHCP commands            See:dhcp             
DNS           ticol_dns.dll       DNS lookup               See:dns              
Event Logging ticol_event.dll     Windows Event Logging    See:event            
FTP           ticol_ftp.dll       A simple FTP client      See:ftp              
Hasp          ticol_hasp.dll      Hasp Key querying        See:hasp             
Highlight     ticol_highlight.dll HTML code highlighting   See:highlight        
HTML          ticol_html.dll      HTML code processing     See:html             
INI           ticol_ini.dll       INI file handling        See:ini              
JSON          ticol_json.dll      Convert JSON to Tcl list See:json_to_list     
Line Get      ticol_line.dll      Retrieve lines from text See:ticol_line       
Linked List   ticol_list.dll      Binary Linked Lists      See:linkedlist       
Map Drive     ticol_mapdrive.dll  Drive Letter Mapping     See:mapdrive         
Miscellaneous ticol_misc.dll      Misc routines            See:misc             
NumWords      ticol_numword.dll   Convert integer to words See:numwords         
Ping          ticol_ping.dll      Simple ICMP ping         See:ping             
R2D           ticol_r2d.dll       Roman to decimal convert See:r2d              
REG           ticol_reg.dll       Simple registry services See:reg              
RLM           ticol_rlm.dll       RLM licence querying     See:rlm              
RTF           ticol_rtf.dll       Simple RTF text output   See:rtf              
Service       ticol_svc.dll       Windows service query    See:service          
Sci Numbers   ticol_sci.dll       Scientific format number See:sci              
SMTP Email    ticol_mail.dll      SMTP emailer             See:mail             
SockPing      ticol_sockping.dll  Socket ping a host       See:sockping         
SQLite        ticol_sqlite.dll    SQLite 3 plugin          See:sql              
TCX           ticol_tcx.dll       Embedded TCX handling    See:tcx              
TicASM        ticol_asm.dll       Assembler interpreter    See:ticol_asm        
Variant       ticol_variant.dll   Variants for calldll     See:variant          
VB            ticol_vb.dll        Some VB-like commands    See:vb               
Windows GDI   ticol_win.dll       Windows GDI graphics     See:win              
Wordcount     ticol_wordcount.dll Word or char count       See:wordcount        
XBase         ticol_xbase.dll     Dbase 3/4 Database       See:xbase            
ZIP           ticol_zip.dll       Simple ZIP package       See:zip              

Tests for these plugins are included with the unit test scripts

See: lib interface - for information about how to write plugins in C/C++

See also: lib, Ticol, lib interface

Bitmap Encoding Plugin

A plugin to encode text in Windows Bitmap (BMP) format

      To load, use:    lib ticol_bmp ?-verbose? ?-nocomplain?

Command and Syntax        

    bool [bmp write filename string|var ?-var? ?-fore rgb? ?-back rgb?\
       ?-encrypt?]
    bool [bmp read filename newvar ?-nocomplain?]
    list [bmp info filename]
    void [bmp graph filename ?-w N? ?-r? ?-crop? ?-asterisk? ?-bytes N?\
       ?-fore colour_code? ?-back colour_code?]
    void [bmp graph -var name ?-w N? ?-r? ?-crop? ?-asterisk? ?-bytes N?\
       ?-fore colour_code? ?-back colour_code?]

A crude form of steganography. Converts text-based data in a string or
variable into Windows BMP format file, which allows the data to be sent
via email or other messaging systems without tampering. The data is not
encrypted but [encrypt] can be used to encode data passed to [bmp]

[bmp read] will read back the original file text to a variable
bmpconv.exe can also be used to convert back to the original file

[bmp write] is binary-compatible. If a file contains binary data, use -var
and pass a [bmp read] binary variable name. The binary data can be verified
using [md5 varname -var]. The resulting file must not be edited using a
graphics editor as BMP 'row-padding' is not followed 

[bmp write -encrypt] will perform basic level symmetric encryption on
the data payload. Decryption using [bmp read] is automatic as long as
the file is not edited

[bmp] encryption is low-strength and serves only to obfuscate (obscure)
the data contents. You can combine [bmp -encrypt] with the more secure
[encrypt] command or call an external encryption API using [calldll]

[bmp graph] will show a graphic dump of pixel data, optionally in reverse
(-r) order. Note that BMP images typically store the data 'upside-down'
Red symbolises 1, grey 0. Reverse (-r) shows the data as stored, otherwise
how it is visible in a paint package is shown. -w width bytes can be shown
per line. This command does not produce decoded visual representations of
general graphic bitmaps, just the bit sequence of an encoded file

If -bytes N is specified then display is capped to N bytes, otherwise
all bytes are displayed

[bmp graph] can read data either from file (default) or from a variable
by using the ?-var varname? argument

The foreground and background image colours should be specified as RGB
values in the format RRGGBB. Hex is usually the best way to do this: 

    0x000000 # (black)                                                          
    0xFFFFFF # (white)                                                          
    0xFF0000 # (red)                                                            
    0x00FF00 # (green)                                                          
    0x0000FF # (blue)                                                           

[bmp info] returns a Tcl list containing information about a file as
follows:

    filesize-bytes data-block-size width height colours bits-per-plane
    bytes-per-pixel padding-space

Example:

    bmp write qbf.bmp $qbf -fore 0xFF0000 -back 0x0                             
    @ start ticol_qbf.bmp                                                       
    bmp read qbf.bmp s                                                          
    puts $s                                                                     

Result:

    Displays bitmap in associated paint package                                 
    The quick brown fox jumps over the lazy dog                                 

Example:

    randomstr 4096 5 -var s -nocomplain    # Set some binary data               
    puts "Starting data MD5: [md5 s -var]"                                      
    bmp write "randomstr.bmp" s -var       # Write binary                       
    unset s                                # Clear s                            
    bmp read randomstr.bmp s               # Read binary back to s              
    puts "\[binary length s\]=[binary length s]"                                
    puts "Read-in data MD5:  [md5 s -var]"                                      

Result:

    Starting data MD5: b46bf481af4fee5b08a9d3d76c135301                         
    [binary length s]=4096                                                      
    Read-in data MD5:  b46bf481af4fee5b08a9d3d76c135301                         

Example: (external obfuscation)

    bmp write test.bmp [encrypt $qbf secret] -fore 0xff0000 -back 0x0           
    bmp read test.bmp s                                                         
    puts [decrypt $s secret]                                                    

Result:

    The quick brown fox jumps over the lazy dog                                 

Example: (internal obfuscation)

    bmp write test.bmp $qbf -encrypt -fore 0xff0000 -back 0x0                   
    bmp read test.bmp in                                                        
    puts $in                                                                    
    assert $in {eq $_ $::qbf} -v -line #__LINE__                                

Result:

    The quick brown fox jumps over the lazy dog                                 
    assert: Line 4: PASS 'eq $_ $::qbf'                                         

Displaying Bitmap Images
------------------------
Although intended for debugging display of encoded data, small,2-colour
graphic bitmap images may be displayed at the console using [bmp graph],
The '-crop' argument will force line breaks on and -asterisk can be used
to produce printable output which may also be redirected to a file. 
Otherwise, console graphics characters will be used

-asterisk may be used to draw using * characters

The background character will be shown as 75% shaded unless -solid or
-asterisk is used

Console colours can be set using standard Ticol (Windows console) screen
colour integers or colour names using -fore and -back

The console width must be set large enough to avoid wrapping at the
configured font size

This feature could be used to add a graphic banner for a Tcl or batch
script.

Example:

    # hello.bmp is 60 x 20 px / 2-colours                                       
    lib ticol_bmp                                                               
    bmp graph hello.bmp -crop -asterisk                                         

Result:

    ************************************************************                
    ****     ********     ***************  ****  ***************                
    *****  ***********  ****************  ****  ****************                
    *****  ***********  ****************  ****  ****************                
    *****  ***********  ****************  ****  ****************                
    *****  ***********  ****************  ****  ****************                
    *****  ***********  ****************  ****  ****************                
    *****  ***********  ****************  ****  ****************                
    *****  ***********  *******    *****  ****  ******    ******                
    *****               ******  **  ****  ****  ****  ***  *****                
    *****  ***********  ****** ***  ****  ****  **** ****   ****                
    *****  ***********  *****  ***  ****  ****  ***  *****  ****                
    *****  ***********  *****  ** ******  ****  ***  *****  ****                
    *****  ***********  *****    *******  ****  ***  *****  ****                
    *****  ***********  *****  *********  ****  ***  *****  ****                
    *****  ***********  *****   ***** **  ****  ***   **** *****                
    ****   ***********  ******       ***  ****  ****  ***  *****                
    ****    **********   ******     ****    **    ***    *******                
    ************************************ ***** *****************                
    ************************************************************                

Displaying Internally Stored BMP Images
---------------------------------------

Example:

    # Generate using base64.exe                                                 
    set bmp "Qk3eAAAAAAAAAD4AAAAoAAAAPAAAABQAAAABAAEAAAAAAKAAA                  
    AASCwAAEgsAAAIAAAACAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAEAAAEA                  
    DB8HgeAAAPAMP4xhgAAAwAx4nCGAAADADHBYMYAAAMAMYBgxgAAAwAxgGD                  
    GAAADADGAYMYAAAMAMIAhxgAAAwDwxjGGAAADADA+DwYAAAMAAAAABgAAA                  
    wAAAAAGAAADAAAAAAYAAAMAMAAABgAAAwAwAAAGAAEDAAAAAAYAAf/+AAA                  
    AAwAAAAAAAAAAAA"                                                            
                                                                                
    # Display from var 'bmp', crop and rotate from stored orientation           
    bmp graph -var bmp -crop -r                                                 

Result:

    Displays the Ticol logo                                                     

See also: plugins

Network Drive Map Plugin

A plugin to map and unmap network drive letters

      To load, use:    lib ticol_mapdrive ?-verbose? ?-nocomplain?

  Command and Syntax

  bool   [mapdrive letter?:? unc-share ?-user u? ?-pass? p ?-persistent? \      
              ?-prompt? ?-delete?]                                              
  string [mapdrive * unc-share ?-user u? ?-pass? p ?-persistent? \              
              ?-prompt? ?-delete?]                                              
                                                                                
  bool [mapdrive *|letter?:? -delete ?-force?]                                  

Maps a drive letter to a UNC share via the Windows mpr.dll API interface
[mapdrive letter] will return boolean 1 or 0
[mapdrive *] will return either a drive letter as "x:" on success or ""
on failure

The share must exist and be in the format:

    \\server\share-name?\path...?   # String must be escaped
    or
    //server/share-name?/path...?   # Preferred format

A colon character is optional when specifying a drive-letter

Backslash and Tcl reserved characters must be escaped unless calling from
the Ticol CLI command prompt

Unix-style forward slash path separators are supported

Examples:

    # Where the highest drive allocated before running is O:                    
    puts [mapdrive * \\\\snoopy\\c\$]      # Note the escaped $                 
    puts [mapdrive * //snoopy/c$]                                               
    puts [mapdrive r: \\\\snoopy\\c\$]     # Note the escaped $                 
    puts [mapdrive s: //snoopy/c$]                                              
    puts [mapdrive * //snoopy/c[chr 36]]   # Avoiding literal $                 

Results:

    p:                                                                          
    q:                                                                          
    1                                                                           
    1                                                                           
    r:                                                                          

Errors
------
Use [win_error] to determine the cause of any error

See also: plugins     

Miscellaneous Routines Plugin

A plugin containing miscellaneous useful routines

      To load, use:    lib ticol_misc ?-verbose? ?-nocomplain?

  Command and Syntax         # Comments
                                                                                
  ip_match mask ip-address   # Match an IP address to a mask (wildcards ok)     
                             # Returns a boolean 1/0                            
  ip_filter list ip-address  # Match an IP address to a list of addresses       
  cidr_match mask ip-address # Match an IP address to a CIDR mask ?.?.?.?/N     
                             # address / bitmask                                
                             # Returns a boolean 1/0                            
                             # e.g. [cidr_match 192.168.3.0/24 192.168          
  is_mac mac-address ?-f?    # Checks if string is a formatted MAC address      
                             # Returns a boolean 1/0                            
  is_mouse_present           # Returns a boolean 1/0                            
  is_networked               # Returns a boolean 1/0                            
  is_safe_mode               # Returns a boolean 1/0                            
  is_vhdboot                 # 1 if booted from VHD (Win8/Server 2012+ only)    
                             # Use [info winver -vernum] > 6.1 to control       
  monitorcount               # Returns a count of connected monitors            
  rot13 string               # Perform rot-13 transformation on string          
                             # and return the result (source is unchanged)      
  sysmetrics N               # Call GetSystemMetrics API with argument N        
                             # Returns an integer value                         
                             # See an API guide for calling values+returns      
  uptime                     # Uptime in seconds since last boot (Win XP+)      
  volume serial X:           # Return the serial of a disk as a hex value       
  volume filesystem X:       # Return the file system type of a disk volume     
  wallpaper get              # Get the path+filename of the wallpaper           
  wallpaper set file ?align? # Set the current wallpaper with alignment...      
                             # -centre, -tile, -stretch (returns a boolean)     

Examples:

IP match can use wildcards * or ? to represent octets or individual digits
octets may be omitted from the right-hand end of the IP address mask

    puts [ip_match 192.168.3.* 192.168.3.1]                                     
    puts [ip_match 192.168.3.* 192.168.101.1]                                   
    puts [ip_match 10.* 10.11.12.13]                                            

Results:

    1                                                                           
    0                                                                           
    1                                                                           

Example:

CIDR (Classless Inter-Domain Routing)

[cidr_match] requires a full IP address with a bitmask value /N where
N specifies significant bits from the left hand end of the IP address

    puts [cidr_match 192.0.0.0/8 192.168.101.1]                                 
    puts [cidr_match 192.168.3.0/24 192.168.101.1]                              

Result:

    1                                                                           
    0                                                                           

Example:

    puts [is_mac abcdefgh]              # Too short + invalid characters        
    puts [is_mac 001122aabbcc]          # Valid unformatted MAC                 
    puts [is_mac 00-11-22-aa-bb-cc -f]  # Check valid formatted MAC             

Result:

    0                                                                           
    1                                                                           
    1                                                                           

Example:

     puts [rot13 "Hello world"]                                                 

Result:

    Uryyb jbeyq                                                                 

Example:

    puts [volume serial c:]                                                     

Result:

    A1B2C3D4                                                                    

Example:

    puts [volume filesystem c:]                                                 

Result:

    NTFS                                                                        

Example:

    set bt [- [now] [/ [uptime] [expr 24*3660.0]]]                              
    set ut [uptime]                                                             
    puts "Uptime is: $ut seconds"                                               
    puts "Boot time: $bt"                                                       
    puts "\[now\] is [now]"                                                     

Results:

    Uptime is: 183206 seconds                                                   
    Boot time: 43089.035953438070000                                            
    [now] is 43091.121631944443000                                              
    assert: Line 82: PASS '> $_ 0'                                              
    Boot date+time: 2017/12/21 15:08:46                                         

See also: long_to_ip, ip_to_long, Plugins, Windows Version Numbers

Registry Plugin (ANSI)

    To load, use:    lib ticol_reg ?-verbose? ?-nocomplain?

Path/Key arguments must be supplied in Tcl "backslash-escaped" format. 
The result (if string or list) will be returned in the same "backslash-
escaped" format. Only ANSI strings are supported by [reg]. Unicode/MBCS
strings are not supported

Supported in Windows 2k/XP or higher

Syntax:

    reg <command> path ?value? ?-type?                                          
    bool    [reg set]                                                           
    string  [reg get]                                                           
    bool    [reg del]                                                           
    regtype [reg type]                                                          

For [reg type] the return will be a string equivalent of one of the 
following keywords

    enum {                                                                      
       REG_NONE                                                                 
       REG_SZ                                                                   
       REG_EXPAND_SZ                                                            
       REG_BINARY                                                               
       REG_DWORD                                                                
       REG_DWORD_BIG_ENDIAN                                                     
       REG_LINK                                                                 
       REG_MULTI_SZ                                                             
       REG_RESOURCE_LIST                                                        
       REG_QWORD                                                                
       REG_UNKNOWN;                                                             
    }                                                                           

       Use either 'reg' or 'registry'

    reg get "HKEY_VALUE\\path" keyname ?-binary var? ?-type?                    

       for [reg get] the default type is -auto

    reg set "HKEY_VALUE\\path" keyname value ?-binary var? ?-type?              

       or [reg set] the default type is -string

    reg del "HKEY_VALUE\\path" keyname ?value?                                  

       if ?value? is omitted then the entire subkey and values are deleted

    reg type "HKEY_VALUE\\path" keyname                                         

       Where the optional '-type' argument is one of:

    -auto (default)                                                             
    -dword       Store a 32-bit HEX value                                       
    -qword       Store a 64-bit HEX value                                       
    -list        Decompose/reassemble a Tcl list (REG_MULTI_SZ)                 
    -multiline   Stores/retrieves CRLF delimited text as REG_MULTI_SZ           
    -string      Stores/retrieves a Tcl list as REG_SZ                          

The type argument must be the last item in the command

Binary strings cannot be passed via the Tcl command line. Instead, for 
[reg set] and [reg get] an argument reference to the variable-name is
passed using "-binary varname" and any preceding variable argument is
ignored

If DWORD or QWORD values are supplied with HEX values, they must include
a leading 0x - e.g. 0xffffffff

Example:

    puts [bool [reg set HKEY_CURRENT_USER\\Console ColorTable06 \               
    0x00508dfc -dword]                                                          
    puts [inttohex [reg get HKEY_CURRENT_USER\\Console \                        
    ColorTable06 -dword]]                                                       
    puts [reg get HKEY_CURRENT_USER\\Console ColorTable06 -dword]]              

Result:

    True                                                                        
    0x508dfc                                                                    
    5279228                                                                     

Where types are mixed between binary and non-binary, strings will be
truncated at the first null character

Example: (Sets partial string)

    binary set a \xfe\xffhello\x21\x00\x22world\x02\xff\xfe -length 16          
    reg set HKEY_CURRENT_USER\\Software\\Ticol BINDATA -binary a                
    reg get HKEY_CURRENT_USER\\Software\\Ticol BINDATA -binary b                
    puts [binary tohex b]                                                       
    puts [reg get HKEY_CURRENT_USER\\Software\\Ticol BINDATA]                   

Result:

    feff68656c6c6f210022776f726c6402                                            
    ¦ hello!                                                                    

Note that -multiline and -string affect the interpretation of sent/returned
Tcl data. Both types are stored as REG_MULTI_SZ but will be encoded or
decoded as appropriate. You can store in Tcl list format and retrieve as
a CRLF-delimited multi-line string (and vice-versa)

Example: (Using line-wrap, where a space is added after the \)

  puts "Country: '[reg get {HKEY_CURRENT_USER\\Control\                         
  Panel\\international} sCountry]'"                                             

Result:

  'United Kingdom'                                                              

Example: (from https://www.tcl.tk/man/tcl8.4/TclCmd/registry.htm#M9)

  set ext .tcl                                                                  
  # Read the type name                                                          
  set type [registry get HKEY_CLASSES_ROOT\\$ext {}]                            
  # Work out where to look for the command                                      
  set path HKEY_CLASSES_ROOT\\$type\\Shell\\Open\\command                       
  # Read the command                                                            
  set command [registry get $path {}]                                           
  puts "Filetype \"$ext\" opens with $command"                                  

Example:

 reg set HKEY_CURRENT_USER\\Software\\Ticol TestValue "Hello world" -string     
 puts [reg get HKEY_CURRENT_USER\\Software\\Ticol TestValue]                    
 puts [reg type HKEY_CURRENT_USER\\Software\\Ticol TestValue]                   
 puts [bool [reg del HKEY_CURRENT_USER\\Software\\Ticol TestValue]]             

Results:

  Hello world                                                                   
  REG_SZ                                                                        
  true                                                                          

See also: plugins, calldll

SQLite SQL Plugin

A plugin which offers rudimentary ANSI SQLite 3 database capability

    To load, use:    lib ticol_sql ?-verbose? ?-nocomplain?

    handle [sql open database_file]
    variant [sql query handle querystring]
    sql close handle
    sql free variant|variant-address

To run a query you must first use [sql open] to obtain a database handle
This handle is passed to [sql query]. You should close the handle using
[sql close] after you have finished querying the database

SQL queries return a VARIANT object. It is vital that this is freed by a
call to [sql free] using either the dereferenced variable ($var) or name
(var) and this must be called before the variable goes out of scope. 
Do NOT use [varray unset var] to release structures returned by this
plugin (see example below)

The returned VARIANT table may be manipulated using the [varray] commands

If a database file does not exist then [sql open] will create it

Example:

    set handle [sql open c:\\path\\to\\my.db]                                   

Example;

    set v [sql query $handle "SELECT * from Customers"]                         

Example:

    sql free v                                                                  

Example:

    sql close $handle                                                           

Full Example:

    lib ticol_sqlite.dll                                                        
    lib ticol_varray.dll                                                        
    newline                                                                     
                                                                                
    set handle [sql open ".\\albums.db"]                                        
    if { $handle } {           # Null is not returned for file not found        
       set v [sql query $handle "SELECT * from Artists"]                        
       set rows [varray size $v]                                                
       set cols [varray size $v 2]                                              
                                                                                
       for {set j 0} {< $j $rows} {incr j} {                                    
          for {set i 0} {< $i $cols} {incr i} {                                 
             puts "[varray get v $j $i]\t" -nonewline                           
          }                                                                     
          newline                                                               
       }                                                                        
       newline                                                                  
       puts "Retrieved table has $rows row(s) and $cols col(s)"                 
       sql free v              # Ensure [sql free] is used on the query         
       sql close $handle                                                        
    } else {                                                                    
       die "Can't open SQL database"                                            
    }                                                                           

Result:

    ArtistID ArtistName                                                         
    1        Puddle of Mudd                                                     
    2        Frank Zappa                                                        
    3        Love/Hate                                                          
    4        Cheap Trick                                                        
    5        Megadeth                                                           
    6        Foo Fighters                                                       
                                                                                
    Retrieved table has 183 row(s) including header, and 2 column(s)            

See also: varray, plugins

CRC32 Plugin

To load, use:    lib ticol_crc ?-verbose? ?-nocomplain?

    value [crc value ?-hex? ?-noprefix?]
    value [file_crc filename ?-hex? ?-noprefix?]

Extends [file] by adding: [file crc filename] into the namespace

Calculates the 32-bit CRC checksum of an ANSI string or file and is tested
on files up to 10Gb in size

By default a decimal value is returned, prefixed with "0x" unless -noprefix
is used. A hex value can be returned by appending the argument -decimal

[file_crc] will calculate the CRC for a file. This is quite efficient even
on fairly large files of up to 1Gb. The maximum file size is 4Gb as this
is the largest which may be accommodated by the 32-bit integer range

Example:

    puts [crc $qbf]                                                             

Result:

    0x414fa339                                                                  

Example:

    puts [file_crc VS97PRO3.ISO]                                                

Result:

    0x22ffdfcc                                                                  

See also: plugins

CScript - VBScript Console Link Plugin

To load, use:    lib ticol_cscript ?-verbose? ?-nocomplain?

    void [cscript script-string ?-echo? ?-nocomplain?]

Offers basic level calling of the BASIC script language from the 
Windows scripting VBScript host which can allow a number of scriptable
objects to be called including, for example, WMI or registered 
applications such as MS Word. This plugin may be useful where the use
of an intermediate script file is not desired.

At present, it is not possible to return a value directly to Ticol as
VBScript, other than for 'Expr()' is not designed to pass results back
to a caller under normal circumstances. Output could be redirected
to a file or database.

To run a script file, load the file using [readfile]

The script is converted to Unicode before running but will be handled
by Ticol as ANSI. '-echo' may be used to echo the Unicode script

Tcl strings containing escaped characters and in particular, escaped
double quotes or paths with backslash characters, MUST be escaped using
[unescape] before passing to [cscript]. This will translate escaped
double-quotes embedded within strings back to literals
A non-Tcl script read directly from file may not require to be escaped.

VBScript code called by [cscript] is currently run in the main thread

The '-nocomplain' allows errors to be ignored. The script call will
return with no error if any exception occurs, otherwise the error will
be fatal and halt the script, displaying the error number and message.

In some circumstances it may be better to use [spawn] or [exec] to 
call the WSH directly via cscript.exe or wscript.exe as this will allow
capture and return of text output back to a Ticol variable.

Example:

    lib ticol_cscript                                                           
    set s "msgbox \"Hello World\"" # String contains escaped double-quotes      
    cscript [unescape $s]          # so we must ensure these are unescaped      
    lib ticol_cscript -unload                                                   

Result:

    <A MsgBox displays 'Hello world'>                                           

Console Output
--------------
By default, VBScript passes printed output to a messagebox to display.
The following scriptlet may be used in order to provide console output
using stdout.WriteLn:

    Set fso = CreateObject ("Scripting.FileSystemObject")                       
    Set stdout = fso.GetStandardStream (1)                                      
    Set stderr = fso.GetStandardStream (2)                                      

Example:

    set s "\                                                                    
    Set fso = CreateObject (\"Scripting.FileSystemObject\")                     
    Set stdout = fso.GetStandardStream (1)                                      
    stdout.WriteLine \"Hello world\""                                           
    lib ticol_cscript                                                           
    cscript [unescape $s]                                                       
    lib ticol_cscript -unload                                                   

Result:

    Hello world                                                                 

Example:

    # Note that Tcl requires embedded dquotes to be escaped in source           
    # We must use [unescape] to pass to VBScript                                
                                                                                
    set s "                                                                     
    Set fso = CreateObject (\"Scripting.FileSystemObject\")                     
    Set stdout = fso.GetStandardStream (1)                                      
    strComputer = \".\"                                                         
    Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")              
    Set cols = objWMIService.ExecQuery(\"SELECT * FROM Win32_ComputerSystem\")  
    For Each objItem In cols                                                    
       stdout.WriteLine \"Name: \" & objItem.Name                               
       stdout.WriteLine \"Manufacturer: \" & objItem.Manufacturer               
       stdout.WriteLine \"Model: \" & objItem.Model                             
    Next"                                                                       
                                                                                
    lib ticol_cscript                                                           
    cscript [unescape $s]                                                       
    lib ticol_cscript -unload                                                   

Enabling WScript (WSH)
----------------------
The Windows Scripting Host (WSH) is installed by default in most
versions of Windows. It may be disabled for security reasons.
If you wish to re-enable and are having problems accessing the 
WScript object, try this registry setting

    Windows Registry Editor Version 5.00                                        
                                                                                
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Script Host\Settings]        
    "Enabled"=dword:00000001                                                    

Available Objects
-----------------
The following objects (ProgIDs) are examples of those known to be
available to use with VBS -> CreateObject()
(depending on 3rd-party application installation in some cases);

    InternetExplorer.Application
    Scripting.Dictionary
    Scripting.FileSystemObject
    Shell.Application
    System.Collections.ArrayList
    System.Collections.Queue
    System.Collections.SortedList
    WbemScripting.SWbemLocator
    Word.Application

    Word.Window

Unavailable Objects
-------------------
The following objects (ProgIDs) are not available:

    Scripting.*  Other than listed above
    WScript      (Root) e.g. Wscript.Version
                 Wscript methods are not inherited when launched as a
                 VBScript engine. The common reason to include is to
                 gain access to console printing using WScript.Echo

    https://blogs.msdn.microsoft.com/ericlippert/2003/10/08/why-\
    cant-i-create-the-wscript-object/

Platforms
---------
Should work on all Windows platforms which have WSH installed
from NT4/SP6a upwards. Does not work on ReactOS

CGI Use
-------
[flush] should be called before calling [cscript], particularly in
order to flush any HTTP headers sent using [puts]. [cscript]
cannot flush pending console output.

    # CGI Server Script                                                         
    # Where variable 's' contains valid VBScript code                           
    puts "Content-type: text/html\r\n\r\n" -nonewline                           
    flush                                                                       
    lib ticol_cscript                                                           
    cscript [unescape $s]                                                       
    lib ticol_cscript -unload                                                   

Recommended IDE
---------------
VBSEdit

    https://www.vbsedit.com

Alternative Solutions
---------------------
A more flexible alternative is to use [spawn] with cscript.exe as this
will offer the full range of commands including WScript.*

Windows cscript.exe accepts only script files. Where direct commands are
preferred rather than an intermediate script files, the author's VBS.EXE
may be used together with [spawn]

Example:

    unset x -nocomplain                                                         
    spawn x cscript.exe //NoLogo myscript.vbs                                   
    puts $x                                                                     

                                                                                
    spawn x vbs.exe /w Console.WriteLine \"Hello world\"                        
    spawn x vbs.exe /w Console.WriteLine 4*atn(1)                               

More Information
----------------
ActiveX objects may be viewed using the NirSoft ActiveXHelper utility

    http://www.nirsoft.net/utils/axhelper.html

The VBScript is documented here:

    https://docs.microsoft.com/en-us/previous-versions/windows/\
    internet-explorer/ie-developer/scripting-articles/d1wf56tt(v=vs.84)
    https://www.w3schools.com/asp/asp_ref_vbscript_functions.asp

See also: spawn, plugins

Random File Database Plugin

To load, use:    lib ticol_db ?-verbose? ?-nocomplain?

    bool   [db create filename {list of fields} ?comment? ?-y?]                 
    handle [db open filename ?mode?]      # Open a database                     
    string [db time handle]               # Last write update time              
    long   [db count handle]              # Count of records written            
    string [db get handle N]              # Get record. Record is base 1        
    bool   [db put handle ?record?]       # Write record. Record is base 1      
    string [db field handle N]            # Field data. Field is base 0         
    short  [db fieldcount handle]         # Count of fields in database         
    string [db fieldname handle N]        # Field name. Field is base 0         
    long   [db fieldsize handle N]        # Field size. Field is base 0         
    short  [db comment handle string]     # Add a database comment              
    bool   [db close handle]              # Close the database                  
    string [db info handle -list]         # Get database info as Tcl list       
    bool   [db is_db handle]              # Validates a file as Ticol DB        
    void   [db info handle]               # Get database info                   

Offers XBase/Dbase III like random-file capabilities with a similar
command-set. Unlike the XBase plugin this is not limited to restrictive
field lengths. DBase is limited to a field length maximum of 
<unsigned char> or 254 bytes per field

A field in the 'db' plugin may be any integer value up to <signed long>
bytes although, in practice it is strongly recommended that field lengths
be kept to under say 1,000 or so bytes per field. The enhanced storage
capacity makes it feasible to store whole web pages or scripts as database
records. XBase and DB files may be used together

Record offsets are a 'base 1' value. Field offsets are a 'base 0' value
That is, the first record is record '1'. The first field is field '0'
Fields are treated as an array and array indices (as we all know) start
at '0'. All field types are 'character' and the DB plugin performs no
data-validation. Binary data must be ASCII encoded (See: [base64] and
[tohex])

[db create] creates an opens a new DB file. It requires a Tcl list 
argument in the form of a series of tuples as {name length}. 

Example: db create $filename {{Name 20} {Address 20}}

Example: db create $filename {{Name 20} 
                              {Address 20}
                             }

Example:

    set handle [db create $filename {{Name 20} {Address 20} {Phone 20}} \       
        "This is a comment" -y]                                                 
    db set $handle 0 "Biffa Bacon"                                              
    db set $handle 1 "49 Letsby Avenue"                                         
    db set $handle 2 "(0123) 4567890"                                           
    db put $handle 1                         # Write record 1                   
    db set $handle 0 "Fatha Bacon"                                              
    db set $handle 1 "49 Letsby Avenue"                                         
    db set $handle 2 "(0976) 543210"                                            
    db put $handle 2                         # Write record 2                   
    db get $handle 1                         # Read record 1                    
    puts "Field 0: '[db field $handle 0]'"                                      
    puts "Field 1: '[db field $handle 1]'"                                      
    puts "Field 2: '[db field $handle 2]'"                                      
    db close $handle                                                            

Results:

    Field 0: 'Biffa Bacon'                                                      
    Field 1: '49 Letsby Avenue'                                                 
    Field 2: '(0123) 4567890'                                                   

Database Structure (in C/C++)
-----------------------------

 +----------------------------+                                                 
 | Ticol DB Record Structure  |                                                 
 +============================+ <-   0                                          
 | Header prolog              |                                                 
 | 276 bytes                  |                                                 
 +----------------------------+ <- 272                                          
 | Header suffix * N records  |                                                 
 | field_count * DB_RECORD    |                                                 
 +----------------------------+ <- 272 + (field count * sizeof(DB_RECORD))      
 | Field data...              |                                                 
 | record_size * record_count |                                                 
 +----------------------------+                                                 

                                                                                
_DB_HEADER_TAG="DBX1"             // 4 bytes header ID tag                      
                                                                                
const _DB_COMMENT_MAX=200;        // A maximum of 200 character comment         
                                                                                
typedef struct DB_HEADER_TYPE {   // +- Header Prolog --------------------+     
  char tag[4];                    // |   4 File type ID tag (magic number)|     
  long version;                   // |   4 > 0  (_DB_HEADER_TAG)          |     
  long prolog_length;             // |   4 Prolog header length           |     
  struct tm ts;                   // |  36 Time struct sizeof(struct tm)  |     
  long record_count;              // |   4 How many records in the DB     |     
  long highest_write;             // |   4 Highest write so far           |     
  long record_length;             // |   4 Record length (All fields)     |     
  long header_length;             // |   4 Total length of header         |     
  long field_count;               // |   4 Field count                    |     
  char comment[_DB_COMMENT_MAX+1];// | 201 200 byte descriptive comment   |     
  char reserved[3]                // |   3 Reserved for byte-alignment    |     
} DB_HEADER, *PDB_HEADER;         // +------------------------------------+     
                                  // | 272 Total (34 x 8 bytes)           |     
                                  // +------------------------------------+     
                                                                                
// field_count * DB_RECORD (N x 85)                                             
                                                                                
const _MAX_DB_FIELD_NAME=80;                                                    
                                                                                
typedef struct DB_RECORD_TYPE {   // +- Record Header Suffix -------------+     
  long length;                    // |   4 Field length                   |     
  char name[_MAX_DB_FIELD_NAME+1];// |  81 The field name + 1             |     
  char reserved[3];               // |   3 Reserved (byte alignment)      |     
} DB_RECORD, *PDB_RECORD;         // +------------------------------------+     
                                  // |  88 Total (11 x 8 bytes)           |     
                                  // +------------------------------------+     
                                                                                
// Header Suffix Size = sizeof(DB_RECORD) * field_count                         
                                                                                
// Record count = ((Entire Header size - 272) / 88)                             
                                                                                
// Rest of file = header_length byte(s) + (record_length * record_count)        

Notes
-----
CGI response limits are unknown and untested

See also: plugins, xbase, sqlite

DHCP Plugin (Simple DHCP commands)

To load, use:    lib ticol_dhcp ?-verbose? ?-nocomplain?

    bool [dhcp enabled adapter_index] 
    bool [dhcp release adapter_index ?-verbose?]
    bool [dhcp renew adapter_index ?-verbose?]
    double [dhcp start]
    double [dhcp end]

The [dhcp] plugin offers extra commands which may be used to query
and/or renew a workstation's IP address

Windows has a concept of an adapter index within an array of adapters. 
We iterate from 0 to adapter_max-1 but we must use the adapter index 
value to manipulate it via [dhcp] as adapters may be unplugged or
inserted, thus rendering the actual, live array 'sparse'.

The required 'adapter_index' value is obtained from [info adapter_index N] 
where N iterated from 0 to [info adapter_count]-1

[dhcp start] and [dhcp end] return the DHCP lease start and expiry dates
as a double value. These double return lease date values can be converted
to a readable date format using the [date] command

Once the adapter is unbound from TCP/IP it will no longer be discoverable
using its index value. [info adapter_index N] should be used to retrieve
the current index value for an adapter which we handle via an index of 
0..N from [info adapter_count]

The [info] command provides a number of arguments which are useful for
handling DHCP requests and querying adapter properties

Notes:

An undefined date is returned in the "C" epoch as 0 or 1970/01/01 00:00:00,
which is interpreted in the Ticol (VB) date scheme as 25569.0

Example:

option expression off                                                           
                                                                                
# CAUTION: This script will release and renew your IP address                   
for {set i 0} {< $i [info adapter_count]} {++ i} {                              
    if {dhcp enabled $i} {                                                      
       puts "ID:[info adapter_id $i]"                                           
       puts "Index: [info adapter_index $i]"                                    
       # Show the current DHCP server and lease dates                           
       puts "DHCP Server     =[info dhcp_server $i]"                            
       puts "DHCP Lease Start=[date [dhcp start $i]]"                           
       puts "DHCP Lease End  =[date [dhcp end $i]]"                             
       if {> [dhcp end $i] 25569.0} {    # If more recent than 1970/01/01       
          set index [info adapter_index $i]                                     
          textcolor magenta                                                     
          puts "Renewing $i (index $index)"                                     
          puts "Released?\t[bool [dhcp release [info adapter_index $i]]]"       
          puts "Renewed?\t[bool [dhcp renew [info adapter_index $i]]]"          
          # Show the new DHCP server and lease dates                            
          puts "DHCP Server     =[info dhcp_server $i]"                         
          puts "DHCP Lease Start=[date [dhcp start $i]]"                        
          puts "DHCP Lease End  =[date [dhcp end $i]]"                          
          textcolor                                                             
          newline                                                               
          break                                                                 
       }                                                                        
       newline                                                                  
    }                                                                           
}                                                                               

See also: plugins, dns, info

DNS Plugin (Simple DNS / Reverse DNS lookups)

To load, use:    lib ticol_dns ?-verbose? ?-nocomplain?

    list [dns hostname|ipv4-address ?-mx?]
    string [rdns ipv4-address]

Performs a DNS or reverse DNS lookup on the hostname or IP address argument. 
[dns] returns a list containing the IPV4 address(es) of the host. More than
one item may be returned. [rdns] returns the hostname matching a given IP
address if that address has an associated record

Examples:

    lib ticol_dns                                                               
    puts [dns yahoo.com]                                                        
    puts [dns yahoo.com -mx]                                                    
    foreach x [dns yahoo.com]  { puts \t$x }                                    
    puts [rdns 8.8.8.8]                                                         
    puts [rdns 127.0.0.1]                                                       

Results:

    98.138.253.109 206.190.36.45 98.139.183.24                                  
    mta6.am0.yahoodns.net mta7.am0.yahoodns.net mta5.am0.yahoodns.net           
       98.138.253.109                                                           
       206.190.36.45                                                            
       98.139.183.24                                                            
    google-public-dns-a.google.com                                              
    snoopy                                                                      

See also: plugins

FTP Plugin (Simple FTP Client)

To load, use:    lib ticol_ftp ?-verbose? ?-nocomplain?

    ftp hostname get|put|ls filepathspec ?-u username? ?-p password?
       ?-t targetpath? ?-pasv? ?-overwrite? ?-port port? ?-v?
    ftp hostname|exists size filespec ?filespec...? ?-u username?
       ?-p password? ?-port port? ?-pasv?

ftp ls:

Accepts one filename. Returns a list containing the contents relating to
the filespec

ftp size:

Accepts multiple filenames. Will return a list containing the discovered 
filenames and their sizes.

Example:

    ftp localhost size /tools/zip-7100a80516.zip /tools/netdiag.zip             

Result:

    {/tools/zip-7100a80516.zip 1292683} {/tools/netdiag.zip 661806}             

ftp exists:

Accepts multiple filenames. Will return a list containing the supplied 
filenames and a boolean 1 or 0 depending if the file exists or not.

Example:

    ftp localhost exists /tools/zip-7100a80516.zip /nosuchfile.zip              

Result:

    {/tools/zip-7100a80516.zip 1} {/nosuchfile.zip 0}                           

See also: plugins

XBase - A Simple Dbase III/IV Plugin

To load, use:    lib ticol_xbase ?-verbose? ?-nocomplain?

    handle [xbase open filename]                                                
    long   [xbase create filename {tcl-list-of-sets}]                           
    bool   [xbase close handle]                                                 
    string [xbase info handle]                                                  
    long   [xbase count handle]                                                 
    string [xbase get handle N]                 # record is base 1              
    short  [xbase fieldcount handle]                                            
    string [xbase fieldname handle N]           # field is base 0               
    long   [xbase fieldsize handle N]           # field is base 0               
    string [xbase field handle N]               # field is base 0               
    bool   [xbase set handle fieldnum value]    # field is base 0               
    bool   [xbase put handle ?record?]          # record is base 1              
    bool   [xbase delete handle record]         # record is base 1              
    bool   [xbase undelete handle record]       # record is base 1              
    bool   [xbase is_deleted handle record]     # record is base 1              
    bool   [xbase pack handle]                                                  
    bool   [xbase setver handle integer]                                        
    bool   [xbase binary set handle integer value]                              
    bool   [xbase binary field handle N]                                        
    bool   [xbase index handle index_name ?field_number?]                       

A basic XBase/Dbase plugin to handle Dbase III/IV files (and equivalents)
This plugin offers simple manipulation of generic XBase database files.
[xbase] can handle DBF files produced by OpenOffice, Excel or XBase
programs

No advanced features are offered such as memo fields or indexing

Standard DBase files suffer from what is, in the 21st century, a fairly
severe field size limitation of 254 characters. A record can have up to
1024 fields and the field name is limited to 31 characters. 
See: ticol_db for a database which can handle larger records.

Note that record indices are "base 1" values, whilst field indices are the
same as arrays (base 0)

[xbase open] will return a handle to an open database. This must be saved
and re-used for other xbase commands

[xbase create] requires a Tcl list one or more braced arguments of field
specifiers as follows:

{field_name length ?type? ?decimals?}  where field_name can be no longer
than 11 characters (for DBase III emulation) and optional 'type' is one
of:

    'C' - Char/string  (default)                                                
    'D' - Date (data is stored as text)                                         
    'L' - Logical                                                               
    'M' - Memo                                                                  
    'N' - Numerical                                                             

Each field set must be separately braced

Example:

    xbase create "test.dbf" {{name 30} {address 25} {age 3}}                    

The Ticol [xbase] lib doesn't enforce data-typing on field-write. However,
it will create files which are compatible with the XBase format. Memo
fields are unsupported and will be treated as text. It is up to the
programmer to write code which enforces data types as necessary

XBase error codes:

enum {                                                                          
    DBERROR_SUCCESS               # 0   No error                                
    DBERROR_ALREADY_OPEN          # 1   The file is already open                
    DBERROR_FILE_NOT_FOUND        # 2   Can't find existing file                
    DBERROR_OPEN_FAIL             # 3   Failed to open existing file            
    DBERROR_INVALID_PARAM         # 4   Invalid argument                        
    DBERROR_UNKNOWN_OPEN_MODE     # 5   Bad file open mode                      
    DBERROR_VERSION               # 6   Wrong DB version on existing file       
    DBERROR_HEADER_LEN            # 7   Bad record header length                
    DBERROR_RECORD_LEN            # 8   Bad record length found                 
    DBERROR_FIELD_COUNT           # 9   Bad field count found                   
    DBERROR_FIELD_LEN             # 10  Bad field length found                  
    DBERROR_FIELD_TOO_BIG         # 11  Max field length is 256 chars           
    DBERROR_MEMORY                # 12  Out of memory                           
    DBERROR_SEEK_FAIL             # 13  Disk access error failed                
    DBERROR_DISK_SPACE            # 14  Low disk space. Cannot write            
    DBERROR_FILE_EXISTS           # 15  Can't create new file                   
    DBERROR_EXCEPTION             # 16  Critical exception occurred             
    DBERROR_UNHANDLED             # 17  Unhandled exception                     
    DBERROR_ERROR                 # 18  Default error code                      
}                                                                               

[struct] may be used to write binary data

[xbase create] returns 0 for success or an error code > 0.
On success, [xbase create] returns a handle to an open file

Example:

    set handle [xbase create {{Name 30 C} {Address 40 C} {Phone 20 C}}]

[xbase close] Closes a database and previously opened handle. It destroys
all resources associated with that handle. It returns a boolean 1 if the
handle is valid and the database was closed successfully

[xbase info] returns a Tcl list containing the following:
record-count field-count {{fieldname length type} ... }

[xbase count] returns a count of the number of records in an open xbase
database file

[xbase get] retrieves a record from an open database. The record must exist
If the record is retrieved then the whole record buffer is returned. Use 
[xbase field] to retrieve individual field values. The record index value
is a "base 1" value with the first record being 1

[xbase fieldcount] returns a count of the number of fields in an open xbase
database

[xbase fieldname] returns the field name for a given xbase field and takes
a base 0 index value

[xbase fieldsize] returns the size of an allocated field in an existing
database file. It takes a base 0 index value

[xbase field] returns individual field data for an open xbase database at
the position of the current record (see [xbase get]). [xbase field] takes
a base 0 index value 

[xbase set] set the value of a given xbase database field. The index value
is array base 0 and the first field is 0. [xbase set] does not actually 
flush (write) the record to disk file (See [xbase put]). 
[xbase set] returns a boolean success return value

[xbase put] writes the pending values in the record buffer to disk file.
If a record number is omitted then data is written to the current record.
The record index value is a "base 1" value, [xbase put] returns a boolean
success return value

[xbase delete] does not physically delete a record. The XBase convention is
to tag a hidden field with a delete flag. Likewise [xbase undelete]. The
command [xbase is_deleted] can be used to test whether a record is deleted
or not

[xbase pack] will 'pack' a database, removing all deleted records and
truncating the file as required. This may be a slow process

[xbase setver] will set the version flag field according to the integer
value passed. Use of this command is not recommended other than for 
experimental use. Common version values are as follows:

In this value, bits 0-2 indicate version number: 3 for dBASE Level 5, 
4 for dBASE Level 7. Bit 3 and bit 7 indicate presence of a dBASE IV or 
dBASE for Windows memo file and bits 4-6 indicate the presence of a 
dBASE IV SQL table; bit 7 indicates the presence of any .DBT memo file 
(either a dBASE III PLUS type or a dBASE IV or dBASE for Windows memo file)
The default value is 0x03

[xbase binary] is able to write data with embedded NULL (0x00) characters
to and from database fields. Note that strings with embedded NULLs will
be truncated at the first NULL with most Ticol commands. You can use
commands such as [binary] to manipulate binary fields (See: binary)

Indexes
-------
Standard XBase database indexes are not provided

[xbase index] provides built-in indexing using Ticol associative arrays

It is trivial to write one's own routines to use the efficient Ticol
associative arrays to index an XBase database file by using the subscript
as the record key value and the array value as the stored record number
value. A proc could be provided to match on partial index key matches.
Duplicate index key entry cases would need to be handled to avoid 
collision. This is the method used by [xbase index]

    set rc [xbase count $handle]                                                
    for {set i 1} {<= $i $rc} {++ i} {                                          
       xbase get $handle $i                                                     
       set text [string left [xbase field $handle 0] 20] # Field 0              
       set index(text) $i    # Usual subscript/value assignment inverted        
    }                                                                           
    set search "Hello world"                                                    
    puts [xbase get $handle [string left $index($search) 20]]                   

See: xbase_index.tcl

Combined Read Database Example
------------------------------
                                                                                
    option expression off                                                       
                                                                                
    lib ticol_xbase -verbose                                                    
                                                                                
    set filename "employee.dbf"                                                 
                                                                                
    set handle [xbase open $filename]                                           
    set rc [xbase count $handle]                                                
    set field_count [xbase fieldcount $handle]                                  
    puts "------------------------------------------------------"               
    puts [xbase info $handle]                                                   
    puts "------------------------------------------------------"               
                                                                                
    for {set i 1} {< $i 10} {++ i} {                                            
       xbase get $handle $i                                                     
       printf "-- Record %4i -------------------------------------\r\n" $i      
       textcolor yellow                                                         
       for {set j 0} {< $j $field_count} {++ j} {                               
          printf "%4i '%10s\t%-44.44s'\r\n" $j\                                 
          [xbase fieldname $j $handle]\                                         
          [xbase field $j $handle]                                              
       }                                                                        
       textcolor                                                                
       puts "----------------------------------------------------"              
    }                                                                           
    xbase close $handle                                                         

Results

    # 29 records                                                                
    # 11 fields                                                                 
    ------------------------------------------------------------------          
    29 11 {{SUPPLR_ID C 10} {COMPANY C 36} {CONTACT C 25} {CON_TITLE C          
     25} {ADDRESS C 42} {CITY C 13} {REGION C 11} {ZIP_CODE C 10} {COU          
    NTRY C 12} {PHONE C 15} {FAX C 15}}                                         
    -- Record    5 ---------------------------------------------------          
       0 ' EMPLOY_ID        666                                       '         
       1 ' LAST_NAME        Ismay                                     '         
       2 'FIRST_NAME        Bruce                                     '         
       3 ' EMP_TITLE        Sales Manager                             '         
       4 ' BIRTHDATE        19711107                                  '         
       5 ' HIRE_DATE        20120123                                  '         
       6 '   ADDRESS        23 Widget Hill                            '         
       7 '      CITY        London                                    '         
       8 '    REGION                                                  '         
       9 '  ZIP_CODE                                                  '         
      10 '   COUNTRY        UK                                        '         
      11 'HOME_PHONE        (044) 0123-4848458                        '         
      12 ' EXTENSION        3453                                      '         
      13 '     NOTES        Graduated from St. Elsewhere              '         
      14 'REPORTS_TO        333                                       '         
    ------------------------------------------------------------------          

See: xbase_inspect.tcl

Create Database Example
-----------------------
                                                                                
    option expression off                                                       
                                                                                
    lib ticol_xbase.dll                                                         
                                                                                
    set handle [xbase create new "{Name 30} {Address 40} {Phone 10}" -y]        
                                                                                
    set info [xbase info $handle]              # Get database info              
    puts "Info Info: $info"                                                     
    puts "Info Records:\t[lindex $info 0]"                                      
    puts "Info Fields: \t[lindex $info 1]"                                      
    puts "Info Structure: [lindex $info 2]"                                     
                                                                                
    foreach {x} [lindex $info 2] {             # Iterate fields                 
       puts "'$x'"                                                              
    }                                                                           
    newline                                                                     
    set rc [xbase count $handle]               # Get record count               
    puts "Database record count for new.dbf is: $rc"                            
                                                                                
    #     set handle  field  value                                              
    xbase set $handle 0 "Biffa Bacon"                                           
    xbase set $handle 1 "49 Letsby Avenue"                                      
    xbase set $handle 2 "(0123) 4567890"                                        
    xbase put $handle 10                       # Write to record 10             
    newline                                                                     
                                                                                
    set i 10                                                                    
    xbase get $handle $i                       # Get the record                 
    set field_count [xbase fieldcount $handle]                                  
    for {set j 0} {< $j $field_count} {++ j} { # Iterate fields                 
       printf "%4i %-10s\t\[%s\]\r\n" $j\                                       
          [xbase fieldname $j $handle]\                                         
          [xbase field $j $handle]                                              
    }                                                                           
    xbase close $handle                                                         

Results:

    Info Info: 0 3 {{NAME C 30} {ADDRESS C 40} {PHONE C 10}}                    
    Info Records:   0                                                           
    Info Fields:    3                                                           
    Info Structure: {NAME C 30} {ADDRESS C 40} {PHONE C 10}                     
                                                                                
    'NAME C 30'                                                                 
    'ADDRESS C 40'                                                              
    'PHONE C 10'                                                                
                                                                                
    0 NAME       [Biffa Bacon                   ]                               
    1 ADDRESS    [49 Letsby Avenue                        ]                     
    2 PHONE      [(0123) 456]                                                   

Notes
-----
CGI response limits are unknown and untested

See: xbase_man_create.tcl
See also: plugins

xbase get

    string [xbase get handle N]   # record is base 1

[xbase get] retrieves a record from an open database. The record must exist
If the record is retrieved then the whole record buffer is returned. Use 
[xbase field] to retrieve individual field values. The record index value
is a "base 1" value with the first record being 1.

    set rc [xbase count $handle]                                                
    for {set i 1} {<= $i %rc} {++ i} {                                          
       xbase get $handle $i                                                     
       set text [string left [xbase field $handle 0] 20] # Field 0              
       set index(text) $i    # Usual subscript/value assignment inverted        
    }                                                                           
    set search "Hello world"                                                    
    puts [xbase get $handle [string left $index($search) 20]]                   

 See also: xbase, plugins

ZIP Plugin

To load, use:    lib ticol_zip ?-verbose? ?-nocomplain?

    integer [zip {filespec list} ?-name filename??.zip?? ?-r?
          ?-p password? ?-verbose? ?-y?]

If '-name' is omitted then a ZIP file with a random filename will be created. 
The filespec-list may contain multiple wildcard filespecs. These will be 
expanded automatically. The filename is returned in the results list

if -r is specified then any given folders will be searched recursively and
this will apply to all filespecs given. The resulting ZIP file may be
encrypted using relatively weak but standard ZIP encryption

To overwrite existing files use '-y' (yes)

[zip] returns a list containing the ZIP filename, size of the ZIP file in
bytes and a count of the number of files successfully added to the ZIP
and any error code (in that order)

The ZIP module, ticol_zip.dll, is based on the InfoZIP code 2.3 and thus
AES encryption is not supported. The library supports password protection
using ZIPCrypt deflate only. You should not use this plugin to create ZIP
files which require high levels of encryption security

Example:

    set result [zip {*.tcl} -r -password secret]  # ZIP recursively             
    puts "ZIP filename is [lindex $r 0]"                                        

Copyright
---------
The internal ZIP library code is Copyright
  Copyright (c) 1990-1999 Info-ZIP.  All rights reserved
  The plugin library is not produced or supplied by Info-ZIP and is
  not supported by the Info-ZIP team

The plugin library is not supplied by Info-ZIP

See also: plugins, lib, Ticol, big numbers, mail

Scientific Format Number Plugin

A plugin to add basic scientific format number support

To load, use:    lib ticol_sci ?-verbose? ?-nocomplain?

    sci-number [sci decimal-number]  # Convert decimal to scientific
    double [dec sci-number]          # Convert scientific to decimal

Where the scientific format number is in the format:

    ?+|-?<double-coefficient>?e|E?+|-?<integer-exponent>

and where 'double-coefficient' may be presented in integer format

[dec] is useful to ensure that a data source containing numbers which 
randomly appear in scientific format are normalised to decimal. [dec]
safely passes-through real/float and integer valueas and will automatically
convert those which appear in scientific format

Ticol does not natively support scientific format numbers without 
conversion. Support for these may be added in future revisions. 
However, these two commands offer the ability to support scientific format
numbers using commands

Scientific representation is an 'inexact' representation. That is, a [big]
integer representation may be more precise than that converted by [sci]

The result of [dec] must be within the bounds of the 64-bit integer range
The input to [sci] should be within the 64-bit integer range

The output format for [sci[ will format with no trailing zeroes in the 
base with 2 digit exponent and with capital letter E output

Example:

    lib ticol_sci                                                               
    puts [sci -0.002]                                                           
    puts [dec -2.000000e-03]                                                    
    puts [dec 2E24]                                                             

Results:

    -2.0E-03                                                                    
    -0.002                                                                      
    2000000000000000000000000.000000                                            

Note that [atof] and [ftoa] may also be used internally to convert

Note that [printf] also supports scientific number output using the %e 
formatting option

Example:

    printf "%e\r\n"  -0.0002                                                    

Result:

    -2.000000e-004                                                              

    https://en.wikipedia.org/wiki/Scientific_notation

Values passed to [dec] will be normalised to x.xx format

Examples:

    puts "[dec 12.1e+1]\t[sci [dec 12.1e+1]]"                                   
    puts "[dec 12.1e-1]\t[sci [dec 12.1e-1]]"                                   
    puts "[dec 3887.1e+1]\t[sci [dec 3887.1e+1]]"                               
    puts "[dec 3887.1e-1]\t[sci [dec 3887.1e-1]]"                               
    puts "[dec 8283887.1e+5]\t[sci [dec 8283887.1e-5]]"                         

Results:

    # Decimal Value              Scientific Value                               
    121.000000000000000          1.21E+2                                        
    1.210000000000000            1.21E+0                                        
    38871.000000000000000        3.8871E                                        
    388.710000000000040          3.8871E+2                                      
    828388710000.000000000000000 8.2838871E+1                                   

See also: atof, ftoa, plugins, bignum

Windows Service Query Plugin

A plugin to query Windows services and retrieve their status

To load, use:    lib ticol_svc ?-verbose? ?-nocomplain?

    service <registered-service-name> ?-verbose?

The command must be run with sufficient access rights
See: is_elevated, elevate

The following codes or verbose status messages are returned:

 Code Verbose                  Comments                                         
 ---------------------------------------------------------------------------    
 0    Running                  The service is installed and running normally    
 1    Stopped                  The service is installed + in a stopped state    
 2    Stopping                 The service is installed and running             
 3    Not found                The requested service was not found              
 4    No error                 Reserved                                         
 5    Can't load advapi32.dll  Cannot load the required system DLL              
 6    Can't locate export      Wrong version of advapi32.dll                    
      QueryServiceStatusEx     Can't locate function in the DLL                 
 7    OpenSCManager failed     Could not open the SCM. Check access rights      
                               Ensure Ticol.exe is run 'elevated'               
 8    Unknown service status   An unknown service status was returned           
 9    Unhandled error          Unhandled error                                  

This offers a simple method of querying Windows services. Only services
with ANSI names are supported. Services typically have two names, an
actual registered service name and a display name. This command requires
the registered service name

This plugin is query only. Service actions are not yet supported

Example: 

    "Branch Cache" service name is        "PeerDistSvc"                         
    "DNS Client" service name is          "Dnscache"                            
    "DHCP Client" service name is         "Dhcp"                                
    "Distributed Link Tracking Client" is "trkwks"                              

NET START will show the display name, not the registered service name
The true name may be obtained from the Windows Service Control Manager
(SCM) via Start->Run->services.msc

Services which have spaces in their registered name must be enclosed in
quotes

Example:

    lib ticol_svc                                                               
    puts [service "FileZilla Server"]                                           
    puts [service "FileZilla Server" -verbose]                                  
    puts [service "Nosuch App"]                                                 
    puts [service "Nosuch App" -verbose]                                        

Result:

    0                                                                           
    Running                                                                     
    3                                                                           
    Not found                                                                   

See also: plugins, lib, Ticol, big numbers, zip

SMTP Mail Plugin

This is a simple SMTP emailer plugin which exposes the following command:

To load, use:    lib ticol_mail ?-verbose? ?-nocomplain?

    sendmail smtp_server from_address {to_list}                                 
       ?-subject {subject}?                                                     
       ?-body {message body}?                                                   
       ?-u username? ?-p password?                                              
       ?-port port_value?                                                       
       ?-a {attachment list}?                                                   
       ?-cc {cc list}? ?-bcc {bcc list}?                                        
       ?-priority <0..9>?                                                       
       ?-dsn? ?-nc? ?-debug?                                                    

Arguments should be braced, especially as indicated

sendmail returns the SMTP error code. 250 usually indicates success.
See SMTP documentation for more information

The first 4 arguments to sendmail are mandatory and positional. 
The remainder are optional and may be supplied in any order

If a username/password is given then SMTP authentication will be attempted
This is a simple SMTP client and SSL/TLS is not supported. Note that SMTP
sends passwords in plain text where there is no complex authentication

A copyright and source of origin signature is added to all emails unless the
-nc (no copyright) argument is passed. Useful debugging information can be
displayed using -debug

Multiple unlimited attachments are supported, but these are not expanded
from wildcards. Wildcards, if found, are ignored. ls filespec var -list
may be used to expand wildcard filespecs. Concatenate using [append] not
[lappend]

Example:

    set smtp_server smtp.example.com                                            
    set from admin@example.com                                                  
    set to [list recipient1@example.com admin@example.com]                      
    set subject "This is a test email sent at [date]"                           
    set body_text "Hi there, this is a nice email - $qbf"                       
                                                                                
    sendmail $smtp_server $from $to -subject $subject -body $body_text          

The mail module, ticolmail.dll, is not based on 3rd party code and was
written by the author of Ticol

See also: plugins, lib, Ticol, big numbers, zip

TCP Socket Ping Plugin

This is a simple TCP 'ping' tester which exposes the following command

    integer [sockping hostname port ?-var varName?]

To load, use:    lib ticol_sockping ?-verbose? ?-nocomplain?

This plugin was written to test for the availability of services on an
internal LAN. Scripts can be written to advise an administrator in the
event that a critical service is no longer 'up'.

This will test for the presence of live socket connectivity by making a
TCP connection to a remote host on the specified port. If the remote host
responds then 0 will be returned. If the host is up but does not respond on
the given port then 1 is returned. Otherwise an error code is returned as 
follows. No data is sent to the remote host and the connection is dropped
as soon as established

Not all services will return a response string (e.g. web servers)

    0       Successfully connected to the remote host on the given port         
    1       Failed to connect to the remote host (timed out/no response)        
    2       Syntax error                                                        
    3       Too many command line arguments                                     
    4       Socket error                                                        
    5       WSAStartup error                                                    
    6       Getaddrinfo error                                                   
    7       Disconnect error (error while disconnecting from remote host)       

Example:

    lib ticol_sockping                                                          
    set r [sockping localhost 25 -var v]  # Check if an SMTP server is up       
    puts "Returned code $r"                                                     
    puts "Response string: '$v'"                                                

Results:

    Returned code 0                                                             
    Response: '220 mail.kerys.co.uk - MailQ v1.16, HP (2 CPU) on HP Wedne       
    sday, 20th December 2017 +0000 23:57:22'                                    

See also; plugins

Big Integer Math Plugin

Large precision integer math with some float operations

    To load, use:    lib ticol_bignum ?-verbose? ?-nocomplain?
    For information: puts [lib ticol_bignum -info]

This module adds large-precision  signed-integer number commands. Standard
Ticol math uses 64-bit integer values. Numbers up to 6500 digits are 
handled (subject to recursive stack memory limits. Since these digits are
strings they can be easily interfaced into databases and files

This is primarily an integer library which takes only integer values.
[fdiv] however, can return float format results. Binary operations such as
[big &] will generally operate only on positive integers as integers of
arbitrary width have no fixed position for a sign flag. Some operations
will allow emulation of fixed-bit-width integers but without sign value.

Whilst it is possible to combine math operations on several big numbers
note that calculation speeds of big math are relatively slow and
inefficient, particularly for division. Calculations may need to be 
restructured to compensate for this. Division involving numbers with
hundreds or thousands of digits may take many minutes to compute

For binary values, there is no fixed bit-width so, again, calculating
negative values is a problem due to the unfixed bit-width for 2's
compliment. Therefore negative binary numbers are not handled rather than
round up to the next highest power of 8 and lose performance. 

For more information ...

See:
    http://www.codeproject.com/Tips/1079637/Twos-Complement-for-Unusual-Integer-Sizes

Float values can be handled by multiplying by a precision factor of say 15, 
performing math such as division and then dividing back down afterwards or
shifting as a string and inserting a decimal point

Values are truncated, not rounded up or down. Ticol natively uses prefix
notation for commands, not infix, so the syntax is:  

    [big <operator> value ?value?]

Example:

    puts [big / 22 7.0]                                                         

Where 'operator' is one of the following integer-only operators:

    + a b        Integer addition                                               
    - a b        Integer subtraction                                            
    * a b        Integer multiplication                                         
    / a b        Integer division (see fdiv for float)                          
                 Note is truncated, not rounded up or down                      
    % a b        Modulo division (remainder after integer division)             
    ++ a         Integer increment                                              
    -- a         Integer decrement                                              
    == a b       Equivalence                                                    
    != a b       Difference                                                     
    < a b        Less-than                                                      
    > a b        Greater-than                                                   
    <= a b       Less-than or equal to                                          
    >= a b       Greater-than or equal to                                       
    & a b ?N?    Bitwise AND (result optionally restricted to N bits)           
    | a b ?N?    Bitwise OR (result optionally restricted to N bits)            
    || a b       Logical OR                                                     
    ^ a b        Bitwise XOR                                                    
    ~ a          Unsigned bitwise NOT                                           
                 (May not return valid bit-width results)                       
    ! a          Logical NOT                                                    
    += a b       Add (Requires a variable name Lvalue without $)                
    -= a b       Sub         "                                                  
    *= a b       Mul         "                                                  
    /= a b       Div         "                                                  
    &= a b       BitAnd      " (result optionally restricted to N bits)         
    |= a b ?N?   BitOr       " (result optionally restricted to N bits)         
    ^= a b       BitXor      "                                                  
    << a b ?N?   Shift a left by b bits ?bitwidth N? (mul by power of 2)        
                 (result optionally restricted to N bits)                       
    >> a b ?N?   Shift a right by b bits ?bitwidth N? (div by power of 2)       
                 (result optionally restricted to N bits)                       
    ** a b       a to the power of b                                            
    abs a        Absolute value (remove sign)                                   
    bintoint a ?-noprefix?                                                      
                 Convert binary string to big integer (0b prefix optional)      
    debug on|off Turn on the trace for "/" and "fdiv" (see graph)               
    div2 a       Rapid division by 2 (faster than [big / x 2]                   
    div10 a b    Rapid division by 10    (faster than [big / x 10]              
    fact a       Factorial (1..1000)                                            
    fdiv a b     Floating point division of two big numbers                     
                 Precision is 18 digits after the decimal point                 
    fib a        Fibonacci number (1..30000)                                    
    fmod a b     Remainder after float division                                 
    fmul a b     Float multiplication <NOT YET IMPLEMENTED>                     
    graph        Display a coloured graph of a big number using the             
                 standard IEEE electrical colour code. Note that orange         
                 may appear as khaki on some systems is resolved using a        
                 registry tweak. See: console colours                           
    inttobin a ?-noprefix?                                                      
                 Convert from unsigned big positive integer to binary           
                 string. Use [big abs] to ensure +ve input value                
    is_even a    Returns boolean true (1) if number is even                     
    max a b      Maximum of two big integers                                    
    mean ...     Mean value of a sequence of integers (returns a float)         
                 Precision is 18 digits after the decimal point                 
    min a b      Minimum of two big integers                                    
    mul2 a       Rapid multiplication by 2 (faster than [big * x 2]             
    pow a b      Raise integer a to the power of b (alias for **)               
    pow2 a       Raise 2 to the power of x (faster than [big pow 2 n])          
    rnd lo hi    Random integer between range lo and hi                         
    sqrt a       Integer square root                                            

Two command offer big floating point operations, [big mean] and [big fdiv]

Usage with conditional expression statements and [option expression on]

  RIGHT if {[big == $n 0]} { ...  # Expression enclosed in square brackets
  WRONG if { big == $n 0 }  { ... # Expression has not been enclosed in []
  RIGHT while {[big < $n 100]} {  # Expression enclosed in square brackets
  WRONG while { big < $n 100 } {  # Expression has not been enclosed in []

Usage with conditional expression statements and [option expression off]

  RIGHT if { big == $n 0 } { ...  # Expression enclosed in square brackets
  RIGHT if {[big == $n 0]} { ...  # Expression has not been enclosed in []
  RIGHT while { big < $n 100 } {  # Expression has not been enclosed in []
  RIGHT while {[big < $n 100]} {  # Expression enclosed in square brackets

Examples:                                   Result

 lib ticol_bignum                         # (Loads the library)                 
 big % -793455062  -139725649             # -94826817                           
 big / 123456789012345678567890 987654321 # 124999998873437                     
 big * 101020280607085690 181818          # 18367305379419105984420             
 big inttobin [big << 7 24]               # 111000000000000000000000000         
 big fdiv 423423423437 23                 # 18409714062.478260869565217391      
 big mean 1234567 987654321 23 7          # 247222229.50000000000000            
 set a 12345; puts [big += a 1000]        # 13345                               
 lib ticol_bignum -unload                 # (Unloads the library)               

Example:

    option expression on                                                        
    proc fac n {                                                                
       if {$n <= 1} {                                                           
          return 1                                                              
       } else {                                                                 
          return [big * $n [fac [big - $n 1]]]                                  
       }                                                                        
    }                                                                           
                                                                                
    set s {}                                                                    
    for {set i 1} {$i < 40} {incr i} {                                          
       set f [fac $i]                                                           
       append s [format "|%4s| %50s|%5s|\n" $i $f [string length $f]]           
    }                                                                           
    puts "+----+---------------------------------------------------+-----+"     
    puts "|   n| Factorial n                                       | Len |"     
    puts "+----+---------------------------------------------------+-----+"     
    puts $s -nonewline                                                          
    puts "+----+---------------------------------------------------+-----+"     
    puts "|  39|    20397882081197443358640281739902897356800000000|     |"     
    puts "|    |             See: http://www.rpbridge.net/7z78.htm |     |"     
    puts "+----+---------------------------------------------------+-----+"     

Result:

    +----+---------------------------------------------------+-----+            
    |   n| Factorial n                                       | Len |            
    +----+---------------------------------------------------+-----+            
    |   1|                                                  1|    1|            
    |   2|                                                  2|    1|            
    |   3|                                                  6|    1|            
    |   4|                                                 24|    2|            
    |   5|                                                120|    3|            
    |   6|                                                720|    3|            
    |   7|                                               5040|    4|            
    |   8|                                              40320|    5|            
    |   9|                                             362880|    6|            
    |  10|                                            3628800|    7|            
    |  11|                                           39916800|    8|            
    |  12|                                          479001600|    9|            
    |  13|                                         6227020800|   10|            
    |  14|                                        87178291200|   11|            
    |  15|                                      1307674368000|   13|            
    |  16|                                     20922789888000|   14|            
    |  17|                                    355687428096000|   15|            
    |  18|                                   6402373705728000|   16|            
    |  19|                                 121645100408832000|   18|            
    |  20|                                2432902008176640000|   19|            
    |  21|                               51090942171709440000|   20|            
    |  22|                             1124000727777607680000|   22|            
    |  23|                            25852016738884976640000|   23|            
    |  24|                           620448401733239439360000|   24|            
    |  25|                         15511210043330985984000000|   26|            
    |  26|                        403291461126605635584000000|   27|            
    |  27|                      10888869450418352160768000000|   29|            
    |  28|                     304888344611713860501504000000|   30|            
    |  29|                    8841761993739701954543616000000|   31|            
    |  30|                  265252859812191058636308480000000|   33|            
    |  31|                 8222838654177922817725562880000000|   34|            
    |  32|               263130836933693530167218012160000000|   36|            
    |  33|              8683317618811886495518194401280000000|   37|            
    |  34|            295232799039604140847618609643520000000|   39|            
    |  35|          10333147966386144929666651337523200000000|   41|            
    |  36|         371993326789901217467999448150835200000000|   42|            
    |  37|       13763753091226345046315979581580902400000000|   44|            
    |  38|      523022617466601111760007224100074291200000000|   45|            
    |  39|    20397882081197443358640281739902897356800000000|   47|            
    +----+---------------------------------------------------+-----+            
    |  39|    20397882081197443358640281739902897356800000000|     |            
    |    |              See: http://www.rpbridge.net/7z78.htm|     |            
    +----+---------------------------------------------------+-----+            

Notes:

See: modulo sign  for a discussion of how Tcl interprets signs with % as
symbol usage in Tcl differs from that of other languages

See also: option, plugins, lib, Ticol, zip, mail

Big Text Plugin

Large text display

    To load, use:    lib ticol_bigtext ?-verbose? ?-nocomplain?
    For information: puts [lib ticol_bigtext -info]

Call using:

    integer [bigtext string ?-x xpos? ?-y ypos? ?-fore colour?\
         ?-back colour?    ?-nocomplain? ?-slow? ?-q?]

    list [bigtext - -list]

This module adds large console text or "calculator" style text handling and
which may be usefully combined with the [box] command, Large text is useful
for banners, status, clock or date display. The caller is responsible for 
clearing the console of any previously displayed text

The text is better represented with some Windows console fonts than others.
There are limitations on representing text accurately using ASCII characters
Certain fonts space characters irregularly and with excess spacing.
Additional spaces between characters may be inserted using the '-space'
argument

The following characters are handled:

    Numerals:       0..9
    Letters:        A..Z (lower case is converted to upper case)
    Punctuation:    . , : ; ! ' " ? <space> <tab>
    Symbols:        + - * / = % \ @ [ ] 

If a character is encountered which is not handled then a trappable error
will be returned unless the '-nocomplain' argument is used, in which case
the request to print an unhandled character will be ignored

The full list of handled characters may be returned as a Tcl list (except
TAB and SPACE) using [bigtext - -list]

Slow text drawing, which can emulate old-fashioned terminal output, is 
enabled using the '-slow' argument

Example:

    # Display the first 20 characters of $qbf                                   
    lib ticol_bigtext                                                           
    cls                                                                         
    bigtext [string left $qbf 20]                                               

Result:

    ___    _    _       _ _ _  __  _  _             _  _                        
     | |_||_   | || || |  |/   |_||_|| |\ \/ |\ |  |_ | |\/                     
     | | ||_   |_\|_|| |_ |\_  |_|| \|_| \/\/| \|  |  |_|/\                     

Example:

    # Draw a real-time clock using large text                                   
    lib ticol_bigtext                                                           
    cls                                                                         
    while {1} {                                                                 
        is_mod $i 7 {                                                           
            box 20 20 40 6 white darkmagenta  -solid                            
            bigtext [time] -x 22 -y 21 -fore white -back darkmagenta            
        }                                                                       
    }                                                                           

Result:

     _  _   _  _   _  _                                                         
    | || |. _||_|.|_  _|                                                        
    |_||_|.|_ |_|. _| _|                                                        

Display Width
-------------
Character widths vary and spaces have been included with some characters to
improve readability. The caller is responsible for limiting the supplied
text to ensure it will fit the console window

[bigtext] returns the total actual character width used either by a string
or a single letter. This can be used to discover both the total string 
or individual character length before drawing if the -q (quiet) argument is
used and thus check that the string will fit on the console and adjusting if
necessary before displaying

Example

    # Discover character and string display widths                              
    lib ticol_bigtext                                                           
    puts [bigtext W -q]                                                         
    puts [bigtext 0 -q]                                                         
    puts [bigtext $qbf -q]                                                      
    puts [bigtext [time] -q]                                                    
    puts [bigtext [date] -q]                                                    

Results:

    5                                                                           
    3                                                                           
    121                                                                         
    20                                                                          
    28                                                                          

See also: box

Wordcount Plugin

A plugin to perform a wordcount or character count

    To load, use:    lib ticol_wordcount ?-verbose? ?-nocomplain?

Call using:

    integer [wordcount arg ?-var? ?-chars?]

This module offers rapid count of characters in either a literal string
or a variable. Using a variable with the -var argument is more
efficient with very large strings as no internal copy is performed
in dereferencing a variable before analysis

The '-chars' argument will return a count of the number of characters
in the supplied string literal or variable

Example:

    lib ticol_wordcount                                                         
    puts [wordcount $qbf]                                                       
    puts [wordcount qbf -var]                                                   
    puts [wordcount qbf -var -chars]                                            

Results:

    9                                                                           
    9                                                                           
    43                                                                          

See also: charcount

Modulo Sign (Discussion)

    integer [% value]
    integer [big % value]

Modulo [%] and [big %] follow C/C++ expression rules in that the sign
is set to the same as the dividend. This may change in future versions if
there is a convincing reason to change

ActiveTcl states that: "The remainder will always have the same sign as 
the divisor and an absolute value smaller than the absolute value of the
divisor. This differs from other languages as follows:

    a b => r Visual BASIC 5 (sign same as dividend)                             
    -------------------------------------------------                           
    + + => +                                                                    
    + - => +                                                                    
    - + => -                                                                    
    - - => -                                                                    
                                                                                
    C++ (sign same as dividend)                                                 
    --------------------------------------------------                          
    + + => +                                                                    
    + - => +                                                                    
    - + => -                                                                    
    - - => -                                                                    
                                                                                
    a b => r ActiveState Tcl (sign same as divisor)                             
    --------------------------------------------------                          
    + + => +                                                                    
    + - => -                                                                    
    - + => +                                                                    
    - - => -                                                                    
                                                                                
    a b => r Ticol Tcl [%] (sign same as dividend)                              
    --------------------------------------------------                          
    + + => +                                                                    
    + - => +                                                                    
    - + => -                                                                    
    - - => -                                                                    
                                                                                
    a b => r Ticol Tcl [big %] (sign same as dividend)                          
    --------------------------------------------------                          
    + + => +                                                                    
    + - => +                                                                    
    - + => -                                                                    
    - - => -                                                                    

Test Cases:

    Divided    Divisor     Result                                               
    ----------------------------------                                          
                                                                                
    C/C++ mod % sign usage                                                      
    ----------------------------------                                          
     123423 %     2323 =      304 +                                             
     123423 %    -2323 =      304 +                                             
    -123423 %     2323 =     -304 -                                             
    -123423 %    -2323 =     -304 -                                             
                                                                                
    Ticol Tcl mod % sign usage                                                  
    ------------------------------                                              
     123423 %     2323 =      304 +                                             
     123423 %    -2323 =      304 +                                             
    -123423 %     2323 =     -304 -                                             
    -123423 %    -2323 =     -304 -                                             
                                                                                
    Ticol Tcl mod big % sign usage                                              
    ------------------------------                                              
     123423 %     2323 =      304 +                                             
     123423 %    -2323 =      304 +                                             
    -123423 %     2323 =     -304 -                                             
    -123423 %    -2323 =     -304 -                                             

Common pitfalls
---------------
"When either a or n is negative, the naive definition breaks down and 
programming languages differ in how these values are defined" (Wikipedia)

"When the result of a modulo operation has the sign of the dividend, it
can lead to surprising mistakes. For example, to test if an integer is
odd, one might be inclined to test if the remainder by 2 is equal to 1:

    bool is_odd(int n) {                                                        
       return n % 2 == 1;                                                       
    }                                                                           

But in a language where modulo has the sign of the dividend, that is 
incorrect, because when n (the dividend) is negative and odd, n mod 2 
returns -1, and the function returns false. One correct alternative is to
test that it is not 0 (because remainder 0 is the same regardless of the 
signs):

    bool is_odd(int n) {                                                        
       return n % 2 != 0;                                                       
    }                                                                           

Or, by understanding in the first place that for any odd number, the 
modulo remainder may be either 1 or -1:

    bool is_odd(int n) {                                                        
       return n % 2 == 1 || n % 2 == -1;                                        
    }                                                                           

See:
    https://en.wikipedia.org/wiki/Modulo_operation

See also: mod, big mod, math

Brainfuck Interpreter Plugin

A Brainfuck interpreter

    To load, use:    lib ticol_bf ?-verbose? ?-nocomplain?
    For information: puts [lib ticol_bf -info]

This module offers a small Brainfuck interpreter which is reasonably fast
but note that Brainfuck is an inherently slow and inefficient language

    string [bf script ?-highlight? ?-nocompress? ?-colour? ?-kc? 
        ?-ks? ?-c? ?-blob ?char?? ?-debug?]

Brainfuck characters [ and ] require special attention as these are Tcl
command prefixes and must therefore be escaped unless reading  directly
from a file (recommended)

The code will be run in compressed format for speed gains unless the
-nocompress argument is used

The default is for direct character output (recommended), but the output
can be cached up to about 32kb using the -c argument. Use cached mode to
return a value to Tcl. If cached mode is not used then an empty string
will be returned.

The output result can be colourised using -colour (-color)

For pseudo graphic (pixel) output, characters other than CRLF and space
may be replaced with a solid block using the -blob argument. -blob
may specify a value which is the blob character (e.g. -blob [chr 254])
Useful characters include 0xb0 (25%), 0xb1 (50%), 0xb2 (75%), 0xdb (solid)

CRLFs may be retained in the compressed code using -kc
Space and TAB characters may be retained using -ks
If any Tcl special characters are returned to Tcl by a script then they
must be escaped

Example:

    # Note that [ and ] are escaped Tcl special characters                      
    lib ticol_bf                                                                
    set s "                                                                     
    ++++++++++\[>+++++++>++++++++++>+++>+<<<<-\]>++.>+.+++++++                  
    +++.>++.<<+++++++++++++++.>.+++.--------------.>+.>."                       
    puts [bf $s -c]       # Use -c cached mode to return to [puts]              
    lib ticol_bf -unload                                                        

Result:

    Hello world                                                                 

Example:

    lib ticol_bf                                                                
    bf [readfile mandelbrot.bf] -colour -blob [chr 0xfe]                        
    lib ticol_bf -unload                                                        

Result:

    <Displays a mandelbrot fractal, coloured with a small, solid blob>          

    http://en.wikipedia.org/wiki/Brainfuck

See: plugins 

Macro Pre-Processor (MPP)
 
The Ticol Macro Preprocesor or MPP is used to enhance the flexibility of
Tcl comments, to add C-like conditional blocks and to preprocess static
aspects of a source script in order to speed up execution

Macro commands apply on a per-file basis

The MPP runs when a file is executed or loaded from the command-line and
the  Ticol Command Line Interface (CLI). Ticol is optimised for small
script files of under about 2Mb and some runtime processing is offloaded
to the MPP Ticol will be slower to load larger files

The PreProcessor completely strips all Tcl and "C"-style comments and 
superfluous whitespace from a file as well as making basic static syntax
checks for mismatched square and curved brackets, braces and double quotes
giving an indication as to which line may have caused the problem

It not only performs useful substitution and conditional execution but
when combined with [calc] it will pre-solve any constant expressions and
perform as much computation on the source file before runtime (See: calc)

Enhanced C-like macro commands are processed by the PreProcessor. 
These include conditional #define, #ifdef, #else, #endif blocks as well
as macro variables. For more information see: help macro 

C-like const escape sequences within strings, such as \t or \n, are 
translated by the MPP. Tcl non-const escape sequences such as \$ will be 
evaluated 'on-the-fly' (see: escape, unescape)

Const hexadecimal, octal and binary numbers such as 0x123, 0o123 or 0b111
are translated to decimal by the MPP. Values which are not to be
translated must be supplied as quoted strings. Other than escape
sequences, string contents are not processed. If you don't want this to
happen, wrap the const values in double-quotes

There is no macro expansion with arguments as in C++ such as:
                                                                                
    #define _toupper(_c)    ( (_c)-'a'+'A' )                                    

In summary. the Ticol Macro PreProcessor does the following:

    * Completely removes all commented code from the source
      In either standard Tcl "short" or "C" style 'long' form
    * Processes all macro statements such as #if, #else, #endif
    * Processes all #define statements and performs substitution
    * Processes all macro #exec, #echo, #if etc. statements
    * Replaces all standard escape sequences wherever possible
    * Performs substitution on standard macro variables e.g. __LINE__
    * Translates original source line numbers to the __LINE__ variable
    * Replaces all unquoted hex, octal and binary numbers with decmimals
    * Replaces all instances of [calc] expressions with prefix Polish form
    * Performs resolution and simplification on [calc] expressions
    * Checks and reports basic syntax errors such as brace/bracket parity
      outside of quoted strings (note that parity within quoted strings
      is not checked

The PreProcessor may be disabled on the command-line with the /NP argument
or from the CLI using:

    option preprocessor off                                                     

Compatible scripts can be run withy the MPP disabled using /NP, but they
must not include macro blocks "C" style comments or other non-standard
aspects of Tcl syntax

The PreProcessor is not usually applied to direct CLI console input, only
to scripts

Include files are not supported by the MPP (there is no #include command)

A special/reserved variable 'NDEBUG' is used to globally disable the
[assert] command (case insignificant). For more information, see: assert

For more details of macro commands etc. See macro

For an example of a good macro system for standard Tcl, see 'Sugar'
    http://wiki.tcl.tk/11156

See also: cli, macro, debugging, escape, unescape, assert

Preset Global Variables and Constants

The following constants or variables are preset on loading Ticol:

    Variable         Type   Description or Example                              
    ----------------------------------------------------------------------      
    ::env()          Array  Const array of Windows environment variables        
                            Subscripts are converted to upper-case indices      
                            are case-sensitive in Tcl                           
    ::argv()         Array  Const array of command line arguments               
    ::exe_name       Const  C:\VC5\MyProjects\picol\Release\ticol.exe           
    ::autoexec_ran   Var    1 if autoexec.tcl ran successfully, otherwise 0     
    ::false          Const  0                                                   
    ::true           Const  1                                                   
    ::eof            Const  -1                                                  
    ::qbf            Const  The quick brown fox jumps over the lazy dog         
    ::pi             Const  3.14159265358979323                                 
    ::NAN            Const  -1.#IND00000000000                                  
    ::win64          Const  Boolean. Whether Windows is 64-bit or not           
    ::tcl_config     Const  String. Path+filename of Ticol config (INI)         
    ::tcl_cgi        Const  1 (true) if running in CGI mode, otherwise 0        
    ::tcl_cli        Const  Boolean. Set to 1 if running from the CLI           
    ::tcl_date       Const  Ticol compilation (build) date/time                 
    ::tcl_threaded   Const  Boolean. Set to 1 if threaded compile of Ticol      
    ::tcl_version    Const  Ticol version. Same as [info tclversion]            
    ::tcl_precision  Const  2. Set in TICOL.INI                                 
    ::errorCode      Var    Tcl error code sent to a the console                
    ::errorLine      Var    May be preset using the #__LINE__ macro             
    ::errorMsg       Var    Error message generated by a Ticol error            
                            Fatal exceptions generate no errorMsg variable      
    ::errorInfo      Var    Additional information                              

Environment variables may be set as individual constants. This depends on
the Windows version in use

Use the command [vars -const] to display all known constants on loading

Note that constants or variables with embedded # characters must be 
enclosed in double-quotes

See also: argv, global variable, global, upvar

Print Functions

See the following topics:

    puts        Simple print output
    format      Format strings for output with puts
    printf      Formatted print similar to C's printf()
    newline     Output a new line
    textcolor   Set the colour of the text output

See also: Ticol

precision

    int [precision double]

Returns the effective precision length for a 'double' or 'real' number and
which ignores trailing whitespace and trailing zeroes

Comparing real numbers can be difficult if they exhibit different precision
Rounding may not always be useful unless a proper rounding values is used. 
This would depend on the precision of both numbers

Improperly formatted values are not valid and will return a precision of
zero. Hexadecimal integers are not supported

Integer values will return a precision of zero

Non-numeric strings will also return a precision of zero

Example:
                                                                                
    puts [precision $pi]                   # Returns 17                         
    puts [precision 100]                   # Returns 0 (integer)                
    puts [precision "100."]                # Returns 0 (integer)                
    puts [precision 123.456]               # Returns 3                          
    puts [precision 123.4560000]           # Returns 3                          
    puts [precision "123.4560000   "]      # Returns 3                          
    puts [precision 123.4a6]               # Returns 0 (not valid)              
    puts [precision Foobar]                # Returns 0 (not valid)              

See also: double, fraction, integral, vfract, data type ranges

printf
 
    integer [printf format-string|literal ?arg? ...]

Print command and wrapper for [format]. Calls [format] and outputs the
result after unescaping backslash escape sequences

[printf] emulates the C/C++ printf function. It can output only to stdout
use [format] to format strings for other output streams

[printf] returns the count of (escaped) characters output to the console

[printf string args] is functionally equivalent to 
[puts [format string args]]

[printf] prints only to the console. To print to a file stream combine
[puts] and [format] as follows...

    set fp [open filename wb]                                                   
    puts $fp [format "%s %i %f\r\n" One 2 3.0]                                  
    close $fp                                                                   

Line Endings:

[printf] doesn't issue Windows CRLF pairs (0x0d,0x0a) or '\r\n'
If you wish to print newlines you must append \r\n escape character 
sequences within a supplied string argument

Example:

    printf "%s %li 0x%x %3.2f\n" "Hello" 12345 987654 $pi                       

Result:

    Hello 12345 0xf1206 3.14

Example:  (Source: https://www.tcl.tk/man/tcl8.4/TclCmd/format.htm)

    option expression on                                                        
    puts "+-------------------------+"                                          
    puts "| 64-bit printf demo      |"                                          
    set sep +-[string repeat - $w1]-+-[string repeat - $w2]-+                   
    printf "$sep\r\n"                                                           
    printf "| %-*s | %-*s |\r\n" $w1 "Index" $w2 "Power"                        
    printf "$sep\r\n"                                                           
                                                                                
    # Print the contents of the table                                           
    option expression on                                                        
    set p 1                                                                     
    for {set i 0} {$i<=30} {incr i} {                                           
       printf "| %*d | %*I64i |\r\n" $w1 $i $w2 $p                              
       set p [expr "wide($p) * 3"]                                              
    }                                                                           
    printf "$sep\r\n"                                                           

Result:

    +-------------------------+                                                 
    | 64-bit printf demo      |                                                 
    +-------+-----------------+                                                 
    | Index | Power           |                                                 
    +-------+-----------------+                                                 
    |     0 |               1 |                                                 
    |     1 |               3 |                                                 
    |     2 |               9 |                                                 
    |     3 |              27 |                                                 
    |     4 |              81 |                                                 
    |     5 |             243 |                                                 
    |     6 |             729 |                                                 
    |     7 |            2187 |                                                 
    |     8 |            6561 |                                                 
    |     9 |           19683 |                                                 
    |    10 |           59049 |                                                 
    |    11 |          177147 |                                                 
    |    12 |          531441 |                                                 
    |    13 |         1594323 |                                                 
    |    14 |         4782969 |                                                 
    |    15 |        14348907 |                                                 
    |    16 |        43046721 |                                                 
    |    17 |       129140163 |                                                 
    |    18 |       387420489 |                                                 
    |    19 |      1162261467 |                                                 
    |    20 |      3486784401 |                                                 
    |    21 |     10460353203 |                                                 
    |    22 |     31381059609 |                                                 
    |    23 |     94143178827 |                                                 
    |    24 |    282429536481 |                                                 
    |    25 |    847288609443 |                                                 
    |    26 |   2541865828329 |                                                 
    |    27 |   7625597484987 |                                                 
    |    28 |  22876792454961 |                                                 
    |    29 |  68630377364883 |                                                 
    |    30 | 205891132094649 |                                                 
    +-------+-----------------+                                                 

Note: The big math plugin module can be used to extend the number range
(See: big math)

See: format and format string for more details

See also: format, echo, puts, format string, file

procs (info procs)
 
 list [procs ?glob-pattern?]
 list [info procs ?glob-pattern?]

Returns a Tcl list of user-defined procedures. These will also be included
in the [commands] list.

Example:

    option echo on                                                              
    procs                                                                       
    procs c??e                                                                  
    procs t*n                                                                   

Result:

    cube rainbow square testmean                                                
    cube                                                                        
    testmean                                                                    

Use: option echo on to display directly to the console

See also: commands, functions, info

Passing Structs to a Procedure By Name

Although Ticol structs are designed primarily to facilitate communication
with external DLL routines they can also be used internally within script
procedures (procs)

Due to the non-binary nature of Tcl, dealing with structs is not as easy
or as transparent as when using a language such as C or C++. A struct may
contain direct instances of simple Tcl variables such as string, list, 
integer or double as well as references to complex abstract data types 
such as Array, Stack or Struct

To pass a struct to a proc you will need to pass it by name value
(See: pass by name)

With Tcl procedures any variable passed by name will need to be referenced
either via the global '::' scope prefix or a local alias created using 
[upvar]. Each referenced struct field requires either a variable aliased
using [upvar] or '::' or a command which can make use of the struct 
variable itself such as [struct item] or [array item]

Once you have created a local reference to the struct you can also, if
necessary create member field references using [upvar]

Struct member fields can be read as well as written back to and
the original struct members are updated when the proc exits

Structs may contain references to other structs but as this can get 
rather complex it is best avoided

Example:

Example of a proc setting and returning various data types

    undef print_arg -nocomplain                                                 
                                                                                
    proc print_arg {value} {                                                    
       # Incoming arg is passed by name                                         
       upvar $value                                                             
       textcolor green                                                          
       #puts "print_arg \{$value\}"                                             
       if {[eq [type $value] stack]} {                                          
          puts "Stack: '[stack item $value 0]'"                                 
       } elseif {[eq [type $value] array]} {                                    
          puts "Array: [array item $value 1]"                                   
       } elseif {[eq [type $value] struct]} {                                   
          textcolor magenta                                                     
          puts "Struct '$value' item(s):"                                       
          puts "  i: '[set ::[struct item ${value}.i]]'"                        
          puts "  f: '[set ::[struct item ${value}.f]]'"                        
          puts "  r: '[set ::[struct item ${value}.r]]'"                        
          puts "  s: '[struct item ${value}.s]'"                                
          puts "  a: '[array item ::[struct item ${value}.a] 1]'"               
          puts "  k: '[stack item ::[struct item ${value}.k] 0]'"               
       } else {                                                                 
          puts "String or number $value is [set $value]"                        
       }                                                                        
       textcolor                                                                
    }                                                                           
                                                                                
    struct ts {     # Test struct: holding various data types                   
       i 20        # Integer Literal                                            
       f 40        # Double/Float Literal                                       
       r 20        # String Reference                                           
       s 256       # String Literal                                             
       a 20        # Array                                                      
       k 20        # Stack                                                      
    }                                                                           
                                                                                
    set s "Hello world"                                                         
                                                                                
    set i 123456789                                                             
    struct set ts.i i                                                           
                                                                                
    set f $pi                                                                   
    struct set ts.f f                                                           
                                                                                
    set r "Hello from string reference"                                         
    struct set ts.r r    # String reference by name                             
    struct set ts.s "Hello from literal string"                                 
                                                                                
    set a(1) "This is array item 1"                                             
    struct set ts.a a     # Array by name reference                             
                                                                                
    stack create stk 200                                                        
    stack push stk "Hello from stack"                                           
    struct set ts.k stk                                                         
                                                                                
    print_arg s          # Pass a variable by name                              
    print_arg i          # Pass an integer variable by name                     
    print_arg f          # Pass a float variable by name                        
    print_arg a          # Pass an array variable by name                       
    print_arg stk        # Pass a stack by name                                 
    print_arg ts         # Pass a struct by name                                

See also: struct, proc, calling by value

pointers

Access to pointers is not a native feature of Tcl. A modest level of
address manipulation can be carried out in Ticol in order to facilitate
the marshalling of data using structs or binary values via [calldll]

The key commands are: struct, setb and ofaddressb

  struct test {f 4}     # Declare struct with member 'f' of 4 bytes width       
                        # The struct will have 4 bytes of memory allocated      
  struct setb f 4321    # Set the struct member field to binary value 4321      
  puts $f               # Show the memory address pointed to by member 'f'      
  puts [ofaddressb f]   # Dereference the struct pointer to get the value       
  struct unset test     # Delete                                                

See also: calldll, addressofb, addressof, ofaddress, malloc, memcpy

puts
 
    puts ?stream|stdout? string ?-nonewline?
    puts ?-nonewline? ?stream|stdout? string

Print a string to the console with a trailing newline by default

To enhance performance [puts] returns a NULL value ("") rather than
allocate and return the supplied string merely to be discarded in most
cases

Buffered output is configured for stdout by default. This may be disabled
using the /NB (no buffer) argument when you launch Ticol. However this may
result in no output under certain conditions until a program exits. If
your code produces delayed output then you may also use the [flush]
command at critical locations in order to force output

Line endings:

[puts] issues a standard Windows CRLF pair (0x0d,0x0a) or \r\n unless
the -nonewline argument is used

Use: option echo on to display command returns directly to the console 
without using [puts] or [printf]

Alternatives:

Use:  set varname   with [option echo on] to display unescaped the
variable contents

Use with [gotoxy]:

If using [puts] with absolute screen coordinates via [gotoxy] you should
use the -nonewline argument if possible to avoid screen scrolling issues

Example:

    option expression off                                                       
    for {set i 0} {< $i 10} {++ i} {                                            
       gotoxy 5 5                                                               
       puts "I am at 5,5" -nonewline                                            
    }                                                                           

[puts] examples:

Example:

    puts "Hello from Tcl" -nonewline                                            
                                                                                
    set a "Hello World"                                                         
    puts stdout $a                                                              
                                                                                
    set fp [open "test.txt" wb]                                                 
    puts $fp                      # Display the file handle (4582320)           
    puts fp "Hello file"                                                        
    close fp                                                                    

Example:  (Source: https://www.tcl.tk/man/tcl8.4/TclCmd/format.htm)

    puts "+-------------------------+"                                          
    puts "|  64-bit integer printf  |"                                          
    set sep +-[string repeat - $w1]-+-[string repeat - $w2]-+                   
    puts $sep                                                                   
    puts [format "| %-*s | %-*s |" $w1 "Index" $w2 "Power"]                     
    puts $sep                                                                   
    # Print the contents of the table                                           
    option expression on                                                        
    set p 1                                                                     
    for {set i 0} {$i<=30} {incr i} {                                           
       puts [format "| %*d | %*I64i |" $w1 $i $w2 $p]                           
       set p [expr "wide($p) * 3"]                                              
    }                                                                           
    puts $sep                                                                   

Result:

    +-------------------------+                                                 
    | 64-bit integer printf   |                                                 
    +-------+-----------------+                                                 
    | Index | Power           |                                                 
    +-------+-----------------+                                                 
    |     0 |               1 |                                                 
    |     1 |               3 |                                                 
    |     2 |               9 |                                                 
    |     3 |              27 |                                                 
    |     4 |              81 |                                                 
    |     5 |             243 |                                                 
    |     6 |             729 |                                                 
    |     7 |            2187 |                                                 
    |     8 |            6561 |                                                 
    |     9 |           19683 |                                                 
    |    10 |           59049 |                                                 
    |    11 |          177147 |                                                 
    |    12 |          531441 |                                                 
    |    13 |         1594323 |                                                 
    |    14 |         4782969 |                                                 
    |    15 |        14348907 |                                                 
    |    16 |        43046721 |                                                 
    |    17 |       129140163 |                                                 
    |    18 |       387420489 |                                                 
    |    19 |      1162261467 |                                                 
    |    20 |      3486784401 |                                                 
    |    21 |     10460353203 |                                                 
    |    22 |     31381059609 |                                                 
    |    23 |     94143178827 |                                                 
    |    24 |    282429536481 |                                                 
    |    25 |    847288609443 |                                                 
    |    26 |   2541865828329 |                                                 
    |    27 |   7625597484987 |                                                 
    |    28 |  22876792454961 |                                                 
    |    29 |  68630377364883 |                                                 
    |    30 | 205891132094649 |                                                 
    +-------+-----------------+                                                 

See also: echo, format, printf, set, file open, pause

qbf

A test constant defined as: "The quick brown fox jumps over the lazy dog"

    https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog

See also: preset globals, const, vars

Decimal to Roman Conversion Plugin

Roman to decimal conversion is offered for amusement only. The maximum
value which can be converted from decimal is 10000. Values above this
will raise a Tcl exception which may be trapped using [catch]

    To load, use:   lib ticol_r2d?.dll? ?-verbose? ?-nocomplain?

Commands:

    string [decimal_to_roman integer]
    integer [roman_to_decimal roman-value]

Order presentation of Roman numerals need not always be significant
and may need careful presentation to achieve expected results:

Example:

    puts [roman_to_decimal IVXCLDM]   # Same as MDXLIV (1544)                   
    puts [decimal_to_roman 1544]      # Same as IVXCLDM (1544)                  

Results:

    1544                                                                        
    MDXLIV                                                                      

Examples:

    lib ticol_r2d                                                               
    puts [decimal_to_roman 1776]                                                
    puts [roman_to_decimal MDCCLXXVI]                                           
    puts [roman_to_decimal [decimal_to_roman 1776]]                             

Results:

    MDCCLXXVI                                                                   
    1776                                                                        
    1776                                                                        

See also; plugins

rnd, rndseq, rnd64
 
    long-integer [rnd low high]
    long-integer [rndseq low high]
    integer64 [rnd64 integer64-low integer64-high]

Return a random number between 'low' and 'high' where either value is a
positive, signed long value in the range 0 to LONG_MAX rather than a
native 64 bit unsigned integer

[rndseq] guarantees sequential random numbers which do not repeat whereas
[rnd] does not. There may be cases where it is undesirable to have a
repeating random number, e.g. list selection or random text-parsing

[int64] returns a random number in a full 64-bit integer range

Not recommended for safety critical implementations

Example:

    puts [rnd 10 200]                                                           
    puts [rnd 10 200]                                                           
    puts [rndseq 10 200]                                                        
    puts [rndseq 10 200]                                                        
    puts [rnd64 $LONG_MAX $INT_MAX]                                             
    puts -[rnd 10 200]                                                          

Result:

    42                                                                          
    42                  # [rnd] can repeat the same number                      
    87                                                                          
    23                  # [rndseq] will not repeat the same number              
    8538924581789421291 # Big random number (use: source defs.tcl)              
    -42                 # Generate negative numbers by prefixing with -         

The big math library can return larger random numbers (See: big)

See also: rnd function, rand, srand, lrand, randomstr, math

RTF Output Plugin

Simple conversion of plain text, outputting to an RTF file

    To load, use:    lib ticol_rtf?.dll? ?-verbose? ?-nocomplain?

Commands:

    rtf write filename text ?-y? ?-font name? ?-fs N? ?-rtf?
    rtf write filename var -var ?-y? ?-font name? ?-fs N? ?-rtf?

Where:

    -var         Data is passed in a variable by name (faster for big data)     
    -y           Overwrite any existing file                                    
    -font        Name of the font (as it appears in Windows)                    
                 e.g. "Courier New" or "Tahoma"                                 
                 May be repeated to define multiple fonts                       
                 Each font will be registered as: \f0, \f1 etc.                 
                 The first font will be the default (font 0) \f0                
    -fs          Default font size in points e.g. -fs 10 for 10 point           
                 May be repeated to define multiple fonts (see -font)           
    -rtf         Specifies that the text will be supplied in RTF format         
                 (excluding headers). This allows custom RTF codes to be        
                 inserted into the body and resolve clashes with single-        
                 backslash Ticol escape codes.                                  
                 If -rtf is omitted and RTF escape-codes given then the         
                 codes will be mis-transformed on output                        
                 If -rtf is omitted then \\ codes will not be escaped           
                 If -rtf used then \\ codes will require [unescape \\x]         
                 Thus you can use "\par [unescape \\b\\fs20]" -rtf              
                 where this would otherwise embed "\f" (formfeed) and           
                 "\b" (backspace)                                               
    -colourtable <rtf-code>        (excluding CRLF)                             
                 Example:                                                       
                 -colourtable "\{\colortbl ;\\red255\\green0\\blue0;\}"         
                 The internal default is:                                       
                 {\\colortbl ;\                                                 
                 \\red255\\green0\\blue0;\                                      
                 \\red0\\green255\\blue0;\                                      
                 \\red0\\green0\\blue255;}                                      

Example:

    rtf write test.rtf $qbf -y -font "Courier New" -fs 12                       

Notes:

RTF commands may be entered into the text string

Since backslash sequences can be complex in Tcl and particularly when 
mixing with RTF and because inserting backslash sequences correctly can
be problematic, you may use [chr 92] to avoid complications. e.g. for
"\f0 use" "[chr 92]f0"

Where there are conflicts with C++/Tcl escape sequences, literal RTF
command characters present in the source-text may need to be escaped. 
e.g. (where $q contains our text)

Remember to use the -rtf argument when calling [rtf write]:

    [string replace $q "{" "\{"]                           # Curly braces       
    [string replace $q [unescape "\\"] [unescape "\\\\"]]  # Backslashes        

or (within source file):

    append s [unescape "\\page\\f1\\fs32\\b "]                                  
    append s $q                                                                 
    append s [unescape "\\b0\\fs18\\f0 \r\n\r\n\\par "]                         

Due to backslash translation issues, RTF backslash commands should
ideally be executed from source files not the CLI. CLI use would 
require: option escape on, option autoexec off

Example:

    lib ticol_rtf                   # Load the plugin library                   
    rtf write testrtf.rtf "[unescape \\fs32\\b] $qbf [\\b0]" -rtf -y            
    @ start testrtf.rtf             # Display the final RTF text                

A More Complex Example:

    # Write a complex RTF document                                              
    # This demonstrates a multi-line string with line-continuation chars        
    # and trailing comments after multi-line string continuations               
    set ct "{\\colortbl ;\          # Start custom colour table                 
    \\red255\\green0\\blue0;\       # 1: Red                                    
    \\red0\\green255\\blue0;\       # 2: Green                                  
    \\red0\\green0\\blue255;\       # 3: Blue                                   
    \\red255\\green255\\blue0;\     # 4: Yellow (for highlighting)              
    \\red128\\green0\\blue255;}"    # 5: Violet                                 
                                                                                
    lib ticol_rtf                   # Load the plugin library                   
                                                                                
                                    # Write the body text                       
    rtf write $filename "The \ul quick\ulnone  [unescape \\super\\cf1] \        
    brown [unescape \\cf0\\nosupersub\\f1] fox \                                
    [unescape \\f0\\fs32\\b\\highlight4\\f2] \                                  
    jumps\par over [unescape \\f0\\highlight0] the \                            
    [unescape \\fs64] \                                                         
    la\cf5 z\cf0 y [unescape \\fs20\\b0] dog!"\                                 
     -rtf -y -colourtable [unescape $ct]\                                       
     -font Tahoma -font "Courier New" -font "Segoe Script"                      
                                                                                
    @ type testrtf.rtf              # Display the raw RTF code                  
    @ start testrtf.rtf             # Display the final RTF text                

Demo Script:

A demo script is included with Ticol which will convert this man file to
RTF which is called man_to_rtf.tcl. The bundled RTF version of the Ticol 
manual is generated using this Tcl script

Useful commands include: readfile, append

See also: plugins, chr, unescape, web search re: RTF control codes

Quickstart Guide for Ticol Tcl

Loading Ticol
-------------
From the Windows console prompt type the following command to load the 
Ticol Command Line Interpreter (CLI):

    ticol.exe                                                                   

Running Scripts from Windows
----------------------------
Scripts can be run directly from the Windows command line using the
following command:

    ticol.exe <scriptname>                                                      

Where '<scriptname>' is the name of a TCL or TCX script

Command-Line Help
-----------------
Use the following command:

    ticol.exe /?                                                                

Quitting Ticol
--------------
The interpreter can be exited either by typing 'exit' at the CLI prompt
or by pressing CTRL+BREAK

The following commands are useful in getting started with Ticol Tcl:

Command                 Description
----------------------------------------------------------------------
                                                                                
#  comment              # Standard Tcl comments are a hash character            
/* comment */           # Ticol supports enhanced C++ style long comments       
ticol.exe               # Load the interpreter CLI from Windows                 
                                                                                
load <scriptname>       # Load a TCL or TCX script into the CLI                 
run                     # Run a previously loaded script                        
run <scriptname>        # Run a script directly from the CLI                    
dump                    # Display a loaded script with highlighting             
                                                                                
puts <string>           # Print a string                                        
set varname value       # Set a variable                                        
$varname                # Dereference a variable                                
expr <expression>       # Evaluate a natural expression                         
eval <command>          # Evaluate a Tcl command                                
if {expr} then {script} # Conditionally evaluate a scriptlet                    
while {expr} {script}   # Evaluate a script while 'expr' is true                
source <scriptname>     # Interpret a script (used for script inclusion)        
                                                                                
option                  # Display configuration option settings                 
option echo on          # Turn on local command result echo (off to disable)    
option expression on    # Confgure flow control commands to use expressions     
vars                    # Display existing variables                            
help <topic>            # Display help on a specific topic                      
find <topic>            # Find help based on a string search                    

See the tutorial and faq sections for more information

See also: tutorial, ticol, faq

randomstr
 
    string [randomstr length ?style? ?-var newvar? ?-nocomplain?]

Create a random string of characters in varying formats and of a given
length

    Style values (where style is a positive integer):                           
                                                                                
    0    Mixed upper/lower case                                                 
    1    Upper case letters                                                     
    2    Lower case letters                                                     
    3    Numbers (0..9)                                                         
    4    Upper/lower case letters and numbers (0..9)                            
    5    Binary data in the range 0x00..0xFF                                    
    6    Hexadecimal numbers 0..9, A..F (capitals)                              
    7    Binary string (even bias)                                              
    8    Binary string with bias to 0                                           
    9    Binary string with bias to 1                                           
    10   Hexadecimal numbers 0..9, A..F (lower-case)                            
    11   Printable ASCII characters 33 to 127 except 96                         
    12   All hexadecimal characters (upper/lower case)                          
    13   Octal characters (0..7)                                                
    14   Password style 0..9, a..z, A..Z  . - % #                               
    15   Safe-readable (confusing characters avoided)                           
    16   Alphanumeric upper-case 0..9, A.,Z                                     

You may define an [enum] to hold the const values

    enum {                                                                      
        RANDOM_STYLE_MIXED                                                      
        RANDOM_STYLE_LOWER                                                      
        RANDOM_STYLE_UPPER                                                      
        RANDOM_STYLE_NUMBERS                                                    
        RANDOM_STYLE_ALL                                                        
        RANDOM_STYLE_BINSTR                                                     
        RANDOM_STYLE_HEX                                                        
        RANDOM_STYLE_BINARY                                                     
        RANDOM_STYLE_BINARYBIAS0                                                
        RANDOM_STYLE_BINARYBIAS1                                                
        RANDOM_STYLE_HEX_LOWER                                                  
        RANDOM_STYLE_PRINTABLE                                                  
        RANDOM_STYLE_HEXALL                                                     
        RANDOM_STYLE_OCTAL                                                      
        RANDOM_STYLE_PASSWORD                                                   
        RANDOM_STYLE_SAFE                                                       
        RANDOM_STYLE_ALNUM_UPPER                                                
    }                                                                           

Example:

    # Display a row of 20 chars of each type of random string                   
    option expression off                                                       
    for {set i 0} {< $i 17} {++ i} {printf "%2i:[randomstr 20 $i]\r\n" $i}      

Result:

     0:YAscKInuJAjvYtxkPpNr                                                     
     1:qawbgcfcehedfewghkpc                                                     
     2:SRLQMENLFPKMLDADKSPL                                                     
     3:31183734177678368924                                                     
     4:Ukwj71Pk9LvrBwjbO3p9                                                     
     5:/ÇÆ,- Ô'Ý?ƒû¨õË?¦t-¶                                                     
     6:5991668E260A498F6BA5                                                     
     7:11001000110111001000                                                     
     8:00100100011011010000                                                     
     9:00011010111111111110                                                     
    10:8173dc3d3a0df1533a19                                                     
    11:>eMTb_L'ADs9_4U;E,$p                                                     
    12:6E646D5D826C3d5063C6                                                     
    13:34575216245752571651                                                     
    14::lGf#M5f#V:UbXM3xcHr                                                     
    15:fyU2A1JkmS7dpEx6MpJJ                                                     
    16:SF8NIL3J35Y7OH1729CM                                                     

See also: rnd, randomise, rndseq, lrand

range
 
    string [range charset ?charset? ...]
    string [range asc-range ?asc-range? ...]

range returns a string of characters generated from 'charset' where
charset is a hyphenated pair of characters: e.g. A-Z or 0-9

Any character pair can be selected in the printable ANSI character set
Multiple character sets are accepted and descending ranges (Z-A) are
accepted

A numeric ASCII range above 9 may be used. Values below 10 will be assumed
to represent integers. The following are valid.

    # Print [asc 32] to [asc 42] and [asc 64] to [asc 80]                       
    # Will return: ' !"#$%&'()*@ABCDEFGHIJKLMNOP'                               
    puts "'[range 32-42 64-80]'"                                                

If the RValue of a character pair is absent then 255 is assumed: e.g. "X-"

A range character may be specified using the [asc] command...
e.g. puts [range [chr 26]-c]

Examples:

    puts [range 0-9 z-w F-J]                                                    
    puts [range 9-0 Z-A]                                                        

Results:

    0123456789zyxwFGHIJ                                                         
    9876543210ZYXWVUTSRQPONMLKJIHGFEDCBA                                        

See also: string, randomstr

read, file read

    string [read filenum ?-bytes N? ?-line? ?-tab?]
    string [file read filenum ?-bytes N? ?-line? ?-tab?]

Read data from an open file stream, as opened with the [open] command
Either reads the entire file or a specified number of bytes in either block
read or line input modes. 

To read lines of a text file into an array use [readfile]

Options
-------
-bytes N    Read N bytes from file. If '-line then line buffer is N bytes
-line       Line input mode. Uses a line buffer of 1024 or -bytes length
            Line mode will remove any trailing CRLF characters from the EOL
-tab        Replace tab characters with a space

Examples:

    set fp [open "input.txt" rt]    # Read a file in text mode                  
    set data [read $fp]             # Read the whole file                       
    puts $data                      # Print the whole file                      
    close $fp                                                                   

                                                                                
    set fp [open "input.txt" rb]    # Read a file in binary mode                
    set data [read $fp -bytes 80]   # Read a raw block of 80 bytes              
    puts $data                      # Print the data (may include CRLFs)        
    close $fp                                                                   

                                                                                
    set fp [open "input.txt" rb]    # Read a file in binary mode                
    set data [read $fp -line]       # Read a line, stripping off CRLFs          
    puts $data                      # Print the input line                      
    close $fp                                                                   

Notes
-----
[read] is not binary-safe. Ticol is not designed for handling binary files

Line ending/CRLF handling varies depending on which mode a file is opened
with. A [read] after opening in "rt" mode will translate CRLF (\r\n) to
\n only and thus may cause a read skew errors of not handled correctly
Be consistent and use either "rt"/"wt" or "rb"/"wb" modes. Don't mix
It is usually preferable to use "rb"/"wb" 

When opening a file in binary mode e.g. set fp [open $filename wb]
be sure force CRLFs on write using:      puts $fp "Text\r\n" -nonewline

When opening a file in text mode such as:  set fp [open $filename wt]
you may use:   puts $fp "Text"

When reading using [file eof $handle] the final valid line of the file will
not return EOF. This will be returned on a subsequent read; so do something
like...

    set fp [open $filename rb]                                                  
    if {$fp} {                                                                  
    do {                                                                        
        set s [read $fp -line -tab]  # Use internal default line buffer of 1024 
        if {! [file eof $fp] } {     # Test EOF here                            
            puts "$i: '$s'"          # -line input will strip CRLFs             
            ++ i                                                                
        }                                                                       
    } while {! [file eof $fp]}                                                  
    close $fp                                                                   

See also: open, file, readfile, writefile

readfile

    string  [readfile filename ?-max byteCount?]
    integer [readfile filename varname|- ?-array? ?-max byteCount?]

Reads data from a file in binary mode, optionally passing into a variable
Where a variable is specified, readfile returns the count of bytes, 
otherwise the read-in data block is returned

The line read within [readfile] handles lines up to 8Kb (8192 bytes) long
Longer lines will be split and wrapped 

The filename should be given unescaped if it contains backslash characters
Certain variables such as $argv() are stored in escaped format. Direct user
input routines may store an unescaped string which may be passed directly

Example:

    readfile [unescape $argv(1)] a -array]                                      

Alternately, Unix-style forward slashes '/' may be used in the filename

If -max is specified combined with -array, then this will limit the maximum
number of lines, otherwise it will limit the total number of bytes read-in

If the -array argument is given then the file will be written to a Tcl array
with an integer subscript of index base 0. The return value in this case 
will be the number of lines in the array. You can use [array walk arrayName]
to inspect the array or [for] to iterate it efficiently

Varname may be omitted by specifying a - sign. This will allow a byte-count
to be specified without specifying a variable.

Example:

    puts [readfile autoexec.bat q]                                              

Result:

    1031                                                                        

Example:

    puts [readfile autoexec.bat q -max 200]                                     

Result

    200                                                                         

Example:

    # Read autoexec.tcl to an array variable                                    
    option expression on                                                        
    readfile autoexec.tcl a -array                                              
    for {set i 0} {$i < [array size a]} {incr i} {puts $i:$a($i)}               

Result:

    <prints out file the contents>                                              

See also: read, file

Recursion and Issues Arising

Recursion and recursive proc calls should be controlled to avoid excessive
nesting, otherwise the interpreter will run out of memory. Well-designed
routines should not need to recurse deeply

The maximum theoretical command/proc recursion depth is set to 5000 but in
practice this may be lower where proc variable consume memory.

If secondary command calls are nested then this recursion depth limit may 
be significantly lower and may result in a non-fatal exception error. 

On a side note, the default ActiveState Tcl recursion limit is 1000, this 
is perhaps a sensible limit. 

A trappable Ticol exception will be raised at a recursion depth of 5000
(use [catch] or [try...catch]) but this may depend on available memory.

Additionally, the breakpoint debugger stack will track only the first 100
command calls, after which no further traces will be stored

The maximum recursion depth is configurable via ticol.ini as

    MaxRecursion=<value>

where <value> > 0 && <value> <= 5000

If exceptions occur then try lowering this value to around 1000 by editing
this value in ticol.ini

Example:

    # This routine will trigger a recursion-depth exception                     
    proc foo {} {                                                               
        set ::highest_level [max $::highest_level [info level]]                 
        puts $::highest_level                                                   
        foo                     # Uncontrolled recursive call                   
    }                                                                           
                                                                                
    foo                         # Start a Stack Overflow                        

See also: proc

regex

    regex (regular expressions) are not supported

[string match] ([match]) supports glob style string matching and [scan]
can also be useful in string matching

Example:

    set string "#08D03F"                                                        
    scan $string "#%2x%2x%2x" r g b                                             
    puts "$r $g $b"                                                             

Results:

    8 141 63                                                                    

    https://www.tcl.tk/man/tcl8.5/TclCmd/scan.htm

See: string match, scan

reserved variables

Ticol reserves a small number of variables for internal use

These values are protected against deletion by [clear] and [run]

    argv            const string                                                
    argv0           const string                                                
    argc            const integer                                               
    eof             const integer                                               
    env             array                                                       
    exe_name        const string                                                
    false           const integer                                               
    NAN             const string                                                
    pi              const float                                                 
    qbf             const string                                                
    tcl_config      const string                                                
    tcl_cli         const string                                                
    tcl_date        const string                                                
    tcl_threaded    const bool                                                  
    tcl_precision   const integer                                               
    tcl_version     const string                                                
    theta           const float                                                 
    this            Defined dynamically during evaluation                       
    true            const integer                                               
    win64           const integer                                               

See also: $this

rename
 
    bool [rename oldname newname]

Rename a procedure (proc). This may be used to temporarily 'hide' or
replace an existing procedure. 

Renaming a proc to an empty string "" or {} will delete the proc.
The author is not a fan of this mechanism and has added the [undef]
command to undefine a proc.

[defined] may also be used to test for the existence of a proc before
it is defined or deleted. Rename has less 'functional transparency'
and obviousness of purpose than [undef]

You cannot rename a currently active proc or its parent proc. Attempting
to do so will generate an error

Example: 

    proc square {x} {                                                           
       * $x $x                                                                  
    }                                                                           
    puts [square 10]                                                            
    rename square skware                                                        
    puts [skware 10]                                                            
    rename skware ""                                                            

Result:

    100                                                                         
    100                                                                         

See also: is_active, undef, defined, file rename, ren

result

    integer [result -code]
    string [result]

Return the result of the last command either as an integer code value

See also: eval, break, continue, return, exit, error

return

    return value ?-code number|nmemonic?
    return [command result] ?-code number|mnemonic?

or:

    return -code number|nmemonic value

[return] exits a procedure or block of code. [return] may optionally
return an error code to the calling routine. There are limits to the use
of [return] and it does not behave in the same way as with other languages
such as C/C++ [return] will exit all nested procedures and unwind the
call-stack down to proc level 0 (the root invocation level)

Example:

    proc level_p {} {                                                           
       puts "in level_p ([info level])"                                         
       proc level_q {} {                                                        
          puts "in level_q ([info level])"                                      
          proc level_r {} {                                                     
             puts "In level_r ([info level])"                                   
             return $::true                                                     
          }                                                                     
          level_r                                                               
       }                                                                        
       level_q                                                                  
       return $::false                                                          
    }                                                                           
    puts [bool [level_p]]                                                       

Results:

    in level_p (1)                                                              
    in level_q (2)                                                              
    In level_r (3)                                                              
    True                                                                        

[return] will return a code value of 2 (return) for all levels > level 0
At level 0 [return] will return code 0 (ok)

    Return Code Values:                                                         
    ------------------------                                                    
    Number    Mnemonic                                                          
    ------------------------                                                    
    0         ok                                                                
    1         error                                                             
    2         return                                                            
    3         break                                                             
    4         continue                                                          
    ------------------------                                                    

The standard return codes can be overridden using the '-code' argument.
This allows Tcl procedures to be written which extend the language and
interact with system codes

Whilst [return] can be used to exit structured control commands such as
[if], [for], [while], [do] etc., results may be unexpected, although you
may use the following to exit control structures:  

    return -code break                                                          

[return] will exit a control structure and if the code is passed back
to nesting level 0 then the script will exit

Example:

    # Will exit the script                                                      
    puts "I will now exit"                                                      
    return 23                                                                   
    puts "I cannot be seen"                                                     

Example:

    # This code will exit the for loop and script when $i == 5                  
    option expression off                                                       
    for {set i 0} {[< $i 10]} {++ i} {                                          
       if {[== $i 5]} {                                                         
          return $i                                                             
          puts "I will not be printed if i==5"                                  
       }                                                                        
    }                                                                           

Example: ([return] cannot be used with control block)

    # Does not exit using return at level 0                                     
    # Will iterate for 10 loops                                                 
    set i 0                                                                     
    while {[< $i 10]} {                                                         
       ++ i                                                                     
       return                                                                   
    }                                                                           

Executing a [return] command from a root-level flow-control block structure
such as [if] will not terminate the script, if you wish to do this then use
a '-code exit' argument which will return to the CLI or Windows.

Example:

    set a [rnd 0 100]                                                           
    if {== a 10} {return 23 -code exit}                                         

Result:

    Script is exited with 22 if random value is 10                              

Return will exit from other flow-control constructs

Example:

    set i 0                                                                     
    goto_block {                                                                
       start {                                                                  
          while {< $i 10} {                                                     
             if {== $i 5} {return $i}                                           
             incr i                                                             
          }                                                                     
       }                                                                        
    }                                                                           
    puts "i is $i"                                                              

Result

    i is 5                                                                      

Using [return] with a code "ok" or code 0 is the only way to return a
string-literal from [eval] without it being interpreted as a command:

Example:

    set x "Hello"                                                               
    puts [eval "return $x -code ok"]                                            

Result:

    Hello                                                                       

[return] can return the result of a Tcl command to another command

Example:

    # Will set array a to a 4 element array                                     
    array set a [return {                                                       
       1 {foo}                                                                  
       2 {bar}                                                                  
       3 {baz}                                                                  
       4 {quux}                                                                 
    }]                                                                          
    array walk a                                                                

                                                                                
    -------------------------------------------                                 
    hash_table object:0x3c9e10 size:250                                         
    -------------------------------------------                                 
    Bucket(73) Node: 0x20bd300 chained nodes:1                                  
       1: 0x20bd300 key:'2' => 'bar'                                            
    Bucket(113) Node: 0x20bd3f0 chained nodes:1                                 
       1: 0x20bd3f0 key:'4' => 'quux'                                           
    Bucket(165) Node: 0x20bd330 chained nodes:1                                 
       1: 0x20bd330 key:'3' => 'baz'                                            
    Bucket(220) Node: 0x20bd2d0 chained nodes:1                                 
       1: 0x20bd2d0 key:'1' => 'foo'                                            

See also: break, continue, stop, exit, if, while, foreach

replacechar

    integer [replacechar variable search_char replacechar]
    integer [replacechar variable search_mask replacechar -mask]

Performs an in-situ replace of characters specified by 'search' with 
'replace'. Because [replacechar] operates on a variable name it avoids
the need for internal and external copy-reassignment which makes it more
efficient with very large strings (i.e. you don't need to [set] the 
return value back to the variable)

Use [string replace] to replace characters within strings

Only the first character of the 'search' and 'replace' arguments are
significant unless the '-mask' argument is used. Each of these arguments
must either be a string or a character representation of an integer using
[chr]

[replacechar] should be used in preference to [string replace] with 
reassignment when replacing single characters

[replacechar] takes a variable name as its first argument, not a string
this also avoids dereference and copy

A count of the number of characters replaced is returned

Example:

    set s [readfile ticol.man]                                                  
    replacechar s "\t" " "                                                      

Example:

    set s [readfile ticol.man]                                                  
    replacechar t1 [chr 0x09] [chr 0x20]                                        

See also: string replace, string, strchr, strstr

rewind
 
    bool [rewind handle]

Rewind the file stream pointer for a given file handle. The handle must
have been opened using [open] or [file open]

See also: tell, seek, open, file

right, string right
 
    string [right string n]
    string [right varName n -var]
    string [string right string n]
    string [string right varName n -var]

Return the rightmost 'n' characters of a string

If n < 1 then an empty string will be returned
If n >= length of 'variable' then the whole string will be returned

If the string contains special Tcl characters []{}$ then call [unescape]
on the string before calling [string right]

Example:

    puts [right $qbf 3]                                                         

Result:

    dog                                                                         

See also: left, mid, setc, setl, setr, strto, strtor, filter

round
 
    double [round number ?places?]           # Command version
    double [funct round number ?places?]     # Command version of function
    double [expr round(N,places)]            # Function version

Rounding to a given number of decimal places

The command [round] and function round() follow PHP's PHP_ROUND_HALF_UP
which rounds values up to 'places' decimal places away from zero.

Thus 1.1 rounds to 1.0, 1.9 rounds to 2.0
The 'tie-breaker' at 1.5 is for 1.5 round to 2.0 and -1.5 into -2.0

Example:

    puts [round 1.1]                                                            
    puts [round 1.5]                                                            
    puts [round 1.9]                                                            
    puts [round -1.1]                                                           
    puts [round -1.5]                                                           
    puts [round -1.9]                                                           
    puts [funct round $pi 4]                                                    
    puts [expr round($pi,4)]                                                    

Result:

    1                                                                           
    2                                                                           
    2                                                                           
    -1                                                                          
    -2                                                                          
    -2                                                                          
    3.1416                                                                      
    3.1416                                                                      

Example:

    puts [round 1.5 0]                                                          

Result:

    2                                                                           

Example:

    puts [round -1.5 0]                                                         

Result:

    -2                                                                          

See also: math, format, fpart, vfract, fraction

setr
 
    string [setr target-variable source-variable | source-string]

Insert source string or variable into the target, right-aligned
[setr] is based on the common BASIC RSET command and can be useful
used in  combination with [makestr]

Example:

    set s [makestr 20 *]                                                        
    setr s "hello"                                                              
    puts $s                                                                     

Result:

    ***************hello                                                        

Example:

Blank out part of a string

    set s "0849 666 616"                                                        
    setr s [string repeat * 7]                                                  
    puts "'$s'"                                                                 

Result:

    0849 *******                                                                

See also: makestr, setc, setl, mid, strstr, string

RLM Network Licence Query Plugin

The [rlm] command may be used to check, analyse and monitor Reprise RLM
licence files using direct or UNC (SMB) file access.

    To load, use:   lib ticol_rlm ?-verbose? ?-nocomplain?

Prerequisite:

    File read access is required to the licence file being checked

Command and Syntax:

    Currently only one subcommand is supported, [rlm check] which can
    analyse licence or module expiry

    integer [rlm check file module ?-months|-days? ?-min|-avg? ?-var r?]

Example:

    option expression off                                                       
    lib ticol_rlm -verbose                                                      
    set filespec "C:\\rlm\\product\\*.lic"                                      
    ls $filespec -r -array a      # Get a list of RLM LIC files                 
                                                                                
    if {array is_set a} {                                                       
        set count [array size a]                                                
        puts "Checking $count file(s)\r\n"                                      
        for {set i 0} {< $i $count} {++ i} {                                    
            try {                                                               
                set r [rlm check [escape [lindex $a($i) 3]] module01]           
                puts "Checking '[escape [lindex $a($i) 3]]' (result $r)"        
                if {> $r 0} {                                                   
                    set unexpired($found) "[lindex $a($i) 3] ($r days)"         
                    ++ found                                                    
                } else {                                                        
                    textcolor red                                               
                    puts "module01 has expired in [escape [lindex $a($i) 3]]"   
                    textcolor                                                   
                }                                                               
            } catch {                                                           
                puts "Error $r analysing file"                                  
            }                                                                   
        }                                                                       
        puts "Line: "#__LINE__                                                  
                                                                                
        if {> $found 0} {                                                       
        set msg "Found $found unexpired RLM files in this tree\r\n\r\n"         
        for {set i 0} {< $i $found} {++ i} {                                    
            append msg "$unexpired($i)\r\n"                                     
        } else {                                                                
            set msg "All files in $filespec have expired module: module01"      
        }                                                                       
        msgbox $msg $scriptname                                                 
    } else {                                                                    
        msgbox "No RLM files found in the current folder tree" $scriptname      
    }                                                                           

Other Tools
-----------
A dummy version of rlmsign-x86.exe is available which can "sign"
skeleton RLM licence files to produce a visually-convincing but
wholly-invalid licence file for test use with scripts. Such fake
licences cannot be used with the Reprise rlm.exe licence server
to serve valid product licences

This is available from the author's website as: rlmsign-x86.exe

rlmtool.exe can unsign licences or create signed, valid licence
files from a template file, providing it has access to the valid
rlmsign-x86.exe on your system. Alternately it can use a template
to create a valid skeleton RLM licence which can be tweaked and
signed later on

Useful Links
------------
Technical information on the RLM licensing system is freely available
for administrators from Reprise Software

Reprise Licence Admin Bundle
    http://www.reprisesoftware.com/admin/software-licensing-download.php
Reprise Licence Admin Guide (PDF)
    http://www.reprisesoftware.com/RLM_License_Administration.pdf

See also: ticol_hasp, plugins

run - (CLI Command - Run a Ticol Tcl script)
 
    run filename?.tcl? ?argument? ?argument...?
    run filename.tcx   ?-password password? ?argument? ?argument...?
    run                # Pre-loaded script (arguments are not allowed)

See also: [clearprocs], [source]

Runs a Tcl script file from the Command Line Interpreter (CLI) shell.
[source] is the standard command to execute external Tcl code
Both [run] and [load] destroy the current program space whereas [source]
or [eval] does not. Use [source] to chain external code where the 
current script and variables need to be retained. [run] functions in
much the same was as in legacy BASIC

[run filename] will load and run the specified filename and this form
will accept arguments. It will also decrypt and run obfuscated TCX files
as necessary. It may be necessary to give a password argument for some 
files.

[run] will internally call [load] and [clear]. If you wish to keep 
existing variables use [eval] or [source] instead. 
Use [clearprocs] to clear any currently defined procedures

If a path is not given to the script then Ticol will look for the script
in the current directory

Use [run] without an argument if the file has already been loaded into
the program space using [load]. Arguments cannot be specified in this
case

[run] and [source] must NOT be used to chain from one script to another. 
Use [eval] instead. Calling [run] from a running script will generate an
error.

[run] will force [option escape] to ON, [source] leaves this setting
unchanged. Entering the CLI will force this option OFF so using [source]
from the CLI will require correctly setting [option escape] in a script
Thus, any scripts which depend on processing Tcl backslash escape
sequences may not work properly if called using [source] from the CLI

Note that TCX protected files which are encoded with the /C:PASSWORD
argument will require the password to be specified using a
'-password password' argument. Files encrypted using other methods will
be automatically self-decrypting and validated. A password may be a 
phrase, user-name, MAC address, workstation or Ticol version. 
The resulting TCX file will be locked accordingly when created

Use [dump] to display non-encrypted (obfuscated) loaded source code at the
console.

The [run] command will clear the user-level variable table including non-
system level consts and will reset the interpreter command count

To call a child script and return to the parent use [source scriptname]

To run a script in elevated mode, use: [elevate scriptname]

Example:

    ticol> run test.tcl                                                         
    ticol> dump                                                                 

Result:

    test.tcl is executed                                                        
    The script is displayed                                                     

See also: source, clearprocs, eval, load, dump, exit, call, elevate

Running Scripts

Scripts may be run in a number of ways, either with or without arguments

Example:

    ticol.exe                      # Load the CLI                               
    load myscript                  # Load the script                            
    dump                           # View it                                    
    run                            # Run it (no args allowed)                   

Example:

    ticol.exe                      # Load the CLI                               
    run myscript arg1 arg2         # Run script from CLI with args              

Example:

    ticol.exe                      # Load the CLI                               
    source myscript.tcl arg1 arg2  # Run using source                           

Example:

    ticol.exe myscript arg1 arg2   # Run from Windows CLI                       

See also: load, run, eval

sc

    integer [sc string1 string2 ?-nocase? ?-verbose? ?-expand?]

Compare two strings and return base 1 index-offset at which there is
a mismatch or return 0 if the strings are identical

[cmp] is a similar command but which returns a boolean value

Comparing complex strings while debugging can be difficult. [sc] simplifies
this process and gives an indication as to the location of a mismatch

'-verbose' shows the characters at the mismatch point (if any).

The '-expand' argument shows more detail regarding the mismatch point.

Example:

    set s "The quick brown dog jumps over the lazy fox"                         
    puts [sc $s $qbf -verbose]                                                  

Result:

    sc: Mismatch at char 17, 'd' != 'f'                                         
    17                                                                          

See also: cmp, string, debugging

scan
 
    string [scan input-string format-mask ?output-variable?...]

[scan] parses an input string using conversion specifiers in the style of 
C sscanf and outputs into one or more variables

This command parses substrings from an input string in a fashion similar to
the ANSI C sscanf function and returns a count of the number of conversions 
successfully performed or -1 if the end of the input string is reached
before any conversions have been performed

The string parameter is the input to be parsed and the format parameter 
indicates how to parse it, using % conversion specifiers. Each variable is
a name of a new variable to be created or set

When a substring is scanned from string that matches a conversion 
specifier, the substring is assigned to the corresponding variable. If no
variables are specified, then scan works in an inline manner, returning the
data that would otherwise be stored in the variables as a list. In the
inline case, an empty string is returned when the end of the input string
is reached before any conversions have been performed.

Any prefix or embedded text in the mask/format-specifier string must match
the parsed string (case sensitive)

printf style alignment and precision values are not supported by [scan]
e.g. %-.2s is not allowed. Width values are, for example. %2s

An asterisk (*) following the percent sign indicates that the value is to
be discarded and not stored in a variable

The Tcl [chars] and [^chars] mask forms are not supported

Scan Format Specifier
--------------------
    %[*][width][length]specifier 

    %%       Literal % character
    *        Placeholder. Skip this value
    %b       Binary value (up to 64 bits)
    %c       Single character
    %d       Decimal integer (64 bit)
    %f       Floating point value
    %g       Floating point value (general)
    %i       Integer (64 bit)
    %n       Number of characters stored in the preceding variable
    %o       Octal
    %s       String
    %x       Lower-case hexadecimal
    %X       Upper-case hexadecimal

The use of %$ is unsupported

Example:

    puts [scan "hello there 1 $pi 12" {%s %*s   %i %4f %2x} s t p q]            
    puts $s,$t,$p,$q                                                            

Result:

    hello,1,3.140000,12                                                         

Example:

    set ip 12.34.56.78                                                          
    scan $ip %d.%d.%d.%d o1 o2 o3 o4                                            
    vars o*                                                                     

Result:

    o1   12                                                                     
    o2   34                                                                     
    o3   56                                                                     
    o4   78                                                                     

Example:

    # Scan an ISO date into [date] format                                       
    scan 20161122 %4i%2i%2i y m d                                               
    puts "$y/$m/$d"                                                             

Result:

    2016/11/22                                                                  

Example:

    set string "#08D03F"                                                        
    scan $string "#%2x%2x%2x" r g b                                             
    assert "$r $g $b" {== $_ "8 208 63"} -v -line #__LINE__                     

Example:

    set result [scan $qbf "The %c%n%c%n%c%n" a p b q c r]                       
    puts "result: $result $a $p $b $q $c $r"                                    
    puts "[chr $a][chr $b][chr $c]"              # "qui"                        
    assert "$a $b $c $p $q $r" {== $_ "113 117 105 5 6 7"} -v                   
    assert "[chr $a][chr $b][chr $c]" {eq $_ "qui"} -v                          

See also: format, printf, clock scan

screen
 
    integer [screen width | height | attribute]

Query screen metrics

Note that Windows 10 can dynamically resize the console window and may
auto-resize a console if you drag it from one monitor to another on a
multiple-monitor system. This may cause screen formatting to become
misaligned

[screen width] and [screen height] are returned as pixels

You can query [screen width] to check for a resized console window

See [console] for specific console functions

Note that when redirecting text at the Windows console using ">" the
true console metrics are not available. [screen] will return generic
default values

Example:

    puts [screen width],[screen height]                                         

Result

    1280,800                                                                    

See also: console, gotoxy, box, msgbox

set
 
    string [set variable-name ?value?]
    string [= variable-name ?value?]

Returns, creates, assigns a value to or from a variable or displays a
variable. [=] is a shorthand alias for [set]

If 'value' argument is absent then the contents of variable are returned
(i.e. the variable will be dereferenced without the need for a $ prefix)
The variable will also be returned without backslash escapes translated

If a variable with a given value argument doesn't exist it will be created
Empty arguments may be specified using {}

Early version of Tcl lacked the '$' operator and instead used [set var] to
echo a variable's contents

To concatenate variables efficiently use append instead of chaining set

    Code                 Description
    --------------       --------------------------------------
                                                                                
    set name             # display/return variable contents                     
                         # without backslash translation                        
    set array(subscript) # display/return array variable contents               
    set name {}          # if name then set to ""                               
    set name {}          # if not name then create and set to ""                
    set name value       # if name then set to var                              
    set name value       # if not name then create and set to val               

Example:

    option echo on                                                              
    set env(windir)         # Echo $env(windir)                                 

Result:

    C:\Windows                                                                  

Example:

    = a 23                                                                      
    = a                                                                         

Result:

    23                                                                          

Struct Members
--------------
Use [struct set] to set a struct field member. See: struct set

Array Promotion
---------------
A standard variable may be promoted to an array by assignment using
[set]. No confirmation of promotion is issued. This helps facilitate
the working of [static] variables

Example:

    set a Hello    # Define a standard/simple variable                          
    puts $a        # Returns "Hello"                                            
    set a(10) 23   # Variable a is now type <array>                             
    puts $a        # Returns "<array>"                                          
    puts $a(10)    # Returns 23                                                 

Results:

    Hello                                                                       
    <array>                                                                     
    23                                                                          

Variables with Embedded Whitespace
----------------------------------
It is not recommended to create and reference variables with embedded
whitespace but it is perfectly possible to do so.

Tab characters within variable names should be avoided at all cost as
these require escaping, which will be blocked by any necessary braces.

Example:

    # Space                                                                     
    set "my var" 23                                                             
    puts ${my var}                                                              
    unset {my var}                                                              

Cautions
--------
Do not call with dollar prefix '$a' as follows:

    set a 10                                                                    
    puts [set $a]   # Will raise error 'set: Variable '10' not found'           

Empty variables may be set using: set var {}

[set variable] will not echo contents if option echo is set to OFF. Use
[option echo on] to see console echo results

See also: $, dereference, struct set. struct,  setb, static, dollar,
          append, variable, variables, vars, unset, puts, echo

setat
 
    string [setat var string startpos len]

Sets a string or array variable directly by copying from a string with 
parameters pointing to the location and length of the segment to copy from
in the source string. [setat] is similar to [mid] except that it sets a 
specified variable. Out of range arguments will return a null string

Invalid startpos and range values will return an empty string

Example:

    setat q $qbf 17 3    # Set q by copying from $qbf at 17 for 3 chars         
    puts "q is $q"                                                              

Result:

    fox                                                                         

Example:

    setat a(0) $qbf 1 3  # Set a(0) by copying from $qbf at 1 for 3 chars       
    puts "a(0) is $a(0)"                                                        

Result:

    The                                                                         

See also: append, mid, mids, cat, cmp, performance

setb, struct setb

    struct setb member-field value ?width? ?-value?
    setb existingVar|newVar variable|string|expression ?width? ?-value?

Set a struct member variable to some binary/integer value which is passed
via the address of a Ticol variable. This will be in Intel 'little-endian'
order. Float values should be converted using [mkn]

An optional width value may be specified:

    [addressof]              [addressofb],[set],[$]                             
    |                        |                                                  
    v                        v                                                  
    +---------------------+  +---------------------+                            
    | Tcl Variable Object |->| Variable Data Block |                            
    |                     |  | (Dynamic)           |                            
    +---------------------+  +---------------------+                            
    |                        |                                                  
    v                        v                                                  
    [ofaddress]              [ofaddressb]                                       

    [addressof]              [addressofb],[set].[setb],[struct setb]            
    |                        |                                                  
    v                        v                                                  
    +---------------------+  +---------------------+                            
    | Struct member 1     |->| Struct Data Block   |                            
    +---------------------+  | (Fixed)             |                            
    | Struct member 2     |->|                     |                            
    +---------------------+  |                     |                            
    | Struct member N     |->|                     |                            
    +---------------------+  +---------------------+                            
    |                        |                                                  
    v                        v                                                  
    [ofaddress]              [ofaddressb]                                       

[setb] will only operate with an rvalue which is a variable
You cannot use a literal as an Rvalue for [setb]

By default a pointer to the original value is stored in the target field

If '-value' is passed then the literal value will be stored as binary
rather than an address value (store value rather than store reference)

Retrieve [struct setb] values using [ofaddress] to dereference back

Literal integer values greater than the range of DWORD must be retrieved
back using [ofaddressb <var> 8]

You can set a buffer to NULL prefix using e.g.

    struct setb buffer [chr 0] 4                                                

Example:

    struct s { a 10 b 20 c 30}                                                  
    struct setb s.a 12345         # Hex is 0x003039                             
    puts "Address:$s.a"           # Will return the address-offset              
    puts "Value:[ofaddressb s.a]" # Will return the binary value                
    newline                                                                     
    dump s                                                                      

Results:

  Address:36931840                                                              
  Value:12345                                                                   
                                                                                
  struct 's' level 0 (0x2338940) data @36931840 (0x2338900)                     
  ¦      60 byte(s), 3 member(s)                                                
  + 1:'->s.a'    => 36931840 (0x2338900) 10 byte(s)                             
  + 2:'->s.b'    => 36931850 (0x233890a) 20 byte(s)                             
  + 3:'->s.c'    => 36931870 (0x233891e) 30 byte(s)                             
                                                                                
  Address   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F ASCII                
  -------- ----------------------- ----------------------- ----------------     
  02338900 39 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 90..............     
       __ __ __ __                                                              
       00 00 30 39 -> 0x3039 -> 12345                                           

Comparing [struct set] with [struct setb]
-----------------------------------------
                                                                                
 struct s {a 10}                                                                
 struct set s.a 23        # Will set to binary 0x32,0x33 (text 23)              
 dump s                                                                         

                                                                                
     struct 's' level 0 (0x259b9b0) data @39434656 (0x259b9a0)                  
 ¦      10 byte(s), 1 member(s)                                                 
 + 1:'->s.a'    => 39434656 (0x259b9a0) 10 byte(s)                              
                                                                                
 Address   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F ASCII                 
 -------- ----------------------- ----------------------- ----------------      
 0259b9a0 32 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 23..............      

                                                                                
 struct s {a 10}                                                                
 struct setb s.a 23       # Will set to binary 0x17 (23)                        
 dump s                                                                         

                                                                                
 struct 's' level 0 (0x259b9b0) data @39434656 (0x259b9a0)                      
 ¦      10 byte(s), 1 member(s)                                                 
 + 1:'->s.a'    => 39434656 (0x259b9a0) 10 byte(s)                              
                                                                                
 Address   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F ASCII                 
 -------- ----------------------- ----------------------- ----------------      
 0259b9a0 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................      

See also: setb, 

See also:    struct, set, addressof, ofaddress, addressofb, ofaddressb, vars,
            pointers, is_pointer, struct elements, calldll, calldll_cdecl, 
            struct dump, type, vars, array, stack, variables, 

setenv
 
    bool [setenv variable value]

Set an environment variable. This does not alter the main operating system
environment table. Changes are lost when Ticol exits

A variable can be cleared by calling with an empty string {} value

Example:

    setenv ticol hello                                                          
    puts "'[getenv ticol]'"                                                     
    setenv ticol {}                                                             
    puts "'[getenv ticol]'"                                                     

Results:

    'hello'                                                                     
    ''                                                                          

See also: getenv

setresult

    result [setresult value]

Experimental. For external DLLs only

Single Line Commands

Commands may be sent from Windows as a single line provided they are
properly punctuated. The entire command must be wrapped in double-quotes
with individual statements separated by semi-colons. A single semi-colon
must follow this command statement

Since Tcl will evaluate and replace all variables within a single
expression it may be necessary to delay evaluation of variables in, say a
terminating while test of a do..while loop by wrapping the test expression
in extra braces, i.e.

    option expression on                                                        
    set i 0                                                                     
    do {                                                                        
       puts "i is $i"                                                           
       if {$i == 2}                                                             
       return                                                                   
       incr i                                                                   
    } while { $i < 10 }                                                         
                                                                                
option expression on                                                            
set i 0; do {puts "i is $i"; if {$i == 2} return; incr i} while {$i < 10 }      
                                                                                
option expression on                                                            
set i 0; do {puts "i is $i"; if {$i == 10} return; incr i} while {{$i < 10}}    

If the terminating punctuation isn't present then the command may be 
executed as an external Windows command or Tcl script. A Tcl script may
follow the single-line command

Example: (Wrapped to fit the help window)

The following command calls the function PingAPI in an external DLL called
ping32.dll with various parameters set and a buffer allocated:

ticol ; "set timeout 0; set datalen 64;set buf [makestr 16]; calldll ping32     
PingAPI localhost [addressofb buf]:4 datalen:2 timeout:4 1000; puts $buf"       

(Note the requirement for the ";" prefix in the above command)

Result:

    127.0.0.1                                                                   

Example: (Wrapped to fit the help window)

    set i 0; array foreach env val ss {printf "$i:\t%-20.20s=%-45.45s\r\n"      
    $ss $val; [incr i];}                                                        

Result:

    <lists the environment table>                                               

See also: Ticol, CLI, spawn, exec, shell

sgn

    integer sgn(N)

sgn() is defined as an expression function. It may also be defined as
a Tcl proc as follows:

    proc sgn x {expr {($x>0)-($x<0)}}                                           

The [expr] function sgn() may also be called using [funct sgn N]

See also: expression functions, funct, proc, math

Single Quotes

Single quote characters do not group statements into an argument or 
characters into a string. Double quotes should be used in most cases to
group characters into a string and curly braces to group words into single
arguments. In most cases you can also use curly braces to group characters
into a string

Example: (wrong)

    puts 'Hello world'    # [puts] will receive 2 arguments...                  
                          # ... "'Hello" and "world'"                           

Examples: (correct)

    puts "Hello world"                                                          
    puts {Hello world}                                                          

See also: double quotes, braces

Disambiguity: sort

    See:

    array
    array get
    array list
    array list
    array sort
    array foreach
    arrays
    lsearch
    lsort
    
See also: faq

spawn
 
    spawn variable ?-echo? program arg1 arg2...
    spawn - ?-echo? program arg1 arg2...

Run a Windows console program with redirected output and interactive input

Output may be directed to a variable or echoed back to the console without 
passing into a variable if the dummy variable '-' is given. 

Return Value
------------
If a variable is given then spawn returns the number of characters read or
0 for a null return. If -echo is not used then the returnd variable may be
printed using [puts]

CTRL+C
------
CTRL+C is available for external applications, if quit using CTRL+C then 
execution will be returned to the Ticol CLI shell

Arguments
---------
[spawn] expects a backslash-escaped input string
[escape] may be used to correctly backslash-format a string
[unescape] may  be used to remove backslash-escapes where required

NOTE: Avoid wrapping the entire argument list in double-quotes. This is not
necessary and may result in failure to launch the command. If you quote
then ensure you quote only the command to be executed and its tail

Examples:

From the CLI with backslash escape processing ON
                                                                                
    CLI:    option escape on; option autoexec off                               
    RIGHT:  spawn - $env(comspec) /c chcp                                       
    RIGHT:  spawn - -echo dir c:\\tools\\*.exe -w -od                           
    RIGHT:  spawn - -echo cmd.exe /c "dir c:\\tools\\*.exe -w"                  
    RIGHT:  spawn - notepad.exe $filename                                       
    WRONG:  spawn - "notepad.exe $filename"                                     

From the CLI with backslash escape processing OFF
                                                                                
    CLI:    option escape off                                                   
            spawn - -echo cmd.exe /c "dir c:\tools\*.exe -w"                    
    RIGHT:  option escape off                                                   
            spawn - -echo c:\tools\mailcmd.exe                                  
    RIGHT:  spawn - -echo c:\\apps\\mailcmd\\mailcmd.exe                        
    RIGHT:  spawn - -echo $env(comspec) /c c:\\apps\\mailcmd\\mailcmd.exe       
    WRONG   spawn - -echo "cmd.exe /c dir c:\\tools\\*.exe -w"                  

NOTE that when -echo is used the output text can have no colour attributes

NOTE that some Windows files cannot be spawned. e.g. qwinsta.exe

NOTE that some console applications may not choose to flush output 
continuously when and this will affect '-echo'. All programs should have 
their output flushed by Windows on exiting.

cmd.exe is not launched by [spawn], this is by design. Some commands such
as chcp or dir will require a command-shell prefix as these are command-
shell commands implemented within CMD.EXE and not external programs. 
e.g. for:

    cmd.exe /c chcp     # chcp is command built into cmd.exe

You may need to input the full, translated COMSPEC value, e.g.: 

    C:\WINDOWS\system32\cmd.exe chcp
    
This may be done using the $env(COMSPEC) const instead of the full path
(case is insignificant)...

    $env(COMSPEC) chcp                                                          
    $env(comspec) chcp                                                          

For local buffered output control see the [flush] command

Keyboard input is unaffected and is not redirected

Example:

    spawn var xping 127.0.0.1 -n:2 ; puts $var                                  

Result:

    1 00:31:40 Reply from: 127.0.0.1 00-00-00-00-00-00 (0 ms)                   
    2 00:31:41 Reply from: 127.0.0.1 00-00-00-00-00-00 (0 ms)                   

Example:

    puts [spawn - cmd.exe /c chcp]                                              

Result:

    Code page is: 850                                                           

Example:

    option autoexec off                                                         
    option escape on                                                            
    puts [spawn - $env(comspec) /c set /a "_num=(2+3)*5"]                       

Result:

    25                                                                          

Example:

    puts [spawn - havewe.exe c:\\ /comma /gb]                                   

Result:

    23.88978                                                                    

Example:

    spawn - -echo xping 127.0.0.1 -n:1        # Echo to the console             

Result:

      XPing: Pinging 127.0.0.1 [Snoopy] with 32 bytes                           
                                                                                
    1 13:37:22 Reply from: 127.0.0.1 00-00-00-00-00-00 (0 ms)                   

Example:

    spawn -echo xping 127.0.0.1 -n:1        # Return via command body           

Result:

    <no result> (the returned text is discarded)                                

Example:

    spawn d $env(comspec) /c dir *.dll /b                                       

Result:

    Returns <list of dlls> in variable d                                        

                                                                                
    puts "Spawning hh.exe to load help topic '$ dereference' ..."               
    spawn - hh.exe "mk:@MSITStore:ticol.chm::/ticol.man.htm#$+dereference"      

Calling External Script Interpreters With [spawn]
-------------------------------------------------
External script interpreters such as VBScrip or PHP may be called using
[spawn] with the console output being redirected back to a variable

Example:

    unset x -nocomplain                                                         
    spawn x cscript.exe //NoLogo myscript.vbs                                   
    puts $x                                                                     

See also: exec, shell, flush, ticol_cscript

split

    list [split <string> ?maskchars?]
    list [split <string> maskchars ?-array var?]
    list [split <string> string -literal]
    list [split <string> string -literal ?-array var?]

Split a string according to mask string and return a Tcl list

If no mask character-set is given then a single space is used
The mask character strings are removed from the returned data
If the first character is matched to mask characters then it is removed 
and replaced with an empty brace pair
If the last character is matched to mask character then it is removed
and replaced with an empty brace pair
If two successive mask characters are matched then the characters are
both removed and replaced with an empty brace pair
Otherwise, characters are returned according to Tcl list rules after any
mask chars are removed and the intervening text split by braces

[split] is extended in Ticol Tcl with the '-literal' argument. This adds
the ability to match literal strings rather than character sets and which
modify interpretation of the 'mask' argument. This is useful when using
say CRLF pairs to split text into an array of lines without additional
processing, or if parsing say HTML text by HTML tag strings. Using [split]
with \r\n without the '-literal' argument will mis-parse the resulting
data and introduce empty lines

Note that if you try to split using a single numeric char e.g. 0x0d, it 
will not work, as the MPP will substitute the numeric string "13" and 
tokenise on that. Instead you must pass [chr 13] or [chr 0x0d]

If you split a string in an array by filtering \r\n (0x0d,0d0a) then note
that because all CRLF pairs are removed, this will also remove all
empty lines comprised of only CRLFs

Example:

    puts [join [split $qbf " "] "\n"]  # Split and rejoin with \n newlines      

Result

    The                                                                         
    quick                                                                       
    brown                                                                       
    fox                                                                         
    jumps                                                                       

Example:

    set s "the quick\nbrown fox\njumps over\nthe lazy\ndog!"                    
    puts [join [split $qbf " "] [chr 10]]                                       

Result:

    {the quick} {brown fox} {jumps over} {the lazy} dog!                        

Examples:

    split "comp.lang.tcl"                                                       
    split "comp.lang.tcl" "."                                                   

Results:

    c o m p . l a n g . t c l                                                   
    comp lang tcl                                                               

Example:

    # Split CRLF-delimited text into array using literal string "\r\n"          
    set s "The quick\r\nbrown fox\r\njumps over\r\nthe lazy\r\ndog!"            
    split $s [chr 0x0d][chr 0x0a] -literal -array a                             
    for {set i 0} {< $i [array size a]} {++ i} {                                
        puts "$i->'$a($i)'"                                                     
    }                                                                           

Results:

    0->'The quick'                                                              
    1->'brown fox'                                                              
    2->'jumps over'                                                             
    3->'the lazy'                                                               
    4->'dog!'                                                                   

See also: join, chartok, list, array, arrays

Square Brackets

Square brackets are used for command substitution. The contents of a pair
of square brackets is evaluated by the interpreter, and the return value
is substituted into the script

See: commands

stack

Native stack data type (built-in / not external)

    integer [stack count    varName]
    bool    [stack create   varName size]
    integer [stack push     varName value ?value? ... ]
    string  [stack pop      varName]
    string  [stack peek     varName]
    void    [stack foreach  varName indexVar {script} ?-r?]
    string  [stack item     varName index ?default?]
    list    [stack list     varName]
    list    [stack listr    varName]
    integer [stack size     varName]
    bool    [stack is_empty varName]
    bool    [stack clear    varName]
    bool    [stack unset    varName -nocomplain]

The stack command implements a native stack data type. The stack must first
be allocated using a call to [stack create]. The stack may be destroyed
using [stack unset] or emptied of contents and retained using
[stack clear]

Items are added using [stack push] and removed using [stack pop]. the head
of the stack may be inspected using [stack peek] and the current size of
the stack queried using [stack size]..[stack peek] is the equivalent to 
[stack head]

It is possible to index the stack in normal (LIFO) or reverse (FIFO) order
by using [stack item]. Note that index values should be array base 0. 

Additionally the contents of a stack can be returned as a Tcl list in
either LIFO or FIFO order by using [stack list] and [stack listr]
respectively

An existing stack may be cleared (re-initialised) to an empty stack using
[stack clear]

Stack objects will be destroyed automatically when the stack variable goes
out of scope within a procedure, but they may also be destroyed 
programmatically using [stack unset s] if necessary

[stack foreach] will operate in LIFO order unless the -r (reverse) argument
is used, in which case the output will be in FIFO order

Operations on an initialised but empty stack will return an empty list {}
Stack operations on a variable not initialised as stack type will result in
a runtime error which is trappable using [catch] or [try ... catch]

A stack overflow will result in a runtime error which is trappable using
[catch] or [try ... catch]

All [stack] commands are addressed via the variable name

Example:

    stack create s 1                                                            
    catch {stack push s man} err; puts $err                                     
    catch {stack push s man} err; puts $err                                     

Result:

    0                                                                           
    Stack overflow                                                              

Stack variables must be passed to procedures by reference (name) and
accessed using [upvar] or should be accessed as global variables.

Example:

    proc display_stack {x} {                                                    
       puts [stack list $x]                                                     
    }                                                                           
                                                                                
    stack create a 10                                                           
    stack push a Hello                                                          
    display_stack                                                               

See also: stack examples, array, catch, try

stack create

    bool [stack create varName size]

Creates a new stack with a fixed size. It is an error to push into a stack
over the stack size. Use [stack count] and [stack is_empty] to manage
the stack size to prevent error cases. Alternatively, use [try..catch] or
[catch]

Example:

    stack create stk 2                                                          
    puts [stack count stk]                                                      
    stack push stk Hello                                                        
    puts [stack count stk]                                                      
    stack push stk world                                                        
    puts [stack count stk]                                                      
    stack push stk Bad!                                                         

Results

    0                                                                           
    1                                                                           
    2                                                                           
    <error>                                                                     

See also: stack, stack push, stack count, stack examples

stack foreach

    void [stack foreach  varName indexVar {script} ?-r?]

Iterates stack contents, applying to a script body in the same manner as 
[foreach]

Example:

    stack create stk 100         # Create a stack of 100 items                  
    unset x -nocomplain          # Ensure iterator x is not set                 
    foreach x $qbf {             # Populate the stack                           
        stack push stk $x        # Push a word into the stack                   
    }                                                                           
    unset y -nocomplain          # Ensure iterator y is not set                 
    stack foreach stk y {        # Iterate the stack                            
        puts "Stack item: $y"                                                   
    }                                                                           

Results:

    Stack item: The                                                             
    Stack item: quick                                                           
    Stack item: brown                                                           
    Stack item: fox                                                             
    Stack item: jumps                                                           
    Stack item: over                                                            
    Stack item: the                                                             
    Stack item: lazy                                                            
    Stack item: dog                                                             

See also: stack, stack create, stack push, stack examples

Stack Examples

Here are a few examples using Ticol stacks:

Example of stack creation

    stack create s 1000                                                         

Example of a foreach loop pushing words from a string

    foreach {x} $qbf {                                                          
       stack push s $x                                                          
    }                                                                           

Example of simple iteration of stack with stack destruction

    set i 0                                                                     
    while {! [stack is_empty s]} {                                              
       puts "[incr i]\t'[stack pop s]'"                                         
    }                                                                           
    puts "Stack empty? [bool [stack is_empty s]]"                               

Result:

    1       'dog'                                                               
    2       'lazy'                                                              
    3       'the'                                                               
    4       'over'                                                              
    5       'jumps'                                                             
    6       'fox'                                                               
    7       'brown'                                                             
    8       'quick'                                                             
    9       'The'                                                               
    Stack empty? true                                                           

Example of proc with for loop to iterate stack declared at higher level

    option expression on        # Use infix flow-control expressions            
    proc stack_iter {s} {                                                       
       upvar s local                                                            
       set limit [stack count local]                                            
       for {set i 0} { $i < $limit} {incr i} {                                  
          puts [stack item local $i]                                            
       }                                                                        
    }                                                                           

Example of clearing a stack object:

    stack clear s                                                               
    puts "Stack size [stack size s]"                                            
    puts "Stack count [stack count s]"                                          
    puts "Stack empty? [bool [stack is_empty s]]"                               

Result:

    Stack size 1000                                                             
    Stack count 0                                                               
    Stack empty? true                                                           

Example of deleting (unsetting) a stack and triggering access error

    stack unset s                                                               
    puts "Stack empty? [bool [stack is_empty s]]"                               

Result:

    stack is_empty: Stack not initialised                                       

Example: stack head:

    proc head1 s {                # Method 1                                    
        upvar $s t                                                              
        return [stack item t -end]                                              
    }                                                                           
                                                                                
    proc head2 s {                                                              
        upvar $s t                                                              
        puts "[stack count t]"                                                  
        return [stack item t [- [stack count t] 1]]                             
    }                                                                           
                                                                                
    stack create s 100                                                          
                                                                                
    foreach {x} $qbf {            # Method 2                                    
        stack push s $x                                                         
    }                                                                           
                                                                                
    puts [head1 s]                                                              
    puts [head2 s]                                                              

Result:

    dog                                                                         
    dog                                                                         

Example: stack head:

    proc tail s {                                                               
        upvar $s t                                                              
        if {eq [type t] "stack"} {                                              
            if {! [stack is_empty t]} {                                         
                return [stack item t 0]                                         
            }                                                                   
        }                                                                       
        return {}                                                               
    }                                                                           

See also: stack, array

static (static variables)

    static varname ?initialValue?  # Declaring with optional initialiser
    static varname -unset          # Unsetting a protected static variable

A static variable is one which holds its value within a [proc] between
evocations. These are useful for counters or other accumulators within
a procedure

[static] declares a static local variable of either simple or array type
and optionally, initialises it to an initial value. Once the local static
variable has been created any initialiser value will be ignored and [set]
should, thereafter be used to set it. The new variable should be set only
in local scope. Any references to the root scope master copy of a static
variable will be discarded when a procedure exits or when [set] is used
locally. No other type is supported by the [static] command.

Static variables are global in scope with a pseudo-namespace. They are not
cleaned up when a proc exits or is undefined and are 'protected' from 
deletion for the lifetime of the script unless forcibly deleted using 
[static varname -unset]

The detected 'type' of the variable when first set will be 'empty'. 
The variable will be set automatically to standard or array type on first
assignment [stack] and other variable types are not supported by [static]

Using combinations of [uplevel] and [upvar] other data types can be made
static within procs (see examples below)

Static variables, including parent array variables may be unset only by 
using  [static varname -unset]. This command 'understands' simple and
array variable types. [static <varname> -unset] understands standard 
vars, arrays, stack, struct and dict types

Example:

    proc foo {} {                                                               
        static a       # [type a] will be 'empty' on declaration                
        set a Hello    # Sets 'a' and global '::foo::a' as string               
    }                                                                           
    static foo::a -unset                                                        

                                                                                
    proc foo {} {                                                               
        static a       # [type a] will be 'empty' on declaration                
        set a(1) Hello # Sets 'a' and global '::foo::a' as array                
    }                                                                           
    static foo::a -unset                                                        

Tcl does not directly support the declaration of static variables
but Ticol offers a [static] command as well as an alternate method of
implementing Tcl code below. Alternatively the [global] command can be
used or [upvar/uplevel] at root level (0)

When run at [proc] level, [static] will create a root level variable with
a name in the form of 'procname::varname' and will set up a local [upvar]
alias as 'varname'. If a default value has been given then the local and
root alias will be set to that value

When run at root callframe level (0), [static] will create a root level
instance of 'varname' and will initialise it to any default value on
creation

[static] should not be used as an alternative to [set] as the argument 
is for initialisation, not assignment. Any subsequent initialiser call
will be ignored

The static variable can be addressed as <procname>::<varname> when
declared in a [proc] or just <varname> if declared in root-level scope

Naming example:

    # Calling from proc [foo] to declare [static q] will create                 
    foo::q                                                                      
    # Which has a global name reference of                                      
    ::foo::q                                                                    
    # And a local upvar proc scope alias of                                     
    q                                                                           
    # The global root copy can be read (not recommended) using                  
    $::foo::q                                                                   
    # You can set both values from within proc scope using                      
    set q value                                                                 
    # You should not assign directly to the root variable                       
    # as changes will be lost when the proc stack unwinds                       

Example Using [static]:

  option expression on    # Required for if {$x}                                
                          # [proc] level 1                                      
  proc foo {{x {}}} {     # Proc [foo] will set var q only if non-zero          
      static q 0          # Declare static q with initial value '0'             
      if {$x} {                                                                 
          set q $x                                                              
          puts "foo->q was set to $q"                                           
      }                                                                         
      puts "foo->q::$q"                                                         
      return $q                                                                 
  }                                                                             
                                                                                
  # [proc] level 0 (root/global scope)                                          
  assert [foo 1] {== $_ 1} -v -line #__LINE__                                   
  # Pass no arg (test for value persistence)                                    
  assert [foo] {== $_ 1} -v -line #__LINE__                                     
  static foo::q -unset                                                          

Mistakes:

Using [static] to assign where [set] is required

  # In root-level scope                                                         
  static foo 23              # [static] is a declaration with initialiser       
  assert $foo {== $_ 23} -v  # PASS                                             
  static foo 11              # [static] is not assignment (init is ignored)     
  assert $foo {== $_ 11} -v  # FAIL                                             
  set foo 11                 # Use [set] to assign a static variable            
  assert $foo {== $_ 11} -v  # PASS                                             

Example Use:

This shows a static variable being used as an incremental counter between
proc evocations...

    proc foo {} {                                                               
        static x 0                                                              
        ++ x                                                                    
        puts "x is $x"                                                          
    }                                                                           
                                                                                
    foo                                                                         
    foo                                                                         
    foo                                                                         
    puts $foo::x                                                                
    static foo::x -unset                                                        

Results:

    x is 1                                                                      
    x is 2                                                                      
    x is 3                                                                      
    3                                                                           

Promoting Arrays to Static
--------------------------
Array variables may be 'promoted' to a protected static type by 
'overloading; as follows, but note that this command cannot accept
an initialiser value...

    array create a   # Declare an array variable                                
    static a         # Promote the array to static/protected type               
                     # An initialiser throws an error without -nocomplain       
    static a 23      # Wrong!!  Not valid for array types                       
    static a 23 -nocomplain  # OK. Will not throw an error                      

Note that individual array elements may not be promoted to 'static', only
whole arrays

Using [static] within recursive procs
-------------------------------------
As each call to a recursive [proc] unwinds, it updates and erases the
global variable rather than cascading values back to each [proc] parent
and then finally back to the root [proc] as the first proc call exits

Instead use the [global] command as per the following example:
(See: unwrap)

    set l "{catalog {cd {{title {Empire Burlesque}} {artist {Bob Dylan}} \      
      {Company Columbia} {Price 10.90} {year 1985} }}}"                         
                                                                                
    proc unwrap_as_text {l} {                                                   
        global foo::s            # Avoid clashes with root a namespace var      
        foreach x $l {                                                          
            if {> [ldepth $l] 2} {                                              
                unwrap_as_text $x                                               
            } else {                                                            
                append ::foo::s "$x\t"                                          
            }                                                                   
        }                                                                       
        if {<= [ldepth $l] 2} {                                                 
            set ::foo::s [string trim $::foo::s "\t "]                          
            append ::foo::s "\r\n"                                              
        }                                                                       
    }                                                                           
    unwrap_as_text $l                                                           
    textcolor cyan                                                              
    puts $foo::s                                                                
    textcolor                                                                   

Results:

    catalog                                                                     
    cd                                                                          
    title   Empire Burlesque                                                    
    artist  Bob Dylan                                                           
    Company Columbia                                                            
    Price   10.90                                                               
    year    1985                                                                

[static] implemented as a proc
------------------------------
This sets up an emulated 'namespace' var in the format 
<procname>::<varname> within the parent proc. The implementation in Tcl
is relatively trivial...

  option expression off                                                         
                                                                                
 undef static                                    # undef built-in command       
                                                                                
 # [proc] level 2                                                               
 if {! [defined static]} {                       # Avoid errors if re-run       
    proc static {name {value {}}} {                                             
       if {> [info level] 1} {                   # If not root, will be 1       
          set scope [uplevel 1 {info procname}]  # Save proc in new scope       
          set varname ${scope}::$name            # Create root scope name       
          if {! [is_set ::$varname]} {           # If root var name empty       
             uplevel 1 "upvar @0 $varname $name" # Run upvar in root            
             uplevel 1 "set $name $value"        # Set local alias              
          } else {                                                              
             uplevel 1 "upvar @0 $varname $name" # Run upvar in root ...        
          }                                      # ... callframe at level 1     
       } elseif {! [is_set ::$name]} {           # We are in root namespace     
          set ::$name $value                     # Set on init                  
       }                                                                        
    }                                                                           
 }                                                                              
                                                                                
 # [proc] level 1                                                               
 proc foo {{x {}}} {   # Proc [foo] will set var q only if non-zero             
    static q 0                                                                  
    if {$x} {                                                                   
       set q $x                                                                 
       puts "foo->q was set to $q"                                              
    }                                                                           
    puts "foo->q::$q"                                                           
    return $q                                                                   
 }                                                                              
                                                                                
 # [proc] level 0 (root/global scope)                                           
 assert [foo 1] {== $_ 1} -v -line #__LINE__                                    
 # Pass no arg (test for persistence)                                           
 assert [foo] {== $_ 1} -v -line #__LINE__                                      

Static Structs
--------------
Structs can be made static within a proc using the following proc
Note that structs are time-costly to set up and require a per-proc-call
fixup

    proc static_struct {name args} {                                            
       set scope [uplevel 1 {info procname}]                                    
       set varname ${scope}::$name                                              
       if {! [is_set ::$varname]} {                                             
          uplevel @0 "struct $varname {$args}"                                  
       }                                                                        
       uplevel 1 "upvar @0 $varname $name"                                      
       foreach {x y} $args {                                                    
          uplevel 1 "upvar @0 ${varname}.$x ${name}.$x"                         
       }                                                                        
    }                                                                           
                                                                                
    proc foo {x y} {                                                            
    static_struct s {a 20 b 30}  # Create a static struct in the proc           
       struct set s.a $x                                                        
       struct set s.b $y                                                        
    }                                                                           
                                                                                
    foo Hello world                  # Pass 2 arguments                         
                                                                                
    puts "foo::s.a '$foo::s.a'"                                                 
    puts "foo::s.b '$foo::s.b'"                                                 

Result:

    foo::s.a 'Hello'                                                            
    foo::s.b 'world'                                                            

Static Stacks
-------------
Stacks can be made static using the proc below
This will handle both proc and root level use

    proc static_stack {name args} {                                             
       set scope [uplevel 1 {info procname}]                                    
       set varname ${scope}::$name                                              
       if {> [info level] 1} {                                                  
          if {! [is_set ::$varname]} {                                          
             uplevel @0 "stack create $varname $args"                           
          }                                                                     
          uplevel 1 "upvar @0 $varname $name"                                   
       } else {                                                                 
             if {! [is_set $name]} {                                            
                uplevel @0 "stack create $name $args"                           
             }                                                                  
       }                                                                        
    }                                                                           
                                                                                
    proc foo {x y} {                                                            
       static_stack $x 100                                                      
       puts "foo: Pushing $y onto stack $x"                                     
       stack push $x $y                                                         
    }                                                                           
                                                                                
    foo s Hello                                                                 
    foo s World                                                                 
                                                                                
    set i 1                                                                     
    stack foreach foo::s x {                                                    
       puts "stack item $i: -$x-"                                               
       ++ i                                                                     
    }                                                                           

See:
   http://wiki.tcl.tk/1532

See also: is_static, global, set, scope

stop
 
    stop

Halts the execution of a program launched from Windows and returns to the
Ticol CLI. When launched from the Windows console the exit command exits
to Windows. Returns code 6.

Example:

    option expression off                                                       
                                                                                
    set i 0                                                                     
    while {1} {                                                                 
       if {> $i 5} {                                                            
          stop                                                                  
       }                                                                        
       puts "i is $i"                                                           
       ++ i                                                                     
    }                                                                           

Results:

    i is 0                                                                      
    i is 1                                                                      
    i is 2                                                                      
    i is 3                                                                      
    i is 4                                                                      
    i is 5                                                                      

See also: exit, return, at_exit, die

store

Highly efficient buffered storage string builder for large strings
Available as either anonymous or named objects

  Anonymous Format:

  void    [store add string ?-size N?]      # Concatenate a string
  string  [store get]                       # Return and clear the buffer
  integer [store length]                    # Get the current store length
  string  [store peek]                      # Peek the current contents
  bool    [store exists]                    # Test for default storage
  bool    [store exists {}]                 # Test for default storage

  Named Format:

  void    [store add name string ?-size N?] # Concatenate a string
  string  [store get name]                  # Return and clear the buffer
  integer [store length name]               # Get the current store length
  string  [store peek name]                 # Peek the current var contents
  bool    [store exists name]               # Test for specific storage

  Storage Management

  list    [store list]                      # List vars and storage size
  integer [store clear]                     # Clear *all* storage

[store] works in 2 forms. One with multiple named global storage variable
name references and the 2nd a single default, global reference with no 
associated variables (i.e. 'anonymous' storage)

High-efficiency, dynamic-buffered memory concatenation command for large
strings. [store] is much faster than [append] and will pre-allocate memory
in 1 megabyte blocks, auto-resizing as necessary. [store] is about 5 times
faster than [append] due to buffered memory pre-allocation avoiding the
need for memory management calls during storage operations.

Store is useful for rapidly building complex CGI based web pages and can
achive fairly quick page generation and load times

Either a default (unnamed) buffer can be used or a named buffer. Note
that the buffer name is not a Tcl variable but a reference to any number
of buffer stores.

There is no return value from [store add], all errors are fatal and
may be trapped using [catch] or [try..catch]

The data is retrieved and the memory store and cleared using [store get]
which also clears and resets the internal memory buffer and frees all
allocated memory. It is good practise to call [store get] before first
use to ensure the buffer has been cleared. [store get] also removes
the storage variable from the table of known variables. To avoid errors
you can test for a variable's existence using [store exists]

Example:

    store add foo             # Add 'foo' to the default storage                
    store add bar $qbf        # Add $qbf to storage object 'bar'                
    puts [store exists]       # TRUE                                            
    puts [store foo]          # TRUE                                            
    puts [store exists bar]   # TRUE                                            
    puts [store exists quux]  # FALSE                                           
    store clear               # Clear all                                       

The default internal buffer cache is 8Kb. For processing large files
such as web-pages you may wish to increase this to about 1Mb. The 
cache size is used at the first call to [store add] or at the first
call after a call to [store get]

The current buffered string length can be queried using [store length]
[store length] or [store length name] returns the current length of
a specific storage variable

[store list] will return a Tcl list containing the active storage
variables and their sizes (string lengths)

Example:

    store add $qbf                                                              
    store add foo $qbf                                                          
    store add "foo bar" Hi                                                      
    puts "'[store list]'"                                                       

Results:

    # These can be iterated using [foreach {a b} [store list]]                  
    '{} 43 foo 43 {foo bar} 2'                                                  

[store clear] will clear ALL storage without further confirmation.
This includes the default (unnamed) storage. This can be used
on exit from a file called by [source] but beware that the parent
variables will also be cleared

Example:

    # Default (unnamed) buffer cache                                            
    option expression off                                                       
    store get                    # Force clear default storage                  
                                                                                
    readfile ticol.man -array a                                                 
    for {set i 0} {< $i [array size a]} {++ i} {                                
       store add "$a($i)\r\n"                                                   
    }                                                                           
    puts "Store length is:     [store length]"                                  
                                                                                
    set q [store get]                                                           
    puts "Final var length is: [string length $q] byte(s)"                      

Example:

    # Using a named buffer cache 'b'                                            
    option expression off                                                       
    store clear                  # Clear all storage                            
                                                                                
    readfile ticol.man -array a                                                 
    for {set i 0} {< $i [array size a]} {++ i} {                                
       store add b "$a($i)\r\n"                                                 
    }                                                                           
    puts "Store length is:     [store length b]"                                
    set q [store get b]                                                         
    puts "Final var length is: [string length $q] byte(s)"                      

See also: append

string equal

    bool [string equal string1 string2]

Compare 2 strings for equality. 

Note that strings which are stored in 'escaped' format are retained in
that format internally and their string length is longer than the
unescaped version. To compare escaped strings with non-escaped strings
call [unescape] first

Example:

    set s1 "Hello\tworld"                                                       
    set s2 "Hello[chr 9]world"                                                  
    puts "Equal? "[bool [string equal [unescape $s1] $s2]]                      
    puts "Equal? "[bool [string equal [unescape $s1] "Hello[chr 9]world"]]      

Results:

    True                                                                        
    True                                                                        

 See also: eq, eqn, ne, nen

String Functions
 
A variety of string functions are available. Some implement the standard
Tcl 'string' command with parameters. Others replicate BASIC-like
functionality. See each command separately for more details

append s v      # Rapidly append variable to value...                           
asc s           # Return the ASCII value of 1st char of a string                
char value      # String cast                                                   
chartok         # Split a string based on characters from a mask                
chr n           # Return character of number N                                  
cmp v s p       # Compare strings                                               
comma s         # Format a numeric string with commas                           
concat v s      # Concatenate strings                                           
filter s m      # Filter string s using mask m                                  
is_alpha s      # Test if a string is composed of alpha characters              
is_alnum s      # Test if a string is composed of alphanumeric characters       
index s n       # Rapid indexing of strings to return a single character        
instr s c       # Locate position of c in string s                              
join l c        # Join strings to form lists                                    
left a n        # Return the left part of string a for n characters             
makestr l c v   # Create a new string using malloc                              
memset v c l    # Rapidly set memory                                            
mid a b p n     # Place string b within string a at p and for n chars           
mids a s n      # Return section of string a starting at s for length n         
range c         # Return a string of characters in the given range              
replacechar v s # Performs an in-situ replace of characters                     
right a n       # Return the right part of string a for n characters            
setc v s n      # Insert string into var c, centrally aligned for length n      
setl a b n      # Place a string b left-aligned into a for n characters         
setr a b n      # Place a string b right-aligned into a for n characters        
scan s c        # Parses a string using conversion specifiers                   
setat s v       # Sets a variable by copying from a variable or string          
slice s n       # Break up freeform text into fields                            
split s m       # Slice up text                                                 
store s         # Buffered concatenation                                        
string          # See the [string] command                                      
strstr a b      # Locate string b within a and return a base 1 offset or 0      
                # You may also specify '-nocase' as a trailing argument         
stripto         # Remove characters up to given position from left              
striptor        # Remove characters up to given position from right             
strsplit        # Split a string                                                
strstr          # Find location of one string in another                        
strtok          # Split a string                                                
trim s c        # Trim both ends of a string                                    
trimleft s c    # Trim the left hand end of a string                            
trimright s c   # Trim the right hand end of a string                           
wrap            # Line-wrap freeform text string                                

See each command for more detailed information, e.g. help string trim

See also: string, string to_wchar, wchar_to_string, wchar_length, functions, 
          array

string is_alpha, string is_alnum

    bool [string is_alpha s] # Test if composed of alpha chars
    bool [string is_alnum s] # Test if composed of alphanumeric chars

Examples:

    puts [is_alpha "abcdef"]                                                    
    puts [is_alpha "abc.ef"]                                                    
    puts [is_alnum "abc123"]                                                    
    puts [is_alnum "abc.23"]                                                    

Results:

    1                                                                           
    0                                                                           
    1                                                                           
    0                                                                           

See also: string, is_*

string to_wchar
 
    wide-string [string to_wchar string|input-variable output-variable]

Convert a standard ANSI string to a wide (unsigned char) string to pass to
an external DLL. It is not recommended that wide strings be used
internally with Ticol as there is no useful means of manipulating them.

string_to_wchar can only pass its output directly into another variable.
If the output variable does not exist it will be created.

Example:

    string to_wchar "hello" fred                                                
    puts $fred                                                                  

                                                                                
    h                                                                           

                                                                                
    dump fred                                                                   

                                                                                
var 'fred' at 31752880 (0x1e482b0), 1 byte(s)                                   
                                                                                
Address   0  1  2  3  4  5  6  7    8  9  A  B  C  D  E  F ASCII                
-------- -----------------------   ----------------------- ----------------     
01e482b0 68 00 65 00 6C 00 6C 00 - 6F 00 00 20 61 75 74 6F h.e.l.l.o.. auto     

Example: (Uses the AutoIT v3 DLL)

  # Note that when passing wchar* to a Unicode DLL requiring LPWSTR we use      
  # addressofb (binary address) not strptr                                      
  puts "Getting window text ..."                                                
  set buflen 4000                                                               
  set buf [makestr [* $buflen 2]]                                               
  string to_wchar "Untitled - Notepad" title                                    
  puts "Check: wchar_length of title is : [wchar_length title]"                 
  call  [calldll AutoItX3 AU3_WinGetText "" [addressofb title]:4 [strptr\       
    buf]:4 $buflen:4]                                                           
  set result [wchar_to_string buf]                                              
  puts "AU3_WinGetText wchar_to_string is: '$result'"                           

See also: wchar_to_string, wchar_length, calldll

stripto
 
    string [stripto mask-string variable|string ?-nocase? ?-var?]

Strip all characters up to the given mask string. If the mask string is not
encountered then a null return will result. 

If the -var argument passed then the payload argument will be assumed
to be a variable. The changed value is returned by the variable contents
will be unchanged

If a string literal is passed then the filtered string will be returned

Examples:

    set x "51cb\r\n<!DOCTYPE html>"                                             
    stripto "<" x                                                               
    puts "x is now: $x"                                                         
    puts [stripto :: "class::name"]                                             
    puts [stripto :: "class::name" -remove]                                     

Results:

    x is now: <!DOCTYPE html>                                                   
    ::name                                                                      
    name                                                                        

See also: striptor, strstr, left, right, mid, mids, strto, strtor

striptor
 
    string [striptor mask-string variable|string ?-nocase? ?-var?]

Reverse strip all characters after the first encountered instance of the
given mask string when searched in reverse direction from the end of the
string. If the mask string is not found then the entire string will be 
returned

If the -var argument passed then the payload argument will be assumed
to be a variable. The changed value is returned by the variable contents
will be unchanged

Examples:

    puts [striptor bye "Goodbye cruel world"]                                   
    puts [striptor :: "class::name"]                                            
    puts [striptor :: "class::name" -remove]                                    

Results:

    Goodbye                                                                     
    class::                                                                     
    class                                                                       

See also: stripto, strstr, left, right, mid, mids, strto, strtor

strto, strtor

    string [strto string maskchars ?-var? ?-nocase? ?-right?]
    string [strtor string maskchars ?-var? ?-nocase? ?-left?]

[strto] will return the contents of a string up to and excluding the first
instance of the first character in the "maskchars" set (if any). If a
character is unmatched then an empty string "" is returned

[strtor] will search in the reverse direction

for [strto] the "-left" argument will return the left hand part
for [strtor] the "-right" argument will return the right hand part

A variable name may be passed using the "-var" argument for speed-intensive
code. Case may be ignored using the "-nocase" argument

These command can simplify code and speed up the splitting of strings into
component parts

Longhand code:

[strto] and [strtor] avoid moderately slow and complex longhand code 
involving indexes and string-slicing such as that below...

    set s "http://foo.com:80"                                                   
    proc strto {s q} {                                                          
       foreach x [split $q] {                                                   
          set p [strstr $s $x]                                                  
          puts "$s x:$x p=$p"                                                   
          if {> $p 0} {                                                         
             -- p                                                               
             return [left $s $p]                                                
          }                                                                     
       }                                                                        
       return ""                                                                
    }                                                                           
    puts [strto $s ":"]                                                         

Result:

    http                                                                        

Examples:

    set s "http://foo.com:80"                                                   
    puts [strtor $s ":" -left]     # Get URL from port value                    
    puts [strtor $s ":" ]          # Get port value from URL                    
    puts [strtor s ":" -left -var] # Get URL from port value (-var)             
    puts [strtor s ":" -var]       # Get port value from URL (-var)             

Results:

    http://foo.com                                                              
    8080                                                                        
    http://foo.com                                                              
    8080                                                                        

See also: strtok, left, right, mid, mids, stripto, striptor

strtok

    integer [strtok string mask array ?-nocase?]

[strtok] 'tokenises' (breaks apart) a whole string as delimited by 'mask'
and returns the individual components in a base 0 integer-indexed array. 
A count of the number of tokenised items in the array is returned

Characters from the mask string are not included in the output. 
Typically a string will be split by spaces, tabs or CRLF pairs. 

If the 'array' argument exists it will be overwritten without warning

Example:

    option expression off                                                       
    set count [strtok $qbf " " a]                                               
    for {set i 0} {< $i $count} {++ i} {puts $a($i)}                            

Result:

    The                                                                         
    quick                                                                       
    brown                                                                       
    fox                                                                         
    jumps                                                                       
    over                                                                        
    the                                                                         
    lazy                                                                        
    dog                                                                         

Example:

     strtok $qbf " " a                                                          
     array foreach a x {puts $x}    # Will be unsorted from array a             

Result:

    The                                                                         
    dog                                                                         
    brown                                                                       
    jumps                                                                       
    over                                                                        
    the                                                                         
    fox                                                                         
    lazy                                                                        
    quick                                                                       

Example:

     strtok $qbf " " a                                                          
     foreach x [array sort a] {puts $x}    # Will be sorted from list           

Result:

    brown                                                                       
    dog                                                                         
    fox                                                                         
    jumps                                                                       
    lazy                                                                        
    over                                                                        
    quick                                                                       
    The                                                                         
    the                                                                         

See also: array, arrays, chartok, strto, strtor

struct

See topics:

    struct create
    struct clear
    struct copy
    struct dump
    struct elements
    struct implementation
    struct item
    struct set
    struct setb
    struct size
    struct unset
    ->
    =>
    =>>
    arrays of struct

[struct] offers a simple emulation of C/C++ binary structs for the purpose
of interfacing with external DLL routines. C-style Unions are not supported

A struct maps one or more Tcl variables to a fixed memory block of the
defined struct size. This block cannot be changed without destroying and 
then recreating the struct. Each struct has a parent Tcl variable and one
or more Tcl 'field' member variables which are referenced by the parent

Each field has not inherent data type and is simply a reference to a
block of memory

The command [class] may be defined as an [alias] for [struct]. Ticol
does not support proper functional class objects, in fact trying to make
structs work like classes is pretty awful (See: class example)

Structs are useful for passing a large collection of variables or
properties to a [proc] when passing by reference (by name) and using
[upvar]

    struct variable: [create], [unset], [clear], [copy], [size] etc.            
    |                                                                           
    v                                                                           
    [addressof]                [addressofb],[set].[setb],[struct setb]          
    |                          |                                                
    v                          v                                                
    +---------------------+    +---------------------+                          
    | Struct member var 1 | -> | "C"-style struct    |                          
    +---------------------+    | data block          |                          
    | Struct member var 2 | -> | (Fixed)             |                          
    +---------------------+    |                     |                          
    | Struct member var N | -> |                     |                          
    +---------------------+    +---------------------+                          
    |                          |                                                
    v                          v                                                
    [ofaddress]                [ofaddressb]                                     

Use [setb] to write binary values to the struct field offsets or 
[struct set] to write plain ANSI text values. You can only write to struct
members, you cannot can write to the host struct variable directly
Data cannot overflow the allocated field area. Any excess is truncated

A Ticol struct is designed to hold binary-compatible data. It cannot hold
Tcl abstract data types such as Stacks or associative Arrays although it
can contain a name reference to any other object type. Such data objects 
would not be useful to pass to external DLLs

New structs can be instantiated from an existing struct using [new]. 
[new] will create a new instance and clear all member fields to NULL

Note that struct field names are limited to 256 characters

Examples of Ticol binary structs:

    struct create s {{f1 10} {f2 20} {f3 30}}                                   
                                                                                
    struct s  {                                                                 
       {a        4}                                                             
    }                                                                           
                                                                                
    struct s  {                                                                 
       a        23                                                              
       b        45                                                              
    }                                                                           
                                                                                
    struct create s 256 {                                                       
       {a        40}                                                            
       {b        100}                                                           
    }                                                                           

The contents of a struct member field is returned by dereferencing
the struct member e.g. $s.a

The field address can be accessed by using [addressofb] and [ofaddressb]
can be used to invert the binary address of the field member

Example:

    struct s {a 20}                                                             
    struct set s.a "Hello world"                                                
    puts "Address of member is:    '[addressofb s.a] '"                         
    puts "Contents via address is: '$s.a'"                                      

Results:

    Address of member is:    '32569824'                                         
    Contents via address is: 'Hello world'                                      

[unset] cannot be used to delete individual struct members. If you wish to
alter a struct object then you must instead delete the entire struct 
object and recreate it

A useful type of struct is one which holds a single member variable.
In such cases the struct itself can be passed as an alias for the member
variable since the struct binary address offset is the same as the first
member offset. This is useful (and safer since the data-block is fixed)
for passing simple, fixed-byte-size variables to external functions via 
[calldll*]

Example:

  struct x {dword 4}                  # A single-field struct, size 4 bytes     
  struct setb x.dword 1234            # Set a binary value in the struct        
  # struct contains:                                                            
  # Address    0  1  2  3  4  5  6  7                                           
  # --------  -----------------------                                           
  # 02339470  D2 04 00 00 00 00 00 00 # 0x000004D2 as little-endian             
  puts [ofaddressb x.dword]           # Display the value                       

Example:

   # Retrieves the true name of a Windows service (ANSI) using API calls        
   lib ticol_calldll                # Load the calldll plugin                   
                                                                                
   set service_name "PeerDistSvc"   # The service we will query                 
                                                                                
   const SC_MANAGER_ENUMERATE_SERVICE 0x0004                                    
                                                                                
   set scm_handle [calldll Advapi32 OpenSCManagerA [info hostname] 0\           
      $SC_MANAGER_ENUMERATE_SERVICE]                                            
                                                                                
   set sc_handle [calldll Advapi32 OpenServiceA $scm_handle $service_name\      
      $SC_MANAGER_ENUMERATE_SERVICE]                                            
                                                                                
   struct dummy {buflen 4}          # Pass buflen by reference as long          
   struct setb dummy.buflen 100                                                 
   struct dummy2 {buf 1024}         # Pass buffer by reference via struct       
   struct setb dummy2.buf [chr 0] 4                                             
   set display_name ""                                                          
                                                                                
   set bresult [calldll Advapi32 GetServiceDisplayNameA $scm_handle\            
      $service_name dummy2.buf:4 dummy.buflen]                                  
                                                                                
   calldll Advapi32 CloseServiceHandle $sc_handle                               
   calldll Advapi32 CloseServiceHandle $scm_handle                              
                                                                                
   puts "Result=[bool $bresult] $service_name=='[ofaddressb $dummy2.buf]'"      

See also: struct create, struct dereference, struct copy, struct set,
          struct size, struct elements, struct dump, struct item
          new, pointers, is_pointer, struct setb, struct elements, calldll, 
          calldll_cdecl, struct dump, type, vars, array, stack, variables,
          addressof, ofaddress, addressofb, ofaddressb, class example, =>

-> => =>> (struct or class dereference)

    ->   struct                         # Dereference a struct member
    =>   structname.field ?length? val  # Set (assign) a field member
    =>>  struct                         # Dereference and [eval] the result

    [->]  is an alias for [struct item]
    [=>>] is an alias for [eval {[struct item]}]  or  [eval [-> {$x}]]

    eval "[-> d.set] {[eval [-> c.get]]}"                                       

Is the same as:

    =>> d.set [=>> c.get]                                                       

Example:  

    # Won't handle nested procs and different levels                            
    # Uses global scope variables v's upvar                                     
                                                                                
    alias class struct                  # Define [class]                        
                                                                                
    class test {                                                                
       data 256                                                                 
       get 20                           # Sufficient size to hold method        
       set 20                                                                   
    }                                                                           
                                                                                
    proc test::constructor {x} {                                                
       upvar $x                                                                 
       set class_name [info procname]                                           
       striptor :: class_name -remove   # Remove "::"                           
       struct set ${x}.get "${class_name}::get ::${x}.data"                     
       struct set ${x}.set "${class_name}::set ::${x}.data"                     
    }                                                                           
                                                                                
    proc test::get {x} {                # Proc is mapped from struct field      
        return [-> $x]                                                          
    }                                                                           
                                                                                
    proc test::set {x} {                # Proc is mapped from struct field      
       # upvar $x                       # Note that upvar is not required       
       return [eval "struct set $x"]    # Requires {*} expand                   
       # return [eval "=> $x"]          # Alternate form                        
    }                                                                           
                                                                                
    new test c                          # Create a new instance of 'test'       
    test::constructor c                 # Call the constructor                  
                                                                                
    =>> c.set {$::qbf}                  # Call the method                       
    puts "Object contains: [=>> c.get]"                                         

Results:

    Object contains: The quick brown fox jumps over the lazy dog                

Comments:

    These commands are experimental
    [return] is, strictly speaking, not required from a [proc]
    [struct set] can be simplified as [=>]. See =>
    {*} would reduce the need to use [eval] with some operations

See also: =>, struct, class, struct item

struct create

    address [struct ?create? structname ?size? {{membername size} ...} 
        ?-init <string>?]
    
Creates a Ticol Tcl struct with one or more member fields

The 'create' and 'size' arguments are optional. size defines the 
struct's binary block size in bytes. This is detected from the 
field definition list but can be overriden via this argument.

Individual member names can be no longer than 256 characters

Struct fields exist as independent variables and are referenced via a
dot-separated, parent-referenced naming structure as in C++ (e.g. s.f1)

Struct member fields must be assigned-to using [struct set], [setb], or
[struct setb] commands. [set] cannot be used to assign to struct members

Field members may optionally be initalised to a string value which may
be specified by the -init <string> argument. Any excess over the defined
field width will be truncated on a per-field basis. 

The default field initialisation is to fill with null characters

Examples:

    struct create s {{f1 10} {f2 20} {f3 30}}                                   
                                                                                
    struct s  {                                                                 
       {a        4}                                                             
    }                                                                           
                                                                                
    struct s  {                                                                 
       a        23                                                              
       b        45                                                              
    }                                                                           
                                                                                
    struct create s 256 {                                                       
       {a        40}                                                            
       {b        100}                                                           
    }                                                                           

Returns address of the new struct's data block if successful. Binary
operations on the return address must be used with extreme caution. 
Ticol structs contain binary blocks of memory space but this cannot be
addressed directly other than by [struct copy]

Struct fields are addressed by struct.field naming. So for struct 's'
with field a, one would reference as: s.a

The struct memory layout is as follows:

    [addressof]                [addressofb],[set].[setb],[struct setb]          
    |                          |                                                
    v                          |                                                
    +---------------------+    |                                                
    | Tcl Variable(s)     |    v                                                
    +---------------------+    +----------------------+                         
    | Struct member 1     | -> |  Struct Data Block   |                         
    +---------------------+    | (Fixed memory block) |                         
    | Struct member 2     | -> |                      |                         
    +---------------------+    |                      |                         
    | Struct member N     | -> |                      |                         
    +---------------------+    +----------------------+                         
    |                          |                                                
    v                          v                                                
    [ofaddress]                [ofaddressb]                                     

See also: pointers, is_pointer, struct setb, struct elements, calldll, 
          calldll_cdecl, struct dump, type, vars, array, stack, variables,
          addressof, ofaddress, addressofb, ofaddressb

arrays of struct

Tcl doesn't naturally or easily lend itself to "C" style arrays of struct 
objects, since arrays are associative and index their subscript based on 
textual values; but it is possible to emulate them with some limitations.

The first of these isn't recommended for use for arrays with more than few 
hundred elements. This is because this method will create individual struct
variables for each element in a pseudo-array. Ticol isn't optimised for 
rapidly handling large numbers of plain variables. 

Standard variable lookup uses a non-optimised LIFO/FILO linear search
array with the most recently declared variables at the "top" of the array
and newer variables are accessed first in any search. This is sufficiently
optimal for standard Tcl use but inadequate for a linear lookup of 
thousands of variables, Also, the variable table is replicated in
recursive calls which would consume high levels of memory.

The method, which allowed dotted array dereferencing is as follows
This method would work just as well without the curved brackets

    struct t {a 10 b 20}   # Declare a struct in the standard manner            
    set i 0                                                                     
    # Create struct elements using a pseudo array which we can dereference      
    loop i 0 10 {                                                               
        new t a($i)        # This creates standard variables not elements       
    }                                                                           
    # Enter some data - we will fill with QBF                                   
    # a($i) looks like an array but it's actually a struct                      
    # so we can dereference the fields                                          
    foreach x $qbf {                                                            
        struct set a($i).a $x                                                   
        struct set a($i).b [* $pi [rnd 1 23]]                                   
        ++ i                                                                    
    }                                                                           
    # Print out the contents using the [->] struct dereference command          
    loop i 0 10 {                                                               
        puts "$i .a = [-> a($i).a]"                                             
        puts "$i .b = [-> a($i).b]"                                             
    }                                                                           
    # Clean-up                                                                  
    struct unset a(*) -glob -nocomplain                                         

                                                                                
    # Results. Each struct is comprised of 2 more more variables                
    # struct    a(0)                 <struct>                                   
    # integer * a(0).a               34179792                                   
    # integer * a(0).b               34179802                                   
    0 .a = The                                                                  
    0 .b = 43.9822971                                                           
    1 .a = quick                                                                
    1 .b = 43.9822971                                                           
    # ...                                                                       

Update (v1.27/2022)
-------------------
A more effective method now exists

As from November 2022 build of v1.27 array elements with struct member
references will be properly dereferenced by $ (as well as by [set] and
[struct item]) instead of the [->] struct dereference command

Any struct insteance can be mapped into an array by name and then 
have struct member dereferenced directly

This lends use to [new] to create a series of unique struct varables
which may be allocated by name to array elements and also to do so
via an allocation loop.

At present this allows only complex dereferences (but not assignment)
in the format:

    $arrayname(subscript).structmember                                          

e.g.:

    $a(foo).bar                                                                 

This form of resolution may be blocked by enclosing the array index
in curly braces

e.g.

    ${a(foo)}.bar                                                               

Example:

    struct template {a 10 b 20} # Underlying struct template                    
    new template foo_1          # Create a new struct based on 'template'       
    struct set foo_1.a "Hello"  # Assign using [struct set]                     
    struct set foo_1.b "World"                                                  
    set a(0) foo_1              # Assign this struct to array element 0         
    puts "$a(0).a $a(0).b"      # Dereference                                   
    puts "${a(0)}.a ${a(0)}.b"  # Don't dereference (see below)                 

Result:

    Hello World                                                                 
    foo_1.a foo_1.b                                                             

If you wish to prevent this form of struct dereference then enclose the
array reference in curly braces thus:

    ${a(0)}.a                                                                   

You may also set struct members via the array, as follows. Although it is
non-intuitive to dereference the array element in order to set a value
in the struct member

    struct set ${a(0)}.a 23                                                     

It is not yet possible to assign to or pass such complex references to
commands. Compatibility extends solely to dollar dereferencing

See also: calling array of struct methods, new, struct, array, ->, foreach, loop

Calling methods within arrays of struct

Here's an example script which calls methods within an array of struct
using various means.

    option expression off                                                       
                                                                                
    struct template {            # Template struct used for instantiation       
        a 100                                                                   
        b 30                                                                    
    }                                                                           
                                                                                
    # Instantiation                                                             
    new template foo_1           # Duplicate template struct into a new one     
    new template foo_2           # Duplicate template struct into a new one     
                                                                                
    # Script/method definition                                                  
    set script {static i 1; puts "Hello $i"; ++ i}                              
    sub foo $script              # Assign our script to a [sub] called 'foo'    
                                                                                
    # Binding the array element to a struct instance                            
    # This is a simple demo and we use only one array element                   
    # array set a 0 foo_1        # Alternate method of setting (1)              
    set a(0) foo_1               # Alternate method of setting (2)              
    puts $a(0)                   # Visual confirmation of object link           
                                                                                
    # Assigning member functionality                                            
    struct set ${a(0)}.a $script # Assign a scriptlet as a "method"             
    struct set ${a(0)}.b foo     # Point to a sub/proc aka a "method"           
                                                                                
    puts ${$a(0).a}              # Will resolve to the method source            
                                                                                
    # Various examples of calling our 'methods'                                 
    # Calling member 'a' ...                                                    
    expand ${$a(0).a}            # Method 1 (Call with expand command)          
    {*}${$a(0).a}                # Method 2 (Call via {*} operator)             
    eval ${$a(0).a}              # Method 3 (Call with eval)                    
    =>> ${a(0)}.a                # Method 4 (Call using =>>)                    
    @> ${$a(0).a}                # Method 5 (Call with 'eval subst')            
    # ${$a(0).a}                 # Won't work as '.a' won't be expanded         
                                                                                
    # Calling member 'b' ...                                                    
    ${$a(0).b}                   # Will work as it calls a single Tcl word      

Results:

    Hello 1                                                                     
    Hello 2                                                                     
    Hello 3                                                                     
    Hello 1                                                                     

See also: arrays of struct, struct, array, class, class example

Class Example

Ticol does not support objects or classes but these can be emulated via 
the [struct] data type

A full, worked example of a pseudo-class using Ticol which a progress-bar 
objects and updates it. Multiple such objects may be created and updated
independently. This form of emulation offers no internal methods, only
links to external ones

    option expression off                                                       
    cls                                                                         
                                                                                
    proc progress_bar {s i} {                                                   
       #################################################                        
       # [in]       s Struct (as pseudo-class object)                           
       # [in]       i Current progress value to use                             
       #################################################                        
       upvar $s                                                                 
                                                                                
       if {ne [type $s] struct} {                                               
          die "[info procname]: arg s is not a struct"                          
       }                                                                        
                                                                                
       # [->] is complex struct member dereference                              
       # $${struct}.member or ${${struct}.member} is proposed                   
                                                                                
       set x [-> ${s}.x]         # Alternate to ${${s}.y}                       
       set y ${${s}.y}                                                          
       set style ${${s}.style}                                                  
       set init ${${s}.init}                                                    
       set scale_max ${${s}.scale_max}                                          
       set width ${${s}.width}                                                  
       set colour ${${s}.colour}                                                
                                                                                
       if {! $init} {                                                           
          if {== $style 0} {                                                    
             box $x [- $y 1] [+ $scale_max 5] 3 black -nofill                   
             gotoxy [+ $x 2] $y                                                 
             for {set j 0} {< $j $width} {++ j} {                               
                textcolor $colour                                               
                if {is_mod $j 2} {                                              
                   puts \xdb -nonewline                                         
                } else {                                                        
                   puts " " -nonewline                                          
                }                                                               
             }                                                                  
             clreol                                                             
          } else {                                                              
             box $x [- $y 1] [+ $scale_max 5] 3 grey -nofill                    
          }                                                                     
          => ${s}.init $::true                                                  
       }                                                                        
                                                                                
       set n [* [+ $width 1] [/ [double $i] $scale_max]]                        
       set percent [* [/ [double $i] $scale_max] 100.0]                         
       gotoxy [+ $x [+ 2 $n]] $y                                                
                                                                                
       if {> $style 0} {                                                        
          if {> $percent 90} {                                                  
             textcolor green                                                    
          } elseif {> $percent 60} {                                            
             textcolor yellow                                                   
          } else {                                                              
             textcolor red                                                      
          }                                                                     
          puts [chr 219] -nonewline                                             
       } else {                                                                 
          textcolor $colour                                                     
          puts [chr 219] -nonewline                                             
       }                                                                        
                                                                                
       # Print n.nn %                                                           
       textcolor                                                                
       gotoxy [/ $width 2.0] [- $y 1]                                           
       textcolor white                                                          
       printf " %.2f %% " $percent                                              
       textcolor                                                                
                                                                                
       if {>= $percent 100} {                                                   
          => ${s}.init $::false                                                 
       }                                                                        
    }                                                                           
                                                                                
    alias class struct                                                          
                                                                                
    # Vars are declared wide enough to hold the max value                       
                                                                                
    class template {                                                            
       init 10                                                                  
       style 10                                                                 
       x 10                                                                     
       y 10                                                                     
       colour 10                                                                
       width 10                                                                 
       scale_max 10                                                             
       method 20                                                                
    }                                                                           
                                                                                
    # Create a new struct (class) instance and clear to NULL                    
                                                                                
    new template st                                                             
                                                                                
    # Use struct member assign [=>] or the verbose [struct set] to assign       
                                                                                
    => st.style 1                                                               
    => st.x 1                                                                   
    => st.y 6                                                                   
    => st.width 74                                                              
    => st.scale_max 400                                                         
    => st.init 0                                                                
    => st.method progress_bar       # Declare a method by name only             
                                                                                
    gotoxy 1 1                                                                  
    textcolor magenta                                                           
    puts "Pseudo-class demo using a progress-bar"                               
    textcolor                                                                   
                                                                                
    for {set i 0} {<= $i $st.scale_max} {++ i} {                                
       gotoxy 1 3                                                               
       puts "Scale value: $i" -nonewline                                        
       [$st.method st $i]                                                       
       sleep 5                                                                  
    }                                                                           
                                                                                
    sleep 500                                                                   
                                                                                
    # Reset some style parameters ...                                           
                                                                                
    => st.x 4                                                                   
    => st.y 12                                                                  
    => st.style 0                                                               
    => st.width 54                                                              
    => st.colour cyan                                                           
    => st.method {progress_bar st $i} # Set a combined method + args            
                                                                                
    # And draw again ...                                                        
                                                                                
    for {set i 0} {<= $i $st.scale_max} {++ i} {                                
       gotoxy 1 3                                                               
       puts "Scale value: $i" -nonewline                                        
       =>> st.method                 # Member evaluation command                
       # @> $st.method               # Experimental method call command         
       # eval [subst $st.method]     # Cumbersome equivalent to [@>]            
       sleep 20                                                                 
    }                                                                           

See also: class, struct, new, =>, =>>, ->

Experimental @> Struct Command

    @> structname.member

The experimental [@>] command is a shorthand for combined [eval [subst]]
This is required as Ticol v1.26 does not currently offer $ dereference
calling of struct methods, only dereferencing of variable contents.

This mechanism may be deprecated in future relases of Ticol
 
Example:

    class template {                                                            
       method 20                                                                
    }                                                                           
    new template st                                                             
    struct set st.method "puts hello"                                           
    =>> st.method               # Member evaluation command                     
    # @> $st.method             # Experimental method call command              
    # eval [subst $st.method]   # Cumbersome equivalent to [@>]                 

Result:

    Hello                                                                       

See also: struct, =>>, ->

struct set

    string [struct set structname.fieldname]
    address [struct set structname.fieldname value ?length?]

    $structname.fieldname            # Dereference field member

Sets a string value to a struct field. This may be less than the field
length and as specified by the optional length argument

[struct set] with no arguments is an alias for [struct item]
[struct set] with arguments returns an integer containing the address
of the struct for use with lib ticol_calldll

[set] also performs the same function

The operator '=>' is an alias for [struct set]

Example:

    struct s {{f1 20} {f2 10}}        # Declare a struct with 2 fields          
    struct set s.f1 "Hello world"     # set adjusts to assign a pointer var     
    puts [struct set s.f1]            # Return the contents of s.1              
    puts $s.f1                        # Return the contents of s.1              
    puts [set s.f1]                   # Dereference the pointer variable        
    puts [ofaddress s.f1]             # Dereference the pointer variable        

Results:

    Hello world     # [struct set] dereference                                  
    Hello world     # Dollar struct member dereference                          
    Hello world     # Plain [set] dereference                                   
    Hello world     # [ofaddress struct.field] will dereference back            

Struct shortcut dereference operators ->, => and =>> are available

See also: struct item. struct, struct dereference, ->, =>, =>>

struct size

    integer [struct size structname]

Returns the total size of a struct or of a struct's data field

Example:

    struct s {{f1 10} {f2 20} {f3 30}}                                          
    puts [struct size s]                                                        
    puts [struct size s.f1]                                                     
    puts [struct size s.f2]                                                     
    puts [struct size s.f3]                                                     

Returns:

       60                                                                       
       10                                                                       
       20                                                                       
       30                                                                       

Field Sizes and Unknown Data Size
---------------------------------
Fixed size fields, combined with an unknown variable size can be a
problem when assigning to struct fields. In such cases the struct field
can assign the address of a variable instead. This should be no more
than 10 characters long. Pass a variable name to [addressof] to store
the address. Use [ofaddress] to revert from address back to content
The variable must not be reassigned during the lifespan of the struct
field

Example:

                                    # Can't take address of a const             
    set qb $qbf                     # So use a variable                         
    struct set foo.a [addressof qb] # e.g. address is 36337072                  
    puts [ofaddress $foo.a]         # Dereference the address back              

Result:

    The quick brown fox jumps over the lazy dog                                 

See also: pointers, is_pointer, struct setb, struct elements, calldll, 
          calldll_cdecl, struct dump, type, vars, array, stack, variables,
          addressof, ofaddress, addressofb, ofaddressb

struct elements

    list [struct elements structname ?-size | -value?]

Returns a Tcl list containing the names of a struct's elements
(fields) which can be used to emulate [struct foreach]

Optionally a list of struct sizes or values (as addresses) can be returned

Examples:

    struct s {{f1 10} {f2 20} {f3 30}}                                          
    puts [struct elements s]                                                    
    foreach x [struct elements s] {                                             
        puts $x                                                                 
    }                                                                           

Returns:

    s.f1 s.f2 s.f3            # A Tcl list of member elements of s              
    s.f1                                                                        
    s.f2                                                                        
    s.f3                                                                        

See also: pointers, is_pointer, struct setb, struct elements, calldll, 
          calldll_cdecl, struct dump, type, vars, array, stack, variables,
          addressof, ofaddress, addressofb, ofaddressb

struct clear

    struct clear structname

Erase a Ticol struct memory block. Cannot be used on binary structs
returned from calldll_* calls

[struct clear] has no return value

Ticol structs are cleared to NULL when created. There is no need to
use struct clear after defining a struct. Typically, a struct is 
cleared after a calldll_* API call

    lib ticol_calldll                                                           
    struct s {f1 10}                                                            
    set ts_ptr [calldll_cdecl msvcrt40.dll localtime t]                         
    struct copy s $ts_ptr                                                       
    struct clear s                                                              

See also: pointers, is_pointer, struct setb, struct elements, calldll, 
          calldll_cdecl, struct dump, type, vars, array, stack, variables,
          addressof, ofaddress, addressofb, ofaddressb

struct copy

    struct copy dest-structname source-address

[struct copy] is provided for use with lib ticol_calldll and in
linking to external DLLs

Use with care: Copies 'struct bytes' count from a struct* memory
address as returned by calldll_*. A copy may be required to use the
return values from some external DLLs or Windows API functions

[struct copy] returns the number of bytes copied to the target struct

Careless use of [struct copy] may result in a fatal exception although
struct field boundaries are protected from being overwritten

Ensure any struct being copied from a DLL address is from a DLL which 
is opened persistently by using [dll_open]

Example:

Calling functions time, localtime and asctime in msvcrt40.dll 

    lib ticol_calldll    # Load our calldll plugin                              
                                                                                
    struct tm {     # Define a Tcl struct which can be mapped                   
       tm_sec   4   # int    seconds after the minute 0-59                      
       tm_min   4   # int    minutes after the hour   0-59                      
       tm_hour  4   # int    hours since midnight     0-23                      
       tm_mday  4   # int    day of the month         1-31                      
       tm_mon   4   # int    months since January     0-11                      
       tm_year  4   # int    years since 1900                                   
       tm_wday  4   # int    days since Sunday        0-6                       
       tm_yday  4   # int    days since January 1     0-365                     
       tm_isdst 4   # int    Daylight Saving Time flag                          
    }               # Where 'int' is a 32-bit long                              
                                                                                
    set t 0                                                                     
    # Clear the struct data-block to zero                                       
    struct clear tm                                                             
                                                                                
    # Load the DLL                                                              
    set handle [dll_open msvcrt40.dll]                                          
                                                                                
    # Call the MSVC API to get the integer C time                               
    calldll_cdecl $handle time t                                                
                                                                                
    # ((time_t)-1) is an error                                                  
    if {== $t -1} {                                                             
       puts "CRT mktime API call failed at line "#__LINE__                      
       exit                                                                     
    }                                                                           
    # Return a struct* tm* address from the time integer                        
    set ts_ptr [calldll_cdecl $handle localtime t]                              
                                                                                
    # Copy the returned binary struct* to our Tcl struct                        
    # Will not copy more bytes than struct can accommodate                      
    struct copy tm $ts_ptr                                                      
                                                                                
    # Get ASCIIZ string using struct tm: (current time)                         
    set ret [calldllstr_cdecl $handle asctime tm]                               
                                                                                
    # Release the DLL                                                           
    dll_close $handle                                                           
                                                                                
    # An ASCIIZ string is now in $ret                                           
    textcolor yellow                                                            
                                                                                
    puts "C++ asctime(now): '[string trim ret [chr 10]]'"                       
    textcolor                                                                   

    Result:

    C++ asctime(NOW): 'Sun Sep 04 19:21:40 2016'                                

One struct can be copied to another using [struct copy] providing that
the struct sizes match. Data is truncated if there is a size mismatch

    struct s {a 43}                                                             
    struct t {a 20}                                                             
    struct set s.a $qbf # Quick brown fox...                                    
    puts $s.a                                                                   
    puts "Copied [struct copy t $s] byte(s)"  # $s returns struct address       
    puts $t.a                                                                   

Result:

    The quick brown fox jumps over the lazy dog                                 
    Copied 20 byte(s)                                                           
    The quick brown fox                                                         

See also: pointers, is_pointer, struct setb, struct elements, calldll, 
          calldll_cdecl, struct dump, type, vars, array, stack, variables,
          addressof, ofaddress, addressofb, ofaddressb

struct dump

    struct dump structname

Produce an analysis of the struct structure, member variables, 
contents and hierarchy

Example:

    struct s {f1 15}                                                            
    struct set s.f1 "Hello world"                                               
    struct dump s                                                               

Result:

    struct 's' level 0 (0x21e2480) data @35529840 (0x21e2470)                   
    | 15 byte(s), 1 member(s)                                                   
    + 1:'s->s.f1'   => 35529840 (0x21e2470) 15 byte(s)                          
                                                                                
    Address   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F ASCII              
    -------- ----------------------- ----------------------- -----------        
    021e2470 48 65 6C 6C 6F 20 77 6F-72 6C 64 00 00 00 00 45 Hello world.       
                                                                                
    * Var 0::'s' has no parents                                                 

See also: pointers, is_pointer, struct setb, struct elements, calldll, 
          calldll_cdecl, struct dump, type, vars, array, stack, variables,
          addressof, ofaddress, addressofb, ofaddressb

struct item

    string [struct item structName.fieldname ?default?]
    string [struct set structName fieldname ?default?]

[struct item] is an alias for [struct set]

Retrieves an item (member field) by directly accessing the struct object
instead of using $ to dereference. This may be useful when handling complex
or multi-layer variable references

[struct set] without a value is analogous to [set var] and will return the
member field's contents

[upvar] is not required for struct members if the parent struct object
is within scope. A default return may be specified where the field
is not found

Example:

    struct foo {                                                                
       a 10                                                                     
       b 20                                                                     
    }                                                                           
                                                                                
    struct set foo.a "Hello"                                                    
    struct set foo.b "World"                                                    
                                                                                
    puts "[struct set foo.a] [struct item foo.b]"                               

Result:

    Hello World                                                                 

See also: pointers, is_pointer, struct setb, struct elements, calldll, 
          calldll_cdecl, struct dump, type, vars, array, stack, variables,
          addressof, ofaddress, addressofb, ofaddressb, passing structs

Struct Implementation Details

Ticol structs are provided primarily to interface with external DLLs via
the calldll_* plugin library. The design maps a series of struct 'member 
field' variables, each of which hold a value representing a starting
address-offset in a block of binary memory space. This allocated space
being shared with DLLs etc. and where the actual data is stored.

As such, struct member variables use a form of 'indirection'

Example:

This can be seen via a [struct dump] of a simple struct

    struct s {{f1 10} {f2 20} {f3 30}}                                          
    struct dump s                                                               

Results:

Here we can see struct 's' which has a data-block allocated at starting
address 33082528 (0x1f8cca0). The first struct member (s.f1) points to this
starting address. The second (s.f2) points to address 33082538 (0x1f8ccaa),
which is +10 bytes higher than the first. The last member (s.f3) points to 
address 33082558 (0x1f8ccbe), at 20 bytes higher, and has a reserved storage
space of 30 bytes. When calling a DLL the struct address 33082528 is
passed to a DLL along, if necessary, with the size [struct size s], which
in this case is 60 bytes total

--
                                                                                
var type is 3 v->val is 33082528                                                
                                                                                
struct 's' level 0 (0x1f8ccf0) data @33082528 (0x1f8cca0)                       
¦ 60 byte(s), 3 member(s)                                                       
+ 1:'s->s.f1'  => 33082528 (0x1f8cca0) 10 byte(s)                               
+ 2:'s->s.f2'  => 33082538 (0x1f8ccaa) 20 byte(s)                               
+ 3:'s->s.f3'  => 33082558 (0x1f8ccbe) 30 byte(s)                               
                                                                                
Address   0  1  2  3  4  5  6  7    8  9  A  B  C  D  E  F ASCII                
-------- -----------------------   ----------------------- ----------------     
01f8cca0 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 ................     
01f8ccb0 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 ................     
01f8ccc0 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 ................     
01f8ccd0 00 00 00 00 00 00 00 00 - 00 00 00 00 74 65 5F 74 ............te_t     
                                                                                
--

Write strategy
--------------
When a write is performed to a struct member field the allocated block size
for that field is erased and the incoming data copied to that part of the
struct data-block area. The command [struct setb] (set byte) allows a
specific byte width to be specified on write. This must be < than the struct
member field size. 

Write-Back on Proc Exit
-----------------------
Struct updates are written back by updating the whole struct data-block when
the stack unwinds during proc exit

See also: struct, structs, struct dump, dump

struct unset

    struct unset structname ?structname...? ?-nocomplain? ?-const? ?-glob?

Deletes all members of a struct and the enclosing struct, releasing
all memory. An error will be raised if the struct does not exist or
if the variable is not a struct type unless '-nocomplain' is used

A const struct may be deleted using the '-const' argument

Multiple struct names may be passed

You cannot unset an individual struct member field

Using structs with calldll_*

    Structs may be passed by name or using [addressof]
    See: calldll

You can also delete a struct using [unset]

See also: pointers, is_pointer, struct setb, struct elements, calldll, 
          calldll_cdecl, struct dump, type, vars, array, stack, variables,
          addressof, ofaddress, addressofb, ofaddressb

struct setb
 
    struct setb variable | value ?byte-width?

Write a variable in binary format to a struct member field

You must correctly supply the byte-width interpretation of the data for
the data to be stored correctly. 4 (long) is the default

'ofaddressb' is the inverse

See also: pointers, is_pointer, struct setb, struct elements, calldll, 
          calldll_cdecl, struct dump, type, vars, array, stack, variables,
          addressof, ofaddress, addressofb, ofaddressb

sizeof
 
    integer [sizeof variable-type]

Returns the interpreted (estimated) byte-size of a variable. This may be
useful when using the calldll* functions to pass data of a specific size

Example:

    set a 10                                                                    
    puts [sizeof [type a]]                                                      

Returns:

    4                                                                           

Example:

    puts [sizeof integer]                                                       

Returns:

    4                                                                           

Note that this value is an estimate and does not represent the actual
storage

See also: type, vars, char, int, int64, double, addressof, addressofb, 
          strptr

sleep
 
    sleep milliseconds

Sleep for a given number of milliseconds

A value of 0 for milliseconds causes Ticol to yield to the operating system
for as long as necessary

A value > 1 will sleep for approximately that number of milliseconds

Negative values are not allowed and will be rounded up to 0

The precise behaviour will be operating system version dependent. Unless 
there is a reason to sleep for a precise time then [sleep 0] is recommended

Example:

time {stack create s 100 ; stack unset s; sleep 0} 10000 # Sleeps for 7 ms      

Note that [time code] will indicate performance in microseconds
(1/1000th ms)

For more information, see:
    https://msdn.microsoft.com/en-us/library/windows/desktop/
    ms686298(v=vs.85).aspx
    https://randomascii.wordpress.com/2013/04/02/sleep-variation-
    investigated/

See also: doevents, ticks

string
 
Standard Tcl string processing commands

See individual sections for more information:

string toupper string ?first? ?last?  Convert chars in range to upper-case
string trim string ?charmask?         Trim string Default mask is space
string trimleft string ?charmask      Trim string left only. Mask as above
string trimright string ?charmask?    Trim string right only. Mask as above
string tolower | toupper string       Convert chars to lower/upper case
string index                          Return the character at a given index
string initcaps                       Word 1st chars forced to uppercase
string is_empty string | var -var     Returns a boolean
string length string                  Return length of a string
string left string|var n ?-var?       Return left n chars of string or var
string map token-list var|string      Remap tokens using a map list
string match ?-nocase? pattern string Glob match a pattern to a string 
string mids string|var start n -var   Return middle section of string/var
string range string first last        Get a sequence of chars from string
string repeat char ?n?                Get a string of repeated characters
string replace string this that       Replace 'this' with 'that' in string
string reverse string|var -var        Reverse a string or variable
string right string|var n -var        Get rightmost n chars of string/var
string slice string index...          Slice a string into a Tcl list
string toarray string arrayname       Convert a CRLF delim string to array
string totitle string                 Convert a string to initcaps

See also: string functions

See also: strings, strsplit, map, left, right, mid, mids, chr, asc, vars
          filter

string index

    string [string index string indexValue]

Returns the character of a string at the given index position

Example:

    puts [string index $qbf 4]                                                  

Result:

    q                                                                           

See also: string, filter

string isempty

    bool [string is_empty string]
    bool [string is_empty var -var]

Test whether a variable or string is empty and return a boolean 1 or 0

Example:

    puts [bool [string isempty $qbf]]                                           
    puts [bool [string isempty {}]]                                             

Results:

    False                                                                       
    True                                                                        

See also; string

string match

    bool [string match pattern string ?-nocase?]

Match a string by "glob" matching where the following rules are used

    ?        Match any single character
    *        Match more than 1 character
    <else>   else match a literal character

Substrings may be matched by wrapping the search pattern in asterisks,
otherwise an exact match will be performed

Character groups, e.g. [a-z], are not yet implemented 

Returns a boolean indicating a successful match

Example:

    puts [string match qu??k quick]         # Exact match                       
    puts [string match qu??k QUICK -nocase] # Exact match, no case              
    puts [string match d*g dog]             # Exact match on 'dog'              
    puts [string match b???n brown]         # Exact match on 'brown'            
    puts [string match "*fox*" $qbf]        # Substring match - no spaces       
    puts [string match "*cat*" $qbf]        # Substring match - no spaces       
    puts [string match "* f?x *" $qbf]      # Substring match with spaces       

Result:

    1                                                                           
    1                                                                           
    1                                                                           
    1                                                                           
    1                                                                           
    0                                                                           
    1                                                                           

See also: string, string trim, string replace, string slice, filter

string repeat

    string [string repeat char ?count? ?separator?]

Returns a repeated string of zero or more characters. If the count value
is omitted then 1 character of the specified type is returned.

If 'char' is s string then the first character of the string is used

If 'count' is zero or less than no characters are returned

An optional separator string may be specified of one or more characters

Example:

    puts [string repeat x]                                                      
    puts [string repeat x 0]                                                    
    puts [string repeat x 10]                                                   
    puts [string repeat x 5 -]                                                  
    puts [string repeat $qbf 3]                                                 

Results

    x                                                                           
                                                                                
    xxxxxxxxxx                                                                  
    x-x-x-x-x                                                                   
    TTT                                                                         

See also: string, string replace

string replace

    string [string replace string this that ?-noescape?]

Replaces instances of 'this' with 'that' in 'string' and returns the 
modified string. String replace is inefficient. See [replacechar] to
replace within simple variables

Argument 'that' may be null (empty), in which case, 'this' will be removed

Note that [string replace] takes a string and does not alter the string 
argument. You must reassign using any returned result

The default behaviour is to escape Tcl backslash escapes within the find
and replace arguments. This can be overridden using the -noescape argument

Example:

    set t1 [readfile ticol.man]                                                 
    set t1 [string replace $t1 "\t" " "]                                        

                                                                                
    puts [expr [string replace 2,100 , ""]*2]   # 4200                          

See also: replacechar, string, filter

string tolower, string toupper, tolower, toupper

    string [string tolower <string>]
    string [string toupper <string>]
    string [string tolower <string>]
    string [string toupper <string>]

String case conversion, short-form versions can be used as a slightly
more efficient alternative

Examples:

    puts [string toupper $qbf]                                                  
    puts [tolower "HELLO WORLD"]                                                

Results:

    THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG                                 
    hello world                                                                 

See also: string, filter

Tcl Semantics

Commands:

A Tcl script is a string containing one or more commands. Semi-colons and 
newlines are command separators unless quoted as described below. 
Close brackets are command terminators during command substitution
(see below) unless quoted. 

Evaluation:

A command is evaluated in two steps. First, the Tcl interpreter breaks the
command into 'words' (arguments or parameters) and performs substitutions
as described below. These substitutions are performed in the same way for
all commands

Secondly, the first word is used to locate a command procedure to carry out
the command, then all of the words of the command are passed to the command
procedure. The command procedure is free to interpret each of its words in
any way it likes, such as an integer, variable name, list, or Tcl script. 
Different commands interpret their words (arguments) differently

Words (parameters):

Words of a command are separated by white space (except for newlines, which 
are command separators). 

Double quotes:

If the first character of a word is double-quote (") then the word is 
terminated by the next double-quote character. If semi-colons, close 
brackets, or white space characters (including newlines) appear between
the quotes then they are treated as ordinary characters and included in
the word. Command substitution, variable substitution, and backslash
substitution are performed on the characters between the quotes as
described below. The double-quotes are not retained as part of the word
 
Argument expansion:

If a word starts with the string {*} followed by a non-whitespace
character, then the leading {*} is removed and the rest of the word is
parsed and substituted as any other word. After substitution, the word is
parsed as a list (without command or variable substitutions; backslash
substitutions are performed as is normal for a list and individual internal
words may be surrounded by either braces or double-quote characters), and
its words are added to the command being substituted. 

For instance, 

    cmd a {*}{b [c]} d {*}{$e f {g h}} # is equivalent to                       
    cmd a b {[c]} d {$e} f {g h}                                                

Braces:

If the first character of a word is an open brace ({) and argument
expansion rule above does not apply, then the word is terminated by the
matching close brace ('}'). Braces nest within the word: for each
additional open brace there must be an additional close brace (however, 
if an open brace or close brace within the word is quoted with a backslash
then it is not counted in locating the matching close brace). 
No substitutions are performed on the characters between the braces except
for backslash-newline substitutions described below nor do semi-colons,
newlines, close brackets, or white space receive any special interpretation
The word will consist of exactly the characters between the outer braces,
not including the braces themselves. 

Command substitution:

If a word contains an open bracket ([) then Tcl performs command 
substitution. To do this it invokes the Tcl interpreter recursively to
process the characters following the open bracket as a Tcl script. The
script may contain any number of commands and must be terminated by a
close bracket (]). The result of the script (i.e. the result of its last
command) is substituted into the word in place of the brackets and all of
the characters between them

There may be any number of command substitutions in a single word.
Command substitution is not performed on words enclosed in braces. 

Variable substitution:

If a word contains a dollar-sign ($) followed by one of the forms described
below, then Tcl performs variable substitution: the dollar-sign and the 
following characters are replaced in the word by the value of a variable. 
Variable substitution may take any of the following forms

    $name                                                                       
    ${name}                                                                     
    ${<complex-nested-dereference>}                                             
    $name(index)                                                                
    ${${<complex-nested-dereference>}(index)}                                   

See:
    https://en.wikipedia.org/wiki/Tcl#Syntax_and_fundamental_semantics

See also: dereference, nested dereferences, Ticol, Tcl

Security

Ticol Tcl should not be used in situations which require high levels of
security. It should be treated as alpha or beta quality and used
cautiously in such situations and only after intensive testing 
(See licence terms)

The following issues should be considered:

Guard User Inputs
-----------------
Ensure that user inputs from say [input] are guarded from malicious
execution. Either parse returns carefully or wrap in {} braces
to prevent automatic execution. 

Command Blocking
----------------
Lock down undesired commands using ticol.ini -> CommandUndefList=
Undefine commands such as: [@], [spawn], [exec], [shell], [run]

e.g. CommandUndefList=spawn exec shell run

Lock down shell execution of unknown command via 
ticol.ini -> AutoExecCommand=TRUE

The following security issues are addressed:

TCX Code Obfuscation
--------------------
This uses moderate strength symmetric encryption for script obfuscation
and is not designed to be "uncrackable". It should, however, be secure
enough for most uses

An MD5 feature is available to allow MD5 signatures of TCX code snippets
and obfuscated TCX files to be checked and validated
See: md5, md5 verification

Due to its interpreted nature and the introspective nature of Tcl it is
not easy to restrict access to obfuscated code. Some TCX content could
leak out via individual code-snippets but the full source code should
not be casually available. The /ECHO argument and [dump] command are
disabled for TCX code. Also [load script.tcx varname] is disallowed

Encryption
----------
The [encrypt] command uses moderate strength symmetric encryption and is
not designed to be "uncrackable". It should, however, be secure enough for
most uses and to prevent all but the most determined attacker

CGI Security
------------
Ticol should be used in public-facing CGI mode with extreme caution and
it may be far-more preferable to use PHP or PERL.

The command-line /EXPR is disabled if Ticol is detected as running in CGI
webserver mode, e.g.

    http://localhost:8800/cgi-bin/ticol.exe?/expr%2022/7.0

ticol.ini should be configured with an entry to block undesired commands
such as [cli], [exec] or [spawn]. A sample CGI ticol.ini is shown below.

-------------------------------------------                                     
[Config]                                                                        
AutoExecScript=FALSE  ; (autoexec.tcl)                                          
CGIDebug=False                                                                  
StartupLog=True                                                                 
PreprocessorVerbose=FALSE                                                       
StackTrace=FALSE                                                                
CommandUndefList="cli exec spawn"                                               
-------------------------------------------                                     

See: command line

Individual commands: e.g. ticol.exe ; "puts Hello" 
are disabled if Ticol is detected as running in CGI webserver mode, e.g.

    http://localhost:8800/cgi-bin/ticol.exe?%3b&%20puts%20{hello%20world}

See: command line

Tcl Strings

In Tcl everything is a string. Interpretation of numbers is automatic
Tcl strings may be unquoted or quoted using double quote characters

    puts Hello          # Assumed to be a literal unquoted string               

is the same as:

    puts "Hello"        # Explicitly declared and quoted string                 

However, strings which are wrapped in double quotes are protected from 
alteration by the Macro PreProcessor (MPP) and unquoted strings cannot
contain whitespace characters such as " " or \t

puts Hello World   # Invalid, will trigger an error. Whitespace not allowed
puts "Hello World" # Valid quoted string with embedded whitespace

Because "everything is a string", (including numbers), these are valid:

    puts [+ 1 "1234"]        # -> 1235                                          
    puts [+ 1 "0x1234"]      # -> 4461                                          
    puts [+ 1 "0o1234"]      # ->  669                                          
    puts [+ 1 0x1234]        # -> 4461                                          
    puts [+ 1 0o1234]        # ->  669                                          
    puts [+ 1 0b10011010010] # -> 1235                                          

Strings Within Strings
----------------------
Under most circumstances, adjacent strings will merge. Embedding double-
quotes within a quoted string effectively splits the string in two. 
Whether or not these halves merge depends on what intervenes between them.
Embedded double-quotes should always be escaped using a backslash.

Embedding double quoted strings within double quoted strings should be
avoided at all cost. There may be cases where it can work but it is 
recommended to avoid double-quoting and use braces to delimit strings
that is, if no variable references need be substituted or embedded 
command called.

Some Examples:

    puts Hello-world                            # An undelimited string         
    puts "Hello world"                          # A quoted string               
    puts {Hello world}                          # A braced string               
    puts "Hello"" world"                        # Adjacent strings merge        
    puts "An accidentally "embedded" string"    # Not recommended               
    puts "Goodbye \"cruel\" string"             # Properly-escaped \"           
    puts "Brave [chr 34]new[chr 34] world"      # Embedded " chars              
    puts {Another "embedded" string}            # No variables present          
    puts "Value of s is $s"                     # $s is substituted             
    puts {Value of s is $s}                     # $s isn't substituted          
    puts [subst {Value of s is $s}]             # $s is substituted             
    puts "Random is [rnd 1 10]"                 # [rnd] is called               
    puts {Random is [rnd 1 10]}                 # [rnd] isn't called            
    puts [subst {Random is [rnd 1 10]}]         # [rnd] is called               
    puts "Substring is {braced}"                # Braces appear in string       

See also: quoting hell, string, Macro PreProcessor. subst, braces, escapes

string range

    string [string range string first last|end]

Returns a range of consecutive characters from string, starting with the 
character whose index is first and ending with the character whose index is 
last

The index-reference values are "base 0' with an index of 0 referring to the 
first character of the string first and last may be specified as for the 
index  method. If first is less than zero then it is treated as if it were
zero, and if last is greater than or equal to the length of the string then
it is treated as if it were end. If first is greater than last then an
empty string is returned. 

Invalid arguments for first and last, e.g. overlapping, will return SUCCESS
(code 0) but an empty string

Example:

   string range $qbf 10 18   # Return chars from char 10 to 18 inclusive        
   string range $qbf 12 6    # Illegal (overlapping) range == empty list        
   string range $qbf 0 1024  # Returns the whole string (over length arg)       
   string range $qbf 0 0     # Returns the first character (0 to 0)             
   string range $qbf 0 -1    # Invalid range                                    
   string range $qbf -1 0    # Invalid range                                    
   string range $qbf -1 -1   # Invalid range                                    
   string range $qbf 0       # Invalid arguments (arg missing)                  

Result:

    brown fox                                                                   
    {}                                                                          
    The quick brown fox jumps over the lazy dog                                 
    T                                                                           

See also: string, filter

string slice

    list [string slice <string> startpos ?next-slice? ...]

Breaks up a string into a well-formed Tcl list by 'slicing' it at fixed 
offsets. This command may be used to break formatted, tabular text, possibly
delineated by CRLFs into list fields. During the process, whitespace is
removed from the source string and a single space added as a list-separator
The slice is applied to each line where data has CRLFs

The data MUST be in a regular (non-varying) columnar format and without
empty "slices to avoid "spiralling". Tab-separated data may not slice well
If the data is irregular, non-columnar, or has a non-regular header the
data will 'spiral' out of sequence

If no index is given or if the index value is specified as 0 then the whole
string is returned

[string slice] copies characters up to and including each numeric argument
with the first characters assumed, it returns a list element and then
continues to the next numeric argument until the string is exhausted. 
To visualise this, imagine slicing a loaf of bread with the knife at the
slice points, leaving a slice to the left

Values must be in ascending order or an error will be raised
Values to the left of the given offset are returned for each offset value

Argument index values larger than the list length are ignored
The index values are a base 1 index-offset into the string not base 0

Example:

    # qbf == "The quick brown fox jumps over the lazy dog"                      
    puts [string slice $qbf 3 9 34 39 50]                                       

Result:

     3     9                       34   39   50 <-- Slice (end) points          
     |     |                        |    |   ||                                 
   The quick brown fox jumps over the lazy dog                                  
   ^  ^     ^                        ^    ^     <-- Start start points          
   1  4     10                      35   40                                     
   |                                                                            
   (start character is assumed)                                                 
                                                                                
   Character ranges 1-3, 4-9, 10-34, 35-39, 40-50                               

Output:

   The quick {brown fox jumps over the} lazy dog                                

Example: (Slicing up redirected output from systeminfo.exe)

    set filename "sysinf.txt"          # Microsoft Windows systeminfo output    
    readfile $filename data            # Read the output file                   
    set q [string slice $data 27 1024] # Slice columns at 27 and 1024 chars     
                                                                                
    foreach {x y} $q {                 # Index using Tcl variables              
       textcolor green                                                          
       printf "%-26.26s  " $x                                                   
       textcolor                                                                
       printf "%-51.51s\n" $y                                                   
    }                                                                           

Result:

    Host Name:                  Gandalf                                         
    OS Name:                    Microsoft Windows XP Professional               
    OS Version:                 5.1.2600 Service Pack 3 Build 2600              
    OS Manufacturer:            Microsoft Corporation                           
    OS Configuration:           Standalone Workstation                          
    OS Build Type:              Multiprocessor Free                             
    ...                                                                         

Bad example. Uses tab characters (0x09) instead of space-padding

    set x "\                                                                    
    684\t.\Backups                                                              
    28\t.\Data                                                                  
    7508\t.\Documents"                                                          
                                                                                
    # Displays with regular spacing                                             
    # 684     .\backups                                                         
    # 28      .\Data                                                            
    # 7508    .\Documents                                                       
    set q [string slice $x 7]                # Won't slice evenly               

See also: string

string toarray

    integer [string toarray string arrayname]

Convert a CRLF-delimited string to an array. The output array
variable must not exist

Example:

    set s [readfile ticol.man]                                                  
    set lines [string toarray $s a]                                             

Result:

    <displays the file contents>                                                

See also: string

trim, trimleft, trimright, string trimleft - string trimright

    string [string trim string ?charset?]
    string [string trimleft string ?charset?]
    string [string trimright string ?charset?]
    string [trim string ?charset?]
    string [trim var ?charset? -var]

Returns a string with any trailing or leading characters from the set 
given by chars removed. 

If 'charset' is not specified then white space is removed (spaces, tabs, 
newlines, and carriage returns)

[trim] is a slightly more efficient version of the standard [string trim]

Example:

    string trimleft [funct fraction [expr 4 * atan(1)]] 0.                      
    string trimleft [funct fraction [/ 22 7.0]] 0.                              

Result:

    141592653589792                                                             
    142857142857143                                                             

Example:

    puts [string trim $qbf " T[range a-w]yz"]                                   

Result:

    x                                                                           

See also: trimprefix, trimsuffix, string, strsplit

trimprefix

    integer [trimprefix varName string ?-nocase?]

Strips the string argument from a variable if found. A count of the number
of characters removed (the length of 'string') is returned

[trimsuffix] does not operate on a string literal.

Example:

    set s $qbf        # "The quick brown fox jumps over the lazy dog"           
    set r [trimprefix s "The "]                                                 
    puts $r                                                                     
    puts '$s'                                                                   

Result:

    4                                                                           
    quick brown fox jumps over the lazy dog                                     

See also: trim, trimsuffix

trimsuffix

    integer [trimsuffix varName string ?-nocase?]

Strips the string argument suffix from varName (if set) and if there is
a corresponding match. [trimsuffix] returns the new length of the variable
if changed, otherwise zero if no changes were made

[trimsuffix] is a command, not a function, and does not operate on a
string literal

If varName does not exist then 0 is returned

See [string trim] for a functional style trim command

Example:

    set s $qbf        # "The quick brown fox jumps over the lazy dog"           
    puts [trimsuffix s "dog"]                                                   
    puts '$qbf'                                                                 

Results:

    40                                                                          
    'The quick brown fox jumps over the lazy'                                   

See also: trim, trimprefix, string trim

strsplit
 
    list [strsplit string separators ?variable?]

[strsplit] splits a string into a list using 'separators' as a template
Optionally the result can be placed in a variable passed by argument
The separator character(s) is/are not included in the output

Example:

    strsplit "hello world" "r" a    # Split by char                             
    puts $a                                                                     

Result:

    {hello wo} ld                 # List of two items                           

See also: string, list

subst

    string [subst ?-nobackslashes? ?-nocommands? ?-novariables? string]

Perform backslash, command, and variable substitutions

subst performs the first stage of Tcl script evaluation, performing
variable substitution and/or command substitution, but stops short of
evaluating the commands contained at the top level of the script.

Subst ignores: option escape on; option autoexec off settings and always
processes Tcl backslash escapes such as \\, \" or \$ 

Any optional arguments must appear before the string value

Example:

    set html   {<html><head>$title</head></html>}                               
    set title  "Hello, World!"                                                  
    set output [subst -nocommands $html]                                        
    set output                                                                  

Result:

    <html><head>Hello, World!</head></html>                                     

Example:

    subst "The quick \"brown fox\" jumps over the lazy dog"                     

Result:

    The quick "brown fox" jumps over the lazy dog                               

See also: {*}, expansion

swap
 
    swap var1 var2

Swap a pair of standard Tcl variables. Since all Tcl variables are accessed
by name rather than memory address (even when addressing via [addressof] 
etc. this is effectively a rename operation which renames each variable with
the other's name with no intermediate placeholder variable

Const types cannot be swapped

Example:

    set a world                                                                 
    set b Hello                                                                 
    swap a b                                                                    
    puts "$a $b"                                                                

Results:

    Hello world                                                                 

[swap] is useful for quickly exchanging sorted and unsorted arrays

Example:

  option expression off                                                         
  # Using a temporary and [swap] to swap between sorted and unsorted            
  # arrays, [swap] moves no data, it merely renames the two variables           
  array sort foo -array out                                                     
  swap out foo            # Swap $foo and $out                                  
  set s [array size foo]                                                        
  for {set i 0} {[< $i $s]} {incr i} {                                          
     printf "%4i:\t%-40.40s\r\n" $i $foo($i)                                    
  }                                                                             

Swapping Upvars in Procedures
-----------------------------
The use of [swap] on local upvars within a procedure will have only local
scope effect. The original parent variables remain unchanged unless
assigned directly

    set a 2                                                                     
    set b 3                                                                     
                                                                                
    proc foo {} {                                                               
       upvar a                                                                  
       upvar b                                                                  
       swap a b                                      # Swap local upvars        
       puts "foo swap 1: locals    a=$a,   b=$b"                                
       puts "foo swap 1: globals ::a=$::a, ::b=$::b"                            
       swap ::a ::b                                  # Swap parent globals      
       puts "foo swap 2: locals:   a:$a    b:$b"                                
       puts "foo swap 2: globals ::a=$::a, ::b=$::b"                            
    }                                                                           
    foo                                                                         
    puts "root: a:$a, b:$b"                                                     

Results:

    foo swap 1: locals    a=3,   b=2                                            
    foo swap 1: globals ::a=2, ::b=3                                            
    foo swap 2: locals:   a:3    b:2                                            
    foo swap 2: globals ::a=3, ::b=2                                            
    root: a:3, b:2                                                              

See also: vars

switch (Flow Control)
 
    switch -option|-- value { arg {script} ... } ...
    switch -option|-- value {
       arg {script}
       arg {script}
       ...
    }
    switch -option|-- value arg {script} ...

Options
-------
                                                                                
                  The first argument is always an option value                  
                  Multiple options may be given                                 
--                No option (required if no option given)                       
-exact            Use exact matching (default)                                  
-glob             Use wildcard style matching  *,?, <chars>                     
-nocase           Case insensitive match with -glob or string matches           
-eval             Treat each block lvalue as an expression                      

[switch] has a high setup cost for each call so it is suggested that
within time-intensive loops you use [if...elseif..else] instead

Switch consists of an introductory flag setting, a value to use as
a reference, which may be the result of an expression or other command,
then a (possibly braced) series of argument + code-block pairs as a Tcl
list.

[switch] does not evaluate it's main argument value. This must be either
a literal, dereferenced variable or a return from some other command

Example:

puts [switch -- [expr "rnd(1,4)"] 1 "return 1"  default "return 99"]

Cascade (fall-through) defaults are supported but at present all code
blocks must be wrapped in braces. 

An enhancement to [switch] is the $this variable which will either
contain the value of any return statement or the evaluated expression
argument if return is absent.

It is very strongly suggested that you always include a 'default' case

[return] is used to return a value from a [switch] case

Example:

    option expression on                                                        
    for {set j 0} {$j < 10} {incr j} {                                          
       puts [switch -- [lindex [list a b c d] [rnd 0 3]] {                      
       a   {return "a"}                                                         
       b   {return "b"}                                                         
       default {return "c or d"}                                                
       }]                                                                       
    }                                                                           

A local variable 'this' is available which represents the variable to be
evaluated. This may be the result of an expression.

If $this is used in a single-line switch then wrap the code in braces ...

Example:

    puts [switch [expr "rnd(1,4)"] 1 "return 1" default {return $this}]         

This example shows cascade or "fall-through" default arguments for cases
2 and 3 ...

    switch -- [expr "rnd(1,4)"] {                                               
       1 {puts "is one"}                                                        
       2 {-}                                                                    
       3 {-}                                                                    
       {puts "is 2 3 or 4 (actually: is $this)"}                                
    }                                                                           
                                                                                
    switch -- [lindex [list a b c d] [rnd 0 3]] {                               
       a   {puts "is a"}                                                        
       b   {puts "is b"}                                                        
       default {puts "c or d"}                                                  
    }                                                                           

Example:

  set count [lcount $qbf]           # Uses global test const $qbf               
  -- count                                                                      
  switch -glob -nocase [lindex $qbf [rnd 0 $count]] {                           
      the*    {puts "is the*"}                                                  
      qu*     {puts "is qu*"}                                                   
      br*     {puts "is br*"}                                                   
      fo*     {puts "is fo*"}                                                   
      j???s   {puts "is j???s"}                                                 
      ?v?r    {puts "is ?v?r"}                                                  
      *y      {puts "is *y"}                                                    
      d?g     {puts "is d?g"}                                                   
      default {puts "is default"}                                               
  }                                                                             

Example: (Performance testing)

  time {switch -glob [lindex $qbf [rnd 0 10]] {default {} }} 100000 -verbose    

Result:

  1 microsecond(s) per iteration                                                

See also: if, on, flow control, proc

sysfree
 
    sysfree varName

Release a string which is typically allocated by an external DLL call using
the Windows SysAlloc*() API calls (via an external DLL)

Note that [sysfree] takes a variable name not a variable contents. That
variable should hold a valid memory address allocated by SysAlloc*

This is functionally-equivalent to calling Win32 API SysFree((BSTR)address)

[sysfree] returns a boolean success. An exception is raised if the passed 
address is invalid. 

Where 'calldll' calls return a SysAlloc allocated string and these strings
are not freed then a memory leak will occur. This could have catastrophic
consequences. Tcl variables in such cases will be addresses pointing to 
memory space

Calling [sysfree] with an address not returned by SysAlloc* is a fatal
error and will terminate the environment

Example:

    # Returns the address of a string                                           
    lib ticol_calldll                                                           
    set datenow [calldllstr "mslib145.dll" "VBDateStr" [now]]                   

    42342.862013888887                                                          

    puts "PHPDate: "[calldllstr "mslib145.dll" "PHPDate" $datenow "r"]          

    PHPDate: Fri, 04 Dec 2015 20:41:18 +0100                                    

    sysfree datenow        # Pass the variable name                             

See also: calldll

The Tcl Language
 
Tcl stands for Tool Command Language. This is a popular and portable script
language which is widely used in Linux environments. The most popular version
of Tcl is also suitable for embedding within C/C++ environments.

It is a simple, but powerful language which can be used to rapidly develop
useful scripts or prototype ideas. Tcl bases everything, even procedures
as 'commands', optionally with one or more qualifying arguments.

It uses braces within the syntax and although Tcl source looks very much
like C/C++, the braces perform a somewhat different function.

Tcl requires adopting a slightly different mindset and reminding one's
self that this is not C.

Tcl is often used in embedded applications and one of the more notable
uses is within Cisco(TM) routers.

Abridged Summary
----------------
All operations are commands, including language structures and code is
written in prefix notation.
Commands accept a variable number of arguments
In standard Tcl there are no keywords so control structures may be 
redefined
Data types are essentially string-based and conversion is automatic
where requred
Tcl is usually a cross-platform language
Standard Tcl supports Unicode and Object Orientation (OO)
Can be bytecode compiled
Full socket support

Ticol Tcl
---------
Ticol is a small subset of the much larger Tcl language and with a small
range of language extension which make it useful for Windows scripting and
as a replacement for Windows batch scripts.

Ticol is intended to be used only for personal, hobby or administrative
scripting and not as an equivalent or replacement for languages such as
C/C++/C# etc. Ticol should not be used in high-risk environments without
appropriate acceptance testing.

Abridged Summary
----------------
Self-contained binary with no dependencies (no .net required)
Small footprint
Was designed primarily for older systems
Unicode is not supported. The language is European ANSI only
Object orientation is not natively supported
Plugins are supported and these differ from standard Tcl's TEA
There is no bytecode compiler. Ticol is purely interpreted
May be used as a CGI back-end script handler
Minimal socket/networking support

For more information search the internet for "Tcl"

See websites:

    http://antirez.com/articoli/tclmisunderstood.html
    http://en.wikipedia.org/wiki/Tcl
    http://wiki.tcl.tk
    https://www.tcl.tk

See also: Tcl syntax, Ticol

TCL History

TCL is an acronym which stands for "Tool Command Language"

The language was designed by John Ousterhout who was said to be
frustrated by programmers inventing their own language and who decided to
create a script language which would more appropriately fit the task

TCL is now well-established and with a pedigree which goes back several
decades back to 1988. The official release of TCL is extremely stable and
is quite fast since it includes a byte-code compiler. It is also quite large
and not designed to be a minimalist/portable distribution. There are 
TCL-like languages such as "Jim" (and Ticol of course) which aim to fulfil
these demands

Ticol TCL is an interpreter-only implementation of a subset of TCL commands
Ticol is strictly interpreted and does not offer byte-code compilation. 
For this reason, Ticol will always be much slower than the official releases
of TCL

The mixed-case acronym "Tcl" is commonly used in place of "TCL"

See also: Ticol, TCL syntax, tcl and php

Tcl Syntax
 
Tcl has a 'C-like' visual appearance but the design has certain non-C-like
limitations. In particular, routines are either commands or procedures 
rather than functions and both of these may be called by dereferencing by
wrapping in square brackets rather than suffixing curved brackets.

Tcl is based on commands with none or more arguments. All language
elements including math operators are available as commands

    command arg1 arg2 ...

Example:

    / 22 7.0                                                                    
    puts Hello -noconfirm                                                       

Ticol (and standard Tcl) has a separate C-like expression language
evaluator which is usually evaluated using the [expr] command. This
uses infix notation but may also include commands.

Example:

    puts [expr "[/ 22 7.0]*3"]                                                  

* Tcl as a language is said to have minimal or no syntax
  One could perhaps consider that it had a C-like appearance

* Variables are 'typeless' and PHP-like and prefixed by a dollar-sign

* Variables are interpreted on-the-fly

* Square brackets trigger evaluation and are the equivalent of function 
  calls:     C/C++:  foo(arg) ->  Tcl:  [foo arg]

* Tcl knows nothing about C-like math expressions. The [expr] or [calc] 
  commands are used to this. In Ticol these may also be controlled by 
  [option expression]

* Square brackets are evaluated first. This is important within strings as
  commands (function calls) are evaluated even within quoted strings 
  unless protected by braces

* Each level of braces prevents/delays evaluation by one level. Each 
  call to interpret removes one level of braces

* Unlike C/C++, Tcl keywords are commands and may have optional switch args
  These perform a similar function to C# and PowerShell attributes in
  passing qualifying and configuring information to the command which may
  modify its behaviour or outputs

* Variables don't inherit "downwards" as in C/C++. Only globals are visible
  (providing they are referenced by the '::' global prefix)
  Instead you must use [upvar] or [uplevel] to access variables lower down
  the call stack or pass via the proc argument list. You can access global
  variables using the global-scope prefix "::" e.g. $::env

* Keywords must generally be separated by whitespace from adjacent symbols
    if { [eq $a 10] } { statement...

* C/C++ like multi-part statements separated by semi-colons are supported

* Flow-control keyword braces must open on the same line as the command
    if { condition } { 
      statement ...

* Braces group statement together and prevent evaluation

* Braces may be nested to allow evaluation only at the Nth level

* Heavy use is often made of lists. (spaced/quoted/braced strings)

* Grouping within Tcl lists is enforced by brace-wrapping

* Commands are defined by CRLF line-end structuring rather than C/C++
  freeform text. Thus CRLFs may be significant in command braces

For more information, search the internet for: tcl syntax

See also: Tcl

TCP/IP and Networking Functionality

Ticol omits the standard Tcl socket support but incorporates a small
number of useful networking routines as follows:

    http                  Remote HTTP data-retrieval
    html                  HTML formatting commands
    info <various>        Network interface information
    ip_to_long            IPV4 conversion routine
    long_to_ip            IPV4 conversion routine

Plugin libraries are available as follows:

    arp                   Simple address resolution protocol
    dns                   Domain Name Service lookup
    ftp                   A simple FTP client library
    ping                  A simple PING library
    smtp                  A simple SMTP client library
    etc.

Additionally, external _stdcall or _cdecl DLLs may be called to add TCP/IP
connectivity

See also: plugins

Syntax Checking (Static)

Ticol performs minimal syntax checking via the Macro PreProcessor (MPP)
Checks are made only on brace and double-quote parity with some indication
being given as to where the error is located if there is a parity mismatch

Command syntax is not checked and errors may not become evident, 
particularly within uncalled code, until that code is eventually called.
Thus, the language requires careful coding and error checking

Perhaps someone may wish to write a static code analyser

See also: syntax, faq

seek

    bool [seek handle offset ?whence?]
    bool [file seek handle offset ?whence?]

Set the file pointer within an open file opened by [open]
The position is set relative to one of 3 options with 0 being the default

    0 seek to an absolute position
    1 seek relative to current position
    2 seek relative to EOF

Useful defines:

    const SEEK_SET        0
    const SEEK_CUR        1
    const SEEK_END        2

Example:

    set fp [try {                                                               
       open ticol.txt rb                                                        
    } catch {                                                                   
       die                                                                      
    }]                                                                          
    if {file seek $fp 10 0} {                                                   
       puts "File position for $fp is at '[file tell $fp]'"                     
    }                                                                           
    close $fp                                                                   

Result:

    File position for 4700672 is at '10'                                        

See also: tell, file

tab

    string [tab ?n?]

[tab] returns a string of n spaces which can be used with [puts], [printf]
or [format] to advance the cursor. [tab] mimics the TAB() command in BASIC
although it offers this functionality by returning a string of spaces 
rather than advancing the cursor directly

[tab] is shorthand for [string repeat " " $n]

Example:

    puts "Hello[tab 10]world"                                                   

Result:

    Hello          world                                                        

See also: string

tell
 
    integer [tell handle]

Returns the file pointer position for an open file handle

Example:

    See tell

See also: file, open, close, rewind, seek

tcl2bat (Conversion Batch Script)

tcl2bat.bat is a batch script which can convert any Ticol Tcl script into
a functioning batch file. See tcl2bat.bat in the distribution pack.

This is achieved by wrapping the original script with a batch file header
which then calls the Ticol interpreter. Converted BAT files can be loaded
and run by Ticol Tcl.

Example:

    tcl2bat foo.tcl                                                             

Result:

    Processing: Infile: 'C:\Tcl\foo.tcl' outfile: 'C:\Tcl\foo.bat'              
    * Done *                                                                    

See also: batch files, md5 batch example, exec, spawn, shell

textcolor
 
    textcolor ?foreground-value|-? ?background-value|-?

Sets the text color

The color arguments may be a symbolic name such as 'blue' or a numeric
value which corresponds with standard Windows console values (e.g. blue,
cyan and these may optionally be prefixed with 'light', e.g. 'lightred')

Either argument may also be omitted by specifying a '-' character
e.g. [textcolor - 13], [textcolor 13 -]

[textcolor] has no return value

The following colour constants are defined. You may also use numeric
values:

    0   0x00   black                                                            
    1   0x01   darkblue                                                         
    2   0x02   darkgreen                                                        
    3   0x03   darkcyan                                                         
    4   0x04   darkred                                                          
    5   0x05   darkmagenta                                                      
    6   0x06   darkyellow                                                       
    7   0x07   grey (gray)                                                      
    8   0x08   darkgrey (darkgray)                                              
    9   0x09   blue                                                             
    10  0x0a   green                                                            
    11  0x0b   cyan                                                             
    12  0x0c   red                                                              
    13  0x0d   magenta                                                          
    14  0x0e   yellow                                                           
    15  0x0f   white                                                            

Both 'grey' (UK) and 'gray' (US) spellings are accepted. Colour values are
case-significant. You may use [enum] to define alternate constants if you
wish

A hyphen (-) may be specified to omit a foreground value

The text colour is reset after each command-line command. Use multiple-
commands separated by semi-colons to apply

Bright background colours are supported in the Windows console, since 
the top bit is used for this purpose, the blink feature is not supported

See also: cls, box, enum

$this

$this is a local flow control variable and is available as follows ...

    Control Structure Type Supported?                                           
    ---------------------------------                                           
    Yes                 No                                                      
    ---------------------------------                                           
    [switch]            [if]                                                    
    [goto_block]        [while]                                                 
    [goto]              [for]                                                   
    [on]                [do]                                                    
                        [foreach]                                               
                        [array foreach]                                         
                        [exit]                                                  
                        [return]                                                
                        [try], [catch]                                          

It is provided only in the 'slow' flow control mechanisms and where its
provision is useful

In [switch] control structures this represents the evaluated switch
selection expression. $this is valid in the following constructs.

    [switch]

Example:

    option expression on                                                        
    for {set j 0} {$j < 10000} {incr j} {                                       
       puts [switch -- [expr "rnd(0,10)"] {                                     
          0 -                                                                   
          1 -                                                                   
          3 -                                                                   
          5 -                                                                   
          7 -                                                                   
          9  {return "$this is odd"}                                            
          2 -                                                                   
          4 -                                                                   
          6 -                                                                   
          8 -                                                                   
          10 {return "$this is very strange"}                                   
        }]                                                                      
    }                                                                           

Example:

    goto_block {                                                                
       one {puts "this is $this"}                                               
       two {puts "this is $this"}                                               
       goto four                                                                
       three {puts "this is $this"}                                             
       four  {puts "this is $this"}                                             
    }                                                                           

Results:

    this is one                                                                 
    this is two                                                                 
    this is four                                                                

See also: do, switch

Thread Use

Windows may allocate background threads for various reasons, e.g. mutex
flag control by Windows. For this reason the number of threads in use by
Ticol may rise during execution. After a period of idle time, the thread
count may also decline

This may also increase the amount of memory used by Ticol as shown by 
Windows Task Manager

ticks
 
    integer [ticks]

Shows the tick count in milliseconds since Windows was loaded

After 9,223,372,036,854,775,807 ms or approximately every 106,751,991,167
days (roughly every 292,471,208 years) this value will wrap around to zero

Example:

  while { 1 } {                                                                 
    textcolor [% [/ [ticks] 1000] 0xf]                                          
    puts "[info hostname]: Uptime Ticks: [/ [ticks] 1000] (sec)" -nonewline     
    puts "[expr {int(([ticks] / 1000.0) / (60*60*24))}]" -nonewline             
    puts " day(s)"                                                              
    textcolor                                                                   
    sleep 1000                                                                  
  }                                                                             

See also: clock, date, time, info

time
 
 time [time ?-ctime? ?-noseconds? ?-separator char?] # Get system time
 time [time double ?-ctime? ?-separator char?]       # Time from a double
 double [time hh:mm?:ss?]                            # Double from time
 integer [time {expression} ?iterations? ?-info | -quiet?] # Script timer

The Ticol [time] command offers 3 modes of operation. It can return the
current system time, convert a double time value to an ASCIIZ time string
or it can time the execution of a Tcl script to microsecond resolution

[time double] requires a full decimal fraction with decimal point

[time] with no arguments returns the system time in HH:MM:SS format
If the -ctime argument is added then a "C" integer time value is returned
-ctime also requires that the resulting date/time be within the C epoch
If seconds digits are not required they may be omitted using -noseconds

[time hh:mm:ss] is unsupported. Use [date hh:mm:ss ?-ctime]

Example: 

    puts [time]                                                                 
    puts [time -separator [chr 32]]                                             
    puts [time -separator .]                                                    
    puts [time -noseconds]                                                      
    puts [time -ctime]                                                          
    puts [time .583333]                                                         
    puts [time [date now] -ctime]                 # Within C epoch              
    puts [date 1970/01/01 00:00:00]                                             
    puts [time [date 1970/01/01 00:00:00] -ctime] # C epoch start               
    puts [time [date 1998/01/30 23:30:00] -ctime] # After C epoch               
    puts [time [date 1968/04/08 23:50:01] -ctime] # Before C epoch              

Result:

    11:41:57                                                                    
    11 41 57                                                                    
    11.41.57                                                                    
    11:41                                                                       
    14:00:00                                                                    
    1478346117                                                                  
    1478346117                                                                  
    22569                                                                       
    0                                                                           
    886203000                                                                   
    Invalid C epoch time: 24936.993067                                          

[time] with expression and iteration arguments runs the given expression
for the number of times specified by 'iterations'. The average execution
time per iteration in milliseconds is displayed. This variation uses the
Windows timer. The '-info' switch displays extra timing information.

[time] used as a profiling tool will require [option echo on] in order
to see the output text

The default number of iterations is 1. The fewer iterations, the less
accurate  the calculation of elapsed time will be. A suggested minimum
number of iterations to test is 10

The resulting ms time is displayed to 4 decimal places. This value is also 
the command return value. The -quiet arg can be used to chain only this 
numeric result back into other code for analysis

Note that [time code] will indicate performance in microseconds 
(1/1000th ms)

Example:

    puts [time {;} 1000000]                                                     

Result:

    1 microseconds per iteration                                                

Example:

    proc square x {expr $x*$x}                                                  
    puts [time {square 42} 10000]                                               

Result:

    94 microsecond(s) per iteration                                             

This may vary when repeated several times ...

    108
    111
    133
    108
    117

Example:

    time {expr 2* asin (1.0)} 100000                                            

Result:

    9 microsecond(s) per iteration                                              

[time] as a flow-control command may be nested...

    time {                                                                      
       time {                                                                   
          set r [randomstr 100]                                                 
          store add $r                                                          
       } 100                                                                    
       set s [store get]                                                        
    } 100000 -info                                                              

Converting Double Date/Time Values

[time double] can be used along with [date double] to return only a time
value from a double which contains a date and time component

Example:

    set d [date now]                                                            
    puts $d                                                                     
    puts [time $d]                                                              

Result:

    42663.7881250000                                                            
    18:54:54                                                                    

See also: date, ticks, clock, timer

timer
 
    integer [timer]

High-resolution millisecond timer. Returns the current tick count in 
milliseconds as the time elapsed since Windows was started.

After 9,223,372,036,854,775,807 (µs) or approx. every 106,751,991,167 days
(roughly every 292,471,208 years) this value will wrap around to zero

timer uses the Win32 timeGetTime() API and resolves to that level of
accuracy

Example:

    set start_time [timer]                                                      
    # Do some processing                                                        
    # for ...                                                                   
    set end_time set [timer]                                                    
    puts "Run took [- $end_time $start_time] milliseconds"                      

See also: time, date, ticks, clock seconds

tk Support

There is no Tk implementation or GUI support in Ticol other than the
[msgbox], [inputbox], [get_fileopen] and [get_filesave] commands

[box], [gotoxy] and [input] may be used to create text-mode dialogs

See also: msgbox, inputbox, get_fileopen, get_filesave

trace
 
    trace add command | * ?-glob?
    trace remove command | *
    trace list
    trace log enabled|disabled

A simplified version of the Tcl [trace] command which traces only commands

The command adds or removes a runtime trace event for the given command. 
'*' may be used to trace all commands. When the traced command is called
details are logged to the console as execute "Exec cmdcount (level)>" and
return values as:  "Retn cmdcount (level)>"

The traced command or proc need not exist at the time the trace is
registered

Only the first command argument is logged to the console
Only commands can be traced. Variables are not traced

If a [subst] argument contains commands, these are not traced unless a 
separate [trace add] registration is made

Tracing Procedures
------------------
Procedures can be traced as follows. Logging can also be added as desired
Note that the [proces] argument in [foreach] must not be wrapped in curly braces

    foreach x [procs] {                                                         
        puts "Adding proc $x to trace"                                          
        trace add $x                                                            
    }                                                                           
    trace log enabled                                                           

Examples:
--------

    trace add *                                                                 
    trace add puts                                                              
    trace add if                                                                
    trace remove puts                                                           
    trace remove *                                                              
    trace list                                                                  

Example:

    trace add incr                                                              

Result:

    Exec 0> incr iters                                                          
    Retn 0> {incr} -> {1061}                                                    

Example:

    trace add factorial                                                         
    assert [factorial 5] {== $_ 120} -v -line #__LINE__                         

Result:

    Exec 1039 (0)> factorial 5                                                  
    Exec 1043 (0)> factorial 4                                                  
    Exec 1047 (0)> factorial 3                                                  
    Exec 1051 (0)> factorial 2                                                  
    Exec 1055 (0)> factorial 1                                                  

    Retn 1055 (0)> {factorial} -> {1}                                           
    Retn 1051 (0)> {factorial} -> {2}                                           
    Retn 1047 (0)> {factorial} -> {6}                                           
    Retn 1043 (0)> {factorial} -> {24}                                          
    Retn 1039 (0)> {factorial} -> {120}                                         

    assert: Line 103: PASS '== $_ 120'                                          

See also: debugging, time, timer

type
 
    string [type varName | string ?-var?]

Return the type of a variable as interpreted by Ticol. If the -var
argument is used [type] will detect only the variable type and will return
"undefined" if a matching variable is not found, otherwise a string
type will be assumed and it will be analysed for the likely data type

Pointer-variables which hold addresses will be interpreted as integer type

Arrays must be passed by reference (arrayname) not $arrayname

Lists are detected only if braced and must present their enclosing braces
either by being quoted or stored in variables 
e.g. [type "{a b c}"] or set a "{a b c}"; puts [type $a]

    Known data types                                                            
    ----------------                                                            
    enum    const                                                               
    const   const                                                               
    array   array                                                               
    stack   stack                                                               
    struct  struct                                                              
    string  string                                                              
    list    string                                                              
                                                                                
    Dereferenced types                                                          
    ------------------                                                          
    integer                                                                     
    float                                                                       

Example:

    set a 10                                                                    
    type $a                                                                     
    type a                                                                      

Result:

    integer                                                                     
    integer                                                                     

Example:

    set a(1) hi                                                                 
    type $a                                                                     
    type a                                                                      

Result:

    string                                                                      
    array                                                                       

Example:

    puts [type $tcl_config]  # Will check the variable's contents               
    puts [type tcl_config]   # Will check the variable type itself              

                                                                                
    string                                                                      
    const                                                                       

See also: sizeof, vars

trunc

    number [trunc number precision]

[trunc] will truncate a real (double) number to a specified precision
without rounding. If an integer is presented then [trunc] will return the 
integer will be returned without any change.

Supplying [trunc] with a non-decimal value, e.g. string or hexadecimal, will
result in a trappable error

Example:

    puts [trunc 12345 4]  # No effect on integers. The integer is returned      
    puts [trunc $pi 0]    # Returns no decimal places (integer)                 
    puts [trunc $pi 2]    # Returns 2 decimal places                            
    puts [trunc hi 1]     # Raises an exception                                 

Results

    12345                                                                       
    3                                                                           
    3.14                                                                        
    <exception>                                                                 

See also: round, math, double

try ... catch (Flow Control)

    result [try {script1} catch {script2} ?-quiet?]

Evaluate script1. If an error is returned by script1 then evaluate script2
and in either case, return the result for further processing

[try..catch] handles runtime errors not syntax errors (incorrect arg 
counts)

Unlike [catch] fatal error messages are suppressed for a [try...catch] 
command because [try] will have its own error-handler in [catch]
The catch-handler must handle any error message output and take any
necessary corrective action such as closing file-handles or deleting
resources

The '-quiet' argument will suppress local error messages and the caller
must rely on using other methods such as $errorMsg to interpret failures

Be careful in that, due to the way the Tcl language operates, a return
value from a command which generates an exception may not be set since
the returning [set] command is not executed. Thus, where [some_command]
raises an exception ...

[try] does not guarantee all variables [catch] may need to address will be
set. Any variables referenced by the relevant [catch] clause must be
set before [try] is called even if set to null

Example:

    try {                                                                       
       set r [some_command]   # r will not be set on exception                  
    } catch {                                                                   
       puts "r was not set!"                                                    
    }                                                                           

You should set any variable first

    set r -9                   # Set r to a suitable default value              
    try {                                                                       
       set r [some_command]   # r will not be set on exception                  
    } catch {                                                                   
       puts "r was set to $r"                                                   
    }                                                                           

The recommended method of setting a return value from a command which
may fail a [try...catch] exception is to set the return value of [try]

Example:

    # Success case                                                              
    set p [try {                                                                
       / 22 7.0                                                                 
    } catch {                                                                   
       0                                                                        
    }]                                                                          
                                                                                
    assert $p {== $_ 3.142857142857143} -v -line #__LINE__    # PASS            
                                                                                
    # Failure case                                                              
    set q [try {                                                                
       / 22 0                                                                   
    } catch {                                                                   
       return -1        # Some arbitrary value which we want set                
    }]                                                                          
                                                                                
    assert $q {== $_ -1} -v -line #__LINE__ # PASS                              

[try] differs from standard Tcl in having a [catch] handler instead
of [finally]

The variable ::errorMsg may be set by the trapped command

Differences between [try] and 'catch' clauses in [try]:

    try    Does not display error messages
    catch  Displays error messages dependent on option echo off

Example:

    try {open fred.txt r} catch {puts "Can't open file"; exit}                  

Result:

    Can't open file                                                             

Example:

    try {                                                                       
       open nosuchfile r                                                        
    } catch {                                                                   
       puts "Caught: '$::errorMsg'"                                             
    }                                                                           

Result:

    Caught: 'open: Failed to open file 'nosuchfile' for mode r access'          

See also: catch, errorMsg

undef

Select from multiple choices:

See either: undef macro         Used by the Macro PreProcessor
            undef command       Used to undefine a proc

See also: proc, macro

undef command (Undefining Commands)

    undef proc|command ?proc|command...? ?-nocomplain?
    undef -glob filter ?-proc? ?-nocomplain?

Undefine a Tcl commands or procs. This is a non-standard command which 
would normally be implemented using the rather clumsy [rename command ""]

You cannot [undef] the active proc from within that proc or any parent
proc. Any attempt to undef any active proc in a call chain will raise an
exception. The -nocomplain argument can be used to ignore errors and
continue however, the current proc cannot delete itself.

The first example of [undef] undefined specific commands by name and has
no return. 

The second example of [undef] used -glob and returns a Tcl list of
commands or procedures undefined. This can be restricted to undefining
procedures only by adding the -proc argument.

You may undefine any command listed by the [commands] command. This allows 
build-in Tcl commands to be re-defined by custom ones of your own choice
You can redefine any aspect of the Tcl language and replace with your own
constructs since all constructs even [for], [while], [return] etc. are
defined as commands and subject to undefinition

All procedures defined with the [proc] command are also present in the
command list and may similarly be undefined and tested using [defined]

If a proc or command does not exist and -nocomplain is omitted then the
script will halt with an error

Aliases
-------
If an attempt is made to undefine a [proc] using [undef] and an alias
still exists then the proc will remain active until the last alias is 
undefined. When the last alias to a [proc] is removed the procedure
will be destroyed

[undef] is not to be confused with the #undef macro. See: undef macro

Example:

    proc foo {} {puts Hello}                                                    
                                                                                
    if {[defined bar]} {                                                        
       undef bar                                                                
       alias bar foo                                                            
    }                                                                           
                                                                                
    bar                                                                         

Result:

    Hello                                                                       

Example:

    puts [undef -glob * -proc]                                                  

Result:

    bar foo moo {my proc}                                                       

See also: is_active, rename, commands, proc, procs, undef macro

proc (Tcl Procedures)
 
    proc procname { arg ?{arg default-value}? ?args?}... {code}

Define a Tcl procedure. The first code bracket MUST be on the same
line as the proc declaration. A [proc] need not have an explicit [return]
as it will return the value of the last operation or expression executed

Procedures are created in global (static) scope and all procs (even if
definitions are nested within other procs) remain visible in global scope
until they are removed using [undef]

There is a small performance cost to [proc] definition due to the fixup
time. You should avoid using [proc] definitions within loops which need
high performance.

A defined [proc] is called when its name is encountered in a script, 
either outside of a string or enclosed in square brackets. 
e.g. foo, [foo] or using call procname.

Nested [proc] definitions are supported

     proc foo {x} {expr $x-1}                                                   

           ^   ^   ^                                                            
           |   |   |                                                            
   proc name   |   body                                                         
               |                                                                
    list of argument names. wrapped in braces                                   

It is recommended that the argument values are wrapped in curly braces
Multiple arguments are all enclosed in a single set of outer braces
A proc may be defined entirely on a single line, but is more usually defined
over multiple lines:

Example:

           proc name                                                            
           |   argument                                                         
           |   |    body                                                        
           |   |    |                                                           
           v   v    v                                                           

    proc cube {x} {                                                             
        set r [* $x $x]                                                         
        * $r $x                                                                 
    }                                                                           

Optional+default arguments may be specified by braced pairs as follows:

              Argument 1                                                        
              |  Argument 2                                                     
              |  | Default value 2                                              
              |  | |    Argument 3                                              
              |  | |    | Default value 3                                       
              |  | |    | |                                                     
              v  v v    v v                                                     

    proc foo {x {y 23} {z skidoo}} {                                            
        puts "proc foo: x=$x y=$y z=$z"                                         
    }                                                                           

Here, proc [foo] is described with mandatory argument 'x' and optional 
arguments 'y' and 'z'. Each of these two optional arguments has a default 
value set. Those default values being, '23' and 'skidoo' respectively.

NULL default arguments are specified by empty double quotes or braces.
e.g.:

                     Argument 1                                                 
                     |     Argument 2                                           
                     |     |    Default value 2                                 
                     |     |    |    Argument 3                                 
                     |     |    |    |    Default value 3                       
                     |     |    |    |    |                                     
                     v     v    v    v    v                                     

    proc http_login {node {user ""} {pass ""} } { ...                           
    proc http_login {node {user {}} {pass {}} } { ...                           

Procedure arguments must be syntactically valid if provided as unquoted 
strings. If unquoted strings must be passed to a proc, then ensure they
are wrapped in double quotes to prevent arguments being interpreted as 
commands:

Example:

    # 1st arg--+  +---------- 2nd arg                                           
    #          |  |                                                             
    proc foo {arg *} {...   # Wrong (assumed to be command operator [*])        
    proc foo {arg "*"} {... # Right, but useless (string const, not var)        
    proc foo {{arg *} {...  # Possibly what was intended (default for arg)      
    #           |  |                                                            
    #           +--+--------- Argument 'arg' with default value of '*'          

Nested procs are permitted and the [upvar] command will reference both
string and array variables in the parent or global context and set the
parent variable accordingly. Note that all procs are declared and available
in global scope (level 0)

Note that the syntax of a proc is not checked until the proc is first
called, thus procs should be tested independently for errors and debugged 
using a separate test file which ensures they are called

Procedure Names
---------------
Procedure names should be alpha or alphanumeric and not be numeric values, 
this is to avoid a conflict with the Macro Preprocessor expression optimiser

Example:

    proc 123 {args} {...    # Wrong. Can't be called other than by [call]       
    proc 3.0 {args} {...    # Wrong. Can't be called other than by [call]       
    proc a12 {args} {...    # Correct. Alpha name prefix                        
    proc abc {args} {...    # Correct. Alpha name prefix                        

Procedure Definition
--------------------
Ticol procs are stored as source code snippets which are not translated in
terms of final '\' escapes until evaluated. A proc may be inspected
using [info args procname] and [info body procname] which shows the proc
code unescaped. If you call another command with the result of [info body]
then backslash escapes will be translated before use. An active proc's own
name can be retrieved using [info procname]

It is much more efficient to define all procs in advance rather than 
defining during the remainder of script execution, within loops etc.

A recursion depth limit of 5000 is imposed on [proc] recursion which is
configurable via ticol.ini.

Example Test:

    # Should be: first 23 skidoo                                                
    foo first                                                                   
    # Should be: second 2 skidoo                                                
    foo second 2                                                                
    # Should be: third 3 three                                                  
    foo third 3 three                                                           

Result:

    proc foo: x=first y=23 z=skidoo                                             
    proc foo: x=second y=2 z=skidoo                                             
    proc foo: x=third y=3 z=three                                               

A valid procedure name may also be one or more spaces...

Example:

    proc { } {} {puts "Foo!"}   # Define procname " "                           
    [{ }]                       # Call using braces                             
    [" "]                       # Call using dquotes                            

Results:

    Foo!                                                                        
    Foo!                                                                        

Example Nested Proc: (where each new [proc] calls a child proc)

    proc level_p {} {                                                           
       puts "in level_p ([info level])"                                         
       proc level_q {} {                                                        
          puts "in level_q ([info level])"                                      
          proc level_r {} {                                                     
             puts "In level_r ([info level])"                                   
          }                                                                     
          level_r                                                               
       }                                                                        
       level_q                                                                  
    }                                                                           

Standard variables can be 'promoted' or 'cast' to array type within a
procedure using a global reference:

Example:

   set q {}                                  # Declare a non-array global       
   proc array_promote {} {                                                      
      puts "Enter array promote. Setting global var ::q as array"               
      set ::q(1) "Hello from array_promote" # Set global q as array element     
      set ::q(2) "Two"                                                          
      puts "::q(1) is '$::q(1)'"                                                
   }                                                                            
   array_promote                                                                
   puts "Test var 'q' in global scope: \$q(1) is '$q(1)'"                       

Result:

   Test in global scope: $q(1) is 'Hello from array_promote'

Variable Length Arguments using 'args'
--------------------------------------
The standard Tcl 'args' variable argument is optionally defined as the last
argument of any proc.

This is a special, trailing argument variable which will receive either
none or all of any excess arguments passed to a proc when it is called.
If args is not specified as the final argument of a proc then any excess
arguments supplied when the proc is called will be ignored

Example:

    proc demo {first {second "none"} args} {                                    
       textcolor darkyellow                                                     
       puts "Enter \[demo\]"                                                    
       puts "first = $first"                                                    
       puts "second = $second"                                                  
       puts "args = $args"                                                      
       textcolor                                                                
       newline                                                                  
    }                                                                           
                                                                                
    demo one                                                                    
    demo one two                                                                
    demo one two three "four five"                                              

Results

    first = one                                                                 
    second = "none"                                                             
    args = ""                                                                   
                                                                                
    first = one                                                                 
    second = two                                                                
    args = ""                                                                   
                                                                                
    first = one                                                                 
    second = two                                                                
    args = three {four five}                                                    

See: pass by name, pass by value

Calling Procs from [eval]
-------------------------
Any proc may be called from [eval] regardless of argument count
without specifying arguments. In such case, null or empty arguments
will be supplied by default to the proc.

Example:

    proc foo {x y} {                                                            
       puts "x is '$x' x is '$y'"                                               
    }                                                                           
    eval foo                                                                    

Result:

    x is '' x is ''                                                             

See also, proc args, args, info args, info body, info procname, unknown,
          varname, static, undef, defined, foreach, for, while, if

unknown (proc)
 
    proc unknown {arguments} {body}

If the [autoexec] option is set to OFF using [option autoexec off] and a
proc called 'unknown' is defined then this proc will be called when a 
given command is unrecognised. 

Enabling [autoexec off] will prevent the CLI from acting as a command shell

The autoexec option is Ticol specific and not standard Tcl

A useful example is one which uses unknown to redefine and extend the Tcl
language...

Example:    (multiple arguments must be quoted or braced)

- - - - - - - - - - - - - - - - - - - - - - - -  - - - - - -                    
    user@host> proc unknown args {return [expr $args]}                          
    user@host> autoexec off          # Required                                 
    user@host> option echo on        # Required                                 
                                                                                
    user@host> "1 + 2"                                                          
    3                                                                           
    user@host> pow(2,20)                                                        
    1048576.000000000000000                                                     
- - - - - - - - - - - - - - - - - - - - - - - -  - - - - - -                    

Example:  (Save the following as unknown.tcl and run it)

- - - - - - - - - - - - - - - - - - - - - - - -  - - - - - -                    
    proc unknown {x y} {                                                        
        puts "In process unknown: $x $y"                                        
    }                                                                           
                                                                                
    autoexec off                                                                
    puts "Running unknown.tcl"                                                  
                                                                                
    foople woople                                                               
- - - - - - - - - - - - - - - - - - - - - - - -  - - - - - -                    

                                                                                
    ticol unknown.tcl                                                           

Result:

    Running unknown.tcl                                                         
    In process unknown: woople                                                  

Example: (One line calculator)

    option autoexec off; option echo on; undef unknown;proc unknown {args} {\   
    textcolor magenta;\                                                         
    puts "Evaluating: '$args'\r\n";\                                            
    textcolor;expr "$args"}                                                     

See:
    http://wiki.tcl.tk/795

See also: autoexec, proc

unlink
 
    integer [unlink filespec ?-verbose? ?-nocomplain?]

Delete a single or multiple files without prompting *.* is banned
anywhere within a filespec

If -verbose is used then messages are shown
If -nocomplain is used then deletion of multiple files will continue
on any errors. If -nocomplain is omitted, an error will be
returned on failing to delete a file. Unless you want your script to halt
if a file is not found then -nocomplain is recommended.

A count of the number of successfully-deleted files is returned
Files which are open or locked by another process cannot be deleted

Example:

    set x [unlink *.tmp]                                                        
    puts "Deleted $x file(s)"                                                   

Result:

    Deleted 23 file(s)                                                          

See also: file, delete, rename

unset
 
    bool [unset var ?var...? ?-nocomplain? ?-const?]
    bool [unset mask ?mask ...? -glob ?-nocomplain? ?-nocase? ?-const?]

Undefine (delete) one or more variables. The -glob argument may be used
with one or more glob search 'masks'. -const is required to delete a
constant. You may also specify -nocase when using -glob.

Use [array unset] to delete entire arrays

Beware of calling [unset -glob] where it may encounter a const variable
and you are not supplying the -const argument. In such cases you must 
also add -nocomplain so that an error is not raised when a const is
encountered

Const variables are not intended to be assigned-to but deletion of a const
and subsequent reuse is possible using -const and also with a glob wildcard
using both -const and -glob arguments together

Example:

    unset argc -const -nocomplain                                               
    unset argv -const -nocomplain                                               

The variable argument(s) may be a string, list, struct, or array element

Example:

    set s Hello                                                                 
    unset s                                                                     
                                                                                
    array set a {                                                               
       1 one                                                                    
       2 two                                                                    
    }                                                                           
                                                                                
    unset a(1)                                                                  

Where a struct is deleted this will also delete all member variables. 
Unsetting an array will delete all elements of the array

Where an array is unset this will destroy the entire array
Where an array element is unset this will remove only that element
Removing all of the elements of an array does not remove the array itself,
you should call [unset arrayname]  or [array unset arrayname] to remove

Global variables (including arrays) should be referenced with a "::" prefix

    e.g.   unset ::globalvar 
    e.g.   unset ::arrayname(1)

The -nocomplain argument will suppress warnings where a variable does not
exist. This may be useful where the script is called (using [eval]) from
another script which may or may not have a variable set

Example:

    # Start of script (chained from another script which may have q set)        
    unset q -nocomplain                                                         
    set q Hello                                                                 
    puts $q                                                                     

A range of single-letter variables can be quickly cleared as follows:

    unset {*}[split [range a-z]] -nocomplain                                    

Which expands to 

    unset a b c d e f g h i j k l m n o p q r s t u v w x y z -nocomplain       

Example:

    unset tcl_version -const  # Delete a const                                  

Glob searching
--------------
Glob deletion of variables may be slow and inefficient if you have a lot of
variables declared. For glob searching the -glob argument is required.
An error will be raised if no matching variables are found unless the
-nocomplain argument is specified

The following wildcards are valid

    *       Any group of characters or all matches
    ?       Any single characters

Example:

    unset * -glob               # Unset all non-const variables                 
    unset * -glob -nocomplain   # Unset all non-const vars without complain     
    unset a* -glob              # Unset all vars starting with 'a'              
    unset ?h?? -glob            # Unset that, this, them etc. (4 letters)       
    unset a* b* c* -glob        # Unset multiple vars starting with a, b, c     
    unset * -glob -const        # Unset all constants                           

See also: clear

unwrap (proc)

    string [unwrap list]

Remove leading+trailing whitespace and then remove x1 level of braces
from a list. 

[unwrap] can be replaced with a proc which offers more comprehensive 
list-unwrapping features (see below)

[unwrap] as a procedure
-----------------------
Below is a simple proc which unwraps a nested list which is defined with
XML-style nesting. It is implemented to display the contents of each root
level but can be adapted to fill database entries, arrays etc.

Script
------
proc [ldepth] must be defined: See: ldepth

    undef unwrap             # Undefine the built-in [unwrap]                   
    option expression off    # If statement uses [eval]                         
    proc unwrap {l} {                                                           
       set s {}                                                                 
       foreach x $l {                                                           
          if {> [ldepth $l] 2} {                                                
             unwrap $x                                                          
          } else {                                                              
             append s "$x\t"                                                    
          }                                                                     
       }                                                                        
       if {<= [ldepth $l] 2} {                                                  
          set s [string trim $s "\t "]                                          
          puts $s                                                               
       }                                                                        
    }                                                                           
                                                                                
    undef unwrap             # Undefine the built-in [unwrap]                   
    option expression on     # If statement uses [expr]                         
                                                                                
    proc unwrap {l} {                                                           
       set s {}                                                                 
       foreach x $l {                                                           
          if {[ldepth $l] > 2} {                                                
             unwrap $x                                                          
          } else {                                                              
             append s "$x\t"                                                    
          }                                                                     
       }                                                                        
       if {[ldepth $l] <= 2} {                                                  
          set s [string trim $s "\t "]                                          
          puts $s                                                               
       }                                                                        
    }                                                                           

Example:

    set l "{catalog {cd {{title {Empire Burlesque}} {artist {Bob Dylan}}\       
    {Company Columbia} {Price 10.90} {year 1985} }}}"                           
    unwrap $l                                                                   

Results:

    catalog                                                                     
    cd                                                                          
    title   Empire Burlesque                                                    
    artist  Bob Dylan                                                           
    Company Columbia                                                            
    Price   10.90                                                               
    year    1985                                                                

See also: list processing, list, ldepth, append

uplevel

    uplevel command 
    uplevel ?level? command ?command...?
    uplevel ?@level? command ?command...?

Run a command in a higher call-frame level. By default 'level' is an
integer representing the number of relative levels above the current one
Each nested [proc] call increases the level by 1. [uplevel] has no return

[uplevel] joins each of its arguments together with spaces after trimming
leading and trailing white-space from each

If more than one command is given then a level value must be given (even if
0 for the current level)

NOTE Ticol uses @ instead of # as a level prefix to reserve the use of #
for embedded comment preprocessing

If level value is omitted then 1 will be assumed and the level immediate to 
the current one will be selected

If 'level' is prefixed by an @ sign then the reference will be an absolute 
level rather than a relative one. Standard Tcl uses the # symbol. The most
common use for '@' will be to refer to the root (global) level @0

Nested Quotes and Uplevel
-------------------------
Occasionally, a need can arise to wrap an uplevel argument in double-quotes
which is within a quoted string already. Nested quotes are not allowed.
However, you may use [chr] to insert quote characters as follows:

In the example below, it is vital to wrap the call to [is_array] in
quotes as the variable 'q' may contain spaces which could lead to an
erroneous return. Here we use [chr 34]. Note that [chr] here may not
be wrapped in braces or this will block the command call.

    if {[uplevel @0 "is_array [chr 34]$q[chr 34]"]} {                           
        ...                                                                     

Alternately, a quote character may be defined as a var or const

    const QUOTE [chr 34]                                                        
    if {[uplevel @0 "is_array $::QUOTE$q($i)$::QUOTE"]} {                       
       ...                                                                      

Local Variables and Uplevel
---------------------------
Passing a combined proc-local variable to execute in an uplevel should be
done by wrapping the whole argument in double-quotes rather than braces
If braces are used then the variable cannot be resolved locally before 
evaluating at the upper level. Thus

    proc foo string {                                                           
       uplevel @0 {eval $string}  # WRONG Can't resolve braced local var        
       ...                                                                      
                                                                                
    proc foo string {                                                           
       uplevel @0 "eval $string"  # CORRECT Can resolve local before call       
       ...                                                                      

Uplevel Example:

    proc clearlocals {level} {                                                  
       set varlist [uplevel $level {info locals}]                               
                                                                                
       foreach {v} $varlist {                                                   
          set command "is_const $v"                                             
          if {[! [uplevel $level $command]]} {                                  
             set command "unset $v -nocomplain"                                 
             if {[uplevel $level $command]} {                                   
                textcolor green                                                 
                puts "* unset $v successfully"                                  
                textcolor                                                       
             }                                                                  
          }                                                                     
       }                                                                        
    }                                                                           
                                                                                
    set a Hello                                                                 
    set b world                                                                 
    set c $qbf                                                                  
    set d "Dee"                                                                 
                                                                                
    clearlocals @0      # Absolute level of 0 (The global root level 0)         
    #clearlocals @1     # Absolute level of 1 (The current proc level 1)        
    #clearlocals 0      # Relative level of 0 (The current proc level 1)        
    #clearlocals 1      # Relative level of 1 (Parent of the current proc)      
                                                                                
    newline                                                                     
    puts "Locals: '[info locals]'"                                              

See also: upvar, set, at_exit, global

upvar
 
    upvar ??@?level? uplevelvar ?localvar? ?-nocomplain?

Create a link to a variable in a parent scope and optionally refer to a
named instance in local scope. Ticol uses the '@' rather than '#' 
character to specify an optional target level.

The name of the referred-to variable and local alias variable may be
identical.[upvar] has no return. [upvar] is also required when passing a
proc variable by name rather than by value.

The variable named by 'uplevelvar' need not exist at the time of the call, 
it will be created as a plain variable in the specified parent scope.

      +------> [parent var 'x']                                                 
      |             |                                                           
      ^             v                                                           
      |        proc(x) {                                                        
      |                                                                         
      +------<--- [local upvar 'x']                                             
                                                                                
               }                                                                

To refer to a variable called 'a' in global scope from within a [proc] as
'a', use either:

    upvar a a                                                                   
    upvar a                                                                     

[upvar] will search parent contexts until the first match to the given
variable name is found. If a match is found a new variable will be created
with the given new name but with the contents of the located variable

If a local variable name is not given then an alias with the same name as
the parent variable will be created automatically

The level value is optional and if omitted then 1 will be assumed. If level
is prefixed by an @ sign then the reference will be an absolute level
rather than a relative one. Tcl uses the # symbol. Ticol uses @ to release
# for embedded comments via preprocessing

Since the [at_exit] command can take no parameter arguments, upvar may be 
required for [at_exit] to access variables in other scopes

Example:

    set a(1) Hello                                                              
    set q a(1)                                                                  
    foo q            # Call the proc by name with q as an alias for a(1)        

It is an error (and unnecessary) to attempt to call [upvar] for a variable
with a locally-declared '::' global scope prefix. e.g. [upvar ::true]

Example:

  proc myproc {} {                                                              
     upvar a b;      # Reference a as local variable b                          
     puts "upvar of a is b->$b"                                                 
  }                                                                             
                                                                                
  set a 123                                                                     
  myproc                                                                        

Result:

  upvar of a is b->123                                                          

Example:

  proc myproc {} {                                                              
     upvar a         # Ticol supports lazy shorthand for [upvar a a]            
     puts "upvar of a is $a"                                                    
  }                                                                             
  set a 123                                                                     
  myproc                                                                        

Result:

  upvar of a is 123                                                             

Example:

  set a {}                                                                      
  proc myproc {} {                                                              
     upvar a         # Ticol supports lazy shorthand for [upvar a a]            
     set a(1) "One"                                                             
     puts "local a is $a(1)"                                                    
  }                                                                             
  myproc                                                                        
  puts "global a is $a(1)"                                                      

Result:

  global a is One                                                               

Passing Variables by Name
-------------------------

                      # A bad example!                                          
                      # Error: upvar: 'x' already exists at level 1             
    proc foo {x} {    # This uses the equivalent of [upvar x x]                 
       upvar $x       # Won't work because new local var 'x' conflicts          
       set $x 23      # Assign                                                  
    }                 # already exists as proc variable via proc                
    set x 0           # body declaration                                        
    foo x             # Call proc [foo]                                         
    puts $x           # Wont' print out 23                                      

                                                                                
                      # Avoiding name clashes...                                
    proc foo {_x} {   # Note that we must dereference the arg var               
       upvar $_x x    # _x avoids conflict with variable name x                 
       set x 23       # Assign to local (will update parent)                    
    }                 # as new variable 'x' does not already exist              
    set x 0           # via proc body declaration. We could also                
    foo x             # name the var 'foo' or 'q' etc.                          
    puts $x           # Will print out 23                                       

                                                                                
    # Variable update cascade back to parent from nested proc calls             
    proc a {x} {                                                                
        upvar $x                                                                
        puts "Enter proc \[a\] x is $x -> $$x"                                  
        ++ $x                                                                   
        proc b {x} {                                                            
            upvar $x                                                            
            puts "Enter proc \[b\] with arg x as $x -> $$x"                     
            set $x 23                                                           
        }                                                                       
        b $x                                                                    
    }                                                                           
                                                                                
    set q 0                                                                     
    textcolor cyan                                                              
    puts "Starting level 0 'q' is '$q'"                                         
    textcolor                                                                   
                                                                                
    a q        # Call the nested proc sequence with variable 'q' "by name"      
                                                                                
    textcolor cyan                                                              
    puts "Resultant level 0 'q' is now $q"                                      
    textcolor                                                                   

To pass array elements or arrays see: passing arrays

To pass structs (or a pseudo-class) to a proc or sub, see the help topic
'upvar with structs'

See: static, uplevel, global, vars, set, at_exit, upvar and structs,
     upvar and arrays, passing variables by name

Passing Arrays to Procedures Using upvar

[upvar] can create a reference for an array or array element and pass
by name.into a [proc] or [sub] while subsequently processing either the
whole array or the element within the [proc] or [sub]

Example:

Passing an array by name using complex dereferencing

    proc foo {x} {                                                              
        upvar $q                                                                
        puts "Type is: [type $q]"                                               
        puts "${${q}(0)}"         # Complex dereferencing                       
    }                                                                           
    set a(0) Hello                                                              
    foo a                                                                       

Result:

    Type is array                                                               
    Hello                                                                       

Example:

Passing an array by name simpler dereferencing and a local alias

    proc foo {q} {                                                              
        upvar $q x                                                              
        puts "Type is: [type x]"                                                
        puts "$x(0)"              # Simple dereferencing using an alias         
    }                                                                           
    set a(0) Hello                                                              
    foo a                                                                       

Result:

    Type is array                                                               
    Hello                                                                       

Example:

Passing an array element by name simpler dereferencing and a local alias
This would also allow loop iteration

    proc foo {q} {                                                              
        upvar [varname $q] x      # Create a local alias to the array           
        puts [array size x]       # Show the array size                         
        puts "$x(0)"              # Simple dereferencing using an alias         
    }                                                                           
    set a(0) Hello                # Set an array element in root                
    foo a(0)                      # Pass an array element to [foo]              

Result:

    1                                                                           
    Type is array                                                               
    Hello                                                                       

See also: arrays, upvar, upvar and structs

Using [upvar] with Structs

Ticol structs emulate "C" style structs but are not identical in usage

A Ticol struct is a single binary memory block, onto which map one or more
standard Tcl variables. Due to this, the parent struct variable is
disjoint and separate from the field (member) variables although they will
all reside within the same scope. 

When [upvar] is used to gain access to a struct it maps onto the struct
parent variable, not the separate field member variables. Most commands
will resolve the mapping between the referred struct and its members

The parent struct variable has a relationship with named members. These
names are fixed and can't be aliased via [upvar]

It may be necessary to separately [upvar] struct member variables

Additionally, the reference [upvar] variable argument passed via [proc]
has to be resolved using a complex/braced dereference. Because of this
additional indirection, upvar-ed structs are more complex to address

Example:
-------

    struct s {a 10}                                                             
                                                                                
    proc foo {x} {                                                              
       upvar $x               # Dereferencing, x gives the struct name          
       puts "-> ${x}.a"       # This requires x to be braced                    
       puts "[set ${x}.a]"    # This requires x to be braced                    
       struct set ${x}.a Bye  # This requires x to be braced                    
    }                                                                           
                                                                                
    struct set s.a Hello                                                        
    foo s                     # Pass the struct via foo                         
    puts "s.a:'$s.a'"         # Will display the changed value                  

Results:
-------

    Hello                                                                       
    Hello                                                                       
    Bye                                                                         

Example:
-------
This demonstrates the need to separately [upvar] struct member field
variables as well as the need to use [struct set] to assign to struct 
member fields. It also reminds that the [++] like operators work only
on simple/scalar variables

Double-dereferencing is required to address struct member variables
as per the example below

    proc process_rgb {st} {                                                     
        upvar $st            # This won't enable access to struct members       
        upvar ${st}.r        # x.r (red)                                        
        upvar ${st}.g        # x.g (green)                                      
        upvar ${st}.b        # x.b (blue)                                       
                                                                                
        # Must use [struct set] to assign. Can't use [++] with complex vars     
        struct set ${st}.r [+ ${${st}.r} 1]                                     
        struct set ${st}.g [+ ${${st}.g} 1]                                     
        struct set ${st}.b [+ ${${st}.b} 1]                                     
    }                                                                           
    struct create template { # Define a template instance                       
        r 5                                                                     
        g 5                                                                     
        b 5                                                                     
    }                                                                           
    new template x           # Instantiate x (members are initialised to 0)     
    new template y           # Instantiate y                                    
    process_rgb x            # Call with x                                      
    process_rgb y            # Call with y                                      

Example
-------
From v1.27 release the following is recommended but the more complex version
(above) will still work

    proc process_rgb {st} {                                                     
        upvar $st q           # This won't enable access to struct members      
        upvar q.r             # x.r (red)                                       
        upvar q.g             # x.g (green)                                     
        upvar q.b             # x.b (blue)                                      
                                                                                
        # Must use [struct set] to assign. Can't use [++] with complex vars     
        struct set q.r [+ $q.r 1]                                               
        struct set q.g [+ $q.g 1]                                               
        struct set q.b [+ $q.b 1]                                               
    }                                                                           
    struct create template { # Define a template instance                       
        r 5                                                                     
        g 5                                                                     
        b 5                                                                     
    }                                                                           
    new template x           # Instantiate x (members are initialised to 0)     
    new template y           # Instantiate y                                    
    process_rgb x            # Call with x                                      
    process_rgb y            # Call with y                                      

See also: upvar, static, struct, struct set, proc

Useful Commands

The following commands have been added to make Ticol development easier

array walk   # Walk an array's hashtable                                        
at_exit      # Call useful debug, trace or cleanup routines on script exit      
commands     # Returns a filterable list of defined commands                    
debug        # Enable console debugging for debug versions of Ticol             
dump         # Dump a variable, giving useful debug information                 
find         # Find help keywords                                               
functions    # Returns a filterable list of expr functions                      
help         # This help file                                                   
mem_used     # Returns the current memory used by Ticol                         
option       # Shows the state of optional configuration settings               
spawn        # Spawn an external program and capture output to a variable       
time         # Time the execution of a block of code                            
trace        # Enable debug tracing                                             
walk         # Walks the variable table showing addresses, type and content     
vars         # Returns a filterable list of variables by level                  
#__LINE__    # Macro to aid tracing line numbers in the original source         

See also: debugging

variable
 
    string [variable var ?value?]

In standard Tcl this creates and initialises a namespace variable.
Ticol does not implement namespaces, it emulates them

Declared as an alias for [set]. Declared for Tcl compatibility
Variables declared in [proc] scope using set are local to that proc and
in the proc namespace

If value is absent then the contents of 'var' are returned instead

Example:

    variable s Hello                                                            
    puts $s                                                                     

Result:

    Hello                                                                       

See also: variables, set, vars, upvar, static, dim, array

Variables
 
Tcl automatically detects variable types. There is no need for type-
declaration when declaring variables. Variable declaration is similar to
PHP with variables starting with a dollar sign and having any alphanumeric
name

Standard Tcl allows any character to be used in a variable name. Ticol
Tcl uses a more restrictive character set. Valid variable name characters
include: A-Z a-z 0-9 _ . : () & <tab> and <space>

  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.:& \t"

Curved brackets define array variables thus varname(subscript)
Spaces are significant within subscripts so subscripts which contain
whitespace should be quoted or braced

Ticol also allows an underscore "_" and the dot character "." in names
and will automatically create parent->field reference names for struct
member variables which include a dot character (See: structs)

A variable name may also contain spaces. Even comprised entirely of
spaces. Unlike standard Tcl, a Ticol variable cannot be an empty string

A variable name may be any length

Examples:

    $a_ticol_variable                                                           
    $also.a.ticol.variable                                                      
    set [string repeat x 10240] Hello                                           
    set $qbf Hello                                                              
    puts $$qbf                                                                  

Variable boundaries can be protected from adjacent recognised characters by
curly-braces around the name. 

Examples:

    set a "ll"; puts "He${a}o => Hello"                                         
    puts ${this_is_a_ticol_variable_reference}                                  

Scoping and Namespaces

Namespaces are emulated by extended variable names only. The global
namespace is implemented and is addressed by prefixing a variable name
with double colon characters "::"

Examples:

    ::argv(1)                                                                   
    ::env(path)                                                                 

Although essentially typeless, the following variable types are
interpreted:

    integer     A 64-bit integer value stored as a string                       
    double      A floating-point value stored as a string                       
    hex         A hexadecimal literal preceded by 0x stored as a string         
    octal       An octal value preceded by 0o stored as a string                
    binary      A binary value preceded by 0b stored as a string                
    array       Standard Tcl hashtable arrays (one true dimension only)         
    struct      Emulation of C-like binary structs for use with calldll_*       

Additionally, bit/byte field-widths may be set when using setb or ofaddressb
in order to exchange data using structs via the Windows API or other DLLs

See also: namespace, type, arrays, struct, vars. calldll, setb, ofaddressb

Variant Handling Plugin

To load, use:    lib ticol_variant ?-verbose? ?-nocomplain?

It is not possible to directly declare a variant. However, to create a
variant in order to interface with an external DLL via [calldll] a 
[struct] could be created or a block of memory allocated of the correct
size could be passed.

The following commands are exported (and are documented separately)

    [varray <subcommands>]  # Main command
    [vsplit]                # Utility command
    [dumpvariant]           # Introspection command

See: varray, vsplit, dumpvariant, struct, malloc

Variant Array Functions (Plugin)
 
Contained in lib plugin: ticol_variant,dll

To load, use:    lib ticol_variant ?-verbose? ?-nocomplain?

A rudimentary mechanism to manipulate Windows VARIANT objects
[varray] is provided to help interface with external DLLs only
For internal use, use standard arrays or the ticol_carray plugin

handle  [varray create size ?lbound?]     Create a new 1D variant array
                                          Lower bound can be set
bool    [varray set handle element value] Assign an item to array element
integer [varray size handle ?dimension?]  Element size of a variant array
string  [varray get handle index ?index?] Get element from 1D or 2D array
integer [varray lbound handle]            Return lower bounds of an array
integer [varray ubound handle]            Return upper bounds of an array
list    [varray list handle]              Return variant array as a list
varray foreach handle var {script}        Iterate elements (in order)
                                          using variable 'var' and {script}
integer [varray unset handle]             Free the associated variable
                                          The caller is responsible for
                                          ensuring the variable is
                                          associated with a variant array

Variant arrays are returned from external DLL functions called by the 
calldll* interface. Such arrays are not garbage-collected automatically
and should be deleted using [varray unset] (if safe) and when no longer
required, in order avoid memory loss

    "Arrays start at 1"

Variant arrays can have any arbitrary lower bound although the default in
this library is 0. [varray set] sets at the actual bounded index rather than
starting at 0 so if the lower bound is 2 then [varray set] element 2 will 
set the first element and so on

One and two dimensional arrays are supported to various degrees

Variant array cleanup is normally performed by [varray unset] but arrays
which have not been unset will be cleaned up and deleted when the lib
is unloaded

Example:

    option expression off                                                       
    set v [vsplit $qbf]      # Split to Variant (SAFEARRAY) array               
    set elems [varray size v]                                                   
    puts "Array has $elems elements:\n"                                         
                                                                                
    for {set i 0} { < $i $elems } {incr i} {                                    
       puts "$i: [varray get v $i]"                                             
    }                                                                           
    varray unset v           # Must be deleted!                                 

Result

    Array has 9 elements:                                                       
                                                                                
    0: The                                                                      
    1: quick                                                                    
    2: brown                                                                    
    3: fox                                                                      
    4: jumps                                                                    
    5: over                                                                     
    6: the                                                                      
    7: lazy                                                                     
    8: dog                                                                      

Example:

    option expression off                                                       
    set v [vsplit $qbf]      # Split to Variant (SAFEARRAY) array               
    set i 0                                                                     
    varray foreach $v x {                                                       
       puts "$i: $x"                                                            
       ++ i                                                                     
    }                                                                           
    varray unset v                                                              

Result

    0: The                                                                      
    1: quick                                                                    
    2: brown                                                                    
    3: fox                                                                      
    4: jumps                                                                    
    5: over                                                                     
    6: the                                                                      
    7: lazy                                                                     
    8: dog                                                                      

Create Example:

    set handle [varray create 5 2]    # Lower bound is set to 2                 
    dumpvariant $handle                                                         
    set elems [varray size $handle]                                             
    varray set $q $lbound Hello                                                 
    varray set $q [+ $lbound 1] World                                           
    puts "Array has $elems elements:\n"                                         
    for {set i $lbound} { < $i [+ $lbound $elems] } {incr i} {                  
       puts "$i: [varray get $q $i]"                                            
    }                                                                           
    varray unset $handle                                                        

                                                                                
    DUMP: Variant 'q' at 36015216 (0x2258c70)                                   
    Type is 8204 (0x200c) BYVAL                                                 
    Variant is an array of type 0xc                                             
    Type is 12 byte Variant VT_VARIANT                                          
    Array has 5 elements:                                                       
    2: Hello                                                                    
    3: World                                                                    
    4:                                                                          
    5:                                                                          
    6:                                                                          

See each varray command for more details

See also: vsplit, calldll, dumpvariant

varname

    string [varname variable]

[varname] extracts the variable name prefix from an array variable where
that name is not known in advance but required, e.g. when passed via a
proc argument and we need to handle the root array name

Examples:

    puts [varname env(path)]                                                    
    proc foo x {                                                                
       puts [varname $x]     # x resolves to 'a(0)'                             
       puts [array size [varname $x]]                                           
    }                                                                           
    set a(0) Hello                                                              
    foo a(0)                                                                    

Results:

    env                                                                         
    a                                                                           
    1                                                                           

 See also: proc, upvar, array

varray size (Plugin)
 
Contained in lib plugin: ticol_variant.dll

To load, use:    lib ticol_variant ?-verbose? ?-nocomplain?

    integer [varray size variable|address ?dimension?]

Returns the array count for a 1D variant string array

The dimension argument is a base 1 value (1st is 1, 2nd is 2)
The default dimension is 1, which represents the first array bound

A 2d array would have a count for dimensions 1 and 2 where 1 would be the
rows and 2 the columns. vvarray lbound and vvarray ubound can be used to 
calculate the varray size value

This command currently supports a maximum of 2 dimensions

Example:

    # Split to Variant (SAFEARRAY) array                                        
    set v [vsplit $qbf]                                                         
    puts "Varray has [varray size v] element(s)"                                

Result:

    Varray has 9 elements:                                                      

See also: arrays, varray get, varray lbound, varray ubound, calldll

varray get (Plugin)

To load, use:    lib ticol_variant ?-verbose? ?-nocomplain?
 
    string [varray get address row ?column?]

Return a string element from a 1D or 2D Variant string array. Such arrays 
may be returned from DLLs such as SQLite queries.

Example:

    # .. code to set variant 'vr'                                               
    option expression off                                                       
    set lbound [varray lbound $vr]                                              
    set ubound [varray ubound vr]                                               
                                                                                
    for {set i $lbound} { < $i $ubound} {incr i} {                              
       puts "\t$i: [varray get vr $i]"                                          
    }                                                                           

See also: arrays, varray size, varray lbound, varray ubound, calldll, 
          dumpvariant

varray lbound (Plugin)

Contained in lib plugin: ticol_variant.dll
 
    integer [varray lbound address|variableName ?dimension?]

Return the lower bounds for a 1 or 2D variant array. 

The default dimension is 1
For 2d arrays specify 2

Example:

    # Split to Variant (SAFEARRAY) array                                        
    set v [vsplit $qbf]                                                         
    puts "Lower bound is [varray lbound v]"                                     
    puts "Upper bound is [varray ubound v]"                                     
    puts "Varray has [varray size v] element(s)"                                

Result:

    Lower bound is 0                                                            
    Upper bound is 8                                                            
    Varray has 9 elements:                                                      

See also: arrays, varray ubound, varray get, varray size, calldll

varray ubound (Plugin)

Contained in lib plugin: ticol_variant.dll

To load, use:    lib ticol_variant ?-verbose? ?-nocomplain?
 
    integer [varray ubound address|variableName ?dimension?]

Return the upper bounds for a 1 or 2D variant array. 

The default dimension is 1
For 2d arrays, specify 2 as the dimension value

Example:

    # Split to Variant (SAFEARRAY) array                                        
    set v [vsplit $qbf]                                                         
    puts "Lower bound is [varray lbound v]"                                     
    puts "Upper bound is [varray ubound v]"                                     
    puts "Varray has [varray size v] element(s)"                                

Result:

    Lower bound is 0                                                            
    Upper bound is 8                                                            
    Varray has 9 elements:                                                      

See also: arrays, varray lbound, varray get, varray size, calldll

vars
 
    vars ?filter? ?-const | -auto | -all? ?-level level? ?-list? ?-quiet? 

Non-interactive tabular display of registered variables
These are colour-coded according to auto-detected type

See: 'info vars' to return a Tcl list of variables by query

Options:

    filter          A glob filter pattern                                       
    -const          Show defined constants                                      
    -auto           Show normal (automatic) variables                           
    -all            Show all variables                                          
    -level N        Show vars at given level N                                  
    -list           Return a Tcl list of all varnames                           
                    (-auto, -const , all and -level may be used)                
    -q              Quiet mode (no text output)                                 

Tcl variables are traditionally strings but Ticol extends to binary structs
Filter is a partial prefix-string-match on the variable name

The '-level level' argument is valid and useful only from within a procedure

Example:

    vars a -const                                                               

Result:

    Defined variables                                                           
    -----------------                                                           
    string   argv0                ticol                                         
    integer  argc                 1                                             

Example:

    puts [vars -const -q]                                                       

Result:

    18                                                                          

See also: walk, info vars, info exists, dump, set, proc, upvar, uplevel,
          global, debugging

vfract

    string [vfract double ?rounding? ?-float?]

Converts a decimal remainder value into a vulgar fraction string
Any integer part is stripped and ignored. Sign is ignored
The maximum internal resolution is X/1000 ths

If the fraction cannot be converted then the original value is returned
On error or overflow, "" is returned

In order to ensure or force a return a rounding value can be specified
The default internal rounding is 4 decimal places

If -float is specified then both denominator and numerator are returned 
as x.0 floating point values thus making re-evaluation easier

Examples:

    puts [vfract .75]                                                           
    puts [vfract 0.343750000]                                                   
    puts [vfract [fraction $pi] 4]                                              
    puts [vfract .318]                                                          
    puts [vfract 1.5]                                                           
    puts [vfract 0.75 -float]                                                   

Results:

    3/4                                                                         
    11/32                                                                       
    177/1250                                                                    
    159/500                                                                     
    1.2                                                                         
    3.0/4/0                                                                     

See also: fpart, fraction, fract, round

vsplit (Plugin)

To load, use:    lib ticol_variant ?-verbose? ?-nocomplain?

    variant [vsplit string ?mask?]

Split a string according to mask characters and return Variant array
If not specified the mask character is a space

Example:

    set v [vsplit $qbf]                                                         
    puts $v                                                                     
    dumparray v                                                                 

Result:

    32539136                                                                    
    DUMP: Variant 'v' at 32539136 (0x1f08200)                                   
    Type is 8204 (0x200c) BYVAL                                                 
    Type is 4 byte float VT_R4                                                  

See also: split, join, varray, variant arrays, list

vwait (Requires threaded compile of Ticol)

    vwait variablename ?timeout-ms?
    vwait undefined-variable

[vwait] will block execution and wait until the variable named as its 
argument changes. [vwait] is of use only with the threaded compile of
Ticol and the event command [after]

If scripts are executed concurrently then conflicts may occur. [vwait]
allows for script synchronisation

Where a main script calls [after] events (threaded version of Ticol),
[vwait] should be called to suspend execution of the primary script 
until all [after] events which reference 'variablename' are completed

[vwait] should not follow [after 0 {code}] as {code} will be executed
immediately and before [vwait] can be evaluated. Thus [vwait] will
never exit

The [vwait] variable argument need not exist. In such case [vwait] will
wait permanently, whilst processing events. [vwait forever] is a common
Tcl idiom where 'forever' is used as a pseudo-variable

Possible Issues

[vwait] will suspend the main event loop so if there is a bug in the
called script then execution may never return to the CLI or to Windows

Examples
--------

Example:

    set wait 0                                                                  
    textcolor magenta                                                           
    puts "I know..."                                                            
    textcolor                                                                   
    after 1000 set wait 1                                                       
    vwait wait                    # Wait until 'wait' changes                   
    textcolor magenta                                                           
    puts "Tcl, but not Tk"                                                      
    textcolor                                                                   

Results:

    I know...                                                                   
    Tcl, but not Tk                                                             

Example:

    proc foo {} {                                                               
       puts "Hello"                                                             
       #for {set i 0} {< $i 10000} {++ i} {doevents}                            
    }                                                                           
    puts "Launching \[foo\] in 4 seconds"                                       
    after 4000 {set vret [foo]}                                                 
    vwait vret      # Wait until [foo] has launched and completed               
    puts "Done"                                                                 

Note:

    Be careful to brace after arguments and not use:                            
                                                                                
    after 4000 set vret [foo]                                                   
                                                                                
    as [foo] will be called before [after]                                      

Example:

    vwait forever    # 'forever' is a variable which does not exist             

Result:

    <loops forever>                                                             

See also: after

waitkey

    integer [waitkey ?timeout-ms? ?-flush?]

Wait for keyboard input or return on timeout if no keypress occurs
If no keyboard input occurs, 0 is returned. Otherwise the ASCII code of
the pressed key is returned

Example:

    set k [waitkey 4000]                                                        
    puts $k                                                                     

Result:

    0      # If no key was pressed within 4000 milliseconds                     
    13     # If ENTER was pressed within 4000 milliseconds                      

 See also: inkey, input, gets

walk

    walk ?count? ?-level value?

Walk is a debugging tool which will let you traverse or 'walk' the raw
Ticol variable table in the current call frame level, showing variable
addresses and other information

Walk shows all variables, distinguishing storage types but doesn't show 
information in as much detail as [vars] or allow filtering by name

Example: (From within proc at, say, level 2)

    walk 10 -level 0                                                            

Results:

    walk: Level 0                                                               
    Type  Address   Name                      Value                             
    ------------------------------------------------------------                
    auto  0x1de8f00 b                         'start'                           
    auto  0x1de8c10 a                         'x'                               
    auto  0x1de8ab0 sw                        '80'                              
    auto  0x1de8460 fn_name                   'testmean'                        
    auto  0x1de84d0 y                         '14'                              
    auto  0x1de81c0 errorCode                 '0'                               
    auto  0x1de8160 errorLine                 '0'                               
    auto  0x1de8100 errorMsg                  ''                                
    auto  0x1de7fa0 errorInfo                 ''                                
    const 0x1e27f20 exe_name                  'c:\tcl\ticol.exe'                

Example:

    if {! [defined bar]} {                                                      
       proc bar {} {                                                            
          set r "In proc bar"                                                   
          set set_level_[info level] [info level]                               
          walk 2                                                                
          mar                                                                   
       }                                                                        
    }                                                                           
                                                                                
    if {! [defined foo]} {                                                      
       proc foo {} {                                                            
          set q "In proc foo"                                                   
          set set_level_[info level] [info level]                               
          walk 2                                                                
          bar                                                                   
       }                                                                        
    }                                                                           
                                                                                
    walk 2                                                                      
    foo                                                                         

Results:

    walk: Level 0                                                               
    Type  Address   Name                      Value                             
    -------------------------------------------------------                     
    auto  0x2337940 errorCode                 '0'                               
    auto  0x23378e0 errorLine                 '0'                               
                                                                                
    walk: Level 1                                                               
    Type  Address   Name                      Value                             
    -------------------------------------------------------                     
    auto  0x233c670 set_level_1               '1'                               
    auto  0x233c270 q                         'In proc foo'                     
                                                                                
    walk: Level 2                                                               
    Type  Address   Name                      Value                             
    -------------------------------------------------------                     
    auto  0x233cd40 set_level_2               '2'                               
    auto  0x233c8b0 r                         'In proc bar'                     

See also: introspection, dump, array walk, vars, info, debugging

watch

    watch add varname|filter
    watch remove varname|filter
    watch on|off
    watch list
    watch trace glob-pattern ?-nocase? ?-value? ?-halt? ?-line N?

[watch] can add variable watches to the single-step breakpoint mode.
Unlimited watches may be enabled but since these are displayed to the
console more than two or three will become unmanageable

Variables need not be in scope at the time of calling [watch]

[watch trace] will trace a variable to the console along with any value
only if that variable exists in the current level context. Otherwise
no output is generated. Optionally, a line number can be specified
using '-line', case can be ignored using '-nocase' and a value can 
be glob-matched instead of matching by variable name using '-value'
The '-halt' argument can be used to halt the script when a match is
found. The matched value will be returned for use with [try/catch]
[watch on|off] has no effect on [watch trace]

Example:

    # Halt the script when variable value in current scope matches 6*           
    watch trace 6* -value -halt -line #__LINE__                                 

[watch] (without trace), is active only when running in single-step/
breakpoint mode after a [halt] instruction has been met. The watched value
is shown only after a [watch] command is encountered. The current status
of a variable is shown. If set then the value is shown

Requires the command:   ticol.exe filename /bp

or:                     option breakpoint on

[watch off|on] can set the watch state prior to entering the single-step 
debugger to avoid screen-clutter. [watch off] will disable any active
watches and [watch on] will (re-)enable them

Example:

    option expression on            # Enable flow-control [expr] syntax         
    option breakpoint on            # Enable the debugger                       
    watch add i                                                                 
    watch add result                                                            
    watch off                                                                   
    halt                            # Enter the debugger                        
                                                                                
    proc watch_test {} {                                                        
       for {set i 0} {$i < 20} {incr i} {                                       
          puts "Loop $i"                                                        
          if {== $i 10} {                                                       
             watch on               # Enable the watches                        
             halt                   # Enter the debugger                        
             set result $::qbf                                                  
             puts "Return result is $result"                                    
             return $result                                                     
          }                                                                     
       }                                                                        
    }                                                                           
    watch_test                                                                  

See also: halt, trace, option breakpoint, dump, walk, debugging

wchar_length
 
    integer [wchar_length variable]

[wchar_length] returns the length of a wide (unsigned char) string.

It accepts only a variable which has been set by [string to_wchar] or via
a DLL return as input. A string literal cannot be used.

For example code see: wchar_to_string

See also: string to_wchar, wchar_to_string

wchar_to_string
 
    string [wchar_to_string variable]

Convert a wide-character (possibly Unicode) variable returned by an
external DLL to ANSI for use with Ticol. Due to the absence of checking,
particular care should be taken when allocating buffers for string
conversion, e.g. not to confuse variable names with values (buflen with
$buflen).

Note that there may be loss of content text during translation

Example: (This uses the AutoIT AutoItX3 DLL)

                                            # Fake passing a Unicode string     
set buflen 4096                             # Set buffer length var to 4kb      
set buf [makestr [* $buflen 2]]             # Allocate an 8kb Unicode buffer    
puts "Getting clipboard as Unicode..."      # We will masquerade as Unicode     
call  [calldll AutoItX3.dll AU3_ClipGet [strptr buf]:4 $buflen:4]               
set result [wchar_to_string buf]            # Convert Unicode to ANSI           
puts "wchar_to_string result is: '$result'" # Display ANSI result in Ticol      

See also: string to_wchar, wchar_length, cast

while (Flow Control)

Syntax:

    while {condition} {code-block}

    while {condition} {
       code-block
    }

The following additional commands are available for use with [for]

   return            # Will exit the script when used at root-level             
   break             # Will be passed back to any enclosing structure           
   stop              # Halt operation, return to the CLI if loaded              
   exit              # Exit the script and return to Windows                    
   goto              # Where used with an enclosing [goto_block]                
   continue          # Resume execution at the head of the loop                 

Standard Tcl [while] loops are supported as well as [do..while] loops
A while loop is about twice as fast as an equivalent [for] loop due to 
additional syntax checks necessary for [for]

Examples:

    while 1 {puts [rnd 1 10] }                                                  
                                                                                
    while { 1 } {puts [rnd 1 10] }                                              
                                                                                
    while { 1 } {                                                               
       puts [rnd 1 10]                                                          
    }                                                                           

Note that you may not use this form. It is not syntactically-valid as
the CRLF is significant and must be "masked" by enclosing braces

    while {condition}                                                           
    {                                                                           
       code block                                                               
    }                                                                           

See also: do while, for, if, foreach, switch, flow control

win_error

    string [win_error errornumber]

Returns a formatted system error string for a given error number

Use [get_win_error] to retrieve the error number

Example:

    puts [win_error [get_win_error]]                                            
    puts [win_error 2]                                                          

Result:

    The operation completed successfully                                        
    The system cannot find the file specified                                   

See also: get_win_error, info

Ticol Workarounds
 
A number of issues may require workarounds

Problems with escaped quotes
----------------------------
This may occur with complex, nested strings or with scripts which generate
other scripts. Use [chr 34] instead

Example:

    puts "One embedded double quote ->[chr 34]<-"                               

Problems with escaped backslashes
---------------------------------
This may occur with complex, nested strings or with scripts which generate
other scripts. Use [chr 92] instead

Example:

    puts "[chr 92]t[chr 92]ricky"                                               

You may also consider escaping or double-escaping backslashes as appropriate,
either manually or using [escape]; and then using [unescape] to revert

Example:

    puts "->[unescape [escape "\t"]]<-"                                         

Result:

    ->      <-                                                                  

[upvar] and [uplevel] don't Support the # Character
---------------------------------------------------
This is by design since # is reserved by the Macro Preprocessor (MPP)
The # character is a hard comment character in Ticol Tcl

Use @ instead of # for [upvar] and [uplevel]

See also: cli, FAQ

wrap

    string [wrap string ?width? ?wrap-string? ?-wordwrap? ?-indent N?]

Format a string by wrapping using line feeds at a fixed length
The default width is 80. 

A 'wrap string' may be specified which is appended to each line
The default wrap string is "\n" (a newline)

Word-wrapping will be attempted if -wordwrap is used. If the word is too
large, it will be truncated and wrapped to the next line without 
hyphenation

An optional -indent value can be specified which will be applied after the
first line. This is commonly used in formatted files such as email headers,
or RLM licence files etc. The indent is subtracted from the line length

Example:

    puts [wrap $qbf 10]                                                         
    puts [wrap $qbf 15]                                                         

Result:

    The quick                                                                   
    brown fox                                                                   
    jumps over                                                                  
     the lazy                                                                   
    dog                                                                         
                                                                                
    The quick brown                                                             
     fox jumps over                                                             
     the lazy dog                                                               

Example:

    lib ticol_bignum                                                            
    puts [wrap [big fact 100] 40]                                               

Result:

    9332621544394415268169923885626670049071                                    
    5968264381621468592963895217599993229915                                    
    6089414639761565182862536979208272237582                                    
    51185210916864000000000000000000000000                                      

Example:

    lib ticol_bignum                                                            
    puts [wrap [big fact 100] 40 -indent 3]                                     

Result:

    9332621544394415268169923885626670049071                                    
       5968264381621468592963895217599993229                                    
       9156089414639761565182862536979208272                                    
       2375825118521091686400000000000000000                                    
       0000000                                                                  

Example:

    puts [wrap $qbf 11 -indent 3]                                               

Result:

    The quick b                                                                 
       rown fox ju                                                              
       mps over th                                                              
       e lazy dog                                                               

Example:

    puts [wrap $qbf 11 "\r\n->" -indent 1]                                      

Result:

    The quick b                                                                 
    -> rown fox ju                                                              
    -> mps over th                                                              
    -> e lazy dog                                                               

Example:

    puts "'[wrap $qbf 20 '[chr 13][chr 10]' -wordwrap]'"                        

Result:

    'The quick brown fox'                                                       
    ' jumps over the'                                                           
    ' lazy dog'                                                                 

Example:

puts "'[wrap $qbf[chr 32]$qbf 20 '[chr 13][chr 10]' -wordwrap -indent 3]'"      

Result:

    'The quick brown fox'                                                       
    '    jumps over the'                                                        
    '    lazy dog The'                                                          
    '    quick brown fox'                                                       
    '    jumps over the'                                                        
    '    lazy dog'                                                              

Example:

Truncated words

    puts [wrap $qbf 4 -wordwrap]                                                

Result:

    The                                                                         
    quic                                                                        
    k                                                                           
    brow                                                                        
    n                                                                           
    fox                                                                         
    jump                                                                        
    s                                                                           
    over                                                                        
     the                                                                        
     laz                                                                        
    y                                                                           
    dog                                                                         

Example:

Truncate with indent

    puts [wrap $qbf 4 -wordwrap -indent 2]                                      

Result:

The                                                                             
  qu                                                                            
  ic                                                                            
  k                                                                             
  br                                                                            
  ow                                                                            
  n                                                                             
  fo                                                                            
  x                                                                             
 ...                                                                            

See also: wrap example, puts, printf, format

wrap example script

This is an example script for the [wrap] command which reads prose text
from a text file and produces multi-columnar output with balanced columns
and word-wrap

    option expression off                                                       
                                                                                
    set filename test.txt                                                       
    set columns 3                                                               
    set height 300                                # 300 lines desired           
                                                                                
    readfile $filename t                          # Read to temp string t       
    set column_width [/ [- [- [screen_width] 1] $columns] $columns]             
                                                                                
    set t [string replace $t "\t" " "]            # Replace tabs                
    set t [wrap $t [- $column_width 1] -wordwrap] # Word-wrap the text          
    split $t "\r\n" -array a                      # Split lines to an array     
    set height [+ [/ [array size a] $columns] 1]  # Balance the columns         
    set row_max [min $height [array size a]]      # Guard row_max               
                                                                                
    dim row $row_max                              # Speed-up allocation         
    for {set i 0}  {< $i $row_max} {++ i} {       # Create blank array          
        set row($i) ""                            # We append to this           
    }                                                                           
                                                                                
    set quit $false                               # Set break flag              
    for {set j 0}  {< $j $columns} {++ j} {       # Iterate and create rows     
        for {set i 0}  {< $i $row_max} {++ i} {                                 
            set s [string repeat " " $column_width ]                            
            set k [+ [* $j $height] $i]                                         
            if {< $k [array size a]} {                                          
                setl s $a($k)                     #                             
                append row($i) " $s"              # Append columns to rows      
            } else {                                                            
                set quit $true                    # Until we reach the max      
                break                                                           
            }                                                                   
        }                                                                       
        if {$quit} {                              # Inner break cannot          
            break                                 # quit the outer for          
        }                                                                       
    }                                                                           
                                                                                
    for {set i 0}  {< $i [array size row]} {++ i} {                             
        puts $row($i)                                                           
    }                                                                           

Results:

 Billy had to go    strategy?          didn't learn       nature.               
 around by way of   You know, we've    that               A piece that can      
 the living room    been collecting    the elevator       defend you            
 to get to where    stories as         wasn't             against               
 his mother was.    people for how     frightening.       anything,             
 'I didn't know     long,              That isn't what    including             
 dragons grew so    we don't know,     they learned.      other dragons.        
 fast', said        really, we don't   They learned the   So what if this       

See: wrap, readfile, screen width, string replace, array size, dim,
     for, string repeat, array size, break

writefile
 
 bool [writefile filename string|varname ?-var? ?length? ?-append?\
         ?-unescape?]

Write information directly to a file

Returns a boolean 1 on success, 0 on failure

If the -var argument is used then the 3rd argument will be interpreted as
a variable rather than a literal. This can significantly speed up writes,
where -unescape is used, particularly with large files. For small data
sets and where -unescape is not required, -var is not required

Examples:

    writefile "fox.txt" $qbf       # Write the contents of var qbf              

                                                                                
    writefile "fox.txt" qbf -var   # Write the contents of var qbf              

                                                                                
    set q [readfile binary.b64]    # Read base 64 data                          
    binary set b [binary base64 x -decode]                                      
    writefile "bread.bin" b -var [binary length b]  # Write binary var b        

The length parameter is necessary only for binary data. If used carelessly 
it could trigger an out-of-bounds memory read. Particular care should be
taken when escaping or unescaping strings

Escape Codes
------------
Note that [writefile] does not escape character translation and by default,
performs a faithful binary-write of string contents. If strings need to be
unescaped then the [unescape] command should be used to process the data
before writing. Alternatively, for large strings, the "-unescape? option can
be used to avoid a time-consuming call to the [unescape] command

See also: file, unescape

Creating Script-Embeddable Content from Files

Occasionally it might be required to embed an external file's contents
directly into a Tcl script. This can be achieved using [base64] encoding
via a very short script as follows...

    # Encoding an input file as embeddable base64 content and saving            
    # to a file...                                                              
    writefile output.fil [base64 [readfile input.fil]]                          

The contents of the resulting file may be copied and pasted into a Tcl
script

See also: readfile, writefile, base64

String Comparison Operators

The following string comparison operators are supported either
independently or in an 'expr' statement:

  bool [eq a b ?-nocase?]         # Equality                                    
  bool [ne a b ?-nocase?]         # Inequality                                  
  bool [lt a b ?-nocase?]         # Less-than                                   
  bool [gt a b ?-nocase?]         # Greater-than                                
  bool [le a b ?-nocase?]         # Greater-than or equal to                    
  bool [ge a b ?-nocase?]         # Less-than or equal to                       
  bool [is_empty a]               # If set but empty                            
  bool [is_set a]                 # If a variable is set                        
  bool [eqi a b]                  # Equality (no case)                          
  bool [nei a b]                  # Inequality (no case)                        
  bool [lti a b]                  # Less-than (no case)                         
  bool [gti a b]                  # Greater-than (no case)                      
  bool [gei a b]                  # Greater-than or equal to (no case)          
  bool [lei a b]                  # Less-than or equal to (no case)             
                                                                                
  bool [eqn a b length ?-nocase?] # Test [eq] over max length                   
  bool [nen a b length ?-nocase?] # Test [ne] over max length                   

The [eq] and [ne] commands are enhanced non-math commands in that they
will compare empty string values "". If a value is empty, no argument is
supplied and an empty string will be presumed. The following results are
generated where 'var' is any valid (non empty) variable. 

Relational operations (>, >= etc.) on empty strings are undefined and will
generate an error. This resolves a traditional Tcl problem in testing for
null command returns

These comparison commands are much faster than chained commands of
[string equal] and [string left]

The result of command expressions containing spaces should be wrapped in 
double quotes, e.g.:    if { eq "Hello world" "[myfunct]" } { ...

The [eqn] and [nen] operators are efficiency friendly ones which provide
fast and simple length equality tests without cumbersome code. Note that
a length value of 0 will compare 2 null strings {} and {} and will thus
return true

    eq "" ""      => true
    ne "" ""      => false
    eq var ""     => false
    ne var ""     => true
    eqn foo bar 0 => true  (compares {} and {})
    eqn foo bar 1 => false

Example:

    set a "Hello"                                                               
    puts [bool [eq "" ""]]  -> true                                             
    puts [bool [ne "" ""]]  -> false                                            
    puts [bool [eq $a ""]]  -> false                                            
    puts [bool [ne $a ""]]  -> true                                             
    puts [bool [eqn $a "Hell" 4]]  -> true                                      
    puts [bool [nen $a "Fred" 4]]  -> true                                      
    puts [bool [eqn $a "he" 2 -nocase]]  -> true                                
    puts [bool [nen $a "hell" 4]]  -> true                                      
    puts [bool [eqn $a "hello" 5 -nocase]]  -> true                             

Example:

    if { eq [set x [gets stdin]] "" } {                                         
       puts "Nothing was entered"                                               
    } else {                                                                    
       puts "You entered '$x'"                                                  
    }                                                                           

When using relational commands in an expression via [expr] or via a flow-
control command with [option expression on] be aware that qualifying
arguments are unsupported in that scenario. You must embed the command
style within the expression

Example:

    puts [expr "Fred eq fred" -nocase]  # Wrong                                 
    puts [expr [eq Fred fred -nocase]]  # Correct                               
                                                                                
    puts [expr "Freddy eqn Freda" 4]    # Wrong                                 
    puts [expr [eqn Freddy Freda 4]]    # Correct                               

See also: math operators, string

escape

    string [escape string]

The interpreter must continually reinterpret source and can't store
permanently unescaped 'special' backslash characters. This can create a
need to escape or unescape strings which contain backslash characters

Console output such as [puts] will unescape strings automatically but
other commands such as [writefile] where there may be a need to retain
literal escape characters do not and will require the use of [unescape]

[escape] will escape a string by inserting backslash characters before 
detected special Tcl characters such as $ or [. This may be required for
strings with literal backslash (\) sequences as returned from external 
DLLs to be used by Ticol. [unescape] is the inverse

Escape sequences are handled differently in the Command Line Interface to
within running scripts. See: option escape, escape sequences

Commands such as [ls] will return strings containing backslash characters
which will require escaping before printing using [puts] or [printf]

A useful workaround for path problems is to use the Unix forward slash
character - '/' which avoids the need to escape when handling paths

Example:

    # The location of ping32.dll is C:\tools                                    
    puts "DLL filename: [calldllstr ping32.dll GetDLLFileName]"                 
    puts "DLL filename: [escape [calldllstr ping32.dll GetDLLFileName]]"        

Result:

    C:    oolsping32.dll    # BAD - Inadvertent \t TAB sequence in \tools       
    C:\tools\ping32.dll     # CORRECT                                           

Example (From the CLI):

    set s "c:\windows\media"                                                    
    puts $s                                                                     
    set s [escape "c:\windows\media"]                                           
    puts $s                                                                     

Result:

    c:\windows\media                                                            
    c:\\windows\\media                                                          

Example (from the Windows CMD line):

    ticol.exe ; "puts [cd {\\\\server\\share\\path}]"                           

if the entire expression is not wrapped then the following errors may 
occur:

    Error: 1 too many [ brackets found
    <argument> is not recognised as an internal or external command

See also: unescape, escape sequences, puts, set, option escape

Ticol Macro PreProcessor Overview (MPP)
 
The macro-PreProcessor emulates some of the block-control behaviour of a 
C++ PreProcessor. See also:  #if, #echo, #exec, #exit

    #ifdef variable                                                             
    #ifndef variable                                                            
    #define variable                                                            
    #undef variable                                                             
    #if <tcl-expression>                                                        
    #else                                                                       
    #endif                                                                      
    #exec                                                                       
    #deflist                                                                    
    #echo                                                                       
    #exit                                                                       

                                                                                
#define variable ?value? Define a macro control variable                        

                         Full definition:

#define<whitespace>LValue?<whitespace>RValue?<whitespace|crlf>                  

                         Macro variables are not replaced within quoted
                         strings in a source script

                         The rvalue may be a Tcl expression but the 
                         expression will be evaluated at run-time. 
                         The expression may contain values which have been
                         previously defined

                         Defined values may optionally be wrapped in double
                         -quotes and may not contain embedded tabs or
                         spaces unless so wrapped. Only a single wrapper of
                         quotes are stripped. Any intermediate quotes
                         remain but the Preprocessor will require such
                         quote characters to be escaped and be properly
                         paired

                         You may use [chr 34] to specify single quotes
                         Macro variables may be full TCL expressions
                         providing that they are defined on a single line. 
                         Defined expressions containing spaces must be 
                         wrapped in double-quotes

                         Variables are literal substring replacements and
                         not Tcl variables. It is therefore recommended to
                         use capitals for macro variables to avoid confusion
                         and unpredictable results.The use of [const] or
                         [enum] is preferable to #define

                         The defined rvalue (if any) must be followed by
                         at least one whitespace or end of line character

#undef variable                                                                 

                         Undefine a previously #define(ed) variable/value

#if <tcl-expression>                                                            

                         Conditionally process according to a Tcl expression
                         Must be paired with an #endif
                         May have an optional #else
                         Nested #if... statements are not supported

#ifdef variable                                                                 

                         Apply control block if variable is defined
                         Nested #ifdef... statements are not supported

#ifndef variable                                                                

                         Apply control block if variable isn't defined
                         Nested #ifndef... statements are not supported

#else                                                                           

                         Apply control block if previous block was false

#endif                                                                          

                         Mark the end of an if..else control block

#exec <tcl-expression>                                                          

                         Execute a Tcl statement within the MPP context

#echo string      ?# comment...?                                                
#echo "string"    ?# comment...?                                                

                         Echo a string from the Macro PreProcessor
                         This may only be a simple string
                         Only basic escape.characters are processed,
                         including \r\n\t
                         The string may be quoted or unquoted
                         May not contain another macro command other than
                         a comment, which will truncate the line
                         Macro substitution will not occur within strings

#deflist                 List all current '#define's                            

                                                                                
#exit                                                                           

                         Immediately exit macro processing
                         No further Tcl source code is output and the
                         source code is truncated at this point

The macro-PreProcessor is run before program execution and it may be 
disabled using the /NP argument

Defined macro variables are not replaced within string constants
Macro variables are case-sensitive

A macro command must be contained on a single line. This line may be 
extended by the use of line-continuation characters (\). Due to this 
restriction a macro variable cannot contain complete blocks of Tcl code
which depend on newline characters for syntax. Nor can comments appear
after a line-continuation character.

Macro symbol replacement is recursive up to a depth of 100 definitions. 
i.e. a #define Lvalue may define an Rvalue consisting of another Rvalue
already defined. This includes Rvalues embedded in subexpressions

It may help code-clarity for defined PreProcessor variables to be in 
upper-case

Remember that macro commands are executed during the preprocessing phase
so all macro commands are executed *before* the script is run

Messages emitted by the MPP may also be redirected and used for logging
purposes

Examples:

    #define comment                                                             
    #define ESCAPE_CHAR 27                # Define an integer value             
    #define ESC ESCAPE_CHAR               # Secondary definition                
    #define HELLO "puts \"Hello \         # Multi-line definition               
    world\""                                                                    
    #define PI_APPROX "[expr \"22/7.0\"]" # Complex expression                  
                                                                                
    #ifdef PI                             # Test var presence                   
    #undef PI                             # Undefine a macro variable           
                                                                                
    #define PI "[expr 4*atan(1)]"         # Define an expression                
    #endif                                                                      
                                                                                
    #if file exists ticol.exe             # Conditional tests                   
    #echo File ticol.exe exists                                                 
    #endif                                                                      

Tcl # comment statement are completely stripped out of the source code, 
as are invalid #if... blocks. This significantly speeds up execution

Nested #if/#endif expressions are not yet supported

The macro variable "true" is pre-defined but may be #undefine'ed

Note that the Macro PreProcessor operates only on loaded/run source code 
and not within the Ticol CLI. Macro variables are not persistent outside of
an individual run session. Macro variables are discarded once execution has
returned to the CLI

A macro #include command is not provided as this functionality is provided
via [eval <script-name>]

Scripts With No Tcl Code
------------------------
It is possible to use the Macro processing language to write a script which
contains no Tcl code at all. For example, the following working CGI script
contains only macro language...

    # Configure the CGI instance of Ticol's ticol.ini                           
    # [config] -> PreprocesorVerbose=FALSE to suppress MPP warnings             
    #echo "Content-type: text/html\r\n\r\n"                                     
    #echo "<html><head><title>Tcl MPP Echo test</title></head"                  
    #echo <body>                                                                
    #echo "<br><center><h1>This file contains no Tcl code</h1></center>"        
    #echo </body>                                                               
    #echo </html>                                                               

See also: escape sequence, debugging, macro variables, 
          #__LINE__, #__DATE__, #__TIME__

Macro #if #exec #echo

    #if <tcl-expression>
    #echo unquoted-literal-string
    #echo "quoted-literal-string"
    #exec <tcl-expression>
    #else
    #endif
    #exit ?errorcode?

Instruct the Macro PreProcessor (MPP) to conditionally process a block of
code depending on the result of <tcl-expression> where this is any valid
Ticol Tcl expression. The expression-handler is local to the MPP so knows
nothing about global variables. Nor can it be used to set variables which
will be used at runtime by the script itself

#if
---
                                                                                
    #if <tcl-expression>                                                        
    #else                                                                       
    #endif                                                                      

#if must be matched with #endif and may also include an optional #else
Unlike C/C++ the start of any # macro statement may be indented using
whitespace characters

# comments or /*...*/ comments may follow any of these commands and the
expression argument must be contained wholly on the same line

The script is persistent to the pre-processor instance so local variables
set by one #if statement may be use by a later one

Global or other variables are not available
Keyword command filtering is not applied

#if allows a script-block to be executed conditionally according to a 
wide-variety of conditions such as the presence of files, the host system,
date/time, local environment settings etc.

The MPP interpreter instance will use default values. You are free to call
[option] (or any other command

Macro variable substitution is performed, including __LINE__

#echo <literal-string>
----------------------
#echo may be used to print a literal text string from the MPP 
There is no need to enclose the string argument in double quotes
No escaping is performed and all characters are printed literally
The printed string need not be enclosed in double-quotes and macro
variable substitution is performed, including __LINE__

Strings need not be wrapped in double-quotes, however if the first
and last characters (excluding whitespace) are double quotes then
these will both be stripped before printing. Otherwise, all double
quotes will be ignored

Example:

    #exec set pie 3.142857142857143        # Approximately pi                   
                                                                                
    #if {== [/ 22 7.0] $pie}                                                    
       #echo "Conditional macro SUCCESS"                                        
       #exec newline                                                            
    #else                                                                       
       #echo "Conditional macro FAILED"                                         
       #exec newline                                                            
    #endif                                                                      

Result:

    Conditional macro SUCCESS                                                   

Example:

    #if eq $env(computername) TORNADO                                           
       #echo Running on TORNADO                                                 
    #else                                                                       
       #echo Not running on TORNADO                                             
    #endif                                                                      

Result:

    Running on TORNADO                                                          

#exec <tcl-expression>
----------------------
#exec will cause its argument to be evaluated in an MPP interpreter 
context as a Tcl expression. This may be any Ticol command or expression
The result will be effective in the MMP context only. The MPP interpreter
instance will be destroyed when the MPP exits

#exec could be a security risk when using scripts which you have not
developed yourself, particularly if scripts are obfuscated as TCX files. 
When using scripts written by others this option should be disabled

#exec may be disabled using /NMX at the command-line or by setting the
INI configuration file section [Config] -> PreprocessorExec=FALSE

Macro variable substitution is performed, including __LINE__

#exit ?integer-code?
--------------------
#exit will immediately exit macro preprocessing without further action
source output will be truncated at the point of exit and no further macro
processing performed. All script prior to #exit will be evaluated

If a numeric argument is given then this will be returned to Windows as an
ERRORLEVEL value when Ticol exits after script execution completes

Example of conditional script interpretation:

    #if % [clock seconds] 2                                                     
       #exec textcolor red                                                      
       #echo Clock is odd                                                       
       #exec textcolor                                                          
       #exit 255                                                                
    #else                                                                       
       #exec textcolor green                                                    
       #echo Clock is even                                                      
       #exec textcolor                                                          
       #exit 254                                                                
    #endif                                                                      

See also. MPP, #ifdef, #else. #endif

Macro Constants

The following Macro PreProcessor (MPP) constants are available. These
should be placed at the end of any line and will commonly be used in
conjunction with [set] or [format]. Multiple #-prefixed variables
are not permitted.

Note that __FILE__ is not defined when calling a script using [eval]

    #__DATE__   Returns an unformatted ISO date YYYYMMDD                        
    #__FILE__   The current full script name/path when called using [run]       
    #__HOST__   The computer host name of the system being run on               
    #__LINE__   The current script line number as an integer                    
    #__TIME__   The current time in 24 hour clock format as HH:MM:SS            
    #__USER__   The currently logged-in username (if any)                       

These values, which are interpreted as comment fields, may be passed
into Tcl variable by placing them as the last clause of a statement

__LINE___ is available as a direct macro variable including within
#echo, #exec and #if

Example:

    set cdate #__DATE__                                                         
    set cfile #__FILE__                                                         
    set cline #__LINE__                                                         
    set ctime #__TIME__                                                         
    printf "%s" #__DATE__                                                       
    puts "In script $cfile, line $cline, date is $cdate at $ctime"              

Result:

    In script C:\tests\test.tcl, line 33, date is 20171007 at 01:00:43          

See also: macro, macro preprocessor

Macro Escape Sequences

These require [option escape on] or configuration of TICOL.INI
 
Hex and octal escape sequences inside strings are checked and translated
by the Macro PreProcessor on running a Ticol script

Literal backslash characters within a string should be represented by a 
double-backslash character. e.g. "c:\\windows\\system32\\notepad.exe"

Strings which are not within a source file i.e. which are read from file or 
input by a user at the Ticol CLI are not passed through the PreProcessor
and are not interpreted. This enables the Ticol CLI to be used as a 
general-purpose interpretive Windows shell. 

    e.g. "c:\windows\system32\notepad.exe"

These should be in the form...

    \xN, \xNN         for hexadecimal sequences                                 
    \N, \NN, \NNN     for octal sequences                                       
                                                                                
    Example:          puts "This is a blob character \xfe."                     
    Result:           (square blob character)                                   
                                                                                
    Example:          puts "\x68\x65\x6C\x6C\x6F"                               
    Example:          puts "\150\145\154\154\157"                               
    Result:           Hello                                                     
                                                                                
    Example:          puts "Hello\tworld"                                       
    Result:           Hello   world                                             

Processing will halt with a line-number indication if an invalid sequence 
is encountered, e.g. "\x ". You can view the translated code using the 
echo (/E) argument. The advantage of preprocessing is that such codes
can be checked for validity and code execution speed is increased.

Square brackets (and other characters) may be escaped by using the [chr]
command as [chr 91]->'[' [chr 93]->']' or \[ and \]

The comment prefix character (#) may be escaped using \#. The Macro 
PreProcessor will treat \# as a literal # character

Other escape sequences:

    Sequence    Description             Processed by                            
    ----------------------------------------------------------                  
    \\          Literal backslash       Interpreter                             
    \$          Dollar sign             Interpreter                             
    \(          Open bracket            Interpreter                             
    \)          Close bracket           Interpreter                             
    \[          Open square bracket     Interpreter                             
    \]          Close square bracket    Interpreter                             
    \{          Open brace              Interpreter                             
    \}          Close brace             Interpreter                             
    \"          Double quote            Interpreter                             
                                                                                
    \a          Bell                    Macro PreProcessor                      
    \b          Backspace               Macro PreProcessor                      
    \f          Formfeed                Macro PreProcessor                      
    \n          Newline                 Macro PreProcessor                      
    \r          Carriage return         Macro PreProcessor                      
    \t          Tab character           Macro PreProcessor                      
    \v          Vertical tab            Macro PreProcessor                      

Macro PreProcessors escapes may be disabled with the /NP argument

Not supported:

    \'          Single quote. Preprocesor restriction - use [chr 39]            
    \"          Double quote. Preprocesor restriction - use [chr 34]            
    \0          Not supported. Directly manipulating null terminators           
                within Ticol strings should be avoided as it may lead to        
                memory corruption                                               

Examples:

    puts "hello\b\b\b\b\bWorld"                                                 
    puts "[chr 91]Hello[chr 93]"                                                

Results:

    World                                                                       
    [Hello]                                                                     

See also: escape, macro, option, unc paths

Debugging Ticol
 
A few features are provided to assist debugging

Pseudo-macro items may be used as the first item within a comment area
to add line number, date and time as follows. (Case is ignored)

    puts "Line: "#__LINE__ This is a comment which should be ignored            
    puts "Date: "#__Date__ So would this be                                     
    puts "Time: "#__time__ And this too                                         

The Macro PreProcessor will check a Tcl file on loading for mismatched
brackets/braces and double-quotes. An indication is given as to the
likely location of any braced section with a mismatch

Line numbers may be traced by setting a variable using: set line #__LINE__

Single-step execution with breakpoints is provided by option breakpoint and
the [halt] instruction

array walk    Walks though the array hashtable showing nodes and content
assert        Assert a value against a Tcl expression
breakpoint    option breakpoint. Enable single-step breakpoints via [halt]
cmdcount      Count of the # of executed commands
commands      List defined commands
debug print   Debug output which can be globally disabled in release code
debug         Enables or disables debugging of a debug release of Ticol
dummy         Dummy command. Just echoes its arguments
dump          Performs a binary dump of variables or structs
empty         Empty command. Does nothing
explain       Expand [calc] and resolve into Tcl commands
functions     List known functions
get_win_error Show last Windows error code 
halt          Enable a breakpoint halt point
inspect       Inspect raw variable information
lib...        -modules shows loaded modules and -info shows version info
level         Return the current [proc] level
#macros       Macros allow conditional interpretation for test purposes
mem_used      Returns the current memory used by Ticol
procs         List defined procs
resume        Disable breakpoint single-stepping until next [halt] is met
sc            Compare two strings showing the offset of any mismatch
stacktrace    option stacktrace. Stack trace dump on fatal error
stack list    List a stack's contents and show state
struct dump   Show detailed information about a struct's memory
time          Times the execution of a block of code, repeating N times
trace         Traces the execution of selected commands
vars          Lists variables by search prefix and type in any level
walk          Walks the variable table showing addresses, type and content
watch         Set a variable watch with breakpoints or trace in real time
win_error     Describe a Windows error code

Output or string return commands require the use of [puts] or [option
echo on] otherwise no output will be seen

[call] is an empty command which does nothing. 

Example:

    call [puts Hello]                                                           

Result:

    Hello                                                                       

See also: assert, trace, time, breakpoints, halt, dump, dummy, debug, walk,
          vars, mem_used, performance, faq, PreProcessor, macro, #if, #else, 
          #endif, option stacktrace option breakpoint, halt, resume, watch

dummy

    integer [dummy]
    string [dummy -return]
    list [dummy -list]

[dummy] is a dummy Tcl command which does nothing other than print out its
own argument list and return the argument count.

Note that if this argument list contains commands then these may be
evaluated before the call to [dummy]

[dummy] may be useful for checking that what is passed into a command
matches expectations and verifying the count of any expanded arguments

If the -return argument is used then the arguments will be concatenated
and returned as a string

If -list is specified then a formatted Tcl list will be returned

Example:

    assert [dummy a {*}{b [c]} d {*}{$e f {g h}}] {== $_ 8} -v                  

Result:

    Enter: [dummy] argc == 8                                                    
        0='dummy'                                                               
        1='a'                                                                   
        2='b'                                                                   
        3='{[c]}'                                                               
        4='d'                                                                   
        5='{$e}'                                                                
        6='f'                                                                   
        7='g h'                                                                 
    assert: PASS '== $_ 8'                                                      

Example:

    dummy [printf hello]                                                        

Result:

    Enter: command [dummy] argc==2                                              
        dummy: 0='dummy'                                                        
        dummy: 1='5'                                                            

Example:

    puts [dummy a {*}{b [c]} d {*}{$e f {g h}} -return]                         

                                                                                
    a b {[c]} d {$e} f g h                                                      

See also: empty, args, debugging

empty

    void [empty ?...?]

[empty] is an empty Tcl command which does nothing. It accepts any number
of arguments and ignores them. It returns a null ("") result.

See also: dummy

Windows DLL Test Interface (wintest.exe)

A simple Windows GUI interface is provided which links to ticol.dll and
is able to either load and run Tcl scripts or to launch the CLI within
ticol.dll. Note that the underlying Ticol interpreter is ANSI only.

This interface is quite rudimentary and intended for testing only. it was 
written in a legacy copy of Visual BASIC 5.0 to enable DLL linkage to be
tested.

Wintest has options to enter the CLI, load/save Tcl files, clear the 
code viewer and set breakpoints or stacktrace.Rudimentary editing is
possible but not recommended. With the Command Line Interface loaded
([cli]) the interface will behave the same as ticol.exe

Wintest is typically supplied with no installer disk set although one
can be generated if requested. Usually one needs only to have msvbvm50.dll
available in the PATH or current folder

To install: Copy the following files to the same folder:

    wintest.exe     # The Windows binary (UPX compressed)                       
    ticol.dll       # The DLL version Ticol Runtime                             
    msvbvm50.dll    # Visual BASIC 5.0 runtime DLL                              
    ticol.ini       # Optional configuration file                               
    autoexec.tcl    # Optional startup Tcl file                                 

You can pass the name of a Tcl file to load to wintest.exe at the 
Windows command line, thus:

    wintest.exe foo.tcl                                                         

You may also optionally run this script by appending the /R argument to
the command line, thus:

    wintest.exe foo.tcl /r                                                      

The [exit] command is used to exit any spawned CLI session

Wintest should not be used for in a production environment

See also: ticol, ticol.dll, plugin

Performance and Efficiency
 
Ticol is a pure interpreted script language. It does not perform any byte-
code compilation or dynamic optimisation, so performance will be slower
than other Tcl variants such as ActiveState. However, since Ticol was
designed for day-to-day administrative script work this should not present
any serious problems

If performance is an issue then it is recommended to use another such as 
ActiveState Tcl

The Ticol Macro PreProcessor (MPP) is able to statically optimise some 
aspects of the script source code but the result will still be interpreted
The MPP strips out comments, so they are not interpreted as commands by
Ticol

Loop-processing constructs will not be as efficient as a compiled language
such as C#. however many of the more complex list-processing or string-
splitting operations are quite efficient. Ticol offers a wide range of 
commands and some plugin libraries which are "closer to C++" and therefore
fast and efficient 

You should avoid single-character loop-processing within Tcl, particularly 
for loops which necessitate complex branched if statements.

Preferably call a dedicated, external DLL function and use Ticol as the
'glue' to hold together more efficient components. A simple plugin
interface library is offered for C++ programmers.

The following commands are offered to enhance performance:

  any           Avoids slow/nested logical or [||] statements                   
  all           Avoids slow/nested logical and [&&] statements                  
  array item    Ambiguous indexing of arrays with default value                 
  calc          Static, self-optimising equivalent to [expr]                    
                Translates expressions into Tcl commands                        
  chr           Return a character by number with optional offset               
  cmp           Efficient compare function                                      
  comma         Efficient comma-formatting of numbers                           
  concat        Efficient string concatenation                                  
  eq, eqi       Efficient comparison commands                                   
  eqn, nen      Shorthand equivalence/disequivalence                            
  funct         Call any expr function directly                                 
  ge, gei       *i is the case insignificant version ...                        
  gt, gti       Greater than (gti:ignore case)                                  
  index         Rapid single char index into a string                           
  is_mod        Modular arithmetic test                                         
  le, lei       Less than or equal (ignore case)                                
  lt, lti       Less than (lei:ignore case)                                     
  makestr       Create a new string                                             
  ne, nei       Not equal (nei:ignore case)                                     
  setr          Assign to a string and align                                    
  setat         String manipulation                                             
  store         Buffered memory storage                                         
  strsplit      Split a string                                                  
  strstr        Locate a substring                                              
  tohex         Convert an integer to hexadecimal                               

The Following Commands Reduce CPU Stress

  sleep         Pause execution for X milliseconds                              
  doevents      Yield briefly to Windows                                        

Move [proc] definition, list evaluation and creation outside of loops if
possible. Pre-evaluate lists into variables.. Make full use of the 
macro-PreProcessor to optimise constants during the preprocessing phase

Use [concat] when concatenating large strings. Avoid chaining the [set]
command as the accumulated string passes through the interpreter and
is repeatedly recopied.

    POOR:                                                                       

    set out $out$ch                                                             

                                                                                
    GOOD:                                                                       

    concat out $ch                                                              

Use [cmp], this compares string segments in memory, instead of extracting
and then comparing the resulting extract in an expression. It can
potentially avoid copying large blocks of data. 'time' tests show it to be
around 30% faster especially within loops

    POOR:                                                                       

    if { eq [mid $data $i 2] "Hi" } {                                           

                                                                                
    GOOD:                                                                       

    if { cmp data "Hi" $i 2 } {                                                 

When extracting small sections of a string use [setat] to directly slice 
the section of a string to a variable, thus avoiding the use of the set call
This sets a variable directly with index values located at a specified
position and length. Timing tests show [setat] is around 50% faster than
using [mids] together with [set]

    POOR:                                                                       

    set ch [mids $data $i 1]                                                    

                                                                                
    GOOD:                                                                       

    setat ch $data $i 1                                                         

Character replacement can be performed rapidly using [replacechar] rather
than re-assigning the result of [string replace]

    POOR:                                                                       

    set s [readfile ticol.man]                                                  
    set s [string replace $s "\t" " "]                                          

                                                                                
    GOOD:                                                                       

    set s [readfile ticol.man]                                                  
    replacechar s "\t" " "                                                      

To concatenate strings, particularly large ones or large numbers of small
ones use [append] instead of repeated calls to chained [set] commands. 
The use of [set] chains creates multiple copies of strings whereas append
appends directly to the original variable. Append is faster than [lappend]
and [store] is about 5 times faster than append but less flexible to use

    POOR:                                                                       

          set a "The quick"                                                     
          set a "$a brown fox"                                                  
          set a "$a jumped over"                                                
          set a "$a the lazy dog"                                               

                                                                                
    GOOD:                                                                       

          set a "The quick"                                                     
          append a " brown fox"                                                 
          append a " jumped over"                                               
          append a " the lazy dog"                                              

                                                                                
    BEST:                                                                       

          store "The quick"                                                     
          store " brown fox"                                                    
          store " jumped over"                                                  
          store " the lazy dog"                                                 
          set a [store get]                                                     

A key to interpreter performance is pre-calculating as much as possible 
outside of intensive loops and making good use of intrinsic functions
instead of synthesising them from other commands

String comparison for string prefixes should use the [eqn] and [nen]
commands which will safely test equality or inequality for a given length
This would be effective in tight loops when comparing large string prefixes
than using a compound of [eq] and [string]

    POOR:                                                                       

    set a "The quick brown fox"                                                 
    if { eq [string left $a 3] "The" } { do something ... }                     

                                                                                
    GOOD:                                                                       

    set a "The quick brown fox"                                                 
     if { eqn $a The 3 } { do something ... }                                   

Use [for]. For loops are often far more efficient than while {} loops

For accurate performance timing, use the 'time' command or run Ticol in
"graph" mode with the /G and /NA command line arguments

The performance of individual commands can be estimated more accurately
when running directly and outside of complex scripts, e.g.

    ticol.exe ; "time {loop i 0 100 {}} 100" /g /na                             
    ticol.exe ; "time {if {} {}} 100" /g /na                                    
    ticol.exe ; "loop i 0 100 {time {}}" /g /na                                 
    ticol.exe ; "time {set a 10} 100" /g /na                                    

[let] may be used for multiple, simultaneous assignments

Use [array foreach] to iterate arrays if you need simple behaviour 

Because it is a generalised expression-handler with significant checking-
overheads, [expr] may be far slower than decomposing an expression into 
dedicated component operations with Tcl 'prefix' syntax. The Macro 
PreProcessor will optimise the [calc] command and expand into raw Tcl
statements. If the MPP is disabled then [calc] will be interpreted
equivalent to [expr]

Example:

   set src [expr ($x & $y) % 3]      # Expression                               
   set src [% [& $x $y] 3]           # Decomposed expression (~50% faster)      
   set src [calc ($x & $y) % 3]      # Expression is optimised by the MPP       

See: 
    http://wiki.tcl.tk/348

Move command calls out of loop arguments and bodies where possible to
avoid repeated calls to slower evaluation commands

Example:

   option expression off                                                        
   for {set i 1} {< $i [linkedlist count $handle]} {++ i} {  # Slower           
                                                                                
   set c [linkedlist count $handle]  # Evaluate once                            
   for {set i 1} {< $i $c} {++ i} {  # Faster                                   

See also: optimising, mpp, calc, performance graph, performance example,
          time, timer, clock, cat, cmp, setat, let, Ticol, Tcl syntax, faq

Performance Example

The script hanoi.tcl can be used to check performance. An equivalent EXE 
version (hanoi.exe) which uses the same libraries is included for
comparison

    ticol hanoi.tcl 17 

  - should take about 4 seconds on an Intel Core i5 with an idle CPU and
about 3 seconds on a core i7 with idle CPU

The precompiled example hanoi.exe will take about 25ms to perform the
same task

See also: performance, performance graph, efficiency, Ticol

Performance Graph (Profiling)

The command-line /G option will generate a graphic profile analysis of 
command calls so that code can be optimised by substituting more efficient
operators for those command which are called most frequently

The graph will show the total number of calls, a graph of that count and
the average (mean) time taken per command call in milliseconds. Note that
this time includes all polymorphisms of commands such as [time] or [date]
which may have significantly varying execution time.

Functions which typically call script snippets such as [while], [for],
[foreach] etc. will show far higher execution times as these command calls
include many others which are listed separately

The timer resolution is approximately 1 microsecond (us) per command call
and the timing in microseconds (us) per call is should be treated as an
estimate. Due to the accuracy of microsecond timers on Windows some timings
may be too small to estimate and are rounded up to 1 us

Graphing has no affect on performance when disabled and a small effect on
performance whilst enabled. Using [time] with /G may give a misleading
view on actual code-efficiency due to the /G analysis impact on performance

Call time per microsecond may vary significantly from evocation to evocation
and the total times include only function calls excluding interpreter kernel
processing time

Note that the final total time is the sum of all commands and will usually
be greater than the actual runtime. This is useful only as a guide to 
performance. Total execution times may differ substantially from actual
runtimes due to the problem of compound commands and procs repeating the
total execution time of endpoint commands they call. To gain a more
accurate estimate, run each command separately

Procedure names are highlighted in magenta, compound flow-control commands
which call [eval] or [expr] are highlighted in yellow. Particularly slow
commands are highlighted in red and have the display value scaled down to ms.

Example:

    ticol.exe adapters.tcl /g                                                   

Results:  (Actual graph is displayed in colour)

Performance Analysis Graph for adapters.tcl

Command      us/Call   Calls Call Graph                                         
puts             691     114 **********************************************     
info           21616     103 ********************************************       
incr               1      30 *************                                      
textcolor         67      27 ************                                       
if            108945      23 **********                                         
<                  4      12 ******                                             
set                1      11 *****                                              
gotoxy            73       9 ****                                               
eq                 0       6 ***                                                
==                 0       6 ***                                                
+                  0       6 ***                                                
                                                                                
Totals:                  386                                                    
Time:          6,054,842 µs     (6.0548 seconds)                                

See also: performance, debugging

Ticol Design and Picol Design Changes

Based on the original Picol source this code has been significantly
re-engineered.

Significantly Redesigned Code
------------------------------
Command lookup now uses a sorted list with binary search
The parser system is modified to add new features including "{*}"
[proc] is redesigned and more complex
[eval] code now applies [subst]
[eval] code block applies [trace]
[eval] code block applies /G performance graphing/profiling
[eval] code block applies stack tracing and debugging
Fixed size buffers are eliminated
Code is optimised by preprocessing using the MPP
Variable parent inheritance chains are enabled
Square braced numbers are not interpreted as procs ([123])

Additions
---------
Multi-level variable dereferencing with $$
Macro preprocessor (MPP) added
Arrays are implemented using hashtables
Structs are new and implemented using C-compatible binary structs
The ability to call/exchange data with external DLLs is added
[expr] (and [calc]) expression handling have been added
[subst] is added
[@], [exec] and [spawn] are added to call external apps
[unknown] can shell out to Windows apps
Plugin support added via [lib] with several plugins made available
Command line help added
Ability to call external DLLs using [calldll]
Sub-language plugins for bf, VBScript and pseudo-assembler

Retained
--------
Simple FIFO variable tables are retained as these are efficient
enough and keep the most recently added variables at the top
See [walk]

See also: design goals, MPP, faq

Ticol Design Goals
 
The following design goals were adopted when designing Ticol Tcl.

 * Single, tiny EXE file, fully portable with no installation needed
 * Final compressed EXE size of about 200 Kb
 * Speed not vitally important as it is intended for low-priority tasks
 * Plugin extension DLLs, including ZIP, big numbers, email etc.
 * Big math library with a range of useful functions
 * No inherent system registry dependencies
 * To offer a useful IT Administration tool as an adjunct to the CMD shell
 * To provide a script language which can ease many routine admin tasks
 * Not to emulate the full Tcl language but to focus on a useful subset
 * Tk would not be implemented as it was not required
 * Fast interpreter performance with minimal code complexity
 * Avoid unnecessary complexity and unnecessary features e.g. namespaces
 * Minimal socket/internet connectivity to keep the code simple
 * No external dependencies other than the MSVC runtime
 * Ability to link to 3rd party DLLs to 'lever' Ticol's usefulness
 * Provide a basic-level conditional Macro-PreProcessor (MPP) facility
 * Offer MPP optimisation of expressions where possible
 * A built-in help/man facility with example code and find feature
 * Enhance comments and provide a long-comment mechanism
 * To improve, where possible and at minimum cost, selected Tcl features
 * Add common script idioms as built-in commands for maximum performance
 * Ability to be called by BAT/CMD or other script files, e.g. FOR /F
 * Ability to spawn console apps and grab text output to a Tcl variable
 * Export to a linkable DLL for other programs to utilise Ticol scripting
 * A more reliable replacement for SET /A in evaluating script expressions
 * Include useful new commands which simplify and speed up Ticol code
 * Support hashed Tcl arrays and C++ struct emulation for DLL linking
 * The widest range of compatibility across all Windows versions and ReactOS
 * Ability to act as a useful Windows console shell
 * Syntax-highlighted code-viewer
 * To support Windows from NT4/SP6 through Windows Server 2016
 * ANSI-only as this is a personal, not a commercial project

Future goals:

 * Regular expression handling
 * Extended API file copy/move
 * Possible limited socket functionality
 * Reduced EXE size, more
 * More plugins
 * Nested #if...#endif macro blocks

See also: Ticol, design, disadvantages, faq

Ticol Disadvantages

Lack of Unicode / Non Western Character Sets
--------------------------------------------
Ticol was designed to meet a particular design spec, the majority of which
was for personal use. One of the goals was to reuse all existing C
library code, all of which was ANSI (ASCII) based in order to get the 
project up and running as quickly as possible. The other was that it
was mainly for home and small office use in the UK only. Consequently
there was no need to invest time rewriting the base library code to
handle non-UK characters. This was a design constraint, not an oversight

The language fulfilled particular requirements and was in daily use
on a number of Windows servers for several years and a LAN monitoring
and diagnostic web page was running under Ticol CGI for at least 2 years

Ticol is shared with anyone who might find it useful but it should be
acknowledged that if they have non-UK (e.g. "unicode" / "wide" character
requirements then this program was not designed for them.

If you have a pressing need for Unicode / non-UK character support then
I can strongly recommend ActiveState Tcl or "Jim" Tcl.

Interpreters are Slow
---------------------
Ticol is fully-interpreted which means that opportunities for optimisation
are limited and it will never run as fast as a compiled language. The
Ticol Pseudo Assembler language does run quite fast though. No part of
Ticol code is precompiled into say "byte code". Despite this Ticol
isn't exactly a slouch and can perform many scripting tasks quite 
quickly. Plugin code is compiled in C++ which means it runs at full
speed.

Lack of Threading Support
-------------------------
I didn't need this and other than some experimental code and an 
experimental threaded version very little time has been invested in
making Ticol threaded. ActiveState is recommended if you need threading

Lack of Full Socket Support
---------------------------
Other than a few specific network based commands I didn't need full
socket support so if you want to write a webserver in Tcl then again
you will need to use ActiveState

See also: Ticol, design goals, design, faq

UNC Path Handling

Ticol file functions will handle UNC paths such as Windows SMB share paths

The handling of share paths may require careful use of [escape] and 
[unescape] if Windows backslash '\' separator characters are used 

It is recommended to use Unix forward-slash '/' characters instead

Examples:

    # [unescape] is required for all escape sequences                           
    set filename "\\\\server\\share\\c\$\\path\\to\\file.txt"                   
    readfile [unescape $filename]                                               
                                                                                
    # [unescape] is required for the dollar $ character escape                  
    set filename "//server/share/c\$/path/to/file.txt"                          
    readfile [unescape $filename]                                               

See also: file, readfile

unescape

    string [unescape string]

Unescape a string by de-duplicating backslash character pairs. This may be
required for strings with literal backslash (\) sequences as returned from
external DLLs to be used by Ticol, or when generating, say RTF output. 
[escape] is the inverse

Ticol requires scripts to have strings in "escaped" format if they contain
literal backslashes. This is achieved by using a double-backslash as with C++
Standard escape characters such as \r  \n  \b are processed by the MPP

Only a small number of commands intrinsically unescape backslashes 
(e.g. [puts]). Other commands such as file/string-handling commands may need
[unescape]

Example:

   append s [unescape "\\b0\\fs18\\f0 \r\n\r\n\\par "]                          
                                                                                
                       ^   ^     ^            ^       # Escaped backslashes
                                      ^ ^ ^ ^         # Escaped by the MPP
Result:

    # $s contains the sequence "\b0\fs18\f0 CRLFCRLF\par "                      

Where CRLF is the byte-sequence 0x0d0x0a

See also: escape, puts, set, option escape

urlencode, urldecode

    string [urlencode string]
    string [urldecode string]

Encode a string containing an HTTP URL

Example:

    puts [urlencode "http://www.mysite.com/page with space"]                    

Result:

    http%3A%2F%2Fwww.mysite.com%2Fpage+with+space                               

See also: 

White Space

    White space is relevant in Tcl outside quoted strings

See also: Ticol, tcl, syntax

INI Handling Plugin

Load using: lib ticol_ini?.dll?

    string  [ini read inifile section key]
    bool    [ini write inifile section key value]
    string  [ini read bool inifile section key]
    bool    [ini write bool inifile section key value]
    integer [ini array read inifile section key array-variable ?-max N?]
    bool    [ini array write inifile section key array-variable]

Various INI file handling routines. The 'section' value must not be
enclosed in square brackets. The file may have comments, delimited by a
semi-colon (;)

[ini read] will read a standard INI file value from an INI file

[ini write] will write an ANSI variable to an INI file with the given
section and key

[ini read bool] performs the same as [ini read] but returns a boolean
value of 1 or 0 depending on whether the key has a value of 1/0,
True/False, or Yes/No

[ini write bool] writes to file the same as [ini write] but evaluates the
value argument and writes either "True" or "False"

[ini array read] will read up to 1000 items by default with an integer
sequence matching the 'key' prefix (e.g. Item1...ItemN) with an array base
of 1 (not 0). This maximum can be altered by using the -max N argument,
e.g. puts [ini array read $inifile config Item q -max 750]. 
[ini array read] returns an integer count of items read into the array. 
Sparse items in the source INI file are not counted

[ini array read] can be used to count the items in the INI array. 
Alternatively you can set a value in the file which explicitly tracks the
count

[ini array write] will write a Tcl array which is indexed with an integer 
subscript starting at array base 0 or array base 1. The INI file will have
items written with a prefix with the key value having a suffix at array 
base 1, e.g. Item1...ItemN. Sparse items in the source array are not 
counted.

Example:

    textcolor cyan                                                              
    lib ticol_ini -verbose                                                      
    textcolor                                                                   
    newline                                                                     
    set inifile test_ini.ini                                                    
                                                                                
    ini write $inifile config testKey Hello                                     
    ini write bool $inifile config testKeyBool 1                                
    ini read $inifile config testKey                                            
    ini read bool $inifile config testKeyBool                                   

Results:

    1                                                                           
    1                                                                           
    Hello                                                                       
    1                                                                           

Example:

    lib ticol_ini -verbose                                                      
    set inifile test_ini.ini                                                    
                                                                                
    array set a {   1 One                                                       
                    2 Two                                                       
                    3 Three                                                     
    } -const                                                                    
                                                                                
    ini write array $inifile config item a                                      
    ini read array $inifile config item a                                       

Results:

    # Writes the values to the INI file                                         

Example:

    lib ticol_ini -verbose                                                      
    set inifile test_ini.ini                                                    
                                                                                
    puts [ini array write $inifile config Item a]                               
    puts [ini array read $inifile config Item q -max 10]                        

Results:

    1                                                                           
    3                                                                           

See also: plugins

ticol.ini - Configuration file for Ticol.exe

A TICOL.INI configuration file is optional and not required

This file permits custom configuration options and is particularly
relevant when using Ticol in CGI mode

The ticol.exe folder is searched first, followed by %APPDATA%\Ticol\
(Default values are shown)

Setting                          Description/Comment
----------------------------------------------------------------------------
                                                                                
[Config]                                                                        
AutoExecCommand=FALSE            Ticol autoexec [unknown] command (ON|OFF)      
AutoExecScript=TRUE              Whether to run autoexec.tcl if found           
                                 Equivalent to argument /NA if FALSE            
                                 Set this to FALSE for Server CGI mode          
BackslashEscapes=TRUE            C/C++ style escape characters: \n \t etc.      
CGIDebug=FALSE                   Send an HTTP 1.1 header to stdout for CGI      
CommandUndefList=<list>          Space-separated list of commands to            
                                 undefine on startup                            
ConsoleEcho=FALSE                Echo evaluation results to the console         
                                 Equivalent to [option echo on|off]             
Debug=FALSE                      Debug command (ON|OFF)                         
EnablePreProcessor=TRUE          /NP (No PreProcessor) command line arg         
FlowControlUsesExpression=TRUE   /EXPREVAL and Option expression ON|OFF         
ForInitAllowsExpression=FALSE;   [for] {init} statement is controlled by        
                                 FlowControlUsesExpression v's [eval]           
HashTableIncrement=0             Increment when resizing array hash tables      
                                 If set to 0 then tables will auto-resize       
HashTableDefaultSize=250         Starting element size for array hash tables    
HashTableResizeThreshold=2.5     Array hash table depth resize threshold        
                                 When mean depth exceeds this the array is      
                                 resized according to HashTableIncrement        
HashTableMinResizeThreshold=200  Min number of array nodes before resize        
MaxRecursion=<value>             The maximum recursion depth (default 5000)     
                                 where <value> > 0 && <value> <= 5000           
OptimiseExpressions=TRUE         PreProcessor optimise [calc] (/NEO)            
Pause=FALSE                      Pauses after execution (/PAUSE)                
PreProcessorVerbose=FALSE        PreProcessor verbosity                         
Prompt=$u@$h$g                   CLI prompt. 'User@Hostname>'                   
StandardComments=FALSE           Toggles end-of-line comments (;#)              
StackTrace=FALSE                 Toggles the error debug stack trace            
Verbose=FALSE                    Sets /V verbose command line argument          
                                 ^ It is recommended that the following         
                                 hash-table values are not altered              

Notes
=======================================================================

CommandUndefList
----------------
If you undefine certain commands you may not receive a bad command
error if autoexec is set to ON since these commands (e.g. EXIT, CALL) will
be passed to Windows and exist as valid commands within Windows
The undef list may be used to make the interpreter instance 'safe'

e.g.

CommandUndefList=foo bar baz                                                    

Prompt
------
For more information see: prompt

StandardComments
----------------
For more information see: comments

Hash Table Tuning
-----------------
It is recommended that the hash table settings be left unchanged as
the default values should give best performance

See also: Ticol, Frequently asked questions, cli prompt

Known Bugs and Feature Anomalies

Does Ticol Have any Bugs?
-------------------------
Very likely, but every effort has been made to try and chase these out
Using a language for real-world tasks usually flushes out a number of bugs
Many such bugs have been found during practical applications of the language

Project Status
--------------
Builds of v1.27+ from 2022 onwards are now in the beta stage of development
with only a few features left unfinished. 

There are some features which I am less than happy with, such as the 
overall handling of escape characters, the problem of double-unescaping
and the workaround of using the [escape] and [unescape] commands. 
Hopefully, this will be improved in future versions

I Found a Bug, What Do I Do?
----------------------------
First of all, check you are using the most up-to-date version

File a bug report. However, ensure you attach supporting documentation, test
or example code otherwise this will be a waste of time.

Send bug reports to: ticol@kerys.co.uk

No Unicode Support
------------------
Ticol is a Western ANSI character-set only script language. Unicode is not
natively supported. This is not a bug but by design but built-in conversion
routines are available to connect with external Unicode/wide-character DLLs

Major Issues
------------
Escape Sequence:

    String escape sequences are a headache since the underlying source 
    code is continually reinterpreted. Constant sequences such as \n
    are handled by the Macro PreProcessor but literal backslash characters
    and escaped double-quotes are problematic. Workarounds for double-
    escaping are to use the [escape] and [unescape] commands to control
    escape handling as preferred

Quoted Strings Within Strings or Within Array Subscripts:

    The parser has limited extended handing for embedded double-quoted
    strings within strings or array subscripts: Double quotes are tolerated
    in Ticol if escaped properly. These are not accepted in ActiveState Tcl

        e.g. set q a("with spaces")   is valid
        e.g. set q a( "hello" )       is not valid

    A workaround is to wrap the entire string in curly braces, either at
    inner or outer levels

    An option would  be to trap for escaped strings in every call or every
    call to a range of commands (e.g. [eq]). At present only command 
    endpoints such as [puts] or [printf] escape strings automatically. 
    Trapping all calls will slow down the interpreter

    The following are not valid in ActiveState Tcl

        set a({Hello world}) x
        set a("Hello world") x
        set a( "Hello\ world" ) x
        set a(Hello world) x

    Valid in ActiveState Tcl:

        set a(Hello\ world) x

    Additionally. Array subscript string are stored in their unescaped
    form so the following two subscripts are different...

        set q (has\ space) x
        set q ("has space") x

    Note that this also works!

    array unset "a(Hello world)"

Long Math Keywords:

    The math keywords, 'add', 'sub', 'and', 'or' have been removed
    by design as detection and processing of these are time-costly to the
    interpreter and they serve no useful benefit

Calldll Byte Widths

    [calldll] byte widths rely on parsing a colon ":" character. This is
    less than ideal and may cause issues with strings such as paths
    which contain a colon. An alternate solution will be looked at in 
    due course

Math Overflow Handling

    Overflow exception handling in string to integer conversion requires
    improvement. Some conversion overflow cases are not trapped

Multi-Threading
---------------
The stable release of Ticol is not multi-threaded. A test/development
version offers rudimentary threaded execution but it not stable for 
anything other than experimentation. Ticol is unlikely to be released
in a stable multi-threaded form as it would require a significant
rewrite of the core

Unfinished Features
-------------------
The following features are unfinished, feature-incomplete, have limitations
or are pending possible future implementation:

    after                               Currently non-threaded
                                        Implemented experimentally only
                                        Would require a threaded rewrite
    dictionary                          Double derefrencing not implemented
                                        [dict] does not preserve ordering
                                        due to underlying hash tables
    vwait                               Experimental, see above
    timezone                            Not implemented
    array unset                         Glob search spec not implemented
                                        Use [unset array -glob]

    Variant arrays                        Partially implemented
    array_create name elems ?dims?
    array_set name ix ?ix? value
    array_add name ix ?ix? data

    fconfigure                          Not implemented

The following [file] commands are not yet implemented:

    lreplace

    Some file commands are of minimal use and can either be emulated
    or called as external commands

    file atime name ?time?    
    file attributes name ?option value option value...?
    file channels ?pattern?
    file copy ?-force? ?--? source ?source ...? targetDir
    file copy ?-force? ?--? source target
    file join
    file join name ?name ...?
    file link ?-linktype? linkName ?target?
    file lstat name varName
    file makelink
    file mtime name ?time?
    file nativename name
    file owned name
    file pathtype name
    file rename ?-force? ?--? source ?source ...? targetDir
    file rootname name
    file separator ?name?
    file system name
    file tail                           May be implemented later
    file tail name                      Best done as a plugin
    file tempfile ?varName? ?template?

    Namespaces                          Emulation only by name prefix

Desirable Features Which May Be Implemented
--------------------------------------------
    Multi-line paste into the CLI
    Rudimentary editor plugin module for the CLI
    Regular expressions (via a plugin perhaps)

Features Which Are Unlikely To Be Implemented
---------------------------------------------
    If the following features are required then ActiveState is recommended
    Threading (other than experimentally)
    Threaded [after] (other than experimentally)
    Threaded [vwait] (other than experimentally)
    Network socket commands
    Unicode/MBCS/Multilanguage string support
    Object orientation / classes (other than via simple struct emulation)

See also: FAQ, Ticol, Tcl syntax, workarounds, error

Hints and Tips

* Ticol is an interpreter not a compiler. It will perform much more slowly
  than a compiled language such as C++, C#. Often around 100 times slower
  You must therefore write code carefully if you need it to perform quickly. 
  Ticol was designed to be used as a script for non time-critical jobs such
  as IT support scripting
* There is a C++ API provided for the development of external plugins which
  can offer greater flexibility and speed
* You can get help on the Ticol command-line using: ticol.exe /?
* ticol.exe can be used as an MD5 generating tool using the /MD5 argument
* Use the command [option debug on] for development level debugging
* Enable [option echo on] when encountering problems and debugging
* C/C++ style backslash escape sequences are not translated from within the
  command-line interface. This is to enable the CLI to be used as a Windows 
  shell. 
  You can enable backslash escape translation using:
  option autoexec off
  option escape on
* You can use ticol.exe as an expression evaluator for batch scripts
  Use the /EXPR command-line argument (See: help /expr)
* Using logical and bitwise operators with [expr]
  Bracket complex sub-expressions when using logical or bitwise operators
  (&&/||, &/|)

  Example:   

       proc is_power {x} {                                                      
          expr "!(($x-1) & $x)"                                                 
       }                                                                        

* All data required for a proc may be declared locally within that proc
  however, handling initialisation will slow down each [proc] call 
* Although the expression-handler routine implements BEDMAS you are 
  recommended to bracket expressions fully, especially with and/or
* Avoid [expr] if maximum speed is required. Use [calc] or expand manually
  Commands or bit operations may be faster than expression math
* Use [option expression off] and eval statements in flow-control for speed
  Eval/commands are faster than calling the expression-processor
* Some non-standard commands may be faster than longhand Tcl commands
  e.g. setat, append, cmp, eqn, concat, store
* Precalculate values wherever possible, especially outside of loops
* Use precalculated lookup tables and arrays (e.g. for Fibonacci values)
* Use conditional statements in the Macro PreProcessor rather than live 
  code to handle static code changes
* Optimise performance and expand Ticol by writing a __stdcall DLL
* You can call the Microsoft Visual C++ runtime library functions via
  calldll*_cdecl
* Division desiring a float result must supply at least one float operand
* You can use casting operators, e.g. [double] to cast from integer in a way
  which aids readability
  e.g.

     puts [/ [double 22] 7] => 3.142857142857143                                
     puts [/ 22 [double 7]] => 3.142857142857143                                
     puts [/ 22 7.0] => 3.142857142857143                                       

* Arrays may be iterated efficiently using [array foreach]
  This is quicker than translating to a list using [array get] with
  [foreach], but note that this iteration is un-ordered
* When using with arrays take care to wrap multi-variable arguments in 
  braces

     WRONG:                                                                     

     foreach x y [array get env] {puts $x=$y}                                   

     RIGHT:                                                                     

     foreach {x y} [array get env] {puts $x=$y}                                 

* Examine the workings of an array's hashtable using [array walk name]
* Variables (including structs) can be examined using [dump varname]
* All standard variables are strings in Tcl
* Tcl lists are well-ordered strings
* An empty string in Tcl is represented as the empty list {} not ""
  e.g. set q {}
* Ticol expands comparison operators to use "" as well as {}
  e.g. set q {}; eq $q ""
* Reduce CPU usage in loops using [sleep] or [doevents]
* Increment numbers using either ++ var or incr var (or -- var or decr var)
* For tips on marshalling data via the [calldll] interface see: pointers
* Windows system errors can be decoded using [win_error]
* If a program appears not to enter loops or exits unexpectedly check for
  option expression ON. You may need to use: option expression OFF
  Eval-based flow-control logic will not work with the former
* Execution stack-tracing and breakpoint single-stepping can be useful
* Try using [calc] instead of [expr] as it may be more efficient since it
  can pre-optimise at the preprocessing stage
* Long comments may be toggled as follows:

  /*                                                                            
  Commented code which is inactive ...                                          
  # */                                                                          
                                                                                
  # /*                                                                          
  Commented code which is active ...                                            
  # */                                                                          

* Take care commenting out 'if' statements because the short comment
  character # is also a macro prefix, and '#if;' is a macro command

See also: performance, faq

help topic

No, you need to type an actual help topic!                                      

For example:

  help cli
  help command line
  help faq
  help load
  help print
  help run
  help tips
  help troubleshooting

Topic and Picnic are English chocolate bars which became popular in 
the 1960s ...

            .................................                                   
           /  -.-   .--.   .-.   .   .-.    /|                                  
         /    /    /  /   /-'   /   /      / |                                  
       /     '    `--'   '     '   `-'    /  |                                  
     /.................................../  /                                   
    |                                   |  /                                    
    | Milk Chocolate Bar with Hazelnuts | /                                     
    |            Price 3d               |/                                      
    '-----------------------------------'                                       

    https://en.wikipedia.org/wiki/Topic_%28chocolate_bar%29

Try again!

See also: help help

Principle of Least Surprise (POLS)

 Principle of Least Astonishment (POLA)

Hopefully, most of the Ticol language follows the Principle of Least
Astonishment (POLA). Some experimental features have been added but
these have been designed to work as sensibly as possible and to be 
well documented.

I found some of the features of standard Tcl to slightly violate
'POLA'. When considering how to improve or add new features thought
has been given to leaning on existing concepts and paradigms from either
C/C++ or BASIC in order to avoid violating POLA.

Non-decimal numbers follow common or standard conventions

    0NN         Decimal value with leading zero (not octal values)
    0xNN        Hexadecimal values
    0oNN        Octal values
    0bNN        Binary values

    https://en.wikipedia.org/wiki/Principle_of_least_astonishment

See also: why write ticol

Why I Wrote Ticol Tcl?

    Why write a Tcl interpreter at all?,
    Why not learn to use PowerShell, PHP, PERL etc?

I was interested in Tcl, and I wanted to experiment writing a script
language which would be able do a number of tasks attempted with batch
scripts. When I started the project I had a lot of free time outside work
and thought, (rather foolishly as it turns out) that it might only take a
couple of months to complete something usable and as a fun hobby project. 
That was a few years ago. I've since been adapting the language constantly
to add new ideas and to solve new tasks. At the time, I was also looking 
for a script language back end for a mail server I was writing and
thought this would fit the bill.

It was an interesting challenge expanding and transforming the original
500-line Picol source code and transforming it into something with well
over 150,000 lines of C++ code (excluding plugins). To add extra interest
I decided to write many of the low-level functionality such as hashtables
and expression-handler from the ground up rather than use public available
standard libraries. Obviously this  wouldn't be recommended for a
commercial project as it would consume far too much time.

I'm not a professional C++ programmer, so in many ways Ticol will fall 
quite short of some people's expectations. It doesn't have regular-
expressions, a bytecode compiler, static syntax validation, full socket
support, multi-threading, and doesn't link into standard Tcl. Some
features have been completed only to the degree that they satisfied
personal requirements.

I have no pretensions about Ticol. As an end-product it originally written
to do some fairly mundane scripting tasks I needed where I found batch 
script too limiting, PowerShell often too bulky or cumbersome, and 
standard Tcl too problematic for my specific requirements. Some features
may not be implemented perfectly or fully. I wanted something tiny and
truly portable with easy use of optional plugin expansion to extend the
language and quickly add new features as required

Drawbacks
---------
Ticol has drawbacks, the big one is that, unlike recent versions of
standard Tcl, it doesn't handle Unicode, but I really didn't need it and
also didn't want to have to rewrite dozens of underlying C++ libraries
I had which were available for use. Had I had to rewrite these for
Unicode/MBCS support then the project would never have been realised. 
Also, Ticol does not perform in-depth code syntax analysis before
execution so scripts need to be written and tested very carefully.
Perhaps someone will write a static code-analyser in Tcl?

For any of my own special requirements I can quickly write a DLL in C++
to perform a more specific task and then use Ticol as a wrapper or "glue"
make the DLL useful. I think this is what John Ousterhout intended
Writing a custom plugin DLL for a specific job isn't particularly
difficult but it does require following a set of rules via an API

There are some features in Ticol Tcl which may be transient and which,
being less than satisfactory, may be removed or modified.

ActiveState Tcl falls back to big-integers when numbers exceed 64-bit
integer range. Ticol does not. ActiveState Tcl has full socket and
multi-threading support as well as regex and Unicode support.

Real World Uses
---------------
What have I used it for? 

A number of small but moderately complex automation tasks, including
a script which ran a detailed  24/7 LAN diagnosis console with 
recommendations of course of action both as a console and web CGI
application. This also queried a Safenet HASP licence server and emailed
notice of impending licence expiries and performed a number of log
filtering and housekeeping tasks, combined with batch scripts. It also 
provided real time discovery of network HASP licence keys and was used for
a few years as part of an RLM licence expiry check and automated licence
renewal system. It filled in the gap between Windows batch scripts and
PowerShell without needing dotnet dependencies to be dealt with.

In more recent years Ticol has been used extensively to generate 
historical replica railway signalling simulator and as a back-end
script language to animate a physical replica illuminated diagram.

The HTML and RTF versions of the Ticol online man (help) file are also
exported using Ticol, complete with syntax highlighting provided by the 
ticol_highlight.dll plugin I also have Ticol working nicely as a
substitute for PHP for simple Web server CGI back-end scripts. In fact
most of the active work performed by the language has been CGI based
running from a portable Apache webserver.

Alternatives
------------
There are certainly far better small Tcl and similar languages out there
such as "Jim", "Little" or "Ch C++" (and I really like Ch)

This discussion about why Tcl is misunderstood may be useful

    http://antirez.com/articoli/tclmisunderstood.html

If you don't like Ticol and don't find it very useful then perhaps you
would like to try one of these excellent alternatives instead?:

    http://www.little-lang.org
    http://jim.tcl.tk/index.html/doc/www/www/index.html
    https://www.softintegration.com

Why Not Use PowerShell?
-----------------------
Why not use Tcl if it does the particular job you require?

I disliked having to use PowerShell. It seemed unwieldy, over-complex,
messy, fragile and far too system-dependent or system-interwoven. For
many tasks it was overkill and ill-suited to tasks which were beyond
the scope of batch scripts but still not terribly complex, I also needed
to be able to apply some standard admin scripts to a wide variety of
Windows systems with varying versions of the underling dotnet runtime
and often without PS installed. A prime motivator was continually 
hitting the frustrating limits of Windows batch scripting, which was often
almost (but not quite) good enough to do the job.

There were occasions where PowerShell was the perfect tool for the job
such as 365 or mail server admin and where I had full ownership of
the system in order to ensure PS was fully updated but I always had an
eye on keeping things as simple as possible, at least system configuration
wise.

Some of these systems would have been software or engineering team test
machines and with a fixed/stable environment which I couldn't tamper with
and where a truly portable script interpreter was needed which would have
zero systemic modification demands on the host.

On most of the systems the underlying dotnet configuration could not
be altered by system admins as these test hosts required specific
dotnet configurations. This placed restrictions on PowerShell usage.

Batch scripts would have been ideal as they had the largest flexibility
and commonality across all O/S from XP to Server 2012 and with fewest
additional dependencies but batch scripts very quickly become unwieldy
for even moderately complex tasks. Ultimate real world solutions were
often a mixture of batch script, PowerShell, VBScript and Tcl.

Another issue was that while I had time at home to work on a hobby project,
free time in the workplace was a luxury and pragmatism was usually the
order of the day. Re-use code, keep things simple and easy to maintain
I found it less time-wasting to fix a bug or add a new feature to Ticol
than spend ages trying to get a PS script working properly.

Moreover, it was fun writing something to do a specific task even if the
tasks, relatively speaking, were fairly trivial. 

These articles may be interesting:

    https://tcl.apache.org/html/why.html
    https://arstechnica.com/civis/viewtopic.php?f=17&t=154990
    https://en.wikipedia.org/wiki/Principle_of_least_astonishment
    https://outsourcedguru.wordpress.com/2016/02/22/why-powershell-sucks-so-badly/comment-page-1/

See also: faq, licence

FAQ - Frequently Asked Questions

In this section the most anticipated questions about Ticol Tcl are covered

    About Ticol                                                                 
    Arrays                                                                      
    Bugs                                                                        
    Classes                                                                     
    Configuration                                                               
    Debugging                                                                   
    DLL Files and Linking to External DLLs                                      
    Documentation                                                               
    Editor, CLI and IDE                                                         
    Efficiency and Performance                                                  
    Errors                                                                      
    File and I/O Issues                                                         
    Flow Control                                                                
    Graphics and Windows GUI                                                    
    International                                                               
    Language and Syntax                                                         
    Licence and Copyright                                                       
    Linked lists                                                                
    Macro Language                                                              
    Math                                                                        
    Plugins                                                                     
    Procedures                                                                  
    Security, Script Tampering and Intellectual Property                        
    Strings                                                                     
    Structs                                                                     
    Testing                                                                     
    Threading                                                                   
    Tricks                                                                      
    Useful References                                                           
    Variants                                                                    
    Windows Console                                                             

See also: Some Common Mistakes

About Ticol
-----------
Q) What is Ticol? What is its intended purpose?
A) Ticol is a personal project to write a compact and portable Tcl
   interpreter for general workplace IT administrative script and hobby use
 
   It has not been specifically designed for public use, but it is offered
   for use by anyone who finds it useful. Some may find shortcomings, such
   as the lack of Unicode support a problem, but it should be understood
   that the author does not require this feature, so this and other
   unnecessary features will NOT be added. Ticol makes extensive use of the
   author's own C++ libraries developed over many years of hobby C/C++ 
   programming

   Ticol is not specifically recommended for use in a high-risk production
   environment without extensive suitability testing

Q) How do you pronounce the name?
A) Ticol == "tickle". This is a word-play on the original source project
   and the fact that the Tcl icon is a feather. The name derives from the
   original source project - 'Picol'

Q) Why write a Tcl interpreter, why not learn to use PowerShell, PERL etc?
A) See: why

Q) In what major ways does Ticol differ from standard Tcl?
A) Ticol is designed to be Windows-specific and is not intended to ever be 
   a cross-platform language. Additionally, as this is a personal project it
   has been written to be ANSI-specific and primarily for use on legacy 
   platforms such as Windows NT, 2000 and XP. Compatibility is, however, 
   maintained for the most recent versions of Windows on 32 and 64-bit 
   platforms

Q) Does Ticol support Unicode
A) No. It is ANSI-specific. If you'd like a Unicode version then I invite
   you to get coding and write your own Tcl version

Q) I really don't like Ticol. What shall I do?
A) Either use another flavour of Tcl or have a go at writing your own using
   the original and excellent 500-line Picol source as a starting point
   I recommend ActiveState Tcl

Q) Can I contribute to Ticol?
A) Yes, you could make a small donation to help with equipment costs
A) Good (well-detailed) bug reports are welcomed. Example scripts should
   be included if possible 

Q) Are there any alternatives to Ticol?
A) Yes, the "Jim" programming language is pretty cool
   http://jim.tcl.tk
   ...and ActiveState Tcl is excellent!

Q) Does Ticol include a full socket library?
A) No. Although I wrote the SMTP libraries I'm not a particularly expert
   socket programmer and didn't really need much in the way of TCP/IP 
   sockets so haven't added general socket functionality other than HTTP
   retrieval. It would be feasible to write a socket command plugin 
   though

Q) What is Tcl anyway and who uses it?
A) Tcl is a simple and usually interpreted language with some visual
   similarities to "C"
   It is used by Cisco(TM) within routers, by ANSYS, Boeing, NIST and
   many others:
   http://wiki.tcl.tk/37389

Q) Isn't Tcl just "toy" language?
A) No, see this thread: 
  http://wiki.tcl.tk/3970

Q) Does Ticol Tcl perform bytecode compilation
A) No. It is strictly an runtime interpreter although the Macro Pre-
   Processor will perform some optimisations to increase speed

Q) What order does Tcl perform evaluation in?
A) Evaluation for square brackets is performed in the reverse order to that
   which might be expected. For example, the statement:

   puts [call [return]]                                                         

   Will be evaluated in the order of innermost-to-outermost brackets:

   1. return  (evaluated first)
   2. call    (evaluated 2nd)
   3. puts    (evaluated last)

   Evaluation arguments which are not enclosed in square brackets is
   evaluated in normal (left to right) order. For example:

   eval {22/7.0}                                                                

   Will be evaluated in the following order:

   eval      (last)
   22/7.0    (first)

Arrays
------
Q) Does Ticol support arrays? 
Q) Does Ticol support multi-dimensional arrays?
A) Basic, single-dimensional arrays are supported and true arrays of arrays

   Multi-dimensional arrays e.g. $a(1,2,3) aren't directly supported in the
   Tcl language, and they are typically emulated by embedding commas (or
   some other separator) in concatenated array index parameters. As this is
   a common Tcl "kludge" and you have to take care to avoid carelessly
   introducing unquoted whitespace into the array subscript.

   a(1,2)                                                                       

   True, arrays of arrays are also supported via nested dereferencing.
   So, ${$a(1)(0)} will reference array element 1 of a(), subscript
   item 0

   See: help arrays
   See: help arrays of arrays
   Simple Variant arrays are supported for use with CallDLL* and structs
   See: help varray

Q) Can fixed, multi-dimensional arrays be emulated?
A) Yes, using the ticol_carray.dll plugin. See the 2d_carray.tcl example

    # Very basic example                                                        
    lib ticol_carray                                                            
    set dim_x 5        # A 5 x 3 matrix (addressed 0..14)                       
    set dim_y 3                                                                 
    set h [carray create [* $dim_x $dim_y]]  # Allocate NULL array              
    proc array_set {handle x y value {rvalue}} {                                
        # [in] x and y should be array-base 0                                   
        carray set $handle  [+ [* $y $::dim_x] $x] $value $rvalue               
    }                                                                           
    proc array_get {handle x y} {                                               
        # Return Lvalue only                                                    
        # [in] x and y should be array-base 0                                   
        return [carray get $handle [+ [* $y $::dim_x] $x]]                      
    }                                                                           
    array_set $h 1 0 "1,0"                                                      
    puts "1 0 -> '[array_get $h 1 0]'"                                          

Q) I see blank lines where comments were included in the source code when
   I use the /ECHO command. Why is this?
A) If comments are indented using whitespace then that whitespace will be
   retained. This is by design since Ticol cannot know if the whitespace is
   significant or not (/ECHO also tries to preserve code readability)

Bugs
----
See: help bugs

Classes
-------
Q) Does Ticol support classes and object orientation
A) No, this is not part of the design
   However, structs offer some operators which may be used to emulate some
   aspects of class-like behaviour but is very cumbersome. Since proper 
   object-oriented principles aren't supported the use of class-like 
   behaviour in Ticol is not encouraged and true object-orientation
   concepts such as data privacy, inheritance, constructors and destructors
   are unsupported. Structs are permitted and these may be passed to 
   procedures ([proc])

   See: class, ->, =>, ->>, static, args
   See: struct

Configuration
-------------
Q) How can I find what version of Ticol Tcl I am running?
A) Use: puts $tcl_version

Q) Does Ticol have a configuration (INI) file?
A) Yes. ticol.ini
   It also has an autoexec file (autoexec.tcl) which can also be used to
   configure

   See: help ticol.ini (from the Ticol CLI (Command Line Interpreter))
   See: autoexec.tcl

Q) Is there a script which can be run at startup to configure standard procs
   for the CLI?
A) Yes. Create a file called 'autoexec.tcl' in the same folder as ticol.exe
   This is enabled by default. You can disable using the command-line using
   the /NA argument or permanently within ticol.ini.

   See: help autoexec.tcl

Q) Can I undefine commands automatically at startup?
A) Yes. Either by including undef commands within an autoexec.tcl file or
   by using a CommandUndefList entry in TICOL.INI

Debugging
---------
Q) Does Ticol support runtime expression assertion for debugging?
A) Yes, use the [assert] command

   See: assert

Q) Can [assert] be disabled globally as in C/C++?
A) Yes, use:    #define ndebug
   at the start of your script to disable, the same as in C++

   See: assert

Q) I get no output on either a console or CGI script, why?
A) Code will be generating an error but you have [option echo off]
   Use [option echo on] and if running as a CGI script then enable the
   following in TICOL.INI. Always enable 'option echo on' if a script
   halts unexpectedly.

   [config]                                                                     
   Debug=True                                                                   
   CGIDebug=True                                                                

   See also: cgi, debugging

Q) I have a recursive procedure which is failing or triggering an
   exception. How can I see what is happening?
A) Use: [trace]. e.g. for a proc called 'foo', use; trace add foo
   which will show the proc call trace along with any arguments
   You can also use single-step debugging with [halt] and [watch]

   See: trace, breakpoint, halt, watch

Q) Can I single-step or use breakpoints within Ticol?
A) Yes. enable breakpoint/debugging mode using either ticol.exe /BP,
   ticol.exe /DEBUG or using the command:

   option breakpoint on                                                         

   You must then issue one or more [halt] instructions within your script
   or at the Ticol command-line interface (CLI)
   When the [halt] instruction is met you may single-step and trace the 
   execution stack

   See: option breakpoint, breakpoints, halt

Q) Do errors show the execution stack?   
   Not by default. Stack-tracing may be enabled separately using the command

   option stacktrace on                                                         

   To provide detailed execution stack trace information should an error be 
   raised

   See: option stacktrace, breakpoints, halt

Q) How can I trace used memory within a script?
A) Use the following example code
   Note that it is normal to see a small amount of memory allocated at the 
   start of a script and periodically, for object allocation. However, 
   memory used should not show a constantly increasing trend:

    proc show_mem_used {m} {                                                    
       textcolor magenta                                                        
       global line                                                              
       puts "[++ line]: Used=[expr [mem_used]-$m] byte(s)"                      
       textcolor                                                                
    }                                                                           
    set start_mem [mem_used]                                                    
    puts "Start memory: $start_mem"                                             
                                                                                
    # Do some tasks here...                                                     
    # e.g. time {struct create s {f1 10}; struct unset s} 1000                  
                                                                                
    show_mem_used $start_mem                                                    

                                                                                
    Start memory: 5013504                                                       
    1: Used=0 byte(s)                                                           
    17 microsecond(s) per iteration                                             
    2: Used=4096 byte(s)                                                        

Q) How can I trace function calls?
A) Use:
                                                                                
      trace add <function-name>                                                 

   to add a trace item, and:

      trace remove <function-name>                                              

   to remove a trace item

   See: trace

Q) How can I debug a problematic Ticol CGI web script?
A) The problem with CGI scripts is that errors may result in a server "500"
   error and generate no useful output.
   You should look at the server's error.log and access.log files
   Add this code as the first line of any CGI script:

   printf "Content-type: text/html\r\n\r\n"                                     

   or:

   puts "Content-type: text/html\r\n"                                           

   Run the script in console mode using the /CD (CGI Debug) argument
   Place a halt command near the suspected cause and run the script from the
   command-line using the /BP (Breakpoint) argument and single-step 
   execution. Enable the following in TICOL.INI during code development

   [config]                                                                     
   Debug=True                                                                   
   CGIDebug=True                                                                

   See also: cgi, debugging

Q) The /G (graph) performance trace shows 1 ms for all commands in Windows
   NT, 2000 or ReactOS, why is this?
A) The necessary precise nanosecond (ns) timing functions are not available
   in these versions of Windows and are thus implemented as microseconds
   (ms). When timed in nanoseconds, the time for most command calls will
   show as '1' for all but the slowest functions and on the slowest systems

DLL Files and Linking to External DLLs
--------------------------------------
Q) Can a DLL be loaded permanently for the duration of a script?
A) Yes, use dll_open to return a handle to the DLL and dll_close to close it

   See: calldll

Q) What return types can be called?
A) Long (DWORD), string (LPSTR), double and COM Variant struct pointer

Q) Are there any examples showing the use of calldll?
A) Yes, search:  help calldll examples

Q) What types of DLL linkage can calldll link to?
A) __stdcall and __cdecl only. Other linkage styles are unsupported

Q) Is linking to COM interface DLLs supported?
A) No, COM DLLs are not supported and there are no plans to add this
   If you want this you will have to write a COM wrapper

Q) How does [calldll] load DLLs?
A) DLLs can be loaded for the duration of the script using 
   dll_open/dll_close or they can be loaded/unloaded on a per-call basis
 
   Note that the Windows IPHLPAPI.DLL will leak memory if loaded 'per call'
   this is a known Microsoft DLL problem

   See: help calldll

Q) Are plugin DLLs supported which can add new commands?
A) Yes. Using the [lib] command and if the plugin programming guidelines
   are adhered to. Lib will load exported commands in the interpreter's
   namespace. By default all are loaded by this can also be filtered

Documentation
-------------
Q) Is there a manual for Ticol?
A) Yes, this is it. Also available in Windows Help (CHM) and HTML format
   Although Ticol has additional commands, the underlying language is the
   same as for standard Tcl. It is recommended that you search online and
   visit the site at 
   http://wiki.tcl.tk
   You should also read LICENCE.TXT and any accompanying README.TXT

Editor, CLI and IDE
-------------------
Q) How can I load a file into the interpreter?
A) From the ticol.exe CLI, use the command:   load filename
A) From the command-line, you can run using:  ticol.exe filename
   Note that in both cases the '.tcl' filetype is optional
   If you are loading/running a TCX file then the filetype must be
   specified

Q) How can I tell what file is loaded?
A) It is displayed on the console Window title bar if loaded

Q) Can I view a loaded script from the CLI?
A) Yes, use the [dump] command to show the script with syntax
   highlighting

   See: dump

Q) What is the maximum command-line length for the CLI?
A) 4096 characters (ANSI/ASCII text only)

Q) What is the maximum file size which can be loaded/evaluated?
A) This is limited only by memory but very large files (> 1mb) may be
   unwieldy and slow to use. Consider breaking up into smaller files
   and calling these using [eval]

Q) What editor is recommended for Ticol?
A) Notepad++ is highly-recommended and is used by the author
   Website:
   https://notepad-plus-plus.org/

   See: help editor
   This topic also explains how to input the Ticol keywords

Q) Some commands give no output even under error conditions. Why?
A) Unlike ActiveState Tcl the CLI echo is OFF by default
   You may use [try/catch] to intercept and handle error conditions
   Standard Tcl echoes command results of all commands by default
   Thus a command issued from the CLI would echo a result twice
   Within scripts you may wish to enable: 'option echo on' if you don't
   intend to use [puts] or [printf] to display command results
   Use the command:   option echo on      :to enable this feature

   See: help option
   See: puts, printf
   See: try, catch

Q) Some commands issue output twice. Why?
A) When running in the CLI you will see an additional results echo if you
   have 'option echo on'. 
   You may disable this using 'option echo off'

   See: help option

Efficiency and Performance
--------------------------
Q) Do you have any tips for writing big programs?
A) Yes, 

   See: big programs, efficiency

Q) Is there any way to analyse script performance?
A) Yes. Run ticol.exe with a script and the /G (Graph) argument to generate
   a performance analysis graph. You can then discover which commands are
   called the most and substitute more efficient commands or expressions
   Example:    ticol myscript.tcl /g

   See: performance, graph

Q) How can I improve performance
A) Write a plugin DLL LIB module which uses C++ code to perform time-
   critical calculations. The C++ interface is quite simple and the 
   C++ source code for the interface can be made available.

   See: performance

Q) Are there any commands which can enhance performance?
A) Yes, several command are supplied with Ticol which implement common 
   routines which might be implemented longhand in Tcl. For example [is_mod]
   offers a rapid way to test modular division

   See: performance

Errors
------
Q) I get a "missing operand" error in [expr], What does this mean?
A) Ticol behaves the same as ActiveState Tcl when handling null (empty)
   "" strings in that empty strings don't present themselves as arguments
   to [expr] and some other commands. If a variable is set to "" and then
   evaluated then no argument can be passed and the expression handler 
   will see a missing argument or "operand"

   Example error using ActiveState Tcl with 'a' set to an empty string:

       % set a ""                                                               
       % puts [expr $a eq "hello"]                                              
       missing operand at _@_                                                   
       in expression " _@_eq hello"                                             

   A workaround is to wrap any strings likely to be "" in braces as {""} or
   to avoid using string operations within expressions and to use commands
   instead

       % set a ""                                                               
       % puts [expr {$a} eq "{}"]                                               

       1                                                                        

                                                                                
       % set a ""                                                               
       % puts [expr {$a} ne "hello"]                                            

       1                                                                        

Q) A [goto_block] doesn't execute at all
Q) I get an unexpected error relating to command syntax in a [goto_block]
A) Check that any initialisation commands at the head of the [goto_block]
   are wrapped within a dummy [goto_block] entry

   Example:

    goto_block {    # WRONG                                                     
        set a 1     # Can't initialise outside a defined block                  
        set b 2                                                                 
        one {       # First goto label ...                                      
                                                                                
    goto_block {    # RIGHT                                                     
        0 {         # Dummy block correctly used as an initialiser              
            set a 1                                                             
            set b 2                                                             
        }                                                                       
        one {       # First goto label ...                                      

Q) I have a loop which hangs and doesn't exit. I can't see anything wrong
   with the expression argument. Why is this?
A) First, check for the correct [option expression] statement which
   matches your coding style. If this setting does not match you could end
   up with infinite loops since Ticol will try to evaluate a constant value
   
   Example:               # Will evaluate only the first argument

   option expression off                                                        
   set i 0                                                                      
   while {$i < 10} {...   # Never executes ($i evaluated and is FALSE)          
                                                                                
   option expression off                                                        
   set i 1                                                                      
   while {$i < 10} {...   # Loops indefinitely ($i evaluated and is TRUE)       

   Correct:

   option expression on                                                         
   set i 0                                                                      
   while {$i < 10} {...   # Works OK ([expr] used to evaluate [<])              
                                                                                
   option expression off                                                        
   set i 0                                                                      
   while {< $i 10} {...   # Works OK ([eval] used to evaluate [<])              

   See: option expression, flow control, expr, eval

Q) I have a statement (e.g. [switch]) which has the correct argument count
   but I still get an error message relating to invalid syntax, why?
A) Check for arguments which are uninitialised (empty) variables or which
   have not been declared in advance. If auto-declared as arguments they 
   will be initialised to {} (the empty list/empty string)

Q) [for], [if] or other statements don't seem to work. Why is this?
A) Check your expression types. If you have used 'prefix expressions' then
   you cannot use 'option expression on'. Either wrap the prefix expression
   in square brackets within the flow-control statement or use 
   option expression off
   Ensure you use [puts], [printf] or [option echo on] to see output

Q) I get "foreach requires matching pairs of lvalue and rvalue (3 found)"
   with the following statement

   foreach x y [array get env] { puts $x=$y }                                   

A) You need to wrap the values x and y in braces as follows

   foreach {x y} [array get env] { puts $x=$y }                                 

Q) I get a 'procedure already defined' error when running a script
A) Either you have defined a procedure with the same name twice within your 
   Tcl script or you are trying to restart and re-run a Tcl script which has
   halted. You may undefine unwanted commands or procs by using the 'rename'
   command. It is recommended that proc definitions are wrapped with a 
   'defined' statement 
   See: help defined

Q) I get an error '<expression>' is not recognized as an internal or 
   external command. Why?
A) You have a flow-control (if, for, switch etc.) expression which is in 
   expression format rather than eval format. Expression format is 'infix' 
   while eval format requires a leading command name. Hence the lvalue will
   be evaluated as a command with 'option expression off'. To resolve the 
   problem change the configuration in TICOL.INI or set 
   'option expression on'
A) You have produced a result which is, somehow being called as a command

   See: help option

Q) Strings with C-like escape sequences don't work from the CLI
A) CLI interaction with Windows requires C-escapes to be disabled
   You can enable escapes within strings if you set the following options:

   option autoexec off                                                          
   option escapes on                                                            

   Strings with escape sequences such as \n will work fine other than from
   the CLI interface

   See: help option

Q) A script halts with no error message, why?
A) Use: [option echo on] to ensure error information is shown
A) Wrap procedures and commands in [catch] statements and trap errors
A) Use a [halt] statement before the offending code and single-step using
   ticol.exe scriptname /bp

Q) When I run a script from the CLI sometimes I get extra output displayed
   when the script finishes
A) When running from the CLI the results of the script are displayed
   This is suppressed when running from the Windows command-line

Q) A macro variable isn't getting replaced, why?
A) Macro variables aren't replaced within quoted strings
   Check that the variable is outside a quoted string

File and I/O Issues
-------------------
Q) How can I print to an open file?
A) Use [puts] with the file handle of an open file as the first paramter

    set fp [open filename wb]                                                   
    puts $fp "Hello\r\n" -nonewline                                             
    close $fp                                                                   

Q) How can I print to a file using formatted [printf] style output?
A) Combine [format] with [puts] as follows

    set fp [open filename wb]                                                   
    puts $fp [format "%s %i %f\r\n" One 2 3.0]                                  
    close $fp                                                                   

Flow Control
------------
Q) How can I exit or 'return' from a child script which has been spawned
   by a parent e.g. using [source script] ?
A) The spawned child script may be exited prematurely by [return -code exit]
   [return] or [return -code ok] at root level will not cause a child script
   to exit and return to the parent

Graphics and Windows GUI
------------------------
Q) Is Tk graphics capability supported?
A) No, Ticol is designed for use as an administrative (ANSI) console script
   language there is no Tk or GUI support in Ticol other than [msgbox], 
   [inputbox], [get_fileopen] and [get_filesave]. You may, however, call 
   external DLLs or the Windows API directly via the calldll plugin

International
-------------
Q) Does Ticol support non-US/UK or international languages?
A) No. It is an ANSI only language. However external libraries can be called
   such as AutoIT and these can handle Unicode strings.
   You are recommended to use Active-state Tcl or some other Unicode
   alternative version of Tcl

Q) When will Unicode/International capability be added to Ticol?
A) Never. This is a program primarily for my own use which I have chosen to
   share with anyone who finds it useful

Language and Syntax
-------------------
Q) Can nested [||] or [&&] statements be made easier to read?
A) Yes. Avoid the need for complex, nested and bracketed logic statements
   by using either [expr], [all] or [any]

   if {|| $a [|| $b [|| $c $d]]} ...    # Nested logical or                     
   if {any $a $b $c $d} ...             # Inline logical or                     
   option expression on                                                         
   if {$a || $b || $c || $d} ...        # Inline logical or                     
                                                                                
   if {&& $a [&& $b [&& $c $d]]} ...    # Nested logical and                    
   if {all $a $b $c $d} ...             # Inline logical and                    
   option expression on                                                         
   if {$a && $b && $c && $d} ...        # Inline logical and                    

   See: help any, all, help expr

Q) Does Tcl perform 'lazy evaluation' on [&&] expressions?
A) Ticol cannot perform lazy evaluation. Tcl always evaluates every []
   statement within an expression. Thus expressions which contain invalid
   statements within some context will always be executed
   e.g.
   if {&& [valid command ...] [invalid command ...]} { ...
   will execute [invalid command ...]

Q) Tcl is alleged to be "typeless" how come you refer to numeric types?
A) Tcl is *internally typeless* (generally speaking). The values represented
   at source code level are not typeless. There is a category difference
   between internal representation and non-internal presentation of data

Q) Is backslash handling in Tcl a nightmare?
A) Yes. See: backslash

Q) [day_of_week] returns unexpected results for dates before 1583. Why?
A) To keep code simple, the Gregorian system is used for all dates and
   in lines with changeover rules for 1583 which are commonly used
   The actual dates used around the world and over time are varied and
   quite complex. The command does not use the Julian calendar system

Q) Can a variable name contain a space?
A) Yes. Either enclose double-quotes or follow normal backslash 
   escaping rules. However, variable names with spaces are not recommended

Q) Can a variable name be one or more spaces as per standard Tcl?
A) Yes. To assign you must wrap the space in curly-braces, thus:

    set { } Hello                                                               
    puts ${ }                                                                   

Q) Can I do lambda or 'lambda-like' anonymous functions/procs?
A) Yes

   See: apply, lmap and lambda

Q) Are const enums supported?
A) Yes

   See: enum

Q) How do I pass a reference to variable up a hierarchy of [procs] ?
A) Pass by variable name from each level
   You must ensure that the inbound var in each proc is dereferenced using 
   $ and that this dereferenced var is set using [upvar]

    # root->[foo]->[bar]->[baz]                                                 
                                                                                
    proc baz x {                                                                
       upvar $x                                                                 
       puts "Endpoint in baz $x is $$x (Level [info level])"                    
    }                                                                           
                                                                                
    proc bar x {                                                                
       upvar $x                                                                 
       baz $x                                                                   
    }                                                                           
                                                                                
    proc foo x {                                                                
       upvar $x                                                                 
       bar $x                                                                   
    }                                                                           
                                                                                
    set s Hello                                                                 
    foo s                                                                       

Q) Are there differences between Ticol and Standard Tcl?
A) Yes, many
   Ticol was developed to implement a subset of standard Tcl with
   a few design differences and a few features I wanted to experiment with

   Tcl doesn't have much in the way of syntax, it is command-based
   An early decision was made not to support Unicode and to be ANSI-only as
   the author had no use for Unicode support and had a large selection of
   existing ANSI C++ library code

   See: help differences

Q) How does Ticol handle special dereferencing cases (e.g. double)?
A) Ticol Tcl supports multi-level dereferencing either using [set] or
   PERL-style multiple $ sign prefixes i.e. $$varname

   See: double dereference

Q) How can I set the following variable which embeds whitespace?
   set q a("Hello world")
A) You cannot embed a double-quoted string within an unquoted string
   You can, however, embed escaped double-quotes within a double-quoted
   string as follows:

   set q "a(\"Hello world\")"                                                   

   Note that this will create an "escaped" string which is not unescaped
   by the macro-preprocessor (MPP). You can unescape this as follows:

   puts [unescape $q]                                                           

Q) Are there any common coding mistakes which can be made when moving to Tcl
   from C/C++ ?
A) Yes. A few quite common ones

   See: help mistakes

Q) What standards have been applied when developing Ticol?
A) Every attempt has been made (given limited time and resources) to 
   implement a subset of the Tcl language as documented at 
   http://www.tcl.tk/
   As this is a hobby program development will be ongoing but may be
   sporadic, intermittent and often experimental

Q) I get a syntax error when executing a script with an if statement?
A) Check that the first brace of the if' body statement is on the same line
   as the if statement itself. If not then you will get an error. The Tcl 
   syntax requires the opening brace to come before any CRLF after the 
   opening if command

   RIGHT:
                                                                                
   if { conditional-test } {  ...   # Correct brace at and of line              
                                                                                
   WRONG:
                                                                                
   if { conditional-test }          # Misplaced brace                           
   { ...                                                                        

   Check if you have flow-control commands set to use expr or eval. Check
   the setting for 'option expression'

   See: help if
   See: help option

Q) Does Ticol support infix flow-control conditional tests?

A) Yes, both infix and prefix operator tests are supported and these apply
   to if, while and for flow-control constructs.

   To enable infix expression logic. Use the command:

   option expression on

   To enable Tcl prefix expression logic. Use the command:

   option expression off

   See: help option expression

Q) How can I comment-out a block of code?
Q) Are long-comments available similar to C/C++ ?

A) Use C/C++ style /* ... */ long comments
A) Use a [goto_block] with a [goto] 'skip' command
A) Use if {0} { ... }
A) Use #ifdef with a macro-variable which has not yet been set

    #ifdef comment                                                              
      ... Some Tcl code or non-Tcl content to be ignored ...                    
      ... this may include syntactically invalid code                           
      ... all content here is removed before execution                          
    #endif                                                                      

    To enable the block later, #define comment before the #ifdef statement

   See: help #ifdef, help goto, help comment

Q) Are short C++ style comments e.g. '//' available?
A) No, but you can emulate them as follows:

    proc // args {                                                              
       # C++ style comment - do nothing. Discard all args on this line          
    }                                                                           

Q) Is the content of a Ticol comment syntactically-significant?
A) No. Since Ticol uses a Macro PreProcessor comments are stripped out
   before syntax is analysed

Q) How can I include a literal hash (#) character in source code as this is
   usually treated as a comment prefix?
A) A hash character may be used in quoted strings
A) Use the escape sequence \#
   Translation and removal of comments is performed by the Macro 
   PreProcessor (MPP) but the MPP will recognise and accept escaped hash 
   symbols

   See: escapes, Macro PreProcessor

Q) How can I iterate an array which has a non-contiguous subscript
A) You may use [array foreach] or [foreach] as follows
   foreach {x y} [array get env] { puts $x=$ }

Q) Are short (one-line) comments allowed at the end of a line?
Q) What is the short-comment syntax?
A) Yes. Unlike standard Tcl, one-line comments are allowed anywhere on a 
   line. The remaining line from the first # character onwards will be 
   ignored. For this reason the '#' character is replaced by '@' in
   the [upvar] and [uplevel] commands.

   A prefixing/separating semi-colon is not mandatory (i.e. ' ;# comment')
   A comment start # symbol is valid only outside of quoted strings

   See: help #ifdef, help #define

Q) Do comments or #ifdef..#endif commenting affect or slow down execution?
A) No. The PreProcessor will strip out all macro and comment lines before 
   they are evaluated. This feature is also used to reduce the size of 
   obfuscated TCX files produced using:  ticol.exe filename /C

Q) Can I have nested #ifdef..#endif blocks?
A) No. These are not supported at present but planned for the future. 
   Ticol macro support is quite basic and doesn't extend to nested 
   #if..#endif constructs yet

   See: help macro

Q) Does the Ticol MPP support complex macro expressions?
A) Yes, via the #if statement which can take Tcl expressions but these
   may be costly to process since each #if loads the script interpreter

   See: macro if

Q) Can I verify or see the preprocessed code content?
A) Yes. If the code has not been encrypted/obfuscated you may use the
   /ECHO command-line argument to echo the resulting code to the console
   This may be redirected to a file using the standard > operator

   Note that code which has been obfuscated cannot be viewed

   See: debugging

Q) How can I add the source code line-number into the program?
A) This can be done via macro variables
   The macro variable #__LINE__ will echo the source code line

   You can assign or display as follows:

   set line #__LINE__                                                           
   puts "Line: $line"                                                           

   or

   puts "Line: "#__LINE__                                                       

   See: help macro

Q) Do Ticol structs support instantiation?
A) Yes. Basic instantiation is supported

   See: struct, new

Q) Does Ticol support a [goto] command
A) Yes. But only as part of a localised [goto_block] command
   The use of [goto] is not recommended and is provided for entertainment
   purposes only

   See: goto_block

Q) How can I call [asc] to get the value of a space?
A) Use [asc " "] or, if the call is embedded in a string use [asc { }]
A) Use [asc [chr 32]] or [asc [chr 0x20]]

Q) How do I go about freeing Windows allocated memory returned from calldll?
A) Pass the return value to a Tcl variable and call [sysfree] or pass via
   another calldll call to free the block
   Windows memory allocation will require the ticol_calldll.dll plugin

   See: calldll, sysfree

Q) How to I zeroise a buffer before passing to the Windows API using 
   calldll?
A) Allocate the buffer using [makestr] then use [memset] to set the buffer
   Memset will clear memory only up to the allocated length of the string
   [makestr] and [memset] are intended only for DLL interface use only
   Windows memory allocation will require the ticol_calldll.dll plugin

   See: makestr, memset, calldll

Q) Are nested [proc] blocks permitted?
A) Yes. However procs are not evaluated or tested for errors until they are
   first called at runtime which may make producing error-free code 
   difficult and debugging problematic

   See: proc, upvar, uplevel

Q) What Abstract Data Types does Ticol support natively?
A) Standard Tcl strings. Since "everything is a string" in standard Tcl this
   includes string, numeric, dictionary and list data. Ticol additionally 
   supports binary structs for use with [calldll] only as well as standard 
   Tcl arrays and stacks. Arrays are implemented as hashtables
   A plugin is also available which offers standard linked-lists

   See:linkedlist
   See also: array, list, stack, linkedlist

Q) How can I save/restore [option] states when calling external scripts?
A) Use the 'push' and 'pop' options
A) Use the following:

   Save:
                                                                                
       set expression_state [option expression]                                 
       option expression off                                                    
       set echostate [option echo]                                              

   Restore:
                                                                                
       option echo $echostate                                                   
       option expression $expression_state                                      

Licence and Copyright
---------------------
Q) Is Ticol free software? (Freeware)
A) Yes. It is free for both personal and business use, but it may not be
   resold. You agree to use it at your own liability with no warranty
   whatsoever and indemnify the author against any claims of liability
   (See the attached licence)

Q) Is Ticol recommended for business-critical applications?
A) No, Ticol is under development and should not be relied on for business-
   critical applications. Businesses may use it for evaluation and testing
   as long as no liabilities are created

Q) Is Ticol open source, can I have the source code?
A) No and no
   Ticol is a derivative work of a BSD project which permits commercial
   /closed-source derivations to be created
   The original Picol source code is freely-available but requires
   some work to fix memory leaks and expand functionality. If you want the
   original source code it can be easily found using a search engine if 
   you search for 'picol'

Q) Awwww... this isn't fair, I *want* the source code, I'm going to tell
   my mum|dad|teacher|brother|etc..
A) See other (and far better) open-source Tcl/Tcl-like projects instead
   where source is freely available (and of far better quality)

Q) What is the purpose of these licence terms?
A) To avoid unreasonable demands, for support, annoyance, or legal
   liabilities being placed on the author

Q) What licence is Ticol supplied with?
A) See: help copyright and
   http://www.linfo.org/bsdlicense.html

Q) My virus scanner reported an infection. Why is this?
A) Ticol contains no intentionally-malicious code. All primary binary
   distributions will have been virus-scanned before release using a 
   commercial virus scanner such as Kaspersky A/V
   However, the binary could have become infected during redistribution by
   a 3rd party or your virus-scanner may be reporting a false-positive. 
   Please cross-check using several other virus-scanners. If in doubt, 
   discard the binary distribution you have and obtain a bone-fide copy
   Ticol should NEVER be bundled with a binary 'wrapper'/installer program
   other than as a clean ZIP or similar archive

Q) Is Ticol guaranteed or warranted?
Q) Ticol trashed my data, my wife divorced me, boss hates me etc. wrecked
   my programming skills, can I sue?
A) No. No warranty of any kind, either express or implied, is offered, and
   by using it you agree to indemnify and hold the author free from any and
   all liabilities either direct or indirect arising from its use
   Every reasonable effort has been made to ensure Ticol is stable, given
   that this is purely a hobby-project and not a commercial one

Q) Can I sell copies of Ticol?
A) No. Resale is prohibited. If you are reselling it you are in breach of
   licence terms and must immediately cease and desist

Q) Why is resale prohibited?
A) The author does not wish to accept contractual liabilities for
   support or to warranty of application in any way

Q) I paid for a copy of Ticol from someone other than the author
   Shouldn't you to provide me free support?
A) No. And you should not have been charged for a copy other than the cost
   of media (CDROM or USB stick)
   And you should ask for your money back

Q) Can I force you to provide support or a warranty?
A) This software is offered gratis and 'as-is' under the licence terms and
   clearly stated to be at used entirely at your own risk

Q) Can I redistribute Ticol from my website?
A) Generally, no. But contact me as admin @ the program's documented 
   web-domain and I will get back to you and work something out if and 
   only if this involves no liabilities, risk or annoyance to the author

Q) Is the documentation included with the main software licence
A) No. The extensive manual (over 900 pages if printed) is covered by
   normal international copyright for written works, but it may be
   supplied free of charge with the binaries

Q) What if I don't like the licence terms?
Q) What if I dislike the Ticol program?
Q) What if I refuse to abide by the licence terms?
A) Stop using this software immediately. 
   You are no longer covered by the licence terms
   Find another product. There are many excellent alternatives

Q) I'd like to negotiate a different licence term, is this possible
A) Contact me and I might discuss

Linked Lists
------------
Q) What type of lists are available?
A) Standard Tcl lists with most key features implemented. Such lists are
   no more than formally-organised (well-defined) strings
   Traditional linked-lists are offered via the ticol_list.dll plugin

   See: linked list

Macro Language
--------------
Q) Is there an #include command for the Macro PreProcessor language?
A) No. It is not required. Use [source scriptname] to include a script
   at runtime

Math
----
Q) How can I use numbers with leading zeroes?
A) This is problem for standard Tcl not for Ticol Tcl which doesn't
   interpret numbers with leading zeroes as octal. Octal values require
   zero followed by the lowercase letter 'o' - i.e. 0oNN

   ..code
       puts [expr 0123*2]   # Returns '246'
   ..end

Q) How can I force a variable to a "float" (double) so it evaluates
   correctly in an expression?
A) Cast to double using the [double] command

   Example:

   set a 22                                                                     
   set b 7                                                                      
   puts [/ $a [double $b]]                                                      

   Result:

   3.142857142857143                                                            

A) Wrap the variable name in braces and append ".0"

   Example:

   set a 22                                                                     
   set b 7                                                                      
   puts [/ $a ${b}.0]                                                           

   Result:

   3.142857142857143                                                            

Q) Are "scientific" format numbers such as 4e23 or 2e64 supported?
A) No, other than via the [atof] and [ftoa] commands and via
   the ticol_sci.dll plugin
   This may be added to ticol_bignum.dll plugin and, natively, later on

   See: atof, ftoa

Q) What are the number ranges used by Ticol?
A) Integers are manipulated as 64 bit values with a signed range
   64 bit signed int  9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
   64 bit unsigned int 10 to 8,446,744,073,709,551,615
   The default integer type is signed. Numbers may be coerced to unsigned 
   using the [unsigned] cast command
   Larger integers (up to 6000 digits) may be used via the big math plugin
   ticol_bignum.dll

   Real numbers are detected automatically by the presence of a decimal
   point value. Coercion can be forced by either appending "/0" or using the
   [float] command
   Floating point values are in the following range:
   double 64 bit signed   1.7E +/- 308 (15 digits of precision)

   See: data type ranges, atof, ftoa

Q) Do I have to bracket math expression when using the [expr] command?
A) No. Operator prioritisation is assigned automatically using standard 
   BEDMAS rules with remaining operators (e.g. +, - being evaluated Left To 
   Right expressions are dynamically rewritten to prioritise * and / 
   operators. However, expression within source code may be easier to read
   and maintain if properly bracketed

   See: help expression

Q) Complex expression results produce fractionally different results to
   those of cscript or other interpreted languages such as Visual BASIC
   Why?
A) Tests show that some expressions in cscript or wscript (and VB) are
   rounded ups to the nearest integer. This may be due to these languages
   using a 'float' value for calculation. Ticol uses 'double' for all non-
   integer calculations and 64-bit integers for integer calculations and 
   does not automatically promote integers to reals. You can override this
   within expressions by using number-casting functions e.g. double()
   Comparing float/double values is a well-known programming problem

   See: help expression

Q) Are there any mathematical accuracy issues with Ticol?
A) Ticol will manifest any or all of the underlying accuracy issues present
   in the MSVC runtime etc. when using large floating point values, but the
   language is not intended for scientific precision but as a script
   language. In particular, very large 64-bit values may require the use of
   an [unsigned] cast

   See: help math, big math

Q) When applying a math operator such as * or / to mixed integer and real 
   numbers will the result be an integer? 
A) If one or more operands of an operator expression are real numbers
   then this operator expression will be evaluated as a 'double'
   This is in the design specifications for standard Tcl

   See: help math

Q) I am calling [expr] with a complex math expression and it either returns
   zero or a wholly inaccurate result. Why?
A) Tcl, by language definition interprets all numeric values automatically. 
   There is no concept of fixed typing but various means of coercing are 
   provided by Ticol including non-standard type-cast commands
   This may cause problems with expressions which involve operations which 
   expect floating point (real) numbers such as division. Real numbers are
   interpreted as so by the presence of a decimal point. Otherwise, integer
   type will be assumed

   Example:
   expr "22/7" returns 3 but 22/7.0 will return 3.142857142857143

   expr 123/(0.123*10)                                                          

Q) Very large numbers come out negative. Is there a fix for this?
A) Ticol uses signed 64-bit integers for all integer calculations. As this
   is a script language high-speed performance is not expected and is thus
   less of an issue. You can coerce these values to be unsigned using the 
   [unsigned] cast command or expression function
   Preferably use the ticol_bignum.dll big math library which offers the
   [big] command and supports up to 4000+ digit numbers

   See: help unsigned, big math

Q) puts [expr "not 0"] does not work, why?
A) The 'not' command is not implemented within [expr]
   Likewise for 'and', 'or', 'xor' long-form commands
   Use the '!' operator

   puts [expr "!0"]  # Outputs 1                                                

Memory
------
Q) Does Ticol support heap memory allocation?
A) Yes, using standard "C" malloc, free, memcpy and memset
   These are provides to help interface with external DLLs via [calldll]
   and for use with structs and not for regular script use
   Extreme care should be taken with the used of these commands as 
   careless use will produce unstable code and may also make the 
   interpreter unstable
   These commands were added only for the interest and amusement of the
   author. If you object to these features, don't use them

   See: malloc, free, memcpy, memset

Plugins
-------
Q) Is it possible to take a screenshot of a image or photograph of a bmp-
   encoded file and recreate the payload contents from it?
A) Yes The BMP plugin uses the bitmap image as a simple data-container
   and does not follow byte-padding rules but if you follow these steps
   you should be able to reproduce
   1 Paste the image into a paint editor
   2 Resize to the correct bit width. (Rows must be a multiple of 4)
   3 Decrease colour depth to 2 colours (the palette is ignored)
   4 You may need to produce a negative image
   5 Save as a Windows BMP image

Procedures
----------
Q) How can I share procedures between multiple tcl applications?
A) Place the desired procedure definitions in a TCL or TCX include file
   Be sure to control definition using if {[defined procname]} or
   forcefully undefine using undef procname -nocomplain before definition

Q) Can I find out the name of the procedure I am 'in' at runtime?
A) Yes, use [info procname]

    proc foo {} {                                                               
       puts "Currently executing proc \[[info procname]\]"                      
       ...                                                                      

Q) How do I get the list of arguments or other information inside a proc?
A) Use the following:
   [info args procname] to get a list of the proc's arguments
   [info body procname] to get the script text for the proc

Q) What are valid procedure names?
A) A procedure may consist of the upper and lower-case letters A-Z as well
   as spaces. Where a proc has spaces, that name must be wrapped in double-
   quotes. Example:

    proc "a b" {} {puts "Hello from proc a b"}                                  
    ["a b"]        # Either                                                     
    "a b"          # Or                                                         

See also: procedures, info

Security, Script Tampering and Script Protection
------------------------------------------------
Q) Are security issues discussed in the help file?
A) Yes, see: security issues

Q) Can Ticol scripts be protected from snooping or tampering by users?
A) Yes. Scripts can be encrypted, or, more accurately, 'obfuscated' since
   the encryption code is NOT 'military-grade'. This should be sufficient
   to prevent all but the most determined snooper. Scripts can be "bound"
   (or, "locked") to execute via password, workstation name or username 
   restrictions

   Additionally, obfuscated snippets of code can be embedded within scripts
   by using the [tcx] command

   See: help protection

Q) Is code-obfuscation strong encryption?
A) No. It is intended to be sufficient to prevent casual code tampering,
   not to deter determined attackers or encryption experts
   Code obfuscation should not be used to protect secure information

Q) I've forgotten the password to an obfuscated script. Can it be decoded?
A) No. The encryption is not 'military grade' but decrypting password 
   protected scripts is not a trivial task. If you think you can crack 
   it then good luck. The author can offer no assistance in decrypting
   files. Keep backups of unencrypted scripts and records of encryption
   criteria for important files. Assume you won't be able to decode them
   and keep regular backups of development code (or use VCS/version 
   control)

   See: help encryption

Q) What filetype is an encrypted Ticol script?
A) TCX

Q) Can encrypted TCX scripts be loaded in the Command Line Interface (CLI)
   console?
A) Yes, but they cannot be viewed from the ticol.exe command line

Q) Can encrypted TCX scripts be identified as TCX scripts?
A) No. The data will appear as purely random
   They may be converted to a bitmap to visualise this

Q) Can I embed TCX encrypted code within another script?
A) Yes, use the ticol_tcx.dll plugin and the [tcx] command to execute
   the block of code
   You will need to "compile" using: ticol.exe /C /64
   Or use /C then use base64.exe to convert to base64 format which will be
   readable by [tcx]

Q) Can encrypted TCX scripts be echoed to the console using /ECHO or
   from the CLI using [dump] ?
A) No

Q) Can I get the source code from an encrypted TCX script?
A) No, the intact script is not visible, although some minimal information
   relating to procs may be displayed when single-stepping using the
   debugger

Q) Can any information leak from a TCX script?
A) Some limited information could be revealed via the debugger and /G
   performance trace option, e.g. command names and values
   [info proc]

Strings
-------
Q) How can I handle and dereference a string which contains Tcl commands? 
   As soon as $var is encountered the commands within are evaluated
A) To test for non-null use:  if {is_empty varname}
   To print out, use          echo varname -var

Q) Does Ticol use counted or uncounted ASCIIZ strings?
A) Ticol uses uncounted ASCIIZ strings except for strings created by
   [binary] commands, which are counted and which currently have limited
   scope

   See: binary

Q) I am having problems with very long numeric strings giving incorrect
   results. Why is this?
A) If using Notepad++ (and possibly some other editors) then very long
   strings may be broken up by the insertion of a carriage-return+line-
   feed pair. This will break the string into 2 or more parts
   To resolve this you should break the string into shorter parts, wrap
   in double-quotes and use line continuation characters.

   See: troubleshooting

Q) Are nested quotes allowed?
A  No but you can emulate them using [chr 34] 

Q) How can I effect nested quotes?
A) Nested quotes are not allowed in Tcl, however, you may use [chr] to
   insert quote characters. Alternately, a quote character may be defined
   as a var or const

    const QUOTE [chr 34]                                                        
    if {[uplevel @0 "is_array $::QUOTE$q($i)$::QUOTE"]} {                       
       ...                                                                      

   See: uplevel

Q) I am having issues escaping within strings. What can help?
A) Use a recommended 'minimal escaping style'. Quotes are not always 
   required in Tcl since (just about) everything is a string anyway
   Don't use quotes where braces could be used
   Don't use braces where no backslash substitutions are otherwise needed

   See: 
   https://wiki.tcl-lang.org/page/Tcl+Minimal+Escaping+Style

Structs
-------
Q) Can Ticol structs be used freely as in C/C++?
Q  Can I used Ticol structs to develop complex ADTs?
A) No. Structs are mainly provided to aid the marshalling of data to/from
   external DLLs. They may have some limited use inside Ticol scripts but
   as they cannot be instantiated as unnamed blocks of data it would be
   difficult to use them to create complex Abstract Data Types.
   The intention is that any routines which require complex ADTs would
   be written as a plugin DLL in C++

Q) How do I get the memory address of a struct data block?
A) Use [addressofb]

   Example:
                                                                                
   struct x {a 10}                                                              
   puts [addressofb x.a]  # Gives the address of struct x and x.a               

Q) How do I retrieve the binary contents of a struct field?
A) Use [ofaddressb]

   Example:
                                                                                
   struct x {a 10}                                                              
   struct setb x.a 12345  # Set binary value                                    
   puts [ofaddressb x.a]  # Will return 12345                                   

Testing
-------
Q) What testing has been performed on Ticol?
A) Reference tests has been made against ActiveState Tcl. Visual BASIC 5,
   MSVC++ and CScript/WScript. Additionally, around 200 test scripts have
   been used to help validate against regression or other errors during
   development. These will usually be included with the distro and can 
   all be run from a single tests.tcl using the command: ticol tests

   See: security issues

Threading
---------
Q) Can Ticol Tcl handle multi-threaded operations?
A) In the general release compile version, no, it doesn't

   An experimental/development version is available which will thread but
   not terribly well. The underlying interpreter is really not designed
   for threading and a proper threaded version would most likely require
   a rewrite from the ground-up. The experimental version queues complete
   execution threads through a single interpreter instance. It does not
   run multiple interpreters and synchronise results
   I may do more work on the threaded version once the unthreaded release
   becomes more stable. Any threaded release is offered only as a test
   platform

   If you require a stable, multi-threaded Tcl then I would recommend 
   ActiveState Tcl

Tricks
------
Q) Is it possible to link Ticol Tcl to any speech engine?
A) Yes, via VBScript and an intermediate temporary file:
                                                                                
    set vbstring {Set Speak=CreateObject("sapi.spvoice")\r\n\                   
                    Speak.Speak $r}         # We will [subst] r                 
                                                                                
    set r $pi                               # Assign some numbers               
    set filename [escape [mktemp vbs]]      # Create a unique file              
    writefile $filename [subst $vbstring]   # Write with subst $r               
    exec cmd /c $filename                   # Works OK                          
    # spawn - cmd /c $filename              # This works OK too                 
    unlink [escape $filename                # Remove temp file                  

A) It is also possible to use lib ticol_cscript.dll without a 
   temporary file as follows:
                                                                                
    set txt {Hello world}                   # Assign some text                  
    set s "Dim Speak                        # VBS Skeleton                      
    Set Speak=CreateObject(\"sapi.spvoice\")                                    
    Speak.Speak \"$txt\""                                                       
    lib ticol_cscript                       # Load cscript plugin               
    cscript [unescape $s]                   # Call [cscript]                    
    lib ticol_cscript -unload                                                   

Useful References
-----------------
    http://wiki.tcl.tk
    http://www.invece.org/tclwise/

Variants
--------
Q) Are VARIANT data types supported
A) Yes. Ticol structs can be used to emulate variants
A) A plugin, ticol_variant.dll offers a small number of variant handling
   routines. The intention is that any complex code requiring variant use
   would be implemented as a plugin DLL written in C++

   See: plugins, ticol_variant, ticol_calldll

Windows Console
---------------
Q) How can I stop Ticol trying to run unknown commands as a Windows command?
A) Use the 'option autoexec off' command to disable this. 
   You may re-enable using 'option autoexec on". 
   This allows Ticol to be used as a Windows command shell or to call a 
   procedure called 'unknown'

   See also: help option
   See also: help ticol.ini
   See also: help unknown
   See also: help autoexec

  -o-

See also: non standard tcl commands, help, tips, Ticol, ticol.ini, bugs, 
          design goals

Ticol-Specific, Non Standard Tcl Commands

Ticol offers the following useful non-standard commands to make the
language more flexible and efficient, either natively or via a simple
plugin system which can be called from scripts. These were developed by
the author out of necessity for various tasks and written in C++ to
avoid using longhand Tcl

/* ... */          C/C++ style long comments
.                  String head dereference (Dot) Similar to C++ '*string'
#if <expression>   A basic C++ like Macro PreProcessor which allows for
#ifdef             conditional execution. #if can take a Tcl command and
#ifndef            execute conditionally depending on the result
#endif             This can regulate execution to a particular time/PC etc.
#define            #define may include complex Tcl substitutions
#undef             Standard macro variables may also be defined or undefined
#else              and used to control execution
#endif             Also, you may execute Tcl commands directly or echo
#echo              output to the console using #exec and #echo
#exec              Execute a Ticol statement
#exit              Immediately halt the Macro PreProcessor
@<command>         Alias for exec. Run a Windows program
addslash           Append a slash to a path (if required)
addressof          Return the address of a var as a number
addressofb         Return the binary address of a string
after              Run a command after a given number of milliseconds
alias              Create an alias of a proc or command
all                Test multiple booleans
any                Test multiple booleans
arity              Return the arity value of a function (See [funct])
arp                ARP lookup of IPV4 address (plugin)
array foreach      Loop foreach element in an array
asc                Return the ASCII value of a character
assert             Debugging routine
at_exit            A routine to be called when a script exits
autoexec           Control the execution of unknown commands
base64             Base64 encode/decode a string or variable
baseconv           Convert from one base to another
beep               Play a system beep with frequency/duration
big                Large number arithmetic (plugin)
bigtext            Large text display (plugin)
binary             Handle binary strings (experimental)
bintoint64         Convert a binary number to an integer
bool               Handle boolean flags and values
box                Draw a graphic box
calc               Macro PreProcessor (MPP) optimised [expr]
call               Call a Tcl function discarding any return value
calldll            Call a __stdcall DLL and return a long    (plugin)
calldll_dbl        Call a __stdcall DLL and return a double  (plugin) 
calldll_str        Call a __stdcall DLL and return a string  (plugin)
calldll_variant    Call a __stdcall DLL and return a variant (plugin)
calldll*_cdecl     Call a __cdecl DLL (e.g. msvcrt40.dll)    (plugin)
calldll_cdecl        Call an external __cdecl DLL (plugin)
calldll_dbl_cdecl       "
calldll_str_cdecl       "
calldll_variant_cdecl   "
calldll_open       Get handle to a DLL (plugin)
calldll_close      Close DLL handle (plugin)
carray             C-like arrays (plugin)
cat                Concatenate values
char               Cast a value to an 8-bit (1 byte) integer
charcount          Count the number of characters in a string
chartok            Transform string to array by characters in a mask
chomp              Efficiently slice up large strings
chr                Return a character 0..255
clear              Erase all variables
cli                Open a new command line interface
clipboard          Manipulate the Windows clipboard
cls                Clear the screen
cmp                Efficient string comparison
concat             Efficiently concatenate strings
console            Control the console
const              Declare a constant string
convbase           Convert from one base to another
comma              Comma-format a numeric value
cos                Calculate the cosine
cosh               Calculate the hyperbolic cosine
copyfile           Copy a file using Windows API
cpu                Get CPU information (plugin)
crc                CRC checksum (plugin)
ctime_to_double    Convert from Unix C time to double
date               Date routines
debug              Turn debug mode On or Off
decimal_to_roman   Plugin command in ticol_r2d
decrypt            Decrypt a string encoded by [encrypt]
degrees            Convert radians to degrees
die                Exit to Windows returning an optional errorlevel
dim                Declare an array (allocate an empty hash table)
diskfree           Show free disk space (64-bit integer)
diskstat           Show disk status as a code or return letter/UNC path
disktype           Return the disk type
dll_close          Unload a DLL loaded with dll_open
dll_open           Load a Windows DLL (__cdecl or __stdcall)
dns                Simple DNS lookup (A, MX)  (plugin)
do                 do-while loop flow-control
doevents           VB equivalent yield control to Windows
double             Cast a value to an 64-bit (8 byte) real number
double_to_ctime    Convert from a double to Unix C time
dump               Debugging analysis of variables or source code
dumpvariant        Debugging analysis of a variant object
echo               Print a literal string without backslash translation
elevate            Self-elevate if running non Windows XP or 7+
encrypt            Encrypt a string or variable using moderate encryption
enum               Declare a series of const enums
eq                 String comparison equals (-nocase)
eqn                String comparison equals for given length (-nocase)
escape             Escape a string by adding backslash characters 
event              Windows event log messages (plugin)
exists             Test if a file exists
exit               Exit the Ticol interpreter
explain            Explain (expand) a [calc] expression
extract            Retrieve characters from the left side of string by mask
fib                Compute fibonacci series value
file_crc           Calculate the CRC32 checksum of a file
filesize           Return the size of a file
filter             Filter characters from a string by mask
fmod               Floating point remainder after division
fpart              Return the fractional part of a double as an integer
fraction           Return the integer part of a real number
free               Release memory assigned by malloc
fromhex            Convert hex characters to a string
ftp                Simple FTP client services (plugin)
funct              Call an expr function directly
functions          List expr functions
ge                 Greater-than or equal-to string comparison
get_fileopen       Common-dialog file-open menu
get_filesave       Common-dialog file-save menu
get_win_error      Retrieve the Windows error number
getcwd             Return the current working directory
getenv             Return an environment variable
getsid             Retrieve a SID matching a given username
goto               Flow-control goto. Valid within a goto_block
goto_block         Block of code which contains line-numbered statements
gotoxy             Locate the cursor
gt                 Greater-than string comparison
halt               Enter the single-step debugger
help               This help file
hostname           Return the configured host name
html               HTML manipulation commands
html header_field  Get an HTTP response field from the header
html remove_header Remove the HTTP response header block
html chomp         Parse and strip HTML source code
http               Retrieve a page of HTML from an HTTP server
in                 Check a list for an item
index              Rapid single character index into a string
ini                INI file handling (Plugin)
inkey              Get a keypress
input_csv          Read standard CSV data from file and return a Tcl list
inputbox           Text input window
inrange            Test if an integer is within a given percent range
instr              BASIC-like string matching with case-ignore arg
int                Cast to integer (64-bit)
integral           Return the integer part of a real number
inttobin           Convert from integer to binary
inttohex           Convert from integer to hex
inttooct           Convert a number to octal format
ip_to_long         Convert an IPV4 address to unsigned long
is_admin           Return true (1) if user is an admin
is_command         Detect if a command or proc is implemented
is_const           Test if a variable is a constant
is_dir             Check if a filename is a directory
is_dst             Test for Daylight Savings Time
is_elevated        Detect if Ticol is running in Win7+ elevated mode
is_empty           Test if a variable is both set and empty
is_list            Test if a string is a valid Tcl list
is_mod             Test for modular division safely
is_pointer         Test if a variable is a pointer (address value)
is_proc            Test if a command is a proc or not
is_set             Test if a variable is set or not
iselevated         Returns true (1) if Ticol is running elevated
json_to_list       Convert JSON format data to a Tcl list
K                  K-combinator. See functional programming
le                 Less-than or equal-to string comparison
left               Return the leftmost characters of a string
len                Alias for [string len]
let                Variant of set which accepts a Tcl expression
lib                Load commands in a Ticol extension DLL
link_to_path       Resolve a Windows LNK shortcut file to a path
linkedlist         Standard (non Tcl) linked lists
list               Returns a well-formed Tcl list
list_to_array      Convert a well-formed list to a Tcl array
load               Load a Ticol script to the CLI or a variable
long               Cast a value to a 32-bit (4 byte) integer
long_to_ip         Convert a long value to IPV4 string
loop               Efficient [for] loop
lrand              Return a random element from a list
ls                 Perform a simple directory listing
lsearch            Search one list for a match to another list
lt                 Less-than string comparison
makestr            Create a string with optional fill character
malloc             Heap memory allocation
map                Tcl map function. Map and replace strings
max                Math test
md5                Generate an MD5 checksum string
md5_file           MD5 checksum of a file
mean               Calculate the mean of a series of numbers
mem_used           Show the memory used by Ticol
memset             Erase/set a buffer for use with calldll*
mid                Set part of a string
mids               Return part of a string
min                Math test
mkdir              Create a folder
movefile           Move a file using the Windows API
msgbox             Display a Windows MessageBox
mul                Math operator
ne                 Compare string inequality
nen                Compare string inequality for a given length
newline            Print a new line to the console
ni                 Test if string not in a list
now                Return the current date/time as a double in VB6 format
null               NULL value
octtoint           Convert from octal to decimal
ofaddress          Inverse of addressof. Returns the original data
ofaddressb         Binary inverse of 'struct setb'
on                 Emulation of BASIC's ON..GOSUB
option             Configure Ticol options at runtime or at the CLI
pause              Wait for a keypress
peek               Check the top of a global stack
pid                Return the interpreter's process ID (PID)
ping               Plugin ICMP ping module (plugin)
pipe               Pipe together a series of commands without nesting
play_wav           Play a WAV file
printf             Calls the format command. Mimics C/C++ printf
precision          Returns the effective precision of a real number
procs              List user-defined procedures
put                Writes n bytes value to a struct variable safely
pwd                Prints the current working directory
rand               Return a random number
randomstr          Return a random string
range              Return a string in a given range
rarp               Reverse ARP lookup of MAC address (plugin)
readfile           Read a file to a variable
registry           Registry commands (plugin)
replacechar        Search and replace single characters
resume             Cancel a previous debug-trace [halt] command
right              Return the rightmost characters of a string
rnd                Return a random number in given range
roman_to_decimal   Plugin command in ticol_r2d
rot13              Apply rot13 transformation to a string
round              Round a value
rtf                Simple RTF text output (plugin)
run                Aliased to [source]. Run a [load] script
run                Run a Tcl file from the interpreter shell
scan               Scan a string to one or more variables
screen             Screen metrics
sendmail           SMTP send mail command (plugin)
service            Simple service view command (plugin)
setat              Set a variable from a string segment
setb               Set a binary value in a struct field
setenv             Set an environment variable
setl               Insert source string to target, left-aligned
setr               Insert source string to target, right-aligned
setresult          Function for plugin DLLs to set the Tcl return value
shell              Spawn a Windows Command shell
short              Cast a value to a 16-bit (2 byte) integer
signed             Convert unsigned integer to signed value
sin                Calculate sine
sinh               Calculate hyperbolic sine
sizeof             Return the estimated byte size of an integer
sleep              Suspend execution for N milliseconds
spawn              Run a console program with output capture
sql                Basic SQLite database commands (plugin)
sqr                Return the square of a number
sqrt               Return the square root of a number
srand              Set the random number seed
stack              Native stack objects
stop               Halts execution and returns to the Ticol CLI
stop_wav           Halt playing a WAV file
store              Rapid buffered string concatenation
strchr             Index a character into a string
string to_wchar    Convert an ANSI string to a wide character string
stripto            Strip LH string to a given character
striptor           Strip RH string to a given character
strptr             Return the binary address of a string
strsplit           Split a string into a list
strstr             Returns the search index for one string in another
strtok             Transform a string to an array by string separator
struct             Declare a binary struct (for calldll)
swap               Swap two standard Tcl variables
sysfree            Release a string returned from a DLL
tab                Return a string of n spaces
tan                Calculate the tangent
tanh               Calculate the hyperbolic tangent
tcx                Run obfuscated TCX code within a script
textcolor          Set the text color
ticks              Shows the tick count in milliseconds
time               Shows the current system time
tohex              Convert a string to hex characters
trim               More efficient [string trim]"
true               Boolean value
try                Try / catch structured error handling
type               Show the interpreted type of a variable
unknown            Standard Tcl but interacts with autoexec on|off
unlink             Delete (unlink) a file
unsigned           Cast a variable to unsigned integer
username           Return the current Windows user
username           Return the current Windows user
varray             Declare a variant array (for calldll)
varray get         Return a 1D or 2D variant string element
varray lbound      Return the lower bound of a variant array
varray size        Returns the count of a 1D string variant
varray ubound      Return the upper bound of a variant array
vars               Display a list of variables
vsplit             Split a variant array
waitkey            Wait for a keypress or time out
watch              Trace variables when in single-step debug mode
wchar_length       Get the length of a wide-character string
wchar_to_string    Convert a wide-character string to ANSI
win_error          Display Windows error messages
wrap               Format a string to a given column width
write              Write to file
xbase              DBase III/IV database plugin
zip                Simple ZIP module (plugin)

  -o-

See also: Ticol

Ticol Uses

Ticol is intended as a casual tool for occasional toolbox scripts and
may have some use as a learning tool for students.

Ticol is not recommended for use as a continually-running script. It is 
intended for relatively short lifespan tool scripts which run for 
possibly less than 100,000 cycles before exiting.

Memory testing will have been performed to check stability on a routine
basis up to around 100,000 operational cycles.

Short scripts which are called intermittently and on a regular basis 
but which exit then restart should be ok. This would most likely
include CGI scripts although Ticol is not recommended as a production
CGI script engine, particularly as there are far better alternatives such
as PHP and PERL.

Although the size of each script is limited only by available memory it
is strongly recommended to keep scripts not much longer than a few 
thousand lines. Larger scripts can be broken up and run on a modular
basis using the [source] command

Ticol Tcl Interpreter for Windows

   "Inside every large program there is a small program trying to get out"      
   (Sir Tony Hoare)                                                             
                                                                                
   "Inside every small program there is a large program trying to get out"      
   (Ticol Author)                                                               

A compact Tcl subset-compatible ANSI script language for Windows which is
a fully portable app needing no dependencies on Windows. Ticol covers a
subset of Tcl with many additional features:

Windows versions from NT4.0/SP6a to Server 2012R2 and recent versions of
ReactOS are supported

Code obfuscation using several methods of endpoint execution-locking is
available. Regular-expressions and pattern-matching are not yet supported
Unicode and non-ANSI character languages are not supported

Ticol is also regularly tested on ReactOS with a view to compatibility

The language adds many additional features to basic Tcl command list
See: ticol specific commands

Ticol was written using MSVC++ 5.0. (Still useful!)

See also: Ticol design goals, alphabetic list, Tcl, help, PreProcessor, 
          Ticol command line, debugging, performance, functions, Tcl syntax, 
          flow control, comments, workarounds, bugs, commands by function
          tips, faq, tutorial

  -o-

Tutorial

A Brief Introduction to Programming in Tcl

At first glance, Tcl code looks very much like C/C++ but the syntax is
nevertheless quite different. If you are a C/C++ programmer then you will
need to unlearn a few habits possibly dispel the odd assumptions about
how you might write code when using Tcl

Tcl uses curved braces to delineate blocks of code, but the equivalent to
C/C++ functions are 'commands'. Commands are evoked, either 'naked' by
using the name of the command openly in a script, or by enclosing within
square brackets, thus:

     command            # Call 'command'                                        
    [command]           # Call 'command'                                        

Commands may have zero or more arguments. Some may be optional and 
command modifiers a typically prefixed with '-' e.g. -nocomplain

    # puts is a command to output to the console                                
    puts "This string won't print a newline" -nonewline                         

Tcl comments begin with a hash # character but Ticol also supports C-
style /* .. */ long comments  The two may be combined.

Various flow-control constructs are available including 'if' and 'for'
which most programmers will already be familiar with. These constructs are
also implemented as Tcl commands and obey the same rules as in many other
languages such as [if], [for], [while], [switch]. [do], [try..catch]

Curly braces delay evaluation and are used to enclose both blocks of code
to be evaluated and strings. In most cases you should enclose strings
within curly braces rather than double quotes but neither is obligatory
unless the string contains whitespace. Since evaluation is delayed, various
blocks of code can be passed around as arguments to commands without being
interpreted while being transferred. The use of curly braces looks a lot
like C/C++ but the function is quite different.

Command arguments are terminated by an unbraced line ending so braces
must 'protect' line-ends using K&R style bracing. Failure to adhere to this
style will cause errors in your script

    proc foo x {            # Correct K&R Tcl style                             
       ...                  # [foo] receives the body argument correctly        
    }                                                                           
                                                                                
    proc foo x              # Incorrect Allman/BSD C/C++ style                  
    {                       # [foo] will fail to receive the body argument      
       ...                  # contained within the braces                       
    }                                                                           

Curly brace and square bracket counts must match within a script

[if], for example, can take a varying number of arguments including
'elseif', each of which must be properly braced if they are to be split 
across multiple lines. Note that you could, if you wished, put the command
on a single line but this is not particularly readable! ...

    if {something} {                                                            
        do-something                                                            
    } elseif {something-else} {                                                 
        do-something-else                                                       
    }                                                                           

The expression arguments used by various flow-control constructs can be
either of 'expression' type with infix command-operators or in Tcl prefix
style with prefix command operators. This option is set either within 
ticol.ini or by using the 'option expression' command

    option expression off   # Commands will use Tcl prefix notation             
    option expression on    # Commands will use natural/infix notation          

Tcl commands should be separated by whitespace. Tab characters should
normally be avoided other than for indenting code for the sake of
readability. You can't 'butt' commands against each other unless they are
both wrapped in square brackets.

    ![rnd 0 1]              # Invalid. Requires a space between ! and [rnd]     
    ! [rnd 0 1]             # Correct style with whitespace as separator        
    puts [rnd 0 1][rnd 0 9] # Correct. Results are concatenated                 

Operators in C/C++ such as +, -, == etc. are implemented in Tcl as commands
and can be called as such. These command-operators may be used as arguments
for flow-control constructs. So commands such as [if] and [for] may take 
relational commands such as [>] or [==]

    option expression off   # Use Tcl prefix syntax                             
    set i [rnd 0 1]         # Set i to either 0 or 1                            
    if {== $i 0} {          # [==] is used in prefix format                     
       puts "i is 0"        # [puts] is used to print output                    
    } else {                # [else] is a subcommand of [if]                    
       puts "i is 1"        # [puts] is called as a 'naked' command             
    }                       # Indent properly for readability!                  

There is no [=] assignment in standard Tcl although Ticol Tcl defines this
command as an alias to [set]. Instead, the [set] command (similar to the
BASIC LET statement), is used either to assign values or to return them to
some other command or a variable.

As an alternative to using [set] to return the contents of a variable, the
dollar prefix may be used to dereference (access) a variable's contents.

    set s "Hello world"      # Assign variable s using the [set] command        
    puts $s                  # Dereference variable and print using [puts]      

We could also enclose the string in the example above in curly braces, thus
and in many cases this is preferable. See brace coding styles in Tcl.

    set s {Hello world}                                                         

Console output is generated either using [set] (standard Tcl) or [printf]
(non standard Ticol command). The [printf] command is merely a wrapper
for the [format] command which is a standard Tcl one. Both [printf] and
[format] offer a C/C++ style range of formatting options and share common
flag characters etc. such as %s or %f

By default [puts] prints a newline whereas [printf] does not

    set s "world"                                                               
    puts "Hello $s"          # Print using [puts]                               
    printf "Hello %s\n", $s  # Print using [printf]                             

Tcl commands may be nested. In which case the innermost command is executed
first, followed by the next innermost and working outwards. Tcl commands
are evaluated within double-quoted strings since evaluation is is not 
delayed by double quotes. If an expression is wrapped in curly braces
such commands are not called immediately

    puts [chr [rnd [asc A] [asc Z]]]    # Prints a letter from A to Z           
    puts "[chr [rnd [asc A] [asc Z]]]"  # Prints a letter from A to Z           
    puts {[chr [rnd [asc A] [asc Z]]]}  # Prints [chr [rnd [asc A] ...          

Complex expressions may be constructed using Tcl commands but the 
syntax may take some getting used to. An alternative is to use
a natural expression supplied to the [expr] command:

    puts [* [+ 23 57] [/ [+ [* 3 10] 3] 13.0]]  # 203.076923076923040           
    puts [expr (23+57)*(((3*10)+3)/13.0)]       # 203.076923076923040           

The [expr] command will prioritise expressions in BEDMAS order and it is
good practice to always brace expressions supplied to [expr]
Note that [expr] is less efficient than using raw Tcl commands but is
much easier to read. Commands may also be inserted into expressions
providing they return a numeric result

    puts [expr [+ 23 57]*(([* 3 10]+3)/13.0)]   # 203.076923076923040           

Variables are deleted using the [unset] commands. Since it is an error
to attempt to delete a non-existent variable you may wish to use the
'-nocomplain' argument. Many commands have a '-nocomplain' option which
will cause any fatal errors to be ignored. [unset] can also take many 
variable names as it's argument and can also perform 'glob' style matching
if required:

    set s "Hello world"                                                         
    unset s -nocomplain       # s exists and is deleted                         
    unset foobar -nocomplain  # foobar does not exist                           

In fact, most commands have optional arguments such as '-nocase' or
'-nocomplain' which modify their behaviour. These are documented 
within the built-in help file. To find out more use the command
help help at the Ticol command line.

Various iterator commands are available and the [for] command looks
very similar to that in C/C++ but again, can operate using Tcl prefix
syntax or expression (infix) syntax as defined by [option expression]

    option expression off                                                       
    for {set i 0} {< $i 10} {++ i} { ... }                                      
    option expression on                                                        
    for {set i 0} {$i < 10} {++ i} { ... }                                      

It is a good idea to pick one style and stick to it within a script to
avoid hard-to-debug errors

Ticol also offers a [while] command, [do...while] and a useful [loop]
command as follows

    loop i 0 10 {        # Sets variable i to zero and iterates to 9            
       puts $i           # Prints numbers from 0 to 9                           
    }                                                                           

Procedures are Tcl's equivalent to C/C++ functions. Once defined you can
call procedures in the same way as built-in commands. Nested commands 
are evoked via square brackets...

    proc foo {x} {                                                              
       return [* $x 2]                                                          
    }                                                                           
    puts [foo 23]        # Call foo and print the result (Returns 46)           

Procedures support calling arguments by name or by value. If you call by
name you will need to use [upvar] to reference the variable within the
parent scope. For a discussion, see: upvar

    proc foo {x} {                                                              
       upvar $x y        # Create a link to the parent variable as 'y'          
       return [* $y 2]                                                          
    }                                                                           
    set i 23                                                                    
    puts [foo i]         # Returns 46                                           

Separate script files may be executed using the [source] command

    source \path\to\some-other.tcl                                              

and scripts may also be loaded and executed at the Ticol command line
using [load] and [run] (or just [run scriptname]). You can also inspect
the currently loaded script (if any) using [dump]

    c:\>ticol.exe        # Run Ticol from the Windows console prompt            
    load foo             # Load foo.tcl                                         
    dump                 # Show the contents of foo.tcl colour highlighted      
    run                  # Run foo                                              
    exit                 # Exit the interpreter and return to Windows           

To halt a running script use the CTRL+BREAK key sequence (not CTRL+C)

To get a better understanding, have a look at the example scripts in this
help file

See also: info, faq, troubleshooting

  -o-

Windows GDI Graphics Plugin

To load, use:    lib ticol_win ?-verbose? ?-nocomplain?

This plugin provides basic access to Windows GDI drawing functionality
including the ability to open a Window and draw various shapes or text

The plugin was developed to provide a solution for a specific task 
rather than as a general purpose windowing solution. This task was
to provide an enriched form of console mode drawing.

This is a new and largely experimental feature and is subject to future
revisions and amendments. The range of commands is not yet complete
but has been widely tested.

See the screensaver sample source code: 'help win example'
This can also be extracted from the screensaver SCR binaries 
using:  'screensavername.scr /echo'

The following commands are supported

    # Return an RCB hex value as RRGGBB (values are optional)                   
    [rgb ?r? ?g? ?b?]                                                           
    # Return window active status (e.g. mouse exit)                             
    [win active]                                                                
    # Draw a character mode box with fill option                                
    [win box x y w h ?fore? ?back? ?-single? ?-solid? ?-nofill?]                
    # Draw a filled circle                                                      
    [win circle handle x y ?colour? ?width?]                                    
    # Close the window and release any resources                                
    [win close]                                                                 
    # Clears the graphic window, optionally to a given colour                   
    [win cls ?colourname|#RRGGBB?]                                              
    # Return the number of columns                                              
    [win columns]                                                               
    # Create a graphic window (only one window currently supported)             
    [win create -w width -h height ?-fullscreen? ?-smallfont? \                 
            ?-largefont? ?-movequit? ?-closeonlostfocus?]                       
    # Default is show                                                           
    [win cursor show|hide]                                                      
    # Drawn an ellipse                                                          
    [win ellipse handle x y w h ?color? ?width? ?-dot|-dash|-dotdash|-solid?\   
             ?-fill ?colour??]                                                  
    # Select a drawing font                                                     
    [win font -name FONTNAME -size NN -bold -italic -underline -normal -clear]  
    # Locates the cursor at character X/Y coordinate using character coords     
    [win gotoxy x y ?-t?]                                                       
    # Return the window height in pixels                                        
    [win height]                                                                
    # Draw a line                                                               
    [win line x1 y2 x2 y2 ?colour? ?width? ?-dot|-dash]-dotdash?]               
    # Plot from the stored [moveto] coords to the given x/y point               
    [win lineto x y ?colour? ?width? ?-dot|-dash]-dotdash?]                     
    # Move the line plotting start point to x/y coords                          
    [win moveto x y]                                                            
    # Test if WM_PAINT was called and if we need to repaint our window          
    [win paint ?n?]                                                             
    # Draw a parallellogram                                                     
    [win para origin-xpos origin-ypos width height  rot_angle1|90 \             
            splay_angle2|90 colour|grey pwidth]                                 
    # Set up for Windows Screensaver preview (Pass handle from Windows)         
    [win preview HANDLE]                                                        
    # Draw a single pixel with optional colour value                            
    [win pset x y ?color?]                                                      
    # Draw a text string to the graphics console window                         
    [win puts string ?-size HEIGHT? ?-rot ANGLE?]                               
    # Draw a rectangle                                                          
    [win rect x y w h ?color? ?width? ?-dot|-dash|-dotdash|-solid?\             
             ?-round? ?-fill ?colour??]                                         
    # Return a random HTML colour as "#RRGGBB" hex                              
    [win rndcolour ?lo|-? ?hi|-?]                                               
    # Return the number of screen rows                                          
    [win rows]                                                                  
    # Set absolute cursor position (pixel coordinates)                          
    [win setpos x y]                                                            
    # Blocking sleep                                                            
    [win snooze ?milliseconds?]                                                 
    # Set foreground/background using Ticol constants or "#RRGGBB" values       
    [win textcolor fore|- ?back?]                                               
    # Get the current cursor X location in columns                              
    [win wherex]                                                                
    # Get the currnt cursor Y location in rows                                  
    [win wherey]                                                                
    # Get the window width in pixels                                            
    [win width]                                                                 
    # Get the absolute pixel position for character coordinate X-axis           
    [win xpos]                                                                  
    # Get the absolute pixel position for character coordinate Y-axis           
    [win ypos]                                                                  

The cycle of coding involves creating a new window and providing a local
event loop to service it. Without an event loop the window will hang.

No handles are required other than to test that the window opened 
correctly since only one graphics output window is allowed
The returned window handle may be passed to Windows API calls

The basic cycle of operation is as follows...

    option expression off                                                       
    # Load the required plugin                                                  
    lib ticol_win -nocomplain                                                   
                                                                                
    # Create the window                                                         
    set h [win create -fullscreen -smallfont]                                   
    if {== $h 0} {                                                              
        console show                                                            
        puts "Failed to open console output window"                             
        pause                                                                   
        die                                                                     
    }                                                                           
                                                                                
    # Optionally, hide the text mode console                                    
    console hide                                                                
    # Optionally, hide the main cursor                                          
    win cursor hide                                                             
                                                                                
    # Service the window                                                        
    while {win active} {                                                        
        # Allow user cancel (if desired)                                        
        if {inkey} {break}                                                      
        # Service the loop here ..                                              
        # main_service_proc  # Call any main service proc                       
        win snooze 100       # Call win snooze if desired                       
        # snooze 100         # Call any alternate/custom snooze                 
    }                                                                           
                                                                                
    # Clean up                                                                  
    win cursor show                                                             
    console show                                                                
    win close                                                                   
    lib ticol_win -unload                                                       

Closing the parent console window will also close the graphics 
window. You may wish to hide the parent console using [console hide]
but it will usually be vital to ensure that [console show] is called
on script exit to avoid losing control of the console.

If execution of your script has to pause you should create a 
suitable 'snooze' procedure or call [win snooze] at regular intervals.
(See: win snooze)

Linux WINE and ReactOS Compatibility
------------------------------------
The ticol_win plugin is not fully compatible with WINE or ReactOS due to 
minor differences in the underlying API.

Windows Compatibility
---------------------
This plugin has been tested with all NT family variants of Windows from 
NT4.0/SP6a to Windows Server 2012R2

See also: plugins, win snooze, win active, console, lib

rgb

    hex-string [rgb ?red? ?green? ?blue?]

[rgb] takes optional integers for red green and blue components and returns
a formatted hexadecimal string as "RRGGBB"

Example
-------

    puts [rgb 8 4 68]                                                           

Result
------
                                                                                
    080444                                                                      

See also: win, hex

win active

    bool [win active]

Returns true while a window opened using [win create] is active

[win active] may also be called passively within long loops to ensure that
the underlying Windows event loop is polled and the window avoids being
'frozen' by Windows assuming it is non-responsive.

Example:

    # The terminating conditions here depend on [win create]                    
    option expression off                                                       
    lib ticol_win                                                               
    win create -fullscreen                                                      
    while {[win active]} {                                                      
        if {inkey} {break}                                                      
        win snooze 100                                                          
        win gotoxy 1 1                                                          
        win textcolor blue                                                      
        win puts [time]                                                         
        win textcolor                                                           
    }                                                                           
    win close                                                                   
    lib ticol_win -unload                                                       

See also: win, win create, win snooze, sleep, plugins

win box

    void [win box x y w h ?fore? ?back? ?-single? ?-solid? ?-nofill?]

Draws a GDI box with optional border style and fill in a window opened 
using [win create]

Coordinates are pixel coordinates, not character coordinates

See also: win, win create, plugins

win circle

    void [win circle x y ?border-colour? ?diameter? \
        ?-fill ?fill-colour??|?<style>?]

Where <style> is one of:  -dot, -dash or -dotdash

Draws a GDI circle with optinal border style and fill in a window which
was opened using [win create]

Coordinates are pixel coordinates, not character coordinates

The smallest diameter which can be drawn is 2 pixels. This is a limitation
of the underling API.[win pset] can be used to draw single pixel points

See also: win, win create, plugins

win close

    bool [win close]

Closes a window which was opened using [win create]

Only a single window is permitted so there is no requirement to pass the
window's handle

See also: win, win create, plugins

win cls

    void [win cls ?colourname|#RRGGBB?]

Clears window which has been created using [win create], optionally 
the command may specify a given colour

See also: win, win create, plugins

win columns. rows

    integer [win columns]
    integer [win rows]

Returns the number of text mode character columns or rows for a window
which has been created using [win create]

The columns and rows value will depend on which font size was selected
when the window was crated

See also: win, win create, plugins

win create

    handle [win create -w width -h height ?-fullscreen? ?-smallfont? \
        ?-largefont? ?-movequit? ?-closeonlostfocus? ?-center?]

Creates and opens a graphics mode window. The width and height may be 
specified in pixels or a full-screen window may be specified.

A full screen window will occupy all monitors on a multi-monitor system.

Small character fonts may be specified or larger than standard fonts via
the -smallfont or -largefont arguments. The font size selection will
affect the column and row values

Optionally, the window can be set to close on mouse movement or if 
focus is lost.

A window opened by [win create] requires an event loop which must
continuously poll [win active]. If this is not done then the window
will 'hang'.

The new window will be positioned by Windows unless the -center option is
given

See also: win, win active, plugins

win cursor

    void [win cursor show|hide]

Either shows or hides the Windows graphic cursor. This does not affect
the console mode (text) cursor

See also: win, win create, plugins

win ellipse

    void [win ellipse handle x y w h ?color? ?width? \
        ?-dot|-dash|-dotdash|-solid? ?-fill ?colour??]

Draws an ellipse in a window which has been created by [win create]

This can have a varying colour, border width or border shading style
as well as a differing fill colour

See also: win, win create, plugins

win font

    void [win font -name FONTNAME -size NN -bold -italic -underline\
         -normal -clear]

Selects a font style for use with [win puts] in a window which has been 
created by [win create]

Varying font style attributes may be specified by the arguments

See also: win, win create, win puts, plugins

win gotoxy

    void [win gotoxy x y ?-t?]

[win gotoxy] works analogously to [gotoxy]. It locates the cursor at 
character coordinates specified by x and y 

Note that these are block character rather than pixel coordinates 
unless the -t argument is used.

The -t argument will force translation from character mode addressing
to pixel mode addressing so that the selected X/Y location is set up
ready for pixel addressing in the same manner as [win setpos]

See [win setpos] to set absolute pixel coordinates for use with -rot

Example:

    # Assuming [win create] and an event loop                                   
    win gotoxy 10 5                                                             
    win textcolor magenta                                                       
    win puts "Hello World"                                                      

Result:

Will print "Hello World" in magenta at x=10, y=5 in character block mode

Do not confuse [win gotoxy] with [gotoxy]

See also: win, win create, win setpos, plugins

win height, width

    integer [win height]
    integer [win width]

Returns the width or height of a window created by [win create] in 
pixels

See also: win, win create, plugins

win line

    void [win line x1 y2 x2 y2 ?colour? ?width? ?-dot|-dash]-dotdash?]

Draws a line from x1,y1 to x2,y2 with optional colour, width and style
values in a windows which has been created by [win create]

See also: win, win create, win lineto, plugins

win lineto

    void [win lineto x y ?colour? ?width? ?-dot|-dash]-dotdash?]

Draws a line from the position last stored by [win moveto] to the x,y
coordinates specified in a window which was created by [win create]

Varying colour, width and line style attributes may be specified

See also: win, win create, win line, win moveto, plugins

win moveto

    void [win moveto x y]

Store the pixel x/y coordinates for use with [win lineto] in a window
created by [win create]

Sequences of [win moveto] followed by [win lineto] enables 'logo' like
'turtle' graphics.

See also: win, win create, win lineto, plugins

win paint

    bool [win paint ?n?]

Test if a window created by [win create] has received a WM_PAINT message
and if it should be redrawn (repainted). Optionally the count of 
required WM_PAINT messages may be specified. The default is 1.

See: win snooze    for example code

See also: win, win create, win active, plugins

win para

    void [win para origin-xpos origin-ypos width height \
        rot_angle1|90 splay_angle2|90 colour|grey pwidth]

Draws a parallelogram in a window created by [win create]. 

The left corner x/y position must be specified as well as the height, 
rotation angle and splay angles

Example:

The paralllogram is located with one point at x,y with a possible rotation
angle about that point. The width and height can be specified and the
'splay' angle which distorts from a rectangular shape can be specified

 (rotation)                                                                     
    x,y      width                                                              
     +--/-------------+                                                         
      \/ splay         \      height                                            
       \                \                                                       
        +----------------+                                                      

Optionally the border colour and width may be specified

See also: win, win create, win circle, win rect, plugins

win preview

    void [win preview HANDLE]

Set up for Windows Screensaver preview in a window object which was 
created by [win create]. You should pass the handle returnd by Windows
via the /P command line callback argument.

This will create a preview window within the control panel's screensaver
preview area

The following snippet may be used to handle Windows screensaver
preview mode...

    for {set i 0} {< $i $argc} {++ i} {                                         
        if {&& [eqi $argv($i) "/p"] [< [+ $i 1] $argc]} {                       
            set preview_handle $argv([+ $i 1])                                  
            win preview $preview_handle                                         
            set i 0                                                             
            set start [timer]                                                   
            draw_preview           # Call your preview proc                     
            while {win active} {                                                
                if {> $i 20} {                                                  
                    draw_preview   # Call your preview proc                     
                    set i 0                                                     
                }                                                               
                loop 10 {                                                       
                    if {! [win active]} {break}                                 
                    doevents                                                    
                    sleep 10                                                    
                }                                                               
                ++ i                                                            
                # Destroy the console screen after 1 minute                     
                if {> [timer] [+ $start 60000]} {break}                         
            }                                                                   
            console show                                                        
            exit                   # You must now exit this instance            
        }                                                                       
    }                                                                           

See also: win, win create, plugins

win pset

    void [win pset x y ?colour?]

Will plot a single pixel in a window opened by [win create] at a given
x and y location. The x/y location must be within the bounds of the current
window

Optionally a colour can be specified using the same format as 
[win textcolor]. If a colour value is omitted then the value set by
[win textcolor] foreground will be used

The colour argument may use Ticol colour constant names or "#RRGGBB" values

[win circle] can be used to draw larger points

See also: win gotoxy, win puts, win textcolor, win circle

win puts

    void [win puts string ?-size HEIGHT? ?-rot ANGLE?]

Draws a text string to in a graphics console window created by [win create]
 
By default, the string is drawn in text mode unless -rot is used and 
will therefore locate at the currently set character block mode coordinate

Rotated text can be specified by passing a rotation angle using the -rot
argument.

Note that -rot requires x/y location to be set using [setpos] rather than
[gotoxy]

Example:

    # Assuming [win create] and an event loop                                   
    win gotoxy 10 5                                                             
    win textcolor magenta                                                       
    win puts "Hello World"                                                      

Result:

Will print "Hello World" in magenta at x=10, y=5 in character block mode

See also: win, win create, win setupos, plugins

win rect

    void [win rect x y w h ?color? ?width? \
        ?-dot|-dash|-dotdash|-solid? ?-round? ?bordersize?? ?-fill ?colour??]

Draws a rectangle in a window which was created by [win create]

Optionally the colour, line width and line style may be specified, as 
well as an optional fill and fill colour

Example:

   x,y                                                                          
    +------------------+                                                        
    |                  |                                                        
    |                  | Height                                                 
    |                  |                                                        
    +------------------+                                                        
           Width                                                                

See also: win, win create, win circle, plugins

win rndcolour

    integer [win rndcolour ?lo|-? ?hi|-?]

Returns a random HTML formatted colour as "#RRGGBB" hex
Optionally this may be restricted to the 'lo' or 'hi' ranges

Values are not bit-masked per RR/GG/BB range but will follow a continuous
range from 'lo' to 'hi'.

Example:

    puts [win rndcolor]                                                         
    puts [win rndcolor 0xff 0xfff]                                              
    puts [win rndcolor 0x1f000 0xff0000]                                        

Result:

    #d0401a                                                                     
    #0006a6                                                                     
    #1e1c23                                                                     

See also: win, win create, plugins

win setpos

    void [win setpos x y]

Sets the absolute cursor position (pixel coordinates) in a window which 
was created using [win create]. 

[gotoxy] sets the drawing location based on character block locations

May be used with [win xpos], [win ypos] to query the pixel location

See also: win, win create, win xpos, win ypos, plugins

win snooze

    void [win snooze ?milliseconds?]

A safe sleep which should be used by code interacting with a window which 
was created by [win create] in preference to [sleep]

[win sleep] will permit polling of [win active] and [win paint] whereas
[sleep] will block polling of these events.

Execution delays should call [win sleep] or a custom, internal snooze
routine rather than calling [sleep] since the latter will block execution
Such a routine would need to handle cleanups as required and also trap
and call any repaint events

The code sample below allows for user cancellation to be turned on/off

    option expression off                                                       
    #######################################                                     
    # An example [snooze] procedure                                             
    # [global] ::cancel_allowed                                                 
    #######################################                                     
    proc snooze {milliseconds} {                                                
        set start [timer]                                                       
        set limit 0                                                             
                                                                                
        if {&& [== $::cancel_allowed $::true] [> $milliseconds 100]} {          
            set limit [/ $milliseconds 100]                                     
                                                                                
            loop i 0 $limit {                                                   
                set k [inkey]                                                   
                if {== $k 27} {                                                 
                    console showcursor                                          
                    textcolor red                                               
                    newline                                                     
                    puts " * Cancelled *"                                       
                    textcolor                                                   
                    win cursor show                                             
                    win close                                                   
                    console show                                                
                    lib ticol_win -unload                                       
                    die                                                         
                }                                                               
                if {! [win active]} {                                           
                    win cursor show                                             
                    win close                                                   
                    console showcursor                                          
                    console show                                                
                    lib ticol_win -unload                                       
                    die                                                         
                }                                                               
                if {[win paint 1]} {                                            
                    # repaint_proc    # Call any repaint procedure here         
                }                                                               
                loop 5 {                                                        
                if {>= [- [timer] $start] $milliseconds} break                  
                    if {! [win active]} break                                   
                    win snooze 20                                               
                }                                                               
            }                                                                   
        } else {                                                                
            if {! [win active]} {                                               
                win cursor show                                                 
                win close                                                       
                console showcursor                                              
                console show                                                    
                lib ticol_win -unload                                           
                die                                                             
            }                                                                   
            win snooze $milliseconds                                            
        }                                                                       
    }                                                                           

See also: win, win create, win active, win paint, sleep, plugins

win textcolor

    void [win textcolor fore|- ?back?]

Sets the default foreground or background colour for text output in a
window which was created using [win create] 

The arguments may use Ticol colour constant names or "#RRGGBB" values

See also: win, win create, plugins

win wherex, wherey

    integer [win wherex]
    integer [win wherey]

Returns the current character-mode cursor X or Y location in rows in a
window which was created by [win create]

[win wherex] and [win wherey] interact with [win gotoxy]

See also: win, win create, plugins

win xpos, win ypos

    integer [win xpos]
    integer [win ypos]

Returns the absolute pixel Y or Y drawing position

These commands interact with [win setpos] and can be used to override
character block-mode coordinates. These commands should not be usd in 
conjunction with [win gotoxy]

See also: win, win create, win setpos, win wherex, win wherey, plugins

Windows GDI Graphics Plugin - Example (LabelMania.scr)

This is the full and complete Ticol Tcl source code for the Ticol 
LabelMania Windows screeensaver. The Tcl source is embedded within a 
Windows SCR (EXE) screensaver binary

This script displays a configured text phrase in embossed-type characters 
randomly across the screen in various colours and orientations, 
periodically clearing the screen and redrawing.

    option expression off                                                       
    sub at_exit {                                                               
        console show                                                            
    }                                                                           
    lib ticol_win                                                               
    console hide                                                                
    const MAX_STRING_LENGTH 60                                                  
    const SCREENSAVER_TIMEOUT_MS 60000                                          
    set opt_preview $false                                                      
    set font_size 18                                                            
    set text " Hello from Ticol Tcl! "                                          
    const APP_NAME "LabelMania"                                                 
    const CSIDL_LOCAL_APPDATA 28                                                
    set appdata [escape [get_csidl $CSIDL_LOCAL_APPDATA]]                       
    if {ne $appdata ""} {                                                       
        set ini_file "$appdata\\$APP_NAME\\${APP_NAME}.ini"                     
        if {! [file isdirectory "$appdata\\$APP_NAME"]} {                       
            file mkdir "$appdata\\$APP_NAME"                                    
        }                                                                       
        if {! [file exists $ini_file]} {                                        
            ini write $ini_file "Config" "Text" $text                           
        }                                                                       
    }                                                                           
    if {ne $ini_file ""} {                                                      
        set s [ini read $ini_file "Config" "Text"]                              
        if {ne $s ""} {                                                         
            set text $s                                                         
        }                                                                       
    }                                                                           
    if {> $argc 0} {                                                            
        for {set i 0} {< $i $argc} {++ i} {                                     
            if {eqn $argv($i) "/C" 2 -nocase} {                                 
                set r [inputbox "Enter a short phrase of up to \                
                    $MAX_STRING_LENGTH characters:" "Configuration" $text]      
                if {ne $r ""} {                                                 
                    set r [string left $r $MAX_STRING_LENGTH]                   
                    ini write $ini_file "config" "text" $r                      
                }                                                               
                console show                                                    
                lib ticol_win -unload                                           
                die                                                             
            }                                                                   
        }                                                                       
    }                                                                           
    win create -fullscreen -closeonlostfocus -movequit                          
    win cursor hide                                                             
    proc display {s} {                                                          
        set start [timer]                                                       
        set text [string left $s $::MAX_STRING_LENGTH]                          
        while {win active} {                                                    
            win cls "#[rnd 0 0xffffff]"                                         
            for {set i 0} {&& [< $i 300] [win active]} {++ i} {                 
                sleep 100                                                       
                win setpos [rnd 0 [win width]] [rnd 0 [win height]]             
                win textcolor "#[rnd 0 0xffffff]"                               
                win puts " $text " -rot [rnd 0 3700] -size $::font_size         
            }                                                                   
            loop 40  {                                                          
                if {! [win active]} break                                       
                sleep 100                                                       
            }                                                                   
            if {$::opt_preview} {                                               
                if {>= [timer] [+ $start $::SCREENSAVER_TIMEOUT_MS]} {          
                    return                                                      
                }                                                               
            }                                                                   
        }                                                                       
    }                                                                           
    if {> $argc 0} {                                                            
        for {set i 0} {< $i $argc} {++ i} {                                     
            if {&& [eqi $argv($i) "/p"] [< [+ $i 1] $argc]} {                   
                set opt_preview $true                                           
                set preview_handle $argv([+ $i 1])                              
                win preview $preview_handle                                     
                set font_size 10                                                
                break                                                           
            }                                                                   
        }                                                                       
    }                                                                           
    display $text                                                               
    console show                                                                
    win close                                                                   
    lib ticol_win -unload                                                       

See also: win, win create, plugins

Potential Plugins and Extensions

Although time and resources are limited, the following plugins might be
nice to do: Source for an example plugin project might be released.
Also a sample project and which will link to a static LIB file

Logo            A Logo like language plugin
Compiler        A pseudo-compiler which can insert Ticol scripts into an
                exectable binary
                This would require Microsoft Visual C++ and use of the
                Ticol LIB file
Plugin sample   Source code for very basic plugin example

See also: plugins, ticol

Appendix - Windows Version Numbers

Major version numbers as reported by [info winver -vernum]

    3.1      Windows NT 3.1  Server and Workstation (3.10.528)                  
    3.5      Windows NT 3.5  Server and Workstation (3.5.807)                   
    3.51     Windows NT 3.51 Server and Workstation (3.51.1057)                 
    3.51     Windows NT 3.51 Newshell Preview plus IE5 (4.0.1057)               
    4.0      Windows 95 (4.0.950)                                               
    4.0      Windows 95 OEM SR1 (95A) (4.00.950)                                
    4.0      Windows 95 OEM SR2 (95B) (4.00.1111)                               
    4.0      Windows 95 OEM SR2.1 (4.03.1212-1214)                              
    4.0      Windows 95 OEM SR2.5C (4.03.1214)                                  
    4.0      Windows NT 4.0  Server and Workstation (4.0.1381)                  
    4.10     Windows 98 (4.10.1998)                                             
    4.10     Windows 98 SE (4.10.2222)                                          
    4.90     Windows ME (4.90.3000)                                             
    5.0      Windows 2000 (5.00.2195)                                           
    5.0      Windows 2000 Professional (5.0.2195)                               
    5.0      Windows 2000 Server (5.0.2195)                                     
    5.1      Windows XP RC1 (5.1.2505)                                          
    5.1      Windows XP (5.1.2600)                                              
    5.1      Windows XP SP1 (5.1.2600.1105-1106)                                
    5.1      Windows XP SP2 (5.1.2600.2180)                                     
    5.1      Windows XP SP3 (5.1.2600)                                          
    5.2      Windows XP Pro x64 Edition (5.2.3790)                              
    5.2      Windows Server 2003 (5.2.3790)) --                                 
    5.2      Windows Home Server (5.2.3790)                                     
    5.2      Windows Server 2003 SP1 (5.2.3790.1180)                            
    5.2      Windows Server 2003R2                                              
    6.0      Windows Vista RTM (6.0.6000.16386)                                 
    6.0      Windows Vista (6.0.6000)                                           
    6.0      Windows Vista SP2 (6.0.6002)                                       
    6.0      Windows Server 2008 (6.0.6001)                                     
    6.1      Windows 7 RTM (6.1.7600.16385)                                     
    6.1      Windows 7 (6.1.7600)                                               
    6.1      Windows Sever 2008R2                                               
    6.1      Windows 7 SP1 (6.1.7601)                                           
    6.2      Windows 8 RTM (6.2.9200.16384)                                     
    6.2      Windows 8 (6.2.9200)                                               
    6.2      Windows Server 2012 (6.2.9200)                                     
    6.3      Windows 8.1 (6.3.9600.16384.130821-1623)                           
    6.3      Windows Server 2012R2 (6.3.9600) (Misreports as 6.2)               

See also: info winver

--------------------------------------------------------------------------

Documentation generated on Sun, 14th May 2023 21:45:19

-o-