figenc

[RADIOACTIVE] rsa and symmetric key encryption scripts and executables
git clone git://git.figbert.com/figenc.git
Log | Files | Refs | README

auto.tcl (21148B)


      1 # auto.tcl --
      2 #
      3 # utility procs formerly in init.tcl dealing with auto execution of commands
      4 # and can be auto loaded themselves.
      5 #
      6 # Copyright (c) 1991-1993 The Regents of the University of California.
      7 # Copyright (c) 1994-1998 Sun Microsystems, Inc.
      8 #
      9 # See the file "license.terms" for information on usage and redistribution of
     10 # this file, and for a DISCLAIMER OF ALL WARRANTIES.
     11 #
     12 
     13 # auto_reset --
     14 #
     15 # Destroy all cached information for auto-loading and auto-execution, so that
     16 # the information gets recomputed the next time it's needed.  Also delete any
     17 # commands that are listed in the auto-load index.
     18 #
     19 # Arguments:
     20 # None.
     21 
     22 proc auto_reset {} {
     23     global auto_execs auto_index auto_path
     24     if {[array exists auto_index]} {
     25 	foreach cmdName [array names auto_index] {
     26 	    set fqcn [namespace which $cmdName]
     27 	    if {$fqcn eq ""} {
     28 		continue
     29 	    }
     30 	    rename $fqcn {}
     31 	}
     32     }
     33     unset -nocomplain auto_execs auto_index ::tcl::auto_oldpath
     34     if {[catch {llength $auto_path}]} {
     35 	set auto_path [list [info library]]
     36     } elseif {[info library] ni $auto_path} {
     37 	lappend auto_path [info library]
     38     }
     39 }
     40 
     41 # tcl_findLibrary --
     42 #
     43 #	This is a utility for extensions that searches for a library directory
     44 #	using a canonical searching algorithm. A side effect is to source the
     45 #	initialization script and set a global library variable.
     46 #
     47 # Arguments:
     48 # 	basename	Prefix of the directory name, (e.g., "tk")
     49 #	version		Version number of the package, (e.g., "8.0")
     50 #	patch		Patchlevel of the package, (e.g., "8.0.3")
     51 #	initScript	Initialization script to source (e.g., tk.tcl)
     52 #	enVarName	environment variable to honor (e.g., TK_LIBRARY)
     53 #	varName		Global variable to set when done (e.g., tk_library)
     54 
     55 proc tcl_findLibrary {basename version patch initScript enVarName varName} {
     56     upvar #0 $varName the_library
     57     global auto_path env tcl_platform
     58 
     59     set dirs {}
     60     set errors {}
     61 
     62     # The C application may have hardwired a path, which we honor
     63 
     64     if {[info exists the_library] && $the_library ne ""} {
     65 	lappend dirs $the_library
     66     } else {
     67 	# Do the canonical search
     68 
     69 	# 1. From an environment variable, if it exists.  Placing this first
     70 	#    gives the end-user ultimate control to work-around any bugs, or
     71 	#    to customize.
     72 
     73         if {[info exists env($enVarName)]} {
     74             lappend dirs $env($enVarName)
     75         }
     76 
     77 	# 2. In the package script directory registered within the
     78 	#    configuration of the package itself.
     79 
     80 	catch {
     81 	    lappend dirs [::${basename}::pkgconfig get scriptdir,runtime]
     82 	}
     83 
     84 	# 3. Relative to auto_path directories.  This checks relative to the
     85 	# Tcl library as well as allowing loading of libraries added to the
     86 	# auto_path that is not relative to the core library or binary paths.
     87 	foreach d $auto_path {
     88 	    lappend dirs [file join $d $basename$version]
     89 	    if {$tcl_platform(platform) eq "unix"
     90 		    && $tcl_platform(os) eq "Darwin"} {
     91 		# 4. On MacOSX, check the Resources/Scripts subdir too
     92 		lappend dirs [file join $d $basename$version Resources Scripts]
     93 	    }
     94 	}
     95 
     96 	# 3. Various locations relative to the executable
     97 	# ../lib/foo1.0		(From bin directory in install hierarchy)
     98 	# ../../lib/foo1.0	(From bin/arch directory in install hierarchy)
     99 	# ../library		(From unix directory in build hierarchy)
    100 	#
    101 	# Remaining locations are out of date (when relevant, they ought to be
    102 	# covered by the $::auto_path seach above) and disabled.
    103 	#
    104 	# ../../library		(From unix/arch directory in build hierarchy)
    105 	# ../../foo1.0.1/library
    106 	#		(From unix directory in parallel build hierarchy)
    107 	# ../../../foo1.0.1/library
    108 	#		(From unix/arch directory in parallel build hierarchy)
    109 
    110         set parentDir [file dirname [file dirname [info nameofexecutable]]]
    111         set grandParentDir [file dirname $parentDir]
    112         lappend dirs [file join $parentDir lib $basename$version]
    113         lappend dirs [file join $grandParentDir lib $basename$version]
    114         lappend dirs [file join $parentDir library]
    115 	if {0} {
    116 	    lappend dirs [file join $grandParentDir library]
    117 	    lappend dirs [file join $grandParentDir $basename$patch library]
    118 	    lappend dirs [file join [file dirname $grandParentDir] \
    119 			      $basename$patch library]
    120 	}
    121     }
    122     # uniquify $dirs in order
    123     array set seen {}
    124     foreach i $dirs {
    125 	# Make sure $i is unique under normalization. Avoid repeated [source].
    126 	if {[interp issafe]} {
    127 	    # Safe interps have no [file normalize].
    128 	    set norm $i
    129 	} else {
    130 	    set norm [file normalize $i]
    131 	}
    132 	if {[info exists seen($norm)]} {
    133 	    continue
    134 	}
    135 	set seen($norm) {}
    136 
    137         set the_library $i
    138         set file [file join $i $initScript]
    139 
    140 	# source everything when in a safe interpreter because we have a
    141 	# source command, but no file exists command
    142 
    143         if {[interp issafe] || [file exists $file]} {
    144             if {![catch {uplevel #0 [list source $file]} msg opts]} {
    145                 return
    146             }
    147 	    append errors "$file: $msg\n"
    148 	    append errors [dict get $opts -errorinfo]\n
    149         }
    150     }
    151     unset -nocomplain the_library
    152     set msg "Can't find a usable $initScript in the following directories: \n"
    153     append msg "    $dirs\n\n"
    154     append msg "$errors\n\n"
    155     append msg "This probably means that $basename wasn't installed properly.\n"
    156     error $msg
    157 }
    158 
    159 
    160 # ----------------------------------------------------------------------
    161 # auto_mkindex
    162 # ----------------------------------------------------------------------
    163 # The following procedures are used to generate the tclIndex file from Tcl
    164 # source files.  They use a special safe interpreter to parse Tcl source
    165 # files, writing out index entries as "proc" commands are encountered.  This
    166 # implementation won't work in a safe interpreter, since a safe interpreter
    167 # can't create the special parser and mess with its commands.
    168 
    169 if {[interp issafe]} {
    170     return	;# Stop sourcing the file here
    171 }
    172 
    173 # auto_mkindex --
    174 # Regenerate a tclIndex file from Tcl source files.  Takes as argument the
    175 # name of the directory in which the tclIndex file is to be placed, followed
    176 # by any number of glob patterns to use in that directory to locate all of the
    177 # relevant files.
    178 #
    179 # Arguments:
    180 # dir -		Name of the directory in which to create an index.
    181 
    182 # args -	Any number of additional arguments giving the names of files
    183 #		within dir.  If no additional are given auto_mkindex will look
    184 #		for *.tcl.
    185 
    186 proc auto_mkindex {dir args} {
    187     if {[interp issafe]} {
    188         error "can't generate index within safe interpreter"
    189     }
    190 
    191     set oldDir [pwd]
    192     cd $dir
    193 
    194     append index "# Tcl autoload index file, version 2.0\n"
    195     append index "# This file is generated by the \"auto_mkindex\" command\n"
    196     append index "# and sourced to set up indexing information for one or\n"
    197     append index "# more commands.  Typically each line is a command that\n"
    198     append index "# sets an element in the auto_index array, where the\n"
    199     append index "# element name is the name of a command and the value is\n"
    200     append index "# a script that loads the command.\n\n"
    201     if {![llength $args]} {
    202 	set args *.tcl
    203     }
    204 
    205     auto_mkindex_parser::init
    206     foreach file [lsort [glob -- {*}$args]] {
    207 	try {
    208 	    append index [auto_mkindex_parser::mkindex $file]
    209 	} on error {msg opts} {
    210 	    cd $oldDir
    211 	    return -options $opts $msg
    212 	}
    213     }
    214     auto_mkindex_parser::cleanup
    215 
    216     set fid [open "tclIndex" w]
    217     puts -nonewline $fid $index
    218     close $fid
    219     cd $oldDir
    220 }
    221 
    222 # Original version of auto_mkindex that just searches the source code for
    223 # "proc" at the beginning of the line.
    224 
    225 proc auto_mkindex_old {dir args} {
    226     set oldDir [pwd]
    227     cd $dir
    228     set dir [pwd]
    229     append index "# Tcl autoload index file, version 2.0\n"
    230     append index "# This file is generated by the \"auto_mkindex\" command\n"
    231     append index "# and sourced to set up indexing information for one or\n"
    232     append index "# more commands.  Typically each line is a command that\n"
    233     append index "# sets an element in the auto_index array, where the\n"
    234     append index "# element name is the name of a command and the value is\n"
    235     append index "# a script that loads the command.\n\n"
    236     if {![llength $args]} {
    237 	set args *.tcl
    238     }
    239     foreach file [lsort [glob -- {*}$args]] {
    240 	set f ""
    241 	set error [catch {
    242 	    set f [open $file]
    243 	    while {[gets $f line] >= 0} {
    244 		if {[regexp {^proc[ 	]+([^ 	]*)} $line match procName]} {
    245 		    set procName [lindex [auto_qualify $procName "::"] 0]
    246 		    append index "set [list auto_index($procName)]"
    247 		    append index " \[list source \[file join \$dir [list $file]\]\]\n"
    248 		}
    249 	    }
    250 	    close $f
    251 	} msg opts]
    252 	if {$error} {
    253 	    catch {close $f}
    254 	    cd $oldDir
    255 	    return -options $opts $msg
    256 	}
    257     }
    258     set f ""
    259     set error [catch {
    260 	set f [open tclIndex w]
    261 	puts -nonewline $f $index
    262 	close $f
    263 	cd $oldDir
    264     } msg opts]
    265     if {$error} {
    266 	catch {close $f}
    267 	cd $oldDir
    268 	error $msg $info $code
    269 	return -options $opts $msg
    270     }
    271 }
    272 
    273 # Create a safe interpreter that can be used to parse Tcl source files
    274 # generate a tclIndex file for autoloading.  This interp contains commands for
    275 # things that need index entries.  Each time a command is executed, it writes
    276 # an entry out to the index file.
    277 
    278 namespace eval auto_mkindex_parser {
    279     variable parser ""          ;# parser used to build index
    280     variable index ""           ;# maintains index as it is built
    281     variable scriptFile ""      ;# name of file being processed
    282     variable contextStack ""    ;# stack of namespace scopes
    283     variable imports ""         ;# keeps track of all imported cmds
    284     variable initCommands       ;# list of commands that create aliases
    285     if {![info exists initCommands]} {
    286 	set initCommands [list]
    287     }
    288 
    289     proc init {} {
    290 	variable parser
    291 	variable initCommands
    292 
    293 	if {![interp issafe]} {
    294 	    set parser [interp create -safe]
    295 	    $parser hide info
    296 	    $parser hide rename
    297 	    $parser hide proc
    298 	    $parser hide namespace
    299 	    $parser hide eval
    300 	    $parser hide puts
    301 	    foreach ns [$parser invokehidden namespace children ::] {
    302 		# MUST NOT DELETE "::tcl" OR BAD THINGS HAPPEN!
    303 		if {$ns eq "::tcl"} continue
    304 		$parser invokehidden namespace delete $ns
    305 	    }
    306 	    foreach cmd [$parser invokehidden info commands ::*] {
    307 		$parser invokehidden rename $cmd {}
    308 	    }
    309 	    $parser invokehidden proc unknown {args} {}
    310 
    311 	    # We'll need access to the "namespace" command within the
    312 	    # interp.  Put it back, but move it out of the way.
    313 
    314 	    $parser expose namespace
    315 	    $parser invokehidden rename namespace _%@namespace
    316 	    $parser expose eval
    317 	    $parser invokehidden rename eval _%@eval
    318 
    319 	    # Install all the registered psuedo-command implementations
    320 
    321 	    foreach cmd $initCommands {
    322 		eval $cmd
    323 	    }
    324 	}
    325     }
    326     proc cleanup {} {
    327 	variable parser
    328 	interp delete $parser
    329 	unset parser
    330     }
    331 }
    332 
    333 # auto_mkindex_parser::mkindex --
    334 #
    335 # Used by the "auto_mkindex" command to create a "tclIndex" file for the given
    336 # Tcl source file.  Executes the commands in the file, and handles things like
    337 # the "proc" command by adding an entry for the index file.  Returns a string
    338 # that represents the index file.
    339 #
    340 # Arguments:
    341 #	file	Name of Tcl source file to be indexed.
    342 
    343 proc auto_mkindex_parser::mkindex {file} {
    344     variable parser
    345     variable index
    346     variable scriptFile
    347     variable contextStack
    348     variable imports
    349 
    350     set scriptFile $file
    351 
    352     set fid [open $file]
    353     set contents [read $fid]
    354     close $fid
    355 
    356     # There is one problem with sourcing files into the safe interpreter:
    357     # references like "$x" will fail since code is not really being executed
    358     # and variables do not really exist.  To avoid this, we replace all $ with
    359     # \0 (literally, the null char) later, when getting proc names we will
    360     # have to reverse this replacement, in case there were any $ in the proc
    361     # name.  This will cause a problem if somebody actually tries to have a \0
    362     # in their proc name.  Too bad for them.
    363     set contents [string map [list \$ \0] $contents]
    364 
    365     set index ""
    366     set contextStack ""
    367     set imports ""
    368 
    369     $parser eval $contents
    370 
    371     foreach name $imports {
    372         catch {$parser eval [list _%@namespace forget $name]}
    373     }
    374     return $index
    375 }
    376 
    377 # auto_mkindex_parser::hook command
    378 #
    379 # Registers a Tcl command to evaluate when initializing the slave interpreter
    380 # used by the mkindex parser.  The command is evaluated in the master
    381 # interpreter, and can use the variable auto_mkindex_parser::parser to get to
    382 # the slave
    383 
    384 proc auto_mkindex_parser::hook {cmd} {
    385     variable initCommands
    386 
    387     lappend initCommands $cmd
    388 }
    389 
    390 # auto_mkindex_parser::slavehook command
    391 #
    392 # Registers a Tcl command to evaluate when initializing the slave interpreter
    393 # used by the mkindex parser.  The command is evaluated in the slave
    394 # interpreter.
    395 
    396 proc auto_mkindex_parser::slavehook {cmd} {
    397     variable initCommands
    398 
    399     # The $parser variable is defined to be the name of the slave interpreter
    400     # when this command is used later.
    401 
    402     lappend initCommands "\$parser eval [list $cmd]"
    403 }
    404 
    405 # auto_mkindex_parser::command --
    406 #
    407 # Registers a new command with the "auto_mkindex_parser" interpreter that
    408 # parses Tcl files.  These commands are fake versions of things like the
    409 # "proc" command.  When you execute them, they simply write out an entry to a
    410 # "tclIndex" file for auto-loading.
    411 #
    412 # This procedure allows extensions to register their own commands with the
    413 # auto_mkindex facility.  For example, a package like [incr Tcl] might
    414 # register a "class" command so that class definitions could be added to a
    415 # "tclIndex" file for auto-loading.
    416 #
    417 # Arguments:
    418 #	name 	Name of command recognized in Tcl files.
    419 #	arglist	Argument list for command.
    420 #	body 	Implementation of command to handle indexing.
    421 
    422 proc auto_mkindex_parser::command {name arglist body} {
    423     hook [list auto_mkindex_parser::commandInit $name $arglist $body]
    424 }
    425 
    426 # auto_mkindex_parser::commandInit --
    427 #
    428 # This does the actual work set up by auto_mkindex_parser::command. This is
    429 # called when the interpreter used by the parser is created.
    430 #
    431 # Arguments:
    432 #	name 	Name of command recognized in Tcl files.
    433 #	arglist	Argument list for command.
    434 #	body 	Implementation of command to handle indexing.
    435 
    436 proc auto_mkindex_parser::commandInit {name arglist body} {
    437     variable parser
    438 
    439     set ns [namespace qualifiers $name]
    440     set tail [namespace tail $name]
    441     if {$ns eq ""} {
    442         set fakeName [namespace current]::_%@fake_$tail
    443     } else {
    444         set fakeName [namespace current]::[string map {:: _} _%@fake_$name]
    445     }
    446     proc $fakeName $arglist $body
    447 
    448     # YUK!  Tcl won't let us alias fully qualified command names, so we can't
    449     # handle names like "::itcl::class".  Instead, we have to build procs with
    450     # the fully qualified names, and have the procs point to the aliases.
    451 
    452     if {[string match *::* $name]} {
    453         set exportCmd [list _%@namespace export [namespace tail $name]]
    454         $parser eval [list _%@namespace eval $ns $exportCmd]
    455 
    456 	# The following proc definition does not work if you want to tolerate
    457 	# space or something else diabolical in the procedure name, (i.e.,
    458 	# space in $alias). The following does not work:
    459 	#   "_%@eval {$alias} \$args"
    460 	# because $alias gets concat'ed to $args.  The following does not work
    461 	# because $cmd is somehow undefined
    462 	#   "set cmd {$alias} \; _%@eval {\$cmd} \$args"
    463 	# A gold star to someone that can make test autoMkindex-3.3 work
    464 	# properly
    465 
    466         set alias [namespace tail $fakeName]
    467         $parser invokehidden proc $name {args} "_%@eval {$alias} \$args"
    468         $parser alias $alias $fakeName
    469     } else {
    470         $parser alias $name $fakeName
    471     }
    472     return
    473 }
    474 
    475 # auto_mkindex_parser::fullname --
    476 #
    477 # Used by commands like "proc" within the auto_mkindex parser.  Returns the
    478 # qualified namespace name for the "name" argument.  If the "name" does not
    479 # start with "::", elements are added from the current namespace stack to
    480 # produce a qualified name.  Then, the name is examined to see whether or not
    481 # it should really be qualified.  If the name has more than the leading "::",
    482 # it is returned as a fully qualified name.  Otherwise, it is returned as a
    483 # simple name.  That way, the Tcl autoloader will recognize it properly.
    484 #
    485 # Arguments:
    486 # name -		Name that is being added to index.
    487 
    488 proc auto_mkindex_parser::fullname {name} {
    489     variable contextStack
    490 
    491     if {![string match ::* $name]} {
    492         foreach ns $contextStack {
    493             set name "${ns}::$name"
    494             if {[string match ::* $name]} {
    495                 break
    496             }
    497         }
    498     }
    499 
    500     if {[namespace qualifiers $name] eq ""} {
    501         set name [namespace tail $name]
    502     } elseif {![string match ::* $name]} {
    503         set name "::$name"
    504     }
    505 
    506     # Earlier, mkindex replaced all $'s with \0.  Now, we have to reverse that
    507     # replacement.
    508     return [string map [list \0 \$] $name]
    509 }
    510 
    511 # auto_mkindex_parser::indexEntry --
    512 #
    513 # Used by commands like "proc" within the auto_mkindex parser to add a
    514 # correctly-quoted entry to the index. This is shared code so it is done
    515 # *right*, in one place.
    516 #
    517 # Arguments:
    518 # name -		Name that is being added to index.
    519 
    520 proc auto_mkindex_parser::indexEntry {name} {
    521     variable index
    522     variable scriptFile
    523 
    524     # We convert all metacharacters to their backslashed form, and pre-split
    525     # the file name that we know about (which will be a proper list, and so
    526     # correctly quoted).
    527 
    528     set name [string range [list \}[fullname $name]] 2 end]
    529     set filenameParts [file split $scriptFile]
    530 
    531     append index [format \
    532 	    {set auto_index(%s) [list source [file join $dir %s]]%s} \
    533 	    $name $filenameParts \n]
    534     return
    535 }
    536 
    537 if {[llength $::auto_mkindex_parser::initCommands]} {
    538     return
    539 }
    540 
    541 # Register all of the procedures for the auto_mkindex parser that will build
    542 # the "tclIndex" file.
    543 
    544 # AUTO MKINDEX:  proc name arglist body
    545 # Adds an entry to the auto index list for the given procedure name.
    546 
    547 auto_mkindex_parser::command proc {name args} {
    548     indexEntry $name
    549 }
    550 
    551 # Conditionally add support for Tcl byte code files.  There are some tricky
    552 # details here.  First, we need to get the tbcload library initialized in the
    553 # current interpreter.  We cannot load tbcload into the slave until we have
    554 # done so because it needs access to the tcl_patchLevel variable.  Second,
    555 # because the package index file may defer loading the library until we invoke
    556 # a command, we need to explicitly invoke auto_load to force it to be loaded.
    557 # This should be a noop if the package has already been loaded
    558 
    559 auto_mkindex_parser::hook {
    560     try {
    561 	package require tbcload
    562     } on error {} {
    563 	# OK, don't have it so do nothing
    564     } on ok {} {
    565 	if {[namespace which -command tbcload::bcproc] eq ""} {
    566 	    auto_load tbcload::bcproc
    567 	}
    568 	load {} tbcload $auto_mkindex_parser::parser
    569 
    570 	# AUTO MKINDEX:  tbcload::bcproc name arglist body
    571 	# Adds an entry to the auto index list for the given pre-compiled
    572 	# procedure name.
    573 
    574 	auto_mkindex_parser::commandInit tbcload::bcproc {name args} {
    575 	    indexEntry $name
    576 	}
    577     }
    578 }
    579 
    580 # AUTO MKINDEX:  namespace eval name command ?arg arg...?
    581 # Adds the namespace name onto the context stack and evaluates the associated
    582 # body of commands.
    583 #
    584 # AUTO MKINDEX:  namespace import ?-force? pattern ?pattern...?
    585 # Performs the "import" action in the parser interpreter.  This is important
    586 # for any commands contained in a namespace that affect the index.  For
    587 # example, a script may say "itcl::class ...", or it may import "itcl::*" and
    588 # then say "class ...".  This procedure does the import operation, but keeps
    589 # track of imported patterns so we can remove the imports later.
    590 
    591 auto_mkindex_parser::command namespace {op args} {
    592     switch -- $op {
    593         eval {
    594             variable parser
    595             variable contextStack
    596 
    597             set name [lindex $args 0]
    598             set args [lrange $args 1 end]
    599 
    600             set contextStack [linsert $contextStack 0 $name]
    601 	    $parser eval [list _%@namespace eval $name] $args
    602             set contextStack [lrange $contextStack 1 end]
    603         }
    604         import {
    605             variable parser
    606             variable imports
    607             foreach pattern $args {
    608                 if {$pattern ne "-force"} {
    609                     lappend imports $pattern
    610                 }
    611             }
    612             catch {$parser eval "_%@namespace import $args"}
    613         }
    614 	ensemble {
    615 	    variable parser
    616 	    variable contextStack
    617 	    if {[lindex $args 0] eq "create"} {
    618 		set name ::[join [lreverse $contextStack] ::]
    619 		catch {
    620 		    set name [dict get [lrange $args 1 end] -command]
    621 		    if {![string match ::* $name]} {
    622 			set name ::[join [lreverse $contextStack] ::]$name
    623 		    }
    624 		    regsub -all ::+ $name :: name
    625 		}
    626 		# create artifical proc to force an entry in the tclIndex
    627 		$parser eval [list ::proc $name {} {}]
    628 	    }
    629 	}
    630     }
    631 }
    632 
    633 # AUTO MKINDEX:  oo::class create name ?definition?
    634 # Adds an entry to the auto index list for the given class name.
    635 auto_mkindex_parser::command oo::class {op name {body ""}} {
    636     if {$op eq "create"} {
    637 	indexEntry $name
    638     }
    639 }
    640 auto_mkindex_parser::command class {op name {body ""}} {
    641     if {$op eq "create"} {
    642 	indexEntry $name
    643     }
    644 }
    645 
    646 return