Importation de chordpack-0.8.2
authorHugo Villeneuve <hugo@hugovil.com>
Wed, 9 Mar 2022 22:17:43 +0000 (17:17 -0500)
committerHugo Villeneuve <hugo@hugovil.com>
Wed, 9 Mar 2022 22:17:43 +0000 (17:17 -0500)
chordpack [new file with mode: 0755]
chordpack-documentation.html [new file with mode: 0644]
chordpro-mode.el [new file with mode: 0644]

diff --git a/chordpack b/chordpack
new file mode 100755 (executable)
index 0000000..f087f95
--- /dev/null
+++ b/chordpack
@@ -0,0 +1,2453 @@
+#!/usr/bin/perl -w
+
+# {{{  header and version          
+#
+# Purpose of the script:
+#
+#    This is a utility for typesetting guitar chords in chordpro format.
+#    It uses TeX typesetting system, namely LaTeX2e macro package for TeX.
+# 
+# Author:           Daniel Polansky, dan.polansky@seznam.cz
+# Release:          0.8.2
+# Script home page: http://mujweb.cz/www/danielpolansky/chordpack/
+#
+# }}}
+
+# {{{  help message                     
+
+$help_message="Usage: chordpack [OPTION]... TASK [FILE]...\n".
+    "\n".
+    "Operate on songs for guitar found in FILEs. The songs are supposed\n".
+    "to be in chordpro format. Operation is determined by TASK, most\n".
+    "common is typesetting with TeX. Possible TASKs are tex, html, ascii,\n".
+    "nochord, transpose key-or-shift, pro. Options are\n".
+    "\n".
+    "   -f song-list-file \tUse song-list-file\n".
+    "   -l language       \tUse language\n".
+    "   -e encoding       \tUse input encoding when typesetting with LaTeX\n".
+    "   -b                \tTypeset with minimum barre chords\n".
+    "   -c chord-style    \tSet the style of chord typesetting\n".
+    "   -s font-sizes     \tSet the font sizes\n".
+    "\n".
+    "For more detailed information see chordpack-documentation.html.\n";
+
+# }}}
+# {{{  support functions                
+sub warning {
+    if (not $chordpack_introduced) {
+       $chordpack_introduced=1;
+       printf STDERR "\nChordpack: warning messages:\n\n";
+    }
+    print STDERR $_[0]; }
+
+sub check_for_the_length {
+    my ($line,$file,$maxlength)=@_;
+
+    if (length($line) > $maxlength) {
+       if (not exists($files_warned{$file})) {
+           $files_warned{$file}=1;
+           if ($error_explained==0) {
+               $error_explained=1;
+               warning "Warning >> means too long line ".
+                   "(line longer than $maxlength characters).\nFile name where this happened".
+                       " follows.\n";}
+           warning ">>  $file.\n";}}}
+
+sub insertstring {
+    my ($inserted,$source,$position)=@_;
+
+    # Inset string $inserted into the string $source at position
+    # $position. If the position is farther than than the length of
+    # $source, die.
+
+    if ($position>=length($source)) {
+       die "insertstring: position too far.\n"; }
+
+    return substr($source,0,$position).$inserted.substr($source,$position,length($source)-$position);}
+
+# }}}
+
+# {{{  global variables and constants   
+$chordpack_introduced=0;
+$error_explained=0;
+$carriage_return_warned=0;
+
+@tex_font_size = ( "\\tiny" , "\\scriptsize" , "\\footnotesize" ,
+                  "\\small" , "\\normalsize" , "\\large", "\\Large" ,
+                  "\\LARGE", "\\huge" , "\\Huge" );
+
+# }}}
+# {{{  global options                   
+
+use Getopt::Std;
+getopts('bf:c:l:s:e:');
+sub option_process {
+    my ($option,$default,$option_letter) = @_;
+    $from_options{$option}=0;
+    eval ("\$$option=\"$default\"");
+    if (eval "defined(\$opt_$option_letter)") {
+       $from_options{$option}=1;
+       eval ("\$$option=\$opt_$option_letter"); }}
+
+
+option_process("language","","l"); 
+# the only supported languages are Czech and German. The default language is English.
+
+option_process("inputenc","","e");
+# for LaTeX typesetting
+
+option_process("chord_style","m","c");
+# chordstyle string contains mi,m or - and also h if h is required
+
+
+option_process("font_sizes",3,"s");
+# currently available values are 0,1,2,3
+
+option_process("title_style","","");
+
+option_process("columns",2,"");
+
+$nobarre=0; $nobarre=1 if defined($opt_b) and $opt_b==1;
+# This option cannot be set using {} command
+
+# option -f is processed in tex task
+
+$ignore_tablature=0;
+$ignore_tablature=1 if $nobarre;
+
+
+# ------------------------------------
+
+sub finalize_language_dependent_settings {
+  $language=lc($language);
+
+  # This is Czech collation for ISO 8859-2 character encoding.
+  # We do not solve a problem of other encodings, also 
+  # we don't know how to tell TeX to understand Codepage1250, for instace.
+  # This collation is not prefect, but working pretty well.
+
+  $collation{"czech"}{"list"}= "\"#$%&'()*+,-.:;<=>[\\]'`{}".
+    "0123456789 AÁBCÈDÏEÉÌFGH".chr(0)."IÍJKLÅ¥MNÒOÓPQRØS©T«UÚÙVWXYÝZ®".
+      "aábcèdïeéìfgh".chr(0)."iíjklåµmnòoópqrøs¹t»uúùvwxyýz¾";
+  $collation{"czech"}{"replace"}={"ch" => chr(0) };
+
+  # english is default
+  $alphabetical_name="Alphabetical index";
+  $transposed_by_1="Transposed by ";
+  $transposed_by_2=" half steps.";
+
+  if ($language eq "czech") {
+    $alphabetical_name="Abecední seznam";
+    $transposed_by_1="Transponováno o ";
+    $transposed_by_2=" pùltonù."; }
+
+  if ($language eq "german") {
+    $alphabetical_name="Alphabetischer index";
+    $transposed_by_1="Transponiert um ";
+    $transposed_by_2=" Halbtöne."; }}
+
+# {{{ finalize_options
+
+sub finalize_options {
+
+    finalize_language_dependent_settings();
+
+    $H_chord=0;
+    $chord_style_string=$chord_style;
+    for ($chord_style_string) {
+       $H_chord=1         if (/h/);
+       $chord_style="-"   if (/jazz/ or /-/);
+       $chord_style="mi"  if (/mi/);
+       $chord_style="low" if (/low/); }
+
+    if ($columns == 2) {
+       $pagewidth="0.47\\textwidth";
+       $twocolumns="[twocolumn]";
+
+       $hoffset=-1.1;
+       $textwidth=16-2*$hoffset; }
+    else {
+       $pagewidth="0.9\\textwidth";
+       $twocolumns="";
+
+       $hoffset=-0.5;
+       $textwidth=16-2*$hoffset;
+
+       $hoffset-=2; }
+
+
+    $font_sizes=0 if ($font_sizes<0);
+    $font_sizes=3 if ($font_sizes>3);
+
+    $text_font_size=$tex_font_size[$font_sizes+2];
+    $chord_font_size=$tex_font_size[$font_sizes+1];
+    $song_title_font_size=$tex_font_size[$font_sizes+4];
+    
+    $tabuline_max=180/(($font_sizes+1)**0.7*$columns**0.7);
+    $tabuline_norm=$tabuline_max*(0.6)."em";
+    $bearable_length=$tabuline_max*(0.85);
+
+
+
+    $songtitles_newpage="";
+    for ($title_style) {
+       $songtitles_newpage="\\newpage" if (/songnewpage/); }
+
+    $album_title_font_size="\\Huge";
+
+    setup_collation();
+
+    #print STDERR %collation_hash;
+}
+
+# }}}
+
+# }}} 
+
+# {{{  shared functions                 
+
+sub min {
+    return $_[0]<$_[1]?$_[0]:$_[1]; }
+
+sub find_chords {
+    my $crdprep = $_[0];
+    for ($crdprep) {
+       s/^[^\]]*\[//;
+       s/\][^\]]*$//; }
+    return split (/][^[]*\[/, $crdprep); }
+
+sub find_text {
+    # parameters: 1 - string of mixed text/chord
+    #             2 - possibly bool indicating whether we sould
+    #                 fix odd characters for tex
+    @text = split (/\[[^:\]]*\]/,$_[0]);
+    if ($_[1]) {
+        for (@text) {
+            $_=fix_odd_characters($_); }}
+    return @text; }
+
+sub to_nobarre_if_required {
+  if ($nobarre) {
+    my $songstart=0;
+    @tpose=();
+    push @input,"{title:none}";          # Add one false song start at an end
+    my $i=0;
+    while ($i<=$#input) {
+      if ($input[$i] =~ /\x7btitle:/) {
+        $transposition="nobarre";
+        transpose();
+        # warning "I am transposing, sir.\n";
+        splice @input,$songstart,$i-$songstart,@tpose;
+        $i=$songstart+$#tpose+1; # Correct $i so that it points to
+        # position after inserted transposed song
+        @tpose=$input[$i];
+        $songstart=$i; }
+      else {
+        push @tpose,$input[$i]; }
+      ++$i;}
+    pop @input; }} # Pop false song start
+
+%to_xml_text_conversion_table_windows1250 =
+  (
+   "9e" => "17e", # z^
+   "9a" => "161", # s^
+   "e8" => "10d", # c^
+   "f8" => "159", # r^
+   "ef" => "10f", # d^
+   "9d" => "165", # t^
+   "f2" => "148", # n^
+   "ec" => "11b", # e^
+   "f9" => "16f", # u°
+   "e5" => "13a", # l'
+
+   "8e" => "17d", # Z^
+   "8a" => "160", # S^
+   "c8" => "10c", # C^
+   "d8" => "158", # R^
+   "cf" => "10e", # D^
+   "8d" => "164", # T^
+   "d2" => "147", # N^
+   "cc" => "11a", # E^
+   "d9" => "16e", # U°
+   "bc" => "13d" # L'
+   # TODO: make difference between l acute and l caron
+  );
+
+%to_xml_text_conversion_table_isolatin2 =
+  (
+   "be" => "17e", # z^
+   "b9" => "161", # s^
+   "e8" => "10d", # c^
+   "f8" => "159", # r^
+   "ef" => "10f", # d^
+   "bb" => "165", # t^
+   "f2" => "148", # n^
+   "ec" => "11b", # e^
+   "f9" => "16f", # u°
+   "cd" => "13a", # l acute
+   "b5" => "13e", # l caron
+
+   "ae" => "17d", # Z^
+   "a9" => "160", # S^
+   "c8" => "10c", # C^
+   "d8" => "158", # R^
+   "cf" => "10e", # D^
+   "ab" => "164", # T^
+   "d2" => "147", # N^
+   "cc" => "11a", # E^
+   "d9" => "16e", # U°
+   "c5" => "139", # L acute
+   "a5" => "13d" # L caron
+  );
+
+
+sub to_xml_text {
+    $text = $_[0];
+    $text =~ s/&/&amp;/g;
+    $text =~ s/>/&gt;/g;
+    $text =~ s/</&lt;/g;
+    $text =~ s/\"/&quot;/g;
+
+    if ($inputenc) {
+      # Convert national character to unicode, Windows 1250 and Iso Latin 2
+      # We convert only those characters which cannot be handled
+      # properly without conversion. For instance, we do not
+      # convert y'.
+
+      if ($inputenc eq "cp1250") {
+        while (($src, $dst) = each %to_xml_text_conversion_table_windows1250) {
+          $text =~ s/\x$src/&#x$dst;/g; }}
+      if ($inputenc eq "latin2") {
+        while (($src, $dst) = each %to_xml_text_conversion_table_isolatin2) {
+          $text =~ s/\x$src/&#x$dst;/g; }}}
+
+    return "$text"; }
+
+sub read_input_into_input_array {
+    # Global interface:
+    #   $opt_f
+    #   @input
+    #   $stdout_opened
+    #   $verbatim_lines
+
+    my $output_file_suffix = $_[0];
+
+    if (defined($opt_f)) {
+
+       open(FILE,"$opt_f") or warning("File \"$opt_f\" does not exist."),exit;
+       $mainpath=$opt_f;$mainpath =~ s|/[^/]*$|/|; chdir "$mainpath";
+
+       # $verbatim_lines=0; <- used to be here but I don't know why.
+       while(<FILE>) {
+           if (/^\x23/)    {next}                      # comment
+           if (s/^ //)     {push @input,"$_";next}     # just one line is verbatim
+           if (/^\s*$/)    {next}                      # whitespace line
+
+           chomp;
+           open(FILE2,"$_") or warning("File \"$_\" does not exist."),exit;
+           my $filename="$_";
+           while(<FILE2>) {
+               #check_for_the_length("$_",$filename,$bearable_length);
+               push @input,"$_"; }
+           close(FILE2);}
+       close(FILE);
+
+       # Open output
+       $output_file=$opt_f;
+       for ($output_file) {
+           if (not s/\.[^.]*$/.$output_file_suffix/) {
+               s/$/.$output_file_suffix/; }}
+
+        # Alphabetical file
+        $output_file_base=$output_file;
+        $output_file_base=~s/\.[^.]*$//; # remove .tex
+
+       open(STDOUT,">".$output_file);
+       $stdout_opened=1; }
+    else {
+       while (<>) {
+           #check_for_the_length("$_",$ARGV,$bearable_length);
+           push @input,"$_"; }}}
+
+# }}}
+# {{{  transposition "class"            
+
+# transposition functions and constants are listed
+# here because they are neede not only in transposition
+# but also in tex setting
+
+
+# {{{ constants                        
+%chord_to_offset = ("C", 0,"C#",1, "Db",1,
+                    "D", 2, "D#",3, "Eb",3,
+                    "E", 4,
+                    "F", 5,"F#",6,"Gb",6,
+                    "G", 7,"G#",8,"Ab",8,
+                    "A", 9,"A#",10,"Bb",10,
+                    "H", 11,
+                    "B", 11);
+
+# chord_price
+
+for my $offset (0..11) {
+    for my $minor (0..1) {
+       $chord_price[$offset][$minor]=0; }}
+
+$chord_price[0][0]=-2;
+$chord_price[5][0]=-1;
+$chord_price[7][0]=-1;
+$chord_price[2][1]=-1;
+$chord_price[4][1]=-1;
+$chord_price[9][1]=-2;
+
+# key_norm
+
+@key_norm=("b","b","#","b","#","b","b","#","b","#","b","#");
+
+# chord_barre
+
+for my $offset (0..11) {
+    for my $minor (0..1) {
+        $chord_barre[$offset][$minor]=1; }}
+
+$chord_barre[0][0]=0;
+$chord_barre[2][0]=0;
+$chord_barre[2][1]=0;
+$chord_barre[4][0]=0;
+$chord_barre[4][1]=0;
+$chord_barre[7][0]=0;
+$chord_barre[9][0]=0;
+$chord_barre[9][1]=0;
+
+# barre recognition is simplified
+# e.g. B7 is not barre, but we care only about base note and
+# major/minor.
+
+# }}}
+
+sub transpose_basic {
+    # global $norm, $shift
+
+    # Down shares 
+
+    $transposed=$_[0];
+    for (my $i=0; $i<$shift; ++$i) {
+        transpose_basic_one_up();}
+
+    # normalize
+    if ($norm eq "b") {
+        for ($transposed) {
+            s/C\x23/Db/;
+            s/D\x23/Eb/;
+            s/F\x23/Gb/;
+            s/G\x23/Ab/;
+            s/A\x23/Bb/; }}
+    else {
+        for ($transposed) {
+            s/Db/C\x23/;
+            s/Eb/D\x23/;
+            s/Gb/F\x23/;
+            s/Ab/G\x23/;
+            s/Bb/A\x23/; }}
+
+    for ($transposed) {
+        s/mi/m/;
+        s/min/m/;
+        s/H/B/; }
+    return $transposed; }
+
+sub transpose_basic_one_up {
+    # global $transposed
+    # Transpose one chord by one halftone up
+
+    for ($transposed) {
+       s/H/B/;
+        if (s/C\x23/D/) {last;}
+        if (s/D\x23/E/) {last;}
+        if (s/F\x23/G/) {last;}
+        if (s/G\x23/A/) {last;}
+        if (s/A\x23/B/) {last;}
+
+        if (s/Db/D/)    {last;}
+        if (s/Eb/E/)    {last;}
+        if (s/Gb/G/)    {last;}
+        if (s/Ab/A/)    {last;}
+        if (s/Bb/B/)    {last;}
+
+        if (s/C/C\x23/) {last;}
+        if (s/D/D\x23/) {last;}
+        if (s/E/F/)     {last;}
+        if (s/F/F\x23/) {last;}
+        if (s/G/G\x23/) {last;}
+        if (s/A/A\x23/) {last;}
+        if (s/B/C/)     {last;}}}
+
+sub transpose {
+    # global @tpose, $transposition
+    # global /*out*/ @tpose
+
+    # $transposition is one of:
+    #      . "nobarre"
+    #      . an integer (number of halftones to be transposed up)
+    #      . destination key
+
+    # @tpose is an array of lines from chordpro songfile to be transposed
+
+    # <i>Normalization</i> is setting either with # or with b depending on
+    # key of the paragraph.
+
+
+
+    # {{{ Count chord frequencies          
+
+    #    count separately for each paragraph
+
+    $paragraph=0;
+    $was_space=1;
+
+    for (@tpose) {
+       chomp;$_.="\n";     #Every line _really_ has endline character
+
+       # {{{ Chords
+     
+       if (/\[/) {
+           enter_paragraph_if_required();
+
+           my @chords = find_chords($_);
+
+           for (@chords) {
+               s/^\((.*)\)$/$1/; #kill brackets
+                
+               s/\/.*$//;       #kill bass
+               s/maj//;
+               $minor=(/m/);
+               $minor=0 if (not $minor);
+
+               $base=substr($_,0,1);
+               $base.="b" if (/^.b/);
+               $base.="#" if (/^.\x23/);
+               #print "$paragraph\n";
+               ++$chord_count[$paragraph][$chord_to_offset{$base}][$minor]; }
+           next }
+
+       # }}}
+       # {{{ Whitespace
+       if (/^\s*$/) {
+           $was_space=1; next }
+       # }}}
+       # {{{ Text
+       enter_paragraph_if_required();
+       # }}}
+    }
+    $paragraphs=$paragraph;
+
+    #    global statistics if nobarre transposition
+
+    if ($transposition eq "nobarre") {
+       for $minor (0..1) {
+           for $offset (0..11) {
+               $song_chord_count[$offset][$minor]=0; }}
+       for $paragraph (1..$paragraphs) {
+           for $minor (0..1) {
+               for $offset (0..11) {
+                   $song_chord_count[$offset][$minor]+=
+                       $chord_count[$paragraph][$offset][$minor]; }}}}
+
+    # }}}
+    # {{{ Determine best keys              
+
+    for ($paragraph=1; $paragraph<=$paragraphs; ++$paragraph) {
+
+       # {{{ debugging print
+       #       print $chord_count[$paragraph];print "\n\n";
+
+       #for my $minor (0..1) {
+       #    print "min:$minor: ";
+       #    for my $chord (0..11) {
+       #       print "$chord_count[$paragraph][$chord][$minor] ";
+       #    }
+       #    print "\n";
+       #}
+       # }}}
+
+       $bestvalue0=10000;
+       $bestkey0=0;
+       for $key (0..11) {
+           $value=0;
+           for my $chord (0..11) {
+               for my $minor (0..1) {
+                   $value+=$chord_price[($chord-$key) % 12][$minor] *
+                        $chord_count[$paragraph][$chord][$minor]; }}
+           $bestvalue0=$value,$bestkey0=$key if $value<$bestvalue0; }
+       $bestkey[$paragraph]=$bestkey0; 
+       $bestvalue[$paragraph]=$bestvalue0; }
+    # }}}
+    # {{{ Determine numeric shift          
+
+    if ($transposition eq "nobarre") {
+       $bestshift=0;
+       $bestprice=100000;
+       for $shift (0..11) {
+           $price=0;
+           for $minor (0..1) {
+               for $offset (0..11) {
+                   $price+=($song_chord_count[$offset][$minor]
+                            * $chord_barre[($offset+$shift)%12][$minor]) }}
+           if ($price<$bestprice) {
+               $bestprice=$price;
+               $bestshift=$shift; }}
+       $shift=$bestshift; }
+    elsif ($transposition =~ /^[-0-9]+$/) {
+       $shift=$transposition % 12; }
+    else {
+       $shift=-1;
+       for $paragraph (1..$paragraphs) {
+           if ($bestvalue[$paragraph]<0) {
+               if (not exists $chord_to_offset{$transposition}) {
+                   warning("Key \"$transposition\" is unknown.\n");
+                   exit; }
+               $shift=($chord_to_offset{$transposition}-$bestkey[$paragraph]) % 12;
+               last; }}}
+
+    # }}}
+    # {{{ Transpose and normalize          
+    $paragraph=0;
+    $was_space=1;
+
+    for (@tpose) {
+       if (/\[/) {                       # Chord instructions  
+           if ($was_space) {
+               $was_space=0;
+               ++$paragraph;
+               $norm=$key_norm[($bestkey[$paragraph]+$shift)%12]; }
+
+           # {{{ Ensure chords contain no spaces
+           if (/\[[^\]]* [^\]]*\]/) {
+               warning "\nSetchord: Chords cannot contain spaces.\n";
+               warning "This was broken at file $ARGV:\n";
+               warning $_;
+               exit; }
+           # }}}
+
+           my @text = split (/\[[^\]]*\]/,$_);
+           my @chords = find_chords($_);
+           # {{{ Transpose
+           for (@chords) {
+               @basses = split (/\//,$_);
+               $tpose=transpose_basic($basses[0]);
+               $tpose.="/".transpose_basic($basses[1]) if ($#basses==1);
+               $_=$tpose; }
+           # }}}
+           # {{{ Print everything out
+           my $out = shift @text;
+           my $textpos=0;
+           for (@chords) {
+               $out.="[$_]$text[$textpos]";
+               ++$textpos; }
+           $_= $out;            # Write the result back to array
+           # }}}
+           next; }
+       if (/^\s*$/) {                    # Whitespace                     
+           $was_space=1; next; }
+       if ($was_space) {                 # ext or instruction            
+           $was_space=0;
+           ++$paragraph;
+           $norm=$key_norm[($bestkey[$paragraph]+$shift)%12]; }}
+    # }}}
+    # {{{ Inform about cappo (the case of nobarre)
+
+    if ($transposition eq "nobarre" and $shift!=0) {
+       $capo=(12-$shift);
+       if ($capo<6) {
+           $capotext="{c:Cappo $capo}\n"}
+       else {
+           $capotext="{c:".$transposed_by_1.$shift.$transposed_by_2."}\n"}
+
+       splice @tpose,1,0,$capotext; }
+
+    # }}}
+}
+
+sub enter_paragraph_if_required {
+    # initializes @chord_count array by the way
+    if ($was_space) {
+       ++$paragraph; $was_space=0;
+
+       for my $chord (0..11) {
+           for my $minor (0..1) {
+               $chord_count[$paragraph][$chord][$minor]=0;}}}}
+
+
+# }}}
+
+# {{{  collation functions              
+sub setup_collation {
+    # global $language,%collation
+    # print STDERR "[".$language."]";
+
+    if (defined($collation{$language})) {
+        #print STDERR "defined";
+        @collation_list=split(//,$collation{$language}{"list"});
+        $i=0;
+        for (@collation_list) {
+            $collation_hash{$_}=$i;
+            ++$i;}}
+    if (defined($collation{$language}{"replace"})) {
+        $collation_replace_ref=$collation{$language}{"replace"};
+        %collation_replace=%$collation_replace_ref; }}
+
+sub by_locale_collation {
+    # global $language,%collation
+    # print STDERR "byloc";
+
+    my $aa=$a;
+    my $bb=$b;
+    while (($old, $new) = each %collation_replace) {
+        $aa=~s/$old/$new/g;
+        $bb=~s/$old/$new/g; }
+
+    $i=0;
+    $min_length=min(length($aa),length($bb));
+    # print STDERR $min_length;
+    while ($i<$min_length) {
+        # print STDERR "[".substr($aa,$i,1)."]";
+        if ($collation_hash{substr($aa,$i,1)} < $collation_hash{substr($bb,$i,1)}) {
+            return -1; }
+        if ($collation_hash{substr($aa,$i,1)} > $collation_hash{substr($bb,$i,1)}) {
+            return 1; }
+        ++$i; }
+    return 0; }
+# }}}
+
+$task = shift @ARGV;
+
+# {{{  undefined task                   
+if (not defined($task)) {
+    print STDERR $help_message;
+    exit; }
+# }}}
+# {{{  learn os dependecies             
+$long_newlines_os=0;
+if ($^O eq "dos" or $^O eq "MSWin32" or $^O eq "os2") {
+    $long_newlines_os=1; }
+# }}}
+# {{{  tex                              
+
+    # {{{  set_one_chord                   
+sub set_one_chord {
+    my $set="";
+    $_=$_[0];
+    
+    # {{{ Switch B <-> H notation (B is common)
+    if ($H_chord) {
+        s/B([^b])/H$1/g;
+        s/B$/H/g; }
+    else {
+        s/H/B/g; }
+    # }}}
+
+    $set.="\\sf ";
+    # It is nice to represent special sequences with nonprintable characters.
+    s/maj/\001/;s/mi/m/;s/min/m/;s/dim/\002/;s/m75-/z/;
+    s/\0017/\001/;s/7\001/\001/;
+
+    #                 brackets
+    my $brackets=0;
+    if (/^\(.*\)$/) {
+        s/\(//;s/\)//;
+        $brackets=1;
+        $set.="("; }
+    #                 basses
+    my $bass="";
+    if (/\//) {
+       @basssplit = split(/\//,"$_");
+       ($bass = $basssplit[1]) =~ s/\043/h/;  #043 is octal hash
+       $_ = $basssplit[0]; }
+
+    my $majset;my $dimset;my $minorset;my $minorshiftedbase;
+
+    # {{{ Chord style dependencies
+    for ($chord_style) {
+       if (/^\-$/) {
+           $majset="\$\\triangle\$";
+           $dimset="o";
+           $minorset="\\raisebox{0.26ex}{--}";
+           $minorshiftedbase="F";
+           last}
+       if (/^mi$/) {
+           $majset="maj7";
+           $dimset="dim";
+           $minorset="mi";
+           $minorshiftedbase="?";
+           last}
+       if (/^m$/) {
+           $majset="7maj";
+           $dimset="dim";
+           $minorset="m";
+           $minorshiftedbase="?";}
+       if (/^low$/) {
+           $majset="7maj";
+           $dimset="dim";
+           $minorset="low";
+           $minorshiftedbase="?";}}
+    # }}}
+
+    my $bot="";  my $top="";
+    my $bot0=""; my $top0="";
+    my $puttobot=0;
+    my $numfound=0;
+    my $force_stay_in_upper_index=0;
+    my @CHORD = split (//, $_);
+    $basenote=uc(shift @CHORD);     # uc() is upper_case()
+
+    
+    # if not reasonable chord, do not try to set indices
+    if (not $basenote=~/[ABCDEFGH]/) {
+        return "\\sf ".$_[0]."\\hskip.7em"; }
+
+    #$set.=$basenote;
+    for (@CHORD) {
+       if ($puttobot) { $bot.=$_; next; }
+       if ($numfound and /[2-9]/ and not $force_stay_in_upper_index) {
+            $bot.=$_; $puttobot=1; next; }
+
+        if (/[-\(]/) {
+            $force_stay_in_upper_index=1; $top.=$_; next; }
+        if (/\)/) {
+            $force_stay_in_upper_index=0; $top.=$_; next; }
+
+       if (/\001/) {
+           my $dest=\$top;
+           if ($numfound) {
+               $puttobot=1;
+               $dest=\$bot;}
+
+           if ($chord_style eq "mi") {
+               $$dest.=7;
+               $bot0.=" " if $bot0;
+               $bot0.="maj";
+               $numfound=1; next; }
+           $$dest.=$majset; $numfound=1;  next; } # maj
+
+       if (/[2-9]/) { $top.="$_"; $numfound=1; next; }
+       if (/\002/) { $top.=$dimset;            next; } # dim
+       if (/z/)    { $top.="\$\\varnothing\$"; next; }
+       if (/b/)    { $top0.="\$\\hskip0.1em\\mathbf{\\flat}\$"; next; }
+       if (/\x23/) { $top0.="\$\\hskip0.1em\\mathbf{\\sharp}\$"; next; }
+       # \x23 is octal hash
+
+       if (/m/)    {                        # minor
+           if ($chord_style eq "-" and 
+               not $basenote eq $minorshiftedbase) {$bot0.="\\hskip0.1em"}
+           $bot0.=$minorset; next;}        
+       if (/\+/)   { $bot.="+"; next; }     # +
+       $top.="$_"; }
+
+    # Set basenote
+
+    if ($chord_style eq "low" and $bot0) {
+       $set.=lc($basenote);
+       $bot0=""; }
+    else {
+       $set.=$basenote; }
+
+    # Now INDEXES are really nasty
+  INDEXES: {
+      if (not $top0 and not $bot and $bot0 and $top and $chord_style eq "-") {
+         $set.="\\crdx{$top}{$bot0}{}{}"; # Typical case of Fm7
+         last INDEXES; }
+      if (not $top and $bot eq "+") {
+         if ($top0 =~ /flat/) {
+             $set.="\\crdx{$top0}{$bot0}{}{}\\hskip-.3em+";
+             last INDEXES; }
+         
+         $set.="\\crdx{$top0}{$bot0}{}{}\\hskip-.1em+";
+         last INDEXES; }
+
+      if ($top =~ /dim/) {      #case of dim in "m" and "mi" style setting
+         $set.="\\crdx{$top0}{$bot0}{}{}dim";
+         last INDEXES; }
+
+      $set.="\\crdx{$top0}{$bot0}{$top}{$bot}";
+  }
+    #
+
+    @basses = split(//,$bass);
+    $set.="\\crdbass{$bass}{}"                if ($#basses==(1-1));
+    $set.="\\crdbass{$basses[0]}{$basses[1]}" if ($#basses==(2-1));
+    
+    $set.=")"                                 if ($brackets);
+    
+    $set.="\\hskip.7em";
+    return $set; }
+
+
+# }}}
+    # {{{  set_tex_head                    
+
+sub set_tex_head {
+
+    finalize_options();
+    if (defined($output_file_base)) {
+        create_alphabetical_toc ($output_file_base); }
+
+    to_nobarre_if_required();
+
+    $head="\\documentclass${twocolumns}{article}\n";
+    if ($language eq "czech") {
+        $head.="\\usepackage{czech}\n"; }
+    if ($inputenc) {
+        $head.="\\usepackage[$inputenc]{inputenc}\n"; }
+    if ($language eq "german") {
+        $head.="\\usepackage{german}\n"; }
+    $head.="\\usepackage{palatino}
+\\usepackage{amsfonts,amssymb}
+\\usepackage{colortbl}
+\\usepackage{verbatim}
+\\usepackage{graphics}
+\\usepackage{exscale}
+\\textwidth=${textwidth}cm
+\\hoffset=${hoffset}cm
+\\textheight=26cm
+\\voffset=-3cm
+\\columnsep=0.07\\columnwidth% This is the size of white space separating two columns
+%
+%
+%   ==================================
+%      Commands and environments
+%   ==================================
+%
+%
+\\newcommand{\\N}{\\\\\\rule{0pt}{0pt}}
+% /\  This is a newline which does not produce underfull box warnings.
+\\newcommand{\\spc}{\\setbox0=\\hbox{x}\\hskip\\wd0}
+\\newcommand{\\largeskip}{\\bigskip\\bigskip}
+% silent \\par not producing undefull hboxes (hack a little)
+\\newcommand{\\spar}{\\rule{0pt}{0pt}\\par}
+
+\\newcommand{\\maxskip}[2]{%
+\\setbox0=\\hbox{#1}\\setbox1=\\hbox{#2}%
+\\ifdim\\wd0<\\wd1\\hskip\\wd1\\else\\hskip\\wd0\\fi}%
+
+\\newdimen\\tempdimen%
+
+\\newcommand{\\filldifrule}[3]{%
+\\setbox0=\\hbox{#1}\\setbox1=\\hbox{#2}%
+\\ifdim\\wd1<\\wd0%
+\\tempdimen=\\wd0%
+\\advance\\tempdimen by - \\wd1%
+\\ifdim\\tempdimen<0.3em\\tempdimen=0.3em\\fi%
+\\advance\\tempdimen by -0.1em%
+\\hskip0.05em%
+\\rule[.5ex]{\\tempdimen}{0.12ex}%
+\\hskip0.05em%
+\\else%
+#3%
+\\fi}
+
+\\newcommand{\\skipdif}[2]{%
+\\setbox0=\\hbox{#1}\\setbox1=\\hbox{#2}%
+\\ifdim\\wd1<\\wd0%
+\\tempdimen=\\wd0%
+\\advance\\tempdimen by - \\wd1%
+\\hskip\\tempdimen%
+\\fi}
+
+\\newcommand{\\leftrepeat}{%
+\\rule[-0.3ex]{0.05em}{2ex}\\hskip0.1em\\rule[-0.3ex]{0.05em}{2ex}%
+\\hskip0.1em\\raisebox{0.1ex}{:} }
+
+\\newcommand{\\rightrepeat}{%
+ \\raisebox{0.1ex}{:}\\hskip0.1em%
+\\rule[-0.3ex]{0.05em}{2ex}\\hskip0.1em\\rule[-0.3ex]{0.05em}{2ex}}
+
+";
+
+$song_title_shared_start=
+"\\newcommand{\\songtitle}[2]{
+\\spar\\vfill
+$songtitles_newpage%
+\\begin{minipage}{\\columnwidth}%
+\\addcontentsline{toc}{subsection}{#1}%
+";
+
+$song_title_shared_end="\\bigskip
+\\end{minipage}\\nopagebreak[4]\\par\\nopagebreak[4]}";
+
+
+
+SONGTITLE: {
+    if ($title_style =~ /norule/) {
+       $head.=$song_title_shared_start.
+"{$song_title_font_size \\sf\\bfseries #1\\\\[0.2ex]}%
+{\\it #2}%
+".$song_title_shared_end;
+       last SONGTITLE; }
+
+    if ($title_style =~ /graybox/) {
+       $head.=$song_title_shared_start.
+"\\begin{tabular}{>{\\columncolor[gray]{0.8}}p{\\textwidth}}%
+$song_title_font_size \\sf\\bfseries\\rule{0pt}{1.6ex}#1%
+\\end{tabular}\\\\[1.5ex]%
+{\\it #2}%
+".$song_title_shared_end;
+       last SONGTITLE; }
+
+    # default style - rule
+
+    $head.=$song_title_shared_start.
+    "\\rule{\\textwidth}{.5ex}\\\\[1.3ex]\n".
+    "{$song_title_font_size \\sf\\bfseries #1\\\\[0.2ex]}%\n".
+    "{\\it #2}%\n".$song_title_shared_end;
+
+}
+
+    $head.="
+\\newcommand{\\albumtitle}[1]{
+%\\spar
+\\vfill
+\\newpage
+\\begin{minipage}{\\columnwidth}
+\\addcontentsline{toc}{section}{#1}
+\\rule{\\textwidth}{.7ex}\\\\
+$album_title_font_size \\sf\\bfseries #1%
+\\bigskip\\bigskip\\bigskip
+\\end{minipage}}
+
+\\newcommand{\\tabuline}[1]{
+\\def\\emptyparameter{}%
+\\def\\currentparameter{#1}%
+\\ifx\\emptyparameter\\currentparameter%
+% This is a case of empty line. Empty line is not as high as nonempty line.
+\\colorbox[gray]{0.87}{\\rule{0pt}{1ex}\\rule{0.95\\textwidth}{0pt}}%
+\\else%
+% This is a case of nonempty line.
+\\colorbox[gray]{0.87}{%
+\\resizebox{0.95\\textwidth}{1.5ex}{%
+\\rule{0pt}{1.5ex}#1\\setbox0=\\hbox{#1}\\hskip-\\wd0\\rule{$tabuline_norm}{0pt}%
+}%
+}%
+\\fi%
+\\\\[-0.3ex]%
+}
+";
+
+    $head.= "
+\\newcommand{\\crdx}[4]{
+\\hskip-0.3em\\lower-0.2ex\\hbox{$chord_font_size\$^\\textsf{#1}_\\textsf{#2}\$}%
+\\hskip0.25em
+\\if:#1:\\if:#2:\\hskip0.1em\\fi\\fi
+\\hskip-0.3em\\lower-0.2ex\\hbox{$chord_font_size\$^\\textsf{#3}_\\textsf{#4}\$}}
+"  if ($chord_style eq "-");
+
+    $head.= "
+\\newcommand{\\crdx}[4]{
+\\hskip-0.3em\\lower-0.2ex\\hbox{$chord_font_size\$^\\textsf{#1}\$}%
+\\hskip0.25em%
+\\if:#2:
+\\else\\hskip-0.3em{}#2\\hskip0.3em\\fi
+\\hskip-0.3em\\lower-0.2ex\\hbox{$chord_font_size\$^\\textsf{#3}_\\textsf{#4}\$}}
+"  if ($chord_style =~ /m|mi|low/);
+
+    $head.= "
+\\newcommand{\\crdbass}[2]{
+\\hskip-0.4em\\big/#1\\crdx{\$%
+\\if b#2%
+\\flat\\fi\\if h#2%
+\\sharp\\fi\$}{}{}{}}
+
+\\newenvironment{tabbingnb}
+{\\noindent
+\\begin{minipage}{0.4\\textwidth}\\begin{tabbing}}
+{\\end{tabbing}\\end{minipage}\\par\\vskip-\\baselineskip}\n";
+
+$head_after_begin_document="
+%
+%
+%            ===========================
+%                  BEGIN DOCUMENT
+%            ===========================
+%
+%
+\\begin{document}
+\\setlength{\\parindent}{0pt}
+\\boldmath
+$text_font_size\n";
+$head_after_begin_document.="\\csprimeson\n" if ($language eq "czech");
+
+#------------------------------------
+
+$table_of_contents.= "
+%
+%
+%                =====================
+%                  TABLE OF CONTENTS
+%                =====================
+%
+%
+\\thispagestyle{empty}
+
+\\tableofcontents
+
+%This is macro of Petr Olsak. It inputs the file but
+%does not cry, if file does not exist
+
+\\newread\\testin
+\\def\\softinput #1 {\\let\\next=\\relax \\openin\\testin=#1
+\\ifeof\\testin%
+\\else\\closein\\testin\\def\\next{\\input #1 }\\fi
+\\next}
+
+% Insert alphabetical table of contents,
+% if there is one
+
+\\openin\\testin=\\jobname.atoc
+\\ifeof\\testin\\closein\\testin%
+\\else\\closein\\testin
+\\newpage
+\\section*{$alphabetical_name}
+\\softinput \\jobname.atoc
+\\fi
+
+\\clearpage\n";
+
+$titlepage_head="
+%
+%        =======================
+%               Titlepage
+%        =======================
+%
+\\thispagestyle{empty}
+\\ 
+\\vskip16\\baselineskip
+{\\Huge
+\\begin{tabular*}{\\textwidth}{c}
+\\hskip\\textwidth\\ \\\\
+\\bfseries ";
+
+$titlepage_tail= "
+\\end{tabular*}}
+\\clearpage";
+
+}
+
+# }}}
+    # {{{  fix_odd_characters              
+sub fix_odd_characters {
+    # fix odd characters for normal text and chords
+    # (there is other slightly different fixing process for tabulatures)
+
+    $_=$_[0];
+
+    # Backslashes previously inserted by chordpack
+    # are coded by character with ascii code 01.
+
+    # assumption - there are no nonprintable characters
+    # this assumption may later be explicitly checked
+
+    s/\\/\x00/g;  # mark backslashes
+   
+    for ($ascii=33;$ascii<=38;++$ascii) {
+        $re="\\x".sprintf "%.2x",$ascii;
+        s/$re/\\char$ascii\x02/g; }        #\x02 is reserved for {}
+
+    s/\{/\$\\{\$/g;    s/\}/\$\\}\$/g;
+    s/\^/\\char094\x02/g;
+    s/\|/\$\|\$/g;
+    s/</\$<\$/g;
+    s/>/\$>\$/g;
+    s/~/\\~{}/g;
+    s/\[/\$\[\$/g;    s/\]/\$\]\$/g;
+
+   
+    # backslashes must be done on their own
+    s/\x00/\$\\backslash\$/g;
+    s/\x01/\\/g;
+    s/\x02/\{\}/g;
+    return $_;
+}
+# }}}
+    # {{{  set_songtitle                   
+sub set_songtitle {
+    # global $songtitle
+    # global @subtitles
+
+    $subtitle_set="";
+    for (@subtitles) {
+        $subtitle_set.=$_."\\\\"; }
+    print "%\n%\n%\n%\n\\songtitle{$songtitle}{$subtitle_set}%\n"; }
+
+# }}}
+    # {{{  previous_block_care             
+sub previous_block_care {
+    # global $previous_block
+
+    $pb=$previous_block;
+
+    if ($previous_block==1) {}
+
+    if ($pb==1) {
+        print $_[0]; }
+    if ($pb==2) {
+        print $_[1]; }
+    if ($pb==3) {
+        set_songtitle(); }
+    $previous_block=0; }  # global change
+
+# }}}
+    # {{{  create_alphabetical_toc         
+sub create_alphabetical_toc {
+    # global $previous_block
+
+    # print STDERR "creating alphabetical";
+
+    $output_file_base=$_[0];
+
+    if (open(TOC,$output_file_base.".toc")) {
+       while (<TOC>) {
+           push @toc,$_; }
+        close (TOC);
+
+        # There may be a problem with sorting for languages. I do not know a solution.
+        # I suppose in that case alphabetical must be edited manually.
+
+        # To be done:
+        #     . switch and option producing alphabetical
+        #     . discuss languages
+        #     . update documentation (not alphabetical, but atoc)
+
+        # print STDERR %collation_hash;
+
+        if (%collation_hash)
+        {
+            # not empty
+            @sorted_toc = sort by_locale_collation @toc;
+        }
+        else
+        {
+            @sorted_toc = sort @toc;
+        }
+
+        @alpha_toc = grep(!/{sect.*}/, @sorted_toc);    
+        
+        open(ATOC,">".$output_file_base.".atoc");
+        for (@alpha_toc) {
+            print ATOC $_; }
+        close (ATOC); }}
+
+# }}}
+
+
+if ($task eq "tex") {
+
+    my $previous_line=0;
+    $previous_block=0;
+    # previous_line:  0=emptyline,      1=text_line, 2=chord, 3=songtitle, 4=albumtitle
+    # previous_block: 0=we're in block, 1=text_line, 2=chord, 3=songtitle, 4=albumtitle
+
+
+    # Definitions:
+    #    White line is a line which contains only whitespace character.
+    #    Block is a maximal sequence of lines which are not white.
+    #    Lines can be of several kinds, one block can contain lines of
+    #    different kinds.
+    #    A kind of a block is the kind of last line of that block.
+
+    #    Variable Previousblock contains the kind of previous block.
+    #    An exception to this is songtitle, which sets previousblock explicitly
+    #    as it's current block.
+
+    %files_warned=();
+    $head_printed=0;
+    $verbatim_tex=0;
+    $table_of_contents_printed=0;
+    $tex_prebegin_part=0;
+    $in_tablature=0;
+    $subtitles_enabled=0;
+
+    @input=();
+    $stdout_opened=0;
+
+    # Read input into @input variable
+    read_input_into_input_array("tex");
+
+    for (@input) {
+        # warning "input: $_";
+        chomp;
+        # {{{ Remove carriage return          
+        if (not $long_newlines_os) {
+            if (s/\x0d//g) {
+                if (not $carriage_return_warned) {
+                    warning "Your chordpro files have DOS carriage return ends of line".
+                        " - not a serious problem.\n";
+                    $carriage_return_warned=1; }}}
+        # }}}
+
+        # {{{ Parenthesis                     
+        s/\" /\'\' /g;
+        s/\"$/\'\'/g;
+        s/ \"/ \`\`/g;
+        s/^\"/\`\`/g;
+        # }}}
+
+        #                                Process line
+
+        # {{{  Head not yet printed           
+
+        if (not $head_printed) {
+            # {{{ Remove comment
+            s/\x23.*$//; 
+            # }}}
+
+            # {{{ In TeX prebegin
+
+            if ($tex_prebegin_part) {
+                if (/\173tex_prebegin_end/) {
+                    $tex_prebegin_part=0;
+                    set_tex_head();
+                    print "$head";
+                    print "$tex_prebegin_text";
+                    print "$head_after_begin_document";
+                    $head_printed=1;
+                    next; }
+                $tex_prebegin_text.="$_\n"; next}
+
+            # }}}
+
+            # {{{ Chordstyle
+            if (/{chordstyle:.*}/) {
+                if ($from_options{"chord_style"}) {
+                    next; }
+
+                s/{[^:]*: *//; s/}//;
+                $chord_style=$_;
+                next; }
+            # }}}
+            # {{{ Language
+            if (/{language:.*}/) {
+                if ($from_options{"language"}) {
+                    next; }
+
+                s/{[^:]*: *//; s/}//;
+                $language=$_;
+                next; }
+            # }}}
+            # {{{ Fontsize
+            if (/{fontsize:.*}/) {
+                if ($from_options{"font_sizes"}) {
+                    next; }
+
+                s/{[^:]*: *//; s/}//;
+
+                $font_sizes=$_;
+                next; }
+            # }}}
+
+            # {{{ Title style
+            if (/{titlestyle:.*}/) {
+                if ($from_options{"title_style"}) {
+                    next; }
+
+                s/{[^:]*: *//; s/}//;
+
+                $title_style=$_;
+                next; }
+            # }}}
+            # {{{ Columns
+            if (/{columns:.*}/) {
+                if ($from_options{"columns"}) {
+                    next; }
+
+                s/{[^:]*: *//; s/}//;
+
+                $columns=$_;
+                next; }
+            # }}}
+
+            # {{{ Emptyline
+            if (/^\s*$/) {
+                next; }
+            # }}}
+
+
+            # {{{ Songbook title
+            if (/{songbooktitle:.*}/) {
+                s/{[^:]*: *//; s/}//;
+                s/&/\\&/g;
+
+                @titlelist = split (/\^/, $_ );
+                
+                set_tex_head();            
+                print "$head";
+                print "$head_after_begin_document";
+                print "$titlepage_head";
+
+                for (@titlelist) {
+                    print "$_\\\\\n"; }
+
+                print "$titlepage_tail";
+                print "$table_of_contents";
+                $table_of_contents_printed=1;
+                
+                $head_printed=1;
+                next; }
+            # }}}
+            # {{{ Songbook title not found
+            if (/{tex_prebegin_start.*}/) {
+                $tex_prebegin_part=1;
+                $tex_prebegin_text=""; next}
+            
+            $backup = $_;
+            set_tex_head();
+            print "$head";
+            print "$head_after_begin_document";
+            $head_printed=1;
+            $_ = $backup;
+
+            # }}}
+
+        }
+
+        # }}}
+        # {{{  Command allowed only in before head
+        # warning ("Here is $_.");
+        if (/{fontsize.*}/ or
+            /{language.*}/ or /{titlestyle.*}/ or /{columns.*}/
+            or /{chordstyle.*}/ ) {
+            warning ("Command $_ can be used only before first {album: } or {title: } command is used.\n");
+            next; }
+        # }}}
+        # {{{  In Verbatim TeX                
+        if ($verbatim_tex) {
+            if (/{vtexe.*}/ or /{verbatim_tex_end.*}/) {
+                $verbatim_tex=0; next; }
+            print "$_\n"; next; }
+        # }}}
+        # {{{  In tablature                   
+
+        if ($in_tablature) {
+            if (/{eot.*}/ or /{end_of_tab.*}/) {
+                $in_tablature=0;
+                if (not $ignore_tablature) {
+                    # \\rule is here just to prevent underfull hbox messages
+                    print "\\rule{0pt}{0pt}\\end{minipage}\n"};
+                next}
+
+            if (not $ignore_tablature) {
+                $_=substr($_,0,$tabuline_max);
+                s/^ +$//g; #no whitespace lines
+                
+                # {{{ ascii based translation             
+                s/\\/\x00/g;  #mark backslashes
+                s/ /\x01/g;   #mark spaces
+                for ($ascii=33;$ascii<=47;++$ascii) {
+                    $re="\\x".sprintf "%.2x",$ascii;
+                    s/$re/\\char$ascii /g; }
+                for ($ascii=93;$ascii<=96;++$ascii) {
+                    $re="\\x".sprintf "%.2x",$ascii;
+                    s/$re/\\char$ascii /g; }
+                for ($ascii=123;$ascii<=126;++$ascii) {
+                    $re="\\x".sprintf "%.2x",$ascii;
+                    s/$re/\\char$ascii /g; }
+                # backslashes and spaces must be done on their own
+                s/\x00/\\char92 /g;
+                s/\x01/\\hskip0.602em /g;
+                # }}}
+
+                print "\\tabuline{$_}\n";}
+            next}
+
+        # }}}
+
+        #                                Line contains
+
+        # {{{  Comment (programmer's kind of) 
+        if (/^\043/) {    # 043 is octal of hash
+            next}
+        # }}}
+
+        # {{{  Subtitles_on command               
+        if (/{subtitles_on.*}/) {
+            $subtitles_enabled=1;
+            next; }
+        # }}}
+        # {{{  Subtitles_off command               
+        if (/{subtitles_off.*}/) {
+            $subtitles_enabled=0;
+            next; }
+        # }}}
+
+        # {{{  Start of verbatim TeX          
+        if (/{vtexs.*}/ or /{verbatim_tex_start.*}/) {
+            $verbatim_tex=1; next }
+        # }}}
+        # {{{  Start of Tablature             
+        if (/{sot.*}/ or /{start_of_tab.*}/) {
+            previous_block_care("\\spar\n","\\spar\\largeskip\n");
+            $in_tablature=1;     
+            if (not $ignore_tablature) {
+                print "\n\\begin{minipage}{\\columnwidth}\\tt\n";
+                $previous_line=1; }
+            next; }
+        # }}}
+
+        # {{{  Table of contents              
+
+        if (/{toc.*}/ or /{table_of_contents.*}/) {
+            warning ("TOC FOUND\n");
+            if ($table_of_contents_printed) {
+                warning("You ask me to print table of contents though it\n".
+                        "has already been printed with songbook's titlepage.\n"); }
+            else {
+                warning ("toc\n");
+                print "$table_of_contents"; }
+            next; }
+
+        # }}}
+
+        # {{{  Title command                  
+        if (/{t:.*}/ or /{title:.*}/) {
+            s/{[^:]*: *//; s/}//;
+
+            if ($previous_line!=0) {
+                $previous_block=$previous_line }
+            #$previous_line=0;
+            
+            # print "\\bigskip"            if ($previous_line==0);
+            previous_block_care("\\bigskip","\\bigskip\\bigskip");
+
+            #s/&/\\&/g;                   # hack, I don't like this
+            #print "%\n%\n%\n%\n\\songtitle{$_}{}%\n";
+            $previous_line=3;
+            $previous_block=3;    # explicit previousblock
+
+            $songtitle=fix_odd_characters($_);
+            #$songtitle=$_;
+            @subtitles=();
+            next; }
+        # }}}
+        # {{{  Subtitle command               
+        if (/{st:.*}/ or /{subtitle:.*}/) {
+            s/{[^:]*: *//; s/}//;
+            
+            if ($subtitles_enabled) {
+                push @subtitles,fix_odd_characters($_); }
+            $previous_block=3;    # explicit previous_block
+
+            # ignored, so far
+            #print "\n\\bigskip"          if ($previous_line==1);
+            #print "\\bigskip\\bigskip"   if ($previous_line==2);
+            #print "\\bigskip"            if ($previous_line==0);
+
+            #s/\173[^:]*: *//; s/\175//;  # 173 is octal left curly brace 175 is right
+            #s/&/\\&/g;
+            #print "%\n%\n%\n%\n\\songtitle{$_}%\n";
+            #$previous_line=3;
+            next}
+        # }}}
+        # {{{  Album command                  
+        if (/{album:.*}/) {
+            
+            print "\n\\bigskip"          if ($previous_line==1);
+            print "\\bigskip\\bigskip"   if ($previous_line==2);
+            print "\\bigskip"            if ($previous_line==0);
+
+            s/\173[^:]*: *//; s/\175//;  # 173 is octal left curly brace 175 is right
+            s/&/\\&/g;
+            print "%\n" .
+                "%\n" .
+                "%          ======================\n" .
+                "%            $_\n".
+                "%          ======================\n" .
+                "%\n" .
+                "%\n\\albumtitle{$_}%\n";
+            $previous_line=4;
+            next}
+        # }}}
+        # {{{  Start of choir                 
+        if (/{soc.*}/ or /{start_of_chorus.*}/) {
+            print "\\it\n";
+            next}
+        # }}}
+        # {{{  End of choir                   
+        if (/{eoc.*}/ or /{end_of_chorus.*}/) {
+            print "\\rm\n";
+            next}
+        # }}}
+        # {{{  Comment                        
+        if (/{c:.*}/ or /{comment:.*}/ or /{comment_italic:.*}/ or /{comment_box:.*}/) {
+            s/{[^:]*: *//; s/}//;
+            s/&/\\&/g;
+
+            previous_block_care("\\spar\n","\\spar\\largeskip\n");
+
+            print "{\\it $_\\rm}\\\\\n";
+            $previous_line=1;  #Comment is close to ordinary text
+            next}
+        # }}}
+        # {{{  Chord command (not chordpack)  
+
+        if (/{ns.*}/ or /{new_song.*}/ or /{define.*}/ or /{textfont.*}/ or /{textsize.*}/
+            or /{chordfont.*}/ or /{chordsize.*}/ or /{no_grids.*}/ or /{ng.*}/ or
+            /{grid.*}/ or /{g.*}/ or /{new_page.*}/ or /{np.*}/ or /{new_physical_pages.*}/ or
+            /{npp.*}/ or /{columns_break.*}/ or /{colb.*}/) {
+            warning($_.": Here is a command of chord but not of chordpack\n");
+            next}
+
+        # }}}
+
+        # {{{  Other command                  
+        if (/{.+}/) {
+            warning($_.": unrecognized command\n");
+            next}
+        # }}}
+
+        # {{{  Repeat marks [: :]             
+        # Final backslashes are represented by character with code 0
+        # This DEPENDS on behaviour of fix_odd_characters function
+
+        s/\[:/\x01leftrepeat/g;  s/:\]/\x01rightrepeat/g;
+        # }}}
+
+        # {{{  Chord instructions             
+        if (/\[[^ ].*\]/) {
+            $_.=" ";
+
+            previous_block_care("\\spar\\bigskip\n","\\spar\\largeskip\n");
+            # { Ensure chords contain no spaces
+            if (/\[[^\]]* [^\]]*\]/) {
+                warning "\nSetchord: Chords cannot contain spaces.\n";
+                warning "This was broken at file $ARGV:\n";
+                warning $_;
+                exit;}
+            # }
+
+            s/\]\[/\] \[/g;               #no chords tightly follow
+
+            # { Determine chord and text arrays
+            my @text = find_text($_,1);
+            my @chords = find_chords($_);
+            # }
+            # {{{ Print tab stops for chords, chords and text
+
+            my $tabstops=$text[0];
+            my $text_line=$text[0];
+            my $chord_line="";
+            my $i=1;
+            for (@chords) {
+                $crd = set_one_chord("$_");
+
+                $text[$i] =~ s/^ /\\hskip.7em /;   #chord is preshifted to the left of the text
+                #$tabstops.="\\=\\maxskip{$crd}{$text[$i]}";
+                $chord_line.="\\>$crd";
+                #$text_line.="\\>$text[$i]";
+                # Join two broken parts of word, if needed
+                $text=$text[$i];
+                if ($i<$#text) {
+                    $last_char=substr($text[$i],length($text[$i])-1,1);
+                    $first_char=substr($text[$i+1],0,1);
+                    if (($last_char =~ /^[^ .,]$/) and ($first_char =~ /^[^ .,]$/)) {
+                        for ($text) {
+                            if (s/ ([^ ])/ \\skipdif{$crd}{$text}$1/) {last;}
+                            if (s/ / \\skipdif{$crd}{$text}/) {last;}
+
+                            $present_dash="";
+                            if(s/-+$//) {$present_dash="-"}                 # Remove dashes already present in adjacent
+                            if($text[$i+1] =~ s/^-+([a-zA-Z])/$1/) {$present_dash="-"}  # texts
+                            s/$/\\filldifrule{$crd}{$text}{$present_dash}/;}}}
+
+                $text_line.="\\>$text";
+                $tabstops.="\\=\\maxskip{$crd}{$text}";
+
+                $text[$i] =~ s/^ /\\hskip.7em /g;  #chord is preshifted to the left of the text
+                ++$i;}
+
+            print "\\begin{tabbingnb}\n";
+            print "$tabstops\\kill\n";
+            print "$chord_line\\\\\n";      
+            print "$text_line\\\\\n";
+            print "\\end{tabbingnb}\n";
+
+            # }}}
+
+            $previous_line=2;
+            next}
+        # }}}
+        # {{{  Spaces only                    
+        if (/^ *$/) {
+            $previous_block=$previous_line if ($previous_line!=0);
+            $previous_line=0;
+            next}
+        # }}}
+        # {{{  Text without chords            
+        previous_block_care("\\spar\n","\\spar\\largeskip\n");
+
+        print fix_odd_characters($_)."\\N\n";
+        $previous_line=1;
+        # }}}
+    }
+
+    print "\\spar\\end{document}\n";
+        exit; }
+
+# }}}
+# {{{  ascii                            
+if ($task eq "ascii") {
+
+    my $first_song = 1;
+
+    @input = ();
+    $stdout_opened = 0;
+
+    read_input_into_input_array("txt");
+
+    finalize_language_dependent_settings();
+    to_nobarre_if_required();
+
+    for (@input) {
+        chomp;
+
+        # Process line
+
+        # Line contains:
+        # {{{  Title command
+        if (/{t:.*}/ or /{title:.*}/) {
+
+            if ($first_song) {
+                $first_song = 0; }
+            else {
+                printf "\n\n"; }
+
+            s/{[^:]*: *//; s/}//;
+            printf "$_\n----------------------------------------\n";
+            next; }
+        # }}}
+        # {{{  Comment      
+        if (/{c:.*}/ or /{comment:.*}/) {
+          s/\173[^:]*: *//; s/\175//;  # 173 is octal left curly brace 175 is right
+          print "\n$_\n\n";
+          next; }
+        # }}}
+        # {{{  Other command
+        if (/{.*}/) {
+          next;}
+        # }}}
+
+        # {{{  Comment (programmer's kind of)
+        if (/^\x23/) {    # \x23 is  hash
+            next; }
+        # }}}
+        # {{{  Chord instructions
+        if (/\[[^:]/) {
+            $_.=" ";
+
+            # {{{ Ensure chords contain no spaces
+            if (/\[[^\]]* [^\]]*\]/) {
+                warning "\nSetchord: Chords cannot contain spaces.\n";
+                warning "This was broken at file $ARGV:\n";
+                warning $_;
+                exit; }
+            # }}}
+
+            s/\]\[/\] \[/g;    #no chords tightly follow
+
+            # {{{ Determine chord and text arrays
+            my @text = find_text($_);
+            my @chords = find_chords($_);
+            # }}}
+            # {{{ Print chords and text
+
+            my $chord_line=$text[0]; $chord_line =~ s/./ /g;
+            my $textpos=1;
+            my $text_line=$text[0];
+            for (@chords) {
+                $crd=$_;
+                $chord_line.=$_ . (' ' x (length($text[$textpos])-length($crd)));
+                $text_line.=$text[$textpos] . ' ' x (length($crd)-length($text[$textpos]));
+                $textpos++; }
+
+            print "$chord_line\n$text_line\n";
+
+            # }}}
+
+            next; }
+        # }}}
+        # {{{  Spaces only
+        if (/^ *$/) {
+            print "\n";
+            next;}
+        # }}}
+        # {{{  Text without chords
+        print "$_\n";
+        # }}}
+    }
+exit;
+}
+# }}}
+# {{{  fmm - freemind mind map          
+
+if ($task eq "fmm") {
+
+    $song_opened = 0;
+    $album_opened = 0;
+
+    @input = ();
+    $stdout_opened = 0;
+
+    read_input_into_input_array("mm");
+
+    printf "<map>\n<node text=\"Songbook\" color=\"#996600\"><node text=\" \">\n";
+
+    my $closing_of_song = "\">\n".
+      "<font name=\"Courier New\" size=\"12\"/>\n".
+        "</node>\n</node>\n";
+
+    my $closing_of_album = "</node>\n";
+
+    for (@input) {
+        chomp;
+
+        # Process line
+
+        # Line contains:
+        # {{{  Title command
+        if (/{t:.*}/ or /{title:.*}/) {
+
+            if ($song_opened) {
+              printf $closing_of_song;
+              $song_opened = 0; }
+
+            s/{[^:]*: *//; s/}//;
+            printf "<node text=\"".to_xml_text("$_")."\" color=\"#669900\" folded=\"true\">\n";
+            printf "<node text=\"";
+
+            $song_opened = 1;
+            next; }
+        # }}}
+        # {{{  Album command
+        if (/{album:.*}/) {
+
+            if ($song_opened) {
+              printf $closing_of_song;
+              $song_opened = 0; }
+
+            if ($album_opened) {
+              printf $closing_of_album;
+              $album_opened = 0; }
+
+            s/{[^:]*: *//; s/}//;
+            printf "<node text=\"".to_xml_text("$_")."\" color=\"#006633\" folded=\"true\">\n";
+            $album_opened = 1;
+            next; }
+        # }}}
+        # {{{  Other command
+        if (/{.*}/) {next;}
+        # }}}
+
+        if (not $song_opened) {
+            next; }
+
+        # {{{  Comment (programmer's kind of)
+        if (/^\x23/) {    # \x23 is  hash
+            next; }
+        # }}}
+        # {{{  Chord instructions
+
+        if (/\[[^:]/) {
+            $_.=" ";
+
+            # {{{ Ensure chords contain no spaces
+
+            if (/\[[^\]]* [^\]]*\]/) {
+                warning "\nSetchord: Chords cannot contain spaces.\n";
+                defined($opt_f) or
+                  warning "Chords with spaces occured in file $ARGV:\n";
+                warning $_;
+                next; }
+
+            # }}}
+
+            s/\]\[/\] \[/g;    #no chords tightly follow
+
+            # {{{ Determine chord and text arrays
+            my @text = find_text($_);
+            my @chords = find_chords($_);
+            # }}}
+            # {{{ Print chords and text
+
+            my $chord_line=$text[0]; $chord_line =~ s/./ /g;
+            my $textpos=1;
+            my $text_line=$text[0];
+            for (@chords) {
+                $crd=$_;
+                $chord_line.=$_ . (' ' x (length($text[$textpos])-length($crd)));
+                $text_line.=$text[$textpos] . ' ' x (length($crd)-length($text[$textpos]));
+                $textpos++; }
+
+            print to_xml_text("$chord_line\n$text_line\n");
+            # }}}
+
+            next; }
+
+        # }}}
+        # {{{  Spaces only
+        if (/^ *$/) {
+            print "\n";
+            next;}
+        # }}}
+        # {{{  Text without chords
+        print to_xml_text("$_")."\n";
+        # }}}
+    }
+
+    if ($song_opened) {
+      printf $closing_of_song;
+      $song_opened = 0; }
+
+    if ($album_opened) {
+      printf $closing_of_album;
+      $album_opened = 0; }
+
+    printf "</node>\n</node>\n</map>";
+exit;
+}
+
+# }}}
+# {{{  nochord                          
+if ($task eq "nochord") {
+    $in_tablature=0;
+while (<>) {
+    chomp;
+
+    # Process line
+
+    # {{{  In tablature                    
+    if ($in_tablature) {
+        if (/\x7beot/ or /\x7bend_of_tab/) {
+            $in_tablature=0;
+            next}
+        next}
+    # }}}
+
+    #                         Line contains
+
+    # {{{  Title command                   
+    if (/{t:.*}/ or /{title:.*}/) {
+        
+        s/\173[^:]*: *//; s/\175//;  # 173 is octal left curly brace 175 is right
+        printf "$_\n";
+        next; }
+    # }}}
+    # {{{  Start of Tablature              
+    if (/\x7bsot/ or /\x7bstart_of_tab/) {
+        $in_tablature=1;
+        next; }
+    # }}}
+    # {{{  Other command                   
+    if (/{.*}/) {next;}
+    # }}}
+
+    # {{{  Comment (programmer's kind of)  
+    if (/^\043/) {    # 043 is octal of hash
+        next; }
+    # }}}
+    # {{{  Chord instructions              
+
+    if (/\[/) {
+        $_.="\n";
+
+        my @text = find_text($_);
+
+        for (@text) {
+            print; }
+
+        next; }
+
+    # }}}
+    # {{{  Spaces only                     
+    if (/^ *$/) {
+        print "\n";
+        next;}
+    # }}}
+    # {{{  Text without chords             
+    print "$_\n";
+    # }}}
+}    
+exit;
+}
+# }}}
+# {{{  pro                              
+
+# Assumption: chord lines do not contain any tabulator characters.
+
+$in_tabulature=0;
+
+if ($task eq "pro") {
+    $previous_was_chord_line=0;
+while (<>) {
+    chomp; $_.="\n";
+    if ($previous_was_chord_line) {
+        $previous_was_chord_line=0;
+
+        chomp;
+        $chord_line =~ s/\s+$//;
+        $_.=" " x (length($chord_line)-length($_));
+
+        $chord_end=length($chord_line)-1;
+        $chord_curr=$chord_end;
+        $looking_for_chord_end=1;
+        while (1) {
+            if ($looking_for_chord_end) {
+                last if $chord_curr==-1;
+                if (not substr($chord_line,$chord_curr,1) eq " ") {
+                    $chord_end=$chord_curr;
+                    $looking_for_chord_end=0; }}
+            else {
+                if ((substr($chord_line,$chord_curr,1) eq " ") or $chord_curr==-1) {
+                    $looking_for_chord_end=1;
+                    $chord=substr($chord_line,$chord_curr+1,$chord_end-$chord_curr);
+                    $_=insertstring("[$chord]",$_,$chord_curr+1); }
+                last if $chord_curr==-1; }
+            --$chord_curr; }
+
+        s/\+*$//;
+        print "$_\n"; }
+    else {
+        if ($in_tabulature) {                         # In tabulature
+            if (/{eot.*}/ or /{end_of_tab.*}/) {
+                $in_tabulature=0; }
+            print "$_"; }
+        else {                                        # Not in tabulature
+            if (/{sot.*}/ or /{start_of_tab.*}/) {
+                $in_tabulature=1; }
+
+            if (/^[ \/\+\x23()12345679A-Hbmajindsu]+$/ and not /^\s*$/) {
+                # ^ Is this a chord line?
+                $previous_was_chord_line=1;
+                $chord_line=$_; }
+            else {
+                print "$_"; }}}}
+exit; }
+# }}}
+# {{{  html                             
+
+$chord_color_command="<font color=\"#aa4422\">";
+# chord_color_command must be <font> tag. Atributes are optional
+
+    # {{{  set_one_chord_html                
+sub set_one_chord_html {
+    $tdxs="<td><small>$chord_color_command";
+    $tdxe="</font></small></td>";
+
+    my $table_s="<table border=0 cellpadding=0 cellspacing=0>";
+    
+    my $set="";
+    $_=$_[0];
+
+    # {{{ Switch B <-> H notation (B is common)
+    if ($H_chord) {
+       s/B([^b])/H$1/g;
+       s/B$/H/g; }
+    else {
+       s/H/B/g; }
+    # }}}
+
+    #$set.="\\sf ";
+    # It is nice to represent special sequences with nonprintable characters.
+    s/maj/\001/;s/mi/m/;s/min/m/;s/dim/\002/;s/m75-/z/;
+    s/\0017/\001/;s/7\001/\001/;
+
+    # {{{ brackets
+    my $brackets=0;
+    if (/^\(.*\)$/) {
+       s/\(//;s/\)//;
+       $brackets=1;
+       $set.="(";
+    }
+    # }}}
+    # {{{ basses
+    my $bass="";
+    if (/\//) {
+       @basssplit = split(/\//,"$_");
+       $bass = $basssplit[1];
+       $_ = $basssplit[0];
+    }
+    # }}}
+
+    my $majset;my $dimset;my $minorset;my $minorshiftedbase;
+
+    # {{{ Chord style dependencies
+#      if ($chord_style==0) {
+#      $majset="\$\\triangle\$";
+#      $dimset="o";
+#      $minorset="\\raisebox{0.26ex}{--}";
+#      $minorshiftedbase="F"; }
+#      else {
+    $majset="7maj";
+    $dimset="dim";
+    $minorset="m";
+    $minorshiftedbase="?";
+#      } 
+    # }}}
+
+    my $bot="";  my $top="";
+    my $bot0=""; my $top0="";
+    my $puttobot=0;
+    my $numfound=0;
+    my @CHORD = split (//, $_);
+    $basenote=uc(shift @CHORD);
+    for (@CHORD) {
+       if ($puttobot) { $bot.="$_"; next; }
+       if ($numfound and /[2-9]/) { $bot.="$_"; $puttobot=1; next; }
+       if ($numfound and /\001/)  { $bot.=$majset; $puttobot=1; next; }
+        
+       if (/[2-9]/) { $top.="$_"; $numfound=1; next; }
+
+       if (/\001/) { $top.=$majset; $numfound=1;  next; } # maj
+       if (/\002/) { $top.=$dimset;               next; } # dim
+       if (/z/)    { $top.="\$\\varnothing\$"; next; }
+       if (/b/)    { $top0.="b"; next; }
+       if (/\043/) { $top0.="#"; next; }
+       # \043 is octal hash
+
+       if (/m/)    {
+           #if (not $basenote eq $minorshiftedbase) {$bot0.="\hskip0.1em"}
+           $bot0.=$minorset; next;
+       } # minor
+       if (/\+/)   { $bot.="+"; next; }  # +
+       $top.="$_";
+    }
+
+
+    $start_with_basenote="$table_s <tr><td>$chord_color_command$basenote</font></td>";
+
+    # Now this really is nasty
+  PRINT: {
+      if (not ($top0 or $bot0 or $top or $bot)) { # No indices at all
+         $set.="$start_with_basenote";
+         $set.="$tdxs&nbsp;<br>&nbsp;$tdxe";
+         last PRINT; }
+
+      if ($top =~ /dim/) {
+         $bot0="&nbsp;" if not $bot0;
+         $set.="$start_with_basenote";
+         $set.="$tdxs$top0<br>$bot0$tdxe"
+             if ($top0 or not $bot0 eq "&nbsp;");
+          $set.="<td>${chord_color_command}dim</font></td>";
+          last PRINT }
+
+#      if (not $bot0 and not $bot) {               # No bottom indices
+#        $set.="$table_s<tr><td>$chord_color_command$basenote<td>";
+#        $set.="<td>$table_s $idxs$top0$top$idxe$idxs&nbsp;$idxe</table></td>" if ($top0 or $top);
+#        last PRINT; }
+      
+      if ($bot0) {
+         $bot="&nbsp;" if not $bot;
+         $set.="$start_with_basenote";
+         $set.="$tdxs$top0<br>&nbsp;$tdxe" if ($top0);
+          $set.="$tdxs$bot0$tdxe";
+         $set.="$tdxs$top<br>$bot$tdxe";
+          last PRINT }
+
+
+      $bot0="&nbsp;";
+      $bot="&nbsp;" unless ($bot);
+
+      $set.="$start_with_basenote";
+      $set.="$tdxs$top0<br>$bot0$tdxe" if ($top0 or $bot0);
+      $set.="$tdxs$top<br>$bot$tdxe" if ($top or not $bot eq "&nbsp;");
+
+  }
+    #
+    
+    $set.="<td>$chord_color_command";
+    @basses = split(//,$bass);
+    $set.="/$bass"                if ($#basses==(1-1));
+    if ($#basses==(2-1)) {
+       $set.="/$basses[0]</font></td>";
+       $set.="<td>$chord_color_command$basses[1]<br>&nbsp;</font></td>";
+       $set.="<td>$chord_color_command";
+    }
+    
+    $set.=")"                                 if ($brackets);
+    $set.="&nbsp;</font></td></tr></table>";
+    
+    return $set;
+}
+
+# }}}
+
+if ($task eq "html") {
+    # {{{  set head variable                  
+    $head="<!doctype html public \"-//W3C//DTD HTML 4.0 Transitional//EN\">
+<html>
+<head>
+<title>Songbook</title>
+</head>
+<body bgcolor=\"#eeeeee\">";
+    # }}}
+
+    #                   Process input files
+
+    my $previous_line=0; my $previous_block=0;
+    # previous_line:  0=emptyline,      1=text_line, 2=chord, 3=title
+    # previousblock: 0=we're in block, 1=text_line, 2=chord, 3=title
+
+    $bearable_length=50;
+    %files_warned=();
+    $head_printed=0;
+    $verbatim_tex=0;
+    $table_of_contents_printed=0;
+    $tex_prebegin_part=0;
+    $it="";
+
+    @input=();
+    $stdout_opened=0;
+
+    # Read input into @input variable
+    read_input_into_input_array("html");
+
+    print $head;
+
+    finalize_language_dependent_settings();
+    to_nobarre_if_required();
+
+    for (@input) {
+        chomp;
+        # {{{ Parenthesis                     
+        s/\" /\'\' /g;
+        s/\"$/\'\'/g;
+        s/ \"/ \`\`/g;
+        s/^\"/\`\`/g;
+        # }}}
+
+        #                                  Process line
+        # {{{  In Verbatim TeX                
+        if ($verbatim_tex) {
+            if (/{texe.*}/ or /{verbatim_tex_end.*}/) {
+                $verbatim_tex=0; next; }
+            print "$_\n"; next; }
+        # }}}
+        # {{{  In tablature                   
+        if ($in_tablature) {
+            if (/{eot.*}/ or /{end_of_tab.*}/) {
+                $in_tablature=0;
+                if (not $ignore_tablature) {
+                    print "</pre></font></td></tr></table>\n"};
+                next;}
+
+            if (not $ignore_tablature) {
+                print "$_\n";}
+            next;}
+        # }}}
+
+        #                                Line contains:
+        # {{{  Start of verbatim TeX          
+        if (/{texs.*}/ or /{verbatim_tex_start.*}/) {
+            $verbatim_tex=1; next; }
+        # }}}
+        # {{{  Start of Tablature             
+        if (/{sot.*}/ or /{start_of_tab.*}/) {
+            # {{{ Care about previous block
+            if ($previous_block!=0) {
+                print "<br>\n"   if ($previous_block==1);
+                print "<br><br>\n" if ($previous_block==2);
+                $previous_block=0;
+            }
+            # }}}
+            $in_tablature=1;     
+            if (not $ignore_tablature) {
+                print "<table><tr><td bgcolor=\"#dddddd\"><font size=\"-1\"><pre>\n";
+                $previous_line=1;
+            }
+            next}
+        # }}}
+        # {{{  Table of contents              
+        if (/{toc.*}/ or /{table_of_contents.*}/) {
+            if ($table_of_contents_printed) {
+                warning("You ask me to print table of contents though it\n".
+                        "has already been printed with songbook's titlepage.\n"); }
+            else {
+                print "$table_of_contents"; }}
+        # }}}
+
+        # {{{  Title command                  
+        if (/{t:.*}/ or /{title:.*}/) {
+            
+            print "\n<br>"          if ($previous_line==1);
+            print "<br><br>"   if ($previous_line==2);
+            print "<br>"            if ($previous_line==0);
+
+            s/\173[^:]*: *//; s/\175//;  # 173 is octal left curly brace 175 is right
+            s/&/&amp;/g;
+            print "<br><br><br><br>\n<H3>$_</H3>\n";
+            $previous_line=3;
+            next;
+        }
+        # }}}
+        # {{{  Album command                  
+        if (/{album:.*}/) {
+            
+            print "\n<br>"          if ($previous_line==1);
+            print "<br><br>\n"      if ($previous_line==2);
+            print "<br>\n"            if ($previous_line==0);
+
+            s/\173[^:]*: *//; s/\175//;  # 173 is octal left curly brace 175 is right
+            s/&/\\&/g;
+            print "<H2> $_</H2>\n";
+            $previous_line=3;
+            next;
+        }
+        # }}}
+        # {{{  Start of choir                 
+        if (/{soc.*}/ or /{start_of_chorus.*}/) {
+            $it="<i>";
+            #print "<it>\n";
+            next;
+        }
+        # }}}
+        # {{{  End of choir                   
+        if (/{eoc.*}/ or /{end_of_chorus.*}/) {
+            $it="";
+            #print "\\rm\n";
+            next;
+        }
+
+        # }}}
+        # {{{  Comment                        
+        if (/{c:.*}/ or /{comment:.*}/) {
+            s/\173[^:]*: *//; s/\175//;  # 173 is octal left curly brace 175 is right
+            s/&/\\&/g;
+
+            # {{{ Care about previous block
+            if ($previous_block!=0) {
+                print "<br>\n"   if ($previous_block==1);
+                print "<br><br>\n" if ($previous_block==2);
+                $previous_block=0;
+            }
+            # }}}
+
+            print "<i>$_</i><br>\n";
+            $previous_line=1;  #Comment is close to ordinary text
+            next;
+        }
+
+        # }}}
+        # {{{  Other command                  
+        if (/{.*}/) {next;}
+        # }}}
+
+        # {{{  Comment (programmer's kind of) 
+        if (/^\043/) {    # 043 is octal of hash
+            next; }
+        # }}}
+        # {{{  Chord instructions             
+
+        if (/\[[^:]/) {
+            $_.=" ";
+
+            # {{{ Care about previous block      
+            if ($previous_block!=0) {
+                print "<br><br>\n"    if ($previous_block==1);
+                print "<br><br><br>\n"  if ($previous_block==2);
+                $previous_block=0;
+            }
+            # }}}
+            # {{{ Ensure chords contain no spaces
+            if (/\[[^\x5d]* [^\x5d]*\]/) {
+                warning "\nSetchord: Chords cannot contain spaces.\n";
+                warning "This was broken at file $ARGV:\n";
+                warning $_;
+                exit; }
+            # }}}
+
+            s/&/&amp;/g;                #care about html-dangerous characters
+
+            s/\]\[/\] \[/g;             #no chords tightly follow
+
+            s/\] /\]&nbsp; /g;          #chord is preshifted to the left of the text
+
+            my @text   = find_text($_);
+            my @chords = find_chords($_);
+
+            # {{{ Print tab stops for chords and chords
+
+            my $chord_line="<tr><td>";
+            my $text_line="<tr><td>$it$text[0]";
+            my $textpos=1;
+            for (@chords) {
+                $crd = set_one_chord_html("$_");
+                $chord_line.="</td><td>$crd";
+                $text_line.="</td><td>$it$text[$textpos]";
+                $textpos++; }
+
+            $chord_line.="</td></tr>\n";
+            $text_line.="</td></tr>\n";
+
+            $text_line=~s, </td>,&nbsp;</td>,g; # space at the end of a cell must be hard
+
+            print "<table border=0 cellpadding=0 cellspacing=0>\n";
+            print "$chord_line";
+            print "$text_line";
+            # }}}
+
+            print "</table>\n";
+            $previous_line=2;
+            next; }
+        # }}}
+
+        # {{{  Spaces only                    
+        if (/^ *$/) {
+            $previous_block=$previous_line if ($previous_line!=0);
+            $previous_line=0;
+            next; }
+        # }}}
+        # {{{  Text without chords            
+        do {
+            # {{{  Care about previous block     
+        if ($previous_block!=0) {
+            print "<br>\n"   if ($previous_block==1);
+            print "<br><br>\n" if ($previous_block==2);
+            $previous_block=0; }
+        # }}}
+
+            print "$_<br>\n";
+            $previous_line=1; }
+        # }}}
+    }
+
+    print "</html>\n";
+    exit; }
+# }}}
+# {{{  transpose                        
+
+if ($task eq "transpose") {
+  $transposition=shift @ARGV;
+  @tpose=<>;
+
+  if ($nobarre) {
+    $transposition="nobarre"; }
+
+  transpose($transposition);
+
+  for (@tpose) {
+    print; }
+  exit; }
+
+# }}}
+# {{{  unknown                          
+print STDERR "Task \"$task\" is unknown.\n".$help_message;
+exit;
+# }}}
+
+if ($stdout_opened) {
+    close (STDOUT); }
+
+# {{{  emacs                            
+#
+# Local Variables:
+# compile-command:"chordpack tex chordpack-testing-song.pro >testing.tex"
+# compile-function:(save-excursion (compile compile-command)(sleep-for 2)(to-buffer "testing.tex")(TeX-compile-to-ps))
+# end:
+#
+# }}}
diff --git a/chordpack-documentation.html b/chordpack-documentation.html
new file mode 100644 (file)
index 0000000..bbf2c08
--- /dev/null
@@ -0,0 +1,404 @@
+<!doctype html public "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html>
+<head>
+<title>Chordpack Documentation</title>
+
+<style type="text/css">
+body { background: #eeeee8; }
+div.contents {
+    margin:  0em 3em 0em 3em;
+/*    padding: 0.5em 0.5em 0.5em 0.5em; */
+    border-style: none;
+    border-width: 1;
+/*    background: #ccddcc; */
+}
+h3.contents { color: green; }
+div.myframe {
+    margin:  0em 0em 1em 1em;
+    padding: 0.05em 1em 1em 1em;
+/*    border-style: none; */
+    border-width: 1;
+    border-color: #848478;
+    background: #e4e4d8;
+}
+
+
+  H1 { color: green }
+  h3 { color: #aa6611; }
+  TT { color: #aa3300;  font-weight: bold; }
+  PRE { color: #aa4444; }
+</style>
+</head>
+<body bgcolor="#eeeeee">
+<table width=600 align=center><tr><td>
+
+<center><h1>Chordpack Documentation</h1>
+<small> For version 0.8.2</small></center>
+<br>
+<br>
+
+<div class=contents>
+<h3 class=contents>Table of contents</h3>
+<a href="#intro">What users for what purpose</a><br>
+<a href="#quick">Quick introduction to chordpro format</a><br>
+<a href="#insta">Installation requirements</a><br>
+<a href="#comma">Command line usage</a><br>
+<a href="#pro">Chordpro commands</a><br>
+<a href="#under">Chordpack specific commands</a><br>
+<a href="#versus">Chordpack versus Chord</a><br>
+<a href="#credi">Credits and Copyrights</a><br>
+</div>
+<div class=contents>
+<h3 class=contents>Links</h3>
+<a href="http://www.fi.muni.cz/~xpolansk/chordpack">Chordpack home page</a><br>
+</div>
+<br>
+<br>
+
+<div class=myframe>
+<a name="intro"><h3>What users for what purpose</h3></a>
+
+<p>You are a technical user of computers, willing to install tools like Perl
+or TeX; you are willing to work with tools without graphical interface. You
+want to typeset guitar songs with chords, and you want a good-looking
+postscript or pdf result, or maybe only HTML result.
+
+<p><b>Chordpack</b> is a utility for typesetting and manipulating
+<em>chordpro</em> chord files. These are files used by <em>chord</em>
+utility. The main functionality provided by Chordpack is typesetting
+song-books using TeX typesetting system and its LaTeX macro
+package. However, Chordpack can perform variety of tasks on chordpro
+files.<br><br>
+
+
+
+Chordpack can do the following tasks:
+
+<ul>
+<li>Generate LaTeX source of songs/songbook</li>
+<li>Generate HTML version of songs/songbook</li>
+<li>Generate ASCII version of songs/songbook</li>
+<li>Generate ASCII version of songs/songbook omitting chords</li>
+<li>Transpose chordpro song to desired key</li>
+<li>Take ordinary ASCII notation of song with chords and output almost
+chordpro version.</li>
+</ul>
+</div>
+<div class=myframe>
+<a name="quick"><h3>Quick introduction to chordpro format</h3></a>
+
+Chordpack understands extension of chordpro format.<br><br>
+
+Chordpro is an ASCII format for marking chords. Chords are denoted in square
+brackets. For instance
+
+<pre>
+   [G]Always [Em]look at the [Am7]bright [D7]side of [G]life [Em] [Am7] [D7]
+</pre>
+
+is a notation for
+
+<pre>
+   G       Em          Am7    D7      G    Em Am7 D7
+   Always look at the bright side of life           
+</pre>
+
+Repetition marks are denoted by <tt>[:</tt> and <tt>:]</tt> (<em>repetition
+marks are only part of extension of chordpro format</em>).<br><br>
+
+Besides this, chordpro format defines braced instructions (commands), most
+important of which is <tt> {title:I am a Title of the Song} </tt>.
+
+Other commands include <tt>{subtitle:...}</tt>, <tt>{start_of_choir}</tt>,
+<tt>{end_of_choir}</tt> etc.<br><br><br>
+
+You should use at most one command per line.<br><br>
+
+<em>
+The important thing about chordpro format is that there are already lots of songs
+on the web (see <a href="http://www.olga.net/">Online Guitar Archive -
+OLGA</a> for instance) in this format.
+</em>
+</div>
+<div class=myframe>
+<a name="insta"><h3>Installation requirements</h3></a>
+Chordpack consists of one script written in Perl. If you want to use
+Chordpack, you need to have <a href="http://www.perl.com/pub/language/info/software.html">
+Perl installed</a>. If you want to use chordpack for its main purpose
+i.e. typesetting in TeX, you need to
+<a href="http://www.tug.org/interest.html#free"> install TeX </a> as well.
+We are using teTeX distribution for Linux but there's a high chance that you
+will want to use some Windows distribution.
+</div>
+
+<!--
+                  Command line usage of chordpack
+-->
+
+<div class=myframe>
+<a name="comma"><h3>Command line usage of chordpack</h3></a>
+
+<p>The first thing to note is that the best printing result is achieved if you
+use chordpack to produce LaTeX source. This however requires installation of
+TeX and LaTeX2e macro package which some of you may find inconvenient. In
+that case, you should generate HTML instead.<br><br>
+
+The usage is
+<pre>
+   chordpack &lt;SWITCHES&gt; &lt;TASK&gt; FILE ...
+</pre>
+<b>TASK</b> is one of the following
+
+<ul>
+<li>tex - create LaTeX source</li>
+<li>html - create HTML page</li>
+<li>ascii - create plain ascii output with chords</li>
+<li>nochord - create plain ascii output without chords</li>
+<li>transpose <em>key-or-shift</em></li>
+<li>pro - create chordpro out of plain ascii with chords</li>
+<li>fmm - create <a href="http://freemind.sourceforge.net/">FreeMind</a>'s mind map</li>
+</ul>
+
+Task transpose has one parameter; either destination key of the song,
+or the number of half-steps by which song has to be transposed up.
+
+<p>Chordpack reads input from FILEs or from standard input and produces the
+result on standard output. An exception to this is option -f, as described below.
+
+<p><b>SWITCHES</b>, most of them applies to tex task only, are one of
+
+<ul>
+<li>-f song-list-file</li>
+<li>-l language</li>
+<li>-b</li>
+<li>-c chord-style</li>
+<li>-s font-sizes</li>
+<li>-e enonding: input encoding to be used with LaTeX and FreeMind</li>
+</ul>
+
+<p>For most of the settings, it is better to set them directly in the input
+file. The use of switch -f in combination with tex task is recommended.
+
+<a name=optionf><h4>-f song-list-file</h4></a>
+
+<p>Use song-list-file and output the result to file created from song-list-file
+by removing its suffix and adding tex suffix. Song-list-file is a file
+containing the list of song files as well as lines that are directly passed
+to Chordpack. The lines directly passed to Chordpack differ from filenames
+in that they start with space. For example:<br>
+
+Let file songbook.list contain
+
+<pre>
+ {toc}                      
+ {album:Two is Enough}      
+                            
+song-one.pro                
+second-song.pro             
+</pre>
+
+Let file song-one.pro contain only
+
+<pre>
+{title:Just a Small Song}   
+[A]Hey [Gm]hey              
+</pre>
+
+and file second-song.pro contain only
+
+<pre>
+{title:Not a Big One}       
+[G#m]Day is [G97maj]long and
+[D#]Girls are [F]sweet      
+</pre>
+
+Then the result of using <tt>chordpack -f songbook.list tex</tt> is the same as
+the result of <tt>chordpack tex songbookfile >songbook.tex</tt> given that file
+<tt>songbookfile</tt> contains
+
+<pre>
+{toc}                       
+{album:Two is Enough}       
+{title:Just a Small Song}   
+[A]Hey [Gm]hey              
+{title:Not a Big One}       
+[G#m]Day is [G97maj]long and
+[D#]Girls are [F]sweet      
+</pre>
+
+<a name="optionl"><h4>-l language</h4></a>
+
+Sets language. Currently, this has effect on TeX typesetting and the only
+supported language is Czech. If you make support for more languages, send me
+one.
+
+<a name="optionb"><h4>-b</h4></a>
+
+<p>With this option set, Chordpack transposes all the songs being set to
+such a key that there are as few barre chords as possible. All tablatures
+(text inside <tt>{start_of_tab}</tt> and <tt>{end_of_tab}</tt>) are ignored
+in this case, because transposition of tablatures in virtually impossible,
+unless you have a very smart artificial intelligence program.
+
+<p>This options is working with the tasks tex, html, ascii and transpose.
+
+<h4>-c chord-style</h4>
+
+Set the style of chord setting. There are four styles available:
+<tt>jazz</tt>, <tt>m</tt>, <tt>mi</tt> and low. The default is m.
+Also, the presence of <tt>h</tt> letter in chord-style string turns on
+Central-European (or at least German and Czech) style of denoting
+last tone of C major key (English denote it B while German H).
+
+<a name="options"><h4>-s font_sizes</h4></a>
+
+Sets the sizes of fonts. These can be 0,1,2,3.
+
+</div>
+<div class=myframe>
+<a name="pro"><h3>Chordpro commands</h3></a>
+
+Here are listed chordpro commands understood by Chordpack. Chord utility
+however understands more commands.<br><br>
+
+<p><b>{title:...}</b> or <b>{t:...}</b>. Title of the song.
+
+<p><b><a name="subtitle">{subtitle:...}</a></b> or <b>{st:...}</b>.
+Subtitle of the song. It is typically author of the song. You may specify
+more subtitles. By default, chordpack does not set these subtitles. You can
+turn setting of subtitles on by <a href="#subtitles_on">{subtitles_on}</a>
+command and later turn setting of them of by <a
+href="#subtitles_off">{subtitles_off}</a>.
+
+<p><b>{comment:...}</b> or <b>{c:...}</b>. Commentary on
+something. Typically set in italics.
+    
+<p><b>{start_of_choir}</b> or <b>{soc}</b>. Marks the beginning of
+choir. Choir is set by italics to distinguish from verse.
+
+<p><b>{end_of_choir}</b> or <b>{eoc}</b>. Marks the end of choir.
+
+<p><b>{start_of_tab}</b> or <b>{sot}</b>. Marks the start of tablature. The
+important thing about tablature is that is is set in non-proportional
+(fixed-width if you wish) font. The result is this marking does not say that
+there is a tablature inside. It says set me in non-proportional font. This
+is however mainly useful for tablatures or other tone notation.
+
+<p><b>{end_of_tab}</b> or <b>{eot}</b>. Marks the end of tablature.
+
+</div>
+<div class=myframe>
+<a name="under"><h3>Chordpack specific commands</h3></a>
+
+Chordpack has abilities that surpass those of Chord and as such it defines
+new commands of its own. These have to do with organization of song-book and
+with setting typesetting parameters as well. <em>You should not use these
+commands as a part of single song files, rather they should be used to
+finalize the look of the song-book being set.</em> They are best used as a
+part of song-list-file (see <a href="#optionf">option -f</a>).<br><br>
+
+<p><b>{album: ... }</b>. This is analogical to <tt>{title: ...}</tt>
+command. It marks the start of a new album. Album title is typeset. The
+commands has also effect on the table of contents.
+
+<p><b><a name="subtitles_on">{subtitles_on}</a></b>. Turn setting of <a
+href="#subtitle">subtitles</a> on.  <p><b><a
+name="subtitles_off">{subtitles_off}</a></b>. Turn setting of <a
+href="#subtitle">subtitles</a> off. This is the default.
+        
+<p><b>{chordstyle: ... }</b>. Sets the style of chord setting (see <a
+href="#optionb">option -b</a>).
+
+<p><b>{language: ... }</b>. Sets language (see <a href="#optionl">option
+-l</a>).
+
+<p><b>{fontsize: ... }</b>. Sets fontsizes (see <a href="#options">option
+-s</a>).
+
+<p><b>{titlestyle: ... }</b>. Sets the style of song titles. In default
+style song titles are set in sans serif with a rule above the song
+title. Style <tt>norule</tt> differs only in that there is no rule. Finally
+style <tt>graybox</tt> sets gray box in the background of the song title.
+If you add <tt>songnewpage</tt> to titlestyle, every song will be set on a
+new page (or column in two column mode). For example, you can write
+<tt>{titlestyle:graybox songnewpage}</tt>.
+
+<p><b>{columns: ... }</b>. Sets the number of columns. Possible values are 1
+and 2, the later being default.
+
+<p><b>{songbooktitle: ... }</b>. Songbooktitle is a string of lines
+separated by ^. It determines the look of title page of the song-book. Each
+line is set on a new line and is centered. The first line is bold. For
+example, if you write <tt>{songbooktitle:The Beatles^With the Beatles^Yellow
+Submarine}</tt>, you will get something close to
+        <center>
+       <b>The Beatles</b><br> With the Beatles<br> Yellow Submarine
+       </center>
+Next to the title page will be set the table of contents together with
+alphabetical table of contents.
+
+<p><b>{toc}</b>. Typesets the table of contents. This is useful if you don't
+want to have title page or you are user of LaTeX and want to design your own
+title page using LaTeX.
+
+<p><b>{verbatim_tex_start: ... }</b> or <b>{vtexs: ... }</b>. Enters
+verbatim TeX mode. In this mode every line is passed directly to TeX
+unchanged. These lines are ignored if you do not use task <tt>tex</tt>.
+
+<p><b>{verbatim_tex_end: ... }</b> or <b>{vtexe: ... }</b>. Quits verbatim
+TeX mode.
+
+</div>
+<div class=myframe>
+<a name="versus"><h3>Chordpack versus Chord</h3></a>
+
+Chordpack was written to surpass the <em>Chord</em> program which generates
+postscript from chordpro format. The main drawback was the setting of
+chords.
+
+Consider example typesetting of chord which looks like
+
+<blockquote>
+<table border=0 cellpadding=0 cellspacing=0>
+<tr><td></td><td><i>Eb7+&nbsp;</i></td><td><i>Eb75-&nbsp;</i></td><td><i>Eb74sus&nbsp;</i></td><td><i>Eb97maj&nbsp;</i></td><td><i>Ebm97maj&nbsp;</i></td><td><i>Ebm9&nbsp;</i></td><td></td></tr>
+<tr><td></td><td>Finally </td><td>he </td><td>closed </td><td>&nbsp;his </td><td>eyes </td><td>shut. </td></tr>
+</table>
+</blockquote>
+
+and compare it to chordpack's
+
+<blockquote>
+<table border=0 cellpadding=0 cellspacing=0>
+<tr><td></td><td><table border=0 cellpadding=0 cellspacing=0> <tr><td><font color="#aa4422">E</font></td><td><small><font color="#aa4422">b<br>&nbsp;</font></small></td><td><small><font color="#aa4422">7<br>+</font></small></td><td><font color="#aa4422">&nbsp;</font></td></tr></table></td><td><table border=0 cellpadding=0 cellspacing=0> <tr><td><font color="#aa4422">E</font></td><td><small><font color="#aa4422">b<br>&nbsp;</font></small></td><td><small><font color="#aa4422">7<br>5-</font></small></td><td><font color="#aa4422">&nbsp;</font></td></tr></table></td><td><table border=0 cellpadding=0 cellspacing=0> <tr><td><font color="#aa4422">E</font></td><td><small><font color="#aa4422">b<br>&nbsp;</font></small></td><td><small><font color="#aa4422">7<br>4sus</font></small></td><td><font color="#aa4422">&nbsp;</font></td></tr></table></td><td><table border=0 cellpadding=0 cellspacing=0> <tr><td><font color="#aa4422">E</font></td><td><small><font color="#aa4422">b<br>&nbsp;</font></small></td><td><small><font color="#aa4422">9<br>7maj</font></small></td><td><font color="#aa4422">&nbsp;</font></td></tr></table></td><td><table border=0 cellpadding=0 cellspacing=0> <tr><td><font color="#aa4422">E</font></td><td><small><font color="#aa4422">b<br>&nbsp;</font></small></td><td><small><font color="#aa4422">m</font></small></td><td><small><font color="#aa4422">9<br>7maj</font></small></td><td><font color="#aa4422">&nbsp;</font></td></tr></table></td><td><table border=0 cellpadding=0 cellspacing=0> <tr><td><font color="#aa4422">E</font></td><td><small><font color="#aa4422">b<br>&nbsp;</font></small></td><td><small><font color="#aa4422">m</font></small></td><td><small><font color="#aa4422">9<br>&nbsp;</font></small></td><td><font color="#aa4422">&nbsp;</font></td></tr></table></td></tr>
+<tr><td></td><td>Finally </td><td>he </td><td>closed </td><td>&nbsp; his </td><td>eyes </td><td>shut. </td></tr>
+</table>
+</blockquote>
+
+
+This just touches on the quality of chord typesetting of chordpack. However,
+chordpack has its weak spots as well when compared to Chord:
+
+<ul>
+  <li>Generates LaTeX source and not postsript (less convenient)</li>
+  <li>Does not generate chord pickings
+</ul>
+
+and maybe other.The main purpose was to set <em>chords nicely</em> and
+enable easy setting of song-books. We believe that the result going out of
+chordpack is close to perfect and I ain't going to spell out every little
+annoyance I had to solve to get things looking well.
+</div>
+<div class=myframe>
+<a name="credi"><h3>Credits and Copyrights</h3></a>
+
+<p>The Chordpack was written by <a
+href="http://mujweb.cz/www/danielpolansky/">Daniel Polansky</a> in
+September 2000; with some tips provided by David Necas aka <a
+href="http://trific.ath.cx/">Yeti</a>.
+
+<p>Chordpack is distributed under GNU General Public Licence - GPL.
+</div>
+
+</td></tr></table>
+</body>
+</html>
diff --git a/chordpro-mode.el b/chordpro-mode.el
new file mode 100644 (file)
index 0000000..102e7a5
--- /dev/null
@@ -0,0 +1,39 @@
+;;; Chordpro mode
+;; Chordpro is a format of songs with guitar chords.
+;;
+;; Instalation:   1) copy this file to emacs load path
+;;                2) add to your .emacs (load "chordpro-mode.el")
+;;                3) add to mode to file extensions you want
+
+(defun chordpro-mode-choir-around ()
+   (interactive)
+   "Surround region with start and end of choir marks..."
+   (vim-filter "echo \"{soc}\";cat;echo \"{eoc}\""))
+
+(defun call-key (key)
+   "Call the command KEY is bound to. Example of usage in lisp: (call-key [f2])."
+   (interactive)
+   (command-execute (key-binding key) t))
+
+(defvar chordpro-font-lock-keywords 
+  ;; Here \n is for endline!
+  '(("\\(\\[[^]]*\\]\\)"       1 'font-lock-string-face)
+    ("^\\(#.*\\)"              1 'font-lock-comment-face)
+    ("\\({subtitle[^}]*}\\)"   1 'font-lock-type-face)
+    ("\\({title[^}]*}\\)"      1 'font-lock-keyword-face)
+    ("\\({[^}]*}\\)"           1 'font-lock-variable-name-face)
+    ))
+
+(define-derived-mode chordpro-mode fundamental-mode "chordpro"
+   "Major mode for editing pro files with guitra chords."
+   (set (make-local-variable 'font-lock-defaults)
+        '(chordpro-font-lock-keywords
+          t   ;; bool - t = Do not fontify using also syntax table.
+          t   ;; bool - t = Do case insensitive matching when fontifying.
+          ))
+   (font-lock-mode t)
+   (font-lock-fontify-buffer)
+   (setq case-fold-search nil)
+   (setq fill-column 71))
+
+(provide 'chordpro-mode)