| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#! perl |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package ChordPro::Utils; |
|
4
|
|
|
|
|
|
|
|
|
5
|
90
|
|
|
90
|
|
1298
|
use v5.26; |
|
|
90
|
|
|
|
|
428
|
|
|
6
|
90
|
|
|
90
|
|
561
|
use utf8; |
|
|
90
|
|
|
|
|
174
|
|
|
|
90
|
|
|
|
|
605
|
|
|
7
|
90
|
|
|
90
|
|
3180
|
use Carp; |
|
|
90
|
|
|
|
|
230
|
|
|
|
90
|
|
|
|
|
8151
|
|
|
8
|
90
|
|
|
90
|
|
669
|
use feature qw( signatures ); |
|
|
90
|
|
|
|
|
299
|
|
|
|
90
|
|
|
|
|
14256
|
|
|
9
|
90
|
|
|
90
|
|
701
|
no warnings "experimental::signatures"; |
|
|
90
|
|
|
|
|
252
|
|
|
|
90
|
|
|
|
|
5199
|
|
|
10
|
90
|
|
|
90
|
|
572
|
use Ref::Util qw( is_arrayref is_hashref ); |
|
|
90
|
|
|
|
|
224
|
|
|
|
90
|
|
|
|
|
6933
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
90
|
|
|
90
|
|
582
|
use Exporter 'import'; |
|
|
90
|
|
|
|
|
226
|
|
|
|
90
|
|
|
|
|
7644
|
|
|
13
|
|
|
|
|
|
|
our @EXPORT; |
|
14
|
|
|
|
|
|
|
our @EXPORT_OK; |
|
15
|
|
|
|
|
|
|
|
|
16
|
90
|
|
|
90
|
|
1613
|
use ChordPro::Files; |
|
|
90
|
|
|
|
|
675
|
|
|
|
90
|
|
|
|
|
16374
|
|
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
################ Filenames ################ |
|
19
|
|
|
|
|
|
|
|
|
20
|
90
|
|
|
90
|
|
777
|
use File::Glob ( ":bsd_glob" ); |
|
|
90
|
|
|
|
|
302
|
|
|
|
90
|
|
|
|
|
35458
|
|
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Derived from Path::ExpandTilde. |
|
23
|
|
|
|
|
|
|
|
|
24
|
90
|
50
|
|
|
|
14693
|
use constant BSD_GLOB_FLAGS => GLOB_NOCHECK | GLOB_QUOTE | GLOB_TILDE | GLOB_ERR |
|
25
|
|
|
|
|
|
|
# add GLOB_NOCASE as in File::Glob |
|
26
|
90
|
|
|
90
|
|
823
|
| ($^O =~ m/\A(?:MSWin32|VMS|os2|dos|riscos)\z/ ? GLOB_NOCASE : 0); |
|
|
90
|
|
|
|
|
211
|
|
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# File::Glob did not try %USERPROFILE% (set in Windows NT derivatives) for ~ before 5.16 |
|
29
|
90
|
|
33
|
90
|
|
712
|
use constant WINDOWS_USERPROFILE => is_msw && $] < 5.016; |
|
|
90
|
|
|
|
|
240
|
|
|
|
90
|
|
|
|
|
621
|
|
|
30
|
|
|
|
|
|
|
|
|
31
|
218
|
|
|
218
|
0
|
590
|
sub expand_tilde ( $dir ) { |
|
|
218
|
|
|
|
|
599
|
|
|
|
218
|
|
|
|
|
437
|
|
|
32
|
|
|
|
|
|
|
|
|
33
|
218
|
50
|
|
|
|
975
|
return undef unless defined $dir; |
|
34
|
218
|
50
|
|
|
|
2222
|
return fn_canonpath($dir) unless $dir =~ m/^~/; |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# Parse path into segments. |
|
37
|
0
|
|
|
|
|
0
|
my ( $volume, $directories, $file ) = fn_splitpath( $dir, 1 ); |
|
38
|
0
|
|
|
|
|
0
|
my @parts = fn_splitdir($directories); |
|
39
|
0
|
|
|
|
|
0
|
my $first = shift( @parts ); |
|
40
|
0
|
0
|
|
|
|
0
|
return fn_canonpath($dir) unless defined $first; |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# Expand first segment. |
|
43
|
0
|
|
|
|
|
0
|
my $expanded; |
|
44
|
0
|
|
|
|
|
0
|
if ( WINDOWS_USERPROFILE and $first eq '~' ) { |
|
45
|
|
|
|
|
|
|
$expanded = $ENV{HOME} || $ENV{USERPROFILE}; |
|
46
|
|
|
|
|
|
|
} |
|
47
|
|
|
|
|
|
|
else { |
|
48
|
0
|
|
|
|
|
0
|
( my $pattern = $first ) =~ s/([\\*?{[])/\\$1/g; |
|
49
|
0
|
|
|
|
|
0
|
($expanded) = bsd_glob( $pattern, BSD_GLOB_FLAGS ); |
|
50
|
0
|
0
|
|
|
|
0
|
croak( "Failed to expand $first: $!") if GLOB_ERROR; |
|
51
|
|
|
|
|
|
|
} |
|
52
|
0
|
0
|
0
|
|
|
0
|
return fn_canonpath($dir) |
|
53
|
|
|
|
|
|
|
if !defined $expanded or $expanded eq $first; |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# Replace first segment with new path. |
|
56
|
0
|
|
|
|
|
0
|
( $volume, $directories ) = fn_splitpath( $expanded, 1 ); |
|
57
|
0
|
|
|
|
|
0
|
$directories = fn_catdir( $directories, @parts ); |
|
58
|
0
|
|
|
|
|
0
|
return fn_catpath($volume, $directories, $file); |
|
59
|
|
|
|
|
|
|
} |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
push( @EXPORT, 'expand_tilde' ); |
|
62
|
|
|
|
|
|
|
|
|
63
|
0
|
|
|
0
|
0
|
0
|
sub sys ( @cmd ) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
64
|
0
|
0
|
|
|
|
0
|
warn("+ @cmd\n") if $::options->{trace}; |
|
65
|
|
|
|
|
|
|
# Use outer defined subroutine, depends on Wx or not. |
|
66
|
0
|
|
|
|
|
0
|
my $res = ::sys(@cmd); |
|
67
|
0
|
0
|
|
|
|
0
|
warn( sprintf("=%02x=> @cmd", $res), "\n" ) if $res; |
|
68
|
0
|
|
|
|
|
0
|
return $res; |
|
69
|
|
|
|
|
|
|
} |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
push( @EXPORT, 'sys' ); |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
################ (Pre)Processing ################ |
|
74
|
|
|
|
|
|
|
|
|
75
|
214
|
|
|
214
|
0
|
497
|
sub make_preprocessor ( $prp ) { |
|
|
214
|
|
|
|
|
457
|
|
|
|
214
|
|
|
|
|
395
|
|
|
76
|
214
|
50
|
|
|
|
970
|
return unless $prp; |
|
77
|
|
|
|
|
|
|
|
|
78
|
214
|
|
|
|
|
492
|
my $prep; |
|
79
|
214
|
|
|
|
|
492
|
foreach my $linetype ( keys %{ $prp } ) { |
|
|
214
|
|
|
|
|
973
|
|
|
80
|
642
|
|
|
|
|
1557
|
my @targets; |
|
81
|
642
|
|
|
|
|
4384
|
my $code = ""; |
|
82
|
642
|
|
|
|
|
3009
|
foreach ( @{ $prp->{$linetype} } ) { |
|
|
642
|
|
|
|
|
2713
|
|
|
83
|
0
|
|
0
|
|
|
0
|
my $flags = $_->{flags} // "g"; |
|
84
|
|
|
|
|
|
|
$code .= "m\0" . $_->{select} . "\0 && " |
|
85
|
0
|
0
|
|
|
|
0
|
if $_->{select}; |
|
86
|
0
|
0
|
|
|
|
0
|
if ( $_->{pattern} ) { |
|
87
|
|
|
|
|
|
|
$code .= "s\0" . $_->{pattern} . "\0" |
|
88
|
0
|
|
|
|
|
0
|
. $_->{replace} . "\0$flags;\n"; |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
else { |
|
91
|
|
|
|
|
|
|
$code .= "s\0" . quotemeta($_->{target}) . "\0" |
|
92
|
0
|
|
|
|
|
0
|
. quotemeta($_->{replace}) . "\0$flags;\n"; |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
} |
|
95
|
642
|
50
|
|
|
|
2469
|
if ( $code ) { |
|
96
|
0
|
|
|
|
|
0
|
my $t = "sub { for (\$_[0]) {\n" . $code . "}}"; |
|
97
|
0
|
|
|
|
|
0
|
$prep->{$linetype} = eval $t; |
|
98
|
0
|
0
|
|
|
|
0
|
die( "CODE : $t\n$@" ) if $@; |
|
99
|
|
|
|
|
|
|
} |
|
100
|
|
|
|
|
|
|
} |
|
101
|
214
|
|
|
|
|
1442
|
$prep; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
push( @EXPORT, 'make_preprocessor' ); |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
################ Utilities ################ |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# Split (pseudo) command line into key/value pairs. |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# Similar to JavaScript, we do not distinguish single- and double |
|
111
|
|
|
|
|
|
|
# quoted strings. |
|
112
|
|
|
|
|
|
|
# \\ \' \" yield \ ' " (JS) |
|
113
|
|
|
|
|
|
|
# \n yields a newline (convenience) |
|
114
|
|
|
|
|
|
|
# Everything else yields the character following the backslash (JS) |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
my %esc = ( n => "\n", '\\' => '\\', '"' => '"', "'" => "'" ); |
|
117
|
|
|
|
|
|
|
|
|
118
|
77
|
|
|
77
|
0
|
274947
|
sub parse_kv ( $line, $kdef = undef ) { |
|
|
77
|
|
|
|
|
210
|
|
|
|
77
|
|
|
|
|
189
|
|
|
|
77
|
|
|
|
|
184
|
|
|
119
|
|
|
|
|
|
|
|
|
120
|
77
|
|
|
|
|
174
|
my @words; |
|
121
|
77
|
100
|
|
|
|
278
|
if ( is_arrayref($line) ) { |
|
122
|
8
|
|
|
|
|
26
|
@words = @$line; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
else { |
|
125
|
|
|
|
|
|
|
# Strip. |
|
126
|
69
|
|
|
|
|
207
|
$line =~ s/^\s+//; |
|
127
|
69
|
|
|
|
|
202
|
$line =~ s/\s+$//; |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# If it doesn't look like key=value, use the default key (if any). |
|
130
|
69
|
100
|
66
|
|
|
407
|
if ( $kdef && $line !~ /^\w+=(?:['"]|[-+]?\d|\w)/ ) { |
|
131
|
30
|
|
|
|
|
193
|
return { $kdef => $line }; |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
|
|
134
|
90
|
|
|
90
|
|
55512
|
use Text::ParseWords qw(quotewords); |
|
|
90
|
|
|
|
|
180967
|
|
|
|
90
|
|
|
|
|
73408
|
|
|
135
|
39
|
|
|
|
|
268
|
@words = quotewords( '\s+', 1, $line ); |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
|
|
138
|
47
|
|
|
|
|
2163
|
my $res = {}; |
|
139
|
47
|
|
|
|
|
144
|
foreach ( @words ) { |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# Quoted values. |
|
142
|
41
|
100
|
|
|
|
245
|
if ( /^(.*?)=(["'])(.*)\2$/ ) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
143
|
10
|
|
|
|
|
29
|
my ( $k, $v ) = ( $1, $3 ); |
|
144
|
10
|
|
0
|
|
|
38
|
$res->{$k} = $v =~ s;\\(.);$esc{$1}//$1;segr; |
|
|
0
|
|
|
|
|
0
|
|
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# Unquoted values. |
|
148
|
|
|
|
|
|
|
elsif ( /^(.*?)=(.+)$/ ) { |
|
149
|
25
|
|
|
|
|
105
|
$res->{$1} = $2; |
|
150
|
|
|
|
|
|
|
} |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# Negated keywords. |
|
153
|
|
|
|
|
|
|
elsif ( /^no[-_]?(.+)/ ) { |
|
154
|
2
|
|
|
|
|
8
|
$res->{$1} = 0; |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# Standalone keywords. |
|
158
|
|
|
|
|
|
|
else { |
|
159
|
4
|
|
|
|
|
12
|
$res->{$_}++; |
|
160
|
|
|
|
|
|
|
} |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
|
|
163
|
47
|
|
|
|
|
255
|
return $res; |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
push( @EXPORT, 'parse_kv' ); |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# Split (pseudo) command lines into key/value pairs. |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
#### LEGACY -- WILL BE REMOVED #### |
|
171
|
|
|
|
|
|
|
|
|
172
|
2
|
|
|
2
|
0
|
4
|
sub parse_kvm ( @lines ) { |
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
4
|
|
|
173
|
|
|
|
|
|
|
|
|
174
|
2
|
50
|
|
|
|
13
|
if ( is_macos() ) { |
|
175
|
|
|
|
|
|
|
# MacOS has the nasty habit to smartify quotes. |
|
176
|
0
|
|
|
|
|
0
|
@lines = map { s/“/"/g; s/”/"/g; s/‘/'/g; s/’/'/gr;} @lines; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
|
|
179
|
90
|
|
|
90
|
|
850
|
use Text::ParseWords qw(quotewords); |
|
|
90
|
|
|
|
|
443
|
|
|
|
90
|
|
|
|
|
90714
|
|
|
180
|
2
|
|
|
|
|
10
|
my @words = quotewords( '\s+', 1, @lines ); |
|
181
|
2
|
|
|
|
|
183
|
parse_kv( \@words ); |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
push( @EXPORT, 'parse_kvm' ); |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# Odd/even. |
|
187
|
|
|
|
|
|
|
|
|
188
|
150
|
|
|
150
|
0
|
448
|
sub is_odd( $arg ) { |
|
|
150
|
|
|
|
|
345
|
|
|
|
150
|
|
|
|
|
297
|
|
|
189
|
150
|
|
|
|
|
1006
|
( $arg % 2 ) != 0; |
|
190
|
|
|
|
|
|
|
} |
|
191
|
1
|
|
|
1
|
0
|
4
|
sub is_even( $arg ) { |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
2
|
|
|
192
|
1
|
|
|
|
|
8
|
( $arg % 2 ) == 0; |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
push( @EXPORT, qw( is_odd is_even ) ); |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# Map true/false etc to true / false. |
|
198
|
|
|
|
|
|
|
|
|
199
|
640
|
|
|
640
|
0
|
1188
|
sub is_true ( $arg ) { |
|
|
640
|
|
|
|
|
1145
|
|
|
|
640
|
|
|
|
|
913
|
|
|
200
|
640
|
100
|
66
|
|
|
2792
|
return 0 if !defined($arg) || $arg eq ''; |
|
201
|
632
|
100
|
|
|
|
3110
|
return 0 if $arg =~ /^(false|null|no|none|off|\s+|0)$/i; |
|
202
|
583
|
|
|
|
|
2220
|
return !!$arg; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
push( @EXPORT, 'is_true' ); |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# Stricter form of true. |
|
208
|
9
|
|
|
9
|
0
|
17
|
sub is_ttrue ( $arg ) { |
|
|
9
|
|
|
|
|
21
|
|
|
|
9
|
|
|
|
|
17
|
|
|
209
|
9
|
50
|
|
|
|
30
|
return 0 if !defined($arg); |
|
210
|
9
|
|
|
|
|
69
|
$arg =~ /^(on|true|1)$/i; |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
push( @EXPORT, 'is_ttrue' ); |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# Fix apos -> quote. |
|
216
|
|
|
|
|
|
|
|
|
217
|
1137
|
|
|
1137
|
0
|
1541
|
sub fq ( $arg ) { |
|
|
1137
|
|
|
|
|
1775
|
|
|
|
1137
|
|
|
|
|
1404
|
|
|
218
|
1137
|
|
|
|
|
2164
|
$arg =~ s/'/\x{2019}/g; |
|
219
|
1137
|
|
|
|
|
3733
|
$arg; |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
push( @EXPORT, 'fq' ); |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# Quote a string if needed unless forced. |
|
225
|
|
|
|
|
|
|
|
|
226
|
11
|
|
|
11
|
0
|
24
|
sub qquote ( $arg, $force = 0 ) { |
|
|
11
|
|
|
|
|
25
|
|
|
|
11
|
|
|
|
|
22
|
|
|
|
11
|
|
|
|
|
20
|
|
|
227
|
11
|
|
|
|
|
27
|
for ( $arg ) { |
|
228
|
11
|
|
|
|
|
38
|
s/([\\\"])/\\$1/g; |
|
229
|
11
|
|
|
|
|
35
|
s/([[:^print:]])/sprintf("\\u%04x", ord($1))/ge; |
|
|
0
|
|
|
|
|
0
|
|
|
230
|
11
|
100
|
100
|
|
|
81
|
return $_ unless /[\\\s]/ || $force; |
|
231
|
9
|
|
|
|
|
66
|
return qq("$_"); |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
push( @EXPORT, 'qquote' ); |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# Safely print values. |
|
238
|
|
|
|
|
|
|
|
|
239
|
90
|
|
|
90
|
|
880
|
use Scalar::Util qw(looks_like_number); |
|
|
90
|
|
|
|
|
184
|
|
|
|
90
|
|
|
|
|
368254
|
|
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# We want overload: |
|
242
|
|
|
|
|
|
|
# sub pv( $val ) |
|
243
|
|
|
|
|
|
|
# sub pv( $label, $val ) |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub pv { |
|
246
|
0
|
|
|
0
|
0
|
0
|
my $val = pop; |
|
247
|
0
|
|
0
|
|
|
0
|
my $label = pop // ""; |
|
248
|
|
|
|
|
|
|
|
|
249
|
0
|
|
|
|
|
0
|
my $suppressundef; |
|
250
|
0
|
0
|
|
|
|
0
|
if ( $label =~ /\?$/ ) { |
|
251
|
0
|
|
|
|
|
0
|
$suppressundef++; |
|
252
|
0
|
|
|
|
|
0
|
$label = $'; |
|
253
|
|
|
|
|
|
|
} |
|
254
|
0
|
0
|
|
|
|
0
|
if ( defined $val ) { |
|
255
|
0
|
0
|
|
|
|
0
|
if ( looks_like_number($val) ) { |
|
256
|
0
|
|
|
|
|
0
|
$val = sprintf("%.3f", $val); |
|
257
|
0
|
|
|
|
|
0
|
$val =~ s/0+$//; |
|
258
|
0
|
|
|
|
|
0
|
$val =~ s/\.$//; |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
else { |
|
261
|
0
|
|
|
|
|
0
|
$val = qquote( $val, 1 ); |
|
262
|
|
|
|
|
|
|
} |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
else { |
|
265
|
0
|
0
|
|
|
|
0
|
return "" if $suppressundef; |
|
266
|
0
|
|
|
|
|
0
|
$val = "" |
|
267
|
|
|
|
|
|
|
} |
|
268
|
0
|
0
|
|
|
|
0
|
defined wantarray ? $label.$val : warn($label.$val."\n"); |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
push( @EXPORT, 'pv' ); |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# Processing JSON. |
|
274
|
|
|
|
|
|
|
|
|
275
|
133
|
|
|
133
|
0
|
423
|
sub json_load( $json, $source = "" ) { |
|
|
133
|
|
|
|
|
332
|
|
|
|
133
|
|
|
|
|
313
|
|
|
|
133
|
|
|
|
|
276
|
|
|
276
|
133
|
|
|
|
|
556
|
my $info = json_parser(); |
|
277
|
133
|
50
|
|
|
|
651
|
if ( $info->{parser} eq "JSON::Relaxed" ) { |
|
278
|
133
|
|
|
|
|
1061
|
state $pp = JSON::Relaxed::Parser->new( croak_on_error => 0, |
|
279
|
|
|
|
|
|
|
strict => 0, |
|
280
|
|
|
|
|
|
|
prp => 1 ); |
|
281
|
133
|
|
|
|
|
3849
|
my $data = $pp->decode($json."\n"); |
|
282
|
133
|
50
|
|
|
|
1158
|
return $data unless $pp->is_error; |
|
283
|
0
|
0
|
|
|
|
0
|
$source .= ": " if $source; |
|
284
|
0
|
|
|
|
|
0
|
die("${source}JSON error: " . $pp->err_msg . "\n"); |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
else { |
|
287
|
0
|
|
|
|
|
0
|
state $pp = JSON::PP->new; |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# Glue lines, so we have at lease some relaxation. |
|
290
|
0
|
|
|
|
|
0
|
$json =~ s/"\s*\\\n\s*"//g; |
|
291
|
|
|
|
|
|
|
|
|
292
|
0
|
0
|
|
|
|
0
|
$pp->relaxed if $info->{relaxed}; |
|
293
|
0
|
|
|
|
|
0
|
$pp->decode($json."\n"); |
|
294
|
|
|
|
|
|
|
} |
|
295
|
|
|
|
|
|
|
} |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# JSON parser, what and how (also used by runtimeinfo(). |
|
298
|
142
|
|
|
142
|
0
|
338
|
sub json_parser() { |
|
|
142
|
|
|
|
|
234
|
|
|
299
|
142
|
|
50
|
|
|
1150
|
my $relax = $ENV{CHORDPRO_JSON_RELAXED} // 2; |
|
300
|
142
|
50
|
|
|
|
601
|
if ( $relax > 1 ) { |
|
301
|
142
|
|
|
|
|
15965
|
require JSON::Relaxed; |
|
302
|
142
|
|
|
|
|
1122
|
return { parser => "JSON::Relaxed", |
|
303
|
|
|
|
|
|
|
version => $JSON::Relaxed::VERSION } |
|
304
|
|
|
|
|
|
|
} |
|
305
|
|
|
|
|
|
|
else { |
|
306
|
0
|
|
|
|
|
0
|
require JSON::PP; |
|
307
|
0
|
|
|
|
|
0
|
return { parser => "JSON::PP", |
|
308
|
|
|
|
|
|
|
relaxed => $relax, |
|
309
|
|
|
|
|
|
|
version => $JSON::PP::VERSION } |
|
310
|
|
|
|
|
|
|
} |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
push( @EXPORT, qw(json_parser json_load) ); |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# Like prp2cfg, but updates. |
|
316
|
|
|
|
|
|
|
# Also allows array pre/append and JSON data. |
|
317
|
|
|
|
|
|
|
# Useful error messages are signalled with exceptions. |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
push( @EXPORT, 'prpadd2cfg' ); |
|
320
|
|
|
|
|
|
|
|
|
321
|
160
|
|
|
160
|
0
|
381259
|
sub prpadd2cfg ( $cfg, @defs ) { |
|
|
160
|
|
|
|
|
407
|
|
|
|
160
|
|
|
|
|
447
|
|
|
|
160
|
|
|
|
|
336
|
|
|
322
|
160
|
|
50
|
|
|
634
|
$cfg //= {}; |
|
323
|
160
|
|
|
|
|
560
|
state $specials = { false => 0, true => 1, null => undef }; |
|
324
|
|
|
|
|
|
|
|
|
325
|
160
|
|
|
|
|
645
|
while ( @defs ) { |
|
326
|
56
|
|
|
|
|
146
|
my $key = shift(@defs); |
|
327
|
56
|
|
|
|
|
134
|
my $value = shift(@defs); |
|
328
|
|
|
|
|
|
|
# warn("K:$key V:$value\n"); |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
# Check and process the value, if needed. |
|
331
|
56
|
100
|
100
|
|
|
900
|
if ( exists $specials->{$value} ) { |
|
|
|
100
|
|
|
|
|
|
|
332
|
3
|
|
|
|
|
9
|
$value = $specials->{$value}; |
|
333
|
|
|
|
|
|
|
# warn("Value => $value\n"); |
|
334
|
|
|
|
|
|
|
} |
|
335
|
|
|
|
|
|
|
elsif ( !( ref($value) |
|
336
|
|
|
|
|
|
|
|| $value !~ /[\[\{\]\}]/ ) ) { |
|
337
|
|
|
|
|
|
|
# Not simple, assume JSON struct. |
|
338
|
1
|
|
|
|
|
6
|
$value = json_load( $value, $value ); |
|
339
|
|
|
|
|
|
|
# use DDP; p($value, as => "Value ->"); |
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
# Note that ':' is not oficially supported by RRJson. |
|
343
|
56
|
|
|
|
|
316
|
my @keys = split( /[:.]/, $key ); |
|
344
|
56
|
|
|
|
|
190
|
my $lastkey = pop(@keys); |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
# Handle pdf.fonts.xxx shortcuts. |
|
347
|
56
|
50
|
|
|
|
275
|
if ( join( ".", @keys ) eq "pdf.fonts" ) { |
|
348
|
0
|
|
|
|
|
0
|
my $s = { pdf => { fonts => { $lastkey => $value } } }; |
|
349
|
0
|
|
|
|
|
0
|
ChordPro::Config::expand_font_shortcuts($s); |
|
350
|
0
|
|
|
|
|
0
|
$value = $s->{pdf}{fonts}{$lastkey}; |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
|
|
353
|
56
|
|
|
|
|
130
|
my $cur = \$cfg; # current pointer in struct |
|
354
|
56
|
|
|
|
|
125
|
my $errkey = ""; # error trail |
|
355
|
56
|
50
|
|
|
|
232
|
if ( $keys[0] eq "chords" ) { |
|
356
|
|
|
|
|
|
|
# Chords are not in the config, but elsewhere. |
|
357
|
0
|
|
|
|
|
0
|
$cur = \ChordPro::Chords::config_chords(); |
|
358
|
0
|
|
|
|
|
0
|
$errkey = "chords."; |
|
359
|
0
|
|
|
|
|
0
|
shift(@keys); |
|
360
|
|
|
|
|
|
|
} |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# Step through the keys. |
|
363
|
56
|
|
|
|
|
170
|
foreach ( @keys ) { |
|
364
|
74
|
100
|
|
|
|
950
|
if ( is_arrayref($$cur) ) { |
|
|
|
50
|
|
|
|
|
|
|
365
|
19
|
|
|
|
|
29
|
my $ok; |
|
366
|
19
|
100
|
|
|
|
115
|
if ( /^[<>]?[-+]?\d+$/ ) { |
|
|
|
50
|
|
|
|
|
|
|
367
|
18
|
|
|
|
|
55
|
$cur = \($$cur->[$_]); |
|
368
|
18
|
|
|
|
|
36
|
$ok++; |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
elsif ( ! exists( $$cur->[0]->{name} ) ) { |
|
371
|
0
|
|
|
|
|
0
|
die("Array ", substr($errkey,0,-1), |
|
372
|
|
|
|
|
|
|
" requires integer index (got \"$_\")\n"); |
|
373
|
|
|
|
|
|
|
} |
|
374
|
|
|
|
|
|
|
else { |
|
375
|
1
|
|
|
|
|
4
|
for my $i ( 0..@{$$cur} ) { |
|
|
1
|
|
|
|
|
5
|
|
|
376
|
1
|
50
|
|
|
|
26
|
if ( $$cur->[$i]->{name} eq $_ ) { |
|
377
|
1
|
|
|
|
|
4
|
$cur = \($$cur->[$i]); |
|
378
|
1
|
|
|
|
|
4
|
$ok++; |
|
379
|
1
|
|
|
|
|
3
|
last; |
|
380
|
|
|
|
|
|
|
} |
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
} |
|
383
|
19
|
50
|
|
|
|
62
|
unless ( $ok ) { |
|
384
|
0
|
|
|
|
|
0
|
die("Array ", substr($errkey,0,-1), |
|
385
|
|
|
|
|
|
|
" has no matching element with name \"$_\"\n"); |
|
386
|
|
|
|
|
|
|
} |
|
387
|
|
|
|
|
|
|
} |
|
388
|
|
|
|
|
|
|
elsif ( is_hashref($$cur) ) { |
|
389
|
55
|
|
|
|
|
174
|
$cur = \($$cur->{$_}); |
|
390
|
|
|
|
|
|
|
} |
|
391
|
|
|
|
|
|
|
else { |
|
392
|
0
|
|
|
|
|
0
|
die("Key ", substr($errkey,0,-1), |
|
393
|
|
|
|
|
|
|
" ", ref($$cur), |
|
394
|
|
|
|
|
|
|
" does not refer to an array or hash\n"); |
|
395
|
|
|
|
|
|
|
} |
|
396
|
74
|
|
|
|
|
263
|
$errkey .= "$_." |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
} |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
# Final key. |
|
401
|
56
|
100
|
|
|
|
498
|
if ( is_arrayref($$cur) ) { |
|
|
|
100
|
|
|
|
|
|
|
402
|
24
|
100
|
|
|
|
310
|
if ( $lastkey =~ />([-+]?\d+)?$/ ) { # append |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
403
|
9
|
100
|
|
|
|
38
|
if ( defined $1 ) { |
|
404
|
5
|
|
|
|
|
45
|
splice( @{$$cur}, |
|
405
|
5
|
100
|
|
|
|
13
|
$1 >= 0 ? 1+$1 : 1+@{$$cur}+$1, 0, $value ); |
|
|
1
|
|
|
|
|
10
|
|
|
406
|
|
|
|
|
|
|
} |
|
407
|
|
|
|
|
|
|
else { |
|
408
|
4
|
|
|
|
|
7
|
push( @{$$cur}, $value ); |
|
|
4
|
|
|
|
|
30
|
|
|
409
|
|
|
|
|
|
|
} |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
elsif ( $lastkey =~ /<([-+]?\d+)?$/ ) { # prepend |
|
412
|
6
|
100
|
|
|
|
51
|
if ( defined $1 ) { |
|
413
|
4
|
|
|
|
|
8
|
splice( @{$$cur}, $1, 0, $value ); |
|
|
4
|
|
|
|
|
30
|
|
|
414
|
|
|
|
|
|
|
} |
|
415
|
|
|
|
|
|
|
else { |
|
416
|
2
|
|
|
|
|
5
|
unshift( @{$$cur}, $value ); |
|
|
2
|
|
|
|
|
14
|
|
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
elsif ( $lastkey =~ /\/([-+]?\d+)?$/ ) { # remove |
|
420
|
3
|
100
|
|
|
|
13
|
if ( defined $1 ) { |
|
421
|
2
|
|
|
|
|
5
|
splice( @{$$cur}, $1, 1 ); |
|
|
2
|
|
|
|
|
13
|
|
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
else { |
|
424
|
1
|
|
|
|
|
4
|
pop( @{$$cur} ); |
|
|
1
|
|
|
|
|
7
|
|
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
else { # replace |
|
428
|
6
|
50
|
|
|
|
33
|
die("Array $errkey requires integer index (got \"$lastkey\")\n") |
|
429
|
|
|
|
|
|
|
unless $lastkey =~ /^[-+]?\d+$/; |
|
430
|
6
|
|
|
|
|
35
|
$$cur->[$lastkey] = $value; |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
} |
|
433
|
|
|
|
|
|
|
elsif ( is_hashref($$cur) ) { |
|
434
|
30
|
50
|
|
|
|
129
|
if ( $errkey =~ /^chords\./ ) { |
|
435
|
|
|
|
|
|
|
# Chords must be defined. |
|
436
|
0
|
|
|
|
|
0
|
ChordPro::Chords::add_config_chord( { name => $lastkey, |
|
437
|
|
|
|
|
|
|
%$value } ); |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
else { |
|
440
|
30
|
|
|
|
|
255
|
$$cur->{$lastkey} = $value; |
|
441
|
|
|
|
|
|
|
} |
|
442
|
|
|
|
|
|
|
} |
|
443
|
|
|
|
|
|
|
else { |
|
444
|
2
|
100
|
|
|
|
22
|
die("Key ", substr($errkey,0,-1), |
|
445
|
|
|
|
|
|
|
" is scalar, not ", |
|
446
|
|
|
|
|
|
|
$lastkey =~ /^(?:[-+]?\d+|[<>])$/ ? "array" : "hash", |
|
447
|
|
|
|
|
|
|
"\n"); |
|
448
|
|
|
|
|
|
|
} |
|
449
|
|
|
|
|
|
|
} |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
# The structure has been modified, but also return for covenience. |
|
452
|
158
|
|
|
|
|
641
|
return $cfg; |
|
453
|
|
|
|
|
|
|
} |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
push( @EXPORT, 'prpadd2cfg' ); |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# Remove markup. |
|
458
|
2385
|
|
|
2385
|
0
|
23616
|
sub demarkup ( $t ) { |
|
|
2385
|
|
|
|
|
4110
|
|
|
|
2385
|
|
|
|
|
3370
|
|
|
459
|
2385
|
|
|
|
|
5350
|
return join( '', grep { ! /^\ } splitmarkup($t) ); |
|
|
2406
|
|
|
|
|
12133
|
|
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
push( @EXPORT, 'demarkup' ); |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
# Split into markup/nonmarkup segments. |
|
464
|
2407
|
|
|
2407
|
0
|
3551
|
sub splitmarkup ( $t ) { |
|
|
2407
|
|
|
|
|
3705
|
|
|
|
2407
|
|
|
|
|
3448
|
|
|
465
|
2407
|
|
|
|
|
15990
|
my @t = split( qr;(?(?:[-\w]+|span\s.*?)>);, $t ); |
|
466
|
2407
|
|
|
|
|
8335
|
return @t; |
|
467
|
|
|
|
|
|
|
} |
|
468
|
|
|
|
|
|
|
push( @EXPORT, 'splitmarkup' ); |
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
# For conditional filling of hashes. |
|
471
|
46
|
|
|
46
|
0
|
87
|
sub maybe ( $key, $value, @rest ) { |
|
|
46
|
|
|
|
|
124
|
|
|
|
46
|
|
|
|
|
78
|
|
|
|
46
|
|
|
|
|
84
|
|
|
|
46
|
|
|
|
|
71
|
|
|
472
|
46
|
50
|
33
|
|
|
215
|
if (defined $key and defined $value) { |
|
473
|
0
|
|
|
|
|
0
|
return ( $key, $value, @rest ); |
|
474
|
|
|
|
|
|
|
} |
|
475
|
|
|
|
|
|
|
else { |
|
476
|
46
|
50
|
33
|
|
|
529
|
( defined($key) || @rest ) ? @rest : (); |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
} |
|
479
|
|
|
|
|
|
|
push( @EXPORT, "maybe" ); |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
# Min/Max. |
|
482
|
90
|
|
|
90
|
|
931
|
use List::Util (); |
|
|
90
|
|
|
|
|
179
|
|
|
|
90
|
|
|
|
|
116070
|
|
|
483
|
|
|
|
|
|
|
*min = \&List::Util::min; |
|
484
|
|
|
|
|
|
|
*max = \&List::Util::max; |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
push( @EXPORT, "min", "max" ); |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# Plural |
|
489
|
0
|
|
|
0
|
0
|
0
|
sub plural( $n, $tag, $plural=undef ) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
490
|
0
|
|
0
|
|
|
0
|
$plural //= $tag . "s"; |
|
491
|
0
|
0
|
0
|
|
|
0
|
( $n || "no" ) . ( $n == 1 ? $tag : $plural ); |
|
492
|
|
|
|
|
|
|
} |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
push( @EXPORT, "plural" ); |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
# Dimensions. |
|
497
|
|
|
|
|
|
|
# Fontsize allows typical font units, and defaults to ref 12. |
|
498
|
0
|
|
|
0
|
0
|
0
|
sub fontsize( $size, $ref=12 ) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
499
|
0
|
0
|
0
|
|
|
0
|
if ( $size && $size =~ /^([.\d]+)(%|e[mx]|p[tx])$/ ) { |
|
500
|
0
|
0
|
|
|
|
0
|
return $ref/100 * $1 if $2 eq '%'; |
|
501
|
0
|
0
|
|
|
|
0
|
return $ref * $1 if $2 eq 'em'; |
|
502
|
0
|
0
|
|
|
|
0
|
return $ref/2 * $1 if $2 eq 'ex'; |
|
503
|
0
|
0
|
|
|
|
0
|
return $1 if $2 eq 'pt'; |
|
504
|
0
|
0
|
|
|
|
0
|
return $1 * 0.75 if $2 eq 'px'; |
|
505
|
|
|
|
|
|
|
} |
|
506
|
0
|
0
|
|
|
|
0
|
$size || $ref; |
|
507
|
|
|
|
|
|
|
} |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
push( @EXPORT, "fontsize" ); |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
# Dimension allows arbitrary units, and defaults to ref 12. |
|
512
|
0
|
|
|
0
|
0
|
0
|
sub dimension( $size, %sz ) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
513
|
0
|
0
|
|
|
|
0
|
return unless defined $size; |
|
514
|
0
|
|
|
|
|
0
|
my $ref; |
|
515
|
0
|
0
|
0
|
|
|
0
|
if ( ( $ref = $sz{fsize} ) |
|
516
|
|
|
|
|
|
|
&& $size =~ /^([.\d]+)(%|e[mx])$/ ) { |
|
517
|
0
|
0
|
|
|
|
0
|
return $ref/100 * $1 if $2 eq '%'; |
|
518
|
0
|
0
|
|
|
|
0
|
return $ref * $1 if $2 eq 'em'; |
|
519
|
0
|
0
|
|
|
|
0
|
return $ref/2 * $1 if $2 eq 'ex'; |
|
520
|
|
|
|
|
|
|
} |
|
521
|
0
|
0
|
0
|
|
|
0
|
if ( ( $ref = $sz{width} ) |
|
522
|
|
|
|
|
|
|
&& $size =~ /^([.\d]+)(%)$/ ) { |
|
523
|
0
|
0
|
|
|
|
0
|
return $ref/100 * $1 if $2 eq '%'; |
|
524
|
|
|
|
|
|
|
} |
|
525
|
0
|
0
|
|
|
|
0
|
if ( $size =~ /^([.\d]+)(p[tx]|[cm]m|in|)$/ ) { |
|
526
|
0
|
0
|
|
|
|
0
|
return $1 if $2 eq 'pt'; |
|
527
|
0
|
0
|
|
|
|
0
|
return $1 * 0.75 if $2 eq 'px'; |
|
528
|
0
|
0
|
|
|
|
0
|
return $1 * 72 / 2.54 if $2 eq 'cm'; |
|
529
|
0
|
0
|
|
|
|
0
|
return $1 * 72 / 25.4 if $2 eq 'mm'; |
|
530
|
0
|
0
|
|
|
|
0
|
return $1 * 72 if $2 eq 'in'; |
|
531
|
0
|
0
|
|
|
|
0
|
return $1 if $2 eq ''; |
|
532
|
|
|
|
|
|
|
} |
|
533
|
0
|
|
|
|
|
0
|
$size; # let someone else croak |
|
534
|
|
|
|
|
|
|
} |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
push( @EXPORT, "dimension" ); |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
# Checking font names against the PDF corefonts. |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
my %corefonts = |
|
541
|
|
|
|
|
|
|
( |
|
542
|
|
|
|
|
|
|
( map { lc($_) => $_ } |
|
543
|
|
|
|
|
|
|
"Times-Roman", |
|
544
|
|
|
|
|
|
|
"Times-Bold", |
|
545
|
|
|
|
|
|
|
"Times-Italic", |
|
546
|
|
|
|
|
|
|
"Times-BoldItalic", |
|
547
|
|
|
|
|
|
|
"Helvetica", |
|
548
|
|
|
|
|
|
|
"Helvetica-Bold", |
|
549
|
|
|
|
|
|
|
"Helvetica-Oblique", |
|
550
|
|
|
|
|
|
|
"Helvetica-BoldOblique", |
|
551
|
|
|
|
|
|
|
"Courier", |
|
552
|
|
|
|
|
|
|
"Courier-Bold", |
|
553
|
|
|
|
|
|
|
"Courier-Oblique", |
|
554
|
|
|
|
|
|
|
"Courier-BoldOblique", |
|
555
|
|
|
|
|
|
|
"Symbol", |
|
556
|
|
|
|
|
|
|
"ZapfDingbats" ), |
|
557
|
|
|
|
|
|
|
); |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
sub is_corefont { |
|
560
|
5341
|
|
|
5341
|
0
|
19030
|
$corefonts{lc $_[0]}; |
|
561
|
|
|
|
|
|
|
} |
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
push( @EXPORT, "is_corefont" ); |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
# Progress reporting. |
|
566
|
|
|
|
|
|
|
|
|
567
|
90
|
|
|
90
|
|
833
|
use Ref::Util qw(is_coderef); |
|
|
90
|
|
|
|
|
205
|
|
|
|
90
|
|
|
|
|
165178
|
|
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
# Progress can return a false result to allow caller to stop. |
|
570
|
|
|
|
|
|
|
|
|
571
|
62
|
|
|
62
|
0
|
190
|
sub progress(%args) { |
|
|
62
|
|
|
|
|
320
|
|
|
|
62
|
|
|
|
|
116
|
|
|
572
|
62
|
|
|
|
|
128
|
state $callback; |
|
573
|
62
|
|
|
|
|
180
|
state $phase = ""; |
|
574
|
62
|
|
|
|
|
146
|
state $index = 0; |
|
575
|
62
|
|
|
|
|
142
|
state $total = ''; |
|
576
|
62
|
50
|
|
|
|
229
|
unless ( %args ) { # reset |
|
577
|
0
|
|
|
|
|
0
|
undef $callback; |
|
578
|
0
|
|
|
|
|
0
|
$phase = ""; |
|
579
|
0
|
|
|
|
|
0
|
$index = 0; |
|
580
|
0
|
|
|
|
|
0
|
return; |
|
581
|
|
|
|
|
|
|
} |
|
582
|
|
|
|
|
|
|
|
|
583
|
62
|
50
|
|
|
|
292
|
$callback = $args{callback} if exists $args{callback}; |
|
584
|
62
|
50
|
|
|
|
413
|
return 1 unless $callback; |
|
585
|
|
|
|
|
|
|
|
|
586
|
0
|
0
|
|
|
|
0
|
if ( exists $args{phase} ) { |
|
587
|
0
|
0
|
|
|
|
0
|
$index = 0 if $phase ne $args{phase}; |
|
588
|
0
|
|
|
|
|
0
|
$phase = $args{phase}; |
|
589
|
|
|
|
|
|
|
} |
|
590
|
0
|
0
|
|
|
|
0
|
if ( exists $args{index} ) { |
|
591
|
0
|
|
|
|
|
0
|
$index = $args{index}; |
|
592
|
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
# Use index<0 to only set callback/phase. |
|
594
|
0
|
0
|
|
|
|
0
|
$index = 0, $total = '', return if $index < 0; |
|
595
|
|
|
|
|
|
|
} |
|
596
|
0
|
0
|
|
|
|
0
|
if ( exists $args{total} ) { |
|
597
|
0
|
|
|
|
|
0
|
$total = $args{total}; |
|
598
|
|
|
|
|
|
|
} |
|
599
|
|
|
|
|
|
|
|
|
600
|
0
|
|
|
|
|
0
|
my $args = { phase => $phase, index => $index, total => $total, %args }; |
|
601
|
|
|
|
|
|
|
|
|
602
|
0
|
|
|
|
|
0
|
my $ret = ++$index; |
|
603
|
0
|
0
|
|
|
|
0
|
if ( is_coderef($callback) ) { |
|
604
|
0
|
|
|
|
|
0
|
$ret = eval { $callback->(%$args) }; |
|
|
0
|
|
|
|
|
0
|
|
|
605
|
0
|
0
|
|
|
|
0
|
if ( $@ ) { |
|
606
|
0
|
|
|
|
|
0
|
warn($@); |
|
607
|
0
|
|
|
|
|
0
|
undef $callback; |
|
608
|
|
|
|
|
|
|
} |
|
609
|
|
|
|
|
|
|
} |
|
610
|
|
|
|
|
|
|
else { |
|
611
|
0
|
0
|
|
|
|
0
|
if ( $callback eq "warn" ) { |
|
612
|
|
|
|
|
|
|
# Simple progress message. Suppress if $index = 0 or total = 1. |
|
613
|
0
|
|
|
|
|
0
|
$callback = |
|
614
|
|
|
|
|
|
|
'%{index=0||' . |
|
615
|
|
|
|
|
|
|
'%{total=1||Progress[%{phase}]: %{index}%{total|/%{}}%{msg| - %{}}}' . |
|
616
|
|
|
|
|
|
|
'}'; |
|
617
|
|
|
|
|
|
|
} |
|
618
|
0
|
|
|
|
|
0
|
my $msg = ChordPro::Output::Common::fmt_subst |
|
619
|
|
|
|
|
|
|
( { meta => $args }, $callback ); |
|
620
|
0
|
|
|
|
|
0
|
$msg =~ s/\n+$//; |
|
621
|
0
|
0
|
|
|
|
0
|
warn( $msg, "\n" ) if $msg; |
|
622
|
|
|
|
|
|
|
} |
|
623
|
|
|
|
|
|
|
|
|
624
|
0
|
|
|
|
|
0
|
return $ret; |
|
625
|
|
|
|
|
|
|
} |
|
626
|
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
push( @EXPORT, "progress" ); |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
# Common items for property directives ({textsize} etc.). |
|
630
|
|
|
|
|
|
|
|
|
631
|
100
|
|
|
100
|
0
|
276
|
sub propitems() { |
|
|
100
|
|
|
|
|
213
|
|
|
632
|
100
|
|
|
|
|
1105
|
qw( chord chorus diagrams footer grid label tab text title toc ); |
|
633
|
|
|
|
|
|
|
} |
|
634
|
|
|
|
|
|
|
|
|
635
|
100
|
|
|
100
|
0
|
311
|
sub propitems_re() { |
|
|
100
|
|
|
|
|
231
|
|
|
636
|
100
|
|
|
|
|
584
|
my $re = join( '|', propitems() ); |
|
637
|
100
|
|
|
|
|
47369
|
qr/(?:$re)/; |
|
638
|
|
|
|
|
|
|
} |
|
639
|
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
push( @EXPORT, "propitems_re" ); |
|
641
|
|
|
|
|
|
|
push( @EXPORT_OK, "propitems" ); |
|
642
|
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
# For debugging encoding problems. |
|
644
|
|
|
|
|
|
|
|
|
645
|
0
|
|
|
0
|
0
|
|
sub as( $s ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
646
|
0
|
0
|
|
|
|
|
return "" unless defined $s; |
|
647
|
0
|
|
|
|
|
|
$s =~ s{ ( [^\x{20}-\x{7f}] ) } |
|
648
|
0
|
|
|
|
|
|
{ join( '', map { sprintf '\x{%02x}', ord $_ } split //, $1) }gex; |
|
|
0
|
|
|
|
|
|
|
|
649
|
0
|
|
|
|
|
|
return $s; |
|
650
|
|
|
|
|
|
|
} |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
push( @EXPORT_OK, "as" ); |
|
653
|
|
|
|
|
|
|
|
|
654
|
0
|
|
|
0
|
0
|
|
sub enumerated( @s ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
655
|
0
|
0
|
|
|
|
|
return "" unless @s; |
|
656
|
0
|
|
|
|
|
|
my $last = pop(@s); |
|
657
|
0
|
|
|
|
|
|
my $ret = ""; |
|
658
|
0
|
0
|
|
|
|
|
$ret .= join(", ", @s) . " and " if @s; |
|
659
|
0
|
|
|
|
|
|
$ret .= $last; |
|
660
|
0
|
|
|
|
|
|
return $ret; |
|
661
|
|
|
|
|
|
|
} |
|
662
|
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
push( @EXPORT_OK, "enumerated" ); |
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
# Determine image type. |
|
666
|
|
|
|
|
|
|
|
|
667
|
0
|
|
|
0
|
|
|
sub _detect_image_format( $test ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
|
|
669
|
0
|
0
|
|
|
|
|
for ( ref($test) ? $$test : $test ) { |
|
670
|
0
|
0
|
|
|
|
|
/^GIF\d\d[a-z]/ and return 'gif'; |
|
671
|
0
|
0
|
|
|
|
|
/^\xFF\xD8\xFF/ and return 'jpeg'; |
|
672
|
0
|
0
|
|
|
|
|
/^\x89PNG\x0D\x0A\x1A\x0A/ and return 'png'; |
|
673
|
0
|
0
|
|
|
|
|
/^\s*P[1-6]/ and return 'pnm'; |
|
674
|
0
|
0
|
|
|
|
|
/^II\x2A\x00/ and return 'tiff'; |
|
675
|
0
|
0
|
|
|
|
|
/^MM\x00\x2A/ and return 'tiff'; |
|
676
|
0
|
0
|
|
|
|
|
/^ |
|
677
|
|
|
|
|
|
|
} |
|
678
|
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
# Not recognized. |
|
680
|
0
|
|
|
|
|
|
return; |
|
681
|
|
|
|
|
|
|
} |
|
682
|
|
|
|
|
|
|
|
|
683
|
0
|
|
|
0
|
0
|
|
sub detect_image_format( $test ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
684
|
0
|
|
|
|
|
|
my $format = _detect_image_format($test); |
|
685
|
|
|
|
|
|
|
|
|
686
|
0
|
0
|
|
|
|
|
if ( $format ) { |
|
687
|
0
|
|
|
|
|
|
return { file_ext => $format, error => "" }; |
|
688
|
|
|
|
|
|
|
} |
|
689
|
0
|
|
|
|
|
|
return { file_ext => "", error => "Unrecognized image type." }; |
|
690
|
|
|
|
|
|
|
} |
|
691
|
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
push( @EXPORT_OK, "detect_image_format" ); |
|
693
|
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
=cut |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
1; |