aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2010-01-24 13:46:32 +1000
committerSteve Bennett <steveb@workware.net.au>2010-10-15 11:02:45 +1000
commitee6dbe0a8d66f8f17a5da82e8dad810d88e33bf6 (patch)
tree994b59d11b3199f697adaea8303ae8b5dc438397
parent541fe7c30691ace08847a35280d9cc8bc02e9a72 (diff)
downloadjimtcl-ee6dbe0a8d66f8f17a5da82e8dad810d88e33bf6.zip
jimtcl-ee6dbe0a8d66f8f17a5da82e8dad810d88e33bf6.tar.gz
jimtcl-ee6dbe0a8d66f8f17a5da82e8dad810d88e33bf6.tar.bz2
Allow catch to specify what is caught
*: Default to the same as Tcl. Not signal, eval, exit. *: Use 'catch -exit' to also catch exit. *: Also map for standard return codes: [info returncodes] *: Also Jim_ReturnCode() *: Add Jim_FindByName() for searching in a char* array *: Fix 'info nameofexectutable' if $::jim_argv0 is not set
-rw-r--r--doc/jim_tcl.txt536
-rw-r--r--jim-interactive.c13
-rw-r--r--jim-syslog.c22
-rw-r--r--jim.c204
-rw-r--r--jim.h9
-rw-r--r--tclcompat.tcl16
-rw-r--r--tests/error.test2
-rw-r--r--tests/misc.test48
8 files changed, 502 insertions, 348 deletions
diff --git a/doc/jim_tcl.txt b/doc/jim_tcl.txt
index fe2e96b..7ac6e3f 100644
--- a/doc/jim_tcl.txt
+++ b/doc/jim_tcl.txt
@@ -71,6 +71,9 @@ Since v0.61:
9. Add support for 'unset -nocomplain'
10. Add support for list commands: 'lassign', 'lrepeat'
11. Fully-functional 'lsearch' is now implemented
+12. Add 'info nameofexecutable' and 'info returncodes'
+13. Allow 'catch' to determine what return codes are caught
+14. Allow 'incr' to increment an unset variable by first setting to 0
TCL INTRODUCTION
-----------------
@@ -169,7 +172,7 @@ The first field must be the name of a command, and the
additional fields, if any, are arguments that will be passed to
that command. For example, the command:
- set a 22
+ set a 22
has three fields: the first, 'set', is the name of a Tcl command, and
the last two, 'a' and '22', will be passed as arguments to
@@ -499,20 +502,20 @@ specify a position in the string relative to the start or end of the string/list
The index may be one of the following forms:
`integer`::
- A simple integer, where '0' refers to the first element of the string
- or list.
+ A simple integer, where '0' refers to the first element of the string
+ or list.
`integer+integer` or::
`integer-integer`::
- The sum or difference of the two integers. e.g. +2+3+ refers to the 5th element.
- This is useful when used with (e.g.) +$i+1+ rather than the more verbose
- +[expr {$i+1\}]+
+ The sum or difference of the two integers. e.g. +2+3+ refers to the 5th element.
+ This is useful when used with (e.g.) +$i+1+ rather than the more verbose
+ +[expr {$i+1\}]+
`end`::
- The last element of the string or list.
+ The last element of the string or list.
`end-integer`::
- The 'nth-from-last' element of the string or list.
+ The 'nth-from-last' element of the string or list.
COMMAND SUMMARY
---------------
@@ -635,14 +638,14 @@ of precedence:
[[OperatorPrecedence]]
`int() double() round() abs()`::
Unary functions.
- int() converts the numeric argument to an integer by truncating down.
- double() converts the numeric argument to floating point.
- round() converts the numeric argument to the closest integer value.
- abs() takes the absolute value of the numeric argument.
+ int() converts the numeric argument to an integer by truncating down.
+ double() converts the numeric argument to floating point.
+ round() converts the numeric argument to the closest integer value.
+ abs() takes the absolute value of the numeric argument.
`sin() cos() tan() asin() acos() atan() sinh() cosh() tanh() ceil() floor() exp() log() log10() sqrt()`::
Unary math functions.
- If Jim is compiled with math support, these functions are available.
+ If Jim is compiled with math support, these functions are available.
`- + ~ !`::
Unary minus, unary plus, bit-wise NOT, logical NOT. None of these operands
@@ -651,7 +654,7 @@ of precedence:
`**`::
Power. e.g. pow(). If Jim is compiled with math support, supports doubles and
- integers. Otherwise supports integers only.
+ integers. Otherwise supports integers only.
`* / %`::
Multiply, divide, remainder. None of these operands may be
@@ -674,7 +677,7 @@ of precedence:
Boolean equal and not equal. Each operator produces a zero/one result.
Valid for all operand types. *Note* that values will be converted to integers
if possible, then floating point types, and finally strings will be compared.
- It is recommended that 'eq' and 'ne' should be used for string comparison.
+ It is recommended that 'eq' and 'ne' should be used for string comparison.
`eq ne`::
String equal and not equal. Uses the string value directly without
@@ -682,8 +685,8 @@ of precedence:
`in ni`::
String in list and not in list. For 'in', result is 1 if the left operand (as a string)
- is contained in the right operand (as a list), or 0 otherwise. The result for
- '{$a ni $list}' is equivalent to '{!($a in $list)}'.
+ is contained in the right operand (as a list), or 0 otherwise. The result for
+ '{$a ni $list}' is equivalent to '{!($a in $list)}'.
`&`::
Bit-wise AND. Valid for integer operands only.
@@ -707,7 +710,7 @@ of precedence:
evaluates to non-zero, then the result is the value of *y*.
Otherwise the result is the value of *z*.
The *x* operand must have a numeric value, while *y* and *z* can
- be of any type.
+ be of any type.
See the C manual for more details on the results
produced by each operator.
@@ -858,14 +861,14 @@ arguments. Support for this feature is also available in Jim.
Consider the following attempt to exec a list:
- set cmd {ls -l}
- exec $cmd
+ set cmd {ls -l}
+ exec $cmd
This will attempt to exec the a command named "ls -l", which will clearly not
work. Typically eval and concat are required to solve this problem, however
it can be solved much more easily with '\{*\}'.
- exec {*}$cmd
+ exec {*}$cmd
This will expand the following argument into individual elements and then evaluate
the resulting command.
@@ -1116,10 +1119,10 @@ A reference can be thought of as holding a value with one level of indirection,
where the value may be garbage collected when unreferenced.
Consider the following example:
- jim> set r [ref "One String" test]
- <reference.<test___>.00000000000000000000>
- jim> getref $r
- One String
+ jim> set r [ref "One String" test]
+ <reference.<test___>.00000000000000000000>
+ jim> getref $r
+ One String
The operation 'ref' creates a references to the value specfied by the
first argument. (The second argument is a "type" used for documentation purposes).
@@ -1127,10 +1130,10 @@ first argument. (The second argument is a "type" used for documentation purposes
The operation 'getref' is the dereferencing operation which retrieves the value
stored in the reference.
- jim> setref $r "New String"
- New String
- jim> getref $r
- New String
+ jim> setref $r "New String"
+ New String
+ jim> getref $r
+ New String
The operation 'setref' replaces the value stored by the reference. If the old value
is no longer accessible by any reference, it will eventually be automatically be garbage
@@ -1148,15 +1151,15 @@ and discard objects which are no longer accessible by any reference.
The 'collect' command may be used to force garbage collection. Consider a reference created
with a finalizer:
- jim> proc f {ref value} { puts "Finaliser called for $ref,$value" }
- jim> set r [ref "One String" test f]
- <reference.<test___>.00000000000
- jim> collect
- 0
- jim> set r ""
- jim> collect
- Finaliser called for <reference.<test___>.00000000000,One String
- 1
+ jim> proc f {ref value} { puts "Finaliser called for $ref,$value" }
+ jim> set r [ref "One String" test f]
+ <reference.<test___>.00000000000
+ jim> collect
+ 0
+ jim> set r ""
+ jim> collect
+ Finaliser called for <reference.<test___>.00000000000,One String
+ 1
Note that once the reference, 'r', was modified so that it no longer
contained a reference to the value, the garbage collector discarded
@@ -1164,22 +1167,22 @@ the value (after calling the finalizer).
The finalizer for a reference may be examined or changed with the 'finalize' command
- jim> finalize $r
- f
- jim> finalize $r newf
- newf
+ jim> finalize $r
+ f
+ jim> finalize $r newf
+ newf
Lambda
------
Jim provides a garbage collected lambda function. This is a procedure
which is able to create an anonymous procedure. Consider:
- jim> set f [lambda {a} {{x 0}} { incr x $a }]
- jim> $f 1
- 1
- jim> $f 2
- 3
- jim> set f ""
+ jim> set f [lambda {a} {{x 0}} { incr x $a }]
+ jim> $f 1
+ 1
+ jim> $f 2
+ 3
+ jim> set f ""
This create an anonymous procedure, 'f', with a static variable
which is incremented by the supplied value and the result returned.
@@ -1375,7 +1378,7 @@ will return '2'.
catch
~~~~~
-+*catch* ?*-signal*? 'command' ?'varName'?+
++*catch* '?-?no?code ...? command ?varName?'+
The 'catch' command may be used to prevent errors from aborting
command interpretation. 'Catch' evalues *command*, and
@@ -1393,9 +1396,16 @@ If the *varName* argument is given, then it gives the name of a variable;
'catch' will set the value of the variable to the string returned from
*command* (either a result or an error message).
-Normally 'catch' will *not* catch any signal. However if *-signal* is specified,
-any signals marked as *handle* by 'signal' will be caught and 'catch' will return
-+JIM_SIGNAL+ (5). In this case, the return values is the name of the signal caught.
+Normally 'catch' will *not* catch any of the codes +JIM_EXIT+, +JIM_EVAL+ or +JIM_SIGNAL+.
+The set of codes which will be caught may be modified by specifying the one more codes before
+*command*. (In this case, *varName* must be specified).
+
+e.g. To catch +JIM_EXIT+ but not +JIM_BREAK+ or +JIM_CONTINUE+
+
+ catch -exit -nobreak -nocontinue { ... }
+
+Note that if a signal marked as 'signal handle' is caught with 'catch -signal', the return value
+(stored in *varName*) is name of the signal caught.
cd
~~
@@ -1411,16 +1421,16 @@ be removed in some applications.
clock
~~~~~
+*clock seconds*+::
- Returns the current time as seconds since the epoch.
+ Returns the current time as seconds since the epoch.
+*clock format* 'seconds' ?*-format* 'format?'+::
- Format the given time (seconds since the epoch) according to the given
- format. See strftime(3) for supported formats.
- If no format is supplied, "%c" is used.
+ Format the given time (seconds since the epoch) according to the given
+ format. See strftime(3) for supported formats.
+ If no format is supplied, "%c" is used.
+*clock scan* 'str' *-format* 'format'+::
- Scan the given time string using the given format string.
- See strptime(3) for supported formats.
+ Scan the given time string using the given format string.
+ See strptime(3) for supported formats.
close
~~~~~
@@ -1468,45 +1478,45 @@ The *option* argument determines what action is carried out by the
command. The legal *options* are:
*dict create* '?key value ...?'+::
- Create and return a new dictionary value that contains each of
- the key/value mappings listed as arguments (keys and values
- alternating, with each key being followed by its associated
- value.)
+ Create and return a new dictionary value that contains each of
+ the key/value mappings listed as arguments (keys and values
+ alternating, with each key being followed by its associated
+ value.)
*dict exists* 'dictionary key ?key ...?'+::
- Returns a boolean value indicating whether the given key (or path
- of keys through a set of nested dictionaries) exists in the given
- dictionary value. This returns a true value exactly when 'dict get'
- on that path will succeed.
+ Returns a boolean value indicating whether the given key (or path
+ of keys through a set of nested dictionaries) exists in the given
+ dictionary value. This returns a true value exactly when 'dict get'
+ on that path will succeed.
*dict get* 'dictionary ?key ...?'+::
- Given a dictionary value (first argument) and a key (second argument),
- this will retrieve the value for that key. Where several keys are
- supplied, the behaviour of the command shall be as if the result
- of 'dict get $dictVal $key' was passed as the first argument to
- dict get with the remaining arguments as second (and possibly
- subsequent) arguments. This facilitates lookups in nested dictionaries.
- If no keys are provided, dict would return a list containing pairs
- of elements in a man- ner similar to array get. That is, the first
- element of each pair would be the key and the second element would
- be the value for that key. It is an error to attempt to retrieve
- a value for a key that is not present in the dictionary.
+ Given a dictionary value (first argument) and a key (second argument),
+ this will retrieve the value for that key. Where several keys are
+ supplied, the behaviour of the command shall be as if the result
+ of 'dict get $dictVal $key' was passed as the first argument to
+ dict get with the remaining arguments as second (and possibly
+ subsequent) arguments. This facilitates lookups in nested dictionaries.
+ If no keys are provided, dict would return a list containing pairs
+ of elements in a man- ner similar to array get. That is, the first
+ element of each pair would be the key and the second element would
+ be the value for that key. It is an error to attempt to retrieve
+ a value for a key that is not present in the dictionary.
*dict set* 'dictionaryName key ?key ...? value'+::
- This operation takes the *name* of a variable containing a dictionary
- value and places an updated dictionary value in that variable
- containing a mapping from the given key to the given value. When
- multiple keys are present, this operation creates or updates a chain
- of nested dictionaries.
+ This operation takes the *name* of a variable containing a dictionary
+ value and places an updated dictionary value in that variable
+ containing a mapping from the given key to the given value. When
+ multiple keys are present, this operation creates or updates a chain
+ of nested dictionaries.
*dict unset* 'dictionaryName key ?key ...? value'+::
- This operation (the companion to 'dict set') takes the name of a
- variable containing a dictionary value and places an updated
- dictionary value in that variable that does not contain a mapping
- for the given key. Where multiple keys are present, this describes
- a path through nested dictionaries to the mapping to remove. At
- least one key must be specified, but the last key on the key-path
- need not exist. All other components on the path must exist.
+ This operation (the companion to 'dict set') takes the name of a
+ variable containing a dictionary value and places an updated
+ dictionary value in that variable that does not contain a mapping
+ for the given key. Where multiple keys are present, this describes
+ a path through nested dictionaries to the mapping to remove. At
+ least one key must be specified, but the last key on the key-path
+ need not exist. All other components on the path must exist.
env
~~~
@@ -1564,10 +1574,10 @@ errorInfo
Returns a human-readable representation of the given error message and stack trace.
Typical usage is:
- if {[catch {...} error]} {
- puts stderr [errorInfo $error [info stacktrace]]
- exit 1
- }
+ if {[catch {...} error]} {
+ puts stderr [errorInfo $error [info stacktrace]]
+ exit 1
+ }
See also 'error'.
@@ -1642,8 +1652,8 @@ An *arg* may have one of the following special forms:
redirected to the given (writable) file descriptor.
+>&filename+::
- Both the standard output and standard error of the last command
- in the pipeline is redirected to the file.
+ Both the standard output and standard error of the last command
+ in the pipeline is redirected to the file.
+>>&filename+::
As above, but append to the file.
@@ -1753,8 +1763,8 @@ abbreviation for *option* is acceptable. The valid options are:
+*file join* 'arg arg ...'+::
Joins multiple path components. Note that if any components is
- an absolute path, the preceding components are ignored.
- Thus 'file join /tmp /root' returns '/root'.
+ an absolute path, the preceding components are ignored.
+ Thus 'file join /tmp /root' returns '/root'.
+*file lstat* 'name varName'+::
Same as 'stat' option (see below) except uses the *lstat*
@@ -1827,8 +1837,8 @@ abbreviation for *option* is acceptable. The valid options are:
+*file tempfile* '?template?'+::
Creates and returns the name of a unique temporary file. If *template* is omitted, a
- default template will be used to place the file in /tmp. See mkstemp(3) for
- the format of the template and security concerns.
+ default template will be used to place the file in /tmp. See mkstemp(3) for
+ the format of the template and security concerns.
+*file type* 'name'+::
Returns a string giving the type of file *name*, which will be
@@ -2037,6 +2047,9 @@ integer) is added to the value of variable *varName*; otherwise
The new value is stored as a decimal string in variable *varName*
and also returned as result.
+If the variable does not exist, the variable is implicitly created
+and set to +0+ first.
+
info
~~~~
@@ -2084,7 +2097,7 @@ The legal *option*'s (which may be abbreviated) are:
'string match'.
+*info hostname*+::
- An alias for 'os.hostname' for compatibility with Tcl 6.x
+ An alias for 'os.gethostname' for compatibility with Tcl 6.x
+*info level* ?'number'?+::
If *number* is not specified, this command returns a number
@@ -2108,8 +2121,11 @@ The legal *option*'s (which may be abbreviated) are:
Matching is determined using the same rules as for 'string match'.
+*info nameofexecutable*+::
- Returns the name of the binary file from which the application was invoked, either
- as a path relative to the current directory or as a full path.
+ Returns the name of the binary file from which the application
+ was invoked, either
+ as a path relative to the current directory or as a full
+ path. If the path can't be determined, returns the empty
+ string.
+*info procs* ?'pattern'?+::
If *pattern* isn't specified, returns a list of all the
@@ -2118,6 +2134,10 @@ The legal *option*'s (which may be abbreviated) are:
are returned. Matching is determined using the same rules as for
'string match'.
++*info returncodes*+::
+ Returns a list representing the mapping of standard return codes
+ to names. e.g. +{0 ok 1 error 2 return ...}+
+
+*info script*+::
If a Tcl script file is currently being evaluated (i.e. there is a
call to 'Jim_EvalFile' active or there is an active invocation
@@ -2205,9 +2225,9 @@ the variables given by the *varName* arguments in order. If there are more varia
list elements, the remaining variables are set to the empty string. If there are more list ele-
ments than variables, a list of unassigned elements is returned.
- jim> lassign {1 2 3} a b; puts a=$a,b=$b
- 3
- a=1,b=2
+ jim> lassign {1 2 3} a b; puts a=$a,b=$b
+ 3
+ a=1,b=2
lindex
~~~~~~
@@ -2330,10 +2350,10 @@ lmap
For example:
- jim> lmap i {1 2 3 4 5} {expr $i*$i}
- 1 4 9 16 25
- jim> lmap a {1 2 3} b {A B C} {list $a $b}
- {1 A} {2 B} {3 C}
+ jim> lmap i {1 2 3 4 5} {expr $i*$i}
+ 1 4 9 16 25
+ jim> lmap a {1 2 3} b {A B C} {list $a $b}
+ {1 A} {2 B} {3 C}
If the body invokes 'continue', no value is added for this iteration.
If the body invokes 'break', the loop ends and no more values are added.
@@ -2395,8 +2415,8 @@ lrepeat
Build a list by repeating elements *number* times (which must be
a positive integer).
- jim> lrepeat 3 a b
- a b a b a b
+ jim> lrepeat 3 a b
+ a b a b a b
lreverse
~~~~~~~~
@@ -2404,8 +2424,8 @@ lreverse
Returns the list in reverse order.
- jim> lreverse {1 2 3}
- 3 2 1
+ jim> lreverse {1 2 3}
+ 3 2 1
lsearch
~~~~~~~
@@ -2419,42 +2439,42 @@ the list are to be matched against pattern and must have one of the values below
*Note* that this command is different from Tcl in that default match type is '-exact' rather than '-glob'.
+'-exact'+::
- *pattern* is a literal string that is compared for exact equality against each list element.
- This is the default.
+ *pattern* is a literal string that is compared for exact equality against each list element.
+ This is the default.
+'-glob'+::
- *pattern* is a glob-style pattern which is matched against each list element using the same
- rules as the string match command.
+ *pattern* is a glob-style pattern which is matched against each list element using the same
+ rules as the string match command.
+'-regexp'+::
- *pattern* is treated as a regular expression and matched against each list element using
- the rules described by 'regexp'.
+ *pattern* is treated as a regular expression and matched against each list element using
+ the rules described by 'regexp'.
+'-all'+::
- Changes the result to be the list of all matching indices (or all matching values if
- '-inline' is specified as well). If indices are returned, the indices will be in numeric
- order. If values are returned, the order of the values will be the order of those values
- within the input list.
+ Changes the result to be the list of all matching indices (or all matching values if
+ '-inline' is specified as well). If indices are returned, the indices will be in numeric
+ order. If values are returned, the order of the values will be the order of those values
+ within the input list.
+'-inline'+::
- The matching value is returned instead of its index (or an empty string if no value
- matches). If '-all' is also specified, then the result of the command is the list of all
- values that matched. The '-inline' and '-bool' options are mutually exclusive.
+ The matching value is returned instead of its index (or an empty string if no value
+ matches). If '-all' is also specified, then the result of the command is the list of all
+ values that matched. The '-inline' and '-bool' options are mutually exclusive.
+'-bool'+::
- Changes the result to '1' if a match was found, or '0' otherwise. If '-all' is also specified,
- the result will be a list of '0' and '1' for each element of the list depending upon whether
- the corresponding element matches. The '-inline' and '-bool' options are mutually exclusive.
+ Changes the result to '1' if a match was found, or '0' otherwise. If '-all' is also specified,
+ the result will be a list of '0' and '1' for each element of the list depending upon whether
+ the corresponding element matches. The '-inline' and '-bool' options are mutually exclusive.
+'-not'+::
- This negates the sense of the match, returning the index (or value
- if '-inline' is specified) of the first non-matching value in the
- list. If '-bool' is also specified, the '0' will be returned if a
- match is found, or '1' otherwise. If '-all' is also specified,
- non-matches will be returned rather than matches.
+ This negates the sense of the match, returning the index (or value
+ if '-inline' is specified) of the first non-matching value in the
+ list. If '-bool' is also specified, the '0' will be returned if a
+ match is found, or '1' otherwise. If '-all' is also specified,
+ non-matches will be returned rather than matches.
+'-nocase'+::
- Causes comparisons to be handled in a case-insensitive manner.
+ Causes comparisons to be handled in a case-insensitive manner.
lsort
~~~~~
@@ -2592,18 +2612,18 @@ Either from the static variable definition, or from the enclosing scope.
Consider the following example:
- set a 1
- proc a {} {a {b 2}} {
- set c 1
- puts "$a $b $c"
- incr a
- incr b
- incr c
- }
- . a
- 1 2 1
- . a
- 2 3 1
+ set a 1
+ proc a {} {a {b 2}} {
+ set c 1
+ puts "$a $b $c"
+ incr a
+ incr b
+ incr c
+ }
+ . a
+ 1 2 1
+ . a
+ 2 3 1
The static variable *a* has no initialiser, so it is initialised from
the enclosing scope with the value 1. (Note that it is an error if there
@@ -2612,7 +2632,7 @@ has an initialiser, so it is initialised to 2.
Unlike a local variable, the value of a static variable is retained across
invocations of the procedure.
-
+
puts
~~~~
+*puts* ?*-nonewline*? '?fileId? string'+
@@ -2656,14 +2676,14 @@ range
Returns a list of integers starting at *start* (defaults to 0)
and ranging up to but not including *end* in steps of *step* defaults to 1).
- jim> range 5
- 0 1 2 3 4
- jim> range 2 5
- 2 3 4
- jim> range 2 10 4
- 2 6
- jim> range 7 4 -2
- 7 5
+ jim> range 5
+ 0 1 2 3 4
+ jim> range 2 5
+ 2 3 4
+ jim> range 2 10 4
+ 2 6
+ jim> range 7 4 -2
+ 7 5
read
~~~~
@@ -2901,28 +2921,28 @@ Commands which return a list of signal names do so using the canonical form:
"+SIGINT SIGTERM+".
+*signal handle* ?'signals ...'?+::
- If no signals are given, returns a list of all signals which are currently
- being handled.
- If signals are specified, these are added to the list of signals currently
- being handled.
+ If no signals are given, returns a list of all signals which are currently
+ being handled.
+ If signals are specified, these are added to the list of signals currently
+ being handled.
+*signal ignore* ?'signals ...'?+::
- If no signals are given, returns a lists all signals which are currently being
- ignored.
- If signals are specified, these are added to the list of signals currently
- being ignored.
+ If no signals are given, returns a lists all signals which are currently being
+ ignored.
+ If signals are specified, these are added to the list of signals currently
+ being ignored.
+*signal default* ?'signals ...'?+::
- If no signals are given, returns a lists all signals which are currently have
- the default behaviour.
- If signals are specified, these are added to the list of signals which have
- the default behaviour.
+ If no signals are given, returns a lists all signals which are currently have
+ the default behaviour.
+ If signals are specified, these are added to the list of signals which have
+ the default behaviour.
+*signal throw* ?'signal'?+::
- Raises the given signal, which defaults to +SIGINT+ if not specified.
- The behaviour is identical to:
+ Raises the given signal, which defaults to +SIGINT+ if not specified.
+ The behaviour is identical to:
- kill signal [pid]
+ kill signal [pid]
sleep
~~~~~
@@ -2999,9 +3019,9 @@ The legal options (which may be abbreviated) are:
the characters in *string1*. If found, return the index of the
first character in the first such match within *string2*. If not
found, return -1. If *firstIndex* is specified, matching will start
- from *firstIndex* of *string1*.
+ from *firstIndex* of *string1*.
::
- See STRING AND LIST INDEX SPECIFICATIONS for all allowed forms for *firstIndex*.
+ See STRING AND LIST INDEX SPECIFICATIONS for all allowed forms for *firstIndex*.
+*string index* 'string charIndex'+::
Returns the *charIndex*'th character of the *string*
@@ -3011,16 +3031,16 @@ The legal options (which may be abbreviated) are:
or equal to the length of the string then an empty string is
returned.
::
- See STRING AND LIST INDEX SPECIFICATIONS for all allowed forms for *charIndex*.
+ See STRING AND LIST INDEX SPECIFICATIONS for all allowed forms for *charIndex*.
+*string last* 'string1 string2 ?lastIndex?'+::
Search *string2* for a sequence of characters that exactly match
the characters in *string1*. If found, return the index of the
first character in the last such match within *string2*. If there
is no match, then return -1. If *lastIndex* is specified, only characters
- up to *lastIndex* of *string2* will be considered in the match.
+ up to *lastIndex* of *string2* will be considered in the match.
::
- See STRING AND LIST INDEX SPECIFICATIONS for all allowed forms for *lastIndex*.
+ See STRING AND LIST INDEX SPECIFICATIONS for all allowed forms for *lastIndex*.
+*string length* 'string'+::
Returns a decimal string giving the number of characters in *string*.
@@ -3122,8 +3142,8 @@ characters with no special interpretation.
special treatment to double quotes or curly braces. For example,
the following script returns 'xyz \{44\}', not 'xyz \{$a\}'.
- set a 44
- subst {xyz {$a}}
+ set a 44
+ subst {xyz {$a}}
switch
@@ -3141,30 +3161,30 @@ no default is given, then the switch command returns an empty string.
If the initial arguments to switch start with - then they are treated
as options. The following options are currently supported:
- +-exact+::
- Use exact matching when comparing string to a
- pattern. This is the default.
+ +-exact+::
+ Use exact matching when comparing string to a
+ pattern. This is the default.
- +-glob+::
- When matching string to the patterns, use glob-style
- matching (i.e. the same as implemented by the string
- match command).
+ +-glob+::
+ When matching string to the patterns, use glob-style
+ matching (i.e. the same as implemented by the string
+ match command).
- +-regexp+::
- When matching string to the patterns, use regular
- expression matching (i.e. the same as implemented
- by the regexp command).
+ +-regexp+::
+ When matching string to the patterns, use regular
+ expression matching (i.e. the same as implemented
+ by the regexp command).
- +-command 'commandname'+::
- When matching string to the patterns, use the given command, which
- must be a single word. The command is invoked as
- 'commandname pattern string', or 'commandname -nocase pattern string'
- and must return 1 if matched, or 0 if not.
+ +-command 'commandname'+::
+ When matching string to the patterns, use the given command, which
+ must be a single word. The command is invoked as
+ 'commandname pattern string', or 'commandname -nocase pattern string'
+ and must return 1 if matched, or 0 if not.
- +--+::
- Marks the end of options. The argument following
- this one will be treated as string even if it starts
- with a -.
+ +--+::
+ Marks the end of options. The argument following
+ this one will be treated as string even if it starts
+ with a -.
Two syntaxes are provided for the pattern and body arguments. The
first uses a separate argument for each of the patterns and commands;
@@ -3188,25 +3208,25 @@ body among several patterns.
Below are some examples of switch commands:
- switch abc a - b {format 1} abc {format 2} default {format 3}
+ switch abc a - b {format 1} abc {format 2} default {format 3}
will return 2,
- switch -regexp aaab {
+ switch -regexp aaab {
^a.*b$ -
b {format 1}
a* {format 2}
default {format 3}
- }
+ }
will return 1, and
- switch xyz {
+ switch xyz {
a -
b {format 1}
a* {format 2}
default {format 3}
- }
+ }
will return 3.
@@ -3424,11 +3444,11 @@ descriptors.
posix: os.fork, os.wait, os.gethostname, os.getids, os.uptime
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*os.fork*+::
- Invokes 'fork(2)' and returns the result.
+ Invokes 'fork(2)' and returns the result.
+*os.wait -nohang* 'pid'+::
- Invokes waitpid(2), with WNOHANG if *-nohang* is specified.
- Returns a list of 3 elements.
+ Invokes waitpid(2), with WNOHANG if *-nohang* is specified.
+ Returns a list of 3 elements.
{0 none 0} if -nohang is specified, and the process is still alive.
@@ -3441,16 +3461,16 @@ posix: os.fork, os.wait, os.gethostname, os.getids, os.uptime
{<pid> other 0} otherwise (core dump, stopped, continued, etc.)
+*os.gethostname*+::
- Invokes 'gethostname(3)' and returns the result.
+ Invokes 'gethostname(3)' and returns the result.
+*os.getids*+::
- Returns the various user/group ids for the current process.
+ Returns the various user/group ids for the current process.
- jim> os.getids
- uid 1000 euid 1000 gid 100 egid 100
+ jim> os.getids
+ uid 1000 euid 1000 gid 100 egid 100
+*os.uptime*+::
- Returns the number of seconds since system boot. See description of 'uptime' in 'sysinfo(2)'.
+ Returns the number of seconds since system boot. See description of 'uptime' in 'sysinfo(2)'.
ANSI I/O (aio) and EVENTLOOP API
--------------------------------
@@ -3461,36 +3481,36 @@ See '<<_open,open>>' and '<<_socket,socket>>' for commands which return an I/O h
aio
~~~
+$handle *read ?-nonewline?* '?len?'+::
- Read and return bytes from the stream. To eof if no len.
+ Read and return bytes from the stream. To eof if no len.
+$handle *gets* '?var?'+::
- Read one line and return it or store it in the var
+ Read one line and return it or store it in the var
+$handle *puts ?-nonewline?* 'str'+::
- Write the string, with newline unless -nonewline
+ Write the string, with newline unless -nonewline
+$handle *flush*+::
- Flush the stream
+ Flush the stream
+$handle *eof*+::
- Returns 1 if stream is at eof
+ Returns 1 if stream is at eof
+$handle *close*+::
- Closes the stream
+ Closes the stream
+$handle *seek* 'offset' *?start|current|end?*+::
- Seeks in the stream (default 'current')
+ Seeks in the stream (default 'current')
+$handle *tell*+::
- Returns the current seek position
+ Returns the current seek position
+$handle *ndelay ?0|1?*+::
- Set O_NDELAY (if arg). Returns current/new setting.
- Note that in general ANSI I/O interacts badly with non-blocking I/O.
- Use with care.
+ Set O_NDELAY (if arg). Returns current/new setting.
+ Note that in general ANSI I/O interacts badly with non-blocking I/O.
+ Use with care.
+$handle *accept*+::
- Server socket only: Accept a connection and return stream
+ Server socket only: Accept a connection and return stream
eventloop: after, vwait
~~~~~~~~~~~~~~~~~~~~~~~
@@ -3498,49 +3518,49 @@ eventloop: after, vwait
The following commands allow a script to be invoked when the given condition occurs.
+$handle *readable* '?readable-script ?eof-script??'+::
- Returns script, or invoke readable-script when readable, eof-script on eof, {} to remove
+ Returns script, or invoke readable-script when readable, eof-script on eof, {} to remove
+$handle *writable* '?writable-script?'+::
- Returns script, or invoke writable-script when writable, {} to remove
+ Returns script, or invoke writable-script when writable, {} to remove
+$handle *onexception* '?exception-script?'+::
- Returns script, or invoke exception-script when oob data, {} to remove
+ Returns script, or invoke exception-script when oob data, {} to remove
Time-based execution is also available via the eventloop API.
+*after* 'time script'+::
- The script is executed after the given number of milliseconds have elapsed.
- Returns an event id.
+ The script is executed after the given number of milliseconds have elapsed.
+ Returns an event id.
+*after cancel* 'id'+::
- Cancels an after event with the given event id.
+ Cancels an after event with the given event id.
+*vwait* 'variable'+::
- A call to vwait is required to enter the eventloop. 'vwait' processes events until
- the named variabled changes. The variable need not exist beforehand.
+ A call to vwait is required to enter the eventloop. 'vwait' processes events until
+ the named variabled changes. The variable need not exist beforehand.
socket
~~~~~~
Various socket types may be created.
+*socket unix* 'path'+::
- A unix domain socket client.
+ A unix domain socket client.
+*socket unix.server* 'path'+::
- A unix domain socket server.
+ A unix domain socket server.
+*socket stream* 'hostname:port'+::
- A TCP socket client.
+ A TCP socket client.
+*socket stream.server* '?hostname:?port'+::
- A TCP socket server (*hostname* defaults to 0.0.0.0).
+ A TCP socket server (*hostname* defaults to 0.0.0.0).
+*socket dgram* 'hostname:port'+::
- A UDP socket client.
+ A UDP socket client.
+*socket pipe*+::
- A pipe. Note that unlike all other socket types, this command returns
- a list of two channels: {read write}
+ A pipe. Note that unlike all other socket types, this command returns
+ a list of two channels: {read write}
This command creates a socket connected (client) or bound (server) to the given
address.
@@ -3548,35 +3568,35 @@ address.
The returned value is channel and may generally be used with the various file I/O
commands (gets, puts, read, etc.), either as object-based syntax or Tcl-compatible syntax.
- set f [socket stream www.google.com:80]
- aio.sockstream1
- $f puts -nonewline "GET / HTTP/1.0\r\n\r\n"
- $f gets
- HTTP/1.0 302 Found
- $f close
+ set f [socket stream www.google.com:80]
+ aio.sockstream1
+ $f puts -nonewline "GET / HTTP/1.0\r\n\r\n"
+ $f gets
+ HTTP/1.0 302 Found
+ $f close
Server sockets, however support only 'accept', which is most useful in conjunction with
the EVENTLOOP API.
- set f [socket stream.server 80]
- $f readable {
- set client [$f accept]
- $client gets $buf
- ...
- $client puts -nonewline "HTTP/1.1 404 Not found\r\n"
- $client close
- }
- vwait done
+ set f [socket stream.server 80]
+ $f readable {
+ set client [$f accept]
+ $client gets $buf
+ ...
+ $client puts -nonewline "HTTP/1.1 404 Not found\r\n"
+ $client close
+ }
+ vwait done
The special type 'pipe' isn't really a socket.
- foreach {r w} [socket pipe] break
+ foreach {r w} [socket pipe] break
- # Must close $w after exec
- exec ps >@$w &
- $w close
+ # Must close $w after exec
+ exec ps >@$w &
+ $w close
- $r readable ...
+ $r readable ...
syslog
~~~~~~
diff --git a/jim-interactive.c b/jim-interactive.c
index 9d78c9f..d4272c5 100644
--- a/jim-interactive.c
+++ b/jim-interactive.c
@@ -13,17 +13,16 @@ int Jim_InteractivePrompt(Jim_Interp *interp)
while (1) {
char buf[1024];
const char *result;
- /* NOTE: These must be kept in the same order as JIM_OK, JIM_ERR, ... */
- const char *retcodestr[] = {
- "ok", "error", "return", "break", "continue", "signal", "exit", "eval"
- };
int reslen;
if (retcode != 0) {
- if (retcode >= 1 && retcode < sizeof(retcodestr) / sizeof(*retcodestr))
- printf("[%s] . ", retcodestr[retcode]);
- else
+ const char *retcodestr = Jim_ReturnCode(retcode);
+ if (*retcodestr == '?') {
printf("[%d] . ", retcode);
+ }
+ else {
+ printf("[%s] . ", retcodestr);
+ }
} else
printf(". ");
fflush(stdout);
diff --git a/jim-syslog.c b/jim-syslog.c
index 29f4d9c..18e63a6 100644
--- a/jim-syslog.c
+++ b/jim-syslog.c
@@ -53,24 +53,6 @@ static const char *priorities[] = {
};
/**
- * Find a matching name in the array of the given length.
- *
- * NULL entries are ignored.
- *
- * Returns the matching index if found, or -1 if not.
- */
-static int find_by_name(const char *name, const char *array[], size_t len)
-{
- int i;
- for (i = 0; i < len; i++) {
- if (array[i] && strcmp(array[i], name) == 0) {
- return i;
- }
- }
- return -1;
-}
-
-/**
* Deletes the syslog command.
*/
static void Jim_SyslogCmdDelete(Jim_Interp *interp, void *privData)
@@ -102,7 +84,7 @@ wrongargs:
}
while (i < argc-1) {
if (Jim_CompareStringImmediate(interp, argv[i], "-facility")) {
- int entry = find_by_name(Jim_GetString(argv[i + 1], NULL), facilities, sizeof(facilities) / sizeof(*facilities));
+ int entry = Jim_FindByName(Jim_GetString(argv[i + 1], NULL), facilities, sizeof(facilities) / sizeof(*facilities));
if (entry < 0) {
Jim_SetResultString(interp, "Unknown facility", -1);
return JIM_ERR;
@@ -147,7 +129,7 @@ wrongargs:
}
if (i<argc-1) {
- priority = find_by_name(Jim_GetString(argv[i], NULL), priorities, sizeof(priorities) / sizeof(*priorities));
+ priority = Jim_FindByName(Jim_GetString(argv[i], NULL), priorities, sizeof(priorities) / sizeof(*priorities));
if (priority < 0) {
Jim_SetResultString(interp, "Unknown priority", -1);
return JIM_ERR;
diff --git a/jim.c b/jim.c
index 8277bd3..09481f3 100644
--- a/jim.c
+++ b/jim.c
@@ -116,6 +116,7 @@ static int JimAddErrorToStack(Jim_Interp *interp, int retcode, const char *filen
static Jim_Obj *Jim_ExpandDictSugar(Jim_Interp *interp, Jim_Obj *objPtr);
static void SetDictSubstFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
static int Jim_IncrCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
+static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, const char *prefix, const char * const *tablePtr, const char *name);
static const Jim_HashTableType JimVariablesHashTableType;
@@ -6333,6 +6334,21 @@ int Jim_GetIndex(Jim_Interp *interp, Jim_Obj *objPtr, int *indexPtr)
* Return Code Object.
* ---------------------------------------------------------------------------*/
+/* NOTE: These must be kept in the same order as JIM_OK, JIM_ERR, ... */
+static const char *jimReturnCodes[] = {
+ [JIM_OK] = "ok",
+ [JIM_ERR] = "error",
+ [JIM_RETURN] = "return",
+ [JIM_BREAK] = "break",
+ [JIM_CONTINUE] = "continue",
+ [JIM_SIGNAL] = "signal",
+ [JIM_EXIT] = "exit",
+ [JIM_EVAL] = "eval",
+ NULL
+};
+
+#define jimReturnCodesSize (sizeof(jimReturnCodes)/sizeof(*jimReturnCodes))
+
static int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr);
static const Jim_ObjType returnCodeObjType = {
@@ -6343,19 +6359,28 @@ static const Jim_ObjType returnCodeObjType = {
JIM_TYPE_NONE,
};
+/* Converts a (standard) return code to a string. Returns "?" for
+ * non-standard return codes.
+ */
+const char *Jim_ReturnCode(int code)
+{
+ if (code < 0 || code >= jimReturnCodesSize) {
+ return "?";
+ }
+ else {
+ return jimReturnCodes[code];
+ }
+}
+
int SetReturnCodeFromAny(Jim_Interp *interp, Jim_Obj *objPtr)
{
int returnCode;
jim_wide wideValue;
- /* NOTE: These must be kept in the same order as JIM_OK, JIM_ERR, ... */
- static const char *options[] = {
- "ok", "error", "return", "break", "continue", "signal", "exit", "eval", NULL
- };
/* Try to convert into an integer */
if (JimGetWideNoErr(interp, objPtr, &wideValue) != JIM_ERR)
returnCode = (int) wideValue;
- else if (Jim_GetEnum(interp, objPtr, options, &returnCode, NULL, JIM_NONE) != JIM_OK) {
+ else if (Jim_GetEnum(interp, objPtr, jimReturnCodes, &returnCode, NULL, JIM_NONE) != JIM_OK) {
Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
Jim_AppendStrings(interp, Jim_GetResult(interp),
"expected return code but got '", Jim_GetString(objPtr, NULL), "'",
@@ -11796,46 +11821,84 @@ static int Jim_CatchCoreCommand(Jim_Interp *interp, int argc,
Jim_Obj *const *argv)
{
int exitCode = 0;
+ int i;
int sig = 0;
- if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-signal")) {
- sig++;
+ /* Which return codes are caught? These are the defaults */
+ jim_wide mask = (1 << JIM_OK | 1 << JIM_ERR | 1 << JIM_BREAK | 1 << JIM_CONTINUE | 1 << JIM_RETURN);
+
+ for (i = 1; i < argc - 2; i++) {
+ const char *arg = Jim_GetString(argv[i], NULL);
+ jim_wide option;
+ int add;
+
+ /* It's a pity we can't use Jim_GetEnum here :-( */
+ if (strncmp(arg, "-no", 3) == 0) {
+ arg += 3;
+ add = 0;
+ }
+ else if (*arg == '-') {
+ arg++;
+ add = 1;
+ }
+ else {
+ goto wrongargs;
+ }
+
+ if (Jim_StringToWide(arg, &option, 10) != JIM_OK) {
+ option = -1;
+ }
+ if (option < 0) {
+ option = Jim_FindByName(arg, jimReturnCodes, jimReturnCodesSize);
+ }
+ if (option < 0) {
+ goto wrongargs;
+ }
+
+ if (add) {
+ mask |= (1 << option);
+ }
+ else {
+ mask &= ~(1 << option);
+ }
}
- if (argc - sig != 2 && argc - sig != 3) {
- Jim_WrongNumArgs(interp, 1, argv, "?-signal? script ?varName?");
+ argc -= i;
+ if (argc != 1 && argc != 2) {
+wrongargs:
+ Jim_WrongNumArgs(interp, 1, argv, "?-?no?code ...? script ?varName?");
return JIM_ERR;
}
- argc -= sig;
- argv += sig;
+ argv += i;
+
+ if (mask & (1 << JIM_SIGNAL)) {
+ sig++;
+ }
interp->signal_level += sig;
- exitCode = Jim_EvalObj(interp, argv[1]);
+ exitCode = Jim_EvalObj(interp, argv[0]);
interp->signal_level -= sig;
- /* If we get TCL_SIGNAL without the -signal parameter,
- * just pass it through to be caught at a higher level
- */
- if (exitCode == JIM_SIGNAL && !sig) {
+ /* Catch or pass through? Only the first 64 codes can be passed through */
+ if (exitCode >= 0 && exitCode < sizeof(mask) && ((1 << exitCode) & mask) == 0) {
+ /* Not caught, pass it up */
return exitCode;
}
- if (sig) {
- if (exitCode == JIM_SIGNAL && interp->signal_level == 0) {
- /* Yes, catch the signal at this level */
- if (interp->signal_to_name) {
- Jim_SetResultString(interp, interp->signal_to_name(interp->signal), -1);
- }
- else {
- Jim_SetResultInt(interp, interp->signal);
- }
- interp->signal = 0;
+ if (sig && exitCode == JIM_SIGNAL && interp->signal_level == 0) {
+ /* Yes, catch the signal at this level */
+ if (interp->signal_to_name) {
+ Jim_SetResultString(interp, interp->signal_to_name(interp->signal), -1);
+ }
+ else {
+ Jim_SetResultInt(interp, interp->signal);
}
+ interp->signal = 0;
}
- if (argc == 3) {
- if (Jim_SetVariable(interp, argv[2], Jim_GetResult(interp))
+ if (argc == 2) {
+ if (Jim_SetVariable(interp, argv[1], Jim_GetResult(interp))
!= JIM_OK)
return JIM_ERR;
}
@@ -12073,11 +12136,12 @@ static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
static const char *commands[] = {
"body", "commands", "procs", "exists", "globals", "level", "locals",
"vars", "version", "patchlevel", "complete", "args", "hostname",
- "script", "source", "stacktrace", "nameofexecutable", NULL
+ "script", "source", "stacktrace", "nameofexecutable", "returncodes", NULL
};
enum {INFO_BODY, INFO_COMMANDS, INFO_PROCS, INFO_EXISTS, INFO_GLOBALS, INFO_LEVEL,
INFO_LOCALS, INFO_VARS, INFO_VERSION, INFO_PATCHLEVEL, INFO_COMPLETE, INFO_ARGS,
- INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE};
+ INFO_HOSTNAME, INFO_SCRIPT, INFO_SOURCE, INFO_STACKTRACE, INFO_NAMEOFEXECUTABLE,
+ INFO_RETURNCODES };
if (argc < 2) {
Jim_WrongNumArgs(interp, 1, argv, "subcommand ?args ...?");
@@ -12211,6 +12275,18 @@ static int Jim_InfoCoreCommand(Jim_Interp *interp, int argc,
/* Redirect to Tcl proc */
return Jim_Eval(interp, "info_nameofexecutable");
}
+ else if (cmd == INFO_RETURNCODES) {
+ Jim_Obj *listObjPtr = Jim_NewListObj(interp, NULL, 0);
+ int i;
+
+ for (i = 0; jimReturnCodes[i]; i++) {
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, i));
+ Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, jimReturnCodes[i], -1));
+ }
+
+ Jim_SetResult(interp, listObjPtr);
+ return JIM_OK;
+ }
return result;
}
@@ -12778,16 +12854,44 @@ void Jim_PrintErrorMessage(Jim_Interp *interp)
}
}
-int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
+static void JimSetFailedEnumResult(Jim_Interp *interp, const char *arg, const char *badtype, const char *prefix, const char * const *tablePtr, const char *name)
+{
+ int count;
+ char **tablePtrSorted;
+ int i;
+
+ for (count = 0; tablePtr[count]; count++) {
+ }
+
+ if (name == NULL)
+ name = "option";
+ Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
+ Jim_AppendStrings(interp, Jim_GetResult(interp),
+ badtype, name, " \"", arg, "\": must be ",
+ NULL);
+ tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
+ memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
+ qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
+ for (i = 0; i < count; i++) {
+ if (i+1 == count && count > 1)
+ Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
+ Jim_AppendStrings(interp, Jim_GetResult(interp),
+ prefix, tablePtrSorted[i], NULL);
+ if (i+1 != count)
+ Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
+ }
+ Jim_Free(tablePtrSorted);
+}
+
+int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
const char * const *tablePtr, int *indexPtr, const char *name, int flags)
{
const char *bad = "bad ";
const char * const *entryPtr = NULL;
- char **tablePtrSorted;
int i;
+ int match = -1;
int arglen;
const char *arg = Jim_GetString(objPtr, &arglen);
- int match = -1;
*indexPtr = -1;
@@ -12822,32 +12926,22 @@ int Jim_GetEnum(Jim_Interp *interp, Jim_Obj *objPtr,
ambiguous:
if (flags & JIM_ERRMSG) {
- int count;
- for (count = 0; tablePtr[count]; count++) {
- }
-
- if (name == NULL)
- name = "option";
- Jim_SetResult(interp, Jim_NewEmptyStringObj(interp));
- Jim_AppendStrings(interp, Jim_GetResult(interp),
- bad, name, " \"", Jim_GetString(objPtr, NULL), "\": must be ",
- NULL);
- tablePtrSorted = Jim_Alloc(sizeof(char*)*count);
- memcpy(tablePtrSorted, tablePtr, sizeof(char*)*count);
- qsort(tablePtrSorted, count, sizeof(char*), qsortCompareStringPointers);
- for (i = 0; i < count; i++) {
- if (i+1 == count && count > 1)
- Jim_AppendString(interp, Jim_GetResult(interp), "or ", -1);
- Jim_AppendString(interp, Jim_GetResult(interp),
- tablePtrSorted[i], -1);
- if (i+1 != count)
- Jim_AppendString(interp, Jim_GetResult(interp), ", ", -1);
- }
- Jim_Free(tablePtrSorted);
+ JimSetFailedEnumResult(interp, arg, bad, "", tablePtr, name);
}
return JIM_ERR;
}
+int Jim_FindByName(const char *name, const char *array[], size_t len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ if (array[i] && strcmp(array[i], name) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
int Jim_IsDict(Jim_Obj *objPtr)
{
return objPtr->typePtr == &dictObjType;
diff --git a/jim.h b/jim.h
index 6ff5e33..fbab8c4 100644
--- a/jim.h
+++ b/jim.h
@@ -697,6 +697,7 @@ JIM_EXPORT int Jim_GetFinalizer (Jim_Interp *interp, Jim_Obj *objPtr, Jim_Obj **
JIM_EXPORT Jim_Interp * Jim_CreateInterp (void);
JIM_EXPORT void Jim_FreeInterp (Jim_Interp *i);
JIM_EXPORT int Jim_GetExitCode (Jim_Interp *interp);
+JIM_EXPORT const char *Jim_ReturnCode(int code);
/* commands */
JIM_EXPORT void Jim_RegisterCoreCommands (Jim_Interp *interp);
@@ -822,6 +823,14 @@ JIM_EXPORT int Jim_GetEnum (Jim_Interp *interp, Jim_Obj *objPtr,
const char * const *tablePtr, int *indexPtr, const char *name, int flags);
JIM_EXPORT int Jim_ScriptIsComplete (const char *s, int len,
char *stateCharPtr);
+/**
+ * Find a matching name in the array of the given length.
+ *
+ * NULL entries are ignored.
+ *
+ * Returns the matching index if found, or -1 if not.
+ */
+JIM_EXPORT int Jim_FindByName(const char *name, const char *array[], size_t len);
/* package utilities */
typedef void (Jim_InterpDeleteProc)(Jim_Interp *interp, void *data);
diff --git a/tclcompat.tcl b/tclcompat.tcl
index e899278..84876c4 100644
--- a/tclcompat.tcl
+++ b/tclcompat.tcl
@@ -95,13 +95,15 @@ proc errorInfo {error {stacktrace ""}} {
}
proc info_nameofexecutable {} {
- if {[string first "/" $::jim_argv0] >= 0} {
- return $::jim_argv0
- }
- foreach path [split [env PATH ""] :] {
- set exec [file join $path $::jim_argv0]
- if {[file executable $exec]} {
- return $exec
+ if {[info exists ::jim_argv0]} {
+ if {[string first "/" $::jim_argv0] >= 0} {
+ return $::jim_argv0
+ }
+ foreach path [split [env PATH ""] :] {
+ set exec [file join $path $::jim_argv0]
+ if {[file executable $exec]} {
+ return $exec
+ }
}
}
return ""
diff --git a/tests/error.test b/tests/error.test
index 66df7c2..a059ba3 100644
--- a/tests/error.test
+++ b/tests/error.test
@@ -49,5 +49,5 @@ test error-1.2 "Modify stacktrace" {
# Package should be able to invoke exit, which should exit if not caught
test error-2.1 "Exit from package" {
- list [catch {package require exitpackage} msg] $msg
+ list [catch -exit {package require exitpackage} msg] $msg
} {6 {Can't load package 'exitpackage'}}
diff --git a/tests/misc.test b/tests/misc.test
index eda2879..cb46591 100644
--- a/tests/misc.test
+++ b/tests/misc.test
@@ -237,4 +237,52 @@ test incr-1.4 "incr array element - shimmering" {
incr a(2)
} 2
+test catch-1.1 "catch ok" {
+ list [catch {set abc 2} result] $result
+} {0 2}
+
+test catch-1.2 "catch error" {
+ list [catch {error 3} result] $result
+} {1 3}
+
+test catch-1.3 "catch break" {
+ list [catch {break} result] $result
+} {3 {}}
+
+test catch-1.4 "catch -nobreak" {
+ set result {}
+ foreach x {a b c} {
+ lappend result $x
+ # This acts just like break since it won't be caught by catch
+ catch -nobreak {break} tmp
+ }
+ set result
+} {a}
+
+test catch-1.5 "catch -no3" {
+ set result {}
+ foreach x {a b c} {
+ lappend result $x
+ # Same as above, but specify as an integer
+ catch -no3 {break} tmp
+ }
+ set result
+} {a}
+
+test catch-1.6 "catch break" {
+ set result {}
+ foreach x {a b c} {
+ lappend result $x
+ # This does nothing since the break is caught
+ catch {break} tmp
+ }
+ set result
+} {a b c}
+
+
+test catch-1.7 "catch exit" {
+ # Normally exit would not be caught
+ dict get [info returncodes] [catch -exit {exit 5} result]
+} {exit}
+
testreport