#!/usr/local/bin/perl # crd2sb - convert chordpro files to songbook (LaTeX style) # Usage: crd2sb filename.crd [filename2.crd] . . [filenamen.crd] # The input files are "*.crd" and the output files are "*.tex" (in the # current directory). # $Id: crd2sb,v 1.9 1997/12/05 19:25:05 abel Exp $ # (C)Copyright 1997 Abel Chow # You may distribute under the terms of the Artistic License, # as specified in the relnotes.txt file. use File::Basename; $debuglevel = 0; $date = localtime; sub Init { $title = ""; $key = ""; $copyright = "Unknown"; $wandm = "Unknown"; $sref = ""; $ccli = ""; $lyrics = ""; $nextenv = "SBVerse"; } sub EndSong { unless ($title) { print STDERR "$file:$.: No title defined for song.\n"; Init(); return; } $ccli = "\\NotCCLIed" if $copyright =~ /\bPublic Domain\b/; print STDERR "$file:$.: key undefined for \"$title\".\n" unless $key; print STDERR "$file:$.: copyright info undefined for \"$title\".\n" if $copyright eq "Unknown"; print STDERR "$file:$.: author undefined for \"$title\".\n" if $wandm eq "Unknown"; # print STDERR "$file:$.: scripture ref. undefined for \"$title\".\n" unless $sref; print STDERR "$file:$.: CCLI status undefined for \"$title\".\n" unless $ccli; if ($env) { DoEnd($env); } print "% This file is machine generated. Do not edit.\n"; print "% crd2sb $file\n"; print "% created on $date\n"; print "\\begin{song}{$title}{$key}\n"; print " {$copyright}\n {$wandm}\n {$sref}\n {$ccli}\n"; print "$lyrics"; print "\\end{song}\n"; Init(); } sub DoNewSong { EndSong(); Init(); } sub DoSubTitle { ($_) = @_; # figure out if it's a copyright if (s/\(C\)// + s/\bCopyright\b//) { s/^\s+//; s/\s+$//; $copyright = $_; return; } if (/Public Domain/) { $copyright = $_; return; } s/^W&M://; s/^\s+//; s/\s+$//; $wandm = $_; } sub DoComment { ($_) = @_; print STDERR "$file:$.: > $_\n" if $debuglevel > 0; if (($p) = /^tex:\s*(.*)/) { $lyrics .= "$p\n"; return; } if (($p) = /^note:\s*(.*)/) { $lyrics .= "\\SBMargNote{$p}\n"; return; } if (($p) = /^intro:\s*(.*)/) { $p =~ s/#/\$\\sharp\$/g; $p =~ s/b/\$\\flat\$/g; $p =~ s/\|/\$|\$/g; $lyrics .= "\\SBIntro{\\ChFont $p}\n"; return; } if (/^section:/) { $nextenv = "SBSection"; return; } # figure out if there's a CCLI ref anywhere $ccli = "\\CCLIed" if /\bccli\b/i; # key setting if (/^\s*key:\s*(.*)/) { my($k) = $1; if ($k !~ /^[A-G]\S*/) { print STDERR "$file:$.: invalid key $k\n"; } $k =~ s/#/\$\\sharp\$/g; $k =~ s/b/\$\\flat\$/g; $key = $k; return; } $sref = $1 if /^\s*sref:\s*(.*)/; } sub DoBegin { my($myenv) = @_; printf STDERR "$file:$.: missing env $env" if $env; $env = $myenv; $lyrics .= "\\begin{$env}\n"; $first = 1; } sub DoEnd { print STDERR "$file:$.: mismatched quote\n" if $quote; $quote = 0; printf STDERR "$file:$.: missing env $env\n" unless ($env); $lyrics .= "\\end{$env}\n"; $env = ""; } unless (@ARGV) { print "usage: crd2sb filename.crd [filename2.crd] ... [filenamen.crd]\n" } Init(); foreach $file (@ARGV) { ($base, $path, $type) = fileparse($file, '\.crd'); unless ($type) { print STDERR "$file: not a Chordpro (*.crd) file\n"; next; } $outfile = "$base.tex"; print STDERR "#### $file -> $outfile ####\n" if $debuglevel > 0; unless (open(INFILE, $file)) { print STDERR "$file: $!\n"; exit; } close(STDOUT); unless (open(STDOUT, ">$outfile")) { print STDERR "$outfile: $!\n"; exit; } while () { if (/^#\s+(.*)/) { DoComment($1); next; } next if /^#/; # directive if (m/^\s*{([^\}:]+)(:\s*([^\}]*))?}/) { # title if ($1 eq "title" || $1 eq "t") { $title = $3; next; } if ($1 eq "subtitle" || $1 eq "st") { DoSubTitle($3); next; } if ($1 eq "comment" || $1 eq "c") { DoComment($3); next; } # new_song if ($1 eq "new_song" || $1 eq "ns") { DoNewSong(); next; } if ($1 eq "start_of_chorus" || $1 eq "soc") { DoBegin("SBChorus"); next; } if ($1 eq "end_of_chorus" || $1 eq "eoc") { DoEnd("SBChorus"); next; } next; } if (/{/ || /}/) { print STDERR "$file:$.: unmatched brace\n"; } # strip blank spaces and comments s/\s+/ /; s/^\s//; s/\s$//; s/&/\\&/; unless ($_) { # blank line DoEnd() if $env; next; } while (/"/) { # " if ($quote) { $quote = 0 if (s/"/\'\'/); # " } } else { $quote = 1 if (s/"/\`\`/); # " } } } # protect the parens in chords s/(\[[^\[\]]*)\(/$1$1/g; s/\(/{\\em /g; s/\)/}/g; tr/\<\>/\(\)/; s/\|/\$|\$/g; s/\[([^\[\]]*)\]-/\\Ch{$1}{\\SBem}/g; s/\[([^\[\]]*)\]([A-Za-z0-9,;:.!"'`]+)/\\Ch{$1}{$2}/g; #'` s/\[([^\[\]]*)\]/\\Ch{$1}{}/g;#}} s/\.\.\./\\ldots{}/g; unless ($env) { DoBegin($nextenv) unless $env; $nextenv = "SBVerse"; } $lyrics .= "\n" if $env && !$first; $first = 0; $lyrics .= "$_\n"; } DoNewSong(); }