| 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__ |