line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#+############################################################################## |
2
|
|
|
|
|
|
|
# # |
3
|
|
|
|
|
|
|
# File: No/Worries/Stat.pm # |
4
|
|
|
|
|
|
|
# # |
5
|
|
|
|
|
|
|
# Description: stat() handling without worries # |
6
|
|
|
|
|
|
|
# # |
7
|
|
|
|
|
|
|
#-############################################################################## |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# |
10
|
|
|
|
|
|
|
# module definition |
11
|
|
|
|
|
|
|
# |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
package No::Worries::Stat; |
14
|
2
|
|
|
2
|
|
518
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
63
|
|
15
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
157
|
|
16
|
|
|
|
|
|
|
our $VERSION = "1.7"; |
17
|
|
|
|
|
|
|
our $REVISION = sprintf("%d.%02d", q$Revision: 1.12 $ =~ /(\d+)\.(\d+)/); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# |
20
|
|
|
|
|
|
|
# used modules |
21
|
|
|
|
|
|
|
# |
22
|
|
|
|
|
|
|
|
23
|
2
|
|
|
2
|
|
15
|
use Fcntl qw(:mode); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
465
|
|
24
|
2
|
|
|
2
|
|
16
|
use No::Worries::Die qw(dief); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
14
|
|
25
|
2
|
|
|
2
|
|
14
|
use No::Worries::Export qw(export_control); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
11
|
|
26
|
2
|
|
|
2
|
|
15
|
use Params::Validate qw(validate :types); |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
287
|
|
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# |
29
|
|
|
|
|
|
|
# constants |
30
|
|
|
|
|
|
|
# |
31
|
|
|
|
|
|
|
|
32
|
2
|
|
|
2
|
|
16
|
use constant ST_DEV => 0; # ID of device containing file |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
154
|
|
33
|
2
|
|
|
2
|
|
13
|
use constant ST_INO => 1; # inode number |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
124
|
|
34
|
2
|
|
|
2
|
|
12
|
use constant ST_MODE => 2; # protection |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
148
|
|
35
|
2
|
|
|
2
|
|
13
|
use constant ST_NLINK => 3; # number of hard links |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
98
|
|
36
|
2
|
|
|
2
|
|
12
|
use constant ST_UID => 4; # user ID of owner |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
98
|
|
37
|
2
|
|
|
2
|
|
12
|
use constant ST_GID => 5; # group ID of owner |
|
2
|
|
|
|
|
12
|
|
|
2
|
|
|
|
|
115
|
|
38
|
2
|
|
|
2
|
|
13
|
use constant ST_RDEV => 6; # device ID (if special file) |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
106
|
|
39
|
2
|
|
|
2
|
|
12
|
use constant ST_SIZE => 7; # total size, in bytes |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
106
|
|
40
|
2
|
|
|
2
|
|
14
|
use constant ST_ATIME => 8; # time of last access |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
120
|
|
41
|
2
|
|
|
2
|
|
14
|
use constant ST_MTIME => 9; # time of last modification |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
127
|
|
42
|
2
|
|
|
2
|
|
11
|
use constant ST_CTIME => 10; # time of last status change |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
121
|
|
43
|
2
|
|
|
2
|
|
14
|
use constant ST_BLKSIZE => 11; # blocksize for filesystem I/O |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
85
|
|
44
|
2
|
|
|
2
|
|
10
|
use constant ST_BLOCKS => 12; # number of 512B blocks allocated |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
116
|
|
45
|
|
|
|
|
|
|
|
46
|
2
|
|
|
2
|
|
14
|
use constant _IMODE => oct(7777); # all mode bits |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
105
|
|
47
|
2
|
|
|
2
|
|
13
|
use constant _IBITS => 12; # number of mode bits |
|
2
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
4310
|
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# |
50
|
|
|
|
|
|
|
# global variables |
51
|
|
|
|
|
|
|
# |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
our( |
54
|
|
|
|
|
|
|
@_Mode2Type, # mode (shifted) to file type |
55
|
|
|
|
|
|
|
%_CachedUid, # cached uid from getpwnam() |
56
|
|
|
|
|
|
|
%_CachedGid, # cached gid from getgrnam() |
57
|
|
|
|
|
|
|
); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# |
60
|
|
|
|
|
|
|
# check user option and set uid and message accordingly |
61
|
|
|
|
|
|
|
# |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub _check_user ($$) { |
64
|
4
|
|
|
4
|
|
9
|
my($option, $message) = @_; |
65
|
4
|
|
|
|
|
6
|
my($user); |
66
|
|
|
|
|
|
|
|
67
|
4
|
|
|
|
|
7
|
$user = $option->{user}; |
68
|
4
|
50
|
|
|
|
12
|
return unless defined($user); |
69
|
0
|
0
|
|
|
|
0
|
if ($user =~ /^\d+$/) { |
70
|
0
|
|
|
|
|
0
|
$option->{uid} = $user; |
71
|
|
|
|
|
|
|
} else { |
72
|
0
|
0
|
|
|
|
0
|
unless (exists($_CachedUid{$user})) { |
73
|
0
|
|
|
|
|
0
|
$_CachedUid{$user} = getpwnam($user); |
74
|
|
|
|
|
|
|
dief("unknown user: %s", $user) |
75
|
0
|
0
|
|
|
|
0
|
unless defined($_CachedUid{$user}); |
76
|
|
|
|
|
|
|
} |
77
|
0
|
|
|
|
|
0
|
$option->{uid} = $_CachedUid{$user}; |
78
|
|
|
|
|
|
|
} |
79
|
0
|
|
|
|
|
0
|
$message->{user} = "user($user)"; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# |
83
|
|
|
|
|
|
|
# check group option and set gid and message accordingly |
84
|
|
|
|
|
|
|
# |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub _check_group ($$) { |
87
|
4
|
|
|
4
|
|
9
|
my($option, $message) = @_; |
88
|
4
|
|
|
|
|
6
|
my($group); |
89
|
|
|
|
|
|
|
|
90
|
4
|
|
|
|
|
6
|
$group = $option->{group}; |
91
|
4
|
50
|
|
|
|
10
|
return unless defined($group); |
92
|
0
|
0
|
|
|
|
0
|
if ($group =~ /^\d+$/) { |
93
|
0
|
|
|
|
|
0
|
$option->{gid} = $group; |
94
|
|
|
|
|
|
|
} else { |
95
|
0
|
0
|
|
|
|
0
|
unless (exists($_CachedGid{$group})) { |
96
|
0
|
|
|
|
|
0
|
$_CachedGid{$group} = getgrnam($group); |
97
|
|
|
|
|
|
|
dief("unknown group: %s", $group) |
98
|
0
|
0
|
|
|
|
0
|
unless defined($_CachedGid{$group}); |
99
|
|
|
|
|
|
|
} |
100
|
0
|
|
|
|
|
0
|
$option->{gid} = $_CachedGid{$group}; |
101
|
|
|
|
|
|
|
} |
102
|
0
|
|
|
|
|
0
|
$message->{group} = "group($group)"; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# |
106
|
|
|
|
|
|
|
# check the mode option and set mode_set, mode_clear and message accordingly |
107
|
|
|
|
|
|
|
# |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub _check_mode ($$) { |
110
|
4
|
|
|
4
|
|
7
|
my($option, $message) = @_; |
111
|
4
|
|
|
|
|
63
|
my($mode, $action, $number); |
112
|
|
|
|
|
|
|
|
113
|
4
|
|
|
|
|
10
|
$mode = $option->{mode}; |
114
|
4
|
50
|
|
|
|
45
|
return unless defined($mode); |
115
|
4
|
50
|
|
|
|
25
|
if ($mode =~ /^([\+\-])?(\d+)$/) { |
116
|
4
|
|
100
|
|
|
21
|
$action = $1 || ""; |
117
|
4
|
100
|
|
|
|
23
|
$number = substr($2, 0, 1) eq "0" ? oct($2) : ($2+0); |
118
|
|
|
|
|
|
|
# use the canonical form for the message |
119
|
4
|
|
|
|
|
19
|
$mode = sprintf("%s%05o", $action, $number); |
120
|
4
|
100
|
|
|
|
12
|
if ($action eq "+") { |
|
|
100
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# check that at least these bits are set |
122
|
1
|
|
|
|
|
3
|
$option->{mode_set} = $number; |
123
|
1
|
|
|
|
|
2
|
$option->{mode_clear} = 0; |
124
|
|
|
|
|
|
|
} elsif ($action eq "-") { |
125
|
|
|
|
|
|
|
# check that at least these bits are cleared |
126
|
1
|
|
|
|
|
3
|
$option->{mode_set} = 0; |
127
|
1
|
|
|
|
|
2
|
$option->{mode_clear} = $number; |
128
|
|
|
|
|
|
|
} else { |
129
|
|
|
|
|
|
|
# check that these bits are exactly the ones set |
130
|
2
|
|
|
|
|
4
|
$option->{mode_set} = $number; |
131
|
2
|
|
|
|
|
4
|
$option->{mode_clear} = _IMODE; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
} else { |
134
|
0
|
|
|
|
|
0
|
dief("invalid mode: %s", $mode); |
135
|
|
|
|
|
|
|
} |
136
|
4
|
|
|
|
|
14
|
$message->{mode} = "mode($mode)"; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# |
140
|
|
|
|
|
|
|
# check the mtime option and set message accordingly |
141
|
|
|
|
|
|
|
# |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub _check_mtime ($$) { |
144
|
4
|
|
|
4
|
|
6
|
my($option, $message) = @_; |
145
|
4
|
|
|
|
|
7
|
my($mtime); |
146
|
|
|
|
|
|
|
|
147
|
4
|
|
|
|
|
6
|
$mtime = $option->{mtime}; |
148
|
4
|
50
|
|
|
|
9
|
return unless defined($mtime); |
149
|
0
|
|
|
|
|
0
|
$message->{mtime} = "mtime($mtime)"; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# |
153
|
|
|
|
|
|
|
# ensure proper ownership |
154
|
|
|
|
|
|
|
# |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub _ensure_owner ($$$$) { |
157
|
0
|
|
|
0
|
|
0
|
my($path, $stat, $option, $message) = @_; |
158
|
0
|
|
|
|
|
0
|
my(@todo); |
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
0
|
@todo = (); |
161
|
0
|
0
|
0
|
|
|
0
|
if ($message->{user} and $stat->[ST_UID] != $option->{uid}) { |
162
|
0
|
|
|
|
|
0
|
$stat->[ST_UID] = $option->{uid}; |
163
|
0
|
|
|
|
|
0
|
push(@todo, $message->{user}); |
164
|
|
|
|
|
|
|
} |
165
|
0
|
0
|
0
|
|
|
0
|
if ($message->{group} and $stat->[ST_GID] != $option->{gid}) { |
166
|
0
|
|
|
|
|
0
|
$stat->[ST_GID] = $option->{gid}; |
167
|
0
|
|
|
|
|
0
|
push(@todo, $message->{group}); |
168
|
|
|
|
|
|
|
} |
169
|
0
|
0
|
0
|
|
|
0
|
return(0) unless @todo and $option->{callback}->($path, "@todo"); |
170
|
0
|
0
|
|
|
|
0
|
chown($stat->[ST_UID], $stat->[ST_GID], $path) |
171
|
|
|
|
|
|
|
or dief("cannot chown(%d, %d, %s): %s", |
172
|
|
|
|
|
|
|
$stat->[ST_UID], $stat->[ST_GID], $path, $!); |
173
|
0
|
|
|
|
|
0
|
return(1) |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# |
177
|
|
|
|
|
|
|
# ensure proper permissions |
178
|
|
|
|
|
|
|
# |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub _ensure_mode ($$$$) { |
181
|
4
|
|
|
4
|
|
11
|
my($path, $stat, $option, $message) = @_; |
182
|
4
|
|
|
|
|
5
|
my($mode); |
183
|
|
|
|
|
|
|
|
184
|
4
|
|
|
|
|
9
|
$mode = $stat->[ST_MODE] & _IMODE; |
185
|
4
|
|
|
|
|
10
|
$mode &= ~$option->{mode_clear}; |
186
|
4
|
|
|
|
|
9
|
$mode |= $option->{mode_set}; |
187
|
4
|
100
|
|
|
|
11
|
return(0) if ($stat->[ST_MODE] & _IMODE) == $mode; |
188
|
3
|
50
|
|
|
|
9
|
return(0) unless $option->{callback}->($path, $message->{mode}); |
189
|
3
|
50
|
|
|
|
56
|
chmod($mode, $path) |
190
|
|
|
|
|
|
|
or dief("cannot chmod(%05o, %s): %s", $mode, $path, $!); |
191
|
3
|
|
|
|
|
12
|
return(1) |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# |
195
|
|
|
|
|
|
|
# ensure proper modification time |
196
|
|
|
|
|
|
|
# |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub _ensure_mtime ($$$$) { |
199
|
0
|
|
|
0
|
|
0
|
my($path, $stat, $option, $message) = @_; |
200
|
|
|
|
|
|
|
|
201
|
0
|
0
|
|
|
|
0
|
return(0) if $stat->[ST_MTIME] == $option->{mtime}; |
202
|
0
|
0
|
|
|
|
0
|
return(0) unless $option->{callback}->($path, $message->{mtime}); |
203
|
|
|
|
|
|
|
utime($stat->[ST_ATIME], $option->{mtime}, $path) |
204
|
|
|
|
|
|
|
or dief("cannot utime(%d, %d, %s): %s", |
205
|
0
|
0
|
|
|
|
0
|
$stat->[ST_ATIME], $option->{mtime}, $path, $!); |
206
|
0
|
|
|
|
|
0
|
return(1); |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# |
210
|
|
|
|
|
|
|
# make sure the the file status is what is expected |
211
|
|
|
|
|
|
|
# |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
my %stat_ensure_options = ( |
214
|
|
|
|
|
|
|
user => { optional => 1, type => SCALAR, regex => qr/^[\w\-]+$/ }, |
215
|
|
|
|
|
|
|
group => { optional => 1, type => SCALAR, regex => qr/^[\w\-]+$/ }, |
216
|
|
|
|
|
|
|
mode => { optional => 1, type => SCALAR, regex => qr/^[\+\-]?\d+$/ }, |
217
|
|
|
|
|
|
|
mtime => { optional => 1, type => SCALAR, regex => qr/^\d+$/ }, |
218
|
|
|
|
|
|
|
follow => { optional => 1, type => BOOLEAN }, |
219
|
|
|
|
|
|
|
callback => { optional => 1, type => CODEREF }, |
220
|
|
|
|
|
|
|
); |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
sub stat_ensure ($@) { |
223
|
4
|
|
|
4
|
1
|
10
|
my($path, %option, %message, @stat, $changed); |
224
|
|
|
|
|
|
|
|
225
|
4
|
|
|
|
|
9
|
$path = shift(@_); |
226
|
4
|
50
|
|
|
|
76
|
%option = validate(@_, \%stat_ensure_options) if @_; |
227
|
4
|
|
|
|
|
95
|
_check_user(\%option, \%message); |
228
|
4
|
|
|
|
|
11
|
_check_group(\%option, \%message); |
229
|
4
|
|
|
|
|
12
|
_check_mode(\%option, \%message); |
230
|
4
|
|
|
|
|
14
|
_check_mtime(\%option, \%message); |
231
|
4
|
|
50
|
3
|
|
33
|
$option{callback} ||= sub { return(1) }; |
|
3
|
|
|
|
|
8
|
|
232
|
4
|
50
|
|
|
|
11
|
dief("no options given") unless keys(%message); |
233
|
4
|
50
|
|
|
|
9
|
if ($option{follow}) { |
234
|
0
|
|
|
|
|
0
|
@stat = stat($path); |
235
|
0
|
0
|
|
|
|
0
|
dief("cannot stat(%s): %s", $path, $!) unless @stat; |
236
|
|
|
|
|
|
|
} else { |
237
|
4
|
|
|
|
|
72
|
@stat = lstat($path); |
238
|
4
|
50
|
|
|
|
16
|
dief("cannot lstat(%s): %s", $path, $!) unless @stat; |
239
|
|
|
|
|
|
|
# we do not try to change symbolic links |
240
|
4
|
50
|
|
|
|
12
|
return(undef) if -l _; |
241
|
|
|
|
|
|
|
} |
242
|
4
|
|
|
|
|
9
|
$changed = 0; |
243
|
|
|
|
|
|
|
# first ensure owner |
244
|
|
|
|
|
|
|
$changed += _ensure_owner($path, \@stat, \%option, \%message) |
245
|
4
|
50
|
33
|
|
|
19
|
if $message{user} or $message{group}; |
246
|
|
|
|
|
|
|
# then ensure mode |
247
|
|
|
|
|
|
|
$changed += _ensure_mode($path, \@stat, \%option, \%message) |
248
|
4
|
50
|
|
|
|
27
|
if $message{mode}; |
249
|
|
|
|
|
|
|
# finally ensure mtime |
250
|
|
|
|
|
|
|
$changed += _ensure_mtime($path, \@stat, \%option, \%message) |
251
|
4
|
50
|
|
|
|
19
|
if $message{mtime}; |
252
|
4
|
|
|
|
|
42
|
return($changed); |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# |
256
|
|
|
|
|
|
|
# return the file type as a string from stat[ST_MODE] |
257
|
|
|
|
|
|
|
# |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
sub stat_type ($) { |
260
|
9
|
|
|
9
|
1
|
4625
|
my($mode) = @_; |
261
|
|
|
|
|
|
|
|
262
|
9
|
100
|
|
|
|
32
|
unless (@_Mode2Type) { |
263
|
1
|
|
|
|
|
3
|
eval { $_Mode2Type[S_IFREG() >> _IBITS] = "plain file" }; |
|
1
|
|
|
|
|
3
|
|
264
|
1
|
|
|
|
|
2
|
eval { $_Mode2Type[S_IFDIR() >> _IBITS] = "directory" }; |
|
1
|
|
|
|
|
2
|
|
265
|
1
|
|
|
|
|
3
|
eval { $_Mode2Type[S_IFIFO() >> _IBITS] = "pipe" }; |
|
1
|
|
|
|
|
2
|
|
266
|
1
|
|
|
|
|
2
|
eval { $_Mode2Type[S_IFSOCK() >> _IBITS] = "socket" }; |
|
1
|
|
|
|
|
3
|
|
267
|
1
|
|
|
|
|
2
|
eval { $_Mode2Type[S_IFBLK() >> _IBITS] = "block device" }; |
|
1
|
|
|
|
|
3
|
|
268
|
1
|
|
|
|
|
3
|
eval { $_Mode2Type[S_IFCHR() >> _IBITS] = "character device" }; |
|
1
|
|
|
|
|
2
|
|
269
|
1
|
|
|
|
|
2
|
eval { $_Mode2Type[S_IFLNK() >> _IBITS] = "symlink" }; |
|
1
|
|
|
|
|
2
|
|
270
|
1
|
|
|
|
|
2
|
eval { $_Mode2Type[S_IFDOOR() >> _IBITS] = "door" }; |
|
1
|
|
|
|
|
19
|
|
271
|
1
|
|
|
|
|
3
|
eval { $_Mode2Type[S_IFPORT() >> _IBITS] = "event port" }; |
|
1
|
|
|
|
|
10
|
|
272
|
1
|
|
|
|
|
2
|
eval { $_Mode2Type[S_IFNWK() >> _IBITS] = "network file" }; |
|
1
|
|
|
|
|
7
|
|
273
|
1
|
|
|
|
|
3
|
eval { $_Mode2Type[S_IFWHT() >> _IBITS] = "whiteout" }; |
|
1
|
|
|
|
|
18
|
|
274
|
|
|
|
|
|
|
} |
275
|
9
|
|
|
|
|
29
|
$mode &= S_IFMT; |
276
|
9
|
|
|
|
|
16
|
$mode >>= _IBITS; |
277
|
9
|
|
50
|
|
|
45
|
return($_Mode2Type[$mode] || "unknown"); |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# |
281
|
|
|
|
|
|
|
# export control |
282
|
|
|
|
|
|
|
# |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
sub import : method { |
285
|
2
|
|
|
2
|
|
12
|
my($pkg, %exported); |
286
|
|
|
|
|
|
|
|
287
|
2
|
|
|
|
|
4
|
$pkg = shift(@_); |
288
|
2
|
|
|
|
|
142
|
grep($exported{$_}++, grep(/^ST?_[A-Z]+$/, keys(%No::Worries::Stat::))); |
289
|
2
|
|
|
|
|
14
|
grep($exported{$_}++, qw(stat_ensure stat_type)); |
290
|
2
|
|
|
|
|
10
|
export_control(scalar(caller()), $pkg, \%exported, @_); |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
1; |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
__DATA__ |