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