diff --git a/build/Makefile.in b/build/Makefile.in index 26ecfb5..10db975 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -22,98 +22,14 @@ PARTCL_LANG_DIR = $(PARROT_LIB_DIR)/languages/partcl PARTCL_EXE = partcl$(EXE) GEN_SOURCES = \ - src/ARE/Actions.pir \ - src/ARE/Compiler.pir \ - src/ARE/Grammar.pir \ - src/StringGlob/Actions.pir \ - src/StringGlob/Compiler.pir \ - src/StringGlob/Grammar.pir \ - src/FileGlob/Actions.pir \ - src/FileGlob/Compiler.pir \ - src/FileGlob/Grammar.pir \ src/Partcl/Actions.pir \ src/Partcl/Compiler.pir \ src/Partcl/Grammar.pir \ src/Partcl/Operators.pir \ - src/Partcl/commands/after.pir \ - src/Partcl/commands/append.pir \ - src/Partcl/commands/apply.pir \ - src/Partcl/commands/array.pir \ - src/Partcl/commands/binary.pir \ - src/Partcl/commands/break.pir \ - src/Partcl/commands/catch.pir \ - src/Partcl/commands/cd.pir \ - src/Partcl/commands/concat.pir \ - src/Partcl/commands/continue.pir \ - src/Partcl/commands/dict.pir \ - src/Partcl/commands/eof.pir \ - src/Partcl/commands/encoding.pir \ - src/Partcl/commands/error.pir \ - src/Partcl/commands/eval.pir \ - src/Partcl/commands/exit.pir \ - src/Partcl/commands/expr.pir \ - src/Partcl/commands/fileevent.pir \ - src/Partcl/commands/file.pir \ - src/Partcl/commands/flush.pir \ - src/Partcl/commands/foreach.pir \ - src/Partcl/commands/format.pir \ - src/Partcl/commands/for.pir \ - src/Partcl/commands/gets.pir \ - src/Partcl/commands/global.pir \ - src/Partcl/commands/glob.pir \ - src/Partcl/commands/if.pir \ - src/Partcl/commands/incr.pir \ - src/Partcl/commands/info.pir \ - src/Partcl/commands/interp.pir \ - src/Partcl/commands/join.pir \ - src/Partcl/commands/lappend.pir \ - src/Partcl/commands/lassign.pir \ - src/Partcl/commands/lindex.pir \ - src/Partcl/commands/linsert.pir \ - src/Partcl/commands/list.pir \ - src/Partcl/commands/llength.pir \ - src/Partcl/commands/lrange.pir \ - src/Partcl/commands/lrepeat.pir \ - src/Partcl/commands/lreplace.pir \ - src/Partcl/commands/lreverse.pir \ - src/Partcl/commands/lset.pir \ - src/Partcl/commands/lsort.pir \ - src/Partcl/commands/namespace.pir \ - src/Partcl/commands/package.pir \ - src/Partcl/commands/proc.pir \ - src/Partcl/commands/puts.pir \ - src/Partcl/commands/pwd.pir \ - src/Partcl/commands/regexp.pir \ - src/Partcl/commands/rename.pir \ - src/Partcl/commands/return.pir \ - src/Partcl/commands/set.pir \ - src/Partcl/commands/socket.pir \ - src/Partcl/commands/source.pir \ - src/Partcl/commands/split.pir \ - src/Partcl/commands/string.pir \ - src/Partcl/commands/subst.pir \ - src/Partcl/commands/switch.pir \ - src/Partcl/commands/time.pir \ - src/Partcl/commands/trace.pir \ - src/Partcl/commands/unset.pir \ - src/Partcl/commands/uplevel.pir \ - src/Partcl/commands/upvar.pir \ - src/Partcl/commands/variable.pir \ - src/Partcl/commands/vwait.pir \ - src/Partcl/commands/while.pir \ - src/TclArray.pir \ - src/TclLexPad.pir \ - src/TclList.pir \ - src/TclString.pir \ src/init.pir \ - src/options.pir src/Partcl.pir: $(GEN_SOURCES) -src/FileGlob/Actions.pm: src/StringGlob/Actions.pm -src/FileGlob/Grammar.pm: src/StringGlob/Grammar.pm -src/init.pm: src/TclLexPad.pm - all: $(PARTCL_EXE) .revision $(PARTCL_EXE) : partcl.pbc diff --git a/docs/overview.pod b/docs/overview.pod deleted file mode 100644 index 960bf69..0000000 --- a/docs/overview.pod +++ /dev/null @@ -1,105 +0,0 @@ -=head1 Overview - -partcl-nqp is a tcl 8.5 compiler for the PVM (Parrot Virtual Machine). - -It uses the Parrot Compiler Toolkit (nqp-rx, HLL::Compiler) to generate -PIR (Parrot Assembly), which is then compiled to PBC (Parrot Bytecode), -which is then bundled into a "fakecutable". The fakecutable contains the -bytecode, and enough C to invoke the VM. It is NOT a JITted version. - -=head1 Languages - -partcl-nqp is written primarily in NQP (Not Quite Perl 6; specifically -nqp-rx), with as little PIR as we can manager. - -=head2 Grammars and Actions - -There are several Perl 6 Grammars in F; Partcl contains the main -language, src/ARE is the standard tcl regexes, etc. - -Each F file contains the rules (See Perl 6's Synopsis 5) for -parsing that mini language. The F file contains instructions to -convert the parse into parrot's AST (Abstract Syntax Tree). - -The small F for each mini language takes advantage of the parrot -library to convert the AST to an OST (opcode syntax tree), which is then -converted to PIR, and finally PBC. - -=head1 Repository layout - -=over 4 - -=item src/Partcl/ - -The grammar/actions for the language itself and for [expr]. - -=item src/Partcl/Operators.pm - -Operators to override NQP's default behavior to be partcl-specific. Used -by [expr]. - -=item src/Partcl/Tcl*.pm - -Override parrot's core types to conform to tcl behavior instead of parrot -defaults. For example, the string representation of [list a b] in parrot -is "2" (the size of the list); but in tcl is "a b". - -=item src/Partcl/commands/ - -The Tcl builtins, e.g. for, puts, etc. Implemented in NQP. Any ensemble -commands have their own file, logical groupings (e.g. all list-related -commands) are bundled, with the remaining commands in main.pm - -So we can generate our own diagnostic error messages, any user-facing -commands have an NQP function signature of C<*@> - however, especially for -the ensemble commands, several smaller commands with NQP-specific paramters -are used. - -=item src/ARE - -The grammar/actions for the regular expression engine. - -=item src/StringGlob - -The grammar/actions for [string match] - -=item src/FileGlob - -The grammar/actions for [glob] - -=item library/ - -Portions of tcl's standard library - these files come directly from tcl's -CVS repository. - -=item src/init.pm - -Internal initialization - set global variables, etc. - -=item src/options.pm - -helper methods for processing options/autocompleting subcommands. - -=item lib/test_more.tcl - -A very small test harness written in tcl that generates TAP, -used to run the tests in t/; this is completely separate from tcltest.tcl, -which is tcl's own non-TAP test harness. - -=item t/ - -Test files, written in tcl, run using lib/test_more.tcl ; While tcl does -have a suite of specification tests, it is very difficult for a new -implementation to run them directly; Our goal is to be able to run the -tests in this directory as a prelude to running the spec tests. - -=back - -=head1 Alternate history - -There is a version of partcl written using C & C. That version has -no active development but is maintained minimally so it compiles with the -latest version of parrot. Much of I version includes hand-translated -NQP based on the PIR from that version. - -=cut diff --git a/docs/todo.pod b/docs/todo.pod deleted file mode 100644 index 41853e8..0000000 --- a/docs/todo.pod +++ /dev/null @@ -1,21 +0,0 @@ -=head1 Big Goals - -Document how to contribute to this version - ast, tcl, nqp ... - -=head1 Tasks/Bugs: - -=over 4 - -=item [proc] {t/cmd_continue.t; t/cmd_break.t} - -User-defined procs should catch continue/break and complain about them, as -should the :main PIR sub. - -=item list processing - -Single opening quotes are an invalid list element. {t/cmd_lappend.t} - -=item [proc] varargs support {t/cmd_proc.t; t/cmd_info.t} -need to handle special {args} parameter. - -=back diff --git a/library/README b/library/README deleted file mode 100644 index 3ab8dfd..0000000 --- a/library/README +++ /dev/null @@ -1,14 +0,0 @@ -The files in this directory (except for this one) come from the tcl -repository at: http://tcl.cvs.sourceforge.net/tcl/tcl/library/ - -# to get a local copy to compare our checked in versions with, use - -make tcl-cvs - -in the top level directory. - -They are included here under the terms in license.terms - -We will probably switch to having the developer check them out, -as soon as we can run tcltest with no modifications. - diff --git a/library/auto.tcl b/library/auto.tcl deleted file mode 100644 index 881e6b9..0000000 --- a/library/auto.tcl +++ /dev/null @@ -1,610 +0,0 @@ -# auto.tcl -- -# -# utility procs formerly in init.tcl dealing with auto execution -# of commands and can be auto loaded themselves. -# -# RCS: @(#) $Id: auto.tcl,v 1.28 2006/11/03 00:34:52 hobbs Exp $ -# -# Copyright (c) 1991-1993 The Regents of the University of California. -# Copyright (c) 1994-1998 Sun Microsystems, Inc. -# -# See the file "license.terms" for information on usage and redistribution -# of this file, and for a DISCLAIMER OF ALL WARRANTIES. -# - -# auto_reset -- -# -# Destroy all cached information for auto-loading and auto-execution, -# so that the information gets recomputed the next time it's needed. -# Also delete any commands that are listed in the auto-load index. -# -# Arguments: -# None. - -proc auto_reset {} { - if {[array exists ::auto_index]} { - foreach cmdName [array names ::auto_index] { - set fqcn [namespace which $cmdName] - if {$fqcn eq ""} {continue} - rename $fqcn {} - } - } - unset -nocomplain ::auto_execs ::auto_index ::tcl::auto_oldpath - if {[catch {llength $::auto_path}]} { - set ::auto_path [list [info library]] - } else { - if {[info library] ni $::auto_path} { - lappend ::auto_path [info library] - } - } -} - -# tcl_findLibrary -- -# -# This is a utility for extensions that searches for a library directory -# using a canonical searching algorithm. A side effect is to source -# the initialization script and set a global library variable. -# -# Arguments: -# basename Prefix of the directory name, (e.g., "tk") -# version Version number of the package, (e.g., "8.0") -# patch Patchlevel of the package, (e.g., "8.0.3") -# initScript Initialization script to source (e.g., tk.tcl) -# enVarName environment variable to honor (e.g., TK_LIBRARY) -# varName Global variable to set when done (e.g., tk_library) - -proc tcl_findLibrary {basename version patch initScript enVarName varName} { - upvar #0 $varName the_library - global env - - set dirs {} - set errors {} - - # The C application may have hardwired a path, which we honor - - if {[info exists the_library] && $the_library ne ""} { - lappend dirs $the_library - } else { - - # Do the canonical search - - # 1. From an environment variable, if it exists. - # Placing this first gives the end-user ultimate control - # to work-around any bugs, or to customize. - - if {[info exists env($enVarName)]} { - lappend dirs $env($enVarName) - } - - # 2. In the package script directory registered within - # the configuration of the package itself. - - if {[catch { - ::${basename}::pkgconfig get scriptdir,runtime - } value] == 0} { - lappend dirs $value - } - - # 3. Relative to auto_path directories. This checks relative to the - # Tcl library as well as allowing loading of libraries added to the - # auto_path that is not relative to the core library or binary paths. - foreach d $::auto_path { - lappend dirs [file join $d $basename$version] - if {$::tcl_platform(platform) eq "unix" - && $::tcl_platform(os) eq "Darwin"} { - # 4. On MacOSX, check the Resources/Scripts subdir too - lappend dirs [file join $d $basename$version Resources Scripts] - } - } - - # 3. Various locations relative to the executable - # ../lib/foo1.0 (From bin directory in install hierarchy) - # ../../lib/foo1.0 (From bin/arch directory in install hierarchy) - # ../library (From unix directory in build hierarchy) - # - # Remaining locations are out of date (when relevant, they ought - # to be covered by the $::auto_path seach above) and disabled. - # - # ../../library (From unix/arch directory in build hierarchy) - # ../../foo1.0.1/library - # (From unix directory in parallel build hierarchy) - # ../../../foo1.0.1/library - # (From unix/arch directory in parallel build hierarchy) - - set parentDir [file dirname [file dirname [info nameofexecutable]]] - set grandParentDir [file dirname $parentDir] - lappend dirs [file join $parentDir lib $basename$version] - lappend dirs [file join $grandParentDir lib $basename$version] - lappend dirs [file join $parentDir library] - if {0} { - lappend dirs [file join $grandParentDir library] - lappend dirs [file join $grandParentDir $basename$patch library] - lappend dirs [file join [file dirname $grandParentDir] \ - $basename$patch library] - } - } - # uniquify $dirs in order - array set seen {} - foreach i $dirs { - # Take note that the [file normalize] below has been noted to - # cause difficulties for the freewrap utility. See Bug 1072136. - # Until freewrap resolves the matter, one might work around the - # problem by disabling that branch. - if {[interp issafe]} { - set norm $i - } else { - set norm [file normalize $i] - } - if {[info exists seen($norm)]} { continue } - set seen($norm) "" - lappend uniqdirs $i - } - set dirs $uniqdirs - foreach i $dirs { - set the_library $i - set file [file join $i $initScript] - - # source everything when in a safe interpreter because - # we have a source command, but no file exists command - - if {[interp issafe] || [file exists $file]} { - if {![catch {uplevel #0 [list source $file]} msg opts]} { - return - } else { - append errors "$file: $msg\n" - append errors [dict get $opts -errorinfo]\n - } - } - } - unset -nocomplain the_library - set msg "Can't find a usable $initScript in the following directories: \n" - append msg " $dirs\n\n" - append msg "$errors\n\n" - append msg "This probably means that $basename wasn't installed properly.\n" - error $msg -} - - -# ---------------------------------------------------------------------- -# auto_mkindex -# ---------------------------------------------------------------------- -# The following procedures are used to generate the tclIndex file -# from Tcl source files. They use a special safe interpreter to -# parse Tcl source files, writing out index entries as "proc" -# commands are encountered. This implementation won't work in a -# safe interpreter, since a safe interpreter can't create the -# special parser and mess with its commands. - -if {[interp issafe]} { - return ;# Stop sourcing the file here -} - -# auto_mkindex -- -# Regenerate a tclIndex file from Tcl source files. Takes as argument -# the name of the directory in which the tclIndex file is to be placed, -# followed by any number of glob patterns to use in that directory to -# locate all of the relevant files. -# -# Arguments: -# dir - Name of the directory in which to create an index. -# args - Any number of additional arguments giving the -# names of files within dir. If no additional -# are given auto_mkindex will look for *.tcl. - -proc auto_mkindex {dir args} { - if {[interp issafe]} { - error "can't generate index within safe interpreter" - } - - set oldDir [pwd] - cd $dir - set dir [pwd] - - append index "# Tcl autoload index file, version 2.0\n" - append index "# This file is generated by the \"auto_mkindex\" command\n" - append index "# and sourced to set up indexing information for one or\n" - append index "# more commands. Typically each line is a command that\n" - append index "# sets an element in the auto_index array, where the\n" - append index "# element name is the name of a command and the value is\n" - append index "# a script that loads the command.\n\n" - if {[llength $args] == 0} { - set args *.tcl - } - - auto_mkindex_parser::init - foreach file [glob -- {*}$args] { - if {[catch {auto_mkindex_parser::mkindex $file} msg opts] == 0} { - append index $msg - } else { - cd $oldDir - return -options $opts $msg - } - } - auto_mkindex_parser::cleanup - - set fid [open "tclIndex" w] - puts -nonewline $fid $index - close $fid - cd $oldDir -} - -# Original version of auto_mkindex that just searches the source -# code for "proc" at the beginning of the line. - -proc auto_mkindex_old {dir args} { - set oldDir [pwd] - cd $dir - set dir [pwd] - append index "# Tcl autoload index file, version 2.0\n" - append index "# This file is generated by the \"auto_mkindex\" command\n" - append index "# and sourced to set up indexing information for one or\n" - append index "# more commands. Typically each line is a command that\n" - append index "# sets an element in the auto_index array, where the\n" - append index "# element name is the name of a command and the value is\n" - append index "# a script that loads the command.\n\n" - if {[llength $args] == 0} { - set args *.tcl - } - foreach file [glob -- {*}$args] { - set f "" - set error [catch { - set f [open $file] - while {[gets $f line] >= 0} { - if {[regexp {^proc[ ]+([^ ]*)} $line match procName]} { - set procName [lindex [auto_qualify $procName "::"] 0] - append index "set [list auto_index($procName)]" - append index " \[list source \[file join \$dir [list $file]\]\]\n" - } - } - close $f - } msg opts] - if {$error} { - catch {close $f} - cd $oldDir - return -options $opts $msg - } - } - set f "" - set error [catch { - set f [open tclIndex w] - puts -nonewline $f $index - close $f - cd $oldDir - } msg opts] - if {$error} { - catch {close $f} - cd $oldDir - error $msg $info $code - return -options $opts $msg - } -} - -# Create a safe interpreter that can be used to parse Tcl source files -# generate a tclIndex file for autoloading. This interp contains -# commands for things that need index entries. Each time a command -# is executed, it writes an entry out to the index file. - -namespace eval auto_mkindex_parser { - variable parser "" ;# parser used to build index - variable index "" ;# maintains index as it is built - variable scriptFile "" ;# name of file being processed - variable contextStack "" ;# stack of namespace scopes - variable imports "" ;# keeps track of all imported cmds - variable initCommands ;# list of commands that create aliases - if {![info exists initCommands]} { - set initCommands [list] - } - - proc init {} { - variable parser - variable initCommands - - if {![interp issafe]} { - set parser [interp create -safe] - $parser hide info - $parser hide rename - $parser hide proc - $parser hide namespace - $parser hide eval - $parser hide puts - $parser invokehidden namespace delete :: - $parser invokehidden proc unknown {args} {} - - # We'll need access to the "namespace" command within the - # interp. Put it back, but move it out of the way. - - $parser expose namespace - $parser invokehidden rename namespace _%@namespace - $parser expose eval - $parser invokehidden rename eval _%@eval - - # Install all the registered psuedo-command implementations - - foreach cmd $initCommands { - eval $cmd - } - } - } - proc cleanup {} { - variable parser - interp delete $parser - unset parser - } -} - -# auto_mkindex_parser::mkindex -- -# -# Used by the "auto_mkindex" command to create a "tclIndex" file for -# the given Tcl source file. Executes the commands in the file, and -# handles things like the "proc" command by adding an entry for the -# index file. Returns a string that represents the index file. -# -# Arguments: -# file Name of Tcl source file to be indexed. - -proc auto_mkindex_parser::mkindex {file} { - variable parser - variable index - variable scriptFile - variable contextStack - variable imports - - set scriptFile $file - - set fid [open $file] - set contents [read $fid] - close $fid - - # There is one problem with sourcing files into the safe - # interpreter: references like "$x" will fail since code is not - # really being executed and variables do not really exist. - # To avoid this, we replace all $ with \0 (literally, the null char) - # later, when getting proc names we will have to reverse this replacement, - # in case there were any $ in the proc name. This will cause a problem - # if somebody actually tries to have a \0 in their proc name. Too bad - # for them. - set contents [string map [list \$ \0] $contents] - - set index "" - set contextStack "" - set imports "" - - $parser eval $contents - - foreach name $imports { - catch {$parser eval [list _%@namespace forget $name]} - } - return $index -} - -# auto_mkindex_parser::hook command -# -# Registers a Tcl command to evaluate when initializing the -# slave interpreter used by the mkindex parser. -# The command is evaluated in the master interpreter, and can -# use the variable auto_mkindex_parser::parser to get to the slave - -proc auto_mkindex_parser::hook {cmd} { - variable initCommands - - lappend initCommands $cmd -} - -# auto_mkindex_parser::slavehook command -# -# Registers a Tcl command to evaluate when initializing the -# slave interpreter used by the mkindex parser. -# The command is evaluated in the slave interpreter. - -proc auto_mkindex_parser::slavehook {cmd} { - variable initCommands - - # The $parser variable is defined to be the name of the - # slave interpreter when this command is used later. - - lappend initCommands "\$parser eval [list $cmd]" -} - -# auto_mkindex_parser::command -- -# -# Registers a new command with the "auto_mkindex_parser" interpreter -# that parses Tcl files. These commands are fake versions of things -# like the "proc" command. When you execute them, they simply write -# out an entry to a "tclIndex" file for auto-loading. -# -# This procedure allows extensions to register their own commands -# with the auto_mkindex facility. For example, a package like -# [incr Tcl] might register a "class" command so that class definitions -# could be added to a "tclIndex" file for auto-loading. -# -# Arguments: -# name Name of command recognized in Tcl files. -# arglist Argument list for command. -# body Implementation of command to handle indexing. - -proc auto_mkindex_parser::command {name arglist body} { - hook [list auto_mkindex_parser::commandInit $name $arglist $body] -} - -# auto_mkindex_parser::commandInit -- -# -# This does the actual work set up by auto_mkindex_parser::command -# This is called when the interpreter used by the parser is created. -# -# Arguments: -# name Name of command recognized in Tcl files. -# arglist Argument list for command. -# body Implementation of command to handle indexing. - -proc auto_mkindex_parser::commandInit {name arglist body} { - variable parser - - set ns [namespace qualifiers $name] - set tail [namespace tail $name] - if {$ns eq ""} { - set fakeName [namespace current]::_%@fake_$tail - } else { - set fakeName [namespace current]::[string map {:: _} _%@fake_$name] - } - proc $fakeName $arglist $body - - # YUK! Tcl won't let us alias fully qualified command names, - # so we can't handle names like "::itcl::class". Instead, - # we have to build procs with the fully qualified names, and - # have the procs point to the aliases. - - if {[string match *::* $name]} { - set exportCmd [list _%@namespace export [namespace tail $name]] - $parser eval [list _%@namespace eval $ns $exportCmd] - - # The following proc definition does not work if you - # want to tolerate space or something else diabolical - # in the procedure name, (i.e., space in $alias) - # The following does not work: - # "_%@eval {$alias} \$args" - # because $alias gets concat'ed to $args. - # The following does not work because $cmd is somehow undefined - # "set cmd {$alias} \; _%@eval {\$cmd} \$args" - # A gold star to someone that can make test - # autoMkindex-3.3 work properly - - set alias [namespace tail $fakeName] - $parser invokehidden proc $name {args} "_%@eval {$alias} \$args" - $parser alias $alias $fakeName - } else { - $parser alias $name $fakeName - } - return -} - -# auto_mkindex_parser::fullname -- -# Used by commands like "proc" within the auto_mkindex parser. -# Returns the qualified namespace name for the "name" argument. -# If the "name" does not start with "::", elements are added from -# the current namespace stack to produce a qualified name. Then, -# the name is examined to see whether or not it should really be -# qualified. If the name has more than the leading "::", it is -# returned as a fully qualified name. Otherwise, it is returned -# as a simple name. That way, the Tcl autoloader will recognize -# it properly. -# -# Arguments: -# name - Name that is being added to index. - -proc auto_mkindex_parser::fullname {name} { - variable contextStack - - if {![string match ::* $name]} { - foreach ns $contextStack { - set name "${ns}::$name" - if {[string match ::* $name]} { - break - } - } - } - - if {[namespace qualifiers $name] eq ""} { - set name [namespace tail $name] - } elseif {![string match ::* $name]} { - set name "::$name" - } - - # Earlier, mkindex replaced all $'s with \0. Now, we have to reverse - # that replacement. - return [string map [list \0 \$] $name] -} - -if {[llength $::auto_mkindex_parser::initCommands]} { - return -} - -# Register all of the procedures for the auto_mkindex parser that -# will build the "tclIndex" file. - -# AUTO MKINDEX: proc name arglist body -# Adds an entry to the auto index list for the given procedure name. - -auto_mkindex_parser::command proc {name args} { - variable index - variable scriptFile - # Do some fancy reformatting on the "source" call to handle platform - # differences with respect to pathnames. Use format just so that the - # command is a little easier to read (otherwise it'd be full of - # backslashed dollar signs, etc. - append index [list set auto_index([fullname $name])] \ - [format { [list source [file join $dir %s]]} \ - [file split $scriptFile]] "\n" -} - -# Conditionally add support for Tcl byte code files. There are some -# tricky details here. First, we need to get the tbcload library -# initialized in the current interpreter. We cannot load tbcload into the -# slave until we have done so because it needs access to the tcl_patchLevel -# variable. Second, because the package index file may defer loading the -# library until we invoke a command, we need to explicitly invoke auto_load -# to force it to be loaded. This should be a noop if the package has -# already been loaded - -auto_mkindex_parser::hook { - if {![catch {package require tbcload}]} { - if {[namespace which -command tbcload::bcproc] eq ""} { - auto_load tbcload::bcproc - } - load {} tbcload $auto_mkindex_parser::parser - - # AUTO MKINDEX: tbcload::bcproc name arglist body - # Adds an entry to the auto index list for the given pre-compiled - # procedure name. - - auto_mkindex_parser::commandInit tbcload::bcproc {name args} { - variable index - variable scriptFile - # Do some nice reformatting of the "source" call, to get around - # path differences on different platforms. We use the format - # command just so that the code is a little easier to read. - append index [list set auto_index([fullname $name])] \ - [format { [list source [file join $dir %s]]} \ - [file split $scriptFile]] "\n" - } - } -} - -# AUTO MKINDEX: namespace eval name command ?arg arg...? -# Adds the namespace name onto the context stack and evaluates the -# associated body of commands. -# -# AUTO MKINDEX: namespace import ?-force? pattern ?pattern...? -# Performs the "import" action in the parser interpreter. This is -# important for any commands contained in a namespace that affect -# the index. For example, a script may say "itcl::class ...", -# or it may import "itcl::*" and then say "class ...". This -# procedure does the import operation, but keeps track of imported -# patterns so we can remove the imports later. - -auto_mkindex_parser::command namespace {op args} { - switch -- $op { - eval { - variable parser - variable contextStack - - set name [lindex $args 0] - set args [lrange $args 1 end] - - set contextStack [linsert $contextStack 0 $name] - $parser eval [list _%@namespace eval $name] $args - set contextStack [lrange $contextStack 1 end] - } - import { - variable parser - variable imports - foreach pattern $args { - if {$pattern ne "-force"} { - lappend imports $pattern - } - } - catch {$parser eval "_%@namespace import $args"} - } - } -} - -return diff --git a/library/history.tcl b/library/history.tcl deleted file mode 100644 index 3a3f16a..0000000 --- a/library/history.tcl +++ /dev/null @@ -1,375 +0,0 @@ -# history.tcl -- -# -# Implementation of the history command. -# -# RCS: @(#) $Id: history.tcl,v 1.7 2005/07/23 04:12:49 dgp Exp $ -# -# Copyright (c) 1997 Sun Microsystems, Inc. -# -# See the file "license.terms" for information on usage and redistribution -# of this file, and for a DISCLAIMER OF ALL WARRANTIES. -# - -# The tcl::history array holds the history list and -# some additional bookkeeping variables. -# -# nextid the index used for the next history list item. -# keep the max size of the history list -# oldest the index of the oldest item in the history. - -namespace eval tcl { - variable history - if {![info exists history]} { - array set history { - nextid 0 - keep 20 - oldest -20 - } - } -} - -# history -- -# -# This is the main history command. See the man page for its interface. -# This does argument checking and calls helper procedures in the -# history namespace. - -proc history {args} { - set len [llength $args] - if {$len == 0} { - return [tcl::HistInfo] - } - set key [lindex $args 0] - set options "add, change, clear, event, info, keep, nextid, or redo" - switch -glob -- $key { - a* { # history add - - if {$len > 3} { - return -code error "wrong # args: should be \"history add event ?exec?\"" - } - if {![string match $key* add]} { - return -code error "bad option \"$key\": must be $options" - } - if {$len == 3} { - set arg [lindex $args 2] - if {! ([string match e* $arg] && [string match $arg* exec])} { - return -code error "bad argument \"$arg\": should be \"exec\"" - } - } - return [tcl::HistAdd [lindex $args 1] [lindex $args 2]] - } - ch* { # history change - - if {($len > 3) || ($len < 2)} { - return -code error "wrong # args: should be \"history change newValue ?event?\"" - } - if {![string match $key* change]} { - return -code error "bad option \"$key\": must be $options" - } - if {$len == 2} { - set event 0 - } else { - set event [lindex $args 2] - } - - return [tcl::HistChange [lindex $args 1] $event] - } - cl* { # history clear - - if {($len > 1)} { - return -code error "wrong # args: should be \"history clear\"" - } - if {![string match $key* clear]} { - return -code error "bad option \"$key\": must be $options" - } - return [tcl::HistClear] - } - e* { # history event - - if {$len > 2} { - return -code error "wrong # args: should be \"history event ?event?\"" - } - if {![string match $key* event]} { - return -code error "bad option \"$key\": must be $options" - } - if {$len == 1} { - set event -1 - } else { - set event [lindex $args 1] - } - return [tcl::HistEvent $event] - } - i* { # history info - - if {$len > 2} { - return -code error "wrong # args: should be \"history info ?count?\"" - } - if {![string match $key* info]} { - return -code error "bad option \"$key\": must be $options" - } - return [tcl::HistInfo [lindex $args 1]] - } - k* { # history keep - - if {$len > 2} { - return -code error "wrong # args: should be \"history keep ?count?\"" - } - if {$len == 1} { - return [tcl::HistKeep] - } else { - set limit [lindex $args 1] - if {[catch {expr {~$limit}}] || ($limit < 0)} { - return -code error "illegal keep count \"$limit\"" - } - return [tcl::HistKeep $limit] - } - } - n* { # history nextid - - if {$len > 1} { - return -code error "wrong # args: should be \"history nextid\"" - } - if {![string match $key* nextid]} { - return -code error "bad option \"$key\": must be $options" - } - return [expr {$tcl::history(nextid) + 1}] - } - r* { # history redo - - if {$len > 2} { - return -code error "wrong # args: should be \"history redo ?event?\"" - } - if {![string match $key* redo]} { - return -code error "bad option \"$key\": must be $options" - } - return [tcl::HistRedo [lindex $args 1]] - } - default { - return -code error "bad option \"$key\": must be $options" - } - } -} - -# tcl::HistAdd -- -# -# Add an item to the history, and optionally eval it at the global scope -# -# Parameters: -# command the command to add -# exec (optional) a substring of "exec" causes the -# command to be evaled. -# Results: -# If executing, then the results of the command are returned -# -# Side Effects: -# Adds to the history list - - proc tcl::HistAdd {command {exec {}}} { - variable history - - # Do not add empty commands to the history - if {[string trim $command] eq ""} { - return "" - } - - set i [incr history(nextid)] - set history($i) $command - set j [incr history(oldest)] - unset -nocomplain history($j) - if {[string match e* $exec]} { - return [uplevel #0 $command] - } else { - return {} - } -} - -# tcl::HistKeep -- -# -# Set or query the limit on the length of the history list -# -# Parameters: -# limit (optional) the length of the history list -# -# Results: -# If no limit is specified, the current limit is returned -# -# Side Effects: -# Updates history(keep) if a limit is specified - - proc tcl::HistKeep {{limit {}}} { - variable history - if {$limit eq ""} { - return $history(keep) - } else { - set oldold $history(oldest) - set history(oldest) [expr {$history(nextid) - $limit}] - for {} {$oldold <= $history(oldest)} {incr oldold} { - unset -nocomplain history($oldold) - } - set history(keep) $limit - } -} - -# tcl::HistClear -- -# -# Erase the history list -# -# Parameters: -# none -# -# Results: -# none -# -# Side Effects: -# Resets the history array, except for the keep limit - - proc tcl::HistClear {} { - variable history - set keep $history(keep) - unset history - array set history [list \ - nextid 0 \ - keep $keep \ - oldest -$keep \ - ] -} - -# tcl::HistInfo -- -# -# Return a pretty-printed version of the history list -# -# Parameters: -# num (optional) the length of the history list to return -# -# Results: -# A formatted history list - - proc tcl::HistInfo {{num {}}} { - variable history - if {$num eq ""} { - set num [expr {$history(keep) + 1}] - } - set result {} - set newline "" - for {set i [expr {$history(nextid) - $num + 1}]} \ - {$i <= $history(nextid)} {incr i} { - if {![info exists history($i)]} { - continue - } - set cmd [string map [list \n \n\t] [string trimright $history($i) \ \n]] - append result $newline[format "%6d %s" $i $cmd] - set newline \n - } - return $result -} - -# tcl::HistRedo -- -# -# Fetch the previous or specified event, execute it, and then -# replace the current history item with that event. -# -# Parameters: -# event (optional) index of history item to redo. Defaults to -1, -# which means the previous event. -# -# Results: -# Those of the command being redone. -# -# Side Effects: -# Replaces the current history list item with the one being redone. - - proc tcl::HistRedo {{event -1}} { - variable history - if {$event eq ""} { - set event -1 - } - set i [HistIndex $event] - if {$i == $history(nextid)} { - return -code error "cannot redo the current event" - } - set cmd $history($i) - HistChange $cmd 0 - uplevel #0 $cmd -} - -# tcl::HistIndex -- -# -# Map from an event specifier to an index in the history list. -# -# Parameters: -# event index of history item to redo. -# If this is a positive number, it is used directly. -# If it is a negative number, then it counts back to a previous -# event, where -1 is the most recent event. -# A string can be matched, either by being the prefix of -# a command or by matching a command with string match. -# -# Results: -# The index into history, or an error if the index didn't match. - - proc tcl::HistIndex {event} { - variable history - if {[catch {expr {~$event}}]} { - for {set i [expr {$history(nextid)-1}]} {[info exists history($i)]} \ - {incr i -1} { - if {[string match $event* $history($i)]} { - return $i; - } - if {[string match $event $history($i)]} { - return $i; - } - } - return -code error "no event matches \"$event\"" - } elseif {$event <= 0} { - set i [expr {$history(nextid) + $event}] - } else { - set i $event - } - if {$i <= $history(oldest)} { - return -code error "event \"$event\" is too far in the past" - } - if {$i > $history(nextid)} { - return -code error "event \"$event\" hasn't occured yet" - } - return $i -} - -# tcl::HistEvent -- -# -# Map from an event specifier to the value in the history list. -# -# Parameters: -# event index of history item to redo. See index for a -# description of possible event patterns. -# -# Results: -# The value from the history list. - - proc tcl::HistEvent {event} { - variable history - set i [HistIndex $event] - if {[info exists history($i)]} { - return [string trimright $history($i) \ \n] - } else { - return ""; - } -} - -# tcl::HistChange -- -# -# Replace a value in the history list. -# -# Parameters: -# cmd The new value to put into the history list. -# event (optional) index of history item to redo. See index for a -# description of possible event patterns. This defaults -# to 0, which specifies the current event. -# -# Side Effects: -# Changes the history list. - - proc tcl::HistChange {cmd {event 0}} { - variable history - set i [HistIndex $event] - set history($i) $cmd -} diff --git a/library/init.tcl b/library/init.tcl deleted file mode 100644 index 6b92c58..0000000 --- a/library/init.tcl +++ /dev/null @@ -1,832 +0,0 @@ -# init.tcl -- -# -# Default system startup file for Tcl-based applications. Defines -# "unknown" procedure and auto-load facilities. -# -# RCS: @(#) $Id: init.tcl,v 1.104.2.11 2008/12/21 20:59:01 dgp Exp $ -# -# Copyright (c) 1991-1993 The Regents of the University of California. -# Copyright (c) 1994-1996 Sun Microsystems, Inc. -# Copyright (c) 1998-1999 Scriptics Corporation. -# Copyright (c) 2004 by Kevin B. Kenny. All rights reserved. -# -# See the file "license.terms" for information on usage and redistribution -# of this file, and for a DISCLAIMER OF ALL WARRANTIES. -# - -if {[info commands package] == ""} { - error "version mismatch: library\nscripts expect Tcl version 7.5b1 or later but the loaded version is\nonly [info patchlevel]" -} -package require -exact Tcl 8.5.6 - -# Compute the auto path to use in this interpreter. -# The values on the path come from several locations: -# -# The environment variable TCLLIBPATH -# -# tcl_library, which is the directory containing this init.tcl script. -# [tclInit] (Tcl_Init()) searches around for the directory containing this -# init.tcl and defines tcl_library to that location before sourcing it. -# -# The parent directory of tcl_library. Adding the parent -# means that packages in peer directories will be found automatically. -# -# Also add the directory ../lib relative to the directory where the -# executable is located. This is meant to find binary packages for the -# same architecture as the current executable. -# -# tcl_pkgPath, which is set by the platform-specific initialization routines -# On UNIX it is compiled in -# On Windows, it is not used - -if {![info exists auto_path]} { - if {[info exists env(TCLLIBPATH)]} { - set auto_path $env(TCLLIBPATH) - } else { - set auto_path "" - } -} -namespace eval tcl { - variable Dir - foreach Dir [list $::tcl_library [file dirname $::tcl_library]] { - if {$Dir ni $::auto_path} { - lappend ::auto_path $Dir - } - } - set Dir [file join [file dirname [file dirname \ - [info nameofexecutable]]] lib] - if {$Dir ni $::auto_path} { - lappend ::auto_path $Dir - } - catch { - foreach Dir $::tcl_pkgPath { - if {$Dir ni $::auto_path} { - lappend ::auto_path $Dir - } - } - } - - if {![interp issafe]} { - variable Path [encoding dirs] - set Dir [file join $::tcl_library encoding] - if {$Dir ni $Path} { - lappend Path $Dir - encoding dirs $Path - } - } - - # TIP #255 min and max functions - namespace eval mathfunc { - proc min {args} { - if {[llength $args] == 0} { - return -code error \ - "too few arguments to math function \"min\"" - } - set val Inf - foreach arg $args { - # This will handle forcing the numeric value without - # ruining the internal type of a numeric object - if {[catch {expr {double($arg)}} err]} { - return -code error $err - } - if {$arg < $val} { set val $arg } - } - return $val - } - proc max {args} { - if {[llength $args] == 0} { - return -code error \ - "too few arguments to math function \"max\"" - } - set val -Inf - foreach arg $args { - # This will handle forcing the numeric value without - # ruining the internal type of a numeric object - if {[catch {expr {double($arg)}} err]} { - return -code error $err - } - if {$arg > $val} { set val $arg } - } - return $val - } - namespace export min max - } -} - -# Windows specific end of initialization - -if {(![interp issafe]) && ($tcl_platform(platform) eq "windows")} { - namespace eval tcl { - proc EnvTraceProc {lo n1 n2 op} { - set x $::env($n2) - set ::env($lo) $x - set ::env([string toupper $lo]) $x - } - proc InitWinEnv {} { - global env tcl_platform - foreach p [array names env] { - set u [string toupper $p] - if {$u ne $p} { - switch -- $u { - COMSPEC - - PATH { - if {![info exists env($u)]} { - set env($u) $env($p) - } - trace add variable env($p) write \ - [namespace code [list EnvTraceProc $p]] - trace add variable env($u) write \ - [namespace code [list EnvTraceProc $p]] - } - } - } - } - if {![info exists env(COMSPEC)]} { - if {$tcl_platform(os) eq "Windows NT"} { - set env(COMSPEC) cmd.exe - } else { - set env(COMSPEC) command.com - } - } - } - InitWinEnv - } -} - -# Setup the unknown package handler - - -if {[interp issafe]} { - package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown} -} else { - # Set up search for Tcl Modules (TIP #189). - # and setup platform specific unknown package handlers - if {$::tcl_platform(os) eq "Darwin" - && $::tcl_platform(platform) eq "unix"} { - package unknown {::tcl::tm::UnknownHandler \ - {::tcl::MacOSXPkgUnknown ::tclPkgUnknown}} - } else { - package unknown {::tcl::tm::UnknownHandler ::tclPkgUnknown} - } - - # Set up the 'clock' ensemble - - namespace eval ::tcl::clock [list variable TclLibDir $::tcl_library] - - proc clock args { - namespace eval ::tcl::clock [list namespace ensemble create -command \ - [uplevel 1 [list namespace origin [lindex [info level 0] 0]]] \ - -subcommands { - add clicks format microseconds milliseconds scan seconds - }] - - # Auto-loading stubs for 'clock.tcl' - - foreach cmd {add format scan} { - proc ::tcl::clock::$cmd args { - variable TclLibDir - source -encoding utf-8 [file join $TclLibDir clock.tcl] - return [uplevel 1 [info level 0]] - } - } - - return [uplevel 1 [info level 0]] - } -} - -# Conditionalize for presence of exec. - -if {[namespace which -command exec] eq ""} { - - # Some machines do not have exec. Also, on all - # platforms, safe interpreters do not have exec. - - set auto_noexec 1 -} - -# Define a log command (which can be overwitten to log errors -# differently, specially when stderr is not available) - -if {[namespace which -command tclLog] eq ""} { - proc tclLog {string} { - catch {puts stderr $string} - } -} - -# unknown -- -# This procedure is called when a Tcl command is invoked that doesn't -# exist in the interpreter. It takes the following steps to make the -# command available: -# -# 1. See if the command has the form "namespace inscope ns cmd" and -# if so, concatenate its arguments onto the end and evaluate it. -# 2. See if the autoload facility can locate the command in a -# Tcl script file. If so, load it and execute it. -# 3. If the command was invoked interactively at top-level: -# (a) see if the command exists as an executable UNIX program. -# If so, "exec" the command. -# (b) see if the command requests csh-like history substitution -# in one of the common forms !!, !, or ^old^new. If -# so, emulate csh's history substitution. -# (c) see if the command is a unique abbreviation for another -# command. If so, invoke the command. -# -# Arguments: -# args - A list whose elements are the words of the original -# command, including the command name. - -proc unknown args { - variable ::tcl::UnknownPending - global auto_noexec auto_noload env tcl_interactive - - # If the command word has the form "namespace inscope ns cmd" - # then concatenate its arguments onto the end and evaluate it. - - set cmd [lindex $args 0] - if {[regexp "^:*namespace\[ \t\n\]+inscope" $cmd] && [llength $cmd] == 4} { - #return -code error "You need an {*}" - set arglist [lrange $args 1 end] - set ret [catch {uplevel 1 ::$cmd $arglist} result opts] - dict unset opts -errorinfo - dict incr opts -level - return -options $opts $result - } - - catch {set savedErrorInfo $::errorInfo} - catch {set savedErrorCode $::errorCode} - set name $cmd - if {![info exists auto_noload]} { - # - # Make sure we're not trying to load the same proc twice. - # - if {[info exists UnknownPending($name)]} { - return -code error "self-referential recursion\ - in \"unknown\" for command \"$name\""; - } - set UnknownPending($name) pending; - set ret [catch { - auto_load $name [uplevel 1 {::namespace current}] - } msg opts] - unset UnknownPending($name); - if {$ret != 0} { - dict append opts -errorinfo "\n (autoloading \"$name\")" - return -options $opts $msg - } - if {![array size UnknownPending]} { - unset UnknownPending - } - if {$msg} { - if {[info exists savedErrorCode]} { - set ::errorCode $savedErrorCode - } else { - unset -nocomplain ::errorCode - } - if {[info exists savedErrorInfo]} { - set ::errorInfo $savedErrorInfo - } else { - unset -nocomplain ::errorInfo - } - set code [catch {uplevel 1 $args} msg opts] - if {$code == 1} { - # - # Compute stack trace contribution from the [uplevel]. - # Note the dependence on how Tcl_AddErrorInfo, etc. - # construct the stack trace. - # - set errorInfo [dict get $opts -errorinfo] - set errorCode [dict get $opts -errorcode] - set cinfo $args - if {[string bytelength $cinfo] > 150} { - set cinfo [string range $cinfo 0 150] - while {[string bytelength $cinfo] > 150} { - set cinfo [string range $cinfo 0 end-1] - } - append cinfo ... - } - append cinfo "\"\n (\"uplevel\" body line 1)" - append cinfo "\n invoked from within" - append cinfo "\n\"uplevel 1 \$args\"" - # - # Try each possible form of the stack trace - # and trim the extra contribution from the matching case - # - set expect "$msg\n while executing\n\"$cinfo" - if {$errorInfo eq $expect} { - # - # The stack has only the eval from the expanded command - # Do not generate any stack trace here. - # - dict unset opts -errorinfo - dict incr opts -level - return -options $opts $msg - } - # - # Stack trace is nested, trim off just the contribution - # from the extra "eval" of $args due to the "catch" above. - # - set expect "\n invoked from within\n\"$cinfo" - set exlen [string length $expect] - set eilen [string length $errorInfo] - set i [expr {$eilen - $exlen - 1}] - set einfo [string range $errorInfo 0 $i] - # - # For now verify that $errorInfo consists of what we are about - # to return plus what we expected to trim off. - # - if {$errorInfo ne "$einfo$expect"} { - error "Tcl bug: unexpected stack trace in \"unknown\"" {} \ - [list CORE UNKNOWN BADTRACE $einfo $expect $errorInfo] - } - return -code error -errorcode $errorCode \ - -errorinfo $einfo $msg - } else { - dict incr opts -level - return -options $opts $msg - } - } - } - - if {([info level] == 1) && ([info script] eq "") \ - && [info exists tcl_interactive] && $tcl_interactive} { - if {![info exists auto_noexec]} { - set new [auto_execok $name] - if {$new ne ""} { - set redir "" - if {[namespace which -command console] eq ""} { - set redir ">&@stdout <@stdin" - } - uplevel 1 [list ::catch \ - [concat exec $redir $new [lrange $args 1 end]] \ - ::tcl::UnknownResult ::tcl::UnknownOptions] - dict incr ::tcl::UnknownOptions -level - return -options $::tcl::UnknownOptions $::tcl::UnknownResult - } - } - if {$name eq "!!"} { - set newcmd [history event] - } elseif {[regexp {^!(.+)$} $name -> event]} { - set newcmd [history event $event] - } elseif {[regexp {^\^([^^]*)\^([^^]*)\^?$} $name -> old new]} { - set newcmd [history event -1] - catch {regsub -all -- $old $newcmd $new newcmd} - } - if {[info exists newcmd]} { - tclLog $newcmd - history change $newcmd 0 - uplevel 1 [list ::catch $newcmd \ - ::tcl::UnknownResult ::tcl::UnknownOptions] - dict incr ::tcl::UnknownOptions -level - return -options $::tcl::UnknownOptions $::tcl::UnknownResult - } - - set ret [catch {set candidates [info commands $name*]} msg] - if {$name eq "::"} { - set name "" - } - if {$ret != 0} { - dict append opts -errorinfo \ - "\n (expanding command prefix \"$name\" in unknown)" - return -options $opts $msg - } - # Filter out bogus matches when $name contained - # a glob-special char [Bug 946952] - if {$name eq ""} { - # Handle empty $name separately due to strangeness - # in [string first] (See RFE 1243354) - set cmds $candidates - } else { - set cmds [list] - foreach x $candidates { - if {[string first $name $x] == 0} { - lappend cmds $x - } - } - } - if {[llength $cmds] == 1} { - uplevel 1 [list ::catch [lreplace $args 0 0 [lindex $cmds 0]] \ - ::tcl::UnknownResult ::tcl::UnknownOptions] - dict incr ::tcl::UnknownOptions -level - return -options $::tcl::UnknownOptions $::tcl::UnknownResult - } - if {[llength $cmds]} { - return -code error "ambiguous command name \"$name\": [lsort $cmds]" - } - } - return -code error "invalid command name \"$name\"" -} - -# auto_load -- -# Checks a collection of library directories to see if a procedure -# is defined in one of them. If so, it sources the appropriate -# library file to create the procedure. Returns 1 if it successfully -# loaded the procedure, 0 otherwise. -# -# Arguments: -# cmd - Name of the command to find and load. -# namespace (optional) The namespace where the command is being used - must be -# a canonical namespace as returned [namespace current] -# for instance. If not given, namespace current is used. - -proc auto_load {cmd {namespace {}}} { - global auto_index auto_path - - if {$namespace eq ""} { - set namespace [uplevel 1 [list ::namespace current]] - } - set nameList [auto_qualify $cmd $namespace] - # workaround non canonical auto_index entries that might be around - # from older auto_mkindex versions - lappend nameList $cmd - foreach name $nameList { - if {[info exists auto_index($name)]} { - namespace eval :: $auto_index($name) - # There's a couple of ways to look for a command of a given - # name. One is to use - # info commands $name - # Unfortunately, if the name has glob-magic chars in it like * - # or [], it may not match. For our purposes here, a better - # route is to use - # namespace which -command $name - if {[namespace which -command $name] ne ""} { - return 1 - } - } - } - if {![info exists auto_path]} { - return 0 - } - - if {![auto_load_index]} { - return 0 - } - foreach name $nameList { - if {[info exists auto_index($name)]} { - namespace eval :: $auto_index($name) - if {[namespace which -command $name] ne ""} { - return 1 - } - } - } - return 0 -} - -# auto_load_index -- -# Loads the contents of tclIndex files on the auto_path directory -# list. This is usually invoked within auto_load to load the index -# of available commands. Returns 1 if the index is loaded, and 0 if -# the index is already loaded and up to date. -# -# Arguments: -# None. - -proc auto_load_index {} { - variable ::tcl::auto_oldpath - global auto_index auto_path - - if {[info exists auto_oldpath] && ($auto_oldpath eq $auto_path)} { - return 0 - } - set auto_oldpath $auto_path - - # Check if we are a safe interpreter. In that case, we support only - # newer format tclIndex files. - - set issafe [interp issafe] - for {set i [expr {[llength $auto_path] - 1}]} {$i >= 0} {incr i -1} { - set dir [lindex $auto_path $i] - set f "" - if {$issafe} { - catch {source [file join $dir tclIndex]} - } elseif {[catch {set f [open [file join $dir tclIndex]]}]} { - continue - } else { - set error [catch { - set id [gets $f] - if {$id eq "# Tcl autoload index file, version 2.0"} { - eval [read $f] - } elseif {$id eq "# Tcl autoload index file: each line identifies a Tcl"} { - while {[gets $f line] >= 0} { - if {([string index $line 0] eq "#") \ - || ([llength $line] != 2)} { - continue - } - set name [lindex $line 0] - set auto_index($name) \ - "source [file join $dir [lindex $line 1]]" - } - } else { - error "[file join $dir tclIndex] isn't a proper Tcl index file" - } - } msg opts] - if {$f ne ""} { - close $f - } - if {$error} { - return -options $opts $msg - } - } - } - return 1 -} - -# auto_qualify -- -# -# Compute a fully qualified names list for use in the auto_index array. -# For historical reasons, commands in the global namespace do not have leading -# :: in the index key. The list has two elements when the command name is -# relative (no leading ::) and the namespace is not the global one. Otherwise -# only one name is returned (and searched in the auto_index). -# -# Arguments - -# cmd The command name. Can be any name accepted for command -# invocations (Like "foo::::bar"). -# namespace The namespace where the command is being used - must be -# a canonical namespace as returned by [namespace current] -# for instance. - -proc auto_qualify {cmd namespace} { - - # count separators and clean them up - # (making sure that foo:::::bar will be treated as foo::bar) - set n [regsub -all {::+} $cmd :: cmd] - - # Ignore namespace if the name starts with :: - # Handle special case of only leading :: - - # Before each return case we give an example of which category it is - # with the following form : - # ( inputCmd, inputNameSpace) -> output - - if {[string match ::* $cmd]} { - if {$n > 1} { - # ( ::foo::bar , * ) -> ::foo::bar - return [list $cmd] - } else { - # ( ::global , * ) -> global - return [list [string range $cmd 2 end]] - } - } - - # Potentially returning 2 elements to try : - # (if the current namespace is not the global one) - - if {$n == 0} { - if {$namespace eq "::"} { - # ( nocolons , :: ) -> nocolons - return [list $cmd] - } else { - # ( nocolons , ::sub ) -> ::sub::nocolons nocolons - return [list ${namespace}::$cmd $cmd] - } - } elseif {$namespace eq "::"} { - # ( foo::bar , :: ) -> ::foo::bar - return [list ::$cmd] - } else { - # ( foo::bar , ::sub ) -> ::sub::foo::bar ::foo::bar - return [list ${namespace}::$cmd ::$cmd] - } -} - -# auto_import -- -# -# Invoked during "namespace import" to make see if the imported commands -# reside in an autoloaded library. If so, the commands are loaded so -# that they will be available for the import links. If not, then this -# procedure does nothing. -# -# Arguments - -# pattern The pattern of commands being imported (like "foo::*") -# a canonical namespace as returned by [namespace current] - -proc auto_import {pattern} { - global auto_index - - # If no namespace is specified, this will be an error case - - if {![string match *::* $pattern]} { - return - } - - set ns [uplevel 1 [list ::namespace current]] - set patternList [auto_qualify $pattern $ns] - - auto_load_index - - foreach pattern $patternList { - foreach name [array names auto_index $pattern] { - if {([namespace which -command $name] eq "") - && ([namespace qualifiers $pattern] eq [namespace qualifiers $name])} { - namespace eval :: $auto_index($name) - } - } - } -} - -# auto_execok -- -# -# Returns string that indicates name of program to execute if -# name corresponds to a shell builtin or an executable in the -# Windows search path, or "" otherwise. Builds an associative -# array auto_execs that caches information about previous checks, -# for speed. -# -# Arguments: -# name - Name of a command. - -if {$tcl_platform(platform) eq "windows"} { -# Windows version. -# -# Note that info executable doesn't work under Windows, so we have to -# look for files with .exe, .com, or .bat extensions. Also, the path -# may be in the Path or PATH environment variables, and path -# components are separated with semicolons, not colons as under Unix. -# -proc auto_execok name { - global auto_execs env tcl_platform - - if {[info exists auto_execs($name)]} { - return $auto_execs($name) - } - set auto_execs($name) "" - - set shellBuiltins [list cls copy date del erase dir echo mkdir \ - md rename ren rmdir rd time type ver vol] - if {$tcl_platform(os) eq "Windows NT"} { - # NT includes the 'start' built-in - lappend shellBuiltins "start" - } - if {[info exists env(PATHEXT)]} { - # Add an initial ; to have the {} extension check first. - set execExtensions [split ";$env(PATHEXT)" ";"] - } else { - set execExtensions [list {} .com .exe .bat] - } - - if {$name in $shellBuiltins} { - # When this is command.com for some reason on Win2K, Tcl won't - # exec it unless the case is right, which this corrects. COMSPEC - # may not point to a real file, so do the check. - set cmd $env(COMSPEC) - if {[file exists $cmd]} { - set cmd [file attributes $cmd -shortname] - } - return [set auto_execs($name) [list $cmd /c $name]] - } - - if {[llength [file split $name]] != 1} { - foreach ext $execExtensions { - set file ${name}${ext} - if {[file exists $file] && ![file isdirectory $file]} { - return [set auto_execs($name) [list $file]] - } - } - return "" - } - - set path "[file dirname [info nameof]];.;" - if {[info exists env(WINDIR)]} { - set windir $env(WINDIR) - } - if {[info exists windir]} { - if {$tcl_platform(os) eq "Windows NT"} { - append path "$windir/system32;" - } - append path "$windir/system;$windir;" - } - - foreach var {PATH Path path} { - if {[info exists env($var)]} { - append path ";$env($var)" - } - } - - foreach dir [split $path {;}] { - # Skip already checked directories - if {[info exists checked($dir)] || ($dir eq {})} { continue } - set checked($dir) {} - foreach ext $execExtensions { - set file [file join $dir ${name}${ext}] - if {[file exists $file] && ![file isdirectory $file]} { - return [set auto_execs($name) [list $file]] - } - } - } - return "" -} - -} else { -# Unix version. -# -proc auto_execok name { - global auto_execs env - - if {[info exists auto_execs($name)]} { - return $auto_execs($name) - } - set auto_execs($name) "" - if {[llength [file split $name]] != 1} { - if {[file executable $name] && ![file isdirectory $name]} { - set auto_execs($name) [list $name] - } - return $auto_execs($name) - } - foreach dir [split $env(PATH) :] { - if {$dir eq ""} { - set dir . - } - set file [file join $dir $name] - if {[file executable $file] && ![file isdirectory $file]} { - set auto_execs($name) [list $file] - return $auto_execs($name) - } - } - return "" -} - -} - -# ::tcl::CopyDirectory -- -# -# This procedure is called by Tcl's core when attempts to call the -# filesystem's copydirectory function fail. The semantics of the call -# are that 'dest' does not yet exist, i.e. dest should become the exact -# image of src. If dest does exist, we throw an error. -# -# Note that making changes to this procedure can change the results -# of running Tcl's tests. -# -# Arguments: -# action - "renaming" or "copying" -# src - source directory -# dest - destination directory -proc tcl::CopyDirectory {action src dest} { - set nsrc [file normalize $src] - set ndest [file normalize $dest] - - if {$action eq "renaming"} { - # Can't rename volumes. We could give a more precise - # error message here, but that would break the test suite. - if {$nsrc in [file volumes]} { - return -code error "error $action \"$src\" to\ - \"$dest\": trying to rename a volume or move a directory\ - into itself" - } - } - if {[file exists $dest]} { - if {$nsrc eq $ndest} { - return -code error "error $action \"$src\" to\ - \"$dest\": trying to rename a volume or move a directory\ - into itself" - } - if {$action eq "copying"} { - # We used to throw an error here, but, looking more closely - # at the core copy code in tclFCmd.c, if the destination - # exists, then we should only call this function if -force - # is true, which means we just want to over-write. So, - # the following code is now commented out. - # - # return -code error "error $action \"$src\" to\ - # \"$dest\": file already exists" - } else { - # Depending on the platform, and on the current - # working directory, the directories '.', '..' - # can be returned in various combinations. Anyway, - # if any other file is returned, we must signal an error. - set existing [glob -nocomplain -directory $dest * .*] - lappend existing {*}[glob -nocomplain -directory $dest \ - -type hidden * .*] - foreach s $existing { - if {([file tail $s] ne ".") && ([file tail $s] ne "..")} { - return -code error "error $action \"$src\" to\ - \"$dest\": file already exists" - } - } - } - } else { - if {[string first $nsrc $ndest] != -1} { - set srclen [expr {[llength [file split $nsrc]] -1}] - set ndest [lindex [file split $ndest] $srclen] - if {$ndest eq [file tail $nsrc]} { - return -code error "error $action \"$src\" to\ - \"$dest\": trying to rename a volume or move a directory\ - into itself" - } - } - file mkdir $dest - } - # Have to be careful to capture both visible and hidden files. - # We will also be more generous to the file system and not - # assume the hidden and non-hidden lists are non-overlapping. - # - # On Unix 'hidden' files begin with '.'. On other platforms - # or filesystems hidden files may have other interpretations. - set filelist [concat [glob -nocomplain -directory $src *] \ - [glob -nocomplain -directory $src -types hidden *]] - - foreach s [lsort -unique $filelist] { - if {([file tail $s] ne ".") && ([file tail $s] ne "..")} { - file copy -force $s [file join $dest [file tail $s]] - } - } - return -} diff --git a/library/license.terms b/library/license.terms deleted file mode 100644 index f1dcaa5..0000000 --- a/library/license.terms +++ /dev/null @@ -1,40 +0,0 @@ -This software is copyrighted by the Regents of the University of -California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState -Corporation and other parties. The following terms apply to all files -associated with the software unless explicitly disclaimed in -individual files. - -The authors hereby grant permission to use, copy, modify, distribute, -and license this software and its documentation for any purpose, provided -that existing copyright notices are retained in all copies and that this -notice is included verbatim in any distributions. No written agreement, -license, or royalty fee is required for any of the authorized uses. -Modifications to this software may be copyrighted by their authors -and need not follow the licensing terms described here, provided that -the new terms are clearly indicated on the first page of each file where -they apply. - -IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY -FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES -ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY -DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, -INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE -IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE -NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR -MODIFICATIONS. - -GOVERNMENT USE: If you are acquiring this software on behalf of the -U.S. government, the Government shall have only "Restricted Rights" -in the software and related documentation as defined in the Federal -Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you -are acquiring the software on behalf of the Department of Defense, the -software shall be classified as "Commercial Computer Software" and the -Government shall have only "Restricted Rights" as defined in Clause -252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the -authors grant the U.S. Government and others acting in its behalf -permission to use and distribute the software in accordance with the -terms specified in this license. diff --git a/library/opt/optparse.tcl b/library/opt/optparse.tcl deleted file mode 100644 index 4622bde..0000000 --- a/library/opt/optparse.tcl +++ /dev/null @@ -1,1074 +0,0 @@ -# optparse.tcl -- -# -# (private) Option parsing package -# Primarily used internally by the safe:: code. -# -# WARNING: This code will go away in a future release -# of Tcl. It is NOT supported and you should not rely -# on it. If your code does rely on this package you -# may directly incorporate this code into your application. -# -# RCS: @(#) $Id: optparse.tcl,v 1.10 2003/09/10 20:27:30 dgp Exp $ - -package require Tcl 8.2 -# When this version number changes, update the pkgIndex.tcl file -# and the install directory in the Makefiles. -package provide opt 0.4.5 - -namespace eval ::tcl { - - # Exported APIs - namespace export OptKeyRegister OptKeyDelete OptKeyError OptKeyParse \ - OptProc OptProcArgGiven OptParse \ - Lempty Lget \ - Lassign Lvarpop Lvarpop1 Lvarset Lvarincr \ - SetMax SetMin - - -################# Example of use / 'user documentation' ################### - - proc OptCreateTestProc {} { - - # Defines ::tcl::OptParseTest as a test proc with parsed arguments - # (can't be defined before the code below is loaded (before "OptProc")) - - # Every OptProc give usage information on "procname -help". - # Try "tcl::OptParseTest -help" and "tcl::OptParseTest -a" and - # then other arguments. - # - # example of 'valid' call: - # ::tcl::OptParseTest save -4 -pr 23 -libsok SybTcl\ - # -nostatics false ch1 - OptProc OptParseTest { - {subcommand -choice {save print} "sub command"} - {arg1 3 "some number"} - {-aflag} - {-intflag 7} - {-weirdflag "help string"} - {-noStatics "Not ok to load static packages"} - {-nestedloading1 true "OK to load into nested slaves"} - {-nestedloading2 -boolean true "OK to load into nested slaves"} - {-libsOK -choice {Tk SybTcl} - "List of packages that can be loaded"} - {-precision -int 12 "Number of digits of precision"} - {-intval 7 "An integer"} - {-scale -float 1.0 "Scale factor"} - {-zoom 1.0 "Zoom factor"} - {-arbitrary foobar "Arbitrary string"} - {-random -string 12 "Random string"} - {-listval -list {} "List value"} - {-blahflag -blah abc "Funny type"} - {arg2 -boolean "a boolean"} - {arg3 -choice "ch1 ch2"} - {?optarg? -list {} "optional argument"} - } { - foreach v [info locals] { - puts stderr [format "%14s : %s" $v [set $v]] - } - } - } - -################### No User serviceable part below ! ############### - - # Array storing the parsed descriptions - variable OptDesc; - array set OptDesc {}; - # Next potentially free key id (numeric) - variable OptDescN 0; - -# Inside algorithm/mechanism description: -# (not for the faint hearted ;-) -# -# The argument description is parsed into a "program tree" -# It is called a "program" because it is the program used by -# the state machine interpreter that use that program to -# actually parse the arguments at run time. -# -# The general structure of a "program" is -# notation (pseudo bnf like) -# name :== definition defines "name" as being "definition" -# { x y z } means list of x, y, and z -# x* means x repeated 0 or more time -# x+ means "x x*" -# x? means optionally x -# x | y means x or y -# "cccc" means the literal string -# -# program :== { programCounter programStep* } -# -# programStep :== program | singleStep -# -# programCounter :== {"P" integer+ } -# -# singleStep :== { instruction parameters* } -# -# instruction :== single element list -# -# (the difference between singleStep and program is that \ -# llength [lindex $program 0] >= 2 -# while -# llength [lindex $singleStep 0] == 1 -# ) -# -# And for this application: -# -# singleStep :== { instruction varname {hasBeenSet currentValue} type -# typeArgs help } -# instruction :== "flags" | "value" -# type :== knowType | anyword -# knowType :== "string" | "int" | "boolean" | "boolflag" | "float" -# | "choice" -# -# for type "choice" typeArgs is a list of possible choices, the first one -# is the default value. for all other types the typeArgs is the default value -# -# a "boolflag" is the type for a flag whose presence or absence, without -# additional arguments means respectively true or false (default flag type). -# -# programCounter is the index in the list of the currently processed -# programStep (thus starting at 1 (0 is {"P" prgCounterValue}). -# If it is a list it points toward each currently selected programStep. -# (like for "flags", as they are optional, form a set and programStep). - -# Performance/Implementation issues -# --------------------------------- -# We use tcl lists instead of arrays because with tcl8.0 -# they should start to be much faster. -# But this code use a lot of helper procs (like Lvarset) -# which are quite slow and would be helpfully optimized -# for instance by being written in C. Also our struture -# is complex and there is maybe some places where the -# string rep might be calculated at great exense. to be checked. - -# -# Parse a given description and saves it here under the given key -# generate a unused keyid if not given -# -proc ::tcl::OptKeyRegister {desc {key ""}} { - variable OptDesc; - variable OptDescN; - if {[string equal $key ""]} { - # in case a key given to us as a parameter was a number - while {[info exists OptDesc($OptDescN)]} {incr OptDescN} - set key $OptDescN; - incr OptDescN; - } - # program counter - set program [list [list "P" 1]]; - - # are we processing flags (which makes a single program step) - set inflags 0; - - set state {}; - - # flag used to detect that we just have a single (flags set) subprogram. - set empty 1; - - foreach item $desc { - if {$state == "args"} { - # more items after 'args'... - return -code error "'args' special argument must be the last one"; - } - set res [OptNormalizeOne $item]; - set state [lindex $res 0]; - if {$inflags} { - if {$state == "flags"} { - # add to 'subprogram' - lappend flagsprg $res; - } else { - # put in the flags - # structure for flag programs items is a list of - # {subprgcounter {prg flag 1} {prg flag 2} {...}} - lappend program $flagsprg; - # put the other regular stuff - lappend program $res; - set inflags 0; - set empty 0; - } - } else { - if {$state == "flags"} { - set inflags 1; - # sub program counter + first sub program - set flagsprg [list [list "P" 1] $res]; - } else { - lappend program $res; - set empty 0; - } - } - } - if {$inflags} { - if {$empty} { - # We just have the subprogram, optimize and remove - # unneeded level: - set program $flagsprg; - } else { - lappend program $flagsprg; - } - } - - set OptDesc($key) $program; - - return $key; -} - -# -# Free the storage for that given key -# -proc ::tcl::OptKeyDelete {key} { - variable OptDesc; - unset OptDesc($key); -} - - # Get the parsed description stored under the given key. - proc OptKeyGetDesc {descKey} { - variable OptDesc; - if {![info exists OptDesc($descKey)]} { - return -code error "Unknown option description key \"$descKey\""; - } - set OptDesc($descKey); - } - -# Parse entry point for ppl who don't want to register with a key, -# for instance because the description changes dynamically. -# (otherwise one should really use OptKeyRegister once + OptKeyParse -# as it is way faster or simply OptProc which does it all) -# Assign a temporary key, call OptKeyParse and then free the storage -proc ::tcl::OptParse {desc arglist} { - set tempkey [OptKeyRegister $desc]; - set ret [catch {uplevel 1 [list ::tcl::OptKeyParse $tempkey $arglist]} res]; - OptKeyDelete $tempkey; - return -code $ret $res; -} - -# Helper function, replacement for proc that both -# register the description under a key which is the name of the proc -# (and thus unique to that code) -# and add a first line to the code to call the OptKeyParse proc -# Stores the list of variables that have been actually given by the user -# (the other will be sets to their default value) -# into local variable named "Args". -proc ::tcl::OptProc {name desc body} { - set namespace [uplevel 1 [list ::namespace current]]; - if {[string match "::*" $name] || [string equal $namespace "::"]} { - # absolute name or global namespace, name is the key - set key $name; - } else { - # we are relative to some non top level namespace: - set key "${namespace}::${name}"; - } - OptKeyRegister $desc $key; - uplevel 1 [list ::proc $name args "set Args \[::tcl::OptKeyParse $key \$args\]\n$body"]; - return $key; -} -# Check that a argument has been given -# assumes that "OptProc" has been used as it will check in "Args" list -proc ::tcl::OptProcArgGiven {argname} { - upvar Args alist; - expr {[lsearch $alist $argname] >=0} -} - - ####### - # Programs/Descriptions manipulation - - # Return the instruction word/list of a given step/(sub)program - proc OptInstr {lst} { - lindex $lst 0; - } - # Is a (sub) program or a plain instruction ? - proc OptIsPrg {lst} { - expr {[llength [OptInstr $lst]]>=2} - } - # Is this instruction a program counter or a real instr - proc OptIsCounter {item} { - expr {[lindex $item 0]=="P"} - } - # Current program counter (2nd word of first word) - proc OptGetPrgCounter {lst} { - Lget $lst {0 1} - } - # Current program counter (2nd word of first word) - proc OptSetPrgCounter {lstName newValue} { - upvar $lstName lst; - set lst [lreplace $lst 0 0 [concat "P" $newValue]]; - } - # returns a list of currently selected items. - proc OptSelection {lst} { - set res {}; - foreach idx [lrange [lindex $lst 0] 1 end] { - lappend res [Lget $lst $idx]; - } - return $res; - } - - # Advance to next description - proc OptNextDesc {descName} { - uplevel 1 [list Lvarincr $descName {0 1}]; - } - - # Get the current description, eventually descend - proc OptCurDesc {descriptions} { - lindex $descriptions [OptGetPrgCounter $descriptions]; - } - # get the current description, eventually descend - # through sub programs as needed. - proc OptCurDescFinal {descriptions} { - set item [OptCurDesc $descriptions]; - # Descend untill we get the actual item and not a sub program - while {[OptIsPrg $item]} { - set item [OptCurDesc $item]; - } - return $item; - } - # Current final instruction adress - proc OptCurAddr {descriptions {start {}}} { - set adress [OptGetPrgCounter $descriptions]; - lappend start $adress; - set item [lindex $descriptions $adress]; - if {[OptIsPrg $item]} { - return [OptCurAddr $item $start]; - } else { - return $start; - } - } - # Set the value field of the current instruction - proc OptCurSetValue {descriptionsName value} { - upvar $descriptionsName descriptions - # get the current item full adress - set adress [OptCurAddr $descriptions]; - # use the 3th field of the item (see OptValue / OptNewInst) - lappend adress 2 - Lvarset descriptions $adress [list 1 $value]; - # ^hasBeenSet flag - } - - # empty state means done/paste the end of the program - proc OptState {item} { - lindex $item 0 - } - - # current state - proc OptCurState {descriptions} { - OptState [OptCurDesc $descriptions]; - } - - ####### - # Arguments manipulation - - # Returns the argument that has to be processed now - proc OptCurrentArg {lst} { - lindex $lst 0; - } - # Advance to next argument - proc OptNextArg {argsName} { - uplevel 1 [list Lvarpop1 $argsName]; - } - ####### - - - - - - # Loop over all descriptions, calling OptDoOne which will - # eventually eat all the arguments. - proc OptDoAll {descriptionsName argumentsName} { - upvar $descriptionsName descriptions - upvar $argumentsName arguments; -# puts "entered DoAll"; - # Nb: the places where "state" can be set are tricky to figure - # because DoOne sets the state to flagsValue and return -continue - # when needed... - set state [OptCurState $descriptions]; - # We'll exit the loop in "OptDoOne" or when state is empty. - while 1 { - set curitem [OptCurDesc $descriptions]; - # Do subprograms if needed, call ourselves on the sub branch - while {[OptIsPrg $curitem]} { - OptDoAll curitem arguments -# puts "done DoAll sub"; - # Insert back the results in current tree; - Lvarset1nc descriptions [OptGetPrgCounter $descriptions]\ - $curitem; - OptNextDesc descriptions; - set curitem [OptCurDesc $descriptions]; - set state [OptCurState $descriptions]; - } -# puts "state = \"$state\" - arguments=($arguments)"; - if {[Lempty $state]} { - # Nothing left to do, we are done in this branch: - break; - } - # The following statement can make us terminate/continue - # as it use return -code {break, continue, return and error} - # codes - OptDoOne descriptions state arguments; - # If we are here, no special return code where issued, - # we'll step to next instruction : -# puts "new state = \"$state\""; - OptNextDesc descriptions; - set state [OptCurState $descriptions]; - } - } - - # Process one step for the state machine, - # eventually consuming the current argument. - proc OptDoOne {descriptionsName stateName argumentsName} { - upvar $argumentsName arguments; - upvar $descriptionsName descriptions; - upvar $stateName state; - - # the special state/instruction "args" eats all - # the remaining args (if any) - if {($state == "args")} { - if {![Lempty $arguments]} { - # If there is no additional arguments, leave the default value - # in. - OptCurSetValue descriptions $arguments; - set arguments {}; - } -# puts "breaking out ('args' state: consuming every reminding args)" - return -code break; - } - - if {[Lempty $arguments]} { - if {$state == "flags"} { - # no argument and no flags : we're done -# puts "returning to previous (sub)prg (no more args)"; - return -code return; - } elseif {$state == "optValue"} { - set state next; # not used, for debug only - # go to next state - return ; - } else { - return -code error [OptMissingValue $descriptions]; - } - } else { - set arg [OptCurrentArg $arguments]; - } - - switch $state { - flags { - # A non-dash argument terminates the options, as does -- - - # Still a flag ? - if {![OptIsFlag $arg]} { - # don't consume the argument, return to previous prg - return -code return; - } - # consume the flag - OptNextArg arguments; - if {[string equal "--" $arg]} { - # return from 'flags' state - return -code return; - } - - set hits [OptHits descriptions $arg]; - if {$hits > 1} { - return -code error [OptAmbigous $descriptions $arg] - } elseif {$hits == 0} { - return -code error [OptFlagUsage $descriptions $arg] - } - set item [OptCurDesc $descriptions]; - if {[OptNeedValue $item]} { - # we need a value, next state is - set state flagValue; - } else { - OptCurSetValue descriptions 1; - } - # continue - return -code continue; - } - flagValue - - value { - set item [OptCurDesc $descriptions]; - # Test the values against their required type - if {[catch {OptCheckType $arg\ - [OptType $item] [OptTypeArgs $item]} val]} { - return -code error [OptBadValue $item $arg $val] - } - # consume the value - OptNextArg arguments; - # set the value - OptCurSetValue descriptions $val; - # go to next state - if {$state == "flagValue"} { - set state flags - return -code continue; - } else { - set state next; # not used, for debug only - return ; # will go on next step - } - } - optValue { - set item [OptCurDesc $descriptions]; - # Test the values against their required type - if {![catch {OptCheckType $arg\ - [OptType $item] [OptTypeArgs $item]} val]} { - # right type, so : - # consume the value - OptNextArg arguments; - # set the value - OptCurSetValue descriptions $val; - } - # go to next state - set state next; # not used, for debug only - return ; # will go on next step - } - } - # If we reach this point: an unknown - # state as been entered ! - return -code error "Bug! unknown state in DoOne \"$state\"\ - (prg counter [OptGetPrgCounter $descriptions]:\ - [OptCurDesc $descriptions])"; - } - -# Parse the options given the key to previously registered description -# and arguments list -proc ::tcl::OptKeyParse {descKey arglist} { - - set desc [OptKeyGetDesc $descKey]; - - # make sure -help always give usage - if {[string equal -nocase "-help" $arglist]} { - return -code error [OptError "Usage information:" $desc 1]; - } - - OptDoAll desc arglist; - - if {![Lempty $arglist]} { - return -code error [OptTooManyArgs $desc $arglist]; - } - - # Analyse the result - # Walk through the tree: - OptTreeVars $desc "#[expr {[info level]-1}]" ; -} - - # determine string length for nice tabulated output - proc OptTreeVars {desc level {vnamesLst {}}} { - foreach item $desc { - if {[OptIsCounter $item]} continue; - if {[OptIsPrg $item]} { - set vnamesLst [OptTreeVars $item $level $vnamesLst]; - } else { - set vname [OptVarName $item]; - upvar $level $vname var - if {[OptHasBeenSet $item]} { -# puts "adding $vname" - # lets use the input name for the returned list - # it is more usefull, for instance you can check that - # no flags at all was given with expr - # {![string match "*-*" $Args]} - lappend vnamesLst [OptName $item]; - set var [OptValue $item]; - } else { - set var [OptDefaultValue $item]; - } - } - } - return $vnamesLst - } - - -# Check the type of a value -# and emit an error if arg is not of the correct type -# otherwise returns the canonical value of that arg (ie 0/1 for booleans) -proc ::tcl::OptCheckType {arg type {typeArgs ""}} { -# puts "checking '$arg' against '$type' ($typeArgs)"; - - # only types "any", "choice", and numbers can have leading "-" - - switch -exact -- $type { - int { - if {![string is integer -strict $arg]} { - error "not an integer" - } - return $arg; - } - float { - return [expr {double($arg)}] - } - script - - list { - # if llength fail : malformed list - if {[llength $arg]==0 && [OptIsFlag $arg]} { - error "no values with leading -" - } - return $arg; - } - boolean { - if {![string is boolean -strict $arg]} { - error "non canonic boolean" - } - # convert true/false because expr/if is broken with "!,... - return [expr {$arg ? 1 : 0}] - } - choice { - if {[lsearch -exact $typeArgs $arg] < 0} { - error "invalid choice" - } - return $arg; - } - any { - return $arg; - } - string - - default { - if {[OptIsFlag $arg]} { - error "no values with leading -" - } - return $arg - } - } - return neverReached; -} - - # internal utilities - - # returns the number of flags matching the given arg - # sets the (local) prg counter to the list of matches - proc OptHits {descName arg} { - upvar $descName desc; - set hits 0 - set hitems {} - set i 1; - - set larg [string tolower $arg]; - set len [string length $larg]; - set last [expr {$len-1}]; - - foreach item [lrange $desc 1 end] { - set flag [OptName $item] - # lets try to match case insensitively - # (string length ought to be cheap) - set lflag [string tolower $flag]; - if {$len == [string length $lflag]} { - if {[string equal $larg $lflag]} { - # Exact match case - OptSetPrgCounter desc $i; - return 1; - } - } elseif {[string equal $larg [string range $lflag 0 $last]]} { - lappend hitems $i; - incr hits; - } - incr i; - } - if {$hits} { - OptSetPrgCounter desc $hitems; - } - return $hits - } - - # Extract fields from the list structure: - - proc OptName {item} { - lindex $item 1; - } - proc OptHasBeenSet {item} { - Lget $item {2 0}; - } - proc OptValue {item} { - Lget $item {2 1}; - } - - proc OptIsFlag {name} { - string match "-*" $name; - } - proc OptIsOpt {name} { - string match {\?*} $name; - } - proc OptVarName {item} { - set name [OptName $item]; - if {[OptIsFlag $name]} { - return [string range $name 1 end]; - } elseif {[OptIsOpt $name]} { - return [string trim $name "?"]; - } else { - return $name; - } - } - proc OptType {item} { - lindex $item 3 - } - proc OptTypeArgs {item} { - lindex $item 4 - } - proc OptHelp {item} { - lindex $item 5 - } - proc OptNeedValue {item} { - expr {![string equal [OptType $item] boolflag]} - } - proc OptDefaultValue {item} { - set val [OptTypeArgs $item] - switch -exact -- [OptType $item] { - choice {return [lindex $val 0]} - boolean - - boolflag { - # convert back false/true to 0/1 because expr !$bool - # is broken.. - if {$val} { - return 1 - } else { - return 0 - } - } - } - return $val - } - - # Description format error helper - proc OptOptUsage {item {what ""}} { - return -code error "invalid description format$what: $item\n\ - should be a list of {varname|-flagname ?-type? ?defaultvalue?\ - ?helpstring?}"; - } - - - # Generate a canonical form single instruction - proc OptNewInst {state varname type typeArgs help} { - list $state $varname [list 0 {}] $type $typeArgs $help; - # ^ ^ - # | | - # hasBeenSet=+ +=currentValue - } - - # Translate one item to canonical form - proc OptNormalizeOne {item} { - set lg [Lassign $item varname arg1 arg2 arg3]; -# puts "called optnormalizeone '$item' v=($varname), lg=$lg"; - set isflag [OptIsFlag $varname]; - set isopt [OptIsOpt $varname]; - if {$isflag} { - set state "flags"; - } elseif {$isopt} { - set state "optValue"; - } elseif {![string equal $varname "args"]} { - set state "value"; - } else { - set state "args"; - } - - # apply 'smart' 'fuzzy' logic to try to make - # description writer's life easy, and our's difficult : - # let's guess the missing arguments :-) - - switch $lg { - 1 { - if {$isflag} { - return [OptNewInst $state $varname boolflag false ""]; - } else { - return [OptNewInst $state $varname any "" ""]; - } - } - 2 { - # varname default - # varname help - set type [OptGuessType $arg1] - if {[string equal $type "string"]} { - if {$isflag} { - set type boolflag - set def false - } else { - set type any - set def "" - } - set help $arg1 - } else { - set help "" - set def $arg1 - } - return [OptNewInst $state $varname $type $def $help]; - } - 3 { - # varname type value - # varname value comment - - if {[regexp {^-(.+)$} $arg1 x type]} { - # flags/optValue as they are optional, need a "value", - # on the contrary, for a variable (non optional), - # default value is pointless, 'cept for choices : - if {$isflag || $isopt || ($type == "choice")} { - return [OptNewInst $state $varname $type $arg2 ""]; - } else { - return [OptNewInst $state $varname $type "" $arg2]; - } - } else { - return [OptNewInst $state $varname\ - [OptGuessType $arg1] $arg1 $arg2] - } - } - 4 { - if {[regexp {^-(.+)$} $arg1 x type]} { - return [OptNewInst $state $varname $type $arg2 $arg3]; - } else { - return -code error [OptOptUsage $item]; - } - } - default { - return -code error [OptOptUsage $item]; - } - } - } - - # Auto magic lazy type determination - proc OptGuessType {arg} { - if { $arg == "true" || $arg == "false" } { - return boolean - } - if {[string is integer -strict $arg]} { - return int - } - if {[string is double -strict $arg]} { - return float - } - return string - } - - # Error messages front ends - - proc OptAmbigous {desc arg} { - OptError "ambigous option \"$arg\", choose from:" [OptSelection $desc] - } - proc OptFlagUsage {desc arg} { - OptError "bad flag \"$arg\", must be one of" $desc; - } - proc OptTooManyArgs {desc arguments} { - OptError "too many arguments (unexpected argument(s): $arguments),\ - usage:"\ - $desc 1 - } - proc OptParamType {item} { - if {[OptIsFlag $item]} { - return "flag"; - } else { - return "parameter"; - } - } - proc OptBadValue {item arg {err {}}} { -# puts "bad val err = \"$err\""; - OptError "bad value \"$arg\" for [OptParamType $item]"\ - [list $item] - } - proc OptMissingValue {descriptions} { -# set item [OptCurDescFinal $descriptions]; - set item [OptCurDesc $descriptions]; - OptError "no value given for [OptParamType $item] \"[OptName $item]\"\ - (use -help for full usage) :"\ - [list $item] - } - -proc ::tcl::OptKeyError {prefix descKey {header 0}} { - OptError $prefix [OptKeyGetDesc $descKey] $header; -} - - # determine string length for nice tabulated output - proc OptLengths {desc nlName tlName dlName} { - upvar $nlName nl; - upvar $tlName tl; - upvar $dlName dl; - foreach item $desc { - if {[OptIsCounter $item]} continue; - if {[OptIsPrg $item]} { - OptLengths $item nl tl dl - } else { - SetMax nl [string length [OptName $item]] - SetMax tl [string length [OptType $item]] - set dv [OptTypeArgs $item]; - if {[OptState $item] != "header"} { - set dv "($dv)"; - } - set l [string length $dv]; - # limit the space allocated to potentially big "choices" - if {([OptType $item] != "choice") || ($l<=12)} { - SetMax dl $l - } else { - if {![info exists dl]} { - set dl 0 - } - } - } - } - } - # output the tree - proc OptTree {desc nl tl dl} { - set res ""; - foreach item $desc { - if {[OptIsCounter $item]} continue; - if {[OptIsPrg $item]} { - append res [OptTree $item $nl $tl $dl]; - } else { - set dv [OptTypeArgs $item]; - if {[OptState $item] != "header"} { - set dv "($dv)"; - } - append res [format "\n %-*s %-*s %-*s %s" \ - $nl [OptName $item] $tl [OptType $item] \ - $dl $dv [OptHelp $item]] - } - } - return $res; - } - -# Give nice usage string -proc ::tcl::OptError {prefix desc {header 0}} { - # determine length - if {$header} { - # add faked instruction - set h [list [OptNewInst header Var/FlagName Type Value Help]]; - lappend h [OptNewInst header ------------ ---- ----- ----]; - lappend h [OptNewInst header {( -help} "" "" {gives this help )}] - set desc [concat $h $desc] - } - OptLengths $desc nl tl dl - # actually output - return "$prefix[OptTree $desc $nl $tl $dl]" -} - - -################ General Utility functions ####################### - -# -# List utility functions -# Naming convention: -# "Lvarxxx" take the list VARiable name as argument -# "Lxxxx" take the list value as argument -# (which is not costly with Tcl8 objects system -# as it's still a reference and not a copy of the values) -# - -# Is that list empty ? -proc ::tcl::Lempty {list} { - expr {[llength $list]==0} -} - -# Gets the value of one leaf of a lists tree -proc ::tcl::Lget {list indexLst} { - if {[llength $indexLst] <= 1} { - return [lindex $list $indexLst]; - } - Lget [lindex $list [lindex $indexLst 0]] [lrange $indexLst 1 end]; -} -# Sets the value of one leaf of a lists tree -# (we use the version that does not create the elements because -# it would be even slower... needs to be written in C !) -# (nb: there is a non trivial recursive problem with indexes 0, -# which appear because there is no difference between a list -# of 1 element and 1 element alone : [list "a"] == "a" while -# it should be {a} and [listp a] should be 0 while [listp {a b}] would be 1 -# and [listp "a b"] maybe 0. listp does not exist either...) -proc ::tcl::Lvarset {listName indexLst newValue} { - upvar $listName list; - if {[llength $indexLst] <= 1} { - Lvarset1nc list $indexLst $newValue; - } else { - set idx [lindex $indexLst 0]; - set targetList [lindex $list $idx]; - # reduce refcount on targetList (not really usefull now, - # could be with optimizing compiler) -# Lvarset1 list $idx {}; - # recursively replace in targetList - Lvarset targetList [lrange $indexLst 1 end] $newValue; - # put updated sub list back in the tree - Lvarset1nc list $idx $targetList; - } -} -# Set one cell to a value, eventually create all the needed elements -# (on level-1 of lists) -variable emptyList {} -proc ::tcl::Lvarset1 {listName index newValue} { - upvar $listName list; - if {$index < 0} {return -code error "invalid negative index"} - set lg [llength $list]; - if {$index >= $lg} { - variable emptyList; - for {set i $lg} {$i<$index} {incr i} { - lappend list $emptyList; - } - lappend list $newValue; - } else { - set list [lreplace $list $index $index $newValue]; - } -} -# same as Lvarset1 but no bound checking / creation -proc ::tcl::Lvarset1nc {listName index newValue} { - upvar $listName list; - set list [lreplace $list $index $index $newValue]; -} -# Increments the value of one leaf of a lists tree -# (which must exists) -proc ::tcl::Lvarincr {listName indexLst {howMuch 1}} { - upvar $listName list; - if {[llength $indexLst] <= 1} { - Lvarincr1 list $indexLst $howMuch; - } else { - set idx [lindex $indexLst 0]; - set targetList [lindex $list $idx]; - # reduce refcount on targetList - Lvarset1nc list $idx {}; - # recursively replace in targetList - Lvarincr targetList [lrange $indexLst 1 end] $howMuch; - # put updated sub list back in the tree - Lvarset1nc list $idx $targetList; - } -} -# Increments the value of one cell of a list -proc ::tcl::Lvarincr1 {listName index {howMuch 1}} { - upvar $listName list; - set newValue [expr {[lindex $list $index]+$howMuch}]; - set list [lreplace $list $index $index $newValue]; - return $newValue; -} -# Removes the first element of a list -# and returns the new list value -proc ::tcl::Lvarpop1 {listName} { - upvar $listName list; - set list [lrange $list 1 end]; -} -# Same but returns the removed element -# (Like the tclX version) -proc ::tcl::Lvarpop {listName} { - upvar $listName list; - set el [lindex $list 0]; - set list [lrange $list 1 end]; - return $el; -} -# Assign list elements to variables and return the length of the list -proc ::tcl::Lassign {list args} { - # faster than direct blown foreach (which does not byte compile) - set i 0; - set lg [llength $list]; - foreach vname $args { - if {$i>=$lg} break - uplevel 1 [list ::set $vname [lindex $list $i]]; - incr i; - } - return $lg; -} - -# Misc utilities - -# Set the varname to value if value is greater than varname's current value -# or if varname is undefined -proc ::tcl::SetMax {varname value} { - upvar 1 $varname var - if {![info exists var] || $value > $var} { - set var $value - } -} - -# Set the varname to value if value is smaller than varname's current value -# or if varname is undefined -proc ::tcl::SetMin {varname value} { - upvar 1 $varname var - if {![info exists var] || $value < $var} { - set var $value - } -} - - - # everything loaded fine, lets create the test proc: - # OptCreateTestProc - # Don't need the create temp proc anymore: - # rename OptCreateTestProc {} -} diff --git a/library/opt/pkgIndex.tcl b/library/opt/pkgIndex.tcl deleted file mode 100644 index c5d3635..0000000 --- a/library/opt/pkgIndex.tcl +++ /dev/null @@ -1,12 +0,0 @@ -# Tcl package index file, version 1.1 -# This file is generated by the "pkg_mkIndex -direct" command -# and sourced either when an application starts up or -# by a "package unknown" script. It invokes the -# "package ifneeded" command to set up package-related -# information so that packages will be loaded automatically -# in response to "package require" commands. When this -# script is sourced, the variable $dir must contain the -# full path name of this file's directory. - -if {![package vsatisfies [package provide Tcl] 8.2]} {return} -package ifneeded opt 0.4.5 [list source [file join $dir optparse.tcl]] diff --git a/library/parray.tcl b/library/parray.tcl deleted file mode 100644 index e331d4d..0000000 --- a/library/parray.tcl +++ /dev/null @@ -1,30 +0,0 @@ -# parray: -# Print the contents of a global array on stdout. -# -# RCS: @(#) $Id: parray.tcl,v 1.4 2005/06/03 10:02:23 dkf Exp $ -# -# Copyright (c) 1991-1993 The Regents of the University of California. -# Copyright (c) 1994 Sun Microsystems, Inc. -# -# See the file "license.terms" for information on usage and redistribution -# of this file, and for a DISCLAIMER OF ALL WARRANTIES. -# - -proc parray {a {pattern *}} { - upvar 1 $a array - if {![array exists array]} { - error "\"$a\" isn't an array" - } - set maxl 0 - set names [lsort [array names array $pattern]] - foreach name $names { - if {[string length $name] > $maxl} { - set maxl [string length $name] - } - } - set maxl [expr {$maxl + [string length $a] + 2}] - foreach name $names { - set nameString [format %s(%s) $a $name] - puts stdout [format "%-*s = %s" $maxl $nameString $array($name)] - } -} diff --git a/library/safe.tcl b/library/safe.tcl deleted file mode 100644 index 501a552..0000000 --- a/library/safe.tcl +++ /dev/null @@ -1,1035 +0,0 @@ -# safe.tcl -- -# -# This file provide a safe loading/sourcing mechanism for safe interpreters. -# It implements a virtual path mecanism to hide the real pathnames from the -# slave. It runs in a master interpreter and sets up data structure and -# aliases that will be invoked when used from a slave interpreter. -# -# See the safe.n man page for details. -# -# Copyright (c) 1996-1997 Sun Microsystems, Inc. -# -# See the file "license.terms" for information on usage and redistribution -# of this file, and for a DISCLAIMER OF ALL WARRANTIES. -# -# RCS: @(#) $Id: safe.tcl,v 1.16.4.1 2008/06/25 16:42:05 andreas_kupries Exp $ - -# -# The implementation is based on namespaces. These naming conventions -# are followed: -# Private procs starts with uppercase. -# Public procs are exported and starts with lowercase -# - -# Needed utilities package -package require opt 0.4.1; - -# Create the safe namespace -namespace eval ::safe { - - # Exported API: - namespace export interpCreate interpInit interpConfigure interpDelete \ - interpAddToAccessPath interpFindInAccessPath setLogCmd - - #### - # - # Setup the arguments parsing - # - #### - - # Make sure that our temporary variable is local to this - # namespace. [Bug 981733] - variable temp - - # Share the descriptions - set temp [::tcl::OptKeyRegister { - {-accessPath -list {} "access path for the slave"} - {-noStatics "prevent loading of statically linked pkgs"} - {-statics true "loading of statically linked pkgs"} - {-nestedLoadOk "allow nested loading"} - {-nested false "nested loading"} - {-deleteHook -script {} "delete hook"} - }] - - # create case (slave is optional) - ::tcl::OptKeyRegister { - {?slave? -name {} "name of the slave (optional)"} - } ::safe::interpCreate - # adding the flags sub programs to the command program - # (relying on Opt's internal implementation details) - lappend ::tcl::OptDesc(::safe::interpCreate) $::tcl::OptDesc($temp) - - # init and configure (slave is needed) - ::tcl::OptKeyRegister { - {slave -name {} "name of the slave"} - } ::safe::interpIC - # adding the flags sub programs to the command program - # (relying on Opt's internal implementation details) - lappend ::tcl::OptDesc(::safe::interpIC) $::tcl::OptDesc($temp) - # temp not needed anymore - ::tcl::OptKeyDelete $temp - - - # Helper function to resolve the dual way of specifying staticsok - # (either by -noStatics or -statics 0) - proc InterpStatics {} { - foreach v {Args statics noStatics} { - upvar $v $v - } - set flag [::tcl::OptProcArgGiven -noStatics]; - if {$flag && (!$noStatics == !$statics) - && ([::tcl::OptProcArgGiven -statics])} { - return -code error\ - "conflicting values given for -statics and -noStatics" - } - if {$flag} { - return [expr {!$noStatics}] - } else { - return $statics - } - } - - # Helper function to resolve the dual way of specifying nested loading - # (either by -nestedLoadOk or -nested 1) - proc InterpNested {} { - foreach v {Args nested nestedLoadOk} { - upvar $v $v - } - set flag [::tcl::OptProcArgGiven -nestedLoadOk]; - # note that the test here is the opposite of the "InterpStatics" - # one (it is not -noNested... because of the wanted default value) - if {$flag && (!$nestedLoadOk != !$nested) - && ([::tcl::OptProcArgGiven -nested])} { - return -code error\ - "conflicting values given for -nested and -nestedLoadOk" - } - if {$flag} { - # another difference with "InterpStatics" - return $nestedLoadOk - } else { - return $nested - } - } - - #### - # - # API entry points that needs argument parsing : - # - #### - - - # Interface/entry point function and front end for "Create" - proc interpCreate {args} { - set Args [::tcl::OptKeyParse ::safe::interpCreate $args] - InterpCreate $slave $accessPath \ - [InterpStatics] [InterpNested] $deleteHook - } - - proc interpInit {args} { - set Args [::tcl::OptKeyParse ::safe::interpIC $args] - if {![::interp exists $slave]} { - return -code error "\"$slave\" is not an interpreter" - } - InterpInit $slave $accessPath \ - [InterpStatics] [InterpNested] $deleteHook; - } - - proc CheckInterp {slave} { - if {![IsInterp $slave]} { - return -code error \ - "\"$slave\" is not an interpreter managed by ::safe::" - } - } - - # Interface/entry point function and front end for "Configure" - # This code is awfully pedestrian because it would need - # more coupling and support between the way we store the - # configuration values in safe::interp's and the Opt package - # Obviously we would like an OptConfigure - # to avoid duplicating all this code everywhere. -> TODO - # (the app should share or access easily the program/value - # stored by opt) - # This is even more complicated by the boolean flags with no values - # that we had the bad idea to support for the sake of user simplicity - # in create/init but which makes life hard in configure... - # So this will be hopefully written and some integrated with opt1.0 - # (hopefully for tcl8.1 ?) - proc interpConfigure {args} { - switch [llength $args] { - 1 { - # If we have exactly 1 argument - # the semantic is to return all the current configuration - # We still call OptKeyParse though we know that "slave" - # is our given argument because it also checks - # for the "-help" option. - set Args [::tcl::OptKeyParse ::safe::interpIC $args] - CheckInterp $slave - set res {} - lappend res [list -accessPath [Set [PathListName $slave]]] - lappend res [list -statics [Set [StaticsOkName $slave]]] - lappend res [list -nested [Set [NestedOkName $slave]]] - lappend res [list -deleteHook [Set [DeleteHookName $slave]]] - join $res - } - 2 { - # If we have exactly 2 arguments - # the semantic is a "configure get" - ::tcl::Lassign $args slave arg - # get the flag sub program (we 'know' about Opt's internal - # representation of data) - set desc [lindex [::tcl::OptKeyGetDesc ::safe::interpIC] 2] - set hits [::tcl::OptHits desc $arg] - if {$hits > 1} { - return -code error [::tcl::OptAmbigous $desc $arg] - } elseif {$hits == 0} { - return -code error [::tcl::OptFlagUsage $desc $arg] - } - CheckInterp $slave - set item [::tcl::OptCurDesc $desc] - set name [::tcl::OptName $item] - switch -exact -- $name { - -accessPath { - return [list -accessPath [Set [PathListName $slave]]] - } - -statics { - return [list -statics [Set [StaticsOkName $slave]]] - } - -nested { - return [list -nested [Set [NestedOkName $slave]]] - } - -deleteHook { - return [list -deleteHook [Set [DeleteHookName $slave]]] - } - -noStatics { - # it is most probably a set in fact - # but we would need then to jump to the set part - # and it is not *sure* that it is a set action - # that the user want, so force it to use the - # unambigous -statics ?value? instead: - return -code error\ - "ambigous query (get or set -noStatics ?)\ - use -statics instead" - } - -nestedLoadOk { - return -code error\ - "ambigous query (get or set -nestedLoadOk ?)\ - use -nested instead" - } - default { - return -code error "unknown flag $name (bug)" - } - } - } - default { - # Otherwise we want to parse the arguments like init and create - # did - set Args [::tcl::OptKeyParse ::safe::interpIC $args] - CheckInterp $slave - # Get the current (and not the default) values of - # whatever has not been given: - if {![::tcl::OptProcArgGiven -accessPath]} { - set doreset 1 - set accessPath [Set [PathListName $slave]] - } else { - set doreset 0 - } - if {(![::tcl::OptProcArgGiven -statics]) \ - && (![::tcl::OptProcArgGiven -noStatics]) } { - set statics [Set [StaticsOkName $slave]] - } else { - set statics [InterpStatics] - } - if {([::tcl::OptProcArgGiven -nested]) \ - || ([::tcl::OptProcArgGiven -nestedLoadOk]) } { - set nested [InterpNested] - } else { - set nested [Set [NestedOkName $slave]] - } - if {![::tcl::OptProcArgGiven -deleteHook]} { - set deleteHook [Set [DeleteHookName $slave]] - } - # we can now reconfigure : - InterpSetConfig $slave $accessPath $statics $nested $deleteHook - # auto_reset the slave (to completly synch the new access_path) - if {$doreset} { - if {[catch {::interp eval $slave {auto_reset}} msg]} { - Log $slave "auto_reset failed: $msg" - } else { - Log $slave "successful auto_reset" NOTICE - } - } - } - } - } - - - #### - # - # Functions that actually implements the exported APIs - # - #### - - - # - # safe::InterpCreate : doing the real job - # - # This procedure creates a safe slave and initializes it with the - # safe base aliases. - # NB: slave name must be simple alphanumeric string, no spaces, - # no (), no {},... {because the state array is stored as part of the name} - # - # Returns the slave name. - # - # Optional Arguments : - # + slave name : if empty, generated name will be used - # + access_path: path list controlling where load/source can occur, - # if empty: the master auto_path will be used. - # + staticsok : flag, if 0 :no static package can be loaded (load {} Xxx) - # if 1 :static packages are ok. - # + nestedok: flag, if 0 :no loading to sub-sub interps (load xx xx sub) - # if 1 : multiple levels are ok. - - # use the full name and no indent so auto_mkIndex can find us - proc ::safe::InterpCreate { - slave - access_path - staticsok - nestedok - deletehook - } { - # Create the slave. - if {$slave ne ""} { - ::interp create -safe $slave - } else { - # empty argument: generate slave name - set slave [::interp create -safe] - } - Log $slave "Created" NOTICE - - # Initialize it. (returns slave name) - InterpInit $slave $access_path $staticsok $nestedok $deletehook - } - - - # - # InterpSetConfig (was setAccessPath) : - # Sets up slave virtual auto_path and corresponding structure - # within the master. Also sets the tcl_library in the slave - # to be the first directory in the path. - # Nb: If you change the path after the slave has been initialized - # you probably need to call "auto_reset" in the slave in order that it - # gets the right auto_index() array values. - - proc ::safe::InterpSetConfig {slave access_path staticsok\ - nestedok deletehook} { - - # determine and store the access path if empty - if {$access_path eq ""} { - set access_path [uplevel \#0 set auto_path] - # Make sure that tcl_library is in auto_path - # and at the first position (needed by setAccessPath) - set where [lsearch -exact $access_path [info library]] - if {$where == -1} { - # not found, add it. - set access_path [concat [list [info library]] $access_path] - Log $slave "tcl_library was not in auto_path,\ - added it to slave's access_path" NOTICE - } elseif {$where != 0} { - # not first, move it first - set access_path [concat [list [info library]]\ - [lreplace $access_path $where $where]] - Log $slave "tcl_libray was not in first in auto_path,\ - moved it to front of slave's access_path" NOTICE - - } - - # Add 1st level sub dirs (will searched by auto loading from tcl - # code in the slave using glob and thus fail, so we add them - # here so by default it works the same). - set access_path [AddSubDirs $access_path] - } - - Log $slave "Setting accessPath=($access_path) staticsok=$staticsok\ - nestedok=$nestedok deletehook=($deletehook)" NOTICE - - # clear old autopath if it existed - set nname [PathNumberName $slave] - if {[Exists $nname]} { - set n [Set $nname] - for {set i 0} {$i<$n} {incr i} { - Unset [PathToken $i $slave] - } - } - - # build new one - set slave_auto_path {} - set i 0 - foreach dir $access_path { - Set [PathToken $i $slave] $dir - lappend slave_auto_path "\$[PathToken $i]" - incr i - } - # Extend the access list with the paths used to look for Tcl - # Modules. We safe the virtual form separately as well, as - # syncing it with the slave has to be defered until the - # necessary commands are present for setup. - foreach dir [::tcl::tm::list] { - lappend access_path $dir - Set [PathToken $i $slave] $dir - lappend slave_auto_path "\$[PathToken $i]" - lappend slave_tm_path "\$[PathToken $i]" - incr i - } - Set [TmPathListName $slave] $slave_tm_path - Set $nname $i - Set [PathListName $slave] $access_path - Set [VirtualPathListName $slave] $slave_auto_path - - Set [StaticsOkName $slave] $staticsok - Set [NestedOkName $slave] $nestedok - Set [DeleteHookName $slave] $deletehook - - SyncAccessPath $slave - } - - # - # - # FindInAccessPath: - # Search for a real directory and returns its virtual Id - # (including the "$") -proc ::safe::interpFindInAccessPath {slave path} { - set access_path [GetAccessPath $slave] - set where [lsearch -exact $access_path $path] - if {$where == -1} { - return -code error "$path not found in access path $access_path" - } - return "\$[PathToken $where]" - } - - # - # addToAccessPath: - # add (if needed) a real directory to access path - # and return its virtual token (including the "$"). -proc ::safe::interpAddToAccessPath {slave path} { - # first check if the directory is already in there - if {![catch {interpFindInAccessPath $slave $path} res]} { - return $res - } - # new one, add it: - set nname [PathNumberName $slave] - set n [Set $nname] - Set [PathToken $n $slave] $path - - set token "\$[PathToken $n]" - - Lappend [VirtualPathListName $slave] $token - Lappend [PathListName $slave] $path - Set $nname [expr {$n+1}] - - SyncAccessPath $slave - - return $token - } - - # This procedure applies the initializations to an already existing - # interpreter. It is useful when you want to install the safe base - # aliases into a preexisting safe interpreter. - proc ::safe::InterpInit { - slave - access_path - staticsok - nestedok - deletehook - } { - - # Configure will generate an access_path when access_path is - # empty. - InterpSetConfig $slave $access_path $staticsok $nestedok $deletehook - - # These aliases let the slave load files to define new commands - - # NB we need to add [namespace current], aliases are always - # absolute paths. - ::interp alias $slave source {} [namespace current]::AliasSource $slave - ::interp alias $slave load {} [namespace current]::AliasLoad $slave - - # This alias lets the slave use the encoding names, convertfrom, - # convertto, and system, but not "encoding system " to set - # the system encoding. - - ::interp alias $slave encoding {} [namespace current]::AliasEncoding \ - $slave - - # Handling Tcl Modules, we need a restricted form of Glob. - ::interp alias $slave glob {} [namespace current]::AliasGlob \ - $slave - - # This alias lets the slave have access to a subset of the 'file' - # command functionality. - - AliasSubset $slave file file dir.* join root.* ext.* tail \ - path.* split - - # This alias interposes on the 'exit' command and cleanly terminates - # the slave. - - ::interp alias $slave exit {} [namespace current]::interpDelete $slave - - # The allowed slave variables already have been set - # by Tcl_MakeSafe(3) - - - # Source init.tcl and tm.tcl into the slave, to get auto_load - # and other procedures defined: - - if {[catch {::interp eval $slave \ - {source [file join $tcl_library init.tcl]}} msg]} { - Log $slave "can't source init.tcl ($msg)" - error "can't source init.tcl into slave $slave ($msg)" - } - - if {[catch {::interp eval $slave \ - {source [file join $tcl_library tm.tcl]}} msg]} { - Log $slave "can't source tm.tcl ($msg)" - error "can't source tm.tcl into slave $slave ($msg)" - } - - # Sync the paths used to search for Tcl modules. This can be - # done only now, after tm.tcl was loaded. - ::interp eval $slave [list ::tcl::tm::add {*}[Set [TmPathListName $slave]]] - - return $slave - } - - - # Add (only if needed, avoid duplicates) 1 level of - # sub directories to an existing path list. - # Also removes non directories from the returned list. - proc AddSubDirs {pathList} { - set res {} - foreach dir $pathList { - if {[file isdirectory $dir]} { - # check that we don't have it yet as a children - # of a previous dir - if {[lsearch -exact $res $dir]<0} { - lappend res $dir - } - foreach sub [glob -directory $dir -nocomplain *] { - if {([file isdirectory $sub]) \ - && ([lsearch -exact $res $sub]<0) } { - # new sub dir, add it ! - lappend res $sub - } - } - } - } - return $res - } - - # This procedure deletes a safe slave managed by Safe Tcl and - # cleans up associated state: - -proc ::safe::interpDelete {slave} { - - Log $slave "About to delete" NOTICE - - # If the slave has a cleanup hook registered, call it. - # check the existance because we might be called to delete an interp - # which has not been registered with us at all - set hookname [DeleteHookName $slave] - if {[Exists $hookname]} { - set hook [Set $hookname] - if {![::tcl::Lempty $hook]} { - # remove the hook now, otherwise if the hook - # calls us somehow, we'll loop - Unset $hookname - if {[catch {{*}$hook $slave} err]} { - Log $slave "Delete hook error ($err)" - } - } - } - - # Discard the global array of state associated with the slave, and - # delete the interpreter. - - set statename [InterpStateName $slave] - if {[Exists $statename]} { - Unset $statename - } - - # if we have been called twice, the interp might have been deleted - # already - if {[::interp exists $slave]} { - ::interp delete $slave - Log $slave "Deleted" NOTICE - } - - return - } - - # Set (or get) the loging mecanism - -proc ::safe::setLogCmd {args} { - variable Log - if {[llength $args] == 0} { - return $Log - } else { - if {[llength $args] == 1} { - set Log [lindex $args 0] - } else { - set Log $args - } - } -} - - # internal variable - variable Log {} - - # ------------------- END OF PUBLIC METHODS ------------ - - - # - # sets the slave auto_path to the master recorded value. - # also sets tcl_library to the first token of the virtual path. - # - proc SyncAccessPath {slave} { - set slave_auto_path [Set [VirtualPathListName $slave]] - ::interp eval $slave [list set auto_path $slave_auto_path] - Log $slave "auto_path in $slave has been set to $slave_auto_path"\ - NOTICE - ::interp eval $slave [list set tcl_library [lindex $slave_auto_path 0]] - } - - # base name for storing all the slave states - # the array variable name for slave foo is thus "Sfoo" - # and for sub slave {foo bar} "Sfoo bar" (spaces are handled - # ok everywhere (or should)) - # We add the S prefix to avoid that a slave interp called "Log" - # would smash our "Log" variable. - proc InterpStateName {slave} { - return "S$slave" - } - - # Check that the given slave is "one of us" - proc IsInterp {slave} { - expr {[Exists [InterpStateName $slave]] && [::interp exists $slave]} - } - - # returns the virtual token for directory number N - # if the slave argument is given, - # it will return the corresponding master global variable name - proc PathToken {n {slave ""}} { - if {$slave ne ""} { - return "[InterpStateName $slave](access_path,$n)" - } else { - # We need to have a ":" in the token string so - # [file join] on the mac won't turn it into a relative - # path. - return "p(:$n:)" - } - } - # returns the variable name of the complete path list - proc PathListName {slave} { - return "[InterpStateName $slave](access_path)" - } - # returns the variable name of the complete path list - proc VirtualPathListName {slave} { - return "[InterpStateName $slave](access_path_slave)" - } - # returns the variable name of the complete tm path list - proc TmPathListName {slave} { - return "[InterpStateName $slave](tm_path_slave)" - } - # returns the variable name of the number of items - proc PathNumberName {slave} { - return "[InterpStateName $slave](access_path,n)" - } - # returns the staticsok flag var name - proc StaticsOkName {slave} { - return "[InterpStateName $slave](staticsok)" - } - # returns the nestedok flag var name - proc NestedOkName {slave} { - return "[InterpStateName $slave](nestedok)" - } - # Run some code at the namespace toplevel - proc Toplevel {args} { - namespace eval [namespace current] $args - } - # set/get values - proc Set {args} { - Toplevel set {*}$args - } - # lappend on toplevel vars - proc Lappend {args} { - Toplevel lappend {*}$args - } - # unset a var/token (currently just an global level eval) - proc Unset {args} { - Toplevel unset {*}$args - } - # test existance - proc Exists {varname} { - Toplevel info exists $varname - } - # short cut for access path getting - proc GetAccessPath {slave} { - Set [PathListName $slave] - } - # short cut for statics ok flag getting - proc StaticsOk {slave} { - Set [StaticsOkName $slave] - } - # short cut for getting the multiples interps sub loading ok flag - proc NestedOk {slave} { - Set [NestedOkName $slave] - } - # interp deletion storing hook name - proc DeleteHookName {slave} { - return [InterpStateName $slave](cleanupHook) - } - - # - # translate virtual path into real path - # - proc TranslatePath {slave path} { - # somehow strip the namespaces 'functionality' out (the danger - # is that we would strip valid macintosh "../" queries... : - if {[string match "*::*" $path] || [string match "*..*" $path]} { - error "invalid characters in path $path" - } - set n [expr {[Set [PathNumberName $slave]]-1}] - for {} {$n>=0} {incr n -1} { - # fill the token virtual names with their real value - set [PathToken $n] [Set [PathToken $n $slave]] - } - # replaces the token by their value - subst -nobackslashes -nocommands $path - } - - - # Log eventually log an error - # to enable error logging, set Log to {puts stderr} for instance - proc Log {slave msg {type ERROR}} { - variable Log - if {[info exists Log] && [llength $Log]} { - {*}$Log "$type for slave $slave : $msg" - } - } - - - # file name control (limit access to files/ressources that should be - # a valid tcl source file) - proc CheckFileName {slave file} { - # This used to limit what can be sourced to ".tcl" and forbid files - # with more than 1 dot and longer than 14 chars, but I changed that - # for 8.4 as a safe interp has enough internal protection already - # to allow sourcing anything. - hobbs - - if {![file exists $file]} { - # don't tell the file path - error "no such file or directory" - } - - if {![file readable $file]} { - # don't tell the file path - error "not readable" - } - } - - # AliasGlob is the target of the "glob" alias in safe interpreters. - - proc AliasGlob {slave args} { - Log $slave "GLOB ! $args" NOTICE - set cmd {} - set at 0 - - set dir {} - set virtualdir {} - - while {$at < [llength $args]} { - switch -glob -- [set opt [lindex $args $at]] { - -nocomplain - - -join { lappend cmd $opt ; incr at } - -directory { - lappend cmd $opt ; incr at - set virtualdir [lindex $args $at] - - # get the real path from the virtual one. - if {[catch {set dir [TranslatePath $slave $virtualdir]} msg]} { - Log $slave $msg - return -code error "permission denied" - } - # check that the path is in the access path of that slave - if {[catch {DirInAccessPath $slave $dir} msg]} { - Log $slave $msg - return -code error "permission denied" - } - lappend cmd $dir ; incr at - } - pkgIndex.tcl { - # Oops, this is globbing a subdirectory in regular - # package search. That is not wanted. Abort, - # handler does catch already (because glob was not - # defined before). See package.tcl, lines 484ff in - # tclPkgUnknown. - error "unknown command glob" - } - -* { - Log $slave "Safe base rejecting glob option '$opt'" - error "Safe base rejecting glob option '$opt'" - } - default { - lappend cmd $opt ; incr at - } - } - } - - Log $slave "GLOB = $cmd" NOTICE - - if {[catch {::interp invokehidden $slave glob {*}$cmd} msg]} { - Log $slave $msg - return -code error "script error" - } - - Log $slave "GLOB @ $msg" NOTICE - - # Translate path back to what the slave should see. - set res {} - foreach p $msg { - regsub -- ^$dir $p $virtualdir p - lappend res $p - } - - Log $slave "GLOB @ $res" NOTICE - return $res - } - - # AliasSource is the target of the "source" alias in safe interpreters. - - proc AliasSource {slave args} { - - set argc [llength $args] - # Extended for handling of Tcl Modules to allow not only - # "source filename", but "source -encoding E filename" as - # well. - if {[lindex $args 0] eq "-encoding"} { - incr argc -2 - set encoding [lrange $args 0 1] - set at 2 - } else { - set at 0 - set encoding {} - } - if {$argc != 1} { - set msg "wrong # args: should be \"source ?-encoding E? fileName\"" - Log $slave "$msg ($args)" - return -code error $msg - } - set file [lindex $args $at] - - # get the real path from the virtual one. - if {[catch {set file [TranslatePath $slave $file]} msg]} { - Log $slave $msg - return -code error "permission denied" - } - - # check that the path is in the access path of that slave - if {[catch {FileInAccessPath $slave $file} msg]} { - Log $slave $msg - return -code error "permission denied" - } - - # do the checks on the filename : - if {[catch {CheckFileName $slave $file} msg]} { - Log $slave "$file:$msg" - return -code error $msg - } - - # passed all the tests , lets source it: - if {[catch {::interp invokehidden $slave source {*}$encoding $file} msg]} { - Log $slave $msg - return -code error "script error" - } - return $msg - } - - # AliasLoad is the target of the "load" alias in safe interpreters. - - proc AliasLoad {slave file args} { - - set argc [llength $args] - if {$argc > 2} { - set msg "load error: too many arguments" - Log $slave "$msg ($argc) {$file $args}" - return -code error $msg - } - - # package name (can be empty if file is not). - set package [lindex $args 0] - - # Determine where to load. load use a relative interp path - # and {} means self, so we can directly and safely use passed arg. - set target [lindex $args 1] - if {$target ne ""} { - # we will try to load into a sub sub interp - # check that we want to authorize that. - if {![NestedOk $slave]} { - Log $slave "loading to a sub interp (nestedok)\ - disabled (trying to load $package to $target)" - return -code error "permission denied (nested load)" - } - - } - - # Determine what kind of load is requested - if {$file eq ""} { - # static package loading - if {$package eq ""} { - set msg "load error: empty filename and no package name" - Log $slave $msg - return -code error $msg - } - if {![StaticsOk $slave]} { - Log $slave "static packages loading disabled\ - (trying to load $package to $target)" - return -code error "permission denied (static package)" - } - } else { - # file loading - - # get the real path from the virtual one. - if {[catch {set file [TranslatePath $slave $file]} msg]} { - Log $slave $msg - return -code error "permission denied" - } - - # check the translated path - if {[catch {FileInAccessPath $slave $file} msg]} { - Log $slave $msg - return -code error "permission denied (path)" - } - } - - if {[catch {::interp invokehidden\ - $slave load $file $package $target} msg]} { - Log $slave $msg - return -code error $msg - } - - return $msg - } - - # FileInAccessPath raises an error if the file is not found in - # the list of directories contained in the (master side recorded) slave's - # access path. - - # the security here relies on "file dirname" answering the proper - # result.... needs checking ? - proc FileInAccessPath {slave file} { - - set access_path [GetAccessPath $slave] - - if {[file isdirectory $file]} { - error "\"$file\": is a directory" - } - set parent [file dirname $file] - - # Normalize paths for comparison since lsearch knows nothing of - # potential pathname anomalies. - set norm_parent [file normalize $parent] - foreach path $access_path { - lappend norm_access_path [file normalize $path] - } - - if {[lsearch -exact $norm_access_path $norm_parent] == -1} { - error "\"$file\": not in access_path" - } - } - - proc DirInAccessPath {slave dir} { - set access_path [GetAccessPath $slave] - - if {[file isfile $dir]} { - error "\"$dir\": is a file" - } - - # Normalize paths for comparison since lsearch knows nothing of - # potential pathname anomalies. - set norm_dir [file normalize $dir] - foreach path $access_path { - lappend norm_access_path [file normalize $path] - } - - if {[lsearch -exact $norm_access_path $norm_dir] == -1} { - error "\"$dir\": not in access_path" - } - } - - # This procedure enables access from a safe interpreter to only a subset of - # the subcommands of a command: - - proc Subset {slave command okpat args} { - set subcommand [lindex $args 0] - if {[regexp $okpat $subcommand]} { - return [$command {*}$args] - } - set msg "not allowed to invoke subcommand $subcommand of $command" - Log $slave $msg - error $msg - } - - # This procedure installs an alias in a slave that invokes "safesubset" - # in the master to execute allowed subcommands. It precomputes the pattern - # of allowed subcommands; you can use wildcards in the pattern if you wish - # to allow subcommand abbreviation. - # - # Syntax is: AliasSubset slave alias target subcommand1 subcommand2... - - proc AliasSubset {slave alias target args} { - set pat ^(; set sep "" - foreach sub $args { - append pat $sep$sub - set sep | - } - append pat )\$ - ::interp alias $slave $alias {}\ - [namespace current]::Subset $slave $target $pat - } - - # AliasEncoding is the target of the "encoding" alias in safe interpreters. - - proc AliasEncoding {slave args} { - - set argc [llength $args] - - set okpat "^(name.*|convert.*)\$" - set subcommand [lindex $args 0] - - if {[regexp $okpat $subcommand]} { - return [::interp invokehidden $slave encoding {*}$args] - } - - if {[string first $subcommand system] == 0} { - if {$argc == 1} { - # passed all the tests , lets source it: - if {[catch {::interp invokehidden \ - $slave encoding system} msg]} { - Log $slave $msg - return -code error "script error" - } - } else { - set msg "wrong # args: should be \"encoding system\"" - Log $slave $msg - error $msg - } - } else { - set msg "wrong # args: should be \"encoding option ?arg ...?\"" - Log $slave $msg - error $msg - } - - return $msg - } - -} diff --git a/library/tclIndex b/library/tclIndex deleted file mode 100644 index 010616f..0000000 --- a/library/tclIndex +++ /dev/null @@ -1,87 +0,0 @@ -# Tcl autoload index file, version 2.0 -# This file is generated by the "auto_mkindex" command -# and sourced to set up indexing information for one or -# more commands. Typically each line is a command that -# sets an element in the auto_index array, where the -# element name is the name of a command and the value is -# a script that loads the command. - -set auto_index(auto_reset) [list source [file join $dir auto.tcl]] -set auto_index(tcl_findLibrary) [list source [file join $dir auto.tcl]] -set auto_index(auto_mkindex) [list source [file join $dir auto.tcl]] -set auto_index(auto_mkindex_old) [list source [file join $dir auto.tcl]] -set auto_index(::auto_mkindex_parser::init) [list source [file join $dir auto.tcl]] -set auto_index(::auto_mkindex_parser::cleanup) [list source [file join $dir auto.tcl]] -set auto_index(::auto_mkindex_parser::mkindex) [list source [file join $dir auto.tcl]] -set auto_index(::auto_mkindex_parser::hook) [list source [file join $dir auto.tcl]] -set auto_index(::auto_mkindex_parser::slavehook) [list source [file join $dir auto.tcl]] -set auto_index(::auto_mkindex_parser::command) [list source [file join $dir auto.tcl]] -set auto_index(::auto_mkindex_parser::commandInit) [list source [file join $dir auto.tcl]] -set auto_index(::auto_mkindex_parser::fullname) [list source [file join $dir auto.tcl]] -set auto_index(history) [list source [file join $dir history.tcl]] -set auto_index(::tcl::HistAdd) [list source [file join $dir history.tcl]] -set auto_index(::tcl::HistKeep) [list source [file join $dir history.tcl]] -set auto_index(::tcl::HistClear) [list source [file join $dir history.tcl]] -set auto_index(::tcl::HistInfo) [list source [file join $dir history.tcl]] -set auto_index(::tcl::HistRedo) [list source [file join $dir history.tcl]] -set auto_index(::tcl::HistIndex) [list source [file join $dir history.tcl]] -set auto_index(::tcl::HistEvent) [list source [file join $dir history.tcl]] -set auto_index(::tcl::HistChange) [list source [file join $dir history.tcl]] -set auto_index(pkg_mkIndex) [list source [file join $dir package.tcl]] -set auto_index(tclPkgSetup) [list source [file join $dir package.tcl]] -set auto_index(tclPkgUnknown) [list source [file join $dir package.tcl]] -set auto_index(::tcl::MacOSXPkgUnknown) [list source [file join $dir package.tcl]] -set auto_index(::pkg::create) [list source [file join $dir package.tcl]] -set auto_index(parray) [list source [file join $dir parray.tcl]] -set auto_index(::safe::InterpStatics) [list source [file join $dir safe.tcl]] -set auto_index(::safe::InterpNested) [list source [file join $dir safe.tcl]] -set auto_index(::safe::interpCreate) [list source [file join $dir safe.tcl]] -set auto_index(::safe::interpInit) [list source [file join $dir safe.tcl]] -set auto_index(::safe::CheckInterp) [list source [file join $dir safe.tcl]] -set auto_index(::safe::interpConfigure) [list source [file join $dir safe.tcl]] -set auto_index(::safe::InterpCreate) [list source [file join $dir safe.tcl]] -set auto_index(::safe::InterpSetConfig) [list source [file join $dir safe.tcl]] -set auto_index(::safe::interpFindInAccessPath) [list source [file join $dir safe.tcl]] -set auto_index(::safe::interpAddToAccessPath) [list source [file join $dir safe.tcl]] -set auto_index(::safe::InterpInit) [list source [file join $dir safe.tcl]] -set auto_index(::safe::AddSubDirs) [list source [file join $dir safe.tcl]] -set auto_index(::safe::interpDelete) [list source [file join $dir safe.tcl]] -set auto_index(::safe::setLogCmd) [list source [file join $dir safe.tcl]] -set auto_index(::safe::SyncAccessPath) [list source [file join $dir safe.tcl]] -set auto_index(::safe::InterpStateName) [list source [file join $dir safe.tcl]] -set auto_index(::safe::IsInterp) [list source [file join $dir safe.tcl]] -set auto_index(::safe::PathToken) [list source [file join $dir safe.tcl]] -set auto_index(::safe::PathListName) [list source [file join $dir safe.tcl]] -set auto_index(::safe::VirtualPathListName) [list source [file join $dir safe.tcl]] -set auto_index(::safe::PathNumberName) [list source [file join $dir safe.tcl]] -set auto_index(::safe::StaticsOkName) [list source [file join $dir safe.tcl]] -set auto_index(::safe::NestedOkName) [list source [file join $dir safe.tcl]] -set auto_index(::safe::Toplevel) [list source [file join $dir safe.tcl]] -set auto_index(::safe::Set) [list source [file join $dir safe.tcl]] -set auto_index(::safe::Lappend) [list source [file join $dir safe.tcl]] -set auto_index(::safe::Unset) [list source [file join $dir safe.tcl]] -set auto_index(::safe::Exists) [list source [file join $dir safe.tcl]] -set auto_index(::safe::GetAccessPath) [list source [file join $dir safe.tcl]] -set auto_index(::safe::StaticsOk) [list source [file join $dir safe.tcl]] -set auto_index(::safe::NestedOk) [list source [file join $dir safe.tcl]] -set auto_index(::safe::DeleteHookName) [list source [file join $dir safe.tcl]] -set auto_index(::safe::TranslatePath) [list source [file join $dir safe.tcl]] -set auto_index(::safe::Log) [list source [file join $dir safe.tcl]] -set auto_index(::safe::CheckFileName) [list source [file join $dir safe.tcl]] -set auto_index(::safe::AliasSource) [list source [file join $dir safe.tcl]] -set auto_index(::safe::AliasLoad) [list source [file join $dir safe.tcl]] -set auto_index(::safe::FileInAccessPath) [list source [file join $dir safe.tcl]] -set auto_index(::safe::Subset) [list source [file join $dir safe.tcl]] -set auto_index(::safe::AliasSubset) [list source [file join $dir safe.tcl]] -set auto_index(::safe::AliasEncoding) [list source [file join $dir safe.tcl]] -set auto_index(tcl_wordBreakAfter) [list source [file join $dir word.tcl]] -set auto_index(tcl_wordBreakBefore) [list source [file join $dir word.tcl]] -set auto_index(tcl_endOfWord) [list source [file join $dir word.tcl]] -set auto_index(tcl_startOfNextWord) [list source [file join $dir word.tcl]] -set auto_index(tcl_startOfPreviousWord) [list source [file join $dir word.tcl]] -set auto_index(::tcl::tm::add) [list source [file join $dir tm.tcl]] -set auto_index(::tcl::tm::remove) [list source [file join $dir tm.tcl]] -set auto_index(::tcl::tm::list) [list source [file join $dir tm.tcl]] -set auto_index(::tcl::tm::UnknownHandler) [list source [file join $dir tm.tcl]] -set auto_index(::tcl::tm::roots) [list source [file join $dir tm.tcl]] -set auto_index(::tcl::tm::path) [list source [file join $dir tm.tcl]] diff --git a/library/tcltest/tcltest.tcl b/library/tcltest/tcltest.tcl deleted file mode 100644 index 3d3e89b..0000000 --- a/library/tcltest/tcltest.tcl +++ /dev/null @@ -1,3387 +0,0 @@ -# tcltest.tcl -- -# -# This file contains support code for the Tcl test suite. It -# defines the tcltest namespace and finds and defines the output -# directory, constraints available, output and error channels, -# etc. used by Tcl tests. See the tcltest man page for more -# details. -# -# This design was based on the Tcl testing approach designed and -# initially implemented by Mary Ann May-Pumphrey of Sun -# Microsystems. -# -# Copyright (c) 1994-1997 Sun Microsystems, Inc. -# Copyright (c) 1998-1999 by Scriptics Corporation. -# Copyright (c) 2000 by Ajuba Solutions -# Contributions from Don Porter, NIST, 2002. (not subject to US copyright) -# All rights reserved. -# -# RCS: @(#) $Id: tcltest.tcl 31404 2008-09-25 05:41:59Z coke $ - -package require Tcl 8.5 ;# -verbose line uses [info frame] -namespace eval tcltest { - - # When the version number changes, be sure to update the pkgIndex.tcl file, - # and the install directory in the Makefiles. When the minor version - # changes (new feature) be sure to update the man page as well. - variable Version 2.3.0 - - # Compatibility support for dumb variables defined in tcltest 1 - # Do not use these. Call [package provide Tcl] and [info patchlevel] - # yourself. You don't need tcltest to wrap it for you. - variable version [package provide Tcl] - variable patchLevel [info patchlevel] - -##### Export the public tcltest procs; several categories - # - # Export the main functional commands that do useful things - namespace export cleanupTests loadTestedCommands makeDirectory \ - makeFile removeDirectory removeFile runAllTests test - - # Export configuration commands that control the functional commands - namespace export configure customMatch errorChannel interpreter \ - outputChannel testConstraint - - # Export commands that are duplication (candidates for deprecation) - namespace export bytestring ;# dups [encoding convertfrom identity] - namespace export debug ;# [configure -debug] - namespace export errorFile ;# [configure -errfile] - namespace export limitConstraints ;# [configure -limitconstraints] - namespace export loadFile ;# [configure -loadfile] - namespace export loadScript ;# [configure -load] - namespace export match ;# [configure -match] - namespace export matchFiles ;# [configure -file] - namespace export matchDirectories ;# [configure -relateddir] - namespace export normalizeMsg ;# application of [customMatch] - namespace export normalizePath ;# [file normalize] (8.4) - namespace export outputFile ;# [configure -outfile] - namespace export preserveCore ;# [configure -preservecore] - namespace export singleProcess ;# [configure -singleproc] - namespace export skip ;# [configure -skip] - namespace export skipFiles ;# [configure -notfile] - namespace export skipDirectories ;# [configure -asidefromdir] - namespace export temporaryDirectory ;# [configure -tmpdir] - namespace export testsDirectory ;# [configure -testdir] - namespace export verbose ;# [configure -verbose] - namespace export viewFile ;# binary encoding [read] - namespace export workingDirectory ;# [cd] [pwd] - - # Export deprecated commands for tcltest 1 compatibility - namespace export getMatchingFiles mainThread restoreState saveState \ - threadReap - - # tcltest::normalizePath -- - # - # This procedure resolves any symlinks in the path thus creating - # a path without internal redirection. It assumes that the - # incoming path is absolute. - # - # Arguments - # pathVar - name of variable containing path to modify. - # - # Results - # The path is modified in place. - # - # Side Effects: - # None. - # - proc normalizePath {pathVar} { - upvar $pathVar path - set oldpwd [pwd] - catch {cd $path} - set path [pwd] - cd $oldpwd - return $path - } - -##### Verification commands used to test values of variables and options - # - # Verification command that accepts everything - proc AcceptAll {value} { - return $value - } - - # Verification command that accepts valid Tcl lists - proc AcceptList { list } { - return [lrange $list 0 end] - } - - # Verification command that accepts a glob pattern - proc AcceptPattern { pattern } { - return [AcceptAll $pattern] - } - - # Verification command that accepts integers - proc AcceptInteger { level } { - return [incr level 0] - } - - # Verification command that accepts boolean values - proc AcceptBoolean { boolean } { - return [expr {$boolean && $boolean}] - } - - # Verification command that accepts (syntactically) valid Tcl scripts - proc AcceptScript { script } { - if {![info complete $script]} { - return -code error "invalid Tcl script: $script" - } - return $script - } - - # Verification command that accepts (converts to) absolute pathnames - proc AcceptAbsolutePath { path } { - return [file join [pwd] $path] - } - - # Verification command that accepts existing readable directories - proc AcceptReadable { path } { - if {![file readable $path]} { - return -code error "\"$path\" is not readable" - } - return $path - } - proc AcceptDirectory { directory } { - set directory [AcceptAbsolutePath $directory] - if {![file exists $directory]} { - return -code error "\"$directory\" does not exist" - } - if {![file isdir $directory]} { - return -code error "\"$directory\" is not a directory" - } - return [AcceptReadable $directory] - } - -##### Initialize internal arrays of tcltest, but only if the caller - # has not already pre-initialized them. This is done to support - # compatibility with older tests that directly access internals - # rather than go through command interfaces. - # - proc ArrayDefault {varName value} { - variable $varName - if {[array exists $varName]} { - return - } - if {[info exists $varName]} { - # Pre-initialized value is a scalar: destroy it! - unset $varName - } - array set $varName $value - } - - # save the original environment so that it can be restored later - ArrayDefault originalEnv [array get ::env] - - # initialize numTests array to keep track of the number of tests - # that pass, fail, and are skipped. - ArrayDefault numTests [list Total 0 Passed 0 Skipped 0 Failed 0] - - # createdNewFiles will store test files as indices and the list of - # files (that should not have been) left behind by the test files - # as values. - ArrayDefault createdNewFiles {} - - # initialize skippedBecause array to keep track of constraints that - # kept tests from running; a constraint name of "userSpecifiedSkip" - # means that the test appeared on the list of tests that matched the - # -skip value given to the flag; "userSpecifiedNonMatch" means that - # the test didn't match the argument given to the -match flag; both - # of these constraints are counted only if tcltest::debug is set to - # true. - ArrayDefault skippedBecause {} - - # initialize the testConstraints array to keep track of valid - # predefined constraints (see the explanation for the - # InitConstraints proc for more details). - ArrayDefault testConstraints {} - -##### Initialize internal variables of tcltest, but only if the caller - # has not already pre-initialized them. This is done to support - # compatibility with older tests that directly access internals - # rather than go through command interfaces. - # - proc Default {varName value {verify AcceptAll}} { - variable $varName - if {![info exists $varName]} { - variable $varName [$verify $value] - } else { - variable $varName [$verify [set $varName]] - } - } - - # Save any arguments that we might want to pass through to other - # programs. This is used by the -args flag. - # FINDUSER - Default parameters {} - - # Count the number of files tested (0 if runAllTests wasn't called). - # runAllTests will set testSingleFile to false, so stats will - # not be printed until runAllTests calls the cleanupTests proc. - # The currentFailure var stores the boolean value of whether the - # current test file has had any failures. The failFiles list - # stores the names of test files that had failures. - Default numTestFiles 0 AcceptInteger - Default testSingleFile true AcceptBoolean - Default currentFailure false AcceptBoolean - Default failFiles {} AcceptList - - # Tests should remove all files they create. The test suite will - # check the current working dir for files created by the tests. - # filesMade keeps track of such files created using the makeFile and - # makeDirectory procedures. filesExisted stores the names of - # pre-existing files. - # - # Note that $filesExisted lists only those files that exist in - # the original [temporaryDirectory]. - Default filesMade {} AcceptList - Default filesExisted {} AcceptList - proc FillFilesExisted {} { - variable filesExisted - - # Save the names of files that already exist in the scratch directory. - foreach file [glob -nocomplain -directory [temporaryDirectory] *] { - lappend filesExisted [file tail $file] - } - - # After successful filling, turn this into a no-op. - proc FillFilesExisted args {} - } - - # Kept only for compatibility - Default constraintsSpecified {} AcceptList - trace variable constraintsSpecified r {set ::tcltest::constraintsSpecified \ - [array names ::tcltest::testConstraints] ;# } - - # tests that use threads need to know which is the main thread - Default mainThread 1 - variable mainThread - if {[info commands thread::id] != {}} { - set mainThread [thread::id] - } elseif {[info commands testthread] != {}} { - set mainThread [testthread id] - } - - # Set workingDirectory to [pwd]. The default output directory for - # Tcl tests is the working directory. Whenever this value changes - # change to that directory. - variable workingDirectory - trace variable workingDirectory w \ - [namespace code {cd $workingDirectory ;#}] - - Default workingDirectory [pwd] AcceptAbsolutePath - proc workingDirectory { {dir ""} } { - variable workingDirectory - if {[llength [info level 0]] == 1} { - return $workingDirectory - } - set workingDirectory [AcceptAbsolutePath $dir] - } - - # Set the location of the execuatble - Default tcltest [info nameofexecutable] - trace variable tcltest w [namespace code {testConstraint stdio \ - [eval [ConstraintInitializer stdio]] ;#}] - - # save the platform information so it can be restored later - Default originalTclPlatform [array get ::tcl_platform] - - # If a core file exists, save its modification time. - if {[file exists [file join [workingDirectory] core]]} { - Default coreModTime \ - [file mtime [file join [workingDirectory] core]] - } - - # stdout and stderr buffers for use when we want to store them - Default outData {} - Default errData {} - - # keep track of test level for nested test commands - variable testLevel 0 - - # the variables and procs that existed when saveState was called are - # stored in a variable of the same name - Default saveState {} - - # Internationalization support -- used in [SetIso8859_1_Locale] and - # [RestoreLocale]. Those commands are used in cmdIL.test. - - if {![info exists [namespace current]::isoLocale]} { - variable isoLocale fr - switch -- $::tcl_platform(platform) { - "unix" { - - # Try some 'known' values for some platforms: - - switch -exact -- $::tcl_platform(os) { - "FreeBSD" { - set isoLocale fr_FR.ISO_8859-1 - } - HP-UX { - set isoLocale fr_FR.iso88591 - } - Linux - - IRIX { - set isoLocale fr - } - default { - - # Works on SunOS 4 and Solaris, and maybe - # others... Define it to something else on your - # system if you want to test those. - - set isoLocale iso_8859_1 - } - } - } - "windows" { - set isoLocale French - } - } - } - - variable ChannelsWeOpened; array set ChannelsWeOpened {} - # output goes to stdout by default - Default outputChannel stdout - proc outputChannel { {filename ""} } { - variable outputChannel - variable ChannelsWeOpened - - # This is very subtle and tricky, so let me try to explain. - # (Hopefully this longer comment will be clear when I come - # back in a few months, unlike its predecessor :) ) - # - # The [outputChannel] command (and underlying variable) have to - # be kept in sync with the [configure -outfile] configuration - # option ( and underlying variable Option(-outfile) ). This is - # accomplished with a write trace on Option(-outfile) that will - # update [outputChannel] whenver a new value is written. That - # much is easy. - # - # The trick is that in order to maintain compatibility with - # version 1 of tcltest, we must allow every configuration option - # to get its inital value from command line arguments. This is - # accomplished by setting initial read traces on all the - # configuration options to parse the command line option the first - # time they are read. These traces are cancelled whenever the - # program itself calls [configure]. - # - # OK, then so to support tcltest 1 compatibility, it seems we want - # to get the return from [outputFile] to trigger the read traces, - # just in case. - # - # BUT! A little known feature of Tcl variable traces is that - # traces are disabled during the handling of other traces. So, - # if we trigger read traces on Option(-outfile) and that triggers - # command line parsing which turns around and sets an initial - # value for Option(-outfile) -- -- the write trace that - # would keep [outputChannel] in sync with that new initial value - # would not fire! - # - # SO, finally, as a workaround, instead of triggering read traces - # by invoking [outputFile], we instead trigger the same set of - # read traces by invoking [debug]. Any command that reads a - # configuration option would do. [debug] is just a handy one. - # The end result is that we support tcltest 1 compatibility and - # keep outputChannel and -outfile in sync in all cases. - debug - - if {[llength [info level 0]] == 1} { - return $outputChannel - } - if {[info exists ChannelsWeOpened($outputChannel)]} { - close $outputChannel - unset ChannelsWeOpened($outputChannel) - } - switch -exact -- $filename { - stderr - - stdout { - set outputChannel $filename - } - default { - set outputChannel [open $filename a] - set ChannelsWeOpened($outputChannel) 1 - - # If we created the file in [temporaryDirectory], then - # [cleanupTests] will delete it, unless we claim it was - # already there. - set outdir [normalizePath [file dirname \ - [file join [pwd] $filename]]] - if {[string equal $outdir [temporaryDirectory]]} { - variable filesExisted - FillFilesExisted - set filename [file tail $filename] - if {[lsearch -exact $filesExisted $filename] == -1} { - lappend filesExisted $filename - } - } - } - } - return $outputChannel - } - - # errors go to stderr by default - Default errorChannel stderr - proc errorChannel { {filename ""} } { - variable errorChannel - variable ChannelsWeOpened - - # This is subtle and tricky. See the comment above in - # [outputChannel] for a detailed explanation. - debug - - if {[llength [info level 0]] == 1} { - return $errorChannel - } - if {[info exists ChannelsWeOpened($errorChannel)]} { - close $errorChannel - unset ChannelsWeOpened($errorChannel) - } - switch -exact -- $filename { - stderr - - stdout { - set errorChannel $filename - } - default { - set errorChannel [open $filename a] - set ChannelsWeOpened($errorChannel) 1 - - # If we created the file in [temporaryDirectory], then - # [cleanupTests] will delete it, unless we claim it was - # already there. - set outdir [normalizePath [file dirname \ - [file join [pwd] $filename]]] - if {[string equal $outdir [temporaryDirectory]]} { - variable filesExisted - FillFilesExisted - set filename [file tail $filename] - if {[lsearch -exact $filesExisted $filename] == -1} { - lappend filesExisted $filename - } - } - } - } - return $errorChannel - } - -##### Set up the configurable options - # - # The configurable options of the package - variable Option; array set Option {} - - # Usage strings for those options - variable Usage; array set Usage {} - - # Verification commands for those options - variable Verify; array set Verify {} - - # Initialize the default values of the configurable options that are - # historically associated with an exported variable. If that variable - # is already set, support compatibility by accepting its pre-set value. - # Use [trace] to establish ongoing connection between the deprecated - # exported variable and the modern option kept as a true internal var. - # Also set up usage string and value testing for the option. - proc Option {option value usage {verify AcceptAll} {varName {}}} { - variable Option - variable Verify - variable Usage - variable OptionControlledVariables - set Usage($option) $usage - set Verify($option) $verify - if {[catch {$verify $value} msg]} { - return -code error $msg - } else { - set Option($option) $msg - } - if {[string length $varName]} { - variable $varName - if {[info exists $varName]} { - if {[catch {$verify [set $varName]} msg]} { - return -code error $msg - } else { - set Option($option) $msg - } - unset $varName - } - namespace eval [namespace current] \ - [list upvar 0 Option($option) $varName] - # Workaround for Bug (now Feature Request) 572889. Grrrr.... - # Track all the variables tied to options - lappend OptionControlledVariables $varName - # Later, set auto-configure read traces on all - # of them, since a single trace on Option does not work. - proc $varName {{value {}}} [subst -nocommands { - if {[llength [info level 0]] == 2} { - Configure $option [set value] - } - return [Configure $option] - }] - } - } - - proc MatchingOption {option} { - variable Option - set match [array names Option $option*] - switch -- [llength $match] { - 0 { - set sorted [lsort [array names Option]] - set values [join [lrange $sorted 0 end-1] ", "] - append values ", or [lindex $sorted end]" - return -code error "unknown option $option: should be\ - one of $values" - } - 1 { - return [lindex $match 0] - } - default { - # Exact match trumps ambiguity - if {[lsearch -exact $match $option] >= 0} { - return $option - } - set values [join [lrange $match 0 end-1] ", "] - append values ", or [lindex $match end]" - return -code error "ambiguous option $option:\ - could match $values" - } - } - } - - proc EstablishAutoConfigureTraces {} { - variable OptionControlledVariables - foreach varName [concat $OptionControlledVariables Option] { - variable $varName - trace variable $varName r [namespace code {ProcessCmdLineArgs ;#}] - } - } - - proc RemoveAutoConfigureTraces {} { - variable OptionControlledVariables - foreach varName [concat $OptionControlledVariables Option] { - variable $varName - foreach pair [trace vinfo $varName] { - foreach {op cmd} $pair break - if {[string equal r $op] - && [string match *ProcessCmdLineArgs* $cmd]} { - trace vdelete $varName $op $cmd - } - } - } - # Once the traces are removed, this can become a no-op - proc RemoveAutoConfigureTraces {} {} - } - - proc Configure args { - variable Option - variable Verify - set n [llength $args] - if {$n == 0} { - return [lsort [array names Option]] - } - if {$n == 1} { - if {[catch {MatchingOption [lindex $args 0]} option]} { - return -code error $option - } - return $Option($option) - } - while {[llength $args] > 1} { - if {[catch {MatchingOption [lindex $args 0]} option]} { - return -code error $option - } - if {[catch {$Verify($option) [lindex $args 1]} value]} { - return -code error "invalid $option\ - value \"[lindex $args 1]\": $value" - } - set Option($option) $value - set args [lrange $args 2 end] - } - if {[llength $args]} { - if {[catch {MatchingOption [lindex $args 0]} option]} { - return -code error $option - } - return -code error "missing value for option $option" - } - } - proc configure args { - RemoveAutoConfigureTraces - set code [catch {eval Configure $args} msg] - return -code $code $msg - } - - proc AcceptVerbose { level } { - set level [AcceptList $level] - if {[llength $level] == 1} { - if {![regexp {^(pass|body|skip|start|error|line)$} $level]} { - # translate single characters abbreviations to expanded list - set level [string map {p pass b body s skip t start e error l line} \ - [split $level {}]] - } - } - set valid [list] - foreach v $level { - if {[regexp {^(pass|body|skip|start|error|line)$} $v]} { - lappend valid $v - } - } - return $valid - } - - proc IsVerbose {level} { - variable Option - return [expr {[lsearch -exact $Option(-verbose) $level] != -1}] - } - - # Default verbosity is to show bodies of failed tests - Option -verbose {body error} { - Takes any combination of the values 'p', 's', 'b', 't', 'e' and 'l'. - Test suite will display all passed tests if 'p' is specified, all - skipped tests if 's' is specified, the bodies of failed tests if - 'b' is specified, and when tests start if 't' is specified. - ErrorInfo is displayed if 'e' is specified. Source file line - information of failed tests is displayed if 'l' is specified. - } AcceptVerbose verbose - - # Match and skip patterns default to the empty list, except for - # matchFiles, which defaults to all .test files in the - # testsDirectory and matchDirectories, which defaults to all - # directories. - Option -match * { - Run all tests within the specified files that match one of the - list of glob patterns given. - } AcceptList match - - Option -skip {} { - Skip all tests within the specified tests (via -match) and files - that match one of the list of glob patterns given. - } AcceptList skip - - Option -file *.test { - Run tests in all test files that match the glob pattern given. - } AcceptPattern matchFiles - - # By default, skip files that appear to be SCCS lock files. - Option -notfile l.*.test { - Skip all test files that match the glob pattern given. - } AcceptPattern skipFiles - - Option -relateddir * { - Run tests in directories that match the glob pattern given. - } AcceptPattern matchDirectories - - Option -asidefromdir {} { - Skip tests in directories that match the glob pattern given. - } AcceptPattern skipDirectories - - # By default, don't save core files - Option -preservecore 0 { - If 2, save any core files produced during testing in the directory - specified by -tmpdir. If 1, notify the user if core files are - created. - } AcceptInteger preserveCore - - # debug output doesn't get printed by default; debug level 1 spits - # up only the tests that were skipped because they didn't match or - # were specifically skipped. A debug level of 2 would spit up the - # tcltest variables and flags provided; a debug level of 3 causes - # some additional output regarding operations of the test harness. - # The tcltest package currently implements only up to debug level 3. - Option -debug 0 { - Internal debug level - } AcceptInteger debug - - proc SetSelectedConstraints args { - variable Option - foreach c $Option(-constraints) { - testConstraint $c 1 - } - } - Option -constraints {} { - Do not skip the listed constraints listed in -constraints. - } AcceptList - trace variable Option(-constraints) w \ - [namespace code {SetSelectedConstraints ;#}] - - # Don't run only the "-constraint" specified tests by default - proc ClearUnselectedConstraints args { - variable Option - variable testConstraints - if {!$Option(-limitconstraints)} {return} - foreach c [array names testConstraints] { - if {[lsearch -exact $Option(-constraints) $c] == -1} { - testConstraint $c 0 - } - } - } - Option -limitconstraints false { - whether to run only tests with the constraints - } AcceptBoolean limitConstraints - trace variable Option(-limitconstraints) w \ - [namespace code {ClearUnselectedConstraints ;#}] - - # A test application has to know how to load the tested commands - # into the interpreter. - Option -load {} { - Specifies the script to load the tested commands. - } AcceptScript loadScript - - # Default is to run each test file in a separate process - Option -singleproc 0 { - whether to run all tests in one process - } AcceptBoolean singleProcess - - proc AcceptTemporaryDirectory { directory } { - set directory [AcceptAbsolutePath $directory] - if {![file exists $directory]} { - file mkdir $directory - } - set directory [AcceptDirectory $directory] - if {![file writable $directory]} { - if {[string equal [workingDirectory] $directory]} { - # Special exception: accept the default value - # even if the directory is not writable - return $directory - } - return -code error "\"$directory\" is not writeable" - } - return $directory - } - - # Directory where files should be created - Option -tmpdir [workingDirectory] { - Save temporary files in the specified directory. - } AcceptTemporaryDirectory temporaryDirectory - trace variable Option(-tmpdir) w \ - [namespace code {normalizePath Option(-tmpdir) ;#}] - - # Tests should not rely on the current working directory. - # Files that are part of the test suite should be accessed relative - # to [testsDirectory] - Option -testdir [workingDirectory] { - Search tests in the specified directory. - } AcceptDirectory testsDirectory - trace variable Option(-testdir) w \ - [namespace code {normalizePath Option(-testdir) ;#}] - - proc AcceptLoadFile { file } { - if {[string equal "" $file]} {return $file} - set file [file join [temporaryDirectory] $file] - return [AcceptReadable $file] - } - proc ReadLoadScript {args} { - variable Option - if {[string equal "" $Option(-loadfile)]} {return} - set tmp [open $Option(-loadfile) r] - loadScript [read $tmp] - close $tmp - } - Option -loadfile {} { - Read the script to load the tested commands from the specified file. - } AcceptLoadFile loadFile - trace variable Option(-loadfile) w [namespace code ReadLoadScript] - - proc AcceptOutFile { file } { - if {[string equal stderr $file]} {return $file} - if {[string equal stdout $file]} {return $file} - return [file join [temporaryDirectory] $file] - } - - # output goes to stdout by default - Option -outfile stdout { - Send output from test runs to the specified file. - } AcceptOutFile outputFile - trace variable Option(-outfile) w \ - [namespace code {outputChannel $Option(-outfile) ;#}] - - # errors go to stderr by default - Option -errfile stderr { - Send errors from test runs to the specified file. - } AcceptOutFile errorFile - trace variable Option(-errfile) w \ - [namespace code {errorChannel $Option(-errfile) ;#}] - -} - -##################################################################### - -# tcltest::Debug* -- -# -# Internal helper procedures to write out debug information -# dependent on the chosen level. A test shell may overide -# them, f.e. to redirect the output into a different -# channel, or even into a GUI. - -# tcltest::DebugPuts -- -# -# Prints the specified string if the current debug level is -# higher than the provided level argument. -# -# Arguments: -# level The lowest debug level triggering the output -# string The string to print out. -# -# Results: -# Prints the string. Nothing else is allowed. -# -# Side Effects: -# None. -# - -proc tcltest::DebugPuts {level string} { - variable debug - if {$debug >= $level} { - puts $string - } - return -} - -# tcltest::DebugPArray -- -# -# Prints the contents of the specified array if the current -# debug level is higher than the provided level argument -# -# Arguments: -# level The lowest debug level triggering the output -# arrayvar The name of the array to print out. -# -# Results: -# Prints the contents of the array. Nothing else is allowed. -# -# Side Effects: -# None. -# - -proc tcltest::DebugPArray {level arrayvar} { - variable debug - - if {$debug >= $level} { - catch {upvar $arrayvar $arrayvar} - parray $arrayvar - } - return -} - -# Define our own [parray] in ::tcltest that will inherit use of the [puts] -# defined in ::tcltest. NOTE: Ought to construct with [info args] and -# [info default], but can't be bothered now. If [parray] changes, then -# this will need changing too. -auto_load ::parray -proc tcltest::parray {a {pattern *}} [info body ::parray] - -# tcltest::DebugDo -- -# -# Executes the script if the current debug level is greater than -# the provided level argument -# -# Arguments: -# level The lowest debug level triggering the execution. -# script The tcl script executed upon a debug level high enough. -# -# Results: -# Arbitrary side effects, dependent on the executed script. -# -# Side Effects: -# None. -# - -proc tcltest::DebugDo {level script} { - variable debug - - if {$debug >= $level} { - uplevel 1 $script - } - return -} - -##################################################################### - -proc tcltest::Warn {msg} { - puts [outputChannel] "WARNING: $msg" -} - -# tcltest::mainThread -# -# Accessor command for tcltest variable mainThread. -# -proc tcltest::mainThread { {new ""} } { - variable mainThread - if {[llength [info level 0]] == 1} { - return $mainThread - } - set mainThread $new -} - -# tcltest::testConstraint -- -# -# sets a test constraint to a value; to do multiple constraints, -# call this proc multiple times. also returns the value of the -# named constraint if no value was supplied. -# -# Arguments: -# constraint - name of the constraint -# value - new value for constraint (should be boolean) - if not -# supplied, this is a query -# -# Results: -# content of tcltest::testConstraints($constraint) -# -# Side effects: -# none - -proc tcltest::testConstraint {constraint {value ""}} { - variable testConstraints - variable Option - DebugPuts 3 "entering testConstraint $constraint $value" - if {[llength [info level 0]] == 2} { - return $testConstraints($constraint) - } - # Check for boolean values - if {[catch {expr {$value && $value}} msg]} { - return -code error $msg - } - if {[limitConstraints] - && [lsearch -exact $Option(-constraints) $constraint] == -1} { - set value 0 - } - set testConstraints($constraint) $value -} - -# tcltest::interpreter -- -# -# the interpreter name stored in tcltest::tcltest -# -# Arguments: -# executable name -# -# Results: -# content of tcltest::tcltest -# -# Side effects: -# None. - -proc tcltest::interpreter { {interp ""} } { - variable tcltest - if {[llength [info level 0]] == 1} { - return $tcltest - } - if {[string equal {} $interp]} { - set tcltest {} - } else { - set tcltest $interp - } -} - -##################################################################### - -# tcltest::AddToSkippedBecause -- -# -# Increments the variable used to track how many tests were -# skipped because of a particular constraint. -# -# Arguments: -# constraint The name of the constraint to be modified -# -# Results: -# Modifies tcltest::skippedBecause; sets the variable to 1 if -# didn't previously exist - otherwise, it just increments it. -# -# Side effects: -# None. - -proc tcltest::AddToSkippedBecause { constraint {value 1}} { - # add the constraint to the list of constraints that kept tests - # from running - variable skippedBecause - - if {[info exists skippedBecause($constraint)]} { - incr skippedBecause($constraint) $value - } else { - set skippedBecause($constraint) $value - } - return -} - -# tcltest::PrintError -- -# -# Prints errors to tcltest::errorChannel and then flushes that -# channel, making sure that all messages are < 80 characters per -# line. -# -# Arguments: -# errorMsg String containing the error to be printed -# -# Results: -# None. -# -# Side effects: -# None. - -proc tcltest::PrintError {errorMsg} { - set InitialMessage "Error: " - set InitialMsgLen [string length $InitialMessage] - puts -nonewline [errorChannel] $InitialMessage - - # Keep track of where the end of the string is. - set endingIndex [string length $errorMsg] - - if {$endingIndex < (80 - $InitialMsgLen)} { - puts [errorChannel] $errorMsg - } else { - # Print up to 80 characters on the first line, including the - # InitialMessage. - set beginningIndex [string last " " [string range $errorMsg 0 \ - [expr {80 - $InitialMsgLen}]]] - puts [errorChannel] [string range $errorMsg 0 $beginningIndex] - - while {![string equal end $beginningIndex]} { - puts -nonewline [errorChannel] \ - [string repeat " " $InitialMsgLen] - if {($endingIndex - $beginningIndex) - < (80 - $InitialMsgLen)} { - puts [errorChannel] [string trim \ - [string range $errorMsg $beginningIndex end]] - break - } else { - set newEndingIndex [expr {[string last " " \ - [string range $errorMsg $beginningIndex \ - [expr {$beginningIndex - + (80 - $InitialMsgLen)}] - ]] + $beginningIndex}] - if {($newEndingIndex <= 0) - || ($newEndingIndex <= $beginningIndex)} { - set newEndingIndex end - } - puts [errorChannel] [string trim \ - [string range $errorMsg \ - $beginningIndex $newEndingIndex]] - set beginningIndex $newEndingIndex - } - } - } - flush [errorChannel] - return -} - -# tcltest::SafeFetch -- -# -# The following trace procedure makes it so that we can safely -# refer to non-existent members of the testConstraints array -# without causing an error. Instead, reading a non-existent -# member will return 0. This is necessary because tests are -# allowed to use constraint "X" without ensuring that -# testConstraints("X") is defined. -# -# Arguments: -# n1 - name of the array (testConstraints) -# n2 - array key value (constraint name) -# op - operation performed on testConstraints (generally r) -# -# Results: -# none -# -# Side effects: -# sets testConstraints($n2) to 0 if it's referenced but never -# before used - -proc tcltest::SafeFetch {n1 n2 op} { - variable testConstraints - DebugPuts 3 "entering SafeFetch $n1 $n2 $op" - if {[string equal {} $n2]} {return} - if {![info exists testConstraints($n2)]} { - if {[catch {testConstraint $n2 [eval [ConstraintInitializer $n2]]}]} { - testConstraint $n2 0 - } - } -} - -# tcltest::ConstraintInitializer -- -# -# Get or set a script that when evaluated in the tcltest namespace -# will return a boolean value with which to initialize the -# associated constraint. -# -# Arguments: -# constraint - name of the constraint initialized by the script -# script - the initializer script -# -# Results -# boolean value of the constraint - enabled or disabled -# -# Side effects: -# Constraint is initialized for future reference by [test] -proc tcltest::ConstraintInitializer {constraint {script ""}} { - variable ConstraintInitializer - DebugPuts 3 "entering ConstraintInitializer $constraint $script" - if {[llength [info level 0]] == 2} { - return $ConstraintInitializer($constraint) - } - # Check for boolean values - if {![info complete $script]} { - return -code error "ConstraintInitializer must be complete script" - } - set ConstraintInitializer($constraint) $script -} - -# tcltest::InitConstraints -- -# -# Call all registered constraint initializers to force initialization -# of all known constraints. -# See the tcltest man page for the list of built-in constraints defined -# in this procedure. -# -# Arguments: -# none -# -# Results: -# The testConstraints array is reset to have an index for each -# built-in test constraint. -# -# Side Effects: -# None. -# - -proc tcltest::InitConstraints {} { - variable ConstraintInitializer - initConstraintsHook - foreach constraint [array names ConstraintInitializer] { - testConstraint $constraint - } -} - -proc tcltest::DefineConstraintInitializers {} { - ConstraintInitializer singleTestInterp {singleProcess} - - # All the 'pc' constraints are here for backward compatibility and - # are not documented. They have been replaced with equivalent 'win' - # constraints. - - ConstraintInitializer unixOnly \ - {string equal $::tcl_platform(platform) unix} - ConstraintInitializer macOnly \ - {string equal $::tcl_platform(platform) macintosh} - ConstraintInitializer pcOnly \ - {string equal $::tcl_platform(platform) windows} - ConstraintInitializer winOnly \ - {string equal $::tcl_platform(platform) windows} - - ConstraintInitializer unix {testConstraint unixOnly} - ConstraintInitializer mac {testConstraint macOnly} - ConstraintInitializer pc {testConstraint pcOnly} - ConstraintInitializer win {testConstraint winOnly} - - ConstraintInitializer unixOrPc \ - {expr {[testConstraint unix] || [testConstraint pc]}} - ConstraintInitializer macOrPc \ - {expr {[testConstraint mac] || [testConstraint pc]}} - ConstraintInitializer unixOrWin \ - {expr {[testConstraint unix] || [testConstraint win]}} - ConstraintInitializer macOrWin \ - {expr {[testConstraint mac] || [testConstraint win]}} - ConstraintInitializer macOrUnix \ - {expr {[testConstraint mac] || [testConstraint unix]}} - - ConstraintInitializer nt {string equal $::tcl_platform(os) "Windows NT"} - ConstraintInitializer 95 {string equal $::tcl_platform(os) "Windows 95"} - ConstraintInitializer 98 {string equal $::tcl_platform(os) "Windows 98"} - - # The following Constraints switches are used to mark tests that - # should work, but have been temporarily disabled on certain - # platforms because they don't and we haven't gotten around to - # fixing the underlying problem. - - ConstraintInitializer tempNotPc {expr {![testConstraint pc]}} - ConstraintInitializer tempNotWin {expr {![testConstraint win]}} - ConstraintInitializer tempNotMac {expr {![testConstraint mac]}} - ConstraintInitializer tempNotUnix {expr {![testConstraint unix]}} - - # The following Constraints switches are used to mark tests that - # crash on certain platforms, so that they can be reactivated again - # when the underlying problem is fixed. - - ConstraintInitializer pcCrash {expr {![testConstraint pc]}} - ConstraintInitializer winCrash {expr {![testConstraint win]}} - ConstraintInitializer macCrash {expr {![testConstraint mac]}} - ConstraintInitializer unixCrash {expr {![testConstraint unix]}} - - # Skip empty tests - - ConstraintInitializer emptyTest {format 0} - - # By default, tests that expose known bugs are skipped. - - ConstraintInitializer knownBug {format 0} - - # By default, non-portable tests are skipped. - - ConstraintInitializer nonPortable {format 0} - - # Some tests require user interaction. - - ConstraintInitializer userInteraction {format 0} - - # Some tests must be skipped if the interpreter is not in - # interactive mode - - ConstraintInitializer interactive \ - {expr {[info exists ::tcl_interactive] && $::tcl_interactive}} - - # Some tests can only be run if the installation came from a CD - # image instead of a web image. Some tests must be skipped if you - # are running as root on Unix. Other tests can only be run if you - # are running as root on Unix. - - ConstraintInitializer root {expr \ - {[string equal unix $::tcl_platform(platform)] - && ([string equal root $::tcl_platform(user)] - || [string equal "" $::tcl_platform(user)])}} - ConstraintInitializer notRoot {expr {![testConstraint root]}} - - # Set nonBlockFiles constraint: 1 means this platform supports - # setting files into nonblocking mode. - - ConstraintInitializer nonBlockFiles { - set code [expr {[catch {set f [open defs r]}] - || [catch {fconfigure $f -blocking off}]}] - catch {close $f} - set code - } - - # Set asyncPipeClose constraint: 1 means this platform supports - # async flush and async close on a pipe. - # - # Test for SCO Unix - cannot run async flushing tests because a - # potential problem with select is apparently interfering. - # (Mark Diekhans). - - ConstraintInitializer asyncPipeClose {expr { - !([string equal unix $::tcl_platform(platform)] - && ([catch {exec uname -X | fgrep {Release = 3.2v}}] == 0))}} - - # Test to see if we have a broken version of sprintf with respect - # to the "e" format of floating-point numbers. - - ConstraintInitializer eformat {string equal [format %g 5e-5] 5e-05} - - # Test to see if execed commands such as cat, echo, rm and so forth - # are present on this machine. - - ConstraintInitializer unixExecs { - set code 1 - if {[string equal macintosh $::tcl_platform(platform)]} { - set code 0 - } - if {[string equal windows $::tcl_platform(platform)]} { - if {[catch { - set file _tcl_test_remove_me.txt - makeFile {hello} $file - }]} { - set code 0 - } elseif { - [catch {exec cat $file}] || - [catch {exec echo hello}] || - [catch {exec sh -c echo hello}] || - [catch {exec wc $file}] || - [catch {exec sleep 1}] || - [catch {exec echo abc > $file}] || - [catch {exec chmod 644 $file}] || - [catch {exec rm $file}] || - [llength [auto_execok mkdir]] == 0 || - [llength [auto_execok fgrep]] == 0 || - [llength [auto_execok grep]] == 0 || - [llength [auto_execok ps]] == 0 - } { - set code 0 - } - removeFile $file - } - set code - } - - ConstraintInitializer stdio { - set code 0 - if {![catch {set f [open "|[list [interpreter]]" w]}]} { - if {![catch {puts $f exit}]} { - if {![catch {close $f}]} { - set code 1 - } - } - } - set code - } - - # Deliberately call socket with the wrong number of arguments. The - # error message you get will indicate whether sockets are available - # on this system. - - ConstraintInitializer socket { - catch {socket} msg - string compare $msg "sockets are not available on this system" - } - - # Check for internationalization - ConstraintInitializer hasIsoLocale { - if {[llength [info commands testlocale]] == 0} { - set code 0 - } else { - set code [string length [SetIso8859_1_Locale]] - RestoreLocale - } - set code - } - -} -##################################################################### - -# Usage and command line arguments processing. - -# tcltest::PrintUsageInfo -# -# Prints out the usage information for package tcltest. This can -# be customized with the redefinition of [PrintUsageInfoHook]. -# -# Arguments: -# none -# -# Results: -# none -# -# Side Effects: -# none -proc tcltest::PrintUsageInfo {} { - puts [Usage] - PrintUsageInfoHook -} - -proc tcltest::Usage { {option ""} } { - variable Usage - variable Verify - if {[llength [info level 0]] == 1} { - set msg "Usage: [file tail [info nameofexecutable]] script " - append msg "?-help? ?flag value? ... \n" - append msg "Available flags (and valid input values) are:" - - set max 0 - set allOpts [concat -help [Configure]] - foreach opt $allOpts { - set foo [Usage $opt] - foreach [list x type($opt) usage($opt)] $foo break - set line($opt) " $opt $type($opt) " - set length($opt) [string length $line($opt)] - if {$length($opt) > $max} {set max $length($opt)} - } - set rest [expr {72 - $max}] - foreach opt $allOpts { - append msg \n$line($opt) - append msg [string repeat " " [expr {$max - $length($opt)}]] - set u [string trim $usage($opt)] - catch {append u " (default: \[[Configure $opt]])"} - regsub -all {\s*\n\s*} $u " " u - while {[string length $u] > $rest} { - set break [string wordstart $u $rest] - if {$break == 0} { - set break [string wordend $u 0] - } - append msg [string range $u 0 [expr {$break - 1}]] - set u [string trim [string range $u $break end]] - append msg \n[string repeat " " $max] - } - append msg $u - } - return $msg\n - } elseif {[string equal -help $option]} { - return [list -help "" "Display this usage information."] - } else { - set type [lindex [info args $Verify($option)] 0] - return [list $option $type $Usage($option)] - } -} - -# tcltest::ProcessFlags -- -# -# process command line arguments supplied in the flagArray - this -# is called by processCmdLineArgs. Modifies tcltest variables -# according to the content of the flagArray. -# -# Arguments: -# flagArray - array containing name/value pairs of flags -# -# Results: -# sets tcltest variables according to their values as defined by -# flagArray -# -# Side effects: -# None. - -proc tcltest::ProcessFlags {flagArray} { - # Process -help first - if {[lsearch -exact $flagArray {-help}] != -1} { - PrintUsageInfo - exit 1 - } - - if {[llength $flagArray] == 0} { - RemoveAutoConfigureTraces - } else { - set args $flagArray - while {[llength $args]>1 && [catch {eval configure $args} msg]} { - - # Something went wrong parsing $args for tcltest options - # Check whether the problem is "unknown option" - if {[regexp {^unknown option (\S+):} $msg -> option]} { - # Could be this is an option the Hook knows about - set moreOptions [processCmdLineArgsAddFlagsHook] - if {[lsearch -exact $moreOptions $option] == -1} { - # Nope. Report the error, including additional options, - # but keep going - if {[llength $moreOptions]} { - append msg ", " - append msg [join [lrange $moreOptions 0 end-1] ", "] - append msg "or [lindex $moreOptions end]" - } - Warn $msg - } - } else { - # error is something other than "unknown option" - # notify user of the error; and exit - puts [errorChannel] $msg - exit 1 - } - - # To recover, find that unknown option and remove up to it. - # then retry - while {![string equal [lindex $args 0] $option]} { - set args [lrange $args 2 end] - } - set args [lrange $args 2 end] - } - if {[llength $args] == 1} { - puts [errorChannel] \ - "missing value for option [lindex $args 0]" - exit 1 - } - } - - # Call the hook - catch { - array set flag $flagArray - processCmdLineArgsHook [array get flag] - } - return -} - -# tcltest::ProcessCmdLineArgs -- -# -# This procedure must be run after constraint initialization is -# set up (by [DefineConstraintInitializers]) because some constraints -# can be overridden. -# -# Perform configuration according to the command-line options. -# -# Arguments: -# none -# -# Results: -# Sets the above-named variables in the tcltest namespace. -# -# Side Effects: -# None. -# - -proc tcltest::ProcessCmdLineArgs {} { - variable originalEnv - variable testConstraints - - # The "argv" var doesn't exist in some cases, so use {}. - if {![info exists ::argv]} { - ProcessFlags {} - } else { - ProcessFlags $::argv - } - - # Spit out everything you know if we're at a debug level 2 or - # greater - DebugPuts 2 "Flags passed into tcltest:" - if {[info exists ::env(TCLTEST_OPTIONS)]} { - DebugPuts 2 \ - " ::env(TCLTEST_OPTIONS): $::env(TCLTEST_OPTIONS)" - } - if {[info exists ::argv]} { - DebugPuts 2 " argv: $::argv" - } - DebugPuts 2 "tcltest::debug = [debug]" - DebugPuts 2 "tcltest::testsDirectory = [testsDirectory]" - DebugPuts 2 "tcltest::workingDirectory = [workingDirectory]" - DebugPuts 2 "tcltest::temporaryDirectory = [temporaryDirectory]" - DebugPuts 2 "tcltest::outputChannel = [outputChannel]" - DebugPuts 2 "tcltest::errorChannel = [errorChannel]" - DebugPuts 2 "Original environment (tcltest::originalEnv):" - DebugPArray 2 originalEnv - DebugPuts 2 "Constraints:" - DebugPArray 2 testConstraints -} - -##################################################################### - -# Code to run the tests goes here. - -# tcltest::TestPuts -- -# -# Used to redefine puts in test environment. Stores whatever goes -# out on stdout in tcltest::outData and stderr in errData before -# sending it on to the regular puts. -# -# Arguments: -# same as standard puts -# -# Results: -# none -# -# Side effects: -# Intercepts puts; data that would otherwise go to stdout, stderr, -# or file channels specified in outputChannel and errorChannel -# does not get sent to the normal puts function. -namespace eval tcltest::Replace { - namespace export puts -} -proc tcltest::Replace::puts {args} { - variable [namespace parent]::outData - variable [namespace parent]::errData - switch [llength $args] { - 1 { - # Only the string to be printed is specified - append outData [lindex $args 0]\n - return - # return [Puts [lindex $args 0]] - } - 2 { - # Either -nonewline or channelId has been specified - if {[string equal -nonewline [lindex $args 0]]} { - append outData [lindex $args end] - return - # return [Puts -nonewline [lindex $args end]] - } else { - set channel [lindex $args 0] - set newline \n - } - } - 3 { - if {[string equal -nonewline [lindex $args 0]]} { - # Both -nonewline and channelId are specified, unless - # it's an error. -nonewline is supposed to be argv[0]. - set channel [lindex $args 1] - set newline "" - } - } - } - - if {[info exists channel]} { - if {[string equal $channel [[namespace parent]::outputChannel]] - || [string equal $channel stdout]} { - append outData [lindex $args end]$newline - return - } elseif {[string equal $channel [[namespace parent]::errorChannel]] - || [string equal $channel stderr]} { - append errData [lindex $args end]$newline - return - } - } - - # If we haven't returned by now, we don't know how to handle the - # input. Let puts handle it. - return [eval Puts $args] -} - -# tcltest::Eval -- -# -# Evaluate the script in the test environment. If ignoreOutput is -# false, store data sent to stderr and stdout in outData and -# errData. Otherwise, ignore this output altogether. -# -# Arguments: -# script Script to evaluate -# ?ignoreOutput? Indicates whether or not to ignore output -# sent to stdout & stderr -# -# Results: -# result from running the script -# -# Side effects: -# Empties the contents of outData and errData before running a -# test if ignoreOutput is set to 0. - -proc tcltest::Eval {script {ignoreOutput 1}} { - variable outData - variable errData - DebugPuts 3 "[lindex [info level 0] 0] called" - if {!$ignoreOutput} { - set outData {} - set errData {} - rename ::puts [namespace current]::Replace::Puts - namespace eval :: [list namespace import [namespace origin Replace::puts]] - namespace import Replace::puts - } - set result [uplevel 1 $script] - if {!$ignoreOutput} { - namespace forget puts - namespace eval :: namespace forget puts - rename [namespace current]::Replace::Puts ::puts - } - return $result -} - -# tcltest::CompareStrings -- -# -# compares the expected answer to the actual answer, depending on -# the mode provided. Mode determines whether a regexp, exact, -# glob or custom comparison is done. -# -# Arguments: -# actual - string containing the actual result -# expected - pattern to be matched against -# mode - type of comparison to be done -# -# Results: -# result of the match -# -# Side effects: -# None. - -proc tcltest::CompareStrings {actual expected mode} { - variable CustomMatch - if {![info exists CustomMatch($mode)]} { - return -code error "No matching command registered for `-match $mode'" - } - set match [namespace eval :: $CustomMatch($mode) [list $expected $actual]] - if {[catch {expr {$match && $match}} result]} { - return -code error "Invalid result from `-match $mode' command: $result" - } - return $match -} - -# tcltest::customMatch -- -# -# registers a command to be called when a particular type of -# matching is required. -# -# Arguments: -# nickname - Keyword for the type of matching -# cmd - Incomplete command that implements that type of matching -# when completed with expected string and actual string -# and then evaluated. -# -# Results: -# None. -# -# Side effects: -# Sets the variable tcltest::CustomMatch - -proc tcltest::customMatch {mode script} { - variable CustomMatch - if {![info complete $script]} { - return -code error \ - "invalid customMatch script; can't evaluate after completion" - } - set CustomMatch($mode) $script -} - -# tcltest::SubstArguments list -# -# This helper function takes in a list of words, then perform a -# substitution on the list as though each word in the list is a separate -# argument to the Tcl function. For example, if this function is -# invoked as: -# -# SubstArguments {$a {$a}} -# -# Then it is as though the function is invoked as: -# -# SubstArguments $a {$a} -# -# This code is adapted from Paul Duffin's function "SplitIntoWords". -# The original function can be found on: -# -# http://purl.org/thecliff/tcl/wiki/858.html -# -# Results: -# a list containing the result of the substitution -# -# Exceptions: -# An error may occur if the list containing unbalanced quote or -# unknown variable. -# -# Side Effects: -# None. -# - -proc tcltest::SubstArguments {argList} { - - # We need to split the argList up into tokens but cannot use list - # operations as they throw away some significant quoting, and - # [split] ignores braces as it should. Therefore what we do is - # gradually build up a string out of whitespace seperated strings. - # We cannot use [split] to split the argList into whitespace - # separated strings as it throws away the whitespace which maybe - # important so we have to do it all by hand. - - set result {} - set token "" - - while {[string length $argList]} { - # Look for the next word containing a quote: " { } - if {[regexp -indices {[^ \t\n]*[\"\{\}]+[^ \t\n]*} \ - $argList all]} { - # Get the text leading up to this word, but not including - # this word, from the argList. - set text [string range $argList 0 \ - [expr {[lindex $all 0] - 1}]] - # Get the word with the quote - set word [string range $argList \ - [lindex $all 0] [lindex $all 1]] - - # Remove all text up to and including the word from the - # argList. - set argList [string range $argList \ - [expr {[lindex $all 1] + 1}] end] - } else { - # Take everything up to the end of the argList. - set text $argList - set word {} - set argList {} - } - - if {$token != {}} { - # If we saw a word with quote before, then there is a - # multi-word token starting with that word. In this case, - # add the text and the current word to this token. - append token $text $word - } else { - # Add the text to the result. There is no need to parse - # the text because it couldn't be a part of any multi-word - # token. Then start a new multi-word token with the word - # because we need to pass this token to the Tcl parser to - # check for balancing quotes - append result $text - set token $word - } - - if { [catch {llength $token} length] == 0 && $length == 1} { - # The token is a valid list so add it to the result. - # lappend result [string trim $token] - append result \{$token\} - set token {} - } - } - - # If the last token has not been added to the list then there - # is a problem. - if { [string length $token] } { - error "incomplete token \"$token\"" - } - - return $result -} - - -# tcltest::test -- -# -# This procedure runs a test and prints an error message if the test -# fails. If verbose has been set, it also prints a message even if the -# test succeeds. The test will be skipped if it doesn't match the -# match variable, if it matches an element in skip, or if one of the -# elements of "constraints" turns out not to be true. -# -# If testLevel is 1, then this is a top level test, and we record -# pass/fail information; otherwise, this information is not logged and -# is not added to running totals. -# -# Attributes: -# Only description is a required attribute. All others are optional. -# Default values are indicated. -# -# constraints - A list of one or more keywords, each of which -# must be the name of an element in the array -# "testConstraints". If any of these elements is -# zero, the test is skipped. This attribute is -# optional; default is {} -# body - Script to run to carry out the test. It must -# return a result that can be checked for -# correctness. This attribute is optional; -# default is {} -# result - Expected result from script. This attribute is -# optional; default is {}. -# output - Expected output sent to stdout. This attribute -# is optional; default is {}. -# errorOutput - Expected output sent to stderr. This attribute -# is optional; default is {}. -# returnCodes - Expected return codes. This attribute is -# optional; default is {0 2}. -# setup - Code to run before $script (above). This -# attribute is optional; default is {}. -# cleanup - Code to run after $script (above). This -# attribute is optional; default is {}. -# match - specifies type of matching to do on result, -# output, errorOutput; this must be a string -# previously registered by a call to [customMatch]. -# The strings exact, glob, and regexp are pre-registered -# by the tcltest package. Default value is exact. -# -# Arguments: -# name - Name of test, in the form foo-1.2. -# description - Short textual description of the test, to -# help humans understand what it does. -# -# Results: -# None. -# -# Side effects: -# Just about anything is possible depending on the test. -# - -proc tcltest::test {name description args} { - global tcl_platform - variable testLevel - variable coreModTime - DebugPuts 3 "test $name $args" - DebugDo 1 { - variable TestNames - catch { - puts "test name '$name' re-used; prior use in $TestNames($name)" - } - set TestNames($name) [info script] - } - - FillFilesExisted - incr testLevel - - # Pre-define everything to null except output and errorOutput. We - # determine whether or not to trap output based on whether or not - # these variables (output & errorOutput) are defined. - foreach item {constraints setup cleanup body result returnCodes - match} { - set $item {} - } - - # Set the default match mode - set match exact - - # Set the default match values for return codes (0 is the standard - # expected return value if everything went well; 2 represents - # 'return' being used in the test script). - set returnCodes [list 0 2] - - # The old test format can't have a 3rd argument (constraints or - # script) that starts with '-'. - if {[string match -* [lindex $args 0]] - || ([llength $args] <= 1)} { - if {[llength $args] == 1} { - set list [SubstArguments [lindex $args 0]] - foreach {element value} $list { - set testAttributes($element) $value - } - foreach item {constraints match setup body cleanup \ - result returnCodes output errorOutput} { - if {[info exists testAttributes(-$item)]} { - set testAttributes(-$item) [uplevel 1 \ - ::concat $testAttributes(-$item)] - } - } - } else { - array set testAttributes $args - } - - set validFlags {-setup -cleanup -body -result -returnCodes \ - -match -output -errorOutput -constraints} - - foreach flag [array names testAttributes] { - if {[lsearch -exact $validFlags $flag] == -1} { - incr testLevel -1 - set sorted [lsort $validFlags] - set options [join [lrange $sorted 0 end-1] ", "] - append options ", or [lindex $sorted end]" - return -code error "bad option \"$flag\": must be $options" - } - } - - # store whatever the user gave us - foreach item [array names testAttributes] { - set [string trimleft $item "-"] $testAttributes($item) - } - - # Check the values supplied for -match - variable CustomMatch - if {[lsearch [array names CustomMatch] $match] == -1} { - incr testLevel -1 - set sorted [lsort [array names CustomMatch]] - set values [join [lrange $sorted 0 end-1] ", "] - append values ", or [lindex $sorted end]" - return -code error "bad -match value \"$match\":\ - must be $values" - } - - # Replace symbolic valies supplied for -returnCodes - foreach {strcode numcode} {ok 0 normal 0 error 1 return 2 break 3 continue 4} { - set returnCodes [string map -nocase [list $strcode $numcode] $returnCodes] - } - } else { - # This is parsing for the old test command format; it is here - # for backward compatibility. - set result [lindex $args end] - if {[llength $args] == 2} { - set body [lindex $args 0] - } elseif {[llength $args] == 3} { - set constraints [lindex $args 0] - set body [lindex $args 1] - } else { - incr testLevel -1 - return -code error "wrong # args:\ - should be \"test name desc ?options?\"" - } - } - - if {[Skipped $name $constraints]} { - incr testLevel -1 - return - } - - # Save information about the core file. - if {[preserveCore]} { - if {[file exists [file join [workingDirectory] core]]} { - set coreModTime [file mtime [file join [workingDirectory] core]] - } - } - - # First, run the setup script - set code [catch {uplevel 1 $setup} setupMsg] - if {$code == 1} { - set errorInfo(setup) $::errorInfo - set errorCode(setup) $::errorCode - } - set setupFailure [expr {$code != 0}] - - # Only run the test body if the setup was successful - if {!$setupFailure} { - - # Verbose notification of $body start - if {[IsVerbose start]} { - puts [outputChannel] "---- $name start" - flush [outputChannel] - } - - set command [list [namespace origin RunTest] $name $body] - if {[info exists output] || [info exists errorOutput]} { - set testResult [uplevel 1 [list [namespace origin Eval] $command 0]] - } else { - set testResult [uplevel 1 [list [namespace origin Eval] $command 1]] - } - foreach {actualAnswer returnCode} $testResult break - if {$returnCode == 1} { - set errorInfo(body) $::errorInfo - set errorCode(body) $::errorCode - } - } - - # Always run the cleanup script - set code [catch {uplevel 1 $cleanup} cleanupMsg] - if {$code == 1} { - set errorInfo(cleanup) $::errorInfo - set errorCode(cleanup) $::errorCode - } - set cleanupFailure [expr {$code != 0}] - - set coreFailure 0 - set coreMsg "" - # check for a core file first - if one was created by the test, - # then the test failed - if {[preserveCore]} { - if {[file exists [file join [workingDirectory] core]]} { - # There's only a test failure if there is a core file - # and (1) there previously wasn't one or (2) the new - # one is different from the old one. - if {[info exists coreModTime]} { - if {$coreModTime != [file mtime \ - [file join [workingDirectory] core]]} { - set coreFailure 1 - } - } else { - set coreFailure 1 - } - - if {([preserveCore] > 1) && ($coreFailure)} { - append coreMsg "\nMoving file to:\ - [file join [temporaryDirectory] core-$name]" - catch {file rename -force \ - [file join [workingDirectory] core] \ - [file join [temporaryDirectory] core-$name] - } msg - if {[string length $msg] > 0} { - append coreMsg "\nError:\ - Problem renaming core file: $msg" - } - } - } - } - - # check if the return code matched the expected return code - set codeFailure 0 - if {!$setupFailure && [lsearch -exact $returnCodes $returnCode] == -1} { - set codeFailure 1 - } - - # If expected output/error strings exist, we have to compare - # them. If the comparison fails, then so did the test. - set outputFailure 0 - variable outData - if {[info exists output] && !$codeFailure} { - if {[set outputCompare [catch { - CompareStrings $outData $output $match - } outputMatch]] == 0} { - set outputFailure [expr {!$outputMatch}] - } else { - set outputFailure 1 - } - } - - set errorFailure 0 - variable errData - if {[info exists errorOutput] && !$codeFailure} { - if {[set errorCompare [catch { - CompareStrings $errData $errorOutput $match - } errorMatch]] == 0} { - set errorFailure [expr {!$errorMatch}] - } else { - set errorFailure 1 - } - } - - # check if the answer matched the expected answer - # Only check if we ran the body of the test (no setup failure) - if {$setupFailure || $codeFailure} { - set scriptFailure 0 - } elseif {[set scriptCompare [catch { - CompareStrings $actualAnswer $result $match - } scriptMatch]] == 0} { - set scriptFailure [expr {!$scriptMatch}] - } else { - set scriptFailure 1 - } - - # if we didn't experience any failures, then we passed - variable numTests - if {!($setupFailure || $cleanupFailure || $coreFailure - || $outputFailure || $errorFailure || $codeFailure - || $scriptFailure)} { - if {$testLevel == 1} { - incr numTests(Passed) - #if {[IsVerbose pass]} { - puts [outputChannel] "++++ $name PASSED" - #} - } - incr testLevel -1 - return - } - - # We know the test failed, tally it... - if {$testLevel == 1} { - incr numTests(Failed) - } - - # ... then report according to the type of failure - variable currentFailure true - if {![IsVerbose body]} { - set body "" - } - puts [outputChannel] "\n" - if {[IsVerbose line]} { - if {![catch {set testFrame [info frame -1]}] && - [dict get $testFrame type] eq "source"} { - set testFile [dict get $testFrame file] - set testLine [dict get $testFrame line] - } else { - set testFile [file normalize [uplevel 1 {info script}]] - if {[file readable $testFile]} { - set testFd [open $testFile r] - set testLine [expr {[lsearch -regexp \ - [split [read $testFd] "\n"] \ - "^\[ \t\]*test [string map {. \\.} $name] "]+1}] - close $testFd - } - } - if {[info exists testLine]} { - puts [outputChannel] "$testFile:$testLine: test failed:\ - $name [string trim $description]" - } - } - puts [outputChannel] "==== $name\ - [string trim $description] FAILED" - if {[string length $body]} { - puts [outputChannel] "==== Contents of test case:" - puts [outputChannel] $body - } - if {$setupFailure} { - puts [outputChannel] "---- Test setup\ - failed:\n$setupMsg" - if {[info exists errorInfo(setup)]} { - puts [outputChannel] "---- errorInfo(setup): $errorInfo(setup)" - puts [outputChannel] "---- errorCode(setup): $errorCode(setup)" - } - } - if {$scriptFailure} { - if {$scriptCompare} { - puts [outputChannel] "---- Error testing result: $scriptMatch" - } else { - puts [outputChannel] "---- Result was:\n$actualAnswer" - puts [outputChannel] "---- Result should have been\ - ($match matching):\n$result" - } - } - if {$codeFailure} { - switch -- $returnCode { - 0 { set msg "Test completed normally" } - 1 { set msg "Test generated error" } - 2 { set msg "Test generated return exception" } - 3 { set msg "Test generated break exception" } - 4 { set msg "Test generated continue exception" } - default { set msg "Test generated exception" } - } - puts [outputChannel] "---- $msg; Return code was: $returnCode" - puts [outputChannel] "---- Return code should have been\ - one of: $returnCodes" - if {[IsVerbose error]} { - if {[info exists errorInfo(body)] && ([lsearch $returnCodes 1]<0)} { - puts [outputChannel] "---- errorInfo: $errorInfo(body)" - puts [outputChannel] "---- errorCode: $errorCode(body)" - } - } - } - if {$outputFailure} { - if {$outputCompare} { - puts [outputChannel] "---- Error testing output: $outputMatch" - } else { - puts [outputChannel] "---- Output was:\n$outData" - puts [outputChannel] "---- Output should have been\ - ($match matching):\n$output" - } - } - if {$errorFailure} { - if {$errorCompare} { - puts [outputChannel] "---- Error testing errorOutput: $errorMatch" - } else { - puts [outputChannel] "---- Error output was:\n$errData" - puts [outputChannel] "---- Error output should have\ - been ($match matching):\n$errorOutput" - } - } - if {$cleanupFailure} { - puts [outputChannel] "---- Test cleanup failed:\n$cleanupMsg" - if {[info exists errorInfo(cleanup)]} { - puts [outputChannel] "---- errorInfo(cleanup): $errorInfo(cleanup)" - puts [outputChannel] "---- errorCode(cleanup): $errorCode(cleanup)" - } - } - if {$coreFailure} { - puts [outputChannel] "---- Core file produced while running\ - test! $coreMsg" - } - puts [outputChannel] "==== $name FAILED\n" - - incr testLevel -1 - return -} - -# Skipped -- -# -# Given a test name and it constraints, returns a boolean indicating -# whether the current configuration says the test should be skipped. -# -# Side Effects: Maintains tally of total tests seen and tests skipped. -# -proc tcltest::Skipped {name constraints} { - variable testLevel - variable numTests - variable testConstraints - - if {$testLevel == 1} { - incr numTests(Total) - } - # skip the test if it's name matches an element of skip - foreach pattern [skip] { - if {[string match $pattern $name]} { - if {$testLevel == 1} { - incr numTests(Skipped) - DebugDo 1 {AddToSkippedBecause userSpecifiedSkip} - } - return 1 - } - } - # skip the test if it's name doesn't match any element of match - set ok 0 - foreach pattern [match] { - if {[string match $pattern $name]} { - set ok 1 - break - } - } - if {!$ok} { - if {$testLevel == 1} { - incr numTests(Skipped) - DebugDo 1 {AddToSkippedBecause userSpecifiedNonMatch} - } - return 1 - } - if {[string equal {} $constraints]} { - # If we're limited to the listed constraints and there aren't - # any listed, then we shouldn't run the test. - if {[limitConstraints]} { - AddToSkippedBecause userSpecifiedLimitConstraint - if {$testLevel == 1} { - incr numTests(Skipped) - } - return 1 - } - } else { - # "constraints" argument exists; - # make sure that the constraints are satisfied. - - set doTest 0 - if {[string match {*[$\[]*} $constraints] != 0} { - # full expression, e.g. {$foo > [info tclversion]} - catch {set doTest [uplevel #0 expr $constraints]} - } elseif {[regexp {[^.:_a-zA-Z0-9 \n\r\t]+} $constraints] != 0} { - # something like {a || b} should be turned into - # $testConstraints(a) || $testConstraints(b). - regsub -all {[.\w]+} $constraints {$testConstraints(&)} c - catch {set doTest [eval expr $c]} - } elseif {![catch {llength $constraints}]} { - # just simple constraints such as {unixOnly fonts}. - set doTest 1 - foreach constraint $constraints { - if {(![info exists testConstraints($constraint)]) \ - || (!$testConstraints($constraint))} { - set doTest 0 - - # store the constraint that kept the test from - # running - set constraints $constraint - break - } - } - } - - if {!$doTest} { - #if {[IsVerbose skip]} { - puts [outputChannel] "++++ $name SKIPPED: $constraints" - #} - - if {$testLevel == 1} { - incr numTests(Skipped) - AddToSkippedBecause $constraints - } - return 1 - } - } - return 0 -} - -# RunTest -- -# -# This is where the body of a test is evaluated. The combination of -# [RunTest] and [Eval] allows the output and error output of the test -# body to be captured for comparison against the expected values. - -proc tcltest::RunTest {name script} { - DebugPuts 3 "Running $name {$script}" - - # If there is no "memory" command (because memory debugging isn't - # enabled), then don't attempt to use the command. - - if {[llength [info commands memory]] == 1} { - memory tag $name - } - - set code [catch {uplevel 1 $script} actualAnswer] - - return [list $actualAnswer $code] -} - -##################################################################### - -# tcltest::cleanupTestsHook -- -# -# This hook allows a harness that builds upon tcltest to specify -# additional things that should be done at cleanup. -# - -if {[llength [info commands tcltest::cleanupTestsHook]] == 0} { - proc tcltest::cleanupTestsHook {} {} -} - -# tcltest::cleanupTests -- -# -# Remove files and dirs created using the makeFile and makeDirectory -# commands since the last time this proc was invoked. -# -# Print the names of the files created without the makeFile command -# since the tests were invoked. -# -# Print the number tests (total, passed, failed, and skipped) since the -# tests were invoked. -# -# Restore original environment (as reported by special variable env). -# -# Arguments: -# calledFromAllFile - if 0, behave as if we are running a single -# test file within an entire suite of tests. if we aren't running -# a single test file, then don't report status. check for new -# files created during the test run and report on them. if 1, -# report collated status from all the test file runs. -# -# Results: -# None. -# -# Side Effects: -# None -# - -proc tcltest::cleanupTests {{calledFromAllFile 0}} { - variable filesMade - variable filesExisted - variable createdNewFiles - variable testSingleFile - variable numTests - variable numTestFiles - variable failFiles - variable skippedBecause - variable currentFailure - variable originalEnv - variable originalTclPlatform - variable coreModTime - - FillFilesExisted - set testFileName [file tail [info script]] - - # Call the cleanup hook - cleanupTestsHook - - # Remove files and directories created by the makeFile and - # makeDirectory procedures. Record the names of files in - # workingDirectory that were not pre-existing, and associate them - # with the test file that created them. - - if {!$calledFromAllFile} { - foreach file $filesMade { - if {[file exists $file]} { - DebugDo 1 {Warn "cleanupTests deleting $file..."} - catch {file delete -force $file} - } - } - set currentFiles {} - foreach file [glob -nocomplain \ - -directory [temporaryDirectory] *] { - lappend currentFiles [file tail $file] - } - set newFiles {} - foreach file $currentFiles { - if {[lsearch -exact $filesExisted $file] == -1} { - lappend newFiles $file - } - } - set filesExisted $currentFiles - if {[llength $newFiles] > 0} { - set createdNewFiles($testFileName) $newFiles - } - } - - if {$calledFromAllFile || $testSingleFile} { - - # print stats - - puts -nonewline [outputChannel] "$testFileName:" - foreach index [list "Total" "Passed" "Skipped" "Failed"] { - puts -nonewline [outputChannel] \ - "\t$index\t$numTests($index)" - } - puts [outputChannel] "" - - # print number test files sourced - # print names of files that ran tests which failed - - if {$calledFromAllFile} { - puts [outputChannel] \ - "Sourced $numTestFiles Test Files." - set numTestFiles 0 - if {[llength $failFiles] > 0} { - puts [outputChannel] \ - "Files with failing tests: $failFiles" - set failFiles {} - } - } - - # if any tests were skipped, print the constraints that kept - # them from running. - - set constraintList [array names skippedBecause] - if {[llength $constraintList] > 0} { - puts [outputChannel] \ - "Number of tests skipped for each constraint:" - foreach constraint [lsort $constraintList] { - puts [outputChannel] \ - "\t$skippedBecause($constraint)\t$constraint" - unset skippedBecause($constraint) - } - } - - # report the names of test files in createdNewFiles, and reset - # the array to be empty. - - set testFilesThatTurded [lsort [array names createdNewFiles]] - if {[llength $testFilesThatTurded] > 0} { - puts [outputChannel] "Warning: files left behind:" - foreach testFile $testFilesThatTurded { - puts [outputChannel] \ - "\t$testFile:\t$createdNewFiles($testFile)" - unset createdNewFiles($testFile) - } - } - - # reset filesMade, filesExisted, and numTests - - set filesMade {} - foreach index [list "Total" "Passed" "Skipped" "Failed"] { - set numTests($index) 0 - } - - # exit only if running Tk in non-interactive mode - # This should be changed to determine if an event - # loop is running, which is the real issue. - # Actually, this doesn't belong here at all. A package - # really has no business [exit]-ing an application. - if {![catch {package present Tk}] && ![testConstraint interactive]} { - exit - } - } else { - - # if we're deferring stat-reporting until all files are sourced, - # then add current file to failFile list if any tests in this - # file failed - - if {$currentFailure \ - && ([lsearch -exact $failFiles $testFileName] == -1)} { - lappend failFiles $testFileName - } - set currentFailure false - - # restore the environment to the state it was in before this package - # was loaded - - set newEnv {} - set changedEnv {} - set removedEnv {} - foreach index [array names ::env] { - if {![info exists originalEnv($index)]} { - lappend newEnv $index - unset ::env($index) - } else { - if {$::env($index) != $originalEnv($index)} { - lappend changedEnv $index - set ::env($index) $originalEnv($index) - } - } - } - foreach index [array names originalEnv] { - if {![info exists ::env($index)]} { - lappend removedEnv $index - set ::env($index) $originalEnv($index) - } - } - if {[llength $newEnv] > 0} { - puts [outputChannel] \ - "env array elements created:\t$newEnv" - } - if {[llength $changedEnv] > 0} { - puts [outputChannel] \ - "env array elements changed:\t$changedEnv" - } - if {[llength $removedEnv] > 0} { - puts [outputChannel] \ - "env array elements removed:\t$removedEnv" - } - - set changedTclPlatform {} - foreach index [array names originalTclPlatform] { - if {$::tcl_platform($index) \ - != $originalTclPlatform($index)} { - lappend changedTclPlatform $index - set ::tcl_platform($index) $originalTclPlatform($index) - } - } - if {[llength $changedTclPlatform] > 0} { - puts [outputChannel] "tcl_platform array elements\ - changed:\t$changedTclPlatform" - } - - if {[file exists [file join [workingDirectory] core]]} { - if {[preserveCore] > 1} { - puts "rename core file (> 1)" - puts [outputChannel] "produced core file! \ - Moving file to: \ - [file join [temporaryDirectory] core-$testFileName]" - catch {file rename -force \ - [file join [workingDirectory] core] \ - [file join [temporaryDirectory] core-$testFileName] - } msg - if {[string length $msg] > 0} { - PrintError "Problem renaming file: $msg" - } - } else { - # Print a message if there is a core file and (1) there - # previously wasn't one or (2) the new one is different - # from the old one. - - if {[info exists coreModTime]} { - if {$coreModTime != [file mtime \ - [file join [workingDirectory] core]]} { - puts [outputChannel] "A core file was created!" - } - } else { - puts [outputChannel] "A core file was created!" - } - } - } - } - flush [outputChannel] - flush [errorChannel] - return -} - -##################################################################### - -# Procs that determine which tests/test files to run - -# tcltest::GetMatchingFiles -# -# Looks at the patterns given to match and skip files and uses -# them to put together a list of the tests that will be run. -# -# Arguments: -# directory to search -# -# Results: -# The constructed list is returned to the user. This will -# primarily be used in 'all.tcl' files. It is used in -# runAllTests. -# -# Side Effects: -# None - -# a lower case version is needed for compatibility with tcltest 1.0 -proc tcltest::getMatchingFiles args {eval GetMatchingFiles $args} - -proc tcltest::GetMatchingFiles { args } { - if {[llength $args]} { - set dirList $args - } else { - # Finding tests only in [testsDirectory] is normal operation. - # This procedure is written to accept multiple directory arguments - # only to satisfy version 1 compatibility. - set dirList [list [testsDirectory]] - } - - set matchingFiles [list] - foreach directory $dirList { - - # List files in $directory that match patterns to run. - set matchFileList [list] - foreach match [matchFiles] { - set matchFileList [concat $matchFileList \ - [glob -directory $directory -types {b c f p s} \ - -nocomplain -- $match]] - } - - # List files in $directory that match patterns to skip. - set skipFileList [list] - foreach skip [skipFiles] { - set skipFileList [concat $skipFileList \ - [glob -directory $directory -types {b c f p s} \ - -nocomplain -- $skip]] - } - - # Add to result list all files in match list and not in skip list - foreach file $matchFileList { - if {[lsearch -exact $skipFileList $file] == -1} { - lappend matchingFiles $file - } - } - } - - if {[llength $matchingFiles] == 0} { - PrintError "No test files remain after applying your match and\ - skip patterns!" - } - return $matchingFiles -} - -# tcltest::GetMatchingDirectories -- -# -# Looks at the patterns given to match and skip directories and -# uses them to put together a list of the test directories that we -# should attempt to run. (Only subdirectories containing an -# "all.tcl" file are put into the list.) -# -# Arguments: -# root directory from which to search -# -# Results: -# The constructed list is returned to the user. This is used in -# the primary all.tcl file. -# -# Side Effects: -# None. - -proc tcltest::GetMatchingDirectories {rootdir} { - - # Determine the skip list first, to avoid [glob]-ing over subdirectories - # we're going to throw away anyway. Be sure we skip the $rootdir if it - # comes up to avoid infinite loops. - set skipDirs [list $rootdir] - foreach pattern [skipDirectories] { - set skipDirs [concat $skipDirs [glob -directory $rootdir -types d \ - -nocomplain -- $pattern]] - } - - # Now step through the matching directories, prune out the skipped ones - # as you go. - set matchDirs [list] - foreach pattern [matchDirectories] { - foreach path [glob -directory $rootdir -types d -nocomplain -- \ - $pattern] { - if {[lsearch -exact $skipDirs $path] == -1} { - set matchDirs [concat $matchDirs [GetMatchingDirectories $path]] - if {[file exists [file join $path all.tcl]]} { - lappend matchDirs $path - } - } - } - } - - if {[llength $matchDirs] == 0} { - DebugPuts 1 "No test directories remain after applying match\ - and skip patterns!" - } - return $matchDirs -} - -# tcltest::runAllTests -- -# -# prints output and sources test files according to the match and -# skip patterns provided. after sourcing test files, it goes on -# to source all.tcl files in matching test subdirectories. -# -# Arguments: -# shell being tested -# -# Results: -# None. -# -# Side effects: -# None. - -proc tcltest::runAllTests { {shell ""} } { - variable testSingleFile - variable numTestFiles - variable numTests - variable failFiles - - FillFilesExisted - if {[llength [info level 0]] == 1} { - set shell [interpreter] - } - - set testSingleFile false - - puts [outputChannel] "Tests running in interp: $shell" - puts [outputChannel] "Tests located in: [testsDirectory]" - puts [outputChannel] "Tests running in: [workingDirectory]" - puts [outputChannel] "Temporary files stored in\ - [temporaryDirectory]" - - # [file system] first available in Tcl 8.4 - if {![catch {file system [testsDirectory]} result] - && ![string equal native [lindex $result 0]]} { - # If we aren't running in the native filesystem, then we must - # run the tests in a single process (via 'source'), because - # trying to run then via a pipe will fail since the files don't - # really exist. - singleProcess 1 - } - - if {[singleProcess]} { - puts [outputChannel] \ - "Test files sourced into current interpreter" - } else { - puts [outputChannel] \ - "Test files run in separate interpreters" - } - if {[llength [skip]] > 0} { - puts [outputChannel] "Skipping tests that match: [skip]" - } - puts [outputChannel] "Running tests that match: [match]" - - if {[llength [skipFiles]] > 0} { - puts [outputChannel] \ - "Skipping test files that match: [skipFiles]" - } - if {[llength [matchFiles]] > 0} { - puts [outputChannel] \ - "Only running test files that match: [matchFiles]" - } - - set timeCmd {clock format [clock seconds]} - puts [outputChannel] "Tests began at [eval $timeCmd]" - - # Run each of the specified tests - foreach file [lsort [GetMatchingFiles]] { - set tail [file tail $file] - puts [outputChannel] $tail - flush [outputChannel] - - if {[singleProcess]} { - incr numTestFiles - uplevel 1 [list ::source $file] - } else { - # Pass along our configuration to the child processes. - # EXCEPT for the -outfile, because the parent process - # needs to read and process output of children. - set childargv [list] - foreach opt [Configure] { - if {[string equal $opt -outfile]} {continue} - lappend childargv $opt [Configure $opt] - } - set cmd [linsert $childargv 0 | $shell $file] - if {[catch { - incr numTestFiles - set pipeFd [open $cmd "r"] - while {[gets $pipeFd line] >= 0} { - if {[regexp [join { - {^([^:]+):\t} - {Total\t([0-9]+)\t} - {Passed\t([0-9]+)\t} - {Skipped\t([0-9]+)\t} - {Failed\t([0-9]+)} - } ""] $line null testFile \ - Total Passed Skipped Failed]} { - foreach index {Total Passed Skipped Failed} { - incr numTests($index) [set $index] - } - if {$Failed > 0} { - lappend failFiles $testFile - } - } elseif {[regexp [join { - {^Number of tests skipped } - {for each constraint:} - {|^\t(\d+)\t(.+)$} - } ""] $line match skipped constraint]} { - if {[string match \t* $match]} { - AddToSkippedBecause $constraint $skipped - } - } else { - puts [outputChannel] $line - } - } - close $pipeFd - } msg]} { - puts [outputChannel] "Test file error: $msg" - # append the name of the test to a list to be reported - # later - lappend testFileFailures $file - } - } - } - - # cleanup - puts [outputChannel] "\nTests ended at [eval $timeCmd]" - cleanupTests 1 - if {[info exists testFileFailures]} { - puts [outputChannel] "\nTest files exiting with errors: \n" - foreach file $testFileFailures { - puts [outputChannel] " [file tail $file]\n" - } - } - - # Checking for subdirectories in which to run tests - foreach directory [GetMatchingDirectories [testsDirectory]] { - set dir [file tail $directory] - puts [outputChannel] [string repeat ~ 44] - puts [outputChannel] "$dir test began at [eval $timeCmd]\n" - - uplevel 1 [list ::source [file join $directory all.tcl]] - - set endTime [eval $timeCmd] - puts [outputChannel] "\n$dir test ended at $endTime" - puts [outputChannel] "" - puts [outputChannel] [string repeat ~ 44] - } - return -} - -##################################################################### - -# Test utility procs - not used in tcltest, but may be useful for -# testing. - -# tcltest::loadTestedCommands -- -# -# Uses the specified script to load the commands to test. Allowed to -# be empty, as the tested commands could have been compiled into the -# interpreter. -# -# Arguments -# none -# -# Results -# none -# -# Side Effects: -# none. - -proc tcltest::loadTestedCommands {} { - variable l - if {[string equal {} [loadScript]]} { - return - } - - return [uplevel 1 [loadScript]] -} - -# tcltest::saveState -- -# -# Save information regarding what procs and variables exist. -# -# Arguments: -# none -# -# Results: -# Modifies the variable saveState -# -# Side effects: -# None. - -proc tcltest::saveState {} { - variable saveState - uplevel 1 [list ::set [namespace which -variable saveState]] \ - {[::list [::info procs] [::info vars]]} - DebugPuts 2 "[lindex [info level 0] 0]: $saveState" - return -} - -# tcltest::restoreState -- -# -# Remove procs and variables that didn't exist before the call to -# [saveState]. -# -# Arguments: -# none -# -# Results: -# Removes procs and variables from your environment if they don't -# exist in the saveState variable. -# -# Side effects: -# None. - -proc tcltest::restoreState {} { - variable saveState - foreach p [uplevel 1 {::info procs}] { - if {([lsearch [lindex $saveState 0] $p] < 0) - && ![string equal [namespace current]::$p \ - [uplevel 1 [list ::namespace origin $p]]]} { - - DebugPuts 2 "[lindex [info level 0] 0]: Removing proc $p" - uplevel 1 [list ::catch [list ::rename $p {}]] - } - } - foreach p [uplevel 1 {::info vars}] { - if {[lsearch [lindex $saveState 1] $p] < 0} { - DebugPuts 2 "[lindex [info level 0] 0]:\ - Removing variable $p" - uplevel 1 [list ::catch [list ::unset $p]] - } - } - return -} - -# tcltest::normalizeMsg -- -# -# Removes "extra" newlines from a string. -# -# Arguments: -# msg String to be modified -# -# Results: -# string with extra newlines removed -# -# Side effects: -# None. - -proc tcltest::normalizeMsg {msg} { - regsub "\n$" [string tolower $msg] "" msg - set msg [string map [list "\n\n" "\n"] $msg] - return [string map [list "\n\}" "\}"] $msg] -} - -# tcltest::makeFile -- -# -# Create a new file with the name , and write to it. -# -# If this file hasn't been created via makeFile since the last time -# cleanupTests was called, add it to the $filesMade list, so it will be -# removed by the next call to cleanupTests. -# -# Arguments: -# contents content of the new file -# name name of the new file -# directory directory name for new file -# -# Results: -# absolute path to the file created -# -# Side effects: -# None. - -proc tcltest::makeFile {contents name {directory ""}} { - variable filesMade - FillFilesExisted - - if {[llength [info level 0]] == 3} { - set directory [temporaryDirectory] - } - - set fullName [file join $directory $name] - - DebugPuts 3 "[lindex [info level 0] 0]:\ - putting ``$contents'' into $fullName" - - set fd [open $fullName w] - fconfigure $fd -translation lf - if {[string equal [string index $contents end] \n]} { - puts -nonewline $fd $contents - } else { - puts $fd $contents - } - close $fd - - if {[lsearch -exact $filesMade $fullName] == -1} { - lappend filesMade $fullName - } - return $fullName -} - -# tcltest::removeFile -- -# -# Removes the named file from the filesystem -# -# Arguments: -# name file to be removed -# directory directory from which to remove file -# -# Results: -# return value from [file delete] -# -# Side effects: -# None. - -proc tcltest::removeFile {name {directory ""}} { - variable filesMade - FillFilesExisted - if {[llength [info level 0]] == 2} { - set directory [temporaryDirectory] - } - set fullName [file join $directory $name] - DebugPuts 3 "[lindex [info level 0] 0]: removing $fullName" - set idx [lsearch -exact $filesMade $fullName] - set filesMade [lreplace $filesMade $idx $idx] - if {$idx == -1} { - DebugDo 1 { - Warn "removeFile removing \"$fullName\":\n not created by makeFile" - } - } - if {![file isfile $fullName]} { - DebugDo 1 { - Warn "removeFile removing \"$fullName\":\n not a file" - } - } - return [file delete $fullName] -} - -# tcltest::makeDirectory -- -# -# Create a new dir with the name . -# -# If this dir hasn't been created via makeDirectory since the last time -# cleanupTests was called, add it to the $directoriesMade list, so it -# will be removed by the next call to cleanupTests. -# -# Arguments: -# name name of the new directory -# directory directory in which to create new dir -# -# Results: -# absolute path to the directory created -# -# Side effects: -# None. - -proc tcltest::makeDirectory {name {directory ""}} { - variable filesMade - FillFilesExisted - if {[llength [info level 0]] == 2} { - set directory [temporaryDirectory] - } - set fullName [file join $directory $name] - DebugPuts 3 "[lindex [info level 0] 0]: creating $fullName" - file mkdir $fullName - if {[lsearch -exact $filesMade $fullName] == -1} { - lappend filesMade $fullName - } - return $fullName -} - -# tcltest::removeDirectory -- -# -# Removes a named directory from the file system. -# -# Arguments: -# name Name of the directory to remove -# directory Directory from which to remove -# -# Results: -# return value from [file delete] -# -# Side effects: -# None - -proc tcltest::removeDirectory {name {directory ""}} { - variable filesMade - FillFilesExisted - if {[llength [info level 0]] == 2} { - set directory [temporaryDirectory] - } - set fullName [file join $directory $name] - DebugPuts 3 "[lindex [info level 0] 0]: deleting $fullName" - set idx [lsearch -exact $filesMade $fullName] - set filesMade [lreplace $filesMade $idx $idx] - if {$idx == -1} { - DebugDo 1 { - Warn "removeDirectory removing \"$fullName\":\n not created\ - by makeDirectory" - } - } - if {![file isdirectory $fullName]} { - DebugDo 1 { - Warn "removeDirectory removing \"$fullName\":\n not a directory" - } - } - return [file delete -force $fullName] -} - -# tcltest::viewFile -- -# -# reads the content of a file and returns it -# -# Arguments: -# name of the file to read -# directory in which file is located -# -# Results: -# content of the named file -# -# Side effects: -# None. - -proc tcltest::viewFile {name {directory ""}} { - FillFilesExisted - if {[llength [info level 0]] == 2} { - set directory [temporaryDirectory] - } - set fullName [file join $directory $name] - set f [open $fullName] - set data [read -nonewline $f] - close $f - return $data -} - -# tcltest::bytestring -- -# -# Construct a string that consists of the requested sequence of bytes, -# as opposed to a string of properly formed UTF-8 characters. -# This allows the tester to -# 1. Create denormalized or improperly formed strings to pass to C -# procedures that are supposed to accept strings with embedded NULL -# bytes. -# 2. Confirm that a string result has a certain pattern of bytes, for -# instance to confirm that "\xe0\0" in a Tcl script is stored -# internally in UTF-8 as the sequence of bytes "\xc3\xa0\xc0\x80". -# -# Generally, it's a bad idea to examine the bytes in a Tcl string or to -# construct improperly formed strings in this manner, because it involves -# exposing that Tcl uses UTF-8 internally. -# -# Arguments: -# string being converted -# -# Results: -# result fom encoding -# -# Side effects: -# None - -proc tcltest::bytestring {string} { - return [encoding convertfrom identity $string] -} - -# tcltest::OpenFiles -- -# -# used in io tests, uses testchannel -# -# Arguments: -# None. -# -# Results: -# ??? -# -# Side effects: -# None. - -proc tcltest::OpenFiles {} { - if {[catch {testchannel open} result]} { - return {} - } - return $result -} - -# tcltest::LeakFiles -- -# -# used in io tests, uses testchannel -# -# Arguments: -# None. -# -# Results: -# ??? -# -# Side effects: -# None. - -proc tcltest::LeakFiles {old} { - if {[catch {testchannel open} new]} { - return {} - } - set leak {} - foreach p $new { - if {[lsearch $old $p] < 0} { - lappend leak $p - } - } - return $leak -} - -# -# Internationalization / ISO support procs -- dl -# - -# tcltest::SetIso8859_1_Locale -- -# -# used in cmdIL.test, uses testlocale -# -# Arguments: -# None. -# -# Results: -# None. -# -# Side effects: -# None. - -proc tcltest::SetIso8859_1_Locale {} { - variable previousLocale - variable isoLocale - if {[info commands testlocale] != ""} { - set previousLocale [testlocale ctype] - testlocale ctype $isoLocale - } - return -} - -# tcltest::RestoreLocale -- -# -# used in cmdIL.test, uses testlocale -# -# Arguments: -# None. -# -# Results: -# None. -# -# Side effects: -# None. - -proc tcltest::RestoreLocale {} { - variable previousLocale - if {[info commands testlocale] != ""} { - testlocale ctype $previousLocale - } - return -} - -# tcltest::threadReap -- -# -# Kill all threads except for the main thread. -# Do nothing if testthread is not defined. -# -# Arguments: -# none. -# -# Results: -# Returns the number of existing threads. -# -# Side Effects: -# none. -# - -proc tcltest::threadReap {} { - if {[info commands testthread] != {}} { - - # testthread built into tcltest - - testthread errorproc ThreadNullError - while {[llength [testthread names]] > 1} { - foreach tid [testthread names] { - if {$tid != [mainThread]} { - catch { - testthread send -async $tid {testthread exit} - } - } - } - ## Enter a bit a sleep to give the threads enough breathing - ## room to kill themselves off, otherwise the end up with a - ## massive queue of repeated events - after 1 - } - testthread errorproc ThreadError - return [llength [testthread names]] - } elseif {[info commands thread::id] != {}} { - - # Thread extension - - thread::errorproc ThreadNullError - while {[llength [thread::names]] > 1} { - foreach tid [thread::names] { - if {$tid != [mainThread]} { - catch {thread::send -async $tid {thread::exit}} - } - } - ## Enter a bit a sleep to give the threads enough breathing - ## room to kill themselves off, otherwise the end up with a - ## massive queue of repeated events - after 1 - } - thread::errorproc ThreadError - return [llength [thread::names]] - } else { - return 1 - } - return 0 -} - -# Initialize the constraints and set up command line arguments -namespace eval tcltest { - # Define initializers for all the built-in contraint definitions - DefineConstraintInitializers - - # Set up the constraints in the testConstraints array to be lazily - # initialized by a registered initializer, or by "false" if no - # initializer is registered. - trace variable testConstraints r [namespace code SafeFetch] - - # Only initialize constraints at package load time if an - # [initConstraintsHook] has been pre-defined. This is only - # for compatibility support. The modern way to add a custom - # test constraint is to just call the [testConstraint] command - # straight away, without all this "hook" nonsense. - if {[string equal [namespace current] \ - [namespace qualifiers [namespace which initConstraintsHook]]]} { - InitConstraints - } else { - proc initConstraintsHook {} {} - } - - # Define the standard match commands - customMatch exact [list string equal] - customMatch glob [list string match] - customMatch regexp [list regexp --] - - # If the TCLTEST_OPTIONS environment variable exists, configure - # tcltest according to the option values it specifies. This has - # the effect of resetting tcltest's default configuration. - proc ConfigureFromEnvironment {} { - upvar #0 env(TCLTEST_OPTIONS) options - if {[catch {llength $options} msg]} { - Warn "invalid TCLTEST_OPTIONS \"$options\":\n invalid\ - Tcl list: $msg" - return - } - if {[llength $::env(TCLTEST_OPTIONS)] % 2} { - Warn "invalid TCLTEST_OPTIONS: \"$options\":\n should be\ - -option value ?-option value ...?" - return - } - if {[catch {eval Configure $::env(TCLTEST_OPTIONS)} msg]} { - Warn "invalid TCLTEST_OPTIONS: \"$options\":\n $msg" - return - } - } - if {[info exists ::env(TCLTEST_OPTIONS)]} { - ConfigureFromEnvironment - } - - proc LoadTimeCmdLineArgParsingRequired {} { - set required false - if {[info exists ::argv] && [lsearch -exact $::argv -help] != -1} { - # The command line asks for -help, so give it (and exit) - # right now. ([configure] does not process -help) - set required true - } - foreach hook { PrintUsageInfoHook processCmdLineArgsHook - processCmdLineArgsAddFlagsHook } { - if {[string equal [namespace current] [namespace qualifiers \ - [namespace which $hook]]]} { - set required true - } else { - proc $hook args {} - } - } - return $required - } - - # Only initialize configurable options from the command line arguments - # at package load time if necessary for backward compatibility. This - # lets the tcltest user call [configure] for themselves if they wish. - # Traces are established for auto-configuration from the command line - # if any configurable options are accessed before the user calls - # [configure]. - if {[LoadTimeCmdLineArgParsingRequired]} { - ProcessCmdLineArgs - } else { - EstablishAutoConfigureTraces - } - - package provide [namespace tail [namespace current]] $Version -} - -################################################################################ -# XXX partcl hacks - goal is to eventually remove these and run tcltest -# natively. Any code in this section is NOT part of the original tcltest.tcl -# library file but was specifically added for partcl; - -proc ::tcltest::outputChannel {} { return stdout } -proc ::tcltest::errorChannel {} { return stderr } - -# Since I keep inadvertently committing them, leaving in the always-on -# modifications for PASSED and SKIPPED; we can revert when we're faster. -################################################################################ diff --git a/library/word.tcl b/library/word.tcl deleted file mode 100644 index b8f7f7d..0000000 --- a/library/word.tcl +++ /dev/null @@ -1,146 +0,0 @@ -# word.tcl -- -# -# This file defines various procedures for computing word boundaries in -# strings. This file is primarily needed so Tk text and entry widgets behave -# properly for different platforms. -# -# Copyright (c) 1996 by Sun Microsystems, Inc. -# Copyright (c) 1998 by Scritpics Corporation. -# -# See the file "license.terms" for information on usage and redistribution of -# this file, and for a DISCLAIMER OF ALL WARRANTIES. -# -# RCS: @(#) $Id: word.tcl,v 1.10 2007/12/13 15:26:03 dgp Exp $ - -# The following variables are used to determine which characters are -# interpreted as white space. - -if {$::tcl_platform(platform) eq "windows"} { - # Windows style - any but a unicode space char - set ::tcl_wordchars {\S} - set ::tcl_nonwordchars {\s} -} else { - # Motif style - any unicode word char (number, letter, or underscore) - set ::tcl_wordchars {\w} - set ::tcl_nonwordchars {\W} -} - -# Arrange for caches of the real matcher REs to be kept, which enables the REs -# themselves to be cached for greater performance (and somewhat greater -# clarity too). - -namespace eval ::tcl { - variable WordBreakRE - array set WordBreakRE {} - - proc UpdateWordBreakREs args { - # Ignores the arguments - global tcl_wordchars tcl_nonwordchars - variable WordBreakRE - - # To keep the RE strings short... - set letter $tcl_wordchars - set space $tcl_nonwordchars - - set WordBreakRE(after) "$letter$space|$space$letter" - set WordBreakRE(before) "^.*($letter$space|$space$letter)" - set WordBreakRE(end) "$space*$letter+$space" - set WordBreakRE(next) "$letter*$space+$letter" - set WordBreakRE(previous) "$space*($letter+)$space*\$" - } - - # Initialize the cache - UpdateWordBreakREs - trace add variable ::tcl_wordchars write ::tcl::UpdateWordBreakREs - trace add variable ::tcl_nonwordchars write ::tcl::UpdateWordBreakREs -} - -# tcl_wordBreakAfter -- -# -# This procedure returns the index of the first word boundary after the -# starting point in the given string, or -1 if there are no more boundaries in -# the given string. The index returned refers to the first character of the -# pair that comprises a boundary. -# -# Arguments: -# str - String to search. -# start - Index into string specifying starting point. - -proc tcl_wordBreakAfter {str start} { - variable ::tcl::WordBreakRE - set result {-1 -1} - regexp -indices -start $start $WordBreakRE(after) $str result - return [lindex $result 1] -} - -# tcl_wordBreakBefore -- -# -# This procedure returns the index of the first word boundary before the -# starting point in the given string, or -1 if there are no more boundaries in -# the given string. The index returned refers to the second character of the -# pair that comprises a boundary. -# -# Arguments: -# str - String to search. -# start - Index into string specifying starting point. - -proc tcl_wordBreakBefore {str start} { - variable ::tcl::WordBreakRE - set result {-1 -1} - regexp -indices $WordBreakRE(before) [string range $str 0 $start] result - return [lindex $result 1] -} - -# tcl_endOfWord -- -# -# This procedure returns the index of the first end-of-word location after a -# starting index in the given string. An end-of-word location is defined to be -# the first whitespace character following the first non-whitespace character -# after the starting point. Returns -1 if there are no more words after the -# starting point. -# -# Arguments: -# str - String to search. -# start - Index into string specifying starting point. - -proc tcl_endOfWord {str start} { - variable ::tcl::WordBreakRE - set result {-1 -1} - regexp -indices -start $start $WordBreakRE(end) $str result - return [lindex $result 1] -} - -# tcl_startOfNextWord -- -# -# This procedure returns the index of the first start-of-word location after a -# starting index in the given string. A start-of-word location is defined to -# be a non-whitespace character following a whitespace character. Returns -1 -# if there are no more start-of-word locations after the starting point. -# -# Arguments: -# str - String to search. -# start - Index into string specifying starting point. - -proc tcl_startOfNextWord {str start} { - variable ::tcl::WordBreakRE - set result {-1 -1} - regexp -indices -start $start $WordBreakRE(next) $str result - return [lindex $result 1] -} - -# tcl_startOfPreviousWord -- -# -# This procedure returns the index of the first start-of-word location before -# a starting index in the given string. -# -# Arguments: -# str - String to search. -# start - Index into string specifying starting point. - -proc tcl_startOfPreviousWord {str start} { - variable ::tcl::WordBreakRE - set word {-1 -1} - regexp -indices $WordBreakRE(previous) [string range $str 0 $start-1] \ - result word - return [lindex $word 0] -} diff --git a/src/ARE/Actions.pm b/src/ARE/Actions.pm deleted file mode 100644 index e4d420f..0000000 --- a/src/ARE/Actions.pm +++ /dev/null @@ -1,119 +0,0 @@ -use NQPHLL; - -class ARE::Actions is HLL::Actions { - - method TOP($/) { - my $past := buildsub( $.ast ); - $past.node($/); - make $past; - } - - method nibbler($/) { - my $past; - if +$ > 1 { - $past := PAST::Regex.new( :pasttype('alt'), :node($/) ); - for $ { $past.push($_.ast); } - } - else { - $past := $[0].ast; - } - make $past; - } - - method termish($/) { - my $past := PAST::Regex.new( :pasttype('concat'), :node($/) ); - my $lastlit := 0; - for $ { - my $ast := $_.ast; - if $ast { - if $lastlit && $ast.pasttype eq 'literal' - && !PAST::Node.ACCEPTS($ast[0]) { - $lastlit[0] := $lastlit[0] ~ $ast[0]; - } - else { - $past.push($ast); - $lastlit := $ast.pasttype eq 'literal' - && !PAST::Node.ACCEPTS($ast[0]) - ?? $ast !! 0; - } - } - } - make $past; - } - - method quantified_atom($/) { - my $past := $.ast; - if $ { - my $qast := $[0].ast; - $qast.unshift($past); - $past := $qast; - } - make $past; - } - - method atom($/) { - my $past := $ - ?? $.ast - !! PAST::Regex.new( ~$/, :pasttype, :node($/) ); - make $past; - } - - method quantifier:sym<*>($/) { - make PAST::Regex.new( :pasttype, :node($/) ); - } - method quantifier:sym<+>($/) { - make PAST::Regex.new( :pasttype, :min(1), :node($/) ); - } - method quantifier:sym($/) { - make PAST::Regex.new( :pasttype, :min(0), :max(1), :node($/) ); - } - - method metachar:sym<^>($/) { - make PAST::Regex.new( :pasttype, :subtype('bos'), :node($/) ); - } - - method metachar:sym<$>($/) { - make PAST::Regex.new( :pasttype, :subtype('eos'), :node($/) ); - } - - method metachar:sym<.>($/) { - make PAST::Regex.new( :pasttype, :subtype<.>, :node($/) ); - } - - method metachar:sym($/) { make $.ast; } - - method metachar:sym<[>($/) { - my $str := ''; - for $ { - if $_[1] { - my $a := pir::ord($_[0]); - my $b := pir::ord(~$_[1][0]); - while $a <= $b { $str := $str ~ pir::chr($a); $a++; } - } - else { $str := $str ~ $_[0]; } - } - my $past := PAST::Regex.new( $str, :pasttype, :node($/) ); - $past.negate( $ gt '' ); - make $past; - } - - method backslash:sym($/) { - make PAST::Regex.new( :pasttype, :subtype(~$), :node($/)); - } - - sub buildsub($rpast, $block = PAST::Block.new() ) { - $rpast := PAST::Regex.new( - PAST::Regex.new( :pasttype('scan') ), - $rpast, - PAST::Regex.new( :pasttype('pass') ), - :pasttype('concat'), - ); - unless $block.symbol('$¢') { $block.symbol('$¢', :scope); } - unless $block.symbol('$/') { $block.symbol('$/', :scope); } - $block.push($rpast); - $block.blocktype('method'); - $block; - } -} - -# vim: expandtab shiftwidth=4 ft=perl6: diff --git a/src/ARE/Compiler.pm b/src/ARE/Compiler.pm deleted file mode 100644 index ba91182..0000000 --- a/src/ARE/Compiler.pm +++ /dev/null @@ -1,12 +0,0 @@ -use NQPHLL; - -class ARE::Compiler is HLL::Compiler { - INIT { - my $compiler := ARE::Compiler.new(); - $compiler.parsegrammar(ARE::Grammar); - $compiler.parseactions(ARE::Actions); - $compiler.language('ARE'); - } -} - -# vim: expandtab shiftwidth=4 ft=perl6: diff --git a/src/ARE/Grammar.pm b/src/ARE/Grammar.pm deleted file mode 100644 index c13974e..0000000 --- a/src/ARE/Grammar.pm +++ /dev/null @@ -1,54 +0,0 @@ -use NQPHLL; - -grammar ARE::Grammar is HLL::Grammar { - token TOP { - - [ $ || <.panic: 'Confused'> ] - } - - token nibbler { - [ '|' ]* - } - - token termish { - + - } - - token quantified_atom { - ? - } - - token atom { - [ - | <.barechar> [ <.barechar>+! > ]? - | - ] - } - - token barechar { <-[\\\[*+?^$]> } - - proto token quantifier { <...> } - token quantifier:sym<*> { } - token quantifier:sym<+> { } - token quantifier:sym { } - - - proto token metachar { <...> } - token metachar:sym<^> { } - token metachar:sym<$> { } - token metachar:sym<.> { } - token metachar:sym { \\ } - - - token metachar:sym<[> { - '[' - $=['^'?] - $=( [ \\ (.) | (<-[\]\\]>) ] [ '-' (.) ]? )* - ']' - } - - proto token backslash { <...> } - token backslash:sym { $=[<[dswDSW]>] } -} - -# vim: expandtab shiftwidth=4 ft=perl6: diff --git a/src/FileGlob/Actions.pm b/src/FileGlob/Actions.pm deleted file mode 100644 index 4f31556..0000000 --- a/src/FileGlob/Actions.pm +++ /dev/null @@ -1,16 +0,0 @@ -use NQPHLL; - -use src::StringGlob::Actions; - -class FileGlob::Actions is StringGlob::Actions { - - method metachar:sym<{>($/) { - my $ast := PAST::Regex.new( :pasttype, :node($/) ); - for $ { - $ast.push(PAST::Regex.new( ~$_, :pasttype, :node($/) ) ); - } - make $ast; - } -} - -# vim: expandtab shiftwidth=4 ft=perl6: diff --git a/src/FileGlob/Compiler.pm b/src/FileGlob/Compiler.pm deleted file mode 100644 index 62b5956..0000000 --- a/src/FileGlob/Compiler.pm +++ /dev/null @@ -1,12 +0,0 @@ -use NQPHLL; - -class FileGlob::Compiler is HLL::Compiler { - INIT { - my $compiler := FileGlob::Compiler.new(); - $compiler.parsegrammar(FileGlob::Grammar); - $compiler.parseactions(FileGlob::Actions); - $compiler.language('FileGlob'); - } -} - -# vim: expandtab shiftwidth=4 ft=perl6: diff --git a/src/FileGlob/Grammar.pm b/src/FileGlob/Grammar.pm deleted file mode 100644 index d881bf5..0000000 --- a/src/FileGlob/Grammar.pm +++ /dev/null @@ -1,17 +0,0 @@ -use NQPHLL; - -use src::StringGlob::Grammar; - -grammar FileGlob::Grammar is StringGlob::Grammar { - - # This is how globs do alternation - token metachar:sym<{> { - '{' ~ '}' [ +% ',' ] - } - - token word { <-[,}]>+ } - - token barechar { <-[\\\[*+?{]> } -} - -# vim: expandtab shiftwidth=4 ft=perl6: diff --git a/src/Partcl.pm b/src/Partcl.pm index 4fa74f4..183ce43 100644 --- a/src/Partcl.pm +++ b/src/Partcl.pm @@ -8,91 +8,10 @@ use src::Partcl::Grammar; use src::Partcl::Actions; use src::Partcl::Compiler; use src::Partcl::Operators; -use src::Partcl::commands::after; -use src::Partcl::commands::append; -use src::Partcl::commands::apply; -use src::Partcl::commands::array; -use src::Partcl::commands::binary; -use src::Partcl::commands::break; -use src::Partcl::commands::catch; -use src::Partcl::commands::cd; -use src::Partcl::commands::concat; -use src::Partcl::commands::continue; -use src::Partcl::commands::dict; -use src::Partcl::commands::eof; -use src::Partcl::commands::encoding; -use src::Partcl::commands::error; -use src::Partcl::commands::eval; -use src::Partcl::commands::exit; -use src::Partcl::commands::expr; -use src::Partcl::commands::fileevent; -use src::Partcl::commands::file; -use src::Partcl::commands::flush; -use src::Partcl::commands::foreach; -use src::Partcl::commands::format; -use src::Partcl::commands::for; -use src::Partcl::commands::gets; -use src::Partcl::commands::global; -use src::Partcl::commands::glob; -use src::Partcl::commands::if; -use src::Partcl::commands::incr; -use src::Partcl::commands::info; -use src::Partcl::commands::interp; -use src::Partcl::commands::join; -use src::Partcl::commands::lappend; -use src::Partcl::commands::lassign; -use src::Partcl::commands::lindex; -use src::Partcl::commands::linsert; -use src::Partcl::commands::list; -use src::Partcl::commands::llength; -use src::Partcl::commands::lrange; -use src::Partcl::commands::lrepeat; -use src::Partcl::commands::lreplace; -use src::Partcl::commands::lreverse; -use src::Partcl::commands::lset; -use src::Partcl::commands::lsort; -use src::Partcl::commands::namespace; -use src::Partcl::commands::package; -use src::Partcl::commands::proc; -use src::Partcl::commands::puts; -use src::Partcl::commands::pwd; -use src::Partcl::commands::regexp; -use src::Partcl::commands::rename; -use src::Partcl::commands::return; -use src::Partcl::commands::set; -use src::Partcl::commands::socket; -use src::Partcl::commands::source; -use src::Partcl::commands::split; -use src::Partcl::commands::string; -use src::Partcl::commands::subst; -use src::Partcl::commands::switch; -use src::Partcl::commands::time; -use src::Partcl::commands::trace; -use src::Partcl::commands::unset; -use src::Partcl::commands::uplevel; -use src::Partcl::commands::upvar; -use src::Partcl::commands::variable; -use src::Partcl::commands::vwait; -use src::Partcl::commands::while; -use src::TclArray; -use src::TclLexPad; -use src::TclList; -use src::TclString; -use src::ARE::Grammar; -use src::ARE::Actions; -use src::ARE::Compiler; -use src::StringGlob::Grammar; -use src::StringGlob::Actions; -use src::StringGlob::Compiler; -use src::FileGlob::Grammar; -use src::FileGlob::Actions; -use src::FileGlob::Compiler; use src::init; -use src::options; sub MAIN(*@ARGS) { - # XXX setup %LEXPAD? Partcl::Compiler.new().command_line(@ARGS); } diff --git a/src/Partcl/Actions.pm b/src/Partcl/Actions.pm index 83f984a..691e42a 100644 --- a/src/Partcl/Actions.pm +++ b/src/Partcl/Actions.pm @@ -5,60 +5,21 @@ class Partcl::Actions is HLL::Actions { method TOP($/) { make $.ast; } ## TOP_eval and TOP_expr create a PAST::Block that uses the - ## lexical scope given by the caller's %LEXPAD. method TOP_eval($/) { make eval_block($.ast); } method TOP_expr($/) { make eval_block($.ast); } sub eval_block($past) { ## This is the runtime equivalent of - ## register lexpad := DYNAMIC::<%LEXPAD>; ## The body of the code to be evaluated - my $lexpad_init := - PAST::Var.new( :name, :scope, :isdecl, - :viviself( PAST::Op.new(:pirop('find_dynamic_lex Ps'), '%LEXPAD')) - ); - - if ! pir::isnull(pir::find_dynamic_lex('@*PARTCL_COMPILER_NAMESPACE')) { - PAST::Block.new( PAST::Stmts.new( $lexpad_init ), $past, :hll, - :namespace(@*PARTCL_COMPILER_NAMESPACE) - ); - } else { - PAST::Block.new( PAST::Stmts.new( $lexpad_init ), $past, :hll); - } } ## TOP_proc creates a PAST::Block that initializes a - ## new lexical scope in %LEXPAD. method TOP_proc($/) { make lex_block($.ast); } sub lex_block($past) { ## This is the runtime equivalent of - ## register lexpad := - ## my %LEXPAD := TclLexPad.newpad(DYNAMIC::<%LEXPAD>); - my $lexpad_init := - PAST::Var.new( :name, :scope, :isdecl, - :viviself( - PAST::Var.new( :name<%LEXPAD>, :scope, :isdecl, - :viviself( - PAST::Op.new( - :pasttype, :name, - PAST::Var.new( :name, :scope, :namespace<> ), - PAST::Op.new(:pirop('find_dynamic_lex Ps'), '%LEXPAD') - ) - ) - ) - ) - ); - - if ! pir::isnull(pir::find_dynamic_lex('@*PARTCL_COMPILER_NAMESPACE')) { - PAST::Block.new( PAST::Stmts.new( $lexpad_init ), $past, :hll, - :namespace(@*PARTCL_COMPILER_NAMESPACE) - ); - } else { - PAST::Block.new( PAST::Stmts.new( $lexpad_init ), $past, :hll); - } } method body($/) { make $