home *** CD-ROM | disk | FTP | other *** search
/ Netrunner 2004 October / NETRUNNER0410.ISO / regular / ActivePerl-5.8.4.810-MSWin32-x86.msi / _55d191448473a0c6b7974214a1de5c59 < prev    next >
Encoding:
Text File  |  2004-06-01  |  10.7 KB  |  428 lines

  1. #=============================================================================
  2. # Package: PPM::PPD
  3. # Purpose: Exposes a simple, object-oriented interfaces to PPDs.
  4. # Notes:
  5. # Author:  Neil Watkiss
  6. #=============================================================================
  7. package PPM::PPD;
  8.  
  9. use strict;
  10. use Data::Dumper;
  11. use XML::Simple ();
  12.  
  13. $PPM::PPD::VERSION = '3.05';
  14.  
  15. sub new {
  16.     my $this = shift;
  17.     my $ppd  = shift;
  18.     my $rep  = shift;    # the repository object that retrieved this PPD
  19.     my $id   = shift;    # the unique key in the repository of this PPD
  20.     die "Error: PPM::PPD constructor called with undef ppd\n" .
  21.       Dumper(caller(0))
  22.         unless defined $ppd;
  23.     my $class = ref($this) || $this;
  24.     my $self = bless {
  25.     rep => $rep,
  26.     id  => $id,
  27.     from => 'unknown',
  28.     }, $class;
  29.     $self->init($ppd);
  30.     return $self;
  31. }
  32.  
  33. sub repository {
  34.     my $o = shift;
  35.     $o->{rep};
  36. }
  37. sub id {
  38.     my $o = shift;
  39.     $o->{id};
  40. }
  41.  
  42. # Whenever PPM::Repository or a subclass creates a PPM::PPD object, it sets
  43. # 'from' to "Repository". This allows the client to decide whether a PPM::PPD
  44. # object is the result of a query, a search, or a describe. Etc.
  45. sub from {
  46.     my $o = shift;
  47.     $o->{from};
  48. }
  49.  
  50. sub is_complete {
  51.     my $o = shift;
  52.     $o->{is_complete};
  53. }
  54.  
  55. sub find_impl_raw {
  56.     my $o = shift;
  57.     my $target = shift;
  58.     for my $impl ($o->implementations) {
  59.     my $match = 1;
  60.     for my $field (keys %$impl) {
  61.         next if ref($impl->{$field});
  62.         my $value = $target->config_get($field);
  63.         if ($value && ref($value) && eval { $value->isa("PPM::Result") }) {
  64.         next unless $value->is_success;
  65.         $match &&= ($value->result eq $impl->{$field});
  66.         }
  67.         else {
  68.         next unless defined $value;
  69.         $match &&= ($value eq $impl->{$field});
  70.         }
  71.     }
  72.     return $impl if $match == 1;
  73.     }
  74.     return undef;
  75. }
  76.  
  77. sub find_impl {
  78.     my $o = shift;
  79.     my $target = shift;
  80.     my $impl = $o->find_impl_raw($target);
  81.     # We must not 'use' this, because the ppminst code also uses PPM::PPD, and
  82.     # it doesn't have PPM::Result available.
  83.     require PPM::Result;
  84.     return PPM::Result::Ok($impl) if $impl;
  85.     PPM::Result::Error("no suitable implementation found for '"
  86.                . $o->name . "'.");
  87. }
  88.  
  89. sub name {
  90.     my $o = shift;
  91.     my $r = $o->{parsed}{NAME};
  92.     return defined $r ? $r : "";
  93. }
  94.  
  95. sub title {
  96.     my $o = shift;
  97.     my $r = $o->{parsed}{TITLE};
  98.     return defined $r ? $r : "";
  99. }
  100.  
  101. sub version_osd {
  102.     my $o = shift;
  103.     my $r = $o->{parsed}{VERSION};
  104.     return defined $r ? $r : "";
  105. }
  106.  
  107. sub version {
  108.     my $o = shift;
  109.     my $v = $o->version_osd;
  110.     printify($v);
  111. }
  112.  
  113. sub printify {
  114.     my $v = shift;
  115.     $v =~ s/(?:[\.,]0)*$//;
  116.     $v .= '.0' unless ($v =~ /[\.,]/ or $v eq '');
  117.     $v = "(any version)" if $v eq '';
  118.     $v =~ tr/,/./;
  119.     $v;
  120. }
  121.  
  122. # This sub returns 1 if $ver is >= to $o->version. It returns 0 otherwise.
  123. # Note: this is only used if the repository doesn't know how to compare
  124. # version numbers. The PPM3Server knows how to do it, the others don't.
  125. sub uptodate {
  126.     my $o = shift;
  127.     my $ver = shift;
  128.  
  129.     return 1 if $ver eq $o->version_osd; # shortcut
  130.  
  131.     my @required = split /[\.,]/, $o->version_osd;
  132.     my @proposed = split /[\.,]/, $ver;
  133.  
  134.     for (my $i=0; $i<@required; $i++) {
  135.     no warnings;
  136.     return 0 if $proposed[$i] < $required[$i];    # too old
  137.     return 1 if $proposed[$i] > $required[$i];    # even newer
  138.     }
  139.     return 1; # They're equal
  140. }
  141.  
  142. sub abstract {
  143.     my $o = shift;
  144.     my $r = $o->{parsed}{ABSTRACT};
  145.     return defined $r ? $r : "";
  146. }
  147.  
  148. sub author {
  149.     my $o = shift;
  150.     my $r = $o->{parsed}{AUTHOR};
  151.     return defined $r ? $r : "";
  152. }
  153.  
  154. sub implementations {
  155.     my $o = shift;
  156.     return @{$o->{parsed}{IMPLEMENTATION} || []};
  157. }
  158.  
  159. sub ppd {
  160.     my $o = shift;
  161.     return $o->{ppd};
  162. }
  163.  
  164. sub init {
  165.     my $o = shift;
  166.     my $ppd = shift;
  167.  
  168.     if ($ppd =~ /<SOFTPKG/) {
  169.     $o->{ppd} = $ppd;
  170.     $o->{source} = caller;
  171.     }
  172.     elsif ($ppd !~ m![\n]! && -f $ppd) {
  173.     $o->loadfile($ppd);
  174.     $o->{source} = $ppd;
  175.     }
  176.     else {
  177.     die "PPM::PPD::init: not a PPD and not a file:\n$ppd";
  178.     }
  179.  
  180.     $o->parse;
  181. }
  182.  
  183. sub loadfile {
  184.     my $o = shift;
  185.     my $file = shift;
  186.     open FILE, $file        || die "can't read $file: $!";
  187.     $o->{ppd} = do { local $/; <FILE> };
  188.     close FILE            || die "can't close $file: $!";
  189. }
  190.  
  191. sub parse {
  192.     my $o = shift;
  193.     my $parser = XML::Simple->new(
  194.     forcearray    => 1,
  195.     forcecontent    => 1,
  196.     keyattr        => [],
  197.     suppressempty    => undef,
  198.     );
  199.     my $tree = eval { $parser->XMLin($o->{ppd}) };
  200.     die "error: can't parse $o->{ppd}: $@" if $@;
  201.  
  202.     # First: SOFTPKG attributes:
  203.     $o->{parsed}{NAME}        = $o->conv($tree->{NAME});
  204.     $o->{parsed}{VERSION}    = $o->conv($tree->{VERSION});
  205.  
  206.     # Next: childless elements:
  207.     $o->{parsed}{ABSTRACT}    = $o->conv($tree->{ABSTRACT}[0]{content});
  208.     $o->{parsed}{AUTHOR}    = $o->conv($tree->{AUTHOR}[0]{content});
  209.     $o->{parsed}{TITLE}        = $o->conv($tree->{TITLE}[0]{content});
  210.  
  211.     # Next: IMPLEMENTATION:
  212.     my @impls;
  213.     for my $impl (@{$tree->{IMPLEMENTATION}}) {
  214.     my $i = PPM::PPD::Implementation->new({});
  215.     for my $key (keys %$impl) {
  216.         # Next: DEPENDENCY:
  217.         if ($key eq 'DEPENDENCY') {
  218.         my @deps = @{$impl->{$key}};
  219.         $i->{DEPENDENCY} = 
  220.           [map { PPM::PPD::Dependency->new($_) } @deps];
  221.         next;
  222.         }
  223.         # Next: LANGUAGE:
  224.         if ($key eq 'LANGUAGE') {
  225.         my $v = $impl->{$key}[0];
  226.         my $lang = {
  227.             NAME    => $o->conv($v->{NAME}),
  228.             VERSION    => $o->conv(
  229.             $v->{COMPAT}[0]{VERSION} || $v->{VERSION}
  230.             ),
  231.             TYPE    => $o->conv($v->{COMPAT}[0]{TYPE}),
  232.         };
  233.         $i->{LANGUAGE} = PPM::PPD::Language->new($lang);
  234.         next;
  235.         }
  236.         # Next: INSTALL or UNINSTALL.
  237.         if ($key eq 'INSTALL' or $key eq 'UNINSTALL') {
  238.         my $v = $impl->{$key}[0];
  239.         $i->{"${key}_SCRIPT"} = PPM::PPD::Script->new({
  240.             EXEC    => $o->conv($v->{EXEC}),
  241.             HREF    => $o->conv($v->{HREF}),
  242.             SCRIPT    => $o->conv($v->{content}),
  243.         });
  244.         }
  245.         # Next: CODEBASE, OS, OSVERSION, etc.
  246.         my @keys = qw(NAME VALUE);
  247.         push @keys, qw(HREF) if $key eq 'CODEBASE';
  248.         for (@keys) {
  249.         next unless exists $impl->{$key}[0]{$_};
  250.         $i->{$key} = $o->conv($impl->{$key}[0]{$_});
  251.         last;
  252.         }
  253.     }
  254.     push @impls, $i;
  255.     }
  256.     $o->{parsed}{IMPLEMENTATION} = \@impls;
  257.     $o->{is_complete} = @impls;
  258. }
  259.  
  260. sub conv {
  261.     use Unicode::String qw(utf8);
  262.     my $o = shift;
  263.     my $u = utf8(shift(@_) || '');
  264.     my $use_utf8 = 0;
  265.     for my $env (qw(LC_ALL LC_CTYPE LANG PPM_LANG)) {
  266.     $use_utf8 = 1, last if $ENV{$env} and $ENV{$env} =~ /UTF-8/;
  267.     }
  268.     # silence "Data outside latin1 range" warnings
  269.     local $SIG{__WARN__} = sub {};
  270.     $u->stringify_as('latin1') unless $use_utf8;
  271.     "$u";
  272. }
  273.  
  274. package PPM::PPD::Base;
  275.  
  276. sub new {
  277.     my $cls = shift;
  278.     my $obj = shift;
  279.     bless $obj, $cls;
  280. }
  281.  
  282. sub AUTOLOAD {
  283.     my $method = $PPM::PPD::Base::AUTOLOAD;
  284.     $method =~ s/^.+:://;
  285.     my $o = shift;
  286.     my $r = $o->{uc($method)};
  287.     defined $r ? $r : '';
  288. }
  289.  
  290. sub version_printable { die }
  291. sub osversion_printable { die }
  292.  
  293. #=============================================================================
  294. # PPM::PPD::Implementation.
  295. # Exposes the following methods:
  296. #
  297. # architecture
  298. # codebase
  299. # os
  300. # osversion_osd
  301. # osversion
  302. # perlcore
  303. # install_script
  304. # uninstall_script
  305. # pythoncore
  306. # prereqs    # returns a list of PPM::PPD::Dependency objects
  307. # language    # returns a PPM::PPD::Language object
  308. #=============================================================================
  309. package PPM::PPD::Implementation;
  310. our @ISA = qw(PPM::PPD::Base);
  311.  
  312. sub osversion_osd {
  313.     my $o = shift;
  314.     my $r = $o->{OSVERSION};
  315.     defined $r ? $r : '';
  316. }
  317.  
  318. sub osversion {
  319.     my $o = shift;
  320.     my $r = $o->osversion_osd;
  321.     PPM::PPD::printify($r);
  322. }
  323.  
  324. sub prereqs {
  325.     my $o = shift;
  326.     return @{$o->{DEPENDENCY} || []};
  327. }
  328.  
  329. sub language {
  330.     my $o = shift;
  331.     $o->{LANGUAGE};
  332. }
  333.  
  334. #=============================================================================
  335. # PPM::PPD::Script
  336. # Exposes the following methods:
  337. #
  338. # exec            # a shell/interpreter to use to run the script
  339. # href            # a script to download
  340. # script        # the content of the script (if href not specified)
  341. #=============================================================================
  342. package PPM::PPD::Script;
  343. our @ISA = qw(PPM::PPD::Base);
  344.  
  345. #=============================================================================
  346. # PPM::PPD::Language.
  347. # Exposes the following methods:
  348. #
  349. # name
  350. # version        # no OSD version for LANGUAGE tag
  351. # type            # one of 'SYNTAX' or 'BINARY'
  352. #
  353. # matches_target($target)    # returns 1 if $target can install PPD, else 0
  354. #=============================================================================
  355. package PPM::PPD::Language;
  356. our @ISA = qw(PPM::PPD::Base);
  357.  
  358. sub matches_target {
  359.     my $o = shift;
  360.     my $t = shift;
  361.     $t->can_install($o->name, $o->version, $o->type);
  362. }
  363.  
  364. #=============================================================================
  365. # PPM::PPD::Dependency.
  366. # Exposes the following methods:
  367. #
  368. # name
  369. # version
  370. # version_osd
  371. # uptodate($ppd)    # returns 1 if the given PPM::PPD object satisfies the
  372. #             # dependency, or 0 otherwise.
  373. #=============================================================================
  374. package PPM::PPD::Dependency;
  375. our @ISA = qw(PPM::PPD::Base);
  376.  
  377. sub version_osd {
  378.     my $o = shift;
  379.     my $r = $o->{VERSION};
  380.     defined $r ? $r : '';
  381. }
  382.  
  383. sub version {
  384.     goto &PPM::PPD::version;
  385. }
  386.  
  387. sub uptodate {
  388.     goto &PPM::PPD::uptodate;
  389. }
  390.  
  391. package PPM::PPD::Search;
  392. @PPM::PPD::Search::ISA = 'PPM::Search';
  393.  
  394. use Data::Dumper;
  395.  
  396. sub matchimpl {
  397.     my $self = shift;
  398.     my ($impl, $field, $re) = @_;
  399.     if ($field eq 'OS')            { return $impl->os =~ $re }
  400.     elsif ($field eq 'OSVERSION')    { return $impl->osversion =~ $re }
  401.     elsif ($field eq 'ARCHITECTURE')    { return $impl->architecture =~ $re}
  402.     elsif ($field eq 'CODEBASE')    { return $impl->codebase =~ $re }
  403.     elsif ($field eq 'PYTHONCORE')    { return $impl->pythoncore =~ $re }
  404.     elsif ($field eq 'PERLCORE')    { return $impl->perlcore =~ $re }
  405.     else {
  406.     warn "unknown search field '$field'" if $^W;
  407.     }
  408. }
  409.  
  410. sub match {
  411.     my $self = shift;
  412.     my ($ppd, $field, $match) = @_;
  413.     my $re = qr/$match/;
  414.     $field = uc($field);
  415.     if ($field eq 'NAME')     { return $ppd->name =~ $re }
  416.     if ($field eq 'AUTHOR')      { return $ppd->author =~ $re }
  417.     if ($field eq 'ABSTRACT')    { return $ppd->abstract =~ $re }
  418.     if ($field eq 'TITLE')       { return $ppd->title =~ $re }
  419.     if ($field eq 'VERSION')     { return $ppd->version_printable =~ $re }
  420.     return (grep { $_ }
  421.         map { $self->matchimpl($_, $field, $re) }
  422.         $ppd->implementations);
  423. }
  424.  
  425. 1;
  426.