#!/usr/bin/perl # $Id: flktran.pl,v 1.15 2010-10-14 06:48:15 steve Exp $ # flktran [options] infile outfile # Perform format translation on filksong files. ### Open Source/Free Software license notice: # The contents of this file may be used under the terms of the GNU # General Public License Version 2 or later (the "GPL"). The text # of this license can be found on this software's distribution media, # or obtained from www.gnu.org/copyleft/gpl.html ### :end license notice ### ### Print usage info: sub usage { print "$0 [options] infile[.flk] [outfile].ext\n"; print " -t use tables\n"; print " -h output html\n"; print " -c output chords\n"; print " -v verbose\n"; print " -dtd fn specify DTD\n"; print " -opt xx specify style options (LaTeX)\n"; print " Formats (extensions): \n"; print " flk FlkTeX (input; default)\n"; print " html HTML\n"; print " fxml FLK-XML\n"; print " tex LaTeX -- sources .flk file\n"; print " txt plain text (default)\n"; } ### Option variables and their defaults: $infmt = "flk"; $infile = ""; $outfmt = "txt"; $outfile = ""; $doctype = ""; # document type (LaTeX or SGML) $options = ""; # LaTeX style options $tables = 0; # use tables for HTML? $verbose = 0; $chords = 0; ### Adjustable parameters: $TABSTOP = 4; # tabstop for indented constructs $WIDTH = 72; # line width for centering $AUTHOR = "Stephen R. Savitzky"; # Author ### Variables set from environment: $WEBSITE = $ENV{'WEBSITE'}; $WEBDIR = $ENV{'WEBDIR'}; $WEBDIR =~ s|/$||; ### State variables: $indent = 0; # current indentation level $plain = 0; # true when inside plain (non-chorded) text $verse = 0; # number of verses seen so far $vlines = 0; # the number of lines in the current # verse or refrain. $plines = 0; # the number of lines in the current # plain text block. $header = 0; # true after header done. ### Variables set from song macros: $title = ""; $subtitle = ""; $notice = ""; $license = ""; $dedication = ""; $tags = ""; $key = ""; $timing = ""; $created = ""; $cvsid = ""; $music = ""; $lyrics = ""; $arranger = ""; ### Handle options: while ($ARGV[0] =~ /^\-/) { if ($ARGV[0] eq "-dtd") { shift; $dtd = shift; } elsif ($ARGV[0] eq "-opt") { shift; $opt = shift; } elsif ($ARGV[0] eq "-h" || $ARGV[0] eq "-html") { shift; $html = 1; } elsif ($ARGV[0] eq "-t"|| $ARGV[0] eq "-tables") { shift; $tables = 1; $chords=1; } elsif ($ARGV[0] eq "-v"|| $ARGV[0] eq "-verbose") { shift; $verbose = 1; } elsif ($ARGV[0] eq "-c"|| $ARGV[0] eq "-chords") { shift; $chords = 1; } else { usage; die "unrecognized option $1\n"; } } if ($ARGV[0]) { $infile = shift; } if ($ARGV[0]) { $outfile= shift; } if ($infile !~ /\./) { $infile .= ".flk"; } if ($html) { $outfmt = "html"; } # If $outfile ends in /, it's a directory. In that case, the output # goes into the corresponding directory, in a file called lyrics.html if ($outfile =~ /\.html$/) { $outfmt = "html"; $html = 1; } if ($outfile && $outfile !~ /\./ && $outfile !~ /\/$/ && $outfmt) { $outfile .= ".$outfmt"; } $html = $outfmt eq "html"; $outfile =~ s|/lyrics.html$|/|; if ($outfile =~ m|^(.*/)?([^/]+)\.[^./]+$|) { $filebase = "$2"; $filedir = ($1 eq "")? "." : $1; $shortname= $2; $htmlfile = "$filebase.html"; } elsif ($outfile =~ m|^(.*/)?([^/]+)/$|) { $filebase = "$2"; $filedir = "$1$2"; $shortname= $2; $htmlfile = "$filebase/"; $outfile = "$filedir/lyrics.html"; } if ($WEBSITE) { $WEBSITE =~ m|http://([^/]+)|; $sitename = $1; } else { $sitename = ''; } if ($verbose) { print STDERR " infile=$infile; outfile=$outfile; format=$outfmt\n"; print STDERR " filedir=$filedir; filebase=$filebase; htmlfile=$htmlfile\n"; } if ($infile) { open(STDIN, $infile); } if ($outfile) { open(STDOUT, ">$outfile"); } ### Formatting constants: if ($html) { $EM = ""; $_EM = ""; $BF = ""; $_BF = ""; $TT = ""; $_TT = ""; $UL = ""; $_UL = ""; $SPOKEN = "(spoken)"; $_SPOKEN = ""; $SUBSEC = "
flktran";
# Creative Commons copyright notice
$SomeRightsReserved =
'
Some Rights Reserved.
';
$CCnotice =
'
This work is licensed under a Creative Commons
Attribution-Noncommercial-Share Alike 3.0 United States License. ';
} else {
$EM = "_";
$_EM = "_";
$BF = "*";
$_BF = "*";
$TT = "";
$_TT = "";
$UL = "";
$_UL = "";
$SPOKEN = "(spoken)";
$_SPOKEN = "";
$SUBSEC = "";
$_SUBSEC = "";
$SUBSUB = "";
$_SUBSUB = "";
$NL = "\n";
$NP = "\f";
$SP = " ";
$AMP = "&";
$FLKTRAN = "flktran";
$SomeRightsReserved = "Some Rights Reserved: CC by-nc-sa/3.0/us";
$CCnotice = 'This work is licensed under a Creative Commons
Attribution-Noncommercial-Share Alike 3.0 United States License. ';
}
### === Dispatch on input format:
### Process input in FlkTeX:
while (\n"; }
indentLine(getContent($_, $TABSTOP) . "\n",
$TABSTOP);
if ($html) { print "\n"; }
}
elsif (/\\tailnote/) { sepVerse();
indentLine(getContent($_, 0) . "\n"); }
# Ignorable TeX macros:
elsif (/\\(small|footnotesize|advance|vfill|vfiller|vbox)/) {}
elsif (/\\(begin|end)\{/) {}
elsif (/\\ignore/) { getContent($_); }
# Default:
else { doLine(); } # Verse or plaintext line
}
########################################################################
###
### Macro handlers:
###
### Each of the following routines handles a LaTeX macro.
###
### Separate verses.
sub sepVerse {
if ($vlines) { endVerse(); }
}
### Handle a blank line.
sub blankLine {
if ($vlines) { endVerse(); }
if ($plain) {
print "\n";
$plines = 0;
}
}
### Begin a song:
### Stash the title, put out the header.
sub begSong {
my ($line) = @_; # input line
$line =~ s/^.*song\}//;
$title = getContent($line);
if ($html) {
my $alinks = " [pdf]";
$alinks .= " [ogg]"
if -f "$filedir/$filebase.ogg";
$alinks .= " [mp3]"
if -f "$filedir/$filebase.mp3";
print "";
print "$WEBSITE$WEBDIR/$htmlfile";
print "
\n";
print " Automatically generated with $FLKTRAN";
print " from $infile.
\n";
if ($cvsid) { print " $cvsid\n"; }
print "
\n"; }
$verse ++; # bump the verse count.
$vlines = 0;
}
### End a verse:
### Only called if there are actually lines in it.
sub endVerse {
if ($html) { print "\n"; }
$vlines = 0;
}
### Begin a refrain:
sub begRefrain {
if ($vlines) { endVerse(); }
$indent += $TABSTOP;
# Note that begVerse will get called when the first line appears,
# so we don't have to deal with verse count, line count, or .
}
### End a refrain:
sub endRefrain {
if ($html) { print ""; }
print "\n";
$vlines = 0;
$indent -= $TABSTOP;
}
### Begin a bridge:
sub begBridge {
if ($vlines) { endVerse(); }
$indent += $TABSTOP * 2;
# Note that begVerse will get called when the first line appears,
# so we don't have to deal with verse count, line count, or .
}
### End a bridge:
sub endBridge {
if ($html) { print ""; }
print "\n";
$vlines = 0;
$indent -= $TABSTOP * 2;
}
### Begin a note:
sub begNote {
if ($vlines) { endVerse(); }
$plines = 0;
$plain ++;
}
### End a note:
sub endNote {
$plines = 0;
$plain --;
}
### Begin a quote:
sub begQuote {
if ($vlines) { endVerse(); }
$plines = 0;
$plain ++;
if ($html) { print "\n"; } $indent += $TABSTOP; } ### End a quote: sub endQuote { $plines = 0; $plain --; $indent -= $TABSTOP; if ($html) { print "\n"; } } ######################################################################## ### ### Block conversion: ### ### Each of these routines converts the start or end of a ### delimited block of lines to output format. ### sub doHeader { if ($html) { htmlHeader(); } else { textHeader(); } $header ++; } sub center { # === need to handle multiple lines === my ($text) = @_; $text =~ s/^[ \t]*//; $text =~ s/[ \t]*\n$//; $text =~ s/\\copyright/Copyright/; $text =~ s/[Ss]ome [Rr]ights [Rr]eserved\.?/$SomeRightsReserved/gs; $text =~ s/\\SomeRightsReserved/$SomeRightsReserved/gs; $text =~ s/\\CcByNcSa/$CCNotice/gs; my $w = $WIDTH - length($text); for ( ; $w > 0; $w -= 2) { $text = " " . $text; } print "$text\n"; } sub hcenter { my ($h, $text) = @_; $text =~ s/^[ \t]*//; $text =~ s/\\copyright/\©/; $text =~ s/\n/\
\n"; }
else { print "\n"; }
}
$_ = deTeX($_);
if ($html) { s/\~/ /g; } else { s/\~/ /g; }
s/\\newline/$NL/g;
s/\\\///g;
indentLine($_, $indent);
$plines ++;
} else {
if ($vlines == 0) { begVerse(); }
if ($tables) { print tableLine($_); }
else { print chordLine($_); }
$vlines ++;
}
}
### Put out a plain line, possibly indented.
sub indentLine {
my ($line, $indent) = @_;
$line =~ s/^[ \t]*//;
while ($indent--) { $line = " ".$line; }
print $line;
}
### Convert an ordinary line to chords + text
# === does not insert indent yet.
sub chordLine {
my ($line) = @_; # input line
my $cline = ""; # chord line
my $dline = ""; # dest. (text) line
my ($scol, $ccol, $dcol, $inchord, $inmacro) = ($indent, 0, 0, 0, 0);
my $c = ''; # current character
my $p = 0; # current position
$line = deTeX($line);
$line =~ s/^[ \t]*//;
$line =~ s/\\sus/sus/g;
$line =~ s/\\min/m/g;
for ($p = 0; $p < length($line); $p++) {
$c = substr($line, $p, 1);
if ($c eq "\n" || $c eq "\r") { break; }
if ($c eq '[') { $inchord ++; }
elsif ($c eq ']') { $inchord --; }
elsif ($c eq ' ') { if (!$inchord) { $scol ++; } }
elsif ($c eq "\t") {
if (!$inchord) { do {$scol ++; } while ($scol % 8); } }
else {
if ($inchord) {
while ($ccol < $scol) { $cline .= ' '; $ccol ++ }
$cline .= $c;
$ccol ++;
} else {
while ($dcol < $scol) { $dline .= ' '; $dcol ++ }
$dline .= $c;
$dcol ++;
$scol++;
}
}
}
# The result has a newline appended to it.
return (($cline && $chords)? $cline . "\n" . $dline : $dline);
}
### Convert a line to a table
### When using tables, each line becomes a separate table.
### This, in turn, becomes a row in a table containing the verse.
sub tableLine {
}
### Convert a line to XML
sub xmlLine {
}
### Remove LaTeX constructs.
### This would be easier with a table.
sub deTeX {
my ($txt) = @_; # input line
while ($txt =~ /\%/) { # TeX comments eat the line break, too.
$txt =~ s/\%.*$//;
$txt .=