home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 November / CPNL0711.ISO / beeld / teken / scribus-1.3.3.9-win32-install.exe / tcl / tcl8.4 / package.tcl < prev    next >
Text File  |  2003-08-05  |  24KB  |  780 lines

  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.2 2003/07/24 08:23:17 rmax 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 {![string length $ext]} {set ext [info sharedlibextension]}
  37.     if {[string equal $tcl_platform(platform) "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 {[string equal $currExt $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 glob $patternList} 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 {[string equal $file "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 {[string length $loadPat]} {
  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 {[string equal [lindex $pkg 1] "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 { eval __package_orig {$what} $args }
  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 lappend list [::tcl::GetAllNamespaces $ns]
  265.             }
  266.             return $list
  267.         }
  268.  
  269.         # init the list of existing namespaces, packages, commands
  270.  
  271.         foreach ::tcl::x [::tcl::GetAllNamespaces] {
  272.             set ::tcl::namespaces($::tcl::x) 1
  273.         }
  274.         foreach ::tcl::x [package names] {
  275.             if {[string compare [package provide $::tcl::x] ""]} {
  276.             set ::tcl::packages($::tcl::x) 1
  277.             }
  278.         }
  279.         set ::tcl::origCmds [info commands]
  280.  
  281.         # Try to load the file if it has the shared library
  282.         # extension, otherwise source it.  It's important not to
  283.         # try to load files that aren't shared libraries, because
  284.         # on some systems (like SunOS) the loader will abort the
  285.         # whole application when it gets an error.
  286.  
  287.         if {[pkg_compareExtension $::tcl::file [info sharedlibextension]]} {
  288.             # The "file join ." command below is necessary.
  289.             # Without it, if the file name has no \'s and we're
  290.             # on UNIX, the load command will invoke the
  291.             # LD_LIBRARY_PATH search mechanism, which could cause
  292.             # the wrong file to be used.
  293.  
  294.             set ::tcl::debug loading
  295.             load [file join . $::tcl::file]
  296.             set ::tcl::type load
  297.         } else {
  298.             set ::tcl::debug sourcing
  299.             source $::tcl::file
  300.             set ::tcl::type source
  301.         }
  302.  
  303.         # As a performance optimization, if we are creating 
  304.         # direct load packages, don't bother figuring out the 
  305.         # set of commands created by the new packages.  We 
  306.         # only need that list for setting up the autoloading 
  307.         # used in the non-direct case.
  308.         if { !$::tcl::direct } {
  309.             # See what new namespaces appeared, and import commands
  310.             # from them.  Only exported commands go into the index.
  311.             
  312.             foreach ::tcl::x [::tcl::GetAllNamespaces] {
  313.             if {! [info exists ::tcl::namespaces($::tcl::x)]} {
  314.                 namespace import -force ${::tcl::x}::*
  315.             }
  316.  
  317.             # Figure out what commands appeared
  318.             
  319.             foreach ::tcl::x [info commands] {
  320.                 set ::tcl::newCmds($::tcl::x) 1
  321.             }
  322.             foreach ::tcl::x $::tcl::origCmds {
  323.                 catch {unset ::tcl::newCmds($::tcl::x)}
  324.             }
  325.             foreach ::tcl::x [array names ::tcl::newCmds] {
  326.                 # determine which namespace a command comes from
  327.                 
  328.                 set ::tcl::abs [namespace origin $::tcl::x]
  329.                 
  330.                 # special case so that global names have no leading
  331.                 # ::, this is required by the unknown command
  332.                 
  333.                 set ::tcl::abs \
  334.                     [lindex [auto_qualify $::tcl::abs ::] 0]
  335.                 
  336.                 if {[string compare $::tcl::x $::tcl::abs]} {
  337.                 # Name changed during qualification
  338.                 
  339.                 set ::tcl::newCmds($::tcl::abs) 1
  340.                 unset ::tcl::newCmds($::tcl::x)
  341.                 }
  342.             }
  343.             }
  344.         }
  345.  
  346.         # Look through the packages that appeared, and if there is
  347.         # a version provided, then record it
  348.  
  349.         foreach ::tcl::x [package names] {
  350.             if {[string compare [package provide $::tcl::x] ""] \
  351.                 && ![info exists ::tcl::packages($::tcl::x)]} {
  352.             lappend ::tcl::newPkgs \
  353.                 [list $::tcl::x [package provide $::tcl::x]]
  354.             }
  355.         }
  356.         }
  357.     } msg] == 1} {
  358.         set what [$c eval set ::tcl::debug]
  359.         if {$doVerbose} {
  360.         tclLog "warning: error while $what $file: $msg"
  361.         }
  362.     } else {
  363.         set what [$c eval set ::tcl::debug]
  364.         if {$doVerbose} {
  365.         tclLog "successful $what of $file"
  366.         }
  367.         set type [$c eval set ::tcl::type]
  368.         set cmds [lsort [$c eval array names ::tcl::newCmds]]
  369.         set pkgs [$c eval set ::tcl::newPkgs]
  370.         if {$doVerbose} {
  371.         if { !$direct } {
  372.             tclLog "commands provided were $cmds"
  373.         }
  374.         tclLog "packages provided were $pkgs"
  375.         }
  376.         if {[llength $pkgs] > 1} {
  377.         tclLog "warning: \"$file\" provides more than one package ($pkgs)"
  378.         }
  379.         foreach pkg $pkgs {
  380.         # cmds is empty/not used in the direct case
  381.         lappend files($pkg) [list $file $type $cmds]
  382.         }
  383.  
  384.         if {$doVerbose} {
  385.         tclLog "processed $file"
  386.         }
  387.     }
  388.     interp delete $c
  389.     }
  390.  
  391.     append index "# Tcl package index file, version 1.1\n"
  392.     append index "# This file is generated by the \"pkg_mkIndex$more\" command\n"
  393.     append index "# and sourced either when an application starts up or\n"
  394.     append index "# by a \"package unknown\" script.  It invokes the\n"
  395.     append index "# \"package ifneeded\" command to set up package-related\n"
  396.     append index "# information so that packages will be loaded automatically\n"
  397.     append index "# in response to \"package require\" commands.  When this\n"
  398.     append index "# script is sourced, the variable \$dir must contain the\n"
  399.     append index "# full path name of this file's directory.\n"
  400.  
  401.     foreach pkg [lsort [array names files]] {
  402.     set cmd {}
  403.     foreach {name version} $pkg {
  404.         break
  405.     }
  406.     lappend cmd ::pkg::create -name $name -version $version
  407.     foreach spec $files($pkg) {
  408.         foreach {file type procs} $spec {
  409.         if { $direct } {
  410.             set procs {}
  411.         }
  412.         lappend cmd "-$type" [list $file $procs]
  413.         }
  414.     }
  415.     append index "\n[eval $cmd]"
  416.     }
  417.  
  418.     set f [open pkgIndex.tcl w]
  419.     puts $f $index
  420.     close $f
  421.     cd $oldDir
  422. }
  423.  
  424. # tclPkgSetup --
  425. # This is a utility procedure use by pkgIndex.tcl files.  It is invoked
  426. # as part of a "package ifneeded" script.  It calls "package provide"
  427. # to indicate that a package is available, then sets entries in the
  428. # auto_index array so that the package's files will be auto-loaded when
  429. # the commands are used.
  430. #
  431. # Arguments:
  432. # dir -            Directory containing all the files for this package.
  433. # pkg -            Name of the package (no version number).
  434. # version -        Version number for the package, such as 2.1.3.
  435. # files -        List of files that constitute the package.  Each
  436. #            element is a sub-list with three elements.  The first
  437. #            is the name of a file relative to $dir, the second is
  438. #            "load" or "source", indicating whether the file is a
  439. #            loadable binary or a script to source, and the third
  440. #            is a list of commands defined by this file.
  441.  
  442. proc tclPkgSetup {dir pkg version files} {
  443.     global auto_index
  444.  
  445.     package provide $pkg $version
  446.     foreach fileInfo $files {
  447.     set f [lindex $fileInfo 0]
  448.     set type [lindex $fileInfo 1]
  449.     foreach cmd [lindex $fileInfo 2] {
  450.         if {[string equal $type "load"]} {
  451.         set auto_index($cmd) [list load [file join $dir $f] $pkg]
  452.         } else {
  453.         set auto_index($cmd) [list source [file join $dir $f]]
  454.         } 
  455.     }
  456.     }
  457. }
  458.  
  459. # tclPkgUnknown --
  460. # This procedure provides the default for the "package unknown" function.
  461. # It is invoked when a package that's needed can't be found.  It scans
  462. # the auto_path directories and their immediate children looking for
  463. # pkgIndex.tcl files and sources any such files that are found to setup
  464. # the package database.  (On the Macintosh we also search for pkgIndex
  465. # TEXT resources in all files.)  As it searches, it will recognize changes
  466. # to the auto_path and scan any new directories.
  467. #
  468. # Arguments:
  469. # name -        Name of desired package.  Not used.
  470. # version -        Version of desired package.  Not used.
  471. # exact -        Either "-exact" or omitted.  Not used.
  472.  
  473. proc tclPkgUnknown {name version {exact {}}} {
  474.     global auto_path env
  475.  
  476.     if {![info exists auto_path]} {
  477.     return
  478.     }
  479.     # Cache the auto_path, because it may change while we run through
  480.     # the first set of pkgIndex.tcl files
  481.     set old_path [set use_path $auto_path]
  482.     while {[llength $use_path]} {
  483.     set dir [lindex $use_path end]
  484.     
  485.     # Make sure we only scan each directory one time.
  486.     if {[info exists tclSeenPath($dir)]} {
  487.         set use_path [lrange $use_path 0 end-1]
  488.         continue
  489.     }
  490.     set tclSeenPath($dir) 1
  491.  
  492.     # we can't use glob in safe interps, so enclose the following
  493.     # in a catch statement, where we get the pkgIndex files out
  494.     # of the subdirectories
  495.     catch {
  496.         foreach file [glob -directory $dir -join -nocomplain \
  497.             * pkgIndex.tcl] {
  498.         set dir [file dirname $file]
  499.         if {![info exists procdDirs($dir)] && [file readable $file]} {
  500.             if {[catch {source $file} msg]} {
  501.             tclLog "error reading package index file $file: $msg"
  502.             } else {
  503.             set procdDirs($dir) 1
  504.             }
  505.         }
  506.         }
  507.     }
  508.     set dir [lindex $use_path end]
  509.     if {![info exists procdDirs($dir)]} {
  510.         set file [file join $dir pkgIndex.tcl]
  511.         # safe interps usually don't have "file readable", 
  512.         # nor stderr channel
  513.         if {([interp issafe] || [file readable $file])} {
  514.         if {[catch {source $file} msg] && ![interp issafe]}  {
  515.             tclLog "error reading package index file $file: $msg"
  516.         } else {
  517.             set procdDirs($dir) 1
  518.         }
  519.         }
  520.     }
  521.  
  522.     set use_path [lrange $use_path 0 end-1]
  523.  
  524.     # Check whether any of the index scripts we [source]d above
  525.     # set a new value for $::auto_path.  If so, then find any
  526.     # new directories on the $::auto_path, and lappend them to
  527.     # the $use_path we are working from.  This gives index scripts
  528.     # the (arguably unwise) power to expand the index script search
  529.     # path while the search is in progress.
  530.     set index 0
  531.     if {[llength $old_path] == [llength $auto_path]} {
  532.         foreach dir $auto_path old $old_path {
  533.         if {$dir ne $old} {
  534.             # This entry in $::auto_path has changed.
  535.             break
  536.         }
  537.         incr index
  538.         }
  539.     }
  540.  
  541.     # $index now points to the first element of $auto_path that
  542.     # has changed, or the beginning if $auto_path has changed length
  543.     # Scan the new elements of $auto_path for directories to add to
  544.     # $use_path.  Don't add directories we've already seen, or ones
  545.     # already on the $use_path.
  546.     foreach dir [lrange $auto_path $index end] {
  547.         if {![info exists tclSeenPath($dir)] 
  548.             && ([lsearch -exact $use_path $dir] == -1) } {
  549.         lappend use_path $dir
  550.         }
  551.     }
  552.     set old_path $auto_path
  553.     }
  554. }
  555.  
  556. # tcl::MacOSXPkgUnknown --
  557. # This procedure extends the "package unknown" function for MacOSX.
  558. # It scans the Resources/Scripts directories of the immediate children
  559. # of the auto_path directories for pkgIndex files.
  560. # Only installed in interps that are not safe so we don't check
  561. # for [interp issafe] as in tclPkgUnknown.
  562. #
  563. # Arguments:
  564. # original -        original [package unknown] procedure
  565. # name -        Name of desired package.  Not used.
  566. # version -        Version of desired package.  Not used.
  567. # exact -        Either "-exact" or omitted.  Not used.
  568.  
  569. proc tcl::MacOSXPkgUnknown {original name version {exact {}}} {
  570.  
  571.     #  First do the cross-platform default search
  572.     uplevel 1 $original [list $name $version $exact]
  573.  
  574.     # Now do MacOSX specific searching
  575.     global auto_path
  576.  
  577.     if {![info exists auto_path]} {
  578.     return
  579.     }
  580.     # Cache the auto_path, because it may change while we run through
  581.     # the first set of pkgIndex.tcl files
  582.     set old_path [set use_path $auto_path]
  583.     while {[llength $use_path]} {
  584.     set dir [lindex $use_path end]
  585.     # get the pkgIndex files out of the subdirectories
  586.     foreach file [glob -directory $dir -join -nocomplain \
  587.         * Resources Scripts pkgIndex.tcl] {
  588.         set dir [file dirname $file]
  589.         if {[file readable $file] && ![info exists procdDirs($dir)]} {
  590.         if {[catch {source $file} msg]} {
  591.             tclLog "error reading package index file $file: $msg"
  592.         } else {
  593.             set procdDirs($dir) 1
  594.         }
  595.         }
  596.     }
  597.     set use_path [lrange $use_path 0 end-1]
  598.     if {[string compare $old_path $auto_path]} {
  599.         foreach dir $auto_path {
  600.         lappend use_path $dir
  601.         }
  602.         set old_path $auto_path
  603.     }
  604.     }
  605. }
  606.  
  607. # tcl::MacPkgUnknown --
  608. # This procedure extends the "package unknown" function for Mac.
  609. # It searches for pkgIndex TEXT resources in all files
  610. # Only installed in interps that are not safe so we don't check
  611. # for [interp issafe] as in tclPkgUnknown.
  612. #
  613. # Arguments:
  614. # original -        original [package unknown] procedure
  615. # name -        Name of desired package.  Not used.
  616. # version -        Version of desired package.  Not used.
  617. # exact -        Either "-exact" or omitted.  Not used.
  618.  
  619. proc tcl::MacPkgUnknown {original name version {exact {}}} {
  620.  
  621.     #  First do the cross-platform default search
  622.     uplevel 1 $original [list $name $version $exact]
  623.  
  624.     # Now do Mac specific searching
  625.     global auto_path
  626.  
  627.     if {![info exists auto_path]} {
  628.     return
  629.     }
  630.     # Cache the auto_path, because it may change while we run through
  631.     # the first set of pkgIndex.tcl files
  632.     set old_path [set use_path $auto_path]
  633.     while {[llength $use_path]} {
  634.     # We look for pkgIndex TEXT resources in the resource fork of shared libraries
  635.     set dir [lindex $use_path end]
  636.     foreach x [concat [list $dir] [glob -directory $dir -nocomplain *] ] {
  637.         if {[file isdirectory $x] && ![info exists procdDirs($x)]} {
  638.         set dir $x
  639.         foreach x [glob -directory $dir -nocomplain *.shlb] {
  640.             if {[file isfile $x]} {
  641.             set res [resource open $x]
  642.             foreach y [resource list TEXT $res] {
  643.                 if {[string equal $y "pkgIndex"]} {source -rsrc pkgIndex}
  644.             }
  645.             catch {resource close $res}
  646.             }
  647.         }
  648.         set procdDirs($dir) 1
  649.         }
  650.     }
  651.     set use_path [lrange $use_path 0 end-1]
  652.     if {[string compare $old_path $auto_path]} {
  653.         foreach dir $auto_path {
  654.         lappend use_path $dir
  655.         }
  656.         set old_path $auto_path
  657.     }
  658.     }
  659. }
  660.  
  661. # ::pkg::create --
  662. #
  663. #    Given a package specification generate a "package ifneeded" statement
  664. #    for the package, suitable for inclusion in a pkgIndex.tcl file.
  665. #
  666. # Arguments:
  667. #    args        arguments used by the create function:
  668. #            -name        packageName
  669. #            -version    packageVersion
  670. #            -load        {filename ?{procs}?}
  671. #            ...
  672. #            -source        {filename ?{procs}?}
  673. #            ...
  674. #
  675. #            Any number of -load and -source parameters may be
  676. #            specified, so long as there is at least one -load or
  677. #            -source parameter.  If the procs component of a 
  678. #            module specifier is left off, that module will be
  679. #            set up for direct loading; otherwise, it will be
  680. #            set up for lazy loading.  If both -source and -load
  681. #            are specified, the -load'ed files will be loaded 
  682. #            first, followed by the -source'd files.
  683. #
  684. # Results:
  685. #    An appropriate "package ifneeded" statement for the package.
  686.  
  687. proc ::pkg::create {args} {
  688.     append err(usage) "[lindex [info level 0] 0] "
  689.     append err(usage) "-name packageName -version packageVersion"
  690.     append err(usage) "?-load {filename ?{procs}?}? ... "
  691.     append err(usage) "?-source {filename ?{procs}?}? ..."
  692.  
  693.     set err(wrongNumArgs) "wrong # args: should be \"$err(usage)\""
  694.     set err(valueMissing) "value for \"%s\" missing: should be \"$err(usage)\""
  695.     set err(unknownOpt)   "unknown option \"%s\": should be \"$err(usage)\""
  696.     set err(noLoadOrSource) "at least one of -load and -source must be given"
  697.  
  698.     # process arguments
  699.     set len [llength $args]
  700.     if { $len < 6 } {
  701.     error $err(wrongNumArgs)
  702.     }
  703.     
  704.     # Initialize parameters
  705.     set opts(-name)        {}
  706.     set opts(-version)        {}
  707.     set opts(-source)        {}
  708.     set opts(-load)        {}
  709.  
  710.     # process parameters
  711.     for {set i 0} {$i < $len} {incr i} {
  712.     set flag [lindex $args $i]
  713.     incr i
  714.     switch -glob -- $flag {
  715.         "-name"        -
  716.         "-version"        {
  717.         if { $i >= $len } {
  718.             error [format $err(valueMissing) $flag]
  719.         }
  720.         set opts($flag) [lindex $args $i]
  721.         }
  722.         "-source"        -
  723.         "-load"        {
  724.         if { $i >= $len } {
  725.             error [format $err(valueMissing) $flag]
  726.         }
  727.         lappend opts($flag) [lindex $args $i]
  728.         }
  729.         default {
  730.         error [format $err(unknownOpt) [lindex $args $i]]
  731.         }
  732.     }
  733.     }
  734.  
  735.     # Validate the parameters
  736.     if { [llength $opts(-name)] == 0 } {
  737.     error [format $err(valueMissing) "-name"]
  738.     }
  739.     if { [llength $opts(-version)] == 0 } {
  740.     error [format $err(valueMissing) "-version"]
  741.     }
  742.     
  743.     if { [llength $opts(-source)] == 0 && [llength $opts(-load)] == 0 } {
  744.     error $err(noLoadOrSource)
  745.     }
  746.  
  747.     # OK, now everything is good.  Generate the package ifneeded statment.
  748.     set cmdline "package ifneeded $opts(-name) $opts(-version) "
  749.     
  750.     set cmdList {}
  751.     set lazyFileList {}
  752.  
  753.     # Handle -load and -source specs
  754.     foreach key {load source} {
  755.     foreach filespec $opts(-$key) {
  756.         foreach {filename proclist} {{} {}} {
  757.         break
  758.         }
  759.         foreach {filename proclist} $filespec {
  760.         break
  761.         }
  762.         
  763.         if { [llength $proclist] == 0 } {
  764.         set cmd "\[list $key \[file join \$dir [list $filename]\]\]"
  765.         lappend cmdList $cmd
  766.         } else {
  767.         lappend lazyFileList [list $filename $key $proclist]
  768.         }
  769.     }
  770.     }
  771.  
  772.     if { [llength $lazyFileList] > 0 } {
  773.     lappend cmdList "\[list tclPkgSetup \$dir $opts(-name)\
  774.         $opts(-version) [list $lazyFileList]\]"
  775.     }
  776.     append cmdline [join $cmdList "\\n"]
  777.     return $cmdline
  778. }
  779.  
  780.