Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
9.4 KiB

  1. #=============================================================================
  2. # Package: PPM::PPD
  3. # Purpose: Exposes a simple, object-oriented interfaces to PPDs.
  4. # Notes:
  5. # Author: Neil Watkiss
  6. # Date: Mon Sep 10 10:47:08 PDT 2001
  7. #=============================================================================
  8. package PPM::PPD;
  9. use strict;
  10. use XML::Parser;
  11. use Data::Dumper;
  12. $PPM::PPD::VERSION = '3.00';
  13. sub new {
  14. my $this = shift;
  15. my $ppd = shift;
  16. die "Error: PPM::PPD constructor called with undef ppd\n" .
  17. Dumper(caller(0))
  18. unless defined $ppd;
  19. my $class = ref($this) || $this;
  20. my $self = {};
  21. bless $self, $class;
  22. $self->init($ppd);
  23. return $self;
  24. }
  25. sub is_complete {
  26. my $o = shift;
  27. $o->{is_complete};
  28. }
  29. sub find_impl {
  30. my $o = shift;
  31. my $target = shift;
  32. # We must not 'use' this, because the ppminst code also uses PPM::PPD, and
  33. # it doesn't have PPM::Result available, because it never needs to find an
  34. # implementation -- it's already installed!
  35. require PPM::Result;
  36. for my $impl ($o->implementations) {
  37. my $match = 1;
  38. for my $field (keys %$impl) {
  39. next if ref($field);
  40. my $value = $target->config_get($field);
  41. next unless $value->is_success;
  42. $match &&= ($value->result eq $impl->{$field});
  43. }
  44. return PPM::Result::Ok($impl) if $match == 1;
  45. }
  46. PPM::Result::Error("no suitable implementation found for '"
  47. . $o->name . "'.");
  48. }
  49. sub name {
  50. my $o = shift;
  51. my $r = $o->{parsed}{NAME};
  52. return defined $r ? $r : "";
  53. }
  54. sub title {
  55. my $o = shift;
  56. my $r = $o->{parsed}{TITLE};
  57. return defined $r ? $r : "";
  58. }
  59. sub version {
  60. my $o = shift;
  61. my $r = $o->{parsed}{VERSION};
  62. return defined $r ? $r : "";
  63. }
  64. sub version_printable {
  65. my $o = shift;
  66. my $v = $o->version;
  67. printify($v);
  68. }
  69. sub printify {
  70. my $v = shift;
  71. $v =~ s/(?:,0)*$//;
  72. $v .= '.0' unless ($v =~ /,/ or $v eq '');
  73. $v = "(any version)" if $v eq '';
  74. $v =~ tr/,/./;
  75. $v;
  76. }
  77. # This sub returns 1 if $ver is >= to $o->version. It returns 0 otherwise.
  78. # Note: this is only used if the repository doesn't know how to compare
  79. # version numbers. The PPM3Server knows how to do it, the others don't.
  80. sub uptodate {
  81. my $o = shift;
  82. my $ver = shift;
  83. return 1 if $ver eq $o->version; # shortcut
  84. my @required = split ',', $o->version;
  85. my @proposed = split ',', $ver;
  86. for (my $i=0; $i<@required; $i++) {
  87. return 0 if $proposed[$i] < $required[$i]; # too old
  88. return 1 if $proposed[$i] > $required[$i]; # even newer
  89. }
  90. return 1; # They're equal
  91. }
  92. sub abstract {
  93. my $o = shift;
  94. my $r = $o->{parsed}{ABSTRACT};
  95. return defined $r ? $r : "";
  96. }
  97. sub author {
  98. my $o = shift;
  99. my $r = $o->{parsed}{AUTHOR};
  100. return defined $r ? $r : "";
  101. }
  102. sub implementations {
  103. my $o = shift;
  104. return @{$o->{parsed}{IMPLEMENTATION} || []};
  105. }
  106. sub ppd {
  107. my $o = shift;
  108. return $o->{ppd};
  109. }
  110. sub init {
  111. my $o = shift;
  112. my $ppd = shift;
  113. if ($ppd =~ /<SOFTPKG/) {
  114. $o->{ppd} = $ppd;
  115. $o->{source} = caller;
  116. }
  117. elsif ($ppd !~ m![\n]! && -f $ppd) {
  118. $o->loadfile($ppd);
  119. $o->{source} = $ppd;
  120. }
  121. else {
  122. die "PPM::PPD::init: not a PPD and not a file:\n$ppd";
  123. }
  124. $o->parse;
  125. $o->prepare;
  126. delete $o->{parsetree};
  127. $o->{is_complete} = 1;
  128. }
  129. sub loadfile {
  130. my $o = shift;
  131. my $file = shift;
  132. open FILE, $file || die "can't read $file: $!";
  133. $o->{ppd} = do { local $/; <FILE> };
  134. close FILE || die "can't close $file: $!";
  135. }
  136. sub parse {
  137. my $o = shift;
  138. my $parser = XML::Parser->new(Style => 'Tree');
  139. $o->{parsetree} = eval { $parser->parse($o->{ppd}) };
  140. die "error: can't parse " . $o->{ppd} . ": $@" if $@;
  141. }
  142. sub prepare {
  143. my $o = shift;
  144. my $tree = $o->{parsetree};
  145. $o->{parsed} = $o->_reparse($tree);
  146. }
  147. sub _reparse {
  148. my $o = shift;
  149. my $tree = shift;
  150. my $ref = {};
  151. my $i;
  152. for ($i=0; $i<@$tree; $i++) { last if $tree->[$i] eq 'SOFTPKG' }
  153. die "error: no SOFTPKG element in PPD from $o->{source}"
  154. if $i >= @$tree;
  155. $tree = $tree->[$i+1];
  156. my $parse_elem = sub {
  157. my $ref = shift;
  158. my $tree = shift;
  159. my $key = shift;
  160. my $req = shift;
  161. my $content = shift; $content = 2 unless defined $content;
  162. my $cref = shift;
  163. my $i;
  164. for ($i=0; $i<@$tree; $i++) { last if $tree->[$i] eq $key }
  165. die "error: missing $key element in PPD from $o->{source}"
  166. if $req && $i >= @$tree;
  167. return if $i >= @$tree;
  168. $cref->($ref, $key, $content, $tree->[$i+1]) if $cref;
  169. $ref->{$key} = $tree->[$i+1][$content] unless $cref;
  170. };
  171. my $parse_attr = sub {
  172. my $ref = shift;
  173. my $tree = shift;
  174. my $key = shift;
  175. my $req = shift;
  176. my $keephash = shift;
  177. my $cref = shift;
  178. die "error: missing $key attribute in PPD from $o->{source}"
  179. if $req && not exists $tree->[0]{$key};
  180. return if $i >= @$tree;
  181. $cref->($ref, $key, $keephash, $tree->[0]{$key}) if $cref;
  182. $ref->{$key} = $keephash ? $tree->[0] : $tree->[0]{$key} unless $cref;
  183. };
  184. my $parse_impls = sub {
  185. my $ref = shift;
  186. my $tree = shift;
  187. my $cref = sub {
  188. my ($ref, $key, $content, $tree) = @_;
  189. $ref->{$key} = (values %{$tree->[$content]})[0];
  190. };
  191. $parse_elem->($ref, $tree, 'ARCHITECTURE', 0, 0, $cref);
  192. $parse_elem->($ref, $tree, 'CODEBASE', 0, 0, $cref);
  193. $parse_elem->($ref, $tree, 'OS', 0, 0, $cref);
  194. $parse_elem->($ref, $tree, 'OSVERSION', 0, 0, $cref);
  195. $parse_elem->($ref, $tree, 'PERLCORE', 0, 0, $cref);
  196. $parse_elem->($ref, $tree, 'PYTHONCORE', 0, 0, $cref);
  197. # Now parse the DEPENDENCY section:
  198. for (my $i=0; $i<@$tree; $i++) {
  199. next unless $tree->[$i] eq 'DEPENDENCY';
  200. my $r = bless {}, 'PPM::PPD::Dependency';
  201. $parse_attr->($r, $tree->[$i+1], 'NAME', 1);
  202. $parse_attr->($r, $tree->[$i+1], 'VERSION', 0);
  203. push @{$ref->{DEPENDENCY}}, $r;
  204. }
  205. };
  206. # First, get the NAME and VERSION tags
  207. $parse_attr->($ref, $tree, 'NAME', 1);
  208. $parse_attr->($ref, $tree, 'VERSION', 1);
  209. # Now validate the AUTHOR, ABSTRACT, and TITLE elements
  210. $parse_elem->($ref, $tree, 'AUTHOR', 0);
  211. $parse_elem->($ref, $tree, 'ABSTRACT', 0);
  212. $parse_elem->($ref, $tree, 'TITLE', 1);
  213. $ref->{ABSTRACT} ||= "(abstract)";
  214. $ref->{AUTHOR} ||= "(author)";
  215. # Now validate the IMPLEMENTATION sections.
  216. for (my $j=0; $j<@{$tree}; $j++) {
  217. next unless $tree->[$j] eq 'IMPLEMENTATION';
  218. my $r = bless {}, 'PPM::PPD::Implementation';
  219. $parse_impls->($r, $tree->[$j+1]);
  220. push @{$ref->{IMPLEMENTATION}}, $r;
  221. }
  222. $ref;
  223. }
  224. package PPM::PPD::Dependency;
  225. sub name {
  226. my $o = shift;
  227. my $r = $o->{NAME};
  228. return defined $r ? $r : "";
  229. }
  230. sub version {
  231. my $o = shift;
  232. my $r = $o->{VERSION};
  233. return defined $r ? $r : "";
  234. }
  235. sub version_printable {
  236. goto &PPM::PPD::version_printable;
  237. }
  238. sub uptodate {
  239. goto &PPM::PPD::uptodate;
  240. }
  241. package PPM::PPD::Implementation;
  242. sub codebase {
  243. my $o = shift;
  244. my $r = $o->{CODEBASE};
  245. return defined $r ? $r : "";
  246. }
  247. sub os {
  248. my $o = shift;
  249. my $r = $o->{OS};
  250. return defined $r ? $r : "";
  251. }
  252. sub osversion {
  253. my $o = shift;
  254. my $r = $o->{OSVERSION};
  255. return defined $r ? $r : "";
  256. }
  257. sub osversion_printable {
  258. my $o = shift;
  259. my $r = $o->osversion;
  260. PPM::PPD::printify($r);
  261. }
  262. sub architecture {
  263. my $o = shift;
  264. my $r = $o->{ARCHITECTURE};
  265. return defined $r ? $r : "";
  266. }
  267. sub pythoncore {
  268. my $o = shift;
  269. my $r = $o->{PYTHONCORE};
  270. return defined $r ? $r : "";
  271. }
  272. sub perlcore {
  273. my $o = shift;
  274. my $r = $o->{PERLCORE};
  275. return defined $r ? $r : "";
  276. }
  277. sub prereqs {
  278. my $o = shift;
  279. return @{$o->{DEPENDENCY} || []};
  280. }
  281. package PPM::PPD::Search;
  282. @PPM::PPD::Search::ISA = 'PPM::Search';
  283. use Data::Dumper;
  284. sub matchimpl {
  285. my $self = shift;
  286. my ($impl, $field, $re) = @_;
  287. if ($field eq 'OS') { return $impl->os =~ $re }
  288. elsif ($field eq 'OSVERSION') { return $impl->osversion =~ $re }
  289. elsif ($field eq 'ARCHITECTURE') { return $impl->architecture =~ $re}
  290. elsif ($field eq 'CODEBASE') { return $impl->codebase =~ $re }
  291. elsif ($field eq 'PYTHONCORE') { return $impl->pythoncore =~ $re }
  292. elsif ($field eq 'PERLCORE') { return $impl->perlcore =~ $re }
  293. else {
  294. warn "unknown search field '$field'" if $^W;
  295. }
  296. }
  297. sub match {
  298. my $self = shift;
  299. my ($ppd, $field, $match) = @_;
  300. my $re = qr/$match/;
  301. $field = uc($field);
  302. if ($field eq 'NAME') { return $ppd->name =~ $re }
  303. if ($field eq 'AUTHOR') { return $ppd->author =~ $re }
  304. if ($field eq 'ABSTRACT') { return $ppd->abstract =~ $re }
  305. if ($field eq 'TITLE') { return $ppd->title =~ $re }
  306. if ($field eq 'VERSION') { return $ppd->version_printable =~ $re }
  307. return (grep { $_ }
  308. map { $self->matchimpl($_, $field, $re) }
  309. $ppd->implementations);
  310. }
  311. unless (caller) {
  312. my $dat = do { local $/; <DATA> };
  313. eval $dat;
  314. die $@ if $@;
  315. }
  316. 1;
  317. __DATA__
  318. package main;
  319. use Data::Dumper;
  320. my $ppd = PPM::PPD->new("./Tk-JPEG.ppd");
  321. print Dumper $ppd;
  322. print Dumper [$ppd->name,
  323. $ppd->version,
  324. # $ppd->title,
  325. $ppd->abstract,
  326. $ppd->author,
  327. $ppd->implementations(),
  328. ];