File Coverage

blib/lib/Resources.pm
Criterion Covered Total %
statement 259 507 51.0
branch 80 202 39.6
condition 23 100 23.0
subroutine 22 38 57.8
pod 20 20 100.0
total 404 867 46.6


line stmt bran cond sub pod time code
1             # Copyright 1995 Francesco Callari, McGill University. See notice
2             # at end of this file.
3             #
4             # Filename: Resources.pm
5             # Author: Francesco Callari (franco@cim.mcgill.ca)
6             # Created: Wed May 31 17:55:21 1995
7             # Version: $Id:
8             # Resources.pm,v 0.1 1995/10/19 02:49:43 franco Exp franco $
9              
10              
11             =head1 NAME
12              
13             Resources - handling application defaults in Perl.
14              
15             =head1 SYNOPSIS
16              
17             use Resources;
18              
19             $res = new Resources;
20             $res = new Resources "resfile";
21              
22             =head1 DESCRIPTION
23              
24             Resources are a way to specify information of interest to program or
25             packages.
26              
27             Applications use resource files to specify and document the values of
28             quantities or attributes of interest.
29              
30             Resources can be loaded from or saved to resource files. Methods are
31             provided to search, modify and create resources.
32              
33             Packages use resources to hardwire in their code the default values for
34             their attributes, along with documentation for the attibutes themselves.
35              
36             Packages inherit resources when subclassed, and the resource names are
37             updated dynamically to reflect a class hierarchy.
38              
39             Methods are provided for interactive resource inspection and editing.
40              
41             =head2 1. Resource inheritance
42              
43             Package attributes are inherited from base and member classes, their names are
44             dynamically updated to reflect the inheritance, and values specified in
45             derived/container classes override those inherited from base/member classes.
46              
47             More precisely, there a few rules governing the inheritance of resource
48             names and values, and they will be explained by way of examples.
49              
50             As far as resource names, the rules are:
51              
52             =over 8
53              
54             =item Base class
55              
56             If Vehicle has a "speed" property, then it can use a resource named
57             "vehicle.speed" to specify its default value.
58              
59             =item Derived class
60              
61             If Car B Vehicle, then Car has a "car.speed" resource automagically
62             defined by inheritance from the base class.
63              
64             =item Container class
65              
66             If Car B member object called Tire, and Tire has a "tire.pressure"
67             resource, then Car inherits a "car.tire.pressure" resource from the member
68             class.
69              
70             =item Application class
71              
72             All resources of Car objects used by a program named "race" have the prefix
73             "race." prepended to their names, e.g. "race.car.speed",
74             "race.car.tire.pressure", etc.
75              
76             =back
77              
78             With regard to assigning values to resources, the rules are:
79              
80             =over 8
81              
82             =item Specification in a file
83              
84             Resources specified in a resource file always override hardcoded resources
85             (with the exception of "hidden" resources, see below).
86              
87             =item Inheritance
88              
89             Resources defined in a derived class (like Car) override those specified in
90             a base class. Likewise, resources defined in a container class override
91             those specified in the members.
92              
93             In the above example, a default value for "car.speed" in Car overrides the
94             value of "vehicle.speed" in any Car object, otherwise "car.speed" assumes the
95             value of "vehicle.speed". Same for "car.tire.pressure".
96              
97             =back
98              
99             =head2 2. Resource Files.
100              
101             A resource specification in a (text) resource file is a line of the form:
102              
103             sequence: value
104              
105             There may be any number of whitespaces between the name and the colon
106             character, and between the colon and the value.
107              
108             =over 8
109              
110             =item B can have four forms:
111              
112             (1) word
113              
114             A B not containing whitespaces, colons (':'), dots ('.') or asterisks
115             ('*'), nor starting with an underscore ('_').
116              
117             Or, recursively:
118              
119             (2) word.sequence
120             (3) word*sequence
121             (4) *sequence
122              
123             The asterisks in a resource name act as wildcards, matching any sequence of
124             characters.
125              
126             For cases (3) or (4) the B must be or match the current application
127             class, otherwise the resource specification is silently ignored (this means
128             that an applications loads from a file only its own resources, and those whose
129             application class is a wildcard).
130              
131             No distinction is made between uppercase and lowercase letters.
132              
133             =item B can be:
134              
135             An unadorned word or a quoted sequence of whitespace-separated words. Both
136             single (' ') and double quotes quotes (" ") are allowed, and they must be
137             paired.
138              
139             Any I scalar constructor in Perl, including anon references to
140             constant arrays or hashes.
141              
142             The special words B, B, B, B (case insensitive) are
143             treated as boolean resources and converted 1 and 0, unless they are quoted.
144              
145             =back
146              
147             Examples of valid resource specifications:
148              
149             car*brand : Ferrari # A word.
150             car.price : 200K # Another word
151             car.name : '312 BB' # A quoted sentence
152             car*runs*alot : yes # A boolean, converted to 1.
153             car*noise*lotsa : 'yes' # yes, taken verbatim
154             car.size : [1, [2, 3]] # An anon array.
155             car.lett : {"P"=>1, "Q"=>[2, 3]} # An anon hash.
156              
157             Examples of illegal resource names:
158              
159             car pedal # Whitespace in the name.
160             .carpedal # Leading dot in name.
161             car._pedal # Leading underscore in _dog.
162             carpedal* # Trailing asterisk.
163             carpedal. # Trailing dot.
164              
165             A resource file may contain comments: anything from a hash ('#') character to
166             the end of a line is ignored, unless the hash character appears inside a
167             quoted value string.
168              
169             Resource specifications may be split across successive lines, by terminating
170             the split lines with a backslash, as per cpp(1).
171              
172             =head2 3. The Resources hash
173              
174             A non-my hash named %Resources can be used to specify the default values for
175             the attributes of a package in its source code, along with documentation for
176             the attributes themselves. The documentation itself is "dynamical" (as opposed
177             to the static, pod-like variety) in that it follows a class hyerarchy and is
178             suitable for interactive display and editing.
179              
180             The %Resources hash is just a hash of
181              
182             $Name => [$Value, $Doc]
183              
184             things. Each hash key B<$Name> is a resource name in the above sequence
185             form. Each hash value is a reference to an anon array B<[$Value, $Doc]>, with
186             B<$Doc> being an optional resource documentation.
187              
188             The resource $Name I contain wildcard ('*') or colon (':') characters,
189             nor start or end with a dot ('.'). Also, it must I be prefixed with the
190             package name (since this is automatically prepended by the B method,
191             see below). Names starting with an underscore ('_') character are special in
192             that they define "hidden" resources. These may not be specified in resource
193             files, nor dynamically viewed/edited: they come handy to specify global
194             parameters when you do not want to use global application-wide variables,
195             and/or want to take advantage of the inheritance mechanism.
196              
197             The resource $Value can be any I scalar Perl constructor, including
198             references to arrays and/or hashes of constants (or references
199             thereof). Boolean values must be specified as 1 or 0.
200              
201             The resource documentation is a just string of any length: it will be
202             appropriately broken into lines for visualization purposes. It can also be
203             missing, in which case an inherited documentation is used (if any exists, see
204             the B method below).
205              
206             The content of a resource hash is registered in a global Resource object using
207             the B method.
208              
209             Here is an example of deafults specification for a package.
210              
211             package Car;
212             @ISA = qw( Vehicle );
213             use vars qw( %Resources );
214              
215             %Resources = (
216             brand => ["FIAT", "The carmaker"],
217             noise => ["Ashtmatic", "Auditory feeling"],
218             sucks => [1, "Is it any good?"],
219             nuts => [ { on => 2, off => [3, 5] }, "Spares"],
220             '_ghost' => [0, "Hidden. Mr. Invisible"]
221             'tire.flat' => [0],
222             );
223              
224             The last line overrides a default in member class Tire. The corresponding
225             doc string is supposedly in the source of that class. The last two hash keys
226             are quoted because of the non alphanumeric characters in them.
227              
228             =head2 4. Objects and resources
229              
230             The recommended way to use resources with Perl objects is to pass a
231             Resource object to the "new" method of a package. The method itself will
232             merge the passed resources with the package defaults, and the passed resource
233             will override the defaults where needed.
234              
235             Resource inheritance via subclassing is then easily achieved via the B
236             method, as shown in the EXAMPLES section.
237              
238             =cut
239            
240             require 5.001;
241             package Resources;
242 1     1   821 use strict;
  1         2  
  1         31  
243 1     1   6 use Carp;
  1         2  
  1         100  
244 1     1   3546 use Safe;
  1         54619  
  1         65  
245 1     1   871 use FileHandle;
  1         13192  
  1         7  
246              
247             #
248             # Global variables
249             #
250 1     1   418 use vars qw( $VERSION %Resources $NAME $Value $Doc $Loaded $Merged );
  1         2  
  1         1964  
251              
252             $VERSION = "1.03";
253              
254             $Value=0, $Doc=1, $Loaded=2, $Merged=3; # Indices in resource value
255              
256              
257             # Resources of Resources ;-)
258             %Resources =
259             (
260             'resources.appclass' => [$0,
261             "The application name of this Resource " .
262             "object."],
263             'resources.editor' => ["/bin/vi",
264             "Resource editor command."],
265             'resources.mergeclass' => [1,
266             "Boolean. True to merge with " .
267             "class inheritance."],
268             'resources.pager' => ["/bin/cat",
269             "Resource pager command."],
270              
271             'resources.resources' => ['%Resources',
272             "The name of the standard default hash."],
273             'resources.separator' => [':',
274             "Pattern separating names from values in " .
275             "resource files."],
276             'resources.tmpfil' => ["/tmp/resedit$$",
277             "Editor temporary file."],
278             'resources.updates' => [0,
279             "Number of resource updates."],
280             'resources.verbosity' => [1,
281             "True to print warnings."],
282             'resources.viewcols' => [78,
283             "Width of view/edit window."],
284             'resources.viewmincols' => [15,
285             "Minimum width of a comment line in view."],
286             'resources.writepod' => [0,
287             "Boolean. True if the write method should " .
288             "output in POD format."],
289             );
290              
291             #
292             # Method declarations
293             #
294             sub new;
295             sub DESTROY;
296             sub load;
297             sub merge;
298             sub put;
299             sub valbyname;
300             sub docbyname;
301             sub valbypattern;
302             sub docbypattern;
303             sub namebyclass;
304             sub valbyclass;
305             sub docbyclass;
306             sub each;
307             sub names;
308             sub view;
309             sub edit;
310              
311              
312             #
313             # Unexported subroutines
314             #
315             sub _chain_classes;
316             sub _parse;
317             sub _parse_ref;
318             sub _error;
319             sub _printformat;
320             sub _dump;
321              
322             =head2 5. Methods in class Resources
323              
324             =head2 5.1. Creation and initialization
325              
326             =over 8
327              
328             =item B
329              
330             Creates a new resource database, initialized with the defaults for
331             class Resources (see below for a list of them).
332              
333             If a nonempty file name is specified in $resfile, it initializes the object
334             with the content of the so named resource file. For safe (non overwriting)
335             loading, see the B method below.
336              
337             If the special file name "_RES_NODEFAULTS" is specified, the object is created
338             completely empty, with not even the Resources class defaults in it.
339              
340             Returns the new object, or undef in case of error.
341              
342             =cut
343              
344             sub new {
345 1     1 1 67 my $type = shift;
346 1         3 my $resfile = shift;
347 1         3 my ($name, $valdoc, $app);
348 1         3 my $res = bless {};
349            
350 1         8 $res->{Load} = 0; # 1 if loading
351 1         2 $res->{Merge} = 0; # 1 if merging
352 1         5 $res->{Wilds} = {}; # Wildcarded resources.
353 1         3 $res->{Res} = {}; # Named resources.
354 1         3 $res->{Owned} = {}; # Inverted index of member clases.
355 1         3 $res->{Isa} = {}; # Inverted index of base classes.
356              
357             # Safe environment for the evaluation of constructors.
358 1 50       11 $res->{Safe} = new Safe or
359             ($res->_error("new", "can't get a Safe object."), return undef);
360              
361             # Hack hack - the special filename "_RES_NODEFAULTS" is
362             # used to prevent resource initialization (e.g. when called by the
363             # "bypattern" method
364 1 50 33     2272 unless ($resfile && $resfile eq "_RES_NODEFAULTS") {
365             # Must make sure this is not overridden by a wildcard
366 1         6 $res->{Wilds}->{'.*resources\.updates'} = [0];
367 1         5 $res->{Res}->{'resources.updates'}->[$Value] = 0;
368            
369             # Get appclass without extensions
370 1 50       9 if (($app = $Resources{'resources.appclass'}->[$Value]) =~ /\./) {
371 1         6 $Resources{'resources.appclass'}->[$Value] = (split(/\./, $app))[0];
372             }
373              
374             # Bootstrap defaults. We don't want any subclassing here
375 1         7 while (($name, $valdoc) = each(%Resources)) {
376 12         40 $res->{Res}->{$name} = $valdoc;
377             }
378             }
379              
380 1 50 33     4 if ($resfile && $resfile ne "_RES_NODEFAULTS") {
381 0 0       0 $res->load($resfile) ||
382             ($res->_error("new", "can't load"), return undef);
383             }
384              
385 1         4 $res;
386             }
387            
388              
389             sub DESTROY {
390 1     1   30 my $res=shift;
391 1         25 Safe::DESTROY($res->{Safe});
392             }
393              
394              
395             =item B
396              
397             Loads resources from a file named $resfile into a resource database.
398              
399             The $nonew argument controls whether loading of non already defined resurces is
400             allowed. If it is true, safe loading is performed: attempting to load
401             non-wildcarded resource names that do not match those already present in the
402             database causes an error. This can be useful if you want to make sure that
403             only pre-defined resources (for which you presumably have hardwired defaults)
404             are loaded. It can be a safety net against typos in a resource file.
405              
406             Use is made of B to parse values specified through Perl
407             constructors (only constants, anon hashes and anon arrays are allowed).
408              
409             Returns 1 if ok, 0 if error.
410              
411             =cut
412            
413             sub load {
414 0     0 1 0 my $res = shift;
415 0         0 my ($filnam, $nonew) = @_;
416 0         0 my ($lin, $prevlin, $comlin, @line);
417 0         0 my ($name, @allvals, $value, %allres, $def, @dum);
418 0         0 my ($sep, $expr, $evaled);
419 0         0 my ($app, $mrgcls);
420              
421 0 0 0     0 $res->_error("load","No filename.") && return 0 unless defined $filnam;
422            
423 0 0 0     0 $res->_error("load", $!) && return 0 unless open(_RESFILE, $filnam);
424 0         0 $res->{Safe}->share('$expr');
425 0   0     0 $sep = $res->{Res}->{'resources.separator'}->[$Value] || ':';
426 0         0 $app = $res->{Res}->{'resources.appclass'}->[$Value];
427 0         0 $mrgcls = $res->{Res}->{'resources.mergeclass'}->[$Value];
428              
429 0         0 $prevlin = '';
430 0         0 while ($lin = <_RESFILE>) {
431 0         0 chomp $lin;
432 0         0 $comlin = $prevlin . $lin;
433              
434             # Hash chars in quoted strings are not comments.
435 0         0 1 while $comlin =~ s/^(.*\".*)\#(.*\".*)$/$1__RES_NO_COMM__$2/ ;
436 0         0 1 while $comlin =~ s/^(.*\'.*)\#(.*\'.*)$/$1__RES_NO_COMM__$2/ ;
437            
438             # Join split lines
439 0 0 0     0 if ($comlin !~ /\#/ && $comlin =~ /\\$/) {
440 0         0 $prevlin .= $comlin;
441 0         0 next;
442             } else {
443 0         0 $prevlin = '';
444             }
445              
446             # Now get rid of comments
447 0         0 @line = split(/\#/, $comlin);
448              
449             # Skip empty lines, get def and put hashes back in place
450 0   0     0 $def = $line[0] || next;
451 0         0 $def =~ s/__RES_NO_COMM__/\#/go;
452              
453             # Split def on first separator
454 0         0 ($name, @allvals)=split(/$sep/, $def);
455 0         0 $value=join($sep, @allvals);
456              
457             # Get rid of trailing/leading whitespaces.
458 0         0 $name =~ s/^\s+|\s+$//g;
459 0         0 $value =~ s/^\s+|\s+$//g;
460              
461 0 0       0 next unless $name;
462              
463             # Application class check
464 0 0 0     0 next if ($mrgcls && $name !~ /^\*|^$app\./);
465            
466             # Name may not
467             # - contain whitespaces or
468             # - terminate with wildcard or dot,
469             # - start with dot
470             # - contain ._ sequences (which are for hidden resources only)
471 0 0 0     0 $res->_error("load", "$filnam: line $.: bad resource name: $name")
472             && return 0 if $name =~ /\s+|^\.|\.$|\*$|\._/o;
473            
474             # Parse value:
475             # If the whole thing is quoted, take it as it is:
476 0 0       0 if ($value =~ s/^\'(.*)\'$|^\"(.*)\"$/$1/ ) {
    0          
477 0         0 $allres{$name} = [ $value ];
478             } elsif ($value =~ /^[\[\{].*/) {
479             # Do anon hashes and arrays
480 0         0 $evaled = $res->{Safe}->reval('$expr=' . $value);
481 0 0       0 if ($@) {
482 0         0 $res->_error("load",
483             "$filnam: error in line $. ($@) - $name : $value");
484 0         0 return 0;
485             } else {
486 0         0 $allres{$name} = [ $evaled ];
487             }
488             } else {
489             # Swallow it anyway, babe ;-)
490 0         0 $allres{$name} = [ $value ];
491             }
492             }
493 0         0 close(_RESFILE);
494            
495             # Safe loading checks
496 0 0       0 if ($nonew) {
497 0         0 my $resnames = join(' ', sort($res->names()));
498              
499 0         0 foreach $name (keys(%allres)) {
500 0 0       0 unless ($resnames =~ /$name/) {
501 0         0 $res->_error("load", "unknown resource $name in $filnam");
502 0         0 return(0);
503             }
504             }
505             }
506              
507 0         0 $res->{Load}=1;
508 0         0 while (($name, $value) = each(%allres)) {
509 0 0       0 $res->put($name, @{$value}) || do {
  0         0  
510 0         0 _error("load", "failed put $name : $value");
511 0         0 $res->{Load}=0;
512 0         0 return 0;
513             };
514             }
515 0         0 $res->{Load}=0;
516              
517 0         0 1;
518             }
519              
520              
521             =item B
522              
523             Merges the %Resources hash of the package defining $class with
524             those of its @memberclasses, writing the result in the resource database.
525              
526             The merging reflects the resource inheritance explained above: the %Resources
527             of all base classes and member classes of $class are inherited along the
528             way. Eventually all these resources have their names prefixed with the name of
529             the package in which $class is defined (lowercased and stripped of all
530             foo::bar:: prefixes), and with the application class as well.
531              
532             In the above example, the defaults of a Car object will be renamed, after
533             merging as:
534              
535             car.brand, car.noise, ...,
536             car.tire.flat
537              
538             and for a Civic object, where Civic is a (i.e. ISA) Car, they will be
539             translated instead as
540              
541             civic.brand, civic.noise, ...,
542             civic.tire.flat
543              
544             Finally, the application name ($0, a.k.a $PROGRAM_NAME in English) is
545             prepended to all resource names, so, if the above Civic package is used
546             by a Perl script named "ilove.pl", the final names after merging are
547              
548             ilove.civic.brand, ilove.civic.noise, ...,
549             ilove.civic.tire.flat
550              
551             The new names are the ones to use when accessing these resources by name.
552              
553             The resource values are inherited accoring to the rules previously indicated,
554             hence with resource files having priority over hardcoded defaults, nnd derived
555             or container classes over base or member classes.
556              
557             Returns 1 if for success, otherwise 0.
558              
559             =cut
560              
561             sub merge {
562 3     3 1 189 my ($res, $class, @members) = @_;
563 3         4 my ($app, @tops, $top, $topclass, $toppack, $mem);
564 0         0 my ($level, $caller, @ignore);
565 0         0 my ($isaname, $isa, $base);
566              
567             # Add to inverted indexes.
568             # Members
569 3         7 for $mem (@members) {
570 1 50       8 $res->{Owned}->{$mem} = '' unless $res->{Owned}->{$mem};
571 1         4 $res->{Owned}->{$mem} .= "$class ";
572             }
573             # Base classes
574 3         3 do {
575 1     1   8 no strict;
  1         2  
  1         4967  
576 3         4 $isaname = "$class\::ISA";
577 3         9 $isa = \@$isaname;
578             };
579 3 100       3 if (defined(@{$isa})) {
  3         8  
580 2         3 for $base (@{$isa}) {
  2         7  
581 2 100       8 $res->{Isa}->{$base} = '' unless $res->{Isa}->{$base};
582 2         7 $res->{Isa}->{$base} .= "$class ";
583             }
584             }
585              
586             # Walk up the caller frames.
587             # If one of the callers is in the Isa list for $class, then $class
588             # defaults have been already merged, so we can bail out.
589             # Otherwise make up class name for $object, taking into account the Owned
590             # list.
591 3 50 33     22 if ($class ne "main"
592             && $class ne lc($res->{Res}->{'resources.appclass'}->[$Value])) {
593 3         4 $level=0;
594 3         3 $toppack = $class;
595 3         31 while (($caller, @ignore)=caller(++$level)) {
596 4 100       15 last if $caller eq "main";
597 2 100 66     32 if (exists($res->{Isa}->{$class})
598             && $res->{Isa}->{$class} =~ /\b$caller\b/) {
599 1         5 return 1;
600             }
601            
602 1 50 33     26 if (exists($res->{Owned}->{$toppack})
603             && $res->{Owned}->{$toppack} =~ /\b$caller\b/) {
604 1         2 $toppack = $caller;
605 1         7 ($topclass = lc($toppack)) =~ s/(.*::)?(\w+)/$2/;
606 1         15 unshift(@tops, $topclass);
607             }
608             }
609 2 50       38 shift(@tops) if $tops[0] =~ /main/o; # get rid of main
610             }
611 2 50       8 unshift(@tops, lc($res->{Res}->{'resources.appclass'}->[$Value]))
612             if $res->valbyname('resources.mergeclass');
613 2         5 $app = join('.', @tops);
614 2 50       5 $app .= '.' if $app;
615 2         14 ($top = lc($class)) =~ s/(.*::)?(\w+)/$2/;
616              
617             # Now recursive merge.
618 2         4 $res->{Merge} = 1;
619 2         3 unshift(@members, $class);
620 2         4 for $mem (@members) {
621 3         10 $res->_merge_pack($app, $top, $mem);
622             }
623 2         3 $res->{Merge} = 0;
624              
625 2         8 1;
626             }
627              
628             =head2 5.2. Looking up resources
629              
630             The values and documentation strings stored in a Resource object can be
631             accessed by specifying their names in three basic ways:
632              
633             =item directly ("byname" methods)
634              
635             As in "my.nice.cosy.couch" .
636              
637             =item by a pattern ("bypattern" methods)
638              
639             As in "m??nice.*" .
640              
641             =item hierarchically ("byclass" methods)
642              
643             If class Nice B Cosy, then asking for "couch" in package Cosy gets you
644             the value/doc of "my.couch". If, instead, Nice B Cosy member, that the
645             method gets you "my.nice.cosy.couch". This behaviour is essential for the
646             proper initialization of subclassed and member packages, as explained in
647             detail below.
648              
649             =back
650              
651             It is also possible to retrieve the whole content of a resource database
652             ("names" and "each" methods)
653              
654             Note that all the resource lookup methods return named (non "wildcarded")
655             resources only. Wildcarded resources (i.e. those specified in resource files,
656             and whose names contain one or more '*') are best thought as placeholders, to
657             be used when the value of an actual named resource is set.
658              
659             For example, a line in a resource file like
660              
661             *background : yellow
662              
663             fixes to yellow the color of all resources whose name ends with "background".
664             However, your actual packages will never worry about unless they really need
665             a background. In this case they either have a "background" resource in
666             their defaults hash, or subclass a package that has one.
667              
668             =over 8
669              
670             =item B
671              
672             Retrieves the value of a named resource from a Resource database. The $name
673             argument is a string containing a resource name with no wildcards.
674              
675             Returns the undefined value if no such resource is defined.
676              
677             =cut
678              
679             sub valbyname {
680 30     30 1 52 my $res = shift;
681 30         36 my ($name) = @_;
682 30         26 my $fullname;
683              
684 30         65 $fullname = $res->{Res}->{'resources.appclass'}->[$Value] . ".$name";
685              
686 30 50       92 if (exists($res->{Res}->{$fullname})) {
    50          
687 0         0 return $res->{Res}->{$fullname}->[$Value];
688             } elsif (exists($res->{Res}->{$name})) {
689 30         89 return $res->{Res}->{$name}->[$Value];
690             } else {
691 0         0 return undef;
692             }
693             }
694              
695             =item B
696              
697             Retrieves the documentation string of a named resource from a Resource
698             database. The $name argument is a string containing a resource name with no
699             wildcards.
700              
701             Returns the undefined value if no such resource is defined.
702              
703             =cut
704              
705             sub docbyname {
706 22     22 1 26 my $res = shift;
707 22         23 my ($name) = @_;
708 22         19 my $fullname;
709              
710 22         42 $fullname = $res->{Res}->{'resources.appclass'}->[$Value] . ".$name";
711              
712 22 50       65 if (exists($res->{Res}->{$fullname})) {
    50          
713 0         0 return $res->{Res}->{$fullname}->[$Doc];
714             } elsif (exists($res->{Res}->{$name})) {
715 22         47 $res->{Res}->{$name}->[$Doc];
716             } else {
717 0         0 return undef;
718             }
719             }
720              
721              
722             =item B
723              
724             Retrieves the full names, values and documentation strings of all the named
725             (non wildcarded) resources whose name matches the given $pattern. The pattern
726             itself is string containing a Perl regular expression, I enclosed in
727             slashes.
728              
729             Returns a new Resource object containing only the matching resources, or
730             the undefined value if no matches are found.
731              
732             =cut
733              
734             sub bypattern {
735 0     0 1 0 my $res = shift;
736 0         0 my ($pattern) = @_;
737 0         0 my ($name, $valdoc);
738 0   0     0 my $newres = new Resources() || return undef;
739              
740 0         0 while (($name, $valdoc) = $res->each()) {
741 0 0       0 $newres->put($name, @{$valdoc}) if $name =~ /$pattern/ ;
  0         0  
742             }
743              
744 0 0       0 return $newres if %{$newres->{Res}};
  0         0  
745 0         0 undef;
746             }
747              
748             =item B
749              
750             Retrieves the full names and values of all named (non wildcarded) resources
751             whose name matches the given pattern.
752              
753             Returns a new Resource object containing only names and values of the matching
754             resources (i.e. with undefined doc strings), or the undefined value if no
755             matches are found.
756              
757             =cut
758              
759             sub valbypattern {
760 0     0 1 0 my $res = shift;
761 0         0 my ($pattern) = @_;
762 0         0 my ($newres, $i);
763            
764 0   0     0 $newres = $res->bypattern($pattern) || return undef;
765 0         0 for $i ($newres->names()) {
766 0         0 undef($newres->{Res}->{$i}->[$Doc]);
767             }
768            
769 0         0 $newres;
770             }
771              
772             =item B
773              
774             Retrieves the full names and documentation strings of all named (non
775             wildcarded) resources whose name matches the given pattern.
776              
777             Returns a new Resource object containing only names and docs of the matching
778             resources (i.e. with undefined resource values), or the undefined value if no
779             matches are found.
780              
781             =cut
782              
783             sub docbypattern {
784 0     0 1 0 my $res = shift;
785 0         0 my ($pattern) = @_;
786 0         0 my ($newres, $i);
787            
788 0   0     0 $newres = $res->bypattern($pattern) || return undef;
789 0         0 for $i ($newres->names()) {
790 0         0 undef($newres->{Res}->{$i}->[$Value]);
791             }
792            
793 0         0 $newres;
794             }
795              
796              
797              
798             =item B
799              
800             To properly initialize the attributes of a package via resources we need a
801             way to know whether the package defaults (contained in its %Resources hash)
802             have been overridden by a derived or container class. For example, to set
803             a field like $dog->{Weight} in a Dog object, we must know if this $dog
804             is being subclassed by Poodle or Bulldog, or if it is a member of Family,
805             since all these other classes might override whatever "weight" default is
806             defined in the %Resources hash of Dog.pm.
807              
808             This information must of course be gathered at runtime: if you tried to name
809             explicitly a resource like "family.dog.weight" inside Dog.pm all the OOP
810             crowd would start booing at you. Your object would not be reusable anymore,
811             being explicitly tied to a particular container class. After all we do use
812             objects mainly because we want to easily reuse code...
813              
814             Enter the "by class" resource lookup methods: B, B and
815             B.
816              
817             Given an $object and a resource $suffix (i.e. a resource name stripped of all
818             container and derived class prefixes), the B method returns a 3
819             element list containing the name/value/doc of that resource in $object. The
820             returned name will be fully qualified with all derived/container classes, up
821             to the application class.
822              
823             For example, in a program called "bark", the statements
824              
825             $dog = new Dog ($res); # $res is a Resources database
826             ($name,$value,$doc) = $res->byclass($dog, "weight");
827              
828             will set $name, $value and $doc equal to those of the "bark.poodle.weight"
829             resource, if this Dog is subclassed by Poodle, and to those of
830             "bark.family.dog.weight", if it is a member of Family instead.
831              
832             The passed name suffix must not contain wildcards nor dots.
833              
834             Be careful not to confuse the "byclass" with the "byname" and "bypattern"
835             retrieval methods: they are used for two radically different goals. See the
836             EXAMPLES section for more.
837              
838             Returns the empty list if no resources are found for the given suffix,
839             or if the suffix is incorrect.
840              
841             =cut
842              
843             sub byclass {
844 4     4 1 12 my ($res, $object, $suffix) = @_;
845 4         5 my ($class, $name, $value, $doc);
846 0         0 my ($level, $topclass, $toppack, @ignore, @tops);
847              
848 4 50       9 ($class = ref($object)) || do {
849 0         0 $res->_error("byclass", "must pass an object reference");
850 0         0 return ();
851             };
852             # No patterns or leading/trailing dots
853 4 50       15 $suffix =~ /\.|\*/ && do {
854 0         0 $res->_error("byclass", "bad suffix $suffix");
855 0         0 return ();
856             };
857            
858             # Walk up the caller frames.
859             # If one of the callers is in the Isa list for $class, then $class
860             # defaults have been already merged, so we can bail out.
861             # Otherwise make up class name for $object, taking into account the Owned
862             # list.
863 4         4 $level=0;
864 4         18 ($name = lc($class)) =~ s/(.*::)?(\w+)/$2/;
865 4         9 unshift(@tops, $name);
866 4         30 while (($toppack, @ignore)=caller(++$level)) {
867 10 100       29 last if $toppack eq "main";
868              
869 6         27 ($topclass = lc($toppack)) =~ s/(.*::)?(\w+)/$2/;
870              
871 6 100 100     54 if (exists($res->{Isa}->{$class})
872             && $res->{Isa}->{$class} =~ /\b$toppack\b/) {
873 2         2 shift(@tops);
874 2         4 unshift(@tops, $topclass);
875 2         3 $class = $toppack;
876 2         18 next;
877             }
878              
879 4 100 100     70 if (exists($res->{Owned}->{$class})
880             && $res->{Owned}->{$class} =~ /\b$toppack\b/) {
881 2         4 unshift(@tops, $topclass);
882 2         18 $class = $toppack;
883             }
884             }
885              
886 4         10 unshift(@tops, lc($res->{Res}->{'resources.appclass'}->[$Value]));
887              
888 4         13 $name = join('.', @tops) . ".$suffix";
889              
890 4 50       11 return () unless exists($res->{Res}->{$name});
891              
892 4         4 ($value, $doc) = @{$res->{Res}->{$name}};
  4         11  
893              
894 4         20 return ($name, $value, $doc);
895             }
896              
897              
898             =item B
899              
900             As the B method above, but returns just the resource name (i.e. the
901             suffix with all the subclasses prepended).
902              
903             =cut
904              
905             sub namebyclass {
906 0     0 1 0 my ($res, $obj, $suffix) = @_;
907 0         0 my @nvd = $res->byclass($obj, $suffix);
908            
909 0         0 $nvd[0];
910             }
911              
912             =item B
913              
914             As the B method above, but returns just the resource value.
915              
916             =cut
917              
918             sub valbyclass {
919 2     2 1 11 my ($res, $obj, $suffix) = @_;
920 2         5 my @nvd = $res->byclass($obj, $suffix);
921            
922 2         18 $nvd[1];
923             }
924              
925              
926             =item B
927              
928             As the B method above, but returns just the resource documentation.
929              
930             =cut
931              
932             sub docbyclass {
933 0     0 1 0 my ($res, $suffix) = @_;
934 0         0 my @nvd = $res->byclass($suffix);
935            
936 0         0 $nvd[2];
937             }
938              
939              
940              
941             =item B
942              
943             Returns the next name/[value,doc] pair of the named (non wildcarded) resources
944             in a resource database, exactly as the B Perl routine.
945              
946             =cut
947              
948             sub each {
949 0     0 1 0 my $res=shift;
950 0         0 return each(%{$res->{Res}});
  0         0  
951             }
952              
953              
954             =item B
955              
956             Returns a list of the names of all named (non-wildcarded) resources in a
957             resource database, or undef if the databasee is empty.
958              
959             =cut
960              
961             sub names {
962 1     1 1 2 my $res=shift;
963 1         2 return keys(%{$res->{Res}});
  1         16  
964             }
965              
966             =head2 5.3. Assigning and removing Resources
967              
968             =item B
969              
970             Writes the value and doc of a resource in the database. It is possible to
971             specify an empty documentation string, but name and value must be defined.
972              
973             Wildcards ('*' characters) are allowed in the $name, but the $doc is ignored
974             in this case (documentation is intended for single resources, not for sets
975             of them).
976              
977             The value is written unchanged unless the resource database already
978             contains a wildcarded resource whose name includes $name (foo*bar
979             includes foo.bar, foo.baz.bar, etc.). In this case the value of the
980             wildcarded resource overrides the passed $value.
981              
982             Returns 1 if ok, 0 if error.
983              
984             =cut
985              
986             # Resource locking
987             # Some conditions may affect if and how a resource gets put inthe database.
988             # In order to implement the value priority policy (loaded resources have
989             # priority, derived and container class have priority over base and member
990             # classes) use is made to the Load and Merge fields in a Resources object,
991             # and of two additional fields in the resources value (indexed by the global
992             # variables $Loaded and $Merged).
993             #
994             sub put {
995 24     24 1 27 my $res=shift;
996 24         33 my ($name, $value, $doc) = @_;
997 24         25 my (@words);
998              
999 24 50 0     153 $res->_error("put", "name or value undefined") and return 0
      33        
1000             unless defined($name) && defined($value);
1001              
1002 24         44 $name = lc($name);
1003 24         44 @words = split(/\s+/, $name);
1004              
1005             # Name must be one word and may not terminate with wildcard or dot
1006             # or start with dot. Must check here too because of defaults.
1007 24 50 0     174 $res->_error("put", "bad resource name: $name") && return 0
      33        
1008             if scalar(@words) > 1 || $name=~/^\.|\.$|\*$/;
1009              
1010              
1011             # Do booleans.
1012 24         41 $value =~ s/^true$|^yes$/1/i;
1013 24         27 $value =~ s/^false$|^no$/0/i;
1014            
1015             # Do wildcards (they take priority over named)
1016             # Match of wildcards is done hyerarchically:
1017             # *b contains a*b
1018             # a*b contains a*c*b
1019             # In case of conlict, newer overwrite older ones.
1020 24 50       45 if ($name =~ /\*/) {
1021 0         0 my ($I_have, $r, $patname, $wild);
1022              
1023 0         0 $I_have=0;
1024              
1025             # Dots must be matched literally when name is used as a pattern
1026 0         0 ($patname = $name) =~ s/\./\\\./go;
1027              
1028             # a*b => a.*b (regexp cannot start with *)
1029 0         0 $patname =~ s/\*/\.\*/g;
1030              
1031             # First compare with known wildcarded resources.
1032 0         0 foreach $wild (keys(%{$res->{Wilds}})) {
  0         0  
1033             # Remove old wildcards if the new one contains them
1034 0 0       0 ($wild =~ /$patname\Z/) && delete($res->{Wilds}->{$wild});
1035              
1036             # Skip if a more general old one is found
1037 0 0       0 ($name =~ /$wild\Z/) && ($I_have = 1, last);
1038             }
1039 0 0       0 $res->{Wilds}->{$patname}=[$value, undef] unless $I_have;
1040              
1041             # Then update the old named ones
1042 0         0 foreach $r (keys(%{$res->{Res}})) {
  0         0  
1043 0 0       0 $res->{Res}->{$r}->[$Value] = $value if $r =~ /$patname\Z/;
1044             }
1045              
1046             } else {
1047             # Named resources.
1048             # Check if it is already wildcarded: if so, use wildcard's value
1049 24         24 my ($wild, $nref, $ex, $putall, $putdoc);
1050            
1051 24         23 foreach $wild (keys(%{$res->{Wilds}})) {
  24         63  
1052 24 50       119 if ($name =~ /$wild\Z/) {
1053 0         0 $value = $res->{Wilds}->{$wild}->[$Value];
1054 0         0 last;
1055             }
1056             }
1057              
1058             # Do merging-locking stuff and write
1059             # Had to use a Karnaugh map to find the right condition...
1060 24   100     78 $ex = exists($res->{Res}->{$name}) || 0;
1061 24 100       91 $nref = $ex ? $res->{Res}->{$name} : undef;
1062 24   100     221 $putall = $res->{Load} || !$ex ||
1063             !$nref->[$Loaded] && (!$res->{Merge} || !$nref->[$Merged]) || 0;
1064 24   50     149 $putdoc = !$putall && $ex && (!$nref->[$Doc] && $doc) || 0;
1065              
1066 24 100       53 if ($putall) {
    50          
1067 12         29 $res->{Res}->{$name}->[$Value] = $value;
1068 12 50       22 $res->{Res}->{$name}->[$Doc] = $doc if $doc;
1069 12         23 $res->{Res}->{$name}->[$Loaded] = $res->{Load};
1070 12         26 $res->{Res}->{$name}->[$Merged] = $res->{Merge};
1071             } elsif ($putdoc) {
1072 0         0 $res->{Res}->{$name}->[$Doc] = $doc;
1073             }
1074             }
1075              
1076 24         69 1;
1077             }
1078              
1079              
1080             =item B
1081              
1082             Removes the named (non wildcarded) resources from the database.
1083              
1084             Returns 1 if OK, 0 if the resource is not found in the database.
1085              
1086             =cut
1087              
1088             sub removebyname {
1089 0     0 1 0 my $res = shift;
1090 0         0 my ($name) = @_;
1091 0         0 my ($i, $cnt, $newres);
1092              
1093 0 0       0 return 0 unless exists $res->{Res}->{$name};
1094 0         0 delete($res->{Res}->{$name});
1095 0         0 1;
1096             }
1097              
1098             =item B
1099              
1100             Removes from the database all resources (both named I wildcarded) whose
1101             name mathes $pattern. An exactly matching name must be specified for
1102             wildcarded resources (foo*bar to remove foo*bar).
1103              
1104             Returns the number of removed resources.
1105              
1106             =cut
1107              
1108             sub removebypattern {
1109 0     0 1 0 my $res = shift;
1110 0         0 my ($name) = @_;
1111 0         0 my ($i, $cnt, $newres);
1112              
1113 0   0     0 $newres=$res->bypattern($name) || return 0;
1114              
1115 0         0 foreach $i ($newres->names()) {
1116 0         0 delete($res->{Res}->{$i});
1117 0         0 $cnt++;
1118             }
1119 0         0 foreach $i (keys(%{$res->{Wilds}})) {
  0         0  
1120 0 0       0 ($cnt++ , delete($res->{Wilds}->{$i})) if $i eq $name;
1121             }
1122              
1123 0         0 $cnt;
1124             }
1125              
1126              
1127             =head2 5.6. Viewing and editing resources.
1128              
1129             =item B
1130              
1131             Outputs the current content of a Resource object by piping to a pager program.
1132              
1133             The environment variable $ENV{RESPAGER}, the resource "resources.pager" and
1134             the environment variable $ENV{PAGER} are looked up, in this very order, to
1135             find the pager program. Defaults to B if none of them is found.
1136              
1137             The output format is the same of a resource file, with the resource names
1138             alphabetically ordered, and the resource documentation strings written
1139             as comments.
1140              
1141             Returns 1 if ok, 0 if error.
1142              
1143             =cut
1144              
1145             sub view {
1146 0     0 1 0 my $res=shift;
1147 0         0 my ($name, $value, $doc, $view, $pager, $p);
1148              
1149 0 0       0 if ($p = $ENV{RESPAGER}) {
    0          
    0          
1150 0         0 $pager = $p;
1151             } elsif ($p = $res->valbyname("resources.pager")) {
1152 0         0 $pager = $p;
1153             } elsif ($p = $ENV{PAGER}) {
1154 0         0 $pager = $p;
1155             } else {
1156 0         0 $pager='/bin/more';
1157             }
1158              
1159             # Make sure we don't output POD.
1160 0         0 my $pod = $res->valbyname("resources.writepod");
1161 0         0 $res->put("resources.writepod", 0);
1162              
1163 0         0 $p = $res->write("|$pager");
1164 0 0       0 $res->_error("view", "write failed") unless $p;
1165              
1166 0         0 $res->put("resources.writepod", $pod);
1167            
1168 0         0 return $p;
1169             }
1170              
1171              
1172             =item B
1173              
1174             Provides dynamical resource editing of a Resource object via an external
1175             editor program. Only resource names and values can be edited (anyway, what is
1176             the point of editing a resource comment on the fly?).
1177              
1178             The environment variables $ENV{RESEDITOR} and the resource "resouces.editor",
1179             are looked up, in this very order, to find the editor program. Defaults to
1180             B if none is found.
1181              
1182             The editor buffer is initialized in the same format of a resource file, with
1183             the resource names alphabetically ordered, and the resource documentation
1184             strings written as comments. The temporary file specified by the
1185             "resources.tmpfil" resource is used to initialize the editor, or
1186             '/tmp/resedit' if that resource is undefined.
1187              
1188             When the editor is exited (after saving the buffer) the method attempts to
1189             reload the edited resources. If an error is found the initial object is left
1190             unchanged, a warning with the first offending line in the file is printed, and
1191             the method returns with undef. Controlled resource loading is obtained by
1192             specifying a true value for the $nonew argument (see B).
1193              
1194             If the loading is successful, a new (edited) resource object is returned,
1195             which can be assigned to the old one for replacement.
1196              
1197             After a successful edit, the value of the resource "resources.updates" (which
1198             is always defined to 0 whenever a new resource is created) is increased by
1199             one. This is meant to notify program the and/or packages of the resource
1200             change, so they can proceed accordingly if they wish.
1201              
1202             =cut
1203              
1204             sub edit {
1205 1     1 1 13 my ($res, $nonew) = @_;
1206 1         2 my ($newres, $editor, $p, $status, $tmpfil);
1207              
1208 1 50       6 if ($p = $ENV{RESEDITOR}) {
    50          
1209 0         0 $editor = $p;
1210             } elsif ($p = $res->valbyname("resources.editor")) {
1211 1         9 $editor = $p;
1212             }
1213              
1214 1   33     3 $tmpfil = ($res->valbyname("resources.tmpfil") || "/tmp/resedit$$.txt");
1215              
1216             # Make sure we don't output POD.
1217 1         3 my $pod = $res->valbyname("resources.writepod");
1218 1         3 $res->put("resources.writepod", 0);
1219 1         6 $p = $res->write($tmpfil);
1220 1         4 $res->put("resources.writepod", $pod);
1221              
1222 1 50 0     4 $p || ($res->_error("edit", "write failed") && return $p);
1223              
1224 1         7530 $status = system("$editor $tmpfil");
1225 1 50       160 return 0 if $status>>8; # Editor failed
1226              
1227 0   0     0 $newres = new Resources("_RES_NODEFAULTS") || undef;
1228 0 0       0 $newres->load($tmpfil, $nonew) || undef($newres);
1229 0         0 unlink($tmpfil);
1230              
1231 0         0 for $p ($newres->names()) {
1232 0 0 0     0 if (exists($res->{Res}->{$p}) && defined($res->{Res}->{$p}->[$Doc])) {
1233 0         0 $newres->{Res}->{$p}->[$Doc] = $res->{Res}->{$p}->[$Doc];
1234             }
1235             }
1236 0         0 ++$newres->{Res}->{'resources.updates'}->[$Value];
1237 0         0 return $newres;
1238             }
1239              
1240             =head2 5.5. Miscellaneous methods
1241              
1242             =item B
1243              
1244             Outputs all resources of a resource database into a resource file (overwriting
1245             it).
1246              
1247             The resource documentation strings are normally written as comments, so the
1248             file itself is immediately available for resource loading. However, if the
1249             boolean resource "resources.writepod" is true, then the (non wildcarded)
1250             resources are output in POD format for your documentational pleasure.
1251              
1252             As usual in Perl, the filename can allo be of the form "|command", in which
1253             case the output is piped into "comma1nd".
1254              
1255             For resources whose value is a reference to an anon array or hash, it produces
1256             the appropriate constant Perl contructor by reverse parsing. The parser itself
1257             is available as a separate method named B<_parse> (see package source for
1258             documentation).
1259              
1260             Returns 1 if ok, 0 if error.
1261              
1262             =cut
1263            
1264             sub write {
1265 1     1 1 2 my $res = shift;
1266 1         2 my ($filnam) = @_;
1267 1         1 my ($name, $value, $doc, $view);
1268              
1269 1 50 0     4 $res->_error("write", "No filename") && return 0 unless defined $filnam;
1270 1 50       14 $filnam = ">$filnam" unless $filnam =~ /^\|/;
1271 1 50 0     189 ($res->_error("write", $!) && return 0) unless open(RESOUT, $filnam);
1272              
1273 1         16 autoflush RESOUT (1);
1274              
1275 1 50       62 if ($res->valbyname("resources.writepod")) {
1276              
1277 0         0 print RESOUT "=head2 Resources\n\n=over 8\n";
1278              
1279 0         0 for $name (sort($res->names())) {
1280 0 0       0 next if $name =~ /\._/; # hidden
1281              
1282 0         0 my $val = $res->valbyname($name);
1283 0         0 my @doclines=split(/ /, $res->docbyname($name));
1284 0         0 my $len=0;
1285 0         0 my $lin;
1286              
1287 0 0       0 $val = $res->_parse($val) if ref($val);
1288 0         0 print RESOUT "\n=item $name : $val\n\n";
1289            
1290 0         0 while (scalar(@doclines)) {
1291 0         0 $lin='';
1292 0   0     0 while (length($lin)<60 && scalar(@doclines)) {
1293 0         0 $lin .= shift(@doclines) . ' ';
1294             }
1295 0         0 chomp $lin;
1296 0         0 print RESOUT "$lin\n";
1297             }
1298             }
1299              
1300             } else {
1301 1         2 $view = "#\n# Wildcarded resources\n#\n";
1302            
1303 1         2 for $name (sort(keys(%{$res->{Wilds}}))) {
  1         5  
1304 1         2 ($value, $doc) = @{$res->{Wilds}->{$name}};
  1         3  
1305 1 50       5 $doc = '' unless $doc;
1306 1         5 $name =~ s/\\\./\./go;
1307 1         33 $name =~ s/\.\*/\*/go;
1308 1 50       5 $value = $res->_parse($value) if ref($value);
1309 1         7 $view .= "$name : $value\__RES_COMM__$doc\n";
1310             }
1311            
1312 1         2 $view .= "#\n# Named resources\n#\n";
1313            
1314 1         4 for $name (sort($res->names())) {
1315 22 50       53 next if $name =~ /\._/o; # "hidden" resource
1316 22         41 $value = $res->valbyname($name);
1317 22         44 $doc = $res->docbyname($name);
1318 22 50       41 $value = $res->_parse($value) if ref($value);
1319 22 100       74 $view .= "$name : $value\__RES_COMM__" . ($doc ? "$doc\n" : "\n");
1320             }
1321            
1322 1         8 $res->_printformat(\*RESOUT, $view);
1323 1         15 close(RESOUT);
1324             }
1325             }
1326              
1327            
1328             #
1329             # LOCAL (UNEXPORTED) METHODS
1330             #
1331             #
1332              
1333              
1334             # $res->_dump -- dumps the content of res on stderr. Used for debugging.
1335             #
1336             sub _dump {
1337 0     0   0 my $res=shift;
1338 0         0 my ($name, $value, $doc, $valdoc);
1339 0         0 warn "_dump: WILDCARDED RESOURCES\n";
1340 0         0 for $name (sort(keys(%{$res->{Wilds}}))) {
  0         0  
1341 0         0 $value= $res->{Wilds}->{$name}->[$Value];
1342 0         0 $name =~ s/\.\*/\*/g;
1343 0         0 $name =~ s/\\//g;
1344 0         0 warn "_dump: $name : $value\n";
1345             }
1346              
1347 0         0 warn "_dump: NAMED RESOURCES\n";
1348 0         0 for $name (sort(keys(%{$res->{Res}}))) {
  0         0  
1349 0         0 $valdoc= $res->{Res}->{$name};
1350 0         0 $name =~ s/\\//g;
1351 0         0 $value= $valdoc->[$Value];
1352 0         0 $doc=$valdoc->[$Doc];
1353 0   0     0 warn "_dump: $name : $value #" . ($doc || '') . "\n";
1354             }
1355             }
1356              
1357             # _parse($value) -- Returns a string containing the value of a resource $name,
1358             # written in the same format as for a resource file (i.e. in
1359             # Perl syntax if the value is not a scalar.
1360             # Returns the string, or undef in case of errors.
1361             #
1362             sub _parse {
1363 0     0   0 my $res=shift;
1364 0         0 my ($value) = @_;
1365 0         0 my ($ref);
1366              
1367 0 0       0 return $value unless $ref = ref($value);
1368 0         0 return _parse_ref($value, $ref);
1369             }
1370              
1371             #
1372             # _parse_ref -- This does recursive parsing for hass/array references .
1373             #
1374             sub _parse_ref {
1375 0     0   0 my ($value, $ref) =@_;
1376 0         0 my $parsed='';
1377            
1378 0 0       0 $ref eq 'ARRAY' && do {
1379 0         0 my $element;
1380 0         0 $parsed = '[';
1381 0         0 for $element (@{$value}) {
  0         0  
1382 0         0 my $refref;
1383 0 0 0     0 if ($refref = ref($element)) {
    0          
1384 0   0     0 my $parspars = _parse_ref($element, $refref)
1385             || return undef;
1386 0         0 $parsed .= $parspars;
1387             } elsif (_isint($element) || _isreal($element)) {
1388 0         0 $parsed .= "$element, ";
1389             } else {
1390 0         0 $parsed .= "'$element', ";
1391             }
1392             }
1393 0         0 $parsed =~ s/,\s$//;
1394 0         0 $parsed .= ']';
1395 0         0 return $parsed;
1396             };
1397              
1398 0 0       0 $ref eq 'HASH' && do {
1399 0         0 my ($nam, $val);
1400 0         0 $parsed = '{';
1401 0         0 while (($nam, $val) = each(%{$value})) {
  0         0  
1402 0         0 my $refref;
1403 0 0       0 return undef if (ref($nam));
1404 0 0 0     0 if ($refref = ref($val)) {
    0          
1405 0   0     0 my $parspars = _parse_ref($val, $refref)
1406             || return undef;
1407 0         0 $parsed .= "'$nam' => $parspars, ";
1408             } elsif (_isint($val) || _isreal($val)) {
1409 0         0 $parsed .= "'$nam' => $val, ";
1410             } else {
1411 0         0 $parsed .= "'$nam' => '$val', ";
1412             }
1413             }
1414 0         0 $parsed =~ s/,\s$//;
1415 0         0 $parsed .= '}';
1416 0         0 return $parsed;
1417             };
1418              
1419 0         0 return undef; # We do only arrays and hashes
1420              
1421             sub _isint {
1422 0     0   0 my ($num)=@_;
1423 0         0 $num =~ /\A-?\d+/o;
1424             }
1425             sub _isreal {
1426 0     0   0 my ($num)=@_;
1427 0         0 $num =~ /((-?\d*\.\d+)|(-?\d*\.\d+[eE]-?\d+))/o;
1428             }
1429             }
1430              
1431              
1432             # _merge_pack($app, $class)
1433             #
1434             # Recursively merges the %Resources of object $obj of package $pack into a
1435             # $res object in application $app. The merging is done topdown, from
1436             # derived and container classes to base and member ones.
1437             #
1438             # The algorithm is as follows:
1439             # 1) Resource names are syntax-checked, then merging is performed for those
1440             # not yet defined
1441             # 2) All base classes of $pack are _merge_packed in turn.
1442             #
1443             # Returns 1 for success, 0 otherwise.
1444             #
1445             sub _merge_pack {
1446 6     6   14 my ($res, $app, $top, $pack, $packclass) = @_;
1447 6         7 my ($defname, $def);
1448              
1449 6 100       23 $packclass || ($packclass = lc($pack)) =~ s/(.*::)?(\w+)/$2/;
1450              
1451 6         6 do {
1452 1     1   10 no strict; # To use symbolic references
  1         2  
  1         613  
1453 6         13 $_ = $res->{Res}->{"resources.resources"}->[$Value];
1454 6 50       19 unless (/^%/) {
1455 0         0 $res->_error("merge", "bad name for %Resources hash: $_");
1456 0         0 return 0;
1457             }
1458 6         15 s/^%//;
1459 6         10 $defname = "$pack\::$_";
1460 6         5 $def = \%{$defname};
  6         52  
1461             };
1462              
1463 6 50       7 if (defined(%{$def})) {
  6         14  
1464 6         6 my ($dname, $dvalue, $val, $vref);
1465 6         8 defloop: while (($dname, $dvalue) = each(%{$def})) {
  28         88  
1466             # Check for bad args:
1467             # Names cannot contain * or :, nor start/end with a dot
1468 22 50       86 $dname =~ /\*|^\.|\.$|\:/ && do {
1469 0         0 $res->error("merge", "Bad default resource name: $dname ");
1470 0         0 return 0;
1471             };
1472             # Values must be 2-elements arrays, with a scalar 2nd
1473             # component (the doc)
1474 22 50 33     161 unless(($vref = ref($dvalue)) && ($vref =~ /ARRAY/o) &&
  22   33     120  
      33        
1475             scalar(@{$dvalue})<=2 && !ref($dvalue->[1]) ) {
1476 0         0 $res->_error("merge", "Bad default resource value for ".
1477             "resource $dname in hash $defname");
1478 0         0 return 0;
1479             };
1480            
1481             # Build class name for resource by inheritance
1482 22 50       47 if ($top eq "main") {
    100          
1483 0         0 $dname = $app . $dname;
1484             } elsif ($top eq $packclass) {
1485 15         31 $dname = "$app$top\.$dname";
1486             } else {
1487 7         46 $dname = "$app$top\.$packclass\.$dname";
1488             }
1489              
1490 22 50 0     23 $res->put($dname, @{$dvalue}) ||
  22         49  
1491             ($res->_error("merge", "error on $dname: $dvalue") && return 0);
1492             }
1493             }
1494              
1495             # Now let's recur on base classes of $obj
1496             #
1497 6         7 my ($isaname, $isa, $base);
1498 0         0 my (@hasa, $mem);
1499              
1500             # Base classes
1501 6         6 do {
1502 1     1   7 no strict;
  1         3  
  1         948  
1503 6         8 $isaname = "$pack\::ISA";
1504 6         17 $isa = \@$isaname;
1505             };
1506 6 100       6 if (defined(@{$isa})) {
  6         14  
1507 3         4 for $base (@{$isa}) {
  3         6  
1508 3 50       9 return 0 unless $res->_merge_pack($app, $top, $base, $packclass);
1509             }
1510             }
1511              
1512             # All done.
1513 6         19 return 1;
1514             }
1515              
1516              
1517             #
1518             # _error ($subname) - wrapper around caller(), used for debugging
1519             #
1520             sub _error {
1521 0     0   0 my $res=shift;
1522 0         0 my ($place, $msg) = @_;
1523              
1524 0 0       0 $res->valbyname("resources.verbosity") &&
1525             warn("error: $0: Resources: $place, $msg\n");
1526            
1527 0         0 1;
1528             }
1529              
1530              
1531             #
1532             # _printformat($fh, $msg)
1533             # prints to filehandle $fh the documentation $doc.
1534             # formatted in resources.viewcolumn columns, not breking expression and
1535             # continuing comments.
1536             #
1537              
1538             sub _printformat {
1539 1     1   2 my $res=shift;
1540 1         3 my ($fh, $msg) = @_;
1541 1         2 my ($line, $cols, $def, $comm, @comms, $below);
1542 0         0 my ($deflen, $commlen, $mincols, $whites);
1543              
1544 1         3 $cols = $res->valbyname("resources.viewcols");
1545 1         3 $mincols = $res->valbyname("resources.viewmincols");
1546 1 50       4 $cols = 78 unless $cols;
1547              
1548 1         13 for $line (split(/\n/, $msg)) {
1549             # print right away if it's short
1550 29 100       54 if (length($line) <= $cols) {
1551 24         52 $line =~ s/__RES_COMM__$//o;
1552 24         48 $line =~ s/__RES_COMM__/ \# /;
1553 24         277 print $fh "$line\n";
1554 24         35 next;
1555             }
1556            
1557 5         17 ($def, $comm) = split(/__RES_COMM__/, $line);
1558 5         9 $deflen = length($def)+1;
1559             # down one line if def is too long
1560 5 50       12 if (($commlen = $cols-($deflen % $cols)) < $mincols) {
1561 0         0 $below=1;
1562 0         0 $commlen=$cols/2;
1563             } else {
1564 5         7 $below=0;
1565             }
1566              
1567 5         20 @comms = split(/\s+/, $comm);
1568 5 50       12 shift(@comms) unless $comms[0];
1569              
1570 5 50       8 unless ($below) {
1571 5         14 print $fh ("$def # ", _commwds($commlen, \@comms), "\n");
1572 5         9 $whites = $deflen % $cols;
1573 5         12 while ($comm=_commwds($commlen, \@comms)) {
1574 2         7 $comm = (' ' x $whites) . "# $comm";
1575 2         23 print $fh "$comm\n";
1576             }
1577             } else {
1578 0         0 print $fh "$def\n";
1579 0         0 $whites = $cols/2 - 1;
1580 0         0 while ($comm=_commwds($commlen, \@comms)) {
1581 0         0 $comm = (' ' x $whites) . "# $comm";
1582 0         0 print $fh "$comm\n";
1583             }
1584             }
1585             }
1586              
1587             sub _commwds {
1588 12     12   16 my ($len, $comp) = @_;
1589 12         11 my ($shft, $wd, $ls, $lw);
1590            
1591 12         13 $ls=1;
1592 12         16 $shft = $wd = '';
1593 12         10 while (1) {
1594 52         43 $wd=shift(@{$comp});
  52         71  
1595 52 100       82 last unless $wd;
1596 42         40 $lw=length($wd)+1;
1597 42 100       76 last if $lw + $ls > $len;
1598 40         46 $shft .= "$wd ";
1599 40         60 $ls += $lw;
1600             }
1601 12 100       28 unshift(@{$comp}, $wd) if $wd;
  2         4  
1602 12         83 return $shft;
1603             }
1604             }
1605              
1606            
1607             1;
1608              
1609             __END__