line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
#=============================================================================== |
3
|
|
|
|
|
|
|
# PODNAME: Logwatch::RecordTree |
4
|
|
|
|
|
|
|
# ABSTRACT: an object to collect and print Logwatch events |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# AUTHOR: Reid Augustin (REID) |
7
|
|
|
|
|
|
|
# EMAIL: reid@hellosix.com |
8
|
|
|
|
|
|
|
# CREATED: Thu Mar 12 18:41:04 PDT 2015 |
9
|
|
|
|
|
|
|
#=============================================================================== |
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
764
|
use 5.008; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
39
|
|
12
|
1
|
|
|
1
|
|
3
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
13
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
0
|
|
|
1
|
|
|
|
|
41
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
package Logwatch::RecordTree; |
16
|
1
|
|
|
1
|
|
2229
|
use Moo; |
|
1
|
|
|
|
|
15352
|
|
|
1
|
|
|
|
|
6
|
|
17
|
1
|
|
|
1
|
|
1201
|
use Carp qw( croak ); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
52
|
|
18
|
1
|
|
|
1
|
|
426
|
use UNIVERSAL::require; |
|
1
|
|
|
|
|
980
|
|
|
1
|
|
|
|
|
8
|
|
19
|
1
|
|
|
1
|
|
27
|
use List::Util qw ( max min sum ); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
88
|
|
20
|
1
|
|
|
1
|
|
461
|
use Sort::Key::Natural qw( natsort natkeysort ); |
|
1
|
|
|
|
|
27686
|
|
|
1
|
|
|
|
|
115
|
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $VERSION = '2.055'; # VERSION |
23
|
|
|
|
|
|
|
|
24
|
1
|
|
|
1
|
|
10
|
use overload '""' => \&sprint; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
11
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $_defaults = {}; # class variable |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
sub defaults { |
29
|
93
|
|
|
93
|
1
|
104
|
my ($self) = @_; |
30
|
|
|
|
|
|
|
|
31
|
93
|
|
66
|
|
|
225
|
my $name = ref $self || $self; |
32
|
93
|
|
100
|
|
|
180
|
$_defaults->{$name} ||= {}; # a hash for each sub-class |
33
|
93
|
|
|
|
|
1683
|
return $_defaults->{$name}; |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub import { |
37
|
1
|
|
|
1
|
|
11
|
my ($class, %hash) = @_; |
38
|
|
|
|
|
|
|
|
39
|
1
|
|
|
|
|
34
|
my $defaults = $class->defaults(); |
40
|
1
|
|
|
|
|
29
|
while (my ($key, $value) = each %hash) { |
41
|
0
|
|
|
|
|
0
|
$defaults->{$key} = $value; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
215
|
50
|
|
215
|
0
|
5345
|
sub check_coderef { die 'Not a CODE ref' if (ref $_[0] ne 'CODE') }; |
46
|
92
|
50
|
|
92
|
0
|
2164
|
sub check_hashref { die 'Not a HASH ref' if (ref $_[0] ne 'HASH') }; |
47
|
95
|
50
|
|
95
|
0
|
2530
|
sub check_arryref { die 'Not an ARRAY ref' if (ref $_[0] ne 'ARRAY') }; |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
has name => ( # name/title for this item |
50
|
|
|
|
|
|
|
is => 'ro', |
51
|
|
|
|
|
|
|
); |
52
|
|
|
|
|
|
|
has sprint_name => ( # callback to print the name |
53
|
|
|
|
|
|
|
is => 'rw', |
54
|
|
|
|
|
|
|
isa => \&check_coderef, |
55
|
|
|
|
|
|
|
default => sub { |
56
|
|
|
|
|
|
|
sub { |
57
|
|
|
|
|
|
|
return $_[0]->name; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
}, |
60
|
|
|
|
|
|
|
); |
61
|
|
|
|
|
|
|
has sort_key => ( # this overrides ->name in sort_children |
62
|
|
|
|
|
|
|
is => 'rw', |
63
|
|
|
|
|
|
|
); |
64
|
|
|
|
|
|
|
has case_sensitive => ( |
65
|
|
|
|
|
|
|
is => 'rw', |
66
|
|
|
|
|
|
|
); |
67
|
|
|
|
|
|
|
has count => ( # count how many times we log this event |
68
|
|
|
|
|
|
|
is => 'rw', |
69
|
|
|
|
|
|
|
default => sub { 0 }, |
70
|
|
|
|
|
|
|
trigger => sub { |
71
|
|
|
|
|
|
|
$_[0]->no_count; |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
); |
74
|
|
|
|
|
|
|
has count_fields => ( # fields to make the count |
75
|
|
|
|
|
|
|
is => 'rw', |
76
|
|
|
|
|
|
|
isa => \&check_arryref, |
77
|
|
|
|
|
|
|
default => sub { [] }, |
78
|
|
|
|
|
|
|
); |
79
|
|
|
|
|
|
|
has count_formatted => ( # count and extended fields, after formatting |
80
|
|
|
|
|
|
|
is => 'rw', |
81
|
|
|
|
|
|
|
); |
82
|
|
|
|
|
|
|
has no_count => ( # suppress count field (probably because it's the same as the parent) |
83
|
|
|
|
|
|
|
is => 'rw', |
84
|
|
|
|
|
|
|
); |
85
|
|
|
|
|
|
|
has children => ( # a hash of child Logwtch::RecordTrees |
86
|
|
|
|
|
|
|
is => 'rw', |
87
|
|
|
|
|
|
|
isa => \&check_hashref, |
88
|
|
|
|
|
|
|
default => sub { {} }, |
89
|
|
|
|
|
|
|
); |
90
|
|
|
|
|
|
|
has limit => ( # limit number of children printed |
91
|
|
|
|
|
|
|
is => 'rw', |
92
|
|
|
|
|
|
|
default => sub { 0 }, # default to no limit |
93
|
|
|
|
|
|
|
); |
94
|
|
|
|
|
|
|
has indent => ( # how much to indent this level of the tree |
95
|
|
|
|
|
|
|
is => 'rw', |
96
|
|
|
|
|
|
|
); |
97
|
|
|
|
|
|
|
has no_indent => ( # flag to suppress indentation of children |
98
|
|
|
|
|
|
|
is => 'rw', |
99
|
|
|
|
|
|
|
); |
100
|
|
|
|
|
|
|
has curr_indent => ( # total indentation of this level |
101
|
|
|
|
|
|
|
is => 'rw', |
102
|
|
|
|
|
|
|
default => sub { '' }, |
103
|
|
|
|
|
|
|
); |
104
|
|
|
|
|
|
|
has post_callback => ( # when array is ready for printing, call this for final adjustments |
105
|
|
|
|
|
|
|
is => 'rw', |
106
|
|
|
|
|
|
|
isa => \&check_coderef, |
107
|
|
|
|
|
|
|
default => sub { sub {} }, |
108
|
|
|
|
|
|
|
); |
109
|
|
|
|
|
|
|
has lines => ( # when array is ready for printing, store a ref to it here |
110
|
|
|
|
|
|
|
is => 'rw', |
111
|
|
|
|
|
|
|
isa => \&check_arryref, |
112
|
|
|
|
|
|
|
); |
113
|
|
|
|
|
|
|
has columnize => ( # flag to indicate we should columnize children |
114
|
|
|
|
|
|
|
is => 'rw', |
115
|
|
|
|
|
|
|
); |
116
|
|
|
|
|
|
|
has neat_names => ( # for the neat freaks |
117
|
|
|
|
|
|
|
is => 'rw', |
118
|
|
|
|
|
|
|
); |
119
|
|
|
|
|
|
|
has neat_format => ( # formatter for neatness, set by sprint |
120
|
|
|
|
|
|
|
is => 'rw', |
121
|
|
|
|
|
|
|
default => sub { "%s" }, # not neat |
122
|
|
|
|
|
|
|
); |
123
|
|
|
|
|
|
|
has extra => ( # a little something extra... |
124
|
|
|
|
|
|
|
is => 'rw', |
125
|
|
|
|
|
|
|
); |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub BUILD { |
128
|
92
|
|
|
92
|
0
|
715
|
my ($self) = @_; |
129
|
|
|
|
|
|
|
|
130
|
92
|
|
|
|
|
101
|
while (my ($key, $value) = each %{$self->defaults}) { |
|
92
|
|
|
|
|
164
|
|
131
|
0
|
|
|
|
|
0
|
$self->$key($value); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub child_by_name { # find child by name(s), follow down the tree |
136
|
394
|
|
|
394
|
1
|
495
|
my ($self, @names) = @_; |
137
|
|
|
|
|
|
|
|
138
|
394
|
|
|
|
|
307
|
my $child = $self; |
139
|
394
|
|
|
|
|
431
|
for my $name (@names) { |
140
|
394
|
100
|
|
|
|
6166
|
return if (not exists $child->children->{$name}); |
141
|
303
|
|
|
|
|
5722
|
$child = $child->children->{$name}; |
142
|
|
|
|
|
|
|
} |
143
|
303
|
|
|
|
|
1698
|
return $child; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub create_child { # create child, add to children |
147
|
91
|
|
|
91
|
1
|
127
|
my ($self, $name, $type, $opts) = @_; |
148
|
|
|
|
|
|
|
|
149
|
91
|
|
100
|
|
|
292
|
$type ||= __PACKAGE__; # default to this package |
150
|
91
|
|
100
|
|
|
236
|
$opts ||= {}; |
151
|
91
|
|
|
|
|
156
|
$opts->{name} = $name; |
152
|
91
|
50
|
|
|
|
291
|
$type->require or croak($@); |
153
|
91
|
|
|
|
|
1975
|
return $self->children->{$name} = $type->new( %{$opts} ); |
|
91
|
|
|
|
|
1790
|
|
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# adopt items, handle name conflicts |
157
|
|
|
|
|
|
|
sub adopt { |
158
|
0
|
|
|
0
|
1
|
0
|
my ($self, $item) = @_; |
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
0
|
my $item_name = $item->name; |
161
|
0
|
|
|
|
|
0
|
my $my_child = $self->child_by_name($item_name); |
162
|
0
|
0
|
|
|
|
0
|
if ($my_child) { |
163
|
|
|
|
|
|
|
# name conflict. my_child must adopt $item's children |
164
|
0
|
|
|
|
|
0
|
my @item_children = values %{$item->children}; |
|
0
|
|
|
|
|
0
|
|
165
|
0
|
0
|
|
|
|
0
|
if (@item_children) { |
166
|
0
|
|
|
|
|
0
|
for my $child (@item_children) { |
167
|
0
|
|
|
|
|
0
|
$my_child->adopt($child); |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
else { |
171
|
|
|
|
|
|
|
# no children, so transfer count directly from item to my_child |
172
|
0
|
|
|
|
|
0
|
$my_child->count($my_child->count + $item->count); |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
else { |
176
|
|
|
|
|
|
|
# no name conflict, just copy over |
177
|
0
|
|
|
|
|
0
|
$self->children->{$item_name} = $item; |
178
|
|
|
|
|
|
|
} |
179
|
0
|
|
|
|
|
0
|
$self->count($self->count + $item->count); |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# log event, add new children if necessaary |
183
|
|
|
|
|
|
|
sub _log_children { |
184
|
197
|
|
|
197
|
|
248
|
my ($self, $name, @children) = @_; |
185
|
|
|
|
|
|
|
|
186
|
197
|
|
|
|
|
138
|
my ($type, $opts); |
187
|
197
|
100
|
|
|
|
379
|
if (ref $name eq 'ARRAY') { |
188
|
42
|
|
|
|
|
34
|
($name, $type, $opts) = @{$name}; |
|
42
|
|
|
|
|
79
|
|
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
197
|
50
|
|
|
|
278
|
$name = "" if (not defined $name); # supposed to be a list of names or array-refs |
192
|
|
|
|
|
|
|
|
193
|
197
|
|
|
|
|
267
|
my $child = $self->child_by_name($name); |
194
|
197
|
100
|
|
|
|
1208
|
if (not defined $child) { |
195
|
91
|
|
|
|
|
179
|
$child = $self->create_child($name, $type, $opts) |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
197
|
100
|
|
|
|
1597
|
if (@children) { |
199
|
111
|
|
|
|
|
197
|
return $child->_log_children(@children); |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
86
|
|
|
|
|
203
|
return $child; |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
sub _count { # add 1 to count down the path |
206
|
283
|
|
|
283
|
|
386
|
my ($self, $name, @children) = @_; |
207
|
|
|
|
|
|
|
|
208
|
283
|
|
|
|
|
4580
|
$self->count($self->count + 1); |
209
|
|
|
|
|
|
|
|
210
|
283
|
100
|
|
|
|
867
|
$name = $name->[0] if (ref $name); |
211
|
283
|
100
|
|
|
|
425
|
if (defined $name) { |
212
|
197
|
|
|
|
|
277
|
return $self->child_by_name($name)->_count(@children); |
213
|
|
|
|
|
|
|
} |
214
|
86
|
|
|
|
|
335
|
return $self; |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub log_no_count { # log new event without counting, add children if necessary |
218
|
0
|
|
|
0
|
1
|
0
|
my ($self, @args) = @_; |
219
|
|
|
|
|
|
|
|
220
|
0
|
|
|
|
|
0
|
return $self->_log_children(@args); |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub log { # log new event adding to count, add children if necessary |
224
|
86
|
|
|
86
|
1
|
951
|
my ($self, @args) = @_; |
225
|
|
|
|
|
|
|
|
226
|
86
|
|
|
|
|
143
|
$self->_log_children(@args); |
227
|
86
|
|
|
|
|
184
|
return $self->_count(@args); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# return sorted list of child names |
231
|
|
|
|
|
|
|
sub sort_children { # sort children |
232
|
2
|
|
|
2
|
1
|
2
|
my ($self) = @_; |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# make hash, value is name, key is sort_key or name |
235
|
8
|
100
|
|
|
|
46
|
my %keys = map { (defined($_->sort_key) ? $_->sort_key : $_->name) => $_ } |
|
2
|
|
|
|
|
30
|
|
236
|
2
|
|
|
|
|
3
|
values %{$self->children}; |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
# sort by hash keys, create array of values to get back to names |
239
|
0
|
|
|
|
|
0
|
my @children = $self->case_sensitive |
240
|
8
|
|
|
|
|
123
|
? map { $keys{$_} } natsort keys %keys |
241
|
2
|
50
|
|
8
|
|
23
|
: map { $keys{$_} } natkeysort { lc $_ } keys %keys; |
|
8
|
|
|
|
|
402
|
|
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
return wantarray |
244
|
|
|
|
|
|
|
? @children |
245
|
2
|
50
|
|
|
|
16
|
: \@children; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# make neat column of child names |
249
|
|
|
|
|
|
|
sub _neaten_children { |
250
|
1
|
|
|
1
|
|
2
|
my ($self) = @_; |
251
|
|
|
|
|
|
|
|
252
|
1
|
|
|
|
|
2
|
my $max = max(1, map { length $_->sprint_name->($_) } values %{$self->children}); |
|
2
|
|
|
|
|
38
|
|
|
1
|
|
|
|
|
21
|
|
253
|
0
|
0
|
|
|
|
0
|
my $format = $self->neat_names < 0 |
254
|
|
|
|
|
|
|
? "%-${max}s" |
255
|
|
|
|
|
|
|
: "%${max}s"; |
256
|
0
|
|
|
|
|
0
|
map { $_->neat_format($format) } values %{$self->children}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
# make neat columns of all the count fields |
260
|
|
|
|
|
|
|
sub _format_child_counts { |
261
|
4
|
|
|
4
|
|
19
|
my ($self, $children, $depth) = @_; |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
# measure each field, save max length for each column |
264
|
4
|
|
|
|
|
4
|
my @maxes; |
265
|
4
|
|
|
|
|
5
|
for my $child (values %{$children}) { |
|
4
|
|
|
|
|
51
|
|
266
|
15
|
|
|
|
|
15
|
unshift @{$child->count_fields}, $child->count; |
|
15
|
|
|
|
|
270
|
|
267
|
15
|
|
|
|
|
906
|
my $ii = 0; |
268
|
15
|
|
|
|
|
11
|
for my $field (@{$child->count_fields}) { |
|
15
|
|
|
|
|
222
|
|
269
|
15
|
|
100
|
|
|
110
|
$maxes[$ii] = max($maxes[$ii] || 0, length $field); |
270
|
15
|
|
|
|
|
35
|
$ii++; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
# string to indent children: total count field width or at least 3 |
275
|
4
|
|
|
|
|
14
|
my $min = sum(1, @maxes); |
276
|
4
|
100
|
|
|
|
13
|
$min = 3 if ($min < 3); |
277
|
4
|
|
|
|
|
9
|
my $child_indent = " " x $min; |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# pad all fields to the max for the column |
280
|
4
|
|
|
|
|
5
|
for my $child (values %{$children}) { |
|
4
|
|
|
|
|
9
|
|
281
|
15
|
|
|
|
|
259
|
my $ccf = $child->count_fields; |
282
|
15
|
|
|
|
|
53
|
my @padded; |
283
|
15
|
|
|
|
|
27
|
for my $ii (0 .. $#maxes) { |
284
|
15
|
|
50
|
|
|
58
|
$padded[$ii] = sprintf "%*s", $maxes[$ii], $ccf->[$ii] || ''; |
285
|
|
|
|
|
|
|
} |
286
|
15
|
|
|
|
|
37
|
$child->count_formatted(join '', @padded); |
287
|
15
|
100
|
|
|
|
48
|
$child->indent($child_indent) if (not defined $child->indent); |
288
|
15
|
|
|
|
|
13
|
shift @{$child->count_fields}; # remove the count field we inserted above |
|
15
|
|
|
|
|
226
|
|
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
# compare our count fields to $other's (for suppression when identical) |
293
|
|
|
|
|
|
|
sub _count_fields_differ { |
294
|
1
|
|
|
1
|
|
2
|
my ($self, $other) = @_; |
295
|
|
|
|
|
|
|
|
296
|
1
|
50
|
|
|
|
22
|
return 1 if ($self->count != $other->count); |
297
|
1
|
|
|
|
|
11
|
for my $ii (0 .. max($#{$self->count_fields}, $#{$other->count_fields})) { |
|
1
|
|
|
|
|
27
|
|
|
1
|
|
|
|
|
25
|
|
298
|
0
|
0
|
0
|
|
|
0
|
return 1 if ( |
|
|
|
0
|
|
|
|
|
299
|
|
|
|
|
|
|
not defined $self->count_fields->[$ii] or |
300
|
|
|
|
|
|
|
not defined $other->count_fields->[$ii] or |
301
|
|
|
|
|
|
|
$self->count_fields->[$ii] ne $other->count_fields->[$ii]); |
302
|
|
|
|
|
|
|
} |
303
|
1
|
|
|
|
|
14
|
return 0; # match |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
sub sprint { |
307
|
3
|
|
|
3
|
1
|
7
|
my ($self, $callback, $path, $parent_indent, $depth) = @_; |
308
|
|
|
|
|
|
|
|
309
|
3
|
|
100
|
|
|
15
|
$path ||= []; |
310
|
3
|
|
50
|
|
|
16
|
$parent_indent ||= ''; |
311
|
3
|
|
100
|
|
|
8
|
$depth ||= 1; |
312
|
|
|
|
|
|
|
|
313
|
3
|
100
|
|
|
|
11
|
if ($depth == 1) { |
314
|
|
|
|
|
|
|
# top level needs to format its own count field |
315
|
1
|
|
|
|
|
6
|
$self->_format_child_counts({ top => $self }, 0); |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
3
|
100
|
|
|
|
22
|
my $count = $self->no_count |
319
|
|
|
|
|
|
|
? '' |
320
|
|
|
|
|
|
|
: $self->count_formatted . ' '; |
321
|
|
|
|
|
|
|
|
322
|
3
|
|
|
|
|
48
|
$self->lines(my $lines = []); |
323
|
|
|
|
|
|
|
|
324
|
3
|
50
|
|
|
|
75
|
if (length($self->name)) { |
325
|
3
|
|
|
|
|
4
|
push @{$lines}, join( '', |
|
3
|
|
|
|
|
77
|
|
326
|
|
|
|
|
|
|
$count, |
327
|
|
|
|
|
|
|
sprintf $self->neat_format, $self->sprint_name->($self), |
328
|
|
|
|
|
|
|
); |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
else { |
331
|
0
|
|
|
|
|
0
|
push @{$lines}, ''; # name is blank, so don't add anything here |
|
0
|
|
|
|
|
0
|
|
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# format count fields and calculate indent for all children, |
335
|
3
|
|
|
|
|
54
|
$self->_format_child_counts($self->children, $depth); |
336
|
|
|
|
|
|
|
|
337
|
3
|
|
|
|
|
24
|
my $children = $self->sort_children; |
338
|
3
|
|
|
|
|
4
|
my $child_count = @{$children}; |
|
3
|
|
|
|
|
6
|
|
339
|
|
|
|
|
|
|
|
340
|
3
|
100
|
|
|
|
10
|
if ($child_count == 1) { # join single child to this line |
341
|
1
|
|
|
|
|
3
|
my $child = $children->[0]; |
342
|
|
|
|
|
|
|
# save the child's flags we're going to alter |
343
|
1
|
|
|
|
|
3
|
my %flags = map { $_ => $child->$_ } qw( no_count no_indent ); |
|
2
|
|
|
|
|
12
|
|
344
|
|
|
|
|
|
|
# suppress count field if child's is same as ours |
345
|
1
|
50
|
|
|
|
4
|
$child->no_count(1) if (not $self->_count_fields_differ($child)); |
346
|
1
|
|
|
|
|
2
|
$child->no_indent(1); |
347
|
1
|
|
|
|
|
5
|
$child->curr_indent($parent_indent); # no extra indent since we concat this line |
348
|
|
|
|
|
|
|
|
349
|
1
|
|
|
|
|
2
|
push @{$path}, $child->name; |
|
1
|
|
|
|
|
4
|
|
350
|
1
|
|
|
|
|
8
|
$child->sprint($callback, $path, $self->curr_indent, $depth + 1); |
351
|
0
|
0
|
|
|
|
0
|
if (length($lines->[0]) + length($child->lines->[0]) <= $self->width) { |
352
|
0
|
|
|
|
|
0
|
$lines->[0] .= ' ' . shift @{$child->lines}; |
|
0
|
|
|
|
|
0
|
|
353
|
|
|
|
|
|
|
} |
354
|
0
|
|
|
|
|
0
|
push @{$lines}, @{$child->lines}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
355
|
0
|
|
|
|
|
0
|
pop @{$path}; |
|
0
|
|
|
|
|
0
|
|
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
# restore child's flags |
358
|
0
|
|
|
|
|
0
|
map { $child->$_($flags{$_}) } keys %flags; |
|
0
|
|
|
|
|
0
|
|
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
2
|
50
|
|
|
|
6
|
if ($child_count > 1) { |
362
|
2
|
|
33
|
|
|
19
|
my $last = $self->limit || $child_count; # if limit is zero, print all |
363
|
2
|
50
|
|
|
|
8
|
$last = $child_count if ($child_count - $last < 3); # if within 3, just print all |
364
|
2
|
|
|
|
|
10
|
$last = $child_count - $last; # convert from limit to the last index |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# handle neat_children flag |
367
|
2
|
100
|
|
|
|
16
|
$self->_neaten_children if ($self->neat_names); |
368
|
|
|
|
|
|
|
|
369
|
1
|
|
|
|
|
1
|
for my $child (@{$children}) { |
|
1
|
|
|
|
|
10
|
|
370
|
1
|
|
|
|
|
10
|
$child->curr_indent($parent_indent . $self->indent); |
371
|
1
|
50
|
|
|
|
6
|
if ($child_count <= $last) { |
372
|
0
|
|
|
|
|
0
|
push @{$lines}, "... and $child_count more"; |
|
0
|
|
|
|
|
0
|
|
373
|
0
|
|
|
|
|
0
|
last; |
374
|
|
|
|
|
|
|
} |
375
|
1
|
|
|
|
|
1
|
push @{$path}, $child->name; |
|
1
|
|
|
|
|
10
|
|
376
|
1
|
|
|
|
|
11
|
$child->sprint($callback, $path, $self->curr_indent, $depth + 1); |
377
|
0
|
|
|
|
|
|
push @{$lines}, @{$child->lines}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
378
|
0
|
|
|
|
|
|
pop @{$path}; |
|
0
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
|
$child_count--; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
0
|
0
|
|
|
|
|
$self->sprint_columns if ($self->columnize); |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# indent children |
386
|
0
|
0
|
|
|
|
|
if (not $self->no_indent) { |
387
|
0
|
|
|
|
|
|
for my $ii (1 .. $#{$lines}) { |
|
0
|
|
|
|
|
|
|
388
|
0
|
|
|
|
|
|
$lines->[$ii] = $self->indent . $lines->[$ii]; |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
0
|
|
|
|
|
|
$self->post_callback->($self, $path); |
393
|
|
|
|
|
|
|
|
394
|
0
|
0
|
|
|
|
|
$callback->($self, $path) if ($callback); |
395
|
|
|
|
|
|
|
|
396
|
0
|
|
|
|
|
|
return join "\n", @{$lines}; |
|
0
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
sub width { |
400
|
0
|
|
|
0
|
1
|
|
my ($self, $new) = @_; |
401
|
|
|
|
|
|
|
|
402
|
0
|
0
|
|
|
|
|
if (@_ > 1) { |
403
|
0
|
|
|
|
|
|
$self->{_width} = $new; |
404
|
|
|
|
|
|
|
} |
405
|
0
|
0
|
|
|
|
|
return $self->{_width} if (exists $self->{_width}); |
406
|
0
|
|
|
|
|
|
return 80; # default page width |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
sub col_width { |
410
|
0
|
|
|
0
|
1
|
|
my ($self, $new) = @_; |
411
|
|
|
|
|
|
|
|
412
|
0
|
0
|
|
|
|
|
if (@_ > 1) { |
413
|
0
|
|
|
|
|
|
$self->{_col_width} = $new; |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
0
|
0
|
|
|
|
|
if (not exists $self->{_col_width}) { |
417
|
|
|
|
|
|
|
# find longest line length, excluding the first line |
418
|
0
|
|
|
|
|
|
my $lines = $self->lines; |
419
|
0
|
|
|
|
|
|
$self->{_col_width} = max( 1, map { length $lines->[$_] } (1 .. $#{$lines})); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
} |
421
|
0
|
|
|
|
|
|
return $self->{_col_width}; |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
sub col_count { |
425
|
0
|
|
|
0
|
1
|
|
my ($self, $new) = @_; |
426
|
|
|
|
|
|
|
|
427
|
0
|
0
|
|
|
|
|
if (@_ > 1) { |
428
|
0
|
|
|
|
|
|
$self->{_col_count} = $new; |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
0
|
0
|
|
|
|
|
if (not exists $self->{_col_count}) { |
432
|
|
|
|
|
|
|
# calculate from curr_indent, width, and col_width |
433
|
0
|
|
|
|
|
|
my $width = $self->width - length($self->curr_indent); |
434
|
0
|
|
|
|
|
|
$width = max(0, $width); # at least 0 |
435
|
0
|
|
0
|
|
|
|
$self->{_col_count} = int ($width / $self->col_width) || 1; # at least 1 |
436
|
|
|
|
|
|
|
} |
437
|
0
|
|
|
|
|
|
return $self->{_col_count}; |
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# re-arrange children into columns |
441
|
|
|
|
|
|
|
sub sprint_columns { |
442
|
0
|
|
|
0
|
1
|
|
my ($self, $width, $col_count, $col_width) = @_; |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
# allow caller to override width, col_count, and col_width |
445
|
0
|
0
|
|
|
|
|
$self->width($width) if (defined $width); |
446
|
0
|
0
|
|
|
|
|
$self->col_count($col_count) if (defined $col_count); |
447
|
0
|
0
|
|
|
|
|
$self->col_width($col_width) if (defined $col_width); |
448
|
|
|
|
|
|
|
|
449
|
0
|
|
|
|
|
|
$col_count = $self->col_count; |
450
|
0
|
|
|
|
|
|
$col_width = $self->col_width; |
451
|
|
|
|
|
|
|
|
452
|
0
|
|
|
|
|
|
my $lines = $self->lines; |
453
|
0
|
|
|
|
|
|
my @new_lines = shift @{$lines}; # first line is unchanged |
|
0
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
|
455
|
0
|
|
|
|
|
|
my $lines_per_col = int ((@{$lines} + $col_count - 1) / $col_count); |
|
0
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
|
457
|
0
|
|
|
|
|
|
for my $ii (0 .. $lines_per_col - 1) { |
458
|
0
|
|
|
|
|
|
my @line; |
459
|
0
|
0
|
|
|
|
|
if ($col_count <= 1) { |
460
|
|
|
|
|
|
|
# single column, just prepend indent |
461
|
0
|
|
|
|
|
|
push @new_lines, join '', |
462
|
|
|
|
|
|
|
$self->indent, |
463
|
|
|
|
|
|
|
$lines->[$ii]; |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
else { |
466
|
|
|
|
|
|
|
# join segments into a line of columns |
467
|
0
|
|
|
|
|
|
for my $jj (0 .. $col_count - 1) { |
468
|
0
|
|
|
|
|
|
my $l = $lines->[$ii + $jj * $lines_per_col]; |
469
|
0
|
0
|
|
|
|
|
push @line, sprintf "%-*s", $col_width, $l if (defined $l); |
470
|
|
|
|
|
|
|
} |
471
|
0
|
|
|
|
|
|
push @new_lines, join '', |
472
|
|
|
|
|
|
|
$self->indent, |
473
|
|
|
|
|
|
|
join(' ', @line); |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
} |
476
|
0
|
|
|
|
|
|
$self->lines(\@new_lines); |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
1; |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
__END__ |