home *** CD-ROM | disk | FTP | other *** search
/ Chip 2000 May / Chip_2000-05_cd1.bin / zkuste / Perl / ActivePerl-5.6.0.613.msi / 䆊䌷䈹䈙䏵-䞅䞆䞀㡆䞃䄦䠥 / _8cc9b1e5655a6962009fbfb11554b89d < prev    next >
Text File  |  2000-03-22  |  86KB  |  2,351 lines

  1. package PPM;
  2. require 5.004;
  3. require Exporter;
  4.  
  5. @ISA = qw(Exporter);
  6. @EXPORT = qw(PPMdat PPMERR InstalledPackageProperties ListOfRepositories RemoveRepository AddRepository GetPPMOptions SetPPMOptions InstallPackage RemovePackage VerifyPackage UpgradePackage RepositoryPackages RepositoryPackageProperties QueryInstalledPackages QueryPPD RepositorySummary);
  7.  
  8. use LWP::UserAgent;
  9. use LWP::Simple;
  10.  
  11. use File::Basename;
  12. use File::Copy;
  13. use File::Path;
  14. use ExtUtils::Install;
  15. use Cwd;
  16. use Config;
  17. use PPM::RelocPerl;
  18. use PPM::SOAPClient;
  19.  
  20. use XML::PPD;
  21. use XML::PPMConfig;
  22. use XML::Parser;
  23. use Archive::Tar;
  24.  
  25. use strict;
  26.  
  27. #set Debug to 1 to debug PPMdat file reading
  28. #             2 to debug parsing PPDs
  29. #
  30. # values may be or'ed together.
  31. #
  32. my $Debug = 0;
  33.  
  34. my $PPMERR;
  35.  
  36. my ($PPM_ver, $CPU, $OS_VALUE, $OS_VERSION, $LANGUAGE);
  37.  
  38. # options from data file.
  39. my ($build_dir, $Ignorecase, $Clean, $Force_install, $Confirm, $Root, $More, $Trace, $TraceFile, $Verbose);
  40.  
  41. my $TraceStarted = 0;
  42.  
  43. my %repositories;
  44. my %cached_ppd_list;
  45.  
  46. my ($current_root, $orig_root);
  47.  
  48. # Keys for this hash are package names.  It is filled in by a successful
  49. # call to read_config().  Each package is a hash with the following keys:
  50. # LOCATION, INST_DATE, INST_ROOT, INST_PACKLIST and INST_PPD.
  51. my %installed_packages = ();
  52.  
  53. # Keys for this hash are CODEBASE, INSTALL_HREF, INSTALL_EXEC,
  54. # INSTALL_SCRIPT, NAME, VERSION, TITLE, ABSTRACT, LICENSE, AUTHOR,
  55. # UNINSTALL_HREF, UNINSTALL_EXEC, UNINSTALL_SCRIPT, PERLCORE_VER and DEPEND.
  56. # It is filled in after a successful call to parsePPD().
  57. my %current_package = ();
  58. my @current_package_stack;
  59.  
  60. # this may get overridden by the config file.
  61. my @required_packages = ('PPM', 'SOAP', 'libnet', 'Archive-Tar', 'Compress-Zlib', 'libwww-perl', 'XML-Parser', 'XML-Element');
  62.  
  63. # Packages that can't be upgraded on Win9x
  64. my @Win9x_denied = qw(xml-parser compress-zlib);
  65. my %Win9x_denied;
  66. @Win9x_denied{@Win9x_denied} = ();
  67.  
  68. # ppm.xml location is in the environment variable 'PPM_DAT', else it is in 
  69. # the same place as this script.
  70. my ($basename, $path) = fileparse($0);
  71.  
  72. if (defined $ENV{'PPM_DAT'} && -f $ENV{'PPM_DAT'}) 
  73. {
  74.     $PPM::PPMdat = $ENV{'PPM_DAT'};
  75. }
  76. elsif (-f "$Config{'installsitelib'}/ppm.xml")
  77. {
  78.     $PPM::PPMdat = "$Config{'installsitelib'}/ppm.xml";
  79. }
  80. elsif (-f "$Config{'installprivlib'}/ppm.xml")
  81. {
  82.     $PPM::PPMdat = "$Config{'installprivlib'}/ppm.xml";
  83. }
  84. elsif (-f $path . "/ppm.xml")
  85. {  
  86.     $PPM::PPMdat = $path . $PPM::PPMdat;
  87. }
  88. else
  89. {
  90.     &Trace("Failed to load PPM_DAT file") if $Trace;
  91.     print "Failed to load PPM_DAT file\n";
  92.     return -1;
  93. }
  94.  
  95. &Trace("Using config file: $PPM::PPMdat") if $Trace;
  96.  
  97. my $init = 0;
  98. chmod(0600, $PPM::PPMdat);
  99.  
  100. #
  101. # Exported subs
  102. #
  103.  
  104. sub InstalledPackageProperties
  105. {
  106.     my (%ret_hash, $dep);
  107.     read_config();
  108.     foreach $_ (keys %installed_packages) {
  109.         parsePPD(%{ $installed_packages{$_}{'INST_PPD'} } );
  110.         $ret_hash{$_}{'NAME'} = $_;
  111.         $ret_hash{$_}{'DATE'} = $installed_packages{$_}{'INST_DATE'};
  112.         $ret_hash{$_}{'TITLE'} = $current_package{'TITLE'};
  113.         $ret_hash{$_}{'AUTHOR'} = $current_package{'AUTHOR'};
  114.         $ret_hash{$_}{'VERSION'} = $current_package{'VERSION'};
  115.         $ret_hash{$_}{'ABSTRACT'} = $current_package{'ABSTRACT'};
  116.         $ret_hash{$_}{'PERLCORE_VER'} = $current_package{'PERLCORE_VER'};
  117.         foreach $dep (keys %{$current_package{'DEPEND'}}) {
  118.             push @{$ret_hash{$_}{'DEPEND'}}, $dep;
  119.         }
  120.     }
  121.     return %ret_hash;
  122. }
  123.  
  124. sub ListOfRepositories
  125. {
  126. my %reps;
  127.  
  128.     read_config();
  129.     foreach (keys %repositories) {
  130.         $reps{$_} = $repositories{$_}{'LOCATION'};
  131.     }
  132.     return %reps;
  133. #    return %repositories;
  134. }
  135.  
  136. sub RemoveRepository
  137. {
  138.     my (%argv) = @_;
  139.     my ($arg, $repository, $save, $loc);
  140.     foreach $arg (keys %argv) {
  141.         if ($arg eq 'repository') { $repository = $argv{$arg}; }
  142.         if ($arg eq 'save') { $save = $argv{$arg}; }
  143.     }
  144.  
  145.     read_config();
  146.     foreach $_ (keys %repositories) {
  147.         if ($_ =~ /^\Q$repository\E$/) {
  148.             &Trace("Removed repository $repositories{$repository}") if $Trace;
  149.             delete $repositories{$repository};
  150.             last;
  151.         }
  152.     }
  153.     if (defined $save && $save != 0) { save_options(); }
  154. }
  155.  
  156. sub AddRepository
  157. {
  158.     my (%argv) = @_;
  159.     my ($arg, $repository, $location, $username, $password, $save);
  160.     foreach $arg (keys %argv) {
  161.         if ($arg eq 'repository') { $repository = $argv{$arg}; }
  162.         if ($arg eq 'location') { $location = $argv{$arg}; }
  163.         if ($arg eq 'username') { $username = $argv{$arg}; }
  164.         if ($arg eq 'password') { $password = $argv{$arg}; }
  165.         if ($arg eq 'save') { $save = $argv{$arg}; }
  166.     }
  167.     
  168.     read_config();
  169.     $repositories{$repository}{'LOCATION'} = $location;
  170.     $repositories{$repository}{'USERNAME'} = $username if defined $username;
  171.     $repositories{$repository}{'PASSWORD'} = $password if defined $password;
  172. #    $repositories{$repository} = $location;
  173.     &Trace("Added repository $location") if $Trace;
  174.     if (defined $save && $save != 0) { save_options(); }
  175. }
  176.  
  177. sub GetPPMOptions
  178. {
  179.     my %ret_hash;
  180.     read_config();
  181.     $ret_hash{'IGNORECASE'} = $Ignorecase;
  182.     $ret_hash{'CLEAN'} = $Clean;
  183.     $ret_hash{'FORCE_INSTALL'} = $Force_install;
  184.     $ret_hash{'CONFIRM'} = $Confirm;
  185.     $ret_hash{'ROOT'} = $Root;
  186.     $ret_hash{'BUILDDIR'} = $build_dir;
  187.     $ret_hash{'MORE'} = $More;
  188.     $ret_hash{'TRACE'} = $Trace;
  189.     $ret_hash{'TRACEFILE'} = $TraceFile;
  190.     $ret_hash{'VERBOSE'} = $Verbose;
  191.     return %ret_hash;
  192. }
  193.  
  194. sub SetPPMOptions
  195. {
  196.     my (%argv) = @_;
  197.     my ($arg, %opts, $save);
  198.     foreach $arg (keys %argv) {
  199.         if ($arg eq 'options') { %opts = %{$argv{$arg}}; }
  200.         if ($arg eq 'save') { $save = $argv{$arg}; }
  201.     }
  202.  
  203.     # set reasonable defaults for any options not explicitly set in %opts
  204.     read_config();
  205.  
  206.     $Ignorecase = $opts{'IGNORECASE'} if defined $opts{'IGNORECASE'};
  207.     $Clean = $opts{'CLEAN'} if defined $opts{'CLEAN'};
  208.     $Force_install = $opts{'FORCE_INSTALL'} if defined $opts{'FORCE_INSTALL'};
  209.     $Confirm = $opts{'CONFIRM'} if defined $opts{'CONFIRM'};
  210.     $Root = $opts{'ROOT'} if defined $opts{'ROOT'};
  211.     $build_dir = $opts{'BUILDDIR'} if defined $opts{'BUILDDIR'};
  212.     $More = $opts{'MORE'} if defined $opts{'MORE'};
  213.     $Trace = $opts{'TRACE'} if defined $opts{'TRACE'};
  214.     $TraceFile = $opts{'TRACEFILE'} if defined $opts{'TRACEFILE'};
  215.     $Verbose = $opts{'VERBOSE'} if defined $opts{'VERBOSE'};
  216.  
  217.     if (defined $save && $save != 0) { save_options(); }
  218. }
  219.  
  220. sub UpgradePackage
  221. {
  222.     my (%argv) = @_;
  223.     my ($arg, $package, $location);
  224.     foreach $arg (keys %argv) {
  225.         if ($arg eq 'package') { $package = $argv{$arg}; }
  226.         if ($arg eq 'location') { $location = $argv{$arg}; }
  227.     }
  228.     return VerifyPackage("package" => $package, "location" => $location, "upgrade" => 1);
  229. }
  230.  
  231. # Returns 1 on success, 0 and sets $PPMERR on failure.
  232. sub InstallPackage
  233. {
  234.     my (%argv) = @_;
  235.     my ($arg, $package, $location, $root);
  236.     foreach $arg (keys %argv) {
  237.         if ($arg eq 'package') { $package = $argv{$arg}; }
  238.         if ($arg eq 'location') { $location = $argv{$arg}; }
  239.         if ($arg eq 'root') { $root = $argv{$arg}; }
  240.     }
  241.     my ($PPDfile,%PPD);
  242.     $root = $current_root if (!defined($root) && defined($current_root));
  243.  
  244.     read_config();
  245.  
  246.     if (!defined($package) && -d "blib" && -f "Makefile") {
  247.         unless (open MAKEFILE, "< Makefile") {
  248.             $PPM::PPMERR = "Couldn't open Makefile for reading: $!";
  249.             return 0;
  250.         }
  251.         while (<MAKEFILE>) {
  252.             if (/^DISTNAME\s*=\s*(\S+)/) {
  253.             $package = $1;
  254.             $PPDfile = "$1.ppd";
  255.             last;
  256.             }
  257.         }
  258.         close MAKEFILE;
  259.         unless (defined $PPDfile) {
  260.             $PPM::PPMERR = "Couldn't determine local package name";
  261.             return 0;
  262.         }
  263.         system("$Config{make} ppd");
  264.         %PPD = readPPDfile($PPDfile, 'XML::PPD');
  265.         if (!%PPD) { return 0; }
  266.         parsePPD(%PPD);
  267.         $Clean = 0;
  268.         goto InstallBlib;
  269.     }
  270.  
  271.     my $packagefile = $package;
  272.     if (!defined($PPDfile = locatePPDfile($packagefile, $location))) {
  273.         &Trace("Could not locate a PPD file for package $package") if $Trace;
  274.         $PPM::PPMERR = "Could not locate a PPD file for package $package";
  275.         return 0;
  276.     }
  277.     if ($Config{'osname'} eq 'MSWin32' && 
  278.         !&Win32::IsWinNT &&
  279.         exists $Win9x_denied{lc($package)}) {
  280.         $PPM::PPMERR = "Package '$package' cannot be installed with PPM on Win9x--see http://www.ActiveState.com/ppm for details";
  281.         return 0;
  282.     }
  283.     %PPD = readPPDfile($PPDfile, 'XML::PPD');
  284.     if (!%PPD) { return 0; }
  285.  
  286.     parsePPD(%PPD);
  287.     if (!$current_package{'CODEBASE'} && !$current_package{'INSTALL_HREF'}) {
  288.         &Trace("Could not locate a PPM binary of '$package' for this platform") if $Trace;
  289.         $PPM::PPMERR = "Could not locate a PPM binary of '$package' for this platform";
  290.         return 0;
  291.     }
  292.  
  293.     if (defined $current_package{'DEPEND'}) {
  294.         my ($dep);
  295.         push(@current_package_stack, [%current_package]);
  296.         foreach $dep (keys %{$current_package{'DEPEND'}}) {
  297.             # Has PPM already installed it?
  298.             if(!defined $installed_packages{$dep}) {
  299.                 # Has *anybody* installed it, or is it part of core Perl?
  300.                 my $p = $dep;
  301.                 $p =~ s@-@/@g;
  302.                 my $found = grep -f, map "$_/$p.pm", @INC;
  303.                 if (!$found) {
  304.                     &Trace("Installing dependency '$dep'...") if $Trace;
  305. #                    print "Installing dependency '$dep'...\n";
  306.                     if(!InstallPackage("package" => $dep, "location" => $location)) {
  307.                         &Trace("Error installing dependency: $PPM::PPMERR") if $Trace;
  308.                         $PPM::PPMERR = "Error installing dependency: $PPM::PPMERR\n";
  309.                         if ($Force_install eq "No") {
  310.                             return 0;
  311.                         }
  312.                     }
  313.                 }
  314.             }
  315.             # make sure minimum version is installed, if necessary
  316.             elsif (defined $current_package{'DEPEND'}{$dep}) {
  317.                 my (@comp) = split (',', $current_package{'DEPEND'}{$dep});
  318.                 # parsePPD fills in %current_package
  319.                 push(@current_package_stack, [%current_package]);
  320.                 parsePPD(%{$installed_packages{$dep}{'INST_PPD'}});
  321.                 my (@inst) = split (',', $current_package{'VERSION'});
  322.  
  323.                 foreach(0..3) {
  324.                     if ($comp[$_] > $inst[$_]) {
  325.                         VerifyPackage("package" => $dep, "upgrade" => 1);
  326.                         last;
  327.                     }
  328.                     last if ($comp[$_] < $inst[$_]);
  329.                 }
  330.                 %current_package = @{pop @current_package_stack};
  331.             }
  332.         }
  333.         %current_package = @{pop @current_package_stack};
  334.     }
  335.     my ($basename, $path) = fileparse($PPDfile);
  336.     # strip the trailing path separator
  337.     my $chr = substr($path, -1, 1);
  338.     if ($chr eq '/' || $chr eq '\\') {
  339.         chop $path;
  340.     }
  341.     
  342.     if ($path =~ /^file:\/\/.*\|/i) {
  343.         # $path is a local directory, let's avoid LWP by changing
  344.         # it to a pathname.
  345.         $path =~ s@^file://@@i;
  346.         $path =~ s@^localhost/@@i;
  347.         $path =~ s@\|@:@;
  348.     }
  349.  
  350.  
  351.     # get the code and put it in $build_dir
  352.     my $install_dir = $build_dir . "/" . $current_package{'NAME'};
  353.     File::Path::rmtree($install_dir,0,0);
  354.     if(!-d $install_dir && !File::Path::mkpath($install_dir, 0, 0755)) {
  355.         &Trace("Could not create $install_dir: $!") if $Trace;
  356.         $PPM::PPMERR = "Could not create $install_dir: $!";
  357.         return 0;
  358.     }
  359.     ($basename) = fileparse($current_package{'CODEBASE'});
  360.     # CODEBASE is a URL
  361.     if ($current_package{'CODEBASE'} =~ m@^...*://@i) {
  362.         if (PPM_getstore("source" => "$current_package{'CODEBASE'}", 
  363.             "target" => "$install_dir/$basename") != 0) {
  364.             return 0;
  365.         }
  366.     }
  367.     # CODEBASE is a full pathname
  368.     elsif (-f $current_package{'CODEBASE'}) {
  369.         &Trace("Copying $current_package{'CODEBASE'} to $install_dir/$basename") if $Trace > 1;
  370.         copy($current_package{'CODEBASE'}, "$install_dir/$basename");
  371.     }
  372.     # CODEBASE is relative to the directory location of the PPD
  373.     elsif (-f "$path/$current_package{'CODEBASE'}") {
  374.         &Trace("Copying $path/$current_package{'CODEBASE'} to $install_dir/$basename") if $Trace > 1;
  375.         copy("$path/$current_package{'CODEBASE'}", "$install_dir/$basename");
  376.     }
  377.     # CODEBASE is relative to the URL location of the PPD
  378.     else {
  379.         if (PPM_getstore("source" => "$path/$current_package{'CODEBASE'}", 
  380.             "target" => "$install_dir/$basename") != 0) {
  381.             return 0;
  382.         }
  383.     }
  384.  
  385.     my $cwd = getcwd();
  386.     chdir($install_dir);
  387.  
  388.     my $tar;
  389.     if ($basename =~ /\.gz$/i) {
  390.         $tar = Archive::Tar->new($basename,1);
  391.     }
  392.     else {
  393.         $tar = Archive::Tar->new($basename,0);
  394.     }
  395.     $tar->extract($tar->list_files);
  396.     $basename =~ /(.*).tar/i;
  397.     chdir($1);
  398.     RelocPerl('.') if ($Config{'osname'} ne 'MSWin32');
  399.  
  400.   InstallBlib:
  401.     my $inst_archlib = $Config{installsitearch};
  402.     my $inst_root = $Config{prefix};
  403.     my $packlist = MM->catfile("$Config{installsitearch}/auto", split(/-/, $current_package{'NAME'}), ".packlist");
  404.  
  405.     # copied from ExtUtils::Install
  406.     my $INST_LIB = MM->catdir(MM->curdir,"blib","lib");
  407.     my $INST_ARCHLIB = MM->catdir(MM->curdir,"blib","arch");
  408.     my $INST_BIN = MM->catdir(MM->curdir,'blib','bin');
  409.     my $INST_SCRIPT = MM->catdir(MM->curdir,'blib','script');
  410.     my $INST_MAN1DIR = MM->catdir(MM->curdir,'blib','man1');
  411.     my $INST_MAN3DIR = MM->catdir(MM->curdir,'blib','man3');
  412.     my $INST_HTMLDIR = MM->catdir(MM->curdir,'blib','html');
  413.     my $INST_HTMLHELPDIR = MM->catdir(MM->curdir,'blib','htmlhelp');
  414.  
  415.     my ($inst_lib, $inst_bin, $inst_script, $inst_man1dir, $inst_man3dir,
  416.         $inst_htmldir, $inst_htmlhelpdir);
  417.     $inst_script = $Config{installscript};
  418.     $inst_man1dir = $Config{installman1dir};
  419.     $inst_man3dir = $Config{installman3dir};
  420.     $inst_bin = $Config{installbin};
  421.     $inst_htmldir = $Config{installhtmldir};
  422.     $inst_htmlhelpdir = $Config{installhtmlhelpdir};
  423.     $inst_lib = $Config{installsitelib};
  424.  
  425.     if (defined $root && ($root !~ /^\Q$inst_root\E$/i)) {
  426.         if ($packlist =~ m#[/|\\]lib[/|\\](.*)#) {
  427.             $packlist = "$root/lib/$1";
  428.         }
  429.         if ($inst_lib =~ m#[/|\\]lib[/|\\](.*)#) {
  430.             $inst_lib = "$root/lib/$1";
  431.         }
  432.         if ($inst_archlib =~ m#[/|\\]site[/|\\](.*)#) {
  433.             $inst_archlib = "$root/site/$1";
  434.         }
  435.         $inst_bin =~ s/\Q$inst_root/$root\E/i;
  436.         $inst_script =~ s/\Q$inst_root/$root\E/i;
  437.         $inst_man1dir =~ s/\Q$inst_root/$root\E/i;
  438.         $inst_man3dir =~ s/\Q$inst_root/$root\E/i;
  439.         $inst_root = $root;
  440.     }
  441.  
  442.     while (1) {
  443.         my $cwd = getcwd();
  444.         &Trace("Calling ExtUtils::Install::install") if $Trace > 1;
  445.         eval {
  446.             ExtUtils::Install::install({
  447.             "read" => $packlist, "write" => $packlist,
  448.             $INST_LIB => $inst_lib, $INST_ARCHLIB => $inst_archlib,
  449.             $INST_BIN => $inst_bin, $INST_SCRIPT => $inst_script,
  450.             $INST_MAN1DIR => $inst_man1dir, $INST_MAN3DIR => $inst_man3dir,
  451.             $INST_HTMLDIR => $inst_htmldir, $INST_HTMLHELPDIR => $inst_htmlhelpdir},0,0,0);
  452.         };
  453.         # install might have croaked in another directory
  454.         chdir($cwd);
  455.         # Can't remove some DLLs, but we can rename them and try again.
  456.         if ($@ && $@ =~ /Cannot forceunlink (\S+)/) {
  457.             &Trace("$@...attempting rename") if $Trace;
  458.             my $oldname = $1;
  459.             $oldname =~ s/:$//;
  460.             my $newname = $oldname . "." . time();
  461.             if(!rename($oldname, $newname)) {
  462.                 &Trace("$!") if $Trace;
  463.                 $PPM::PPMERR = $@; 
  464.                 return 0; 
  465.             }
  466.         }
  467.         # Some other error
  468.         elsif($@) {
  469.             &Trace("$@") if $Trace;
  470.             $PPM::PPMERR = $@; 
  471.             return 0;
  472.         }
  473.         else { last; }
  474.     }
  475.  
  476.     if (defined $current_package{'INSTALL_SCRIPT'}) {
  477.         run_script("script" => $current_package{'INSTALL_SCRIPT'}, 
  478.                    "scriptHREF" => $current_package{'INSTALL_HREF'},
  479.                    "exec" => $current_package{'INSTALL_EXEC'},
  480.                    "inst_root" => $inst_root, "inst_archlib" => $inst_archlib);
  481.     }
  482.  
  483.     chdir($cwd);
  484.  
  485. # ask to store this location as default for this package?
  486.     PPMdat_add_package($path, $packlist, $inst_root);
  487.     # if 'install.ppm' exists, don't remove; system()
  488.     # has probably not finished with it yet.
  489.     if ($Clean eq "Yes" && !-f "$install_dir/install.ppm") {
  490.         File::Path::rmtree($install_dir,0,0);
  491.     }
  492.     &Trace("Package $package successfully installed") if $Trace;
  493.     reread_config();
  494.  
  495.     return 1;
  496. }
  497.  
  498. # Returns a hash with key $location, and elements of arrays of package names.
  499. # Uses '%repositories' if $location is not specified.
  500. sub RepositoryPackages
  501. {
  502.     my (%argv) = @_;
  503.     my ($arg, $location, %ppds);
  504.     foreach $arg (keys %argv) {
  505.         if ($arg eq 'location') { $location = $argv{$arg}; }
  506.     }
  507.  
  508.     if (defined $location) {
  509.         @{$ppds{$location}} = list_available("location" => $location);
  510.     }
  511.     else {
  512.         read_config();  # need repositories
  513.         foreach $_ (keys %repositories) {
  514.             $location = $repositories{$_}{'LOCATION'};
  515.             @{$ppds{$location}} = list_available("location" => $location);           
  516.         }
  517.     }
  518.  
  519.     return %ppds;
  520. }
  521.  
  522. sub RepositoryPackageProperties
  523. {
  524.     my (%argv) = @_;
  525.     my ($arg, $package, $location, $PPDfile);
  526.     foreach $arg (keys %argv) {
  527.         if ($arg eq 'package') { $package = $argv{$arg}; }
  528.         if ($arg eq 'location') { $location = $argv{$arg}; }
  529.     }
  530.     read_config();
  531.     if (!defined($PPDfile = locatePPDfile($package, $location))) {
  532.         &Trace("RepositoryPackageProperties: Could not locate a PPD file for package $package") if $Trace;
  533.         $PPM::PPMERR = "Could not locate a PPD file for package $package";
  534.         return;
  535.     }
  536.  
  537.     my %PPD = readPPDfile($PPDfile, 'XML::PPD');
  538.     if (!%PPD) { return; }
  539.  
  540.     parsePPD(%PPD);
  541.     
  542.     my (%ret_hash, $dep);
  543.     $ret_hash{'NAME'} = $current_package{'NAME'};
  544.     $ret_hash{'TITLE'} = $current_package{'TITLE'};
  545.     $ret_hash{'AUTHOR'} = $current_package{'AUTHOR'};
  546.     $ret_hash{'VERSION'} = $current_package{'VERSION'};
  547.     $ret_hash{'ABSTRACT'} = $current_package{'ABSTRACT'};
  548.     $ret_hash{'PERLCORE_VER'} = $current_package{'PERLCORE_VER'};
  549.     foreach $dep (keys %{$current_package{'DEPEND'}}) {
  550.         push @{$ret_hash{'DEPEND'}}, $dep;
  551.     }
  552.     
  553.     return %ret_hash;
  554. }
  555.  
  556. # Returns 1 on success, 0 and sets $PPMERR on failure.
  557. sub RemovePackage
  558. {
  559.     my (%argv) = @_;
  560.     my ($arg, $package, $force);
  561.     foreach $arg (keys %argv) {
  562.         if ($arg eq 'package') { $package = $argv{$arg}; }
  563.         if ($arg eq 'force') { $force = $argv{$arg}; }
  564.     }
  565.     my %PPD;
  566.  
  567.     read_config();
  568.     if (!defined $installed_packages{$package}) {
  569.         my $pattern = $package;
  570.         undef $package;
  571.         # Do another lookup, ignoring case
  572.         foreach $_ (keys %installed_packages) {
  573.             if (/^$pattern$/i) {
  574.                 $package = $_;
  575.                 last;
  576.             }
  577.         }
  578.         if (!defined $package) {
  579.             &Trace("Package '$pattern' has not been installed by PPM") if $Trace;
  580.             $PPM::PPMERR = "Package '$pattern' has not been installed by PPM";
  581.             return 0;
  582.         }
  583.     }
  584.     
  585.     # Don't let them remove PPM itself, libnet, Archive-Tar, etc.
  586.     # but we can force removal if we're upgrading
  587.     unless (defined $force) {
  588.         foreach (@required_packages) {
  589.             if ($_ eq $package) {
  590.                 &Trace("Package '$package' is required by PPM and cannot be removed") if $Trace;
  591.                 $PPM::PPMERR = "Package '$package' is required by PPM and cannot be removed";
  592.                 return 0;
  593.             }
  594.         }
  595.     }
  596.     
  597.     my $install_dir = $build_dir . "/" . $package;
  598.  
  599.     %PPD = %{ $installed_packages{$package}{'INST_PPD'} };
  600.     parsePPD(%PPD);
  601.     my $cwd = getcwd();
  602.     if (defined $current_package{'UNINSTALL_SCRIPT'}) {
  603.         if (!chdir($install_dir)) {
  604.             &Trace("Could not chdir() to $install_dir: $!") if $Trace;
  605.             $PPM::PPMERR = "Could not chdir() to $install_dir: $!";
  606.             return 0;
  607.         }
  608.         run_script("script" => $current_package{'UNINSTALL_SCRIPT'}, 
  609.                    "scriptHREF" => $current_package{'UNINSTALL_HREF'},
  610.                    "exec" => $current_package{'UNINSTALL_EXEC'});
  611.         chdir($cwd);
  612.     }
  613.     else {
  614.         if (-f $installed_packages{$package}{'INST_PACKLIST'}) {
  615.             &Trace("Calling ExtUtils::Install::uninstall") if $Trace > 1;
  616.             eval {
  617.                 ExtUtils::Install::uninstall("$installed_packages{$package}{'INST_PACKLIST'}", 0, 0);
  618.             };
  619.             warn $@ if $@;
  620.         }
  621.     }
  622.  
  623.     File::Path::rmtree($install_dir,0,0);
  624.     PPMdat_remove_package($package);
  625.     &Trace("Package $package removed") if $Trace;
  626.     reread_config();
  627.     return 1;
  628. }
  629.  
  630. # returns "0" if package is up-to-date; "1" if an upgrade is available;
  631. # undef and sets $PPMERR on error; and the new VERSION string if a package
  632. # was upgraded.
  633. sub VerifyPackage
  634. {
  635.     my (%argv) = @_;
  636.     my ($arg, $package, $location, $upgrade, $force);
  637.     foreach $arg (keys %argv) {
  638.         if ($arg eq 'package') { $package = $argv{$arg}; }
  639.         if ($arg eq 'location') { $location = $argv{$arg}; }
  640.         if ($arg eq 'upgrade') { $upgrade = $argv{$arg}; }
  641.         if ($arg eq 'force') { $force = $argv{$arg}; }
  642.     }
  643.  
  644.     my ($installedPPDfile, $comparePPDfile, %installedPPD, %comparePPD);
  645.  
  646.     read_config();
  647.  
  648.     if (!defined $installed_packages{$package}) {
  649.         my $pattern = $package;
  650.         undef $package;
  651.         # Do another lookup, ignoring case
  652.         foreach $_ (keys %installed_packages) {
  653.             if (/^$pattern$/i) {
  654.                 $package = $_;
  655.                 last;
  656.             }
  657.         }
  658.         if (!defined $package) {
  659.             &Trace("Package '$pattern' has not been installed by PPM") if $Trace;
  660.             $PPM::PPMERR = "Package '$pattern' has not been installed by PPM";
  661.             return undef;
  662.         }
  663.     }
  664.  
  665.     %installedPPD = %{ $installed_packages{$package}{'INST_PPD'} };
  666.  
  667.     if (!defined($comparePPDfile = locatePPDfile($package, $location))) {
  668.         &Trace("VerifyPackage: Could not locate a PPD file for $package") if $Trace;
  669.         $PPM::PPMERR = "Could not locate a PPD file for $package";
  670.         return undef;
  671.     }
  672.     %comparePPD = readPPDfile($comparePPDfile, 'XML::PPD');
  673.     if (!%comparePPD) { return undef; }
  674.  
  675.     parsePPD(%installedPPD);
  676.     my ($inst_version) = $current_package{'VERSION'};
  677.     my ($inst_major, $inst_minor, 
  678.         $inst_patch1, $inst_patch2) = split (',', $inst_version); 
  679.     my ($inst_root) = $installed_packages{$package}{'INST_ROOT'};
  680.     parsePPD(%comparePPD);
  681.     my ($comp_version) = $current_package{'VERSION'};
  682.     my ($comp_major, $comp_minor, 
  683.         $comp_patch1, $comp_patch2) = split (',', $comp_version); 
  684.  
  685.     if ($force || $comp_major > $inst_major ||
  686.         ($comp_major == $inst_major && $comp_minor > $inst_minor) ||
  687.         ($comp_major == $inst_major && $comp_minor == $inst_minor &&
  688.          $comp_patch1 > $inst_patch1) ||
  689.         ($comp_major == $inst_major && $comp_minor == $inst_minor &&
  690.          $comp_patch1 == $inst_patch1 && $comp_patch2 > $inst_patch2)) {
  691.         &Trace("Upgrade to $package is available") if $Trace > 1;
  692.         if ($upgrade) {
  693.             if ($Config{'osname'} eq 'MSWin32' && 
  694.                 !&Win32::IsWinNT &&
  695.                 exists $Win9x_denied{lc($package)}) {
  696.                 $PPM::PPMERR = "Package '$package' cannot be upgraded with PPM on Win9x--see http://www.ActiveState.com/ppm for details";
  697.                 return undef;
  698.             }
  699.  
  700.             if (!defined $location) {
  701.             # need to remember the $location, because once we remove the
  702.             # package, it's unavailable.
  703.                 $location = $installed_packages{$package}{'LOCATION'};
  704.             }
  705.         unless (defined locatePPDfile($package, $location)) {
  706.         &Trace("VerifyPackage: Could not locate a PPD file for $package") if $Trace;
  707.         $PPM::PPMERR = "Could not locate a PPD file for $package";
  708.         return undef;
  709.         }
  710.             RemovePackage("package" => $package, "force" => 1);
  711.             InstallPackage("package" => $package, "location" => $location,
  712.                 "root" => $inst_root) or return undef;
  713.             return $comp_version;
  714.         }
  715.         return '1';
  716.     }
  717.     else {
  718.         # package is up to date
  719.         return '0';
  720.     }
  721. }
  722.  
  723. # Changes where the packages are installed.
  724. # Returns previous root on success, undef and sets $PPMERR on failure.
  725. sub chroot
  726. {
  727.     my (%argv) = @_;
  728.     my ($arg, $location, $previous_root);
  729.     foreach $arg (keys %argv) {
  730.         if ($arg eq 'location') { $location = $argv{$arg}; }
  731.     }
  732.  
  733.     if (!-d $location) {
  734.         &Trace("'$location' does not exist.") if $Trace;
  735.         $PPM::PPMERR = "'$location' does not exist.\n";
  736.         return undef;
  737.     }
  738.  
  739.     if (!defined $orig_root) {
  740.         $orig_root = $Config{installbin};
  741.         $orig_root =~ s/bin.*$//i;
  742.         chop $orig_root;
  743.         $current_root = $orig_root;
  744.     }
  745. # mjn: move this to front-end?
  746.     $previous_root = $current_root;
  747.     $current_root = $location;
  748.     return $previous_root;
  749. }
  750.  
  751. sub QueryInstalledPackages
  752. {
  753.     my (%argv) = @_;
  754.     my ($searchRE, $searchtag, $ignorecase, $package, %ret_hash);
  755.     $PPM::PPMERR = undef;
  756.     my ($arg);
  757.     foreach $arg (keys %argv) {
  758.         if ($arg eq 'searchRE' && defined $argv{$arg}) {
  759.             $searchRE = $argv{$arg};
  760.             eval { $searchRE =~ /$searchRE/ };
  761.             if ($@) {
  762.                 &Trace("'$searchRE': invalid regular expression.") if $Trace;
  763.                 $PPM::PPMERR = "'$searchRE': invalid regular expression.";
  764.                 return ();
  765.             }
  766.         }
  767.         if ($arg eq 'searchtag' && (defined $argv{$arg})) { $searchtag = uc($argv{$arg}); }
  768.         if ($arg eq 'ignorecase') { $ignorecase = $argv{$arg}; }
  769.     }
  770.     if (!defined $ignorecase) {
  771.         $ignorecase = $Ignorecase;
  772.     }
  773.  
  774.     read_config();
  775.     foreach $package (keys %installed_packages) {
  776.         my $results;
  777.         if (defined $searchtag) {
  778.             my %Package = %{ $installed_packages{$package} };
  779.             parsePPD( %{ $Package{'INST_PPD'} } );
  780.             $results = $current_package{$searchtag};
  781.         }
  782.         else {
  783.             $results = $package;
  784.         }
  785.         if (!defined $searchRE) {
  786.             $ret_hash{$package} = $results;
  787.         }
  788.         elsif ($results =~ /$searchRE/) {
  789.             $ret_hash{$package} = $results;
  790.         }
  791.         elsif ($ignorecase eq "Yes" && ($results =~ /$searchRE/i)) {
  792.             $ret_hash{$package} = $results;
  793.         }
  794.     }
  795.  
  796.     return %ret_hash;
  797. }
  798.  
  799. # Returns the matched string on success, "" on no match, and undef
  800. # on error.
  801. sub QueryPPD
  802. {
  803.     my (%argv) = @_;
  804.     my ($location, $searchRE, $searchtag, $ignorecase, $package);
  805.     my ($arg, $PPDfile, $string);
  806.     foreach $arg (keys %argv) {
  807.         if ($arg eq 'location') { $location = $argv{$arg}; }
  808.         if ($arg eq 'searchRE' && defined $argv{$arg}) {
  809.             $searchRE = $argv{$arg};
  810.             eval { $searchRE =~ /$searchRE/ };
  811.             if ($@) {
  812.                 &Trace("'$searchRE': invalid regular expression") if $Trace;
  813.                 $PPM::PPMERR = "'$searchRE': invalid regular expression.";
  814.                 return undef;
  815.             }
  816.         }
  817.         if ($arg eq 'searchtag') { $searchtag = $argv{$arg}; }
  818.         if ($arg eq 'ignorecase') { $ignorecase = $argv{$arg}; }
  819.         if ($arg eq 'package') { $package = $argv{$arg}; }
  820.     }
  821.     if (!defined $ignorecase) {
  822.         $ignorecase = $Ignorecase;
  823.     }
  824.  
  825.     if (!$location) {
  826.         read_config();
  827.     }
  828.  
  829.     if (!defined($PPDfile = locatePPDfile($package, $location))) {
  830.         &Trace("QueryPPD: Could not locate a PPD file for package $package") if $Trace;
  831.         $PPM::PPMERR = "Could not locate a PPD file for package $package";
  832.         return undef;
  833.     }
  834.     my %PPD = readPPDfile($PPDfile, 'XML::PPD');
  835.     if (!%PPD) { return undef; }
  836.  
  837.     parsePPD(%PPD);
  838.  
  839.     my $retval = "";
  840.  
  841.     if ($searchtag eq 'abstract') {
  842.         $string = $current_package{'ABSTRACT'}
  843.     }
  844.     elsif ($searchtag eq 'author') {
  845.         $string = $current_package{'AUTHOR'}
  846.     }
  847.     elsif ($searchtag eq 'title') {
  848.         $string = $current_package{'TITLE'}
  849.     }
  850.  
  851.     if (!$searchRE) {
  852.         $retval = $string;
  853.     }
  854.     elsif ($ignorecase eq "Yes") {
  855.         if ($string =~ /$searchRE/i) {
  856.             $retval = $string;
  857.         }
  858.     }
  859.     elsif ($string =~ /$searchRE/) {
  860.         $retval = $string;
  861.     }
  862.  
  863.     return $retval;
  864. }
  865.  
  866. # Returns a summary of available packages for all repositories.
  867. # Returned hash has the following structure:
  868. #
  869. #    $hash{repository}{package_name}{NAME}
  870. #    $hash{repository}{package_name}{VERSION}
  871. #    etc.
  872. #
  873. sub RepositorySummary {
  874.     my (%argv) = @_;
  875.     my ($arg, $location, %summary, %locations);
  876.  
  877.     ###########################################################################
  878.     # See if we were given the location of a repository to get the summary for.
  879.     ###########################################################################
  880.     foreach $arg (keys %argv) {
  881.         if ($arg eq 'location') { $location = $argv{$arg}; }
  882.     }
  883.  
  884.     ###########################################################################
  885.     # If we weren't given the location of a repository to query the summary
  886.     # for, check all of the repositories that we know about.
  887.     ###########################################################################
  888.     if (!defined $location) {
  889.         read_config();  # need repositories
  890.         foreach (keys %repositories) {
  891.             $locations{$repositories{$_}{'LOCATION'}} = $repositories{$_}{'SUMMARYFILE'};
  892.         }
  893.     }
  894.     ###########################################################################
  895.     # Otherwise, we were given a repository to query, figure out where we can
  896.     # find the summary file for that repository.
  897.     ###########################################################################
  898.     else {
  899.         foreach (keys %repositories) {
  900.             if ($location =~ /\Q$repositories{$_}{'LOCATION'}\E/i) {
  901.                 $locations{$repositories{$_}{'LOCATION'}} = $repositories{$_}{'SUMMARYFILE'};
  902.                 last;
  903.             }
  904.         }
  905.     }
  906.     
  907.     ###########################################################################
  908.     # Check all of the summary file locations that we were able to find.
  909.     ###########################################################################
  910.     foreach $location (keys %locations) {
  911.         my $summaryfile = $locations{$location};
  912.         if (!$summaryfile) {
  913.             &Trace("RepositorySummary: No summary available from $location.") if $Trace;
  914.             $PPM::PPMERR = "No summary available from $location.\n";
  915.             next;
  916.         }
  917.  
  918.         # Todo: The following code should really be a call to
  919.         #   readPPDfile( "$location/$summaryfile", 'XML::RepositorySummary' );
  920.         # for validation.
  921.         if ($summaryfile ne 'fetch_summary' && !valid_URL_or_file("$location/$summaryfile")) {
  922.             &Trace("RepositorySummary: No summary available from $location.") if $Trace;
  923.             $PPM::PPMERR = "No summary available from $location.\n";
  924.         }
  925.         else {
  926.             my ($data);
  927.             if ($location =~ m@^...*://@i) {
  928.                 next if (!defined ($data = read_href("href" => "$location/$summaryfile", "request" => 'GET')));
  929.             } else {
  930.                 local $/;
  931.                 next if (!open (DATAFILE, "$location/$summaryfile"));
  932.                 $data = <DATAFILE>;
  933.                 close(DATAFILE);
  934.             }
  935.  
  936.             # take care of '&'
  937.             $data =~ s/&(?!\w+;)/&/go;
  938.  
  939.             my $parser = new XML::Parser( Style => 'Objects', Pkg => 'XML::RepositorySummary' );
  940.             my @parsed = @{ $parser->parse( $data ) };
  941.  
  942.             my $packages = ${$parsed[0]}{Kids};
  943.  
  944.             foreach my $package (@{$packages}) {
  945.                 my $elem_type = ref $package;
  946.                 $elem_type =~ s/.*:://;
  947.                 next if ($elem_type eq 'Characters');
  948.         
  949.                 if ($elem_type eq 'SOFTPKG') {
  950.                     my %ret_hash;
  951.                     parsePPD(%{$package});
  952.                     $ret_hash{'NAME'} = $current_package{'NAME'};
  953.                     $ret_hash{'TITLE'} = $current_package{'TITLE'};
  954.                     $ret_hash{'AUTHOR'} = $current_package{'AUTHOR'};
  955.                     $ret_hash{'VERSION'} = $current_package{'VERSION'};
  956.                     $ret_hash{'ABSTRACT'} = $current_package{'ABSTRACT'};
  957.                     $ret_hash{'PERLCORE_VER'} = $current_package{'PERLCORE_VER'};
  958.                     foreach my $dep (keys %{$current_package{'DEPEND'}}) {
  959.                         push @{$ret_hash{'DEPEND'}}, $dep;
  960.                     }
  961.                     $summary{$location}{$current_package{'NAME'}} = \%ret_hash;
  962.                 }
  963.             }
  964.         }
  965.     }
  966.  
  967.     return %summary;
  968. }
  969.  
  970. #
  971. # Internal subs
  972. #
  973.  
  974. sub PPM_getstore {
  975.     my (%argv) = @_;
  976.     my ($arg, $source, $target, $username, $password);
  977.     foreach $arg (keys %argv) {
  978.         if ($arg eq 'source') { $source = $argv{$arg}; }
  979.         if ($arg eq 'target') { $target = $argv{$arg}; }
  980.     }
  981.     if (!defined $source || !defined $target) {
  982.         &Trace("PPM_getstore: No source or no target") if $Trace;
  983.         $PPM::PPMERR = "No source or no target\n";
  984.         return 1;
  985.     }
  986.  
  987.     # Do we need to do authorization?
  988.     # This is a hack, but will have to do for now.
  989.     foreach (keys %repositories) {
  990.         if ($source =~ /^$repositories{$_}{'LOCATION'}/i) {
  991.             $username = $repositories{$_}{'USERNAME'};
  992.             $password = $repositories{$_}{'PASSWORD'};
  993.             last;
  994.         }
  995.     }
  996.  
  997.     if (defined $ENV{HTTP_proxy} || defined $username) {
  998.         my $ua = new LWP::UserAgent;
  999.         $ua->agent("$0/0.1 " . $ua->agent);
  1000.         my $proxy_user = $ENV{HTTP_proxy_user};
  1001.         my $proxy_pass = $ENV{HTTP_proxy_pass};
  1002.         $ua->env_proxy;
  1003.         my $req = new HTTP::Request('GET' => $source);
  1004.         if (defined $proxy_user && defined $proxy_pass) {
  1005.             &Trace("PPM_getstore: calling proxy_authorization_basic($proxy_user, $proxy_pass)") if $Trace > 1;
  1006.  
  1007.             $req->proxy_authorization_basic("$proxy_user", "$proxy_pass");
  1008.         }
  1009.         if (defined $username && defined $password) {
  1010.             &Trace("PPM_getstore: calling proxy_authorization_basic($username, $password)") if $Trace > 1;
  1011.             $req->authorization_basic($username, $password);
  1012.         }
  1013.         my $res = $ua->request($req);
  1014.         if ($res->is_success) {
  1015.             if (!open(OUT, ">$target")) {
  1016.                 &Trace("PPM_getstore: Couldn't open $target for writing") if $Trace;
  1017.                 $PPM::PPMERR = "Couldn't open $target for writing\n";
  1018.                 return 1;
  1019.             }
  1020.             binmode(OUT);
  1021.             print OUT $res->content;
  1022.             close(OUT);
  1023.         } else {
  1024.             &Trace("PPM_getstore: Error reading $source: " . $res->code . " " . $res->message) if $Trace;
  1025.             $PPM::PPMERR = "Error reading $source: " . $res->code . " " . $res->message . "\n";
  1026.             return 1;
  1027.         }
  1028.     }
  1029.     else {
  1030.         my $status = LWP::Simple::getstore($source, $target);
  1031.         if ($status < 200 || $status > 299) {
  1032.             &Trace("PPM_getstore: Read of $source failed") if $Trace;
  1033.             $PPM::PPMERR = "Read of $source failed";
  1034.             return 1;
  1035.         }
  1036.     }
  1037.     return 0;
  1038. }
  1039.  
  1040. sub save_options
  1041. {
  1042.     read_config();
  1043.     # Read in the existing PPM configuration file
  1044.     my %PPMConfig = readPPDfile( $PPM::PPMdat, 'XML::PPMConfig' );
  1045.     if (!%PPMConfig) { return 0; }
  1046.  
  1047.     # Remove all of the declarations for REPOSITORY and PPMPRECIOUS;
  1048.     # we'll output these from the lists we've got in memory instead.
  1049.     my $idx;
  1050.     foreach $idx (0 .. @{$PPMConfig{Kids}})
  1051.     {
  1052.         my $elem = $PPMConfig{Kids}[$idx];
  1053.         my $elem_type = ref $elem;
  1054.         if ($elem_type =~ /::REPOSITORY$|::PPMPRECIOUS$/o)
  1055.         {
  1056.             splice( @{$PPMConfig{Kids}}, $idx, 1 );
  1057.             redo;   # Restart again so we don't miss any
  1058.         }
  1059.     }
  1060.  
  1061.     # Traverse the info we read in and replace the values in it with the new
  1062.     # config options that we've got.
  1063.     my $elem;
  1064.     foreach $elem (@{ $PPMConfig{Kids} })
  1065.     {
  1066.         my $elem_type = ref $elem;
  1067.         $elem_type =~ s/.*:://;
  1068.         next if ($elem_type ne 'OPTIONS');
  1069.  
  1070.         $elem->{IGNORECASE}   = $Ignorecase;
  1071.         $elem->{CLEAN}        = $Clean;
  1072.         $elem->{CONFIRM}      = $Confirm;
  1073.         $elem->{FORCEINSTALL} = $Force_install;
  1074.         $elem->{ROOT}         = $Root;
  1075.         $elem->{BUILDDIR}     = $build_dir;
  1076.         $elem->{MORE}         = $More;
  1077.         $elem->{TRACE}        = $Trace;
  1078.         $elem->{TRACEFILE}    = $TraceFile;
  1079.         $elem->{VERBOSE}      = $Verbose;
  1080.     }
  1081.  
  1082.     # Find out where the package listings start and insert our PPMPRECIOUS and
  1083.     # updated list of REPOSITORYs.
  1084.     foreach $idx (0 .. @{$PPMConfig{Kids}})
  1085.     {
  1086.         my $elem = $PPMConfig{Kids}[$idx];
  1087.         my $elem_type = ref $elem;
  1088.         $elem_type =~ s/.*:://;
  1089.         next unless (($elem_type eq 'PACKAGE') or
  1090.                      ($idx == $#{$PPMConfig{Kids}}));
  1091.  
  1092.         # Insert our PPMPRECIOUS
  1093.         my $chardata = new XML::PPMConfig::Characters;
  1094.         $chardata->{Text} = join( ';', @required_packages );
  1095.         my $precious = new XML::PPMConfig::PPMPRECIOUS;
  1096.         push( @{$precious->{Kids}}, $chardata );
  1097.         splice( @{$PPMConfig{Kids}}, $idx, 0, $precious );
  1098.  
  1099.         # Insert the list of repositories we've got
  1100.         my $rep_name;
  1101.         foreach $rep_name (keys %repositories)
  1102.         {
  1103.             my $repository = new XML::PPMConfig::REPOSITORY;
  1104.             $repository->{NAME}     = $rep_name;
  1105.             $repository->{LOCATION} = $repositories{ $rep_name }{'LOCATION'};
  1106.             $repository->{USERNAME} = $repositories{ $rep_name }{'USERNAME'} if defined $repositories{ $rep_name }{'USERNAME'};
  1107.             $repository->{PASSWORD} = $repositories{ $rep_name }{'PASSWORD'} if defined $repositories{ $rep_name }{'PASSWORD'};
  1108.             $repository->{SUMMARYFILE} = $repositories{ $rep_name }{'SUMMARYFILE'} if defined $repositories{ $rep_name }{'SUMMARYFILE'};
  1109.             splice( @{$PPMConfig{Kids}}, $idx, 0, $repository );
  1110.         }
  1111.         last;
  1112.     }
  1113.     # Take the data structure we've got and bless it into a PPMCONFIG object so
  1114.     # that we can output it.
  1115.     my $cfg = bless \%PPMConfig, 'XML::PPMConfig::PPMCONFIG';
  1116.  
  1117.     # Open the output file and output the PPM config file
  1118.     if (!open( DAT, ">$PPM::PPMdat" )) {
  1119.         &Trace("open of $PPM::PPMdat failed: $!") if $Trace;
  1120.         $PPM::PPMERR = "open of $PPM::PPMdat failed: $!\n";
  1121.         return 1;
  1122.     }
  1123.     my $oldout = select DAT;
  1124.     $cfg->output();
  1125.     select $oldout;
  1126.     close( DAT );
  1127.     &Trace("Wrote config file") if $Trace > 1;
  1128. }
  1129.  
  1130. ###############################################################################
  1131. # Subroutine:   list_available
  1132. ###############################################################################
  1133. # Gets a listing of all of the packages available in the repository.  If an
  1134. # argument of 'location' is provided in %argv, it is used as the repository to
  1135. # query.  This method returns to the caller a complete list of all of the
  1136. # available packages at the repository in a list context, returning 'undef' if
  1137. # any errors occurred.
  1138. ###############################################################################
  1139. sub list_available
  1140. {
  1141.     my (%argv) = @_;
  1142.     my ($arg, $location, @ppds);
  1143.     foreach $arg (keys %argv) {
  1144.         if ($arg eq 'location') { $location = $argv{$arg}; }
  1145.     }
  1146.  
  1147.     if ($location =~ /^file:\/\/.*\|/i) {
  1148.         # $location is a local directory, let's avoid LWP by changing
  1149.         # it to a pathname.
  1150.         $location =~ s@^file://@@i;
  1151.         $location =~ s@^localhost/@@i;
  1152.         $location =~ s@\|@:@;
  1153.     }
  1154.  
  1155.     # URL in UNC notation
  1156.     if ($location =~ /^file:\/\/\/\//i) {
  1157.         $location =~ s@^file://@@i;
  1158.     }
  1159.  
  1160.     # directory or UNC    
  1161.     if (-d $location || $location =~ /^\\\\/ || $location =~ /^\/\//) {
  1162.         opendir(PPDDIR, $location) or return undef;
  1163.         my ($file);
  1164.         @ppds = grep { /\.ppd$/i && -f "$location/$_" } readdir(PPDDIR);
  1165.         foreach $file (@ppds) {
  1166.             $file =~ s/\.ppd//i;
  1167.         }
  1168.     }
  1169.     elsif ($location =~ m@^...*://@i) {
  1170.         if ($cached_ppd_list{$location}) {
  1171.             return @{$cached_ppd_list{$location}};
  1172.         }
  1173.  
  1174.         #######################################################################
  1175.         # If we're accessing a SOAP server, do things differently than we would
  1176.         # for FTP, HTTP, etc.
  1177.         #######################################################################
  1178.         if ($location =~ m#^soap://#i)
  1179.         {
  1180.             my $client = new PPM::SOAPClient( $location );
  1181.             @ppds = $client->packages();
  1182.         }
  1183.         else
  1184.         {
  1185.             my $doc = read_href("href" => $location, "request" => 'GET');
  1186.             if (!defined $doc) {
  1187.                 return undef;
  1188.             }
  1189.  
  1190.             if ($doc =~ /^<head><title>/) {
  1191.                 # read an IIS format directory listing
  1192.                 @ppds = grep { /\.ppd/i } split('<br>', $doc);
  1193.                 my $file;
  1194.                 foreach $file (@ppds) {
  1195.                     $file =~ s/\.ppd<.*$//is;
  1196.                     $file =~ s@.*>@@is;
  1197.                 }
  1198.             }
  1199.             elsif ($doc =~ /<BODY BGCOLOR=FFFFFF>\n\n<form name=VPMform/s) {
  1200.                 # read output of default.prk over an HTTP connection
  1201.                 @ppds = grep { /^<!--Key:.*-->$/ } split('\n', $doc);
  1202.                 my $file;
  1203.                 foreach $file (@ppds) {
  1204.                     if ($file =~ /^<!--Key:(.*)-->$/) {
  1205.                         $file = $1;
  1206.                     }
  1207.                 }
  1208.             }
  1209.             else {
  1210.                 # read an Apache (?) format directory listing
  1211.                 @ppds = grep { /\.ppd/i } split('\n', $doc);
  1212.                 my $file;
  1213.                 foreach $file (@ppds) {
  1214.                     $file =~ s/^.*>(.*?)\.ppd<.*$/$1/i;
  1215.                 }
  1216.             }
  1217.         }
  1218.  
  1219.         #######################################################################
  1220.         # All done, take the list of PPDs that we've queried and cache it for
  1221.         # later re-use, then return it to the caller.
  1222.         #######################################################################
  1223.         @{$cached_ppd_list{$location}} = sort @ppds;
  1224.         return @{$cached_ppd_list{$location}};
  1225.     }
  1226.     return sort @ppds;
  1227. }
  1228.  
  1229. sub read_href
  1230. {
  1231.     my (%argv) = @_;
  1232.     my ($arg, $href, $request, $proxy_user, $proxy_pass);
  1233.     foreach $arg (keys %argv) {
  1234.         if ($arg eq 'href') { $href = $argv{$arg}; }
  1235.         if ($arg eq 'request') { $request = $argv{$arg}; }
  1236.     }
  1237.     # If this is a SOAP URL, handle it differently than FTP/HTTP/file.
  1238.     if ($href =~ m#^soap://#io)
  1239.     {
  1240.         my ($srvr, $fcn) = ($href =~ m@(.*)/(.+?)$@o);
  1241.         my $content;
  1242.  
  1243.         my $client = new PPM::SOAPClient( $srvr );
  1244.         return $client->fetch_summary() if ($fcn eq 'fetch_summary');
  1245.         return $client->fetch_ppd( $fcn ) if (uc($request) eq 'HEAD');
  1246.         return;
  1247.     }
  1248.  
  1249.     # Otherwise its a standard URL, go ahead and request it using LWP.
  1250.     my $ua = new LWP::UserAgent;
  1251.     $ua->agent("$0/0.1 " . $ua->agent);
  1252.     if (defined $ENV{HTTP_proxy}) {
  1253.         $proxy_user = $ENV{HTTP_proxy_user};
  1254.         $proxy_pass = $ENV{HTTP_proxy_pass};
  1255.         &Trace("read_href: calling env_proxy: $ENV{'HTTP_proxy'}") if $Trace > 1;
  1256.         $ua->env_proxy;
  1257.     }
  1258.     my $req = new HTTP::Request $request => $href;
  1259.     if (defined $proxy_user && defined $proxy_pass) {
  1260.         &Trace("read_href: calling proxy_authorization_basic($proxy_user, $proxy_pass)") if $Trace > 1;
  1261.         $req->proxy_authorization_basic("$proxy_user", "$proxy_pass");
  1262.     }
  1263.  
  1264.     # Do we need to do authorization?
  1265.     # This is a hack, but will have to do for now.
  1266.     foreach (keys %repositories) {
  1267.         if ($href =~ /^$repositories{$_}{'LOCATION'}/i) {
  1268.             my $username = $repositories{$_}{'USERNAME'};
  1269.             my $password = $repositories{$_}{'PASSWORD'};
  1270.             if (defined $username && defined $password) {
  1271.                 &Trace("read_href: calling proxy_authorization_basic($username, $password)") if $Trace > 1;
  1272.                 $req->authorization_basic($username, $password);
  1273.                 last;
  1274.             }
  1275.         }
  1276.     }
  1277.  
  1278.     # send request
  1279.     my $res = $ua->request($req);
  1280.  
  1281.     # check the outcome
  1282.     if ($res->is_success) {
  1283.         return $res->content;
  1284.     } else {
  1285.         &Trace("read_href: Error reading $href: " . $res->code . " " . $res->message) if $Trace;
  1286.         $PPM::PPMERR = "Error reading $href: " . $res->code . " " . $res->message . "\n";
  1287.         return undef;
  1288.     }
  1289. }
  1290.  
  1291. sub reread_config
  1292. {
  1293.     %current_package = ();
  1294.     %installed_packages = ();
  1295.     $init = 0;
  1296.     read_config();
  1297. }
  1298.  
  1299. # returns 0 on success, 1 and sets $PPMERR on error.
  1300. sub PPMdat_add_package
  1301. {
  1302.     my ($location, $packlist, $inst_root) = @_;
  1303.     my $package = $current_package{'NAME'};
  1304.     my $time_str = localtime;
  1305.  
  1306.     # If we already have this package installed, remove it from the PPM
  1307.     # Configuration file so we can put the new one in.
  1308.     if (defined $installed_packages{$package} ) {
  1309.         # remove the existing entry for this package.
  1310.         PPMdat_remove_package($package);
  1311.     }
  1312.  
  1313.     # Build the new SOFTPKG data structure for this package we're adding.
  1314.     my $softpkg =
  1315.         new XML::PPMConfig::SOFTPKG( NAME    => $package,
  1316.                                      VERSION => $current_package{VERSION}
  1317.                                    );
  1318.  
  1319.     if (defined $current_package{TITLE})
  1320.     {
  1321.         my $chardata =
  1322.             new XML::PPMConfig::Characters( Text => $current_package{TITLE} );
  1323.         my $newelem = new XML::PPMConfig::TITLE;
  1324.         push( @{$newelem->{Kids}}, $chardata );
  1325.         push( @{$softpkg->{Kids}}, $newelem );
  1326.     }
  1327.  
  1328.     if (defined $current_package{ABSTRACT})
  1329.     {
  1330.         my $chardata =
  1331.             new XML::PPMConfig::Characters( Text => $current_package{ABSTRACT});
  1332.         my $newelem = new XML::PPMConfig::ABSTRACT;
  1333.         push( @{$newelem->{Kids}}, $chardata );
  1334.         push( @{$softpkg->{Kids}}, $newelem );
  1335.     }
  1336.  
  1337.     if (defined $current_package{AUTHOR})
  1338.     {
  1339.         my $chardata =
  1340.             new XML::PPMConfig::Characters( Text => $current_package{AUTHOR} );
  1341.         my $newelem = new XML::PPMConfig::AUTHOR;
  1342.         push( @{$newelem->{Kids}}, $chardata );
  1343.         push( @{$softpkg->{Kids}}, $newelem );
  1344.     }
  1345.  
  1346.     if (defined $current_package{LICENSE})
  1347.     {
  1348.         my $chardata =
  1349.             new XML::PPMConfig::Characters( Text => $current_package{LICENSE});
  1350.         my $newelem = new XML::PPMConfig::LICENSE;
  1351.         push( @{$newelem->{Kids}}, $chardata );
  1352.         push( @{$softpkg->{Kids}}, $newelem );
  1353.     }
  1354.  
  1355.     my $impl = new XML::PPMConfig::IMPLEMENTATION;
  1356.     push( @{$softpkg->{Kids}}, $impl );
  1357.  
  1358.     if (defined $current_package{PERLCORE_VER})
  1359.     {
  1360.         my $newelem = new XML::PPMConfig::PERLCORE( VERSION => $current_package{PERLCORE_VER} );
  1361.         push( @{$impl->{Kids}}, $newelem );
  1362.     }
  1363.  
  1364.     foreach $_ (keys %{$current_package{DEPEND}})
  1365.     {
  1366.         my $newelem = new XML::PPMConfig::DEPENDENCY( NAME => $_, VERSION => $current_package{DEPEND}{$_} );
  1367.         push( @{$impl->{Kids}}, $newelem );
  1368.     }
  1369.  
  1370.     my $codebase = new XML::PPMConfig::CODEBASE( HREF => $current_package{CODEBASE} );
  1371.     push( @{$impl->{Kids}}, $codebase );
  1372.  
  1373.     my $inst = new XML::PPMConfig::INSTALL;
  1374.     push( @{$impl->{Kids}}, $inst );
  1375.     if (defined $current_package{INSTALL_EXEC})
  1376.         { $inst->{EXEC} = $current_package{INSTALL_EXEC}; }
  1377.     if (defined $current_package{INSTALL_HREF})
  1378.         { $inst->{HREF} = $current_package{INSTALL_HREF}; }
  1379.     if (defined $current_package{INSTALL_SCRIPT})
  1380.     {
  1381.         my $chardata =
  1382.             new XML::PPMConfig::Characters( Text => $current_package{INSTALL_SCRIPT} );
  1383.         push( @{$inst->{Kids}}, $chardata );
  1384.     }
  1385.  
  1386.     my $uninst = new XML::PPMConfig::UNINSTALL;
  1387.     push( @{$impl->{Kids}}, $uninst );
  1388.     if (defined $current_package{UNINSTALL_EXEC})
  1389.         { $uninst->{EXEC} = $current_package{UNINSTALL_EXEC}; }
  1390.     if (defined $current_package{UNINSTALL_HREF})
  1391.         { $uninst->{HREF} = $current_package{UNINSTALL_HREF}; }
  1392.     if (defined $current_package{UNINSTALL_SCRIPT})
  1393.     {
  1394.         my $chardata =
  1395.             new XML::PPMConfig::Characters( Text => $current_package{UNINSTALL_SCRIPT} );
  1396.         push( @{$uninst->{Kids}}, $chardata );
  1397.     }
  1398.  
  1399.     # Then, build the PACKAGE object and stick the SOFTPKG inside of it.
  1400.     my $pkg = new XML::PPMConfig::PACKAGE( NAME => $package );
  1401.  
  1402.     if (defined $location)
  1403.     {
  1404.         my $chardata = new XML::PPMConfig::Characters( Text => $location );
  1405.         my $newelem = new XML::PPMConfig::LOCATION;
  1406.         push( @{$newelem->{Kids}}, $chardata );
  1407.         push( @{$pkg->{Kids}}, $newelem );
  1408.     }
  1409.  
  1410.     if (defined $packlist)
  1411.     {
  1412.         my $chardata = new XML::PPMConfig::Characters( Text => $packlist );
  1413.         my $newelem = new XML::PPMConfig::INSTPACKLIST;
  1414.         push( @{$newelem->{Kids}}, $chardata );
  1415.         push( @{$pkg->{Kids}}, $newelem );
  1416.     }
  1417.  
  1418.     if (defined $inst_root)
  1419.     {
  1420.         my $chardata = new XML::PPMConfig::Characters( Text => $inst_root );
  1421.         my $newelem = new XML::PPMConfig::INSTROOT;
  1422.         push( @{$newelem->{Kids}}, $chardata );
  1423.         push( @{$pkg->{Kids}}, $newelem );
  1424.     }
  1425.  
  1426.     if (defined $time_str)
  1427.     {
  1428.         my $chardata = new XML::PPMConfig::Characters( Text => $time_str);
  1429.         my $newelem = new XML::PPMConfig::INSTDATE;
  1430.         push( @{$newelem->{Kids}}, $chardata );
  1431.         push( @{$pkg->{Kids}}, $newelem );
  1432.     }
  1433.  
  1434.     my $instppd = new XML::PPMConfig::INSTPPD;
  1435.     push( @{$instppd->{Kids}}, $softpkg );
  1436.     push( @{$pkg->{Kids}}, $instppd );
  1437.  
  1438.     # Now that we've got the structure built, read in the existing PPM
  1439.     # Configuration file, add this to it, and spit it back out.
  1440.     my %PPMConfig = readPPDfile( $PPM::PPMdat, 'XML::PPMConfig' );
  1441.     if (!%PPMConfig) { return 1; }
  1442.     push( @{$PPMConfig{Kids}}, $pkg );
  1443.     my $cfg = bless \%PPMConfig, 'XML::PPMConfig::PPMCONFIG';
  1444.  
  1445.     if (!open( DAT, ">$PPM::PPMdat" )) {
  1446.         &Trace("open of $PPM::PPMdat failed: $!") if $Trace;
  1447.         $PPM::PPMERR = "open of $PPM::PPMdat failed: $!\n";
  1448.         return 1;
  1449.     }
  1450.     my $oldout = select DAT;
  1451.     $cfg->output();
  1452.     select $oldout;
  1453.     close( DAT );
  1454.     &Trace("PPMdat_add_package: wrote $PPM::PPMdat") if $Trace > 1;
  1455.  
  1456.     return 0;
  1457. }
  1458.  
  1459. # returns 0 on success, 1 and sets $PPMERR on error.
  1460. sub PPMdat_remove_package
  1461. {
  1462.     my ($package) = @_;
  1463.  
  1464.     # Read in the existing PPM configuration file
  1465.     my %PPMConfig = readPPDfile( $PPM::PPMdat, 'XML::PPMConfig' );
  1466.     if (!%PPMConfig) { return 1; }
  1467.  
  1468.     # Try to find the package that we're supposed to be removing, and yank it
  1469.     # out of the list of installed packages.
  1470.     my $idx;
  1471.     foreach $idx (0 .. @{$PPMConfig{Kids}})
  1472.     {
  1473.         my $elem = $PPMConfig{Kids}[$idx];
  1474.         my $elem_type = ref $elem;
  1475.         next if ($elem_type !~ /::PACKAGE$/o);
  1476.         next if ($elem->{NAME} ne $package);
  1477.         splice( @{$PPMConfig{Kids}}, $idx, 1 );
  1478.     }
  1479.  
  1480.     # Take the data structure we've got and bless it into a PPMCONFIG object so
  1481.     # that we can output it again.
  1482.     my $cfg = bless \%PPMConfig, 'XML::PPMConfig::PPMCONFIG';
  1483.  
  1484.     # Now that we've removed the package, save the configuration file back out.
  1485.     if (!open( DAT, ">$PPM::PPMdat" )) {
  1486.         $PPM::PPMERR = "open of $PPM::PPMdat failed: $!\n";
  1487.         return 1;
  1488.     }
  1489.     my $oldout = select DAT;
  1490.     $cfg->output();
  1491.     select $oldout;
  1492.     close( DAT );
  1493.     &Trace("PPMdat_remove_package: wrote $PPM::PPMdat") if $Trace > 1;
  1494.  
  1495.     return 0;
  1496. }
  1497.  
  1498. # Run $script using system().  If $scriptHREF is specified, its contents are 
  1499. # used as the script.  If $exec is specified, the script is saved to a
  1500. # temporary file and executed by $exec.
  1501. sub run_script
  1502. {
  1503.     my (%argv) = @_;
  1504.     my ($arg, $script, $scriptHREF, $exec, $inst_root, $inst_archlib);
  1505.     foreach $arg (keys %argv) {
  1506.         if ($arg eq 'script') { $script = $argv{$arg}; }
  1507.         if ($arg eq 'scriptHREF') { $scriptHREF = $argv{$arg}; }
  1508.         if ($arg eq 'exec') { $exec = $argv{$arg}; }
  1509.         if ($arg eq 'inst_root') { $inst_root = $argv{$arg}; }
  1510.         if ($arg eq 'inst_archlib') { $inst_archlib = $argv{$arg}; }
  1511.     }
  1512.  
  1513.     my (@commands, $tmpname);
  1514.  
  1515.     if ($scriptHREF) {
  1516.         if ($exec) {
  1517.             # store in a temp file.
  1518.             $tmpname = $build_dir . "/PPM-" . time();
  1519.             LWP::Simple::getstore($scriptHREF, $tmpname);
  1520.         }
  1521.         else {
  1522.             my $doc = LWP::Simple::get $scriptHREF;
  1523.             if (!defined $doc) {
  1524.                 print $PPM::PPMERR;
  1525.             }
  1526.             @commands = split("\n", $doc);
  1527.             if (!defined $commands[0]) {
  1528.                 print $PPM::PPMERR;
  1529.                 return 0;
  1530.             }
  1531.         }
  1532.     }
  1533.     else {
  1534.         if (-f $script) {
  1535.             $tmpname = $script;
  1536.         }
  1537.         else {
  1538.             # change any escaped chars
  1539.             $script =~ s/</</gi;
  1540.             $script =~ s/>/>/gi;
  1541.  
  1542.             @commands = split(';;', $script);
  1543.             if ($exec) {
  1544.                 my $command;
  1545.  
  1546.                 # store in a temp file.
  1547.                 $tmpname = $build_dir . "/PPM-" . time();
  1548.                 open(TMP, ">$tmpname");
  1549.                 foreach $command (@commands) {
  1550.                     print TMP "$command\n";
  1551.                 }
  1552.                 close(TMP);
  1553.             }
  1554.         }
  1555.     }
  1556.     $ENV{'PPM_INSTROOT'} = $inst_root;
  1557.     $ENV{'PPM_INSTARCHLIB'} = $inst_archlib;
  1558.     if ($exec) {
  1559.         if ($exec =~ /^PPM_PERL$/i) {
  1560.             $exec = $^X;
  1561.         }
  1562.         system("start $exec $tmpname");
  1563.     }
  1564.     else {
  1565.         my $command;
  1566.         for $command (@commands) {
  1567.             system($command);
  1568.         }
  1569.     }
  1570. }
  1571.  
  1572. sub parsePPD
  1573. {
  1574.     my %PPD = @_;
  1575.     my $pkg;
  1576.  
  1577.     %current_package = ();
  1578.  
  1579.     # Get the package name and version from the attributes and stick it
  1580.     # into the 'current package' global var
  1581.     $current_package{NAME}    = $PPD{NAME};
  1582.     $current_package{VERSION} = $PPD{VERSION};
  1583.  
  1584.     # Get all the information for this package and put it into the 'current
  1585.     # package' global var.
  1586.     my $got_implementation = 0;
  1587.     my $elem;
  1588.  
  1589.     foreach $elem (@{$PPD{Kids}})
  1590.     {
  1591.         my $elem_type = ref $elem;
  1592.         $elem_type =~ s/.*:://;
  1593.         next if ($elem_type eq 'Characters');
  1594.  
  1595.         if ($elem_type eq 'TITLE')
  1596.         {
  1597.             # Get the package title out of our _only_ char data child
  1598.             $current_package{TITLE} = $elem->{Kids}[0]{Text};
  1599.         }
  1600.         elsif ($elem_type eq 'LICENSE')
  1601.         {
  1602.             # Get the HREF for the license out of our attribute
  1603.             $current_package{LICENSE} = $elem->{HREF};
  1604.         }
  1605.         elsif ($elem_type eq 'ABSTRACT')
  1606.         {
  1607.             # Get the package abstract out of our _only_ char data child
  1608.             $current_package{ABSTRACT} = $elem->{Kids}[0]{Text};
  1609.         }
  1610.         elsif ($elem_type eq 'AUTHOR')
  1611.         {
  1612.             # Get the authors name out of our _only_ char data child
  1613.             $current_package{AUTHOR} = $elem->{Kids}[0]{Text};
  1614.         }
  1615.         elsif ($elem_type eq 'IMPLEMENTATION')
  1616.         {
  1617.             # If we don't have a valid implementation yet, check if this is
  1618.             # it.
  1619.             next if ($got_implementation);
  1620.             $got_implementation = implementation( @{ $elem->{Kids} } );
  1621.         }
  1622.         else
  1623.         {
  1624.             &Trace("Unknown element '$elem_type' found inside SOFTPKG") if $Trace;
  1625.             die "Unknown element '$elem_type' found inside SOFTPKG.";
  1626.         }
  1627.     } # End of "for each child element inside the PPD"
  1628.  
  1629.     if ($Trace > 3 and (%current_package) ) {
  1630.         &Trace("Read a PPD:");
  1631.         my $elem;
  1632.         foreach $elem (keys %current_package) {
  1633.             &Trace("\t$elem:\t$current_package{$elem}");
  1634.         }
  1635.     }
  1636.  
  1637.     if ( ($Debug & 2) and (%current_package) )
  1638.     {
  1639.         print "Read a PPD...\n";
  1640.         my $elem;
  1641.         foreach $elem (keys %current_package)
  1642.             { print "\t$elem:\t$current_package{$elem}\n"; }
  1643.     }
  1644. }
  1645.  
  1646. # Tests the passed IMPLEMENTATION for suitability on the current platform.
  1647. # Fills in the CODEBASE, INSTALL_HREF, INSTALL_EXEC, INSTALL_SCRIPT,
  1648. # UNINSTALL_HREF, UNINSTALL_EXEC, UNINSTALL_SCRIPT and DEPEND keys of
  1649. # %current_package.  Returns 1 on success, 0 otherwise.
  1650. sub implementation
  1651. {
  1652.     my @impl = @_;
  1653.  
  1654.     # Declare the tmp vars we're going to use to hold onto things.
  1655.     my ($ImplProcessor, $ImplOS, $ImplOSVersion, $ImplLanguage, $ImplCodebase);
  1656.     my ($ImplInstallHREF, $ImplInstallEXEC, $ImplInstallScript);
  1657.     my ($ImplUninstallHREF, $ImplUninstallEXEC, $ImplUninstallScript);
  1658.     my ($ImplArch, $ImplPerlCoreVer, %ImplDepend);
  1659.  
  1660.     my $elem;
  1661.     foreach $elem (@impl)
  1662.     {
  1663.         my $elem_type = ref $elem;
  1664.         $elem_type =~ s/.*:://;
  1665.         next if ($elem_type eq 'Characters');
  1666.  
  1667.         if ($elem_type eq 'CODEBASE')
  1668.         {
  1669.             # Get the reference to the codebase out of our attributes.
  1670.             $ImplCodebase = $elem->{HREF};
  1671.         }
  1672.         elsif ($elem_type eq 'DEPENDENCY')
  1673.         {
  1674.             # Get the name of any dependencies we have out of our attributes.
  1675.             # Dependencies in old PPDs might not have version info.
  1676.             $ImplDepend{$elem->{NAME}} = (defined $elem->{VERSION} && $elem->{VERSION} ne "") ? $elem->{VERSION} : "0,0,0,0";
  1677.         }
  1678.         elsif ($elem_type eq 'LANGUAGE')
  1679.         {
  1680.             # Get the language out of our attributes (if we don't already have
  1681.             # the right one).
  1682.             if ($ImplLanguage && ($ImplLanguage ne $LANGUAGE))
  1683.                 { $ImplLanguage = $elem->{VALUE}; }
  1684.         }
  1685.         elsif ($elem_type eq 'ARCHITECTURE') {
  1686.             $ImplArch = $elem->{VALUE};
  1687.         }
  1688.         elsif ($elem_type eq 'OS')
  1689.         {
  1690.             # Get the OS value out of our attribute.
  1691.             $ImplOS = $elem->{VALUE};
  1692.         }
  1693.         elsif ($elem_type eq 'OSVERSION')
  1694.         {
  1695.             # Get the OS version value out of our attribute
  1696.             $ImplOSVersion = $elem->{VALUE};
  1697.         }
  1698.         elsif ($elem_type eq 'PERLCORE')
  1699.         {
  1700.             # Get the compiled Perl core value out of our attributes
  1701.             $ImplPerlCoreVer = $elem->{VERSION};
  1702.         }
  1703.         elsif ($elem_type eq 'PROCESSOR')
  1704.         {
  1705.             # Get the processor value out of our attribute
  1706.             $ImplProcessor = $elem->{VALUE};
  1707.         }
  1708.         elsif ($elem_type eq 'INSTALL')
  1709.         {
  1710.             # Get anything which might have been an attribute
  1711.             $ImplInstallHREF = $elem->{HREF};
  1712.             $ImplInstallEXEC = $elem->{EXEC};
  1713.             # Get any raw Perl script out of here (if we've got any)
  1714.             if ( (exists $elem->{Kids}) and (exists $elem->{Kids}[0]{Text}) )
  1715.                 { $ImplInstallScript = $elem->{Kids}[0]{Text}; }
  1716.         }
  1717.         elsif ($elem_type eq 'UNINSTALL')
  1718.         {
  1719.             # Get anything which might have been an attribute
  1720.             $ImplUninstallHREF = $elem->{HREF};
  1721.             $ImplUninstallEXEC = $elem->{EXEC};
  1722.             # Get any raw Perl script out of here (if we've got any)
  1723.             if ( (exists $elem->{Kids}) and (exists $elem->{Kids}[0]{Text}) )
  1724.                 { $ImplUninstallScript = $elem->{Kids}[0]{Text}; }
  1725.         }
  1726.         else
  1727.         {
  1728.             die "Unknown element '$elem_type' found inside of IMPLEMENTATION.";
  1729.         }
  1730.     } # end of 'for every element inside IMPLEMENTATION'
  1731.  
  1732.     # Check to see if we've found a valid IMPLEMENTATION for the target
  1733.     # machine.
  1734.     if ( (defined $ImplArch) and ($ImplArch ne $Config{'archname'}) )
  1735.         { return 0; }
  1736.     if ( (defined $ImplProcessor) and ($ImplProcessor ne $CPU) )
  1737.         { return 0; }
  1738.     if ( (defined $ImplLanguage) and ($ImplLanguage ne $LANGUAGE) )
  1739.         { return 0; }
  1740.     if ( (defined $ImplOS) and ($ImplOS ne $OS_VALUE) )
  1741.         { return 0; }
  1742.     if ( (defined $ImplOSVersion) and ($ImplOSVersion ne $OS_VERSION) )
  1743.         { return 0; }
  1744.  
  1745.     # Got a valid IMPLEMENTATION, stuff all the values we just read in into the
  1746.     # 'current package' global var.
  1747.     $current_package{PERLCORE_VER} = $ImplPerlCoreVer
  1748.         if (defined $ImplPerlCoreVer);
  1749.     $current_package{CODEBASE} = $ImplCodebase
  1750.         if (defined $ImplCodebase);
  1751.     $current_package{INSTALL_HREF} = $ImplInstallHREF
  1752.         if (defined $ImplInstallHREF);
  1753.     $current_package{INSTALL_EXEC} = $ImplInstallEXEC
  1754.         if (defined $ImplInstallEXEC);
  1755.     $current_package{INSTALL_SCRIPT} = $ImplInstallScript
  1756.         if (defined $ImplInstallScript);
  1757.     $current_package{UNINSTALL_HREF} = $ImplUninstallHREF
  1758.         if (defined $ImplUninstallHREF);
  1759.     $current_package{UNINSTALL_EXEC} = $ImplUninstallEXEC
  1760.         if (defined $ImplUninstallEXEC);
  1761.     $current_package{UNINSTALL_SCRIPT} = $ImplUninstallScript
  1762.         if (defined $ImplUninstallScript);
  1763.     %{$current_package{DEPEND}} = %ImplDepend
  1764.         if (%ImplDepend);
  1765.  
  1766.     return 1;
  1767. }
  1768.  
  1769. sub valid_URL_or_file
  1770. {
  1771.     my ($File) = @_;
  1772.     if ($File =~ /^file:\/\/.*\|/i) {
  1773.         # $File is a local directory, let's avoid LWP by changing
  1774.         # it to a pathname.
  1775.         $File =~ s@^file://@@i;
  1776.         $File =~ s@^localhost/@@i;
  1777.         $File =~ s@\|@:@;
  1778.     }
  1779.     return 1 if (-f $File);
  1780.     return 1 if ($File =~ m@^...*://@i && defined read_href("href" => $File, "request" => 'HEAD'));
  1781.     &Trace("valid_URL_or_file: $File is not valid") if $Trace;
  1782.     return 0;
  1783. }
  1784.  
  1785. # Builds a fully qualified pathname or URL to a PPD for $Package.
  1786. # If '$location' argument is given, that is used.  Otherwise, the
  1787. # '<LOCATION>' tag for a previously installed version is used, and
  1788. # if that fails, the default locations are looked at. 
  1789. #
  1790. # returns undef if it can't find a valid file or URL.
  1791. #
  1792. sub locatePPDfile
  1793. {
  1794.     my ($Package, $location) = @_;
  1795.     my $PPDfile;
  1796.     if (defined($location)) {
  1797.         if ($location =~ /[^\/]$/) { $location .= "/"; }
  1798.         $PPDfile = $location . $Package . ".ppd";
  1799.         if (!valid_URL_or_file($PPDfile)) {
  1800.             # not good.
  1801.             undef $PPDfile;
  1802.         }
  1803.     }
  1804.     else {
  1805.         # Is $Package a filename or URL?
  1806.         if (valid_URL_or_file($Package)) {
  1807.             $PPDfile = $Package;
  1808.         }
  1809.         # does the package have a <LOCATION> in $PPM::PPMdat?
  1810.         elsif (defined( $installed_packages{$Package} )) {
  1811.             $location = $installed_packages{$Package}{'LOCATION'};
  1812.             if ($location =~ /[^\/]$/) { $location .= "/"; }
  1813.             $PPDfile = $location . $Package . ".ppd";
  1814.             if (!valid_URL_or_file($PPDfile)) {
  1815.                 # not good.
  1816.                 undef $PPDfile;
  1817.             }
  1818.         }
  1819.  
  1820.         if (!defined $PPDfile) {
  1821.             # No, try the defaults.
  1822.             my $location;
  1823.  
  1824.             foreach $_ (keys %repositories) {
  1825.                 $location = $repositories{$_}{'LOCATION'};
  1826.                 if ($location =~ /[^\/]$/) { $location .= "/"; }
  1827.                 $PPDfile = $location . $Package . ".ppd";
  1828.  
  1829.                 if (valid_URL_or_file($PPDfile)) {
  1830.                     last;
  1831.                 }
  1832.                 undef $PPDfile;
  1833.             }
  1834.         }
  1835.     }
  1836.  
  1837.     # return the fully qualified pathname or HREF
  1838.     return $PPDfile;
  1839. }
  1840.  
  1841. # reads and parses $PPDfile, which can be a file or HREF
  1842. sub readPPDfile
  1843. {
  1844.     my ($PPDfile, $Pkg) = @_;
  1845.     my (@DATA, $CONTENTS);
  1846.     $Pkg = 'XML::PPD' unless (defined $Pkg);
  1847.     
  1848.     if ($PPDfile =~ /^file:\/\/.*\|/i) {
  1849.         # $PPDfile is a local directory, let's avoid LWP by changing
  1850.         # it to a pathname.
  1851.         $PPDfile =~ s@^file://@@i;
  1852.         $PPDfile =~ s@^localhost/@@i;
  1853.         $PPDfile =~ s@\|@:@;
  1854.     }
  1855.  
  1856.     # if $PPDfile is a SOAP request
  1857.     if ($PPDfile =~ m#^soap://#i) {
  1858.         my ($srvr, $pkg) = ($PPDfile =~ m@(.*)/(.+?)$@o);
  1859.         my $client = new PPM::SOAPClient( $srvr );
  1860.         $CONTENTS = $client->fetch_ppd( $pkg );
  1861.     }
  1862.     # if $PPDfile is an HREF 
  1863.     elsif ($PPDfile =~ m@^...*://@i) {
  1864.         if (!(@DATA = read_href("href" => $PPDfile, "request" => 'GET'))) {
  1865.             &Trace("readPPDfile: read_href of $PPDfile failed") if $Trace;
  1866.             return undef;
  1867.         }
  1868.         $CONTENTS = join('', @DATA);
  1869.     } else {
  1870.     # else $PPDfile is a regular file
  1871.         if (!open (DATAFILE, $PPDfile)) {
  1872.             &Trace("readPPDfile: open of $PPDfile failed") if $Trace;
  1873.             $PPM::PPMERR = "open of $PPDfile failed: $!\n";
  1874.             return undef;
  1875.         }
  1876.         @DATA = <DATAFILE>;
  1877.         close(DATAFILE);
  1878.         $CONTENTS = join('', @DATA);
  1879.     }
  1880.  
  1881.     # take care of '&'
  1882.     $CONTENTS =~ s/&(?!\w+;)/&/go;
  1883.  
  1884.     # Got everything we need, no just parse the PPD file
  1885.     my $parser = new XML::Parser( Style => 'Objects', Pkg => $Pkg );
  1886.     my @parsed = @{ $parser->parse( $CONTENTS ) };
  1887.  
  1888.     # Validate what we've read in and output an error if something wasn't
  1889.     # parsed properly.  As well, if we found any errors, return undef.
  1890.     if (!$parsed[0]->rvalidate( \&PPM::parse_err ))
  1891.         { return undef; }
  1892.  
  1893.     # Done, and things did seem to get parsed properly, return the structure.
  1894.     return %{$parsed[0]};
  1895. }
  1896.  
  1897. # Spits out the error from parsing, and sets our global error message
  1898. # accordingly.
  1899. sub parse_err
  1900. {
  1901.     &Trace("parse_err: @_") if $Trace;
  1902.     warn @_;
  1903.     $PPM::PPMERR = 'Errors found while parsing document.';
  1904. }
  1905.  
  1906. # reads and parses the PPM data file $PPM::PPMdat.  Stores config information in
  1907. # $PPM_ver, $build_dir, %repositories, $CPU, $OS_VALUE, and $OS_VERSION.
  1908. # Stores information about individual packages in the hash %installed_packages.
  1909. sub read_config
  1910. {
  1911.     if ($init) { return; }
  1912.     else { $init++; }
  1913.  
  1914.     my %PPMConfig = readPPDfile($PPM::PPMdat, 'XML::PPMConfig');
  1915.     if (!%PPMConfig) { return 0; }
  1916.  
  1917.     my $elem;
  1918.     foreach $elem (@{$PPMConfig{Kids}})
  1919.     {
  1920.         my $subelem = ref $elem;
  1921.         $subelem =~ s/.*:://;
  1922.         next if ($subelem eq 'Characters');
  1923.  
  1924.         if ($subelem eq 'PPMVER')
  1925.         {
  1926.             # Get the value out of our _only_ character data element.
  1927.             $PPM_ver = $elem->{Kids}[0]{Text};
  1928.         }
  1929.         elsif ($subelem eq 'PPMPRECIOUS')
  1930.         {
  1931.             # Get the value out of our _only_ character data element.
  1932.             @required_packages = split( ';', $elem->{Kids}[0]{Text} );
  1933.         }
  1934.         elsif ($subelem eq 'PLATFORM')
  1935.         {
  1936.             # Get values out of our attributes
  1937.             $CPU        = $elem->{CPU};
  1938.             $OS_VALUE   = $elem->{OSVALUE};
  1939.             $OS_VERSION = $elem->{OSVERSION};
  1940.             $LANGUAGE   = $elem->{LANGUAGE};
  1941.         }
  1942.         elsif ($subelem eq 'REPOSITORY')
  1943.         {
  1944.             # Get a repository out of the element attributes
  1945.             my ($name);
  1946.             $name = $elem->{NAME};
  1947.             $repositories{ $name }{'LOCATION'} = $elem->{LOCATION};
  1948.             $repositories{ $name }{'USERNAME'} = $elem->{USERNAME};
  1949.             $repositories{ $name }{'PASSWORD'} = $elem->{PASSWORD};
  1950.             $repositories{ $name }{'SUMMARYFILE'} = $elem->{SUMMARYFILE};
  1951.         }
  1952.         elsif ($subelem eq 'OPTIONS')
  1953.         {
  1954.             # Get our options out of the element attributes
  1955.             $Ignorecase     = $elem->{IGNORECASE};
  1956.             $Clean          = $elem->{CLEAN};
  1957.             $Confirm        = $elem->{CONFIRM};
  1958.             $Force_install  = $elem->{FORCEINSTALL};
  1959.             $Root           = $elem->{ROOT};
  1960.             $More           = $elem->{MORE};
  1961.             $Trace          = defined $elem->{TRACE} ? $elem->{TRACE} : "0";
  1962.             $TraceFile      = defined $elem->{TRACEFILE} ? $elem->{TRACEFILE} : "PPM.LOG";
  1963.             $Verbose        = defined $elem->{VERBOSE} ? $elem->{VERBOSE} : "1";
  1964.  
  1965. #if (defined $Root)
  1966. #                { PPM::chroot( 'location' => $Root ); }
  1967.  
  1968.             $build_dir      = $elem->{BUILDDIR};
  1969.             # Strip trailing separator
  1970.             my $chr = substr( $build_dir, -1, 1 );
  1971.             if ($chr eq '/' || $chr eq '\\')
  1972.                 { chop $build_dir; }
  1973.                 
  1974.             if ($Trace && !$TraceStarted) {
  1975.                 $TraceFile = "PPM.log" if (!defined $TraceFile);
  1976.                 open(PPMTRACE, ">>$TraceFile");
  1977.                 my $oldfh = select(PPMTRACE);
  1978.                 $| = 1;
  1979.                 select($oldfh);
  1980.                 &Trace("starting up...");
  1981.                 $TraceStarted = 1;
  1982.             }
  1983.         }
  1984.         elsif ($subelem eq 'PACKAGE')
  1985.         {
  1986.             # Get our package name out of our attributes
  1987.             my $pkg = $elem->{NAME};
  1988.  
  1989.             # Gather the information on this package from the child elements.
  1990.             my ($loc, $instdate, $root, $packlist, $ppd);
  1991.             my $child;
  1992.             foreach $child (@{$elem->{Kids}})
  1993.             {
  1994.                 my $child_type = ref $child;
  1995.                 $child_type =~ s/.*:://;
  1996.                 next if ($child_type eq 'Characters');
  1997.  
  1998.                 if ($child_type eq 'LOCATION')
  1999.                     { $loc = $child->{Kids}[0]{Text}; }
  2000.                 elsif ($child_type eq 'INSTDATE')
  2001.                     { $instdate = $child->{Kids}[0]{Text}; }
  2002.                 elsif ($child_type eq 'INSTROOT')
  2003.                     { $root = $child->{Kids}[0]{Text}; }
  2004.                 elsif ($child_type eq 'INSTPACKLIST')
  2005.                     { $packlist = $child->{Kids}[0]{Text}; }
  2006.                 elsif ($child_type eq 'INSTPPD')
  2007.                 {
  2008.                     # Find the SOFTPKG inside here and hang onto it
  2009.                     my $tmp;
  2010.                     foreach $tmp (@{$child->{Kids}})
  2011.                     {
  2012.                         if ((ref $tmp) =~ /::SOFTPKG$/o)
  2013.                             { $ppd = $tmp; }
  2014.                     }
  2015.                 }
  2016.                 else
  2017.                 {
  2018.                     die "Unknown element inside of $pkg PACKAGE; $child";
  2019.                 }
  2020.             } # end 'foreach $child...'
  2021.  
  2022.             my %package_details = (
  2023.                                     LOCATION      => $loc,
  2024.                                     INST_DATE     => $instdate,
  2025.                                     INST_ROOT     => $root,
  2026.                                     INST_PACKLIST => $packlist,
  2027.                                     INST_PPD      => $ppd
  2028.                                   );
  2029.             $installed_packages{ $pkg } = \%package_details;
  2030.         }
  2031.         else
  2032.         {
  2033.             die "Unknown element found in PPD_DAT file; $subelem";
  2034.         }
  2035.     }
  2036.     if ($Debug & 1) {
  2037.         print "This is ppm, version $PPM_ver.\nRepository locations:\n";
  2038.         foreach (keys %repositories) { 
  2039.             print "\t$_: $repositories{$_}{'LOCATION'}\n"
  2040.         }
  2041.         print "Platform is $OS_VALUE version $OS_VERSION on a $CPU CPU.\n";
  2042.         print "Packages will be built in $build_dir\n";
  2043.         print "Commands will " . ($Confirm eq "Yes" ? "" : "not ") . "be confirmed.\n";
  2044.         print "Temporary files will " . ($Clean eq "Yes" ? "" : "not ") . "be deleted.\n";
  2045.         print "Installations will " . ($Force_install eq "Yes" ? "" : "not ") . "continue if a dependency cannot be installed.\n";
  2046.         print "Screens will " . ($More > 0 ? "pause after each $More lines.\n" : "not pause after the screen is full.\n");
  2047.         print "Tracing info will " . ($Trace > 0 ? "be written to $TraceFile.\n" : "not be written.\n");
  2048.         print "Case-" . ($Ignorecase eq "Yes" ? "in" : "") . "sensitive searches will be performed.\n";
  2049.  
  2050.         my $pkg;
  2051.         foreach $pkg (keys %installed_packages)
  2052.         {
  2053.             print "\nFound installed package $pkg, " .
  2054.             "installed on $installed_packages{$pkg}{INST_DATE}\n" .
  2055.             "in directory root $installed_packages{$pkg}{INST_ROOT} " .
  2056.             "from $installed_packages{$pkg}{'LOCATION'}.\n\n";
  2057.         }
  2058.     }
  2059. }
  2060.  
  2061. sub Trace
  2062. {
  2063.     print PPMTRACE "$0: @_ at ",  scalar localtime(), "\n";
  2064. }
  2065.  
  2066. 1;
  2067.  
  2068. __END__
  2069.  
  2070. =head1 NAME
  2071.  
  2072. ppm - PPM (Perl Package Management)
  2073.  
  2074. =head1 SYNOPSIS
  2075.  
  2076.  use PPM;
  2077.  
  2078.  PPM::InstallPackage("package" => $package, "location" => $location, "root" => $root);
  2079.  PPM::RemovePackage("package" => $package, "force" => $force);
  2080.  PPM::VerifyPackage("package" => $package, "location" => $location, "upgrade" => $upgrade);
  2081.  PPM::QueryInstalledPackages("searchRE" => $searchRE, "searchtag" => $searchtag, "ignorecase" => $ignorecase);
  2082.  PPM::InstalledPackageProperties();
  2083.  PPM::QueryPPD("location" => $location, "searchRE" => $searchRE, "searchtag" => $searchtag, "ignorecase" => $ignorecase, "package" => $package);
  2084.  
  2085.  PPM::ListOfRepositories();
  2086.  PPM::RemoveRepository("repository" => $repository, "save" => $save);
  2087.  PPM::AddRepository("repository" => $repository, "location" => $location, "save" => $save);
  2088.  PPM::RepositoryPackages("location" => $location);
  2089.  PPM::RepositoryPackageProperties("package" => $package, "location" => $location);
  2090.  PPM::RepositorySummary("location" => $location);
  2091.  
  2092.  PPM::GetPPMOptions();
  2093.  PPM::SetPPMOptions("options" => %options, "save" => $save);
  2094.  
  2095. =head1 DESCRIPTION
  2096.  
  2097. PPM is a group of functions intended to simplify the tasks of locating,
  2098. installing, upgrading and removing software 'packages'.  It can determine
  2099. if the most recent version of a software package is installed on a system,
  2100. and can install or upgrade that package from a local or remote host.
  2101.  
  2102. PPM uses files containing a modified form of the Open Software Distribution
  2103. (OSD) specification for information about software packages.
  2104. These description files, which are written in Extensible Markup
  2105. Language (XML) code, are referred to as 'PPD' files.  Information about
  2106. OSD can be found at the W3C web site (at the time of this writing,
  2107. http://www.w3.org/TR/NOTE-OSD.html).  The modifications to OSD used by PPM
  2108. are documented in PPM::ppd.
  2109.  
  2110. PPD files for packages are generated from POD files using the pod2ppd
  2111. command.
  2112.  
  2113. =head1 USAGE
  2114.  
  2115. =over 4
  2116.  
  2117. =item  PPM::InstallPackage("package" => $package, "location" => $location, "root" => $root);
  2118.  
  2119. Installs the specified package onto the local system.  'package' may
  2120. be a simple package name ('foo'), a pathname (P:\PACKAGES\FOO.PPD) or
  2121. a URL (HTTP://www.ActiveState.com/packages/foo.ppd).  In the case of a
  2122. simple package name, the function will look for the package's PPD file
  2123. at 'location', if provided; otherwise, it will use information stored
  2124. in the PPM data file (see 'Files' section below) to locate the PPD file
  2125. for the requested package.  The package's files will be installed under
  2126. the directory specified in 'root'; if not specified the default value
  2127. of 'root' will be used.
  2128.  
  2129. The function uses the values stored in the PPM data file to determine the
  2130. local operating system, operating system version and CPU type.  If the PPD
  2131. for this package contains implementations for different platforms, these
  2132. values will be used to determine which one is installed.
  2133.  
  2134. InstallPackage() updates the PPM data file with information about the package
  2135. installation. It stores a copy of the PPD used for installation, as well
  2136. as the location from which this PPD was obtained.  This location will
  2137. become the default PPD location for this package.
  2138.  
  2139. During an installation, the following actions are performed:
  2140.  
  2141.     - the PPD file for the package is read
  2142.     - a directory for this package is created in the directory specified in
  2143.       <BUILDDIR> in the PPM data file.
  2144.     - the file specified with the <CODEBASE> tag in the PPD file is 
  2145.       retrieved/copied into the directory created above.
  2146.     - the package is unarchived in the directory created for this package
  2147.     - individual files from the archive are installed in the appropriate
  2148.       directories of the local Perl installation.
  2149.     - perllocal.pod is updated with the install information.
  2150.     - if provided, the <INSTALL> script from the PPD is executed in the
  2151.       directory created above.
  2152.     - information about the installation is stored in the PPM data file.
  2153.  
  2154. =item PPM::RemovePackage("package" => $package, "force" => $force)
  2155.  
  2156. Removes the specified package from the system.  Reads the package's PPD
  2157. (stored during installation) for removal details.  If 'force' is
  2158. specified, even a package required by PPM will be removed (useful
  2159. when installing an upgrade).
  2160.  
  2161. =item PPM::VerifyPackage("package" => $package, "location" => $location, "upgrade" => $upgrade)
  2162.  
  2163. Reads a PPD file for 'package', and compares the currently installed
  2164. version of 'package' to the version available according to the PPD.
  2165. The PPD file is expected to be on a local directory or remote site
  2166. specified either in the PPM data file or in the 'location' argument.
  2167. The 'location' argument may be a directory location or a URL.
  2168. The 'upgrade' argument forces an upgrade if the installed package is
  2169. not up-to-date.
  2170.  
  2171. The PPD file for each package will initially be searched for at
  2172. 'location', and if not found will then be searched for using the
  2173. locations specified in the PPM data file.
  2174.  
  2175. =item  PPM::QueryInstalledPackages("searchRE" => $searchRE, "searchtag" => $searchtag, "ignorecase" => $ignorecase);
  2176.  
  2177. Returns a hash containing information about all installed packages.
  2178. By default, a list of all installed packages is returned.  If a regular
  2179. expression 'searchRE' is specified, only packages matching it are 
  2180. returned.  If 'searchtag' is specified, the pattern match is applied 
  2181. to the appropriate tag (e.g., ABSTRACT).
  2182.  
  2183. The data comes from the PPM data file, which contains installation
  2184. information about each installed package.
  2185.  
  2186. =item PPM::InstalledPackageProperties();
  2187.  
  2188. Returns a hash with package names as keys, and package properties as
  2189. attributes.
  2190.  
  2191. =item PPM::QueryPPD("location" => $location, "searchRE" => $searchRE, "searchtag" => $searchtag, "ignorecase" => $ignorecase, "package" => $package);
  2192.  
  2193. Searches for 'searchRE' (a regular expression) in the <ABSTRACT>,
  2194. <AUTHOR> or <TITLE> tags of the PPD file for 'package' at 'location'.
  2195. 'location' may be either a remote address or a directory path, and if
  2196. it is not provided, the default location as specified in the PPM data
  2197. file will be used.  If the 'ignorecase' option is specified, it overrides
  2198. the current global case-sensitivity setting.  On success, the matching
  2199. string is returned.
  2200.  
  2201. =item PPM::RepositoryPackages("location" => $location);
  2202.  
  2203. Returns a hash, with 'location' being the key, and arrays of all packages
  2204. with package description (PPD) files available at 'location' as its
  2205. elements.  'location' may be either a remote address or a directory path.
  2206. If 'location' is not specified, the default location as specified in
  2207. the PPM data file will be used.
  2208.  
  2209. =item PPM::ListOfRepositories();
  2210.  
  2211. Returns a hash containing the name of the repository and its location.
  2212. These repositories will be searched if an explicit location is not
  2213. provided in any function needing to locate a PPD.
  2214.  
  2215. =item PPM::RemoveRepository("repository" => $repository, "save" => $save);
  2216.  
  2217. Removes the repository named 'repository' from the list of available
  2218. repositories.  If 'save' is not specified, the change is for the current
  2219. session only.
  2220.  
  2221. =item PPM::AddRepository("repository" => $repository, "location" => $location, "save" => $save);
  2222.  
  2223. Adds the repository named 'repository' to the list of available repositories.
  2224. If 'save' is not specified, the change is for the current session only.
  2225.  
  2226. =item PPM::RepositoryPackageProperties("package" => $package, "location" => $location);
  2227.  
  2228. Reads the PPD file for 'package', from 'location' or the default repository,
  2229. and returns a hash with keys being the various tags from the PPD (e.g.
  2230. 'ABSTRACT', 'AUTHOR', etc.).
  2231.  
  2232. =item PPM::RepositorySummary("location" => $location);
  2233.  
  2234. Attempts to retrieve the summary file associated with the specified repository,
  2235. or from all repositories if 'location' is not specified.  The return value
  2236. is a hash with the key being the repository, and the data being another
  2237. hash of package name keys, and package detail data.
  2238.  
  2239. =item PPM::GetPPMOptions();
  2240.  
  2241. Returns a hash containing values for all PPM internal options ('IGNORECASE',
  2242. 'CLEAN', 'CONFIRM', 'ROOT', 'BUILDDIR').
  2243.  
  2244. =item PPM::SetPPMOptions("options" => %options, "save" => $save);
  2245.  
  2246. Sets internal PPM options as specified in the 'options' hash, which is
  2247. expected to be the hash previously returned by a call to GetPPMOptions().
  2248.  
  2249. =back
  2250.  
  2251. =head1 EXAMPLES
  2252.  
  2253. =over 4
  2254.  
  2255. =item PPM::AddRepository("repository" => 'ActiveState', "location" => "http://www.ActiveState.com/packages", "save" => 1);
  2256.  
  2257. Adds a repository to the list of available repositories, and saves it in
  2258. the PPM options file.
  2259.  
  2260. =item PPM::InstallPackage("package" => 'http://www.ActiveState.com/packages/foo.ppd');
  2261.  
  2262. Installs the software package 'foo' based on the information in the PPD
  2263. obtained from the specified URL.
  2264.  
  2265. =item PPM::VerifyPackage("package" => 'foo', "upgrade" => true)
  2266.  
  2267. Compares the currently installed version of the software package 'foo' to
  2268. the one available according to the PPD obtained from the package-specific
  2269. location provided in the PPM data file, and upgrades to a newer
  2270. version if available.  If a location for this specific package is not
  2271. given in PPM data file, a default location is searched.
  2272.  
  2273. =item PPM::VerifyPackage("package" => 'foo', "location" => 'P:\PACKAGES', "upgrade" => true);
  2274.  
  2275. Compares the currently installed version of the software package 'foo'
  2276. to the one available according to the PPD obtained from the specified
  2277. directory, and upgrades to a newer version if available.
  2278.  
  2279. =item PPM::VerifyPackage("package" => 'PerlDB');
  2280.  
  2281. Verifies that package 'PerlDB' is up to date, using package locations specified
  2282. in the PPM data file.
  2283.  
  2284. =item PPM::RepositoryPackages("location" => http://www.ActiveState.com/packages);
  2285.  
  2286. Returns a hash keyed on 'location', with its elements being an array of
  2287. packages with PPD files available at the specified location.
  2288.  
  2289. =item PPM::QueryPPD("location" => 'P:\PACKAGES', "searchRE" => 'ActiveState', "searchtag" => 'author');
  2290.  
  2291. Searches the specified location for any package with an <AUTHOR> tag
  2292. containing the string 'ActiveState'.  On a successful search, the matching
  2293. string is returned.
  2294.  
  2295. =item %opts = PPM::GetPPMOptions();
  2296.  
  2297. =item $opts{'CONFIRM'} = 'No';
  2298.  
  2299. =item PPM::SetPPMOptions("options" => \%opts, "save" => 1);
  2300.  
  2301. Sets and saves the value of the option 'CONFIRM' to 'No'.
  2302.  
  2303. =back
  2304.  
  2305. =head1 ENVIRONMENT VARIABLES
  2306.  
  2307. =over 4
  2308.  
  2309. =item HTTP_proxy
  2310.  
  2311. If the environment variable 'HTTP_proxy' is set, then it will
  2312. be used as the address of a proxy for accessing the Internet.
  2313. If the environment variables 'HTTP_proxy_user' and 'HTTP_proxy_pass'
  2314. are set, they will be used as the login and password for the 
  2315. proxy server.
  2316.  
  2317. =back
  2318.  
  2319. =head1 FILES
  2320.  
  2321. =over 4
  2322.  
  2323. =item package.ppd
  2324.  
  2325. A description of a software package, in Perl Package Distribution (PPD)
  2326. format.  More information on this file format can be found in L<XML::PPD>.
  2327. PPM stores a copy of the PPD it uses to install or upgrade any software
  2328. package.
  2329.  
  2330. =item ppm.xml - PPM data file.
  2331.  
  2332. The XML format file in which PPM stores configuration and package
  2333. installation information.  This file is created when PPM is installed,
  2334. and under normal circumstances should never require modification other
  2335. than by PPM itself.  For more information on this file, refer to
  2336. L<XML::PPMConfig>.
  2337.  
  2338. =back
  2339.  
  2340. =head1 AUTHOR
  2341.  
  2342. Murray Nesbitt, E<lt>F<murray@activestate.com>E<gt>
  2343.  
  2344. =head1 SEE ALSO
  2345.  
  2346. L<XML::PPMConfig>
  2347. .
  2348.  
  2349. =cut
  2350.  
  2351.