line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Hub::Perl::Language; |
2
|
1
|
|
|
1
|
|
4
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
27
|
|
3
|
1
|
|
|
1
|
|
70972
|
use Compress::Zlib; |
|
1
|
|
|
|
|
85422
|
|
|
1
|
|
|
|
|
341
|
|
4
|
1
|
|
|
1
|
|
11
|
use Hub qw/:lib/; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
8
|
|
5
|
|
|
|
|
|
|
our $VERSION = '4.00043'; |
6
|
|
|
|
|
|
|
our @EXPORT = qw//; |
7
|
|
|
|
|
|
|
our @EXPORT_OK = qw/ |
8
|
|
|
|
|
|
|
sizeof |
9
|
|
|
|
|
|
|
check |
10
|
|
|
|
|
|
|
expect |
11
|
|
|
|
|
|
|
fear |
12
|
|
|
|
|
|
|
abort |
13
|
|
|
|
|
|
|
opts |
14
|
|
|
|
|
|
|
objopts |
15
|
|
|
|
|
|
|
cmdopts |
16
|
|
|
|
|
|
|
hashopts |
17
|
|
|
|
|
|
|
bestof |
18
|
|
|
|
|
|
|
subst |
19
|
|
|
|
|
|
|
getuid |
20
|
|
|
|
|
|
|
getgid |
21
|
|
|
|
|
|
|
max |
22
|
|
|
|
|
|
|
min |
23
|
|
|
|
|
|
|
flip |
24
|
|
|
|
|
|
|
rmval |
25
|
|
|
|
|
|
|
cpref |
26
|
|
|
|
|
|
|
random_id |
27
|
|
|
|
|
|
|
checksum |
28
|
|
|
|
|
|
|
merge |
29
|
|
|
|
|
|
|
flatten |
30
|
|
|
|
|
|
|
replace |
31
|
|
|
|
|
|
|
digout |
32
|
|
|
|
|
|
|
diff |
33
|
|
|
|
|
|
|
touch |
34
|
|
|
|
|
|
|
intdiv |
35
|
|
|
|
|
|
|
dice |
36
|
|
|
|
|
|
|
indexmatch |
37
|
|
|
|
|
|
|
/; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# Sorting |
40
|
|
|
|
|
|
|
our ($a,$b) = (); |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# Regular expression used for Hub::check comparisons |
43
|
1
|
|
|
1
|
|
7
|
use constant EXPR_NUMERIC => '\A[+-]?[\d\.Ee_]+\Z'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
941
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# Not all interpreters have getpwnam compiled in |
46
|
|
|
|
|
|
|
eval ("getpwnam('')"); |
47
|
|
|
|
|
|
|
our $HAS_GETPWNAM = $@ ? 0 : 1; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# Not all interpreters have getgrnam compiled in |
50
|
|
|
|
|
|
|
eval ("getgrnam('')"); |
51
|
|
|
|
|
|
|
our $HAS_GETGRNAM = $@ ? 0 : 1; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
54
|
|
|
|
|
|
|
# sizeof - Integer size of hashes, arrays, and scalars |
55
|
|
|
|
|
|
|
# |
56
|
|
|
|
|
|
|
# sizeof \%hash |
57
|
|
|
|
|
|
|
# sizeof \@array |
58
|
|
|
|
|
|
|
# sizeof \$scalar_ref |
59
|
|
|
|
|
|
|
# sizeof $scalar |
60
|
|
|
|
|
|
|
# sizeof \%more, \@than, $one |
61
|
|
|
|
|
|
|
# |
62
|
|
|
|
|
|
|
# Sizes are computed as follows: |
63
|
|
|
|
|
|
|
# |
64
|
|
|
|
|
|
|
# HASH - Number of keys in the hash |
65
|
|
|
|
|
|
|
# ARRAY - Number of elements |
66
|
|
|
|
|
|
|
# SCALAR - Length as returned by C |
67
|
|
|
|
|
|
|
# |
68
|
|
|
|
|
|
|
# The total size of all arguments is returned. |
69
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
70
|
|
|
|
|
|
|
#|test(match,3) sizeof( { a=>1, b=>2, c=>3 } ); # Hash |
71
|
|
|
|
|
|
|
#|test(match,3) sizeof( [ 'a1', 'b2', 'c3' ] ); # Array |
72
|
|
|
|
|
|
|
#|test(match,3) sizeof( "abc" ); # Scalar |
73
|
|
|
|
|
|
|
#|test(match,3) sizeof( \"abc" ); # Scalar (ref) |
74
|
|
|
|
|
|
|
#|test(match,0) sizeof( undef ); # Nothing |
75
|
|
|
|
|
|
|
#|test(match,3) sizeof( "a", "b", "c" ); # Multiple values |
76
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub sizeof { |
79
|
0
|
|
|
0
|
1
|
0
|
my $result = 0; |
80
|
0
|
|
|
|
|
0
|
foreach my $unk ( @_ ) { |
81
|
0
|
0
|
|
|
|
0
|
$result += !defined $unk |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
82
|
|
|
|
|
|
|
? 0 |
83
|
|
|
|
|
|
|
: !ref($unk) |
84
|
|
|
|
|
|
|
? length($unk) |
85
|
|
|
|
|
|
|
: isa($unk, 'HASH') |
86
|
|
|
|
|
|
|
? Hub::sizeof([keys %$unk]) |
87
|
|
|
|
|
|
|
: isa($unk, 'ARRAY') |
88
|
|
|
|
|
|
|
? $#$unk + 1 |
89
|
|
|
|
|
|
|
: ref($unk) =~ /^(SCALAR|REF)$/ |
90
|
|
|
|
|
|
|
? Hub::sizeof($$unk) |
91
|
|
|
|
|
|
|
: croak("Cannot compute size of: $unk"); |
92
|
|
|
|
|
|
|
} |
93
|
0
|
|
|
|
|
0
|
return $result; |
94
|
|
|
|
|
|
|
}#sizeof |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
97
|
|
|
|
|
|
|
# check - True if all items in list pass the test. |
98
|
|
|
|
|
|
|
# |
99
|
|
|
|
|
|
|
# check [OPTIONS], [TEST], LIST |
100
|
|
|
|
|
|
|
# |
101
|
|
|
|
|
|
|
# OPTIONS: |
102
|
|
|
|
|
|
|
# |
103
|
|
|
|
|
|
|
# -opr (or|and|xor) Operator (default: 'and') |
104
|
|
|
|
|
|
|
# |
105
|
|
|
|
|
|
|
# TEST: |
106
|
|
|
|
|
|
|
# |
107
|
|
|
|
|
|
|
# -test (def|num|str|match|blessed|eval) Test type (default: 'def') |
108
|
|
|
|
|
|
|
# -isa EXPR |
109
|
|
|
|
|
|
|
# -ref EXPR |
110
|
|
|
|
|
|
|
# |
111
|
|
|
|
|
|
|
# OPERATORS: |
112
|
|
|
|
|
|
|
# |
113
|
|
|
|
|
|
|
# and True when all items pass the test. |
114
|
|
|
|
|
|
|
# or True when any single item passes the test. |
115
|
|
|
|
|
|
|
# xor Alternation pattern. True unless two consecutive values |
116
|
|
|
|
|
|
|
# both pass or fail the test. |
117
|
|
|
|
|
|
|
# |
118
|
|
|
|
|
|
|
# BASIC TEST: |
119
|
|
|
|
|
|
|
# |
120
|
|
|
|
|
|
|
# def Items are defined |
121
|
|
|
|
|
|
|
# num Items are numeric |
122
|
|
|
|
|
|
|
# str Items are *not* numeric |
123
|
|
|
|
|
|
|
# |
124
|
|
|
|
|
|
|
# OTHER TESTS: |
125
|
|
|
|
|
|
|
# |
126
|
|
|
|
|
|
|
# match=EXPR Items match EXPR |
127
|
|
|
|
|
|
|
# eval Items are eval'd and truth is based on $@. Note that the |
128
|
|
|
|
|
|
|
# eval *actually* happens, so don't do anything that will |
129
|
|
|
|
|
|
|
# break your code. The intention of this check is for: |
130
|
|
|
|
|
|
|
# |
131
|
|
|
|
|
|
|
#|test(!abort) my $compression = check( '-test=eval', 'use IO::Zlib' ) ? 1 : 0; |
132
|
|
|
|
|
|
|
# |
133
|
|
|
|
|
|
|
# STRUCTURE TESTS: |
134
|
|
|
|
|
|
|
# |
135
|
|
|
|
|
|
|
# blessed Items are blessed |
136
|
|
|
|
|
|
|
# ref=EXPR Item's ref matches EXPR (does *not* include @ISA) |
137
|
|
|
|
|
|
|
# isa=EXPR Item's ref or @ISA match EXPR. Much like UNIVERSAL::isa |
138
|
|
|
|
|
|
|
# except allows regular expressions. |
139
|
|
|
|
|
|
|
# |
140
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
141
|
|
|
|
|
|
|
#|test(false) check( undef, undef, undef ); # none are defined |
142
|
|
|
|
|
|
|
#|test(false) check( 1, undef ); # only one is defined |
143
|
|
|
|
|
|
|
#|test(true) check( 1, 1 ); # both are defined |
144
|
|
|
|
|
|
|
#|test(true) check( 1, undef, -opr => 'or' ); # one is defined |
145
|
|
|
|
|
|
|
#| |
146
|
|
|
|
|
|
|
#|test(false) check( -opr => 'xor', 1, 1 ); |
147
|
|
|
|
|
|
|
#|test(false) check( -opr => 'xor', undef, undef ); |
148
|
|
|
|
|
|
|
#| |
149
|
|
|
|
|
|
|
#|test(true) check( -opr => 'xor', undef, 1 ); |
150
|
|
|
|
|
|
|
#|test(true) check( -opr => 'xor', 1, undef ); |
151
|
|
|
|
|
|
|
#| |
152
|
|
|
|
|
|
|
#|test(true) check( -opr => 'xor', 1, undef, 1, undef ); |
153
|
|
|
|
|
|
|
#|test(false) check( -opr => 'xor', 1, undef, 1, 1, undef ); |
154
|
|
|
|
|
|
|
#|test(true) check( -opr => 'xor', undef, 1, undef, 1 ); |
155
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub check { |
158
|
|
|
|
|
|
|
|
159
|
37
|
|
|
37
|
1
|
176
|
my $opts = { |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
'test' => 'def', |
162
|
|
|
|
|
|
|
'opr' => 'and', |
163
|
|
|
|
|
|
|
'match' => '', |
164
|
|
|
|
|
|
|
'isa' => '', |
165
|
|
|
|
|
|
|
'ref' => '', |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
}; |
168
|
|
|
|
|
|
|
|
169
|
37
|
|
|
|
|
91
|
Hub::opts( \@_, $opts ); |
170
|
|
|
|
|
|
|
|
171
|
37
|
50
|
|
|
|
86
|
$$opts{'ref'} and $$opts{'test'} = 'ref'; |
172
|
|
|
|
|
|
|
|
173
|
37
|
50
|
|
|
|
74
|
$$opts{'isa'} and $$opts{'test'} = 'isa'; |
174
|
|
|
|
|
|
|
|
175
|
37
|
|
|
|
|
55
|
my ($opt,$val) = ('',''); |
176
|
|
|
|
|
|
|
|
177
|
37
|
50
|
|
|
|
110
|
($opt,$val) = $$opts{'test'} =~ /^(\w+)=(.*)/ and do { |
178
|
|
|
|
|
|
|
|
179
|
0
|
|
|
|
|
0
|
$$opts{$opt} = $val; |
180
|
|
|
|
|
|
|
|
181
|
0
|
|
|
|
|
0
|
$$opts{'test'} = $opt; |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
}; |
184
|
|
|
|
|
|
|
|
185
|
37
|
0
|
|
|
|
79
|
my $ok = $$opts{'opr'} eq 'and' ? 1 : $$opts{'opr'} eq 'or' ? 0 : undef; |
|
|
50
|
|
|
|
|
|
186
|
|
|
|
|
|
|
|
187
|
37
|
|
|
|
|
92
|
for( my $i = 0; $i <= $#_; $i++ ) { |
188
|
|
|
|
|
|
|
|
189
|
37
|
|
|
|
|
43
|
my $result = 0; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# Test item |
192
|
|
|
|
|
|
|
|
193
|
37
|
50
|
|
|
|
83
|
$$opts{'test'} eq 'def' and $result = defined $_[$i]; |
194
|
|
|
|
|
|
|
|
195
|
37
|
50
|
|
|
|
80
|
$$opts{'test'} eq 'num' and $result = $_[$i] =~ EXPR_NUMERIC; |
196
|
|
|
|
|
|
|
|
197
|
37
|
50
|
|
|
|
72
|
$$opts{'test'} eq 'str' and $result = $_[$i] !~ EXPR_NUMERIC; |
198
|
|
|
|
|
|
|
|
199
|
37
|
50
|
|
|
|
76
|
$$opts{'test'} eq 'match' and $result = $_[$i] =~ /$$opts{'match'}/; |
200
|
|
|
|
|
|
|
|
201
|
37
|
50
|
|
|
|
192
|
$$opts{'test'} eq 'blessed' and $result = blessed( $_[$i] ) ? 1 : 0; |
|
|
50
|
|
|
|
|
|
202
|
|
|
|
|
|
|
|
203
|
37
|
50
|
|
|
|
95
|
$$opts{'test'} eq 'isa' and $result = isa($_[$i], $$opts{'isa'}); |
204
|
|
|
|
|
|
|
|
205
|
37
|
50
|
|
|
|
81
|
$$opts{'test'} eq 'ref' and do { |
206
|
|
|
|
|
|
|
|
207
|
0
|
0
|
0
|
|
|
0
|
if( ref($_[$i]) && $$opts{'ref'} ) { |
208
|
|
|
|
|
|
|
|
209
|
0
|
|
|
|
|
0
|
$result = scalar($_[$i]) =~ $$opts{'ref'}; |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
}#if |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
}; |
214
|
|
|
|
|
|
|
|
215
|
37
|
50
|
|
|
|
120
|
$$opts{'test'} eq 'eval' and do { |
216
|
|
|
|
|
|
|
|
217
|
1
|
|
|
1
|
|
7
|
no warnings; # useless use of eval return |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6218
|
|
218
|
|
|
|
|
|
|
|
219
|
0
|
|
|
|
|
0
|
ref($_[$i]) eq 'CODE' ? eval &{ $_[$i] } |
220
|
0
|
0
|
|
|
|
0
|
: !ref($_[$i]) ? eval { "$_[$i]" } |
|
0
|
0
|
|
|
|
0
|
|
221
|
|
|
|
|
|
|
: croak 'Cannot eval: $_[$i]'; |
222
|
|
|
|
|
|
|
|
223
|
0
|
|
|
|
|
0
|
$result = !$@; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
}; |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# Assign result |
228
|
|
|
|
|
|
|
|
229
|
37
|
50
|
|
|
|
94
|
$ok &= $result if( $$opts{'opr'} eq 'and' ); |
230
|
|
|
|
|
|
|
|
231
|
37
|
50
|
|
|
|
74
|
$ok |= $result if( $$opts{'opr'} eq 'or' ); |
232
|
|
|
|
|
|
|
|
233
|
37
|
50
|
|
|
|
138
|
if( $$opts{'opr'} eq 'xor' ) { |
234
|
|
|
|
|
|
|
|
235
|
0
|
0
|
|
|
|
0
|
if( ($i % 2) == 0 ) { |
236
|
|
|
|
|
|
|
|
237
|
0
|
|
|
|
|
0
|
$ok = $result; |
238
|
|
|
|
|
|
|
|
239
|
0
|
|
|
|
|
0
|
next; |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
} else { |
242
|
|
|
|
|
|
|
|
243
|
0
|
|
|
|
|
0
|
$ok ^= $result; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
}#if |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
}#if |
248
|
|
|
|
|
|
|
|
249
|
37
|
50
|
|
|
|
168
|
last unless $ok; |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
}#for |
252
|
|
|
|
|
|
|
|
253
|
37
|
|
|
|
|
287
|
return $ok; |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
}#check |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
258
|
|
|
|
|
|
|
# opts [OPTIONS], \ARRAY, [\HASH] |
259
|
|
|
|
|
|
|
# |
260
|
|
|
|
|
|
|
# Split parameter arrays into options and arguments. |
261
|
|
|
|
|
|
|
# |
262
|
|
|
|
|
|
|
# OPTIONS: |
263
|
|
|
|
|
|
|
# |
264
|
|
|
|
|
|
|
# -prefix=EXPR # specify option prefix, default is single dash (-). |
265
|
|
|
|
|
|
|
# |
266
|
|
|
|
|
|
|
# -assign=EXPR # specify assignment character, default is the |
267
|
|
|
|
|
|
|
# equal sign (=). |
268
|
|
|
|
|
|
|
# |
269
|
|
|
|
|
|
|
# -append=EXPR # specify append character, default is the |
270
|
|
|
|
|
|
|
# plus sign (+). |
271
|
|
|
|
|
|
|
# |
272
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
273
|
|
|
|
|
|
|
# |
274
|
|
|
|
|
|
|
# In array context, we return two references. Which may cause confusion: |
275
|
|
|
|
|
|
|
# |
276
|
|
|
|
|
|
|
# my %opts = Hub::opts( \@_ ); # Wrong! |
277
|
|
|
|
|
|
|
# my $opts = Hub::opts( \@_ ); # Correct! |
278
|
|
|
|
|
|
|
# my ($opts,$args) = Hub::opts( \@_ ); # Correct! |
279
|
|
|
|
|
|
|
# |
280
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
281
|
|
|
|
|
|
|
# |
282
|
|
|
|
|
|
|
# Options are extracted (via splice) from the referenced array. The advantage |
283
|
|
|
|
|
|
|
# is both for performance (don't make a copy of the array), and so you may |
284
|
|
|
|
|
|
|
# use @_ (or @ARGV, etc) normally, as data: |
285
|
|
|
|
|
|
|
# |
286
|
|
|
|
|
|
|
#|test(match,a;b;c;d) # at-underscore contains everyting but the '-with' option |
287
|
|
|
|
|
|
|
#| |
288
|
|
|
|
|
|
|
#| sub myjoin { |
289
|
|
|
|
|
|
|
#| my $opts = Hub::opts( \@_ ); |
290
|
|
|
|
|
|
|
#| return join( $$opts{'with'}, @_ ); |
291
|
|
|
|
|
|
|
#| } |
292
|
|
|
|
|
|
|
#| |
293
|
|
|
|
|
|
|
#| myjoin( 'a', 'b', '-with=;', 'c', 'd' ); |
294
|
|
|
|
|
|
|
# |
295
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
296
|
|
|
|
|
|
|
# |
297
|
|
|
|
|
|
|
# 1. Arguments are elements which do *not* begin with a dash (-). |
298
|
|
|
|
|
|
|
# |
299
|
|
|
|
|
|
|
# 2. Options are elements which begin with a B dash (-) and are not |
300
|
|
|
|
|
|
|
# negative numbers. |
301
|
|
|
|
|
|
|
# |
302
|
|
|
|
|
|
|
# 3. An option of '-opts' is reserved for passing in already parsed option |
303
|
|
|
|
|
|
|
# hashes. |
304
|
|
|
|
|
|
|
# |
305
|
|
|
|
|
|
|
# 4. Options will have their leading dash (-) removed. |
306
|
|
|
|
|
|
|
# |
307
|
|
|
|
|
|
|
# 5. Options values are formed as: |
308
|
|
|
|
|
|
|
# |
309
|
|
|
|
|
|
|
# Given: opt1 will be: because: |
310
|
|
|
|
|
|
|
# |
311
|
|
|
|
|
|
|
# -opt1=value 'value' contains an equal sign |
312
|
|
|
|
|
|
|
# -opt1 nextelem 'nextelem' next element is *not* an option |
313
|
|
|
|
|
|
|
# -opt1 -option2 1 next element is also an option |
314
|
|
|
|
|
|
|
# -opt1 1 it is the last element |
315
|
|
|
|
|
|
|
# -opt1 1 it is the last element |
316
|
|
|
|
|
|
|
# -opt1=a -opt1=b b last one wins |
317
|
|
|
|
|
|
|
# -opt1=a +opt1=b [ 'a', 'b' ] it was specified using '+' |
318
|
|
|
|
|
|
|
# +opt1=a +opt1=b [ 'a', 'b' ] they can both be '+' |
319
|
|
|
|
|
|
|
# |
320
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
321
|
|
|
|
|
|
|
# |
322
|
|
|
|
|
|
|
# For example: |
323
|
|
|
|
|
|
|
# |
324
|
|
|
|
|
|
|
# my($opts,$args) = Hub::opts( [ 'a', 'b', '-c' => 'c', '-x', '-o=out' ] ); |
325
|
|
|
|
|
|
|
# |
326
|
|
|
|
|
|
|
# print "Opts:\n", Hub::hprint( $opts ); |
327
|
|
|
|
|
|
|
# print "Args:\n", Hub::hprint( $args ); |
328
|
|
|
|
|
|
|
# |
329
|
|
|
|
|
|
|
# Will print: |
330
|
|
|
|
|
|
|
# |
331
|
|
|
|
|
|
|
# Opts: |
332
|
|
|
|
|
|
|
# c => c |
333
|
|
|
|
|
|
|
# o => out |
334
|
|
|
|
|
|
|
# x => 1 |
335
|
|
|
|
|
|
|
# Args: |
336
|
|
|
|
|
|
|
# a |
337
|
|
|
|
|
|
|
# b |
338
|
|
|
|
|
|
|
# |
339
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
sub opts { |
342
|
186
|
|
|
186
|
1
|
661
|
my $opts = { |
343
|
|
|
|
|
|
|
'append' => '\+', |
344
|
|
|
|
|
|
|
'prefix' => '-', |
345
|
|
|
|
|
|
|
'assign' => '=', |
346
|
|
|
|
|
|
|
}; |
347
|
186
|
|
|
|
|
278
|
my $argv = shift; |
348
|
186
|
100
|
|
|
|
403
|
my $options = ref($_[0]) eq 'HASH' ? shift : {}; |
349
|
186
|
|
|
|
|
275
|
my @remove = (); |
350
|
186
|
100
|
|
|
|
377
|
Hub::opts(\@_,$opts) if @_; |
351
|
186
|
50
|
33
|
|
|
908
|
croak "Provide an array reference" if defined $argv && not isa($argv, 'ARRAY'); |
352
|
186
|
100
|
66
|
|
|
1022
|
return $options unless defined $argv && @$argv; |
353
|
153
|
|
|
|
|
359
|
for( my $idx = 0; $idx <= $#$argv; $idx++ ) { |
354
|
274
|
100
|
|
|
|
882
|
next unless defined $$argv[$idx]; |
355
|
265
|
100
|
|
|
|
594
|
next if ref( $$argv[$idx] ); |
356
|
211
|
100
|
|
|
|
1694
|
if( my($prefix,$k) = |
357
|
|
|
|
|
|
|
$$argv[$idx] =~/^($$opts{'append'}|$$opts{'prefix'})((?!\d|$$opts{'prefix'}).*)$/ ) { |
358
|
141
|
50
|
|
|
|
266
|
next unless $k; |
359
|
141
|
50
|
|
|
|
683
|
if( $k eq 'opts' ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
360
|
0
|
0
|
|
|
|
0
|
Hub::merge( $options, $$argv[$idx+1], -overwrite => 1 ) |
361
|
|
|
|
|
|
|
if defined $$argv[$idx+1]; |
362
|
0
|
|
|
|
|
0
|
push @remove, ($idx, $idx+1); |
363
|
|
|
|
|
|
|
} elsif( $k =~ /$$opts{'assign'}/ ) { |
364
|
47
|
|
|
|
|
325
|
my ($k2,$v) = $k =~ /([^$$opts{'assign'}]+)?$$opts{'assign'}(.*)/; |
365
|
47
|
|
|
|
|
108
|
_assignopt( $opts, $options, $k2, $v, $prefix ); |
366
|
47
|
|
|
|
|
181
|
push @remove, $idx; |
367
|
|
|
|
|
|
|
} elsif( $idx < $#$argv ) { |
368
|
86
|
50
|
33
|
|
|
763
|
if( !defined $$argv[$idx+1] |
|
|
|
33
|
|
|
|
|
369
|
|
|
|
|
|
|
|| ( (defined $$argv[$idx+1]) |
370
|
|
|
|
|
|
|
&& $$argv[$idx+1] !~ /^($$opts{'append'}|$$opts{'prefix'})(?!\d)/) ) { |
371
|
86
|
|
|
|
|
486
|
_assignopt( $opts, $options, $k, $$argv[++$idx], $prefix ); |
372
|
86
|
|
|
|
|
342
|
push @remove, ( ($idx-1), $idx ); |
373
|
|
|
|
|
|
|
} else { |
374
|
0
|
|
|
|
|
0
|
_assignopt( $opts, $options, $k, 1, $prefix ); |
375
|
0
|
|
|
|
|
0
|
push @remove, $idx; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
} else { |
378
|
8
|
|
|
|
|
19
|
_assignopt( $opts, $options, $k, 1, $prefix ); |
379
|
8
|
|
|
|
|
29
|
push @remove, $idx; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
} |
383
|
153
|
|
|
|
|
190
|
my $offset = 0; |
384
|
153
|
|
|
|
|
205
|
map { splice @$argv, $_ - $offset++, 1 } @remove; |
|
227
|
|
|
|
|
382
|
|
385
|
153
|
100
|
|
|
|
340
|
wantarray and return ($options,@$argv); |
386
|
136
|
|
|
|
|
379
|
return $options; |
387
|
|
|
|
|
|
|
}#opts |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
390
|
|
|
|
|
|
|
# objopts - Split @_ into ($self,$opts), leaving @_ with remaining items. |
391
|
|
|
|
|
|
|
# objopts \@params, [\%defaults] |
392
|
|
|
|
|
|
|
# |
393
|
|
|
|
|
|
|
# Convienence method for splitting instance method parameters. |
394
|
|
|
|
|
|
|
# Returns an array. |
395
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
396
|
|
|
|
|
|
|
#|test(match) # Test return value |
397
|
|
|
|
|
|
|
#| |
398
|
|
|
|
|
|
|
#| my $obj = mkinst( 'Object' ); |
399
|
|
|
|
|
|
|
#| my @result = objopts( [ $obj ] ); |
400
|
|
|
|
|
|
|
#| join( ',', map { ref($_) } @result ); |
401
|
|
|
|
|
|
|
#| |
402
|
|
|
|
|
|
|
#=Hub::Base::Object, |
403
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
404
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
sub objopts { |
406
|
36
|
|
|
36
|
1
|
48
|
my $params = shift; |
407
|
36
|
|
|
|
|
46
|
my $defaults = shift; |
408
|
36
|
|
|
|
|
52
|
my $self = $$params[0]; # not shifted |
409
|
36
|
|
|
|
|
41
|
shift @$params; |
410
|
36
|
|
|
|
|
130
|
Hub::expect(-blessed => $self, -back => 1); |
411
|
36
|
100
|
|
|
|
108
|
my $opts = Hub::opts($params, $defaults) if @$params; |
412
|
36
|
|
|
|
|
130
|
return($self, $opts, @$params); |
413
|
|
|
|
|
|
|
}#objopts |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
416
|
|
|
|
|
|
|
# cmdopts - Extract short and long options from @ARGV |
417
|
|
|
|
|
|
|
# cmdopts \@arguments |
418
|
|
|
|
|
|
|
# cmdopts \@arguments, \%default_options |
419
|
|
|
|
|
|
|
# |
420
|
|
|
|
|
|
|
# Single-dash paramaters are always boolean flags. Flags are broken apart such |
421
|
|
|
|
|
|
|
# that: |
422
|
|
|
|
|
|
|
# |
423
|
|
|
|
|
|
|
# -lal |
424
|
|
|
|
|
|
|
# |
425
|
|
|
|
|
|
|
# becomes |
426
|
|
|
|
|
|
|
# |
427
|
|
|
|
|
|
|
# -l -a -l |
428
|
|
|
|
|
|
|
# |
429
|
|
|
|
|
|
|
# To create a list (ARRAY) of items, use '++' where you would normally use '--'. |
430
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
431
|
|
|
|
|
|
|
#|test(match,a-b-c) |
432
|
|
|
|
|
|
|
#| my $opts = cmdopts(['--letters=a', '++letters=b', '++letters=c']); |
433
|
|
|
|
|
|
|
#| join('-', @{$$opts{'letters'}}); |
434
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
sub cmdopts { |
437
|
1
|
|
|
1
|
1
|
2
|
my $argv = shift; |
438
|
1
|
|
|
|
|
2
|
my @flags = (); |
439
|
|
|
|
|
|
|
# Parse-out flags (single-dash parameters) |
440
|
1
|
|
|
|
|
1
|
my $i = 0; |
441
|
1
|
|
|
|
|
6
|
for (my $i = 0; $i < @$argv;) { |
442
|
0
|
|
|
|
|
0
|
my $arg = $$argv[$i]; |
443
|
0
|
0
|
|
|
|
0
|
if ($arg =~ /^-\w/) { |
444
|
0
|
|
|
|
|
0
|
push @flags, $arg =~ /(\w)/g; |
445
|
0
|
|
|
|
|
0
|
splice @$argv, $i, 1; |
446
|
|
|
|
|
|
|
} else { |
447
|
0
|
|
|
|
|
0
|
$i++; |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
# Parse double-dash parameters |
451
|
1
|
|
|
|
|
5
|
my $result = Hub::opts( $argv, @_, '-prefix=-{2}', '-append=\+{2}' ); |
452
|
|
|
|
|
|
|
# Inject flags in final result |
453
|
1
|
|
|
|
|
3
|
foreach my $flag (@flags) { |
454
|
0
|
0
|
|
|
|
0
|
$result->{$flag} = defined $$result{$flag} ? $$result{$flag} + 1 : 1; |
455
|
|
|
|
|
|
|
} |
456
|
1
|
|
|
|
|
7
|
return $result; |
457
|
|
|
|
|
|
|
}#cmdopts |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
460
|
|
|
|
|
|
|
# hashopts - Get options and parameters as a hash |
461
|
|
|
|
|
|
|
# hashopts \@parameters |
462
|
|
|
|
|
|
|
# |
463
|
|
|
|
|
|
|
# The purpose of this method is to even out the returned parameter list by |
464
|
|
|
|
|
|
|
# adding an undefined value if there are an odd number of elements in the list. |
465
|
|
|
|
|
|
|
# This avoids the Perl warning: |
466
|
|
|
|
|
|
|
# |
467
|
|
|
|
|
|
|
# Odd number of elements in hash assignment |
468
|
|
|
|
|
|
|
# |
469
|
|
|
|
|
|
|
# When parsing options as: |
470
|
|
|
|
|
|
|
# |
471
|
|
|
|
|
|
|
# my ($opts, %fields) = Hub::opts(...) |
472
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
473
|
|
|
|
|
|
|
#|test(!defined) |
474
|
|
|
|
|
|
|
#| my ($opts, %hash) = Hub::hashopts(['key1', -foo]); |
475
|
|
|
|
|
|
|
#| $hash{'key1'} |
476
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
sub hashopts { |
479
|
0
|
|
|
0
|
1
|
0
|
my ($opts, @fields) = Hub::opts(@_); |
480
|
0
|
0
|
|
|
|
0
|
push @fields, undef if ((scalar (@fields) % 2) != 0); |
481
|
0
|
|
|
|
|
0
|
return ($opts, @fields); |
482
|
|
|
|
|
|
|
}#hashopts |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
485
|
|
|
|
|
|
|
# _assignopt Assign an option value. |
486
|
|
|
|
|
|
|
# _assignopt \%options, \%dest, $key, $val |
487
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
sub _assignopt { |
490
|
141
|
|
|
141
|
|
186
|
my $opts = $_[0]; |
491
|
141
|
50
|
|
|
|
487
|
if( $_[4] !~ /^$$opts{'append'}$/ ) { |
492
|
141
|
|
|
|
|
305
|
$_[1]->{$_[2]} = $_[3]; |
493
|
141
|
|
|
|
|
260
|
return; |
494
|
|
|
|
|
|
|
}; |
495
|
0
|
0
|
|
|
|
0
|
if( defined $_[1]->{$_[2]} ) { |
496
|
0
|
0
|
|
|
|
0
|
if( ref($_[1]->{$_[2]}) eq 'ARRAY' ) { |
497
|
0
|
|
|
|
|
0
|
push @{$_[1]->{$_[2]}}, $_[3]; |
|
0
|
|
|
|
|
0
|
|
498
|
|
|
|
|
|
|
} else { |
499
|
0
|
|
|
|
|
0
|
my $v = $_[1]->{$_[2]}; |
500
|
0
|
|
|
|
|
0
|
$_[1]->{$_[2]} = [ $v, $_[3] ]; |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
} else { |
503
|
0
|
|
|
|
|
0
|
push @{$_[1]->{$_[2]}}, $_[3]; |
|
0
|
|
|
|
|
0
|
|
504
|
|
|
|
|
|
|
# $_[1]->{$_[2]} = $_[3]; |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
}#_assignopt |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
509
|
|
|
|
|
|
|
# subst |
510
|
|
|
|
|
|
|
# |
511
|
|
|
|
|
|
|
# Call to perl's substitution operator. Represented here as a function to |
512
|
|
|
|
|
|
|
# facilitate transformation by reducing the need for temporaries. In essence, |
513
|
|
|
|
|
|
|
# the goal is to reduce: |
514
|
|
|
|
|
|
|
# |
515
|
|
|
|
|
|
|
# my $bakname = getfilename(); |
516
|
|
|
|
|
|
|
# $bakname =~ s/\.db$/\.bak/; |
517
|
|
|
|
|
|
|
# |
518
|
|
|
|
|
|
|
# to: |
519
|
|
|
|
|
|
|
# |
520
|
|
|
|
|
|
|
# my $bakname = Hub::subst( getfilename(), '\.db$', '.bak' ); |
521
|
|
|
|
|
|
|
# |
522
|
|
|
|
|
|
|
# without modifying the original string returned by getfilename(). |
523
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
sub subst { |
526
|
0
|
|
|
0
|
1
|
0
|
my ($s,$l,$r,$m) = @_; |
527
|
|
|
|
|
|
|
# s string to operate on |
528
|
|
|
|
|
|
|
# l left-half of s/// operation |
529
|
|
|
|
|
|
|
# r right-half of s/// operation |
530
|
|
|
|
|
|
|
# m modifier for s/// operation |
531
|
0
|
0
|
|
|
|
0
|
return '' unless Hub::check( $s, $l, $r ); |
532
|
0
|
0
|
|
|
|
0
|
ref($s) eq 'SCALAR' and $s = $$s; |
533
|
0
|
|
0
|
|
|
0
|
$m ||= ''; |
534
|
0
|
|
|
|
|
0
|
eval( "\$s =~ s/$l/$r/$m" ); |
535
|
0
|
0
|
|
|
|
0
|
croak $@ if $@; |
536
|
0
|
|
|
|
|
0
|
return $s; |
537
|
|
|
|
|
|
|
}#subst |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
540
|
|
|
|
|
|
|
# getuid - Return the UID of the provided user |
541
|
|
|
|
|
|
|
# getuid $user_name |
542
|
|
|
|
|
|
|
# If perl has not been compiled with 'getpwnam', $user_name is returned. |
543
|
|
|
|
|
|
|
# -1 is returned when no user is found |
544
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
sub getuid { |
547
|
0
|
0
|
|
0
|
1
|
0
|
return $_[0] if Hub::check($_[0], -test => 'num'); |
548
|
0
|
0
|
|
|
|
0
|
if ($HAS_GETPWNAM) { |
549
|
0
|
0
|
|
|
|
0
|
my ($login,$pass,$uid,$gid) = getpwnam($_[0]) or return -1; |
550
|
0
|
|
|
|
|
0
|
return $uid; |
551
|
|
|
|
|
|
|
} else { |
552
|
0
|
|
|
|
|
0
|
return $_[0]; |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
}#getuid |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
557
|
|
|
|
|
|
|
# getgid - Return the GID of the provided group |
558
|
|
|
|
|
|
|
# getgid - $group_name |
559
|
|
|
|
|
|
|
# If perl has not been compiled with 'getgrnam', $group_name is returned. |
560
|
|
|
|
|
|
|
# -1 is returned when no group is found |
561
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
sub getgid { |
564
|
0
|
0
|
|
0
|
1
|
0
|
return $_[0] if Hub::check($_[0], -test => 'num'); |
565
|
0
|
0
|
|
|
|
0
|
if ($HAS_GETGRNAM) { |
566
|
0
|
0
|
|
|
|
0
|
my ($name,$passwd,$gid,$members) = getgrnam($_[0]) or return -1; |
567
|
0
|
|
|
|
|
0
|
return $gid; |
568
|
|
|
|
|
|
|
} else { |
569
|
0
|
|
|
|
|
0
|
return $_[0]; |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
}#getgid |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
574
|
|
|
|
|
|
|
# touch LIST |
575
|
|
|
|
|
|
|
# |
576
|
|
|
|
|
|
|
# Changes the access and modification times on each file of a list of files. |
577
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
sub touch { |
580
|
0
|
0
|
|
0
|
1
|
0
|
map { Hub::writefile( $_, '' ) unless -e $_ } @_; |
|
0
|
|
|
|
|
0
|
|
581
|
0
|
|
|
|
|
0
|
my $t = time; |
582
|
0
|
|
|
|
|
0
|
utime $t, $t, @_; |
583
|
|
|
|
|
|
|
}#touch |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
586
|
|
|
|
|
|
|
# expect - Croak if arguments do not match their expected type |
587
|
|
|
|
|
|
|
# expect [OPTIONS], [TEST], LIST |
588
|
|
|
|
|
|
|
# |
589
|
|
|
|
|
|
|
# OPTIONS: |
590
|
|
|
|
|
|
|
# |
591
|
|
|
|
|
|
|
# -back \d # Carp level (for reporting further up the callstack) |
592
|
|
|
|
|
|
|
# -not 0|1 # Invert the result |
593
|
|
|
|
|
|
|
# |
594
|
|
|
|
|
|
|
# TESTS: |
595
|
|
|
|
|
|
|
# |
596
|
|
|
|
|
|
|
# -blessed # All LIST items are blessed |
597
|
|
|
|
|
|
|
# -match=EXPR # All LIST items match /EXPR/ |
598
|
|
|
|
|
|
|
# -ref=EXPR # All LIST items' ref match /EXPR/ |
599
|
|
|
|
|
|
|
# |
600
|
|
|
|
|
|
|
# By default, LIST is made up of key/value pairs, where the key is the type |
601
|
|
|
|
|
|
|
# (what ref() will return) and the value is what will be tested. LIST may |
602
|
|
|
|
|
|
|
# contain one or more key/value pairs such as: |
603
|
|
|
|
|
|
|
# |
604
|
|
|
|
|
|
|
# HASH => arg |
605
|
|
|
|
|
|
|
# REF => arg |
606
|
|
|
|
|
|
|
# My::Package => arg |
607
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
608
|
|
|
|
|
|
|
#|test(true) Hub::expect( -match => 'and|or|xor', 'and' ); |
609
|
|
|
|
|
|
|
#|test(true) Hub::expect( HASH => {}, HASH => {} ); |
610
|
|
|
|
|
|
|
#|test(abort) Hub::expect( -blessed => {} ); |
611
|
|
|
|
|
|
|
#|test(true) Hub::expect( -blessed => mkinst( 'Object' ) ); |
612
|
|
|
|
|
|
|
#|test(abort) Hub::expect( -match => 'and|or|xor', 'if', 'or', 'and' ); |
613
|
|
|
|
|
|
|
#|test(abort) Hub::expect( ARRAY => {} ); |
614
|
|
|
|
|
|
|
#|test(abort) Hub::expect( -blessed => 'abc' ); |
615
|
|
|
|
|
|
|
#|test(true) Hub::expect( -ref => 'HASH', {} ); |
616
|
|
|
|
|
|
|
#|test(true) Hub::expect( -ref => 'HASH', mkinst('Object') ); |
617
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
sub expect { |
620
|
36
|
|
|
36
|
1
|
81
|
my $opts = Hub::opts( \@_ ); |
621
|
36
|
50
|
|
|
|
84
|
my $invert = defined $$opts{'not'} ? 1 : 0; |
622
|
36
|
|
|
|
|
50
|
delete $$opts{'not'}; |
623
|
36
|
|
50
|
|
|
93
|
my $back = $$opts{'back'} || 0; |
624
|
36
|
50
|
|
|
|
89
|
if( $$opts{'match'} ) { |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
625
|
0
|
0
|
0
|
|
|
0
|
abort( -back => $back, -msg => "Expected: $$opts{'match'}" ) |
626
|
|
|
|
|
|
|
unless( Hub::check( "-test=match=$$opts{'match'}", @_ ) |
627
|
|
|
|
|
|
|
xor $invert ); |
628
|
0
|
|
|
|
|
0
|
@_ = (); |
629
|
|
|
|
|
|
|
} elsif( $$opts{'blessed'} ) { |
630
|
36
|
50
|
25
|
|
|
123
|
abort( -back => $back, -msg => "Expected: blessed" ) |
631
|
|
|
|
|
|
|
unless( Hub::check( "-test=blessed", $$opts{'blessed'}, @_ ) |
632
|
|
|
|
|
|
|
xor $invert ); |
633
|
|
|
|
|
|
|
} elsif( $$opts{'ref'} ) { |
634
|
0
|
0
|
0
|
|
|
0
|
abort( -back => $back, -msg => "Expected: hashable" ) |
635
|
|
|
|
|
|
|
unless( Hub::check( "-ref=$$opts{'ref'}", @_ ) |
636
|
|
|
|
|
|
|
xor $invert ); |
637
|
|
|
|
|
|
|
} else { |
638
|
0
|
|
|
|
|
0
|
while( my ($k,$v) = (shift,shift) ) { |
639
|
0
|
0
|
|
|
|
0
|
last unless defined $k; |
640
|
0
|
0
|
|
|
|
0
|
abort( -back => $back, -msg => "Expected: '$k', got '" |
|
|
0
|
|
|
|
|
|
641
|
|
|
|
|
|
|
. ref($v) . "'" ) |
642
|
|
|
|
|
|
|
if( $invert ? ref($v) eq $k : ref($v) ne $k ); |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
} |
645
|
36
|
|
|
|
|
86
|
1; |
646
|
|
|
|
|
|
|
}#expect |
647
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
649
|
|
|
|
|
|
|
# Croak if arguments match their feared type. |
650
|
|
|
|
|
|
|
# This is a shortcut to L with a '-not=1' option. |
651
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
652
|
|
|
|
|
|
|
#|test(abort) Hub::fear( HASH => {} ); |
653
|
|
|
|
|
|
|
#|test(true) Hub::fear( HASH => [] ); |
654
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
sub fear { |
657
|
0
|
|
|
0
|
1
|
0
|
return Hub::expect( '-not=1', @_ ); |
658
|
|
|
|
|
|
|
}#fear |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
661
|
|
|
|
|
|
|
# abort - Croak nicely. |
662
|
|
|
|
|
|
|
# abort -msg => 'Croak message' |
663
|
|
|
|
|
|
|
# abort -back => LEVEL |
664
|
|
|
|
|
|
|
# |
665
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
666
|
|
|
|
|
|
|
#|test(abort) abort( -msg => 'Goddamn hippies' ); |
667
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
sub abort { |
670
|
0
|
|
|
0
|
1
|
0
|
my $opts = Hub::opts(\@_); |
671
|
0
|
|
0
|
|
|
0
|
$$opts{'msg'} ||= $@; |
672
|
0
|
|
0
|
|
|
0
|
$$opts{'msg'} ||= $!; |
673
|
0
|
0
|
|
|
|
0
|
$$opts{'back'} = 1 unless defined $$opts{'back'}; |
674
|
0
|
|
|
|
|
0
|
$Carp::CarpLevel = $$opts{'back'}; |
675
|
0
|
|
|
|
|
0
|
croak $$opts{'msg'}; |
676
|
|
|
|
|
|
|
}#abort |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
679
|
|
|
|
|
|
|
# bestof @list |
680
|
|
|
|
|
|
|
# bestof @list, -by=max|min|def|len|gt|lt|true |
681
|
|
|
|
|
|
|
# |
682
|
|
|
|
|
|
|
# Best value by criteria (default 'def'). |
683
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
sub bestof { |
686
|
31
|
|
|
31
|
1
|
84
|
my $opts = Hub::opts( \@_ ); |
687
|
31
|
|
50
|
|
|
167
|
$$opts{'by'} ||= 'def'; |
688
|
31
|
|
|
|
|
109
|
my $best = $_[0]; |
689
|
31
|
|
|
|
|
85
|
for( my $i = 1; $i <= $#_; $i++ ) { |
690
|
31
|
100
|
|
|
|
59
|
if( not defined $best ) { |
691
|
1
|
|
|
|
|
4
|
$best = $_[$i]; |
692
|
1
|
50
|
33
|
|
|
15
|
($$opts{'by'} eq 'def') && (defined $best) and last; |
693
|
0
|
|
|
|
|
0
|
next; |
694
|
|
|
|
|
|
|
} |
695
|
30
|
50
|
33
|
|
|
122
|
if( defined $_[$i] && defined $best ) { |
696
|
30
|
|
|
|
|
44
|
my $isbetter = 0; |
697
|
30
|
50
|
|
|
|
86
|
$$opts{'by'} eq 'gt' and $isbetter = $_[$i] gt $best; |
698
|
30
|
50
|
|
|
|
62
|
$$opts{'by'} eq 'lt' and $isbetter = $_[$i] lt $best; |
699
|
30
|
50
|
33
|
|
|
79
|
$$opts{'by'} eq 'max' and Hub::check( '-test=num', $_[$i], $best ) |
700
|
|
|
|
|
|
|
and $isbetter = $_[$i] > $best; |
701
|
30
|
50
|
33
|
|
|
79
|
$$opts{'by'} eq 'min' and Hub::check( '-test=num', $_[$i], $best ) |
702
|
|
|
|
|
|
|
and $isbetter = $_[$i] < $best; |
703
|
30
|
50
|
|
|
|
64
|
$$opts{'by'} eq 'len' and $isbetter = length($_[$i]) > length($best); |
704
|
30
|
0
|
0
|
|
|
64
|
$$opts{'by'} eq 'true' and $isbetter = |
|
|
0
|
0
|
|
|
|
|
|
|
50
|
|
|
|
|
|
705
|
|
|
|
|
|
|
defined $best && $best |
706
|
|
|
|
|
|
|
? 0 # should call 'last' here |
707
|
|
|
|
|
|
|
: defined $_[$i] && $_[$i] |
708
|
|
|
|
|
|
|
? 1 |
709
|
|
|
|
|
|
|
: 0; |
710
|
30
|
50
|
|
|
|
101
|
$isbetter and $best = $_[$i]; |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
} |
713
|
31
|
|
|
|
|
199
|
return $best; |
714
|
|
|
|
|
|
|
}#bestof |
715
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
717
|
|
|
|
|
|
|
# min - Minimum value |
718
|
|
|
|
|
|
|
# |
719
|
|
|
|
|
|
|
# min @LIST |
720
|
|
|
|
|
|
|
# |
721
|
|
|
|
|
|
|
# Returns the least element in a set. |
722
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
723
|
|
|
|
|
|
|
#|test(match,1) Hub::min(1,2); # Two integers |
724
|
|
|
|
|
|
|
#|test(match,1) Hub::min(2,1,3); # Three integers |
725
|
|
|
|
|
|
|
#|test(match,-1) Hub::min(2,-1,3); # Three integers |
726
|
|
|
|
|
|
|
#|test(match,1) Hub::min(1); # One integer |
727
|
|
|
|
|
|
|
#|test(match,1) Hub::min(1,undef); # Undefined value |
728
|
|
|
|
|
|
|
#|test(match,0) Hub::min(undef,1,0); # Zero |
729
|
|
|
|
|
|
|
#|test(match,0.009) Hub::min(.009,1.001); # Three decimal values |
730
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
sub min { |
733
|
0
|
|
|
0
|
1
|
0
|
return Hub::bestof( -by => 'min', @_ ); |
734
|
|
|
|
|
|
|
}#min |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
737
|
|
|
|
|
|
|
# max - Maximum value |
738
|
|
|
|
|
|
|
# |
739
|
|
|
|
|
|
|
# max @LIST |
740
|
|
|
|
|
|
|
# |
741
|
|
|
|
|
|
|
# Returns the greatest element in a set. |
742
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
743
|
|
|
|
|
|
|
#|test(match,2) Hub::max(.009,-1.01,2,undef,0); # Three decimal values |
744
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
sub max { |
747
|
0
|
|
|
0
|
1
|
0
|
return Hub::bestof( -by => 'max', @_ ); |
748
|
|
|
|
|
|
|
}#max |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
751
|
|
|
|
|
|
|
# intdiv - Integer division |
752
|
|
|
|
|
|
|
# |
753
|
|
|
|
|
|
|
# intdiv $DIVIDEND, $DIVISOR |
754
|
|
|
|
|
|
|
# |
755
|
|
|
|
|
|
|
# Returns an array with the number of times the divisor is contained in the |
756
|
|
|
|
|
|
|
# dividend, and the remainder. |
757
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
758
|
|
|
|
|
|
|
#|test(match) join(',',Hub::intdiv(3,2)); # 3 divided by 2 is 1R1 |
759
|
|
|
|
|
|
|
#=1,1 |
760
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
sub intdiv { |
763
|
0
|
|
|
0
|
1
|
0
|
my ($dividend,$divisor) = @_; |
764
|
0
|
0
|
|
|
|
0
|
return( undef, undef ) if $divisor == 0; |
765
|
0
|
|
|
|
|
0
|
return( int( $dividend / $divisor ), ( $dividend % $divisor ) ); |
766
|
|
|
|
|
|
|
}#intdiv |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
769
|
|
|
|
|
|
|
# given a hash reference, swap keys with values and return a new hash reference. |
770
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
sub flip { |
773
|
0
|
|
0
|
0
|
1
|
0
|
my $hash = shift || return undef; |
774
|
0
|
|
|
|
|
0
|
my $new_hash = {}; |
775
|
0
|
0
|
|
|
|
0
|
if (isa($hash, 'HASH')) { |
776
|
0
|
|
|
|
|
0
|
keys %$hash; # reset |
777
|
0
|
|
|
|
|
0
|
while (my ($k,$v) = each %$hash) { |
778
|
0
|
0
|
|
|
|
0
|
if ($$new_hash{$v}) { |
779
|
0
|
0
|
|
|
|
0
|
$$new_hash{$v} = [$$new_hash{$v}] unless isa($$new_hash{$v}, 'ARRAY'); |
780
|
0
|
|
|
|
|
0
|
push @{$$new_hash{$v}}, $k; |
|
0
|
|
|
|
|
0
|
|
781
|
|
|
|
|
|
|
} else { |
782
|
0
|
|
|
|
|
0
|
$$new_hash{$v} = $k; |
783
|
|
|
|
|
|
|
} |
784
|
|
|
|
|
|
|
} |
785
|
|
|
|
|
|
|
} |
786
|
0
|
|
|
|
|
0
|
return $new_hash; |
787
|
|
|
|
|
|
|
}#flip |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
790
|
|
|
|
|
|
|
# rmval - Remove matching elements from a hash or an array. |
791
|
|
|
|
|
|
|
# rmval \@array, $value |
792
|
|
|
|
|
|
|
# rmval \%hash, $value |
793
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
794
|
|
|
|
|
|
|
#|test(match,124) join('',@{rmval([1,2,3,4],3)}); |
795
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
sub rmval { |
798
|
0
|
|
|
0
|
1
|
0
|
my ($container, $value) = @_; |
799
|
0
|
0
|
|
|
|
0
|
if (isa($container, 'HASH')) { |
|
|
0
|
|
|
|
|
|
800
|
0
|
|
|
|
|
0
|
foreach my $key ( keys %$container ) { |
801
|
0
|
0
|
|
|
|
0
|
if( $$container{$key} eq $value ) { |
802
|
0
|
|
|
|
|
0
|
delete $$container{$key}; |
803
|
|
|
|
|
|
|
} |
804
|
|
|
|
|
|
|
} |
805
|
|
|
|
|
|
|
} elsif (isa($container, 'ARRAY')) { |
806
|
0
|
|
|
|
|
0
|
my $index = 0; |
807
|
0
|
|
|
|
|
0
|
foreach my $item (@$container) { |
808
|
0
|
0
|
|
|
|
0
|
if ($item eq $value) { |
809
|
0
|
|
|
|
|
0
|
splice @$container, $index, 1; |
810
|
|
|
|
|
|
|
# keep going |
811
|
|
|
|
|
|
|
} else { |
812
|
0
|
|
|
|
|
0
|
$index++; |
813
|
|
|
|
|
|
|
} |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
} else { |
816
|
0
|
|
|
|
|
0
|
croak "Cannot remove value from the provided container."; |
817
|
|
|
|
|
|
|
} |
818
|
0
|
|
|
|
|
0
|
return $container; |
819
|
|
|
|
|
|
|
}#rmval |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
822
|
|
|
|
|
|
|
# cpref - Recursively clone the reference, returning a new reference. |
823
|
|
|
|
|
|
|
# cpref ?ref |
824
|
|
|
|
|
|
|
# Implemented because the Clone module found on CPAN crashes under my mod_perl |
825
|
|
|
|
|
|
|
# and FastCGI test servers... |
826
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
sub cpref { |
829
|
1
|
|
|
1
|
1
|
2
|
my $ref = shift; |
830
|
1
|
|
|
|
|
3
|
my $new = (); |
831
|
1
|
50
|
|
|
|
4
|
return $ref unless ref($ref); |
832
|
1
|
50
|
|
|
|
6
|
if (isa($ref, 'HASH')) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
833
|
1
|
50
|
|
|
|
6
|
$new = blessed $ref ? ref($ref)->new() : {}; |
834
|
1
|
|
|
|
|
2
|
keys %$ref; # reset iterator |
835
|
1
|
|
|
|
|
8
|
while( my($k,$v) = each %$ref ) { |
836
|
22
|
50
|
|
|
|
48
|
if( ref($v) ) { |
837
|
0
|
0
|
|
|
|
0
|
$new->{$k} = cpref($v) unless $v eq $ref; |
838
|
|
|
|
|
|
|
} else { |
839
|
22
|
|
|
|
|
106
|
$new->{$k} = $v; |
840
|
|
|
|
|
|
|
} |
841
|
|
|
|
|
|
|
} |
842
|
|
|
|
|
|
|
} elsif (isa($ref, 'ARRAY')) { |
843
|
0
|
0
|
|
|
|
0
|
$new = blessed $ref ? ref($ref)->new() : []; |
844
|
0
|
|
|
|
|
0
|
foreach my $v ( @$ref ) { |
845
|
0
|
0
|
|
|
|
0
|
if( ref($v) ) { |
846
|
0
|
|
|
|
|
0
|
push @$new, cpref($v); |
847
|
|
|
|
|
|
|
} else { |
848
|
0
|
|
|
|
|
0
|
push @$new, $v; |
849
|
|
|
|
|
|
|
} |
850
|
|
|
|
|
|
|
} |
851
|
|
|
|
|
|
|
} elsif (isa($ref, 'SCALAR')) { |
852
|
0
|
|
|
|
|
0
|
my $tmp = $$ref; |
853
|
0
|
|
|
|
|
0
|
$new = \$tmp; |
854
|
|
|
|
|
|
|
} elsif (ref($ref) eq 'REF') { |
855
|
0
|
0
|
|
|
|
0
|
$$ref eq $ref and |
856
|
|
|
|
|
|
|
warn "Self reference cannot be copied: $ref"; |
857
|
0
|
0
|
|
|
|
0
|
($$ref ne $ref) and $new = cpref($$ref); |
858
|
|
|
|
|
|
|
} elsif (ref($ref) eq 'CODE') { |
859
|
0
|
|
|
|
|
0
|
$new = $ref; |
860
|
|
|
|
|
|
|
} else { |
861
|
0
|
|
|
|
|
0
|
croak "Cannot copy reference: $ref\n"; |
862
|
|
|
|
|
|
|
} |
863
|
1
|
|
|
|
|
7
|
return $new; |
864
|
|
|
|
|
|
|
}#cpref |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
867
|
|
|
|
|
|
|
# random_id - Get a random numeric value for use as an id |
868
|
|
|
|
|
|
|
# random_id |
869
|
|
|
|
|
|
|
# |
870
|
|
|
|
|
|
|
# Creates a checksum of the current time() plus 4 digit rand() number. |
871
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
sub random_id { |
874
|
0
|
|
|
0
|
1
|
|
return Hub::checksum(sprintf("%d%03d", time(), int(rand()*1000))); |
875
|
|
|
|
|
|
|
}#random_id |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
878
|
|
|
|
|
|
|
# checksum - Create a unique identifier for the provided data |
879
|
|
|
|
|
|
|
# checksum [params..] |
880
|
|
|
|
|
|
|
# Params can be scalars, hash references, array references and the like. |
881
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
882
|
|
|
|
|
|
|
#|test(match) |
883
|
|
|
|
|
|
|
#| |
884
|
|
|
|
|
|
|
#| my $x = 'like catfood'; |
885
|
|
|
|
|
|
|
#| Hub::checksum( 'my', { cats => 'breath' }, ( 'smells', $x ) ); |
886
|
|
|
|
|
|
|
#| |
887
|
|
|
|
|
|
|
#~ 2023611966 |
888
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
sub checksum { |
891
|
0
|
|
|
0
|
1
|
|
my $buffer = ""; |
892
|
0
|
|
|
|
|
|
foreach my $param ( @_ ) { |
893
|
0
|
0
|
|
|
|
|
if( ref($param) eq 'HASH' ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
894
|
0
|
|
|
|
|
|
$buffer .= Hub::flatten( $param ); |
895
|
|
|
|
|
|
|
} elsif( ref($param) eq 'ARRAY' ) { |
896
|
0
|
|
|
|
|
|
$buffer .= Hub::checksum( @$param ); |
897
|
|
|
|
|
|
|
} elsif( ref($param) eq 'SCALAR' ) { |
898
|
0
|
|
|
|
|
|
$buffer .= $$param; |
899
|
|
|
|
|
|
|
} elsif( ref($param) eq "Fh" ) { |
900
|
0
|
0
|
|
|
|
|
$param =~ /(.*)/ and $buffer .= $1; |
901
|
|
|
|
|
|
|
} else { |
902
|
0
|
|
|
|
|
|
$buffer .= $param; |
903
|
|
|
|
|
|
|
}#if |
904
|
|
|
|
|
|
|
}#foreach |
905
|
0
|
|
|
|
|
|
my $crc32 = crc32($buffer); # crc32 is faster than adler32 |
906
|
0
|
|
|
|
|
|
return $crc32; |
907
|
|
|
|
|
|
|
}#checksum |
908
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
910
|
|
|
|
|
|
|
# merge - Merge several hashes |
911
|
|
|
|
|
|
|
# merge \%target, \%source, [\%source..], [options] |
912
|
|
|
|
|
|
|
# returns \%hash |
913
|
|
|
|
|
|
|
# |
914
|
|
|
|
|
|
|
# Merges the provided hashes. The first argument (destination hash) has |
915
|
|
|
|
|
|
|
# precedence (as in values are NOT overwritten) unless -overwrite is given. |
916
|
|
|
|
|
|
|
# |
917
|
|
|
|
|
|
|
# By default this routine modifies \%target. Specifiy -copy circumvent. |
918
|
|
|
|
|
|
|
# |
919
|
|
|
|
|
|
|
# OPTIONS: |
920
|
|
|
|
|
|
|
# |
921
|
|
|
|
|
|
|
# -copy Do not modify \%target. |
922
|
|
|
|
|
|
|
# |
923
|
|
|
|
|
|
|
# -overwrite=1 Overwrite values as they are encounterred. |
924
|
|
|
|
|
|
|
# |
925
|
|
|
|
|
|
|
# -prune=1 Gives the destination hash the same structure as |
926
|
|
|
|
|
|
|
# the source hash (or the composite of all which is |
927
|
|
|
|
|
|
|
# in common when multiple source hashes are provided). |
928
|
|
|
|
|
|
|
# |
929
|
|
|
|
|
|
|
# If the destination is missing a value, it is |
930
|
|
|
|
|
|
|
# initialized from the source hash. |
931
|
|
|
|
|
|
|
# |
932
|
|
|
|
|
|
|
# If the destination has a value which is not in all |
933
|
|
|
|
|
|
|
# of the source hashes, it is deleted. |
934
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
sub merge { |
937
|
0
|
|
|
0
|
1
|
|
my ($opts) = Hub::opts(\@_, { |
938
|
|
|
|
|
|
|
'overwrite' => 0, |
939
|
|
|
|
|
|
|
'prune' => 0, |
940
|
|
|
|
|
|
|
'copy' => 0, |
941
|
|
|
|
|
|
|
}); |
942
|
0
|
|
|
|
|
|
my $target = shift; # destination hash |
943
|
0
|
0
|
|
|
|
|
$target = {} unless defined $target; |
944
|
0
|
0
|
|
|
|
|
my $dh = $$opts{'copy'} ? Hub::cpref($target) : $target; |
945
|
0
|
0
|
|
|
|
|
return unless isa($dh, 'HASH'); |
946
|
0
|
|
|
|
|
|
foreach my $sh ( @_ ) { |
947
|
0
|
|
|
|
|
|
_merge_hash($dh, $sh, $opts); |
948
|
|
|
|
|
|
|
} |
949
|
0
|
|
|
|
|
|
return $dh; |
950
|
|
|
|
|
|
|
}#merge |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
sub _merge_hash { |
953
|
0
|
|
|
0
|
|
|
my ($dh,$sh,$opts) = @_; |
954
|
0
|
0
|
|
|
|
|
if ($$opts{'prune'}) { |
955
|
0
|
|
|
|
|
|
my @d_keys = keys %$dh; |
956
|
0
|
|
|
|
|
|
foreach my $k ( @d_keys ) { |
957
|
0
|
0
|
|
|
|
|
delete $$dh{$k} unless defined $$sh{$k}; |
958
|
|
|
|
|
|
|
} |
959
|
|
|
|
|
|
|
} |
960
|
0
|
|
|
|
|
|
keys %$sh; # reset iterator |
961
|
0
|
|
|
|
|
|
while( my($k,$v) = each %$sh ) { |
962
|
0
|
|
|
|
|
|
_merge_element( $dh, $k, $v, $opts ); |
963
|
|
|
|
|
|
|
} |
964
|
|
|
|
|
|
|
}#_merge_hash |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
sub _merge_array { |
967
|
0
|
|
|
0
|
|
|
my ($da,$sa,$opts) = @_; # destination array, source array |
968
|
0
|
|
|
|
|
|
for (my $i = 0; $i < @$sa; $i++) { |
969
|
0
|
0
|
|
|
|
|
if (defined $$sa[$i]) { |
970
|
0
|
0
|
0
|
|
|
|
if (isa($$da[$i], 'HASH') && isa($$sa[$i], 'HASH')) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
971
|
0
|
|
|
|
|
|
_merge_hash($$da[$i], $$sa[$i], $opts); |
972
|
|
|
|
|
|
|
} elsif (isa($$da[$i], 'ARRAY') && isa($$sa[$i], 'ARRAY')) { |
973
|
0
|
|
|
|
|
|
_merge_array($$da[$i], $$sa[$i], $opts); |
974
|
|
|
|
|
|
|
} elsif (!exists $$da[$i] || $$opts{'overwrite'}) { |
975
|
0
|
|
|
|
|
|
$$da[$i] = $$sa[$i]; |
976
|
|
|
|
|
|
|
} |
977
|
|
|
|
|
|
|
} |
978
|
|
|
|
|
|
|
} |
979
|
|
|
|
|
|
|
}#_merge_array |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
sub _merge_element { |
982
|
0
|
|
|
0
|
|
|
my ($dh, $k, $v, $opts) = @_; |
983
|
0
|
0
|
|
|
|
|
if (defined($$dh{$k})) { |
984
|
0
|
0
|
0
|
|
|
|
if (isa($$dh{$k}, 'HASH') && isa($v, 'HASH')) { |
|
|
0
|
0
|
|
|
|
|
985
|
0
|
|
|
|
|
|
_merge_hash($$dh{$k}, $v, $opts); |
986
|
|
|
|
|
|
|
} elsif (isa($$dh{$k}, 'ARRAY') && isa($v, 'ARRAY')) { |
987
|
0
|
|
|
|
|
|
_merge_array($$dh{$k}, $v, $opts); |
988
|
|
|
|
|
|
|
} else { |
989
|
|
|
|
|
|
|
# do not chage the type (unless overwriting) |
990
|
0
|
0
|
|
|
|
|
$$opts{'overwrite'} and $$dh{$k} = $v; |
991
|
|
|
|
|
|
|
} |
992
|
|
|
|
|
|
|
} else { |
993
|
0
|
|
|
|
|
|
my $vcopy = Hub::cpref($v); |
994
|
0
|
0
|
|
|
|
|
$$dh{$k} = defined($vcopy) ? $vcopy : ''; |
995
|
|
|
|
|
|
|
} |
996
|
|
|
|
|
|
|
}#_merge_element |
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
999
|
|
|
|
|
|
|
# flatten - Get a consistent unique-by-data string for some data structure. |
1000
|
|
|
|
|
|
|
# flatten \%hash |
1001
|
|
|
|
|
|
|
# flatten \%array |
1002
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
sub flatten { |
1005
|
0
|
|
0
|
0
|
1
|
|
my $ptr = shift || return; |
1006
|
0
|
|
|
|
|
|
my $buf = ""; |
1007
|
0
|
0
|
|
|
|
|
if (isa($ptr, 'HASH')) { |
|
|
0
|
|
|
|
|
|
1008
|
0
|
|
|
|
|
|
foreach my $k ( sort keys %$ptr ) { |
1009
|
0
|
|
|
|
|
|
my $v = $$ptr{$k}; |
1010
|
0
|
0
|
|
|
|
|
if( ref($v) ) { |
1011
|
0
|
|
|
|
|
|
$buf .= $k; |
1012
|
0
|
|
|
|
|
|
$buf .= Hub::flatten( $v ); |
1013
|
|
|
|
|
|
|
} else { |
1014
|
0
|
0
|
0
|
|
|
|
if( !$k || $v =~ /\n/ ) { |
1015
|
0
|
|
|
|
|
|
$buf .= $v; |
1016
|
|
|
|
|
|
|
} else { |
1017
|
0
|
|
|
|
|
|
$buf .= $k . $v; |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
} |
1020
|
|
|
|
|
|
|
} |
1021
|
|
|
|
|
|
|
} elsif (isa($ptr, 'ARRAY')) { |
1022
|
0
|
|
|
|
|
|
foreach my $v (sort @$ptr) { |
1023
|
0
|
0
|
|
|
|
|
if (ref($v)) { |
1024
|
0
|
|
|
|
|
|
$buf .= Hub::flatten($v); |
1025
|
|
|
|
|
|
|
} else { |
1026
|
0
|
|
|
|
|
|
$buf .= $v; |
1027
|
|
|
|
|
|
|
} |
1028
|
|
|
|
|
|
|
} |
1029
|
|
|
|
|
|
|
} else { |
1030
|
0
|
|
|
|
|
|
die "Cannot flatten structure: $ptr\n"; |
1031
|
|
|
|
|
|
|
} |
1032
|
0
|
|
|
|
|
|
return $buf; |
1033
|
|
|
|
|
|
|
}#flatten |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1036
|
|
|
|
|
|
|
# replace MATCHING_REGEX, SUBSTITUTION_REGEX, TEXT |
1037
|
|
|
|
|
|
|
# |
1038
|
|
|
|
|
|
|
# Do a s/// operation on a given segment of the string. |
1039
|
|
|
|
|
|
|
# |
1040
|
|
|
|
|
|
|
# For example, say we want to remove the ': ;' pattern from the style portion, |
1041
|
|
|
|
|
|
|
# but not from the data portion: |
1042
|
|
|
|
|
|
|
# |
1043
|
|
|
|
|
|
|
# keep this: ;stuff |
1044
|
|
|
|
|
|
|
# |
1045
|
|
|
|
|
|
|
# Use this method as: |
1046
|
|
|
|
|
|
|
# |
1047
|
|
|
|
|
|
|
# $text = Hub::replace( "style=\".*?\"", "s/[\\w\\-]+\\s*:\\s*;//g", $text ); |
1048
|
|
|
|
|
|
|
# |
1049
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1050
|
|
|
|
|
|
|
|
1051
|
|
|
|
|
|
|
sub replace { |
1052
|
|
|
|
|
|
|
|
1053
|
0
|
|
|
0
|
1
|
|
my ($match,$replace,$str) = @_; |
1054
|
|
|
|
|
|
|
|
1055
|
0
|
0
|
|
|
|
|
return unless $str; |
1056
|
|
|
|
|
|
|
|
1057
|
0
|
|
|
|
|
|
while( $str =~ m/\G.*?($match)/gs ) { |
1058
|
|
|
|
|
|
|
|
1059
|
0
|
|
|
|
|
|
my $substr = $1; |
1060
|
|
|
|
|
|
|
|
1061
|
0
|
|
|
|
|
|
my $beg = pos($str) - length( $substr ); |
1062
|
|
|
|
|
|
|
|
1063
|
0
|
0
|
|
|
|
|
if( eval "\$substr =~ $replace" ) { |
1064
|
|
|
|
|
|
|
|
1065
|
0
|
|
|
|
|
|
pos $str = $beg; |
1066
|
|
|
|
|
|
|
|
1067
|
0
|
|
|
|
|
|
$str =~ s/\G$match/$substr/; |
1068
|
|
|
|
|
|
|
|
1069
|
0
|
|
|
|
|
|
pos $str = $beg + length($substr); |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
}#if |
1072
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
}#while |
1074
|
|
|
|
|
|
|
|
1075
|
0
|
|
|
|
|
|
return $str; |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
}#replace |
1078
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1080
|
|
|
|
|
|
|
# digout REF, ID |
1081
|
|
|
|
|
|
|
# |
1082
|
|
|
|
|
|
|
# Return an array of all nested values in an order that can be processed. |
1083
|
|
|
|
|
|
|
# |
1084
|
|
|
|
|
|
|
# NOTE! Scalar values are returned as references. |
1085
|
|
|
|
|
|
|
# See how 'packdata' uses this method to dereference. |
1086
|
|
|
|
|
|
|
# |
1087
|
|
|
|
|
|
|
# Arrays are ignored unless their members are hashes with an _id member. |
1088
|
|
|
|
|
|
|
# |
1089
|
|
|
|
|
|
|
# Reverse the results of this array to process data in a way that the children |
1090
|
|
|
|
|
|
|
# are affected before their parents. |
1091
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1092
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
sub digout { |
1094
|
|
|
|
|
|
|
|
1095
|
0
|
|
|
0
|
1
|
|
my $r = shift; |
1096
|
0
|
|
0
|
|
|
|
my $id = shift || ''; |
1097
|
|
|
|
|
|
|
|
1098
|
0
|
0
|
|
|
|
|
return unless ref($r); |
1099
|
|
|
|
|
|
|
|
1100
|
0
|
|
|
|
|
|
my $h = {}; |
1101
|
|
|
|
|
|
|
|
1102
|
0
|
|
|
|
|
|
my $data = []; |
1103
|
|
|
|
|
|
|
|
1104
|
0
|
0
|
|
|
|
|
if( ref($r) eq 'ARRAY' ) { |
|
|
0
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
|
1106
|
0
|
|
|
|
|
|
foreach my $elem ( @$r ) { |
1107
|
|
|
|
|
|
|
|
1108
|
0
|
0
|
|
|
|
|
if( ref($elem) eq 'HASH' ) { |
1109
|
|
|
|
|
|
|
|
1110
|
0
|
0
|
|
|
|
|
if( $$elem{'_id'} ) { |
1111
|
|
|
|
|
|
|
|
1112
|
0
|
|
|
|
|
|
$h->{$$elem{'_id'}} = $elem; |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
}#if |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
}#if |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
}#foreach |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
} elsif( ref($r) eq 'HASH' ) { |
1121
|
|
|
|
|
|
|
|
1122
|
0
|
|
|
|
|
|
$h = $r; |
1123
|
|
|
|
|
|
|
|
1124
|
|
|
|
|
|
|
}#if |
1125
|
|
|
|
|
|
|
|
1126
|
0
|
|
|
|
|
|
foreach my $k ( keys %$h ) { |
1127
|
|
|
|
|
|
|
|
1128
|
0
|
0
|
|
|
|
|
if( ref($h->{$k}) ) { |
1129
|
|
|
|
|
|
|
|
1130
|
0
|
|
|
|
|
|
push @$data, { |
1131
|
|
|
|
|
|
|
key => $k, |
1132
|
|
|
|
|
|
|
id => "$id:$k", |
1133
|
|
|
|
|
|
|
val => $h->{$k}, |
1134
|
|
|
|
|
|
|
}; |
1135
|
|
|
|
|
|
|
|
1136
|
0
|
|
|
|
|
|
push @$data, @{ &digout($h->{$k},"$id:$k") }; |
|
0
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
} else { |
1139
|
|
|
|
|
|
|
|
1140
|
0
|
|
|
|
|
|
push @$data, { |
1141
|
|
|
|
|
|
|
key => $k, |
1142
|
|
|
|
|
|
|
id => "$id:$k", |
1143
|
|
|
|
|
|
|
val => \$h->{$k}, |
1144
|
|
|
|
|
|
|
}; |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
}#if |
1147
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
}#foreach |
1149
|
|
|
|
|
|
|
|
1150
|
0
|
|
|
|
|
|
return $data; |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
}#digout |
1153
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1155
|
|
|
|
|
|
|
# diff - Creates a nest of the differences between the provided structures. |
1156
|
|
|
|
|
|
|
# diff \%hash1, \%hash2 |
1157
|
|
|
|
|
|
|
# diff \@array1, \@array2 |
1158
|
|
|
|
|
|
|
# |
1159
|
|
|
|
|
|
|
# If a conflict of types (with the same key) is encounterred, the right-hand |
1160
|
|
|
|
|
|
|
# sturcture is used. |
1161
|
|
|
|
|
|
|
# |
1162
|
|
|
|
|
|
|
# NOTE: Although this routine compares contents, it returns references to the |
1163
|
|
|
|
|
|
|
# original hashes (use L on the result to detatch.) |
1164
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
sub diff { |
1167
|
0
|
|
|
0
|
1
|
|
my ($l,$r) = @_; |
1168
|
0
|
0
|
|
|
|
|
if (isa($l, 'HASH')) { |
|
|
0
|
|
|
|
|
|
1169
|
0
|
|
|
|
|
|
return _diff_hashes( $l, $r ); |
1170
|
|
|
|
|
|
|
} elsif (isa($l, 'ARRAY')) { |
1171
|
0
|
|
|
|
|
|
return _diff_arrays( $l, $r ); |
1172
|
|
|
|
|
|
|
} |
1173
|
|
|
|
|
|
|
}#diff |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1176
|
|
|
|
|
|
|
# _diff_hashes &HASH, &HASH |
1177
|
|
|
|
|
|
|
# |
1178
|
|
|
|
|
|
|
# Difference between two hashes. |
1179
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1180
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
sub _diff_hashes { |
1182
|
0
|
|
|
0
|
|
|
my ($l,$r) = @_; |
1183
|
0
|
0
|
|
|
|
|
return unless ref($l) eq 'HASH'; |
1184
|
0
|
0
|
|
|
|
|
return unless ref($r) eq 'HASH'; |
1185
|
0
|
|
|
|
|
|
my $h = undef; |
1186
|
0
|
|
|
|
|
|
my @lkeys = keys %$l; |
1187
|
0
|
|
|
|
|
|
while( my $key = shift @lkeys ) { |
1188
|
0
|
0
|
|
|
|
|
if( defined $r->{$key} ) { |
1189
|
0
|
0
|
|
|
|
|
if( ref($l->{$key}) eq ref($r->{$key}) ) { |
1190
|
0
|
0
|
|
|
|
|
if( ref($l->{$key}) eq 'HASH' ) { |
|
|
0
|
|
|
|
|
|
1191
|
0
|
|
|
|
|
|
my $subh = _diff_hashes( $l->{$key}, $r->{$key} ); |
1192
|
0
|
0
|
|
|
|
|
$h->{$key} = $subh if $subh; |
1193
|
|
|
|
|
|
|
} elsif( ref($l->{$key}) eq 'ARRAY' ) { |
1194
|
0
|
|
|
|
|
|
my $suba = _diff_arrays( $l->{$key}, $r->{$key} ); |
1195
|
0
|
0
|
|
|
|
|
$h->{$key} = $suba if $suba; |
1196
|
|
|
|
|
|
|
} else { |
1197
|
0
|
0
|
|
|
|
|
$h->{$key} = $r->{$key} unless $l->{$key} eq $r->{$key}; |
1198
|
|
|
|
|
|
|
} |
1199
|
|
|
|
|
|
|
} else { |
1200
|
0
|
|
|
|
|
|
$h->{$key} = $r->{$key}; |
1201
|
|
|
|
|
|
|
} |
1202
|
|
|
|
|
|
|
} else { |
1203
|
0
|
|
|
|
|
|
$h->{$key} = $l->{$key}; |
1204
|
|
|
|
|
|
|
} |
1205
|
|
|
|
|
|
|
} |
1206
|
0
|
|
|
|
|
|
my @rkeys = keys %$r; |
1207
|
0
|
|
|
|
|
|
while( my $key = shift @rkeys ) { |
1208
|
0
|
0
|
|
|
|
|
$h->{$key} = $r->{$key} unless defined $l->{$key}; |
1209
|
|
|
|
|
|
|
} |
1210
|
0
|
|
|
|
|
|
return $h; |
1211
|
|
|
|
|
|
|
}#_diff_hashes |
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1214
|
|
|
|
|
|
|
# _diff_arrays &ARRAY, &ARRAY |
1215
|
|
|
|
|
|
|
# |
1216
|
|
|
|
|
|
|
# Difference between two arrays. |
1217
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
sub _diff_arrays { |
1220
|
0
|
|
|
0
|
|
|
my ($l,$r) = @_; |
1221
|
0
|
0
|
|
|
|
|
return unless isa($l, 'ARRAY'); |
1222
|
0
|
0
|
|
|
|
|
return unless isa($r, 'ARRAY'); |
1223
|
0
|
|
|
|
|
|
my $a = undef; |
1224
|
0
|
|
|
|
|
|
my $idx = 0; |
1225
|
0
|
|
|
|
|
|
my $min = Hub::min( $#$l, $#$r ); |
1226
|
0
|
|
|
|
|
|
for( my $idx = 0; $idx <= $min; $idx++ ) { |
1227
|
0
|
|
|
|
|
|
my $lval = $l->[$idx]; |
1228
|
0
|
|
|
|
|
|
my $rval = $r->[$idx]; |
1229
|
0
|
0
|
|
|
|
|
if( ref($lval) eq ref($rval) ) { |
1230
|
0
|
0
|
|
|
|
|
if( ref($lval) eq 'HASH' ) { |
|
|
0
|
|
|
|
|
|
1231
|
0
|
|
|
|
|
|
my $subh = _diff_hashes( $lval, $rval ); |
1232
|
0
|
0
|
|
|
|
|
push( @$a, $subh ) if $subh; |
1233
|
|
|
|
|
|
|
} elsif( ref($rval) eq 'ARRAY' ) { |
1234
|
0
|
|
|
|
|
|
my $suba = _diff_arrays( $lval, $rval ); |
1235
|
0
|
0
|
|
|
|
|
push( @$a, $suba ) if $suba; |
1236
|
|
|
|
|
|
|
} else { |
1237
|
0
|
0
|
|
|
|
|
push( @$a, $rval ) unless $lval eq $rval; |
1238
|
|
|
|
|
|
|
} |
1239
|
|
|
|
|
|
|
} else { |
1240
|
0
|
|
|
|
|
|
push @$a, $rval; |
1241
|
|
|
|
|
|
|
} |
1242
|
0
|
|
|
|
|
|
$idx++; |
1243
|
|
|
|
|
|
|
} |
1244
|
0
|
0
|
|
|
|
|
if( $#$l > $#$r ) { |
1245
|
0
|
|
|
|
|
|
foreach my $idx ( ($#$r + 1) .. $#$l ) { |
1246
|
0
|
|
|
|
|
|
push @$a, $l->[$idx]; |
1247
|
|
|
|
|
|
|
} |
1248
|
|
|
|
|
|
|
} else { |
1249
|
0
|
|
|
|
|
|
foreach my $idx ( ($#$l + 1) .. $#$r ) { |
1250
|
0
|
|
|
|
|
|
push @$a, $r->[$idx]; |
1251
|
|
|
|
|
|
|
} |
1252
|
|
|
|
|
|
|
} |
1253
|
0
|
|
|
|
|
|
return $a; |
1254
|
|
|
|
|
|
|
}#_diff_arrays |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1257
|
|
|
|
|
|
|
# dice - Break apart the string into the least number of segments |
1258
|
|
|
|
|
|
|
# dice [options] $string |
1259
|
|
|
|
|
|
|
# options: |
1260
|
|
|
|
|
|
|
# beg=$literal Begin of balanced pair, Default is '{' |
1261
|
|
|
|
|
|
|
# end=$literal End of balanced pair, Default is '}' |
1262
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1263
|
|
|
|
|
|
|
#|test(match,a;{b{c}};c;{d}) join( ';', dice( "a{b{c}}c{d}" ) ); |
1264
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
sub dice { |
1267
|
|
|
|
|
|
|
|
1268
|
0
|
|
|
0
|
1
|
|
my $opts = { |
1269
|
|
|
|
|
|
|
'beg' => '{', |
1270
|
|
|
|
|
|
|
'end' => '}', |
1271
|
|
|
|
|
|
|
}; |
1272
|
|
|
|
|
|
|
|
1273
|
0
|
|
|
|
|
|
Hub::opts( \@_, $opts ); |
1274
|
0
|
|
|
|
|
|
my $text = shift; |
1275
|
0
|
|
|
|
|
|
my @result = (); |
1276
|
|
|
|
|
|
|
|
1277
|
0
|
|
|
|
|
|
my %beg = ( |
1278
|
|
|
|
|
|
|
str => $$opts{'beg'}, |
1279
|
|
|
|
|
|
|
char => substr($$opts{'beg'}, 0, 1), |
1280
|
|
|
|
|
|
|
len => length($$opts{'beg'}), |
1281
|
|
|
|
|
|
|
); |
1282
|
|
|
|
|
|
|
|
1283
|
0
|
|
|
|
|
|
my %end = ( |
1284
|
|
|
|
|
|
|
str => $$opts{'end'}, |
1285
|
|
|
|
|
|
|
char => substr($$opts{'end'}, 0, 1), |
1286
|
|
|
|
|
|
|
len => length($$opts{'end'}), |
1287
|
|
|
|
|
|
|
); |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
# find the beginning |
1290
|
0
|
|
|
|
|
|
my ($p,$p2,$p3) = (0,0,0); |
1291
|
0
|
|
|
|
|
|
while( ($p = index( $text, $beg{'str'}, 0 )) > -1 ) { |
1292
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
# find the end |
1294
|
0
|
|
|
|
|
|
my $p2 = $p + $beg{'len'}; # start of the current search |
1295
|
0
|
|
|
|
|
|
my $p3 = index( $text, $end{'char'}, $p2 ); # point of closing |
1296
|
0
|
|
|
|
|
|
while( $p3 > -1 ) { |
1297
|
0
|
|
|
|
|
|
my $ic = 0; # inner count |
1298
|
0
|
|
|
|
|
|
my $im = index( $text, $beg{'char'}, $p2 ); # inner match |
1299
|
0
|
|
0
|
|
|
|
while( ($im > -1) && ($im < $p3) ) { |
1300
|
0
|
|
|
|
|
|
$ic++; |
1301
|
0
|
|
|
|
|
|
$p2 = ($im + 1); |
1302
|
0
|
|
|
|
|
|
$im = index( $text, $beg{'char'}, $p2 ); |
1303
|
|
|
|
|
|
|
} |
1304
|
0
|
0
|
|
|
|
|
last unless $ic > 0; |
1305
|
0
|
|
|
|
|
|
for( 1 .. $ic ) { |
1306
|
0
|
|
|
|
|
|
$p3 = index( $text, $end{'char'}, ($p3 + 1) ); |
1307
|
|
|
|
|
|
|
} |
1308
|
|
|
|
|
|
|
} |
1309
|
0
|
0
|
|
|
|
|
if( $p3 > $p ) { |
1310
|
0
|
|
|
|
|
|
my $str = substr( $text, $p, (($p3 + $end{'len'}) - $p) ); |
1311
|
0
|
|
|
|
|
|
my $left = substr( $text, 0, $p ); |
1312
|
0
|
|
|
|
|
|
my $right = substr( $text, $p + length($str) ); |
1313
|
0
|
|
|
|
|
|
push @result, $left, $str; |
1314
|
0
|
|
|
|
|
|
$text = $right; |
1315
|
|
|
|
|
|
|
} else { |
1316
|
0
|
|
|
|
|
|
croak "Unmatched $beg{'str'}"; |
1317
|
|
|
|
|
|
|
} |
1318
|
|
|
|
|
|
|
} |
1319
|
|
|
|
|
|
|
|
1320
|
0
|
0
|
|
|
|
|
$text and push @result, $text; |
1321
|
0
|
|
|
|
|
|
return @result; |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
}#dice |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1326
|
|
|
|
|
|
|
# indexmatch - Search for an expression within a string and return the offset |
1327
|
|
|
|
|
|
|
# indexmatch [options] $string, $expression, $position |
1328
|
|
|
|
|
|
|
# indexmatch [options] $string, $expression |
1329
|
|
|
|
|
|
|
# |
1330
|
|
|
|
|
|
|
# Returns -1 if $expression is not found. |
1331
|
|
|
|
|
|
|
# |
1332
|
|
|
|
|
|
|
# options: |
1333
|
|
|
|
|
|
|
# |
1334
|
|
|
|
|
|
|
# -after=1 Return the position *after* the expression. |
1335
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1336
|
|
|
|
|
|
|
#|test(match,4) indexmatch("abracadabra", "[cd]") |
1337
|
|
|
|
|
|
|
#|test(match,3) indexmatch("abracadabra", "a", 3) |
1338
|
|
|
|
|
|
|
#|test(match,-1) indexmatch("abracadabra", "d{2,2}") |
1339
|
|
|
|
|
|
|
#|test(match,3) indexmatch("scant", "can", "-after=1") |
1340
|
|
|
|
|
|
|
#| - indexmatch("scant", "can") |
1341
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1342
|
|
|
|
|
|
|
|
1343
|
|
|
|
|
|
|
sub indexmatch { |
1344
|
0
|
|
|
0
|
1
|
|
my ($opts, $str, $expr, $from) = Hub::opts(\@_, {'after' => 0,}); |
1345
|
0
|
0
|
|
|
|
|
croak "undefined search string" unless defined $str; |
1346
|
0
|
0
|
|
|
|
|
croak "undefined search expression" unless defined $expr; |
1347
|
0
|
0
|
|
|
|
|
$from = 0 if not defined $from; |
1348
|
0
|
|
|
|
|
|
my $temp_str = substr $str, $from; |
1349
|
0
|
0
|
|
|
|
|
croak "undefined search substring" unless defined $temp_str; |
1350
|
0
|
|
|
|
|
|
my $pos = undef; |
1351
|
0
|
|
|
|
|
|
my @match = $temp_str =~ /($expr)/; |
1352
|
0
|
0
|
|
|
|
|
$pos = index $temp_str, $match[0] if (defined $match[0]); |
1353
|
0
|
0
|
|
|
|
|
return defined $pos |
|
|
0
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
? $$opts{'after'} |
1355
|
|
|
|
|
|
|
? $from + $pos + length($match[0]) |
1356
|
|
|
|
|
|
|
: $from + $pos |
1357
|
|
|
|
|
|
|
: -1; |
1358
|
|
|
|
|
|
|
}#indexmatch |
1359
|
|
|
|
|
|
|
|
1360
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
1361
|
|
|
|
|
|
|
1; |