line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package App::week::CalYear; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
12
|
use v5.14; |
|
1
|
|
|
|
|
3
|
|
4
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
28
|
|
5
|
1
|
|
|
1
|
|
5
|
use utf8; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
29
|
use Exporter 'import'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
48
|
|
8
|
|
|
|
|
|
|
our @EXPORT_OK = qw(@calyear); |
9
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
6
|
use Encode; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
81
|
|
11
|
1
|
|
|
1
|
|
5
|
use Data::Dumper; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
49
|
|
12
|
1
|
|
|
1
|
|
6
|
use open IO => ':utf8'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
10
|
|
13
|
1
|
|
|
1
|
|
164
|
use List::Util qw(uniq); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
55
|
|
14
|
1
|
|
|
1
|
|
6
|
use Hash::Util qw(lock_keys); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
8
|
|
15
|
1
|
|
|
1
|
|
84
|
use Text::VisualWidth::PP qw(vwidth); |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
47
|
|
16
|
1
|
|
|
1
|
|
6
|
use Text::ANSI::Fold; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
59
|
|
17
|
1
|
|
|
1
|
|
12
|
use Date::Japanese::Era; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
tie our @calyear, __PACKAGE__; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub TIEARRAY { |
22
|
1
|
|
|
1
|
|
2
|
my $pkg = shift; |
23
|
1
|
|
|
|
|
4
|
bless {}, $pkg; |
24
|
|
|
|
|
|
|
} |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub FETCH { |
27
|
0
|
|
|
0
|
|
|
my($obj, $year) = @_; |
28
|
0
|
|
0
|
|
|
|
$obj->{$year} //= [ CalYear($year) ]; |
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my %config = ( |
32
|
|
|
|
|
|
|
show_year => 1, |
33
|
|
|
|
|
|
|
overstruck => 1, |
34
|
|
|
|
|
|
|
wareki => undef, |
35
|
|
|
|
|
|
|
netbsd => undef, |
36
|
|
|
|
|
|
|
crashspace => undef, |
37
|
|
|
|
|
|
|
tabify => undef, |
38
|
|
|
|
|
|
|
shortmonth => undef, |
39
|
|
|
|
|
|
|
weeknumber => 0, # 0)none 1)us 2)standard 3)iso |
40
|
|
|
|
|
|
|
); |
41
|
|
|
|
|
|
|
lock_keys %config; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub Configure { |
44
|
0
|
|
|
0
|
0
|
|
while (my($k, $v) = splice(@_, 0, 2)) { |
45
|
0
|
|
|
|
|
|
$config{$k} = $v; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
sub CalYear { |
50
|
0
|
|
|
0
|
0
|
|
my $year = sprintf "%4d", shift; |
51
|
|
|
|
|
|
|
my $cal = normalize( |
52
|
0
|
0
|
|
|
|
|
$config{weeknumber} > 1 ? gcal($year) : cal($year) |
53
|
|
|
|
|
|
|
); |
54
|
0
|
|
|
|
|
|
my @cal = split /\n/, $cal, -1; |
55
|
0
|
|
|
|
|
|
my @monthline = do { |
56
|
0
|
|
|
|
|
|
map { $_ - 2 } # 2 lines up |
57
|
0
|
|
|
|
|
|
grep { $cal[$_] =~ /\s 1 \s/x } # find 1st day |
|
0
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
0 .. $#cal; |
59
|
|
|
|
|
|
|
}; |
60
|
0
|
0
|
|
|
|
|
@monthline == 4 or die "cal(1) command format error.\n"; |
61
|
|
|
|
|
|
|
|
62
|
0
|
|
|
|
|
|
state $fielder = do { |
63
|
0
|
|
|
|
|
|
my @weekline = map $_ + 1, @monthline; |
64
|
0
|
|
|
|
|
|
fielder($cal[ $weekline[0] ]); |
65
|
|
|
|
|
|
|
}; |
66
|
|
|
|
|
|
|
|
67
|
0
|
|
|
|
|
|
my @month = ( [ $cal[0] ], map [], 1..12 ); |
68
|
0
|
|
|
|
|
|
for my $i (0 .. $#monthline) { |
69
|
0
|
|
|
|
|
|
my $start = $monthline[$i]; |
70
|
0
|
|
|
|
|
|
for my $n (0..7) { |
71
|
0
|
|
|
|
|
|
my @m = $fielder->($cal[$start + $n]); |
72
|
0
|
|
|
|
|
|
push @{$month[$i * 3 + 1]}, $m[0]; |
|
0
|
|
|
|
|
|
|
73
|
0
|
|
|
|
|
|
push @{$month[$i * 3 + 2]}, $m[1]; |
|
0
|
|
|
|
|
|
|
74
|
0
|
|
|
|
|
|
push @{$month[$i * 3 + 3]}, $m[2]; |
|
0
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
} |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
0
|
0
|
|
|
|
|
insert_week_number(@month[1..12]) if $config{weeknumber} == 1; |
79
|
0
|
|
|
|
|
|
tidy_up(@month[1..12]); |
80
|
|
|
|
|
|
|
|
81
|
0
|
|
0
|
|
|
|
my $wareki = $config{wareki} // $month[1][1] =~ /火/; |
82
|
0
|
|
|
|
|
|
for my $month (&show_year($year)) { |
83
|
0
|
0
|
0
|
|
|
|
1 <= $month and $month <= 12 or next; |
84
|
0
|
|
|
|
|
|
insert_year(\$month[$month][0], $year, $month, $wareki); |
85
|
|
|
|
|
|
|
} |
86
|
0
|
|
|
|
|
|
@month; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub normalize { |
90
|
0
|
|
|
0
|
0
|
|
local $_ = shift; |
91
|
0
|
0
|
|
|
|
|
if (/\t/) { $_ = expand_tab($_) } |
|
0
|
|
|
|
|
|
|
92
|
0
|
0
|
|
|
|
|
if (/\cH/) { s/.\cH//g } |
|
0
|
|
|
|
|
|
|
93
|
0
|
|
|
|
|
|
$_; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
sub cal { |
97
|
0
|
|
|
0
|
0
|
|
my $option = shift; |
98
|
0
|
|
|
|
|
|
local $_ = `cal $option`; |
99
|
0
|
0
|
|
|
|
|
if ($config{crashspace}) { |
100
|
0
|
|
|
|
|
|
s/ +$//mg; |
101
|
|
|
|
|
|
|
} |
102
|
0
|
0
|
|
|
|
|
if ($config{netbsd}) { |
103
|
0
|
|
|
|
|
|
s/(Su|Mo|We|Fr|Sa)/sprintf '%2.1s', $1/mge; |
|
0
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
} |
105
|
0
|
0
|
|
|
|
|
if ($config{shortmonth}) { |
106
|
0
|
|
|
|
|
|
s{([A-Z][a-z][a-z])(\w+ )}{ |
107
|
1
|
|
|
1
|
|
1221
|
use integer; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
16
|
|
108
|
0
|
|
|
|
|
|
my $sp = length($2); |
109
|
0
|
|
|
|
|
|
(' ' x ($sp/2 + $sp%2)) . $1 . (' ' x ($sp/2)); |
110
|
|
|
|
|
|
|
}mge; |
111
|
|
|
|
|
|
|
} |
112
|
0
|
0
|
0
|
|
|
|
if ($config{tabify} and !/\t/) { |
113
|
|
|
|
|
|
|
# does not expect wide characters |
114
|
0
|
|
|
|
|
|
s{(.{8})}{ $1 =~ s/ +$/\t/r }ge; |
|
0
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
} |
116
|
0
|
|
|
|
|
|
$_; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub gcal { |
120
|
0
|
|
|
0
|
0
|
|
my $option = shift; |
121
|
0
|
0
|
|
|
|
|
my $iso = '--iso-week-number=' . ($config{weeknumber} == 2 ? 'no' : 'yes'); |
122
|
0
|
|
|
|
|
|
my $exec = "gcal -i -H no $iso -K $option"; |
123
|
0
|
|
|
|
|
|
local $_ = qx/$exec/; |
124
|
0
|
|
|
|
|
|
$_; |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub insert_week_number { |
128
|
0
|
|
|
0
|
0
|
|
my $n = 1; |
129
|
0
|
|
|
|
|
|
for my $month (@_) { |
130
|
0
|
|
|
|
|
|
$month->[0] .= ' '; |
131
|
0
|
|
|
|
|
|
$month->[1] .= ' CW'; |
132
|
0
|
|
|
|
|
|
for (@{$month}[2..7]) { |
|
0
|
|
|
|
|
|
|
133
|
0
|
0
|
|
|
|
|
my $cw = /\S/ ? sprintf(' %02d', $n) : ' '; |
134
|
0
|
0
|
|
|
|
|
$n++ if /\S$/; |
135
|
0
|
|
|
|
|
|
$_ .= $cw; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
sub tidy_up { |
141
|
0
|
|
|
0
|
0
|
|
for my $month (@_) { |
142
|
|
|
|
|
|
|
# insert frame |
143
|
0
|
|
|
|
|
|
$_ = " $_ " for @$month; |
144
|
|
|
|
|
|
|
# fix month name: |
145
|
0
|
|
|
|
|
|
for ($month->[0]) { |
146
|
|
|
|
|
|
|
# 1) Take care of cal(1) multibyte string bug. |
147
|
|
|
|
|
|
|
# 2) Normalize off-to-right to off-to-left. |
148
|
0
|
0
|
|
|
|
|
if (/^( +)(\S+)( +)$/) { |
149
|
0
|
|
|
|
|
|
my $sp = length $1.$3; |
150
|
0
|
|
|
|
|
|
my $left = int $sp / 2; |
151
|
0
|
|
|
|
|
|
my $right = $left + $sp % 2; |
152
|
0
|
|
|
|
|
|
$_ = ' ' x $left . $2 . ' ' x $right; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub fielder { |
159
|
0
|
|
|
0
|
0
|
|
my $dow_line = shift; |
160
|
1
|
|
|
1
|
|
631
|
use Unicode::EastAsianWidth; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
105
|
|
161
|
0
|
|
|
|
|
|
my $dow_re = qr/\p{InFullwidth}|[ \S]\S/; |
162
|
0
|
0
|
|
|
|
|
$dow_line =~ m{^ (\s*) |
163
|
|
|
|
|
|
|
( (?: $dow_re [ ]){6} $dow_re (?:[ ]CW)? ) (\s+) |
164
|
|
|
|
|
|
|
( (?: $dow_re [ ]){6} $dow_re (?:[ ]CW)? ) (\s+) |
165
|
|
|
|
|
|
|
( (?: $dow_re [ ]){6} $dow_re (?:[ ]CW)? ) |
166
|
|
|
|
|
|
|
}x or die "cal(1): unexpected day-of-week line."; |
167
|
0
|
|
|
|
|
|
my $w = vwidth $2; |
168
|
0
|
|
|
|
|
|
my @w = (length $1, $w, length $3, $w, length $5, $w); |
169
|
0
|
|
|
|
|
|
my $blank = ' ' x $w; |
170
|
0
|
|
|
|
|
|
my $fold = Text::ANSI::Fold->new(width => \@w, padding => 1); |
171
|
|
|
|
|
|
|
sub { |
172
|
0
|
|
|
0
|
|
|
my $l = shift; |
173
|
0
|
|
|
|
|
|
my @f = $fold->text($l)->chops; |
174
|
0
|
|
0
|
|
|
|
map { $_ // $blank } @f[1, 3, 5]; |
|
0
|
|
|
|
|
|
|
175
|
0
|
|
|
|
|
|
}; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub show_year { |
179
|
0
|
|
|
0
|
0
|
|
my $conf = $config{show_year}; |
180
|
0
|
|
|
|
|
|
my $year = shift; |
181
|
0
|
0
|
|
|
|
|
if ((my $ref = ref $conf) eq '') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
182
|
0
|
|
|
|
|
|
( $conf ); |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
elsif ($ref eq 'ARRAY') { |
185
|
0
|
|
|
|
|
|
@{$conf}; |
|
0
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
elsif ($ref eq 'HASH') { |
188
|
0
|
|
|
|
|
|
uniq do { |
189
|
|
|
|
|
|
|
map { |
190
|
0
|
|
|
|
|
|
my $v = $conf->{$_}; |
191
|
0
|
0
|
|
|
|
|
ref $v eq 'ARRAY' ? @$v : $v |
192
|
|
|
|
|
|
|
} |
193
|
0
|
0
|
|
|
|
|
grep { $_ eq '*' or $_ == $year } |
|
0
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
keys %$conf; |
195
|
|
|
|
|
|
|
}; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub insert_year { |
200
|
0
|
|
|
0
|
0
|
|
local *_ = shift; |
201
|
0
|
|
|
|
|
|
my($year, $month, $wareki) = @_; |
202
|
0
|
|
|
|
|
|
my $len = length($year); |
203
|
0
|
|
|
|
|
|
s/^[ ]\K[ ]{$len}/$year/; |
204
|
0
|
0
|
0
|
|
|
|
if (1873 <= $year and $wareki) { |
205
|
0
|
|
|
|
|
|
my $era = Date::Japanese::Era->new($year, $month, 1); |
206
|
0
|
|
|
|
|
|
$year = sprintf '%s%d', $era->name, $era->year; |
207
|
0
|
|
|
|
|
|
$len = vwidth $year; |
208
|
|
|
|
|
|
|
} |
209
|
0
|
|
|
|
|
|
s/[ ]{$len}(?=[ ]$)/$year/; |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
sub expand_tab { |
213
|
0
|
|
|
0
|
0
|
|
local $_ = shift; |
214
|
0
|
|
|
|
|
|
my $ts = 8; |
215
|
0
|
|
|
|
|
|
s{ (?:^|\G) (?.*?) \K (?\t+) }{ |
216
|
0
|
|
|
|
|
|
my $w = vwidth($+{lead}); |
217
|
0
|
|
|
|
|
|
(' ' x ($ts * length($+{tab}) - ($w % $ts))); |
218
|
|
|
|
|
|
|
}xgme; |
219
|
0
|
|
|
|
|
|
$_; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
1; |