### ===========================================================================
### -package  : SimplePackage
# -version    : 0.2
# -purpose    : Package handling.
# -overview   :
#     This package improves the Tcl facilities for package loading and version
#  control.  It works by each package declaring itself.  This declaration
#  includes such information as the namespaces used by the package, its
#  exported commands and aliases or the packages it requires, so that this
#  information can not only be queried later, but also used for other
#  purposes.  As an example, it is possible to obtain the list of exported
#  commands from a package plus those exported from all packages it may
#  require, recursively, before actually modifying other namespaces not owned
#  by it.
#
# -usage      :
#     Packages may be either known or unknown to Tcl, depending on whether or
#  not either K<package ifneeded> or K<package provide> has been invoked for
#  the package.  In turn, known packages may be either available or provided,
#  depending on whether or not K<package provide> has been invoked for them. 
#  In turn, provided packages may be either declared or undeclared, depending
#  on whether K<::Simple::Package::declare> has been invoked for them.  All
#  undeclared packages are considered to be installed (that is, ready to be
#  used) but declared packages may be either installed or uninstalled,
#  depending on whether or not K<::Simple::Package::install> has been invoked
#  for them.  The following drawing sketches the situation:
#
#
#             .===========.     .-----------.
#             |  unknown  |     |   known   | *
#             `==========='     `-----------'
#                                 /       \
#                      .===========.     .-----------.
#                      | available |     | provided  |
#                      `==========='     `-----------'
#                                          /       \
#                               .===========.     .-----------.
#                               | declared  | *   |undeclared |
#                               `==========='     `-----------'
#                                 /       \         /
#                      .-----------.     .===========.
#                      |uninstalled| *   | installed | *
#                      `-----------'     `==========='
#
#
#     It is possible to get the list of packages in those states which have an
#  asterisk by its box in the drawing above (known, declared, uninstalled and
#  installed) via the K<::Simple::Package::information <state>> commands
#  where "<state>" is the corresponding state.  The format of each returned
#  package is {name version}.
#
#     Each package can be considered to be in one of the four main states
#  depicted with double-line boxes in the drawing above: I<unknown>,
#  I<available>, I<declared> or I<installed>.  The
#  K<::Simple::Package::information state> command returns the state of a
#  package among these main four.
#
#     At the beginning, all packages are I<unknown>.  Where Tcl evaluates all
#  of the F<pkgIndex.tcl> files in the V<auto_path> directories, K<package
#  ifneeded> is typically executed for some packages which become
#  I<available>.  These packages can then be required with
#  K<::Simple::Package::require> or K<package require>, which should invoke
#  K<package provide> so that the packages become provided. 
#
#     During this process, K<::Simple::Package::declare> can be either invoked
#  or not.  In the later case, the package is considered I<installed>,
#  although a full install might require manually importing the package
#  namespaces via K<namespace import>.  In the former case, the package
#  becomes I<declared>, state in which many package details can be queried
#  (see below) before actually installing the package via
#  K<::Simple::Package::install>.  With this, the package finally reaches the
#  I<installed> state.
#
#     Another possibility is to use K<::Simple::Package::require-and-install>
#  which requires the package and, if declared, automatically installs it.
#
#     It is also possible to go backwards in the above described route by
#  using K<::Simple::Package::uninstall>, K<::Simple::Package::undeclare> and
#  K<package forget> (or substituting the first two calls by
#  K<::Simple::Package::uninstall-and-undeclare).  The following drawings
#  sketches these transitions.  In the drawings the prefix
#  "::Simple::Package::" is abbreviated to "::S::P::".  First, for declared
#  packages:
#
#
#                                 .===========.
#                               ,>|  unknown  |
#                              (  `==========='\
#                package forget \               \  package ifneeded
#                                \               )
#                                 .===========.<'
#     ,----------->-----------> ,>| available | -->--------->-------------.
#     |                        (  `==========='\   (::S::P::require OR    |
#     |       ::S::P::undeclare \               \  package require) AND   |
#     |                          \               ) ::S::P::declare        |
#  ::S::P::                       .===========.<'                         |
# uninstall-and-undeclare       ,>| declared  |     ::S::P::require-and-install
#     |                        (  `==========='\                          |
#     |       ::S::P::uninstall \               \  ::S::P::install        |
#     |                          \               )                        |
#     |                           .===========.<'                         |
#      `----------<-----------<-- | installed | <-----------<-------------'
#                                 `==========='
#
#     For undeclared packages:
#
#                                 .===========.
#                                >|  unknown  |
#                              (' `==========='\
#                package forget \               \  package ifneeded
#                                \               )
#                                 .===========.<'
#                                >| available |
#                              (' `==========='\   ::S::P::require OR
#                   :S::P::     \               \  package require OR
#        uninstall-and-undeclare \               ) ::S::P::install-and-require
#                                 .===========.<'
#                                 | installed |
#                                 `==========='
#
#
#     Notice that in the case of undeclared packages, a full installation
#  might require manually importing the package namespaces via K<namespace
#  import> and, conversely, a full uninstallation might require manually
#  forgetting the package namespaces via K<namespace forget>.  This is not
#  necessary for declared packages as the installation and uninstallation
#  procedures take care of that.
#
#     The purpose of having I<declared> and I<installed> states is twofold. 
#  First, it is possible to require a package and get information about it
#  without any effect in other namespaces until it is installed.  Second, it
#  is possible to "reset" the status of a package to its default configuration
#  by uninstalling and reinstalling it.
#
#     The K<::Simple::Package::information> command provides many subcommands
#  to query information about declared packages:  K<aliases>, K<aliasesall>,
#  K<collisions>, K<commands>, K<declared>, K<export>, K<exportedcommands>,
#  K<exportedcommandsall>, K<importedinto>, K<install>, K<installed>,
#  K<known>, K<namespaces>, K<packagealias>, K<packagecommand>,
#  K<packagenamespace>, K<privatecommands>, K<publiccommands>, K<required>,
#  K<requiredall>, K<requiredby>, K<state>, K<uninstall> and K<uninstalled>.
#  Refer to the description of each command below for further information.
# 
#     Notice that it is very easy to convert undeclared packages into declared
#  ones, thus benefit from all the extra functionality available, by simply
#  adding a proper call to K<::Simple::Package::declare>, for example in the
#  corresponding entry of the F<pkgIndex.tcl> file.
#
#     Upon requiring a package which gets declared in the process, all its
#  required packages are also required, recursively.  Something similar
#  happens when installing a declared package, which results in all its
#  required packages being installed as well, recursively.  Notice, though,
#  that the contrary is not true; that is, uninstalling a package does not
#  uninstall any other package.  This feature might be provided in a future
#  revision of the package.
#
#     Finally, after a package has been installed, it is possible to import
#  all its exported commands into another namespace via
#  K<::Simple::Package::import> and unimport them via
#  K<::Simple::Package::unimport>.
#  
# -keywords   : package alias export import install load require
# -commands   :
#
#  * K<::Simple::Package::information known> ?packagePattern?
#    Returns the list of known packages.
#
#  * K<::Simple::Package::information declared> ?packagePattern?
#    Returns the list of declared packages.
#
#  * K<::Simple::Package::information uninstalled> ?packagePattern?
#    Returns the list of uninstalled packages.
#
#  * K<::Simple::Package::information installed> ?packagePattern?
#    Returns the list of installed packages.
#
#  * K<::Simple::Package::information state> package ?version?
#    Returns the state of a package.
#
#  * K<::Simple::Package::require> package ?version?
#    Requires a package.
#
#  * K<::Simple::Package::declare> package version ?-required list?
#    ?-namespaces list? ?-export list? ?-aliases list? ?-install script?
#    ?-firsttimeintsall script? ?-uninstall script?
#    Declares a package.
#
#  * K<::Simple::Package::information namespaces> package ?version?
#    Returns a package namespaces.
#
#  * K<::Simple::Package::information packagenamespace> namespace
#    Returns the declared package a namespace belongs to.
#
#  * K<::Simple::Package::information packagealias> alias
#    Returns the declared package an alias belongs to.
#
#  * K<::Simple::Package::information packagecommand> command
#    Returns the declared package a command belongs to.
#
#  * K<::Simple::Package::information required> package ?version?
#    Returns a package list of required packages.
#
#  * K<::Simple::Package::information requiredall> package ?version?
#    Returns a package full list of required packages.
#
#  * K<::Simple::Package::information requiredby> package ?version?
#    Returns the list of declared packages which require a package.
#
#  * K<::Simple::Package::information install> package ?version?
#    Returns a package install script.
#
#  * K<::Simple::Package::information uninstall> package ?version?
#    Returns a package uninstall script.
#
#  * K<::Simple::Package::information aliases> package ?version?
#    Returns a package list of aliases.
#
#  * K<::Simple::Package::information export> package ?version?
#    Returns a package list of exported commands.
#
#  * K<::Simple::Package::information exportedcommands> package ?version?
#    Returns a package current list of exported commands.
#
#  * K<::Simple::Package::information privatecommands> package ?version?
#    Returns a package list of private commands.
#
#  * K<::Simple::Package::information publiccommands> package ?version?
#    Returns a package list of public commands.
#
#  * K<::Simple::Package::information commands> package ?version?
#    Returns a package list of public and exported commands.
#
#  * K<::Simple::Package::information exportedcommandsall> package ?version?
#    Returns a package full list of exported commands.
#
#  * K<::Simple::Package::information aliasesall> package ?version?
#    Returns a package full list of aliases.
#
#  * K<::Simple::Package::information collisions> package ?namespace?
#    Returns the list of collisions upon importing a package.
#
#  * K<::Simple::Package::information importedinto> package ?version?
#    Returns the namespaces into which a package has been imported.
#
#  * K<::Simple::Package::install> package
#    Installs a package.
#
#  * K<::Simple::Package::require-and-install> package ?version?
#    Requires and installs a package.
#
#  * K<::Simple::Package::import> package namespace
#    Imports a package exported commands into a namespace.
#
#  * K<::Simple::Package::unimport> package namespace
#    Unimports a package exported commands from a namespace.
#
#  * K<::Simple::Package::uninstall> package
#    Uninstalls a package.
#
#  * K<::Simple::Package::undeclare> package
#    Undeclares a package.
#
#  * K<::Simple::Package::uninstall-and-undeclare> package
#    Uninstalls and undeclares a package.
#
#  * K<::Simple::Package::configure>
#    Configures the package options.
#
#  * K<::Simple::Package::cget>
#    Gets the package options.
#
# -variables  :
#
#  { ::Simple::Package::PackageRequiredPackages
#                     -array         "Array containing for each package
#                                     its required packages in the format
#                                     {name ?version?}"}
#  { ::Simple::Package::PackageExportedCommands
#                     -array         "Array containing for each package
#                                     its exported commands"}
#  { ::Simple::Package::PackageAliases
#                     -array         "Array containing for each package
#                                     its aliases"}
#  { ::Simple::Package::PackageInstallScript
#                     -array         "Array containing for each package
#                                     its install script"}
#  { ::Simple::Package::PackageFirstInstallScript
#                     -array         "Array containing for each package
#                                     its first time install script"}
#  { ::Simple::Package::PackageUninstallScript
#                     -array         "Array containing for each package
#                                     its uninstall script"}
#  { ::Simple::Package::PackageNamespaces
#                     -array         "Array containing for each package
#                                     its namespaces"}
#  { ::Simple::Package::InstalledPackages
#                     -array         "Array whose elements are the installed
#                                     declared packages"}
#  { ::Simple::Package::DeclaredPackages -array         
#                                    "Array whose elements are the declared
#                                     packages"}
#  { ::Simple::Package::PackageImportedInto
#                     -array         "Array containing for each package
#                                     the list of packages it has been imported
#                                     into"}
#
# -examples   : 
#
#  # Install the package
#  package require SimplePackage
#  
#  # Create an ifneeded script for a package which does not declare it
#  package ifneeded UndeclaredPackage 1.0 {
#     package provide UndeclaredPackage 1.0
#  }
#  
#  # Create an ifneeded script for a package declaring it
#  package ifneeded DeclaredPackage 1.0 {
#     ::Simple::Package::declare DeclaredPackage 1.0 -required {
#        {UndeclaredPackage 1.0}
#     } -namespaces {
#        ::Declared
#     } -export {
#        ::Declared::export
#     } -aliases {
#        {alias ::Declared::public}
#     } -install {
#        puts {Installing DeclaredPackage 1.0}
#        set ::Declared::PackageVariable 1
#     } -firsttimeinstall {
#        puts {   for the first time!}
#        proc ::Declared::export {} {puts export}
#        proc ::Declared::public {} {puts public}
#     } -uninstall {
#        puts {Uninstalling DeclaredPackage 1.0}
#     }
#  }
#  
#  # No packages initially installed
#  # This displays the following:
#  #    installed:
#  puts "installed: [::Simple::Package::information installed *eclared*]"
#  
#  # Now require the declared package
#  ::Simple::Package::require DeclaredPackage
#  
#  # The undeclared package has been installed
#  # This displays the following:
#  #    installed: {UndeclaredPackage 1.0}
#  puts "installed: [::Simple::Package::information installed *eclared*]"
#  
#  # Create a couple of commands which would collide with the package exported
#  # commands and aliases upon installing it
#  proc export {} {}
#  proc alias {} {}
#  
#  # Which commands are exported by the declared package ?
#  # This displays the following:
#  #    export: export
#  puts "export: [::Simple::Package::information\
#     exportedcommands DeclaredPackage]"
#  
#  # Which commands are exported by the declared package ?
#  # This displays the following:
#  #    aliases: {alias ::Declared::public}
#  puts "aliases: [::Simple::Package::information aliases DeclaredPackage]"
#  
#  # Would those command or aliases collide with other commands or aliases
#  # in the global namespace ?
#  # This displays the following:
#  #    collisions: ::Declared::export alias
#  puts "collisions:\
#     [::Simple::Package::information collisions DeclaredPackage]"
#  
#  # Let's remove the colliding commands and assess again whether the
#  # collisions remain
#  rename export {}
#  rename alias {}
#  # This displays the following:
#  #    collisions:
#  puts "collisions:\
#     [::Simple::Package::information collisions DeclaredPackage]"
#  
#  # No collisions, we may safely install the declared package
#  # This displays the following:
#  #    Installing DeclaredPackage 1.0
#  #       for the first time!
#  ::Simple::Package::install DeclaredPackage
#  
#  # Increase and display the package variable
#  # This displays the following:
#  #    2
#  puts [incr ::Declared::PackageVariable]
#  
#  # Into which namespaces has the declared package been imported ?
#  # This displays the following:
#  #    importedinto: ::
#  puts "importedinto:\
#     [::Simple::Package::information importedinto DeclaredPackage]"
#  
#  # Which commands are provided by the declared package ?
#  # This displays the following:
#  #    commands: ::Declared::export ::Declared::public
#  puts "commands:\
#     [::Simple::Package::information commands DeclaredPackage]"
#  
#  # Ok, use them
#  # This displays the following:
#  #    public
#  ::Declared::public
#  # This displays the following:
#  #    export
#  export
#  
#  # We are done, let's get rid of the declared package
#  # This displays the following:
#  #    Uninstalling DeclaredPackage 1.0
#  ::Simple::Package::uninstall DeclaredPackage
#  
#  # The package commands are no longer available
#  # This displays the following:
#  #    invalid command name "export"
#  catch {export} result
#  puts $result
#  
#  # The undeclared package remains installed
#  # This displays the following:
#  #    installed: {UndeclaredPackage 1.0}
#  puts "installed: [::Simple::Package::information installed *eclared*]"
#  
#  # We need the declared package again!
#  # This displays the following:
#  #    Installing DeclaredPackage 1.0
#  ::Simple::Package::require-and-install DeclaredPackage
#  
#  # Verify that the package variable has its default value
#  # This displays the following:
#  #    1
#  puts $::Declared::PackageVariable
#  package require SimplePackage
#
# -todo       :
#  * Allow a I<-exact> flag in K<::Simple::Package::declare>,
#    K<::Simple::Package::require-and-install> and
#    ::Simple::Package::Priv::require-package as present in K<package
#    require>.
#
#  * Currently all packages required by a given package are installed when
#    that package is installed.  It would be nice to have a mechanism so that
#    packages not explicitely installed and no longer required by any
#    other could be automatically uninstalled.
#
#  * Provide a K<::Simple::Package::reset> command to reset the state of an
#    installed package by uninstalling and reinstalling it. If effect, this
#    may reduce to evaluating the uninstall and install scripts.
#
#  * Provide something similar to K<::Simple::Package::information collisions>
#    but informing about the collisions when importing a namespace (instead of
#    a package) into another namespace.
#
#  * Allow a {package version} pair where a package name is expected in the
#    user procedures, so that it is possible to call the procedure either with
#    a single argument {package version} or package or with two arguments
#    {package} {version}.
#
#  * Provide a K<::Simple::Package::modify> command.
#
#  * The install, first time install and uninstall package scripts should be
#    stored as procedures instead as of variables in order to benefit from 
#    its byte compilation.
#
# -history    :
#  23-apr-2000   First public release, version 0.2
#
# -copyright  :
#  Copyright (C) 1999, 2000, Juan C. Gil (jgil@gmv.es)
#
### ===========================================================================
