File | /wise/base/deliv/dev/lib/perl/WISE/Params.pm | Statements Executed | 38746 | Total Time | 0.106723 seconds |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine | |
---|---|---|---|---|---|---|
1 | 1 | 1 | 0.01648 | 0.04028 | WISE::Params:: | parsedefs |
1 | 1 | 1 | 0.01028 | 0.01337 | WISE::Params:: | paramsfromfile |
71 | 4 | 1 | 0.00981 | 0.03212 | WISE::Params:: | Param_parse |
1 | 1 | 1 | 0.00575 | 0.00593 | WISE::Params:: | expand_tags |
345 | 3 | 1 | 0.00326 | 0.00436 | WISE::Params:: | paruqindex |
1 | 1 | 1 | 0.00247 | 0.00388 | WISE::Params:: | check_pragmas |
408 | 4 | 1 | 0.00234 | 0.00234 | WISE::Params:: | parunquote |
404 | 4 | 1 | 0.00216 | 0.00216 | WISE::Params:: | parblankquoted |
86 | 1 | 1 | 0.00214 | 0.00464 | WISE::Params:: | valstr |
76 | 1 | 1 | 0.00181 | 0.00639 | WISE::Params:: | paramchecklist |
168 | 3 | 1 | 0.00171 | 0.00250 | WISE::Params:: | parquote |
86 | 1 | 1 | 0.00164 | 0.00164 | WISE::Params:: | typeoftype |
86 | 1 | 1 | 0.00147 | 0.00147 | WISE::Params:: | paramcheck |
76 | 1 | 1 | 0.00125 | 0.00125 | WISE::Params:: | check_map_keys |
33 | 1 | 1 | 0.00119 | 0.00141 | WISE::Params:: | Param_reassign |
24 | 2 | 1 | 0.00113 | 0.00188 | WISE::Params:: | paramlist |
138 | 3 | 1 | 0.00093 | 0.00093 | WISE::Params:: | assign_ref |
29 | 1 | 1 | 0.00086 | 0.00086 | WISE::Params:: | parse_passon_param |
1 | 1 | 1 | 0.00086 | 0.00619 | WISE::Params:: | Param_lines |
3 | 1 | 1 | 0.00084 | 0.00239 | WISE::Params:: | paramlist2hash |
2 | 1 | 1 | 0.00075 | 0.00118 | WISE::Params:: | paramparselines |
1 | 1 | 1 | 0.00069 | 0.00069 | WISE::Params:: | parwrapup |
1 | 1 | 1 | 0.00060 | 0.00071 | WISE::Params:: | steps_resolve |
76 | 1 | 1 | 0.00052 | 0.00052 | WISE::Params:: | check_list_count |
5 | 1 | 1 | 0.00048 | 0.00048 | WISE::Params:: | parexpandlist |
1 | 1 | 1 | 0.00032 | 0.06583 | WISE::Params:: | Params |
1 | 1 | 1 | 0.00020 | 0.00020 | WISE::Params:: | check_required |
2 | 1 | 1 | 5.1e-5 | 5.1e-5 | WISE::Params:: | Param_get |
1 | 1 | 1 | 4.3e-5 | 0.00623 | WISE::Params:: | Param_print |
1 | 1 | 1 | 2.1e-5 | 0.00010 | WISE::Params:: | parwhoiam |
1 | 1 | 1 | 1.1e-5 | 1.1e-5 | WISE::Params:: | origval |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | BEGIN |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | Csh_set_params |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | Emit_nmpar_defs |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | Param_addval |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | Param_default |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | Param_help |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | Param_specified |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | cshescape |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | loadparamsfromfile |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | par_safe_eval |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | paramsfrommodel |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | paramsfromtty |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | parcollapselist |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | parqpat |
0 | 0 | 0 | 0 | 0 | WISE::Params:: | pass_filters |
Line | Stmts. | Exclusive Time | Avg. | Code |
---|---|---|---|---|
1 | #! /usr/bin/env perl | |||
2 | ||||
3 | 3 | 3.8e-5 | 1.3e-5 | use strict; # spent 20µs making 1 call to strict::import |
4 | 3 | 3.7e-5 | 1.2e-5 | use warnings; # spent 19µs making 1 call to warnings::import |
5 | ||||
6 | 1 | 1.0e-6 | 1.0e-6 | my ($dataroot, $dynaroot, $localroot,$ramroot, |
7 | $unilocalroot, $hostname); | |||
8 | ||||
9 | #use Sys::Hostname qw/hostname/; | |||
10 | ||||
11 | 3 | 0.00042 | 0.00014 | use Cwd (); |
12 | ||||
13 | BEGIN { | |||
14 | 7 | 0.00865 | 0.00124 | chomp(($hostname) = `/bin/hostname -s 2>/dev/null`); |
15 | $hostname ||= 'UNKNOWN'; | |||
16 | $dataroot = $ENV{WISE_DATAROOT}; | |||
17 | $localroot = $ENV{WISE_LOCAL} || "/local"; | |||
18 | $unilocalroot= $ENV{WISE_UNILOCAL} || "/compute/$hostname"; | |||
19 | my $cwd = Cwd::fastcwd(); # spent 52µs making 1 call to Cwd::fastcwd | |||
20 | 1 | 2.8e-5 | 2.8e-5 | if($cwd) { |
21 | if($cwd =~ s!/+(scans|mops|coadds|ingest|ql)(/+.*|$)!!) { | |||
22 | $cwd =~ s!^/+wise-ops/+\d\d/+wise/+!/wise/!; | |||
23 | $dynaroot = $cwd if -d "$cwd/ref/params/wrap"; | |||
24 | } | |||
25 | } | |||
26 | 1 | 0.00020 | 0.00020 | } |
27 | ||||
28 | ||||
29 | package WISE::Params; | |||
30 | ||||
31 | # Functional interface to parameter handling. | |||
32 | # See bottom for WISE::Pars package/class and its methods. | |||
33 | ||||
34 | #use threads::shared qw(share); | |||
35 | ||||
36 | use WISE::Env (cfglib=>'/wise/base/deliv/dev/lib/perl', # spent 796µs making 1 call to WISE::Env::import, max recursion depth 1 | |||
37 | 3 | 5.2e-5 | 1.7e-5 | import=>[qw/$cfgdir $cfglib $basedir/]); |
38 | ||||
39 | 3 | 0.00087 | 0.00029 | use WISE::Spawn; # spent 26µs making 1 call to Exporter::Lite::import |
40 | 3 | 0.00074 | 0.00025 | use WISE::UtilsLight; # spent 28µs making 1 call to Exporter::Lite::import |
41 | ||||
42 | 1 | 2.0e-6 | 2.0e-6 | my $version = '$Id: Params.pm 7640 2010-03-21 21:07:10Z tim $ '; |
43 | ||||
44 | 3 | 3.1e-5 | 1.0e-5 | use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION *DATA); # spent 74µs making 1 call to vars::import |
45 | ||||
46 | 3 | 5.2e-5 | 1.7e-5 | use Exporter::Lite; # spent 37µs making 1 call to Exporter::Lite::import |
47 | ||||
48 | 1 | 1.0e-6 | 1.0e-6 | $VERSION = 1.99; |
49 | 1 | 4.0e-6 | 4.0e-6 | @ISA = (); |
50 | ||||
51 | 1 | 1.0e-6 | 1.0e-6 | @EXPORT_OK = qw(); |
52 | ||||
53 | 3 | 2.8e-5 | 9.3e-6 | use vars qw(@PARAMFILES @PARAMS_FROM_FILES); # spent 35µs making 1 call to vars::import |
54 | ||||
55 | #use Carp; | |||
56 | 3 | 3.6e-5 | 1.2e-5 | use File::Basename; # spent 79µs making 1 call to Exporter::import |
57 | 3 | 0.00040 | 0.00013 | use Scalar::Util 'reftype'; # spent 62µs making 1 call to Exporter::import |
58 | ||||
59 | 1 | 1.0e-6 | 1.0e-6 | my $FPre = q/^\s*[-+]?(?:(?:(?:\d*\.?\d+|\d+\.)(?:[Ee][+-]?\d+)?)?|(?:[Nn]an|[Ii]nf))?\s*$/; |
60 | ||||
61 | 1 | 1.1e-5 | 1.1e-5 | my %bools = (t=>1,f=>0,true=>1,false=>0,on=>1,off=>0,1=>1,0=>0, |
62 | "y"=>1,n=>0,yes=>1,"no"=>0); | |||
63 | ||||
64 | 1 | 1.0e-6 | 1.0e-6 | my $err = '*** PARAMS'; |
65 | 1 | 0 | 0 | my $warn= '=== PARAMS'; |
66 | 1 | 1.0e-6 | 1.0e-6 | my $options_watch; |
67 | 1 | 0 | 0 | my $perlok = 0; # Disallow Perl data structures in list parameters |
68 | ||||
69 | 1 | 2.0e-6 | 2.0e-6 | my %legal_file_pragmas = (in => 1, glob => 1, resolve => 1); |
70 | ||||
71 | my @defparamfiles = ( | |||
72 | ($dataroot||$dynaroot||'/wise/fops'). | |||
73 | "/ref/params/wrap/All.params", # Global | |||
74 | 1 | 8.0e-6 | 8.0e-6 | (map {"$_/params/All.params"} (ref($cfgdir) # Config |
75 | ? @$cfgdir | |||
76 | : $cfgdir//'NONE')), | |||
77 | "./All.params" # Local | |||
78 | ); | |||
79 | ||||
80 | 1 | 4.0e-6 | 4.0e-6 | our %tag_predefs = ( |
81 | #'@cfgdir' => $cfgdir, # Not ready for this | |||
82 | '@dynaroot' => $dynaroot, | |||
83 | '@dataroot' => $dataroot||'/wise/fops', | |||
84 | '@local' => $localroot, | |||
85 | '@unilocal' => $unilocalroot, | |||
86 | '@hostname' => $hostname, | |||
87 | ); | |||
88 | ||||
89 | 1 | 1.0e-6 | 1.0e-6 | our %params_from_model_opts = (); |
90 | ||||
91 | BEGIN { | |||
92 | 1 | 1.0e-6 | 1.0e-6 | $^W = 0 if $ENV{_PARAM_DEBUG_}; |
93 | 1 | 0.01285 | 0.01285 | } |
94 | ||||
95 | ||||
96 | # spent 65.8ms (323µs+65.5) within WISE::Params::Params which was called
# once (323µs+65.5ms) by WISE::Pars::new at line 39 of /wise/base/deliv/dev/lib/perl/WISE/Pars.pm | |||
97 | 53 | 0.00021 | 4.0e-6 | my $paramdefs = shift || "-"; |
98 | my $opts = shift || {}; | |||
99 | my ($argv); | |||
100 | local $_; | |||
101 | ||||
102 | if(($_=reftype($opts))!~/hash/i) { # spent 22µs making 1 call to Scalar::Util::reftype | |||
103 | die "$err: Don't know what to do with options of type $_ .\n"; | |||
104 | } | |||
105 | ||||
106 | my $ctxt = defined $opts->{defs} ? $opts->{defs} : {}; | |||
107 | ||||
108 | my $values = defined $opts->{vals} ? $opts->{vals} : {}; | |||
109 | my $refs = $opts->{refs} || {}; | |||
110 | my $strict = $opts->{strict}; # warn | |||
111 | ||||
112 | if(($_=reftype($ctxt))!~/hash/i) { # spent 5µs making 1 call to Scalar::Util::reftype | |||
113 | die "$err: Don't know what to do with defs of type '$_'.\n"; | |||
114 | } | |||
115 | my $conditions = $opts->{conditions} || {}; | |||
116 | ||||
117 | $opts->{debug} ||= $ENV{_PARAM_DEBUG_} || 0; | |||
118 | ||||
119 | my $debug = $opts->{debug}; | |||
120 | ||||
121 | # Variable references associated to command line param.s | |||
122 | ||||
123 | if(($_=reftype($refs))!~/hash/i) { # spent 5µs making 1 call to Scalar::Util::reftype | |||
124 | die "$err: Don't know what to do with refs of type '$_'.\n"; | |||
125 | } | |||
126 | if(($_=reftype($values))!~/hash/i) { # spent 5µs making 1 call to Scalar::Util::reftype | |||
127 | die "$err: Don't know what to do with vals of type '$_'.\n"; | |||
128 | } | |||
129 | if(($_=reftype($ctxt))!~/hash/i) { # spent 4µs making 1 call to Scalar::Util::reftype | |||
130 | die "$err: Don't know what to do with ctxt of type '$_'.\n"; | |||
131 | } | |||
132 | ||||
133 | # Create a local options param so it's passed on to other subs | |||
134 | my $myopts = {%$opts, defs=>$ctxt}; | |||
135 | ||||
136 | 2 | 1.0e-6 | 5.0e-7 | if(! $myopts->{noinit}) { |
137 | # Initialize data structures | |||
138 | %$values = (); | |||
139 | %$ctxt = (); | |||
140 | } | |||
141 | ||||
142 | $ctxt->{_meta} = {}; | |||
143 | $ctxt->{_meta}{vals} = $values; | |||
144 | ||||
145 | $ctxt->{_meta}{debug} = $debug; | |||
146 | $^W = 0 if $debug; # Otherwise we get a bunch of uninit warnings | |||
147 | ||||
148 | # Get our idea of our name. | |||
149 | my $iam = $ctxt->{_meta}{iam} = $opts->{iam} || parwhoiam(); | |||
150 | $ctxt->{_meta}{iamorig} = $iam; # In case we're renamed | |||
151 | $err = "*** $iam/PARAMS"; | |||
152 | ||||
153 | my %refs; | |||
154 | %refs = %$refs; | |||
155 | $ctxt->{_meta}{refs} = \%refs; | |||
156 | ||||
157 | my $tag_aliases = {}; | |||
158 | %$tag_aliases = %{ $opts->{tag_aliases} || {} }; | |||
159 | $ctxt->{_meta}{tag_aliases} = $tag_aliases; | |||
160 | ||||
161 | # Parse parameter definitions | |||
162 | &parsedefs($paramdefs,$myopts); # ... modifies $ctxt # spent 40.3ms making 1 call to WISE::Params::parsedefs | |||
163 | ||||
164 | # Replace help, if specified | |||
165 | my $help = $opts->{help}; | |||
166 | 2 | 6.0e-6 | 3.0e-6 | if($help) { |
167 | die "$err: Help option not a hash ref.\n" | |||
168 | if ref($help) !~ /hash/i;; | |||
169 | for my $param (keys %$help) { | |||
170 | my $ref = $help->{$param}; | |||
171 | $ref = ! ref($ref) ? [$ref] : $ref; | |||
172 | die "$err: Help for parameter '$param' not an ". | |||
173 | "array ref or scalar.\n" | |||
174 | if ref($ref) !~ /array/i; | |||
175 | my $key = $param; | |||
176 | $key = '_meta' if $param =~ /^(general|top)$/i; | |||
177 | my $help = []; | |||
178 | @$help = map { (split /\n/) } @$ref; | |||
179 | $ctxt->{lc $key}{help} = $help; | |||
180 | } | |||
181 | } | |||
182 | ||||
183 | if($myopts->{parseonly}) { | |||
184 | return $ctxt; | |||
185 | } | |||
186 | ||||
187 | # Get argument list and save in defs structure | |||
188 | my @save; | |||
189 | 2 | 7.0e-6 | 3.5e-6 | if($myopts->{argv}) { |
190 | $argv = []; | |||
191 | @save = @$argv = @{ $myopts->{argv} }; | |||
192 | } else { | |||
193 | @save = @ARGV; | |||
194 | $argv = \@ARGV; | |||
195 | } | |||
196 | $ctxt->{_meta}{argv} = $argv; # Named params will be eaten up ... | |||
197 | $ctxt->{_meta}{argv_orig} = \@save; # ... so keep the originals here | |||
198 | ||||
199 | 2 | 6.0e-6 | 3.0e-6 | if(! @PARAMFILES && ! $ENV{NO_DEFAULT_PAR_FILES} && |
200 | ! $opts->{nodefparfiles}) { | |||
201 | # Set up default parameter files. | |||
202 | @PARAMFILES = (defined $ENV{PAR_FILES} | |||
203 | ? split(/[:\s]+/,$ENV{PAR_FILES}) | |||
204 | : (@defparamfiles | |||
205 | ? @defparamfiles | |||
206 | : () )); | |||
207 | push @PARAMFILES, (defined $ENV{PAR_ADDFILES} | |||
208 | ? split(/[:\s]+/,$ENV{PAR_ADDFILES}) | |||
209 | : () ); | |||
210 | } | |||
211 | ||||
212 | @PARAMFILES = grep($_, @PARAMFILES); # Remove null names | |||
213 | { | |||
214 | 2 | 7.0e-6 | 3.5e-6 | my %there; |
215 | @PARAMFILES = grep(! $there{$_}++,@PARAMFILES); # Rem. dups | |||
216 | } | |||
217 | ||||
218 | # Copy relevant parts of the environment to the "conditions" hash | |||
219 | $ctxt->{_meta}{conditions} = {}; | |||
220 | for (qw(SODA_CONFIG PATH LD_LIBRARY_PATH PERL5LIB WISE HOSTNAME HOST | |||
221 | PWD USER UNIX DOMAIN OSTYPE LOGNAME GROUP USERNAME HOSTTYPE)) { | |||
222 | 64 | 8.4e-5 | 1.3e-6 | my $envval = $ENV{$_}; |
223 | my $val; | |||
224 | # Split certain ':' or ',' separated lines | |||
225 | 6 | 7.9e-5 | 1.3e-5 | if($envval && /^(PATH|SODA_CONFIG|LD_LIBRARY_PATH|PERL5LIB)$/) { |
226 | $val = []; | |||
227 | @$val = split /[:,]+/, $envval; | |||
228 | } | |||
229 | $ctxt->{_meta}{conditions}{$_} = $val; | |||
230 | } | |||
231 | # Add explicit user conditions (possibly overriding those above. | |||
232 | for (keys %$conditions) { | |||
233 | my $cond = $conditions->{$_}; | |||
234 | my $val; | |||
235 | if(ref $cond) { | |||
236 | $val = []; | |||
237 | @$val = @$cond; | |||
238 | } else { | |||
239 | $val = $cond; | |||
240 | } | |||
241 | $ctxt->{_meta}{conditions}{$_} = $val; | |||
242 | } | |||
243 | ||||
244 | # Parse command line and assign param values and fill data structures | |||
245 | my @leftover = Param_parse($argv,{%$myopts,from=>1}); # spent 15.2ms making 1 call to WISE::Params::Param_parse | |||
246 | ||||
247 | warn "$warn: Leftover arguments: @leftover.\n" | |||
248 | if $strict && @leftover; | |||
249 | ||||
250 | # Add to predefined tags | |||
251 | $tag_predefs{'@iam'} = $ctxt->{_meta}{iam}; | |||
252 | ||||
253 | # Do tag expansion iteratively | |||
254 | if(! $opts->{notags} || ref $opts->{notags}) { # spent 5.93ms making 1 call to WISE::Params::expand_tags | |||
255 | expand_tags($ctxt,$opts); | |||
256 | } | |||
257 | ||||
258 | # Check to see if all required values are assigned | |||
259 | if(! $myopts->{norequired}) { # spent 198µs making 1 call to WISE::Params::check_required | |||
260 | check_required($ctxt,$opts); | |||
261 | } | |||
262 | ||||
263 | # Optionally check against file pragmas | |||
264 | if(! $opts->{nopragmas}) { # spent 3.88ms making 1 call to WISE::Params::check_pragmas | |||
265 | check_pragmas($ctxt,$opts); | |||
266 | } | |||
267 | ||||
268 | # If asked, print the parameters | |||
269 | if($myopts->{print}) { Param_print({defs=>$ctxt,%{$myopts->{printopts}}}); } | |||
270 | ||||
271 | return $ctxt; | |||
272 | } | |||
273 | ||||
274 | # Must be called only once per instantiation | |||
275 | # spent 40.3ms (16.5+23.8) within WISE::Params::parsedefs which was called
# once (16.5ms+23.8ms) by WISE::Params::Params at line 162 | |||
276 | 26 | 0.00028 | 1.1e-5 | my($paramdefs,$opts) = (shift,shift); |
277 | my $com = '#'; | |||
278 | my($cmnt,$ref,$orig,$ctxt,$values,$gotcmd,$unnamed); | |||
279 | my($aliases,$list,$hash,$otype,$type,$itype,$size,$mapkeys,$attribs); | |||
280 | my($intdefault,$default,$required,$nodefault,$postdefaults,%there); | |||
281 | my($i,$lastdef,$help,$refs,$iam,$tmp,$debug,$minlist,$maxlist,$rawtype); | |||
282 | my($minval,$maxval,$file_pragmas,$private,$quiet,$preinits); | |||
283 | my(@allhelp,@unnamed,%allaliases,@allparnames,@def); | |||
284 | local $_; | |||
285 | ||||
286 | $ctxt = $opts->{defs} || die "$err/parsedefs: No defs.\n"; | |||
287 | $nodefault = $opts->{nodefault}; | |||
288 | $postdefaults = $opts->{defaults} || {}; # Out of band defaults | |||
289 | $preinits = $opts->{inits} || {}; # Out of band values | |||
290 | $debug = $opts->{debug} || $ctxt->{_meta}{debug} || 0; | |||
291 | ||||
292 | $iam = $ctxt->{_meta}{iam}; | |||
293 | ||||
294 | $ctxt->{_meta}{parnames} = \@allparnames; | |||
295 | $ctxt->{_meta}{aliases} = \%allaliases; | |||
296 | $ctxt->{_meta}{help} = \@allhelp; | |||
297 | $ctxt->{_meta}{unnamed} = \@unnamed; | |||
298 | ||||
299 | $values = $ctxt->{_meta}{vals}; | |||
300 | $refs = $ctxt->{_meta}{refs}; | |||
301 | ||||
302 | # If $paramdefs is a array ref., read the contents pairwise into key=value | |||
303 | # pairs and join the keys into a str separated by newlines, | |||
304 | # and take the values as references to the target variables. | |||
305 | 3 | 2.4e-5 | 8.0e-6 | if(ref($paramdefs) =~ /array/i) { |
306 | my $newdefs = ""; | |||
307 | while(@$paramdefs) { | |||
308 | 1122 | 0.00153 | 1.4e-6 | my ($key,$val,$parm,$i); |
309 | # Copy comments and blank lines without further attention | |||
310 | 1 | 0.00018 | 0.00018 | $newdefs .= shift(@$paramdefs)."\n" |
311 | while @$paramdefs && | |||
312 | ($paramdefs->[0] =~ /^\s*$com/ || | |||
313 | $paramdefs->[0] =~ /^\s*$/); | |||
314 | last if ! @$paramdefs; | |||
315 | # Expand inclusions | |||
316 | 7 | 0.00014 | 2.0e-5 | if($paramdefs->[0] =~ /^\s*\$include_defs\s+(\S.*?)\s*$/) { |
317 | require WISE::Pars; | |||
318 | my $include_call = $1; | |||
319 | 1 | 1.8e-5 | 1.8e-5 | my @inc_defs = eval "WISE::ParamDefs->$include_call"; # spent 651µs making 1 call to WISE::ParamDefs::basic |
320 | die "$err: Attempt to include definitions failed on ". | |||
321 | "'$include_call'.\n$@" | |||
322 | if $@; | |||
323 | shift @$paramdefs; | |||
324 | unshift @$paramdefs, @inc_defs; | |||
325 | next; | |||
326 | } | |||
327 | # Get param. definition | |||
328 | $key = shift @$paramdefs; | |||
329 | # Look ahead and see if a default is in the next entry | |||
330 | $key .= shift @$paramdefs if @$paramdefs && | |||
331 | $paramdefs->[0] =~ /^\s*=/; | |||
332 | # Add to accumulating definition string. | |||
333 | $newdefs .= "$key\n"; | |||
334 | # Skip comments | |||
335 | 1 | 0.00019 | 0.00019 | $newdefs .= shift(@$paramdefs)."\n" |
336 | while @$paramdefs && $paramdefs->[0] =~ /^\s*$com/; | |||
337 | # Get target reference (if any) | |||
338 | $val = shift @$paramdefs if @$paramdefs && ref $paramdefs->[0]; | |||
339 | # Pull out first parameter alias | |||
340 | ($parm) = $key =~ m/^\s*([^$com].*?)\s*[,:]/; | |||
341 | # Use it as the key to the var. ref. | |||
342 | if(! $parm && $val) { | |||
343 | # Error if a valid identifier couldn't be found | |||
344 | die "$err: Pairwise definition of '$key' not valid.\n"; | |||
345 | } | |||
346 | # Don't populate the hash if target is undefined | |||
347 | $refs->{lc $parm} = $val if $val; | |||
348 | ++$i; | |||
349 | } | |||
350 | # Now param defs are all one big string with individual defs | |||
351 | # separated by newlines | |||
352 | $paramdefs = $newdefs; | |||
353 | } | |||
354 | ||||
355 | # Join lines split by newlines preceded by \ | |||
356 | $paramdefs =~ s/(?<!\\)\\\n+[ \t]*//g; | |||
357 | ||||
358 | # Now there's one parameter def. per line, and one line per param. def | |||
359 | ||||
360 | # Split on newlines | |||
361 | ||||
362 | for (split(/\n+/,$paramdefs)) { | |||
363 | 6201 | 0.01085 | 1.8e-6 | next if /^\s*$/; # Skip blank lines |
364 | ||||
365 | push @def,$_; | |||
366 | ||||
367 | my (@aliases,@aliasesorig); | |||
368 | ||||
369 | print "PARAM: Raw def of ".($iam||"?")." = '$_'\n" if $debug; | |||
370 | print "PARAM: ctxt = $ctxt\n" if $debug; | |||
371 | ||||
372 | $orig = $_; | |||
373 | # Substitute in tags | |||
374 | s/\$iam\b/$iam/g if $iam; # Replace command name tag | |||
375 | # Look for an unquoted, unescaped comment character | |||
376 | 605 | 0.00143 | 2.4e-6 | if (($i=paruqindex($_,$com))>=0) { # spent 3.53ms making 259 calls to WISE::Params::paruqindex, avg 14µs/call |
377 | # Trim off comments but save them as possible help strings | |||
378 | $cmnt = &parunquote(substr($_,$i+1),1); # spent 1.04ms making 173 calls to WISE::Params::parunquote, avg 6µs/call | |||
379 | substr($_,$i) = ""; # Erase the line up to the comment char | |||
380 | $cmnt =~ s/^\s*$//; # Reduce blank lines to an empty string | |||
381 | } else { | |||
382 | # No comment | |||
383 | $cmnt = undef; | |||
384 | } | |||
385 | # Save help-related comments | |||
386 | 259 | 0.00033 | 1.3e-6 | if(defined $cmnt) { |
387 | # Comment is present and is not doubled (##), so save as help. | |||
388 | # (Double comments not saved (e.g. "## This is not help!")) | |||
389 | 173 | 0.00025 | 1.5e-6 | if($cmnt !~ /^$com/) { |
390 | 175 | 0.00031 | 1.8e-6 | if($cmnt=~/^\s*Command\s*=>\s*(\S+)/i) { |
391 | # Special syntax: Program name specified. Save it. | |||
392 | # Only take the first. | |||
393 | $iam = $ctxt->{_meta}{iam} = $1 if ! $iam; | |||
394 | $help = undef; | |||
395 | next; | |||
396 | } else { | |||
397 | # Just save the comment as regular help | |||
398 | ($help = $cmnt) =~ s/^ //; # .. after killing a leading sp | |||
399 | } | |||
400 | } else { | |||
401 | # Doubled comment. Treat as white space. | |||
402 | next; | |||
403 | } | |||
404 | } else { | |||
405 | $help = undef; | |||
406 | } | |||
407 | # Skip lines that are blank after comment stripping, after recording | |||
408 | # any help lines. | |||
409 | print "\tPARAM: HELP=/".(defined $help?$help:"<undef>")."/ ; ". | |||
410 | "lastdef='".(defined $lastdef?$lastdef:"<undef>")."'\n" | |||
411 | if $debug>1; | |||
412 | 172 | 1.0e-4 | 5.8e-7 | if (/^\s*$/) { |
413 | 344 | 0.00029 | 8.3e-7 | if(defined $help) { |
414 | # There was help on this line; which kind of help? | |||
415 | 454 | 0.00051 | 1.1e-6 | if(! defined $lastdef) { # General help |
416 | # This is before any param. def.s, so it's general help | |||
417 | # (i.e. not related to one parameter) | |||
418 | # (Only push blank help lines if there's help there now.) | |||
419 | print "\tPARAM: For General = ". | |||
420 | scalar(@allhelp)."\n" | |||
421 | if $debug>1; | |||
422 | push @allhelp,$help; | |||
423 | } else { # Parameter help | |||
424 | # This comment line followed a parameter def., so it's | |||
425 | # help specifically for that param. | |||
426 | # (Only push blank help lines if there's help there now.) | |||
427 | $ctxt->{$lastdef}{help} ||= []; | |||
428 | print "\tPARAM: For $lastdef = ". | |||
429 | scalar(@{$ctxt->{$lastdef}{help}})."\n" | |||
430 | if $debug>1; | |||
431 | push @{$ctxt->{$lastdef}{help}},$help | |||
432 | if @{$ctxt->{$lastdef}{help}} || $help ne ""; | |||
433 | } | |||
434 | next; | |||
435 | } | |||
436 | } | |||
437 | print "\tPARAM: Massaged def = '$_'\n" if $debug; | |||
438 | ||||
439 | # Parse definition | |||
440 | m'^ \s* ((?:[-\w]+\s*,\s*)*[-\w]+) \s* : # 1 = aliases | |||
441 | \s* (?:(list(?:\s*of)? | |||
442 | (?:\s+\d+(?:(?:\s+to\s+|\s*\.\.\s*|\s*-\s*) | |||
443 | (?:\d+|inf(?:inity)?))?)? | |||
444 | )\s+ )? # 2 = list (n) | |||
445 | ( # Types | |||
446 | (?: # numeric types | |||
447 | (?: r(?:eal)? | float | # real | |||
448 | d(?:oub(?:le)?)? | dbl | | |||
449 | i(?:nt(?:eger)?)? # integer | |||
450 | ) | |||
451 | (?: \s* \( \s* | |||
452 | (?:[-+.\de]+|-inf(?:inity)?) | |||
453 | (?:\s+to\s+|\s*\.\.\s*) | |||
454 | (?:[-+.\de]+|inf(?:inity)?) | |||
455 | \s* \) )? # range | |||
456 | ) | | |||
457 | (?: # Bare string types | |||
458 | (?: | |||
459 | (?:s(?:tr(?:ing)?)?(?:\(\s*\d+\s*\))?) | # string | |||
460 | (?:c(?:h(?:ar(?:acter)?)?)?(?:\(\s*\d+\s*\))?) | # string | |||
461 | ) | |||
462 | (?: \s* \( \s* # length range | |||
463 | \d+ | |||
464 | (?:\s+to\s+|\s*\.\.\s*) | |||
465 | (?:\d+|inf(?:inity)?) | |||
466 | \s* \) )? | |||
467 | ) | | |||
468 | (?:(?:map(?:ping)? | hash | name[sd]?) \s* | |||
469 | (?: \([^()]*\) )? ) | # map/name type | |||
470 | b(?:ool(?:ean)?)? | # boolean type | |||
471 | (?:(?:file|fil?)(?:\s*\([^\)]*?\))?) | # file with pragma | |||
472 | sw(?:itch)? # switch | |||
473 | #| (?:g(?:r(?:oup)?)?(?:\(.*?\))?) # group | |||
474 | ) \s* # 3 = type | |||
475 | (?: ((?:(?:private|quiet)(?:\([^\)]*\))? \s*)+) )? # 4 = attributes | |||
476 | (?: = | |||
477 | (?: \s* | |||
478 | ( (?:(?:\$required|\$unnamed) \s* )+ ) # 5 =req/unnamed | |||
479 | )? | |||
480 | (?: \s* (\$default) \s* )? # 6 = internal default | |||
481 | (?: \s* (\S.*?) )? \s* # 7 = default value | |||
482 | )? | |||
483 | $'ix; # | |||
484 | ||||
485 | print "\tPARAM: Raw parse = /". | |||
486 | join("/",map {$_//'UNDEF'} $1,$2,$3,$4,$5,$6,$7)."/\n" | |||
487 | if $debug; | |||
488 | ||||
489 | if(! $1 || ! $3) { | |||
490 | die "$err: Bad parameter definition: '$orig'.\n"; | |||
491 | } | |||
492 | ($aliases,$list,$rawtype) = ($1,$2,$3); | |||
493 | my $origtype = $rawtype; | |||
494 | ($attribs,$required,$intdefault,$default) = ($4,$5,$6,$7); | |||
495 | 86 | 6.2e-5 | 7.2e-7 | if($required) { |
496 | $unnamed = $required =~ s/\s*\$unnamed\s*//g; | |||
497 | } else { | |||
498 | $unnamed = undef; | |||
499 | } | |||
500 | ||||
501 | 95 | 0.00010 | 1.1e-6 | if($list) { |
502 | $list =~ s/list(\s*of)?\s*/list of /; | |||
503 | 13 | 1.2e-5 | 9.2e-7 | if($list =~ s/(?<=list of )(\d+)(?:\s*(?:-|\.\.|\sto\s)\s*(\d+|inf))?\s*//i) { |
504 | $minlist = $1; | |||
505 | $maxlist = defined $2 ? $2 : $1; | |||
506 | } else { | |||
507 | $minlist = $maxlist = undef; | |||
508 | } | |||
509 | } else { | |||
510 | $minlist = $maxlist = undef; | |||
511 | } | |||
512 | @aliasesorig = split(/,+\s*/,$aliases); | |||
513 | @aliases = map {lc $_} @aliasesorig; | |||
514 | @there{@aliases} = (1)x@aliases; | |||
515 | @aliases = (@aliases,grep(! $there{$_}++,@aliasesorig)); | |||
516 | if($unnamed) { | |||
517 | push @unnamed,$aliases[0]; | |||
518 | } | |||
519 | 94 | 7.1e-5 | 7.6e-7 | if($rawtype =~ s/\s* \( \s* |
520 | ([-+.\de]+|-inf(?:inity)?) | |||
521 | (?:\s+to\s+|\s*\.\.\s*) | |||
522 | ([-+.\de]+|inf(?:inity)?) | |||
523 | \s* \) \s*//x) { | |||
524 | ($minval,$maxval) = ($1,$2); | |||
525 | 2 | 4.0e-6 | 2.0e-6 | $minval = eval {$minval + 0} |
526 | if $minval !~ /^-inf/; | |||
527 | 2 | 1.0e-6 | 5.0e-7 | $maxval = eval {$maxval + 0} |
528 | if defined $maxval && $maxval !~ /^inf/ && ! $@; | |||
529 | die "$err: Minval '$minval' or maxval '$maxval' is illegal ". | |||
530 | "for parameter $aliases[0].\n$@" | |||
531 | if $@; | |||
532 | $maxval = 'inf' if ! defined $maxval; | |||
533 | } else { | |||
534 | $minval = $maxval = undef; | |||
535 | } | |||
536 | 101 | 7.5e-5 | 7.4e-7 | if($rawtype =~ s/^(file|fil?)(?:\s*\((.*)\))/$1/i && $2) { |
537 | my $pragmas = $2; | |||
538 | $file_pragmas = []; | |||
539 | @$file_pragmas = split /\s*,\s*/, $pragmas; | |||
540 | print "\tPARAM: parsed pragmas for $aliasesorig[0]($1) = ". | |||
541 | "/@$file_pragmas/\n" | |||
542 | if $debug; | |||
543 | my @nope = grep {! $legal_file_pragmas{lc $_} } @$file_pragmas; | |||
544 | die "$err: Illegal file pragma(s) /".join("/",@nope)."/ ". | |||
545 | "for parameter $aliases[0].\n$@" | |||
546 | if @nope; | |||
547 | } else { | |||
548 | $file_pragmas = undef; | |||
549 | } | |||
550 | ||||
551 | ($type,$itype,$size,$mapkeys,$otype) = &typeoftype($rawtype); # spent 1.64ms making 86 calls to WISE::Params::typeoftype, avg 19µs/call | |||
552 | ||||
553 | if(! $itype) { | |||
554 | die "$err: Type unknown for parameter $aliases[0], ". | |||
555 | "type='$rawtype'.\n"; | |||
556 | } | |||
557 | $list = "list of" if $itype == 8 || $itype == 9; | |||
558 | $hash = $itype == 8 ? 1 : 0; | |||
559 | $private = $quiet = undef; | |||
560 | 27 | 0.00015 | 5.5e-6 | if($attribs) { |
561 | while($attribs =~ m%(private|quiet)(?:\(\s*(.*?)\s*\))?%ig) { | |||
562 | 84 | 0.00014 | 1.6e-6 | my ($attr,$spec) = ($1,$2); |
563 | $private = $spec ? $spec : 1 if lc($attr) eq 'private'; | |||
564 | $quiet = $spec ? $spec : 1 if lc($attr) eq 'quiet'; | |||
565 | } | |||
566 | } | |||
567 | # Default handling | |||
568 | 2 | 2.0e-6 | 1.0e-6 | if($nodefault) { |
569 | $default = undef; | |||
570 | } elsif(exists $postdefaults->{$aliases[0]}) { | |||
571 | $default = $postdefaults->{$aliases[0]}; | |||
572 | $default = undef, $required = 1 | |||
573 | if $default && $default=~/^\s*\$required\s*$/; | |||
574 | } | |||
575 | my $init; | |||
576 | if(exists $preinits->{$aliases[0]}) { | |||
577 | $init = $preinits->{$aliases[0]}; | |||
578 | } | |||
579 | if(my ($dup) = grep defined $allaliases{$_},@aliases) { | |||
580 | die "$err: Duplicate parameter alias '$dup' ". | |||
581 | "found for param $allaliases{$dup} and $aliases[0].\n"; | |||
582 | } | |||
583 | if($type eq "switch" and $list) { | |||
584 | die "$err: Switch parameter $aliases[0] ". | |||
585 | "may not be a list.\n"; | |||
586 | } | |||
587 | if($type eq 'switch' && defined $default && | |||
588 | ! defined $postdefaults->{$aliases[0]}) { | |||
589 | die "$err: Switch parameter $aliases[0] may not ". | |||
590 | "have a default.\n"; | |||
591 | } | |||
592 | # Init meta structures for this param | |||
593 | @{$values}{@aliases,@aliasesorig} = () if $values; | |||
594 | 28 | 6.6e-5 | 2.4e-6 | if($list && $values) { |
595 | @{$values}{@aliases,@aliasesorig} = | |||
596 | ([])x(@aliases+@aliasesorig) | |||
597 | if ! $hash; | |||
598 | @{$values}{@aliases,@aliasesorig} = | |||
599 | ({})x(@aliases+@aliasesorig) | |||
600 | if $hash; | |||
601 | } | |||
602 | if($list) { $list .= " "; } | |||
603 | ||||
604 | $ref = { | |||
605 | name=>$aliases[0], nameorig=>$aliasesorig[0], | |||
606 | aliases=>\@aliases, aliasesorig=>\@aliasesorig, | |||
607 | list=>$list, hash=>$hash, intdefault=>$intdefault, | |||
608 | otype=>$otype, type=>$type, itype=>$itype, rawtype=>$origtype, | |||
609 | size=>$size, mapkeys=>$mapkeys, | |||
610 | private=>$private, quiet=>$quiet, | |||
611 | defaultstr=>$default, required=>$required, | |||
612 | need=>$required, minlist=>$minlist, maxlist=>$maxlist, | |||
613 | minval=>$minval, maxval=>$maxval, file_pragmas=>$file_pragmas, | |||
614 | help=>[], unnamed=>$unnamed | |||
615 | }; | |||
616 | $ctxt->{$aliases[0]} = $ref; | |||
617 | ||||
618 | push @allparnames, $aliases[0]; | |||
619 | @allaliases{@aliases,@aliasesorig} = | |||
620 | ($aliases[0]) x (@aliases+@aliasesorig); | |||
621 | ||||
622 | if(defined $help && $help ne "") { | |||
623 | push @{$ctxt->{$aliases[0]}{help}},$help; | |||
624 | } | |||
625 | ||||
626 | print "\tPARAM: Def = /$aliases[0]/$type/$itype/". | |||
627 | ($default//"UNDEF")."/".($required//"UNDEF")."/\n" | |||
628 | if $debug; | |||
629 | ||||
630 | 172 | 0.00103 | 6.0e-6 | if($type ne "switch") { |
631 | if(defined $default) { # spent 15.9ms making 62 calls to WISE::Params::Param_parse, avg 257µs/call | |||
632 | Param_parse(["-$aliases[0]",$default], | |||
633 | {%$opts,defaultdef=>1,from=>0}); | |||
634 | } | |||
635 | if(defined $init) { | |||
636 | Param_parse(["-$aliases[0]",$init], | |||
637 | {%$opts,defaultdef=>0,from=>5}); | |||
638 | } | |||
639 | } else { | |||
640 | # Switch. Need to handle value differently | |||
641 | Param_parse(["-$aliases[0]=0"], # spent 1.03ms making 6 calls to WISE::Params::Param_parse, avg 172µs/call | |||
642 | {%$opts,defaultdef=>1,from=>0}); | |||
643 | if(defined $init) { | |||
644 | Param_parse(["-$aliases[0]=$init"], | |||
645 | {%$opts,defaultdef=>0,from=>5}); | |||
646 | } | |||
647 | } | |||
648 | ||||
649 | $lastdef = $aliases[0]; | |||
650 | ||||
651 | } # Param loop | |||
652 | ||||
653 | $ctxt->{_meta}{def} = \@def; | |||
654 | ||||
655 | return $ctxt; | |||
656 | } | |||
657 | ||||
658 | ||||
659 | # Read command line param.s (including those from the environment and files) | |||
660 | # and assign them according to the defined parameters. | |||
661 | ||||
662 | # spent 32.1ms (9.81+22.3) within WISE::Params::Param_parse which was called 71 times, avg 452µs/call:
# 62 times (7.44ms+8.47ms) by WISE::Params::parsedefs at line 631, avg 257µs/call
# 6 times (662µs+371µs) by WISE::Params::parsedefs at line 641, avg 172µs/call
# 2 times (1.05ms+-1052000ns) by WISE::Params::paramsfromfile at line 1590, avg 0/call
# once (656µs+14.5ms) by WISE::Params::Params at line 245 | |||
663 | 1846 | 0.00229 | 1.2e-6 | my ($argv,$opts) = @_; |
664 | $opts = { %$opts }; # Shallow clone | |||
665 | ||||
666 | my $ctxt = $opts->{defs} or die "$err/parparse: No defs.\n"; | |||
667 | my $nofiles = $opts->{nofiles}; | |||
668 | my $debug = $opts->{debug} || $ctxt->{_meta}{debug} || 0; | |||
669 | my $nopasson = $opts->{nopasson} || 0; | |||
670 | my $from = $opts->{from}; | |||
671 | my $defaultdef = $opts->{defaultdef} || 0; | |||
672 | my $strict = exists $opts->{strict} ? $opts->{strict} : 1; # warn | |||
673 | ||||
674 | my $iam = $ctxt->{_meta}{iam}; | |||
675 | my $values = $ctxt->{_meta}{vals} || 0; | |||
676 | my $refs = $ctxt->{_meta}{refs} || 0; | |||
677 | ||||
678 | # Mark the param.s originally passed (before file and eniron var. | |||
679 | # interpolation) with their source of origin. | |||
680 | # -1=unknown, 0=default, 1=cmd line, 2=environ var., 3=file, 4=pass-on, | |||
681 | # 5=unnamed params | |||
682 | $from = $opts->{from} = ! defined $from ? -1 : $from; | |||
683 | ||||
684 | print "\tPARAM_PARSE start: from=$from ...\n" | |||
685 | if $debug; | |||
686 | print "\tPARAM: ctxt=$ctxt, from=$from, argv='@$argv'\n" | |||
687 | if $debug; | |||
688 | ||||
689 | if(! (keys %$ctxt)) { | |||
690 | return 0; | |||
691 | } | |||
692 | ||||
693 | 71 | 4.4e-5 | 6.2e-7 | if(ref($argv) && ref($argv) !~ /array/i) { |
694 | die "*** $err: ARGV passed to Param_parse not an array ref.\n"; | |||
695 | } elsif ($argv && ! ref($argv)) { | |||
696 | require Text::ParseWords; Text::ParseWords->import(); | |||
697 | my $txt = $argv; | |||
698 | $argv = []; | |||
699 | @$argv = shellwords($txt); | |||
700 | } else { | |||
701 | $argv ||= []; # Got to put something there. | |||
702 | } | |||
703 | ||||
704 | # More printble argv for error msgs | |||
705 | my @print_argv = @$argv; | |||
706 | for (@print_argv) { | |||
707 | 406 | 0.00033 | 8.2e-7 | $_ //= 'UNDEFINED'; |
708 | $_ = length($_)>100 ? substr($_,0,100)."..." : $_; | |||
709 | } | |||
710 | ||||
711 | my @add; # Unnamed params to process later | |||
712 | ||||
713 | # Get parameters from "standard" files named in paramfiles | |||
714 | 3 | 1.4e-5 | 4.7e-6 | if(@PARAMFILES && ! $nofiles) { |
715 | my @files = @PARAMFILES; # Process in reverse order | |||
716 | @PARAMFILES = (); # To avoid infinite recursion | |||
717 | @add = paramsfromfile(1,\@files,$ctxt); # spent 13.4ms making 1 call to WISE::Params::paramsfromfile | |||
718 | } | |||
719 | ||||
720 | # Step through named parameters | |||
721 | ||||
722 | my $pos = 0; # Position in cmd line arg list | |||
723 | ||||
724 | ARGS: while (1) { | |||
725 | ||||
726 | 4588 | 0.00578 | 1.3e-6 | ++$pos; |
727 | my $p = shift @$argv; | |||
728 | ||||
729 | print "\t\tPARAM: ctxt=$ctxt, param='".($p//'undef')."', ". | |||
730 | "next='".($argv->[0]//'undef')."'\n" | |||
731 | if $debug; | |||
732 | ||||
733 | 71 | 5.5e-5 | 7.7e-7 | if(! defined $p) { last ARGS; } # Stop if not defined or if '--' |
734 | if($p eq '--') { last ARGS; } | |||
735 | ||||
736 | if($p eq '-.DEBUG') { | |||
737 | $debug = $ctxt->{_meta}{debug} = 1; | |||
738 | next ARGS; | |||
739 | } | |||
740 | ||||
741 | my $parname; | |||
742 | if($p =~ /^-(.+)$/) { | |||
743 | $parname = $1; | |||
744 | } else { | |||
745 | # END OF PARAMETER LIST, beginning of unnamed arguments | |||
746 | if($strict) { | |||
747 | my @dashes = grep {/^-+[^\d]/} @$argv; | |||
748 | if(@dashes) { | |||
749 | if($strict>1) { | |||
750 | die "$err: Some unnamed args begin with a dash: ". | |||
751 | join(",",@dashes).".\n"; | |||
752 | } else { | |||
753 | warn "$warn: Some unnamed args begin with a dash: ". | |||
754 | join(",",@dashes).".\n"; | |||
755 | } | |||
756 | } | |||
757 | } | |||
758 | --$pos; | |||
759 | unshift @$argv,$p; | |||
760 | last ARGS; | |||
761 | } | |||
762 | ||||
763 | # Named parameter found. Process. | |||
764 | ||||
765 | # OK if parameter is not defined? | |||
766 | my $skipok; | |||
767 | 105 | 6.2e-5 | 5.9e-7 | if($parname =~ s/^%//) { |
768 | $skipok = 1; | |||
769 | } else { | |||
770 | $skipok = 0; | |||
771 | } | |||
772 | ||||
773 | # Add to or overwrite lists and hashes? | |||
774 | my $addlist; | |||
775 | 105 | 6.4e-5 | 6.1e-7 | if($parname =~ s/^\+//) { |
776 | # Add to lists/hashes | |||
777 | $addlist = 1; | |||
778 | } elsif($parname =~ s/^=//) { | |||
779 | # Overwrite lists/hashes | |||
780 | $addlist = 0; | |||
781 | } else { | |||
782 | # Final answer depends on whether the param is a hash or a list | |||
783 | $addlist = undef; | |||
784 | } | |||
785 | ||||
786 | # Special processing for the '-help' option | |||
787 | # -help or -help[123] or -help_[123] or [brief,long,full,pod]_help | |||
788 | if($parname =~ /^(?:(brief|long|full)_?)?help(?:_?([0-2]))?$/ix) { | |||
789 | print "\t\tPARAM: Getting help /$1/$2/ for $parname\n" | |||
790 | if $debug; | |||
791 | my $val = &Param_help($1||$2||0,{%$opts,defs=>$ctxt, | |||
792 | param=>join(",",@$argv)}); | |||
793 | exit($val ? 0 : 1); | |||
794 | } | |||
795 | ||||
796 | # Special processing for the '-cache_parameters' option | |||
797 | if($parname =~ /^cache_parameters$/i) { | |||
798 | print "\t\tPARAM: Caching parameters for '$iam' ...\n" | |||
799 | if $debug; | |||
800 | ¶msfrommodel($0,{usecache=>1, | |||
801 | param_defs=>$ctxt->{_meta}{def},}) | |||
802 | or die "$err: Unable to update cache.\n"; | |||
803 | exit; | |||
804 | } | |||
805 | ||||
806 | # Get parameters from files named on command line | |||
807 | if($parname =~ /^@(\S+)$/) { | |||
808 | my $files = $1; | |||
809 | print "--- Reading params from files '$files' ...\n" if $debug; | |||
810 | push @add,paramsfromfile($skipok,[split(':',$files)],$ctxt); | |||
811 | next ARGS; | |||
812 | } | |||
813 | ||||
814 | my $rawname = $parname; | |||
815 | my $val; | |||
816 | ||||
817 | # Look for an '=xxxx' subfield to the parameter that indicates | |||
818 | # a value is directly attached, not in the next word. | |||
819 | my $equals; | |||
820 | 222 | 0.00014 | 6.4e-7 | if($rawname !~ /:/ && $rawname =~ /^([^=]+)=(.+)$/) { |
821 | $rawname = $1; | |||
822 | $parname = lc $rawname; | |||
823 | $val = $2; | |||
824 | $equals = 1; | |||
825 | } else { | |||
826 | $equals = 0; | |||
827 | $parname = lc $parname; | |||
828 | } | |||
829 | ||||
830 | 145 | 0.00030 | 2.0e-6 | if(! $nopasson && $parname =~ /:/) { |
831 | # Handle pass-on parameters (i.e. parameters meant to be | |||
832 | # passed on from one program to subsequent programs) | |||
833 | ||||
834 | my $arg = shift @$argv; | |||
835 | ++$pos; | |||
836 | ||||
837 | print "\t\tPARAM: Calling passon with /$parname/ = /$arg/ ...\n" | |||
838 | if $debug; | |||
839 | ||||
840 | push @add, parse_passon_param($parname,$arg, { %$opts }); # spent 859µs making 29 calls to WISE::Params::parse_passon_param, avg 30µs/call | |||
841 | ||||
842 | next ARGS; | |||
843 | } | |||
844 | ||||
845 | # We only reach here if the parameter is a named parameter tag | |||
846 | # ("-something") meant for this app. | |||
847 | ||||
848 | # Check to see if the name is recognized | |||
849 | my $full = $ctxt->{_meta}{aliases}{$parname}; | |||
850 | if(! defined $full) { | |||
851 | if(! $skipok) { # Not known, and we care | |||
852 | die "$err: Unrecognized parameter: '$parname'.\n". | |||
853 | "$err: Bad cmd args: !".join("! ",@print_argv)."!.\n"; | |||
854 | } else { # Not known, but we don't care | |||
855 | # Skip OK params are assumed to have args, unless they | |||
856 | # are already provided by the '=' | |||
857 | shift @$argv if @$argv && ! $equals; | |||
858 | next ARGS; | |||
859 | } | |||
860 | } | |||
861 | ||||
862 | my $fullorig = $ctxt->{$full}{nameorig}; # Original case | |||
863 | 138 | 0.00010 | 7.5e-7 | if($ctxt->{$full}{type} ne 'switch') { |
864 | 138 | 8.9e-5 | 6.4e-7 | if( ! $equals) { |
865 | ++$pos; | |||
866 | $val = shift @$argv; | |||
867 | } | |||
868 | if(! defined $val) { | |||
869 | die "$err: Parameter '$parname' missing its arg.\n". | |||
870 | "$err: Bad cmd args: !".join("! ",@print_argv)."!.\n"; | |||
871 | } | |||
872 | } | |||
873 | ||||
874 | # Record exactly how the user actually refered to the parameter name. | |||
875 | $ctxt->{$full}{given} = $parname; | |||
876 | # ...and exactly how the string looked originally | |||
877 | $ctxt->{$full}{origval} = $val; | |||
878 | ||||
879 | # Check and return the value for this parameter | |||
880 | print "\t\tPARAM: VAL before paramchecklist = /$val/\n" | |||
881 | if $debug; | |||
882 | ||||
883 | my $ary = ¶mchecklist($full,$val,$ctxt,$opts); # spent 6.39ms making 76 calls to WISE::Params::paramchecklist, avg 84µs/call | |||
884 | ||||
885 | if(! defined $ary) { | |||
886 | die "$err: Paramchecklist failed on $full='$val'"; | |||
887 | } | |||
888 | ||||
889 | # Remove escape char.s and quotes | |||
890 | 149 | 0.00041 | 2.8e-6 | if(ref($ary)=~/array/i) { |
891 | $_=parunquote($_,1) for @$ary; # spent 558µs making 86 calls to WISE::Params::parunquote, avg 6µs/call | |||
892 | } else { | |||
893 | for my $elem (values %$ary) { | |||
894 | 59 | 0.00018 | 3.1e-6 | if(! ref($elem)) { # spent 107µs making 20 calls to WISE::Params::parunquote, avg 5µs/call |
895 | $elem = parunquote($elem,1); | |||
896 | } else { | |||
897 | $_ = parunquote($_, 1) for @$elem; # spent 636µs making 129 calls to WISE::Params::parunquote, avg 5µs/call | |||
898 | } | |||
899 | } | |||
900 | } | |||
901 | ||||
902 | 1 | 0.00070 | 0.00070 | $val = (! $ctxt->{$full}{list} |
903 | ? $ary->[0] | |||
904 | : $ary); | |||
905 | ||||
906 | if($ctxt->{$full}{hash} && $ctxt->{$full}{val}) { | |||
907 | # If defined as a hash, add rather than replace, unless | |||
908 | # over-ridden by $addlist | |||
909 | if($addlist || ! defined $addlist) { | |||
910 | my @val = ref($val)=~/hash/i ? %$val : @$val; | |||
911 | $val = {}; | |||
912 | %$val = ( %{$ctxt->{$full}{val}}, @val ); | |||
913 | } else { | |||
914 | # $addlist is explicitly 0, meaning we need to replace | |||
915 | # the current hash value | |||
916 | # This will be done below by virtue of not having | |||
917 | # executed the line above. | |||
918 | } | |||
919 | } elsif ($ctxt->{$full}{list} && ! $ctxt->{$full}{hash} && | |||
920 | $ctxt->{$full}{val}) { | |||
921 | # It's a list (not a hash) with an extant value. | |||
922 | # See if we've been instructed to add to the list | |||
923 | # rather than replace it. | |||
924 | if($addlist) { | |||
925 | @$val = (@{$ctxt->{$full}{val}},@$val); | |||
926 | } | |||
927 | } | |||
928 | ||||
929 | my $nargs = $ctxt->{$full}{type} ne 'switch' && ! $equals | |||
930 | ? 1 : 0; | |||
931 | ||||
932 | # Assign final values | |||
933 | ||||
934 | print "\t\tPARAM: name=$full;val=$val". | |||
935 | (ref($val)?"=".join(",",ref($val)=~/hash/i?%$val:@$val) | |||
936 | :""). | |||
937 | ";from=$from;defdef=$defaultdef;". | |||
938 | "req=".($ctxt->{$full}{required}//"undef").";refs=$refs". | |||
939 | ($refs&&$refs->{$full}?":$refs->{$full}":""). | |||
940 | ";hash=$ctxt->{$full}{hash}/mapkeys=". | |||
941 | (ref($ctxt->{$full}{mapkeys}) | |||
942 | ? "$ctxt->{$full}{mapkeys}:". | |||
943 | join(",",map{$_//'undef'}@{$ctxt->{$full}{mapkeys}}) | |||
944 | : "none")."\n" | |||
945 | if $debug; | |||
946 | ||||
947 | # Check validity of map keys | |||
948 | check_map_keys($full, $val, $from, $ctxt); # spent 1.25ms making 76 calls to WISE::Params::check_map_keys, avg 16µs/call | |||
949 | ||||
950 | # Check number of list elements | |||
951 | check_list_count($full,$val,$ctxt); # spent 521µs making 76 calls to WISE::Params::check_list_count, avg 7µs/call | |||
952 | ||||
953 | # Assign value(s) | |||
954 | ||||
955 | if($values) { | |||
956 | # Antiquated global values hash | |||
957 | @{$values}{@{$ctxt->{$full}{aliases}}, | |||
958 | @{$ctxt->{$full}{aliasesorig}}} = | |||
959 | ($val) x (@{$ctxt->{$full}{aliases}}+ | |||
960 | @{$ctxt->{$full}{aliasesorig}}); | |||
961 | } | |||
962 | ||||
963 | $ctxt->{$full}{val} = $val; | |||
964 | $ctxt->{$full}{default} = $val if $defaultdef; | |||
965 | $ctxt->{$full}{isdefault} = 1 if $defaultdef; | |||
966 | $ctxt->{$full}{specified} = 0 if $defaultdef; | |||
967 | $ctxt->{$full}{isdefault} = 0 if ! $defaultdef; | |||
968 | $ctxt->{$full}{specified} = 1 if ! $defaultdef; | |||
969 | $ctxt->{$full}{need} = 0; | |||
970 | $ctxt->{$full}{source} = $from; | |||
971 | $ctxt->{$full}{pos} = $pos-1-$nargs; | |||
972 | $ctxt->{$full}{nargs} = $nargs; | |||
973 | $ctxt->{$full}{origname} = $rawname; | |||
974 | ||||
975 | # If a variable ref. was defined for this option, | |||
976 | # put the value there too. | |||
977 | 152 | 0.00045 | 3.0e-6 | if($refs) { |
978 | my $ref = $refs->{$full} || $refs->{$fullorig}; | |||
979 | assign_ref($ref, $val, $ctxt->{$full}, $full); # spent 531µs making 76 calls to WISE::Params::assign_ref, avg 7µs/call | |||
980 | } | |||
981 | ||||
982 | # ======================== | |||
983 | # End of parameter parsing | |||
984 | ||||
985 | ||||
986 | } # while(1) | |||
987 | ||||
988 | # Process parameters that take from the unnamed parameter list | |||
989 | my $unmparams = $ctxt->{_meta}{unnamed}; | |||
990 | if($from == 1 && $unmparams && @$unmparams && @$argv) { | |||
991 | for my $param (@$unmparams) { | |||
992 | last if ! @$argv; | |||
993 | next if $ctxt->{$param}{specified}; | |||
994 | # If a parameter associated itself with unnamed parameters, | |||
995 | # assign those values. | |||
996 | my $val; | |||
997 | #print "/--- 1 @$argv/\n"; | |||
998 | if($ctxt->{$param}{list}) { | |||
999 | my @get; | |||
1000 | if($ctxt->{$param}{maxlist} && | |||
1001 | $ctxt->{$param}{maxlist} !~ /^inf/i && | |||
1002 | $ctxt->{$param}{maxlist} < @$argv ) { | |||
1003 | @get = splice @$argv,0,$ctxt->{$param}{maxlist}; | |||
1004 | } else { | |||
1005 | @get = @$argv; | |||
1006 | @$argv = (); | |||
1007 | } | |||
1008 | $val = join(",",map { parquote($_,",") } @get); | |||
1009 | } else { | |||
1010 | $val = shift(@$argv); | |||
1011 | } | |||
1012 | #print "/--- 2 @$argv/\n"; | |||
1013 | Param_parse(["-$param",$val],{defs=>$ctxt, | |||
1014 | vals=>$values, | |||
1015 | refs=>$refs, | |||
1016 | from=>5}); | |||
1017 | } | |||
1018 | } | |||
1019 | ||||
1020 | # Return any left over parameters from files or pass-on params | |||
1021 | ||||
1022 | return (@$argv); | |||
1023 | } | |||
1024 | ||||
1025 | # spent 859µs within WISE::Params::parse_passon_param which was called 29 times, avg 30µs/call:
# 29 times (859µs+0) by WISE::Params::Param_parse at line 840, avg 30µs/call | |||
1026 | 609 | 0.00062 | 1.0e-6 | my $parname = shift; |
1027 | my $arg = shift; | |||
1028 | my $opts = shift || {}; | |||
1029 | my $ctxt = $opts->{defs}; | |||
1030 | my $iam = $ctxt->{_meta}{iam} || $opts->{iam}; | |||
1031 | my $dofilters = ! $opts->{nofilters}; | |||
1032 | my $debug = $opts->{debug} || $ctxt->{_meta}{debug}; | |||
1033 | my $from = $opts->{from}; | |||
1034 | my @add; | |||
1035 | ||||
1036 | require Text::ParseWords; | |||
1037 | ||||
1038 | my (@list,@abslist); | |||
1039 | ||||
1040 | # Split on the ':' "path" separator and filter out any | |||
1041 | # empty or undefined elements other than the first, for which | |||
1042 | # a NULL string is interpreted meaning the current executable. | |||
1043 | @list = split(/:/,$parname); | |||
1044 | ||||
1045 | # Test for illegal '-:' construct. | |||
1046 | die "$err: Illegal pass-on parameter: '$parname'.\n" | |||
1047 | if ! @list; | |||
1048 | ||||
1049 | # Remove and save a possible terminal filter | |||
1050 | my $filter; | |||
1051 | ($list[0] eq '*' && @list>1 ? $list[1] : $list[0]) =~ s|\{(.+)\}$|| | |||
1052 | and | |||
1053 | $filter = $1; | |||
1054 | ||||
1055 | # Substitute current name for empty field | |||
1056 | @list = ($list[0]||lc($iam), grep(defined $_, @list[1..$#list])); | |||
1057 | ||||
1058 | # Test for name match. | |||
1059 | ||||
1060 | # Expand any abbrev.s | |||
1061 | for my $nm (@list) { | |||
1062 | 52 | 7.0e-5 | 1.3e-6 | if(defined $WISE::Spawn::abbrev{$nm}) { |
1063 | $nm = $WISE::Spawn::abbrev{$nm}; | |||
1064 | } | |||
1065 | } | |||
1066 | ||||
1067 | @abslist = @list; | |||
1068 | ||||
1069 | print "\t\t\tPARM->passon: /@list/ = /$arg/\n" if $debug; | |||
1070 | ||||
1071 | my $global = 0; | |||
1072 | 58 | 8.6e-5 | 1.5e-6 | if ($list[0] eq '*' && defined $list[1]) { |
1073 | # Propogate; see if the next name matches. If so, | |||
1074 | # shift out the propogator and continue normal | |||
1075 | # processing. If not, propogate the whole thing. | |||
1076 | if(lc($list[1]) eq '*' && @list == 2) { | |||
1077 | # A global wildcard match ('-*:* ...'). Match and propgate. | |||
1078 | print "\t\t\t\tPARM->passon: !!! WILDCARD MATCH\n" if $debug; | |||
1079 | $global = 1; | |||
1080 | } elsif(lc($list[1]) eq lc($iam)) { | |||
1081 | # A normal match. Do not propagate | |||
1082 | shift @list; | |||
1083 | shift @list; | |||
1084 | } | |||
1085 | } elsif(lc($list[0]) eq lc($iam)) { | |||
1086 | # A direct match; shift it out and process below. | |||
1087 | print "\t\t\t\tPARM->passon: !!! MATCH\n" if $debug; | |||
1088 | shift @list; | |||
1089 | } else { | |||
1090 | # No match | |||
1091 | print "\t\t\t\tPARM->passon: NO MATCH: /$list[0]/!=/$iam/\n" if $debug; | |||
1092 | return (); | |||
1093 | } | |||
1094 | ||||
1095 | if($dofilters && $filter) { | |||
1096 | # Process any terminal parameter filters | |||
1097 | print "\t\t\tPARM->passon: Filtering with /$filter/\n" if $debug; | |||
1098 | pass_filters($filter,{defs=>$ctxt,debug=>$debug}) or return (); | |||
1099 | } | |||
1100 | ||||
1101 | # The name matched, see if there are more names which should be | |||
1102 | # passed on. If the list is empty, the arg.s are meant for this | |||
1103 | # execution. | |||
1104 | ||||
1105 | if(@list) { # There are more | |||
1106 | # There are more names, so pass them on down the line. | |||
1107 | # Construct the pass-on param. name | |||
1108 | my $param = '-'.join(':',@list).':'; | |||
1109 | my $absparam = '-'.join(':',@abslist).':'; | |||
1110 | ||||
1111 | # Error if there's no argument for the parameter | |||
1112 | die "$err: Pass-on paramter $param has no ". | |||
1113 | "argument.\n" | |||
1114 | if ! defined $arg ; | |||
1115 | ||||
1116 | # Save parameters which need to be passed on further | |||
1117 | ||||
1118 | $ctxt->{_meta}{pass_on} = [] | |||
1119 | if ! $ctxt->{_meta}{pass_on}; | |||
1120 | $ctxt->{_meta}{pass_on_abs} = [] | |||
1121 | if ! $ctxt->{_meta}{pass_on_abs}; | |||
1122 | $ctxt->{_meta}{pass_from} = [] | |||
1123 | if ! $ctxt->{_meta}{pass_from}; | |||
1124 | ||||
1125 | push @{$ctxt->{_meta}{pass_on}},$param; | |||
1126 | push @{$ctxt->{_meta}{pass_from}},$from; | |||
1127 | # Argument | |||
1128 | push @{$ctxt->{_meta}{pass_on}}, $arg; | |||
1129 | push @{$ctxt->{_meta}{pass_from}},undef; | |||
1130 | # Same in absolute addressing | |||
1131 | push @{$ctxt->{_meta}{pass_on_abs}},$absparam; | |||
1132 | push @{$ctxt->{_meta}{pass_on_abs}}, $arg; | |||
1133 | ||||
1134 | ||||
1135 | # Since there are more names, this program can't be the | |||
1136 | # target. Move on to the next arg. | |||
1137 | # UNLESS the '$global' flag is set, in which case we're BOTH processing | |||
1138 | # and propagating | |||
1139 | ||||
1140 | if(! $global) { | |||
1141 | return (); | |||
1142 | } | |||
1143 | } | |||
1144 | ||||
1145 | # No more names, or match was global, so this app. is the target. Put | |||
1146 | # arg.s in the array and parse them. | |||
1147 | ||||
1148 | die "$err: Pass-on paramter $parname has no argument.\n" | |||
1149 | if ! defined $arg; | |||
1150 | ||||
1151 | # Put arguemnt targetted for this exec. onto its argument | |||
1152 | # list for parsing, after first separating on whitespace. | |||
1153 | ||||
1154 | my @args = Text::ParseWords::quotewords('\s+',0,$arg); | |||
1155 | ||||
1156 | print "\t\t\tPARM->passon: PARSING /@args/\n" if $debug; | |||
1157 | ||||
1158 | push @add,Param_parse(\@args,{%$opts,from=>4}); | |||
1159 | ||||
1160 | return @add; | |||
1161 | } | |||
1162 | ||||
1163 | sub pass_filters { | |||
1164 | my $filter = shift; | |||
1165 | my $opts = shift || {}; | |||
1166 | my $ctxt = $opts->{defs}; | |||
1167 | my $debug = $opts->{debug} || $ctxt->{_meta}{debug}; | |||
1168 | my @filter; | |||
1169 | ||||
1170 | @filter = split /\s*,\s*/,$filter if $filter; | |||
1171 | ||||
1172 | while(@filter) { | |||
1173 | my $test = shift @filter; | |||
1174 | # Get param name and comparison value | |||
1175 | my ($tstparam,$check) = | |||
1176 | $test=~/^\s*([^=]+)\s*=\s*([^=]+)\s*$/; | |||
1177 | print "--- Applying filter '$tstparam=$check'\n" if $debug; | |||
1178 | die "$err: Filter not in 'param=test' form: '$test'.\n" | |||
1179 | if ! $tstparam; | |||
1180 | # Remove outer quotes from value | |||
1181 | $check =~ s/'([^'])'/$1/ | |||
1182 | or | |||
1183 | $check =~ s/"([^"])"/$1/; | |||
1184 | my $tstfull = $ctxt->{_meta}{aliases}{lc $tstparam}; | |||
1185 | my $parval = $ctxt->{$tstfull}{val}; | |||
1186 | my $partype= $ctxt->{$tstfull}{type}; | |||
1187 | die "$err: Unrecognized filter parameter '$tstparam'.\n" | |||
1188 | if ! $tstfull; | |||
1189 | die "$err: Can't filter on list param '$tstparam'.\n" | |||
1190 | if $ctxt->{$tstfull}{list}; | |||
1191 | die "$err: Numeric filter '$tstparam=$check' has ". | |||
1192 | "non-numeric value.\n" | |||
1193 | if $partype=~/^(u?i)|d|r|float/ && $check !~ /$FPre/; | |||
1194 | $parval = 0 | |||
1195 | if $partype=~/^sw/ && ! defined $parval; | |||
1196 | # If there's a match, pull off the filter and carry on. | |||
1197 | # Otherwise, skip this param. | |||
1198 | if(defined $parval && | |||
1199 | (lc($check) =~ /^<def(ined)?>$/ || | |||
1200 | ($partype=~/^s|c|f/ && $check eq $parval) || | |||
1201 | ($partype=~/^(u?i)|d|r|float/ && $check == $parval) || | |||
1202 | ($partype=~/^b/ && defined $bools{$check} && | |||
1203 | $bools{$check} == $bools{$parval}) || | |||
1204 | ($partype=~/^sw/ && !!$check + 0 == !!$parval + 0) | |||
1205 | ) ) { | |||
1206 | # Filter matched. Do nothing and carry on | |||
1207 | print "--- Filter '$tstparam=$check' matched ($parval).\n" | |||
1208 | if $debug; | |||
1209 | } else { | |||
1210 | # Filter failed; skip parameter | |||
1211 | print "--- Filter '$tstparam=$check' failed ($parval).\n" | |||
1212 | if $debug; | |||
1213 | return 0; | |||
1214 | } | |||
1215 | } | |||
1216 | ||||
1217 | return 1; | |||
1218 | } | |||
1219 | ||||
1220 | # spent 521µs within WISE::Params::check_list_count which was called 76 times, avg 7µs/call:
# 76 times (521µs+0) by WISE::Params::Param_parse at line 951, avg 7µs/call | |||
1221 | 380 | 0.00033 | 8.8e-7 | my $param = shift; |
1222 | my $val = shift; | |||
1223 | my $ctxt = shift; | |||
1224 | ||||
1225 | 4 | 1.5e-5 | 3.7e-6 | if ($ctxt->{$param}{list} && defined $ctxt->{$param}{minlist}) { |
1226 | # Range check number of list entries | |||
1227 | my $n = @$val; | |||
1228 | if((defined $ctxt->{$param}{minlist} && | |||
1229 | defined $ctxt->{$param}{maxlist} && | |||
1230 | ($n < $ctxt->{$param}{minlist} || | |||
1231 | ($ctxt->{$param}{maxlist}!~/^inf/i && | |||
1232 | $n > $ctxt->{$param}{maxlist}))) || | |||
1233 | (defined $ctxt->{$param}{minlist} && | |||
1234 | ! defined $ctxt->{$param}{maxlist} && | |||
1235 | $n != $ctxt->{$param}{minlist}) | |||
1236 | ) { | |||
1237 | die "$err: Number of list values ($n) for parameter ". | |||
1238 | "$param is outside the range ". | |||
1239 | (defined $ctxt->{$param}{minlist} && $ctxt->{$param}{maxlist} | |||
1240 | ? "$ctxt->{$param}{minlist} to $ctxt->{$param}{maxlist}" | |||
1241 | : "$ctxt->{$param}{minlist} to $ctxt->{$param}{minlist}" | |||
1242 | ).".\n"; | |||
1243 | } | |||
1244 | } | |||
1245 | ||||
1246 | return 1; | |||
1247 | } | |||
1248 | ||||
1249 | # spent 1.25ms within WISE::Params::check_map_keys which was called 76 times, avg 16µs/call:
# 76 times (1.25ms+0) by WISE::Params::Param_parse at line 948, avg 16µs/call | |||
1250 | 456 | 0.00034 | 7.5e-7 | my $param = shift; |
1251 | my $val = shift; | |||
1252 | my $from = shift; | |||
1253 | my $ctxt = shift; | |||
1254 | ||||
1255 | 28 | 0.00010 | 3.7e-6 | if(ref($val) && $ctxt->{$param}{mapkeys} && |
1256 | @{$ctxt->{$param}{mapkeys}}) { | |||
1257 | # A specific set of allowable mapping keys were | |||
1258 | # defined. Check that only those keys are used. | |||
1259 | my (%seen,@nope); | |||
1260 | @$val = grep { ! $seen{$_}++ } @$val if ref($val) !~ /hash/i; | |||
1261 | #print STDERR "--- $0: $param $from /$val/ /$ctxt->{$param}{type}/ ". | |||
1262 | # "/$ctxt->{$param}{origval}/ ". | |||
1263 | # (ref($val)!~/hash/i | |||
1264 | # ? join(",",@$val) | |||
1265 | # : join(",",map{"$_=>$val->{$_}"}keys%$val))."\n"; | |||
1266 | my @keys = ref($val) =~ /hash/i ? keys %$val : @$val; | |||
1267 | 2 | 8.0e-6 | 4.0e-6 | if($ctxt->{$param}{mapkeys}[0] =~ /^\@defaults$/i && $from==0) { |
1268 | # Get legal keys from defaults | |||
1269 | $ctxt->{$param}{mapkeys} = []; | |||
1270 | @{$ctxt->{$param}{mapkeys}} = @keys; | |||
1271 | #print STDERR "--- $0: $param /@keys/\n"; | |||
1272 | } | |||
1273 | my @names = map { lc($_) } @{$ctxt->{$param}{mapkeys}}; | |||
1274 | for my $k (@keys) { | |||
1275 | 66 | 5.7e-5 | 8.6e-7 | my $matched = 0; |
1276 | for my $name (@names) { | |||
1277 | 756 | 0.00054 | 7.1e-7 | if($name =~ s/^\s*-\+// || $name =~ s/^\s*\+-//) { |
1278 | # Convert to re for optional leading '+' or '-' | |||
1279 | $name = "/[-+]?$name/"; | |||
1280 | } | |||
1281 | my ($re) = $name=~m|^/(.*)/$|; | |||
1282 | $matched = 1 | |||
1283 | if defined $re ? lc($k)=~/^$re$/ : lc($k) eq $name; | |||
1284 | last if $matched; | |||
1285 | } | |||
1286 | push @nope, $k if ! $matched; | |||
1287 | } | |||
1288 | if(@nope) { | |||
1289 | die "$err: Value for parameter $param has illegal key(s) ". | |||
1290 | "'@nope'.\n"; | |||
1291 | } | |||
1292 | } | |||
1293 | ||||
1294 | return 1; | |||
1295 | } | |||
1296 | ||||
1297 | ||||
1298 | ||||
1299 | # spent 933µs within WISE::Params::assign_ref which was called 138 times, avg 7µs/call:
# 76 times (531µs+0) by WISE::Params::Param_parse at line 979, avg 7µs/call
# 33 times (216µs+0) by WISE::Params::Param_reassign at line 1501, avg 7µs/call
# 29 times (186µs+0) by WISE::Params::expand_tags at line 2542, avg 6µs/call | |||
1300 | 690 | 0.00058 | 8.4e-7 | my $ref = shift; |
1301 | my $val = shift; | |||
1302 | my $def = shift; | |||
1303 | my $parname= shift || ""; # For better error msgs | |||
1304 | ||||
1305 | return if ! $ref; | |||
1306 | ||||
1307 | if(! ref($ref) || ref($ref)!~/scalar|hash|array/i) { | |||
1308 | die "$err: Don't know how to assign to target ref ". | |||
1309 | "'$ref'/'".ref($ref)."' ($parname)\n"; | |||
1310 | } | |||
1311 | ||||
1312 | if(ref($val) =~ /hash|array/i) { | |||
1313 | my @val = (ref($val) eq "ARRAY" | |||
1314 | ? @$val | |||
1315 | : ref($val) eq "HASH" | |||
1316 | ? map { ($_,$val->{$_}) } sort keys %$val | |||
1317 | : () | |||
1318 | ); | |||
1319 | ||||
1320 | if((ref($ref) =~ /hash/i || $def->{itype} == 8) && (@val%2) > 0) { | |||
1321 | die "$err: Value for hash reference for parameter $parname ". | |||
1322 | "has odd number of elements.\n"; | |||
1323 | } | |||
1324 | if(ref($ref) =~ /array/i) { | |||
1325 | if($def->{type} eq 'name') { | |||
1326 | # Only want the hash values | |||
1327 | @$ref = map { $val[$_] } grep { ! $_%2 } 0..$#val; | |||
1328 | } else { | |||
1329 | @$ref = @val; | |||
1330 | } | |||
1331 | } elsif(ref($ref) =~ /hash/i) { | |||
1332 | %$ref = @val; | |||
1333 | } else { # Scalar ref | |||
1334 | my $new; | |||
1335 | if($def->{hash}) { | |||
1336 | $new = {}; | |||
1337 | %$new = @val; | |||
1338 | } else { | |||
1339 | $new = []; | |||
1340 | @$new = @val; | |||
1341 | } | |||
1342 | $$ref = $new; | |||
1343 | } | |||
1344 | } else { | |||
1345 | $$ref = $val; | |||
1346 | } | |||
1347 | ||||
1348 | return $ref; | |||
1349 | } | |||
1350 | ||||
1351 | sub Param_specified { | |||
1352 | my $alias = shift; | |||
1353 | my $opts = shift || {}; | |||
1354 | my $ctxt = $opts->{defs}; | |||
1355 | my $stealth = $opts->{stealth}; | |||
1356 | ||||
1357 | return if ! defined $alias; | |||
1358 | ||||
1359 | my $full = ( $ctxt->{_meta}{aliases}{lc $alias} || | |||
1360 | # Also look for stealth values | |||
1361 | ($stealth && exists $ctxt->{lc $alias} ? lc $alias : undef) ); | |||
1362 | ||||
1363 | if($full) { | |||
1364 | return $ctxt->{$full}{specified}; | |||
1365 | } else { | |||
1366 | return; | |||
1367 | } | |||
1368 | } | |||
1369 | ||||
1370 | sub Param_addval { | |||
1371 | my $full = shift; | |||
1372 | my $val = shift; | |||
1373 | my $opts = shift || {}; | |||
1374 | my $ctxt = $opts->{defs} or die "$err/addval: No defs.\n"; | |||
1375 | my $aliases = []; | |||
1376 | @$aliases = @{$opts->{aliases} || []}; # Make a copy | |||
1377 | my $tp = $opts->{type}; | |||
1378 | my $help = []; | |||
1379 | @$help = @{$opts->{help} || []}; | |||
1380 | my $stealth = $opts->{stealth}; | |||
1381 | ||||
1382 | $full = lc $full; | |||
1383 | ||||
1384 | die "$err/addval: Name '$full' already used" | |||
1385 | if exists $ctxt->{$full}; | |||
1386 | ||||
1387 | die "$err/addval: One of the aliases '@$aliases' of '$full' already used" | |||
1388 | if grep exists $ctxt->{_meta}{aliases}{$_}, @$aliases; | |||
1389 | ||||
1390 | unshift @$aliases,$full; | |||
1391 | ||||
1392 | push @{$ctxt->{_meta}{parnames}},$full; | |||
1393 | ||||
1394 | # Don't make aliases visible in stealth mode | |||
1395 | @{$ctxt->{_meta}{aliases}}{@$aliases} = ($full) x @$aliases | |||
1396 | if ! $stealth; | |||
1397 | ||||
1398 | my ($type,$itype,$size,$mapkeys,$otype); | |||
1399 | ||||
1400 | if($tp) { | |||
1401 | ($type,$itype,$size,$mapkeys,$otype) = WISE::Params::typeoftype($tp); | |||
1402 | } else { | |||
1403 | ($type,$itype,$size,$mapkeys,$otype) = ("",0,undef,undef,""); | |||
1404 | } | |||
1405 | ||||
1406 | my $ref; | |||
1407 | my $ishash = ref($val)=~/hash/i; | |||
1408 | ||||
1409 | $ref = {name => $full, nameorig => $full, | |||
1410 | aliases => $aliases, aliasesorig => $aliases, | |||
1411 | list => ref($val), otype => $otype, | |||
1412 | type => $type, itype => $itype, | |||
1413 | hash => $ishash, size => $size, | |||
1414 | mapkeys => $mapkeys, help => $help | |||
1415 | }; | |||
1416 | ||||
1417 | $ctxt->{$full} = $ref; | |||
1418 | ||||
1419 | my @vals = Param_reassign($full,$val,$opts); | |||
1420 | ||||
1421 | # Remove name from radar in stealth mode. | |||
1422 | # (Had to be added in above so reassign would work.) | |||
1423 | pop @{$ctxt->{_meta}{parnames}} if $stealth; | |||
1424 | ||||
1425 | return @vals; | |||
1426 | } | |||
1427 | ||||
1428 | # spent 1.41ms (1.19+216µs) within WISE::Params::Param_reassign which was called 33 times, avg 43µs/call:
# 33 times (1.19ms+216µs) by WISE::Params::check_pragmas at line 2033, avg 43µs/call | |||
1429 | 462 | 0.00036 | 7.8e-7 | my $opts = ref $_[-1] ? pop : {}; |
1430 | die "$err/reassign: opts not a hash ref" if ref($opts) ne 'HASH'; | |||
1431 | my $ctxt = $opts->{defs} || die "$err/reassign: No defs.\n"; | |||
1432 | my $default = $opts->{default}; # Only change if not specified | |||
1433 | my $missok = $opts->{missingok}; | |||
1434 | my $stealth = $opts->{stealth}; | |||
1435 | my $asgiven = $opts->{asgiven}; # Don't change specified/default status | |||
1436 | my $public = $opts->{public}; # Make private params public | |||
1437 | my $vals = $ctxt->{_meta}{vals}; | |||
1438 | my $refs = $ctxt->{_meta}{refs}; | |||
1439 | my ($alias,$newval,@newvals); | |||
1440 | ||||
1441 | die "$err/reassign: Uneven (non-hash) arg count.\n" if @_%2; | |||
1442 | ||||
1443 | while(@_) { | |||
1444 | ||||
1445 | 660 | 0.00080 | 1.2e-6 | $alias = shift; |
1446 | $newval= shift; | |||
1447 | ||||
1448 | die "$err/reassign: Undefined alias" | |||
1449 | if ! defined $alias && ! $missok; | |||
1450 | ||||
1451 | next if ! defined $alias; | |||
1452 | ||||
1453 | my $full = ( $ctxt->{_meta}{aliases}{lc $alias} || | |||
1454 | # Also look for stealth values | |||
1455 | ($stealth && exists $ctxt->{lc $alias} | |||
1456 | ? lc $alias : undef) ); | |||
1457 | ||||
1458 | die "$err/reassign: Alias '$alias' not found" | |||
1459 | if ! defined $full && ! $missok; | |||
1460 | ||||
1461 | next if ! defined $full; | |||
1462 | ||||
1463 | # Make no change if the param was specified explicitly and we | |||
1464 | # only want to change the default. | |||
1465 | next if $default && Param_specified($full,$opts); | |||
1466 | ||||
1467 | $ctxt->{$full} ||= {}; | |||
1468 | ||||
1469 | $ctxt->{$full}{need} = 0; # Since we have a value ... | |||
1470 | ||||
1471 | # If a scalar was presented to a list param, make it an array ref | |||
1472 | if(! ref($newval) && $ctxt->{$full}{list}) { | |||
1473 | $newval = [$newval]; | |||
1474 | } | |||
1475 | ||||
1476 | if(! $asgiven) { | |||
1477 | if(! $default) { | |||
1478 | $ctxt->{$full}{specified} = 1; | |||
1479 | $ctxt->{$full}{isdefault} = 0; | |||
1480 | } else { | |||
1481 | $ctxt->{$full}{specified} = 0; | |||
1482 | $ctxt->{$full}{isdefault} = 1; | |||
1483 | } | |||
1484 | } | |||
1485 | ||||
1486 | if($public) { $ctxt->{$full}{private} = 0; } | |||
1487 | ||||
1488 | my $ref = $refs->{$full}; | |||
1489 | ||||
1490 | print "params: Reassigning $full = '".(defined$newval?$newval:"undef"). | |||
1491 | "'.\n" | |||
1492 | if $opts->{verbose}; | |||
1493 | ||||
1494 | # Assign | |||
1495 | ||||
1496 | $ctxt->{$full}{val} = $newval; | |||
1497 | @{$vals}{@{$ctxt->{$full}{aliases}},@{$ctxt->{$full}{aliasesorig}}} = | |||
1498 | ($newval)x(@{$ctxt->{$full}{aliases}}+ | |||
1499 | @{$ctxt->{$full}{aliasesorig}}); | |||
1500 | ||||
1501 | assign_ref($ref, $newval, $ctxt->{$full}, $full); # spent 216µs making 33 calls to WISE::Params::assign_ref, avg 7µs/call | |||
1502 | ||||
1503 | # If treating as a default, make this the default value of record. | |||
1504 | if($default) { | |||
1505 | $ctxt->{$full}{default} = $newval; | |||
1506 | $ctxt->{$full}{defaultstr} = valstr($ctxt,$full,1); | |||
1507 | } | |||
1508 | ||||
1509 | push @newvals,$newval; | |||
1510 | } | |||
1511 | ||||
1512 | # Return the whole array (array context), or just the final value (scalar) | |||
1513 | return (@newvals); | |||
1514 | } | |||
1515 | ||||
1516 | sub Param_default { | |||
1517 | my $opts = ref $_[-1] ? pop : {}; | |||
1518 | return Param_reassign(@_,{%$opts,default => 1}); | |||
1519 | } | |||
1520 | ||||
1521 | # spent 51µs within WISE::Params::Param_get which was called 2 times, avg 25µs/call:
# 2 times (51µs+0) by WISE::Pars::get at line 110 of /wise/base/deliv/dev/lib/perl/WISE/Pars.pm, avg 25µs/call | |||
1522 | 16 | 2.0e-5 | 1.3e-6 | my $opts = ref $_[-1] ? pop : {}; |
1523 | die "$err/get: opts not a hash ref" if ref($opts) ne 'HASH'; | |||
1524 | my $ctxt = $opts->{defs} || die "$err/get: No defs.\n"; | |||
1525 | my $missok = $opts->{missingok}; | |||
1526 | my $stealth = $opts->{stealth}; | |||
1527 | my ($alias,$val,@vals); | |||
1528 | ||||
1529 | while(@_) { | |||
1530 | ||||
1531 | 14 | 1.7e-5 | 1.2e-6 | $alias = shift; |
1532 | ||||
1533 | die "$err/get: Undefined alias.\n" | |||
1534 | if ! defined $alias && ! $missok; | |||
1535 | ||||
1536 | next if ! defined $alias; | |||
1537 | ||||
1538 | my $full = ( $ctxt->{_meta}{aliases}{$alias} || | |||
1539 | # Also look for stealth values | |||
1540 | ($stealth && exists $ctxt->{$alias} ? $alias : undef )); | |||
1541 | ||||
1542 | die "$err/get: Alias '$alias' not found" | |||
1543 | if ! defined $full && ! $missok; | |||
1544 | ||||
1545 | $val = defined $full ? $ctxt->{$full}{val} : undef; | |||
1546 | ||||
1547 | push @vals,$val; | |||
1548 | } | |||
1549 | ||||
1550 | return wantarray ? @vals : $vals[-1] ; | |||
1551 | } | |||
1552 | ||||
1553 | # spent 13.4ms (10.3+3.09) within WISE::Params::paramsfromfile which was called
# once (10.3ms+3.09ms) by WISE::Params::Param_parse at line 717 | |||
1554 | 9 | 1.3e-5 | 1.4e-6 | my $skipok = shift; |
1555 | my $files = shift || []; | |||
1556 | my $ctxt = shift || die "$err/fromfile: No defs.\n"; | |||
1557 | my (@in,@add,$debug); | |||
1558 | ||||
1559 | $debug = $ctxt->{_meta}{debug} || 0; | |||
1560 | ||||
1561 | print "--- Processing @$files...\n" if $debug; | |||
1562 | for my $in (@$files) { | |||
1563 | 16 | 9.7e-5 | 6.1e-6 | if(! defined $in || $in eq "") { next; } |
1564 | 5 | 0.01006 | 0.00201 | if($in ne '-') { |
1565 | 2 | 2.0e-6 | 1.0e-6 | if($skipok && (! -e $in || -z $in)) { |
1566 | print "--- Skipping param file '$in'\n" if $debug; | |||
1567 | next; | |||
1568 | } | |||
1569 | open(IN,"<$in") or | |||
1570 | die "$err: Can't input from '$in': $!\n"; | |||
1571 | } else { | |||
1572 | open(IN,"<&STDIN") or | |||
1573 | die "$err: Can't input from STDIN: $!\n"; | |||
1574 | } | |||
1575 | 4 | 0.00010 | 2.5e-5 | if($in ne "-" || ! -t STDIN || ! -t STDOUT || ! -t STDERR) { |
1576 | print "--- Reading from $in\n" if $debug; | |||
1577 | chomp(@in = <IN>); # For file input or redirected stdin | |||
1578 | } else { | |||
1579 | chomp(@in = ¶msfromtty(\*IN,$ctxt)); # input from terminal | |||
1580 | } | |||
1581 | close(IN) or | |||
1582 | die "$err: Trouble reading (on close) from '$in': $!\n"; | |||
1583 | ||||
1584 | my @fixed = paramparselines($in,\@in,$ctxt->{_meta}{conditions}); # spent 1.18ms making 2 calls to WISE::Params::paramparselines, avg 591µs/call | |||
1585 | print "--- Fixed = //".join("//",@fixed)."//\n" if $debug; | |||
1586 | 6 | 7.8e-5 | 1.3e-5 | if(@fixed) { |
1587 | print "PARAM: === Parsing ".@fixed." param lines from $in ...\n" | |||
1588 | if $debug; | |||
1589 | push @PARAMS_FROM_FILES,($in,[@fixed]); | |||
1590 | push @add, Param_parse(\@fixed,{defs=>$ctxt,from=>3}); # spent 1.91ms making 2 calls to WISE::Params::Param_parse, avg 0/call, max recursion depth 1 | |||
1591 | } | |||
1592 | } | |||
1593 | ||||
1594 | print "--- from param file(s):\n---------\n". | |||
1595 | (@add?"@add":"none")."\n---------\n" | |||
1596 | if $debug; | |||
1597 | return @add; | |||
1598 | } | |||
1599 | ||||
1600 | # spent 1.18ms (747µs+435µs) within WISE::Params::paramparselines which was called 2 times, avg 591µs/call:
# 2 times (747µs+435µs) by WISE::Params::paramsfromfile at line 1584, avg 591µs/call | |||
1601 | 14 | 5.8e-5 | 4.1e-6 | my($infile,$in,$conditions) = @_; |
1602 | my($i,@in,$par,$val,@lines,%env,%cond); | |||
1603 | local $_; | |||
1604 | ||||
1605 | if($conditions) { | |||
1606 | $cond{lc $_} = $conditions->{$_} for keys %$conditions; | |||
1607 | } | |||
1608 | ||||
1609 | # Pre-filter to join continuation lines | |||
1610 | 1 | 4.6e-5 | 4.6e-5 | CONT: for (@$in) { |
1611 | 159 | 0.00014 | 8.6e-7 | chomp; |
1612 | if(/^\s+\S/) { # Continuation line | |||
1613 | if(defined $in[-1]) { | |||
1614 | s/^\s*//; | |||
1615 | $in[-1] =~ s/\s*$//; | |||
1616 | $in[-1] .= " ".$_; | |||
1617 | next CONT; | |||
1618 | } else { | |||
1619 | die "$err: Bad format reading from '$infile': ". | |||
1620 | "line continues from nowhere:\n$_\n"; | |||
1621 | } | |||
1622 | } | |||
1623 | push @in,$_; | |||
1624 | } | |||
1625 | ||||
1626 | LINE: for (@in) { | |||
1627 | 254 | 0.00061 | 2.4e-6 | if (($i=paruqindex($_,'#'))>=0) { # Comments # spent 435µs making 53 calls to WISE::Params::paruqindex, avg 8µs/call |
1628 | my $cmnt = substr($_,$i+1); | |||
1629 | substr($_,$i) = ""; # Remove comment for subsequent processing | |||
1630 | my ($envtxt) = $cmnt =~ /(\{[^}]*\})/; | |||
1631 | if($envtxt) { | |||
1632 | my $env = par_safe_eval("$envtxt"); | |||
1633 | die "$err: Error parsing configuration environment ". | |||
1634 | "'$env'.\n$@" | |||
1635 | if $@; | |||
1636 | %env = (%env,%$env); | |||
1637 | } | |||
1638 | } | |||
1639 | 24 | 1.4e-5 | 5.8e-7 | if(/^\s*$/) { next LINE; } # Blank line |
1640 | # Check conditions | |||
1641 | if(%cond && %env) { | |||
1642 | for my $k (keys %env) { | |||
1643 | #print "\t$k ...\n"; | |||
1644 | next if ! defined $env{$k}; | |||
1645 | next if ! defined $cond{lc $k}; | |||
1646 | if(! ref $cond{lc $k}) { | |||
1647 | next LINE if lc($env{$k}) ne lc($cond{lc $k}); | |||
1648 | } else { | |||
1649 | # Given value is an array ref. | |||
1650 | my @vals = grep { defined $_ && length $_ } @{$cond{lc $k}}; | |||
1651 | next if ! @vals; | |||
1652 | my @env; | |||
1653 | # The environment to check against may also be an array | |||
1654 | if(ref $env{$k}) { @env = @{$env{$k}}; } | |||
1655 | else { @env = ($env{$k}); } | |||
1656 | my $mchs = 0; | |||
1657 | for my $env (@env) { | |||
1658 | # A match to *any* array element is acceptable | |||
1659 | ++$mchs if grep( defined $_ && lc($env) eq lc($_), | |||
1660 | @vals ); | |||
1661 | } | |||
1662 | next LINE if ! $mchs; | |||
1663 | } | |||
1664 | } | |||
1665 | } | |||
1666 | ($par,$val) = m/^(-\S+)(?:\s+(\S.*))?$/; | |||
1667 | push @lines,$par; | |||
1668 | push @lines,$val if defined $val; | |||
1669 | } | |||
1670 | ||||
1671 | return @lines; | |||
1672 | } | |||
1673 | ||||
1674 | ||||
1675 | sub paramsfromtty { | |||
1676 | my $IN = shift; | |||
1677 | my $ctxt = shift || die "$err/tty: No defs.\n"; | |||
1678 | my ($ans,$nm,@in,$r,$def,$onoff,$val); | |||
1679 | my $iam = $ctxt->{_meta}{iam}; | |||
1680 | ||||
1681 | select +(select($IN), $|=1)[0]; | |||
1682 | ||||
1683 | print "\nEnter options for $iam:\n"; | |||
1684 | OUTER: while(1) { | |||
1685 | print "\nOption name ('?[12] [param]' for options, 'run' to run)? "; | |||
1686 | $ans = <$IN>; | |||
1687 | if(! defined $ans) { last OUTER; } | |||
1688 | chomp $ans; $ans = lc $ans; | |||
1689 | $ans =~ s/^\s*(.*?)\s*$/$1/; | |||
1690 | if($ans =~ /^$/) { next OUTER; } | |||
1691 | if($ans =~ /^run|exit|quit|go$/i) { last; } | |||
1692 | if($ans !~ /^-/) { print "'$ans' needs leading '-'.\n"; next OUTER; } | |||
1693 | $ans =~ s/^-//; | |||
1694 | if($ans =~ /^(?:\?|help)(\d)?(?:\s+?(-?\w+(?:,-?\w+)*))?$/i) { | |||
1695 | ¶mhelp($1||0,1,$2); next OUTER; } | |||
1696 | $nm = $ctxt->{_meta}{aliases}{$ans}; | |||
1697 | if(! $nm) { print "'$ans' not recognized.\n"; next OUTER; } | |||
1698 | $r = $ctxt->{$nm}; | |||
1699 | if($r->{type} eq 'switch') { | |||
1700 | print "Switch $nm set.\n"; | |||
1701 | push @in,"-$nm\n"; | |||
1702 | next OUTER; | |||
1703 | } | |||
1704 | $val = $ctxt->{$nm}{val}; | |||
1705 | $def = sprintf("%-s (%-s %-s)",$nm, | |||
1706 | ($r->{list} ? $r->{list}." " : "").$r->{type}, | |||
1707 | (defined $val && (! ref($val) || @$val) | |||
1708 | ? "= ".(! ref($val) ? $val : join(",",@$val)) | |||
1709 | : "") | |||
1710 | ); | |||
1711 | INNER: { | |||
1712 | print "Enter new value for $def: "; | |||
1713 | $ans = <$IN>; | |||
1714 | if (! defined $ans) { next OUTER; } | |||
1715 | chomp $ans; $ans =~ s/^\s*(.*?)\s*$/$1/; | |||
1716 | if ($ans =~ /^$/) { next OUTER; } | |||
1717 | if(! defined ¶mchecklist($nm,$ans,$ctxt)) { | |||
1718 | print "Illegal value for this type. Redo.\n"; | |||
1719 | redo INNER; | |||
1720 | } | |||
1721 | print "Setting $nm to value '$ans'.\n"; | |||
1722 | push @in,"-$nm\t$ans\n"; | |||
1723 | } | |||
1724 | } | |||
1725 | ||||
1726 | return @in; | |||
1727 | } | |||
1728 | ||||
1729 | ||||
1730 | # spent 6.39ms (1.81+4.58) within WISE::Params::paramchecklist which was called 76 times, avg 84µs/call:
# 76 times (1.81ms+4.58ms) by WISE::Params::Param_parse at line 883, avg 84µs/call | |||
1731 | 681 | 0.00076 | 1.1e-6 | my($parname,$val) = (shift,shift); |
1732 | my $ctxt = shift || die "$err/checklist: No defs.\n"; | |||
1733 | my $opts = shift || {}; | |||
1734 | my(@ary,$ary,@out,$perlish); | |||
1735 | ||||
1736 | my $debug = $ctxt->{_meta}{debug} =~ /chk/; | |||
1737 | print "\t\t\t\tPARAMS paramchecklist: $parname=/$val/, ". | |||
1738 | "list='$ctxt->{$parname}{list}'\n" | |||
1739 | if $debug; | |||
1740 | ||||
1741 | 109 | 0.00019 | 1.8e-6 | if($ctxt->{$parname}{list}) { |
1742 | ($ary,$perlish) = ¶mlist($parname,$val,$ctxt); # spent 717µs making 11 calls to WISE::Params::paramlist, avg 65µs/call | |||
1743 | if($debug) { | |||
1744 | eval "use WISE::Dumper; 1;" | |||
1745 | or die "$err: Can't load WISE::Dumper.\n$@"; | |||
1746 | print "\t\t\t\tPARAMS paramchecklist: ary=$ary\n"; | |||
1747 | print Dumper($ary); | |||
1748 | } | |||
1749 | return undef if ! defined $ary; | |||
1750 | @ary = @$ary; | |||
1751 | } else { | |||
1752 | @ary = ($val); | |||
1753 | } | |||
1754 | ||||
1755 | 149 | 0.00017 | 1.1e-6 | if($perlish) { |
1756 | # As a special case, if the parameter starts with [ or { and | |||
1757 | # ends with a matching bracket, parse it as Perl. | |||
1758 | $ctxt->{$parname}{perlish} = $perlish; | |||
1759 | @out = @ary; | |||
1760 | } else { | |||
1761 | # Normal param parsing rules | |||
1762 | 6 | 7.9e-5 | 1.3e-5 | if($ctxt->{$parname}{itype} == 8) { |
1763 | # If it's supposed to be a hash, try to parse it accordingly | |||
1764 | if(grep(/->|=>?/,@ary) != @ary) { | |||
1765 | warn "$err: Map parameter $parname value doesn't ". | |||
1766 | "look like a mapping\n"; | |||
1767 | return undef; | |||
1768 | } | |||
1769 | return paramlist2hash($parname,\@ary,$ctxt); # spent 2.39ms making 3 calls to WISE::Params::paramlist2hash, avg 798µs/call | |||
1770 | } | |||
1771 | for $val (@ary) { | |||
1772 | 258 | 0.00062 | 2.4e-6 | $val = ¶mcheck($parname,$val,$ctxt,$opts); # spent 1.47ms making 86 calls to WISE::Params::paramcheck, avg 17µs/call |
1773 | return undef if ! defined $val; | |||
1774 | push @out,($val ne 'undef'? $val : undef); | |||
1775 | } | |||
1776 | } | |||
1777 | ||||
1778 | return \@out; | |||
1779 | } | |||
1780 | ||||
1781 | sub paramlist { | |||
1782 | 288 | 0.00033 | 1.2e-6 | my $parname = shift; |
1783 | my $val = shift; | |||
1784 | my $ctxt = shift || die "$err/paramlist: No defs.\n"; | |||
1785 | my ($i,@ary,$tmp,$perlish); | |||
1786 | my $quoted = "\000"; | |||
1787 | my $sep = "\001"; | |||
1788 | local $_; | |||
1789 | ||||
1790 | if($val eq 'undef') { return []; } | |||
1791 | $ctxt = undef if ! $parname; # Special case; assume string processing | |||
1792 | # Blank-out quoted/escaped parts of the string using NULL as a replacement | |||
1793 | $tmp = parblankquoted($val,$quoted); # spent 130µs making 24 calls to WISE::Params::parblankquoted, avg 5µs/call | |||
1794 | 144 | 0.00041 | 2.8e-6 | if($perlok && ($tmp =~ /^\s*\{.*\}\s*$/ || $tmp =~ /^\s*\[.*\]\s*$/)) { |
1795 | # As a special case, if the parameter starts with [ or { and | |||
1796 | # ends with a matching bracket, parse it as Perl. | |||
1797 | my $parsed; | |||
1798 | { | |||
1799 | local $^W = 0; # Suppress warnings to aid compactness | |||
1800 | $parsed = par_safe_eval($val); | |||
1801 | } | |||
1802 | if($@) { | |||
1803 | warn "$err: Unable to parse Perl-ish value '$val':\n$@"; | |||
1804 | return; | |||
1805 | } | |||
1806 | if(! $parsed || ref($parsed) !~ /array|hash/i) { | |||
1807 | warn "$err: Non-array-like result from parsing of ". | |||
1808 | "Perl-ish value '$val' = $parsed ."; | |||
1809 | return; | |||
1810 | } | |||
1811 | @ary = ref($parsed) =~ /array/i ? @$parsed : %$parsed; | |||
1812 | $perlish = 1; | |||
1813 | } else { | |||
1814 | # Parse normally | |||
1815 | # Replace active (unescaped) commas with binary 1's | |||
1816 | 24 | 0.00034 | 1.4e-5 | if(! $ctxt || $ctxt->{$parname}{itype} != 8) { |
1817 | # Normal list. Split on commas. | |||
1818 | $tmp =~ s/,/$sep/g; | |||
1819 | } else { | |||
1820 | # Mapping. Split on a ',.*=' pattern (more or less) | |||
1821 | $tmp =~ s/,(?=\s*[-\w\d_@#$%&:.!]+\s*(?:->|=>?))/$sep/g; | |||
1822 | } | |||
1823 | # Replace the NULLs in $tmp with the original characters | |||
1824 | $i=0; | |||
1825 | while($tmp=~m|$quoted+|) { | |||
1826 | substr($tmp, $-[0], $+[0]-$-[0]) = substr($val, $-[0], $+[0]-$-[0]); | |||
1827 | } | |||
1828 | # Split on the embedded binary 1's | |||
1829 | @ary = split(/$sep/,$tmp); | |||
1830 | # Handle lists of integers and strings allowing for range operations | |||
1831 | # Remove bracketing parens | |||
1832 | if(@ary && defined $ary[0] && defined $ary[-1] && # spent 137µs making 24 calls to WISE::Params::parblankquoted, avg 6µs/call | |||
1833 | parblankquoted($ary[0], 'X') =~ /^\s*\(/ && | |||
1834 | parblankquoted($ary[-1],'X') =~ /\)\s*$/) { | |||
1835 | $ary[0] =~ s/^\s*\(//; | |||
1836 | $ary[-1] =~ s/\)\s*$//; | |||
1837 | } | |||
1838 | 13 | 0.00012 | 9.5e-6 | if (! $ctxt || $ctxt->{$parname}{type} eq 'char') { |
1839 | if(grep(/^[0-9a-zA-Z]..[0-9a-zA-Z]$/,@ary)) { # spent 484µs making 5 calls to WISE::Params::parexpandlist, avg 97µs/call | |||
1840 | @ary = &parexpandlist(\@ary,1,1); | |||
1841 | } | |||
1842 | } elsif($ctxt->{$parname}{type} =~ /u?integer/) { | |||
1843 | if($val =~ /^\(?([-+\d\s,]|\.\.+)+\)?$/) { | |||
1844 | @ary = &parexpandlist(\@ary,1,1); | |||
1845 | } elsif ($val =~ /\.\./) { # Range operator is malformed; error | |||
1846 | my $bad; | |||
1847 | ($bad = $val) =~ s/[-+\d\s,()]|\.\.+//g; | |||
1848 | $bad = join('',sort split("",$bad)); | |||
1849 | $bad =~ s/(.)\1+/$1/g; | |||
1850 | warn "$err: Expandable list '$val' has illegal char.s: ". | |||
1851 | "'$bad'\n"; | |||
1852 | return undef; | |||
1853 | } | |||
1854 | } | |||
1855 | } | |||
1856 | ||||
1857 | return wantarray ? (\@ary,$perlish) : \@ary; | |||
1858 | } | |||
1859 | ||||
1860 | # spent 2.39ms (839µs+1.55) within WISE::Params::paramlist2hash which was called 3 times, avg 798µs/call:
# 3 times (839µs+1.55ms) by WISE::Params::paramchecklist at line 1769, avg 798µs/call | |||
1861 | 21 | 5.2e-5 | 2.5e-6 | my $parname = shift; |
1862 | my $list = shift; | |||
1863 | my $ctxt = shift || die "$err/list2hash: No defs.\n"; | |||
1864 | my @newlist; | |||
1865 | ||||
1866 | for (@$list) { | |||
1867 | 231 | 0.00081 | 3.5e-6 | if(! /^\s*(\S+)\s*(?:->|=>?)\s*(\S.*?)\s*$/) { |
1868 | warn "$err: Hashizing list failed on item '$_'\n"; | |||
1869 | return; | |||
1870 | } | |||
1871 | my ($k,$v) = ($1,$2); | |||
1872 | # Check for embedded, unquoted/unescaped, non-terminal commas. | |||
1873 | # The non-terminal requirement avoids interpreting 'x=y,' as a | |||
1874 | # single-element array | |||
1875 | my $commaix; | |||
1876 | if(($commaix = paruqindex($v,",")) >= 0 && # spent 1.16ms making 13 calls to WISE::Params::paramlist, avg 89µs/call
# spent 393µs making 33 calls to WISE::Params::paruqindex, avg 12µs/call | |||
1877 | $commaix < length($v)-1) { | |||
1878 | # There're embedded commas, so make a list of it | |||
1879 | $v=paramlist('',$v,$ctxt); # (Null parname => handle as str list) | |||
1880 | } | |||
1881 | defined $_ and $_ eq 'undef' and $_ = undef for (ref($v) ? (@$v) : ($v)); | |||
1882 | 1 | 9.5e-5 | 9.5e-5 | push @newlist,$k,$v; |
1883 | } | |||
1884 | ||||
1885 | my %ashash = @newlist; | |||
1886 | ||||
1887 | return \%ashash; | |||
1888 | } | |||
1889 | ||||
1890 | # spent 1.47ms within WISE::Params::paramcheck which was called 86 times, avg 17µs/call:
# 86 times (1.47ms+0) by WISE::Params::paramchecklist at line 1772, avg 17µs/call | |||
1891 | 939 | 0.00106 | 1.1e-6 | my ($parname,$val) = (shift,shift); |
1892 | my $ctxt = shift || die "$err/paramcheck: No defs.\n"; | |||
1893 | my $opts = shift || {}; | |||
1894 | ||||
1895 | # No need to type check switches if no val is attached | |||
1896 | if($ctxt->{$parname}{type} eq 'switch' && | |||
1897 | ! defined $val) { return 1; } | |||
1898 | ||||
1899 | if(defined $val && $val eq 'undef') { return $val; } | |||
1900 | ||||
1901 | if(! defined $val) { return undef; } | |||
1902 | ||||
1903 | my $numeric; | |||
1904 | my $string; | |||
1905 | 89 | 0.00017 | 2.0e-6 | if($ctxt->{$parname}{type} =~ /integer/) { # int |
1906 | if($val =~ /^\s*[-+]?\d+\s*$/) { # decimal | |||
1907 | $val = $val + 0; | |||
1908 | } elsif($val =~ /^\s*0[xX][0-9a-fA-F]+\s*$/ || # hex | |||
1909 | $val =~ /^\s*0[oO][0-7]+\s*$/) { # octal | |||
1910 | $val =~ s/[oO]//; | |||
1911 | $val = oct($val); | |||
1912 | } elsif($val =~ /^\s*0[bB][01]+\s*$/) { # binary | |||
1913 | $val =~ s/0[bB]([01]+)/0 x (32-length($1)).$1/e; | |||
1914 | $val = unpack("N",pack("B*",$val)); | |||
1915 | } else { | |||
1916 | warn "$err: Integer parameter '$parname' ". | |||
1917 | "has illegal value '$val'.\n"; | |||
1918 | return undef; | |||
1919 | } | |||
1920 | $numeric = 1; | |||
1921 | } elsif($ctxt->{$parname}{type} eq 'real' || | |||
1922 | $ctxt->{$parname}{type} eq 'double') { # real | |||
1923 | if($val !~ /$FPre/) { | |||
1924 | warn "$err: Real parameter '$parname' ". | |||
1925 | "has illegal value '$val'.\n"; | |||
1926 | return undef; | |||
1927 | } | |||
1928 | { | |||
1929 | 2 | 2.0e-5 | 1.0e-5 | local $^W = 0; |
1930 | $val = $val + 0.0; | |||
1931 | } | |||
1932 | $numeric = 1; | |||
1933 | } elsif($ctxt->{$parname}{type} eq 'boolean' or | |||
1934 | $ctxt->{$parname}{type} eq 'switch') { # bools | |||
1935 | if(! defined $bools{$val}) { | |||
1936 | warn "$err: Boolean parameter '$parname' ". | |||
1937 | "has illegal value '$val'.\n"; | |||
1938 | return undef; | |||
1939 | } | |||
1940 | $val = $bools{$val}; | |||
1941 | } elsif($ctxt->{$parname}{type} eq 'file') { # file/dir name | |||
1942 | $val =~ s%/+$%%; # No trailing slashies | |||
1943 | $val =~ s%/+%/%; # No doubled slashies | |||
1944 | } elsif($ctxt->{$parname}{type} eq 'string' || | |||
1945 | $ctxt->{$parname}{type} eq 'char') { # string | |||
1946 | # nothing to do | |||
1947 | $string = 1; | |||
1948 | } elsif($ctxt->{$parname}{type} eq 'name') { | |||
1949 | # nothing to do | |||
1950 | } | |||
1951 | # we're done! $val contains the string. | |||
1952 | ||||
1953 | # Optionally check numeric ranges and string lengths | |||
1954 | 5 | 7.0e-6 | 1.4e-6 | if(($numeric || $string) && defined $ctxt->{$parname}{minval}) { |
1955 | my $min = $ctxt->{$parname}{minval}; | |||
1956 | my $max = $ctxt->{$parname}{maxval}; | |||
1957 | my $testval = $val; | |||
1958 | $testval = length($val//'') if $string; | |||
1959 | if(($min !~ /-inf/ && $testval < $min) || | |||
1960 | ($max !~ /inf/ && $testval > $max)) { | |||
1961 | warn "$err: Numeric parameter '$parname' value '$val' ". | |||
1962 | "is outside specified range $min..$max .\n"; | |||
1963 | return undef; | |||
1964 | } | |||
1965 | } | |||
1966 | ||||
1967 | return $val; | |||
1968 | ||||
1969 | } | |||
1970 | ||||
1971 | # spent 3.88ms (2.47+1.41) within WISE::Params::check_pragmas which was called
# once (2.47ms+1.41ms) by WISE::Params::Params at line 264 | |||
1972 | 3 | 5.0e-6 | 1.7e-6 | my $ctxt = shift; |
1973 | my $opts = shift || {}; | |||
1974 | ||||
1975 | for my $parname (@{$ctxt->{_meta}{parnames}}) { | |||
1976 | 319 | 0.00088 | 2.8e-6 | next if $ctxt->{$parname}{type} !~ /file/i; |
1977 | next if ! defined $ctxt->{$parname}{val}; | |||
1978 | # Get value(s) | |||
1979 | my @vals = ($ctxt->{$parname}{list} | |||
1980 | ? @{$ctxt->{$parname}{val}} | |||
1981 | : $ctxt->{$parname}{val} | |||
1982 | ); | |||
1983 | ||||
1984 | # Clean up | |||
1985 | for my $val (@vals) { | |||
1986 | 33 | 7.1e-5 | 2.2e-6 | $val =~ s|//+|/|g; |
1987 | } | |||
1988 | ||||
1989 | my @pragmas = @{$ctxt->{$parname}{file_pragmas} || []}; | |||
1990 | ||||
1991 | 6 | 7.0e-6 | 1.2e-6 | if(@pragmas) { |
1992 | # Expand embedded globs for list params | |||
1993 | if(grep(lc($_) eq 'glob', @pragmas) && $ctxt->{$parname}{list}) { | |||
1994 | my @expanded; | |||
1995 | for my $val (@vals) { | |||
1996 | # The eval is so File::Glob won't be loaded until needed | |||
1997 | 3 | 0.01015 | 0.00338 | my @new = glob($val); |
1998 | push @expanded,@new; | |||
1999 | } | |||
2000 | @vals = @expanded; | |||
2001 | } | |||
2002 | if(grep(lc($_) eq 'resolve', @pragmas)) { | |||
2003 | # Won't work right if -run_dir is set and the user | |||
2004 | # is doing this through Env.pm | |||
2005 | require WISE::UtilsLight; | |||
2006 | for my $val (@vals) { | |||
2007 | if(! $ctxt->{run_dir} || ! $ctxt->{run_dir}{val} || | |||
2008 | $val =~ m|^/|) { | |||
2009 | $val = WISE::UtilsLight::normalizepath( | |||
2010 | $val, | |||
2011 | {resolve=>1,notrail=>1} | |||
2012 | ); | |||
2013 | } | |||
2014 | } | |||
2015 | } | |||
2016 | # Check to see if the file is present (if not tags extant) | |||
2017 | 2 | 5.0e-6 | 2.5e-6 | if(grep {lc($_) eq 'in'} @pragmas) { |
2018 | for my $val (@vals) { | |||
2019 | 2 | 0.00159 | 0.00079 | if($val && $val !~ /%[^%]+%/ && |
2020 | ! -e $val && ! -e "$val.gz") { | |||
2021 | die "$err: File parameter '$parname' value '$val' ". | |||
2022 | "does not exist but is tagged as input.\n"; | |||
2023 | } | |||
2024 | } | |||
2025 | } | |||
2026 | } # if @pragmas | |||
2027 | ||||
2028 | # Write back new value | |||
2029 | my $val = ($ctxt->{$parname}{list} | |||
2030 | ? \@vals | |||
2031 | : $vals[0] | |||
2032 | ); | |||
2033 | Param_reassign($parname,$val,{%$opts,asgiven=>1,defs=>$ctxt}); # spent 1.41ms making 33 calls to WISE::Params::Param_reassign, avg 43µs/call | |||
2034 | ||||
2035 | } # for parnames | |||
2036 | } | |||
2037 | ||||
2038 | # spent 198µs within WISE::Params::check_required which was called
# once (198µs+0) by WISE::Params::Params at line 259 | |||
2039 | 5 | 9.0e-6 | 1.8e-6 | my $ctxt = shift || die "$err/checkreq: No defs.\n"; |
2040 | my $opts = shift || {}; | |||
2041 | my $debug = $ctxt->{_meta}{debug} || 0; | |||
2042 | local $_; | |||
2043 | ||||
2044 | for my $parname (@{$ctxt->{_meta}{parnames}}) { | |||
2045 | 172 | 0.00018 | 1.0e-6 | print "\t\t\tPARAM: $parname required = ". |
2046 | "/".($ctxt->{$parname}{required}//'undef')."/\n" | |||
2047 | if $debug; | |||
2048 | if($ctxt->{$parname}{need}) { | |||
2049 | die "$err: Parameter '$ctxt->{$parname}{name}' ". | |||
2050 | "required value not given.\n" | |||
2051 | } | |||
2052 | } | |||
2053 | } | |||
2054 | ||||
2055 | # spent 1.64ms within WISE::Params::typeoftype which was called 86 times, avg 19µs/call:
# 86 times (1.64ms+0) by WISE::Params::parsedefs at line 551, avg 19µs/call | |||
2056 | 516 | 0.00112 | 2.2e-6 | my $type = shift; |
2057 | my ($size,$mapkeys,$itype,$otype); | |||
2058 | local $_; | |||
2059 | ||||
2060 | $_ = $otype = $type; | |||
2061 | 257 | 0.00022 | 8.5e-7 | if (/^i(?:nt(?:eger)?)?/ix) { $type = "integer"; $itype = 1;} |
2062 | elsif(/^r(?:eal)? | float$/ix) { $type = "real"; $itype = 2; } | |||
2063 | elsif(/^d(?:oub(?:le)?)? | dbl$/ix) { $type = "double"; $itype = 2; } | |||
2064 | elsif(/^b(?:ool(?:ean)?)?$/ix) { $type = "boolean"; $itype = 4; } | |||
2065 | elsif(/^sw(?:itch)?$/ix) { $type = "switch"; $itype = 5; } | |||
2066 | elsif(/^s(?:tr(?:ing)?)?(?:\[(\d+)\])?$/ix) | |||
2067 | { $type = "string"; $itype = 6; | |||
2068 | $size = $1||undef; } | |||
2069 | elsif(/^c(?:h(?:ar(?:acter)?)?)?(?:\[(\d+)\])?$/ix) | |||
2070 | { $type = "char"; $itype = 6; | |||
2071 | $size = $1||undef; } | |||
2072 | elsif(/^f(?:ile)?(?:\[(\d+)\])?$/ix) | |||
2073 | { $type = "file"; $itype = 7; | |||
2074 | $size = $1||undef; } | |||
2075 | elsif(/^(n(?:ame[sd]?)?|map(?:ping)?|hash)\s*(?:\((.*?)\))?$/ix) { | |||
2076 | my $tp = $1; | |||
2077 | my $names = $2; | |||
2078 | 8 | 3.9e-5 | 4.9e-6 | if(defined $names) { |
2079 | $mapkeys = []; | |||
2080 | @$mapkeys = split(/\s*,+\s*/,$names); | |||
2081 | } else { | |||
2082 | $mapkeys = undef; | |||
2083 | } | |||
2084 | $itype = $tp =~ /name/i ? 9 : 8; | |||
2085 | $type = $tp =~ /name/i ? 'name' : 'map'; | |||
2086 | } | |||
2087 | else { $type = ""; $itype = 0; } | |||
2088 | ||||
2089 | return wantarray ? ($type,$itype,$size,$mapkeys,$otype) : $type; | |||
2090 | ||||
2091 | } | |||
2092 | ||||
2093 | # Utility function to resolve -steps and -step_macros params to a hash | |||
2094 | ||||
2095 | # spent 707µs (603+104) within WISE::Params::steps_resolve which was called
# once (603µs+104µs) at line 305 of /wise/base/deliv/dev/bin/getfix | |||
2096 | 13 | 0.00030 | 2.3e-5 | my $steps = shift; |
2097 | my $macros = shift || {}; | |||
2098 | my $opts = shift || {}; | |||
2099 | my $iam = $opts->{iam} || parwhoiam(); # spent 104µs making 1 call to WISE::Params::parwhoiam | |||
2100 | my $err = "*** $iam/STEPS"; | |||
2101 | 1 | 4.0e-6 | 4.0e-6 | my @steps = @{$steps |
2102 | ? (ref($steps) | |||
2103 | ? (ref($steps)=~/hash/i ? [keys %$steps] : $steps) | |||
2104 | : [$steps]) | |||
2105 | : [] }; | |||
2106 | ||||
2107 | my %do; | |||
2108 | ||||
2109 | 129 | 0.00011 | 8.6e-7 | my %all = (map { (lc($_)=>1) } grep {! /^[-+]?@/} map { (ref($_)?@$_:$_) } |
2110 | values %$macros); | |||
2111 | ||||
2112 | my @order; | |||
2113 | ||||
2114 | # Examine each job step specification | |||
2115 | for my $step_spec (@steps) { | |||
2116 | 56 | 7.4e-5 | 1.3e-6 | my ($addsub,$step) = $step_spec =~ /^([-+]?)(\S+)$/; |
2117 | die "$err: Job step spec '$step_spec' not recognized.\n" | |||
2118 | if ! $step; | |||
2119 | $addsub ||= ""; | |||
2120 | my @sub_steps = (lc $step); | |||
2121 | my $loop = 0; | |||
2122 | # Iteratively expand macros | |||
2123 | 8 | 6.0e-6 | 7.5e-7 | while(grep {/\@/} @sub_steps) { |
2124 | 1 | 1.0e-5 | 1.0e-5 | die "$err: Possible loop in job step resolution.\n" |
2125 | if ++$loop > 100; | |||
2126 | for my $i (grep { $sub_steps[$_]=~/^[-+]?\@/ } 0..$#sub_steps) { | |||
2127 | my ($macro_addsub,$macro) = $sub_steps[$i] =~ /^([-+]?)(\S+)$/; | |||
2128 | $macro_addsub ||= ""; | |||
2129 | $macro = lc $macro; | |||
2130 | die "$err: Unknown macro '$macro'.\n" | |||
2131 | if ! $macros->{$macro}; | |||
2132 | splice @sub_steps,$i,1,(map {s/^--//; s/^(-\+|\+-)/-/; | |||
2133 | $_; } | |||
2134 | map {$macro_addsub.lc($_)} | |||
2135 | (ref($macros->{$macro}) | |||
2136 | ? @{$macros->{$macro}} | |||
2137 | : $macros->{$macro}) ); | |||
2138 | last; | |||
2139 | } | |||
2140 | } | |||
2141 | for my $sub_step_spec (@sub_steps) { | |||
2142 | 56 | 8.1e-5 | 1.4e-6 | my ($sub_addsub,$sub_step) = $sub_step_spec =~ /^([-+]?)(\S+)$/; |
2143 | die "$err: Job step '$sub_step' not recognized.\n" | |||
2144 | if %all && ! $all{$sub_step}; | |||
2145 | $sub_addsub ||= ""; | |||
2146 | my $combined_addsub = $addsub.$sub_addsub; | |||
2147 | s/^--//, s/^(-\+|\+-)/-/ for $combined_addsub; | |||
2148 | 16 | 2.2e-5 | 1.4e-6 | if($combined_addsub eq '-') { |
2149 | delete $do{$sub_step} if $do{$sub_step}; | |||
2150 | } else { | |||
2151 | $do{$sub_step} = 1; | |||
2152 | push @order, $sub_step; | |||
2153 | } | |||
2154 | } | |||
2155 | } | |||
2156 | ||||
2157 | # Restore order | |||
2158 | my %seen; | |||
2159 | my @do = (map { ($_=>$do{$_}) } | |||
2160 | grep { ! $seen{$_}++ } | |||
2161 | grep { $do{$_} } | |||
2162 | @order); | |||
2163 | ||||
2164 | return wantarray ? @do : \%do; | |||
2165 | } | |||
2166 | ||||
2167 | # Produce help output or a help string. | |||
2168 | # | |||
2169 | # If $param is a comma separated list of valid param names or aliases, | |||
2170 | # o/p help for only those param.s. Otherwise, dump all help. | |||
2171 | # If $opts->{current} is set, give current values instead of using defaults. | |||
2172 | # | |||
2173 | # This code is a mess, mainly because POD output was shoehorned in. It badly | |||
2174 | # needs a rewrite. The pod2html generator is fairly picky in the sort of POD | |||
2175 | # written out if a standard-looking manpage-type page is to result. That's | |||
2176 | # why there's alot of funny stuff. Be careful to test the results of | |||
2177 | # xxx -help2 | perl -MWISE -e '$p=WISE::Pars->new([<STDIN>],{parseonly=>1});' | |||
2178 | # -e '$p->help("pod")' \ | |||
2179 | # | pod2html > xxx.html | |||
2180 | # pretty carefully as well as the results of just 'xxx -help' and 'xxx -help2'. | |||
2181 | sub Param_help { | |||
2182 | my ($help,$opts,$param) = (shift||0,shift,shift); | |||
2183 | my ($ctxt,%param,$curr,$op,$fileh,$opt,$pod,$iam,$synop,$descrip); | |||
2184 | my $max_spec = defined $opts->{maxspec} ? $opts->{maxspec} : 75; | |||
2185 | my $debug; | |||
2186 | my $line = ""; | |||
2187 | ||||
2188 | $help =~ s/^_|_$//g; | |||
2189 | $help = {brief=>0,long=>1,full=>2,pod=>3}->{$help} if $help !~ /^\d$/; | |||
2190 | $help ||= 0; | |||
2191 | $help = 0 if $help < 0 || $help > 3; | |||
2192 | ||||
2193 | if(! ref $opts) { $opts = { current=>$opts }; } | |||
2194 | $curr = $opts->{current}; | |||
2195 | $op = ! $opts->{noop}; | |||
2196 | $fileh = $opts->{fileh} || \*STDOUT; | |||
2197 | $param ||= $opts->{param}; | |||
2198 | $pod = $opts->{pod} || ($help==3); # Print out help in pod format | |||
2199 | $help = $pod ? 3 : $help; | |||
2200 | $ctxt = $opts->{defs} || die "$err/help: No defs.\n"; | |||
2201 | $debug = $opts->{debug} || $ctxt->{_meta}{debug}; | |||
2202 | ||||
2203 | print "\t\t\tPARAM: Help ctxt = $ctxt\n" if $debug; | |||
2204 | ||||
2205 | my $lead = $pod ? "" : "# "; | |||
2206 | # =over and =back must be matched (paired) throughout. | |||
2207 | my $over = $pod ? "\n=over 4\n\n" : ""; | |||
2208 | my $back = $pod ? "\n=back\n\n" : ""; | |||
2209 | my $item = $pod ? "\n=item " : ""; | |||
2210 | my $para = $pod ? "\n" : ""; | |||
2211 | my $indent = $pod ? " " : ""; | |||
2212 | my $sep = $pod ? "- " : ""; | |||
2213 | ||||
2214 | if(! defined $param || $param eq "") { | |||
2215 | # All parameters being printed. Check for synopsis and description | |||
2216 | my $genhelp = $ctxt->{_meta}{help} || []; | |||
2217 | if(@{$genhelp} || $pod) { | |||
2218 | # (If pod-izing, we always need to have a name - synopsis, | |||
2219 | # whether there is one or not) | |||
2220 | # A one line synopsis | |||
2221 | $synop = "$lead".(@{$genhelp}?$genhelp->[0]:"(Missing-synopsis)"); | |||
2222 | $descrip= ($help > 1 && @{$genhelp} > 1 | |||
2223 | ? "$lead$indent".join("\n$lead$indent", | |||
2224 | @{$genhelp}[1..$#$genhelp]). | |||
2225 | "\n" | |||
2226 | : "" | |||
2227 | ); | |||
2228 | ($iam = $ctxt->{_meta}{iam}||"(Missing-name)") | |||
2229 | =~ s/(\s+|::).*//; | |||
2230 | $iam =~ s/perl-e/(Missing-name)/; | |||
2231 | # Pod::Html is picky; it looks for | |||
2232 | # /^=head1\s*NAME\b(\n\n)?(\S+\s+-+.*\S)/m | |||
2233 | # I.e. more or less "=head1 NAME something - something" | |||
2234 | $line .= ($pod?"=head1 NAME $iam\n\n$indent$iam $sep ". | |||
2235 | "$synop\n\n$over$back" | |||
2236 | :"${lead}Command => $iam\n\n$synop\n"). | |||
2237 | ($pod?"=head1 DESCRIPTION\n\n":""). | |||
2238 | "$over$descrip$back\n"; | |||
2239 | } else { | |||
2240 | $line .= "\n"; | |||
2241 | } | |||
2242 | $param = undef; | |||
2243 | } else { # $param is defined and specifies which parameters need help | |||
2244 | for (split /,/,$param) { | |||
2245 | s/^\s*-?(\w+)\s*$/$1/; | |||
2246 | my $full = $ctxt->{_meta}{aliases}{$_}; | |||
2247 | if(! defined $full) { | |||
2248 | warn "$err: Parameter '$_' not defined.\n"; | |||
2249 | return undef; | |||
2250 | } | |||
2251 | $param{$full} = 1; | |||
2252 | } | |||
2253 | $param = \%param; | |||
2254 | } | |||
2255 | ||||
2256 | $line .= "=head1 ARGUMENTS\n\n$over$back" | |||
2257 | if $pod && @{$ctxt->{_meta}{parnames}}; | |||
2258 | ||||
2259 | # Precompute some values so we can check for max. lengths | |||
2260 | my (%aliases,%specs); | |||
2261 | my ($alias_len,$spec_len) = (24,16); | |||
2262 | if(! $pod) { | |||
2263 | for (@{$ctxt->{_meta}{parnames}}) { | |||
2264 | next if defined $param && ! defined $param->{$_}; | |||
2265 | my $r = $ctxt->{$_}; | |||
2266 | my $mapkeys = $r->{mapkeys}; | |||
2267 | $mapkeys = $mapkeys && @$mapkeys | |||
2268 | ? " (".join(",",@$mapkeys).")" | |||
2269 | : ""; | |||
2270 | my $list = $r->{list} ? "list of " : ""; | |||
2271 | my $nlist = (defined $r->{minlist} | |||
2272 | ? ("$r->{minlist}".(defined $r->{maxlist} | |||
2273 | ? " to $r->{maxlist}" | |||
2274 | : "") | |||
2275 | )." " | |||
2276 | : "" | |||
2277 | ); | |||
2278 | my $range = (defined $r->{minval} | |||
2279 | ? " ($r->{minval} to $r->{maxval})" | |||
2280 | : ""); | |||
2281 | my $fpragmas = ($r->{type} eq 'file' && $r->{file_pragmas} && | |||
2282 | @{$r->{file_pragmas}} | |||
2283 | ? "(".join(",",@{$r->{file_pragmas}}).")" | |||
2284 | : ""); | |||
2285 | my $private = $r->{private} | |||
2286 | ? $r->{private} eq "1" | |||
2287 | ? " private" | |||
2288 | : " private($r->{private})" | |||
2289 | : ""; | |||
2290 | my $quiet = $r->{quiet} | |||
2291 | ? $r->{quiet} eq "1" | |||
2292 | ? " quiet" | |||
2293 | : " quiet($r->{quiet})" | |||
2294 | : ""; | |||
2295 | $aliases{$_} = join(",",@{$r->{aliasesorig}}); | |||
2296 | $specs{$_} = $list.$nlist.$r->{type}.$fpragmas.$mapkeys.$range. | |||
2297 | $private.$quiet; | |||
2298 | if(! $max_spec || length($aliases{$_}.$specs{$_}) <= $max_spec) { | |||
2299 | if(length($aliases{$_}) > $alias_len) { | |||
2300 | $alias_len = length($aliases{$_}); | |||
2301 | } | |||
2302 | if(length($specs{$_}) > $spec_len) { | |||
2303 | $spec_len = length($specs{$_}); | |||
2304 | } | |||
2305 | } | |||
2306 | } | |||
2307 | } | |||
2308 | ||||
2309 | for (@{$ctxt->{_meta}{parnames}}) { | |||
2310 | next if defined $param && ! defined $param->{$_}; | |||
2311 | my $r = $ctxt->{$_}; | |||
2312 | next if $r->{quiet} && $help < 2; | |||
2313 | my $val = $r->{val}; | |||
2314 | print "\t\t\tPARAM: Data for $_ = ". | |||
2315 | "/$r->{type}/$val/$r->{required}/\n" if $debug; | |||
2316 | my @val = ref($val) eq "ARRAY" ? @$val | |||
2317 | : ref($val) eq "HASH" ? %$val : (); | |||
2318 | my ($op,$cl,$asep) = $r->{hash} ? ("(",")",",") : ("(",")",","); | |||
2319 | if($r->{hash}) { | |||
2320 | my @tmp = @val; | |||
2321 | @val = (); | |||
2322 | while(@tmp) { | |||
2323 | my $k = shift(@tmp); | |||
2324 | my $v = shift(@tmp); | |||
2325 | $v //= 'undef'; | |||
2326 | push @val,"$k=$v"; | |||
2327 | } | |||
2328 | } | |||
2329 | my $def; | |||
2330 | if($curr) { | |||
2331 | # Use a value currently defined rather than just the default | |||
2332 | $def = ! defined $val ? "undef" | |||
2333 | : ! ref($val) ? $val | |||
2334 | : ! @val ? "$op$cl" | |||
2335 | : $op.join($asep,map {defined$_?$_:'undef'} @val).$cl; | |||
2336 | } else { | |||
2337 | $def .= $r->{required} ? "\$required " : ""; | |||
2338 | $def .= $r->{unnamed} ? "\$unnamed " : ""; | |||
2339 | $def .= $r->{intdefault} ? "\$default " : ""; | |||
2340 | $def .= defined $r->{default} && | |||
2341 | $r->{type} ne 'switch' ? valstr($ctxt,$_,1) : ""; | |||
2342 | } | |||
2343 | $def = defined $def && $def ne "" ? ($pod?$def:"= $def") : ""; | |||
2344 | ||||
2345 | my $line2 = ""; | |||
2346 | my $perhelp = $r->{help} || []; | |||
2347 | ||||
2348 | if(! $pod) { | |||
2349 | $line2 .= sprintf("%-${alias_len}s: %-${spec_len}s %-s ", | |||
2350 | $aliases{$_}, $specs{$_}, $def); | |||
2351 | $line2 =~ s/\s*$//; | |||
2352 | } else { | |||
2353 | # Is there a better way to get vertical space? | |||
2354 | $line2 .= "=for html <br><br>\n <p></p>\n\n"; | |||
2355 | $line2 .= "$over$item\nE<12>\n\n$back"; | |||
2356 | # Argument header. See comment below. | |||
2357 | $line2 .= "\n\n=head2 Parameter $r->{aliasesorig}[0] "; | |||
2358 | } | |||
2359 | ||||
2360 | if($help>0) { | |||
2361 | $line2 .= " " x (61-length($line2))." $lead".$perhelp->[0] | |||
2362 | if $help==1 && @$perhelp; | |||
2363 | if($pod) { | |||
2364 | # As for the NAME field, to be indexed properly the | |||
2365 | # =head2 (begun above) needs to look like | |||
2366 | # '=head2 something - something". | |||
2367 | $line2 .= "$sep ".(@$perhelp?$perhelp->[0] | |||
2368 | :"(Missing-description")."\n\n"; | |||
2369 | $line2 .= "$over"; | |||
2370 | $line2 .= "${lead}${item}B<Aliases>S< >- ". | |||
2371 | ($#{$r->{aliasesorig}} > 0 ? | |||
2372 | join(",", | |||
2373 | @{$r->{aliasesorig}}[1..$#{$r->{aliasesorig}}]) | |||
2374 | : "none"). | |||
2375 | "\n\n"; | |||
2376 | $line2 .= "${lead}${item}B<Type>S< > - ". | |||
2377 | ($r->{list}||"").$r->{type}. | |||
2378 | "\n\n"; | |||
2379 | $line2 .= "${lead}${item}B<Default>S< >- $def\n\n" | |||
2380 | if defined $def; | |||
2381 | $line2 .= "$back"; | |||
2382 | } | |||
2383 | if(@$perhelp) { | |||
2384 | # Is there a better way to get vertical space? | |||
2385 | $line2 .= "$over$item\nE<12>\n\n$back" if $pod; | |||
2386 | $line2 .= "\n$lead$indent". | |||
2387 | join("\n$lead$indent", | |||
2388 | @{$perhelp}[($pod?1:0)..$#$perhelp])."\n" | |||
2389 | if $help>1; | |||
2390 | } | |||
2391 | } elsif($pod) { | |||
2392 | $line2 .= "\n\n$over$back"; | |||
2393 | } | |||
2394 | ||||
2395 | $line .= "$line2\n"; | |||
2396 | ||||
2397 | } | |||
2398 | ||||
2399 | if($op) { | |||
2400 | my $save = select $fileh; | |||
2401 | $| = 1; | |||
2402 | print $fileh $line; | |||
2403 | select $save; | |||
2404 | } | |||
2405 | ||||
2406 | return $line; | |||
2407 | } | |||
2408 | ||||
2409 | # spent 5.93ms (5.75+186µs) within WISE::Params::expand_tags which was called
# once (5.75ms+186µs) by WISE::Params::Params at line 254 | |||
2410 | 13 | 3.1e-5 | 2.4e-6 | my $ctxt = shift; |
2411 | my $opts = shift || {}; | |||
2412 | my %notags = | |||
2413 | $opts->{notags} && ref $opts->{notags} ? %{$opts->{notags}} : (); | |||
2414 | my %tags = | |||
2415 | $opts->{tags} && ref $opts->{tags} ? %{$opts->{tags}} : (); | |||
2416 | my %tag_params = | |||
2417 | $opts->{tag_params} && ref $opts->{tag_params} | |||
2418 | ? %{$opts->{tag_params}} : (); | |||
2419 | my $debug = $ctxt->{_meta}{debug} || $opts->{debug}; | |||
2420 | my $found; | |||
2421 | my %expanded; | |||
2422 | my %where; | |||
2423 | local $_; | |||
2424 | ||||
2425 | while(! defined $found || $found) { | |||
2426 | 8 | 1.4e-5 | 1.8e-6 | $found = 0; |
2427 | for my $param (@{$ctxt->{_meta}{parnames}}) { | |||
2428 | 1859 | 0.00217 | 1.2e-6 | next if %tag_params && ! $tag_params{$param}; |
2429 | next if ! $ctxt->{$param}{val}; | |||
2430 | next if $ctxt->{$param}{hash}; | |||
2431 | my @origvals = ($ctxt->{$param}{list} | |||
2432 | ? @{$ctxt->{$param}{val}} | |||
2433 | : $ctxt->{$param}{val}); | |||
2434 | print "--- |$param|@origvals|\n" if $debug; | |||
2435 | 296 | 0.00028 | 9.3e-7 | next if ! grep {/%/} @origvals; |
2436 | 61 | 0.00013 | 2.1e-6 | next if ! grep {/%[^%]+%/} @origvals; |
2437 | my $i = $ctxt->{$param}{list} ? 0 : undef; | |||
2438 | 183 | 0.00027 | 1.5e-6 | for my $origval (@origvals) { |
2439 | 1 | 0.00010 | 0.00010 | next if $origval !~ /%[^%]+%/; # No more |
2440 | print "--- |$param|".($i//"undef")."|$origval|$found|\n" | |||
2441 | if $debug; | |||
2442 | while($origval =~ /%([^%]+)%/g) { | |||
2443 | # Expand to full param name, if an alias of any param | |||
2444 | 1816 | 0.00190 | 1.0e-6 | my $origtag = $1; |
2445 | my @args; | |||
2446 | my $tag = $origtag; | |||
2447 | if($tag =~ s/\((.*)\)$//) { # Strip off and save arg.s | |||
2448 | @args = split(/,/,$1); | |||
2449 | } | |||
2450 | # Look for tag alias | |||
2451 | $tag = ($ctxt->{_meta}{tag_aliases} && | |||
2452 | $ctxt->{_meta}{tag_aliases}{lc $tag}) | |||
2453 | // $tag; | |||
2454 | # Expand to full param name | |||
2455 | 4 | 4.0e-6 | 1.0e-6 | if($ctxt->{_meta}{aliases}{lc $tag}) { |
2456 | $tag = $ctxt->{_meta}{aliases}{lc $tag}; | |||
2457 | } elsif(defined $tag_predefs{lc $tag}) { | |||
2458 | # Fine | |||
2459 | } else { | |||
2460 | # Need a known parameter or predefined value | |||
2461 | next; | |||
2462 | } | |||
2463 | # Not suppressed | |||
2464 | next if $notags{$tag} || $notags{lc $tag} || | |||
2465 | $notags{"%$tag%"} || $notags{"%".lc($tag)."%"} ; | |||
2466 | next if %tags && | |||
2467 | ! ($tags{$tag} || $tags{lc $tag} || | |||
2468 | $tags{"%$tag%"} || $tags{"%".lc($tag)."%"}); | |||
2469 | # Need a value | |||
2470 | my $val = (defined $ctxt->{$tag} && | |||
2471 | defined $ctxt->{$tag}{val} | |||
2472 | ? $ctxt->{$tag}{val} # Param value | |||
2473 | : $tag_predefs{$tag} # Pre-defined value | |||
2474 | ); | |||
2475 | print "--- |$param|$origtag|$tag|$ctxt->{$param}{val}|". | |||
2476 | ($ctxt->{$tag}{val}//"undef")."|".($val//"undef")."|\n" | |||
2477 | if $debug; | |||
2478 | next if ! defined $val; | |||
2479 | # Must be a scalar | |||
2480 | next if ref $val; | |||
2481 | # No recursion | |||
2482 | next if $tag eq $param; | |||
2483 | # No cycles | |||
2484 | die "$err: Cycle suspected in replacing tag $tag ". | |||
2485 | "in param $param.\n" | |||
2486 | if $expanded{$param}{$tag} && | |||
2487 | $expanded{$param}{$tag} > 9; | |||
2488 | # If val is non-empty, apply the indicated operations | |||
2489 | # (so far this is just a substr or format). | |||
2490 | if(@args) { | |||
2491 | if(@args == 1 && | |||
2492 | $args[0] =~ m/^[-+]?\d+(\.\d+)?[sdf]$/) { | |||
2493 | # sprintf format | |||
2494 | $val = sprintf("%$args[0]",$val); | |||
2495 | } elsif(grep(/^[-+]?\d+$/,@args) == 2) { | |||
2496 | # substring | |||
2497 | $val = substr($val,$args[0],$args[1]||1); | |||
2498 | } else { | |||
2499 | die "$err: Unrecognized args for tag '$tag'; /". | |||
2500 | join("/",@args)."/\n"; | |||
2501 | } | |||
2502 | } | |||
2503 | # We have a valid tag. Substitute/expand next occurance. | |||
2504 | my $newval = $origval; | |||
2505 | 76 | 0.00059 | 7.8e-6 | if($ctxt->{$param}{itype} == 7 && length($val) == 0 && |
2506 | $ctxt->{$param}{val} =~ m@[^/][-~_.]%$origtag%@) { | |||
2507 | # File types also eat preceding '-', '_', '~', or '.' | |||
2508 | # if the tag value is the empty string and the prev | |||
2509 | # character is not first or after a '/'. | |||
2510 | $newval =~ s/[-~_.]%$origtag%//; | |||
2511 | } else { | |||
2512 | $newval =~ s/\Q%$origtag%\E/$val/; | |||
2513 | } | |||
2514 | # Assign new value | |||
2515 | 76 | 8.8e-5 | 1.2e-6 | if(defined $i) { |
2516 | $ctxt->{$param}{val}[$i] = $newval; | |||
2517 | } else { | |||
2518 | $ctxt->{$param}{val} = $newval; | |||
2519 | } | |||
2520 | print "--- |$newval|\n" if $debug; | |||
2521 | 2 | 2.0e-6 | 1.0e-6 | if($ctxt->{$param}{isdefault} && $ctxt->{$tag}{specified}) { |
2522 | # Param now has a specified component | |||
2523 | $ctxt->{$param}{isdefault} = 0; | |||
2524 | $ctxt->{$param}{specified} = 1; | |||
2525 | } | |||
2526 | # Flag that we did something | |||
2527 | ++$found; # Continue iterating through all param values | |||
2528 | $expanded{$param}{$tag}++; | |||
2529 | $where{$param} = $i; | |||
2530 | } # While still tags | |||
2531 | } continue { # Value list | |||
2532 | ++$i; | |||
2533 | } | |||
2534 | } # Param | |||
2535 | } | |||
2536 | ||||
2537 | # Assign refs for values with substitutions | |||
2538 | 1 | 1.3e-5 | 1.3e-5 | if($ctxt->{_meta}{refs}) { |
2539 | for my $param (keys %where) { | |||
2540 | 87 | 0.00022 | 2.5e-6 | $ctxt->{_meta}{vals}{$param} = $ctxt->{$param}{val}; |
2541 | my $ref = $ctxt->{_meta}{refs}{$param}; | |||
2542 | assign_ref($ref, $ctxt->{$param}{val}, $ctxt->{$param}, $param); # spent 186µs making 29 calls to WISE::Params::assign_ref, avg 6µs/call | |||
2543 | } | |||
2544 | } | |||
2545 | ||||
2546 | return 1; | |||
2547 | } | |||
2548 | ||||
2549 | # spent 6.19ms (856µs+5.33) within WISE::Params::Param_lines which was called
# once (856µs+5.33ms) by WISE::Params::Param_print at line 2677 | |||
2550 | 15 | 4.8e-5 | 3.2e-6 | my $width = shift || 80; |
2551 | my $prefix = shift || " "; | |||
2552 | my (@lines,$opts,$lines,$xtra,$title,$ctxt,$iam); | |||
2553 | ||||
2554 | 2 | 2.0e-6 | 1.0e-6 | if(ref($prefix) =~ /hash/i) { |
2555 | $opts = $prefix; | |||
2556 | $prefix = undef; | |||
2557 | } | |||
2558 | $opts ||= {}; | |||
2559 | $prefix ||= $opts->{prefix} || ""; | |||
2560 | $xtra ||= $opts->{extra}; | |||
2561 | $title ||= $opts->{title}; | |||
2562 | ||||
2563 | $ctxt = $opts->{defs} || die "$err/lines: No defs.\n"; | |||
2564 | $iam = $ctxt->{_meta}{iam}; | |||
2565 | ||||
2566 | $lines = $title ? "\n${prefix}Named parameters for $iam:\n" : ""; | |||
2567 | ||||
2568 | for (@{$ctxt->{_meta}{parnames}}) { | |||
2569 | # from = -1/undef = n_o value | |||
2570 | # from = 0 => d_efault | |||
2571 | # from = 1 => c_ommand line | |||
2572 | # from = 2 => e_nvironment | |||
2573 | # from = 3 => f_ile | |||
2574 | # from = 4 => p_ass-on | |||
2575 | # from = 5 => i_nitialization | |||
2576 | 258 | 0.00106 | 4.1e-6 | my $from = $ctxt->{$_}{source} // -1; |
2577 | $from = [qw/n d c e f p i/]->[$from+1] // '?'; | |||
2578 | push @lines,sprintf("%s(%s) = %-s;",$_,$from,valstr($ctxt,$_,0,1)); # spent 4.64ms making 86 calls to WISE::Params::valstr, avg 54µs/call | |||
2579 | } | |||
2580 | $lines .= parwrapup($width,$prefix,$prefix,0,@lines); # spent 689µs making 1 call to WISE::Params::parwrapup | |||
2581 | 2 | 4.0e-6 | 2.0e-6 | if($xtra) { |
2582 | if(@ARGV) { | |||
2583 | $lines .= "${prefix}Unnamed parameters:\n". | |||
2584 | parwrapup(80,"$prefix ","$prefix ",1,@ARGV)."\n"; | |||
2585 | } | |||
2586 | if(defined $ctxt->{_meta}{pass_on} && | |||
2587 | @{$ctxt->{_meta}{pass_on}}) { | |||
2588 | $lines .= "${prefix}Passed-on parameters:\n". | |||
2589 | WISE::Spawn::Param_passon({defs=>$ctxt, dump=>1}); | |||
2590 | } | |||
2591 | } | |||
2592 | ||||
2593 | return $lines; | |||
2594 | } | |||
2595 | ||||
2596 | # spent 11µs within WISE::Params::origval which was called
# once (11µs+0) by WISE::Pars::origval at line 208 of /wise/base/deliv/dev/lib/perl/WISE/Pars.pm | |||
2597 | 3 | 5.0e-6 | 1.7e-6 | my $ctxt = shift; |
2598 | my $full = shift; | |||
2599 | return $ctxt->{$full}{origval}; | |||
2600 | } | |||
2601 | ||||
2602 | # spent 4.64ms (2.14+2.50) within WISE::Params::valstr which was called 86 times, avg 54µs/call:
# 86 times (2.14ms+2.50ms) by WISE::Params::Param_lines at line 2578, avg 54µs/call | |||
2603 | 1204 | 0.00138 | 1.1e-6 | my $ctxt = shift; |
2604 | my $full = shift; | |||
2605 | my $def = shift; | |||
2606 | my $print= shift; | |||
2607 | my $k; | |||
2608 | ||||
2609 | my $val = $def ? $ctxt->{$full}{default} : $ctxt->{$full}{val}; | |||
2610 | if($ctxt->{$full}{perlish}) { | |||
2611 | # Special case; dump as Perl string | |||
2612 | eval "use WISE::Dumper; 1;" | |||
2613 | or die "$err: Can't load WISE::Dumper.\n$@"; | |||
2614 | ($val = Dumper $val) =~ s/\n\s+//sg; | |||
2615 | $val =~ s/^\s*\$VAR\S+\s*=\s*//; | |||
2616 | $val =~ s/;\s*$//; | |||
2617 | return $val; | |||
2618 | } | |||
2619 | my @val = ( ref($val) eq "ARRAY" | |||
2620 | ? @$val | |||
2621 | : ( ref($val) eq "HASH" | |||
2622 | ? (map { ($_,$val->{$_}) } sort keys %$val) | |||
2623 | : () )); | |||
2624 | my ($op,$cl,$sep) = $ctxt->{$full}{list} ? ("(",")",",") : ("","",","); | |||
2625 | 31 | 8.5e-5 | 2.7e-6 | if($ctxt->{$full}{hash}) { |
2626 | my @tmp = @val; | |||
2627 | @val = (); | |||
2628 | push @val,shift(@tmp)."=".(ref($tmp[0]) # spent 1.95ms making 129 calls to WISE::Params::parquote, avg 15µs/call | |||
2629 | ? join(",",map { parquote($_,",")//'undef' } | |||
2630 | 129 | 0.00079 | 6.1e-6 | @{shift(@tmp)}) # spent 247µs making 20 calls to WISE::Params::parquote, avg 12µs/call |
2631 | : parquote(shift(@tmp)//'undef',",") | |||
2632 | ) | |||
2633 | while @tmp; | |||
2634 | } elsif ($ctxt->{$full}{list}) { | |||
2635 | $_ = parquote($_,",") for @val; # spent 300µs making 19 calls to WISE::Params::parquote, avg 16µs/call | |||
2636 | } | |||
2637 | 1 | 6.3e-5 | 6.3e-5 | if(@val>10 && $ctxt->{$full}{type} eq 'integer') { |
2638 | @val = split /,/,parcollapselist(@val); | |||
2639 | } | |||
2640 | if($print && @val>10 && $ctxt->{$full}{type} eq 'file') { | |||
2641 | # For p/o ONLY, remove common prefixes from path lists | |||
2642 | @val = sort @val; | |||
2643 | my @new = ($val[0]); | |||
2644 | my $last = $val[0]; | |||
2645 | for (@val[1..$#val]) { | |||
2646 | my $xor = "$_" ^ "$last"; # Common substrings will end up as NULLs | |||
2647 | my ($nulls) = $xor =~ /^(\000*)/; | |||
2648 | if(length($nulls) > 20) { | |||
2649 | push @new, "...".substr($_,length($nulls)-5); | |||
2650 | } else { | |||
2651 | push @new, $_; | |||
2652 | } | |||
2653 | $last = $_; | |||
2654 | } | |||
2655 | @val = @new; | |||
2656 | } | |||
2657 | $val = ( ! defined $val | |||
2658 | ? 'undef' | |||
2659 | : ( ! ref($val) | |||
2660 | ? $val | |||
2661 | : $op.join($sep,map {defined $_?$_:'undef'} @val).$cl )); | |||
2662 | ||||
2663 | return $val; | |||
2664 | } | |||
2665 | ||||
2666 | # spent 6.23ms (43µs+6.19) within WISE::Params::Param_print which was called
# once (43µs+6.19ms) by WISE::UtilsLight::banner at line 171 of /wise/base/deliv/dev/lib/perl/WISE/UtilsLight.pm | |||
2667 | 12 | 4.2e-5 | 3.5e-6 | my ($copyto,$xtra,$width) = (shift,shift,shift); |
2668 | my ($lines,$opts,$to,$ctxt); | |||
2669 | ||||
2670 | 2 | 1.0e-6 | 5.0e-7 | if(ref $copyto) { $opts = $copyto; $copyto = undef; } |
2671 | $opts ||= {}; | |||
2672 | $width ||= $opts->{width}||120; | |||
2673 | $to = exists $opts->{to} ? $opts->{to} : \*STDOUT; | |||
2674 | $copyto ||= $to ? $copyto||$opts->{copyto} : undef; | |||
2675 | $xtra ||= $opts->{extra}; | |||
2676 | ||||
2677 | $lines = Param_lines($width,{%$opts,width=>$width,extra=>$xtra,title=>1}); # spent 6.19ms making 1 call to WISE::Params::Param_lines | |||
2678 | print $to $lines if $to; | |||
2679 | print $copyto $lines if $copyto; | |||
2680 | ||||
2681 | return $lines; | |||
2682 | } | |||
2683 | ||||
2684 | sub Csh_set_params { | |||
2685 | my $ctxt = shift || die "$err/cshset: No defs.\n"; | |||
2686 | my (@lines); | |||
2687 | ||||
2688 | for ($ctxt->{_meta}{parnames}) { | |||
2689 | my $val = $ctxt->{$_}; | |||
2690 | printf("set opt_%s = %-s;\n",$_, | |||
2691 | (! defined $val ? "''": | |||
2692 | (! ref($val) ? &cshescape($val) : | |||
2693 | '('.join(" ",map {&cshescape($_)} @$val).')' ))); | |||
2694 | } | |||
2695 | } | |||
2696 | ||||
2697 | sub cshescape { | |||
2698 | ||||
2699 | local $_ = shift; | |||
2700 | ||||
2701 | if(! defined $_) { $_ = ""; } | |||
2702 | ||||
2703 | s/([^\\])!/$1\\!/g; | |||
2704 | s/\'/\'\"\'\"\'/g; | |||
2705 | ||||
2706 | "'$_'"; | |||
2707 | ||||
2708 | } | |||
2709 | ||||
2710 | sub loadparamsfromfile { | |||
2711 | my $from = shift; | |||
2712 | my $opts = shift || {}; | |||
2713 | my ($fromfile,$start,$end,$prefix,$suffix,$verbose); | |||
2714 | ||||
2715 | return if ! defined $from; | |||
2716 | ||||
2717 | $start = $opts->{paramstart} || "PARAM_START"; | |||
2718 | $end = $opts->{paramend} || "PARAM_END"; | |||
2719 | $prefix = $opts->{paramprefix} || ""; | |||
2720 | $suffix = $opts->{paramsuffix} || ""; | |||
2721 | $verbose = $opts->{verbose}; | |||
2722 | ||||
2723 | if(ref($from) =~ /glob/i) { | |||
2724 | print "Reading param. def.s from filehandle.\n" if $verbose; | |||
2725 | } elsif($from eq '-') { | |||
2726 | print "Reading param. def.s from stdin.\n" if $verbose; | |||
2727 | $from = \*STDIN; | |||
2728 | } else { | |||
2729 | $fromfile = $from; | |||
2730 | print "Reading param. def.s from '$from'.\n" if $verbose; | |||
2731 | if (! open(\*DEFS,"<$from") ) { | |||
2732 | warn "$err/loadparamsfromfile: Can't open input file '$from'; $!\n"; | |||
2733 | return; | |||
2734 | } | |||
2735 | $from = \*DEFS; | |||
2736 | } | |||
2737 | ||||
2738 | # Read the file and filter for the param def.s | |||
2739 | print "Reading ...\n" if $verbose; | |||
2740 | my @defs = <$from>; | |||
2741 | close $from; | |||
2742 | ||||
2743 | if($start) { | |||
2744 | @defs = grep(/$start/../$end/,@defs); | |||
2745 | return if ! @defs; | |||
2746 | shift @defs if @defs; # Remove markers | |||
2747 | pop @defs if @defs; | |||
2748 | } | |||
2749 | # If no start tag is defined, assume the whole file should be used | |||
2750 | ||||
2751 | # Remove line-by-line prefix and suffix | |||
2752 | if($prefix) { s/^\s*$prefix// for @defs; } | |||
2753 | if($suffix) { s/$suffix\s*$// for @defs; } | |||
2754 | ||||
2755 | my $ctxttr = join("",@defs); # Make one big string | |||
2756 | ||||
2757 | return wantarray ? ($ctxttr,$fromfile) : $ctxttr; | |||
2758 | } | |||
2759 | ||||
2760 | # Return the index to the first unquoted and unescaped occurrence of $c in $s | |||
2761 | # spent 4.36ms (3.26+1.10) within WISE::Params::paruqindex which was called 345 times, avg 13µs/call:
# 259 times (2.55ms+984µs) by WISE::Params::parsedefs at line 376, avg 14µs/call
# 53 times (392µs+43µs) by WISE::Params::paramparselines at line 1627, avg 8µs/call
# 33 times (322µs+71µs) by WISE::Params::paramlist2hash at line 1876, avg 12µs/call | |||
2762 | 1078 | 0.00273 | 2.5e-6 | my ($s,$c,$j) = @_; |
2763 | return -1 if ! $s || $s !~ /$c/; | |||
2764 | # Blank out quoted text | |||
2765 | $s = parblankquoted($s,"\000"); # spent 1.10ms making 194 calls to WISE::Params::parblankquoted, avg 6µs/call | |||
2766 | ||||
2767 | return index($s,$c,$j||0); | |||
2768 | } | |||
2769 | ||||
2770 | # Blank out escaped/quoted char.s in $s | |||
2771 | # spent 2.16ms within WISE::Params::parblankquoted which was called 404 times, avg 5µs/call:
# 194 times (1.10ms+0) by WISE::Params::paruqindex at line 2765, avg 6µs/call
# 162 times (790µs+0) by WISE::Params::parquote at line 2826, avg 5µs/call
# 24 times (137µs+0) by WISE::Params::paramlist at line 1832, avg 6µs/call
# 24 times (130µs+0) by WISE::Params::paramlist at line 1793, avg 5µs/call | |||
2772 | 808 | 0.00128 | 1.6e-6 | my $s = shift; |
2773 | return $s if ! $s || $s !~ /"/; | |||
2774 | my $blank = shift || "\000"; | |||
2775 | my $qpat = &parqpat; | |||
2776 | my ($i,$j); | |||
2777 | ||||
2778 | # Get rid of double backslashes | |||
2779 | # (Use a null as a blank-out char.) | |||
2780 | $s =~ s|\\\\|$blank$blank|g; | |||
2781 | ||||
2782 | # Blank out singly-escaped single characters | |||
2783 | $s =~ s/\\[^\\]/$blank$blank/g; | |||
2784 | ||||
2785 | # Blank out quoted parts of $s | |||
2786 | while($s =~ m/$qpat/o) { | |||
2787 | $s = $`.$1.($blank x length($2)).$'; | |||
2788 | } | |||
2789 | ||||
2790 | return $s; | |||
2791 | } | |||
2792 | ||||
2793 | # This RE will find paired double quotes even if escaped quotes (\") are | |||
2794 | # embedded. It handles double escapes correctly. | |||
2795 | sub parqpat { | |||
2796 | q@((?<!\\\\)(?:\\\\{2})*)("(?:(?:(?<!\\\\)(?:\\\\{2})*\\\\")|[^"])*")@; | |||
2797 | } | |||
2798 | ||||
2799 | # Remove quotes and un-escape the text | |||
2800 | # spent 2.34ms within WISE::Params::parunquote which was called 408 times, avg 6µs/call:
# 173 times (1.04ms+0) by WISE::Params::parsedefs at line 378, avg 6µs/call
# 129 times (636µs+0) by WISE::Params::Param_parse at line 897, avg 5µs/call
# 86 times (558µs+0) by WISE::Params::Param_parse at line 891, avg 6µs/call
# 20 times (107µs+0) by WISE::Params::Param_parse at line 894, avg 5µs/call | |||
2801 | 823 | 0.00134 | 1.6e-6 | my $s = shift; |
2802 | return $s if ! $s || $s !~ /"/; | |||
2803 | my $outer = shift; | |||
2804 | my ($t); | |||
2805 | my $qpat = q@((?<!\\\\)(?:\\\\{2})*)("(?:(?:(?<!\\\\)(?:\\\\{2})*\\\\")|[^"])*")@; | |||
2806 | $qpat = "^$qpat\$" if $outer; | |||
2807 | ||||
2808 | while($s =~ m/$qpat/o) { | |||
2809 | # print "\n\tMatched = /$1/$2/\n"; | |||
2810 | 3 | 8.0e-6 | 2.7e-6 | $t = $2; |
2811 | $t = substr($t,1,-1); | |||
2812 | $s = $`.$1.$t.$'; | |||
2813 | # print "\tresult = /$s/\n"; | |||
2814 | } | |||
2815 | ||||
2816 | if($s =~ m|\\+|) { $s =~ s|(\\+)|'\\'x(int(length($1)/2))|eg; } | |||
2817 | ||||
2818 | return $s; | |||
2819 | } | |||
2820 | ||||
2821 | # Backwhack any instances of $c that aren't already quoted or backwhacked | |||
2822 | # spent 2.50ms (1.71+790µs) within WISE::Params::parquote which was called 168 times, avg 15µs/call:
# 129 times (1.34ms+614µs) by WISE::Params::valstr at line 2628, avg 15µs/call
# 20 times (170µs+77µs) by WISE::Params::valstr at line 2630, avg 12µs/call
# 19 times (201µs+99µs) by WISE::Params::valstr at line 2635, avg 16µs/call | |||
2823 | 1314 | 0.00158 | 1.2e-6 | my $s = shift; |
2824 | my $c = shift; | |||
2825 | return $s if ! $s || ! defined $c; | |||
2826 | my $t = parblankquoted($s,"!"); # spent 790µs making 162 calls to WISE::Params::parblankquoted, avg 5µs/call | |||
2827 | ||||
2828 | my $i = -1; | |||
2829 | my $off = 0; | |||
2830 | while(($i = index($t,$c,$i+1)) >= 0) { | |||
2831 | substr($s,$i+$off++,0,"\\"); # Insert backwhack | |||
2832 | } | |||
2833 | return $s; | |||
2834 | } | |||
2835 | ||||
2836 | # spent 689µs within WISE::Params::parwrapup which was called
# once (689µs+0) by WISE::Params::Param_lines at line 2580 | |||
2837 | 16 | 0.00012 | 7.7e-6 | my ($columns, $ip, $xp, $resplit) = |
2838 | (shift||80,shift,shift,shift); | |||
2839 | local $_; | |||
2840 | my (@t) = map {defined $_ ? $_ : ""} @_; | |||
2841 | ||||
2842 | if(! @t) { return ""; } | |||
2843 | ||||
2844 | my ($r,$s) = ("",""); | |||
2845 | my $lead1 = defined $ip ? $ip : ""; | |||
2846 | my $lead2 = defined $xp ? $xp : ""; | |||
2847 | my ($lead,$ll,$ll1,$ll2); | |||
2848 | ||||
2849 | if($resplit) { @t = split(" ",join(" ",@t)); } | |||
2850 | $ll1 = ($columns||80) - length($lead1) - 1; | |||
2851 | $ll2 = ($columns||80) - length($lead2) - 1; | |||
2852 | ||||
2853 | $ll = $ll1; | |||
2854 | $lead = $lead1; | |||
2855 | for (@t) { | |||
2856 | 258 | 0.00048 | 1.8e-6 | s/\s*(.*?)\s*/$1/; |
2857 | 88 | 7.7e-5 | 8.7e-7 | if(length($r)+length($_) >= $ll) { |
2858 | if(length $r > 0) { $s .= "$lead$r\n"; } | |||
2859 | $r = ""; | |||
2860 | $ll = $ll2; | |||
2861 | $lead = $lead2; | |||
2862 | } | |||
2863 | $r .= "$_ "; | |||
2864 | ||||
2865 | } | |||
2866 | ||||
2867 | if(length $r > 0) { $s .= "$lead$r\n"; } | |||
2868 | ||||
2869 | return $s; | |||
2870 | ||||
2871 | } | |||
2872 | ||||
2873 | ||||
2874 | # spent 484µs within WISE::Params::parexpandlist which was called 5 times, avg 97µs/call:
# 5 times (484µs+0) by WISE::Params::paramlist at line 1839, avg 97µs/call | |||
2875 | 65 | 8.7e-5 | 1.3e-6 | my $str = shift; |
2876 | my $nouniq = shift; | |||
2877 | my $nosort = shift; | |||
2878 | my $min = shift; | |||
2879 | my $max = shift; | |||
2880 | my $sep = shift || '[,\s]+'; | |||
2881 | my $run = shift || '\.\.+'; | |||
2882 | my (@bits); | |||
2883 | local $_; | |||
2884 | ||||
2885 | for ( ref $str ? (@$str) : (split(/$sep/,$str)) ) { | |||
2886 | 192 | 0.00037 | 1.9e-6 | if( /([+-]?\w+)$run([+-]?\w+)/ ) { push @bits, $1..$2; } |
2887 | elsif( /([+-]?\w+)$run/ ) { | |||
2888 | if(defined $max) { push @bits, $1..$max; } | |||
2889 | else { die "$err: Dangling range fragment: '$_'\n"; } | |||
2890 | } elsif( /$run([+-]?\w+)/) { | |||
2891 | if(defined $min) { push @bits, $min..$1; } | |||
2892 | else { die "$err: Dangling range fragment: '$_'\n"; } | |||
2893 | } else { push @bits, $_; } | |||
2894 | } | |||
2895 | ||||
2896 | if(! $nouniq) { | |||
2897 | my %n; | |||
2898 | @n{@bits}=(0)x(scalar(@bits)); | |||
2899 | @bits = grep(!$n{$_}++,@bits); | |||
2900 | } | |||
2901 | if(! $nosort) { | |||
2902 | @bits = sort {$a <=> $b} @bits; | |||
2903 | } | |||
2904 | ||||
2905 | return wantarray ? @bits : scalar(@bits); | |||
2906 | } | |||
2907 | ||||
2908 | ||||
2909 | # Reverse the above operation. | |||
2910 | # Parameters can be: | |||
2911 | # List of integers | |||
2912 | # Ref. to a list of int.s, followed by | |||
2913 | # Positional arg.s | |||
2914 | # An option hash | |||
2915 | sub parcollapselist { | |||
2916 | my @list = @_; | |||
2917 | my ($str,$i,$n,$x,$y,$start,$end,$alpha,$nexty); | |||
2918 | my ($list,$uniq,$opts); | |||
2919 | ||||
2920 | if(ref $list[0]) { | |||
2921 | $list = shift @list; # Lis | |||
2922 | @list = @$list; # Local copy | |||
2923 | # Other arg.s. | |||
2924 | if(ref($list[0]) !~ /hash/i) { | |||
2925 | # Positional | |||
2926 | ($uniq) = @list; # Uniquify | |||
2927 | } else { | |||
2928 | # Named options | |||
2929 | $opts = $list[0]; | |||
2930 | $uniq = $opts->{unique} || $opts->{uniq}; | |||
2931 | } | |||
2932 | ||||
2933 | } | |||
2934 | ||||
2935 | $alpha = grep(/[a-zA-Z]/,@list); | |||
2936 | ||||
2937 | if($uniq) { | |||
2938 | my %n; | |||
2939 | @list = grep(!$n{$_}++,@list); | |||
2940 | } | |||
2941 | ||||
2942 | # There's no "nosort" option because we must sort the list to collapse i | |||
2943 | @list = sort {$a <=> $b} @list if ! $alpha; | |||
2944 | @list = sort {$a cmp $b} @list if $alpha; | |||
2945 | ||||
2946 | $n = @list; | |||
2947 | $str = ""; | |||
2948 | $start = undef; | |||
2949 | $end = undef; | |||
2950 | # Nothing perl-ish here. | |||
2951 | for ($i=0; $i<$n; ++$i, $y = $x) { | |||
2952 | $x = $list[$i]; | |||
2953 | if($i == 0) { $str = "$x"; next; } | |||
2954 | $nexty = $y; | |||
2955 | $nexty++; | |||
2956 | if((! $alpha && $x == $nexty) || | |||
2957 | ( $alpha && $x eq $nexty)) { | |||
2958 | if(! defined $start) { $start = $y; } | |||
2959 | $end = $x; | |||
2960 | } else { | |||
2961 | if(defined $start) { $str .= "..$end"; } | |||
2962 | $start = $end = undef; | |||
2963 | $str .= ",$x"; | |||
2964 | } | |||
2965 | } | |||
2966 | if(defined $start) { $str .= "..$end"; } | |||
2967 | ||||
2968 | return wantarray ? ($str,$alpha) : $str; | |||
2969 | } | |||
2970 | ||||
2971 | ||||
2972 | # spent 104µs (21+83) within WISE::Params::parwhoiam which was called
# once (21µs+83µs) by WISE::Params::steps_resolve at line 2099 | |||
2973 | 3 | 4.0e-6 | 1.3e-6 | my $iam; |
2974 | 1 | 9.0e-6 | 9.0e-6 | if($0 eq "-e") { $iam = "perl-e"; } |
2975 | else { $iam = basename($0); } # spent 83µs making 1 call to File::Basename::basename | |||
2976 | return $iam; | |||
2977 | } | |||
2978 | ||||
2979 | ||||
2980 | # Provide a (fairly) safe compartment for eval'ing external code. | |||
2981 | # (Still not safe against %SIG hijacking.) | |||
2982 | sub par_safe_eval { | |||
2983 | my $code = shift; | |||
2984 | my $opts = shift || {}; | |||
2985 | my $share = $opts->{share}; | |||
2986 | warn("*** $0/par_safe_eval: share is not an array ref.\n"), return | |||
2987 | if $share && ref($share) !~ /array/i; | |||
2988 | if($code=~/( # Disallow package manipulation | |||
2989 | \w\s*\}?:: | \bpackage\b | | |||
2990 | # Disallow signal manipulation | |||
2991 | [\$%] \s* \{? \s* SIG \s* \}? \b | | |||
2992 | # Disallow call stack manipulation | |||
2993 | [\$\@] \s* \{? \s* _ \s* \}? \b )/x) { | |||
2994 | $@ = "*** $0/par_afe_eval: string contains unsafe ". | |||
2995 | "words/symbols '$1'.\n code='$code'\n"; | |||
2996 | return; | |||
2997 | } | |||
2998 | { | |||
2999 | require Safe; | |||
3000 | Safe->import; | |||
3001 | ||||
3002 | my $safe = Safe->new; | |||
3003 | $safe->permit_only(qw/:base_core :base_mem :base_orig :base_math/); | |||
3004 | $safe->deny(qw/:sys_db warn die dbmopen tie untie sselect select | |||
3005 | pipe_op sockpair/); | |||
3006 | $safe->share_from("main",$share) if $share; | |||
3007 | return $safe->reval($code,1); | |||
3008 | } | |||
3009 | } | |||
3010 | ||||
3011 | ||||
3012 | # Output a file which, when included by a main prog., will do Perl-like | |||
3013 | # arg. handling using libnmpar. | |||
3014 | sub Emit_nmpar_defs { | |||
3015 | my $from = shift; # File handle or name to get def.s from | |||
3016 | my $to = shift; # File handles or base name to write to | |||
3017 | my $opts = shift; # Option hash | |||
3018 | my ($genhelp,@defs,$ctxt,$verbose,$itflag,$fromfile,$toc,$toh,$suf,$toname); | |||
3019 | my ($prog_name,$prog_file,$prog_title,$stdout,$stdin,$ctxttr,$cname,$hname); | |||
3020 | my ($ext,$path) = ("",""); | |||
3021 | ||||
3022 | $ctxt = $opts->{defs} || die "$err/emit: No defs.\n"; | |||
3023 | $verbose = $opts->{verbose}; | |||
3024 | $prog_name = $opts->{iam}; | |||
3025 | $suf = $opts->{suf} || "-def"; | |||
3026 | ||||
3027 | $toname = ! ref($to) && $to ne "-"; | |||
3028 | $opts->{incc} &&= $toname; | |||
3029 | ||||
3030 | if(! $to) { | |||
3031 | ($to,$path,$ext) = fileparse($from,'\.[^.]+'); | |||
3032 | $to .= $suf; | |||
3033 | } elsif($toname) { | |||
3034 | ($to,$path,$ext) = fileparse($to,'\.[^.]+'); | |||
3035 | } | |||
3036 | $ext ||= ""; | |||
3037 | ||||
3038 | $opts->{base} ||= fileparse($to,'\.[^.]+') if $toname; | |||
3039 | ||||
3040 | print "\n== Param processing ==\n From=$from, to=$to, ext=$ext". | |||
3041 | "base=$opts->{base}\n\n" | |||
3042 | if $verbose; | |||
3043 | ||||
3044 | if(defined $from) { | |||
3045 | # Need to read and parse def.s; otherwise we assume Params() has | |||
3046 | # already been called and put the results in $ctxt. | |||
3047 | ||||
3048 | ($ctxttr,$fromfile) = loadparamsfromfile($from,$opts); | |||
3049 | ||||
3050 | ($verbose&&warn("$warn; No param defs in $from.\n"),exit(0)) | |||
3051 | if ! $ctxttr; | |||
3052 | ||||
3053 | $prog_name ||= fileparse($fromfile,'\.[^.]+') if $fromfile; | |||
3054 | $stdin = $fromfile eq "-"; | |||
3055 | ||||
3056 | # Parse parameter def.s into internal structure | |||
3057 | print "Parsing ...\n$ctxttr" if $verbose; | |||
3058 | $ctxt = Params($ctxttr,{parseonly=>1,sep=>"\n",verbose=>$verbose, | |||
3059 | iam=>$prog_name,defs=>{}}); | |||
3060 | } | |||
3061 | Param_help(2,{defs=>$ctxt}) if $verbose; | |||
3062 | ||||
3063 | $genhelp = [ @{$ctxt->{_meta}{help} || []} ]; | |||
3064 | ||||
3065 | # Program name is in the first general help line by default if | |||
3066 | # it contains Command => | |||
3067 | # ... there's a '::' in the line, on the left is the program name, | |||
3068 | # on the right is the program file name. | |||
3069 | my ($tmpprog,$tmpfile) = | |||
3070 | $genhelp->[0] =~ | |||
3071 | /\s*Command\s*=>\s*([_a-zA-Z][-\w]*)(?::\s*:\s*(\S+))?/; | |||
3072 | if($tmpprog) { | |||
3073 | shift @{$genhelp}; # Shift out if found. | |||
3074 | $prog_name = $tmpprog; | |||
3075 | $prog_file = $tmpfile if $tmpfile; | |||
3076 | } | |||
3077 | ||||
3078 | $prog_name ||= $ctxt->{_meta}{iam}; # Last chance | |||
3079 | ||||
3080 | if(! $prog_name || $prog_name !~ /^[_a-zA-Z][-\w]*$/) { # No legal name | |||
3081 | die "$err/nmpar: Program name '$prog_name' is missing ". | |||
3082 | "or illegal.\n"; | |||
3083 | } | |||
3084 | ||||
3085 | (my $c_prog_name=$prog_name) =~ s/-/_/g; # Make into a valid c identifier | |||
3086 | ||||
3087 | print "GenHelp=\n@{$genhelp}\n" if $verbose > 1; | |||
3088 | ||||
3089 | # Prepare output file handles | |||
3090 | if(ref $to) { | |||
3091 | print "Writing nmpar def.s to filehandles.\n" if $verbose; | |||
3092 | if(ref($to)=~/array/i) { | |||
3093 | $toh = $to->[0]; | |||
3094 | $toc = $to->[1]; | |||
3095 | } else { | |||
3096 | $toh = $to; | |||
3097 | $toc = $to; | |||
3098 | } | |||
3099 | } elsif($to eq '-') { | |||
3100 | # Output 'file' is stdout. | |||
3101 | print "Writing nmpar def.s to stdout.\n" if $verbose; | |||
3102 | $toh = $toc = \*STDOUT; | |||
3103 | $stdout = 1; | |||
3104 | } else { | |||
3105 | # Not a handle or '-', must be a name | |||
3106 | $hname = "$to.h"; | |||
3107 | print "Writing include file '$hname'.\n" if $verbose; | |||
3108 | if(! open(\*NMPARH,">$hname")) { | |||
3109 | die "$err/nmpar: Can't open output file '$hname'; $!\n"; | |||
3110 | } | |||
3111 | $toh = \*NMPARH; | |||
3112 | $cname = "$to$ext"; | |||
3113 | print "Writing C code file '$cname'.\n" if $verbose; | |||
3114 | if(! open(\*NMPARC,">$cname")) { | |||
3115 | die "$err/nmpar: Can't open output file '$cname'; $!\n"; | |||
3116 | } | |||
3117 | $toc = \*NMPARC; | |||
3118 | } | |||
3119 | ||||
3120 | # Processing options | |||
3121 | # Disallow environment option specification | |||
3122 | $opts->{noenv} ||= grep(/\*NO_ENV\*/ ,@defs); | |||
3123 | # Disallow init (rc) file opt specification | |||
3124 | $opts->{noinit} ||= grep(/\*NO_INIT\*/,@defs); | |||
3125 | # Disallow non-option (unnamed) command line arguments | |||
3126 | $opts->{noargs} ||= grep(/\*NO_ARGS\*/,@defs); | |||
3127 | ||||
3128 | # Prepare the general help for use | |||
3129 | shift @{$genhelp} while @$genhelp && ! $genhelp->[0]; # Skip blank lines | |||
3130 | ||||
3131 | $prog_title = shift @{$genhelp} if $genhelp->[0]; # Synopsis | |||
3132 | $prog_title ||= ""; | |||
3133 | $prog_title =~ s/(?:^|[^\\])([""])/\\$1/; # Escape embedded quotes | |||
3134 | $prog_file ||= $fromfile || "UNKNOWN"; | |||
3135 | ||||
3136 | # | |||
3137 | # Emit .h file code for option processing in 'main'. | |||
3138 | ||||
3139 | print "Writing include file to $toh ...\n" if $verbose; | |||
3140 | ||||
3141 | print $toh "/* This file was auto-generated on ".gmtime(time). | |||
3142 | " GMT; DO NOT EDIT */\n\n"; | |||
3143 | print $toh "/* prog_name\t\t=\t\"$prog_name\"; */\n"; | |||
3144 | print $toh "/* prog_title\t\t=\t\"$prog_title\"; */\n"; | |||
3145 | print $toh "/* prog_file_name\t\t=\t$prog_file; */\n\n"; | |||
3146 | ||||
3147 | (my $code = <<" EOT")=~s/^ *\t//gm; | |||
3148 | \t#include \"namedlist.h\" | |||
3149 | \t#include \"nmpar.h\" | |||
3150 | \tNamedList * ${c_prog_name}_defparse(NamedList * defs, NamedList * opts); | |||
3151 | \tint ${c_prog_name}_help(int help, NamedList * opts); | |||
3152 | \tNamedList * cmdlnopts=NULL, * cmdlndefs=NULL; | |||
3153 | \t#ifdef __cplusplus | |||
3154 | \t#define __BLOCKSTART__ do { | |||
3155 | \t#define __BLOCKEND__ } while(0) | |||
3156 | \t#else | |||
3157 | \t#define __BLOCKSTART__ ({ | |||
3158 | \t#define __BLOCKEND__ nargs; }) | |||
3159 | \t#endif | |||
3160 | \t#define DoOptions(argc,argv) \\ | |||
3161 | \t __BLOCKSTART__ \\ | |||
3162 | \t int nargs,help; \\ | |||
3163 | \t if((help=help_wanted(argc,argv,NULL)) >= 0) { \\ | |||
3164 | \t (void)${c_prog_name}_help(help,NULL); \\ | |||
3165 | \t exit(0); \\ | |||
3166 | \t } \\ | |||
3167 | \t cmdlndefs = ${c_prog_name}_defparse(0,0); /* Set up defs */ \\ | |||
3168 | \t cmdlnopts = /* first parse defaults */ \\ | |||
3169 | \t parse_cmd_line(cmdlndefs,0,0,NULL,cmdlnopts,NULL); \\ | |||
3170 | \t cmdlnopts = /* parse command line */ \\ | |||
3171 | \t parse_cmd_line(cmdlndefs,argc,argv,&nargs, \\ | |||
3172 | \t cmdlnopts,NULL); \\ | |||
3173 | \t __BLOCKEND__ | |||
3174 | EOT | |||
3175 | print $toh "$code\n\n"; | |||
3176 | print $toh "\n#include \"$cname\"\n\n" if $opts->{incc}; | |||
3177 | ||||
3178 | close $toh if ! $stdout; | |||
3179 | ||||
3180 | # | |||
3181 | # Emit .c (or .cpp) code for option processing; to be linked to main | |||
3182 | ||||
3183 | print "Writing C code file to $toc ...\n" if $verbose; | |||
3184 | ||||
3185 | print $toc "/* This file was auto-generated on ".gmtime(time). | |||
3186 | " GMT; DO NOT EDIT */\n\n"; | |||
3187 | print $toc "/* prog_name\t\t=\t\"$prog_name\"; */\n"; | |||
3188 | print $toc "/* prog_title\t\t=\t\"$prog_title\"; */\n"; | |||
3189 | print $toc "/* prog_file_name\t\t=\t$prog_file; */\n\n"; | |||
3190 | ||||
3191 | print $toc "#include \"namedlist.h\"\n"; | |||
3192 | print $toc "#include \"nmpar.h\"\n\n"; | |||
3193 | ||||
3194 | print $toc "NamedList * ${c_prog_name}_defparse(NamedList * defs,". | |||
3195 | "NamedList * opts)\n"; | |||
3196 | print $toc "{\n"; | |||
3197 | print $toc " NamedList *def;\n\n"; | |||
3198 | print $toc " if(defs == NULL) defs = nm_alloc();\n\n"; | |||
3199 | ||||
3200 | # Write individual parameter definition block structures | |||
3201 | ||||
3202 | for my $p (@{$ctxt->{_meta}{parnames}}) { | |||
3203 | my ($par,$def,$flag,$descrip,@doc,$val,@aliases, | |||
3204 | $type,$dummy,$fmt,$flag_code,$flag_arg); | |||
3205 | $par = lc $p; | |||
3206 | $def = $ctxt->{$par}; # All parameter definition info | |||
3207 | print "Param=$par, def=$def\n" if $verbose; | |||
3208 | next if ! $par || $par =~ /^[\*\s]/ || ! ref $def; | |||
3209 | if($par !~ /^[^:,""=''\#]+$/) { # Test name for legality ' | |||
3210 | die "$err/nmpar: Opt name '$par' is illegal.\n"; | |||
3211 | } | |||
3212 | $type = $def->{type}; | |||
3213 | # Reduce plethora of types to a more manageable number | |||
3214 | if($type eq 'char' || $type eq 'name' || $type eq 'file') | |||
3215 | { $type = 'string'; } | |||
3216 | elsif($type eq 'real') { $type = 'double'; } | |||
3217 | print "\tType=$type\n" if $verbose; | |||
3218 | ||||
3219 | # Map the type to a namedlist definition | |||
3220 | print $toc " (void)nm_add1(defs,\"$par(n)\",nm_alloctmp());\n"; | |||
3221 | print $toc " nm_fetch(defs,\"$par\",def,NamedList *,NULL);\n"; | |||
3222 | print $toc " (void)nm_add1(def,\"name(s)\",\"$par\");\n"; | |||
3223 | my $nalias = @{$def->{aliases}}; | |||
3224 | print $toc " { char *aliases[]={\n". | |||
3225 | " \"".join('","',@{$def->{aliases}})."\"\n". | |||
3226 | " };\n". | |||
3227 | " (void)nm_add1(def,\"aliases(s[*$nalias])\",aliases);\n". | |||
3228 | " }\n"; | |||
3229 | print $toc " (void)nm_add1(def,\"type(s)\",\"$type\");\n"; | |||
3230 | print $toc " (void)nm_add1(def,\"itype(i)\",$def->{itype});\n"; | |||
3231 | print $toc " (void)nm_add1(def,\"list(i)\",1);\n" if $def->{list}; | |||
3232 | if(defined $def->{default}) { | |||
3233 | if($def->{list}) { | |||
3234 | # Default lists look better if space is stripped from around | |||
3235 | # commas. We can't handle embedded commas anyway, so do the | |||
3236 | # substitution quick and dirty. | |||
3237 | $def->{default} =~ s/,\s+/, /g; | |||
3238 | } | |||
3239 | my $default = $def->{defaultstr}; | |||
3240 | $default =~ s/^\s*([''""])(.*)\1\s*$/$2/; # Strip outer quotes | |||
3241 | $default =~ s/"/\\"/g; # Escape interior quotes " | |||
3242 | print $toc " (void)nm_add1(def,\"default(s)\",\"$default\");\n"; | |||
3243 | print "\tDefault=$default\n" if $verbose; | |||
3244 | } | |||
3245 | print $toc " (void)nm_add1(def,\"required(i)\",1);\n" | |||
3246 | if $def->{required}; | |||
3247 | # End main definition | |||
3248 | print $toc "\n\n"; | |||
3249 | ||||
3250 | # Done with parameter definitions | |||
3251 | } | |||
3252 | print $toc "\n return defs;\n"; | |||
3253 | print $toc "\n}\n"; | |||
3254 | ||||
3255 | # Now write help routine | |||
3256 | (my $help0 = Param_help(0,{noop=>1,defs=>$ctxt})) =~ s/"/\\"/g; | |||
3257 | $help0 =~ s/\n/\\n"\n"/g; | |||
3258 | (my $help1 = Param_help(1,{noop=>1,defs=>$ctxt})) =~ s/"/\\"/g; | |||
3259 | $help1 =~ s/\n/\\n"\n"/g; | |||
3260 | (my $help2 = Param_help(2,{noop=>1,defs=>$ctxt})) =~ s/"/\\"/g; | |||
3261 | $help2 =~ s/\n/\\n"\n"/g; | |||
3262 | (my $help3 = Param_help(3,{noop=>1,defs=>$ctxt})) =~ s/"/\\"/g; | |||
3263 | $help3 =~ s/\n/\\n"\n"/g; | |||
3264 | my $tmp = <<" EOC"; | |||
3265 | \tstatic char * help0 = | |||
3266 | \t"$help0"; | |||
3267 | \t | |||
3268 | \tstatic char * help1 = | |||
3269 | \t"$help1"; | |||
3270 | \t | |||
3271 | \tstatic char * help2 = | |||
3272 | \t"$help2"; | |||
3273 | \t | |||
3274 | \tstatic char * help3 = | |||
3275 | \t"$help3"; | |||
3276 | \t | |||
3277 | \tint ${c_prog_name}_help(int help,NamedList * opts) | |||
3278 | \t{ | |||
3279 | \t if (help == 0) fprintf(stdout,"%s",help0); | |||
3280 | \t else if(help == 1) fprintf(stdout,"%s",help1); | |||
3281 | \t else if(help == 2) fprintf(stdout,"%s",help2); | |||
3282 | \t else if(help == 3) fprintf(stdout,"%s",help3); | |||
3283 | \t else return -1; // Failure | |||
3284 | \t return 0; // Success | |||
3285 | \t} | |||
3286 | EOC | |||
3287 | print "\n\nHelp o/p = ".length($tmp)." bytes.\n" if $verbose; | |||
3288 | ||||
3289 | # A 5.6.0 bug requires doing this translation line-by-line | |||
3290 | $tmp = (join "\n",map { s/^ *\t//; $_; } split /\n/,$tmp)."\n"; | |||
3291 | print $toc "\n\n$tmp\n"; | |||
3292 | ||||
3293 | close $toc if ! $stdout; | |||
3294 | ||||
3295 | return 1; | |||
3296 | } | |||
3297 | ||||
3298 | # Get the parameters from the help listing of another program | |||
3299 | # using the params() call. These parameters will be usable as input | |||
3300 | # to a subsequent params() call, allowing us to clone param lists | |||
3301 | # and build on them. | |||
3302 | sub paramsfrommodel { | |||
3303 | my $exec = shift; | |||
3304 | my $opts = shift || {}; | |||
3305 | $opts = {%params_from_model_opts, %$opts}; # Add default opts | |||
3306 | my $execpath = $opts->{execpath} || ""; | |||
3307 | my $usecache = exists $opts->{use_cache} || exists $opts->{usecache} | |||
3308 | ? $opts->{use_cache} // $opts->{usecache} | |||
3309 | : 1; | |||
3310 | my $cachedir = $opts->{cachedir}; | |||
3311 | my $umask = $opts->{cache_umask} || 02; | |||
3312 | my $from = $opts->{included_from}; | |||
3313 | my $param_defs = $opts->{param_defs}; | |||
3314 | my ($base,$cachefile,$cache_uptodate,$ref); | |||
3315 | ||||
3316 | # Establish name of cache, if used | |||
3317 | ||||
3318 | if($usecache && ! $cachedir) { | |||
3319 | if(! $execpath) { | |||
3320 | ($exec,$execpath) = WISE::UtilsLight::whichami($exec); | |||
3321 | } | |||
3322 | $execpath = WISE::UtilsLight::normalizepath($execpath,{resolve=>1}); | |||
3323 | $cachedir = "${execpath}param_cache/"; | |||
3324 | $cachefile = $cachedir.basename($exec).".params"; | |||
3325 | } | |||
3326 | ||||
3327 | # Expand executable path | |||
3328 | ||||
3329 | if($execpath && $exec !~ m|^/|) { | |||
3330 | $exec = "$execpath/$exec"; | |||
3331 | } | |||
3332 | ||||
3333 | if($usecache && -e $cachefile && -e $exec) { | |||
3334 | # Compare cache time to executable time | |||
3335 | $cache_uptodate = -e $cachefile && -M _ < -M $exec; | |||
3336 | # Also compare to WISE::Pars.pm time | |||
3337 | $cache_uptodate &&= -e $cachefile && -M _ < -M $INC{'WISE/Pars.pm'} | |||
3338 | if $INC{'WISE/Pars.pm'}; | |||
3339 | $cache_uptodate &&= -e $cachefile && -M _ < -M $INC{'WISE/Params.pm'} | |||
3340 | if $INC{'WISE/Params.pm'}; | |||
3341 | } | |||
3342 | ||||
3343 | ||||
3344 | my @defs; | |||
3345 | ||||
3346 | if($param_defs) { | |||
3347 | # Newest defs handed to us | |||
3348 | ||||
3349 | @defs = @$param_defs; | |||
3350 | $cache_uptodate = 0; # Always update in this case | |||
3351 | ||||
3352 | } else { | |||
3353 | # Open source for command line defs | |||
3354 | my $def_fh; | |||
3355 | ||||
3356 | # Try to read cache | |||
3357 | if($usecache && $cache_uptodate) { | |||
3358 | if(! open($def_fh,"<$cachefile")) { | |||
3359 | # May have been removed between testing for up-to-dateness and | |||
3360 | # now, so just carry on as if there was no cache | |||
3361 | $def_fh = undef; | |||
3362 | } else { | |||
3363 | $ref = $cachefile; | |||
3364 | } | |||
3365 | } | |||
3366 | ||||
3367 | # No cache. Get by running executable | |||
3368 | if(! $def_fh) { | |||
3369 | local $ENV{NO_DEFAULT_PAR_FILES} = 1; | |||
3370 | if(! open($def_fh,"$exec -help2 |")) { | |||
3371 | warn "*** $0/PARAMMODEL: Couldn't start $exec to get ". | |||
3372 | "model param.s; $!\n"; | |||
3373 | return; | |||
3374 | } | |||
3375 | $ref = $exec; | |||
3376 | } | |||
3377 | ||||
3378 | # Read them | |||
3379 | ||||
3380 | while(<$def_fh>) { | |||
3381 | chomp; | |||
3382 | push @defs, $_; | |||
3383 | } | |||
3384 | ||||
3385 | if(! close($def_fh)) { | |||
3386 | warn "*** $0/PARAMMOD: Process failed getting model param.s from ". | |||
3387 | "'$ref' (close); $!.\n"; | |||
3388 | return; | |||
3389 | } | |||
3390 | } | |||
3391 | ||||
3392 | if(! @defs) { | |||
3393 | warn "*** $0/PARAMMOD: Process read NO model param.s from ". | |||
3394 | "'$ref'; $!.\n"; | |||
3395 | return; | |||
3396 | } | |||
3397 | ||||
3398 | # Update cache | |||
3399 | ||||
3400 | if($usecache && ! $cache_uptodate) { UPDATE: { | |||
3401 | if(! -e $cachedir) { | |||
3402 | last UPDATE if ! -w "$cachedir/.."; | |||
3403 | if(! mkdir($cachedir)) { | |||
3404 | warn "*** $0/PARAMMOD: Unable to make cache directory ". | |||
3405 | "'$cachedir'; $!.\n"; | |||
3406 | last UPDATE; | |||
3407 | } | |||
3408 | } | |||
3409 | last UPDATE if -e $cachefile && ! -w $cachefile; | |||
3410 | # If $exec's parameters form part of someone else's parameters, | |||
3411 | # through an 'include_defs' directive, we need to | |||
3412 | # invalidate their cache too | |||
3413 | if($from) { | |||
3414 | my $from_cache = $cachedir.basename($from).".params"; | |||
3415 | unlink($from_cache) if -e $from_cache; | |||
3416 | } | |||
3417 | # Use temporary file so update is atomic | |||
3418 | require File::Temp; | |||
3419 | my $cachefh = File::Temp->new(UNLINK=>0,DIR=>$cachedir); | |||
3420 | print $cachefh join("\n",@defs),"\n","\n## Cache_time=".time()."\n" | |||
3421 | or warn("*** $0/PARAMMOD: Unable to write cache file ". | |||
3422 | "'$cachefile'; $!.\n"), | |||
3423 | last UPDATE; | |||
3424 | close $cachefh | |||
3425 | or warn "*** $0/PARAMMOD: Unable to write (close) cache file ". | |||
3426 | "'$cachefile'; $!.\n"; | |||
3427 | my $perms = (0666 & ~$umask); | |||
3428 | chmod $perms, $cachefh->filename(); | |||
3429 | rename($cachefh->filename(),$cachefile); | |||
3430 | chmod $perms, $cachefile; # ... in case above chmod didn't work | |||
3431 | } } | |||
3432 | ||||
3433 | # Set scalar return type | |||
3434 | ||||
3435 | my $defs; | |||
3436 | $defs = join("\n",@defs)."\n" if ! $opts->{arrayref}; | |||
3437 | $defs = \ @defs if $opts->{arrayref}; | |||
3438 | ||||
3439 | return wantarray && ! $opts->{arrayref} ? @defs : $defs; | |||
3440 | } | |||
3441 | ||||
3442 | 1 | 2.2e-5 | 2.2e-5 | 1; |