os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/library/package.tcl
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 # package.tcl --
     2 #
     3 # utility procs formerly in init.tcl which can be loaded on demand
     4 # for package management.
     5 #
     6 # RCS: @(#) $Id: package.tcl,v 1.23.2.4 2006/09/22 01:26:24 andreas_kupries Exp $
     7 #
     8 # Copyright (c) 1991-1993 The Regents of the University of California.
     9 # Copyright (c) 1994-1998 Sun Microsystems, Inc.
    10 #
    11 # See the file "license.terms" for information on usage and redistribution
    12 # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    13 #
    14 
    15 # Create the package namespace
    16 namespace eval ::pkg {
    17 }
    18 
    19 # pkg_compareExtension --
    20 #
    21 #  Used internally by pkg_mkIndex to compare the extension of a file to
    22 #  a given extension. On Windows, it uses a case-insensitive comparison
    23 #  because the file system can be file insensitive.
    24 #
    25 # Arguments:
    26 #  fileName	name of a file whose extension is compared
    27 #  ext		(optional) The extension to compare against; you must
    28 #		provide the starting dot.
    29 #		Defaults to [info sharedlibextension]
    30 #
    31 # Results:
    32 #  Returns 1 if the extension matches, 0 otherwise
    33 
    34 proc pkg_compareExtension { fileName {ext {}} } {
    35     global tcl_platform
    36     if {$ext eq ""} {set ext [info sharedlibextension]}
    37     if {$tcl_platform(platform) eq "windows"} {
    38         return [string equal -nocase [file extension $fileName] $ext]
    39     } else {
    40         # Some unices add trailing numbers after the .so, so
    41         # we could have something like '.so.1.2'.
    42         set root $fileName
    43         while {1} {
    44             set currExt [file extension $root]
    45             if {$currExt eq $ext} {
    46                 return 1
    47             } 
    48 
    49 	    # The current extension does not match; if it is not a numeric
    50 	    # value, quit, as we are only looking to ignore version number
    51 	    # extensions.  Otherwise we might return 1 in this case:
    52 	    #		pkg_compareExtension foo.so.bar .so
    53 	    # which should not match.
    54 
    55 	    if { ![string is integer -strict [string range $currExt 1 end]] } {
    56 		return 0
    57 	    }
    58             set root [file rootname $root]
    59 	}
    60     }
    61 }
    62 
    63 # pkg_mkIndex --
    64 # This procedure creates a package index in a given directory.  The
    65 # package index consists of a "pkgIndex.tcl" file whose contents are
    66 # a Tcl script that sets up package information with "package require"
    67 # commands.  The commands describe all of the packages defined by the
    68 # files given as arguments.
    69 #
    70 # Arguments:
    71 # -direct		(optional) If this flag is present, the generated
    72 #			code in pkgMkIndex.tcl will cause the package to be
    73 #			loaded when "package require" is executed, rather
    74 #			than lazily when the first reference to an exported
    75 #			procedure in the package is made.
    76 # -verbose		(optional) Verbose output; the name of each file that
    77 #			was successfully rocessed is printed out. Additionally,
    78 #			if processing of a file failed a message is printed.
    79 # -load pat		(optional) Preload any packages whose names match
    80 #			the pattern.  Used to handle DLLs that depend on
    81 #			other packages during their Init procedure.
    82 # dir -			Name of the directory in which to create the index.
    83 # args -		Any number of additional arguments, each giving
    84 #			a glob pattern that matches the names of one or
    85 #			more shared libraries or Tcl script files in
    86 #			dir.
    87 
    88 proc pkg_mkIndex {args} {
    89     global errorCode errorInfo
    90     set usage {"pkg_mkIndex ?-direct? ?-lazy? ?-load pattern? ?-verbose? ?--? dir ?pattern ...?"};
    91 
    92     set argCount [llength $args]
    93     if {$argCount < 1} {
    94 	return -code error "wrong # args: should be\n$usage"
    95     }
    96 
    97     set more ""
    98     set direct 1
    99     set doVerbose 0
   100     set loadPat ""
   101     for {set idx 0} {$idx < $argCount} {incr idx} {
   102 	set flag [lindex $args $idx]
   103 	switch -glob -- $flag {
   104 	    -- {
   105 		# done with the flags
   106 		incr idx
   107 		break
   108 	    }
   109 	    -verbose {
   110 		set doVerbose 1
   111 	    }
   112 	    -lazy {
   113 		set direct 0
   114 		append more " -lazy"
   115 	    }
   116 	    -direct {
   117 		append more " -direct"
   118 	    }
   119 	    -load {
   120 		incr idx
   121 		set loadPat [lindex $args $idx]
   122 		append more " -load $loadPat"
   123 	    }
   124 	    -* {
   125 		return -code error "unknown flag $flag: should be\n$usage"
   126 	    }
   127 	    default {
   128 		# done with the flags
   129 		break
   130 	    }
   131 	}
   132     }
   133 
   134     set dir [lindex $args $idx]
   135     set patternList [lrange $args [expr {$idx + 1}] end]
   136     if {[llength $patternList] == 0} {
   137 	set patternList [list "*.tcl" "*[info sharedlibextension]"]
   138     }
   139 
   140     set oldDir [pwd]
   141     cd $dir
   142 
   143     if {[catch {eval [linsert $patternList 0 glob --]} fileList]} {
   144 	global errorCode errorInfo
   145 	cd $oldDir
   146 	return -code error -errorcode $errorCode -errorinfo $errorInfo $fileList
   147     }
   148     foreach file $fileList {
   149 	# For each file, figure out what commands and packages it provides.
   150 	# To do this, create a child interpreter, load the file into the
   151 	# interpreter, and get a list of the new commands and packages
   152 	# that are defined.
   153 
   154 	if {$file eq "pkgIndex.tcl"} {
   155 	    continue
   156 	}
   157 
   158 	# Changed back to the original directory before initializing the
   159 	# slave in case TCL_LIBRARY is a relative path (e.g. in the test
   160 	# suite). 
   161 
   162 	cd $oldDir
   163 	set c [interp create]
   164 
   165 	# Load into the child any packages currently loaded in the parent
   166 	# interpreter that match the -load pattern.
   167 
   168 	if {$loadPat ne ""} {
   169 	    if {$doVerbose} {
   170 		tclLog "currently loaded packages: '[info loaded]'"
   171 		tclLog "trying to load all packages matching $loadPat"
   172 	    }
   173 	    if {![llength [info loaded]]} {
   174 		tclLog "warning: no packages are currently loaded, nothing"
   175 		tclLog "can possibly match '$loadPat'"
   176 	    }
   177 	}
   178 	foreach pkg [info loaded] {
   179 	    if {! [string match -nocase $loadPat [lindex $pkg 1]]} {
   180 		continue
   181 	    }
   182 	    if {$doVerbose} {
   183 		tclLog "package [lindex $pkg 1] matches '$loadPat'"
   184 	    }
   185 	    if {[catch {
   186 		load [lindex $pkg 0] [lindex $pkg 1] $c
   187 	    } err]} {
   188 		if {$doVerbose} {
   189 		    tclLog "warning: load [lindex $pkg 0] [lindex $pkg 1]\nfailed with: $err"
   190 		}
   191 	    } elseif {$doVerbose} {
   192 		tclLog "loaded [lindex $pkg 0] [lindex $pkg 1]"
   193 	    }
   194 	    if {[lindex $pkg 1] eq "Tk"} {
   195 		# Withdraw . if Tk was loaded, to avoid showing a window.
   196 		$c eval [list wm withdraw .]
   197 	    }
   198 	}
   199 	cd $dir
   200 
   201 	$c eval {
   202 	    # Stub out the package command so packages can
   203 	    # require other packages.
   204 
   205 	    rename package __package_orig
   206 	    proc package {what args} {
   207 		switch -- $what {
   208 		    require { return ; # ignore transitive requires }
   209 		    default { uplevel 1 [linsert $args 0 __package_orig $what] }
   210 		}
   211 	    }
   212 	    proc tclPkgUnknown args {}
   213 	    package unknown tclPkgUnknown
   214 
   215 	    # Stub out the unknown command so package can call
   216 	    # into each other during their initialilzation.
   217 
   218 	    proc unknown {args} {}
   219 
   220 	    # Stub out the auto_import mechanism
   221 
   222 	    proc auto_import {args} {}
   223 
   224 	    # reserve the ::tcl namespace for support procs
   225 	    # and temporary variables.  This might make it awkward
   226 	    # to generate a pkgIndex.tcl file for the ::tcl namespace.
   227 
   228 	    namespace eval ::tcl {
   229 		variable file		;# Current file being processed
   230 		variable direct		;# -direct flag value
   231 		variable x		;# Loop variable
   232 		variable debug		;# For debugging
   233 		variable type		;# "load" or "source", for -direct
   234 		variable namespaces	;# Existing namespaces (e.g., ::tcl)
   235 		variable packages	;# Existing packages (e.g., Tcl)
   236 		variable origCmds	;# Existing commands
   237 		variable newCmds	;# Newly created commands
   238 		variable newPkgs {}	;# Newly created packages
   239 	    }
   240 	}
   241 
   242 	$c eval [list set ::tcl::file $file]
   243 	$c eval [list set ::tcl::direct $direct]
   244 
   245 	# Download needed procedures into the slave because we've
   246 	# just deleted the unknown procedure.  This doesn't handle
   247 	# procedures with default arguments.
   248 
   249 	foreach p {pkg_compareExtension} {
   250 	    $c eval [list proc $p [info args $p] [info body $p]]
   251 	}
   252 
   253 	if {[catch {
   254 	    $c eval {
   255 		set ::tcl::debug "loading or sourcing"
   256 
   257 		# we need to track command defined by each package even in
   258 		# the -direct case, because they are needed internally by
   259 		# the "partial pkgIndex.tcl" step above.
   260 
   261 		proc ::tcl::GetAllNamespaces {{root ::}} {
   262 		    set list $root
   263 		    foreach ns [namespace children $root] {
   264 			eval [linsert [::tcl::GetAllNamespaces $ns] 0 \
   265 				lappend list]
   266 		    }
   267 		    return $list
   268 		}
   269 
   270 		# init the list of existing namespaces, packages, commands
   271 
   272 		foreach ::tcl::x [::tcl::GetAllNamespaces] {
   273 		    set ::tcl::namespaces($::tcl::x) 1
   274 		}
   275 		foreach ::tcl::x [package names] {
   276 		    if {[package provide $::tcl::x] ne ""} {
   277 			set ::tcl::packages($::tcl::x) 1
   278 		    }
   279 		}
   280 		set ::tcl::origCmds [info commands]
   281 
   282 		# Try to load the file if it has the shared library
   283 		# extension, otherwise source it.  It's important not to
   284 		# try to load files that aren't shared libraries, because
   285 		# on some systems (like SunOS) the loader will abort the
   286 		# whole application when it gets an error.
   287 
   288 		if {[pkg_compareExtension $::tcl::file [info sharedlibextension]]} {
   289 		    # The "file join ." command below is necessary.
   290 		    # Without it, if the file name has no \'s and we're
   291 		    # on UNIX, the load command will invoke the
   292 		    # LD_LIBRARY_PATH search mechanism, which could cause
   293 		    # the wrong file to be used.
   294 
   295 		    set ::tcl::debug loading
   296 		    load [file join . $::tcl::file]
   297 		    set ::tcl::type load
   298 		} else {
   299 		    set ::tcl::debug sourcing
   300 		    source $::tcl::file
   301 		    set ::tcl::type source
   302 		}
   303 
   304 		# As a performance optimization, if we are creating 
   305 		# direct load packages, don't bother figuring out the 
   306 		# set of commands created by the new packages.  We 
   307 		# only need that list for setting up the autoloading 
   308 		# used in the non-direct case.
   309 		if { !$::tcl::direct } {
   310 		    # See what new namespaces appeared, and import commands
   311 		    # from them.  Only exported commands go into the index.
   312 		    
   313 		    foreach ::tcl::x [::tcl::GetAllNamespaces] {
   314 			if {! [info exists ::tcl::namespaces($::tcl::x)]} {
   315 			    namespace import -force ${::tcl::x}::*
   316 			}
   317 
   318 			# Figure out what commands appeared
   319 			
   320 			foreach ::tcl::x [info commands] {
   321 			    set ::tcl::newCmds($::tcl::x) 1
   322 			}
   323 			foreach ::tcl::x $::tcl::origCmds {
   324 			    unset -nocomplain ::tcl::newCmds($::tcl::x)
   325 			}
   326 			foreach ::tcl::x [array names ::tcl::newCmds] {
   327 			    # determine which namespace a command comes from
   328 			    
   329 			    set ::tcl::abs [namespace origin $::tcl::x]
   330 			    
   331 			    # special case so that global names have no leading
   332 			    # ::, this is required by the unknown command
   333 			    
   334 			    set ::tcl::abs \
   335 				    [lindex [auto_qualify $::tcl::abs ::] 0]
   336 			    
   337 			    if {$::tcl::x ne $::tcl::abs} {
   338 				# Name changed during qualification
   339 				
   340 				set ::tcl::newCmds($::tcl::abs) 1
   341 				unset ::tcl::newCmds($::tcl::x)
   342 			    }
   343 			}
   344 		    }
   345 		}
   346 
   347 		# Look through the packages that appeared, and if there is
   348 		# a version provided, then record it
   349 
   350 		foreach ::tcl::x [package names] {
   351 		    if {[package provide $::tcl::x] ne ""
   352 			    && ![info exists ::tcl::packages($::tcl::x)]} {
   353 			lappend ::tcl::newPkgs \
   354 			    [list $::tcl::x [package provide $::tcl::x]]
   355 		    }
   356 		}
   357 	    }
   358 	} msg] == 1} {
   359 	    set what [$c eval set ::tcl::debug]
   360 	    if {$doVerbose} {
   361 		tclLog "warning: error while $what $file: $msg"
   362 	    }
   363 	} else {
   364 	    set what [$c eval set ::tcl::debug]
   365 	    if {$doVerbose} {
   366 		tclLog "successful $what of $file"
   367 	    }
   368 	    set type [$c eval set ::tcl::type]
   369 	    set cmds [lsort [$c eval array names ::tcl::newCmds]]
   370 	    set pkgs [$c eval set ::tcl::newPkgs]
   371 	    if {$doVerbose} {
   372 		if { !$direct } {
   373 		    tclLog "commands provided were $cmds"
   374 		}
   375 		tclLog "packages provided were $pkgs"
   376 	    }
   377 	    if {[llength $pkgs] > 1} {
   378 		tclLog "warning: \"$file\" provides more than one package ($pkgs)"
   379 	    }
   380 	    foreach pkg $pkgs {
   381 		# cmds is empty/not used in the direct case
   382 		lappend files($pkg) [list $file $type $cmds]
   383 	    }
   384 
   385 	    if {$doVerbose} {
   386 		tclLog "processed $file"
   387 	    }
   388 	}
   389 	interp delete $c
   390     }
   391 
   392     append index "# Tcl package index file, version 1.1\n"
   393     append index "# This file is generated by the \"pkg_mkIndex$more\" command\n"
   394     append index "# and sourced either when an application starts up or\n"
   395     append index "# by a \"package unknown\" script.  It invokes the\n"
   396     append index "# \"package ifneeded\" command to set up package-related\n"
   397     append index "# information so that packages will be loaded automatically\n"
   398     append index "# in response to \"package require\" commands.  When this\n"
   399     append index "# script is sourced, the variable \$dir must contain the\n"
   400     append index "# full path name of this file's directory.\n"
   401 
   402     foreach pkg [lsort [array names files]] {
   403 	set cmd {}
   404 	foreach {name version} $pkg {
   405 	    break
   406 	}
   407 	lappend cmd ::pkg::create -name $name -version $version
   408 	foreach spec $files($pkg) {
   409 	    foreach {file type procs} $spec {
   410 		if { $direct } {
   411 		    set procs {}
   412 		}
   413 		lappend cmd "-$type" [list $file $procs]
   414 	    }
   415 	}
   416 	append index "\n[eval $cmd]"
   417     }
   418 
   419     set f [open pkgIndex.tcl w]
   420     puts $f $index
   421     close $f
   422     cd $oldDir
   423 }
   424 
   425 # tclPkgSetup --
   426 # This is a utility procedure use by pkgIndex.tcl files.  It is invoked
   427 # as part of a "package ifneeded" script.  It calls "package provide"
   428 # to indicate that a package is available, then sets entries in the
   429 # auto_index array so that the package's files will be auto-loaded when
   430 # the commands are used.
   431 #
   432 # Arguments:
   433 # dir -			Directory containing all the files for this package.
   434 # pkg -			Name of the package (no version number).
   435 # version -		Version number for the package, such as 2.1.3.
   436 # files -		List of files that constitute the package.  Each
   437 #			element is a sub-list with three elements.  The first
   438 #			is the name of a file relative to $dir, the second is
   439 #			"load" or "source", indicating whether the file is a
   440 #			loadable binary or a script to source, and the third
   441 #			is a list of commands defined by this file.
   442 
   443 proc tclPkgSetup {dir pkg version files} {
   444     global auto_index
   445 
   446     package provide $pkg $version
   447     foreach fileInfo $files {
   448 	set f [lindex $fileInfo 0]
   449 	set type [lindex $fileInfo 1]
   450 	foreach cmd [lindex $fileInfo 2] {
   451 	    if {$type eq "load"} {
   452 		set auto_index($cmd) [list load [file join $dir $f] $pkg]
   453 	    } else {
   454 		set auto_index($cmd) [list source [file join $dir $f]]
   455 	    } 
   456 	}
   457     }
   458 }
   459 
   460 # tclPkgUnknown --
   461 # This procedure provides the default for the "package unknown" function.
   462 # It is invoked when a package that's needed can't be found.  It scans
   463 # the auto_path directories and their immediate children looking for
   464 # pkgIndex.tcl files and sources any such files that are found to setup
   465 # the package database.  (On the Macintosh we also search for pkgIndex
   466 # TEXT resources in all files.)  As it searches, it will recognize changes
   467 # to the auto_path and scan any new directories.
   468 #
   469 # Arguments:
   470 # name -		Name of desired package.  Not used.
   471 # version -		Version of desired package.  Not used.
   472 # exact -		Either "-exact" or omitted.  Not used.
   473 
   474 
   475 proc tclPkgUnknown [expr {
   476 			  [info exists tcl_platform(tip,268)]
   477 			  ? "name args"
   478 			  : "name version {exact {}}"
   479 		      }] {
   480     global auto_path env
   481 
   482     if {![info exists auto_path]} {
   483 	return
   484     }
   485     # Cache the auto_path, because it may change while we run through
   486     # the first set of pkgIndex.tcl files
   487     set old_path [set use_path $auto_path]
   488     while {[llength $use_path]} {
   489 	set dir [lindex $use_path end]
   490 	
   491 	# Make sure we only scan each directory one time.
   492 	if {[info exists tclSeenPath($dir)]} {
   493 	    set use_path [lrange $use_path 0 end-1]
   494 	    continue
   495 	}
   496 	set tclSeenPath($dir) 1
   497 
   498 	# we can't use glob in safe interps, so enclose the following
   499 	# in a catch statement, where we get the pkgIndex files out
   500 	# of the subdirectories
   501 	catch {
   502 	    foreach file [glob -directory $dir -join -nocomplain \
   503 		    * pkgIndex.tcl] {
   504 		set dir [file dirname $file]
   505 		if {![info exists procdDirs($dir)] && [file readable $file]} {
   506 		    if {[catch {source $file} msg]} {
   507 			tclLog "error reading package index file $file: $msg"
   508 		    } else {
   509 			set procdDirs($dir) 1
   510 		    }
   511 		}
   512 	    }
   513 	}
   514 	set dir [lindex $use_path end]
   515 	if {![info exists procdDirs($dir)]} {
   516 	    set file [file join $dir pkgIndex.tcl]
   517 	    # safe interps usually don't have "file readable", 
   518 	    # nor stderr channel
   519 	    if {([interp issafe] || [file readable $file])} {
   520 		if {[catch {source $file} msg] && ![interp issafe]}  {
   521 		    tclLog "error reading package index file $file: $msg"
   522 		} else {
   523 		    set procdDirs($dir) 1
   524 		}
   525 	    }
   526 	}
   527 
   528 	set use_path [lrange $use_path 0 end-1]
   529 
   530 	# Check whether any of the index scripts we [source]d above
   531 	# set a new value for $::auto_path.  If so, then find any
   532 	# new directories on the $::auto_path, and lappend them to
   533 	# the $use_path we are working from.  This gives index scripts
   534 	# the (arguably unwise) power to expand the index script search
   535 	# path while the search is in progress.
   536 	set index 0
   537 	if {[llength $old_path] == [llength $auto_path]} {
   538 	    foreach dir $auto_path old $old_path {
   539 		if {$dir ne $old} {
   540 		    # This entry in $::auto_path has changed.
   541 		    break
   542 		}
   543 		incr index
   544 	    }
   545 	}
   546 
   547 	# $index now points to the first element of $auto_path that
   548 	# has changed, or the beginning if $auto_path has changed length
   549 	# Scan the new elements of $auto_path for directories to add to
   550 	# $use_path.  Don't add directories we've already seen, or ones
   551 	# already on the $use_path.
   552 	foreach dir [lrange $auto_path $index end] {
   553 	    if {![info exists tclSeenPath($dir)] 
   554 		    && ([lsearch -exact $use_path $dir] == -1) } {
   555 		lappend use_path $dir
   556 	    }
   557 	}
   558 	set old_path $auto_path
   559     }
   560 }
   561 
   562 # tcl::MacOSXPkgUnknown --
   563 # This procedure extends the "package unknown" function for MacOSX.
   564 # It scans the Resources/Scripts directories of the immediate children
   565 # of the auto_path directories for pkgIndex files.
   566 # Only installed in interps that are not safe so we don't check
   567 # for [interp issafe] as in tclPkgUnknown.
   568 #
   569 # Arguments:
   570 # original -		original [package unknown] procedure
   571 # name -		Name of desired package.  Not used.
   572 #ifndef TCL_TIP268
   573 # version -		Version of desired package.  Not used.
   574 # exact -		Either "-exact" or omitted.  Not used.
   575 #else
   576 # args -		List of requirements. Not used.
   577 #endif
   578 
   579 if {[info exists tcl_platform(tip,268)]} {
   580     proc tcl::MacOSXPkgUnknown {original name args} {
   581 	#  First do the cross-platform default search
   582 	uplevel 1 $original [linsert $args 0 $name]
   583 
   584 	# Now do MacOSX specific searching
   585 	global auto_path
   586 
   587 	if {![info exists auto_path]} {
   588 	    return
   589 	}
   590 	# Cache the auto_path, because it may change while we run through
   591 	# the first set of pkgIndex.tcl files
   592 	set old_path [set use_path $auto_path]
   593 	while {[llength $use_path]} {
   594 	    set dir [lindex $use_path end]
   595 	    # get the pkgIndex files out of the subdirectories
   596 	    foreach file [glob -directory $dir -join -nocomplain \
   597 			      * Resources Scripts pkgIndex.tcl] {
   598 		set dir [file dirname $file]
   599 		if {[file readable $file] && ![info exists procdDirs($dir)]} {
   600 		    if {[catch {source $file} msg]} {
   601 			tclLog "error reading package index file $file: $msg"
   602 		    } else {
   603 			set procdDirs($dir) 1
   604 		    }
   605 		}
   606 	    }
   607 	    set use_path [lrange $use_path 0 end-1]
   608 	    if {$old_path ne $auto_path} {
   609 		foreach dir $auto_path {
   610 		    lappend use_path $dir
   611 		}
   612 		set old_path $auto_path
   613 	    }
   614 	}
   615     }
   616 } else {
   617     proc tcl::MacOSXPkgUnknown {original name version {exact {}}} {
   618 
   619 	#  First do the cross-platform default search
   620 	uplevel 1 $original [list $name $version $exact]
   621 
   622 	# Now do MacOSX specific searching
   623 	global auto_path
   624 
   625 	if {![info exists auto_path]} {
   626 	    return
   627 	}
   628 	# Cache the auto_path, because it may change while we run through
   629 	# the first set of pkgIndex.tcl files
   630 	set old_path [set use_path $auto_path]
   631 	while {[llength $use_path]} {
   632 	    set dir [lindex $use_path end]
   633 	    # get the pkgIndex files out of the subdirectories
   634 	    foreach file [glob -directory $dir -join -nocomplain \
   635 			      * Resources Scripts pkgIndex.tcl] {
   636 		set dir [file dirname $file]
   637 		if {[file readable $file] && ![info exists procdDirs($dir)]} {
   638 		    if {[catch {source $file} msg]} {
   639 			tclLog "error reading package index file $file: $msg"
   640 		    } else {
   641 			set procdDirs($dir) 1
   642 		    }
   643 		}
   644 	    }
   645 	    set use_path [lrange $use_path 0 end-1]
   646 	    if {$old_path ne $auto_path} {
   647 		foreach dir $auto_path {
   648 		    lappend use_path $dir
   649 		}
   650 		set old_path $auto_path
   651 	    }
   652 	}
   653     }
   654 }
   655 
   656 # tcl::MacPkgUnknown --
   657 # This procedure extends the "package unknown" function for Mac.
   658 # It searches for pkgIndex TEXT resources in all files
   659 # Only installed in interps that are not safe so we don't check
   660 # for [interp issafe] as in tclPkgUnknown.
   661 #
   662 # Arguments:
   663 # original -		original [package unknown] procedure
   664 # name -		Name of desired package.  Not used.
   665 # version -		Version of desired package.  Not used.
   666 # exact -		Either "-exact" or omitted.  Not used.
   667 
   668 proc tcl::MacPkgUnknown {original name version {exact {}}} {
   669 
   670     #  First do the cross-platform default search
   671     uplevel 1 $original [list $name $version $exact]
   672 
   673     # Now do Mac specific searching
   674     global auto_path
   675 
   676     if {![info exists auto_path]} {
   677 	return
   678     }
   679     # Cache the auto_path, because it may change while we run through
   680     # the first set of pkgIndex.tcl files
   681     set old_path [set use_path $auto_path]
   682     while {[llength $use_path]} {
   683 	# We look for pkgIndex TEXT resources in the resource fork of shared libraries
   684 	set dir [lindex $use_path end]
   685 	foreach x [concat [list $dir] [glob -directory $dir -nocomplain *] ] {
   686 	    if {[file isdirectory $x] && ![info exists procdDirs($x)]} {
   687 		set dir $x
   688 		foreach x [glob -directory $dir -nocomplain *.shlb] {
   689 		    if {[file isfile $x]} {
   690 			set res [resource open $x]
   691 			foreach y [resource list TEXT $res] {
   692 			    if {$y eq "pkgIndex"} {source -rsrc pkgIndex}
   693 			}
   694 			catch {resource close $res}
   695 		    }
   696 		}
   697 		set procdDirs($dir) 1
   698 	    }
   699 	}
   700 	set use_path [lrange $use_path 0 end-1]
   701 	if {$old_path ne $auto_path} {
   702 	    foreach dir $auto_path {
   703 		lappend use_path $dir
   704 	    }
   705 	    set old_path $auto_path
   706 	}
   707     }
   708 }
   709 
   710 # ::pkg::create --
   711 #
   712 #	Given a package specification generate a "package ifneeded" statement
   713 #	for the package, suitable for inclusion in a pkgIndex.tcl file.
   714 #
   715 # Arguments:
   716 #	args		arguments used by the create function:
   717 #			-name		packageName
   718 #			-version	packageVersion
   719 #			-load		{filename ?{procs}?}
   720 #			...
   721 #			-source		{filename ?{procs}?}
   722 #			...
   723 #
   724 #			Any number of -load and -source parameters may be
   725 #			specified, so long as there is at least one -load or
   726 #			-source parameter.  If the procs component of a 
   727 #			module specifier is left off, that module will be
   728 #			set up for direct loading; otherwise, it will be
   729 #			set up for lazy loading.  If both -source and -load
   730 #			are specified, the -load'ed files will be loaded 
   731 #			first, followed by the -source'd files.
   732 #
   733 # Results:
   734 #	An appropriate "package ifneeded" statement for the package.
   735 
   736 proc ::pkg::create {args} {
   737     append err(usage) "[lindex [info level 0] 0] "
   738     append err(usage) "-name packageName -version packageVersion"
   739     append err(usage) "?-load {filename ?{procs}?}? ... "
   740     append err(usage) "?-source {filename ?{procs}?}? ..."
   741 
   742     set err(wrongNumArgs) "wrong # args: should be \"$err(usage)\""
   743     set err(valueMissing) "value for \"%s\" missing: should be \"$err(usage)\""
   744     set err(unknownOpt)   "unknown option \"%s\": should be \"$err(usage)\""
   745     set err(noLoadOrSource) "at least one of -load and -source must be given"
   746 
   747     # process arguments
   748     set len [llength $args]
   749     if { $len < 6 } {
   750 	error $err(wrongNumArgs)
   751     }
   752     
   753     # Initialize parameters
   754     set opts(-name)		{}
   755     set opts(-version)		{}
   756     set opts(-source)		{}
   757     set opts(-load)		{}
   758 
   759     # process parameters
   760     for {set i 0} {$i < $len} {incr i} {
   761 	set flag [lindex $args $i]
   762 	incr i
   763 	switch -glob -- $flag {
   764 	    "-name"		-
   765 	    "-version"		{
   766 		if { $i >= $len } {
   767 		    error [format $err(valueMissing) $flag]
   768 		}
   769 		set opts($flag) [lindex $args $i]
   770 	    }
   771 	    "-source"		-
   772 	    "-load"		{
   773 		if { $i >= $len } {
   774 		    error [format $err(valueMissing) $flag]
   775 		}
   776 		lappend opts($flag) [lindex $args $i]
   777 	    }
   778 	    default {
   779 		error [format $err(unknownOpt) [lindex $args $i]]
   780 	    }
   781 	}
   782     }
   783 
   784     # Validate the parameters
   785     if { [llength $opts(-name)] == 0 } {
   786 	error [format $err(valueMissing) "-name"]
   787     }
   788     if { [llength $opts(-version)] == 0 } {
   789 	error [format $err(valueMissing) "-version"]
   790     }
   791     
   792     if { [llength $opts(-source)] == 0 && [llength $opts(-load)] == 0 } {
   793 	error $err(noLoadOrSource)
   794     }
   795 
   796     # OK, now everything is good.  Generate the package ifneeded statment.
   797     set cmdline "package ifneeded $opts(-name) $opts(-version) "
   798     
   799     set cmdList {}
   800     set lazyFileList {}
   801 
   802     # Handle -load and -source specs
   803     foreach key {load source} {
   804 	foreach filespec $opts(-$key) {
   805 	    foreach {filename proclist} {{} {}} {
   806 		break
   807 	    }
   808 	    foreach {filename proclist} $filespec {
   809 		break
   810 	    }
   811 	    
   812 	    if { [llength $proclist] == 0 } {
   813 		set cmd "\[list $key \[file join \$dir [list $filename]\]\]"
   814 		lappend cmdList $cmd
   815 	    } else {
   816 		lappend lazyFileList [list $filename $key $proclist]
   817 	    }
   818 	}
   819     }
   820 
   821     if { [llength $lazyFileList] > 0 } {
   822 	lappend cmdList "\[list tclPkgSetup \$dir $opts(-name)\
   823 		$opts(-version) [list $lazyFileList]\]"
   824     }
   825     append cmdline [join $cmdList "\\n"]
   826     return $cmdline
   827 }
   828