| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#! perl |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
717
|
use v5.26; |
|
|
1
|
|
|
|
|
4
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package ChordPro::A2Crd; |
|
6
|
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
6
|
use App::Packager; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
11
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
144
|
use ChordPro::Version; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
23
|
|
|
10
|
1
|
|
|
1
|
|
8
|
use ChordPro::Chords; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
57
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = $ChordPro::Version::VERSION; |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 NAME |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
ChordPro::A2Crd - convert lyrics and chords to ChordPro |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
perl -MA2Crd -e run -- [ options ] [ file ... ] |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
(But noone does that.) |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
When the associated B program has been installed correctly: |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
chordpro --a2crd [ options ] [ file ... ] |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
B, referred to as B, will read a text file |
|
31
|
|
|
|
|
|
|
containing the lyrics of one or many songs with chord information |
|
32
|
|
|
|
|
|
|
written visually above the lyrics. This is often referred to as I |
|
33
|
|
|
|
|
|
|
data. B will then generate equivalent ChordPro output. |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
Typical a2crd input: |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Title: Swing Low Sweet Chariot |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
D G D |
|
40
|
|
|
|
|
|
|
Swing low, sweet chariot, |
|
41
|
|
|
|
|
|
|
A7 |
|
42
|
|
|
|
|
|
|
Comin’ for to carry me home. |
|
43
|
|
|
|
|
|
|
D7 G D |
|
44
|
|
|
|
|
|
|
Swing low, sweet chariot, |
|
45
|
|
|
|
|
|
|
A7 D |
|
46
|
|
|
|
|
|
|
Comin’ for to carry me home. |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
D G D |
|
49
|
|
|
|
|
|
|
I looked over Jordan, and what did I see, |
|
50
|
|
|
|
|
|
|
A7 |
|
51
|
|
|
|
|
|
|
Comin’ for to carry me home. |
|
52
|
|
|
|
|
|
|
D G D |
|
53
|
|
|
|
|
|
|
A band of angels comin’ after me, |
|
54
|
|
|
|
|
|
|
A7 D |
|
55
|
|
|
|
|
|
|
Comin’ for to carry me home. |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
Note that the output from the conversion will generally need some |
|
58
|
|
|
|
|
|
|
additional editing to be useful as input to ChordPro. |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
B is a wrapper around L, which |
|
61
|
|
|
|
|
|
|
does all of the work. |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
B will read one or more text files containing the lyrics of |
|
64
|
|
|
|
|
|
|
one or many songs plus chord information. B will then |
|
65
|
|
|
|
|
|
|
generate a photo-ready, professional looking, impress-your-friends |
|
66
|
|
|
|
|
|
|
sheet-music suitable for printing on your nearest printer. |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
B is a rewrite of the Chordii program. |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
For more information about the ChordPro file format, see |
|
71
|
|
|
|
|
|
|
L. |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=cut |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
################ Common stuff ################ |
|
76
|
|
|
|
|
|
|
|
|
77
|
1
|
|
|
1
|
|
6
|
use strict; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
31
|
|
|
78
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
28
|
|
|
79
|
1
|
|
|
1
|
|
7
|
use utf8; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
5
|
|
|
80
|
1
|
|
|
1
|
|
36
|
use Carp; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
94
|
|
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
################ The Process ################ |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
package main; |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
our $options; |
|
87
|
|
|
|
|
|
|
our $config; |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
package ChordPro::A2Crd; |
|
90
|
|
|
|
|
|
|
|
|
91
|
1
|
|
|
1
|
|
7
|
use ChordPro::Config; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
49
|
|
|
92
|
|
|
|
|
|
|
|
|
93
|
1
|
|
|
1
|
|
7
|
use File::LoadLines; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
66
|
|
|
94
|
1
|
|
|
1
|
|
7
|
use Encode qw(decode decode_utf8 encode_utf8); |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
4614
|
|
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# API: Main entry point. |
|
97
|
|
|
|
|
|
|
sub a2crd { |
|
98
|
19
|
|
|
19
|
0
|
62
|
my ($opts) = @_; |
|
99
|
19
|
50
|
|
|
|
62
|
$options = { %$options, %$opts } if $opts; |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# One configurator to bind them all. |
|
102
|
19
|
|
|
|
|
95
|
$config = ChordPro::Config::configurator({}); |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# Process input. |
|
105
|
|
|
|
|
|
|
my $lines = $opts->{lines} |
|
106
|
|
|
|
|
|
|
? delete($opts->{lines}) |
|
107
|
19
|
50
|
|
|
|
302
|
: loadlines( @ARGV ? $ARGV[0] : \*STDIN); |
|
|
|
50
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
|
109
|
19
|
|
|
|
|
12696
|
return [ a2cho($lines) ]; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
################ Subroutines ################ |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# Replace tabs with blanks, retaining layout. |
|
115
|
|
|
|
|
|
|
my $tabstop; |
|
116
|
|
|
|
|
|
|
sub expand { |
|
117
|
31
|
|
|
31
|
0
|
66
|
my ( $line ) = @_; |
|
118
|
31
|
50
|
|
|
|
67
|
return $line unless $line; |
|
119
|
31
|
|
66
|
|
|
68
|
$tabstop //= $::config->{a2crd}->{tabstop}; |
|
120
|
31
|
50
|
|
|
|
62
|
return $line unless $tabstop > 0; |
|
121
|
|
|
|
|
|
|
|
|
122
|
31
|
|
|
|
|
96
|
my ( @l ) = split( /\t/, $line, -1 ); |
|
123
|
31
|
50
|
|
|
|
79
|
return $l[0] if @l == 1; |
|
124
|
|
|
|
|
|
|
|
|
125
|
31
|
|
|
|
|
69
|
$line = shift(@l); |
|
126
|
31
|
|
|
|
|
194
|
$line .= " " x ($tabstop-length($line)%$tabstop) . shift(@l) while @l; |
|
127
|
|
|
|
|
|
|
|
|
128
|
31
|
|
|
|
|
76
|
return $line; |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# API: Produce ChordPro data from AsciiCRD lines. |
|
132
|
|
|
|
|
|
|
sub a2cho { |
|
133
|
19
|
|
|
19
|
0
|
66
|
my ( $lines ) = @_; |
|
134
|
19
|
|
|
|
|
47
|
my $map = ""; |
|
135
|
19
|
|
|
|
|
44
|
my @lines_with_tabs_replaced ; |
|
136
|
19
|
|
|
|
|
68
|
foreach ( @$lines ) { |
|
137
|
757
|
100
|
|
|
|
1895
|
if(/\t/) { |
|
138
|
31
|
|
|
|
|
79
|
$_ = expand($_) ; |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
#s/=20/ /g ; # replace HTML coded space with ascii space, no, MUST LEAVE IN because it can mess up fingering diagrams like A/F#=202220 |
|
142
|
757
|
|
|
|
|
1722
|
s/=3D/=/g ; # replace HTML coded equal with ascii = |
|
143
|
|
|
|
|
|
|
# s/\s*$// ; # remove all trailing whitespace -- no, MUST LEAVE IN so chords indicated above trailing whitespace will be properly formatted |
|
144
|
|
|
|
|
|
|
|
|
145
|
757
|
|
|
|
|
1126
|
my $n_ch_chords=0 ; |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
#An odd format for chords, [ch]Chordname[\ch], possibly from reformated webpage |
|
148
|
|
|
|
|
|
|
# need to strip out and consider it to be a chord line |
|
149
|
757
|
|
|
|
|
1801
|
while(s/\[ch\](.*?)\[\/ch\]/$1/) { |
|
150
|
16
|
|
|
|
|
82
|
$n_ch_chords++ ; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
757
|
|
|
|
|
1419
|
push @lines_with_tabs_replaced, $_ ; |
|
154
|
|
|
|
|
|
|
|
|
155
|
757
|
100
|
|
|
|
1416
|
if($n_ch_chords < 1) { |
|
156
|
754
|
|
|
|
|
1363
|
$map .= classify($_); |
|
157
|
|
|
|
|
|
|
} else { |
|
158
|
3
|
|
|
|
|
7
|
$map .= "c" ; |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
} |
|
161
|
19
|
|
|
|
|
214
|
maplines( $map, \@lines_with_tabs_replaced ); |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# Classify the line and return a single-char token. |
|
166
|
|
|
|
|
|
|
my $classify; |
|
167
|
|
|
|
|
|
|
sub classify { |
|
168
|
754
|
|
|
754
|
0
|
1398
|
my ( $line ) = @_; |
|
169
|
754
|
100
|
|
|
|
2348
|
return '_' if $line =~ /^\s*$/; # empty line |
|
170
|
611
|
100
|
|
|
|
1309
|
return '{' if $line =~ /^\{.+/; # directive |
|
171
|
601
|
100
|
|
|
|
1239
|
unless ( defined $classify ) { |
|
172
|
1
|
|
|
|
|
4
|
my $classifier = $::config->{a2crd}->{classifier}; |
|
173
|
1
|
|
|
|
|
19
|
$classify = __PACKAGE__->can("classify_".$classifier); |
|
174
|
1
|
50
|
|
|
|
6
|
unless ( $classify ) { |
|
175
|
0
|
|
|
|
|
0
|
warn("No such classifier: $classifier, using classic\n"); |
|
176
|
0
|
|
|
|
|
0
|
$classify = \&classify_classic; |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
} |
|
180
|
601
|
|
|
|
|
1159
|
$classify->($line); |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub classify_classic { |
|
184
|
0
|
|
|
0
|
0
|
0
|
my ( $line ) = @_; |
|
185
|
|
|
|
|
|
|
# Lyrics or Chords heuristic. |
|
186
|
0
|
|
|
|
|
0
|
my @words = split ( /\s+/, $line ); |
|
187
|
0
|
|
|
|
|
0
|
my $len = length($line); |
|
188
|
0
|
|
|
|
|
0
|
$line =~ s/\s+//g; |
|
189
|
0
|
0
|
|
|
|
0
|
my $type = ( $len / length($line) - 1 ) < 1 ? 'l' : 'c'; |
|
190
|
0
|
|
|
|
|
0
|
my $p = ChordPro::Chords::Parser->default; |
|
191
|
0
|
0
|
|
|
|
0
|
if ( $type eq 'l') { |
|
192
|
0
|
|
|
|
|
0
|
foreach (@words) { |
|
193
|
0
|
0
|
|
|
|
0
|
if (length $_ > 0) { |
|
194
|
0
|
0
|
|
|
|
0
|
if (!ChordPro::Chords::parse_chord($_)) { |
|
195
|
0
|
|
|
|
|
0
|
return 'l'; |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
} |
|
199
|
0
|
|
|
|
|
0
|
return 'c'; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
0
|
|
|
|
|
0
|
return $type; |
|
202
|
|
|
|
|
|
|
} |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# JJW -- attempts at using "relaxed" in the standard chordnames parser were too relaxed |
|
205
|
|
|
|
|
|
|
# so I made this to try to parse unspecified chords that still have well defined "parts" in the chordname |
|
206
|
|
|
|
|
|
|
# these chords probably are understandable by a human, but too out of spec for the chordpro parser to interpret |
|
207
|
|
|
|
|
|
|
# my use of regex is probably not optimal -- I haven't had a lot of regex experience. |
|
208
|
|
|
|
|
|
|
# this currently only works for the roman chord notation |
|
209
|
|
|
|
|
|
|
sub generic_parse_chord |
|
210
|
|
|
|
|
|
|
{ |
|
211
|
885
|
|
|
885
|
0
|
1541
|
my $word = shift ; |
|
212
|
|
|
|
|
|
|
|
|
213
|
885
|
|
|
|
|
1298
|
my ($chord,$bass) ; |
|
214
|
885
|
100
|
|
|
|
1821
|
if ( $word =~ m;^(.*)/(.*); ) { |
|
215
|
10
|
|
|
|
|
31
|
$chord = $1; |
|
216
|
10
|
|
|
|
|
21
|
$bass = $2; |
|
217
|
|
|
|
|
|
|
} else { |
|
218
|
875
|
|
|
|
|
1286
|
$chord=$word ; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
|
|
221
|
885
|
100
|
|
|
|
1735
|
if($bass) { |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# this was the first attempt, but found it to be to restrictive |
|
224
|
|
|
|
|
|
|
#return 0 if(! ($bass =~ /^($roots)$/) ) ; |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# now allow anything after the "/" |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# in anticipation of nashville and solfege ; |
|
230
|
885
|
|
|
|
|
1356
|
my $roots = "^[A-G]" ; |
|
231
|
885
|
|
|
|
|
1196
|
my $found_chord_base="" ; |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# first part of chord needs to be [A-G] |
|
234
|
885
|
100
|
|
|
|
4174
|
return 0 if(! ($chord =~ s/($roots)//)) ; |
|
235
|
|
|
|
|
|
|
|
|
236
|
43
|
|
|
|
|
138
|
$found_chord_base .= $1 ; |
|
237
|
|
|
|
|
|
|
|
|
238
|
43
|
|
|
|
|
99
|
$chord = lc($chord) ; # simplify to lowercase for further parsing |
|
239
|
|
|
|
|
|
|
|
|
240
|
43
|
100
|
|
|
|
169
|
if($chord =~ s/^([b#]|flat|sharp)//) { |
|
241
|
3
|
|
|
|
|
10
|
$found_chord_base .= $1 ; |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
|
|
244
|
43
|
50
|
|
|
|
197
|
if($chord =~ s/^(minor|major)//) { |
|
245
|
0
|
|
|
|
|
0
|
$found_chord_base .= $1 ; |
|
246
|
|
|
|
|
|
|
} |
|
247
|
|
|
|
|
|
|
|
|
248
|
43
|
50
|
|
|
|
125
|
if($chord =~ s/^(min|maj)//) { |
|
249
|
0
|
|
|
|
|
0
|
$found_chord_base .= $1 ; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
|
|
252
|
43
|
100
|
|
|
|
155
|
if($chord =~ s/^(m|dim|0|o|aug|\+)//) { |
|
253
|
8
|
|
|
|
|
23
|
$found_chord_base .= $1 ; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
43
|
|
|
|
|
156
|
$chord =~ s/^[\d]*// ; # to get the 7 in "A7", etc |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# all that should remain are note numbers and note modifiers b, #, "sus", "add", "flat", "sharp", -, + |
|
259
|
|
|
|
|
|
|
# strip those possible combinations one at a time |
|
260
|
|
|
|
|
|
|
|
|
261
|
43
|
|
|
|
|
183
|
while( $chord =~ s/^(b|#|\+|\-|flat|sharp|sus|add)*?\d// ) {} ; |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
# if all that remains are digits and "#b-", it's probably a chord |
|
264
|
43
|
|
|
|
|
103
|
my $n_ok = ($chord =~ tr/0123456789#b-//) ; |
|
265
|
|
|
|
|
|
|
|
|
266
|
43
|
100
|
|
|
|
211
|
return 1 if $n_ok == length $chord ; |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# determine if the input line is a fingering definition for a chord |
|
270
|
|
|
|
|
|
|
sub decode_fingering |
|
271
|
|
|
|
|
|
|
{ |
|
272
|
787
|
|
|
787
|
0
|
1487
|
my ($line,$return_chordpro_fingering) = @_ ; |
|
273
|
787
|
|
|
|
|
1035
|
my $is_fingering=0 ; |
|
274
|
787
|
|
|
|
|
1177
|
my $input_line = $line ; |
|
275
|
787
|
|
|
|
|
1056
|
my $any_chord_ok=1 ; # allows any text for the chord preceding a fingering pattern to be valid |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# since more than one chord can be defined on a single input text line, |
|
278
|
|
|
|
|
|
|
# hold all results in these two arrays |
|
279
|
787
|
|
|
|
|
1074
|
my (@chords,@fingerss) ; |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# THIS ONLY WORKS FOR FRETS <=9 right now |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# is it a fingering notation? |
|
284
|
|
|
|
|
|
|
|
|
285
|
787
|
|
|
|
|
1142
|
my $pre = "^.*?\\s*?" ; # the pattern to match just before a chord name |
|
286
|
787
|
|
|
|
|
1462
|
my $valid = "[A-G]{1}\\S*?" ; # a valid chordname |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# ("chord:") followed by "|x2344x|" or "x2344x" |
|
289
|
787
|
|
|
|
|
3579
|
while($line =~ /$pre($valid)\:+?\s*?(\|?[xX0-9]{3,7}\|?)/) { |
|
290
|
38
|
|
|
|
|
92
|
my $cname=$1 ; |
|
291
|
38
|
|
|
|
|
60
|
my $fingers_this=$2 ; |
|
292
|
38
|
|
|
|
|
59
|
my $nobar_fingers=$fingers_this ; |
|
293
|
38
|
|
|
|
|
116
|
$nobar_fingers =~ s/\|//g ; |
|
294
|
|
|
|
|
|
|
|
|
295
|
38
|
50
|
33
|
|
|
150
|
if($any_chord_ok || generic_parse_chord($cname)) { |
|
296
|
38
|
|
|
|
|
64
|
push @chords,$cname ; |
|
297
|
38
|
|
|
|
|
66
|
push @fingerss,$nobar_fingers ; |
|
298
|
38
|
|
|
|
|
53
|
$is_fingering=1 ; |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
|
|
301
|
38
|
|
|
|
|
538
|
$line =~ s/.*?$nobar_fingers// ; |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# ("chord") followed by "|x2344x|" "x2344x" |
|
306
|
787
|
|
|
|
|
10839
|
while($line =~ /$pre($valid)\s+?(\|?[xX0-9]{3,7}\|?)/) { |
|
307
|
32
|
|
|
|
|
85
|
my $cname=$1 ; |
|
308
|
32
|
|
|
|
|
59
|
my $fingers_this=$2 ; |
|
309
|
32
|
|
|
|
|
43
|
my $nobar_fingers=$fingers_this ; |
|
310
|
32
|
|
|
|
|
59
|
$nobar_fingers =~ s/\|//g ; |
|
311
|
|
|
|
|
|
|
|
|
312
|
32
|
50
|
33
|
|
|
77
|
if($any_chord_ok || generic_parse_chord($1)) { |
|
313
|
32
|
|
|
|
|
66
|
push @chords,$cname ; |
|
314
|
32
|
|
|
|
|
49
|
push @fingerss,$nobar_fingers ; |
|
315
|
32
|
|
|
|
|
52
|
$is_fingering=1 ; |
|
316
|
|
|
|
|
|
|
} |
|
317
|
|
|
|
|
|
|
|
|
318
|
32
|
|
|
|
|
490
|
$line =~ s/.*?$nobar_fingers// ; |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# "(chord) = (fingering)" format |
|
322
|
787
|
|
|
|
|
3615
|
while($line =~ /$pre($valid)\s*?\=\s*?([xX0123456789]{3,7})/) { |
|
323
|
384
|
|
|
|
|
855
|
my $cname=$1 ; |
|
324
|
384
|
|
|
|
|
582
|
my $fingers_this=$2 ; |
|
325
|
384
|
|
|
|
|
552
|
my $nobar_fingers=$fingers_this ; |
|
326
|
384
|
|
|
|
|
639
|
$nobar_fingers =~ s/\|//g ; |
|
327
|
|
|
|
|
|
|
|
|
328
|
384
|
50
|
33
|
|
|
873
|
if($any_chord_ok || generic_parse_chord($1)) { |
|
329
|
384
|
|
|
|
|
733
|
push @chords,$cname ; |
|
330
|
384
|
|
|
|
|
540
|
push @fingerss,$nobar_fingers ; |
|
331
|
384
|
|
|
|
|
543
|
$is_fingering=1 ; |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
|
|
334
|
384
|
|
|
|
|
5257
|
$line =~ s/.*?$nobar_fingers// ; |
|
335
|
|
|
|
|
|
|
} |
|
336
|
|
|
|
|
|
|
|
|
337
|
787
|
100
|
|
|
|
1667
|
if($is_fingering) { |
|
338
|
402
|
100
|
|
|
|
1356
|
return 1 if ! $return_chordpro_fingering ; |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# handle situation where more than one chord is defined on an input text line |
|
341
|
201
|
|
|
|
|
281
|
my @output_lines ; |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
#push @output_lines, $input_line if 1 ; # only for debugging |
|
344
|
|
|
|
|
|
|
|
|
345
|
201
|
|
|
|
|
393
|
foreach my $chord (@chords) { |
|
346
|
227
|
|
|
|
|
383
|
my $fingers = shift @fingerss ; |
|
347
|
227
|
|
|
|
|
399
|
my $min_fret=100 ; |
|
348
|
227
|
|
|
|
|
293
|
my $max_fret=0 ; |
|
349
|
227
|
|
|
|
|
293
|
my @frets ; |
|
350
|
|
|
|
|
|
|
|
|
351
|
227
|
|
|
|
|
788
|
while($fingers =~ s/(.)//) { |
|
352
|
1359
|
|
|
|
|
2681
|
my $fret=$1 ; |
|
353
|
1359
|
|
|
|
|
2275
|
push @frets, $fret ; |
|
354
|
|
|
|
|
|
|
|
|
355
|
1359
|
100
|
|
|
|
3478
|
if($fret =~ /[0-9]/) { |
|
356
|
1213
|
100
|
|
|
|
2282
|
$min_fret = $fret if $min_fret > $fret ; |
|
357
|
1213
|
100
|
|
|
|
4352
|
$max_fret = $fret if $max_fret < $fret ; |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
} |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# now convert the requested fingering to chordpro format |
|
362
|
227
|
|
|
|
|
336
|
my $bf=$min_fret ; |
|
363
|
|
|
|
|
|
|
|
|
364
|
227
|
|
|
|
|
560
|
my $chordpro = "{define $chord base-fret $bf frets" ; |
|
365
|
227
|
100
|
|
|
|
505
|
$bf-- if $bf > 0 ; |
|
366
|
|
|
|
|
|
|
|
|
367
|
227
|
|
|
|
|
398
|
foreach my $fret (@frets) { |
|
368
|
1359
|
|
|
|
|
2220
|
$chordpro = $chordpro . " " ; |
|
369
|
|
|
|
|
|
|
|
|
370
|
1359
|
100
|
|
|
|
3011
|
if($fret =~ /[0-9]/) { |
|
371
|
1213
|
|
|
|
|
1796
|
my $rf = $fret-$bf ; |
|
372
|
|
|
|
|
|
|
|
|
373
|
1213
|
|
|
|
|
2468
|
$chordpro .= "$rf" ; |
|
374
|
|
|
|
|
|
|
} else { |
|
375
|
146
|
|
|
|
|
288
|
$chordpro .= '-' ; |
|
376
|
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
} |
|
378
|
|
|
|
|
|
|
|
|
379
|
227
|
|
|
|
|
415
|
$chordpro .= "}" ; |
|
380
|
227
|
|
|
|
|
570
|
push @output_lines, $chordpro ; |
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
|
|
383
|
201
|
|
|
|
|
584
|
return @output_lines ; |
|
384
|
|
|
|
|
|
|
} |
|
385
|
|
|
|
|
|
|
|
|
386
|
385
|
|
|
|
|
948
|
return 0 ; |
|
387
|
|
|
|
|
|
|
} |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# classification characters are: |
|
390
|
|
|
|
|
|
|
# 'l' = normal text line, usually lyrics but may be other plain text as well |
|
391
|
|
|
|
|
|
|
# 'C' = a comment |
|
392
|
|
|
|
|
|
|
# 'f' = a chord fingering request |
|
393
|
|
|
|
|
|
|
# 't' = tablature |
|
394
|
|
|
|
|
|
|
# 'c' = chords, usually to be output inline with a subsequent 'l' line |
|
395
|
|
|
|
|
|
|
# '{' = an embedded chordpro directive found in the input file, to be output with no changes |
|
396
|
|
|
|
|
|
|
# '_' = a blank line, i.e. it contains only whitespace |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
# Alternative classifier by Jeff Welty. |
|
399
|
|
|
|
|
|
|
# Strategy: Percentage of recognzied chords. |
|
400
|
|
|
|
|
|
|
sub classify_pct_chords { |
|
401
|
601
|
|
|
601
|
0
|
1003
|
my ( $line ) = @_; |
|
402
|
601
|
|
|
|
|
1435
|
my $lc_line = lc($line) ; |
|
403
|
601
|
|
|
|
|
870
|
my $local_debug=0 ; |
|
404
|
|
|
|
|
|
|
|
|
405
|
601
|
100
|
|
|
|
1317
|
return 'C' if $line =~ /^\s*\[.+?\]/; # comment |
|
406
|
595
|
100
|
|
|
|
1247
|
return 'C' if $line =~ /^\s*\#.+?/; # comment |
|
407
|
592
|
100
|
|
|
|
1154
|
return 'C' if $lc_line =~ /(from|email|e\-mail)\:?.+?@+/ ; # email is treated as a comment |
|
408
|
590
|
100
|
|
|
|
1171
|
return 'C' if $lc_line =~ /(from|email|e\-mail)\:.+?/ ; # same as above, but there MUST be a colon, and no @ is necessary |
|
409
|
588
|
100
|
|
|
|
1154
|
return 'C' if $lc_line =~ /(date|subject)\:.+?/ ; # most likely part of email lines is treated as a comment |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# check for a chord fingering specification, i.e. A=x02220 |
|
412
|
586
|
100
|
|
|
|
1204
|
return 'f' if decode_fingering($line,0) ; |
|
413
|
|
|
|
|
|
|
|
|
414
|
385
|
|
|
|
|
557
|
if(0) { |
|
415
|
|
|
|
|
|
|
#Oct 31 and before |
|
416
|
|
|
|
|
|
|
return 't' if $line =~ /^\s*?[A-G|a-g]\s*\|.*?\-.*\|/; # tablature |
|
417
|
|
|
|
|
|
|
return 't' if $line =~ /^\s*?[A-G|a-g]\s*\-.*?\-.*\|*/; # tablature |
|
418
|
|
|
|
|
|
|
} else { |
|
419
|
|
|
|
|
|
|
# try to accomodate tablature lines with text after the tab |
|
420
|
|
|
|
|
|
|
|
|
421
|
385
|
|
|
|
|
536
|
my $longest_tablature_string=0 ; |
|
422
|
385
|
|
|
|
|
592
|
my $tmpline = $line ; |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
# REGEX components: |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
# start with any amount of whitespace |
|
427
|
|
|
|
|
|
|
# ^\s*? |
|
428
|
|
|
|
|
|
|
# must be one string note |
|
429
|
|
|
|
|
|
|
# [A-G|a-g] |
|
430
|
|
|
|
|
|
|
# one or more of : or | |
|
431
|
|
|
|
|
|
|
# [:\|]+ |
|
432
|
|
|
|
|
|
|
# in the tablature itself, separators of : or |, modifiers of b=bend,p=pull off,h=hammer on,x=muted,0-9 fret positionsj,\/=slides,() for two digit fret positions |
|
433
|
|
|
|
|
|
|
# [\-:\|bphxBPHX0-9\/\\\(\)]*? |
|
434
|
|
|
|
|
|
|
# one or more of : or | |
|
435
|
|
|
|
|
|
|
# [:\|]+ |
|
436
|
|
|
|
|
|
|
|
|
437
|
385
|
|
|
|
|
1407
|
while($tmpline =~ s/^(\s*?[A-G|a-g][:\|]+[\-:\|bphxBPHX0-9\/\\\(\)]*?[:\|]+)//) { |
|
438
|
55
|
50
|
|
|
|
242
|
$longest_tablature_string = length($1) if $longest_tablature_string < length($1) ; |
|
439
|
|
|
|
|
|
|
} |
|
440
|
|
|
|
|
|
|
|
|
441
|
385
|
100
|
|
|
|
861
|
return 't' if $longest_tablature_string > 8 ; |
|
442
|
|
|
|
|
|
|
} |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# count number of specific characters to help identify tablature lines |
|
446
|
330
|
|
|
|
|
786
|
my $n_v = ($line =~ tr/v//) ; |
|
447
|
330
|
|
|
|
|
671
|
my $n_dash = ($line =~ tr/-//) ; |
|
448
|
330
|
|
|
|
|
730
|
my $n_equal = ($line =~ tr/=//) ; |
|
449
|
330
|
|
|
|
|
675
|
my $n_bar = ($line =~ tr/|//) ; |
|
450
|
330
|
|
|
|
|
634
|
my $n_c_accent = ($line =~ tr/^//) ; |
|
451
|
330
|
|
|
|
|
690
|
my $n_period = ($line =~ tr/.//) ; |
|
452
|
330
|
|
|
|
|
634
|
my $n_space = ($line =~ tr/ //) ; |
|
453
|
330
|
|
|
|
|
618
|
my $n_slash = ($line =~ tr/\///) ; |
|
454
|
330
|
|
|
|
|
663
|
my $n_underscore = ($line =~ tr/_//) ; |
|
455
|
330
|
|
|
|
|
639
|
my $n_digit = ($line =~ tr/0123456789//) ; |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# some inputs are of the form "| / / / _ / | / / / / / |", to indicate strumming patterns |
|
458
|
|
|
|
|
|
|
# need to recognize this as tablature for nice formatting, and if chords are in the line |
|
459
|
|
|
|
|
|
|
# preceding they will be included in the tablature by maplines() to ensure correct formatting |
|
460
|
330
|
|
|
|
|
522
|
my $longest_strumming_string=0 ; |
|
461
|
330
|
|
|
|
|
524
|
my $cntline = $line ; |
|
462
|
|
|
|
|
|
|
|
|
463
|
330
|
|
|
|
|
1620
|
while( $cntline =~ s/([\|\/ _]+?)//) { |
|
464
|
3100
|
100
|
|
|
|
14801
|
$longest_strumming_string = length($1) if $longest_strumming_string < length($1) ; |
|
465
|
|
|
|
|
|
|
} |
|
466
|
|
|
|
|
|
|
|
|
467
|
330
|
50
|
|
|
|
680
|
return 't' if ($longest_strumming_string >= 6) ; |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
# Lyrics or Chords heuristic. |
|
472
|
330
|
|
|
|
|
1612
|
my @words = split ( /\s+/, $line ); |
|
473
|
|
|
|
|
|
|
|
|
474
|
330
|
|
|
|
|
752
|
my $n_tot_chars = length($line) ; |
|
475
|
330
|
|
|
|
|
1515
|
$line =~ s/\s+//g ; |
|
476
|
330
|
|
|
|
|
686
|
my $n_nonblank_chars = length($line) ; |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
# have to wait until $n_nonblank_chars is computed to do these tests |
|
479
|
330
|
100
|
100
|
|
|
1300
|
return 'l' if ($n_dash == $n_nonblank_chars || $n_equal == $n_nonblank_chars) ; # only "-" or "=", meant to be a textual underline indication of the previous line |
|
480
|
322
|
100
|
|
|
|
1141
|
return 't' if (($n_period + $n_dash + $n_bar + $n_c_accent + $n_v + $n_digit)/$n_nonblank_chars > 0.8) ; # mostly characters used in standard tablature |
|
481
|
218
|
100
|
|
|
|
487
|
return 't' if (($n_bar + $n_slash + $n_underscore)/$n_nonblank_chars >= 0.5) ; # mostly characters used in strumming tablature |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
|
|
484
|
216
|
|
|
|
|
310
|
my $n_chords=0 ; |
|
485
|
216
|
|
|
|
|
293
|
my $n_words=0 ; |
|
486
|
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
#print("CL:") ; # JJW, uncomment for debugging |
|
488
|
|
|
|
|
|
|
|
|
489
|
216
|
|
|
|
|
498
|
foreach (@words) { |
|
490
|
1157
|
100
|
|
|
|
2848
|
if (length $_ > 0) { |
|
491
|
1083
|
|
|
|
|
1555
|
$n_words++ ; |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
|
|
494
|
1083
|
100
|
|
|
|
2436
|
my $is_chord = ChordPro::Chords::parse_chord($_) ? 1 : 0 ; |
|
495
|
1083
|
100
|
|
|
|
2648
|
if(! $is_chord) { |
|
496
|
885
|
100
|
|
|
|
1615
|
if(generic_parse_chord($_)) { |
|
497
|
4
|
50
|
|
|
|
26
|
print STDERR "$_ detected by generic, not internal parse_chord\n" if $local_debug ; |
|
498
|
4
|
|
|
|
|
9
|
$is_chord=1 ; |
|
499
|
|
|
|
|
|
|
} |
|
500
|
|
|
|
|
|
|
} |
|
501
|
|
|
|
|
|
|
|
|
502
|
1083
|
100
|
|
|
|
2208
|
$n_chords++ if $is_chord ; |
|
503
|
1083
|
50
|
|
|
|
2287
|
print STDERR " ($is_chord:$_)" if $local_debug ; |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
#print(" \'$is_chord:$_\'") ; # JJW, uncomment for debugging |
|
506
|
|
|
|
|
|
|
} |
|
507
|
|
|
|
|
|
|
} |
|
508
|
216
|
50
|
|
|
|
455
|
print STDERR "\n" if $local_debug ; |
|
509
|
|
|
|
|
|
|
|
|
510
|
216
|
50
|
|
|
|
511
|
return '_' if $n_words == 0 ; # blank line, redundant logic with sub classify(), but makes this more robust to changes in classify() ; |
|
511
|
|
|
|
|
|
|
|
|
512
|
216
|
100
|
|
|
|
583
|
my $type = $n_chords/$n_words > 0.4 ? 'c' : 'l' ; |
|
513
|
|
|
|
|
|
|
|
|
514
|
216
|
100
|
|
|
|
481
|
if($type eq 'l') { |
|
515
|
|
|
|
|
|
|
# is it likely the line had a lot of unknown chords, check |
|
516
|
|
|
|
|
|
|
# the ratio of total chars to nonblank chars , if it is large then |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
# it's probably a chord line |
|
519
|
|
|
|
|
|
|
# $type = 'c' if $n_words > 1 && $n_tot_chars/$n_nonblank_chars > 2. ; |
|
520
|
|
|
|
|
|
|
} |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
#print(" --- ($n_chords/$n_words) = $type\n") ; # JJW, uncomment for debugging |
|
523
|
|
|
|
|
|
|
|
|
524
|
216
|
|
|
|
|
989
|
return $type ; |
|
525
|
|
|
|
|
|
|
} |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
# reformat an input line classified as a comment for the chordpro format |
|
528
|
|
|
|
|
|
|
sub format_comment_line |
|
529
|
|
|
|
|
|
|
{ |
|
530
|
69
|
|
|
69
|
0
|
120
|
my $line = $_[0] ; |
|
531
|
|
|
|
|
|
|
# remove [] from original comment |
|
532
|
69
|
|
|
|
|
158
|
$line =~ s/\[// ; |
|
533
|
69
|
|
|
|
|
129
|
$line =~ s/\]// ; |
|
534
|
69
|
50
|
|
|
|
149
|
return '' if $line eq '' ; |
|
535
|
69
|
|
|
|
|
238
|
return "{comment:" . $line . "}" ; |
|
536
|
|
|
|
|
|
|
} |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
# Process the lines via the map. |
|
539
|
|
|
|
|
|
|
my $infer_titles; |
|
540
|
|
|
|
|
|
|
sub maplines { |
|
541
|
19
|
|
|
19
|
0
|
115
|
my ( $map, $lines ) = @_; |
|
542
|
19
|
|
|
|
|
45
|
my @out; |
|
543
|
19
|
|
|
|
|
39
|
my $local_debug=0 ; |
|
544
|
19
|
|
66
|
|
|
64
|
$infer_titles //= $::config->{a2crd}->{'infer-titles'}; |
|
545
|
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
# Preamble. |
|
547
|
|
|
|
|
|
|
# Pass empty lines. |
|
548
|
|
|
|
|
|
|
|
|
549
|
19
|
50
|
|
|
|
160
|
print STDERR "====== _C =====\n" if $local_debug ; |
|
550
|
19
|
50
|
|
|
|
63
|
print STDERR "MAP: \'$map\' \n" if $local_debug ; |
|
551
|
|
|
|
|
|
|
|
|
552
|
19
|
|
|
|
|
114
|
while ( $map =~ s/^([_C])// ) { |
|
553
|
13
|
50
|
|
|
|
38
|
print STDERR "$1 == @{$lines}[0]\n" if $local_debug ; |
|
|
0
|
|
|
|
|
0
|
|
|
554
|
|
|
|
|
|
|
# simply output blank or comment lines at the start of the file |
|
555
|
|
|
|
|
|
|
# but don't count the line as possible title |
|
556
|
13
|
100
|
|
|
|
48
|
my $pre = ($1 eq "C" ? "{comment:" : "" ) ; |
|
557
|
13
|
100
|
|
|
|
36
|
my $post = ($1 eq "C" ? "}" : "" ) ; |
|
558
|
13
|
|
|
|
|
69
|
push( @out, $pre . shift( @$lines ) . $post ); |
|
559
|
|
|
|
|
|
|
} |
|
560
|
|
|
|
|
|
|
|
|
561
|
19
|
50
|
|
|
|
62
|
print STDERR "====== infer title =====\n" if $local_debug ; |
|
562
|
|
|
|
|
|
|
# Infer title/subtitle. |
|
563
|
19
|
100
|
66
|
|
|
58
|
if ( $infer_titles && $map =~ s/^l// ) { |
|
564
|
18
|
|
|
|
|
327
|
push( @out, "{title: " . shift( @$lines ) . "}"); |
|
565
|
18
|
100
|
|
|
|
85
|
if ( $map =~ s/^l// ) { |
|
566
|
8
|
|
|
|
|
56
|
push( @out, "{subtitle: " . shift( @$lines ) . "}"); |
|
567
|
|
|
|
|
|
|
} |
|
568
|
|
|
|
|
|
|
} |
|
569
|
|
|
|
|
|
|
|
|
570
|
19
|
50
|
|
|
|
88
|
print STDERR "====== UNTIL chords or tablature =====\n" if $local_debug ; |
|
571
|
|
|
|
|
|
|
# Pass lines until we have chords or tablature |
|
572
|
|
|
|
|
|
|
|
|
573
|
19
|
|
|
|
|
128
|
while ($map =~ /^(.)(.)(.)/) { |
|
574
|
331
|
50
|
|
|
|
991
|
push @out, "ULC $map" if $local_debug ; |
|
575
|
|
|
|
|
|
|
# some unusual situations to handle, |
|
576
|
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
# cl. => exit this loop for normal cl processing |
|
578
|
|
|
|
|
|
|
# .t => exit the loop |
|
579
|
|
|
|
|
|
|
# l.t or c.t => output the l or c as comment, then exit the loop |
|
580
|
|
|
|
|
|
|
# [_f{C].. => output the blank, fingering,directive or comment, and continue the loop |
|
581
|
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
# we have to stop one line before tablature, in case the line before the tablature needs to be included in the |
|
583
|
|
|
|
|
|
|
# tablature itself |
|
584
|
331
|
50
|
|
|
|
567
|
print STDERR "$1 == @{$lines}[0]\n" if $local_debug ; |
|
|
0
|
|
|
|
|
0
|
|
|
585
|
|
|
|
|
|
|
|
|
586
|
331
|
100
|
100
|
|
|
970
|
last if($1 eq "c" && $2 eq "l") ; |
|
587
|
326
|
100
|
|
|
|
637
|
last if($2 eq "t" ) ; |
|
588
|
|
|
|
|
|
|
|
|
589
|
322
|
100
|
100
|
|
|
1283
|
if(($1 eq "c" || $1 eq "l") && $3 eq "t") { |
|
|
|
|
100
|
|
|
|
|
|
590
|
6
|
|
|
|
|
22
|
push @out, format_comment_line(shift(@$lines)) ; |
|
591
|
6
|
|
|
|
|
30
|
$map =~ s/.// ; |
|
592
|
6
|
|
|
|
|
23
|
last ; |
|
593
|
|
|
|
|
|
|
} |
|
594
|
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
# in the remaining cases, output the line (properly handled), and continue the loop |
|
596
|
316
|
100
|
100
|
|
|
1127
|
if ( $1 eq "l" or $1 eq "C") { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
597
|
59
|
|
|
|
|
146
|
push @out, format_comment_line(shift(@$lines)) ; |
|
598
|
|
|
|
|
|
|
} |
|
599
|
|
|
|
|
|
|
elsif ( $1 eq "f" ) { |
|
600
|
191
|
|
|
|
|
440
|
foreach my $fchart (decode_fingering(shift( @$lines ),1) ) { |
|
601
|
217
|
|
|
|
|
415
|
push( @out, $fchart); |
|
602
|
|
|
|
|
|
|
} |
|
603
|
|
|
|
|
|
|
} |
|
604
|
|
|
|
|
|
|
elsif ( $1 eq "{" ) { |
|
605
|
8
|
|
|
|
|
17
|
my $line = shift @$lines ; |
|
606
|
8
|
|
|
|
|
16
|
push( @out, $line); |
|
607
|
|
|
|
|
|
|
|
|
608
|
8
|
100
|
|
|
|
33
|
if($line =~ /{sot}/) { |
|
609
|
|
|
|
|
|
|
# output all subsequent lines until {eot} is found |
|
610
|
1
|
|
|
|
|
3
|
while(1) { |
|
611
|
8
|
|
|
|
|
15
|
$line = shift @$lines ; |
|
612
|
8
|
50
|
|
|
|
17
|
die "Malformed input, {sot} has no matching {eot}" if ! $line ; |
|
613
|
8
|
|
|
|
|
19
|
$map = s/.// ; |
|
614
|
8
|
|
|
|
|
13
|
push( @out, $line); |
|
615
|
8
|
100
|
|
|
|
32
|
last if $line =~ /{eot}/ ; |
|
616
|
|
|
|
|
|
|
} |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
} |
|
619
|
|
|
|
|
|
|
} |
|
620
|
|
|
|
|
|
|
else { |
|
621
|
58
|
|
|
|
|
129
|
push( @out, shift( @$lines ) ); |
|
622
|
|
|
|
|
|
|
} |
|
623
|
316
|
|
|
|
|
1316
|
$map =~ s/.// ; |
|
624
|
|
|
|
|
|
|
} |
|
625
|
|
|
|
|
|
|
|
|
626
|
19
|
50
|
|
|
|
90
|
push @out, "====== FINAL LOOP =====" if $local_debug ; |
|
627
|
|
|
|
|
|
|
# Process the lines using the map. |
|
628
|
19
|
|
|
|
|
71
|
while ( $map ) { |
|
629
|
|
|
|
|
|
|
# warn($map); |
|
630
|
187
|
50
|
|
|
|
328
|
push @out, "FL $map" if $local_debug ; |
|
631
|
187
|
|
|
|
|
366
|
$map =~ /(.)/ ; |
|
632
|
187
|
50
|
|
|
|
320
|
print STDERR "$1 == @{$lines}[0]\n" if $local_debug ; |
|
|
0
|
|
|
|
|
0
|
|
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
#a fingering line, simply output the directive and continue |
|
635
|
187
|
100
|
|
|
|
394
|
if ( $map =~ s/^f// ) { |
|
636
|
10
|
|
|
|
|
26
|
foreach my $fchart (decode_fingering(shift( @$lines ),1) ) { |
|
637
|
10
|
|
|
|
|
27
|
push( @out, $fchart); |
|
638
|
|
|
|
|
|
|
} |
|
639
|
10
|
|
|
|
|
31
|
next ; |
|
640
|
|
|
|
|
|
|
} |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
# Blank line - output the blank line and continue |
|
643
|
177
|
100
|
|
|
|
444
|
if ( $map =~ s/^_// ) { |
|
644
|
78
|
|
|
|
|
152
|
push( @out, ''); |
|
645
|
78
|
|
|
|
|
116
|
shift(@$lines); |
|
646
|
78
|
|
|
|
|
157
|
next ; |
|
647
|
|
|
|
|
|
|
} |
|
648
|
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
# A comment line, output and continue |
|
650
|
99
|
100
|
|
|
|
227
|
if ( $map =~ s/^C// ) { |
|
651
|
4
|
|
|
|
|
14
|
push @out, format_comment_line(shift(@$lines)) ; |
|
652
|
4
|
|
|
|
|
10
|
next ; |
|
653
|
|
|
|
|
|
|
} |
|
654
|
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
# Tablature |
|
656
|
95
|
|
|
|
|
139
|
my $in_tablature=0 ; |
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
# special case: chords or lyrics before tabs, keep the chords or lyrics in {sot}, which is probably |
|
659
|
|
|
|
|
|
|
# what the original text intended for alignment with the tablature |
|
660
|
95
|
100
|
|
|
|
267
|
if ( $map =~ s/^[cl]t/t/ ) { |
|
661
|
17
|
50
|
|
|
|
51
|
if(! $in_tablature) { |
|
662
|
17
|
|
|
|
|
37
|
push( @out, "{sot}") ; |
|
663
|
17
|
|
|
|
|
32
|
$in_tablature=1 ; |
|
664
|
|
|
|
|
|
|
} |
|
665
|
17
|
|
|
|
|
35
|
push( @out, shift(@$lines)); |
|
666
|
|
|
|
|
|
|
} |
|
667
|
|
|
|
|
|
|
|
|
668
|
95
|
|
|
|
|
244
|
while( $map =~ s/^t// ) { |
|
669
|
155
|
100
|
|
|
|
309
|
if(! $in_tablature) { |
|
670
|
12
|
|
|
|
|
26
|
push( @out, "{sot}") ; |
|
671
|
12
|
|
|
|
|
21
|
$in_tablature=1 ; |
|
672
|
|
|
|
|
|
|
} |
|
673
|
155
|
|
|
|
|
482
|
push( @out, shift(@$lines)); |
|
674
|
|
|
|
|
|
|
# and Fall through. |
|
675
|
|
|
|
|
|
|
} |
|
676
|
|
|
|
|
|
|
|
|
677
|
95
|
100
|
|
|
|
204
|
if($in_tablature) { |
|
678
|
|
|
|
|
|
|
# Text line OR chord line with following blank line or EOF -- make part of tablature |
|
679
|
29
|
100
|
|
|
|
107
|
if ( $map =~ s/^[cl](_|$)// ) { |
|
680
|
9
|
|
|
|
|
21
|
push( @out, shift(@$lines)); |
|
681
|
9
|
|
|
|
|
15
|
push( @out, ''); |
|
682
|
9
|
|
|
|
|
14
|
shift(@$lines); |
|
683
|
|
|
|
|
|
|
} |
|
684
|
|
|
|
|
|
|
|
|
685
|
29
|
|
|
|
|
65
|
push( @out, "{eot}") ; |
|
686
|
29
|
|
|
|
|
42
|
$in_tablature=0 ; |
|
687
|
29
|
|
|
|
|
71
|
next ; |
|
688
|
|
|
|
|
|
|
} |
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
# Blank line preceding lyrics: pass. |
|
691
|
66
|
50
|
|
|
|
183
|
if ( $map =~ s/^_l/l/ ) { |
|
692
|
0
|
|
|
|
|
0
|
push( @out, ''); |
|
693
|
0
|
|
|
|
|
0
|
shift(@$lines); |
|
694
|
|
|
|
|
|
|
} |
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
# The normal case: chords + lyrics. |
|
697
|
66
|
100
|
|
|
|
411
|
if ( $map =~ s/^cl// ) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
698
|
39
|
|
|
|
|
128
|
push( @out, combine( shift(@$lines), shift(@$lines), "cl" ) ); |
|
699
|
|
|
|
|
|
|
} |
|
700
|
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
# Empty line preceding a chordless lyrics line. |
|
702
|
|
|
|
|
|
|
elsif ( $map =~ s/^__l// ) { |
|
703
|
0
|
|
|
|
|
0
|
push( @out, '' ); |
|
704
|
0
|
|
|
|
|
0
|
shift( @$lines ); |
|
705
|
0
|
|
|
|
|
0
|
push( @out, combine( shift(@$lines), shift(@$lines), "__l" ) ); |
|
706
|
|
|
|
|
|
|
} |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
# Chordless lyrics line. |
|
709
|
|
|
|
|
|
|
elsif ( $map =~ s/^_l// ) { |
|
710
|
0
|
|
|
|
|
0
|
push( @out, combine( shift(@$lines), shift(@$lines), "_l" ) ); |
|
711
|
|
|
|
|
|
|
} |
|
712
|
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
# Lone directives. |
|
714
|
|
|
|
|
|
|
elsif ( $map =~ s/^{// ) { |
|
715
|
1
|
|
|
|
|
4
|
my $line = shift @$lines ; |
|
716
|
1
|
|
|
|
|
3
|
push( @out, $line); |
|
717
|
|
|
|
|
|
|
|
|
718
|
1
|
50
|
|
|
|
8
|
if($line =~ /{sot}/) { |
|
719
|
|
|
|
|
|
|
# output all subsequent lines until {eot} is found |
|
720
|
0
|
|
|
|
|
0
|
while(1) { |
|
721
|
0
|
|
|
|
|
0
|
$line = shift @$lines ; |
|
722
|
0
|
0
|
|
|
|
0
|
die "Malformed input, {sot} has no matching {eot}" if ! $line ; |
|
723
|
0
|
|
|
|
|
0
|
$map = s/.// ; |
|
724
|
0
|
|
|
|
|
0
|
push( @out, $line); |
|
725
|
0
|
0
|
|
|
|
0
|
last if $line =~ /{eot}/ ; |
|
726
|
|
|
|
|
|
|
} |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
} |
|
729
|
|
|
|
|
|
|
} |
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
# Lone lyrics. |
|
732
|
|
|
|
|
|
|
elsif ( $map =~ s/^l// ) { |
|
733
|
22
|
|
|
|
|
67
|
push( @out, shift( @$lines ) ); |
|
734
|
|
|
|
|
|
|
} |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
# Lone chords. |
|
737
|
|
|
|
|
|
|
elsif ( $map =~ s/^c// ) { |
|
738
|
4
|
|
|
|
|
13
|
push( @out, combine( shift(@$lines), '', "c" ) ); |
|
739
|
|
|
|
|
|
|
} |
|
740
|
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
# Empty line. |
|
742
|
|
|
|
|
|
|
elsif ( $map =~ s/^_// ) { |
|
743
|
0
|
|
|
|
|
0
|
push( @out, '' ); |
|
744
|
0
|
|
|
|
|
0
|
shift( @$lines ); |
|
745
|
|
|
|
|
|
|
} |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
# Can't happen. |
|
748
|
|
|
|
|
|
|
else { |
|
749
|
0
|
|
|
|
|
0
|
croak("MAP: $map"); |
|
750
|
|
|
|
|
|
|
} |
|
751
|
|
|
|
|
|
|
} |
|
752
|
19
|
50
|
|
|
|
587
|
return wantarray ? @out : \@out; |
|
753
|
|
|
|
|
|
|
} |
|
754
|
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
# Combine two lines (chords + lyrics) into lyrics with [chords]. |
|
756
|
|
|
|
|
|
|
sub combine { |
|
757
|
43
|
|
|
43
|
0
|
114
|
my ( $l1, $l2 ) = @_; |
|
758
|
43
|
|
|
|
|
67
|
my $res = ""; |
|
759
|
43
|
|
|
|
|
172
|
while ( $l1 =~ /^(\s*)(\S+)(.*)/ ) { |
|
760
|
130
|
|
|
|
|
708
|
$res .= join( '', |
|
761
|
|
|
|
|
|
|
substr( $l2, 0, length($1), '' ), |
|
762
|
|
|
|
|
|
|
'[' . $2 . ']', |
|
763
|
|
|
|
|
|
|
substr( $l2, 0, length($2), '' ) ); |
|
764
|
130
|
|
|
|
|
501
|
$l1 = $3; |
|
765
|
|
|
|
|
|
|
} |
|
766
|
43
|
|
|
|
|
209
|
return $res.$l2; |
|
767
|
|
|
|
|
|
|
} |
|
768
|
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
################ Options and Configuration ################ |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
=head1 COMMAND LINE OPTIONS |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=over 4 |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
=item B<--output=>I (short: B<-o>) |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
Designates the name of the output file where the results are written |
|
778
|
|
|
|
|
|
|
to. Default is standard output. |
|
779
|
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
=item B<--version> (short: B<-V>) |
|
781
|
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
Prints the program version and exits. |
|
783
|
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
=item B<--help> (short: -h) |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
Prints a help message. No other output is produced. |
|
787
|
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
=item B<--manual> |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
Prints the manual page. No other output is produced. |
|
791
|
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
=item B<--ident> |
|
793
|
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
Shows the program name and version. |
|
795
|
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
=item B<--verbose> |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
Provides more verbose information of what is going on. |
|
799
|
|
|
|
|
|
|
|
|
800
|
|
|
|
|
|
|
=back |
|
801
|
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
=cut |
|
803
|
|
|
|
|
|
|
|
|
804
|
1
|
|
|
1
|
|
10
|
use Getopt::Long 2.13; |
|
|
1
|
|
|
|
|
17
|
|
|
|
1
|
|
|
|
|
29
|
|
|
805
|
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
# Package name. |
|
807
|
|
|
|
|
|
|
my $my_package; |
|
808
|
|
|
|
|
|
|
# Program name and version. |
|
809
|
|
|
|
|
|
|
my ($my_name, $my_version); |
|
810
|
|
|
|
|
|
|
my %configs; |
|
811
|
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
sub app_setup { |
|
813
|
0
|
|
|
0
|
0
|
|
goto &ChordPro::app_setup; |
|
814
|
0
|
|
|
|
|
|
my ($appname, $appversion, %args) = @_; |
|
815
|
0
|
|
|
|
|
|
my $help = 0; # handled locally |
|
816
|
0
|
|
|
|
|
|
my $manual = 0; # handled locally |
|
817
|
0
|
|
|
|
|
|
my $ident = 0; # handled locally |
|
818
|
0
|
|
|
|
|
|
my $version = 0; # handled locally |
|
819
|
0
|
|
|
|
|
|
my $defcfg = 0; # handled locally |
|
820
|
0
|
|
|
|
|
|
my $fincfg = 0; # handled locally |
|
821
|
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
# Package name. |
|
823
|
0
|
|
|
|
|
|
$my_package = $args{package}; |
|
824
|
|
|
|
|
|
|
# Program name and version. |
|
825
|
0
|
0
|
|
|
|
|
if ( defined $appname ) { |
|
826
|
0
|
|
|
|
|
|
($my_name, $my_version) = ($appname, $appversion); |
|
827
|
|
|
|
|
|
|
} |
|
828
|
|
|
|
|
|
|
else { |
|
829
|
0
|
|
|
|
|
|
($my_name, $my_version) = qw( MyProg 0.01 ); |
|
830
|
|
|
|
|
|
|
} |
|
831
|
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
# Config files. |
|
833
|
0
|
|
|
|
|
|
my $app_lc = lc("ChordPro"); # common config |
|
834
|
0
|
0
|
|
|
|
|
if ( -d "/etc" ) { # some *ux |
|
835
|
|
|
|
|
|
|
$configs{sysconfig} = |
|
836
|
0
|
|
|
|
|
|
File::Spec->catfile( "/", "etc", "$app_lc.json" ); |
|
837
|
|
|
|
|
|
|
} |
|
838
|
|
|
|
|
|
|
|
|
839
|
0
|
|
0
|
|
|
|
my $e = $ENV{CHORDIIRC} || $ENV{CHORDRC}; |
|
840
|
0
|
0
|
0
|
|
|
|
if ( $ENV{HOME} && -d $ENV{HOME} ) { |
|
841
|
0
|
0
|
|
|
|
|
if ( -d File::Spec->catfile( $ENV{HOME}, ".config" ) ) { |
|
842
|
|
|
|
|
|
|
$configs{userconfig} = |
|
843
|
0
|
|
|
|
|
|
File::Spec->catfile( $ENV{HOME}, ".config", $app_lc, "$app_lc.json" ); |
|
844
|
|
|
|
|
|
|
} |
|
845
|
|
|
|
|
|
|
else { |
|
846
|
|
|
|
|
|
|
$configs{userconfig} = |
|
847
|
0
|
|
|
|
|
|
File::Spec->catfile( $ENV{HOME}, ".$app_lc", "$app_lc.json" ); |
|
848
|
|
|
|
|
|
|
} |
|
849
|
0
|
|
0
|
|
|
|
$e ||= File::Spec->catfile( $ENV{HOME}, ".chordrc" ); |
|
850
|
|
|
|
|
|
|
} |
|
851
|
0
|
|
0
|
|
|
|
$e ||= "/chordrc"; # Windows, most likely |
|
852
|
0
|
0
|
0
|
|
|
|
$configs{legacyconfig} = $e if -s $e && -r _; |
|
853
|
|
|
|
|
|
|
|
|
854
|
0
|
0
|
|
|
|
|
if ( -s ".$app_lc.json" ) { |
|
855
|
0
|
|
|
|
|
|
$configs{config} = ".$app_lc.json"; |
|
856
|
|
|
|
|
|
|
} |
|
857
|
|
|
|
|
|
|
else { |
|
858
|
0
|
|
|
|
|
|
$configs{config} = "$app_lc.json"; |
|
859
|
|
|
|
|
|
|
} |
|
860
|
|
|
|
|
|
|
|
|
861
|
0
|
|
|
|
|
|
my $options = |
|
862
|
|
|
|
|
|
|
{ |
|
863
|
|
|
|
|
|
|
verbose => 0, # verbose processing |
|
864
|
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
# Development options (not shown with -help). |
|
866
|
|
|
|
|
|
|
debug => 0, # debugging |
|
867
|
|
|
|
|
|
|
trace => 0, # trace (show process) |
|
868
|
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
# Service. |
|
870
|
|
|
|
|
|
|
_package => $my_package, |
|
871
|
|
|
|
|
|
|
_name => $my_name, |
|
872
|
|
|
|
|
|
|
_version => $my_version, |
|
873
|
|
|
|
|
|
|
_stdin => \*STDIN, |
|
874
|
|
|
|
|
|
|
_stdout => \*STDOUT, |
|
875
|
|
|
|
|
|
|
_stderr => \*STDERR, |
|
876
|
|
|
|
|
|
|
_argv => [ @ARGV ], |
|
877
|
|
|
|
|
|
|
}; |
|
878
|
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
# Colled command line options in a hash, for they will be needed |
|
880
|
|
|
|
|
|
|
# later. |
|
881
|
0
|
|
|
|
|
|
my $clo = {}; |
|
882
|
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
# Sorry, layout is a bit ugly... |
|
884
|
0
|
0
|
|
|
|
|
if ( !GetOptions |
|
885
|
|
|
|
|
|
|
($clo, |
|
886
|
|
|
|
|
|
|
"output|o=s", # Saves the output to FILE |
|
887
|
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
### Configuration handling ### |
|
889
|
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
'config|cfg=s@', |
|
891
|
|
|
|
|
|
|
'noconfig|no-config', |
|
892
|
|
|
|
|
|
|
'sysconfig=s', |
|
893
|
|
|
|
|
|
|
'nosysconfig|no-sysconfig', |
|
894
|
|
|
|
|
|
|
'userconfig=s', |
|
895
|
|
|
|
|
|
|
'nouserconfig|no-userconfig', |
|
896
|
|
|
|
|
|
|
'nodefaultconfigs|no-default-configs|X', |
|
897
|
|
|
|
|
|
|
'define=s%', |
|
898
|
|
|
|
|
|
|
'print-default-config' => \$defcfg, |
|
899
|
|
|
|
|
|
|
'print-final-config' => \$fincfg, |
|
900
|
|
|
|
|
|
|
|
|
901
|
|
|
|
|
|
|
### Standard options ### |
|
902
|
|
|
|
|
|
|
|
|
903
|
|
|
|
|
|
|
"version|V" => \$version, # Prints version and exits |
|
904
|
|
|
|
|
|
|
'ident' => \$ident, |
|
905
|
|
|
|
|
|
|
'help|h|?' => \$help, |
|
906
|
|
|
|
|
|
|
'manual' => \$manual, |
|
907
|
|
|
|
|
|
|
'verbose|v+', |
|
908
|
|
|
|
|
|
|
'trace', |
|
909
|
|
|
|
|
|
|
'debug+', |
|
910
|
|
|
|
|
|
|
) ) |
|
911
|
|
|
|
|
|
|
{ |
|
912
|
|
|
|
|
|
|
# GNU convention: message to STDERR upon failure. |
|
913
|
0
|
|
|
|
|
|
app_usage(\*STDERR, 2); |
|
914
|
|
|
|
|
|
|
} |
|
915
|
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
my $pod2usage = sub { |
|
917
|
|
|
|
|
|
|
# Load Pod::Usage only if needed. |
|
918
|
0
|
|
|
0
|
|
|
require Pod::Usage; |
|
919
|
0
|
|
|
|
|
|
Pod::Usage->import; |
|
920
|
0
|
|
|
|
|
|
my $f = "pod/A2Crd.pod"; |
|
921
|
0
|
|
|
|
|
|
unshift( @_, -input => getresource($f) ); |
|
922
|
0
|
|
|
|
|
|
&pod2usage; |
|
923
|
0
|
|
|
|
|
|
}; |
|
924
|
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
# GNU convention: message to STDOUT upon request. |
|
926
|
0
|
0
|
0
|
|
|
|
app_ident(\*STDOUT) if $ident || $help || $manual; |
|
|
|
|
0
|
|
|
|
|
|
927
|
0
|
0
|
0
|
|
|
|
if ( $manual or $help ) { |
|
928
|
0
|
0
|
|
|
|
|
app_usage(\*STDOUT, 0) if $help; |
|
929
|
0
|
0
|
|
|
|
|
$pod2usage->(VERBOSE => 2) if $manual; |
|
930
|
|
|
|
|
|
|
} |
|
931
|
0
|
0
|
|
|
|
|
app_ident(\*STDOUT, 0) if $version; |
|
932
|
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
# If the user specified a config, it must exist. |
|
934
|
|
|
|
|
|
|
# Otherwise, set to a default. |
|
935
|
0
|
|
|
|
|
|
for my $config ( qw(sysconfig userconfig) ) { |
|
936
|
0
|
|
|
|
|
|
for ( $clo->{$config} ) { |
|
937
|
0
|
0
|
|
|
|
|
if ( defined($_) ) { |
|
938
|
0
|
0
|
|
|
|
|
die("$_: $!\n") unless -r $_; |
|
939
|
0
|
|
|
|
|
|
next; |
|
940
|
|
|
|
|
|
|
} |
|
941
|
|
|
|
|
|
|
# Use default. |
|
942
|
0
|
0
|
|
|
|
|
next if $clo->{nodefaultconfigs}; |
|
943
|
0
|
0
|
|
|
|
|
next unless $configs{$config}; |
|
944
|
0
|
|
|
|
|
|
$_ = $configs{$config}; |
|
945
|
0
|
0
|
|
|
|
|
undef($_) unless -r $_; |
|
946
|
|
|
|
|
|
|
} |
|
947
|
|
|
|
|
|
|
} |
|
948
|
0
|
|
|
|
|
|
for my $config ( qw(config) ) { |
|
949
|
0
|
|
|
|
|
|
for ( $clo->{$config} ) { |
|
950
|
0
|
0
|
|
|
|
|
if ( defined($_) ) { |
|
951
|
0
|
|
|
|
|
|
foreach my $c ( @$_ ) { |
|
952
|
|
|
|
|
|
|
# Check for resource names. |
|
953
|
0
|
0
|
0
|
|
|
|
if ( ! -r $c && $c !~ m;[/.]; ) { |
|
954
|
0
|
|
|
|
|
|
$c = ::rsc_or_file( $c, "config" ); |
|
955
|
|
|
|
|
|
|
} |
|
956
|
0
|
0
|
|
|
|
|
die("$c: $!\n") unless -r $c; |
|
957
|
|
|
|
|
|
|
} |
|
958
|
0
|
|
|
|
|
|
next; |
|
959
|
|
|
|
|
|
|
} |
|
960
|
|
|
|
|
|
|
# Use default. |
|
961
|
0
|
0
|
|
|
|
|
next if $clo->{nodefaultconfigs}; |
|
962
|
0
|
0
|
|
|
|
|
next unless $configs{$config}; |
|
963
|
0
|
|
|
|
|
|
$_ = [ $configs{$config} ]; |
|
964
|
0
|
0
|
|
|
|
|
undef($_) unless -r $_->[0]; |
|
965
|
|
|
|
|
|
|
} |
|
966
|
|
|
|
|
|
|
} |
|
967
|
|
|
|
|
|
|
# If no config was specified, and no default is available, force no. |
|
968
|
0
|
|
|
|
|
|
for my $config ( qw(sysconfig userconfig config) ) { |
|
969
|
0
|
0
|
|
|
|
|
$clo->{"no$config"} = 1 unless $clo->{$config}; |
|
970
|
|
|
|
|
|
|
} |
|
971
|
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
####TODO: Should decode all, and remove filename exception. |
|
973
|
0
|
|
|
|
|
|
for ( keys %{ $clo->{define} } ) { |
|
|
0
|
|
|
|
|
|
|
|
974
|
0
|
|
|
|
|
|
$clo->{define}->{$_} = decode_utf8($clo->{define}->{$_}); |
|
975
|
|
|
|
|
|
|
} |
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
# Plug in command-line options. |
|
978
|
0
|
|
|
|
|
|
@{$options}{keys %$clo} = values %$clo; |
|
|
0
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
# warn(Dumper($options), "\n") if $options->{debug}; |
|
980
|
|
|
|
|
|
|
|
|
981
|
0
|
0
|
0
|
|
|
|
if ( $defcfg || $fincfg ) { |
|
982
|
0
|
0
|
|
|
|
|
print ChordPro::Config::config_default() |
|
983
|
|
|
|
|
|
|
if $defcfg; |
|
984
|
0
|
0
|
|
|
|
|
print ChordPro::Config::config_final() |
|
985
|
|
|
|
|
|
|
if $fincfg; |
|
986
|
0
|
|
|
|
|
|
exit 0; |
|
987
|
|
|
|
|
|
|
} |
|
988
|
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
# Return result. |
|
990
|
0
|
|
|
|
|
|
$options; |
|
991
|
|
|
|
|
|
|
} |
|
992
|
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
sub app_ident { |
|
994
|
0
|
|
|
0
|
0
|
|
my ($fh, $exit) = @_; |
|
995
|
0
|
0
|
|
|
|
|
print {$fh} ("This is ", |
|
|
0
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
$my_package |
|
997
|
|
|
|
|
|
|
? "$my_package [$my_name $my_version]" |
|
998
|
|
|
|
|
|
|
: "$my_name version $my_version", |
|
999
|
|
|
|
|
|
|
"\n"); |
|
1000
|
0
|
0
|
|
|
|
|
exit $exit if defined $exit; |
|
1001
|
|
|
|
|
|
|
} |
|
1002
|
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
sub app_usage { |
|
1004
|
0
|
|
|
0
|
0
|
|
my ($fh, $exit) = @_; |
|
1005
|
0
|
|
|
|
|
|
my $cmd = $0; |
|
1006
|
0
|
0
|
|
|
|
|
$cmd .= " --a2crd" if $cmd !~ m;(?:^|\/|\\)a2crd(?:\.\w+)$;; |
|
1007
|
0
|
|
|
|
|
|
print ${fh} <
|
|
1008
|
|
|
|
|
|
|
Usage: $cmd [ options ] [ file ... ] |
|
1009
|
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
Options: |
|
1011
|
|
|
|
|
|
|
--output=FILE -o Saves the output to FILE |
|
1012
|
|
|
|
|
|
|
--version -V Prints version and exits |
|
1013
|
|
|
|
|
|
|
--help -h This message |
|
1014
|
|
|
|
|
|
|
--manual The full manual |
|
1015
|
|
|
|
|
|
|
--ident Show identification |
|
1016
|
|
|
|
|
|
|
--verbose Verbose information |
|
1017
|
|
|
|
|
|
|
EndOfUsage |
|
1018
|
0
|
0
|
|
|
|
|
exit $exit if defined $exit; |
|
1019
|
|
|
|
|
|
|
} |
|
1020
|
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
=head1 AUTHOR |
|
1022
|
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
Johan Vromans C<< >> |
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
=head1 SUPPORT |
|
1026
|
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
A2Crd is part of ChordPro (the program). Development is hosted on |
|
1028
|
|
|
|
|
|
|
GitHub, repository L. |
|
1029
|
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
Please report any bugs or feature requests to the GitHub issue tracker, |
|
1031
|
|
|
|
|
|
|
L. |
|
1032
|
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
A user community discussing ChordPro can be found at |
|
1034
|
|
|
|
|
|
|
L. |
|
1035
|
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
=head1 LICENSE |
|
1037
|
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
Copyright (C) 2010,2018 Johan Vromans, |
|
1039
|
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
This program is free software. You can redistribute it and/or |
|
1041
|
|
|
|
|
|
|
modify it under the terms of the Artistic License 2.0. |
|
1042
|
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, |
|
1044
|
|
|
|
|
|
|
but without any warranty; without even the implied warranty of |
|
1045
|
|
|
|
|
|
|
merchantability or fitness for a particular purpose. |
|
1046
|
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
=cut |
|
1048
|
|
|
|
|
|
|
|
|
1049
|
|
|
|
|
|
|
1; |