Welcome to Ticol Tcl Interpreter (C) M Shaw 2010-2023 _______ __ ___ ___ ____ /_ __(_)______ / / _ __ < / |_ /_ / / / / / __/ _ \/ / | |/ / / / / __/ / / /_/ /_/\__/\___/_/ |___/ /_(_)____//_/ See any associated README.TXT for latest updates and amendments --------------------------------------------------------------------------- Ticol v1.27 - 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. --------------------------------------------------------------------------- Acknowledgements are made for the following code: 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) 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 Copyright(C) 1980,1983,1988,1993 The Regents of the University of California Documentation generated on Sun, 14th May 2023 21:45:20 -------------------------------------------------------------------------- Ticol Manual - Copyright (C) M Shaw 2010-2023 _______ __ ___ ___ ____ /_ __(_)______ / / _ __ < / |_ /_ / / / / / __/ _ / / | |/ / / / / __/ / / /_/ /_/__/___/_/ |___/ /_(_)____//_/ See any associated README.TXT for latest updates and amendments Use: help 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 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 world"" Result: Hello world See also: syntax, Tcl -------------------------------------------------------------------------- addslash addslash varname -var -separator -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 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 # Display ANSI or plain text ansi -slow # Slow down output (useful for animations) ansi -veryslow # Slowest output (useful for animations) ansi -mono # Monochrome only (disable colour codes) ansi -width N # Wrap to console width of N characters ansi -80 # Force 80 column line-wrap in > 80 col mode # 80 column displays will wrap at column 80 ansi -step # Single-step output by CSI code ansi -trace # Show translated CSI code to the Title bar ansi -noansi # Don't apply the detected ANSI codes ansi -trans # List translated codes as detected ansi -codes # Output found CSI codes. 1 per line # Add -text to include plaintext ansi -nocrlf # Suppress CRLFs in the source text ansi -fix # Fix with single line drawing characters ansi -fix2 # Fix with double line drawing characters ansi -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 " 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 {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 $m $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 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 =%-45.45s " $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: %-20.20s=%-45.45s " $ss $val incr i } Result: (prints out environment table) 1: ProgramFiles =C:Program Files (x86) 2: USERPROFILE =C:UsersAdmin 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 $l =$r" } ++ i } Result: 1 ALLUSERSPROFILE =C:ProgramData 2 APPDATA =C:UsersAdminAppDataRoaming 3 COMPUTERNAME =FIDO 4 ComSpec =C:Windowssystem32cmd.exe 5 CommonProgramFiles =C:Program Files (x86)Common Files 6 CommonProgramFiles(x86) =C:Program Files (x86)Common Files 7 CommonProgramW6432 =C:Program FilesCommon 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' '[array item a $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:UsersAdmin 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:UsersAdmin OS=Windows_NT ComSpec=C:Windowssystem32cmd.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:UsersAdminAppDataRoaming HOMEDRIVE=C: HOMEPATH=UsersAdmin LOCALAPPDATA=C:UsersAdminAppDataLocal 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:UsersAdminAppDataLocal C:UsersPublic C:UsersAdminAppDataRoaming 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 "" 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 $env($x)"} Result: TEMP C:UsersAdminAppDataLocalTemp TMP C:UsersAdminAppDataLocalTemp 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 %s " $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 Value '$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:WINDOWSsystem32cmd.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: %-40.40s " $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: %-40.40s " $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: ref: %s => %-40.40s " $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 ' 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: $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 " " -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 type placeholder puts $a(0) Result: Hello 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 "" # [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 or 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= 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 ] 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 ?-explain? calc {} ?-explain? calc "" ?-explain? calc ?-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 $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 $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 # 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 ?.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: 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: 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 is omitted then infile.tcx is created /64 Base64 encode TCX output produced with /C (above) /PW: 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] 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: 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= 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_USERConsole] "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 ] # Make a compressed number integer [cvn ] # 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 + - . " 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 or 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 xfexffhellox21x00x22worldx02xffxfe -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 " [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 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 "" 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:' 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 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 " 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 " 123 4.567 Hi from printf 123 4.567000 -- calldll mslib145 MapNetworkDrive "\srv01c[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' Rvalue:'$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]' ' [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 Test Form
	

Enter your name:

Enter some text:

 

Tcl back-end script option expression off puts "Content-type: text/html " # 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 "" array foreach _POST val subscript { # Iterate array $_POST() puts "
Result $_POST($subscript)='$val'
" } puts "
" } flush # Flush stdout Result: 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 " ". 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 [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: [/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 " [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: 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 tcl commands... where 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_USERConsole] "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: emp icol.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 # Return specific date using PHP format date now # Returns current date as a double value date now -format # 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 -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 ] and [date -format 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: 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: 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 %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} 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 [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 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 [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 BR Number: %5s LNER Number: %4i " $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 arrays Variant arrays are available for use with calldll 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 puts $a # Returns "" 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_.:& " 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 \snoopyc$] [* [* 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"] " 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 world # Not valid, will raise an error puts {Hello world} # Valid grouping using braces puts "Hello world" # 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 "" 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 "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 " "] " " 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 . '.xit l + Parents of 3::'b' (0x20cbde0 / 34389472): + Value: '' | | Lvl::Variable Name This -> Parent Type Const | ------------------------------------------------------------------ + 2::b 0x020cbe40 -> 0x020cb890 array False + Value: '' + 1::b 0x020cb890 -> 0x020c9540 array False + Value: '' + 0::b 0x020c9540 -> 0x00000000 array False + Value: '' 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 o icol.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 o icol.exe "$(FULL_CURRENT_PATH)" /na * You may also need to add /PAUSE to halt the screen before closing C:path o icol.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 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! " 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 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: clfoo.tcx MD5 is: 0823aceda778c586e1ee072c2f7d9919 * Encrypt: Successfully created obfuscated output file C: clfoo.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?: