| 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; |