line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#! perl |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
784
|
use v5.26; |
|
1
|
|
|
|
|
5
|
|
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package ChordPro::A2Crd; |
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
8
|
use App::Packager; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
13
|
|
8
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
164
|
use ChordPro::Version; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
43
|
|
10
|
1
|
|
|
1
|
|
7
|
use ChordPro::Chords; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
64
|
|
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
|
|
14
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
23
|
|
78
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
42
|
|
79
|
1
|
|
|
1
|
|
6
|
use utf8; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
5
|
|
80
|
1
|
|
|
1
|
|
27
|
use Carp; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
106
|
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
################ The Process ################ |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
package main; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
our $options; |
87
|
|
|
|
|
|
|
our $config; |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
package ChordPro::A2Crd; |
90
|
|
|
|
|
|
|
|
91
|
1
|
|
|
1
|
|
9
|
use ChordPro::Config; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
53
|
|
92
|
|
|
|
|
|
|
|
93
|
1
|
|
|
1
|
|
6
|
use File::LoadLines; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
63
|
|
94
|
1
|
|
|
1
|
|
6
|
use Encode qw(decode decode_utf8 encode_utf8); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
4896
|
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# API: Main entry point. |
97
|
|
|
|
|
|
|
sub a2crd { |
98
|
19
|
|
|
19
|
0
|
66
|
my ($opts) = @_; |
99
|
19
|
50
|
|
|
|
63
|
$options = { %$options, %$opts } if $opts; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# One configurator to bind them all. |
102
|
19
|
|
|
|
|
91
|
$config = ChordPro::Config::configurator({}); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# Process input. |
105
|
|
|
|
|
|
|
my $lines = $opts->{lines} |
106
|
|
|
|
|
|
|
? delete($opts->{lines}) |
107
|
19
|
50
|
|
|
|
292
|
: loadlines( @ARGV ? $ARGV[0] : \*STDIN); |
|
|
50
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
109
|
19
|
|
|
|
|
13045
|
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
|
63
|
my ( $line ) = @_; |
118
|
31
|
50
|
|
|
|
63
|
return $line unless $line; |
119
|
31
|
|
66
|
|
|
74
|
$tabstop //= $::config->{a2crd}->{tabstop}; |
120
|
31
|
50
|
|
|
|
66
|
return $line unless $tabstop > 0; |
121
|
|
|
|
|
|
|
|
122
|
31
|
|
|
|
|
98
|
my ( @l ) = split( /\t/, $line, -1 ); |
123
|
31
|
50
|
|
|
|
79
|
return $l[0] if @l == 1; |
124
|
|
|
|
|
|
|
|
125
|
31
|
|
|
|
|
62
|
$line = shift(@l); |
126
|
31
|
|
|
|
|
191
|
$line .= " " x ($tabstop-length($line)%$tabstop) . shift(@l) while @l; |
127
|
|
|
|
|
|
|
|
128
|
31
|
|
|
|
|
73
|
return $line; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# API: Produce ChordPro data from AsciiCRD lines. |
132
|
|
|
|
|
|
|
sub a2cho { |
133
|
19
|
|
|
19
|
0
|
85
|
my ( $lines ) = @_; |
134
|
19
|
|
|
|
|
55
|
my $map = ""; |
135
|
19
|
|
|
|
|
50
|
my @lines_with_tabs_replaced ; |
136
|
19
|
|
|
|
|
93
|
foreach ( @$lines ) { |
137
|
757
|
100
|
|
|
|
1871
|
if(/\t/) { |
138
|
31
|
|
|
|
|
74
|
$_ = 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
|
|
|
|
|
1410
|
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
|
|
|
|
|
1119
|
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
|
|
|
|
|
1721
|
while(s/\[ch\](.*?)\[\/ch\]/$1/) { |
150
|
16
|
|
|
|
|
77
|
$n_ch_chords++ ; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
757
|
|
|
|
|
1446
|
push @lines_with_tabs_replaced, $_ ; |
154
|
|
|
|
|
|
|
|
155
|
757
|
100
|
|
|
|
1340
|
if($n_ch_chords < 1) { |
156
|
754
|
|
|
|
|
1275
|
$map .= classify($_); |
157
|
|
|
|
|
|
|
} else { |
158
|
3
|
|
|
|
|
15
|
$map .= "c" ; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
} |
161
|
19
|
|
|
|
|
207
|
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
|
1314
|
my ( $line ) = @_; |
169
|
754
|
100
|
|
|
|
2490
|
return '_' if $line =~ /^\s*$/; # empty line |
170
|
611
|
100
|
|
|
|
1369
|
return '{' if $line =~ /^\{.+/; # directive |
171
|
601
|
100
|
|
|
|
1100
|
unless ( defined $classify ) { |
172
|
1
|
|
|
|
|
3
|
my $classifier = $::config->{a2crd}->{classifier}; |
173
|
1
|
|
|
|
|
20
|
$classify = __PACKAGE__->can("classify_".$classifier); |
174
|
1
|
50
|
|
|
|
4
|
unless ( $classify ) { |
175
|
0
|
|
|
|
|
0
|
warn("No such classifier: $classifier, using classic\n"); |
176
|
0
|
|
|
|
|
0
|
$classify = \&classify_classic; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
} |
180
|
601
|
|
|
|
|
1108
|
$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
|
890
|
|
|
890
|
0
|
1431
|
my $word = shift ; |
212
|
|
|
|
|
|
|
|
213
|
890
|
|
|
|
|
1737
|
my ($chord,$bass) ; |
214
|
890
|
100
|
|
|
|
1849
|
if ( $word =~ m;^(.*)/(.*); ) { |
215
|
15
|
|
|
|
|
41
|
$chord = $1; |
216
|
15
|
|
|
|
|
49
|
$bass = $2; |
217
|
|
|
|
|
|
|
} else { |
218
|
875
|
|
|
|
|
1219
|
$chord=$word ; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
890
|
100
|
|
|
|
1464
|
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
|
890
|
|
|
|
|
1253
|
my $roots = "^[A-G]" ; |
231
|
890
|
|
|
|
|
1131
|
my $found_chord_base="" ; |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# first part of chord needs to be [A-G] |
234
|
890
|
100
|
|
|
|
4308
|
return 0 if(! ($chord =~ s/($roots)//)) ; |
235
|
|
|
|
|
|
|
|
236
|
48
|
|
|
|
|
156
|
$found_chord_base .= $1 ; |
237
|
|
|
|
|
|
|
|
238
|
48
|
|
|
|
|
124
|
$chord = lc($chord) ; # simplify to lowercase for further parsing |
239
|
|
|
|
|
|
|
|
240
|
48
|
100
|
|
|
|
183
|
if($chord =~ s/^([b#]|flat|sharp)//) { |
241
|
3
|
|
|
|
|
8
|
$found_chord_base .= $1 ; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
48
|
50
|
|
|
|
139
|
if($chord =~ s/^(minor|major)//) { |
245
|
0
|
|
|
|
|
0
|
$found_chord_base .= $1 ; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
48
|
50
|
|
|
|
134
|
if($chord =~ s/^(min|maj)//) { |
249
|
0
|
|
|
|
|
0
|
$found_chord_base .= $1 ; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
48
|
100
|
|
|
|
168
|
if($chord =~ s/^(m|dim|0|o|aug|\+)//) { |
253
|
9
|
|
|
|
|
22
|
$found_chord_base .= $1 ; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
48
|
|
|
|
|
174
|
$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
|
48
|
|
|
|
|
208
|
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
|
48
|
|
|
|
|
108
|
my $n_ok = ($chord =~ tr/0123456789#b-//) ; |
265
|
|
|
|
|
|
|
|
266
|
48
|
100
|
|
|
|
256
|
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
|
1379
|
my ($line,$return_chordpro_fingering) = @_ ; |
273
|
787
|
|
|
|
|
1115
|
my $is_fingering=0 ; |
274
|
787
|
|
|
|
|
1103
|
my $input_line = $line ; |
275
|
787
|
|
|
|
|
1111
|
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
|
|
|
|
|
1203
|
my (@chords,@fingerss) ; |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# THIS ONLY WORKS FOR FRETS <=9 right now |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# is it a fingering notation? |
284
|
|
|
|
|
|
|
|
285
|
787
|
|
|
|
|
1073
|
my $pre = "^.*?\\s*?" ; # the pattern to match just before a chord name |
286
|
787
|
|
|
|
|
1029
|
my $valid = "[A-G]{1}\\S*?" ; # a valid chordname |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# ("chord:") followed by "|x2344x|" or "x2344x" |
289
|
787
|
|
|
|
|
3448
|
while($line =~ /$pre($valid)\:+?\s*?(\|?[xX0-9]{3,7}\|?)/) { |
290
|
38
|
|
|
|
|
93
|
my $cname=$1 ; |
291
|
38
|
|
|
|
|
70
|
my $fingers_this=$2 ; |
292
|
38
|
|
|
|
|
54
|
my $nobar_fingers=$fingers_this ; |
293
|
38
|
|
|
|
|
120
|
$nobar_fingers =~ s/\|//g ; |
294
|
|
|
|
|
|
|
|
295
|
38
|
50
|
33
|
|
|
98
|
if($any_chord_ok || generic_parse_chord($cname)) { |
296
|
38
|
|
|
|
|
70
|
push @chords,$cname ; |
297
|
38
|
|
|
|
|
60
|
push @fingerss,$nobar_fingers ; |
298
|
38
|
|
|
|
|
58
|
$is_fingering=1 ; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
38
|
|
|
|
|
542
|
$line =~ s/.*?$nobar_fingers// ; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# ("chord") followed by "|x2344x|" "x2344x" |
306
|
787
|
|
|
|
|
10760
|
while($line =~ /$pre($valid)\s+?(\|?[xX0-9]{3,7}\|?)/) { |
307
|
32
|
|
|
|
|
78
|
my $cname=$1 ; |
308
|
32
|
|
|
|
|
55
|
my $fingers_this=$2 ; |
309
|
32
|
|
|
|
|
52
|
my $nobar_fingers=$fingers_this ; |
310
|
32
|
|
|
|
|
60
|
$nobar_fingers =~ s/\|//g ; |
311
|
|
|
|
|
|
|
|
312
|
32
|
50
|
33
|
|
|
75
|
if($any_chord_ok || generic_parse_chord($1)) { |
313
|
32
|
|
|
|
|
66
|
push @chords,$cname ; |
314
|
32
|
|
|
|
|
81
|
push @fingerss,$nobar_fingers ; |
315
|
32
|
|
|
|
|
47
|
$is_fingering=1 ; |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
32
|
|
|
|
|
457
|
$line =~ s/.*?$nobar_fingers// ; |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# "(chord) = (fingering)" format |
322
|
787
|
|
|
|
|
3478
|
while($line =~ /$pre($valid)\s*?\=\s*?([xX0123456789]{3,7})/) { |
323
|
384
|
|
|
|
|
885
|
my $cname=$1 ; |
324
|
384
|
|
|
|
|
605
|
my $fingers_this=$2 ; |
325
|
384
|
|
|
|
|
579
|
my $nobar_fingers=$fingers_this ; |
326
|
384
|
|
|
|
|
639
|
$nobar_fingers =~ s/\|//g ; |
327
|
|
|
|
|
|
|
|
328
|
384
|
50
|
33
|
|
|
779
|
if($any_chord_ok || generic_parse_chord($1)) { |
329
|
384
|
|
|
|
|
695
|
push @chords,$cname ; |
330
|
384
|
|
|
|
|
548
|
push @fingerss,$nobar_fingers ; |
331
|
384
|
|
|
|
|
521
|
$is_fingering=1 ; |
332
|
|
|
|
|
|
|
} |
333
|
|
|
|
|
|
|
|
334
|
384
|
|
|
|
|
5039
|
$line =~ s/.*?$nobar_fingers// ; |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
787
|
100
|
|
|
|
1686
|
if($is_fingering) { |
338
|
402
|
100
|
|
|
|
1376
|
return 1 if ! $return_chordpro_fingering ; |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# handle situation where more than one chord is defined on an input text line |
341
|
201
|
|
|
|
|
310
|
my @output_lines ; |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
#push @output_lines, $input_line if 1 ; # only for debugging |
344
|
|
|
|
|
|
|
|
345
|
201
|
|
|
|
|
366
|
foreach my $chord (@chords) { |
346
|
227
|
|
|
|
|
381
|
my $fingers = shift @fingerss ; |
347
|
227
|
|
|
|
|
376
|
my $min_fret=100 ; |
348
|
227
|
|
|
|
|
299
|
my $max_fret=0 ; |
349
|
227
|
|
|
|
|
290
|
my @frets ; |
350
|
|
|
|
|
|
|
|
351
|
227
|
|
|
|
|
803
|
while($fingers =~ s/(.)//) { |
352
|
1359
|
|
|
|
|
2625
|
my $fret=$1 ; |
353
|
1359
|
|
|
|
|
2140
|
push @frets, $fret ; |
354
|
|
|
|
|
|
|
|
355
|
1359
|
100
|
|
|
|
3136
|
if($fret =~ /[0-9]/) { |
356
|
1213
|
100
|
|
|
|
2216
|
$min_fret = $fret if $min_fret > $fret ; |
357
|
1213
|
100
|
|
|
|
3856
|
$max_fret = $fret if $max_fret < $fret ; |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# now convert the requested fingering to chordpro format |
362
|
227
|
|
|
|
|
357
|
my $bf=$min_fret ; |
363
|
|
|
|
|
|
|
|
364
|
227
|
|
|
|
|
517
|
my $chordpro = "{define $chord base-fret $bf frets" ; |
365
|
227
|
100
|
|
|
|
474
|
$bf-- if $bf > 0 ; |
366
|
|
|
|
|
|
|
|
367
|
227
|
|
|
|
|
362
|
foreach my $fret (@frets) { |
368
|
1359
|
|
|
|
|
2050
|
$chordpro = $chordpro . " " ; |
369
|
|
|
|
|
|
|
|
370
|
1359
|
100
|
|
|
|
2899
|
if($fret =~ /[0-9]/) { |
371
|
1213
|
|
|
|
|
1799
|
my $rf = $fret-$bf ; |
372
|
|
|
|
|
|
|
|
373
|
1213
|
|
|
|
|
2218
|
$chordpro .= "$rf" ; |
374
|
|
|
|
|
|
|
} else { |
375
|
146
|
|
|
|
|
255
|
$chordpro .= '-' ; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
227
|
|
|
|
|
321
|
$chordpro .= "}" ; |
380
|
227
|
|
|
|
|
566
|
push @output_lines, $chordpro ; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
201
|
|
|
|
|
586
|
return @output_lines ; |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
|
386
|
385
|
|
|
|
|
875
|
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
|
1066
|
my ( $line ) = @_; |
402
|
601
|
|
|
|
|
1416
|
my $lc_line = lc($line) ; |
403
|
601
|
|
|
|
|
804
|
my $local_debug=0 ; |
404
|
|
|
|
|
|
|
|
405
|
601
|
100
|
|
|
|
1292
|
return 'C' if $line =~ /^\s*\[.+?\]/; # comment |
406
|
595
|
100
|
|
|
|
1122
|
return 'C' if $line =~ /^\s*\#.+?/; # comment |
407
|
592
|
100
|
|
|
|
1217
|
return 'C' if $lc_line =~ /(from|email|e\-mail)\:?.+?@+/ ; # email is treated as a comment |
408
|
590
|
100
|
|
|
|
1217
|
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
|
|
|
|
1146
|
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
|
|
|
|
971
|
return 'f' if decode_fingering($line,0) ; |
413
|
|
|
|
|
|
|
|
414
|
385
|
|
|
|
|
599
|
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
|
|
|
|
|
597
|
my $longest_tablature_string=0 ; |
422
|
385
|
|
|
|
|
582
|
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
|
|
|
|
|
1454
|
while($tmpline =~ s/^(\s*?[A-G|a-g][:\|]+[\-:\|bphxBPHX0-9\/\\\(\)]*?[:\|]+)//) { |
438
|
55
|
50
|
|
|
|
262
|
$longest_tablature_string = length($1) if $longest_tablature_string < length($1) ; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
385
|
100
|
|
|
|
896
|
return 't' if $longest_tablature_string > 8 ; |
442
|
|
|
|
|
|
|
} |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# count number of specific characters to help identify tablature lines |
446
|
330
|
|
|
|
|
784
|
my $n_v = ($line =~ tr/v//) ; |
447
|
330
|
|
|
|
|
631
|
my $n_dash = ($line =~ tr/-//) ; |
448
|
330
|
|
|
|
|
639
|
my $n_equal = ($line =~ tr/=//) ; |
449
|
330
|
|
|
|
|
636
|
my $n_bar = ($line =~ tr/|//) ; |
450
|
330
|
|
|
|
|
670
|
my $n_c_accent = ($line =~ tr/^//) ; |
451
|
330
|
|
|
|
|
649
|
my $n_period = ($line =~ tr/.//) ; |
452
|
330
|
|
|
|
|
627
|
my $n_space = ($line =~ tr/ //) ; |
453
|
330
|
|
|
|
|
649
|
my $n_slash = ($line =~ tr/\///) ; |
454
|
330
|
|
|
|
|
640
|
my $n_underscore = ($line =~ tr/_//) ; |
455
|
330
|
|
|
|
|
590
|
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
|
|
|
|
|
469
|
my $longest_strumming_string=0 ; |
461
|
330
|
|
|
|
|
495
|
my $cntline = $line ; |
462
|
|
|
|
|
|
|
|
463
|
330
|
|
|
|
|
1499
|
while( $cntline =~ s/([\|\/ _]+?)//) { |
464
|
3100
|
100
|
|
|
|
13766
|
$longest_strumming_string = length($1) if $longest_strumming_string < length($1) ; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
330
|
50
|
|
|
|
661
|
return 't' if ($longest_strumming_string >= 6) ; |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
# Lyrics or Chords heuristic. |
472
|
330
|
|
|
|
|
1489
|
my @words = split ( /\s+/, $line ); |
473
|
|
|
|
|
|
|
|
474
|
330
|
|
|
|
|
759
|
my $n_tot_chars = length($line) ; |
475
|
330
|
|
|
|
|
1500
|
$line =~ s/\s+//g ; |
476
|
330
|
|
|
|
|
1069
|
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
|
|
|
1295
|
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
|
|
|
|
1075
|
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
|
|
|
|
526
|
return 't' if (($n_bar + $n_slash + $n_underscore)/$n_nonblank_chars >= 0.5) ; # mostly characters used in strumming tablature |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
|
484
|
216
|
|
|
|
|
313
|
my $n_chords=0 ; |
485
|
216
|
|
|
|
|
302
|
my $n_words=0 ; |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
#print("CL:") ; # JJW, uncomment for debugging |
488
|
|
|
|
|
|
|
|
489
|
216
|
|
|
|
|
423
|
foreach (@words) { |
490
|
1157
|
100
|
|
|
|
4354
|
if (length $_ > 0) { |
491
|
1083
|
|
|
|
|
1455
|
$n_words++ ; |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
|
494
|
1083
|
100
|
|
|
|
2321
|
my $is_chord = ChordPro::Chords::parse_chord($_) ? 1 : 0 ; |
495
|
1083
|
100
|
|
|
|
2610
|
if(! $is_chord) { |
496
|
890
|
100
|
|
|
|
1659
|
if(generic_parse_chord($_)) { |
497
|
9
|
50
|
|
|
|
28
|
print STDERR "$_ detected by generic, not internal parse_chord\n" if $local_debug ; |
498
|
9
|
|
|
|
|
16
|
$is_chord=1 ; |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
1083
|
100
|
|
|
|
2075
|
$n_chords++ if $is_chord ; |
503
|
1083
|
50
|
|
|
|
2243
|
print STDERR " ($is_chord:$_)" if $local_debug ; |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
#print(" \'$is_chord:$_\'") ; # JJW, uncomment for debugging |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
} |
508
|
216
|
50
|
|
|
|
466
|
print STDERR "\n" if $local_debug ; |
509
|
|
|
|
|
|
|
|
510
|
216
|
50
|
|
|
|
415
|
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
|
|
|
|
512
|
my $type = $n_chords/$n_words > 0.4 ? 'c' : 'l' ; |
513
|
|
|
|
|
|
|
|
514
|
216
|
100
|
|
|
|
495
|
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
|
|
|
|
|
954
|
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
|
121
|
my $line = $_[0] ; |
531
|
|
|
|
|
|
|
# remove [] from original comment |
532
|
69
|
|
|
|
|
172
|
$line =~ s/\[// ; |
533
|
69
|
|
|
|
|
133
|
$line =~ s/\]// ; |
534
|
69
|
50
|
|
|
|
152
|
return '' if $line eq '' ; |
535
|
69
|
|
|
|
|
225
|
return "{comment:" . $line . "}" ; |
536
|
|
|
|
|
|
|
} |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
# Process the lines via the map. |
539
|
|
|
|
|
|
|
my $infer_titles; |
540
|
|
|
|
|
|
|
sub maplines { |
541
|
19
|
|
|
19
|
0
|
93
|
my ( $map, $lines ) = @_; |
542
|
19
|
|
|
|
|
47
|
my @out; |
543
|
19
|
|
|
|
|
46
|
my $local_debug=0 ; |
544
|
19
|
|
66
|
|
|
75
|
$infer_titles //= $::config->{a2crd}->{'infer-titles'}; |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
# Preamble. |
547
|
|
|
|
|
|
|
# Pass empty lines. |
548
|
|
|
|
|
|
|
|
549
|
19
|
50
|
|
|
|
182
|
print STDERR "====== _C =====\n" if $local_debug ; |
550
|
19
|
50
|
|
|
|
70
|
print STDERR "MAP: \'$map\' \n" if $local_debug ; |
551
|
|
|
|
|
|
|
|
552
|
19
|
|
|
|
|
128
|
while ( $map =~ s/^([_C])// ) { |
553
|
13
|
50
|
|
|
|
34
|
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
|
|
|
|
46
|
my $pre = ($1 eq "C" ? "{comment:" : "" ) ; |
557
|
13
|
100
|
|
|
|
36
|
my $post = ($1 eq "C" ? "}" : "" ) ; |
558
|
13
|
|
|
|
|
83
|
push( @out, $pre . shift( @$lines ) . $post ); |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
19
|
50
|
|
|
|
64
|
print STDERR "====== infer title =====\n" if $local_debug ; |
562
|
|
|
|
|
|
|
# Infer title/subtitle. |
563
|
19
|
100
|
66
|
|
|
61
|
if ( $infer_titles && $map =~ s/^l// ) { |
564
|
18
|
|
|
|
|
417
|
push( @out, "{title: " . shift( @$lines ) . "}"); |
565
|
18
|
100
|
|
|
|
121
|
if ( $map =~ s/^l// ) { |
566
|
8
|
|
|
|
|
43
|
push( @out, "{subtitle: " . shift( @$lines ) . "}"); |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
|
570
|
19
|
50
|
|
|
|
108
|
print STDERR "====== UNTIL chords or tablature =====\n" if $local_debug ; |
571
|
|
|
|
|
|
|
# Pass lines until we have chords or tablature |
572
|
|
|
|
|
|
|
|
573
|
19
|
|
|
|
|
107
|
while ($map =~ /^(.)(.)(.)/) { |
574
|
331
|
50
|
|
|
|
610
|
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
|
|
|
|
546
|
print STDERR "$1 == @{$lines}[0]\n" if $local_debug ; |
|
0
|
|
|
|
|
0
|
|
585
|
|
|
|
|
|
|
|
586
|
331
|
100
|
100
|
|
|
845
|
last if($1 eq "c" && $2 eq "l") ; |
587
|
326
|
100
|
|
|
|
638
|
last if($2 eq "t" ) ; |
588
|
|
|
|
|
|
|
|
589
|
322
|
100
|
100
|
|
|
1268
|
if(($1 eq "c" || $1 eq "l") && $3 eq "t") { |
|
|
|
100
|
|
|
|
|
590
|
6
|
|
|
|
|
40
|
push @out, format_comment_line(shift(@$lines)) ; |
591
|
6
|
|
|
|
|
53
|
$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
|
|
|
1183
|
if ( $1 eq "l" or $1 eq "C") { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
597
|
59
|
|
|
|
|
136
|
push @out, format_comment_line(shift(@$lines)) ; |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
elsif ( $1 eq "f" ) { |
600
|
191
|
|
|
|
|
455
|
foreach my $fchart (decode_fingering(shift( @$lines ),1) ) { |
601
|
217
|
|
|
|
|
397
|
push( @out, $fchart); |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
elsif ( $1 eq "{" ) { |
605
|
8
|
|
|
|
|
20
|
my $line = shift @$lines ; |
606
|
8
|
|
|
|
|
16
|
push( @out, $line); |
607
|
|
|
|
|
|
|
|
608
|
8
|
100
|
|
|
|
24
|
if($line =~ /{sot}/) { |
609
|
|
|
|
|
|
|
# output all subsequent lines until {eot} is found |
610
|
1
|
|
|
|
|
3
|
while(1) { |
611
|
8
|
|
|
|
|
14
|
$line = shift @$lines ; |
612
|
8
|
50
|
|
|
|
14
|
die "Malformed input, {sot} has no matching {eot}" if ! $line ; |
613
|
8
|
|
|
|
|
21
|
$map = s/.// ; |
614
|
8
|
|
|
|
|
12
|
push( @out, $line); |
615
|
8
|
100
|
|
|
|
21
|
last if $line =~ /{eot}/ ; |
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
else { |
621
|
58
|
|
|
|
|
116
|
push( @out, shift( @$lines ) ); |
622
|
|
|
|
|
|
|
} |
623
|
316
|
|
|
|
|
1281
|
$map =~ s/.// ; |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
|
626
|
19
|
50
|
|
|
|
61
|
push @out, "====== FINAL LOOP =====" if $local_debug ; |
627
|
|
|
|
|
|
|
# Process the lines using the map. |
628
|
19
|
|
|
|
|
60
|
while ( $map ) { |
629
|
|
|
|
|
|
|
# warn($map); |
630
|
187
|
50
|
|
|
|
354
|
push @out, "FL $map" if $local_debug ; |
631
|
187
|
|
|
|
|
348
|
$map =~ /(.)/ ; |
632
|
187
|
50
|
|
|
|
312
|
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
|
|
|
|
419
|
if ( $map =~ s/^f// ) { |
636
|
10
|
|
|
|
|
32
|
foreach my $fchart (decode_fingering(shift( @$lines ),1) ) { |
637
|
10
|
|
|
|
|
33
|
push( @out, $fchart); |
638
|
|
|
|
|
|
|
} |
639
|
10
|
|
|
|
|
26
|
next ; |
640
|
|
|
|
|
|
|
} |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
# Blank line - output the blank line and continue |
643
|
177
|
100
|
|
|
|
419
|
if ( $map =~ s/^_// ) { |
644
|
78
|
|
|
|
|
160
|
push( @out, ''); |
645
|
78
|
|
|
|
|
125
|
shift(@$lines); |
646
|
78
|
|
|
|
|
161
|
next ; |
647
|
|
|
|
|
|
|
} |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
# A comment line, output and continue |
650
|
99
|
100
|
|
|
|
225
|
if ( $map =~ s/^C// ) { |
651
|
4
|
|
|
|
|
31
|
push @out, format_comment_line(shift(@$lines)) ; |
652
|
4
|
|
|
|
|
18
|
next ; |
653
|
|
|
|
|
|
|
} |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
# Tablature |
656
|
95
|
|
|
|
|
132
|
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
|
|
|
|
232
|
if ( $map =~ s/^[cl]t/t/ ) { |
661
|
17
|
50
|
|
|
|
53
|
if(! $in_tablature) { |
662
|
17
|
|
|
|
|
40
|
push( @out, "{sot}") ; |
663
|
17
|
|
|
|
|
31
|
$in_tablature=1 ; |
664
|
|
|
|
|
|
|
} |
665
|
17
|
|
|
|
|
36
|
push( @out, shift(@$lines)); |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
|
668
|
95
|
|
|
|
|
264
|
while( $map =~ s/^t// ) { |
669
|
155
|
100
|
|
|
|
298
|
if(! $in_tablature) { |
670
|
12
|
|
|
|
|
37
|
push( @out, "{sot}") ; |
671
|
12
|
|
|
|
|
17
|
$in_tablature=1 ; |
672
|
|
|
|
|
|
|
} |
673
|
155
|
|
|
|
|
509
|
push( @out, shift(@$lines)); |
674
|
|
|
|
|
|
|
# and Fall through. |
675
|
|
|
|
|
|
|
} |
676
|
|
|
|
|
|
|
|
677
|
95
|
100
|
|
|
|
232
|
if($in_tablature) { |
678
|
|
|
|
|
|
|
# Text line OR chord line with following blank line or EOF -- make part of tablature |
679
|
29
|
100
|
|
|
|
122
|
if ( $map =~ s/^[cl](_|$)// ) { |
680
|
9
|
|
|
|
|
24
|
push( @out, shift(@$lines)); |
681
|
9
|
|
|
|
|
20
|
push( @out, ''); |
682
|
9
|
|
|
|
|
13
|
shift(@$lines); |
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
|
685
|
29
|
|
|
|
|
64
|
push( @out, "{eot}") ; |
686
|
29
|
|
|
|
|
42
|
$in_tablature=0 ; |
687
|
29
|
|
|
|
|
67
|
next ; |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
# Blank line preceding lyrics: pass. |
691
|
66
|
50
|
|
|
|
177
|
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
|
|
|
|
340
|
if ( $map =~ s/^cl// ) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
698
|
39
|
|
|
|
|
159
|
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
|
|
|
|
7
|
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
|
|
|
|
|
68
|
push( @out, shift( @$lines ) ); |
734
|
|
|
|
|
|
|
} |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
# Lone chords. |
737
|
|
|
|
|
|
|
elsif ( $map =~ s/^c// ) { |
738
|
4
|
|
|
|
|
12
|
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
|
|
|
|
584
|
return wantarray ? @out : \@out; |
753
|
|
|
|
|
|
|
} |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
# Combine two lines (chords + lyrics) into lyrics with [chords]. |
756
|
|
|
|
|
|
|
sub combine { |
757
|
43
|
|
|
43
|
0
|
125
|
my ( $l1, $l2 ) = @_; |
758
|
43
|
|
|
|
|
79
|
my $res = ""; |
759
|
43
|
|
|
|
|
169
|
while ( $l1 =~ /^(\s*)(\S+)(.*)/ ) { |
760
|
130
|
|
|
|
|
682
|
$res .= join( '', |
761
|
|
|
|
|
|
|
substr( $l2, 0, length($1), '' ), |
762
|
|
|
|
|
|
|
'[' . $2 . ']', |
763
|
|
|
|
|
|
|
substr( $l2, 0, length($2), '' ) ); |
764
|
130
|
|
|
|
|
477
|
$l1 = $3; |
765
|
|
|
|
|
|
|
} |
766
|
43
|
|
|
|
|
220
|
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
|
|
13
|
use Getopt::Long 2.13; |
|
1
|
|
|
|
|
17
|
|
|
1
|
|
|
|
|
35
|
|
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; |