File Coverage

lib/ChordPro/Config.pm
Criterion Covered Total %
statement 386 545 70.8
branch 123 230 53.4
condition 65 142 45.7
subroutine 41 48 85.4
pod 0 23 0.0
total 615 988 62.2


line stmt bran cond sub pod time code
1             #! perl
2              
3             package main;
4              
5             our $options;
6             our $config;
7              
8             package ChordPro::Config;
9              
10 81     81   1670 use v5.26;
  81         311  
11 81     81   599 use utf8;
  81         250  
  81         792  
12 81     81   2255 use Carp;
  81         211  
  81         5040  
13 81     81   632 use feature qw( signatures );
  81         248  
  81         14749  
14 81     81   803 no warnings "experimental::signatures";
  81         364  
  81         3771  
15              
16 81     81   671 use App::Packager;
  81         317  
  81         615  
17 81     81   54130 use ChordPro;
  81         302  
  81         3508  
18 81     81   583 use ChordPro::Utils;
  81         211  
  81         8972  
19 81     81   1579 use File::LoadLines;
  81         25877  
  81         3759  
20 81     81   2770 use File::Spec;
  81         217  
  81         1909  
21 81     81   69910 use JSON::PP ();
  81         1289785  
  81         3207  
22 81     81   659 use Scalar::Util qw(reftype);
  81         201  
  81         5418  
23 81     81   584 use List::Util qw(any);
  81         189  
  81         5334  
24 81     81   52507 use Hash::Util;
  81         272569  
  81         753  
25              
26             =head1 NAME
27              
28             ChordPro::Config - Configurator.
29              
30             =head1 DESCRIPTION
31              
32             This module first establishes a well-defined (builtin) configuration.
33              
34             Then it processes the config files specified by the environment and
35             adds the information to the global $config hash.
36              
37             The configurations files are 'relaxed' JSON files. This means that
38             they may contain comments and trailing comma's.
39              
40             This module can be run standalone and will print the default config.
41              
42             =encoding utf8
43              
44             =cut
45              
46             #sub hmerge($$;$);
47             #sub clone($);
48             #sub default_config();
49              
50 199     199 0 1110 sub configurator ( $opts = undef ) {
  199         576  
  199         349  
51 199         1809 my $pp = JSON::PP->new->relaxed;
52              
53             # Test programs call configurator without options.
54             # Prepare a minimal config.
55 199 100       12387 unless ( $opts ) {
56 80         260 my $cfg = $pp->decode( default_config() );
57 80         10553592 $config = $cfg;
58 80         467 $options = { verbose => 0 };
59 80         644 process_config( $cfg, "" );
60 80         587 $cfg->{settings}->{lineinfo} = 0;
61 80         1878 return $cfg;
62             }
63 119 100       632 if ( keys(%$opts) ) {
64 9   50     28 $options = { %{$options//{}}, %$opts };
  9         93  
65             }
66              
67 119         297 my @cfg;
68 119   100     770 my $verbose = $options->{verbose} //= 0;
69              
70             # Load defaults.
71 119 50       645 warn("Reading: \n") if $verbose > 1;
72 119         590 my $cfg = $pp->decode( default_config() );
73              
74             # Default first.
75 119         15819141 @cfg = prep_configs( $cfg, "" );
76             # Bubble default config to be the first.
77 119 50       749 unshift( @cfg, pop(@cfg) ) if @cfg > 1;
78              
79             # Collect other config files.
80             my $add_config = sub {
81 11     11   35 my $fn = shift;
82 11         46 $cfg = get_config( $fn );
83 11         88 push( @cfg, prep_configs( $cfg, $fn ) );
84 119         1075 };
85              
86 119         502 foreach my $c ( qw( sysconfig userconfig config ) ) {
87 357 100       1774 next if $options->{"no$c"};
88 9 100       66 if ( ref($options->{$c}) eq 'ARRAY' ) {
89 2         5 $add_config->($_) foreach @{ $options->{$c} };
  2         12  
90             }
91             else {
92 7 50       36 warn("Adding config for $c\n") if $verbose;
93 7         45 $add_config->( $options->{$c} );
94             }
95             }
96              
97             # Now we have a list of all config files. Weed out dups.
98 119         813 for ( my $a = 0; $a < @cfg; $a++ ) {
99 249 50 66     1739 if ( $a && $cfg[$a]->{_src} eq $cfg[$a-1]->{_src} ) {
100 0         0 splice( @cfg, $a, 1 );
101 0         0 redo;
102             }
103 249 50       951 print STDERR ("Config[$a]: ", $cfg[$a]->{_src}, "\n" )
104             if $verbose;
105             }
106              
107 119         429 $cfg = shift(@cfg);
108 119 50       492 warn("Process: $cfg->{_src}\n") if $verbose > 1;
109              
110             # Presets.
111 119 50       699 if ( $options->{reference} ) {
112 0         0 $cfg->{user}->{name} = "chordpro";
113 0         0 $cfg->{user}->{fullname} = ::runtimeinfo("short");
114             }
115             else {
116             $cfg->{user}->{name} =
117             lc( $ENV{USER} || $ENV{LOGNAME}
118 119   50     57671 || getlogin() || getpwuid($<) || "chordpro" );
119 119   50     770 $cfg->{user}->{fullname} = eval { (getpwuid($<))[6] } || "";
120             }
121              
122             # Add some extra entries to prevent warnings.
123 119         897 for ( qw(title subtitle footer) ) {
124 357 100       1525 next if exists($cfg->{pdf}->{formats}->{first}->{$_});
125 238         920 $cfg->{pdf}->{formats}->{first}->{$_} = "";
126             }
127              
128             my $backend_configurator =
129 119         2240 UNIVERSAL::can( $options->{backend}, "configurator" );
130              
131             # Apply config files
132 119         554 foreach my $new ( @cfg ) {
133 130         537 my $file = $new->{_src}; # for diagnostics
134             # Handle obsolete keys.
135 130 50       665 if ( exists $new->{pdf}->{diagramscolumn} ) {
136 0   0     0 $new->{pdf}->{diagrams}->{show} //= "right";
137 0         0 delete $new->{pdf}->{diagramscolumn};
138 0         0 warn("$file: pdf.diagramscolumn is obsolete, use pdf.diagrams.show instead\n");
139             }
140 130 50       950 if ( exists $new->{pdf}->{formats}->{default}->{'toc-title'} ) {
141 0   0     0 $new->{toc}->{title} //= $new->{pdf}->{formats}->{default}->{'toc-title'};
142 0         0 delete $new->{pdf}->{formats}->{default}->{'toc-title'};
143 0         0 warn("$file: pdf.formats.default.toc-title is obsolete, use toc.title instead\n");
144             }
145              
146             # Process.
147 130         435 local $::config = $cfg;
148 130         758 process_config( $new, $file );
149             # Merge final.
150 130         756 $cfg = hmerge( $cfg, $new );
151             }
152              
153             # Handle defines from the command line.
154 119         1134 $cfg = hmerge( $cfg, prp2cfg( $options->{define}, $cfg ) );
155              
156             # Sanitize added extra entries.
157 119         934 for ( qw(title subtitle footer) ) {
158             delete($cfg->{pdf}->{formats}->{first}->{$_})
159 357 100 50     2338 if ($cfg->{pdf}->{formats}->{first}->{$_} // 1) eq "";
160 357         861 for my $class ( qw(title first default) ) {
161 1071         2307 my $t = $cfg->{pdf}->{formats}->{$class}->{$_};
162 1071 100       2108 next unless $t;
163 833 50       1770 die("Config error in pdf.formats.$class.$_: not an array\n")
164             unless ref($t) eq 'ARRAY';
165 833 50       2222 if ( ref($t->[0]) ne 'ARRAY' ) {
166 833         1404 $t = [ $t ];
167             }
168 833         1258 my $tt = $_;
169 833         1415 for ( @$t) {
170 833 50 33     3559 die("Config error in pdf.formats.$class.$tt: ",
171             scalar(@$_), " fields instead of 3\n")
172             if @$_ && @$_ != 3;
173             }
174             }
175             }
176              
177 119 50       970 if ( $cfg->{pdf}->{fontdir} ) {
178 119         351 my @a;
179 119 50       647 if ( ref($cfg->{pdf}->{fontdir}) eq 'ARRAY' ) {
180 119         297 @a = @{ $cfg->{pdf}->{fontdir} };
  119         485  
181             }
182             else {
183 0         0 @a = ( $cfg->{pdf}->{fontdir} );
184             }
185 119         419 $cfg->{pdf}->{fontdir} = [];
186 119 50       1167 my $split = $^O =~ /^MS*/ ? qr(;) : qr(:);
187 119         640 foreach ( @a ) {
188 0         0 push( @{ $cfg->{pdf}->{fontdir} },
189 0         0 map { expand_tilde($_) } split( $split, $_ ) );
  0         0  
190             }
191             }
192             else {
193 0         0 $cfg->{pdf}->{fontdir} = [];
194             }
195              
196 119         323 my @allfonts = keys(%{$cfg->{pdf}->{fonts}});
  119         1276  
197 119         515 for my $ff ( @allfonts ) {
198 1190 50 66     5419 unless ( $cfg->{pdf}->{fonts}->{$ff}->{name}
      33        
199             || $cfg->{pdf}->{fonts}->{$ff}->{description}
200             || $cfg->{pdf}->{fonts}->{$ff}->{file} ) {
201 0         0 delete( $cfg->{pdf}->{fonts}->{$ff} );
202 0         0 next;
203             }
204 1190   50     4697 $cfg->{pdf}->{fonts}->{$ff}->{color} //= "foreground";
205 1190   100     4246 $cfg->{pdf}->{fonts}->{$ff}->{background} //= "background";
206 1190         2053 for ( qw(name file description size) ) {
207             delete( $cfg->{pdf}->{fonts}->{$ff}->{$_} )
208 4760 100       10862 unless defined( $cfg->{pdf}->{fonts}->{$ff}->{$_} );
209             }
210             }
211              
212 119 50       1690 if ( defined $options->{diagrams} ) {
    50          
    50          
213             warn( "Invalid value for diagrams: ",
214             $options->{diagrams}, "\n" )
215 0 0       0 unless $options->{diagrams} =~ /^(all|none|user)$/i;
216 0         0 $cfg->{diagrams}->{show} = lc $options->{'diagrams'};
217             }
218             elsif ( defined $options->{'user-chord-grids'} ) {
219             $cfg->{diagrams}->{show} =
220 0 0       0 $options->{'user-chord-grids'} ? "user" : 0;
221             }
222             elsif ( defined $options->{'chord-grids'} ) {
223             $cfg->{diagrams}->{show} =
224 0 0       0 $options->{'chord-grids'} ? "all" : 0;
225             }
226              
227 119         455 for ( qw( transpose transcode decapo lyrics-only strict ) ) {
228 595 100       1634 next unless defined $options->{$_};
229 18         97 $cfg->{settings}->{$_} = $options->{$_};
230             }
231              
232 119         416 for ( "front-matter", "back-matter" ) {
233 238 100       832 next unless defined $options->{$_};
234 6         20 $cfg->{pdf}->{$_} = $options->{$_};
235             }
236              
237 119 50       508 if ( defined $options->{'chord-grids-sorted'} ) {
238 0         0 $cfg->{diagrams}->{sorted} = $options->{'chord-grids-sorted'};
239             }
240              
241             # For convenience...
242 119         818 bless( $cfg, __PACKAGE__ );
243              
244 119 50       464 return $cfg if $options->{'cfg-print'};
245              
246             # Backend specific configs.
247 119 100       500 $backend_configurator->($cfg) if $backend_configurator;
248              
249             # Locking the hash is mainly for development.
250 119         756 $cfg->lock;
251              
252 119 50       239440 if ( $options->{verbose} > 1 ) {
253 0   0     0 my $cp = ChordPro::Chords::get_parser() // "";
254 0         0 warn("Parsers:\n");
255 0         0 while ( my ($k, $v) = each %{ChordPro::Chords::Parser->parsers} ) {
  0         0  
256 0 0       0 warn( " $k",
257             $v eq $cp ? " (active)": "",
258             "\n");
259             }
260             }
261              
262 119         46026 return $cfg;
263             }
264              
265             # Get the decoded contents of a single config file.
266 132     132 0 352 sub get_config ( $file ) {
  132         389  
  132         276  
267 132 50       556 Carp::confess("FATAL: Undefined config") unless defined $file;
268 132         491 my $verbose = $options->{verbose};
269 132 50       530 warn("Reading: $file\n") if $verbose > 1;
270 132         689 $file = expand_tilde($file);
271              
272 132 50       1281 if ( $file =~ /\.json$/i ) {
    0          
273 132 50       11016 if ( open( my $fd, "<:raw", $file ) ) {
274 132         1965 my $pp = JSON::PP->new->relaxed;
275 132         10470 my $new = $pp->decode( loadlines( $fd, { split => 0 } ) );
276 132         32458228 precheck( $new, $file );
277 132         4889 close($fd);
278 132         3341 return $new;
279             }
280             else {
281 0         0 die("Cannot open config $file [$!]\n");
282             }
283             }
284             elsif ( $file =~ /\.prp$/i ) {
285 0 0       0 if ( -e -f -r $file ) {
286 0         0 require ChordPro::Config::Properties;
287 0         0 my $cfg = new Data::Properties;
288 0         0 $cfg->parse_file($file);
289 0         0 return $cfg->data;
290             }
291             else {
292 0         0 die("Cannot open config $file [$!]\n");
293             }
294             }
295             else {
296 0         0 Carp::confess("Unrecognized config type: $file\n");
297             }
298             }
299              
300             # Check config for includes, and prepend them.
301 249     249 0 731 sub prep_configs ( $cfg, $src ) {
  249         639  
  249         669  
  249         521  
302 249         1040 $cfg->{_src} = $src;
303              
304 249         741 my @res;
305              
306             # If there are includes, add them first.
307 249         8238 my ( $vol, $dir, undef ) = File::Spec->splitpath($cfg->{_src});
308 249         818 foreach my $c ( @{ $cfg->{include} } ) {
  249         1448  
309             # Check for resource names.
310 119 50 0     680 if ( $c !~ m;[/.]; ) {
    0          
311 119         954 $c = ::rsc_or_file( $c, "config" );
312             }
313             elsif ( $dir ne ""
314             && !File::Spec->file_name_is_absolute($c) ) {
315             # Prepend dir of the caller, if needed.
316 0         0 $c = File::Spec->catpath( $vol, $dir, $c );
317             }
318 119         921 my $cfg = get_config($c);
319             # Recurse.
320 119         914 push( @res, prep_configs( $cfg, $c ) );
321             }
322              
323             # Push this and return.
324 249         733 push( @res, $cfg );
325 249         1125 return @res;
326             }
327              
328 210     210 0 618 sub process_config ( $cfg, $file ) {
  210         624  
  210         509  
  210         568  
329 210         742 my $verbose = $options->{verbose};
330              
331 210 50       1125 warn("Process: $file\n") if $verbose > 1;
332              
333 210 100       1139 if ( $cfg->{tuning} ) {
334 199         1783 my $res =
335             ChordPro::Chords::set_tuning( $cfg );
336 199 50       799 warn( "Invalid tuning in config: ", $res, "\n" ) if $res;
337 199         857 $cfg->{_tuning} = $cfg->{tuning};
338 199         699 $cfg->{tuning} = [];
339             }
340              
341 210         1208 ChordPro::Chords::reset_parser;
342 210         1536 ChordPro::Chords::Parser->reset_parsers;
343 210         1231 local $::config = hmerge( $::config, $cfg );
344 210 100       1707 if ( $cfg->{chords} ) {
345 199         2109 ChordPro::Chords::push_parser($cfg->{notes}->{system});
346 199         700 my $c = $cfg->{chords};
347 199 50 66     1558 if ( @$c && $c->[0] eq "append" ) {
348 0         0 shift(@$c);
349             }
350 199         898 foreach ( @$c ) {
351 49742         104207 my $res =
352             ChordPro::Chords::add_config_chord($_);
353             warn( "Invalid chord in config: ",
354 49742 50       127586 $_->{name}, ": ", $res, "\n" ) if $res;
355             }
356 199 50       1321 if ( $verbose > 1 ) {
357 0         0 warn( "Processed ", scalar(@$c), " chord entries\n");
358 0         0 warn( "Totals: ",
359             ChordPro::Chords::chord_stats(), "\n" );
360             }
361 199         1024 $cfg->{_chords} = delete $cfg->{chords};
362 199         1056 ChordPro::Chords::pop_parser();
363             }
364             }
365              
366 0     0 0 0 sub config_final ( $delta ) {
  0         0  
  0         0  
367 0         0 $options->{'cfg-print'} = 1;
368 0         0 my $cfg = configurator($options);
369              
370 0 0       0 if ( $delta ) {
371 0         0 my $pp = JSON::PP->new->relaxed;
372 0         0 my $def = $pp->decode( default_config() );
373 0         0 $cfg->reduce($def);
374             }
375 0         0 $cfg->unlock;
376 0         0 $cfg->{tuning} = delete $cfg->{_tuning};
377 0 0 0     0 delete($cfg->{tuning}) if $delta && !defined($cfg->{tuning});
378 0         0 $cfg->{chords} = delete $cfg->{_chords};
379 0         0 delete $cfg->{chords};
380 0         0 delete $cfg->{_src};
381 0         0 $cfg->lock;
382              
383 0 0       0 if ( $ENV{CHORDPRO_CFGPROPS} ) {
384 0         0 cfg2props($cfg);
385             }
386             else {
387 0         0 my $pp = JSON::PP->new->canonical->indent(4)->pretty;
388 0         0 $pp->encode({%$cfg});
389             }
390             }
391              
392 0     0 0 0 sub config_default () {
  0         0  
393 0 0       0 if ( $ENV{CHORDPRO_CFGPROPS} ) {
394 0         0 my $pp = JSON::PP->new->relaxed;
395 0         0 my $cfg = $pp->decode( default_config() );
396 0         0 cfg2props($cfg);
397             }
398             else {
399 0         0 default_config();
400             }
401             }
402              
403             # Config in properties format.
404              
405 0     0 0 0 sub cfg2props ( $o, $path ) {
  0         0  
  0         0  
  0         0  
406 0   0     0 $path //= "";
407 0         0 my $ret = "";
408 0 0       0 if ( !defined $o ) {
    0          
    0          
    0          
409 0         0 $ret .= "$path: undef\n";
410             }
411             elsif ( UNIVERSAL::isa( $o, 'HASH' ) ) {
412 0 0       0 $path .= "." unless $path eq "";
413 0         0 for ( sort keys %$o ) {
414 0         0 $ret .= cfg2props( $o->{$_}, $path . $_ );
415             }
416             }
417             elsif ( UNIVERSAL::isa( $o, 'ARRAY' ) ) {
418 0 0       0 $path .= "." unless $path eq "";
419 0         0 for ( my $i = 0; $i < @$o; $i++ ) {
420 0         0 $ret .= cfg2props( $o->[$i], $path . "$i" );
421             }
422             }
423             elsif ( $o =~ /^\d+$/ ) {
424 0         0 $ret .= "$path: $o\n";
425             }
426             else {
427 0         0 $o =~ s/\\/\\\\/g;
428 0         0 $o =~ s/"/\\"/g;
429 0         0 $o =~ s/\n/\\n/;
430 0         0 $o =~ s/\t/\\t/;
431 0         0 $o =~ s/([^\x00-\xff])/sprintf("\\x{%x}", ord($1))/ge;
  0         0  
432 0         0 $ret .= "$path: \"$o\"\n";
433             }
434              
435 0         0 return $ret;
436             }
437              
438             # Locking/unlocking. Locking the hash is mainly for development, to
439             # trap accidental modifications and typos.
440              
441 303     303 0 591 sub lock ( $self ) {
  303         587  
  303         548  
442 303         1437 Hash::Util::lock_hashref_recurse($self);
443             }
444              
445 224     224 0 507 sub unlock ( $self ) {
  224         490  
  224         390  
446 224         1133 Hash::Util::unlock_hashref_recurse($self);
447             }
448              
449 16     16 0 32 sub is_locked ( $self ) {
  16         29  
  16         31  
450 16         68 Hash::Util::hashref_locked($self);
451             }
452              
453             # Augment / Reduce.
454              
455 15     15 0 1999 sub augment ( $self, $hash ) {
  15         32  
  15         38  
  15         29  
456              
457 15         63 my $locked = $self->is_locked;
458 15 100       166 $self->unlock if $locked;
459              
460 15         23709 $self->_augment( $hash, "" );
461              
462 15 100       88 $self->lock if $locked;
463              
464 15         25683 $self;
465             }
466              
467              
468 43     43   79 sub _augment ( $self, $hash, $path ) {
  43         74  
  43         75  
  43         72  
  43         60  
469              
470 43         146 for my $key ( keys(%$hash) ) {
471              
472             warn("Config augment error: unknown item $path$key\n")
473 45 0 33     180 unless exists $self->{$key}
      33        
      0        
474             || $path =~ /^pdf\.(?:info|fonts|fontconfig)\./
475             || $path =~ /^meta\./
476             || $key =~ /^_/;
477              
478             # Hash -> Hash.
479             # Hash -> Array.
480 45 100 66     249 if ( ref($hash->{$key}) eq 'HASH' ) {
    100          
481 28 50       91 if ( ref($self->{$key}) eq 'HASH' ) {
    0          
482              
483             # Hashes. Recurse.
484 28         175 _augment( $self->{$key}, $hash->{$key}, "$path$key." );
485             }
486             elsif ( ref($self->{$key}) eq 'ARRAY' ) {
487              
488             # Hash -> Array.
489             # Update single array element using a hash index.
490 0         0 foreach my $ix ( keys(%{$hash->{$key}}) ) {
  0         0  
491 0 0       0 die unless $ix =~ /^\d+$/;
492 0         0 $self->{$key}->[$ix] = $hash->{$key}->{$ix};
493             }
494             }
495             else {
496             # Overwrite.
497 0         0 $self->{$key} = $hash->{$key};
498             }
499             }
500              
501             # Array -> Array.
502             elsif ( ref($hash->{$key}) eq 'ARRAY'
503             and ref($self->{$key}) eq 'ARRAY' ) {
504              
505             # Arrays. Overwrite or append.
506 2 50       6 if ( @{$hash->{$key}} ) {
  2         6  
507 2         3 my @v = @{ $hash->{$key} };
  2         6  
508 2 50       11 if ( $v[0] eq "append" ) {
    100          
509 0         0 shift(@v);
510             # Append the rest.
511 0         0 push( @{ $self->{$key} }, @v );
  0         0  
512             }
513             elsif ( $v[0] eq "prepend" ) {
514 1         3 shift(@v);
515             # Prepend the rest.
516 1         6 unshift( @{ $self->{$key} }, @v );
  1         8  
517             }
518             else {
519             # Overwrite.
520 1         6 $self->{$key} = $hash->{$key};
521             }
522             }
523             else {
524             # Overwrite.
525 0         0 $self->{$key} = $hash->{$key};
526             }
527             }
528              
529             else {
530             # Overwrite.
531 15         57 $self->{$key} = $hash->{$key};
532             }
533             }
534              
535 43         93 $self;
536             }
537              
538 81     81   480424 use constant DEBUG => 0;
  81         225  
  81         223605  
539              
540 1     1 0 1587 sub reduce ( $self, $hash ) {
  1         3  
  1         2  
  1         2  
541              
542 1         4 my $locked = $self->is_locked;
543              
544 1         42 warn("O: ", qd($hash,1), "\n") if DEBUG;
545 1         3 warn("N: ", qd($self,1), "\n") if DEBUG;
546 1         5 my $state = _reduce( $self, $hash, "" );
547              
548 1 50       113 $self->lock if $locked;
549              
550 1         3 warn("== ", qd($self,1), "\n") if DEBUG;
551 1         5 return $self;
552             }
553              
554 68     68   86 sub _ref ( $self ) {
  68         85  
  68         86  
555 68   66     368 reftype($self) // ref($self);
556             }
557              
558 6     6   11 sub _reduce ( $self, $orig, $path ) {
  6         7  
  6         16  
  6         13  
  6         7  
559              
560 6         10 my $state;
561              
562 6 100 66     10 if ( _ref($self) eq 'HASH' && _ref($orig) eq 'HASH' ) {
563              
564 3         9 warn("D: ", qd($self,1), "\n") if DEBUG && !%$orig;
565 3 50       8 return 'D' unless %$orig;
566              
567 3         13 my %hh = map { $_ => 1 } keys(%$self), keys(%$orig);
  12         27  
568 3         19 for my $key ( sort keys(%hh) ) {
569              
570             warn("Config reduce error: unknown item $path$key\n")
571 6 50 33     18 unless exists $self->{$key}
572             || $key =~ /^_/;
573              
574 6 50       19 unless ( defined $orig->{$key} ) {
575 0         0 warn("D: $path$key\n") if DEBUG;
576 0         0 delete $self->{$key};
577 0   0     0 $state //= 'M';
578 0         0 next;
579             }
580              
581             # Hash -> Hash.
582 6 100 66     16 if ( _ref($orig->{$key}) eq 'HASH'
    100 66        
    50 100        
      50        
      50        
      33        
      33        
      33        
      33        
      33        
583             and _ref($self->{$key}) eq 'HASH'
584             or
585             _ref($orig->{$key}) eq 'ARRAY'
586             and _ref($self->{$key}) eq 'ARRAY' ) {
587             # Recurse.
588 4         48 my $m = _reduce( $self->{$key}, $orig->{$key}, "$path$key." );
589 4 50 33     19 delete $self->{$key} if $m eq 'D' || $m eq 'I';
590 4 50 100     18 $state //= 'M' if $m ne 'I';
591             }
592              
593             elsif ( ($self->{$key}//'') eq ($orig->{$key}//'') ) {
594 1         4 warn("I: $path$key\n") if DEBUG;
595 1         4 delete $self->{$key};
596             }
597             elsif ( !defined($self->{$key})
598             and _ref($orig->{$key}) eq 'ARRAY'
599 0         0 and !@{$orig->{$key}}
600             or
601             !defined($orig->{$key})
602             and _ref($self->{$key}) eq 'ARRAY'
603 0         0 and !@{$self->{$key}} ) {
604             # Properties input [] yields undef.
605 0         0 warn("I: $path$key\n") if DEBUG;
606 0         0 delete $self->{$key};
607             }
608             else {
609             # Overwrite.
610 1         3 warn("M: $path$key => $self->{$key}\n") if DEBUG;
611 1   50     8 $state //= 'M';
612             }
613             }
614 3   50     16 return $state // 'I';
615             }
616              
617 3 50 33     24 if ( _ref($self) eq 'ARRAY' && _ref($orig) eq 'ARRAY' ) {
618              
619             # Arrays.
620 3 100   5   18 if ( any { _ref($_) } @$self ) {
  5         9  
621             # Complex arrays. Recurse.
622 1         28 for ( my $key = 0; $key < @$self; $key++ ) {
623 1         8 my $m = _reduce( $self->[$key], $orig->[$key], "$path$key." );
624             #delete $self->{$key} if $m eq 'D'; # TODO
625 1 50 50     8 $state //= 'M' if $m ne 'I';
626             }
627 1   50     7 return $state // 'I';
628             }
629              
630             # Simple arrays (only scalar values).
631 2 100       16 if ( my $dd = @$self - @$orig ) {
632 1         7 $path =~ s/\.$//;
633 1 50       5 if ( $dd > 0 ) {
634             # New is larger. Check for prepend/append.
635             # Deal with either one, not both. Maybe later.
636 1         3 my $t;
637 1         7 for ( my $ix = 0; $ix < @$orig; $ix++ ) {
638 1 50       5 next if $orig->[$ix] eq $self->[$ix];
639 1         2 $t++;
640 1         3 last;
641             }
642 1 50       14 unless ( $t ) {
643 0         0 warn("M: $path append @{$self}[-$dd..-1]\n") if DEBUG;
644 0         0 splice( @$self, 0, $dd, "append" );
645 0         0 return 'M';
646             }
647 1         3 undef $t;
648 1         5 for ( my $ix = $dd; $ix < @$self; $ix++ ) {
649 2 50       11 next if $orig->[$ix-$dd] eq $self->[$ix];
650 0         0 $t++;
651 0         0 last;
652             }
653 1 50       3 unless ( $t ) {
654 1         2 warn("M: $path prepend @{$self}[0..$dd-1]\n") if DEBUG;
655 1         3 splice( @$self, $dd );
656 1         4 unshift( @$self, "prepend" );
657 1         5 return 'M';
658             }
659 0         0 warn("M: $path => @$self\n") if DEBUG;
660 0         0 $state = 'M';
661             }
662             else {
663 0         0 warn("M: $path => @$self\n") if DEBUG;
664 0         0 $state = 'M';
665             }
666 0   0     0 return $state // 'I';
667             }
668              
669             # Equal length arrays with scalar values.
670 1         21 my $t;
671 1         8 for ( my $ix = 0; $ix < @$orig; $ix++ ) {
672 1 50       4 next if $orig->[$ix] eq $self->[$ix];
673 1         2 warn("M: $path$ix => $self->[$ix]\n") if DEBUG;
674 1         2 $t++;
675 1         3 last;
676             }
677 1 50       3 if ( $t ) {
678 1         2 warn("M: $path\n") if DEBUG;
679 1         5 return 'M';
680             }
681 0         0 warn("I: $path\[]\n") if DEBUG;
682 0         0 return 'I';
683             }
684              
685             # Two scalar values.
686 0         0 $path =~ s/\.$//;
687 0 0       0 if ( $self eq $orig ) {
688 0         0 warn("I: $path\n") if DEBUG;
689 0         0 return 'I';
690             }
691              
692 0         0 warn("M $path $self\n") if DEBUG;
693 0         0 return 'M';
694             }
695              
696 7150     7150 0 9905 sub hmerge( $left, $right, $path = "" ) {
  7150         9528  
  7150         9119  
  7150         10094  
  7150         9203  
697              
698             # Merge hashes. Right takes precedence.
699             # Based on Hash::Merge::Simple by Robert Krimen.
700              
701 7150         54896 my %res = %$left;
702              
703 7150         19327 for my $key ( keys(%$right) ) {
704              
705             warn("Config error: unknown item $path$key\n")
706 26405 50 66     55270 unless exists $res{$key}
      66        
      33        
      33        
      33        
      33        
707             || $path eq "pdf.fontconfig."
708             || $path =~ /^pdf\.(?:info|fonts)\./
709             || $path =~ /^meta\./
710             || $path =~ /^delegates\./
711             || $path =~ /^debug\./
712             || $key =~ /^_/;
713              
714 26405 100 66     81996 if ( ref($right->{$key}) eq 'HASH'
    100 100        
715             and
716             ref($res{$key}) eq 'HASH' ) {
717              
718             # Hashes. Recurse.
719 6689         19454 $res{$key} = hmerge( $res{$key}, $right->{$key}, "$path$key." );
720             }
721             elsif ( ref($right->{$key}) eq 'ARRAY'
722             and
723             ref($res{$key}) eq 'ARRAY' ) {
724             warn("AMERGE $key: ",
725             join(" ", map { qq{"$_"} } @{ $res{$key} }),
726             " + ",
727 2905         4110 join(" ", map { qq{"$_"} } @{ $right->{$key} }),
728             " \n") if 0;
729             # Arrays. Overwrite or append.
730 2905 100       4132 if ( @{$right->{$key}} ) {
  2905         6378  
731 1607         2345 my @v = @{ $right->{$key} };
  1607         9962  
732 1607 50       4613 if ( $v[0] eq "append" ) {
    50          
733 0         0 shift(@v);
734             # Append the rest.
735             warn("PRE: ",
736             join(" ", map { qq{"$_"} } @{ $res{$key} }),
737             " + ",
738 0         0 join(" ", map { qq{"$_"} } @v),
739             "\n") if 0;
740 0         0 push( @{ $res{$key} }, @v );
  0         0  
741             warn("POST: ",
742 0         0 join(" ", map { qq{"$_"} } @{ $res{$key} }),
743             "\n") if 0;
744             }
745             elsif ( $v[0] eq "prepend" ) {
746 0         0 shift(@v);
747             # Prepend the rest.
748 0         0 unshift( @{ $res{$key} }, @v );
  0         0  
749             }
750             else {
751             # Overwrite.
752 1607         4910 $res{$key} = $right->{$key};
753             }
754             }
755             else {
756             # Overwrite.
757 1298         2917 $res{$key} = $right->{$key};
758             }
759             }
760             else {
761             # Overwrite.
762 16811         27563 $res{$key} = $right->{$key};
763             }
764             }
765              
766 7150         19753 return \%res;
767             }
768              
769 40     40 0 114 sub clone ( $source ) {
  40         95  
  40         71  
770              
771 40 50       129 return if not defined($source);
772              
773 81     81   871 use Storable;
  81         227  
  81         79445  
774 40         52544 my $clone = Storable::dclone($source);
775 40         405 $clone->unlock;
776 40         74491 return $clone;
777              
778             }
779              
780 132     132 0 509 sub precheck ( $cfg, $file ) {
  132         924  
  132         713  
  132         494  
781              
782 132         621 my $verbose = $options->{verbose};
783 132 50       662 warn("Verify config \"$file\"\n") if $verbose > 1;
784 132         345 my $p;
785             $p = sub {
786 283271     283271   460240 my ( $o, $path ) = @_;
787 283271   100     444952 $path //= "";
788 283271 50       994280 if ( !defined $o ) {
    100          
    100          
789 0         0 warn("$file: Undefined config \"$path\" (using 'false')\n");
790 0         0 $_[0] = '';
791             }
792             elsif ( UNIVERSAL::isa( $o, 'HASH' ) ) {
793 50244 100       91835 $path .= "." unless $path eq "";
794 50244         203337 for ( sort keys %$o ) {
795 119293         251112 $p->( $o->{$_}, $path . $_ );
796             }
797             }
798             elsif ( UNIVERSAL::isa( $o, 'ARRAY' ) ) {
799 19175 50       36211 $path .= "." unless $path eq "";
800 19175         40327 for ( my $i = 0; $i < @$o; $i++ ) {
801 163846         359959 $p->( $o->[$i], $path . "$i" );
802             }
803             }
804 132         1953 };
805              
806 132         777 $p->($cfg);
807             }
808              
809              
810             ## Data::Properties compatible API.
811             #
812             # Note: Lookup always takes the context into account.
813             # Note: Always signals undefined values.
814              
815             my $prp_context = "";
816              
817 12     12 0 17 sub get_property ( $p, $prp, $def = undef ) {
  12         19  
  12         16  
  12         22  
  12         15  
818 12 100       64 for ( split( /\./,
819             $prp_context eq ""
820             ? $prp
821             : "$prp_context.$prp" ) ) {
822 34 100       91 if ( /^\d+$/ ) {
823 5 50       12 die("No config $prp\n") unless _ref($p) eq 'ARRAY';
824 5         16 $p = $p->[$_];
825             }
826             else {
827 29 50       50 die("No config $prp\n") unless _ref($p) eq 'HASH';
828 29         69 $p = $p->{$_};
829             }
830             }
831 12   66     41 $p //= $def;
832 12 50       23 die("No config $prp\n") unless defined $p;
833 12         67 $p;
834             }
835              
836             *gps = \&get_property;
837              
838             sub set_property {
839 0     0 0 0 ...;
840             }
841              
842 2     2 0 6 sub set_context ( $self, $ctx = "" ) {
  2         4  
  2         7  
  2         3  
843 2         7 $prp_context = $ctx;
844             }
845              
846 0     0 0 0 sub get_context () {
  0         0  
847 0         0 $prp_context;
848             }
849              
850             # For testing
851 81     81   3446 use base qw(Exporter);
  81         1604  
  81         69077  
852             our @EXPORT = qw( _c );
853 12     12   1789 sub _c ( @args ) { $::config->gps(@args) }
  12         27  
  12         20  
  12         37  
854              
855             # Get the raw contents of the builtin (default) config.
856 199     199 0 494 sub default_config () {
  199         409  
857 199         1109 return <<'End_Of_Config';
858             // Configuration for ChordPro.
859             //
860             // This is a relaxed JSON document, so comments are possible.
861              
862             {
863             // Includes. These are processed first, before the rest of
864             // the config file.
865             //
866             // "include" takes a list of either filenames or preset names.
867             // "include" : [ "modern1", "lib/mycfg.json" ],
868             "include" : [ "guitar" ],
869              
870             // General settings, often changed by configs and command line.
871             "settings" : {
872             // Strict behaviour.
873             "strict" : true,
874             // Obsolete -- lineinfo is always included.
875             "lineinfo" : true,
876             // Titles flush: default center.
877             "titles" : "center",
878             // Columns, default one.
879             "columns" : 1,
880             // Suppress empty chord lines.
881             // Overrides the -a (--single-space) command line options.
882             "suppress-empty-chords" : true,
883             // Suppress blank lyrics lines.
884             "suppress-empty-lyrics" : true,
885             // Suppress chords.
886             // Overrides --lyrics-only command line option.
887             "lyrics-only" : false,
888             // Memorize chords in sections, to be recalled by [^].
889             "memorize" : false,
890             // Chords inline.
891             // May be a string containing pretext %s posttext.
892             // Defaults to "[%s]" if set to a value that doesn't contain "%s".
893             "inline-chords" : false,
894             // Same, for annotations. Ignored unless inline-chords is set.
895             // Must be a string containing pretext %s posttext.
896             // Default is "%s".
897             "inline-annotations" : "%s",
898             // Chords under the lyrics.
899             "chords-under" : false,
900             // Transposing.
901             "transpose" : 0,
902             // Transcoding.
903             "transcode" : "",
904             // Always decapoize.
905             "decapo" : false,
906             // Chords parsing strategy.
907             // Strict (only known) or relaxed (anything that looks sane).
908             "chordnames": "strict",
909             // Allow note names in [].
910             "notenames" : false,
911             // Always replace chords by their canonical form.
912             "chords-canonical" : false,
913             // If false, chorus labels are used as tags.
914             "choruslabels" : true,
915             // Substitute Unicode sharp/flats in chord names.
916             // Will fallback to ChordProSymbols the font doesn't have the glyphs.
917             "truesf" : false,
918             },
919              
920             // Metadata.
921             // For these keys you can use {meta key ...} as well as {key ...}.
922             // If strict is nonzero, only the keys named here are allowed.
923             // If strict is zero, {meta ...} will accept any key.
924             // Important: "title" and "subtitle" must always be in this list.
925             // The separator is used to concatenate multiple values.
926             // If autosplit is true, the separator is also used to split
927             // values upon input.
928             "metadata" : {
929             "keys" : [ "title", "subtitle",
930             "artist", "composer", "lyricist", "arranger",
931             "album", "copyright", "year",
932             "sorttitle",
933             "key", "time", "tempo", "capo", "duration" ],
934             "strict" : true,
935             "separator" : "; ",
936             "autosplit" : true,
937             },
938             // Globally defined (added) meta data,
939             // This is explicitly NOT intended for the metadata items above.
940             "meta" : {
941             },
942              
943             // Assets.
944             "assets" : {},
945              
946             // Dates. Format is a strftime template.
947             "dates" : {
948             "today" : {
949             "format" : "%A, %B %e, %Y"
950             }
951             },
952              
953             // User settings. These are usually set by a separate config file.
954             //
955             "user" : {
956             "name" : "",
957             "fullname" : "",
958             },
959              
960             // Instrument settings. These are usually set by a separate
961             // config file.
962             //
963             "instrument" : {
964             "type" : "",
965             "description" : "",
966             },
967              
968             // Note (chord root) names.
969             // Strings and tuning.
970             "tuning" : [ "E2", "A2", "D3", "G3", "B3", "E4" ],
971              
972             // In case of alternatives, the first one is used for output.
973             // Note that it is tempting to use real sharps and flats for output,
974             // but most fonts don't have the glyphs :(.
975             "notes" : {
976              
977             "system" : "common",
978              
979             "sharp" : [ "C", [ "C#", "Cis", "C♯" ],
980             "D", [ "D#", "Dis", "D♯" ],
981             "E",
982             "F", [ "F#", "Fis", "F♯" ],
983             "G", [ "G#", "Gis", "G♯" ],
984             "A", [ "A#", "Ais", "A♯" ],
985             "B",
986             ],
987              
988             "flat" : [ "C",
989             [ "Db", "Des", "Dâ™­" ], "D",
990             [ "Eb", "Es", "Ees", "Eâ™­" ], "E",
991             "F",
992             [ "Gb", "Ges", "Gâ™­" ], "G",
993             [ "Ab", "As", "Aes", "Aâ™­" ], "A",
994             [ "Bb", "Bes", "Bâ™­" ], "B",
995             ],
996              
997             // Movable means position independent (e.g. nashville).
998             "movable" : false,
999             },
1000              
1001             // User defined chords.
1002             // "base" defaults to 1.
1003             // Use 0 for an empty string, and -1 for a muted string.
1004             // "fingers" is optional.
1005             // "display" (optional) can be used to change the way the chord is displayed.
1006             "chords" : [
1007             // {
1008             // "name" : "Bb",
1009             // "base" : 1,
1010             // "frets" : [ 1, 1, 3, 3, 3, 1 ],
1011             // "fingers" : [ 1, 1, 2, 3, 4, 1 ],
1012             // "display" : "B\u266d",
1013             // },
1014             // If the name of the first entry is "defaults" its properties may
1015             // be used as defaults for the rest of the chords.
1016             ],
1017              
1018             // Format to show chord names. May contain markup.
1019             // "chord-format" : "%{root}%{qual|%{}}%{ext|%{}}%{bass|/%{}}",
1020             "chord-formats" : {
1021             "common" : "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}",
1022             "roman" : "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}",
1023             "nashville" : "%{root|%{}%{qual|%{}}%{ext|%{}}%{bass|/%{}}|%{name}}",
1024             },
1025              
1026             // Printing chord diagrams.
1027             // "show": prints the chords used in the song.
1028             // "all": all chords used.
1029             // "user": only prints user defined chords.
1030             // "sorted": order the chords by key.
1031             // "suppress": a series of chord (names) thet will not generate
1032             // diagrams, e.g. if they are considered trivial.
1033             // Note: The type of diagram (string or keyboard) is determined
1034             // by the value of "instrument.type".
1035             "diagrams" : {
1036             "show" : "all",
1037             "sorted" : false,
1038             "suppress" : [],
1039             },
1040              
1041             // Diagnostic messages.
1042             "diagnostics" : {
1043             "format" : "\"%f\", line %n, %m\n\t%l",
1044             },
1045              
1046             // Table of contents.
1047             "contents" : [
1048             { "fields" : [ "songindex" ],
1049             "label" : "Table of Contents",
1050             "line" : "%{title}",
1051             "pageno" : "%{page}",
1052             "fold" : false,
1053             "omit" : false,
1054             },
1055             { "fields" : [ "sorttitle", "artist" ],
1056             "label" : "Contents by Title",
1057             "line" : "%{title}%{artist| - %{}}",
1058             "pageno" : "%{page}",
1059             "fold" : false,
1060             "omit" : false,
1061             },
1062             { "fields" : [ "artist", "sorttitle" ],
1063             "label" : "Contents by Artist",
1064             "line" : "%{artist|%{} - }%{title}",
1065             "pageno" : "%{page}",
1066             "fold" : false,
1067             "omit" : true,
1068             },
1069             ],
1070             // Table of contents, old style.
1071             // This will be ignored when new style contents is present.
1072             "toc" : {
1073             // Title for ToC.
1074             "title" : "Table of Contents",
1075             "line" : "%{title}",
1076             // Sorting order.
1077             // Currently only sorting by page number and alpha is implemented.
1078             "order" : "page",
1079             },
1080              
1081             // Delegates.
1082             // Basically a delegate is a section {start_of_XXX} which content is
1083             // collected and handled later by the delegate module.
1084              
1085             "delegates" : {
1086             "abc" : {
1087             "type" : "image",
1088             "module" : "ABC",
1089             "handler" : "abc2svg",
1090             "config" : "default", // or "none", or "myformat.fmt"
1091             // The preamble is a list of lines inserted before the ABC data.
1092             // DO NOT MODIFY unless you know what you are doing!
1093             "preamble" : [
1094             // Get rid of as much space as possible.
1095             "%%topspace 0",
1096             "%%titlespace 0",
1097             "%%musicspace 0",
1098             "%%composerspace 0",
1099             "%%infospace 0",
1100             "%%textspace 0",
1101             "%%leftmargin 0cm",
1102             "%%rightmargin 0cm",
1103             "%%staffsep 0",
1104             // Use ChordPro fonts for lyrics and chords.
1105             "%%textfont pdf.fonts.text",
1106             "%%gchordfont pdf.fonts.chord",
1107             ],
1108             "preprocess" : { "abc" : [], "svg" : [] },
1109             "omit" : false,
1110             },
1111             "ly" : {
1112             "type" : "image",
1113             "module" : "Lilypond",
1114             "handler" : "ly2svg",
1115             "config" : "default", // or "none", or ...
1116             // The preamble is a list of lines inserted before the lilipond data.
1117             // This is a good place to set the version and global customizations.
1118             "preamble" : [
1119             "\\version \"2.21.0\"",
1120             "\\header { tagline = ##f }",
1121             ],
1122             "omit" : false,
1123             },
1124             },
1125              
1126             // Definitions for PDF output.
1127              
1128             "pdf" : {
1129              
1130             // Choose a PDF::API2 compatible library, or leave empty to
1131             // have ChordPro choose one for you.
1132             "library" : "", // or "PDF::API2", or "PDF::Builder"
1133              
1134             // PDF Properties. Arbitrary key/values may be added.
1135             // Note that the context for substitutions is the first song.
1136             "info" : {
1137             "title" : "%{title}",
1138             "author" : "",
1139             "subject" : "",
1140             "keywords" : "",
1141             },
1142              
1143             // Papersize, 'a4' or [ 595, 842 ] etc.
1144             "papersize" : "a4",
1145              
1146             "theme" : {
1147             // Forgeround color. Usually black.
1148             "foreground" : "black",
1149             // Shades of grey.
1150             // medium is used for pressed keys in keyboard diagrams.
1151             "foreground-medium" : "grey70",
1152             // light is used as background for comments, cell bars, ...
1153             "foreground-light" : "grey90",
1154             // Background color. Usually none or white.
1155             "background" : "none",
1156             },
1157              
1158             // Space between columns, in pt.
1159             "columnspace" : 20,
1160              
1161             // Page margins.
1162             // Note that top/bottom exclude the head/footspace.
1163             "margintop" : 80,
1164             "marginbottom" : 40,
1165             "marginleft" : 40,
1166             "marginright" : 40,
1167             "headspace" : 60,
1168             "footspace" : 20,
1169              
1170             // Special: head on first page only, add the headspace to
1171             // the other pages so they become larger.
1172             "head-first-only" : false,
1173              
1174             // Spacings.
1175             // Baseline distances as a factor of the font size.
1176             "spacing" : {
1177             "title" : 1.2,
1178             "lyrics" : 1.2,
1179             "chords" : 1.2,
1180             "diagramchords" : 1.2,
1181             "grid" : 1.2,
1182             "tab" : 1.0,
1183             "toc" : 1.4,
1184             "empty" : 1.0,
1185             },
1186             // Note: By setting the font size and spacing for empty lines to
1187             // smaller values, you get a fine(r)-grained control over the
1188             // spacing between the various parts of the song.
1189              
1190             // Style of chorus.
1191             "chorus" : {
1192             "indent" : 0,
1193             // Chorus side bar.
1194             // Suppress by setting offset and/or width to zero.
1195             "bar" : {
1196             "offset" : 8,
1197             "width" : 1,
1198             "color" : "foreground",
1199             },
1200             "tag" : "Chorus",
1201             // Recall style: Print the tag using the type.
1202             // Alternatively quote the lines of the preceding chorus.
1203             "recall" : {
1204             "choruslike" : false,
1205             "tag" : "Chorus",
1206             "type" : "comment",
1207             "quote" : false,
1208             },
1209             },
1210              
1211             // This opens a margin for margin labels.
1212             "labels" : {
1213             // Margin width. Default is 0 (no margin labels).
1214             // "auto" will automatically reserve a margin if labels are used.
1215             "width" : "auto",
1216             // Alignment for the labels. Default is left.
1217             "align" : "left",
1218             // Alternatively, render labels as comments.
1219             "comment" : "" // "comment", "comment_italic" or "comment_box",
1220             },
1221              
1222             // Alternative songlines with chords in a side column.
1223             // Value is the column position.
1224             // "chordscolumn" : 400,
1225             "chordscolumn" : 0,
1226             "capoheading" : "%{capo|Capo: %{}}",
1227              
1228             // A {titles: left} may conflict with customized formats.
1229             // Set to non-zero to ignore the directive.
1230             "titles-directive-ignore" : false,
1231              
1232             // Chord diagrams.
1233             // A chord diagram consists of a number of cells.
1234             // Cell dimensions are specified by "width" and "height".
1235             // The horizontal number of cells depends on the number of strings.
1236             // The vertical number of cells is "vcells", which should
1237             // be 4 or larger to accomodate most chords.
1238             // The horizontal distance between diagrams is "hspace" cells.
1239             // The vertical distance is "vspace" cells.
1240             // "linewidth" is the thickness of the lines as a fraction of "width".
1241             // Diagrams for all chords of the song can be shown at the
1242             // "top", "bottom" or "right" side of the first page,
1243             // or "below" the last song line.
1244             "diagrams" : {
1245             "show" : "bottom",
1246             "width" : 6, // width of a cell
1247             "height" : 6, // height of a cell
1248             "vcells" : 4, // vertical cells
1249             "linewidth" : 0.1, // fraction of cell width
1250             "nutwidth" : 5, // width (in linewidth) of the top nut
1251             "hspace" : 3.95, // fraction of width
1252             "vspace" : 3, // fraction of height
1253             "dotsize" : 0.8, // fraction of a cell
1254             "barwidth" : 0.8, // fraction of a dot
1255             "fingers" : true, // show fingering if available (or "below")
1256             },
1257              
1258             // Keyboard diagrams.
1259             // A keyboard diagram consists of a number of keys.
1260             // Dimensions are specified by "width" (a key) and "height".
1261             // The horizontal distance between diagrams is "hspace" * keys * width.
1262             // The vertical distance is "vspace" * height.
1263             // "linewidth" is the thickness of the lines as a fraction of "width".
1264             // Diagrams for all chords of the song can be shown at the
1265             // "top", "bottom" or "right" side of the first page,
1266             // or "below" the last song line.
1267             "kbdiagrams" : {
1268             "show" : "bottom",
1269             "width" : 4, // of a single key
1270             "height" : 20, // of the diagram
1271             "keys" : 14, // or 7, 10, 14, 17, 21
1272             "base" : "C", // or "F"
1273             "linewidth" : 0.1, // fraction of a single key width
1274             "pressed" : "foreground-medium", // colour of a pressed key
1275             "hspace" : 3.95, // ??
1276             "vspace" : 0.3, // fraction of height
1277             },
1278              
1279             // Grid section lines.
1280             // Suppress when "show" is false, e.g. for singers.
1281             // The width and colour of the cell bar lines can be specified.
1282             // Enable by setting the width to the desired width.
1283             "grids" : {
1284             "cellbar" : {
1285             "width" : 0,
1286             "color" : "foreground-medium",
1287             },
1288             "show": true,
1289             "symbols" : {
1290             "color" : "blue",
1291             },
1292             "volta" : {
1293             "span" : 0.7,
1294             "color" : "blue",
1295             },
1296             },
1297              
1298             // Even/odd pages. A value of -1 denotes odd/even pages.
1299             "even-odd-pages" : 1,
1300             // Align songs to even/odd pages. When greater than 1, force alignment.
1301             "pagealign-songs" : 1,
1302             // PDF file to add as front matter.
1303             "front-matter" : "",
1304             // PDF file to add as back matter.
1305             "back-matter" : "",
1306              
1307             // Formats.
1308             // Pages have two title elements and one footer element. They also
1309             // can have a page of an existing PDF file as underlay (background).
1310             // Topmost is "title". It uses the "title" font as defined further below.
1311             // Second is "subtitle". It uses the "subtitle" font.
1312             // The "footer" uses the "footer" font.
1313             // All elements can have three fields, that are placed to the left side,
1314             // centered, and right side of the page.
1315             // The contents of all fields is defined below. You can use metadata
1316             // items in the fields as shown. By default, the "title" element shows the
1317             // value of metadata item "title", centered on the page. Likewise
1318             // "subtitle".
1319             // NOTE: The "title" and "subtitle" page elements have the same names
1320             // as the default metadata values which may be confusing. To show
1321             // metadata item, e.g. "artist", add its value to one of the
1322             // title/subtitle fields. Don't try to add an artist page element.
1323              
1324             "formats" : {
1325             // Titles/Footers.
1326              
1327             // Titles/footers have 3 parts, which are printed left,
1328             // centered and right.
1329             // For odd/even printing, the 1st background page is used
1330             // for left pages and the next page (if it exists) for right pages.
1331             // For even/odd printing, the order is reversed.
1332              
1333             // By default, a page has:
1334             "default" : {
1335             // No title/subtitle.
1336             "title" : [ "", "", "" ],
1337             "subtitle" : [ "", "", "" ],
1338             // Footer is title -- page number.
1339             "footer" : [ "%{title}", "", "%{page}" ],
1340             // Background page.
1341             "background" : "",
1342             },
1343             // The first page of a song has:
1344             "title" : {
1345             // Title and subtitle.
1346             "title" : [ "", "%{title}", "" ],
1347             "subtitle" : [ "", "%{subtitle}", "" ],
1348             // Footer with page number.
1349             "footer" : [ "", "", "%{page}" ],
1350             // Background page.
1351             "background" : "",
1352             },
1353             // The very first output page is slightly different:
1354             "first" : {
1355             // It has title and subtitle, like normal 'first' pages.
1356             // But no footer.
1357             "footer" : [ "", "", "" ],
1358             // Background page.
1359             "background" : "",
1360             },
1361             },
1362              
1363             // Split marker for syllables that are smaller than chord width.
1364             // split-marker is a 3-part array: 'start', 'repeat', and 'final'.
1365             // 'final' is always printed, last.
1366             // 'start' is printed if there is enough room.
1367             // 'repeat' is printed repeatedly to fill the rest.
1368             // If split-marker is a single string, this is 'start'.
1369             // All elements may be left empty strings.
1370             "split-marker" : [ "", "", "" ],
1371              
1372             // Font families and properties.
1373             // "fontconfig" maps members of font families to physical fonts.
1374             // Optionally, additional properties of the fonts can be specified.
1375             // Physical fonts can be the names of TrueType/OpenType fonts,
1376             // or names of built-in fonts (corefonts).
1377             // Relative filenames are looked up in the fontdir.
1378             // "fontdir" : [ "/usr/share/fonts/liberation", "/home/me/fonts" ],
1379              
1380             "fontdir" : [],
1381             "fontconfig" : {
1382             // alternatives: regular r normal
1383             // alternatives: bold b strong
1384             // alternatives: italic i oblique o emphasis
1385             // alternatives: bolditalic bi italicbold ib boldoblique bo obliquebold ob
1386             "times" : {
1387             "" : "Times-Roman",
1388             "bold" : "Times-Bold",
1389             "italic" : "Times-Italic",
1390             "bolditalic" : "Times-BoldItalic",
1391             },
1392             "helvetica" : {
1393             "" : "Helvetica",
1394             "bold" : "Helvetica-Bold",
1395             "oblique" : "Helvetica-Oblique",
1396             "boldoblique" : "Helvetica-BoldOblique",
1397             },
1398             "courier" : {
1399             "" : "Courier",
1400             "bold" : "Courier-Bold",
1401             "italic" : "Courier-Italic",
1402             "bolditalic" : "Courier-BoldItalic",
1403             },
1404             "dingbats" : {
1405             "" : "ZapfDingbats",
1406             },
1407             },
1408              
1409             // "fonts" maps output elements to fonts as defined in "fontconfig".
1410             // The elements can have a background colour associated.
1411             // Colours are "#RRGGBB" or predefined names like "black", "white",
1412             // and lots of others.
1413             // NOTE: In the built-in config we use only "name" since that can
1414             // be overruled with user settings.
1415              
1416             "fonts" : {
1417             "title" : {
1418             "name" : "Times-Bold",
1419             "size" : 14
1420             },
1421             "text" : {
1422             "name" : "Times-Roman",
1423             "size" : 12
1424             },
1425             "chord" : {
1426             "name" : "Helvetica-Oblique",
1427             "size" : 10
1428             },
1429             "chordfingers" : {
1430             "file" : "ChordProSymbols.ttf", // do not change
1431             "numbercolor" : "background",
1432             },
1433             "comment" : {
1434             "name" : "Helvetica",
1435             "size" : 12,
1436             "background" : "foreground-light"
1437             },
1438             "comment_italic" : {
1439             "name" : "Helvetica-Oblique",
1440             "size" : 12,
1441             },
1442             "comment_box" : {
1443             "name" : "Helvetica",
1444             "size" : 12,
1445             "frame" : 1
1446             },
1447             "tab" : {
1448             "name" : "Courier",
1449             "size" : 10
1450             },
1451             "toc" : {
1452             "name" : "Times-Roman",
1453             "size" : 11
1454             },
1455             "grid" : {
1456             "name" : "Helvetica",
1457             "size" : 10
1458             },
1459             },
1460              
1461             // Element mappings that can be specified, but need not since
1462             // they default to other elements.
1463             // subtitle --> text
1464             // chorus --> text
1465             // comment --> text
1466             // comment_italic --> chord
1467             // comment_box --> chord
1468             // annotation --> chord
1469             // toc --> text
1470             // grid --> chord
1471             // grid_margin --> comment
1472             // footer --> subtitle @ 60%
1473             // empty --> text
1474             // diagram --> comment
1475             // diagram_base --> text (but at a small size)
1476              
1477             // Bookmarks (PDF outlines).
1478             // fields: primary and (optional) secondary fields.
1479             // label: outline label (omitted if there's only one outline)
1480             // line: text of the outline element
1481             // collapse: initial display is collapsed
1482             // letter: sublevel with first letters if more
1483             // fold: group by primary (NYI)
1484             // omit: ignore this
1485             "outlines" : [
1486             { "fields" : [ "sorttitle", "artist" ],
1487             "label" : "By Title",
1488             "line" : "%{title}%{artist| - %{}}",
1489             "collapse" : false,
1490             "letter" : 5,
1491             "fold" : false,
1492             },
1493             { "fields" : [ "artist", "sorttitle" ],
1494             "label" : "By Artist",
1495             "line" : "%{artist|%{} - }%{title}",
1496             "collapse" : false,
1497             "letter" : 5,
1498             "fold" : false,
1499             },
1500             ],
1501              
1502             // This will show the page layout if non-zero.
1503             "showlayout" : false,
1504              
1505             // CSV generation for MobileSheetsPro. Adapt for other tools.
1506             // Note that the resultant file will conform to RFC 4180.
1507             "csv" : {
1508             "fields" : [
1509             { "name" : "title", "meta" : "title" },
1510             { "name" : "pages", "meta" : "pagerange" },
1511             { "name" : "sort title", "meta" : "sorttitle" },
1512             { "name" : "artists", "meta" : "artist" },
1513             { "name" : "composers", "meta" : "composer" },
1514             { "name" : "collections", "meta" : "collection" },
1515             { "name" : "keys", "meta" : "key_actual" },
1516             { "name" : "years", "meta" : "year" },
1517             // Add "omit" : true to omit a field.
1518             // To add fields with fixed values, use "value":
1519             { "name" : "my_field", "value" : "text", "omit" : true },
1520             ],
1521             // Field separator.
1522             "separator" : ";",
1523             // Values separator.
1524             "vseparator" : "|",
1525             // Restrict CSV to song pages only (do not include matter pages).
1526             "songsonly" : true,
1527             },
1528             },
1529              
1530             // Settings for ChordPro backend.
1531             "chordpro" : {
1532             // Style of chorus.
1533             "chorus" : {
1534             // Recall style: Print the tag using the type.
1535             // Alternatively quote the lines of the preceding chorus.
1536             // If no tag+type or quote: use {chorus}.
1537             // Note: Variant 'msp' always uses {chorus}.
1538             "recall" : {
1539             // "tag" : "Chorus", "type" : "comment",
1540             "tag" : "", "type" : "",
1541             // "quote" : false,
1542             "quote" : false,
1543             },
1544             },
1545             },
1546              
1547             // Settings for HTML backend.
1548             "html" : {
1549             // Stylesheet links.
1550             "styles" : {
1551             "display" : "chordpro.css",
1552             "print" : "chordpro_print.css",
1553             },
1554             },
1555              
1556             // Settings for LaTeX backend.
1557             "latex" : {
1558             "template_include_path" : [ ],
1559             "templates" : {
1560             "songbook" : "songbook.tt",
1561             "comment" : "comment.tt",
1562             "image" : "image.tt"
1563             }
1564             },
1565              
1566             // Settings for Text backend.
1567             "text" : {
1568             // Style of chorus.
1569             "chorus" : {
1570             // Recall style: Print the tag using the type.
1571             // Alternatively quote the lines of the preceding chorus.
1572             // If no tag+type or quote: use {chorus}.
1573             // Note: Variant 'msp' always uses {chorus}.
1574             "recall" : {
1575             // "tag" : "Chorus", "type" : "comment",
1576             "tag" : "", "type" : "",
1577             // "quote" : false,
1578             "quote" : false,
1579             },
1580             },
1581             },
1582              
1583             // Settings for A2Crd.
1584             "a2crd" : {
1585             // Treat leading lyrics lines as title/subtitle lines.
1586             "infer-titles" : true,
1587             // Classification algorithm.
1588             "classifier" : "pct_chords",
1589             // Tab stop width for tab expansion. Set to zero to disable.
1590             "tabstop" : 8,
1591             },
1592              
1593             // Settings for the parser/preprocessor.
1594             // For selected lines, you can specify a series of
1595             // { "target" : "xxx", "replace" : "yyy" }
1596             // Every occurrence of "xxx" will be replaced by "yyy".
1597             // Use wisely.
1598             "parser" : {
1599             "preprocess" : {
1600             // All lines.
1601             "all" : [],
1602             // Directives.
1603             "directive" : [],
1604             // Song lines (lyrics) only.
1605             "songline" : [],
1606             },
1607             },
1608              
1609             // For (debugging (internal use only)).
1610             "debug" : {
1611             "chords" : 0,
1612             "config" : 0,
1613             "echo" : 0,
1614             "fonts" : 0,
1615             "images" : 0,
1616             "layout" : 0,
1617             "meta" : 0,
1618             "mma" : 0,
1619             "spacing" : 0,
1620             "song" : 0,
1621             "songfull" : 0,
1622             "csv" : 0,
1623             "abc" : 0,
1624             "ly" : 0,
1625             "svg" : 0,
1626             // For temporary use.
1627             "x1" : 0,
1628             "x2" : 0,
1629             "x3" : 0,
1630             },
1631              
1632             }
1633             // End of config.
1634             End_Of_Config
1635             }
1636              
1637             # For convenience.
1638              
1639 214     214 0 375 sub diagram_strings ( $self ) {
  214         369  
  214         306  
1640             # tuning is usually removed from the config.
1641             # scalar( @{ $self->{tuning} } );
1642 214         727 ChordPro::Chords::strings();
1643             }
1644              
1645 0     0 0   sub diagram_keys ( $self ) {
  0            
  0            
1646 0           $self->{kbdiagrams}->{keys};
1647             }
1648              
1649             # For debugging messages.
1650 0     0 0   sub qd ( $val, $compact = 0 ) {
  0            
  0            
  0            
1651 81     81   3617 use Data::Dumper qw();
  81         13355  
  81         40650  
1652 0           local $Data::Dumper::Sortkeys = 1;
1653 0           local $Data::Dumper::Indent = 1;
1654 0           local $Data::Dumper::Quotekeys = 0;
1655 0           local $Data::Dumper::Deparse = 1;
1656 0           local $Data::Dumper::Terse = 1;
1657 0           local $Data::Dumper::Trailingcomma = !$compact;
1658 0           local $Data::Dumper::Useperl = 1;
1659 0           local $Data::Dumper::Useqq = 0; # I want unicode visible
1660 0           my $x = Data::Dumper::Dumper($val);
1661 0 0         if ( $compact ) {
1662 0           $x =~ s/^bless\( (.*), '[\w:]+' \)$/$1/s;
1663 0           $x =~ s/\s+/ /gs;
1664             }
1665 0 0         defined wantarray ? $x : warn($x,"\n");
1666             }
1667              
1668             unless ( caller ) {
1669             binmode STDOUT => ':utf8';
1670             print( default_config() );
1671             exit;
1672             }
1673              
1674             1;
1675              
1676             =head1 DEFAULT CONFIGURATION
1677              
1678             The default configuration as built in. User and system
1679             configs go on top of this one.
1680              
1681             See L for
1682             extensive details and examples.
1683              
1684