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