texlive[44553] Master: songs (10jun17)

commits+karl at tug.org commits+karl at tug.org
Sat Jun 10 23:36:05 CEST 2017


Revision: 44553
          http://tug.org/svn/texlive?view=revision&revision=44553
Author:   karl
Date:     2017-06-10 23:36:05 +0200 (Sat, 10 Jun 2017)
Log Message:
-----------
songs (10jun17)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/songs/README
    trunk/Master/texmf-dist/doc/latex/songs/history.txt
    trunk/Master/texmf-dist/doc/latex/songs/sample/Makefile
    trunk/Master/texmf-dist/doc/latex/songs/sample/chordbook.tex
    trunk/Master/texmf-dist/doc/latex/songs/sample/lyricbook.tex
    trunk/Master/texmf-dist/doc/latex/songs/sample/slidebook.tex
    trunk/Master/texmf-dist/doc/latex/songs/sample/songs.sbd
    trunk/Master/texmf-dist/doc/latex/songs/sample/transparencies.tex
    trunk/Master/texmf-dist/doc/latex/songs/songidx/bible.can
    trunk/Master/texmf-dist/doc/latex/songs/songidx/catholic.can
    trunk/Master/texmf-dist/doc/latex/songs/songidx/greek.can
    trunk/Master/texmf-dist/doc/latex/songs/songidx/protestant.can
    trunk/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can
    trunk/Master/texmf-dist/doc/latex/songs/songs.pdf
    trunk/Master/texmf-dist/source/latex/songs/songs.dtx
    trunk/Master/texmf-dist/source/latex/songs/songs.ins
    trunk/Master/texmf-dist/tex/latex/songs/songs.sty
    trunk/Master/tlpkg/libexec/ctan2tds

Added Paths:
-----------
    trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.lua

Removed Paths:
-------------
    trunk/Master/texmf-dist/doc/latex/songs/songidx/Makefile
    trunk/Master/texmf-dist/doc/latex/songs/songidx/authidx.c
    trunk/Master/texmf-dist/doc/latex/songs/songidx/chars.h
    trunk/Master/texmf-dist/doc/latex/songs/songidx/fileio.c
    trunk/Master/texmf-dist/doc/latex/songs/songidx/fileio.h
    trunk/Master/texmf-dist/doc/latex/songs/songidx/scripidx.c
    trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.c
    trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.h
    trunk/Master/texmf-dist/doc/latex/songs/songidx/songsort.c
    trunk/Master/texmf-dist/doc/latex/songs/songidx/titleidx.c
    trunk/Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h

Modified: trunk/Master/texmf-dist/doc/latex/songs/README
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/README	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/README	2017-06-10 21:36:05 UTC (rev 44553)
@@ -24,7 +24,7 @@
 
 II. Copyright Notice
 
-The Songs LaTeX Package is copyright (C) 2012 Kevin W. Hamlen.
+The Songs LaTeX Package is copyright (C) 2017 Kevin W. Hamlen.
 
 It is free software; you can redistribute it and/or modify it under the terms
 of the GNU General Public License as published by the Free Software Foundation;
@@ -45,15 +45,13 @@
 
 1. The LaTeX2e "Songs" style package for creating songbooks, with source
 consisting of:
-     songs.dtx (self-documenting source code)
-     songs.ins (installer script for docstrip)
-     Makefile  (makefile for generating songs.sty and songs.pdf)
+     songs.dtx  (self-documenting source code)
+     songs.ins  (installer script for docstrip)
+     Makefile   (makefile for generating songs.sty and songs.pdf)
 
-2. The songidx index-generation program, to be compiled from C sources:
-     songidx/*.c      (C source files)
-     songidx/*.h      (C header files)
-     songidx/*.can    (text data files used by songidx at runtime)
-     songidx/Makefile (makefile for generating the songidx executable)
+2. The songidx index-generation script:
+     songidx/songidx.lua  (Lua source file)
+     songidx/*.can        (text data files used by songidx at runtime)
 
 (Please note that the "makeindex" program that comes standard with LaTeX
 is NOT a suitable replacement for songidx.)
@@ -87,18 +85,7 @@
      mkdir /usr/local/share/texmf/tex/latex/songs
      cp songs.sty /usr/local/share/texmf/tex/latex/songs
 
-2. If you want to create song books with indexes, you will also need to
-compile the songidx program:
-
-     cd songidx
-     gmake
-
-If desired, copy the resulting songidx program to an appropriate directory
-in your path:
-
-     cp songidx /usr/local/bin
-
-3. The sample books can be compiled using the files in songs/sample.  If
+2. The sample books can be compiled using the files in songs/sample.  If
 pdflatex is in your path, you should be able to invoke GNU Make to generate
 all of them (note that GNU-compatible version of Make is REQUIRED):
 

Modified: trunk/Master/texmf-dist/doc/latex/songs/history.txt
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/history.txt	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/history.txt	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,8 +1,80 @@
 Songs LaTeX Package Revision History:
 
+Version 3.0 [2017/06/05]: Fifth CTAN Release
+
+  * Transitioned the songidx program to LuaTeX, eliminating the need for C-compiled programs.  This significant change to the install process warrants the transition to a new major version number.
+
+  * The sbdchk program is being retired, since I don't think anyone is using it and it relies on LaTeX hyphenation algorithms that aren't well suited to the task of chord placement.  If anyone wants it back, let me know and I'll consider porting it to Lua.
+
+  * Corrected a longstanding, elusive bug in the page-builder that occasionally inserted unwanted vertical space between songs.
+
+  * Corrected a bug in the indexer that sometimes incorrectly sorted entries that are strict prefixes of other entries (e.g., entry "Smith" now always precedes entry "Smith, John").
+
+  * Fixed a bug in the chord replaying logic that failed to apply transposition and note renaming to replayed chords within chord-over-ligature macros.
+
+  * Added support for lyric fonts with no hyphen character (usually for non-hyphenated languages).
+
+Version 2.18 [2015/08/18]:
+
+  * Added \echofont for customizing the font of echo parts.
+
+  * Added \meterfont for customizing the font of meter numbers, and fixed a bug that italicized meter numbers within italicized lyric sections. Also added a tiny vertical space between meter numbers and meter bars for improved readability.
+
+  * Bug fix: Customizing \versefont and \chorusfont no longer nullifies custom line spacing via \baselineadj.
+
+  * \includeonlysongs may now specify repeated songs without throwing errors. (Note that this can result in duplicate index entries, however.)
+
+  * Fixed a bug in songidx that caused an infinite loop when the system's locale (conflictingly) classifies certain characters as "digits", but doesn't recognize those same characters as constituting numbers.
+
+  * Documentation improvements (color, reorganization, and clarifications)
+
+  * Experimental: Index sorting now optionally uses ICU collation instead of locale to sort international texts, affording users many new sorting options.  (Separate installation of the ICU library is required.)  Note: ICU support does not yet include ICU-based "bucketing" (grouping of index entries into alphabetic subsections, as in title indexes).  Such support is expected to appear in a future release.
+
+  * Better support for small LaTeX environments with missing packages
+
+  * Compliance with new 2015/01/01 LaTeX release (e-TeX extended registers available by default on newer installations without explicit loading of e-TeX style package).
+
+  * Windows installer now installs native 64-bit songidx and sbdchk programs on 64-bit systems.
+
+Version 2.17 [2013/07/10]:
+
+  * Fixed a major error in the page-builder (introduced in v2.16 by the fix to \sclearpage) which caused the last page of many songs environments to be dropped.  The fix required a significant overhaul of the page-building algorithm, so is given its own release.
+
+  * Fixed a returning bug that caused the first lines of numbered, centered verses to be slightly off-center.
+
+Version 2.16 [2013/07/02]:
+
+  * \pagepreludes now sets \songpos to 0, since otherwise songs could become separated from their preludes.
+
+  * Fixed a bug introduced in v2.15 that caused errors when a song title began with a multinational character encoded by the inputenc package.
+
+  * Fixed a bug introduced in v2.14 that broke the \authsepword, \authbyword, and \authignoreword macros (again).
+
+  * Fixed a bug that caused \sclearpage (and macros that call it) to sometimes output a blank page.  The same bug also caused the \songpos algorithm to occasionally miss the best song placement.
+
+  * Added extra error-checking for macro name-clashes with other packages.
+
+Version 2.15 [2013/02/02]:
+
+  * New page format: In non-slides mode, activating \pagepreludes typesets each song on a fresh page with title spanning the entire page width, but with the rest of the song typeset in multiple columns below the title.
+
+  * New \songtarget and \songlink macros elaborate the nopdfindex option with finer control over PDF bookmark indexes and hyperlinks.  The \ifpdfindex conditional has been retired.
+
+  * Some index-sorting problems related to the inputenc package have been resolved.  (For even better sorting of international alphabets, use a LaTeX version with native Unicode support, such as XeTeX, instead of inputenc.)
+ 
+  * The author indexer now treats everything except spaces, commas, and semicolons not enclosed in braces as parts of words.  This helps to support unusual band names that contain punctuation or numbers.
+
+  * Setting \idxheadwidth to 0pt now suppresses the letter divisions in large indexes entirely.
+
+  * Fixed a bug that caused the author indexer to recognize only the last \authignoreword.
+
+  * Fixed a bug in the title indexer that incorrectly indexed titles beginning with numbers.
+
+  * Updated the Unix installer to bring it into compliance with Gnu autoconf 2.69 standards.  See the README file for new (easier) install instructions.
+
 Version 2.14 [2012/03/17]: Fourth CTAN Release
 
-  * Corrected a bug introduced in version 2.13 that broke \titleprefixword, \authsepword, \authbyword, and \authignoreword.
+  * Corrected a bug introduced in version 2.13 that broke \titleprefixword, \authsepword, \authbyword, and \authignoreword.  (Turns out this fix contained another bug that left the last three of these macros unfixed until v2.16.  See v2.16 notes above.)
 
   * Corrected a bug that caused misformatted multiline index entries in single-column indexes.
 
@@ -18,7 +90,7 @@
 
   * New \notrans macro allows transposition to be suppressed within a chord name without suppressing note name conversion (via \notenamesin and \notenamesout).
 
-  * New \sepindexesfalse macro disables automatic page breaks at the end of indexes, and disables auto-resizing of small, multi-column indexes to full-page, single-column indexes.  This allows the user to put many small indexes on a single page if desired.
+  * New \sepindexesfalse macro disables automatic page breaks at the end of indexes, and makes every index n-column (where n is specified in the optional argument to \showindex) instead of converting short indexes to centered, single-columns.  This allows the user to put many small indexes on a single page if desired.
 
   * Declaring a large number of song indexes no longer causes TeX to exceed the write-register limit.
 

Modified: trunk/Master/texmf-dist/doc/latex/songs/sample/Makefile
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/sample/Makefile	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/sample/Makefile	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,6 +1,7 @@
 LATEX = pdflatex
+TEXLUA = texlua
 TEXOUT = pdf
-SONGIDX = ../songidx/songidx
+SONGIDX = ../songidx/songidx.lua
 BIBLE = ../songidx/bible.can
 
 all: chordbook.$(TEXOUT) lyricbook.$(TEXOUT) slidebook.$(TEXOUT) transparencies.$(TEXOUT)
@@ -16,11 +17,8 @@
 indexes: $(patsubst %.sxd,%.sbx,$(wildcard *.sxd))
 
 %.sbx: %.sxd $(SONGIDX) $(BIBLE)
-	$(SONGIDX) -b $(BIBLE) $< $@
+	$(TEXLUA) $(SONGIDX) -b $(BIBLE) $< $@
 
-$(SONGIDX): ../songidx/*.c ../songidx/*.h
-	$(MAKE) -C ../songidx songidx
-
 ../songs.sty: ../songs.dtx
 	$(MAKE) -C .. songs.sty
 

Modified: trunk/Master/texmf-dist/doc/latex/songs/sample/chordbook.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/sample/chordbook.tex	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/sample/chordbook.tex	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-% Copyright (C) 2012 Kevin W. Hamlen
+% Copyright (C) 2017 Kevin W. Hamlen
 %
 % This program is free software; you can redistribute it and/or
 % modify it under the terms of the GNU General Public License

Modified: trunk/Master/texmf-dist/doc/latex/songs/sample/lyricbook.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/sample/lyricbook.tex	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/sample/lyricbook.tex	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-% Copyright (C) 2012 Kevin W. Hamlen
+% Copyright (C) 2017 Kevin W. Hamlen
 %
 % This program is free software; you can redistribute it and/or
 % modify it under the terms of the GNU General Public License

Modified: trunk/Master/texmf-dist/doc/latex/songs/sample/slidebook.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/sample/slidebook.tex	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/sample/slidebook.tex	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-% Copyright (C) 2012 Kevin W. Hamlen
+% Copyright (C) 2017 Kevin W. Hamlen
 %
 % This program is free software; you can redistribute it and/or
 % modify it under the terms of the GNU General Public License

Modified: trunk/Master/texmf-dist/doc/latex/songs/sample/songs.sbd
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/sample/songs.sbd	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/sample/songs.sbd	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-% Copyright (C) 2012 Kevin W. Hamlen
+% Copyright (C) 2017 Kevin W. Hamlen
 %
 % This program is free software; you can redistribute it and/or
 % modify it under the terms of the GNU General Public License

Modified: trunk/Master/texmf-dist/doc/latex/songs/sample/transparencies.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/sample/transparencies.tex	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/sample/transparencies.tex	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-% Copyright (C) 2012 Kevin W. Hamlen
+% Copyright (C) 2017 Kevin W. Hamlen
 %
 % This program is free software; you can redistribute it and/or
 % modify it under the terms of the GNU General Public License

Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/Makefile
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/Makefile	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/Makefile	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,16 +0,0 @@
-srcdir = .
-CC = cc
-CFLAGS = -I$(srcdir)
-LDFLAGS =
-
-all: songidx
-
-.SUFFIXES:
-.SUFFIXES: .c .o
-
-songidx: *.h $(patsubst %.c,%.o,$(wildcard *.c))
-	$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.o,$^) -o $@
-
-clean:
-	-rm $(DESTDIR)*.o $(DESTDIR)songidx
-

Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/authidx.c
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/authidx.c	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/authidx.c	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,488 +0,0 @@
-/* Copyright (C) 2012 Kevin W. Hamlen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA  02110-1301, USA.
- *
- * The latest version of this program can be obtained from
- * http://songs.sourceforge.net.
- */
-
-
-/* Create a LaTeX author index file from an author .dat file. */
-
-#if HAVE_CONFIG_H
-#  include "config.h"
-#else
-#  include "vsconfig.h"
-#endif
-
-#include "chars.h"
-#include "songidx.h"
-#include "fileio.h"
-
-#if HAVE_STRING_H
-#  include <string.h>
-#elif HAVE_STRINGS_H
-#  include <strings.h>
-#endif
-
-typedef struct wordlist
-{
-  struct wordlist *next;
-  WCHAR *w;
-}
-WORDLIST;
-
-WORDLIST wl_and = { NULL, ws_lit("and") };
-WORDLIST wl_by = { NULL, ws_lit("by") };
-WORDLIST wl_unknown = { NULL, ws_lit("unknown") };
-
-/* wl_insert(<wordlist>,<word>)
- *  Insert a word onto the head of a wordlist. */
-static void
-wl_insert(wl,w)
-  WORDLIST **wl;
-  const WCHAR *w;
-{
-  WORDLIST *head = (WORDLIST *) malloc(sizeof(WORDLIST));
-  head->next = ((*wl==&wl_and) || (*wl==&wl_by) || (*wl==&wl_unknown)) ?
-               NULL : *wl;
-  head->w = (WCHAR *) calloc(ws_strlen(w)+1, sizeof(WCHAR));
-  ws_strcpy(head->w, w);
-  *wl = head;
-}
-
-/* matchany(<string>,<wordlist>)
- *  If a word in <wordlist> case-insensitively matches to a prefix of <string>,
- *  and if the match concludes with whitespace, then return the length of the
- *  match plus 1.  Otherwise return 0.
- */
-static size_t
-matchany(s,wl)
-  const WCHAR *s;
-  WORDLIST *wl;
-{
-  const WCHAR *s2, *w2;
-
-  for (; wl; wl=wl->next)
-  {
-    for (s2=s, w2=wl->w; *w2; ++s2, ++w2)
-      if (wc_tolower(*s2)!=wc_tolower(*w2)) break;
-    if ((*w2 == wc_null) && wc_isspace(*s2)) return s2-s+1;
-  }
-  return 0;
-}
-
-/* matchwithin(<string>,<wordlist>)
- *  Return 1 if any word in <wordlist> appears in <string> (case insensitive
- *  match). */
-static char
-matchwithin(s,wl)
-  const WCHAR *s;
-  WORDLIST *wl;
-{
-  const WCHAR *s2, *w2;
-
-  for (; wl; wl=wl->next)
-  {
-    for (; *s; ++s)
-    {
-      for (s2=s, w2=wl->w; *w2; ++s2, ++w2)
-        if (wc_tolower(*s2)!=wc_tolower(*w2)) break;
-      if (*w2 == wc_null) return 1;
-    }
-  }
-  return 0;
-}
-
-/* partofword(<char>)
- *   Return 0 if <char> is a word-delimiter and 1 otherwise. Word-delimiters
- *   are anything other than alphabetic characters, hyphens, apostrophes,
- *   backslashes, and braces.
- */
-static int
-partofword(c)
-  WCHAR c;
-{
-  return (wc_isalpha(c) || (c==wc_apostrophe) || (c==wc_backquote)
-          || (c==wc_backslash) || (c==wc_lbrace) || (c==wc_rbrace));
-}
-
-/* isjrsuffix(<string>)
- *   Return 1 if <string> points to the name suffix "Jr" and 0 otherwise. */
-static int
-isjrsuffix(s)
-  const WCHAR *s;
-{
-  WCHAR buf[3];
-
-  if (!partofword(*s) || !partofword(*(s+1)) || partofword(*(s+2)))
-    return 0;
-  buf[0] = *s;
-  buf[1] = *(s+1);
-  buf[2] = wc_null;
-  return !ws_coll(buf, ws_lit("Jr"));
-}
-
-/* grabauthor(<string>,<seplist>,<afterlist>,<ignorelist>)
- *   Return a string of the form "Sirname, Restofname" denoting the full name
- *   of the first author found in <string>; or return NULL if no author name
- *   can be found.  Set <string> to point to the suffix of <string> that was
- *   not parsed.  The string returned (if any) is static, so successive calls
- *   will overwrite it.
- *
- *   Heuristics:
- *     * Names are separated by punctuation (other than hyphens, periods,
- *       apostrophes, or backslashes) or by the word "and" (or whatever words
- *       are in seplist).
- *       Special case: If a comma is followed by the abbreviation "Jr" or by a
- *       roman numeral, then the comma does NOT end the author's name.
- *     * If a name contains the word "by" (or anything in afterlist), then
- *       everything before it is not considered part of the name.  (Let's hope
- *       nobody is named "By".)
- *     * The author's last name is always the last capitalized word in the
- *       name unless the last capitalized word is "Jr." or a roman numeral.
- *       In that case the author's last name is the second-last capitalized
- *       word.
- *     * If an author appears to have only a first name, or if the last name
- *       found according to the above heuristics is an abbreviation (ending in
- *       a period), look ahead in <string> until we find someone with a last
- *       name and use that one.  This allows us to identify the first author in
- *       a string like "Joe, Billy E., and Bob Smith" to be "Joe Smith".
- *     * If the resultant name contains the word "unknown" (or any word in
- *       unknownlist), it's probably not a real name.  Recursively attempt to
- *       parse the next author. */
-static WCHAR *
-grabauthor(authline,seplist,afterlist,unknownlist)
-  const WCHAR **authline;
-  WORDLIST *seplist;
-  WORDLIST *afterlist;
-  WORDLIST *unknownlist;
-{
-  static WCHAR buf[MAXLINELEN+2], *bp;
-  const WCHAR *first, *last, *suffix, *next, *scanahead, *endp;
-  size_t len;
-
-  /* Point "first" to the first character of the name, "last" to the first
-   * character of the sirname, "suffix" to any suffix like "Jr." or "III"
-   * (or NULL if there is none), and "next" to the first character beyond the
-   * end of the name. */
-  if (!authline) return NULL;
-  for (first=*authline; (*first!=wc_null) && !partofword(*first); ++first) ;
-  if (((len = matchany(first,seplist)) > 0) ||
-      ((len = matchany(first,afterlist)) > 0))
-    first += len;
-  if (*first==wc_null) return NULL;
-  last=suffix=NULL;
-  for (next=first; *next; ++next)
-  {
-    skipesc(&next);
-    if (*next==wc_comma)
-    {
-      for (scanahead = next+1; *scanahead==wc_space; ++scanahead) ;
-      if (isjrsuffix(scanahead)) continue;
-      if (ws_strchr(ws_lit("XVI"), *scanahead) &&
-          !partofword(*(scanahead+ws_strspn(scanahead, ws_lit("XVI")))))
-        continue;
-      *authline = next+1;
-      break;
-    }
-    else if (wc_ispunct(*next) && (*next!=wc_hyphen) && (*next!=wc_period) &&
-             (*next!=wc_apostrophe) && (*next!=wc_backquote) &&
-             (*next!=wc_backslash))
-    {
-      *authline = next+1;
-      break;
-    }
-    if (*next == wc_space)
-    {
-      for (++next; *next==wc_space; ++next) ;
-      if ((len = matchany(next,seplist)) > 0)
-      {
-        *authline=next+len;
-        break;
-      }
-      if ((len = matchany(next,afterlist)) > 0)
-      {
-        first = next+len;
-        next += len-1;
-        last = suffix = NULL;
-        continue;
-      }
-      if (isjrsuffix(next))
-        suffix=next;
-      else if (ws_strchr(ws_lit("XVI"), *next) &&
-               !partofword(*(next+ws_strspn(next, ws_lit("XVI")))))
-        suffix=next;
-      else
-      {
-        scanahead = next;
-        skipesc(&scanahead);
-        if (wc_isupper(*scanahead))
-        {
-          last = next;
-          suffix = NULL;
-        }
-      }
-      --next;
-    }
-  }
-  if (*next==wc_null) *authline = next;
-
-  /* Put the sirname into the buffer first. */
-  endp = NULL;
-  if (last)
-  {
-    endp = (suffix ? suffix : next)-1;
-    if (endp<last) abort();
-    for (; (endp>last) && ((*endp==wc_space) || (*endp==wc_comma)); --endp) ;
-  }
-  if (!endp || (*endp==wc_period))
-  {
-    /* Here's where it gets tough.  We either have a single-word name, or the
-     * last name ends in a "." which means maybe it's just a middle initial or
-     * other abbreviation.  We could be dealing with a line like, "Billy,
-     * Joe E., and Bob Smith", in which case we have to go searching for the
-     * real last name. To handle this case, we will try a recursive call. */
-    scanahead = *authline;
-    if (grabauthor(&scanahead,seplist,afterlist,unknownlist) &&
-        ((bp = ws_strchr(buf, wc_comma)) != NULL))
-       /* got it! Make our old last name part of the first name. */
-      last = NULL;
-    else if (last)
-    {
-      /* Couldn't find a last name. Just use this one and hope it's okay. */
-      ws_strncpy(buf, last, endp-last+1);
-      bp = buf+(endp-last+1);
-    }
-    else
-      bp = buf;
-  }
-  else
-  {
-    ws_strncpy(buf, last, endp-last+1);
-    bp = buf+(endp-last+1);
-  }
-
-  /* Next, put the first name into the buffer. */
-  endp = (last ? last : (suffix ? suffix : next))-1;
-  if (endp<first) abort();
-  for (; (endp>=first) && ((*endp==wc_space) || (*endp==wc_comma)); --endp) ;
-  ++endp;
-  if (endp>first)
-  {
-    if (bp > buf)
-    {
-      *bp++ = wc_comma;
-      *bp++ = wc_space;
-    }
-    ws_strncpy(bp, first, endp-first);
-    bp += endp-first;
-  }
-
-  /* Finally, put the suffix (if any) into the buffer. */
-  if (suffix)
-  {
-    for (endp=next-1;
-         (endp>=suffix) && ((*endp==wc_space) || (*endp==wc_comma));
-         --endp) ;
-    ++endp;
-    if (endp>suffix)
-    {
-      if (bp > buf) *bp++ = wc_space;
-      ws_strncpy(bp, suffix, endp-suffix);
-      bp += endp-suffix;
-    }
-  }
-
-  *bp = wc_null;
-  if (matchwithin(buf, unknownlist))
-    return grabauthor(authline,seplist,afterlist,unknownlist);
-  return buf;
-}
-
-/* genauthorindex(<file>,<inname>,<outname>)
- *   Read author data from file handle <file> and generate from it a LaTeX
- *   author index file named <outname>.
- *   Return 0 on success, 1 on warnings, or 2 on error. */
-int
-genauthorindex(fs,outname)
-  FSTATE *fs;
-  const char *outname;
-{
-  FILE *f;
-  int eof = 0;
-  int arraysize, numauthors, i;
-  WORDLIST *seplist=&wl_and, *afterlist=&wl_by, *ignorelist=&wl_unknown;
-  SONGENTRY **authors;
-  WCHAR authorbuf[MAXLINELEN], *bp;
-  WCHAR songnumbuf[MAXLINELEN], linknamebuf[MAXLINELEN];
-  WCHAR *thisnum, *thislink;
-  const WCHAR *auth;
-
-  fprintf(stderr, "songidx: Parsing author index data file %s...\n",
-          fs->filename);
-
-  eof = 0;
-  authors = NULL;
-  for (arraysize=numauthors=i=0; !eof; ++i)
-  {
-    if (!filereadln(fs,authorbuf,&eof)) return 2;
-    if (eof) break;
-    if (authorbuf[0] == wc_percent)
-    {
-      if (!ws_strncmp(authorbuf, ws_lit("%sep "), 5))
-        wl_insert(&seplist, authorbuf + 5);
-      else if (!ws_strncmp(authorbuf, ws_lit("%after "), 7))
-        wl_insert(&afterlist, authorbuf + 7);
-      else if (!ws_strncmp(authorbuf, ws_lit("%ignore "), 8))
-        wl_insert(&ignorelist, authorbuf + 8);
-      --i;
-      continue;
-    }
-    if (!filereadln(fs,songnumbuf,&eof))
-    {
-      fileclose(fs);
-      return 2;
-    }
-    if (eof)
-    {
-      fprintf(stderr, "songidx:%s:%d: incomplete author entry"
-                      " (orphan byline)\n", fs->filename, fs->lineno);
-      fileclose(fs);
-      return 2;
-    }
-    if (!filereadln(fs,linknamebuf,&eof))
-    {
-      fileclose(fs);
-      return 2;
-    }
-    if (eof)
-    {
-      fprintf(stderr, "songidx:%s:%d: incomplete author entry"
-                      " (missing hyperlink)\n", fs->filename, fs->lineno);
-      fileclose(fs);
-      return 2;
-    }
-    if (((thisnum = (WCHAR *) calloc(ws_strlen(songnumbuf)+1,
-                                     sizeof(WCHAR))) == NULL) ||
-        ((thislink = (WCHAR *) calloc(ws_strlen(linknamebuf)+1,
-                                      sizeof(WCHAR))) == NULL))
-    {
-      fprintf(stderr, "songidx:%s:%d: song number/link too long"
-                      " (out of memory)\n", fs->filename, fs->lineno);
-      return 2;
-    }
-    ws_strcpy(thisnum, songnumbuf);
-    ws_strcpy(thislink, linknamebuf);
-
-    for (bp=authorbuf;
-         (auth=grabauthor((const WCHAR **) &bp, seplist, afterlist,
-                          ignorelist)) != NULL;
-         ++numauthors)
-    {
-      if (numauthors >= arraysize)
-      {
-        SONGENTRY **temp;
-        arraysize *= 2;
-        if (arraysize==0) arraysize=64;
-        temp = (SONGENTRY **) realloc(authors,arraysize*sizeof(SONGENTRY *));
-        if (!temp)
-        {
-          fprintf(stderr, "songidx:%s:%d: too many song authors"
-                          " (out of memory)\n", fs->filename, fs->lineno);
-          return 2;
-        }
-        authors = temp;
-      }
-      if ((authors[numauthors] = (SONGENTRY *)
-                                 malloc(sizeof(SONGENTRY))) == NULL)
-      {
-        fprintf(stderr, "songidx:%s:%d: too many song authors"
-                        " (out of memory)\n", fs->filename, fs->lineno);
-        return 2;
-      }
-      if ((authors[numauthors]->title =
-           (WCHAR *) calloc(ws_strlen(auth)+1, sizeof(WCHAR))) == NULL)
-      {
-        fprintf(stderr, "songidx:%s:%d: author name too long (out of memory)\n",
-                fs->filename, fs->lineno);
-        return 2;
-      }
-      ws_strcpy(authors[numauthors]->title, auth);
-      authors[numauthors]->num = thisnum;
-      authors[numauthors]->linkname = thislink;
-      authors[numauthors]->idx = i;
-    }
-  }
-  fileclose(fs);
-
-  /* Sort the array by author. */
-  qsort(authors, numauthors, sizeof(*authors), songcmp);
-
-  /* Generate an author index LaTeX file from the sorted data.
-   * Combine any entries with the same author name into a single index entry. */
-  fprintf(stderr, "songidx: Generating author index TeX file %s...\n", outname);
-  if (strcmp(outname,"-"))
-  {
-    if ((f = fopen(outname, "w")) == NULL)
-    {
-      fprintf(stderr, "songidx: Unable to open %s for writing.\n", outname);
-      return 2;
-    }
-  }
-  else
-  {
-    f = stdout;
-    outname = "stdout";
-  }
-
-#define TRYWRITE(x) \
-  if (!(x)) \
-  { \
-    fprintf(stderr, "songidx:%s: write error\n", outname); \
-    if (f == stdout) fflush(f); else fclose(f); \
-    return 2; \
-  }
-
-  TRYWRITE(ws_fputs(ws_lit("\\begin{idxblock}{"), f) >= 0)
-  for (i=0; i<numauthors; ++i)
-  {
-    if ((i>0) && !ws_coll(authors[i]->title, authors[i-1]->title))
-    {
-      TRYWRITE(ws_fputs(ws_lit("\\\\"), f) >= 0)
-    }
-    else
-    {
-      TRYWRITE((ws_fputs(ws_lit("}\n\\idxentry{"), f) >= 0) &&
-               (ws_fputs(authors[i]->title, f) >= 0) &&
-               (ws_fputs(ws_lit("}{"), f) >= 0))
-    }
-    TRYWRITE((ws_fputs(ws_lit("\\hyperlink{"), f) >= 0) &&
-             (ws_fputs(authors[i]->linkname, f) >= 0) &&
-             (ws_fputs(ws_lit("}{"), f) >= 0) &&
-             (ws_fputs(authors[i]->num, f) >= 0) &&
-             (ws_fputs(ws_lit("}"), f) >= 0))
-  }
-  TRYWRITE(ws_fputs(ws_lit("}\n\\end{idxblock}\n"), f) >= 0)
-
-#undef TRYWRITE
-
-  if (f == stdout)
-    fflush(f);
-  else
-    fclose(f);
-  return 0;
-}

Modified: trunk/Master/texmf-dist/doc/latex/songs/songidx/bible.can
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/bible.can	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/bible.can	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 Kevin W. Hamlen
+# Copyright (C) 2017 Kevin W. Hamlen
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License

Modified: trunk/Master/texmf-dist/doc/latex/songs/songidx/catholic.can
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/catholic.can	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/catholic.can	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 Kevin W. Hamlen
+# Copyright (C) 2017 Kevin W. Hamlen
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License

Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/chars.h
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/chars.h	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/chars.h	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,137 +0,0 @@
-/* Copyright (C) 2012 Kevin W. Hamlen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA  02110-1301, USA.
- *
- * The latest version of this program can be obtained from
- * http://songs.sourceforge.net.
- */
-
-#ifndef CHARS_H
-#define CHARS_H
-
-#if HAVE_CONFIG_H
-#  include "config.h"
-#else
-#  include "vsconfig.h"
-#endif
-
-#if HAVE_STDLIB_H
-#  include <stdlib.h>
-#endif
-#if HAVE_WCHAR_H
-#  include <wchar.h>
-#endif
-#if HAVE_WCTYPE_H
-#  include <wctype.h>
-#endif
-
-#if HAVE_WCHAR_T
-
-typedef wchar_t WCHAR;
-
-#define wc_lit(c) (L ## c)
-#define wc_isalpha(c) iswalpha((c))
-#define wc_ispunct(c) iswpunct((c))
-#define wc_isupper(c) iswupper((c))
-#define wc_isspace(c) iswspace((c))
-#define wc_isdigit(c) iswdigit((c))
-#define wc_tolower(c) towlower((c))
-#define wc_toupper(c) towupper((c))
-
-#define ws_lit(s) ws_lit2(s)
-#define ws_lit2(s) (L ## s)
-#define ws_strlen(p) wcslen((p))
-#define ws_strcmp(p1,p2) wcscmp((p1),(p2))
-#define ws_strncmp(p1,p2,n) wcsncmp((p1),(p2),(n))
-#define ws_strcpy(p1,p2) wcscpy((p1),(p2))
-#define ws_strncpy(p1,p2,n) wcsncpy((p1),(p2),(n))
-#define ws_memmove(p1,p2,n) wmemmove((p1),(p2),(n))
-#define ws_strchr(p,c) wcschr((p),(c))
-#define ws_strspn(p1,p2) wcsspn((p1),(p2))
-#define ws_strpbrk(p1,p2) wcspbrk((p1),(p2))
-#define ws_strtol(p1,p2,n) wcstol((p1),(p2),(n))
-#define ws_coll(p1,p2) wcscoll((p1),(p2))
-#define ws_fprintf1(fh,fs,x) fwprintf((fh),(fs),(x))
-#define ws_fprintf2(fh,fs,x,y) fwprintf((fh),(fs),(x),(y))
-#define ws_fprintf3(fh,fs,x,y,z) fwprintf((fh),(fs),(x),(y),(z))
-#define ws_fgets(p,n,fh) fgetws((p),(n),(fh))
-#define ws_fputs(s,fh) fputws((s),(fh))
-#define ws_fputc(c,fh) fputwc((c),(fh))
-
-#else
-
-#include <ctype.h>
-#if HAVE_STRING_H
-#  include <string.h>
-#elif HAVE_STRINGS_H
-#  include <strings.h>
-#endif
-
-typedef char WCHAR;
-
-#define wc_lit(c) (c)
-#define wc_isalpha(c) isalpha((c))
-#define wc_ispunct(c) ispunct((c))
-#define wc_isupper(c) isupper((c))
-#define wc_isspace(c) isspace((c))
-#define wc_isdigit(c) isdigit((c))
-#define wc_tolower(c) tolower((c))
-#define wc_toupper(c) toupper((c))
-
-#define ws_lit(s) (s)
-#define ws_strlen(p) strlen((p))
-#define ws_strcmp(p1,p2) strcmp((p1),(p2))
-#define ws_strncmp(p1,p2,n) strncmp((p1),(p2),(n))
-#define ws_strcpy(p1,p2) strcpy((p1),(p2))
-#define ws_strncpy(p1,p2,n) strncpy((p1),(p2),(n))
-#define ws_memmove(p1,p2,n) memmove((p1),(p2),(n))
-#define ws_strchr(p,c) strchr((p),(c))
-#define ws_strspn(p1,p2) strspn((p1),(p2))
-#define ws_strpbrk(p1,p2) strpbrk((p1),(p2))
-#define ws_strtol(p1,p2,n) strtol((p1),(p2),(n))
-#define ws_coll(p1,p2) strcoll((p1),(p2))
-#define ws_fprintf1(fh,fs,x) fprintf((fh),(fs),(x))
-#define ws_fprintf2(fh,fs,x,y) fprintf((fh),(fs),(x),(y))
-#define ws_fprintf3(fh,fs,x,y,z) fprintf((fh),(fs),(x),(y),(z))
-#define ws_fgets(p,n,fh) fgets((p),(n),(fh))
-#define ws_fputs(s,fh) fputs((s),(fh))
-#define ws_fputc(c,fh) fputc((c),(fh))
-
-#endif
-
-#define wc_null wc_lit('\0')
-#define wc_hyphen wc_lit('-')
-#define wc_apostrophe wc_lit('\'')
-#define wc_backquote wc_lit('`')
-#define wc_backslash wc_lit('\\')
-#define wc_lbrace wc_lit('{')
-#define wc_rbrace wc_lit('}')
-#define wc_comma wc_lit(',')
-#define wc_period wc_lit('.')
-#define wc_space wc_lit(' ')
-#define wc_newline wc_lit('\n')
-#define wc_cr wc_lit('\r')
-#define wc_asterisk wc_lit('*')
-#define wc_hash wc_lit('#')
-#define wc_pipe wc_lit('|')
-#define wc_colon wc_lit(':')
-#define wc_semicolon wc_lit(';')
-#define wc_capA wc_lit('A')
-#define wc_percent wc_lit('%')
-#define wc_tilda wc_lit('~')
-
-#endif
-

Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/fileio.c
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/fileio.c	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/fileio.c	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,122 +0,0 @@
-/* Copyright (C) 2012 Kevin W. Hamlen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA  02110-1301, USA.
- *
- * The latest version of this program can be obtained from
- * http://songs.sourceforge.net.
- */
-
-#if HAVE_CONFIG_H
-#  include "config.h"
-#else
-#  include "vsconfig.h"
-#endif
-
-#include "chars.h"
-#include "songidx.h"
-#include "fileio.h"
-
-#if HAVE_STRING_H
-#  include <string.h>
-#endif
-
-/* fileopen(<fstate>,<filename>)
- *   Open <filename> for reading, storing the file handle in <fstate>. Return
- *   1 on success or 0 on failure.
- */
-int
-fileopen(fs,fnam)
-  FSTATE *fs;
-  const char *fnam;
-{
-  if (strcmp(fnam,"-"))
-  {
-    fs->f = fopen(fnam, "r");
-    if (!fs->f)
-    {
-      fprintf(stderr, "songidx: Unable to open %s for reading.\n", fnam);
-      return 0;
-    }
-  }
-  else
-  {
-    fs->f = stdin;
-    fnam = "stdin";
-  }
-  fs->filename = (char *) calloc(strlen(fnam)+1, sizeof(char));
-  if (!fs->filename)
-  {
-    fprintf(stderr, "songidx: Out of memory!\n");
-    fclose(fs->f);
-    return 0;
-  }
-  strcpy(fs->filename, fnam);
-  fs->lineno = 0;
-  return 1;
-}
-
-/* fileclose(<fstate>)
- *   Close file handle <fstate>.
- */
-void
-fileclose(fs)
-  FSTATE *fs;
-{
-  if (fs->f != stdin) fclose(fs->f);
-  free(fs->filename);
-  fs->f = NULL;
-  fs->filename = NULL;
-  fs->lineno = 0;
-}
-
-/* filereadln(<fstate>,<buffer>,<eof flag>)
- *   Read a line of output into <buffer>, which should be at least MAXLINELEN
- *   wide-characters in size.  If the line is too long to fit into the buffer,
- *   close the file and report an error.  Eliminate the trailing \n from the
- *   returned line.  Return 1 on success or end-of-line, or 0 on failure.  If
- *   end-of-line, set <eof flag> to 1. */
-int
-filereadln(fs,buf,eof)
-  FSTATE *fs;
-  WCHAR *buf;
-  int *eof;
-{
-  size_t n;
-
-  ++fs->lineno;
-  if (!ws_fgets(buf, MAXLINELEN, fs->f))
-  {
-    if (!ferror(fs->f))
-    {
-      *eof=1;
-      return 1;
-    }
-    fprintf(stderr, "songidx:%s:%d: read error\n", fs->filename, fs->lineno);
-    fileclose(fs);
-    return 0;
-  }
-  n = ws_strlen(buf);
-  if ((n==0) || ((buf[n-1] != wc_newline) && (buf[n-1] != wc_cr)))
-  {
-    fprintf(stderr, "songidx:%s:%d: line too long\n", fs->filename, fs->lineno);
-    fileclose(fs);
-    return 0;
-  }
-  buf[n-1] = wc_null;
-  if ((n>=2) && ((buf[n-2] == wc_newline) || (buf[n-2] == wc_cr)))
-    buf[n-2] = wc_null;
-  return 1;
-}

Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/fileio.h
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/fileio.h	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/fileio.h	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,54 +0,0 @@
-/* Copyright (C) 2012 Kevin W. Hamlen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA  02110-1301, USA.
- *
- * The latest version of this program can be obtained from
- * http://songs.sourceforge.net.
- */
-
-#ifndef FILEIO_H
-#define FILEIO_H
-
-#if HAVE_CONFIG_H
-#  include "config.h"
-#else
-#  include "vsconfig.h"
-#endif
-
-#include "chars.h"
-
-#if HAVE_STDIO_H
-#  include <stdio.h>
-#endif
-
-/* Define the maximum length of lines found in the input file */
-#define MAXLINELEN 1024
-
-/* FSTATE structures model the state of a FILE */
-typedef struct
-{
-  FILE *f;
-  char *filename;
-  int lineno;
-}
-FSTATE;
-
-/* The following functions are in fileio.c */
-extern int fileopen(FSTATE *fs, const char *fnam);
-extern void fileclose(FSTATE *fs);
-extern int filereadln(FSTATE *fs, WCHAR *buf, int *eof);
-
-#endif

Modified: trunk/Master/texmf-dist/doc/latex/songs/songidx/greek.can
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/greek.can	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/greek.can	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 Kevin W. Hamlen
+# Copyright (C) 2017 Kevin W. Hamlen
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License

Modified: trunk/Master/texmf-dist/doc/latex/songs/songidx/protestant.can
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/protestant.can	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/protestant.can	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 Kevin W. Hamlen
+# Copyright (C) 2017 Kevin W. Hamlen
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License

Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/scripidx.c
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/scripidx.c	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/scripidx.c	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,1105 +0,0 @@
-/* Copyright (C) 2012 Kevin W. Hamlen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA  02110-1301, USA.
- *
- * The latest version of this program can be obtained from
- * http://songs.sourceforge.net.
- */
-
-
-/* Create a LaTeX scripture index file from a scripture index .dat file. */
-
-#if HAVE_CONFIG_H
-#  include "config.h"
-#else
-#  include "vsconfig.h"
-#endif
-
-#include "chars.h"
-#include "songidx.h"
-#include "fileio.h"
-
-#if HAVE_STRING_H
-#  include <string.h>
-#else
-#  if HAVE_STRINGS_H
-#    include <strings.h>
-#  endif
-#endif
-
-/* The following constant should be a large integer (but not greater than
- * INT_MAX) limiting the maximum number of verses in a single chapter of the
- * bible. */
-#define MAXVERSES 999
-
-/* A BBOOK struct describes a book of the Bible, including:
- *   name: a \0-separated string of names (the first one is the name used when
- *         printing the scripture index)
- *   verses: an array of numbers giving the number of verses in each chapter
- *   aliases: the number of names in the name field
- *   chapters: the number of chapters in the book */
-typedef struct
-{
-  WCHAR *name;
-  int *verses;
-  int aliases;
-  int chapters;
-}
-BBOOK;
-
-/* A VERSE struct refers to a verse in a book of the Bible.
- *   chapter: the integral chapter number of the verse (1 if the book has only
- *            one chapter)
- *   verse: the integral verse number */
-typedef struct
-{
-  int chapter, verse;
-}
-VERSE;
-
-/* A SONGLIST is a list of songs
- *   num: a string denoting the song number (e.g. "3" or "H10")
- *   link: a string denoting the hyperlink label for the song
- *   int: an integer key used to order songs when sorting
- *   next: next pointer */
-typedef struct songliststruct
-{
-  WCHAR *num;
-  WCHAR *link;
-  int key;
-  struct songliststruct *next;
-}
-SONGLIST;
-
-/* A VERSELIST is the list of verses in a particular book of the Bible at which
- * the set of songs referencing that verse differs from the set of songs
- * referencing the previous verse in the book.  Such a list allows us to
- * sparsely represent the set of songs referencing each verse in the book
- * without having to create a distinct list entry for each verse in the entire
- * book.  For example, if song "H1" references verses 3:1-10, that would
- * represented by a two-element list:
- *      [ {v={chapter=3, verse=1}, adds=[H1], drops=[]};
- *        {v={chapter=3, verse=10}, adds=[], drops=[H1]} ]
- * This is more reasonable than creating ten list items to refer to such a
- * range.
- *   v: the verse at which the set of song references is changing
- *   adds: a list of songs that refer to this verse but not the previous one
- *   drops: a list of songs that refer to this verse but not the next one
- *   next: pointer to next verse at which the set of references changes */
-typedef struct verseliststruct
-{
-  VERSE v;
-  SONGLIST *adds, *drops;
-  struct verseliststruct *next;
-}
-VERSELIST;
-
-/* The bible global variable stores the array of bible books being used to
- * generate the scripture index.  The numbooks variable stores the number of
- * elements in the bible array. */
-BBOOK *bible = NULL;
-int numbooks = 0;
-
-/* free_bible()
- *   Free the bible array, set it to NULL, and set numbooks to 0. */
-static void
-free_bible()
-{
-  int i;
-
-  if (!bible) return;
-  for (i=0; i<numbooks; ++i)
-  {
-    if (bible[i].name) free(bible[i].name);
-    if (bible[i].verses) free(bible[i].verses);
-  }
-  free(bible);
-  bible = NULL;
-  numbooks = 0;
-}
-
-/* free_songlist(<slist>)
- *   Free song list <slist>. */
-static void
-free_songlist(sl)
-  SONGLIST *sl;
-{
-  SONGLIST *next;
-
-  for (; sl; sl=next)
-  {
-    if (sl->num) free(sl->num);
-    if (sl->link) free(sl->link);
-    next = sl->next;
-    free(sl);
-  }
-}
-
-/* free_verselist(<vlist>)
- *    Free verse list <vlist>. */
-static void
-free_verselist(vl)
-  VERSELIST *vl;
-{
-  VERSELIST *next;
-
-  for (; vl; vl=next)
-  {
-    free_songlist(vl->adds);
-    free_songlist(vl->drops);
-    next = vl->next;
-    free(vl);
-  }
-}
-
-/* freeidxarray(<array>,<len>)
- *   Free the array of verselists given by <array>.  The array should have
- *   length <len>. */
-static void
-freeidxarray(idx,len)
-  VERSELIST **idx;
-  int len;
-{
-  int i;
-
-  if (!idx) return;
-  for (i=0; i<len; ++i) free_verselist(idx[i]);
-  free(idx);
-}
-
-/* readbible(<filename>)
- *   Read bible data file <filename> into the bible array and set numbooks to
- *   the number of books read.  Return 0 on error or 1 on success. */
-static int
-readbible(filename)
-  const char *filename;
-{
-  FSTATE fs;
-  int eof = 0;
-  WCHAR buf[MAXLINELEN];
-  int verses[MAXLINELEN/2+1];
-  int arraysize, j;
-  WCHAR *p1, *p2;
-
-  if (!fileopen(&fs,filename)) return 0;
-
-  bible = NULL;
-  eof = 0;
-  for (arraysize=numbooks=0; !eof; ++numbooks)
-  {
-    if (!filereadln(&fs,buf,&eof))
-    {
-      free_bible();
-      return 0;
-    }
-    if (eof) break;
-    if (*buf == wc_hash)
-    {
-      --numbooks;
-      continue;
-    }
-    if (numbooks >= arraysize)
-    {
-      BBOOK *temp;
-      arraysize *= 2;
-      if (arraysize==0) arraysize=64;
-      temp = (BBOOK *) realloc(bible,arraysize*sizeof(BBOOK));
-      if (!temp)
-      {
-        fprintf(stderr, "songidx:%s:%d: too many bible books (out of memory)\n",
-                fs.filename, fs.lineno);
-        free_bible();
-        return 0;
-      }
-      bible = temp;
-    }
-    if ((bible[numbooks].name =
-         (WCHAR *) calloc(ws_strlen(buf)+1,sizeof(WCHAR))) == NULL)
-    {
-      fprintf(stderr, "songidx:%s:%d: bible book name too large"
-                      " (out of memory)\n", fs.filename, fs.lineno);
-      fileclose(&fs);
-      free_bible();
-      return 0;
-    }
-    for(j=1, p1=buf, p2=bible[numbooks].name; *p1!=wc_null; ++p1, ++p2)
-    {
-      if (*p1 == wc_pipe)
-      {
-        ++j;
-        *p2=wc_null;
-      }
-      else *p2=*p1;
-    }
-    *p2 = wc_null;
-    if (j == 0)
-    {
-      fprintf(stderr, "songidx:%s:%d: bible book has no name\n",
-              fs.filename, fs.lineno);
-      free(bible[numbooks].name);
-      fileclose(&fs);
-      free_bible();
-      return 0;
-    }
-    bible[numbooks].aliases = j;
-    do
-    {
-      if (!filereadln(&fs,buf,&eof))
-      {
-        free_bible();
-        return 0;
-      }
-      if (eof)
-      {
-        fprintf(stderr, "songidx:%s:%d: incomplete bible book entry (book"
-                        " title with no verses)\n", fs.filename, fs.lineno);
-        free_bible();
-        return 0;
-      }
-    }
-    while (*buf == wc_hash);
-    for (j=0, p1=buf; *p1!=wc_null; ++j)
-    {
-      long v = ws_strtol(p1, &p1, 10);
-      if ((v<=0) || (v>MAXVERSES))
-      {
-        fprintf(stderr, "songidx:%s:%d: illegal verse count for chapter %d\n",
-                fs.filename, fs.lineno, j+1);
-        fileclose(&fs);
-        free_bible();
-        return 0;
-      }
-      verses[j] = (int) v;
-    }
-    if (j == 0)
-    {
-      fprintf(stderr, "songidx:%s:%d: book ", fs.filename, fs.lineno);
-      ws_fputs(bible[numbooks].name, stderr);
-      fprintf(stderr, " has no chapters\n");
-      fileclose(&fs);
-      free_bible();
-      return 0;
-    }
-    bible[numbooks].chapters = j;
-    if ((bible[numbooks].verses = (int *) calloc(j, sizeof(int))) == NULL)
-    {
-      fprintf(stderr, "songidx:%s:%d: too many verses (out of memory)\n",
-              fs.filename, fs.lineno);
-      fileclose(&fs);
-      free_bible();
-      return 0;
-    }
-    for (j=0; j<bible[numbooks].chapters; ++j)
-      bible[numbooks].verses[j] = verses[j];
-  }
-
-  fileclose(&fs);
-  return 1;
-}
-
-/* ws_stricmp(<string1>,<string2>)
- *   Case-insensitive, wide-string comparison function.  (Some standard C libs
- *   already have one of these, but not all do; for compatibility I have to
- *   write my own.) */
-static int
-ws_stricmp(p1,p2)
-  const WCHAR *p1;
-  const WCHAR *p2;
-{
-  for (;;)
-  {
-    int diff = wc_tolower(*p1) - wc_tolower(*p2);
-    if (diff != 0) return diff;
-    if (*p1 == wc_null) return 0;
-    ++p1; ++p2;
-  }
-}
-
-/* parseref(<string>,<book>,<verse>)
- *   Interpret <string> as a scripture reference, and store the NUMBER of the
- *   book of the Bible it refers to in <book>, and the chapter and verse that
- *   it refers to in <verse>.  Return a pointer into <string> to the suffix of
- *   <string> that was not parsed.  The <book> and <verse> should hold the book
- *   and verse of the PREVIOUS scripture reference (if any) on entry to the
- *   function.  If book or chapter information is missing, they will be drawn
- *   from this info.  That way, successive calls can correctly parse a run-on
- *   string like "Philippians 3:1,5; 4:3", inferring that "5" refers to
- *   "Philippians 3" and "4:3" refers to "Philippians".  If the parser
- *   encounters an error in processing the book name (e.g., a book name was
- *   specified but not recognized), <book> is set to numbooks.  If no chapter
- *   or no verse is provided (e.g., the reference is just "Philippians" or
- *   "Philippians 3") then the chapter and/or verse fields of <verse> are set
- *   to -1. */
-static WCHAR *
-parseref(r,book,v)
-  WCHAR *r;
-  int *book;
-  VERSE *v;
-{
-  WCHAR *p;
-  WCHAR c;
-  int i;
-  const WCHAR *a;
-
-  while (*r==wc_space) ++r;
-  p = ws_strpbrk(r, ws_lit(",;-"));
-  if (!p) p=r+ws_strlen(r);
-  if (p>r) --p;
-  while ((p>r) && (*p==wc_space)) --p;
-  while ((p>r) && (wc_isdigit(*p) || (*p==wc_colon))) --p;
-  if (p>r)
-  {
-    if (*p==wc_space)
-    {
-      *p++ = wc_null;
-      for (*book=0; *book<numbooks; ++(*book))
-      {
-        for (i=0, a=bible[*book].name;
-             i<bible[*book].aliases;
-             ++i, a+=ws_strlen(a)+1)
-          if (!ws_stricmp(r,a)) break;
-        if (i<bible[*book].aliases) break;
-      }
-    }
-    else
-    {
-      *book=numbooks;
-    }
-    r=p;
-    v->chapter = v->verse = -1;
-  }
-  while (wc_isdigit(*p)) ++p;
-  if (*p==wc_colon)
-  {
-    *p++ = wc_null;
-    v->chapter = ws_strtol(r,NULL,10);
-    r=p;
-    while (wc_isdigit(*p)) ++p;
-  }
-  if (r==p) return NULL;
-  c = *p;
-  *p = wc_null;
-  if ((v->chapter < 0) && (*book>=0) && (*book<numbooks) &&
-      (bible[*book].chapters == 1))
-  {
-    /* Special case: This book only has one chapter. */
-    v->chapter = 1;
-  }
-  if (v->chapter < 0)
-    v->chapter = ws_strtol(r,NULL,10);
-  else
-    v->verse = ws_strtol(r,NULL,10);
-  *p = c;
-  while (*p==wc_space) ++p;
-  if (*p && (*p!=wc_comma) && (*p!=wc_semicolon) && (*p!=wc_hyphen))
-    return NULL;
-  return p;
-}
-
-/* vlt(<v1>,<v2>)
- *   Return 1 if verse <v1> precedes <v2> and 0 otherwise. */
-static int
-vlt(v1,v2)
-  const VERSE v1;
-  const VERSE v2;
-{
-  return ((v1.chapter == v2.chapter) ? (v1.verse < v2.verse)
-                                     : (v1.chapter < v2.chapter));
-}
-
-/* vcpy(<vdest>,<vsrc>)
- *   Copy the data from verse <vsrc> into verse <vdest>. */
-static void
-vcpy(vdest,vsrc)
-  VERSE *vdest;
-  const VERSE vsrc;
-{
-  vdest->chapter = vsrc.chapter;
-  vdest->verse = vsrc.verse;
-}
-
-/* getvcount(<book>,<chapter>)
- *   Return the number of verses in chapter <chapter> of book number <book> of
- *   the Bible. */
-static int
-getvcount(book,chapter)
-  int book;
-  int chapter;
-{
-  if ((book<0) || (book>=numbooks))
-  {
-    /* This should never happen. */
-    fprintf(stderr, "songidx: Internal error (getvcount):"
-                    " Book %d out of range.\n", book);
-    return 0;
-  }
-  if ((chapter<1) || (chapter > bible[book].chapters))
-    return 0;
-  return bible[book].verses[chapter-1];
-}
-
-/* vinc(<book>,<verse>)
- *   Return a reference to the verse immediately following <verse> in book
- *   number <book> of the Bible.  The verse returned is static, so successive
- *   calls will overwrite it.  If <verse> is the last verse in <book>, the
- *   return value will reference verse 1 of a non-existant chapter. */
-static VERSE
-vinc(book,v)
-  int book;
-  const VERSE v;
-{
-  static VERSE vret;
-  int cnt;
-
-  vret.chapter = v.chapter;
-  vret.verse = v.verse + 1;
-  cnt = getvcount(book, vret.chapter);
-  if (cnt <= 0)
-  {
-    /* This should never happen. */
-    fprintf(stderr, "songidx: Internal Error (vinc): Book ");
-    ws_fputs(bible[book].name, stderr);
-    fprintf(stderr, " has no chapter %d.\n", vret.chapter);
-  }
-  else if (vret.verse > cnt)
-  {
-    ++vret.chapter;
-    vret.verse = 1;
-  }
-  return vret;
-}
-
-/* vdec(<book>,<verse>)
- *   Return a reference to the verse immediately preceding <verse> in book
- *   number <book> of the Bible.  The verse returned is static, so successive
- *   calls will overwrite it.  If <verse> is the first verse in <book>, the
- *   return value will reference chapter -1 and an error message will be
- *   printed to stderr. */
-static VERSE
-vdec(book,v)
-  int book;
-  const VERSE v;
-{
-  static VERSE vret;
-  vret.chapter = v.chapter;
-  vret.verse = v.verse - 1;
-  if (vret.verse < 1)
-  {
-    --vret.chapter;
-    vret.verse = getvcount(book, vret.chapter);
-    if (vret.verse <= 0)
-    {
-      /* This should never happen. */
-      fprintf(stderr, "songidx: Internal Error (vdec): Book ");
-      ws_fputs(bible[book].name, stderr);
-      fprintf(stderr, " has no chapter %d.\n", vret.chapter);
-      vret.verse = -1;
-    }
-  }
-  return vret;
-}
-
-/* addref(<reflist>,<song>,<link>,<key>)
- *   Add a reference to song (<song>,<link>,<key>) to REFLIST <reflist>.  The
- *   <reflist> is assumed to be sorted by <key>.  If a reference with the same
- *   <key> already exists, <reflist> is left unchanged.  Both <song> and
- *   <link> are COPIED into the list.  Return 1 on success or 0 on failure. */
-static int
-addref(sl,n,l,key)
-  SONGLIST **sl;
-  const WCHAR *n;
-  const WCHAR *l;
-  int key;
-{
-  SONGLIST *newsong;
-
-  for (; *sl; sl=&((*sl)->next))
-    if ((*sl)->key >= key) break;
-  if (*sl && ((*sl)->key == key)) return 1;
-
-  if ((newsong = (SONGLIST *) malloc(sizeof(SONGLIST))) == NULL)
-  {
-    fprintf(stderr, "songidx: too many scripture references (out of memory)\n");
-    return 0;
-  }
-  newsong->num = newsong->link = NULL;
-  if (((newsong->num = (WCHAR *) calloc(ws_strlen(n)+1,
-                                        sizeof(WCHAR))) == NULL) ||
-      ((newsong->link = (WCHAR *) calloc(ws_strlen(l)+1,
-                                         sizeof(WCHAR))) == NULL))
-  {
-    fprintf(stderr, "songidx: too many scripture references (out of memory)\n");
-    if (newsong->num) free(newsong->num);
-    if (newsong->link) free(newsong->link);
-    free(newsong);
-    return 0;
-  }
-  ws_strcpy(newsong->num, n);
-  ws_strcpy(newsong->link, l);
-  newsong->key = key;
-  newsong->next = *sl;
-  *sl = newsong;
-  return 1;
-}
-
-/* insertref(<type>,<verselist>,<verse>,<song>,<link>,<key>)
- *   If <type> is INSERT_ADD, then insert song (<song>,<link>,<key>) into the
- *   set of "adds" for verse <verse> in <verselist>.  If <type> is INSERT_DROP,
- *   then insert the song into the set of "drops" for verse <verse>.  In either
- *   case, create a new entry for <verse> in <verselist> if it doesn't already
- *   exist.  Return 1 on success or 0 on failure. */
-#define INSERT_ADD 1
-#define INSERT_DROP 0
-
-static int
-insertref(type,vpp,v,n,l,key)
-  char type;
-  VERSELIST **vpp;
-  const VERSE v;
-  const WCHAR *n;
-  const WCHAR *l;
-  int key;
-{
-  for (;;)
-  {
-    if (!*vpp || vlt(v, (*vpp)->v))
-    {
-      VERSELIST *vnew = NULL;
-      SONGLIST *sl = NULL;
-      if (((vnew = (VERSELIST *) malloc(sizeof(VERSELIST))) == NULL) ||
-          ((sl = (SONGLIST *) malloc(sizeof(SONGLIST))) == NULL))
-      {
-        fprintf(stderr, "songidx: too many scripture references"
-                        " (out of memory)\n");
-        if (vnew) free(vnew);
-        if (sl) free(sl);
-        return 0;
-      }
-      sl->num = sl->link = NULL;
-      if (((sl->num = (WCHAR *) calloc(ws_strlen(n)+1,
-                                       sizeof(WCHAR))) == NULL) ||
-          ((sl->link = (WCHAR *) calloc(ws_strlen(l)+1,
-                                        sizeof(WCHAR))) == NULL))
-      {
-        fprintf(stderr, "songidx: too many scripture references"
-                        " (out of memory)\n");
-        if (sl->num) free(sl->num);
-        if (sl->link) free(sl->link);
-        free(vnew);
-        free(sl);
-        return 0;
-      }
-      ws_strcpy(sl->num, n);
-      ws_strcpy(sl->link, l);
-      sl->key = key;
-      sl->next = NULL;
-      vcpy(&(vnew->v), v);
-      if (type)
-      {
-        vnew->adds = sl;
-        vnew->drops = NULL;
-      }
-      else
-      {
-        vnew->adds = NULL;
-        vnew->drops = sl;
-      }
-      vnew->next = *vpp;
-      *vpp = vnew;
-      return 1;
-      }
-    else if (!vlt((*vpp)->v, v))
-    {
-      return addref(type ? &((*vpp)->adds) : &((*vpp)->drops), n, l, key);
-    }
-    vpp=&((*vpp)->next);
-  }
-}
-
-/* addrefs(<dest>,<src>)
- *   Take the union of SONGLIST <dest> and SONGLIST <src>, storing the result
- *   in <dest>.  Return 1 on success, or 0 on failure. */
-static int
-addrefs(dest,src)
-  SONGLIST **dest;
-  const SONGLIST *src;
-{
-  for (; src; src=src->next)
-    if (!addref(dest, src->num, src->link, src->key)) return 0;
-  return 1;
-}
-
-/* removerefs(<dest>,<src>)
- *   Take the set difference between SONGLIST <dest> and SONGLIST <src>,
- *   storing the result in <dest>. */
-static void
-removerefs(dest,src)
-  SONGLIST **dest;
-  const SONGLIST *src;
-{
-  SONGLIST **sl;
-
-  for (; src; src=src->next)
-  {
-    for (sl=dest; *sl; sl=&((*sl)->next))
-      if ((*sl)->key >= src->key) break;
-    if (*sl && ((*sl)->key == src->key))
-    {
-      SONGLIST *temp = (*sl)->next;
-      free((*sl)->num);
-      free((*sl)->link);
-      free(*sl);
-      *sl = temp;
-    }
-  }
-}
-
-/* slcmp(<slist1>,<slist2>)
- *   Compare SONGLIST's <slist1> and <slist2>, returning 1 if they are
- *   different and 0 if they are the same. */
-static int
-slcmp(s1,s2)
-  const SONGLIST *s1;
-  const SONGLIST *s2;
-{
-  for (; s1 && s2; s1=s1->next, s2=s2->next)
-    if (s1->key != s2->key) return 1;
-  return (s1 || s2);
-}
-
-/* print_vrange(<file>,<book>,<verse1>,<verse2>,<lastchapter>)
- *   Output LaTeX material to file handle <file> for verse range
- *   <verse1>-<verse2> of book number <book>.  Depending on <lastchapter>,
- *   the outputted material might be the start of a new index entry or the
- *   continuation of a previous entry.  If <lastchapter> is 0, start a new
- *   index entry.  If <lastchapter> is >0, continue the previous entry and
- *   print the chapter of <verse1> only if it differs from <lastchapter>.
- *   If <lastchapter> is <0, continue the previous entry and always print the
- *   chapter number of <verse1>.  Return 0 on success or -1 on failure. */
-static int
-print_vrange(f,book,v1,v2,lastchapter)
-  FILE *f;
-  int book;
-  const VERSE v1;
-  const VERSE v2;
-  int lastchapter;
-{
-  if (ws_fputs(lastchapter ? ws_lit(",") : ws_lit("\\idxentry{"), f) < 0)
-    return -1;
-  if (v1.verse <= 0)
-  {
-    if (ws_fprintf1(f, lastchapter ? ws_lit("\\thinspace %d") : ws_lit("%d"),
-                    v1.chapter) < 0)
-      return -1;
-  }
-  else if ((book>=0) && (book<numbooks) && (bible[book].chapters == 1))
-  {
-    /* This book has only one chapter. */
-    if (ws_fprintf1(f, lastchapter ? ws_lit("\\thinspace %d") : ws_lit("%d"),
-                    v1.verse) < 0)
-      return -1;
-  }
-  else if ((lastchapter<=0) || (lastchapter!=v1.chapter) ||
-           (v1.chapter!=v2.chapter))
-  {
-    if (ws_fprintf2(f, lastchapter ? ws_lit(" %d:%d") : ws_lit("%d:%d"),
-                    v1.chapter, v1.verse) < 0)
-      return -1;
-  }
-  else
-  {
-    if (ws_fprintf1(f, lastchapter ? ws_lit("\\thinspace %d") : ws_lit("%d"),
-                    v1.verse) < 0)
-      return -1;
-  }
-
-  if (vlt(v1,v2))
-  {
-    if (v2.verse <= 0)
-    {
-      if (ws_fprintf1(f, ws_lit("--%d"), v2.chapter) < 0) return -1;
-    }
-    else if (v1.chapter!=v2.chapter)
-    {
-      if (ws_fprintf2(f, ws_lit("--%d:%d"), v2.chapter, v2.verse) < 0)
-        return -1;
-    }
-    else
-    {
-      if (ws_fprintf1(f, ws_lit("--%d"), v2.verse) < 0) return -1;
-    }
-  }
-  return 0;
-}
-
-/* print_reflist(<file>,<slist>)
- *   Output the list of song references given by <slist> to file handle <file>.
- *   Return 0 on success or -1 on failure. */
-static int
-print_reflist(f,sp)
-  FILE *f;
-  const SONGLIST *sp;
-{
-  char first;
-  for (first=1; sp; sp=sp->next)
-  {
-    if (!first)
-    {
-      if (ws_fputs(ws_lit("\\\\"), f) < 0) return -1;
-    }
-    first = 0;
-    if ((ws_fputs(ws_lit("\\hyperlink{"), f) < 0) ||
-        (ws_fputs(sp->link, f) < 0) ||
-        (ws_fputs(ws_lit("}{"), f) < 0) ||
-        (ws_fputs(sp->num, f) < 0) ||
-        (ws_fputs(ws_lit("}"), f) < 0))
-      return -1;
-  }
-  return 0;
-}
-
-/* genscriptureindex(<fstate>,<outname>,<biblename>)
- *   Generate a LaTeX file named <outname> containing material suitable to
- *   typeset the scripture index data found in input file handle <file>.
- *   Input Bible data from an ascii file named <biblename>.  Return 0 on
- *   success, 1 if there were warnings, and 2 if there was a fatal error. */
-int
-genscriptureindex(fs,outname,biblename)
-  FSTATE *fs;
-  const char *outname;
-  const char *biblename;
-{
-  FILE *f;
-  int eof = 0;
-  VERSELIST **idx;
-  int book, book2;
-  WCHAR ref[MAXLINELEN], songnum[MAXLINELEN], linkname[MAXLINELEN];
-  int songcount;
-  WCHAR *p;
-  VERSE v1, v2;
-  char hadwarnings = 0;
-
-  fprintf(stderr, "songidx: Parsing scripture index data file %s...\n",
-          fs->filename);
-
-  /* Read the bible data file into the bible array. */
-  if (!readbible(biblename)) return 2;
-  if ((idx = (VERSELIST **) calloc(numbooks, sizeof(VERSELIST *))) == NULL)
-  {
-    fprintf(stderr, "songidx: too many books of the bible (out of memory)\n");
-    free_bible();
-    return 2;
-  }
-  for (book=0; book<numbooks; ++book) idx[book] = NULL;
-
-  /* Walk through the input file and construct a VERSELIST for each book
-   * of the Bible. Each VERSELIST represents the set of verses in that book
-   * referred to by songs in the song book. */
-  songcount = 0;
-  book = -1;
-  v1.chapter = -1;
-  v1.verse = -1;
-  for (;;)
-  {
-    /* Get the next song data record. */
-    if (!filereadln(fs,ref,&eof))
-    {
-      freeidxarray(idx,numbooks);
-      free_bible();
-      return 2;
-    }
-    if (eof) break;
-    if (!filereadln(fs,songnum,&eof) || eof ||
-        !filereadln(fs,linkname,&eof) || eof)
-    {
-      if (eof)
-      {
-        fprintf(stderr, "songidx:%s:%d: incomplete entry\n",
-                fs->filename, fs->lineno);
-        fileclose(fs);
-      }
-      freeidxarray(idx,numbooks);
-      free_bible();
-      return 2;
-    }
-
-    /* Add all scripture references for this song to the VERSELIST array */
-    p = ref;
-    while (*p != wc_null)
-    {
-      /* Parse the next reference. */
-      p = parseref(p, &book, &v1);
-      if (!p)
-      {
-        fprintf(stderr, "songidx:%s:%d: WARNING: Malformed scripture reference"
-                        " for song ", fs->filename, fs->lineno);
-        ws_fputs(songnum, stderr);
-        fprintf(stderr, ". Ignoring it.\n");
-        hadwarnings = 1;
-        break;
-      }
-      if (book<0)
-      {
-        fprintf(stderr, "songidx:%s:%d: WARNING: Scripture reference for song ",
-                fs->filename, fs->lineno);
-        ws_fputs(songnum, stderr);
-        fprintf(stderr, " doesn't include a book name. Ignoring it.\n");
-        hadwarnings = 1;
-        break;
-      }
-      if (book>=numbooks)
-      {
-        fprintf(stderr, "songidx:%s:%d: WARNING: Scripture reference for song ",
-                fs->filename, fs->lineno);
-        ws_fputs(songnum, stderr);
-        fprintf(stderr, " references unknown book. Ignoring it.\n");
-        hadwarnings = 1;
-        break;
-      }
-      if (v1.chapter < 1)
-      {
-        fprintf(stderr, "songidx:%s:%d: WARNING: Scripture reference for song ",
-                fs->filename, fs->lineno);
-        ws_fputs(songnum, stderr);
-        fprintf(stderr, " has no chapter or verse. Ignoring it.\n");
-        hadwarnings = 1;
-        break;
-      }
-      /* Unless the reference ended in a "-", it consists of just one verse. */
-      v2.chapter = v1.chapter;
-      v2.verse = v1.verse;
-      if (*p==wc_hyphen)
-      {
-         /* This reference ended in a "-", which means it starts a range.
-         * Parse the next reference to find the range's end. */
-        book2 = book;
-        p = parseref(++p, &book2, &v2);
-        if (!p)
-        {
-          fprintf(stderr, "songidx:%s:%d: WARNING: Malformed scripture"
-                          " reference for song ", fs->filename, fs->lineno);
-          ws_fputs(songnum, stderr);
-          fprintf(stderr, ". Ignoring it.\n");
-          hadwarnings = 1;
-          break;
-        }
-        if (book2!=book)
-        {
-          fprintf(stderr, "songidx:%s:%d: WARNING: Scripture reference for"
-                          " song ", fs->filename, fs->lineno);
-          ws_fputs(songnum, stderr);
-          fprintf(stderr, " appears to span books! Ignoring it.\n");
-          hadwarnings = 1;
-          break;
-        }
-      }
-      if (*p!=wc_null) ++p;
-      /* If either the start or end references of the range were incomplete
-       * (e.g., the range was something like "Philippians 3", where the verse
-       * numbers are not explicit), try to fill in the missing information
-       * based on how many verses are in the chapter and how many chapters are
-       * in the book. */
-      if (v1.verse <= 0) v1.verse = 1;
-      if (v2.verse <= 0)
-      {
-        v2.verse = getvcount(book,v2.chapter);
-        if (v2.verse <= 0)
-        {
-          fprintf(stderr, "songidx:%s:%d: WARNING: Scripture reference for "
-                          " song ", fs->filename, fs->lineno);
-          ws_fputs(songnum, stderr);
-          fprintf(stderr, " refers implicitly to chapter %d of ", v2.chapter);
-          ws_fputs(bible[book].name, stderr);
-          fprintf(stderr, ", which doesn't exist. Ignoring it.\n");
-          hadwarnings = 1;
-          break;
-        }
-      }
-      if (v1.verse > getvcount(book,v1.chapter))
-      {
-        fprintf(stderr, "songidx:%s:%d: WARNING: Song ",
-                fs->filename, fs->lineno);
-        ws_fputs(songnum, stderr);
-        fprintf(stderr, " references ");
-        ws_fputs(bible[book].name, stderr);
-        fprintf(stderr, " %d:%d, which doesn't exist! Ignoring it.\n",
-                v1.chapter, v1.verse);
-        hadwarnings = 1;
-        break;
-      }
-      if (v2.verse > getvcount(book,v2.chapter))
-      {
-        fprintf(stderr, "songidx:%s:%d: WARNING: Song ",
-                fs->filename, fs->lineno);
-        ws_fputs(songnum, stderr);
-        fprintf(stderr, " references ");
-        ws_fputs(bible[book].name, stderr);
-        fprintf(stderr, " %d:%d, which doesn't exist! Ignoring it.\n",
-                v2.chapter, v2.verse);
-        hadwarnings = 1;
-        break;
-      }
-
-      /* Add the range to the SONGLIST array. */
-      if (!insertref(INSERT_ADD, &(idx[book]), v1, songnum, linkname,
-                     songcount) ||
-          !insertref(INSERT_DROP, &(idx[book]), v2, songnum, linkname,
-                     songcount))
-      {
-        fileclose(fs);
-        freeidxarray(idx,numbooks);
-        free_bible();
-        return 2;
-      }
-    }
-    /* finished adding all refs for this song */
-    ++songcount;
-  }
-  /* finished adding all refs for all songs */
-  fileclose(fs);
-
-  /* Now create the index .sbx file */
-  fprintf(stderr, "songidx: Generating scripture index TeX file %s...\n",
-          outname);
-  if (strcmp(outname,"-"))
-  {
-    if ((f = fopen(outname, "w")) == NULL)
-    {
-      fprintf(stderr, "songidx: Unable to open %s for writing.\n", outname);
-      freeidxarray(idx,numbooks);
-      free_bible();
-      return 2;
-    }
-  }
-  else
-  {
-    f = stdout;
-    outname = "stdout";
-  }
-
-#define TRYWRITE(x) \
-  if (!(x)) \
-  { \
-    fprintf(stderr, "songidx:%s: write error\n", outname); \
-    if (f == stdout) fflush(f); else fclose(f); \
-    freeidxarray(idx,numbooks); \
-    free_bible(); \
-    return 2; \
-  }
-
-  /* For each book of the Bible that has songs that reference it, go through
-   * its VERSELIST and generate a sequence of index entries.  Wherever
-   * possible, compact adjacent entries that have identical SONGLISTS so that
-   * we never have two consecutive index entries with identical right-hand
-   * sides. */
-  for (book=0; book<numbooks; ++book)
-    if (idx[book])
-    {
-      VERSE vrstart;
-      VERSELIST *vp;
-      SONGLIST *sl = NULL;
-      int lastchapter; /* 0=none, -1=force printing of chapter */
-
-      TRYWRITE((ws_fputs(ws_lit("\\begin{idxblock}{"), f) >=0)
-               && (ws_fputs(bible[book].name, f) >= 0)
-               && (ws_fputs(ws_lit("}\n"), f) >=0))
-      lastchapter = 0;
-      vcpy(&vrstart, idx[book]->v);
-      for (vp=idx[book]; vp; vp=vp->next)
-      {
-        if (!addrefs(&sl, vp->adds))
-        {
-          fclose(f);
-          freeidxarray(idx,numbooks);
-          free_bible();
-          return 2;
-        }
-        if (!slcmp(vp->drops, vp->next ? vp->next->adds : NULL))
-        {
-          /* Set of drops here equals set of adds next time.  There's at least
-	   * a chance that we can combine this item and the next one into a
-	   * single index entry. */
-          if (vp->next && !vlt(vinc(book,vp->v), vp->next->v))
-          {
-            /* If the next item is adjacent to this one, do nothing.  Just let
-	     * the range in progress be extended.  We'll output a single entry
-	     * for all of these adjacent verses when we reach the end. */
-            continue;
-          }
-          else if (vp->next && !slcmp(sl, vp->drops))
-          {
-            /* Otherwise, if the next item is not adjacent but all refs are
-	     * dropped here, then print a partial entry to be continued with a
-	     * comma next time. */
-            TRYWRITE(print_vrange(f, book, vrstart, vp->v, lastchapter) >= 0)
-            lastchapter = (vrstart.chapter != vp->v.chapter) ?
-                          -1 : vp->v.chapter;
-            vcpy(&vrstart, vp->next->v);
-            continue;
-          }
-        }
-        if (vp->drops)
-        {
-          /* Some songs get dropped here, and either the next item is not
-	   * adjacent to this one, or it's adjacent and the set of adds is not
-	   * the same.  In either case, that means the set of refs changes at
-	   * this point, so we need to output a full entry (or finish the one
-	   * in progress). */
-          TRYWRITE((print_vrange(f, book, vrstart, vp->v, lastchapter) >= 0) &&
-                   (ws_fputs(ws_lit("}{"), f) >= 0) &&
-                   (print_reflist(f, sl) >= 0) &&
-                   (ws_fputs(ws_lit("}\n"), f) >= 0))
-          removerefs(&sl, vp->drops);
-          lastchapter = 0;
-          vcpy(&vrstart, (!sl && vp->next) ? vp->next->v : vinc(book,vp->v));
-        }
-        if (sl && vp->next && vp->next->adds && vlt(vrstart, vp->next->v))
-        {
-          /* There are verses between this item and the next which have refs,
-	   * but the refs change at the beginning of the next item.  Make an
-	   * entry for the intermediate block of verses. */
-          VERSE vrend;
-          vcpy(&vrend, vdec(book,vp->next->v));
-          TRYWRITE((print_vrange(f, book, vrstart, vrend, lastchapter) >= 0) &&
-                   (ws_fputs(ws_lit("}{"), f) >= 0) &&
-                   (print_reflist(f, sl) >= 0) &&
-                   (ws_fputs(ws_lit("}\n"), f) >= 0))
-          lastchapter = 0;
-          vcpy(&vrstart, vp->next->v);
-        }
-      }
-      if (sl)
-      {
-        fprintf(stderr, "songidx: scripture references in ");
-        ws_fputs(bible[book].name, stderr);
-        fprintf(stderr, " appear to extend past the end of the book!"
-                        " Aborting.\n");
-        fclose(f);
-        freeidxarray(idx,numbooks);
-        free_bible();
-        return 2;
-      }
-      TRYWRITE(ws_fputs(ws_lit("\\end{idxblock}\n"), f) >= 0)
-    }
-
-#undef TRYWRITE
-
-  fclose(f);
-  freeidxarray(idx,numbooks);
-  free_bible();
-
-  return hadwarnings;
-}

Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.c
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.c	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.c	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,270 +0,0 @@
-/* Copyright (C) 2012 Kevin W. Hamlen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA  02110-1301, USA.
- *
- * The latest version of this program can be obtained from
- * http://songs.sourceforge.net.
- *
- *
- * This program generates an index.sbx file from an index.sxd file. Index.sxd
- * files are generated by the song book LaTeX files while you compile a song
- * book. They contain lists of all the song titles, song numbers, and
- * scripture references. Once the .sxd files exist, run this program to
- * convert them in to .sbx files. This program will sort the song titles
- * alphabetically, group all the scripture entries into the appropriate
- * chunks, etc. The TeX files that it creates are then used by the LaTeX
- * files next time you compile the song book.
- *
- * The syntax for running the program is:
- *    songidx input.sxd output.sbx
- * or, if you're compiling a scripture index:
- *    songidx -b bible.can input.sxd output.sbx
- *
- * The program should compile on unix with
- *
- *   cc *.c -o songidx
- *
- * On Windows, you'll need to find a C compiler in order to compile it,
- * or go to http://songs.sourceforge.net to obtain a Windows self-installer
- * with pre-compiled binaries.
- */
-
-#if HAVE_CONFIG_H
-#  include "config.h"
-#else
-#  include "vsconfig.h"
-#endif
-
-#include "chars.h"
-#include "songidx.h"
-#include "fileio.h"
-
-#if HAVE_STRING_H
-#  include <string.h>
-#elif HAVE_STRINGS_H
-#  include <strings.h>
-#endif
-
-#if HAVE_SETLOCALE
-#if HAVE_LOCALE_H
-#  include <locale.h>
-#endif
-#endif
-
-extern int gentitleindex(FSTATE *fs, const char *outname);
-extern int genscriptureindex(FSTATE *fs, const char *outname,
-                             const char *biblename);
-extern int genauthorindex(FSTATE *fs, const char *outname);
-
-#define BIBLEDEFAULT "bible.can"
-
-#if HAVE_STRRCHR
-#  define STRRCHR(x,y) strrchr((x),(y))
-#else
-#  define STRRCHR(x,y) local_strrchr((x),(y))
-char *
-local_strrchr(s,c)
-  const char *s;
-  int c;
-{
-  const char *t;
-  if (!s) return NULL;
-  for (t=s; *t; ++s) ;
-  for (; t>=s; --s)
-    if (*t == c) return t;
-  return NULL;
-}
-#endif
-
-int
-main(argc,argv)
-  int argc;
-  char *argv[];
-{
-  FSTATE fs;
-  int eof = 0;
-  WCHAR buf[MAXLINELEN];
-  int retval;
-  const char *bible, *inname, *outname, *locale;
-  int i;
-
-  bible = inname = outname = locale = NULL;
-  for (i=1; i<argc; ++i)
-  {
-    if (!strcmp(argv[i],"-v") || !strcmp(argv[i],"--version"))
-    {
-      printf("songidx " VERSION "\n"
-        "Copyright (C) 2012 Kevin W. Hamlen\n"
-        "License GPLv2: GNU GPL version 2 or later"
-        " <http://gnu.org/licenses/gpl.html>\n"
-        "This is free software: you are free to change and redistribute it.\n"
-        "There is NO WARRANTY, to the extent permitted by law.\n");
-      return 0;
-    }
-    else if (!strcmp(argv[i],"-h") || !strcmp(argv[i],"--help"))
-    {
-      printf("Syntax: %s [options] input.sxd [output.sbx]\n", argv[0]);
-      printf(
-        "Available options:\n"
-        "  -b FILE              Sets the bible format when generating a\n"
-        "  --bible FILE          scripture index (default: " BIBLEDEFAULT ")\n"
-        "\n"
-        "  -l LOCALE            Overrides the default system locale (affects\n"
-        "  --locale LOCALE       (how non-English characters are parsed).\n"
-        "                        See local system help for valid LOCALE's.\n"
-        "\n"
-        "  -h                   Displays this help file and stops.\n"
-        "  --help\n"
-        "\n"
-        "  -o FILE              Sets the output filename.\n"
-        "  --output FILE\n"
-        "\n"
-        "  -v                   Print version information and stop.\n"
-        "  --version\n"
-        "\n"
-        "If omitted, [output.sbx] defaults to the input filename but with\n"
-        "the file extension renamed to '.sbx'. To read or write to stdin or\n"
-        "stdout, use '-' in place of input.sxd or output.sbx.\n"
-        "\n"
-        "See http://songs.sourceforge.net for support.\n");
-      return 0;
-    }
-    else if (!strcmp(argv[i],"-b") || !strcmp(argv[i],"--bible"))
-    {
-      if (bible)
-      {
-        fprintf(stderr, "songidx: multiple bible files specified\n");
-        return 2;
-      }
-      else if (++i < argc)
-        bible = argv[i];
-      else
-      {
-        fprintf(stderr, "songidx: %s option requires an argument\n", argv[i-1]);
-        return 2;
-      }
-    }
-    else if (!strcmp(argv[i],"-l") || !strcmp(argv[i],"--locale"))
-    {
-      if (locale)
-      {
-        fprintf(stderr, "songidx: multiple locales specified\n");
-        return 2;
-      }
-      else if (++i < argc)
-        locale = argv[i];
-      else
-      {
-        fprintf(stderr, "songidx: %s option requires an argument\n", argv[i-1]);
-        return 2;
-      }
-    }
-    else if (!strcmp(argv[i],"-o") || !strcmp(argv[i],"--output"))
-    {
-      if (outname)
-      {
-        fprintf(stderr, "songidx: multiple output files specified\n");
-        return 2;
-      }
-      else if (++i < argc)
-        outname = argv[i];
-      else
-      {
-        fprintf(stderr, "songidx: %s option requires an argument\n", argv[i-1]);
-        return 2;
-      }
-    }
-    else if ((argv[i][0] == '-') && (argv[i][1] != '\0'))
-    {
-      fprintf(stderr, "songidx: unknown option %s\n", argv[i]);
-      return 2;
-    }
-    else if (!inname) inname=argv[i];
-    else if (!outname) outname=argv[i];
-    else
-    {
-      fprintf(stderr, "songidx: too many command line arguments\n");
-      return 2;
-    }
-  }
-
-#if HAVE_SETLOCALE
-  if (!locale)
-    (void) setlocale(LC_ALL, "");
-  else if (!setlocale(LC_ALL, locale))
-  {
-    fprintf(stderr, "songidx: invalid locale: %s\n", locale);
-    return 2;
-  }
-#endif
-
-  if (!inname)
-  {
-    fprintf(stderr, "songidx: no input file specified\n");
-    return 2;
-  }
-  if (!outname)
-  {
-    if (!strcmp(inname,"-"))
-      outname = "-";
-    else
-    {
-      char *b;
-      const char *p1 = STRRCHR(inname,'.');
-      if ((p1 < STRRCHR(inname,'/')) || (p1 < STRRCHR(inname,'\\')))
-        p1 = inname+strlen(inname);
-      b = (char *) malloc(p1-inname+5);
-      strncpy(b,inname,p1-inname);
-      strcpy(b+(p1-inname),".sbx");
-      outname = b;
-    }
-  }
-  if (!bible) bible = BIBLEDEFAULT;
-
-  if (!fileopen(&fs, inname)) return 2;
-
-  /* The first line of the input file is a header line that dictates which kind
-   * of index the data file is for. */
-  if (!filereadln(&fs, buf, &eof)) return 2;
-  retval = 2;
-  if (eof)
-  {
-    fprintf(stderr, "songidx:%s: file is empty\n", fs.filename);
-    fileclose(&fs);
-  }
-  else if (!ws_strcmp(buf, ws_lit("TITLE INDEX DATA FILE")))
-    retval = gentitleindex(&fs, outname);
-  else if (!ws_strcmp(buf, ws_lit("SCRIPTURE INDEX DATA FILE")))
-    retval = genscriptureindex(&fs, outname, bible);
-  else if (!ws_strcmp(buf, ws_lit("AUTHOR INDEX DATA FILE")))
-    retval = genauthorindex(&fs, outname);
-  else
-  {
-    fprintf(stderr, "songidx:%s:%d: file has unrecognized format\n",
-            fs.filename, fs.lineno);
-    fileclose(&fs);
-  }
-
-  /* Report the outcome to the user. */
-  if (retval == 0)
-    fprintf(stderr, "songidx: Done!\n");
-  else if (retval == 1)
-    fprintf(stderr, "songidx: COMPLETED WITH ERRORS. SEE ABOVE.\n");
-  else
-    fprintf(stderr, "songidx: FAILED. SEE ERROR MESSAGES ABOVE.\n");
-
-  return retval;
-}

Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.h
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.h	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.h	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,53 +0,0 @@
-/* Copyright (C) 2012 Kevin W. Hamlen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA  02110-1301, USA.
- *
- * The latest version of this program can be obtained from
- * http://songs.sourceforge.net.
- */
-
-#ifndef SONGIDX_H
-#define SONGIDX_H
-
-#if HAVE_CONFIG_H
-#  include "config.h"
-#else
-#  include "vsconfig.h"
-#endif
-
-#include "chars.h"
-
-/* A SONGENTRY struct consists of three fields:
- *   title: a string representing the title of the song
- *   num: a string representing the song's number in the book (might be
- *        something like "H10")
- *   linkname: a string denoting the internal hyperlink label for this song
- *   idx: a unique integer key that should be used to sort songs with
- *        identical titles */
-typedef struct
-{
-  WCHAR *title;
-  WCHAR *num;
-  WCHAR *linkname;
-  int idx;
-}
-SONGENTRY;
-
-/* The following functions are in songsort.c */
-extern void skipesc(const WCHAR **p);
-extern int songcmp(const void *s1, const void *s2);
-
-#endif

Added: trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.lua
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.lua	2017-06-10 21:36:05 UTC (rev 44553)
@@ -0,0 +1,1070 @@
+-- Copyright (C) 2017 Kevin W. Hamlen
+--
+-- This program is free software; you can redistribute it and/or
+-- modify it under the terms of the GNU General Public License
+-- as published by the Free Software Foundation; either version 2
+-- of the License, or (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, write to the Free Software
+-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+-- MA  02110-1301, USA.
+--
+-- The latest version of this program can be obtained from
+-- http://songs.sourceforge.net.
+
+
+VERSION = "3.0"
+BIBLEDEFAULT = "bible.can"
+
+-- fileopen(<filename>)
+--   Open <filename> for reading, returning a filestate table on success or
+--   nil on failure.
+function fileopen(fnam)
+  local handle
+  if fnam ~= "-" then
+    local msg,errno
+    handle,msg,errno = io.open(fnam, "r")
+    if not handle then
+      io.stderr:write("songidx: Unable to open ",fnam," for reading.\n",
+        "Error ",errno,": ",msg,"\n")
+      return nil
+    end
+  else
+    handle = io.stdin
+    fnam = "stdin"
+  end
+  return { f=handle, filename=fnam, lineno=0 }
+end
+
+-- filereadln(<fstate>)
+--   Read a line of input, returning the string or nil if at file-end.
+function filereadln(fs)
+  fs["lineno"] = fs["lineno"] + 1
+  local s = fs["f"]:read()
+  if not s then return nil end
+  return s
+end
+
+-- closeout(<handle>)
+--   Close an output file <handle>.
+function closeout(handle)
+  if handle == io.stdout then handle:flush()
+  else handle:close() end
+end
+
+-- errorout(<handle>)
+--   Respond to a write failure to <handle>.
+function errorout(handle)
+  closeout(handle)
+  io.stderr:write("songidx: Error writing to output file. Aborting.\n")
+    return 2
+  end
+
+-- fileclose(<fstate>)
+--   Close file table <fstate>.
+function fileclose(fs)
+  if (fs["f"] ~= io.stdin) then fs["f"]:close() end
+  fs["f"] = nil
+  fs["filename"] = nil
+  fs["lineno"] = 0
+end
+
+-- numprefix(<string>)
+--   Find the longest prefix of <string> that represents a number (according to
+--   the current locale) and contains no whitespace.  Return the number and the
+--   suffix of <string> not parsed.  If there is no such prefix, return false
+--   and the original <string>.
+function numprefix(s)
+  local _, len = unicode.utf8.find(s,"^%S*")
+  for i=len, 1, -1 do
+    local n = tonumber(s:sub(1,i))
+    if n then return n, s:sub(i+1) end
+  end
+  return false, s
+end
+
+-- cleantitle(<song>)
+--   Remove macros and braces from <song>'s title, and convert it to uppercase.
+--   Macro-spaces ("\ ") are converted to regular spaces (" ").  Cache the
+--   result to avoid re-cleaning during sorting.
+function cleantitle(s)
+  if not s["clean"] then
+    local t = s["title"]:gsub("\\[^%a%s]","")
+                        :gsub("\\(%s)","%1")
+                        :gsub("\\%a+%s*","")
+                        :gsub("{%s*","")
+                        :gsub("}","")
+    s["clean"] = unicode.utf8.upper(t)
+  end
+  return s["clean"]
+end
+
+-- songcmp(<song1>,<song2>)
+--   Return true if <song1> is less than <song2>, and false otherwise.  The
+--   ordering is first by title, then by index.  This function is suitable for
+--   use with table.sort().
+function songcmp(s1,s2)
+  local t1 = cleantitle(s1)
+  local t2 = cleantitle(s2)
+
+  while true do
+    -- Find the next word or number in each string.
+    t1, t2 = unicode.utf8.match(t1,"%w.*"), unicode.utf8.match(t2,"%w.*")
+
+    -- If there is no next word/number in both, sort by index.  If there is
+    -- no next word/number in one but not the other, sort the shorter string
+    -- before the longer one.
+    if not t1 then
+      if not t2 then break end
+      return true
+    elseif not t2 then return false end
+
+    -- If one is a number, sort the number before the word.  If both are
+    -- numbers (and are not equal), then sort in numerical order.
+    local n1, n2
+    n1, t1 = numprefix(t1)
+    n2, t2 = numprefix(t2)
+    if n1 then
+      if not n2 or n1 < n2 then return true end
+      if n1 > n2 then return false end
+    elseif n2 then return false
+    else
+      -- Otherwise, both are words.  Lexicographically compare the words
+      -- according to the current locale's collation conventions.  If the
+      -- locale considers them "equal" (i.e., w1<w2 is false but w1<=w2 is true)
+      -- then continue the loop to consider the next pair of words.
+      local w1, w2 = unicode.utf8.match(t1,"^[%a'`]*"),
+                     unicode.utf8.match(t2,"^[%a'`]*")
+      local diff = w1 < w2
+      if diff or not (w1 <= w2) then return diff end
+      t1, t2 = t1:sub(#w1+1), t2:sub(#w2+1)
+    end
+  end
+
+  -- If all corresponding words/numbers are equal, then sort alternate-form
+  -- entries (e.g., lyrics) after normal entries (e.g., titles)
+  local alt1, alt2 = s1["title"]:sub(1,1), s2["title"]:sub(1,1)
+  if alt1 == "*" and alt2 ~= "*" then return false end
+  if alt1 ~= "*" and alt2 == "*" then return true end
+
+  -- If everything is the same, sort by the right-hand sides of the index
+  -- entries (e.g., the song or page numbers).
+  return s1["idx"] < s2["idx"]
+end
+
+-- setstartchars(<array>)
+--   Try to find canonical "start characters" for each block of songs in a
+--   sorted <array> of songs.  Since Lua doesn't currently have any means of
+--   imparting a locale's alphabet, we adopt the following strategy:  Extract
+--   the first unicode character from each title in the sorted list of titles
+--   until reaching one that the locale's collation algorithm says is "bigger"
+--   than the last.  Find the "smallest" first character in that set using
+--   NON-UNICODE lexicographic sort.  This tends to be the most canonical one,
+--   since unicode tends to put canonical (e.g., no-accent) glyphs at lower
+--   code points than non-canonical (e.g., accented) ones.  (Unfortunately, if
+--   none of the titles in the block start with the desired canonical glyph,
+--   there's no way to guess it; we just use the best one available.)
+function setstartchars(songs)
+  local start = 1
+  local best
+
+  for i=1, #songs do
+    local c = unicode.utf8.match(cleantitle(songs[i]),"%w")
+    if c then
+      c = unicode.utf8.upper(c)
+      if not best then
+        songs[start]["newblk"], start, best = "\\#", i, c
+      elseif best < c then
+        songs[start]["newblk"], start, best = best, i, c
+      elseif #c < #best then best = c
+      elseif #c == #best then
+        for j=1,#c do
+          if c:byte(j) < best:byte(j) then best = c; break
+          elseif c:byte(j) > best:byte(j) then break end
+        end
+      end
+    elseif best then
+      songs[start]["newblk"], start, best = best, i, nil
+    end
+  end
+
+  if start <= #songs then
+    if best then
+      songs[start]["newblk"] = best
+    else
+      songs[start]["newblk"] = "\\#"
+    end
+  end
+end
+
+prelist = { A=true, THE=true }
+wt_and = { AND=true }
+wt_by = { BY=true }
+wt_unknown = { UNKNOWN=true }
+
+-- rotate(<title>)
+--   If the first word of <title> is any word in prelist, then shift that word
+--   to the end of the string, preceded by a comma and a space.  So for example,
+--   if prelist contains "The", then rotate("The title") returns "Title, The".
+--   Words in prelist are matched case-insensitively, and the new first word
+--   becomes capitalized.  If <title> begins with the marker character '*',
+--   that character is ignored and left unchanged.
+function rotate(s)
+  local t = unicode.utf8.upper(s)
+  local n = 0
+  if s:sub(1,1) == "*" then n = 1 end
+  for pre in pairs(prelist) do
+    if t:sub(1+n,n+#pre) == pre and
+       unicode.utf8.find(t, "^%s+%S", n+#pre+1) then
+      local len = unicode.utf8.len(pre)
+      local x, y, z = unicode.utf8.match(unicode.utf8.sub(s,n+len+1),"^%s+(%W*)(%w?)(.*)$")
+      return s:sub(1,n) .. x .. unicode.utf8.upper(y) .. z:gsub("\\%s","%0\1"):match("^(.-)%s*$"):gsub("\1","") .. ",~" .. unicode.utf8.sub(s,1+n,n+len)
+    end
+  end
+  return s
+end
+
+-- matchany(<string>,<init>,<wordtable>)
+--   If a word in <wordtable> case-insensitively matches to <string> starting
+--   at index <init>, and if the match concludes with whitespace, then return
+--   the index of the whitespace; otherwise return <init>.
+function matchany(s,init,wt)
+  local t = s:sub(init)
+  local u = unicode.utf8.upper(t)
+  for w,_ in pairs(wt) do
+    if u:sub(1,#w) == w and
+       (#w == #u or unicode.utf8.find(u, "^%s", #w+1)) then
+      return init + #(unicode.utf8.sub(t,1,unicode.utf8.len(w)))
+    end
+  end
+  return init
+end
+
+-- issuffix(<string>,<init>)
+--   Return true if the abbreviation "Jr" or a roman numeral (followed by
+--   nothing, space, period, comma, or semicolon) appears at position <init>
+--   within <string>.  Return false otherwise.
+function issuffix(s,i)
+  return unicode.utf8.find(s, "^Jr$", i) or
+         unicode.utf8.find(s, "^Jr[%s,;%.]", i) or
+         unicode.utf8.find(s, "^[IVX]+$", i) or
+         unicode.utf8.find(s, "^[IVX]+[%s,;%.]", i)
+end
+
+-- grabauthor(<string>)
+--   Return a string of the form "Sirname, Restofname" denoting the full name
+--   of the first author found in <string>; or return nil if no author name
+--   can be found.  Also return an index to the suffix of <string> that was
+--   not parsed, and just the "Sirname" part as a stand-alone string.
+--
+--   Precondition:  Caller must first sanitize <string> as follows:
+--     <string>:gsub("[\1\2\3]","")
+--             :gsub("\\\\","\\\1"):gsub("\\{","\\\2"):gsub("\\}","\\\3")
+--
+--   Postcondition: Caller must then unsanitize the returned <string> with:
+--     <string>:gsub("\1","\\"):gsub("\2","{"):gsub("\3","}")
+--
+--   This is to allow grabauthor() to safely use Lua's %b pattern to find
+--   balanced brace pairs without getting confused by escaped braces.
+--
+--   Heuristics:
+--     * Names are separated by punctuation (other than hyphens, periods,
+--       apostrophes, or backslashes) or by the word "and" (or whatever words
+--       are in wt_and).
+--       Special case: If a comma is followed by the abbreviation "Jr" or by a
+--       roman numeral, then the comma does NOT end the author's name.
+--     * If a name contains the word "by" (or anything in wt_by), then
+--       everything before it is not considered part of the name.  (Let's hope
+--       nobody is named "By".)
+--     * The author's last name is always the last capitalized word in the
+--       name unless the last capitalized word is "Jr." or a roman numeral.
+--       In that case the author's last name is the penultimate captialized
+--       word.
+--     * If an author appears to have only a first name, or if the last name
+--       found according to the above heuristics is an abbreviation (ending in
+--       a period), look ahead in <string> until we find someone with a last
+--       name and use that one.  This allows us to identify the first author in
+--       a string like "Joe, Billy E., and Bob Smith" to be "Joe Smith".
+--     * If the resultant name contains the word "unknown" (or any word in
+--       wt_unknown), it's probably not a real name.  Recursively attempt
+--       to parse the next author.
+function grabauthor(authline,i)
+  i = unicode.utf8.find(authline, "[^%s,;]", i)
+  if not i then return nil end
+  i = unicode.utf8.find(authline, "%S", matchany(authline,i,wt_and))
+  if not i then return nil end
+  i = unicode.utf8.find(authline, "%S", matchany(authline,i,wt_by))
+  if not i then return nil end
+  local skip = (matchany(authline,i,wt_unknown) > i)
+
+  -- Set "first" to the index of the start of the first name, "last" to the
+  -- index of the start of the sirname, "suffix" to the index of any suffix
+  -- like "Jr." or "III" (or nil if there is none), and i to the index of the
+  -- first character beyond the end of this author's name.
+  local first,last,suffix = i,nil,nil
+  while i <= #authline do
+    while true do
+      local j = select(2,authline:find("^\\%A", i)) or
+                select(2,authline:find("^\\%a+%s*", i)) or
+                select(2,authline:find("^%b{}", i))
+      if not j then break end
+      i = j + 1
+    end
+    if i > #authline then break
+    elseif authline:sub(i,i) == "," then
+      local j = unicode.utf8.find(authline, "%S", i+1)
+      if j and issuffix(authline,j) then i = i + 1
+      else break end
+    elseif authline:sub(i,i) == ";" then break
+    elseif unicode.utf8.find(authline, "^%s", i) then
+      i = unicode.utf8.find(authline, "%S", i)
+      if not i then i = #authline + 1; break end
+      if matchany(authline, i, wt_and) > i then break end
+      skip = skip or (matchany(authline, i, wt_unknown) > i)
+      local j = matchany(authline, i, wt_by)
+      if j > i then
+        j = unicode.utf8.find(authline, "%S", j)
+        if not j then last = i; break end  -- last name of "By"?
+        i,first,last,suffix = j,j,nil,nil
+      elseif issuffix(authline,i) then
+        suffix = i
+      elseif unicode.utf8.find(authline:sub(i):gsub("\\%A",""):gsub("\\%a+%s*",""),"^[%s{}'`\"]*%u") then
+        last,suffix = i,nil
+      end
+    else
+      i = select(2, unicode.utf8.find(authline, ".", i)) + 1
+    end
+  end
+
+  -- If an "unknown" word appeared, skip this entry and parse the next.
+  if skip then return grabauthor(authline,i) end
+
+  -- Find the sirname.
+  local sirname, fullname
+  if last then
+    sirname = unicode.utf8.gsub(authline:sub(last,(suffix or i)-1),
+                                "([^%s,;\\])[%s,;]+$", "%1")
+  end
+  if not sirname or unicode.utf8.find(sirname, "%a%.$") then
+    -- Here's where it gets tough.  We either have a single-word name, or the
+    -- last name ends in a "." which means maybe it's just a middle initial or
+    -- other abbreviation.  We could be dealing with a line like, "Billy,
+    -- Joe E., and Bob Smith", in which case we have to go searching for the
+    -- real last name.  To handle this case, we will try a recursive call.
+    local _,_,r = grabauthor(authline,i)
+    if r or not sirname then
+      fullname = unicode.utf8.gsub(authline:sub(first,i-1),
+                                   "([^%s,;\\])[%s,;]+$", "%1")
+      if r then return (r .. ", " .. fullname), i, r
+      else return fullname, i, nil end
+    end
+  end
+
+  -- Add the first name.
+  fullname = sirname .. ", " ..
+    unicode.utf8.gsub(authline:sub(first,(last or suffix or i)-1),
+                      "([^%s,;\\])[%s,;]+$", "%1")
+
+  -- Add the suffix, if any.
+  if suffix then
+    fullname = fullname .. " " ..
+      unicode.utf8.gsub(authline:sub(suffix,i-1), "([^%s,;\\])[%s,;]+$", "%1")
+  end
+
+  return fullname, i, sirname
+end
+
+-- genindex(<fstate>,<outname>)
+--   Reads a title (if <authorindex>=false) or author (if <authorindex>=true)
+--   index data file from file table <fstate> and generates a new file named
+--   <outfile> containing a LaTeX title/author index.
+--   Return Value: 0 on success, 1 on warnings, or 2 on failure
+function genindex(fs,outname,authorindex)
+  local songs = {}
+  local seen = {}
+  local wt = { wt_sep, wt_after, wt_prefix, wt_ignore }
+  local typ = authorindex and "author" or "title"
+
+  io.stderr:write("songidx: Parsing ",typ," index data file ",fs["filename"],"...\n")
+
+  while true do
+    local buf = filereadln(fs)
+    if not buf then break end
+    if buf:sub(1,1) == "%" then
+      local j = buf:match("^()%%sep ") or
+                buf:match("^%%()after ") or
+                buf:match("^%%p()refix ") or
+                buf:match("^%%ig()nore ")
+      if j then
+        if not seen[j] then wt[j], seen[j] = {}, true end
+        wt[j][unicode.utf8.upper(buf:sub(buf:find(" ")+1))] = true
+      end
+    else
+      local snum = filereadln(fs)
+      if not snum then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": incomplete song entry (orphan ",typ,")\n")
+        return 2
+      end
+      local link = filereadln(fs)
+      if not link then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": incomplete song entry (missing hyperlink)\n")
+        return 2
+      end
+      if authorindex then
+        local i,a = 1
+        buf = buf:gsub("[\1\2\3]",""):gsub("\\\\","\\\1")
+                 :gsub("\\{","\\\2"):gsub("\\}","\\\3")
+        while true do
+          a,i = grabauthor(buf, i)
+          if not a then break end
+          a = a:gsub("\1","\\"):gsub("\2","{"):gsub("\3","}")
+          table.insert(songs, {title=a, num=snum, linkname=link, idx=#songs})
+        end
+      else
+        buf = rotate(unicode.utf8.gsub(unicode.utf8.gsub(buf,"([^%s\\])%s+$","%1"),"^(%*?)%s+","%1"))
+        table.insert(songs, {title=buf, num=snum, linkname=link, idx=#songs})
+      end
+    end
+  end
+  fileclose(fs)
+
+  -- Sort the song table.
+  table.sort(songs, songcmp)
+  -- Find the index blocks.
+  setstartchars(songs)
+
+  -- Write the sorted data out to the output file.
+  io.stderr:write("songidx: Generating ",typ," index TeX file ",outname,"...\n")
+  local f,msg,errno
+  if outname == "-" then
+    f, outname = io.stdout, "stdout"
+  else
+    f,msg,errno = io.open(outname, "w")
+    if not f then
+      io.stderr:write("songidx: Unable to open ",outname," for writing.\n",
+        "Error ",errno,": ",msg,"\n")
+      return 2
+    end
+  end
+  for i=1, #songs do
+    if i>1 and songs[i]["title"] == songs[i-1]["title"] then
+      if not f:write("\\\\\\songlink{",songs[i]["linkname"],"}{",songs[i]["num"],"}") then return errorout(f) end
+    else
+      if songs[i]["newblk"] then
+        if i>1 then
+          if not f:write("}\n\\end{idxblock}\n") then return errorout(f) end
+        end
+        if not f:write("\\begin{idxblock}{",songs[i]["newblk"]) then return errorout(f) end
+      end
+      if songs[i]["title"]:find("^%*") then
+        if not f:write("}\n\\idxaltentry{",songs[i]["title"]:sub(2)) then return errorout(f) end
+      else
+        if not f:write("}\n\\idxentry{",songs[i]["title"]) then return errorout(f) end
+      end
+      if not f:write("}{\\songlink{",songs[i]["linkname"],"}{",songs[i]["num"],"}") then return errorout(f) end
+    end
+  end
+  if #songs > 0 then
+    if not f:write("}\n\\end{idxblock}\n") then return errorout(f) end
+  end
+
+  return 0
+end
+
+bible = {}
+chapX = 0
+
+-- readbible(<filename>)
+--   Read bible data file <filename> into the bible table.  Return nil on error
+--   or true on success.
+function readbible(filename)
+  local fs = fileopen(filename)
+  if not fs then return nil end
+  bible = {}
+
+  while true do
+    local buf = filereadln(fs)
+    if not buf then break end
+    if buf:sub(1,1) ~= "#" and buf:find("%S") then
+      local t, vbuf = { name = buf:match("^[^|]*"),
+                        aliases = "|" .. unicode.utf8.upper(buf) .. "|" }
+      repeat
+        vbuf = filereadln(fs)
+        if not vbuf then
+          io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": incomplete bible book entry (book title with no verses)\n")
+          fileclose(fs)
+          return nil
+        end
+      until vbuf:sub(1,1) ~= "#" and buf:find("%S")
+      if vbuf:find("[^%d%s]") then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": verse count includes a non-digit\n")
+        fileclose(fs)
+        return nil
+      end
+      for n in vbuf:gmatch("%d+") do
+        local i = tonumber(n)
+        if not i then
+          io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": invalid number ",n,"\n")
+          fileclose(fs)
+          return nil
+        end
+        i = math.floor(i)
+        if chapX < i+1 then chapX = i+1 end
+        table.insert(t, i)
+      end
+      table.insert(bible, t)
+    end
+  end
+
+  fileclose(fs)
+  return true
+end
+
+-- parseref(<string>,<init>,<book>,<chapter>)
+--   Interpret the characters starting at index <init> of <string> as a
+--   scripture reference, and return four values: (1) the index of the first
+--   character after <init> not parsed, (2) the book number parsed, (3) the
+--   chapter number parsed (or 1 if the book has only verses), and (4) the
+--   verse number parsed.  Arguments <book> and <chapter> are the PREVIOUS book
+--   number and chapter parsed, or -1 if none.  If book or chapter information
+--   is missing from <string>, they will be drawn from <book> and <chapter>.
+--   That way, successive calls can correctly parse a run-on string like
+--   "Philippians 3:1,5; 4:3", infering that "5" refers to "Philippians 3" and
+--   "4:3" refers to "Philippians".  If the parser encounters an error in
+--   processing the book name (e.g., a book name was specified but not
+--   recognized), then #bible+1 is returned for the book.  If no chapter or no
+--   verse is provided (e.g., the reference is just "Philippians" or
+--   "Philippians 3") then the chapter and/or verse are returned as -1.
+function parseref(s,i,book,ch)
+  local v = -1
+  i = unicode.utf8.find(s,"%S",i)
+  if not i then return nil end
+  local j = unicode.utf8.find(s,"[%d:]*%s*[,;%-]",i) or
+            unicode.utf8.find(s,"[%d:]*%s*$",i)
+  local bookname = "|" .. unicode.utf8.upper(unicode.utf8.match(s:sub(i,j-1), "(.-)%s*$")) .. "|"
+  i = j
+  if bookname ~= "||" then
+    book, ch = #bible+1, -1
+    for b,t in pairs(bible) do
+      if t["aliases"]:find(bookname, 1, true) then book = b; break end
+    end
+  end
+  j = unicode.utf8.find(s,"%D",i) or (#s+1)
+  if s:sub(j,j) == ":" then
+    ch, i = (tonumber(s:sub(i,j-1)) or -1), j+1
+    j = unicode.utf8.find(s,"%D",i) or (#s+1)
+  end
+  if ch<=0 and book>0 and #bible[book] == 1 then
+    -- Special case: This book has only one chapter.
+    ch = 1
+  end
+  if ch <= 0 then
+    ch = tonumber(s:sub(i,j-1)) or -1
+  else
+    v = tonumber(s:sub(i,j-1)) or -1
+  end
+  i = unicode.utf8.find(s,"%S",j)
+  if not i then i = #s+1
+  elseif not s:find("^[,;%-]",i) then return nil end
+  return i, book, ch, v
+end
+
+-- vlt(<chapter1>,<verse1>,<chapter2>,<verse2>)
+--   Return true if <chapter1>:<verse1> precedes <chapter2>:<verse2> and false
+--   otherwise.
+function vlt(ch1,v1,ch2,v2)
+  return ch1 < ch2 or (ch1 == ch2 and v1 < v2)
+end
+
+-- vinc(<book>,<chapter>,<verse>)
+--   Return the chapter,verse pair of the verse immediately following
+--   <chapter>:<verse> in <book>.  If <chapter>:<verse> is the last verse in
+--   <book>, the returned chapter will not exist in <book>.
+function vinc(book,ch,v)
+  if v < bible[book][ch] then return ch, v+1 end
+  return ch+1, 1
+end
+
+-- vdec(<book>,<chapter>,<verse>)
+--   Return the chapter,verse pair of the verse immediately preceding
+--   <chapter>:<verse> in <book>.  If <chapter> and <verse> are both 1,
+--   then 0,nil will be returned.
+function vdec(book,ch,v)
+  if v > 1 then return ch, v-1 end
+  return ch-1, bible[book][ch-1]
+end
+
+-- unpack_cv(<cv>)
+--   Decompose a chapter-verse key (computed as cv*chapX+v) into the original
+--   chapter and verse numbers.
+function unpack_cv(cv)
+  local v = cv % chapX
+  return (cv-v)/chapX, v
+end
+
+-- eqdom(<table1>,<table2>)
+--   Return true if two tables have identical domains; false otherwise.
+function eqdom(t1,t2)
+  for k,_ in pairs(t1) do if not t2[k] then return false end end
+  for k,_ in pairs(t2) do if not t1[k] then return false end end
+  return true
+end
+
+-- insertref(<is_add>,<changeset>,<chapter>,<verse>,<song>,<link>,<key>)
+--   Insert song <song>,<link>,<key> into the set of "adds" (if <is_add>=true)
+--   or "drops" (if <is_add>=false) for verse <chapter>:<verse> in <changeset>.
+--   A <changeset> is a table that maps cv's to {adds=<refset>, drops=<refset>}
+--   tables, where cv's are chapter-verse pairs encoded as chapter*chapX+verse.
+--   Each such entry denotes a verse where the set of songs that reference it
+--   changes.  The "adds" field lists the songs that refer to this verse but
+--   not the previous one.  The "drops" field lists the songs that refer to
+--   this verse but not the next.  This formulation allows us to efficiently
+--   represent range-references (e.g., "Psalms 1:1-8") without creating a
+--   separate table entry for each verse in the range.
+--   Create a new entry for <chapter>:<verse> in <changeset> if it doesn't
+--   already exist.  Return the new <changeset>.
+function insertref(is_add,set,ch,v,n,l,k)
+  if not set then set = {} end
+  local cv = ch*chapX+v
+  if not set[cv] then set[cv] = { adds={}, drops={} } end
+  set[cv][is_add and "adds" or "drops"][k] = { num=n, link=l }
+  return set
+end
+
+-- print_vrange(<file>,<book>,<ch1>,<v1>,<ch2>,<v2>,<lastchapter>)
+--   Output LaTeX material to file <file> for verse range <ch1>:<v1>--<ch2>:<v2>
+--   of book number <book>.  Depending on <lastchapter>, the outputted material
+--   might be the start of a new index entry or the continuation of a previous
+--   entry.  If <lastchapter> is positive, continue the previous entry and
+--   print the chapter of <ch1>:<v1> only if it differs from <lastchapter>.  If
+--   <lastchapter> is negative, continue the previous entry and always print
+--   the chapter number of <ch1>:<v1>.
+function print_vrange(f,b,ch1,v1,ch2,v2,lch)
+  local r = f:write(lch == 0 and "\\idxentry{" or ",")
+  
+  if v1 <= 0 then
+    if lch ~= 0 then r = r and f:write("\\thinspace ") end
+    r = r and f:write(ch1)
+  elseif 0 <= b and b < #bible and #bible[b] == 1 then
+    -- This book has only one chapter.
+    if lch ~= 0 then r = r and f:write("\\thinspace ") end
+    r = r and f:write(v1)
+  elseif lch <= 0 or lch ~= ch1 or ch1 ~= ch2 then
+    if lch ~= 0 then r = r and f:write(" ") end
+    r = r and f:write(ch1,":",v1)
+  else
+    if lch ~= 0 then r = r and f:write("\\thinspace ") end
+    r = r and f:write(v1)
+  end
+
+  if vlt(ch1,v1,ch2,v2) then
+    if v2 <= 0 then
+      r = r and f:write("--",ch2)
+    elseif ch1 ~= ch2 then
+      r = r and f:write("--",ch2,":",v2)
+    else
+      r = r and f:write("--",v2)
+    end
+  end
+
+  return r
+end
+
+-- print_reflist(<file>,<refset>)
+--   Output the list of song references given by <refset> in sorted order
+--   to file <file>.
+function print_reflist(f,t)
+  local r = true
+  local s = {}
+  for k,_ in pairs(t) do table.insert(s,k) end
+  table.sort(s)
+
+  local first = true
+  for _,k in ipairs(s) do
+    if first then first = false else r = r and f:write("\\\\") end
+    r = r and f:write("\\songlink{",t[k]["link"],"}{",t[k]["num"],"}")
+  end
+  return r
+end
+
+function debug_print_reflist(r)
+  local first = true
+  io.stderr:write("{")
+  for k,_ in pairs(r) do
+    if first then first=false else io.stderr:write(",") end
+    io.stderr:write(k)
+  end
+  io.stderr:write("}")
+end
+
+function debug_print_changeset(x)
+  if not x then io.stderr:write("nil") else
+    for cv,r in pairs(x) do
+      local ch,v = unpack_cv(cv)
+      io.stderr:write("{",ch,":",v," --> adds=")
+      debug_print_reflist(r["adds"])
+      io.stderr:write(", drops=")
+      debug_print_reflist(r["drops"])
+      io.stderr:write("}")
+    end
+  end
+end
+
+-- genscriptureindex(<fstate>,<outname>,<biblename>)
+--   Generate a LaTeX file named <outname> containing material suitable to
+--   typeset the scripture index data found in input file <fstate>.  Input
+--   bible data from an ascii file named <biblename>.  Return 0 on success,
+--   1 if there were warnings, and 2 if there was a fatal error.
+function genscriptureindex(fs,outname,biblename)
+  local hadwarnings = 0
+  local idx = {}
+
+  io.stderr:write("songidx: Parsing scripture index data file ",fs["filename"],"...\n")
+
+  -- Read the bible data file into the bible array.
+  if not readbible(biblename) then return 2 end
+
+  -- Walk through the input file and construct a <changeset> for each book
+  -- of the bible.  Each changeset represents the set of verses in that book
+  -- referred to by songs in the song book.
+  local key = 0
+  while true do
+    local ref = filereadln(fs)
+    if not ref then break end
+    local n = filereadln(fs)
+    if not n then
+      io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": incomplete song entry (orphan reference line)\n")
+      fileclose(fs)
+      return 2
+    end
+    local l = filereadln(fs)
+    if not l then
+      io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": incomplete song entry (missing hyperlink)\n")
+      fileclose(fs)
+      return 2
+    end
+    key = key + 1
+
+    local i = 1
+    local book, ch1, v1, ch2, v2 = -1, -1, -1, -1, -1
+    while i <= #ref do
+      i,book,ch1,v1 = parseref(ref,i,book,ch1)
+      ch2,v2 = ch1,v1
+      if not i then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Malformed scripture reference for song ",n,". Ignoring it.\n")
+        hadwarnings = 1
+        break
+      end
+      if book < 1 then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," doesn't include a book name. Ignoring it.\n")
+        hadwarnings = 1
+        break
+      end
+      if book > #bible then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," references unknown book. Ignoring it.\n")
+        hadwarnings = 1
+        break
+      end
+      if ch1 < 1 then ch1 = 1 end
+      if ch1 > #bible[book] then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," refers to ",bible[book]["name"]," ",ch1,", which doesn't exist. Ignoring it.\n")
+        hadwarnings = 1
+        break
+      end
+      if v1 < 1 then v1 = 1 end
+      if v1 > bible[book][ch1] then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," refers to ",bible[book]["name"]," ",ch1,":",v1,", which doesn't exist. Ignoring it.\n")
+        hadwarnings = 1
+        break
+      end
+
+      if ref:sub(i,i) == "-" then
+        -- If the reference ends in a "-", it starts an explicit range.
+        -- Parse the next reference to find the range's end.
+        i = unicode.utf8.find(ref, "[^%s%-]", i)
+        if not i then
+          io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," has range with no limit. Ignoring it.\n")
+          hadwarnings = 1
+          break
+        end
+        local book2
+        i,book2,ch2,v2 = parseref(ref,i,book,ch1)
+        if not i then
+          io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Malformed scripture reference for song ",n,". Ignoring it.\n")
+          hadwarnings = 1
+          break
+        end
+        if book2 ~= book then
+          io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," appears to span books! Ignoring it.\n")
+          hadwarnings = 1
+          break
+        end
+      end
+      if ch2 < 1 then ch2 = #bible[book] end
+      if ch2 > #bible[book] then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," refers implicitly to ",bible[book]["name"]," ",ch2,", which doesn't exist. Ignoring it.\n")
+        hadwarnings = 1
+        break
+      end
+      if v2 < 1 then v2 = bible[book][ch2]
+      elseif v2 > bible[book][ch2] then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," refers implicitly to chapter ",ch2," of ",bible[book]["name"],", which doesn't exist. Ignoring it.\n")
+        hadwarnings = 1
+        break
+      end
+      if vlt(ch2,v2,ch1,v1) then
+        io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," contains backwards range ",bible[book]["name"]," ",ch1,":",v1,"-",ch2,":",v2,". Ignoring it.\n")
+        hadwarnings = 1
+        break
+      end
+      if i < #ref then i = i + 1 end
+
+      idx[book] = insertref(true, idx[book], ch1, v1, n, l, key)
+      idx[book] = insertref(false, idx[book], ch2, v2, n, l, key)
+    end
+  end
+  fileclose(fs)
+
+  -- Now create the index .sbx file.
+  io.stderr:write("songidx: Generating scripture index TeX file ",outname,"...\n")
+  local f,msg,errno
+  if outname == "-" then
+    f, outname = io.stdout, "stdout"
+  else
+    f,msg,errno = io.open(outname, "w")
+    if not f then
+      io.stderr:write("songidx: Unable to open ",outname," for writing.\n",
+        "Error ",errno,": ",msg,"\n")
+      return 2
+    end
+  end
+
+  -- For each book of the bible the has songs that reference it, go through its
+  -- <changeset> and generate a sequence of index entries.  Wherever possible,
+  -- compact adjacent entries that have identical <refset>'s so that we never
+  -- have two consecutive index entries with identical right-hand sides.
+  for b=1,#bible do
+    local x = idx[b]
+    if x then
+      -- io.stderr:write("idx[",b,"] = ")
+      -- debug_print_changeset(x)
+      local s, t = {}, {}
+      for cv,_ in pairs(x) do table.insert(s,cv) end
+      table.sort(s)
+      local lch = 0  -- 0=none, -1=force printing of chapter
+      local cv1 = s[1]
+      local ch1,v1 = unpack_cv(cv1)
+      if not f:write("\\begin{idxblock}{",bible[b]["name"],"}\n") then return errorout(f) end
+      for i,cv in ipairs(s) do
+        local this = x[cv]
+        local ch,v = unpack_cv(cv)
+        local ncv,nxt,nch,nv = s[i+1]
+        if ncv then
+          nxt,nch,nv = x[ncv], unpack_cv(ncv)
+        end
+        for k,r in pairs(this["adds"]) do t[k] = r end
+        local skip = false
+        if ncv and eqdom(this["drops"], nxt["adds"]) then
+          -- Set of drops here equals set of adds next time.  There's at least
+          -- a chance that we can combine this item and the next one into a
+          -- single index entry.
+          local ch2, v2 = vinc(b,ch,v)
+          if not vlt(ch2,v2, nch,nv) then
+            -- If the next item is adjacent to this one, do nothing.  Just let
+            -- the range in progress be extended.  We'll output a single entry
+            -- for all of these adjacent verses when we reach the end.
+            skip = true
+          elseif eqdom(t, this["drops"]) then
+            -- Otherwise, if the next item is not adjacent but all refs are
+            -- dropped here, then print a partial entry to be continued with a
+            -- comma next time.
+            if not print_vrange(f,b,ch1,v1,ch,v,lch) then return errorout(f) end
+            lch = (ch1 == ch) and ch or -1
+            ch1, v1 = nch, nv
+            skip = true
+          end
+        end
+        if not skip then
+          if next(this["drops"]) then
+            -- Some songs get dropped here, and either the next item is not
+            -- adjacent to this one, or it's adjacent and the set of adds is not
+            -- the same.  In either case, that means the set of refs changes at
+            -- this point, so we need to output a full entry (or finish the one
+            -- in progress).
+            if not (print_vrange(f,b,ch1,v1,ch,v,lch) and
+                    f:write("}{") and
+                    print_reflist(f,t) and
+                    f:write("}\n")) then return errorout(f) end
+            for k,_ in pairs(this["drops"]) do t[k] = nil end
+            lch = 0
+            if not next(t) and ncv then
+              ch1, v1 = nch, nv
+            else
+              ch1, v1 = vinc(b,ch,v)
+            end
+          end
+          if next(t) and ncv and next(nxt["adds"]) and vlt(ch1,v1,nch,nv) then
+            -- There are verses between this item and the next which have refs,
+            -- but the refs change at the beginning of the next item.  Make an
+            -- entry for the intermediate block of verses.
+            local ch2, v2 = vdec(b,nch,nv)
+            if not (print_vrange(f,b,ch1,v1,ch2,v2,lch) and
+                    f:write("}{") and
+                    print_reflist(f,t) and
+                    f:write("}\n")) then return errorout(f) end
+            lch, ch1, v1 = 0, nch, nv
+          end
+        end
+      end
+      if not f:write("\\end{idxblock}\n") then return errorout(f) end
+    end
+  end
+
+  closeout(f)
+  return hadwarnings
+end
+
+-- Main program entry point
+function main()
+  local fs, biblename, inname, outname, locale
+
+  local i = 1
+  while arg[i] do
+    if arg[i] == "-v" or arg[i] == "--version" then
+      io.write("songidx ", VERSION, "\n",
+        "Copyright (C) 2017 Kevin W. Hamlen\n",
+        "License GPLv2: GNU GPL version 2 or later",
+	      " <http://gnu.org/licenses/gpl.html>\n",
+	      "This is free software: you are free to change and redistribute it.\n",
+	      "There is NO WARRANTY, to the extent permitted by law.\n")
+      return 0
+    elseif arg[i] == "-h" or arg[i] == "--help" then
+      io.write("Syntax: ",arg[-1]," ",arg[0]," [options] input.sxd [output.sbx]\n",
+"Available options:\n",
+"  -b FILE          Set the bible format when generating a scripture index\n",
+"  --bible FILE      (default: ", BIBLEDEFAULT, ")\n",
+"\n",
+"  -l LOCALE        Override the default system locale (affecting how non-\n",
+"  --locale LOCALE   English characters are sorted).  See your system help\n",
+"                    for valid LOCALEs.\n",
+"\n",
+"  -h               Display this help file and stop.\n",
+"  --help\n",
+"\n",
+"  -v               Print version information and stop.\n",
+"  --version\n",
+"\n",
+"If omitted, [output.sbx] defaults to the input filename but with the file\n",
+"extension renamed to '.sbx'. To read or write to stdin or stdout, use '-'\n",
+"in place of input.sxd or output.sbx.\n",
+"\n",
+"See http://songs.sourceforge.net for support.\n")
+      return 0
+    elseif arg[i] == "-b" or arg[i] == "--bible" then
+      if biblename then
+        io.stderr:write("songidx: multiple bible files specified\n")
+        return 2
+      end
+      i = i + 1
+      if arg[i] then
+        biblename = arg[i]
+      else
+        io.stderr:write("songidx: ",arg[i-1]," option requires an argument\n")
+        return 2
+      end
+    elseif arg[i] == "-l" or arg[i] == "--locale" then
+      if locale then
+        io.stderr:write("songidx: multiple locales specified\n")
+        return 2
+      end
+      i = i + 1
+      if arg[i] then
+        locale = arg[i]
+      else
+        io.stderr:write("songidx: ",arg[i-1]," requires an argument\n")
+        return 2
+      end
+    elseif arg[i] == "-o" or arg[i] == "--output" then
+      if outname then
+        io.stderr:write("songidx: multiple output files specified\n")
+        return 2
+      end
+      i = i + 1
+      if arg[i] then
+        outname = arg[i]
+      else
+        io.stderr:write("songidx: ",arg[i-1]," option requires an argument\n")
+        return 2
+      end
+    elseif arg[i]:sub(1,1) == "-" and arg[i] ~= "-" then
+      io.stderr:write("songidx: unknown option ",arg[i],"\n")
+      return 2
+    elseif not inname then inname = arg[i]
+    elseif not outname then outname = arg[i]
+    else
+      io.stderr:write("songidx: too many command line arguments\n")
+      return 2
+    end
+    i = i + 1
+  end
+
+  if not locale then
+    os.setlocale("")
+  elseif not os.setlocale(locale) then
+    io.stderr:write("songidx: invalid locale: ",locale,"\n")
+    return 2
+  end
+
+  if not inname then
+    io.stderr:write("songidx: no input file specified\n")
+    return 2
+  end
+  if not outname then
+    if inname == "-" then
+      outname = "-"
+    else
+      local n
+      outname,n = inname:gsub("%.[^%./\\]*$", ".sbx")
+      if n == 0 then outname = inname .. ".sbx" end
+    end
+  end
+  if not biblename then biblename = BIBLEDEFAULT end
+
+  fs = fileopen(inname)
+  if not fs then return 2 end
+
+  local retval = 2
+  local buf = filereadln(fs)
+  if not buf then
+    io.stderr:write("songidx:",fs["filename"],": file is empty\n")
+    fileclose(fs)
+  elseif buf == "TITLE INDEX DATA FILE" then
+    retval = genindex(fs,outname,false)
+  elseif buf == "SCRIPTURE INDEX DATA FILE" then
+    retval = genscriptureindex(fs,outname,biblename)
+  elseif buf == "AUTHOR INDEX DATA FILE" then
+    retval = genindex(fs,outname,true)
+  else
+    io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": file has unrecognized format\n")
+    fileclose(fs)
+  end
+
+  if retval == 0 then
+    io.stderr:write("songidx: Done!\n")
+  elseif retval == 1 then
+    io.stderr:write("songidx: COMPLETED WITH ERRORS. SEE ABOVE.\n")
+  else
+    io.stderr:write("songidx: FAILED. SEE ERROR MESSAGES ABOVE.\n")
+  end
+
+  return retval
+end
+
+os.exit(main())
+


Property changes on: trunk/Master/texmf-dist/doc/latex/songs/songidx/songidx.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/songsort.c
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/songsort.c	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/songsort.c	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,162 +0,0 @@
-/* Copyright (C) 2012 Kevin W. Hamlen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA  02110-1301, USA.
- *
- * The latest version of this program can be obtained from
- * http://songs.sourceforge.net.
- */
-
-
-#if HAVE_CONFIG_H
-#  include "config.h"
-#else
-#  include "vsconfig.h"
-#endif
-
-#include "chars.h"
-#include "songidx.h"
-#include "fileio.h"
-
-/* skipesc_sp(<ptr>,<flag>)
- *   Walk <ptr> past any LaTeX macros or braces until we reach the next "real"
- *   character.  If <flag> is 1, then make an exception for space-macros.
- *   The space macro "\ " causes p to stop on the space. */
-void
-skipesc_sp(p, stop_on_space)
-  const WCHAR **p;
-  int stop_on_space;
-{
-  for (;;)
-  {
-    if (**p == wc_backslash)
-    {
-      ++(*p);
-      if (stop_on_space && wc_isspace(**p)) return;
-      if (wc_isalpha(**p)) while (wc_isalpha(**p)) ++(*p);
-      else if (**p) ++(*p);
-      while (wc_isspace(**p)) ++(*p);
-    }
-    else if (**p == wc_lbrace)
-    {
-      ++(*p);
-      while (wc_isspace(**p)) ++(*p);
-    }
-    else if (**p == wc_rbrace)
-      ++(*p);
-    else
-      break;
-  }
-}
-
-/* skipesc(<ptr>)
- *   Walk <ptr> past any LaTeX macros or braces until we reach the next "real"
- *   character. */
-void
-skipesc(p)
-  const WCHAR **p;
-{
-  skipesc_sp(p,0);
-}
-
-/* inword(<char>)
- *   Return 0 if <char> is a word-delimiter (for sorting purposes) and 1
- *   otherwise.  Other than TeX macros (which are handled by skipesc above),
- *   words only have alphabetics and apostrophes. */
-static int
-inword(c)
-  WCHAR c;
-{
-  return (wc_isalpha(c) || (c==wc_apostrophe) || (c==wc_backquote));
-}
-
-/* songcmp(<song1>,<song2>)
- *   Return a negative number if <song1> is less than <song2>, a positive
- *   number if <song1> is greater than <song2>, and 0 if <song1> and <song2>
- *   are equal.  The ordering is first by title, then by index.  This function
- *   is suitable for use with qsort(). */
-int
-songcmp(s1,s2)
-  const void *s1;
-  const void *s2;
-{
-  static WCHAR buf1[MAXLINELEN+1], *bp1;
-  static WCHAR buf2[MAXLINELEN+1], *bp2;
-  const WCHAR *t1 = (*((const SONGENTRY **) s1))->title;
-  const WCHAR *t2 = (*((const SONGENTRY **) s2))->title;
-  int diff;
-
-  for (;;)
-  {
-    /* Find the next word or number in each string. */
-    skipesc(&t1);
-    while(*t1 && !wc_isalpha(*t1) && !wc_isdigit(*t1))
-    {
-      ++t1;
-      skipesc(&t1);
-    }
-    skipesc(&t2);
-    while(*t2 && !wc_isalpha(*t2) && !wc_isdigit(*t2))
-    {
-      ++t2;
-      skipesc(&t2);
-    }
-
-    /* If there is no next word/number in one or both, sort the shorter
-     * string before the longer one. */
-    if ((*t1==wc_null) || (*t2==wc_null))
-    {
-      if ((*t1==wc_null) && (*t2==wc_null)) break;
-      return (t1!=wc_null) ? 1 : -1;
-    }
-
-    /* If one is a number, sort the number before the word.  If both are
-     * numbers, sort in numerical order. */
-    if (wc_isdigit(*t1) || wc_isdigit(*t2))
-    {
-      long n1, n2;
-      WCHAR *p1, *p2;
-      if (!wc_isdigit(*t1)) return 1;
-      if (!wc_isdigit(*t2)) return -1;
-      n1 = ws_strtol(t1, &p1, 10);
-      t1 = p1;
-      n2 = ws_strtol(t2, &p2, 10);
-      t2 = p2;
-      if (n1 == n2) continue;
-      return n1-n2;
-    }
-
-    /* Otherwise, both are words.  Copy the words into scratch buffers,
-     * omitting any macros or braces.  Then lexographically compare the buffer
-     * contents to determine how the original strings should be sorted. */
-    for (bp1=buf1; inword(*t1); skipesc_sp(&t1,1)) *bp1++=wc_tolower(*t1++);
-    for (bp2=buf2; inword(*t2); skipesc_sp(&t2,1)) *bp2++=wc_tolower(*t2++);
-    *bp1 = *bp2 = wc_null;
-    if ((diff = ws_coll(buf1,buf2)) != 0) return diff;
-  }
-
-  /* If each corresponding word/number is identical, then sort alternate-
-   * form entries (e.g., lyrics) after normal entries (e.g., titles). */
-  if (((*((const SONGENTRY **) s1))->title[0] == wc_asterisk)
-      && ((*((const SONGENTRY **) s2))->title[0] != wc_asterisk))
-    return 1;
-  if (((*((const SONGENTRY **) s1))->title[0] != wc_asterisk)
-      && ((*((const SONGENTRY **) s2))->title[0] == wc_asterisk))
-    return -1;
-
-  /* If everything is the same, sort by the right-hand sides of the index
-   * entries (e.g., the song or page numbers). */
-  return (*((const SONGENTRY **) s1))->idx - (*((const SONGENTRY **) s2))->idx;
-}

Modified: trunk/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 Kevin W. Hamlen
+# Copyright (C) 2017 Kevin W. Hamlen
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License

Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/titleidx.c
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/titleidx.c	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/titleidx.c	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,379 +0,0 @@
-/* Copyright (C) 2012 Kevin W. Hamlen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA  02110-1301, USA.
- *
- * The latest version of this program can be obtained from
- * http://songs.sourceforge.net.
- */
-
-
-/* Create a LaTeX title index file from a title .sxd file. */
-
-#if HAVE_CONFIG_H
-#  include "config.h"
-#else
-#  include "vsconfig.h"
-#endif
-
-#include "chars.h"
-#include "songidx.h"
-#include "fileio.h"
-
-#if HAVE_STRING_H
-#  include <string.h>
-#elif HAVE_STRINGS_H
-#  include <strings.h>
-#endif
-
-/* By convention, some word prefixes always get moved to the end of a title
- * before it is indexed.  For example, in English "The Song Title" becomes
- * "Song Title, The".  The following structure stores the list of these prefix
- * words.  The default is "The" and "A". */
-typedef struct prefix
-{
-  struct prefix *next;
-  WCHAR *s;
-}
-PREFIX;
-
-PREFIX pre_A = { NULL, ws_lit("A") };
-PREFIX pre_defaults = { &pre_A, ws_lit("The") };
-
-/* freeprefixes(<list>)
- *   Free prefix list <list>. */
-static void
-freeprefixes(pl)
-  PREFIX *pl;
-{
-  if (pl == &pre_defaults) return;
-  while (pl)
-  {
-    PREFIX *nxt = pl->next;
-    free(pl->s);
-    free(pl);
-    pl = nxt;
-  }
-}
-
-/* rotate(<title>, <prelist>)
- *   If the first word of <title> is any word in list <prelist>, then <title>
- *   is modified in-place so that the word is shifted to the end of the string
- *   and preceded by a comma and a space.  So for example, rotate("The Title",
- *   <prelist>) where <prelist> is the default results in "Title, The".  Words
- *   in <prelist> are matched case-insensitively.  If <title> begins with the
- *   marker character '*', that character is ignored and left unchanged.  Note
- *   that <title> MUST be allocated with ONE EXTRA WCHAR OF STORAGE in order
- *   to leave room for the extra comma.
- *   Return Value: 1 if <title> was modified and 0 otherwise. */
-static int
-rotate(s,pl)
-  WCHAR *s;
-  PREFIX *pl;
-{
-  const WCHAR *s2, *w2;
-
-  w2 = NULL;
-  if (*s==wc_asterisk) ++s;
-  for (; pl; pl=pl->next)
-  {
-    for (s2=s, w2=pl->s; *w2; ++s2, ++w2)
-      if (wc_tolower(*s2)!=wc_tolower(*w2)) break;
-    if ((*w2 == wc_null) && (*s2 != wc_null) && !wc_isalpha(*s2)) break;
-  }
-  if (pl)
-  {
-    WCHAR buf[MAXLINELEN];
-    size_t slen = ws_strlen(s);
-    size_t wlen = w2 - pl->s;
-    ws_strncpy(buf, s, wlen);
-    buf[wlen] = wc_null;
-    ws_memmove(s, s+wlen+1, slen-wlen-1);
-    *s = wc_toupper(*s);
-    *(s+slen-wlen-1) = wc_comma;
-    *(s+slen-wlen) = wc_tilda;
-    ws_strcpy(s+slen-wlen+1, buf);
-    return 1;
-  }
-  return 0;
-}
-
-/* getstartchar(<string>,<default>)
- *   Returns the first non-escaped, alphabetic character of <string>. If
- *   <string> has no non-escaped, alphabetic characters, <default> is returned
- *   instead. */
-static WCHAR
-getstartchar(s,def)
-  const WCHAR *s;
-  WCHAR def;
-{
-  if (!s) return def;
-  skipesc(&s);
-  while ((*s!=wc_null) && !wc_isalpha(*s))
-  {
-    ++s;
-    skipesc(&s);
-  }
-  return (*s!=wc_null) ? wc_toupper(*s) : def;
-}
-
-/* freesongarray(<array>,<len>)
- *   Free song array <array>, which should have length <len>. */
-static void
-freesongarray(songs,len)
-  SONGENTRY **songs;
-  int len;
-{
-  int i;
-
-  if (!songs) return;
-  for (i=0; i<len; ++i)
-  {
-    if (songs[i])
-    {
-      if (songs[i]->title) free(songs[i]->title);
-      if (songs[i]->num) free(songs[i]->num);
-      if (songs[i]->linkname) free(songs[i]->linkname);
-      free(songs[i]);
-    }
-  }
-  free(songs);
-}
-
-/* gentitleindex(<file>,<outname>)
- *   Reads a title index data file from file handle <file> and generates a new
- *   file named <outfile> containing a LaTeX title index.
- *   Return Value: 0 on success, 1 on warnings, or 2 on failure */
-int
-gentitleindex(fs,outname)
-  FSTATE *fs;
-  const char *outname;
-{
-  FILE *f;
-  int eof = 0;
-  int numsongs, arraysize, i;
-  PREFIX *prelist = &pre_defaults;
-  SONGENTRY **songs;
-  WCHAR buf[MAXLINELEN], *bp;
-  WCHAR startchar;
-
-  fprintf(stderr, "songidx: Parsing title index data file %s...\n",
-          fs->filename);
-
-  songs = NULL;
-  eof = 0;
-  for (arraysize=numsongs=0; !eof; ++numsongs)
-  {
-    if (!filereadln(fs,buf,&eof))
-    {
-      freesongarray(songs,numsongs);
-      return 2;
-    }
-    if (eof) break;
-    if (buf[0] == wc_percent)
-    {
-      if (!ws_strncmp(buf, wc_lit("%prefix "), 8))
-      {
-        PREFIX *temp = (prelist==&pre_defaults) ? NULL : prelist;
-        prelist = (PREFIX *) malloc(sizeof(PREFIX));
-        prelist->next = temp;
-        prelist->s = (WCHAR *) calloc(ws_strlen(buf)-7, sizeof(WCHAR));
-        ws_strcpy(prelist->s, buf+8);
-      }
-      --numsongs;
-      continue;
-    }
-    if (numsongs >= arraysize)
-    {
-      SONGENTRY **temp;
-      arraysize *= 2;
-      if (arraysize==0) arraysize=64;
-      temp = (SONGENTRY **) realloc(songs,arraysize*sizeof(SONGENTRY *));
-      if (!temp)
-      {
-        fprintf(stderr, "songidx:%s:%d: too many songs (out of memory)\n",
-                fs->filename, fs->lineno);
-        freeprefixes(prelist);
-        freesongarray(songs,numsongs);
-        return 2;
-      }
-      songs = temp;
-    }
-    if ((songs[numsongs] = (SONGENTRY *) malloc(sizeof(SONGENTRY))) == NULL)
-    {
-      fprintf(stderr, "songidx:%s:%d: too many songs (out of memory)\n",
-              fs->filename, fs->lineno);
-      freeprefixes(prelist);
-      freesongarray(songs,numsongs);
-      return 2;
-    }
-    songs[numsongs]->title = songs[numsongs]->num =
-      songs[numsongs]->linkname = NULL;
-    for (bp=buf+ws_strlen(buf)-1; (bp > buf) && wc_isspace(*bp); --bp) ;
-    if (wc_isspace(*bp)) *bp = wc_null;
-    for (bp=buf; wc_isspace(*bp); ++bp) ;
-    /* The following allocates one extra char of storage because we might add
-     * a comma later. */
-    if ((songs[numsongs]->title =
-         (WCHAR *) calloc(ws_strlen(bp)+2, sizeof(WCHAR))) == NULL)
-    {
-      fprintf(stderr, "songidx:%s:%d: too many songs (out of memory)\n",
-              fs->filename, fs->lineno);
-      freeprefixes(prelist);
-      freesongarray(songs,numsongs+1);
-      return 2;
-    }
-    ws_strcpy(songs[numsongs]->title, bp);
-    rotate(songs[numsongs]->title, prelist);
-    if (!filereadln(fs,buf,&eof))
-    {
-      freeprefixes(prelist);
-      freesongarray(songs,numsongs+1);
-      return 2;
-    }
-    if (eof)
-    {
-      fprintf(stderr, "songidx:%s:%d: incomplete song entry (orphan title)\n",
-              fs->filename, fs->lineno);
-      freeprefixes(prelist);
-      freesongarray(songs,numsongs+1);
-      return 2;
-    }
-    if ((songs[numsongs]->num =
-         (WCHAR *) calloc(ws_strlen(buf)+1, sizeof(WCHAR))) == NULL)
-    {
-      fprintf(stderr, "songidx:%s:%d: too many songs (out of memory)\n",
-              fs->filename, fs->lineno);
-      freeprefixes(prelist);
-      freesongarray(songs,numsongs+1);
-      return 2;
-    }
-    ws_strcpy(songs[numsongs]->num, buf);
-    if (!filereadln(fs,buf,&eof))
-    {
-      freeprefixes(prelist);
-      freesongarray(songs,numsongs+1);
-      return 2;
-    }
-    if (eof)
-    {
-      fprintf(stderr, "songidx:%s:%d: incomplete song entry"
-                      " (missing hyperlink)\n", fs->filename, fs->lineno);
-      freeprefixes(prelist);
-      freesongarray(songs,numsongs+1);
-      return 2;
-    }
-    if ((songs[numsongs]->linkname =
-         (WCHAR *) calloc(ws_strlen(buf)+1, sizeof(WCHAR))) == NULL)
-    {
-      fprintf(stderr, "songidx:%s:%d: too many songs (out of memory)\n",
-              fs->filename, fs->lineno);
-      freeprefixes(prelist);
-      freesongarray(songs,numsongs);
-      return 2;
-    }
-    ws_strcpy(songs[numsongs]->linkname, buf);
-    songs[numsongs]->idx = numsongs;
-  }
-  fileclose(fs);
-  freeprefixes(prelist);
-
-  /* Sort the song array */
-  qsort(songs, numsongs, sizeof(*songs), songcmp);
-
-  /* Write the sorted data out to the output file. */
-  fprintf(stderr, "songidx: Generating title index TeX file %s...\n", outname);
-  if (strcmp(outname,"-"))
-  {
-    if ((f = fopen(outname, "w")) == NULL)
-    {
-      fprintf(stderr, "songidx: Unable to open %s for writing.\n", outname);
-      return 2;
-    }
-  }
-  else
-  {
-    f = stdout;
-    outname = "stdout";
-  }
-
-#define TRYWRITE(x) \
-  if (!(x)) \
-  { \
-    fprintf(stderr, "songidx:%s: write error\n", outname); \
-    if (f == stdout) fflush(f); else fclose(f); \
-    freesongarray(songs,numsongs); \
-    return 2; \
-  }
-
-  startchar = 'A';
-  if (numsongs>0)
-  {
-    startchar = getstartchar(songs[0]->title, wc_capA);
-    TRYWRITE((ws_fputs(ws_lit("\\begin{idxblock}{"), f) >= 0) &&
-             (ws_fputc(startchar, f)==startchar))
-  }
-  for (i=0; i<numsongs; ++i)
-  {
-    WCHAR c = getstartchar(songs[i]->title, startchar);
-    if ((i>0) && !ws_coll(songs[i]->title, songs[i-1]->title))
-    {
-      TRYWRITE((ws_fputs(ws_lit("\\\\\\hyperlink{"), f) >= 0) &&
-               (ws_fputs(songs[i]->linkname, f) >= 0) &&
-               (ws_fputs(ws_lit("}{"), f) >= 0) &&
-               (ws_fputs(songs[i]->num, f) >= 0) &&
-               (ws_fputs(ws_lit("}"), f) >= 0))
-      continue;
-    }
-    else
-    {
-      TRYWRITE(ws_fputs(ws_lit("}\n"), f) >= 0)
-    }
-    if (startchar != c)
-    {
-      startchar = c;
-      TRYWRITE((ws_fputs(ws_lit("\\end{idxblock}\n\\begin{idxblock}{"), f) >= 0)
-               && (ws_fputc(startchar, f)==startchar)
-               && (ws_fputs(ws_lit("}\n"), f) >= 0))
-    }
-    if (songs[i]->title[0] == wc_asterisk)
-    {
-      TRYWRITE((ws_fputs(ws_lit("\\idxaltentry{"), f) >= 0) &&
-               (ws_fputs(songs[i]->title+1, f) >= 0) &&
-               (ws_fputs(ws_lit("}{\\hyperlink{"), f) >= 0) &&
-               (ws_fputs(songs[i]->linkname, f) >= 0) &&
-               (ws_fputs(ws_lit("}{"), f) >= 0) &&
-               (ws_fputs(songs[i]->num, f) >= 0) &&
-               (ws_fputs(ws_lit("}"), f) >= 0))
-    }
-    else
-    {
-      TRYWRITE((ws_fputs(ws_lit("\\idxentry{"), f) >= 0) &&
-               (ws_fputs(songs[i]->title, f) >= 0) &&
-               (ws_fputs(ws_lit("}{\\hyperlink{"), f) >= 0) &&
-               (ws_fputs(songs[i]->linkname, f) >= 0) &&
-               (ws_fputs(ws_lit("}{"), f) >= 0) &&
-               (ws_fputs(songs[i]->num, f) >= 0) &&
-               (ws_fputs(ws_lit("}"), f) >= 0))
-    }
-  }
-  TRYWRITE(ws_fputs(ws_lit("}\n\\end{idxblock}\n"), f) >= 0)
-
-#undef TRYWRITE
-
-  if (f == stdout) fflush(f); else fclose(f);
-  freesongarray(songs,numsongs);
-  return 0;
-}

Deleted: trunk/Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h
===================================================================
--- trunk/Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,116 +0,0 @@
-/* Copyright (C) 2012 Kevin W. Hamlen
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA  02110-1301, USA.
- *
- * The latest version of this program can be obtained from
- * http://songs.sourceforge.net.
- */
-
-/* This configuration file should work as-is for any system that has standard
- * C libraries.  It has been tested successfully on Visual Studio and Linux.
- * If your system is non-standard, follow the directions below to customize it
- * for your system. */
-
-#ifndef CONFIG_H
-#define CONFIG_H
-
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-/* #undef HAVE_INTTYPES_H */
-
-/* Define to 1 if you have the <locale.h> header file. */
-#define HAVE_LOCALE_H 1
-
-/* Define to 1 if you have the <memory.h> header file. */
-#define HAVE_MEMORY_H 1
-
-/* Define to 1 if your system has a GNU libc compatible `realloc' function,
-   and to 0 otherwise. */
-#define HAVE_REALLOC 1
-
-/* Define to 1 if you have the `setlocale' function. */
-#define HAVE_SETLOCALE 1
-
-/* Define to 1 if you have the <stdint.h> header file. */
-/* #undef HAVE_STDINT_H */
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#define HAVE_STDLIB_H 1
-
-/* Define to 1 if you have the <stdio.h> header file. */
-#define HAVE_STDIO_H 1
-
-/* Define to 1 if you have the <strings.h> header file. */
-/* #undef HAVE_STRINGS_H */
-
-/* Define to 1 if you have the <string.h> header file. */
-#define HAVE_STRING_H 1
-
-/* Define to 1 if you have the `strrchr' function. */
-#define HAVE_STRRCHR 1
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#define HAVE_SYS_TYPES_H 1
-
-/* Define to 1 if you have the <unistd.h> header file. */
-/* #undef HAVE_UNISTD_H */
-
-/* Define to 1 if you have the <wchar.h> header file. */
-#define HAVE_WCHAR_H 1
-
-/* Define to 1 if you have the wchar_t type. */
-#define HAVE_WCHAR_T 1
-
-/* Define to 1 if you have the <wctype.h> header file. */
-#define HAVE_WCTYPE_H 1
-
-/* Name of package */
-#define PACKAGE "songs"
-
-/* Define to the address where bug reports for this package should be sent. */
-#define PACKAGE_BUGREPORT ""
-
-/* Define to the full name of this package. */
-#define PACKAGE_NAME "songs"
-
-/* Define to the full name and version of this package. */
-#define PACKAGE_STRING "songs 2.14"
-
-/* Define to the one symbol short name of this package. */
-#define PACKAGE_TARNAME "songs"
-
-/* Define to the version of this package. */
-#define PACKAGE_VERSION "2.14"
-
-/* Define to 1 if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* Version number of package */
-#define VERSION "2.14"
-
-/* Define to empty if `const' does not conform to ANSI C. */
-/* #undef const */
-
-/* Define to rpl_realloc if the replacement function should be used. */
-/* #undef realloc */
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-/* #undef size_t */
-
-#endif

Modified: trunk/Master/texmf-dist/doc/latex/songs/songs.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/source/latex/songs/songs.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/songs/songs.dtx	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/source/latex/songs/songs.dtx	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,6 +1,6 @@
 % \iffalse meta-comment
 %
-% Copyright (C) 2012 Kevin W. Hamlen
+% Copyright (C) 2017 Kevin W. Hamlen
 %
 % This program is free software; you can redistribute it and/or
 % modify it under the terms of the GNU General Public License
@@ -14,8 +14,8 @@
 %
 % You should have received a copy of the GNU General Public License
 % along with this program; if not, write to the Free Software
-% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-% MA  02110-1301, USA.
+% Foundation, Inc., 51 Franklin Street, 5th Floor, Boston,
+% MA 02110-1301, USA.
 %
 % The latest version of this program can be obtained from
 % http://songs.sourceforge.net.
@@ -29,99 +29,161 @@
 %<package>\NeedsTeXFormat{LaTeX2e}
 %<package>\ProvidesPackage{songs}
 %<*package>
-  [2012/03/17 v2.14 Songs package]
+  [2017/06/05 v3.0 Songs package]
 %</package>
 %
 %<*driver>
 \documentclass{ltxdoc}
-\usepackage[bookmarks]{hyperref}
+
+% This documentation compiles as part of the Songs self-installer, so it
+% needs to work even on tiny LaTeX installations with few packages.  We
+% therefore try to be especially robust to missing package failures.
+\newcommand\trypackage[4][]{\IfFileExists{#2.sty}{\usepackage[#1]{#2}#3}{#4}}
+\trypackage{microtype}{\linepenalty=20 \parfillskip=0pt plus\textwidth\relax}{}
+\trypackage{lmodern}{\usepackage[T1]{fontenc}}{}
+\trypackage{ifpdf}{}{\expandafter\newif\csname ifpdf\endcsname\pdffalse}
+\trypackage{color}{}{}
+\ifpdf
+  \trypackage[bookmarks,linkbordercolor={.6 0 0}]{hyperref}{}{}
+\fi
 \usepackage[nopdfindex]{songs}
-\let\oldSE\StopEventually
 
+% Provide back-up hyperlinking and color macros that do nothing,
+% in case the hyperref and/or color packages are absent.
+\providecommand\href[2]{#2}
+\providecommand\url[1]{#1}
+\providecommand\pdfbookmark[3][]{}
+\providecommand\hyperdef[3]{}
+\providecommand\hyperlink[2]{#2}
+\providecommand\texorpdfstring[2]{#1}
+\providecommand\definecolor[3]{}
+\providecommand\color[1]{}
+\providecommand\textcolor[2]{#2}
+\providecommand\microtypesetup[1]{}
+
 % Configure the document:
+\let\oldSE\StopEventually
 \EnableCrossrefs
 \CodelineIndex
 \RecordChanges
 %\OnlyDescription
 
-% Create the \Songs logo, if appropriate fonts are available:
-\IfFileExists{harmony.sty}{
-  \usepackage{harmony}
-  \usepackage{graphics}
-  \newcommand\Songs{\texorpdfstring{{\sffamily s\kern.07ex\resizebox{!}{1ex}{\Ganz}\kern-.09ex ngs}}{songs}}
-}{
-  \newcommand\Songs{\texorpdfstring{{\sffamily songs}}{songs}}
-}
+% Create the \Songs logo, if the musix13 font is available:
+\newcommand\Songs{\texorpdfstring{{\sffamily songs}}{songs}}
+\newcount\imode
+\imode=\interactionmode
+\batchmode
+\newfont\musicfont{musix13}
+\interactionmode=\imode
+\ifx\musicfont\nullfont\else
+  \newdimen\msize
+  \newcommand\wholenote{%
+    \msize4ex
+    \expandafter\ifx\csname musicfont\the\msize\endcsname\relax
+      \expandafter\newfont\csname musicfont\the\msize\endcsname
+        {musix13 at \the\msize}%
+    \fi
+    \kern.13ex
+    \raise.5ex\hbox{{\csname musicfont\the\msize\endcsname\symbol9}}%
+    \kern1.53ex
+  }
+  \renewcommand\Songs{\texorpdfstring{{\sffamily s\wholenote ngs}}{songs}}
+\fi
 
 % Create the logo for Christopher Rath's Songbook package:
 \newcommand{\Rath}{{\sffamily Song$\flat$ook}}
 
+% Colors
+\definecolor{myred}{rgb}{0.6,0,0}
+\definecolor{myblu}{rgb}{0,0,0.6}
+\definecolor{mygra}{gray}{0.33}
+\def\PrintDescribeMacro#1{\strut{\MacroFont\color{myred}\string#1}\ }
+\let\PrintMacroName\PrintDescribeMacro
+\def\PrintDescribeEnv#1{\strut{\MacroFont\color{myred}#1}\ }
+\let\PrintEnvName\PrintDescribeEnv
+{\makeatletter\gdef\verbatim at font{\normalfont\color{myblu}\ttfamily}}
+
+% For some reason, doc.sty removes the \verbatim at font customization hook
+% for \verb|...|.  (Why??)  That's not nice, so we put it back:
+{\makeatletter
+ \gdef\verb {\relax\ifmmode\hbox\else\leavevmode\null\fi
+   \bgroup \let\do\do at noligs \verbatim at nolig@list
+   \verbatim at font \verb at eol@error \let\do\@makeother \dospecials
+   \@ifstar{\@sverb}{\@vobeyspaces \frenchspacing \@sverb}}
+}
+
 % Define some environments to simulate the interior of a verse,
 % for showing samples in the documentation:
 {\makeatletter
  \gdef\likeverse{%
-   \SB at insongtrue\SB at inversetrue%
-   \SB at loadactives%
-   \global\SB at ctail\SB at cr@%
+   \SB at insongtrue\SB at inversetrue
+   \SB at loadactives
+   \global\SB at ctail\SB at cr@
  }
  \gdef\chordheight{\SB at setbaselineskip}
 }
 
 % Typeset a block of LaTeX code:
+\newcommand\pfs{\parfillskip0pt plus1fil\relax}
 \newenvironment{codeblock}{%
-  \medskip%
-  \vbox\bgroup\begingroup%
-    \narrower\rightskip=0pt plus1fil%
-    \parindent=0pt%
-    \obeylines%
+  \medskip
+  \begingroup
+    \ifdim\parindent=0pt \parindent=20pt\fi
+    \indent\vbox\bgroup
+      \hsize\linewidth
+      \advance\hsize-\parindent
+      \rightskip=0pt plus1fil\pfs
+      \parindent=0pt
+      \color{myblu}%
+      \obeylines
 }{%
-  \endgroup\egroup%
-  \medskip%
+  \egroup\endgroup
+  \medskip
 }
 
 % Typeset a sample song or scripture quotation:
 \songcolumns{0}
 \newenvironment{sample}{%
-  \medskip%
-  \noindent\hfil%
-  \vbox\bgroup%
-    \hsize.5\hsize%
-    \advance\hsize-.5\columnsep%
-    \versesep=5pt%
+  \medskip
+  \noindent\hfil
+  \vbox\bgroup
+    \hsize.5\hsize
+    \advance\hsize-.5\columnsep
+    \versesep=5pt
+    \pfs
 }{%
-  \egroup%
-  \medskip%
+  \egroup{\pfs\par}\medskip
 }
 
 % Typeset a sample lyric book fragment:
 \newenvironment{lyrics}{%
-  \medskip%
-  \noindent\hfil%
-  \vbox\bgroup\begingroup%
-    \hsize=.7\textwidth%
-    \leftskip=20pt\rightskip=0pt plus1fil%
-    \parindent=-20pt%
-    \likeverse\obeylines%
+  \medskip
+  \noindent\hfil
+  \vbox\bgroup\begingroup
+    \hsize=.7\textwidth
+    \leftskip=20pt\rightskip=0pt plus1fil\pfs
+    \parindent=-20pt
+    \likeverse\obeylines
 }{%
-  \par\endgroup\egroup%
-  \hfil\par%
-  \medskip%
+  \par\endgroup\egroup
+  \hfil{\pfs\par}
+  \medskip
 }
 
 % Typeset a sample chord book fragment:
 \newenvironment{chorded}{%
-  \medskip%
-  \noindent\hfil%
-  \vbox\bgroup\begingroup%
-    \hsize=.7\textwidth%
-    \leftskip=20pt\rightskip=0pt plus1fil%
-    \parindent=-20pt%
-    \likeverse\obeylines%
-    \versesep=5pt\chordheight%
+  \medskip
+  \noindent\hfil
+  \vbox\bgroup\begingroup
+    \hsize=.7\textwidth
+    \leftskip=20pt\rightskip=0pt plus1fil\pfs
+    \parindent=-20pt
+    \likeverse\obeylines
+    \versesep=5pt\chordheight
 }{%
-  \par\endgroup\egroup%
-  \hfil\par%
-  \medskip%
+  \par\endgroup\egroup
+  \hfil{\pfs\par}
+  \medskip
 }
 
 % Typeset a "<code> produces <text>" example:
@@ -129,70 +191,77 @@
 \setlength{\prodlen}{2.7in}
 \newbox\prodbox
 \newcommand{\example}{%
-  \medskip\setbox\prodbox\hbox\bgroup%
+  \medskip\setbox\prodbox\hbox\bgroup\begingroup\color{myblu}%
 }
 \newcommand{\produces}{%
-  \egroup%
-  \indent%
-  \vbox{\hbox to\prodlen{\unhbox\prodbox\hfil}}%
-  \ {\it produces}\quad%
-  \afterassignment\prodprefix%
-  \setbox\prodbox\hbox}
+  \endgroup\egroup
+  \indent
+  \vbox{\hbox to\prodlen{\unhbox\prodbox\hfil}}
+  \ {\it produces}\quad
+  \afterassignment\prodprefix
+  \setbox\prodbox\hbox
+}
 \newcommand{\prodprefix}{%
-  \likeverse\chordheight%
-  \aftergroup\prodsuffix}
-\newcommand{\prodsuffix}{\unhbox\prodbox\par\medskip}
+  \likeverse\chordheight
+  \aftergroup\prodsuffix
+}
+\newcommand{\prodsuffix}{\unhbox\prodbox{\pfs\par}\medskip}
 \newbox\vcbox
 \newdimen\vcadjust
 \newcommand{\vcenterbox}[1]{%
   \setbox\vcbox\vbox{\hbox{#1}}%
-  \vcadjust=.5\ht\vcbox%
-  \advance\vcadjust by-6pt%
-  \lower\vcadjust\box\vcbox%
+  \vcadjust=.5\ht\vcbox
+  \advance\vcadjust by-6pt
+  \lower\vcadjust\box\vcbox
 }
 
 % Recode \DescribeMacro and \DescribeEnv to make nice pdfbookmark entries.
 % Also create some \MainImpl macros that make pdfbookmarks to help the reader
 % find the "real" implementations of important macros.
-\newcount\seclevel%
-\newcommand{\getseclevel}{%
-  \ifnum\value{subsection}=0 \seclevel=2 %
-  \else\ifnum\value{subsubsection}=0 \seclevel=3 %
-  \else\seclevel=4 \fi\fi%
+\newcount\seclevel
+\newcommand\mybookmark[2]{%
+  \ifnum\value{subsection}=0 \seclevel=2
+  \else\ifnum\value{subsubsection}=0 \seclevel=3
+  \else\seclevel=4 \fi\fi
+  \setcounter{tocdepth}{4}%
+  \pdfbookmark[\the\seclevel]{#1}{#2}%
+  \setcounter{tocdepth}{3}%
 }
 {\makeatletter
  \xdef\bschar{\@backslashchar}
  \global\let\For\@for}
 \newcommand\DescMacro[1]{%
-  \getseclevel%
-  \pdfbookmark[\the\seclevel]{\bschar\bschar#1}{macdef-#1}%
+  \ifhmode\unskip\fi
+  \mybookmark{\bschar\bschar#1}{macdef-#1}%
   \expandafter\DescribeMacro\expandafter{\csname#1\endcsname}%
-  \hyperdef{macro}{#1}{}\kern0pt%
+  \hyperdef{macro}{#1}{}\unpenalty
+  \ignorespaces
 }
 \newcommand\DescMacroGroup[3]{%
-  \getseclevel%
-  \pdfbookmark[\the\seclevel]{\bschar\bschar#2}{macdef-#1}%
+  \ifhmode\unskip\fi
+  \mybookmark{\bschar\bschar#2}{macdef-#1}%
   \expandafter\DescribeMacro\expandafter{\csname#2\endcsname}%
-  \For\temp:=#3\do{\hyperdef{macro}{\temp}{}}%
+  \For\temp:=#3\do{\hyperdef{macro}{\temp}{}}\unpenalty
+  \ignorespaces
 }
 \newcommand\MainImpl[1]{%
   \pdfbookmark[3]{\bschar\bschar#1}{mimpl-#1}%
 }
 \newcommand{\DescEnv}[1]{%
-  \getseclevel%
-  \pdfbookmark[\the\seclevel]{#1}{envdef-#1}%
+  \ifhmode\unskip\fi
+  \mybookmark{#1}{envdef-#1}%
   \DescribeEnv{#1}%
-  \hyperdef{env}{#1}{}\kern0pt%
+  \hyperdef{env}{#1}{}\unpenalty
+  \ignorespaces
 }
 \newcommand{\MainEnvImpl}[1]{%
-  \getseclevel%
-  \pdfbookmark[\the\seclevel]{#1}{eimpl-#1}%
+  \mybookmark{#1}{eimpl-#1}%
 }
 
 % Create macros to hyperlink macro and environment names to their
 % documentation points.
-\newcommand{\mac}[1]{{\tt\hyperlink{macro.#1}{\char92 #1}}}
-\newcommand{\env}[1]{{\tt\hyperlink{env.#1}{#1}}}
+\newcommand{\mac}[1]{{\tt\hyperlink{macro.#1}{\textcolor{myblu}{\char92 #1}}}}
+\newcommand{\env}[1]{{\tt\hyperlink{env.#1}{\textcolor{myblu}{#1}}}}
 
 % Defining bookmarks for definitions of active characters is a little trickier
 % because many of these characters have special meanings either to TeX or to
@@ -199,33 +268,30 @@
 % PDF.  The only reliable way is to insert an "\ooo" escape sequence into the
 % bookmark text, where ooo is the ascii number of the character expressed in
 % octal.  To achieve this, we use `\string<symbol> to obtain the decimal
-% ascii number d of the symbol, then do some math (implemented in \octalize)
-% to compute the decimal number n whose digits correspond to the octal
-% representation of d, and then write n to the bookmark text using \the.
+% ascii number d of the symbol, then do some math (implemented in \octal)
+% to compute and tokenize each octal digit of d into the bookmark text.
 \newcount\cnta
 \newcount\cntb
-\newcount\cntc
-\newcommand{\octalize}[2]{%
+\newcommand\ooo{}
+\newcommand\octal{%
   \cntb\cnta
-  \divide\cntb#1
-  \multiply\cntb#2
-  \advance\cntc\cntb
-  \divide\cntb#2
-  \multiply\cntb#1
-  \advance\cnta-\cntb
+  \divide\cnta8
+  \multiply\cnta-8
+  \advance\cntb\cnta
+  \edef\ooo{\the\cntb\ooo}%
+  \divide\cnta-8
 }
 \newcommand{\DescChar}[2]{%
+  \ifhmode\unskip\fi
   \expandafter\let\csname string#1\expandafter\endcsname
     \expandafter=\string#2%
-  \getseclevel
   \cnta\expandafter`\string#2%
-  \cntc0
-  \octalize{64}{100}%
-  \octalize{8}{10}%
-  \octalize{1}{1}%
-  \pdfbookmark[\the\seclevel]{\bschar\the\cntc}{#1def}%
-  \expandafter\DescribeEnv\string#2%
-  \hyperdef{env}{#1}{}\kern0pt%
+  \def\ooo{}\octal\octal\octal
+  \mybookmark{\bschar\ooo}{#1def}%
+  {\def\SpecialUsageIndex##1{}%
+   \expandafter\DescribeMacro\string#2}%
+  \hyperdef{env}{#1}{}\unpenalty
+  \ignorespaces
 }
 \newcommand{\refchar}[1]{{\tt\hyperlink{env.#1}{\csname string#1\endcsname}}}
 
@@ -236,8 +302,8 @@
 
 \IndexPrologue{%
   \subsection{Codeline Index}%
-  Numbers underlined refer to the code line where the corresponding entry
-  is defined; numbers in roman refer to the code lines where the entry
+  Underlined numbers refer to the code line where the corresponding entry
+  is defined; other numbers refer to the code lines where the entry
   is used.}
 
 {\makeatletter
@@ -251,6 +317,7 @@
    \mathsurround0pt
    \parfillskip0pt
    \small
+   \microtypesetup{protrusion=false}%
    \def\@idxitem{\par\hangindent15pt}%
    \def\subitem{\@idxitem\hspace*{15pt}}%
    \def\subsubitem{\@idxitem\hspace*{25pt}}%
@@ -269,21 +336,21 @@
 % Create a conditional that typesets its first argument if we're including
 % the implementation section, otherwise typesets its second argument.
 \newcommand\ImplOrDesc[2]{%
-  \ifx\StopEventually\oldSE#1\else#2\fi%
+  \ifx\StopEventually\oldSE#1\else#2\fi
 }
 
 % Hyphenating the word "choruses" looks weird. No "ruses" please!
-\hyphenation{choruses}
+\hyphenation{choruses white-space}
 
 % An environment for describing the implementation of a package option:
 \let\oldsmei\SpecialMainEnvIndex
 \newenvironment{option}[1]{%
-  \let\SpecialMainEnvIndex\SpecialMainOptIndex%
+  \let\SpecialMainEnvIndex\SpecialMainOptIndex
   \begin{environment}{#1}%
-    \let\SpecialMainEnvIndex\oldsmei%
+    \let\SpecialMainEnvIndex\oldsmei
 }{%
   \end{environment}%
-  \let\SpecialMainEnvIndex\oldsmei%
+  \let\SpecialMainEnvIndex\oldsmei
 }
 
 % Describe the default setting for an option:
@@ -293,8 +360,9 @@
 \newcommand{\chord}[1]{{\sffamily\slshape#1}}
 
 % Here are a few macros to produce nice syntax parameters:
-\newcommand{\argp}[1]{\meta{arg#1}}
-\newcommand\Meta[1]{\textrm{\meta{#1}}}
+\newcommand\Meta[1]{{\color{mygra}\meta{#1}}}
+\newcommand\argp[1]{\Meta{arg#1}}
+\newcommand\Metarm[1]{\textrm{\Meta{#1}}}
 \newcommand\OR{\,$\mid$\,}
 \newcommand\SPC{\char`\ }
 
@@ -303,9 +371,9 @@
  \global\let\oldamp=\@addmarginpar
  \global\let\oldlwnl=\@latex at warning@no at line
  \gdef\@addmarginpar{%
-   \let\@latex at warning@no at line\@gobble%
-   \oldamp%
-   \let\@latex at warning@no at line\oldlwnl%
+   \let\@latex at warning@no at line\@gobble
+   \oldamp
+   \let\@latex at warning@no at line\oldlwnl
  }
 }
 
@@ -319,7 +387,7 @@
 %</driver>
 % \fi
 %
-% \CheckSum{8597}
+% \CheckSum{8792}
 %
 % \CharacterTable
 %  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z
@@ -359,12 +427,11 @@
 % \DoNotIndex{\hfil,\hfilneg,\hskip,\hss,\indent,\kern,\nobreak,\noindent,\nointerlineskip,\offinterlineskip,\par,\penalty,\strut,\thinspace,\vadjust,\vfil,\vfilneg,\vphantom,\vskip}
 % \DoNotIndex{\@octets,\four,\three,\two,\UTFviii@,\UTFviii at zero@octets,\0,\1,\2,\3,\4,\5,\6,\7,\8,\9,\X,\O}
 %
-%
 % \GetFileInfo{songs.dtx}
 %
-% \title{The \Songs\ package\thanks{This document corresponds to
+% \title{The \Songs{} package\thanks{This manual documents
 %    \textsf{songs}~\fileversion, dated~\filedate,
-%    \copyright~2012 Kevin W.~Hamlen, and
+%    \copyright~2017 Kevin W.~Hamlen, and
 %    distributed under version~2 the GNU General Public License
 %    as published by the Free Software Foundation.}}
 % \author{Kevin W. Hamlen}
@@ -372,10 +439,10 @@
 % \maketitle
 %
 % \begin{abstract}
-% The \Songs\ package produces songbooks that contain lyrics and chords
+% The \Songs{} package produces songbooks that contain lyrics and chords
 % (but not full sheet music).
 % It allows lyric books, chord books, overhead slides, and digital projector
-% slides to all be maintained and generated from a single \LaTeX\ source
+% slides to all be maintained and generated from a single \LaTeX{} source
 % document.
 % Automatic transposition, guitar tablature diagrams, handouts, and
 % a variety of specialized song indexes are supported.
@@ -383,7 +450,7 @@
 %
 % \section{Introduction}
 %
-% The \Songs\ \LaTeX\ package produces books of songs that contain lyrics and
+% The \Songs{} \LaTeX{} package produces books of songs that contain lyrics and
 % (optionally) chords.
 % A single source document yields a lyric book for singers, a chord book for
 % musicians, and overhead or digital projector slides for corporate singing.
@@ -390,7 +457,7 @@
 %
 % The software is especially well suited for churches and religious
 % fellowships desiring to create their own books of worship songs.
-% Rather than purchasing a fixed hymnal of songs, the \Songs\ package allows
+% Rather than purchasing a fixed hymnal of songs, the \Songs{} package allows
 % worship coordinators to maintain a constantly evolving repertoire of music
 % to which they can add and remove songs over time.
 % As the book content changes, the indexes, spacing, and other formatting
@@ -402,7 +469,7 @@
 % \section{Terms of Use}
 %
 % \noindent
-% The \Songs\ package is free software; you can redistribute it and/or
+% The \Songs{} package is free software; you can redistribute it and/or
 % modify it under the terms of the GNU General Public License
 % as published by the Free Software Foundation; either version~2
 % of the License, or (at your option) any later version.
@@ -414,15 +481,15 @@
 % This program is distributed in the hope that it will be useful,
 % but {\sc without any warranty}; without even the implied warranty of
 % {\sc merchantability} or {\sc fitness for a particular purpose}. See the
-% GNU General Public License in \S\ref{sec:license} for more details.
+% GNU General Public License in \S\ref{sec:license} for details.
 % A copy of the license can also be obtained by writing to the
-% Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-% Boston, MA  02110-1301, USA.
+% Free Software Foundation, Inc., 51 Franklin Street, 5th Floor,
+% Boston, MA 02110-1301, USA.
 %
 % \medskip
 %
 % \noindent
-% This software is copyright \copyright~2012 Kevin W.~Hamlen.
+% This software is copyright \copyright~2017 Kevin W.~Hamlen.
 % For contact information or the latest version, see the project webpage at:
 %
 % \vskip1.5ex
@@ -434,27 +501,26 @@
 %
 % For those who would like to start making song books quickly, the
 % following is a sample document that yields a simple song book with
-% one song and one title index.
+% one song.
 % Starting from this template, you can begin to add songs and customizations
 % to create a larger book.
 % Instructions for compiling this sample song book follow the listing.
 %
+% \begingroup\color{myblu}
 % \begin{verbatim}
 % \documentclass{article}
 % \usepackage[chorded]{songs}
 %
-% \newindex{titleidx}{titleidx}
 % \noversenumbers
 %
 % \begin{document}
-% \showindex{Complete Index of Songs}{titleidx}
+%
 % \songsection{Worship Songs}
 %
-% \begin{songs}{titleidx}
+% \begin{songs}{}
 % \beginsong{Doxology}[by={Louis Bourgeois and Thomas Ken},
 %                      sr={Revelation 5:13},
-%                      cr={Public domain.},
-%                      index={Praise God, from Whom all blessings flow}]
+%                      cr={Public domain.}]
 % \beginverse
 % \[G]Praise God, \[D]from \[Em]Whom \[Bm]all \[Em]bless\[D]ings \[G]flow;
 % \[G]Praise Him, all \[D]crea\[Em]tures \[C]here \[G]be\[D]low;
@@ -467,9 +533,9 @@
 %
 % \end{document}
 % \end{verbatim}
+% \endgroup\nointerlineskip\vskip-6pt plus0pt minus0pt
 %
-% To compile this book, execute three commands.
-% First, use \LaTeX\ (|pdflatex| is recommended) to compile the document:
+% To compile this book, run \LaTeX{} (|pdflatex| is recommended):
 %
 % \begin{codeblock}
 % |pdflatex mybook.tex|
@@ -477,25 +543,12 @@
 %
 % \noindent
 % (where |mybook.tex| is the name of the source document above).
-% Next, use the |songidx| program provided with this distribution to
-% generate the indexes:
-%
-% \begin{codeblock}
-% |songidx titleidx.sxd titleidx.sbx|
-% \end{codeblock}
-%
-% \noindent
-% Finally, regenerate the document using \LaTeX\ so that the newly
-% generated index data will be included:
-%
-% \begin{codeblock}
-% |pdflatex mybook.tex|
-% \end{codeblock}
-%
-% \noindent
 % The final document is named |mybook.pdf| if you use |pdflatex| or
 % |mybook.dvi| if you use regular |latex|.
 %
+% Note that compiling a document that includes indexes requires extra steps.
+% See \S\ref{sec:compiling} for details.
+%
 % \begin{figure}
 % \noindent\vbox{\begingroup\hsize=352pt
 %   \versesep=12pt\columnsep=7pt\parindent=20pt
@@ -579,15 +632,15 @@
 %
 % \section{Initialization and Options}\label{sec:options}
 %
-% Each \LaTeX\ document that uses the \Songs\ package should contain a
+% Each \LaTeX{} document that uses the \Songs{} package should contain a
 % line like the following near the top of the document:
 %
 % \begin{codeblock}
-% |\usepackage[|\meta{options}|]{songs}|
+% |\usepackage[|\Meta{options}|]{songs}|
 % \end{codeblock}
 %
 % \noindent
-% Supported \meta{options} include the following:
+% Supported \Meta{options} include the following:
 %
 % \paragraph{Output Type.}
 % \DescEnv{lyric}
@@ -594,19 +647,20 @@
 % \DescEnv{chorded}
 % \DescEnv{slides}
 % \DescEnv{rawtext}
-% The \Songs\ package can produce four kinds of books: lyric books, chord
+% The \Songs{} package can produce four kinds of books: lyric books, chord
 % books, books of overhead slides, and raw text output.
 % You can specify which kind of book is to be produced by specifying one of
 % |lyric|, |chorded|, |slides|, or |rawtext| as an option.
-% If none of these are specified, |chorded| is the default.
+% The |slides| and |chorded| options can be used together to create chorded
+% slides.
+% If no output options are specified, |chorded| is the default.
 %
 % Lyric books omit all chords, whereas chord books include chords and
 % additional information for musicians (specified using \mac{musicnote}).
-% Books of overhead slides omit all chords and typeset
-% one song per page in a large font, centered.
+% Books of overhead slides typeset one song per page in a large font, centered.
 %
-% Raw text output yields an ascii text file named \meta{jobname}|.txt|
-% (where \meta{jobname} is the root filename) containing lyrics without chords.
+% Raw text output yields an ascii text file named \Meta{jobname}|.txt|
+% (where \Meta{jobname} is the root filename) containing lyrics without chords.
 % This can be useful for importing song books into another program, such as a
 % spell-checker.
 %
@@ -626,7 +680,7 @@
 % \DescEnv{showmeasures}
 % \DescMacro{measureson}
 % \DescMacro{measuresoff}
-% The \Songs\ package includes a facility for placing measure bars in chord
+% The \Songs{} package includes a facility for placing measure bars in chord
 % books (see \S\ref{sec:measures}).
 % To omit these measure bars, use the |nomeasures| option;
 % to display them, use the |showmeasures| option (the default).
@@ -636,14 +690,14 @@
 % \paragraph{Transposition.}
 % \DescEnv{transposecapos}
 % The |transposecapos| option changes the effect of the \mac{capo} macro.
-% Normally, using |\capo{|\meta{n}|}| within a song environment produces a
+% Normally, using |\capo{|\Meta{n}|}| within a song environment produces a
 % textual note in chord books that suggests the use of a guitar capo on fret
-% \meta{n}.
+% \Meta{n}.
 % However, when the |transposecapos| option is active, these textual notes
-% are omitted and instead the effect of |\capo{|\meta{n}|}| is the
-% same as for \mac{transpose}|{|\meta{n}|}|.
+% are omitted and instead the effect of |\capo{|\Meta{n}|}| is the
+% same as for \mac{transpose}|{|\Meta{n}|}|.
 % That is, chords between the \mac{capo} macro and the end of the song are
-% automatically transposed up by \meta{n} half-steps.
+% automatically transposed up by \Meta{n} half-steps.
 % This can be useful for adapting a chord book for guitarists to one that can
 % be used by pianists, who don't have the luxury of capos.
 % See \S\ref{sec:notes} and \S\ref{sec:transpose} for more information on the
@@ -658,9 +712,9 @@
 % |\indexesoff| macros.
 %
 % \DescEnv{nopdfindex}
-% The |nopdfindex| option suppresses the creation of the pdf bookmark index
-% that is normally included in |.pdf| files.
-% If not generating a |.pdf| file, this option has no effect.
+% PDF bookmark entries and hyperlinks can be suppressed with the |nopdfindex|
+% option.
+% For finer control of PDF indexes, see \S\ref{sec:idxcust}.
 %
 % \paragraph{Scripture Quotations.}
 % \DescEnv{noscripture}
@@ -683,9 +737,9 @@
 % Often it is useful to be able to extract a subset of songs from the master
 % document---e.g.~to create a handout or set of overhead slides for a specific
 % worship service.
-% To do this, you can type |\includeonlysongs{|\meta{songlist}|}| in the
+% To do this, you can type |\includeonlysongs{|\Meta{songlist}|}| in the
 % document preamble (i.e., before the |\begin{document}| line), where
-% \meta{songlist} is a comma-separated list of the song numbers to include.
+% \Meta{songlist} is a comma-separated list of the song numbers to include.
 % For example, 
 %
 % \begin{codeblock}
@@ -701,7 +755,7 @@
 % between songs unless they are followed by a star (e.g., \mac{nextcol}|*|).
 % To force a column- or page-break at a specific point in a partial book,
 % add the word |nextcol|, |brk|, |sclearpage|, or |scleardpage| at the
-% corresponding point in the \meta{songlist}.
+% corresponding point in the \Meta{songlist}.
 %
 % The |\includeonlysongs| macro only reorders songs within each
 % \env{songs} environment (see \S\ref{sec:songs}), not between different
@@ -708,135 +762,6 @@
 % \env{songs} environments.
 % It also cannot be used in conjunction with the \env{rawtext} option.
 %
-% \section{Book Sections}\label{sec:sections}
-%
-% \paragraph{Section Titles.}
-% \DescMacro{songsection}
-% \DescMacro{songchapter}
-% Section titles in a song book can be produced with
-%
-% \begin{codeblock}
-% |\songsection{|\meta{title}|}|
-% \end{codeblock}
-%
-% \noindent
-% which acts like \LaTeX's |\section| command except that it centers
-% the \meta{title} text in sans serif font and omits the section number.
-% When using the |book| document class, use |\songchapter|
-% instead of |\songsection|.
-%
-% \paragraph{Indexes.}
-% \DescMacro{newindex}
-% \DescMacro{newauthorindex}
-% \DescMacro{newscripindex}
-% The \Songs\ package supports three kinds of indexes: indexes by title and/or
-% notable lyrics, indexes by author, and indexes by scripture reference.
-% To generate an index, first declare the index in the document preamble
-% (i.e., before the |\begin{document}| line) with one of the following:
-%
-% \begin{codeblock}
-% |\newindex{|\meta{id}|}{|\meta{filename}|}|
-% |\newauthorindex{|\meta{id}|}{|\meta{filename}|}|
-% |\newscripindex{|\meta{id}|}{|\meta{filename}|}|
-% \end{codeblock}
-%
-% \noindent
-% The \meta{id} should be an alphabetic identifier that will be used to
-% identify the index in other macros that reference it.
-% The \meta{filename} should be a string that, when appended with an
-% extension, constitutes a valid filename on the system.
-% Auxiliary files named \meta{filename}|.sxd| and \meta{filename}|.sbx|
-% are generated during the automatic index generation process.
-% For example:
-%
-% \begin{codeblock}
-% |\newindex{mainindex}{idxfile}|
-% \end{codeblock}
-%
-% \noindent
-% creates a title index named ``|mainindex|'' whose data is
-% stored in files named |idxfile.sxd| and |idxfile.sbx|.
-%
-% \DescMacro{showindex}
-% To display the index in the document, use:
-%
-% \begin{codeblock}
-% |\showindex[|\meta{columns}|]{|\meta{title}|}{|\meta{id}|}|
-% \end{codeblock}
-%
-% \noindent
-% where \meta{id} is the same identifier used in the \mac{newindex},
-% \mac{newauthorindex}, or \mac{newscripindex} command, and where
-% the \meta{title} is the title of the index, which should consist only of
-% simple text (no font or formatting macros, since those cannot be used in
-% pdf bookmark indexes).
-% The |[|\meta{columns}|]| part is optional; if specified it dictates the
-% number of columns if the index can't fit in a single column.
-% For example, for a 2-column title index, write:
-%
-% \begin{codeblock}
-% |\showindex[2]{Index of Song Titles}{mainindex}|
-% \end{codeblock}
-%
-% \section{Compiling}\label{sec:compiling}
-%
-% As with a typical \LaTeX\ document, compiling a song book document requires
-% three steps.
-% First, use \LaTeX\ (|pdflatex| is recommended) to generate auxiliary files
-% from the |.tex| file:
-%
-% \begin{codeblock}
-% |pdflatex mybook.tex|
-% \end{codeblock}
-%
-% Second, use the |songidx| program to generate an index for each index that
-% you declared with \mac{newindex}, \mac{newauthorindex}, or
-% \mac{newscripindex}.
-% The syntax of the |songidx| command is:
-%
-% \begin{codeblock}
-% |songidx |[|-b| \meta{canon}|.can|] \meta{filename}|.sxd| \meta{filename}|.sbx|
-% \end{codeblock}
-%
-% \noindent
-% where \meta{filename} is the same \meta{filename} that was used in the
-% \mac{newindex}, \mac{newauthorindex}, or \mac{newscripindex} macro.
-% If the index was declared with \mac{newscripindex}, then the |-b| option
-% is used to specify which version of the bible you wish to use as a basis
-% for sorting your scripture index.
-% The \meta{canon} part can be any of the |.can| files provided with
-% the |songidx| distribution.
-% If you are using a Protestant, Catholic, or Greek Orthodox Christian bible
-% with book names in English, then the |bible.can| canon file should work
-% well.
-% For other bibles, you should create your own |.can| file by copying and
-% modifying one of the existing |.can| files.
-%
-% For example, if your song book |.tex| file contains the lines
-%
-% \begin{codeblock}
-% \mac{newindex}|{titleidx}{titlfile}|
-% \mac{newauthorindex}|{authidx}{authfile}|
-% \mac{newscripindex}|{scripidx}{scrpfile}|
-% \end{codeblock}
-%
-% \noindent
-% then the commands to generate indexes sorted according to a Christian
-% English bible are:
-%
-% \begin{codeblock}
-% |songidx titlfile.sxd titlfile.sbx|
-% |songidx authfile.sxd authfile.sbx|
-% |songidx -b bible.can scrpfile.sxd scrpfile.sbx|
-% \end{codeblock}
-%
-% Once the indexes are generated, generate the final book by invoking
-% \LaTeX\ one more time:
-%
-% \begin{codeblock}
-% |pdflatex mybook.tex|
-% \end{codeblock}
-%
 % \section{Songs}\label{sec:songs}
 %
 % \subsection{Beginning a Song}
@@ -847,19 +772,19 @@
 % Each |songs| environment begins and ends with:
 %
 % \begin{codeblock}
-% |\begin{songs}{|\meta{indexes}|}|
+% |\begin{songs}{|\Meta{indexes}|}|
 % $\vdots$
 % |\end{songs}|
 % \end{codeblock}
 %
 % \noindent
-% \meta{indexes} is a comma-separated list of index identifiers
-% (the \meta{id}'s specified with \mac{newindex})---one identifier
-% for each index that is to include songs in this song set.
+% \Meta{indexes} is a comma-separated list of index \Meta{id}'s
+% (see \S\ref{sec:indexes})---one identifier for each index that is to
+% include songs in this song set.
 % Between the |\begin{songs}| and |\end{songs}| lines of
-% a song section can appear only songs (see below)
-% or inter-song environments (see \S\ref{sec:between}).
-% No text in a |songs| environment may lie outside of these environments.
+% a song section only songs (see below) or inter-song environments
+% (see \S\ref{sec:between}) may appear.
+% No text in a |songs| environment may appear outside of these environments.
 %
 % \paragraph{Songs.}
 % \DescMacro{beginsong}
@@ -867,7 +792,7 @@
 % A song begins and ends with:
 %
 % \begin{codeblock}
-% |\beginsong{|\meta{titles}|}[|\meta{otherinfo}|]|
+% |\beginsong{|\Meta{titles}|}[|\Meta{otherinfo}|]|
 % $\vdots$
 % |\endsong|
 % \end{codeblock}
@@ -876,25 +801,25 @@
 % Songs should appear only within \env{songs} environments (see above)
 % unless you are supplying your own page-builder (see \S\ref{sec:layout}).
 %
-% In the \mac{beginsong} line, \meta{titles} is one or more song titles
+% In the \mac{beginsong} line, \Meta{titles} is one or more song titles
 % separated by |\\|.
 % If multiple titles are provided, the first is typeset normally
 % atop the song and the rest are each typeset in parentheses on
 % separate lines.
 %
-% The |[|\meta{otherinfo}|]| part is an optional comma-separated list of
-% key-value pairs (keyvals) of the form \meta{key}|=|\meta{value}.
+% The |[|\Meta{otherinfo}|]| part is an optional comma-separated list of
+% key-value pairs (keyvals) of the form \Meta{key}|=|\Meta{value}.
 % The possible keys and their values are:
 %
 % \medskip
 % \noindent\hfil\vbox{\halign{#\hfil&\kern2em{\it#}\hfil\cr
-%   |by={|\meta{authors}|}|&authors, composers, and other contributors\cr
-%   |cr={|\meta{copyright}|}|&copyright information\cr
-%   |li={|\meta{license}|}|&licensing information\cr
-%   |sr={|\meta{refs}|}|&related scripture references\cr
-%   |index={|\meta{lyrics}|}|&an extra index entry for a line of lyrics\cr
-%   |ititle={|\meta{title}|}|&an extra index entry for a hidden title\cr}}
-% \medskip
+%   |by={|\Meta{authors}|}|&authors, composers, and other contributors\cr
+%   |cr={|\Meta{copyright}|}|&copyright information\cr
+%   |li={|\Meta{license}|}|&licensing information\cr
+%   |sr={|\Meta{refs}|}|&related scripture references\cr
+%   |index={|\Meta{lyrics}|}|&an extra index entry for a line of lyrics\cr
+%   |ititle={|\Meta{title}|}|&an extra index entry for a hidden title\cr}}
+% {\pfs\par}\medskip
 %
 % \noindent
 % For example, a song that begins and ends with
@@ -911,7 +836,7 @@
 %  \setcounter{songnum}{1}%
 %  \vskip1pt%
 %  \beginsong{Title1 \\ Title2}[by={Joe Smith}, sr={Job 3},
-%     cr={\copyright~\the\year\ XYZ.}, li={Used with permission.}]
+%     cr={\copyright~\the\year{} XYZ.}, li={Used with permission.}]
 %  \endsong
 % \end{sample}
 %
@@ -922,7 +847,7 @@
 %
 % \paragraph{Song Authors.}
 % \DescEnv{by=}
-% The |by={|\meta{authors}|}| keyval lists one or more authors,
+% The |by={|\Meta{authors}|}| keyval lists one or more authors,
 % composers, translators, etc.
 % An entry is added to each author index associated with the current
 % \env{songs} environment for each contributor listed.
@@ -944,7 +869,7 @@
 %
 % \paragraph{Copyright Info.}
 % \DescEnv{cr=}
-% The |cr={|\meta{copyright}|}| keyval specifies the copyright-holder of the
+% The |cr={|\Meta{copyright}|}| keyval specifies the copyright-holder of the
 % song, if any.
 % For example:
 %
@@ -958,13 +883,13 @@
 % \paragraph{Licensing Info.}
 % \DescEnv{li=}
 % \DescMacro{setlicense}
-% Licensing information is provided by |li={|\meta{license}|}|, where
-% \meta{license} is any text.
+% Licensing information is provided by |li={|\Meta{license}|}|, where
+% \Meta{license} is any text.
 % Licensing information is displayed in fine print under the song just
 % after the copyright information (if any).
-% Alternatively, writing |\setlicense{|\meta{license}|}| anywhere between
+% Alternatively, writing |\setlicense{|\Meta{license}|}| anywhere between
 % the \mac{beginsong} and \mac{endsong} lines is equivalent to using
-% |li={|\meta{license}|}| in the \mac{beginsong} line.
+% |li={|\Meta{license}|}| in the \mac{beginsong} line.
 %
 % When many songs in a book are covered by a common license, it is
 % usually convenient to create a macro to abbreviate the licensing
@@ -983,17 +908,17 @@
 %
 % \paragraph{Scripture References.}
 % \DescEnv{sr=}
-% The \Songs\ package has extensive support for scripture citations and
+% The \Songs{} package has extensive support for scripture citations and
 % indexes of scripture citations.
 % To cite scripture references for the song, use the keyval
-% |sr={|\meta{refs}|}|, where \meta{refs} is a list of scripture
+% |sr={|\Meta{refs}|}|, where \Meta{refs} is a list of scripture
 % references.
 % Index entries are added to all scripture indexes associated
 % with the current \env{songs} environment for each such reference.
-% The |songidx| index generation program expects \meta{refs} to be a list
-% of references in which semicolons are used to separate references to
-% different books, and commas are used to separate references to to
-% different chapters and verses within the same book.
+% The |songidx| index generation script (see \S\ref{sec:compiling}) expects
+% \Meta{refs} to be a list of references in which semicolons are used to
+% separate references to different books, and commas are used to separate
+% references to to different chapters and verses within the same book.
 % For example, one valid scripture citation is
 %
 % \begin{codeblock}
@@ -1000,28 +925,28 @@
 % |sr={John 3:16,17, 4:1-5; Jude 3}|
 % \end{codeblock}
 %
-% The full formal syntax of a valid \meta{refs} argument is given in
+% The full formal syntax of a valid \Meta{refs} argument is given in
 % Figure~\ref{fig:srsyntax}.
 % \begin{figure}
 % \noindent\hfil\vbox{\advance\baselineskip2pt
 % \halign{\hfil{\tt#}\,$\longrightarrow$\,&{\tt#}\hfil\cr
-%   \Meta{refs}&\Meta{nothing}\OR\Meta{ref};\SPC\Meta{ref};$\ldots$;\SPC\Meta{ref}\cr
-%   \Meta{ref}&\Meta{many-chptr-book}\SPC\Meta{chapters}\OR\Meta{one-chptr-book}\SPC\Meta{verses}\cr
-%   \Meta{many-chptr-book}&Genesis\OR Exodus\OR Leviticus\OR Numbers\OR $\ldots$\cr
-%   \Meta{one-chptr-book}&Obadiah\OR Philemon\OR 2 John\OR 3 John\OR Jude\cr
-%   \Meta{chapters}&\Meta{chref},\SPC\Meta{chref},$\ldots$,\SPC\Meta{chref}\cr
-%   \Meta{chref}&\Meta{chapter}\OR\Meta{chapter}-\Meta{chapter}\OR\Meta{chapter}:\Meta{verses}\OR\cr
-%   \omit&\quad\Meta{chapter}:\Meta{verse}-\Meta{chapter}:\Meta{verse}\cr
-%   \Meta{verses}&\Meta{vref},\Meta{vref},$\ldots$,\Meta{vref}\cr
-%   \Meta{vref}&\Meta{verse}\OR\Meta{verse}-\Meta{verse}\cr}}
+%   \Metarm{refs}&\Metarm{nothing}\OR\Metarm{ref};\SPC\Metarm{ref};$\ldots$;\SPC\Metarm{ref}\cr
+%   \Metarm{ref}&\Metarm{many-chptr-book}\SPC\Metarm{chapters}\OR\Metarm{one-chptr-book}\SPC\Metarm{verses}\cr
+%   \Metarm{many-chptr-book}&Genesis\OR Exodus\OR Leviticus\OR Numbers\OR $\ldots$\cr
+%   \Metarm{one-chptr-book}&Obadiah\OR Philemon\OR 2 John\OR 3 John\OR Jude\cr
+%   \Metarm{chapters}&\Metarm{chref},\SPC\Metarm{chref},$\ldots$,\SPC\Metarm{chref}\cr
+%   \Metarm{chref}&\Metarm{chapter}\OR\Metarm{chapter}-\Metarm{chapter}\OR\Metarm{chapter}:\Metarm{verses}\OR\cr
+%   \omit&\quad\Metarm{chapter}:\Metarm{verse}-\Metarm{chapter}:\Metarm{verse}\cr
+%   \Metarm{verses}&\Metarm{vref},\Metarm{vref},$\ldots$,\Metarm{vref}\cr
+%   \Metarm{vref}&\Metarm{verse}\OR\Metarm{verse}-\Metarm{verse}\cr}}
 % \caption{Formal syntax rules for song scripture references}\label{fig:srsyntax}
 % \end{figure}
-% In those syntax rules, \meta{chapter} and \meta{verse} stand for arabic
+% In those syntax rules, \Meta{chapter} and \Meta{verse} stand for arabic
 % numbers denoting a valid chapter number for the given book, and a valid
 % verse number for the given chapter, respectively.
 % Note that when referencing a book that has only one chapter,
 % one should list only its verses after the book name
-% (rather than |1:|\meta{verses}).
+% (rather than |1:|\Meta{verses}).
 %
 % \subsection{Verses and Choruses}
 %
@@ -1066,53 +991,6 @@
 % Lines that are too long to fit are wrapped with hanging indentation
 % of width |\parindent|.
 %
-% \paragraph{Repeating Choruses.}
-% \DescMacro{repchoruses}
-% When making overhead slides, it is often convenient to repeat the song's
-% chorus after the first verse on each page, so that the projector-operator
-% need not flip back to the first slide each time the chorus is to be sung.
-% You can say |\repchoruses| to automate this process.
-% This causes the first chorus in each song to be automatically repeated
-% after the first verse on each subsequent page of the song (unless that
-% verse is already immediately followed by a chorus).
-% If the first chorus is part of a set of two or more consecutive choruses,
-% then the whole set of choruses is repeated.
-% (A set of choruses is assumed to consist of things like pre-choruses that
-% should always be repeated along with the chorus.)
-% Choruses are not automatically inserted immediately after unnumbered
-% verses (i.e., verses that begin with \mac{beginverse}|*|).
-% Unnumbered verses are assumed to be bridges or endings that aren't
-% followed by a chorus.
-%
-% \DescMacro{norepchoruses}
-% The above covers the common cases, but some songs have more complex forms
-% that demand a manual approach.
-% Before a song with irregular form, say |\norepchoruses| to turn automatic
-% chorus-repeating off.
-% Then, at points within the song where you want a chorus to be repeated on
-% the overhead slides, type a construction like,
-%
-% \begin{codeblock}
-% \mac{ifslides}
-% \mac{beginchorus}
-% $\vdots$
-% \mac{endchorus}
-% |\fi|
-% \end{codeblock}
-%
-% \noindent
-% and copy and paste the desired chorus into the middle.
-% This inserts a repeated chorus at that point when generating slides,
-% but not when generating a lyric book or chord book.
-% After the song is concluded, type
-%
-% \begin{codeblock}
-% \mac{ifslides}\mac{repchoruses}|\fi|
-% \end{codeblock}
-%
-% \noindent
-% to turn automatic chorus-repeating back on, if desired.
-%
 % \subsection{Chords}\label{sec:chords}
 %
 % \DescMacro{[}
@@ -1120,9 +998,9 @@
 % \DescChar{amp}{&}
 % Between the \mac{beginverse} and \mac{endverse} lines, or between
 % the \mac{beginchorus} and \mac{endchorus} lines,
-% chords can be produced using the macro |\[|\meta{chordname}|]|\eat\].
+% chords can be produced using the macro |\[|\Meta{chordname}|]|\eat\].
 % Chords only appear in chord books; they are omitted from lyric books.
-% The \meta{chordname} may consist of arbitrary text.
+% The \Meta{chordname} may consist of arbitrary text.
 % To produce sharp and flat symbols, use |#| and |&| respectively.
 %
 % Any text that immediately follows the |\[]|\eat\] macro with no
@@ -1135,7 +1013,7 @@
 % \eat\]
 %
 % \noindent
-% If whitespace (a space or \meta{return}) immediately follows,
+% If whitespace (a space or \Meta{return}) immediately follows,
 % then the chord name be typeset without any lyric text
 % below it, indicating that the chord is to be struck between
 % any surrounding words.
@@ -1215,19 +1093,19 @@
 % \paragraph{Symbols Under Chords.}
 % \DescMacro{DeclareLyricChar}
 % If you are typesetting songs in a language whose alphabet contains symbols
-% that \LaTeX\ treats as punctuation, you can use the |\DeclareLyricChar|
-% macro to instruct the \Songs\ package to treat the symbol as
+% that \LaTeX{} treats as punctuation, you can use the |\DeclareLyricChar|
+% macro to instruct the \Songs{} package to treat the symbol as
 % non-chord-ending, so that it is included under chords by default just
 % like an alphabetic character.
 %
 % \begin{codeblock}
-% |\DeclareLyricChar{|\meta{token}|}|
+% |\DeclareLyricChar{|\Meta{token}|}|
 % \end{codeblock}
 %
 % \noindent
-% Here, \meta{token} must be a single \TeX\ macro control sequence,
-% active character, letter (something \TeX\ assigns catcode 11), or
-% punctuation symbol (something \TeX\ assigns catcode 12).
+% Here, \Meta{token} must be a single \TeX{} macro control sequence,
+% active character, letter (something \TeX{} assigns catcode 11), or
+% punctuation symbol (something \TeX{} assigns catcode 12).
 % For example, by default,
 %
 % \example|\[Fmaj7]s\dag range|\produces{\[Fmaj7]s\dag range}
@@ -1253,7 +1131,7 @@
 % Likewise, you can type
 %
 % \begin{codeblock}
-% |\DeclareNonLyric{|\meta{token}|}|
+% |\DeclareNonLyric{|\Meta{token}|}|
 % \end{codeblock}
 %
 % \noindent
@@ -1263,7 +1141,7 @@
 %
 % \DescMacro{DeclareNoHyphen}
 % To declare a token to be lyric-ending but without the added hyphenation,
-% use |\DeclareNoHyphen{|\meta{token}|}| instead.
+% use |\DeclareNoHyphen{|\Meta{token}|}| instead.
 % Such tokens are pushed out away from long chord names so that they never
 % fall under the chord, but hyphenation is not added to the resulting gap.
 %
@@ -1315,11 +1193,11 @@
 % |\newcommand{\Csharp}{C\shrp}|
 % \end{codeblock}
 %
-% \subsection{Replaying Chords}\label{sec:replay}
+% \subsection{Replaying Chords and Choruses}\label{sec:replay}
 %
 % \DescChar{hat}{^}
 % Many songs consist of multiple verses that use the same chords.
-% The \Songs\ package simplifies this common case by providing a means to
+% The \Songs{} package simplifies this common case by providing a means to
 % replay the chord sequence of a previous verse without having to retype
 % all the chords.
 % To replay a chord from a previous verse, type a hat symbol (|^|) anywhere
@@ -1402,7 +1280,7 @@
 % other chords that you wish to replay.
 %
 % \paragraph{Memorizing Multiple Chord Sequences.}
-% By default, the \Songs\ package only memorizes one sequence of chords
+% By default, the \Songs{} package only memorizes one sequence of chords
 % at a time and \refchar{hat} replays chords from that most recently
 % memorized sequence.
 % However, you can memorize and replay multiple independent sequences
@@ -1413,18 +1291,18 @@
 % To declare a new chord-replay register, type
 %
 % \begin{codeblock}
-% |\newchords{|\meta{regname}|}|
+% |\newchords{|\Meta{regname}|}|
 % \end{codeblock}
 %
 % \noindent
-% where \meta{regname} is any unique alphabetic name.
+% where \Meta{regname} is any unique alphabetic name.
 %
 % Once you've declared a register, you can memorize into that register
-% by providing the \meta{regname} as an optional argument to
+% by providing the \Meta{regname} as an optional argument to
 % \mac{memorize}:
 %
 % \begin{codeblock}
-% \mac{memorize}|[|\meta{regname}|]|
+% \mac{memorize}|[|\Meta{regname}|]|
 % \end{codeblock}
 %
 % \noindent
@@ -1432,15 +1310,15 @@
 % register with the new chord sequence.
 %
 % \DescMacro{replay}
-% To replay chord from a particular register, type
+% To replay chords from a particular register, type
 %
 % \begin{codeblock}
-% |\replay[|\meta{regname}|]|
+% |\replay[|\Meta{regname}|]|
 % \end{codeblock}
 %
 % \noindent
 % Subsequent uses of \refchar{hat} reproduce chords from the sequence
-% stored in register \meta{regname}.
+% stored in register \Meta{regname}.
 %
 % Register contents are global, so you can memorize a chord sequence from one
 % song and replay it in others.
@@ -1447,6 +1325,45 @@
 % You can also use |\replay| multiple times in the same verse or chorus to
 % replay a sequence more than once.
 %
+% \paragraph{Replaying Choruses.}
+% \DescMacro{repchoruses}
+% When making overhead slides, it is often convenient to repeat the song's
+% chorus after the first verse on each page, so that the projector-operator
+% need not flip back to the first slide each time the chorus is to be sung.
+% You can say |\repchoruses| to automate this process.
+% This causes the first chorus in each subsequent song to be automatically
+% repeated after the first verse on each subsequent page of the song (unless
+% that verse is already immediately followed by a chorus).
+% If the first chorus is part of a set of two or more consecutive choruses,
+% then the whole set of choruses is repeated.
+% (A set of choruses is assumed to consist of things like pre-choruses that
+% should always be repeated along with the chorus.)
+% Choruses are not automatically inserted immediately after unnumbered
+% verses (i.e., verses that begin with \mac{beginverse}|*|).
+% Unnumbered verses are assumed to be bridges or endings that aren't
+% followed by a chorus.
+%
+% \DescMacro{norepchoruses}
+% Writing |\norepchoruses| turns off chorus repetition for subsequent songs.
+%
+% If you need finer control over where replayed choruses appear, use the
+% conditional macros covered in \S\ref{sec:conditionals} instead of
+% |\repchoruses|.
+% For example, to manually insert a chorus into only slide books at a
+% particular point (without affecting other versions of your book),
+% you could write:
+%
+% \begin{codeblock}
+% \mac{ifslides}
+% \mac{beginchorus}
+% $\vdots$
+% \mac{endchorus}
+% |\fi|
+% \end{codeblock}
+%
+% \noindent
+% and copy and paste the desired chorus into the middle.
+%
 % \subsection{Line and Column Breaks}
 %
 % \paragraph{Line Breaking.}
@@ -1517,7 +1434,7 @@
 %
 % \paragraph{Echo Parts.}
 % \DescMacro{echo}
-% To typeset an echo part, use |\echo{|\meta{lyrics and chords}|}|.
+% To typeset an echo part, use |\echo{|\Meta{lyrics and chords}|}|.
 % Echo parts are parenthesized and italicized.
 % For example,
 %
@@ -1527,7 +1444,7 @@
 % \paragraph{Repeated Lines.}
 % \DescMacro{rep}
 % To indicate that a line should be sung multiple times by all singers, put
-% |\rep{|\meta{n}|}| at the end of the line.
+% |\rep{|\Meta{n}|}| at the end of the line.
 % For example,
 %
 % \example|Alleluia! \rep{4}|\produces{Alleluia! \rep{4}}
@@ -1564,15 +1481,15 @@
 % it to indicate the time signature of the piece.
 % By default, these numbers are 4/4, denoting four quarter notes
 % per measure.
-% To change the default, type |\meter{|\meta{n}|}{|\meta{d}|}|
+% To change the default, type |\meter{|\Meta{n}|}{|\Meta{d}|}|
 % somewhere after the \mac{beginsong} line of the song but before the
-% first measure bar, to declare a time signature of \meta{n} \meta{d}th
+% first measure bar, to declare a time signature of \Meta{n} \Meta{d}th
 % notes per measure.
 %
 % \DescMacro{mbar}
 % You can also change meters mid-song either by using |\meter| in the
-% middle of the song or by typing |\mbar{|\meta{n}|}{|\meta{d}|}|
-% to produce a measure bar with a time signature of \meta{n}/\meta{d}.
+% middle of the song or by typing |\mbar{|\Meta{n}|}{|\Meta{d}|}|
+% to produce a measure bar with a time signature of \Meta{n}/\Meta{d}.
 % For example,
 %
 % \begin{codeblock}
@@ -1599,7 +1516,7 @@
 % and chord books, use:
 %
 % \begin{codeblock}
-% |\textnote{|\meta{text}|}|
+% |\textnote{|\Meta{text}|}|
 % \end{codeblock}
 %
 % \noindent
@@ -1606,11 +1523,11 @@
 % To create a textual note that is displayed only in chord books, use:
 %
 % \begin{codeblock}
-% |\musicnote{|\meta{text}|}|
+% |\musicnote{|\Meta{text}|}|
 % \end{codeblock}
 %
 % \noindent
-% Both of these create a shaded box containing \meta{text}.
+% Both of these create a shaded box containing \Meta{text}.
 % For example,
 %
 % \begin{codeblock}
@@ -1631,71 +1548,13 @@
 % \DescMacro{capo}
 % One special kind of textual note suggests to guitarists a fret on which
 % they should put their capos.
-% Macro |\capo{|\meta{n}|}| should be used for this purpose.
-% It normally has the same effect as \mac{musicnote}|{capo |\meta{n}|}|;
+% Macro |\capo{|\Meta{n}|}| should be used for this purpose.
+% It normally has the same effect as \mac{musicnote}|{capo |\Meta{n}|}|;
 % however, if the \env{transposecapos} option is active then it
-% instead has the effect of \mac{transpose}|{|\meta{n}|}|.
+% instead has the effect of \mac{transpose}|{|\Meta{n}|}|.
 % See \S\ref{sec:transpose} for more information on automatic chord
 % transposition.
 %
-% \subsection{Index Entries}\label{sec:ientry}
-%
-% Every song automatically gets entries in the current section's title
-% index(es).
-% However, you can also add extra index entries for a song to any index.
-%
-% \paragraph{Indexing Lyrics.}
-% \DescEnv{index=}
-% For example, title indexes often have entries for memorable lines
-% of lyrics in a song in addition to the song's title.
-% You can add an index entry for the current song to the section's
-% title index(es) by adding |index={|\meta{lyrics}|}| to the song's
-% \mac{beginsong} line.
-% For example,
-%
-% \begin{codeblock}
-% \mac{beginsong}|{Doxology}|
-% |          [index={Praise God from Whom all blessings flow}]|
-% \end{codeblock}
-%
-% \noindent
-% causes the song to be indexed both as ``\textit{Doxology}'' and as
-% ``Praise God from Whom all blessings flow'' in the section's title index(es).
-% You can use |index=| multiple times in a \mac{beginsong} line to produce
-% multiple additional index entries.
-% Index entries produced with |index={|\meta{lyrics}|}| are
-% typeset in an upright font instead of in italics to distinguish
-% them from song titles.
-%
-% \paragraph{Indexing Extra Song Titles.}
-% \DescEnv{ititle=}
-% To add a regular index entry typeset in italics to the title
-% index(es), use:
-%
-% \begin{codeblock}
-% |ititle={|\meta{title}|}|
-% \end{codeblock}
-%
-% \noindent
-% in the \mac{beginsong} line instead.
-% Like \env{index=} keyvals, |ititle=| can be used multiple times to produce
-% multiple additional index entries.
-%
-% \DescMacro{indexentry}
-% \DescMacro{indextitleentry}
-% You can also create index entries by saying
-% |\indexentry[|\meta{indexes}|]{|\meta{lyrics}|}| (which creates an
-% entry like \env{index=}) or
-% |\indextitleentry[|\meta{indexes}|]{|\meta{title}|}| (which
-% creates an entry like \env{ititle=}).
-% These two macros can be used anywhere between the song's \mac{beginsong}
-% and \mac{endsong} lines, and can be used multiple times to produce
-% multiple entries.
-% If specified, \meta{indexes} is a comma-separated list of the identifiers
-% of indexes to which the entry should be added.
-% Otherwise the new entry is added to all of the title indexes for the current
-% \env{songs} environment.
-%
 % \subsection{Chords in Ligatures}
 %
 % This subsection covers an advanced topic and can probably be
@@ -1704,30 +1563,30 @@
 % The \mac{[\eat]} macro is the normal means by which chords should be inserted
 % into a song; however, a special case occurs when a chord falls within a
 % ligature.
-% Ligatures are combinations of letters or symbols that \TeX\ normally
+% Ligatures are combinations of letters or symbols that \TeX{} normally
 % typesets as a single font character so as to produce cleaner-looking
 % output.
 % The only ligatures in English are: ff, fi, fl, ffi, and ffl.
-% Other languages have additional ligatures like \ae\ and \oe.
+% Other languages have additional ligatures like \ae{} and \oe.
 % Notice that in each of these cases, the letters are ``squished''
 % together to form a single composite symbol.
 %
 % \DescMacro{ch}
-% When a chord macro falls inside a ligature, \LaTeX\ fails to compact
+% When a chord macro falls inside a ligature, \LaTeX{} fails to compact
 % the ligature into a single font character even in non-chorded versions of
 % the book.
-% To avoid this minor typograhpical error, use the |\ch| macro to typeset
+% To avoid this minor typographical error, use the |\ch| macro to typeset
 % the chord:
 %
 % \begin{codeblock}
-% |\ch{|\meta{chord}|}{|\meta{pre}|}{|\meta{post}|}{|\meta{full}|}|
+% |\ch{|\Meta{chord}|}{|\Meta{pre}|}{|\Meta{post}|}{|\Meta{full}|}|
 % \end{codeblock}
 %
 % \noindent
-% where \meta{chord} is the chord text, \meta{pre} is the text to
+% where \Meta{chord} is the chord text, \Meta{pre} is the text to
 % appear before the hyphen if the ligature is broken by auto-hyphenation,
-% \meta{post} is the text to appear after the hyphen if the ligature
-% is broken by auto-hyphenation, and \meta{full} is the full ligature
+% \Meta{post} is the text to appear after the hyphen if the ligature
+% is broken by auto-hyphenation, and \Meta{full} is the full ligature
 % if it is not broken by hyphenation.
 % For example, to correctly typeset |\[Gsus4]dif\[G]ficult|\eat\],
 % in which the \chord{G} chord falls in the middle of the ``ffi''
@@ -1739,7 +1598,7 @@
 % This causes the ``ffi'' ligature to appear intact yet still correctly
 % places the \chord{G} chord over the second f.
 % To use the |\ch| macro with a replayed chord name (see \S\ref{sec:replay}),
-% use |^| as the \meta{chord}.
+% use |^| as the \Meta{chord}.
 %
 % \DescMacro{mch}
 % The |\mch| macro is exactly like the \mac{ch} macro except that it
@@ -1769,6 +1628,267 @@
 % no special treatment; it leaves the ``ffi'' ligature intact when measure
 % bars are not being displayed.
 %
+% \section{Guitar Tablatures}\label{sec:tablatures}
+%
+% \DescMacro{gtab}
+% Guitar tablature diagrams can be created by using the construct
+%
+% \begin{codeblock}
+% |\gtab{|\Meta{chord}|}{|\Meta{fret}|:|\Meta{strings}|:|\Meta{fingering}|}|
+% \end{codeblock}
+%
+% \noindent
+% where the \Meta{fret} and \Meta{fingering} parts are both optional
+% (and you may omit any colon that borders an omitted argument).
+%
+% \Meta{chord} is a chord name to be placed above the diagram.
+%
+% \Meta{fret} is an optional digit from 2 to 9 placed to the
+% left of the diagram.
+%
+% \Meta{strings} should be a series of symbols, one for each string
+% of the guitar from lowest pitch to highest.
+% Each symbol should be one of:
+% |X| if that string is not to be played,
+% |0| (zero or the letter O) if that string is to be played open, or
+% one of |1| through |9| if that string is to be played on the given
+% numbered fret.
+%
+% \Meta{fingering} is an optional series of digits, one for each
+% string of the guitar from lowest pitch to highest.
+% Each digit should be one of:
+% |0| if no fingering information is to be displayed for that string (e.g., if
+% the string is not being played or is being played open), or
+% one of |1| through |4| to indicate that the given numbered finger is to be
+% used to hold down that string.
+%
+% Here are some examples to illustrate:
+%
+% \example|\gtab{A}{X02220:001230}|\produces{\vcenterbox{\gtab{A}{{\hphantom{4}}:X02220:001230}}}
+% \example|\gtab{C#sus4}{4:XX3341}|\produces{\vcenterbox{\gtab{C\shrp sus4}{4:XX3341}}}
+% \example|\gtab{B&}{X13331}|\produces{\vcenterbox{\gtab{B\flt}{{\hphantom{4}}:X13331:}}}
+%
+% \DescMacro{minfrets}
+% By default, tablature diagrams always consist of at least 4 fret rows
+% (more if the \Meta{strings} argument contains a number larger than 4).
+% To change the minimum number of fret rows, change the value of |\minfrets|.
+% For example, typing
+%
+% \begin{codeblock}
+% |\minfrets=1|
+% \end{codeblock}
+%
+% \noindent
+% causes tablature diagrams to have only as many rows are required to
+% accommodate the largest digit appearing in the \Meta{strings} argument.
+%
+% \section{Automatic Transposition}\label{sec:transpose}
+%
+% \DescMacro{transpose}
+% You can automatically transpose some or all of the chords in a song up by
+% \Meta{n} half-steps by adding the line
+%
+% \begin{codeblock}
+% |\transpose{|\Meta{n}|}|
+% \end{codeblock}
+%
+% \noindent
+% somewhere between the song's \mac{beginsong} line and the first chord to
+% be transposed.
+% For example, if a song's first chord is |\[D]|\eat\], and the line
+% |\transpose{2}| appears before it, then the chord appears as an
+% \chord{E} in the resulting document.
+% Specifying a negative number for \Meta{n} transposes subsequent chords
+% down instead of up.
+%
+% The |\transpose| macro affects all chords appearing after it until the
+% \mac{endsong} line.
+% If two |\transpose| macros appear in the same song, their effects are
+% cumulative.
+%
+% When the \env{transposecapos} option is active, the \mac{capo}
+% macro acts like |\transpose|.
+% See \S\ref{sec:notes} for more information.
+%
+% \paragraph{Enharmonics.}
+% \DescMacro{preferflats}
+% \DescMacro{prefersharps}
+% When using \mac{transpose} to automatically transpose the chords of a song,
+% the \Songs{} package code chooses between enharmonically equivalent
+% names for ``black key'' notes based on the first chord of the song.
+% For example, if |\transpose{1}| is used, and if the first chord of the
+% song is an \chord{E}, then all \chord{A} chords that appear in
+% the song are transcribed as \chord{B\flt} chords rather than
+% \chord{A\shrp} chords, since the key of \chord{F}-major (\chord{E}
+% transposed up by one half-step) has a flatted key signature.
+% Usually this guess produces correct results, but if not, you can use
+% either |\preferflats| or |\prefersharps| after the \mac{transpose} line
+% to force all transcription to use flatted names or sharped names
+% respectively, when resolving enharmonic equivalents.
+%
+% \paragraph{Modulated Verses.}
+% Automatic transposition can be used in conjunction with chord-replaying
+% (see \S\ref{sec:chords}) to produce modulated verses.
+% For example,
+%
+% \begin{codeblock}
+% \mac{beginverse}\mac{memorize}
+% |\[F#]This is a \[B/F#]memorized \[F#]verse. \[E&7]|\eat\]
+% \mac{endverse}
+% \mac{transpose}|{2}|
+% \mac{beginverse}
+% |^This verse is ^modulated up two ^half-steps.|
+% \mac{endverse}
+% \end{codeblock}
+%
+% \noindent produces
+%
+% \begin{chorded}\memorize
+%   \[F#]This is a \[B/F#]memorized \[F#]verse. \[E&7]\eat\]
+%   \vskip5pt\replay\transpose{2}%
+%   ^This verse is ^modulated up two ^half-steps.
+%   \transpose{-2}%
+% \end{chorded}
+%
+% \paragraph{Both Keys.}
+% \DescMacro{trchordformat}
+% By default, when chords are automatically transposed using \mac{transpose},
+% only the transposed chords are printed.
+% However, in some cases you may wish to print the old chords and the
+% transposed chords together so that musicians playing transposing and
+% non-transposing instruments can play from the same piece of music.
+% This can be achieved by redefining the |\trchordformat| macro, which
+% receives two arguments---the original chord name and the transposed chord
+% name, respectively.
+% For example, to print the old chord above the new chord above each lyric,
+% define
+%
+% \begin{codeblock}
+% |\renewcommand{\trchordformat}[2]{\vbox{\hbox{#1}\hbox{#2}}}|
+% \end{codeblock}
+%
+% \paragraph{Changing Note Names.}
+% \DescMacro{solfedge}
+% \DescMacro{alphascale}
+% In many countries it is common to use the solfedge names for the notes of
+% the scale (\chord{LA, SI, DO, RE, MI, FA, SOL\/}) instead of the
+% alphabetic names (\chord{A, B, C, D, E, F, G\/}).
+% By default, the transposition logic only understands alphabetic names, but
+% you can tell it to look for solfedge names by typing |\solfedge|.
+% To return to alphabetic names, type |\alphascale|.
+%
+% \DescMacro{notenames}
+% You can use other note names as well.
+% To define your own note names, type
+%
+% \begin{codeblock}
+% |\notenames{|\Meta{nameA}|}{|\Meta{nameB}|}|$\ldots$|{|\Meta{nameG}|}|
+% \end{codeblock}
+%
+% \noindent
+% where each of \Meta{nameA} through \Meta{nameG} must consist entirely of
+% a sequence of one or more \emph{uppercase} letters.
+% For example, some solfedge musicians use \chord{TI} instead of \chord{SI}
+% for the second note of the scale.
+% To automatically transpose such music, use:
+%
+% \begin{codeblock}
+% |\notenames{LA}{TI}{DO}{RE}{MI}{FA}{SOL}|
+% \end{codeblock}
+%
+% \DescMacro{notenamesin}
+% \DescMacro{notenamesout}
+% The \Songs{} package can also automatically convert one set of note names
+% to another.
+% For example, suppose you have a large song book in which chords have been
+% typed using alphabetic note names, but you wish to produce a book that
+% uses the equivalent solfedge names.
+% You could achieve this by using the |\notenamesin| macro to tell the
+% \Songs{} package which note names you typed in the input file, and then
+% using |\notenamesout| to tell the \Songs{} package how you want it to
+% typeset each note name in the output file.
+% The final code looks like this:
+%
+% \begin{codeblock}
+% |\notenamesin{A}{B}{C}{D}{E}{F}{G}|
+% |\notenamesout{LA}{SI}{DO}{RE}{MI}{FA}{SOL}|
+% \end{codeblock}
+%
+% \noindent
+% The syntaxes of |\notenamesin| and |\notenamesout| are identical to that
+% of \mac{notenames} (see above), except that the arguments of |\notenamesout|
+% can consist of any \LaTeX{} code that is legal in horizontal mode, not just
+% uppercase letters.
+%
+% To stop converting between note names, use \mac{alphascale}, \mac{solfedge},
+% or \mac{notenames} to reset all note names back to identical input and
+% output scales.
+%
+% \paragraph{Transposing Chords In Macros.}
+% \DescMacro{transposehere}
+% The automatic transposition logic does not find chord names that are hidden
+% inside macro bodies.
+% For example, if you abbreviate a chord by typing,
+%
+% \begin{codeblock}
+% |\newcommand{\mychord}{F|\mac{shrp}| sus4/C|\mac{shrp}|}|
+% \mac{transpose}|{4}|
+% |\[\mychord]|\eat\]
+% \end{codeblock}
+%
+% \noindent
+% then the \mac{transpose} macro fails to transpose it; the
+% resulting chord is still an \chord{F\shrp sus4/C\shrp} chord.
+% To fix the problem, you can use |\transposehere| in your macros to
+% explicitly invoke the transposition logic on chord names embedded in
+% macro bodies.
+% The above example could be corrected by instead defining:
+%
+% \begin{codeblock}
+% |\newcommand{\mychord}{\transposehere{F|\mac{shrp}| sus4/C|\mac{shrp}|}}|
+% \end{codeblock}
+%
+% \DescMacro{notrans}
+% Transposition can be suppressed within material that would otherwise be
+% transposed by using the |\notrans| macro.
+% For example, writing
+%
+% \begin{codeblock}
+% \mac{transposehere}|{G = \notrans{G}}|
+% \end{codeblock}
+%
+% \noindent
+% would typeset a transposed \chord{G} followed by a non-transposed
+% \chord{G} chord.
+% This does not suppress note name conversion (see \mac{notenames}).
+% To suppress both transposition and note name conversion, just use
+% braces (e.g., |{G}| instead of |\notrans{G}|).
+%
+% \paragraph{Transposing Guitar Tablatures.}
+% \DescMacro{gtabtrans}
+% The songs package cannot automatically transpose tablature diagrams
+% (see \S\ref{sec:tablatures}).
+% Therefore, when automatic transposition is taking place, only the chord
+% names of \mac{gtab} macros are displayed (and transposed); the diagrams
+% are omitted.
+% To change this default, redefine the |\gtabtrans| macro, whose two
+% arguments are the two arguments to \mac{gtab}.
+% For example, to display original tablatures without transposing them even
+% when transposition has been turned on, write
+%
+% \begin{codeblock}
+% |\renewcommand{\gtabtrans}[2]{|\mac{gtab}|{|\mac{notrans}|{#1}}{#2}}|
+% \end{codeblock}
+%
+% \noindent
+% To transpose the chord name but not the diagram under it, replace
+% \mac{notrans}|{#1}| with simply |#1| in the above.
+% To restore the default behavior, write
+%
+% \begin{codeblock}
+% |\renewcommand{\gtabtrans}[2]{|\mac{transposehere}|{#1}}|
+% \end{codeblock}
+%
 % \section{Between Songs}\label{sec:between}
 %
 % Never put any material directly into the top level of a \env{songs}
@@ -1794,7 +1914,7 @@
 % Material contributed in an |intersong| environment is subject to the same
 % column-breaking rules as songs (see \S\ref{sec:layout}), but all other
 % formatting is up to you.
-% By default, \LaTeX\ inserts interline glue below the last line of an
+% By default, \LaTeX{} inserts interline glue below the last line of an
 % |intersong| environment.
 % To suppress this, end the |intersong| content with |\par\nointerlineskip|.
 %
@@ -1841,16 +1961,16 @@
 % Scripture quotations begin and end with
 %
 % \begin{codeblock}
-% |\beginscripture{|\meta{ref}|}|
+% |\beginscripture{|\Meta{ref}|}|
 % $\vdots$
 % |\endscripture|
 % \end{codeblock}
 %
 % \noindent
-% where \meta{ref} is a scripture reference that is
+% where \Meta{ref} is a scripture reference that is
 % typeset at the end of the quotation.
-% The \meta{ref} argument should conform to the same syntax
-% rules as for the \meta{ref} arguments passed to \mac{beginsong}
+% The \Meta{ref} argument should conform to the same syntax
+% rules as for the \Meta{ref} arguments passed to \mac{beginsong}
 % macros (see \S\ref{sec:songs}).
 %
 % The text of the scripture quotation between the |\beginscripture| and
@@ -1968,265 +2088,199 @@
 %   \endscripture
 % \end{sample}
 %
-% \section{Guitar Tablatures}\label{sec:tablatures}
+% \section{Chapters and Sections}\label{sec:sectioning}
 %
-% \DescMacro{gtab}
-% Guitar tablature diagrams can be created by using the construct
+% \DescMacro{songsection}
+% \DescMacro{songchapter}
+% Song books can be divided into chapters and sections using all the usual
+% macros provided by \LaTeX{} (e.g., |\chapter|, |\section|, etc.) and by
+% other macro packages.
+% In addition, the \Songs{} package provides two helpful built-in sectioning
+% macros:
 %
 % \begin{codeblock}
-% |\gtab{|\meta{chord}|}{|\meta{fret}|:|\meta{strings}|:|\meta{fingering}|}|
+% |\songchapter{|\Meta{title}|}|
+% |\songsection{|\Meta{title}|}|
 % \end{codeblock}
 %
 % \noindent
-% where the \meta{fret} and \meta{fingering} parts are both optional
-% (and you may omit any colon that borders an omitted argument).
+% which act like \LaTeX's |\chapter| and |\section| commands except that they
+% center the \Meta{title} text in sans serif font and omit the chapter/section
+% number.
+% The |\songchapter| macro only works in document classes that support
+% |\chapter| (e.g., the |book| class).
 %
-% \meta{chord} is a chord name to be placed above the diagram.
+% \section{Indexes}
 %
-% \meta{fret} is an optional digit (any number from 2 to 9) placed to the
-% left of the diagram.
+% \subsection{Index Creation}\label{sec:indexes}
 %
-% \meta{strings} should be a series of symbols, one for each string
-% of the guitar from lowest pitch to highest.
-% Each symbol should be one of:
-% |X| if that string is not to be played,
-% |0| (zero or the letter O) if that string is to be played open, or
-% one of |1| through |9| if that string is to be played on the given
-% numbered fret.
+% \DescMacro{newindex}
+% \DescMacro{newauthorindex}
+% \DescMacro{newscripindex}
+% The \Songs{} package supports three kinds of indexes: indexes by title and/or
+% notable lyrics, indexes by author, and indexes by scripture reference.
+% To generate an index, first declare the index in the document preamble
+% (i.e., before the |\begin{document}| line) with one of the following:
 %
-% \meta{fingering} is an optional series of digits, one for each
-% string of the guitar from lowest pitch to highest.
-% Each digit should be one of:
-% |0| if no fingering information is to be displayed for that string (e.g., if
-% the string is not being played or is being played open), or
-% one of |1| through |4| to indicate that the given numbered finger is to be
-% used to hold down that string.
+% \begin{codeblock}
+% |\newindex{|\Meta{id}|}{|\Meta{filename}|}|
+% |\newauthorindex{|\Meta{id}|}{|\Meta{filename}|}|
+% |\newscripindex{|\Meta{id}|}{|\Meta{filename}|}|
+% \end{codeblock}
 %
-% Here are some examples to illustrate:
+% \noindent
+% The \Meta{id} should be an alphabetic identifier that will be used to
+% identify the index in other macros that reference it.
+% The \Meta{filename} should be a string that, when appended with an
+% extension, constitutes a valid filename on the system.
+% Auxiliary files named \Meta{filename}|.sxd| and \Meta{filename}|.sbx|
+% are generated during the automatic index generation process.
+% For example:
 %
-% \example|\gtab{A}{X02220:001230}|\produces{\vcenterbox{\gtab{A}{{\hphantom{4}}:X02220:001230}}}
-% \example|\gtab{C#sus4}{4:XX3341}|\produces{\vcenterbox{\gtab{C\shrp sus4}{4:XX3341}}}
-% \example|\gtab{B&}{X13331}|\produces{\vcenterbox{\gtab{B\flt}{{\hphantom{4}}:X13331:}}}
-%
-% \DescMacro{minfrets}
-% By default, tablature diagrams always consist of at least 4 fret rows
-% (more if the \meta{strings} argument contains a number larger than 4).
-% To change the minimum number of fret rows, change the value of |\minfrets|.
-% For example, typing
-%
 % \begin{codeblock}
-% |\minfrets=1|
+% |\newindex{mainindex}{idxfile}|
 % \end{codeblock}
 %
 % \noindent
-% causes tablature diagrams to have only as many rows are required to
-% accommodate the largest digit appearing in the \meta{strings} argument.
+% creates a title index named ``|mainindex|'' whose data is
+% stored in files named |idxfile.sxd| and |idxfile.sbx|.
 %
-% \section{Automatic Transposition}\label{sec:transpose}
+% \DescMacro{showindex}
+% To display the index in the document, use:
 %
-% \DescMacro{transpose}
-% You can automatically transpose some or all of the chords in a song up by
-% \meta{n} half-steps by adding the line
-%
 % \begin{codeblock}
-% |\transpose{|\meta{n}|}|
+% |\showindex[|\Meta{columns}|]{|\Meta{title}|}{|\Meta{id}|}|
 % \end{codeblock}
 %
 % \noindent
-% somewhere between the song's \mac{beginsong} line and the first chord to
-% be transposed.
-% For example, if a song's first chord is |\[D]|\eat\], and the line
-% |\transpose{2}| appears before it, then the chord appears as an
-% \chord{E} in the resulting document.
-% Specifying a negative number for \meta{n} transposes subsequent chords
-% down instead of up.
+% where \Meta{id} is the same identifier used in the \mac{newindex},
+% \mac{newauthorindex}, or \mac{newscripindex} command, and where
+% the \Meta{title} is the title of the index, which should consist only of
+% simple text (no font or formatting macros, since those cannot be used in
+% pdf bookmark indexes).
+% The |[|\Meta{columns}|]| part is optional; if specified it dictates the
+% number of columns if the index can't fit in a single column.
+% For example, for a 2-column title index, write:
 %
-% The |\transpose| macro affects all chords appearing after it until the
-% \mac{endsong} line.
-% If two |\transpose| macros appear in the same song, their effects are
-% cumulative.
-%
-% When the \env{transposecapos} option is active, the \mac{capo}
-% macro acts like |\transpose|.
-% See \S\ref{sec:notes} for more information.
-%
-% \paragraph{Enharmonics.}
-% \DescMacro{preferflats}
-% \DescMacro{prefersharps}
-% When using \mac{transpose} to automatically transpose the chords of a song,
-% the \Songs\ package code chooses between enharmonically equivalent
-% names for ``black key'' notes based on the first chord of the song.
-% For example, if |\transpose{1}| is used, and if the first chord of the
-% song is an \chord{E}, then all \chord{A} chords that appear in
-% the song are transcribed as \chord{B\flt} chords rather than
-% \chord{A\shrp} chords, since the key of \chord{F}-major (\chord{E}
-% transposed up by one half-step) has a flatted key signature.
-% Usually this guess produces correct results, but if not, you can use
-% either |\preferflats| or |\prefersharps| after the \mac{transpose} line
-% to force all transcription to use flatted names or sharped names
-% respectively, when resolving enharmonic equivalents.
-%
-% \paragraph{Modulated Verses.}
-% Automatic transposition can be used in conjunction with chord-replaying
-% (see \S\ref{sec:chords}) to produce modulated verses.
-% For example,
-%
 % \begin{codeblock}
-% \mac{beginverse}\mac{memorize}
-% |\[F#]This is a \[B/F#]memorized \[F#]verse. \[E&7]|\eat\]
-% \mac{endverse}
-% \mac{transpose}|{2}|
-% \mac{beginverse}
-% |^This verse is ^modulated up two ^half-steps.|
-% \mac{endverse}
+% |\showindex[2]{Index of Song Titles}{mainindex}|
 % \end{codeblock}
 %
-% \noindent produces
+% \subsection{Index Entries}\label{sec:ientry}
 %
-% \begin{chorded}\memorize
-%   \[F#]This is a \[B/F#]memorized \[F#]verse. \[E&7]\eat\]
-%   \vskip5pt\replay\transpose{2}%
-%   ^This verse is ^modulated up two ^half-steps.
-%   \transpose{-2}%
-% \end{chorded}
+% Every song automatically gets entries in the current \env{songs}
+% environment's list of title index(es) (see \S\ref{sec:songs}).
+% However, you can also add extra index entries for a song to any index.
 %
-% \paragraph{Both Keys.}
-% \DescMacro{trchordformat}
-% By default, when chords are automatically transposed using \mac{transpose},
-% only the transposed chords are printed.
-% However, in some cases you may wish to print the old chords and the
-% transposed chords together so that musicians playing transposing and
-% non-transposing instruments can play from the same piece of music.
-% This can be achieved by redefining the |\trchordformat| macro, which
-% receives two arguments---the original chord name and the transposed chord
-% name, respectively.
-% For example, to print the old chord above the new chord above each lyric,
-% define
+% \paragraph{Indexing Lyrics.}
+% \DescEnv{index=}
+% For example, title indexes often have entries for memorable lines
+% of lyrics in a song in addition to the song's title.
+% You can add an index entry for the current song to the section's
+% title index(es) by adding |index={|\Meta{lyrics}|}| to the song's
+% \mac{beginsong} line.
+% For example,
 %
 % \begin{codeblock}
-% |\renewcommand{\trchordformat}[2]{\vbox{\hbox{#1}\hbox{#2}}}|
+% \mac{beginsong}|{Doxology}|
+% |          [index={Praise God from Whom all blessings flow}]|
 % \end{codeblock}
 %
-% \paragraph{Changing Note Names.}
-% \DescMacro{solfedge}
-% \DescMacro{alphascale}
-% In many countries it is common to use the solfedge names for the notes of
-% the scale (\chord{LA, SI, DO, RE, MI, FA, SOL\/}) instead of the
-% alphabetic names (\chord{A, B, C, D, E, F, G\/}).
-% By default, the transposition logic only understands alphabetic names, but
-% you can tell it to look for solfedge names by typing |\solfedge|.
-% To return to alphabetic names, type |\alphascale|.
+% \noindent
+% causes the song to be indexed both as ``\textit{Doxology}'' and as
+% ``Praise God from Whom all blessings flow'' in the section's title index(es).
+% You can use |index=| multiple times in a \mac{beginsong} line to produce
+% multiple additional index entries.
+% Index entries produced with |index={|\Meta{lyrics}|}| are
+% typeset in an upright font instead of in italics to distinguish
+% them from song titles.
 %
-% \DescMacro{notenames}
-% You can use other note names as well.
-% To define your own note names, type
+% \paragraph{Indexing Extra Song Titles.}
+% \DescEnv{ititle=}
+% To add a regular index entry typeset in italics to the title
+% index(es), use:
 %
 % \begin{codeblock}
-% |\notenames{|\meta{nameA}|}{|\meta{nameB}|}|$\ldots$|{|\meta{nameG}|}|
+% |ititle={|\Meta{title}|}|
 % \end{codeblock}
 %
 % \noindent
-% where each of \meta{nameA} through \meta{nameG} must consist entirely of
-% a sequence of one or more \emph{uppercase} letters.
-% For example, some solfedge musicians use \chord{TI} instead of \chord{SI}
-% for the second note of the scale.
-% To automatically transpose such music, use:
+% in the \mac{beginsong} line instead.
+% Like \env{index=} keyvals, |ititle=| can be used multiple times to produce
+% multiple additional index entries.
 %
-% \begin{codeblock}
-% |\notenames{LA}{TI}{DO}{RE}{MI}{FA}{SOL}|
-% \end{codeblock}
+% \DescMacro{indexentry}
+% \DescMacro{indextitleentry}
+% You can also create index entries by saying
+% |\indexentry[|\Meta{indexes}|]{|\Meta{lyrics}|}| (which creates an
+% entry like \env{index=}) or
+% |\indextitleentry[|\Meta{indexes}|]{|\Meta{title}|}| (which
+% creates an entry like \env{ititle=}).
+% These two macros can be used anywhere between the song's \mac{beginsong}
+% and \mac{endsong} lines, and can be used multiple times to produce
+% multiple entries.
+% If specified, \Meta{indexes} is a comma-separated list of the identifiers
+% of indexes to which the entry should be added.
+% Otherwise the new entry is added to all of the title indexes for the current
+% \env{songs} environment.
 %
-% \DescMacro{notenamesin}
-% \DescMacro{notenamesout}
-% The \Songs\ package can also automatically convert one set of note names
-% to another.
-% For example, suppose you have a large song book in which chords have been
-% typed using alphabetic note names, but you wish to produce a book that
-% uses the equivalent solfedge names.
-% You could achieve this by using the |\notenamesin| macro to tell the
-% \Songs\ package which note names you typed in the input file, and then
-% using |\notenamesout| to tell the \Songs\ package how you want it to
-% typeset each note name in the output file.
-% The final code looks like this:
+% \subsection{Compiling}\label{sec:compiling}
 %
+% As with a typical \LaTeX{} document, compiling a song book document with
+% indexes requires three steps.
+% First, use \LaTeX{} (|pdflatex| is recommended) to generate auxiliary files
+% from the |.tex| file:
+%
 % \begin{codeblock}
-% |\notenamesin{A}{B}{C}{D}{E}{F}{G}|
-% |\notenamesout{LA}{SI}{DO}{RE}{MI}{FA}{SOL}|
+% |pdflatex mybook.tex|
 % \end{codeblock}
 %
-% \noindent
-% The syntaxes of |\notenamesin| and |\notenamesout| are identical to that
-% of \mac{notenames} (see above), except that the arguments of |\notenamesout|
-% can consist of any \LaTeX\ code that is legal in horizontal mode, not just
-% uppercase letters.
+% Second, use the |songidx.lua| script to generate an index for each index that
+% you declared with \mac{newindex}, \mac{newauthorindex}, or
+% \mac{newscripindex}.
+% The script can be launched using Lua\TeX, using the following syntax:
 %
-% To stop converting between note names, use \mac{alphascale}, \mac{solfedge},
-% or \mac{notenames} to reset all note names back to identical input and
-% output scales.
-%
-% \paragraph{Transposing Chords In Macros.}
-% \DescMacro{transposehere}
-% The automatic transposition logic won't find chord names that are hidden
-% inside macro bodies.
-% For example, if you abbreviate a chord by typing,
-%
 % \begin{codeblock}
-% |\newcommand{\mychord}{F|\mac{shrp}| sus4/C|\mac{shrp}|}|
-% \mac{transpose}|{4}|
-% |\[\mychord]|\eat\]
+% |texlua songidx.lua |\textcolor{black}{[}|-b| \Meta{canon}|.can|\textcolor{black}{]} \Meta{filename}|.sxd| \Meta{filename}|.sbx|
 % \end{codeblock}
 %
 % \noindent
-% then the \mac{transpose} macro fails to transpose it; the
-% resulting chord is still an \chord{F\shrp sus4/C\shrp} chord.
-% To fix the problem, you can use |\transposehere| in your macros to
-% explicitly invoke the transposition logic on chord names embedded in
-% macro bodies.
-% The above example could be corrected by instead defining:
+% where \Meta{filename} is the same \Meta{filename} that was used in the
+% \mac{newindex}, \mac{newauthorindex}, or \mac{newscripindex} macro.
+% If the index was declared with \mac{newscripindex}, then the |-b| option
+% is used to specify which version of the bible you wish to use as a basis
+% for sorting your scripture index.
+% The \Meta{canon} part can be any of the |.can| files provided with
+% the |songidx| distribution.
+% If you are using a Protestant, Catholic, or Greek Orthodox Christian bible
+% with book names in English, then the |bible.can| canon file should work
+% well.
+% For other bibles, you should create your own |.can| file by copying and
+% modifying one of the existing |.can| files.
 %
-% \begin{codeblock}
-% |\newcommand{\mychord}{\transposehere{F|\mac{shrp}| sus4/C|\mac{shrp}|}}|
-% \end{codeblock}
+% For example, if your song book |.tex| file contains the lines
 %
-% \DescMacro{notrans}
-% Transposition can be suppressed within material that would otherwise be
-% transposed by using the |\notrans| macro.
-% For example, writing
-%
 % \begin{codeblock}
-% \mac{transposehere}|{G = \notrans{G}}|
+% \mac{newindex}|{titleidx}{titlfile}|
+% \mac{newauthorindex}|{authidx}{authfile}|
+% \mac{newscripindex}|{scripidx}{scrpfile}|
 % \end{codeblock}
 %
 % \noindent
-% would typeset a transposed \chord{G} followed by a non-transposed
-% \chord{G} chord.
-% This does not suppress note name conversion (see \mac{notenames}).
-% To suppress both transposition and note name conversion, just use
-% braces (e.g., |{G}| instead of |\notrans{G}|).
+% then to generate indexes sorted according to a Christian English bible,
+% execute:
 %
-% \paragraph{Transposing Guitar Tablatures.}
-% \DescMacro{gtabtrans}
-% The songs package cannot automatically transpose tablature diagrams
-% (see \S\ref{sec:tablatures}).
-% Therefore, when automatic transposition is taking place, only the chord
-% names of \mac{gtab} macros are displayed (and transposed); the diagrams
-% are omitted.
-% To change this default, redefine the |\gtabtrans| macro, whose two
-% arguments are the two arguments to \mac{gtab}.
-% For example, to display original tablatures without transposing them even
-% when transposition has been turned on, write
-%
 % \begin{codeblock}
-% |\renewcommand{\gtabtrans}[2]{|\mac{gtab}|{|\mac{notrans}|{#1}}{#2}}|
+% |texlua songidx.lua titlfile.sxd titlfile.sbx|
+% |texlua songidx.lua authfile.sxd authfile.sbx|
+% |texlua songidx.lua -b bible.can scrpfile.sxd scrpfile.sbx|
 % \end{codeblock}
 %
-% \noindent
-% To transpose the chord name but not the diagram under it, replace
-% \mac{notrans}|{#1}| with simply |#1| in the above.
-% To restore the default behavior, write
+% Once the indexes are generated, generate the final book by invoking
+% \LaTeX{} one more time:
 %
 % \begin{codeblock}
-% |\renewcommand{\gtabtrans}[2]{|\mac{transposehere}|{#1}}|
+% |pdflatex mybook.tex|
 % \end{codeblock}
 %
 % \section{Customizing the Book}
@@ -2373,21 +2427,26 @@
 %
 % \DescMacro{stitlefont}
 % Song titles are typeset in a sans-serif, slanted font by default
-% (sans-serif, upright if producing slides).
+% (sans-serif, upright if producing slides), with minimal line spacing.
 % You can change this default by redefining |\stitlefont|.
-% For example, to cause titles to be typeset in a roman font, you could
-% define
+% For example, to cause titles to be typeset in a roman font with lines
+% spaced 20 points apart, you could define
 %
 % \begin{codeblock}
-% |\renewcommand{\stitlefont}{\rmfont\Large}|
+% |\renewcommand{\stitlefont}{|
+% |  \rmfont\Large\baselineskip=20pt\lineskiplimit=0pt|
+% |}|
 % \end{codeblock}
 %
 % \DescMacro{versefont}
 % \DescMacro{chorusfont}
+% \DescMacro{meterfont}
+% \DescMacro{echofont}
 % \DescMacro{notefont}
-% You can apply additional font changes to verses, choruses, and
-% textual notes produced with \mac{textnote} and \mac{musicnote} by
-% redefining |\versefont|, |\chorusfont|, and |\notefont|, respectively.
+% You can apply additional font changes to verses, choruses, meter numbers,
+% echo parts produced with \mac{echo}, and textual notes produced with
+% \mac{textnote} and \mac{musicnote}, by redefining |\versefont|,
+% |\chorusfont|, |\meterfont|, |\echofont|, and |\notefont|, respectively.
 % For example, to typeset choruses in italics, you could define
 %
 % \begin{codeblock}
@@ -2469,7 +2528,7 @@
 %
 % \DescMacro{baselineadj}
 % The vertical distance between the baselines of consecutive lines of
-% lyrics is computed by the \Songs\ package based on several factors
+% lyrics is computed by the \Songs{} package based on several factors
 % including the lyric font size, the chord font size (if in \env{chorded}
 % mode), and whether \env{slides} mode is currently active.
 % You can adjust the results of this computation by redefining skip
@@ -2485,7 +2544,7 @@
 % \DescMacro{clineparams}
 % To change the vertical distance between chords and the lyrics below them,
 % redefine the |\clineparams| macro with a definition that adjusts the
-% \LaTeX\ parameters |\baselineskip|, |\lineskiplimit|, and |\lineskip|.
+% \LaTeX{} parameters |\baselineskip|, |\lineskiplimit|, and |\lineskip|.
 % For example, to cause the baselines of chords and their lyrics to be
 % 12 points apart with at least 1 point of space between the bottom of the
 % chord and the top of the lyric, you could write:
@@ -2566,7 +2625,7 @@
 % For complete control over the appearance of the header and footer material
 % that precedes and concludes each song, you can redefine the macros
 % |\makeprelude| and |\makepostlude|.
-% When typesetting a song, the \Songs\ package code invokes both of these
+% When typesetting a song, the \Songs{} package code invokes both of these
 % macros once (after processing all the material between the \mac{beginsong}
 % and \mac{endsong} lines), placing the results within vboxes.
 % The resulting vboxes are placed atop and below the song content.
@@ -2575,6 +2634,21 @@
 % |\makepostlude| displays the song's copyright and licensing information in
 % fine print.
 %
+% As a simple example, the following causes each song to start with its
+% number and title(s), centered, in a large, boldface font, and then centers
+% the rest of the prelude material (e.g., references and authors) below that
+% (using \mac{extendprelude}).
+%
+% \begin{codeblock}
+% |\renewcommand\makeprelude{%|
+% |  |\mac{resettitles}
+% |  \centering|
+% |  {\Large\bfseries|\mac{thesongnum}|. |\mac{songtitle}|\par|
+% |   |\mac{nexttitle}\mac{foreachtitle}|{(|\mac{songtitle}|)\par}}%|
+% |  |\mac{extendprelude}
+% |}|
+% \end{codeblock}
+%
 % \paragraph{Page- and Column-breaking.}
 % \DescMacro{vvpenalty}
 % \DescMacro{ccpenalty}
@@ -2591,7 +2665,7 @@
 % a verse, respectively;
 % and penalties of value |\brkpenalty| are inserted wherever \mac{brk} is
 % used on a line by itself.
-% The higher the penalty, the less likely \TeX\ is to place a
+% The higher the penalty, the less likely \TeX{} is to place a
 % page- or column-break at that site.
 % If any are set to $-10000$ or lower, breaks are forced there.
 % By default, |\interlinepenalty| is set to 1000 and the rest are set to 200
@@ -2621,7 +2695,7 @@
 % \DescMacro{chorusjustify}
 % \DescMacro{justifyleft}
 % \DescMacro{justifycenter}
-% To cause verse or chorus text to be justified flush-left or centered, set
+% To left-justify or center the lines of verses or choruses, redefine
 % |\versejustify| or |\chorusjustify| to |\justifyleft| or |\justifycenter|,
 % respectively.
 % For example, to cause choruses to be centered, one could type:
@@ -2686,14 +2760,14 @@
 % For example, a musician's chord book might include extra verses with
 % alternate chordings.
 %
-% \DescMacroGroup{if}{if...}{ifchorded,iflyric,ifslides,ifpartiallist,ifsongindexes,ifmeasures,ifpdfindex,ifrawtext,iftranscapos,ifnolyrics,ifvnumbered}
-% A conditional block begins with a macro named |\if|\meta{type}, where
-% \meta{type} is one of the types listed in the first column of
+% \DescMacroGroup{if}{if...}{ifchorded,iflyric,ifslides,ifpartiallist,ifsongindexes,ifmeasures,ifrawtext,iftranscapos,ifnolyrics,ifpagepreludes,ifvnumbered}
+% A conditional block begins with a macro named |\if|\Meta{type}, where
+% \Meta{type} is one of the types listed in the first column of
 % Table~\ref{tab:conditionals}.
 % \begin{table}
 % \newcommand\tablerule{\noalign{\hrule}}
-% \newskip\oldbaselineskip \oldbaselineskip\baselineskip
-% \newskip\oldlineskip \oldlineskip\lineskip
+% \newlength\oldbaselineskip \oldbaselineskip\baselineskip
+% \newlength\oldlineskip \oldlineskip\lineskip
 % \newdimen\oldlineskiplimit \oldlineskiplimit\lineskiplimit
 % \newcommand\oninterlineskip{%
 %   \baselineskip\oldbaselineskip
@@ -2711,10 +2785,10 @@
 %   a partial list of songs&\cr\tablerule
 % &|songindexes|&&the \env{noindexes} option is not active&\cr\tablerule
 % &|measures|&&the \env{nomeasures} option is not active&\cr\tablerule
-% &|pdfindex|&&the \env{nopdfindex} option is not active&\cr\tablerule
 % &|rawtext|&&the \env{rawtext} option is active&\cr\tablerule
 % &|transcapos|&&the \env{transposecapos} option is active&\cr\tablerule
 % &|nolyrics|&&the \mac{nolyrics} macro is in effect&\cr\tablerule
+% &|pagepreludes|&&the \mac{pagepreludes} macro is in effect&\cr\tablerule
 % &|vnumbered|&&the current verse is numbered (i.e., it was started
 %   with \mac{beginverse} instead of \mac{beginverse}|*|)&\cr}
 % \hrule}
@@ -2721,20 +2795,20 @@
 % \caption{Conditional macros}\label{tab:conditionals}
 % \end{table}
 % The conditional block concludes with the macro |\fi|.
-% Between the |\if|\meta{type} and the |\fi| may also appear an |\else|.
+% Between the |\if|\Meta{type} and the |\fi| may also appear an |\else|.
 % For example, in the construction
 %
 % \begin{codeblock}
 % |\ifchorded|
-% \quad\meta{A}
+% \quad\Meta{A}
 % |\else|
-% \quad\meta{B}
+% \quad\Meta{B}
 % |\fi|
 % \end{codeblock}
 %
 % \noindent
-% material \meta{A} is only included if the \env{chorded} option is active,
-% and material \meta{B} is only included if the \env{chorded} option is not
+% material \Meta{A} is only included if the \env{chorded} option is active,
+% and material \Meta{B} is only included if the \env{chorded} option is not
 % active.
 %
 % \subsection{Page Layout}\label{sec:layout}
@@ -2781,10 +2855,18 @@
 % (see \mac{songpos} below).
 % \item Indexes produced with \mac{showindex} are typeset to the width of
 % the enclosing environment.
-% Thus, you should be sure to reset \LaTeX\ back to one column (via
+% Thus, you should be sure to reset \LaTeX{} back to one column (via
 % |\onecolumn|) before executing \mac{showindex}.
 % \end{itemize}
 %
+% \DescMacro{pagepreludes}
+% Song preludes (i.e., the material atop each song, including the title) are
+% typeset by default at column width.
+% Writing |\pagepreludes| typesets subsequent preludes at page width atop
+% fresh pages, with the rest of the song in multiple columns beneath its title.
+% (To prohibit separation of songs from their preludes, it also sets
+% \mac{songpos} to 0.)
+%
 % \DescMacro{columnsep}
 % The horizontal distance between consecutive columns is controlled by
 % the |\columnsep| dimension.
@@ -2795,7 +2877,7 @@
 % \end{codeblock}
 %
 % \DescMacro{colbotglue}
-% When \LaTeX\ ends each column it inserts glue equal to |\colbotglue|.
+% When \LaTeX{} ends each column it inserts glue equal to |\colbotglue|.
 % In lyric books this macro is set to |0pt| so that each column ends flush with
 % the bottom of the page.
 % In other books that have ragged bottoms, it is set to stretchable
@@ -2819,7 +2901,7 @@
 % \end{codeblock}
 %
 % \DescMacro{songpos}
-% The \Songs\ package uses a song-positioning algorithm that
+% The \Songs{} package uses a song-positioning algorithm that
 % moves songs to the next column or page in order to avoid column- or
 % page-breaks within songs.
 % The algorithm has four levels of aggressiveness, numbered from 0 to 3.
@@ -2826,7 +2908,7 @@
 % You can change the aggressiveness level by typing
 %
 % \begin{codeblock}
-% |\songpos{|\meta{level}|}|
+% |\songpos{|\Meta{level}|}|
 % \end{codeblock}
 %
 % \noindent
@@ -2838,7 +2920,7 @@
 % songs.
 % Level 1 avoids only page-turns within songs.
 % Level 0 turns off the song-positioning algorithm entirely.
-% This causes songs to be positioned wherever \TeX\ thinks is best
+% This causes songs to be positioned wherever \TeX{} thinks is best
 % based on penalty settings (see \mac{vvpenalty} and \mac{spenalty}).
 %
 % \DescMacro{spenalty}
@@ -2852,25 +2934,20 @@
 %
 % \subsection{Indexes}\label{sec:idxcust}
 %
-% \paragraph{Index Appearance.}
-% \DescMacro{indexsongsas}
-% By default, the right-hand side of each index entry contains a list of
-% one or more song numbers.
-% To instead list page numbers, use the |\indexsongsas| macro:
+% \subsubsection{Index Appearance}
 %
+% \paragraph{Index Titles.}
+% To customize the appearance of index titles, redefine the \mac{songsection}
+% and/or \mac{songchapter} macros from \S\ref{sec:sectioning}.
+% For example, to use \LaTeX's built-in |\section| and |\chapter| macros
+% instead, you could write:
+%
 % \begin{codeblock}
-% |\indexsongsas{|\meta{id}|}{\thepage}|
+% |\renewcommand{|\mac{songchapter}|}{\chapter}|
+% |\renewcommand{|\mac{songsection}|}{\section}|
 % \end{codeblock}
 %
-% \noindent
-% where \meta{id} is the same identifier used in the \mac{newindex},
-% \mac{newauthorindex}, or \mac{newscripindex} macro that created the index.
-% The second argument must always be something that expands into raw text
-% without any formatting, since this text gets output to auxiliary files that
-% are lexographically sorted by the index-generation program.
-% To go back to indexing songs by song number, use \mac{thesongnum} in place
-% of |\thepage| in the above.
-%
+% \paragraph{Layout and page divisions.}
 % \DescMacro{sepindexestrue}
 % \DescMacro{sepindexesfalse}
 % Indexes are by default typeset on separate pages, and when an index is
@@ -2880,6 +2957,18 @@
 % starting unnecessary new pages.
 % To re-enable the defaults, use |\sepindexestrue|.
 %
+% \DescMacro{idxheadwidth}
+% The |\idxheadwidth| length defines the width of the shaded boxes that
+% begin each alphabetic block of a large title index.
+% Setting it to 0pt suppresses the boxes entirely.
+% For example, to set the width of those boxes to 1 centimeter, you could
+% define
+%
+% \begin{codeblock}
+% |\setlength{\idxheadwidth}{1cm}|
+% \end{codeblock}
+%
+% \paragraph{Fonts and colors.}
 % \DescMacro{idxrefsfont}
 % To control the formatting of the list of references on the right-hand side
 % of index entries, redefine |\idxrefsfont|.
@@ -2922,16 +3011,6 @@
 % |\renewcommand{\idxbgcolor}{red}|
 % \end{codeblock}
 %
-% \DescMacro{idxheadwidth}
-% The |\idxheadwidth| length defines the width of the shaded boxes that
-% begin each alphabetic block of a large title index.
-% For example, to set the width of those boxes to 1 centimeter, you could
-% define
-%
-% \begin{codeblock}
-% |\setlength{\idxheadwidth}{1cm}|
-% \end{codeblock}
-%
 % \DescMacro{idxauthfont}
 % The font used to typeset entries of an author index is controlled by
 % |\idxauthfont|.
@@ -2963,9 +3042,9 @@
 % \DescMacro{idxcont}
 % In a scripture index, when a column break separates a block of entries
 % devoted to a book of the bible, the new column is titled
-% ``\meta{bookname} (continued)'' by default.
+% ``\Meta{bookname} (continued)'' by default.
 % You can change this default by redefining the |\idxcont| macro, which
-% receives the \meta{bookname} as its single argument.
+% receives the \Meta{bookname} as its single argument.
 % For example, to typeset an index in German, one might define
 %
 % \begin{codeblock}
@@ -2972,7 +3051,110 @@
 % |\renewcommand{\idxcont}[1]{\small\textbf{#1} (fortgefahren)}|
 % \end{codeblock}
 %
-% \paragraph{Alphabetization Options.}
+% \subsubsection{Entry References}
+%
+% \DescMacro{indexsongsas}
+% By default, the right-hand side of each index entry contains a list of
+% one or more song numbers.
+% To instead list page numbers, use the |\indexsongsas| macro:
+%
+% \begin{codeblock}
+% |\indexsongsas{|\Meta{id}|}{\thepage}|
+% \end{codeblock}
+%
+% \noindent
+% where \Meta{id} is the same identifier used in the \mac{newindex},
+% \mac{newauthorindex}, or \mac{newscripindex} macro that created the index.
+% The second argument must always be something that expands into raw text
+% without any formatting, since this text gets output to auxiliary files that
+% are lexographically sorted by the index-generation program.
+% To go back to indexing songs by song number, use \mac{thesongnum} in place
+% of |\thepage| in the above.
+%
+% \subsubsection{PDF Bookmarks and Links}
+%
+% \DescMacro{songtarget}
+% \DescMacro{songlink}
+% Each \mac{beginsong} environment adds a PDF bookmark (if generating a PDF)
+% and hyperlink target (if using the |hyperref| package) for the
+% song by invoking |\songtarget| with two arguments:
+% (1) a suggested PDF bookmark level, and (2) a link target name.
+% Links in indexes to these targets are created by |\songlink|, which
+% also gets two arguments:
+% (1) the link target name (same as the second argument to \mac{songtarget}),
+% and (2) the text to be linked.
+%
+% Redefine these macros to customize or suppress these bookmarks, targets,
+% and links.
+% For example, to enable both bookmarks and links (the default behavior) use:
+%
+% \begin{codeblock}
+% |\renewcommand{\songtarget}[2]|
+% |  {\pdfbookmark[#1]{|\mac{thesongnum}|. |\mac{songtitle}|}{#2}}|
+% |\renewcommand{\songlink}[2]{\hyperlink{#1}{#2}}|
+% \end{codeblock}
+%
+% \noindent
+% To enable links but not bookmarks, use:
+%
+% \begin{codeblock}
+% |\renewcommand{\songtarget}[2]{\hypertarget{#2}{\relax}}|
+% |\renewcommand{\songlink}[2]{\hyperlink{#1}{#2}}|
+% \end{codeblock}
+%
+% \noindent
+% To disable both bookmarks and links, use:
+%
+% \begin{codeblock}
+% |\renewcommand{\songtarget}[2]{}|
+% |\renewcommand{\songlink}[2]{#2}|
+% \end{codeblock}
+%
+% \subsubsection{Sort Order}
+%
+% The alphabetic ordering of entries in title and author indexes is dictated
+% by the computer system on which the \Songs{} software is installed.
+% Different languages and regions have different sorting conventions, so the
+% |songidx| Lua script delegates decisions about order to your operating system.
+% If the default ordering proves inadequate, you can modify it by changing your
+% operating system's \emph{locale} (see your system's local help files).
+% Alternatively, you can explicitly tell the |songidx| program which locale to
+% use in one of three ways:
+%
+% \begin{itemize}
+% \item \emph{Windows:}
+% Edit the |generate.bat| file in the |Sample| folder (or your working folder)
+% with any plain text editor (e.g., Vim or Notepad).
+% Near the top, find the line that says |SET locale=|.
+% After the |=|, type any valid locale name.
+% For a list of valid locale names on Windows, please see the ``Language name
+% abbreviation'' column of Microsoft's online National Language Support (NLS)
+% API Reference:
+%
+% {\centering\url{http://msdn.microsoft.com/en-us/goglobal/bb896001.aspx}\par}
+%
+% \item \emph{Unix:}
+% Create an environment variable named |SONGIDX_LOCALE| and set it equal to
+% the desired locale name.
+% The command |locale -a| lists all valid locale names on most Unix systems.
+%
+% \item \emph{Command-line:}
+% If you are executing the |songidx| script manually, use the |-l| option to
+% specify the locale:
+%
+% \begin{codeblock}
+% |texlua songidx -l sv_SE myindex.sxd myindex.sbx|
+% \end{codeblock}
+%
+% \end{itemize}
+%
+% \subsubsection{Special Words In Song Info}
+%
+% The following macros control how certain keywords are treated when parsing
+% and sorting index entries.
+% They only affect indexes that have already been declared, so put them
+% strictly after all your index creation commands (see \S\ref{sec:indexes}).
+%
 % \DescMacro{titleprefixword}
 % In English, when a title begins with ``The'' or ``A'', it is traditional to
 % move these words to the end of the title and sort the entry by the following
@@ -2996,10 +3178,9 @@
 % This macro may only be used in the document preamble but may be used
 % multiple times to declare multiple prefix words.
 %
-% \paragraph{Special Words In Song Info.}
 % \DescMacro{authsepword}
 % When parsing author index entries, the word ``and'' is recognized by the
-% |songidx| program as a conjunctive that separates author names.
+% |songidx| script as a conjunctive that separates author names.
 % To override this default and specify a different conjunctive, use the
 % |\authsepword| macro one or more times in the document preamble.
 % For example, to instead treat ``und'' as a conjunctive, you could say,
@@ -3012,9 +3193,6 @@
 % The first use of |\authsepword| and each of the following macros overrides
 % the default, so if you also want to continue to treat ``and'' as a
 % conjunctive, you must also say |\authsepword{and}| explicitly.
-% The |\authsepword| macro and each of the following macros may only be used
-% in the document preamble but may be used multiple times to declare multiple
-% special words.
 %
 % \DescMacro{authbyword}
 % When parsing author index entries, the word ``by'' is recognized as a
@@ -3052,15 +3230,15 @@
 % The headers and footers are then defined so as to refer to the first
 % and/or last invisible mark that ends up on each page once the document
 % is divided into pages.
-% This section describes the marks made available by the \Songs\ package.
+% This section describes the marks made available by the \Songs{} package.
 % For more detailed information about the marks already provided by
-% \LaTeX\ and how to use them, consult any \LaTeX\ user manual.
+% \LaTeX{} and how to use them, consult any \LaTeX{} user manual.
 %
 % \DescMacro{songmark}
 % \DescMacro{versemark}
 % \DescMacro{chorusmark}
 % To add song information to page headings and footers, redefine |\songmark|,
-% |\versemark|, or |\chorusmark| to add the necessary \TeX\ marks to the
+% |\versemark|, or |\chorusmark| to add the necessary \TeX{} marks to the
 % current page whenever a new song, verse, or chorus begins.
 % These macros expect no arguments; to access the current song's
 % information including titles, use the macros documented in
@@ -3084,19 +3262,19 @@
 % To do so, use the |\newsongkey| macro, which has the syntax
 %
 % \begin{codeblock}
-% |\newsongkey{|\meta{keyname}|}{|\meta{initcode}|}[|\meta{default}|]{|\meta{setcode}|}|
+% |\newsongkey{|\Meta{keyname}|}{|\Meta{initcode}|}[|\Meta{default}|]{|\Meta{setcode}|}|
 % \end{codeblock}
 %
 % \noindent
-% Here, \meta{keyname} is the name of the new key for the keyval,
-% \meta{initcode} is \LaTeX\ code that is executed at the start of each
+% Here, \Meta{keyname} is the name of the new key for the keyval,
+% \Meta{initcode} is \LaTeX{} code that is executed at the start of each
 % \mac{beginsong} line before the \mac{beginsong} arguments are processed,
-% \meta{default} (if specified) is the default value used for the keyval when
-% \meta{keyname} appears in \mac{beginsong} without a value,
-% and \meta{setcode} is macro code that is executed whenever
-% \meta{key} is parsed as part of the \mac{beginsong} keyval arguments.
-% In \meta{setcode}, |#1| expands to the value given by the user for the
-% keyval (or to \meta{default} if no value was given).
+% \Meta{default} (if specified) is the default value used for the keyval when
+% \Meta{keyname} appears in \mac{beginsong} without a value,
+% and \Meta{setcode} is macro code that is executed whenever
+% \Meta{key} is parsed as part of the \mac{beginsong} keyval arguments.
+% In \Meta{setcode}, |#1| expands to the value given by the user for the
+% keyval (or to \Meta{default} if no value was given).
 %
 % For example, to define a new song key called |arr| which stores its
 % value in a macro called |\arranger|, one could write:
@@ -3146,12 +3324,12 @@
 %
 % For more detailed information about keyvals and how they work, consult the
 % documentation for David Carlisle's |keyval| package, which comes standard
-% with most \LaTeXe\ installations.
+% with most \LaTeXe{} installations.
 %
 % \subsection{Font Kerning Corrections}
 %
 % \paragraph{Chord Overstriking.}
-% In order to conserve space and keep songs readable, the \Songs\ package
+% In order to conserve space and keep songs readable, the \Songs{} package
 % pushes chords down very close to the lyrics with which they are paired.
 % Unfortunately, this can sometimes cause low-hanging characters in chord
 % names to overstrike the lyrics they sit above.
@@ -3169,7 +3347,7 @@
 % minimizes low-hanging symbols; but if you lack such a font, then the
 % following trick works pretty well.
 % Somewhere in the preamble of your document, you can write the following
-% \LaTeX\ code:
+% \LaTeX{} code:
 %
 % \begin{codeblock}
 % |\renewcommand{\chordlocals}{\catcode`(\active|
@@ -3220,7 +3398,7 @@
 %
 % \paragraph{Scripture Font Quotation Marks.}
 % \DescMacro{shiftdblquotes}
-% The \Songs\ package compensates for a kerning problem in the Zaph Chancery
+% The \Songs{} package compensates for a kerning problem in the Zaph Chancery
 % font (used to typeset scripture quotations) by redefining the |``| and |''|
 % token sequences to be active characters that yield double-quotes shifted
 % 1.1 points and 2 points left, respectively, of their normal positions.
@@ -3291,7 +3469,7 @@
 % (or sets |\songtitle| to |\relax| if there are no more titles).
 %
 % \DescMacro{foreachtitle}
-% The |\foreachtitle| macro accepts \LaTeX\ code as its single
+% The |\foreachtitle| macro accepts \LaTeX{} code as its single
 % argument and executes it once for each (remaining) song title.
 % Within the provided code, use |\songtitle| to get the current title.
 % For example, the following code generates a comma-separated list of all
@@ -3312,97 +3490,11 @@
 % songs to be extracted.
 % Redefining it after the preamble may have unpredictable results.
 %
-% \section{Index Generation}
-%
-% The material in this section describes macros provided by the \Songs\
-% package that are used during the automatic generation of the song book
-% indexes.
-% Since index generation is automatic, document authors should not
-% normally need to use any of these macros directly.
-% The documentation in this section is therefore provided purely for
-% completeness and for informational purposes.
-% For instructions on how to automatically generate indexes when compiling
-% a song book, see \S\ref{sec:compiling}.
-% For info on how to customize the appearance of indexes, see
-% \S\ref{sec:idxcust}.
-%
-% Automatic generation of song book indexes is a three stage process:
-%
-% \begin{enumerate}
-%
-% \item
-% Each time a song book \LaTeX\ file is compiled, an auxiliary file named
-% \meta{filename}|.sxd| is written out for each \meta{filename}
-% defined using \mac{newindex}, \mac{newauthorindex}, or \mac{newscripindex}.
-% These |.sxd| files are plain text files that can be viewed using any
-% standard text editor.
-% They begin with a line identifying the type of index (title, author, or
-% scripture) and then contain triples of lines, one triple for each song to
-% appear in the index.
-% The first line of a triple has the information by which the song is to be
-% indexed (a title, author, or scripture reference).
-% The second line has the song's number in the book (yielded by
-% \mac{thesongnum}).
-% The third line is an identifying label for the song used in hyperlinking.
-%
-% \item
-% Once the |.sxd| files have been generated, an external program is used
-% to transform each |.sxd| file into a |.sbx| file.
-% Since the standard |makeindex| program provided with \LaTeX\ is not powerful
-% enough to sort scripture references, distributions of \Songs\ package come
-% with a specialized |songidx| program to do this.
-%
-% \item
-% The |.sbx| files produced by the |songidx| program are then read
-% in by the \mac{showindex} macro next time the source document is
-% compiled using \LaTeX.
-% These |.sbx| files consist of the macros and environments described below.
-%
-% \end{enumerate}
-% 
-% \DescEnv{idxblock}
-% In indexes that are blocked off into sections, one for each letter of the
-% alphabet, the \meta{filename}|.sbx| files generated for that index
-% consist of a series of |idxblock| environments, one for each such section.
-% An |idxblock| environment begins and ends with
-%
-% \begin{codeblock}
-% |\begin{idxblock}{|\meta{letter}|}|
-% $\vdots$
-% |\end{idxblock}|
-% \end{codeblock}
-% 
-% \noindent
-% where \meta{letter} is the letter of the alphabet for that block.
-%
-% \DescMacro{idxentry}
-% \DescMacro{idxaltentry}
-% The index entries themselves are created by lines of the form
-%
-% \begin{codeblock}
-% |\idxentry{|\meta{leftside}|}{|\meta{rightside}|}|
-% |\indexaltentry{|\meta{leftside}|}{|\meta{rightside}|}|
-% \end{codeblock}
-%
-% \noindent
-% each of which creates an index entry with \meta{leftside} on the left,
-% followed by a series of dots, followed by \meta{rightside} on the
-% right.
-% The |\indexentry| macro is used for ``normal'' entries (e.g., titles in a
-% title index), and |\indexaltentry| is used for ``alternate'' entries
-% (e.g., lyric lines in a title index).
-%
-% Within \meta{rightside}, multiple items are separated with |\\|
-% macros instead of commas.
-% When used in an index |.sbx| file, the |\\| macro produces a comma followed
-% by some complex spacing that allows index lines to be broken suitably if
-% they are too long to fit in one physical line.
-%
 % \section{Other Resources}
 %
-% There are a number of other \LaTeX\ packages available for typesetting
+% There are a number of other \LaTeX{} packages available for typesetting
 % songs, tablature diagrams, or song books.
-% Probably the best of these is the \Rath\ package by Christopher Rath
+% Probably the best of these is the \Rath{} package by Christopher Rath
 % (\href{http://rath.ca/Misc/Songbook/}{{\tt http://rath.ca/Misc/Songbook/}}).
 % Most of the differences between other packages and this one are intentional;
 % the following is a summary of where I've adopted various differing design
@@ -3411,50 +3503,50 @@
 % \bigskip
 %
 % \paragraph{Ease of Song Entry.}
-% Much of the \Songs\ package programming is devoted to easing the burden of
+% Much of the \Songs{} package programming is devoted to easing the burden of
 % typing chords.
-% With most \LaTeX\ song book packages the user types chords using a standard
-% \LaTeX\ macro syntax like |\chord{|\meta{chord}|}{|\meta{lyric}|}|.
-% The \Songs\ package uses a less conventional
-% |\[|\meta{chord}|]|\meta{lyric}\eat\] syntax for several
+% With most \LaTeX{} song book packages the user types chords using a standard
+% \LaTeX{} macro syntax like |\chord{|\Meta{chord}|}{|\Meta{lyric}|}|.
+% The \Songs{} package uses a less conventional
+% |\[|\Meta{chord}|]|\Meta{lyric}\eat\] syntax for several
 % reasons detailed below.
 %
-% First, macros in the standard \LaTeX\ syntax require more key-presses
-% than macros in the \Songs\ package's syntax.
+% First, macros in the standard \LaTeX{} syntax require more key-presses
+% than macros in the \Songs{} package's syntax.
 % This can become become very taxing when typing up a large book.
 % Chords often appear as frequently as one per syllable, especially in hymns,
 % so keeping the syntax as brief as possible is desirable.
 %
-% Second, the standard \LaTeX\ macro syntax requires the user to estimate how
-% much of the \meta{lyric} will lie below the chord (because the \meta{lyric}
-% part must be enclosed in braces) whereas the \Songs\ package's syntax does
+% Second, the standard \LaTeX{} macro syntax requires the user to estimate how
+% much of the \Meta{lyric} will lie below the chord (because the \Meta{lyric}
+% part must be enclosed in braces) whereas the \Songs{} package's syntax does
 % not.
 % Estimating this accurately can be quite difficult, since in many cases the
-% \meta{lyric} part must include punctuation or multiple words to get proper
+% \Meta{lyric} part must include punctuation or multiple words to get proper
 % results.
-% The \Songs\ package automates this for the user, significantly easing the
+% The \Songs{} package automates this for the user, significantly easing the
 % task of chord-entry.
 %
-% Third, unlike the standard \LaTeX\ chord syntax, the \Songs\ package's
+% Third, unlike the standard \LaTeX{} chord syntax, the \Songs{} package's
 % syntax handles all hyphenation of chorded lyrics fully automatically.
 % Extra hyphenation must be introduced in chord books wherever a chord
 % is wider than the syllable it sits above.
-% With the standard \LaTeX\ chord syntax such hyphenation must be
+% With the standard \LaTeX{} chord syntax such hyphenation must be
 % introduced manually by the user (usually via a special hyphenation macro),
-% but the \Songs\ package does this automatically.
+% but the \Songs{} package does this automatically.
 %
 % Fourth and finally, some other packages allow the user to use ``|b|''
-% in a \meta{chord} to produce a flat symbol, whereas the \Songs\ package
+% in a \Meta{chord} to produce a flat symbol, whereas the \Songs{} package
 % requires an ``|&|'' instead.
 % Using ``|b|'' is more intuitive but prevents the
-% use of ``|b|'' for any other purpose within a \meta{chord}, such as to
+% use of ``|b|'' for any other purpose within a \Meta{chord}, such as to
 % produce a literal ``b'' or to type another macro name like |\hbox| that
 % contains a ``b''.
-% Consequently, the \Songs\ package uses the less obvious ``|&|'' symbol to
+% Consequently, the \Songs{} package uses the less obvious ``|&|'' symbol to
 % produce flat symbols.
 %
 % \paragraph{Song Structure.}
-% The \Songs\ package provides a relatively small number of macros for
+% The \Songs{} package provides a relatively small number of macros for
 % typesetting high-level song structure, including verses, choruses,
 % textual comments, and conditional macros that indicate that certain sections
 % should go in chord books but not lyric books.
@@ -3465,7 +3557,7 @@
 % users to learn.
 %
 % \paragraph{Multiple columns.}
-% The \Songs\ package was designed from the ground up to produce song books
+% The \Songs{} package was designed from the ground up to produce song books
 % with many songs per page, arranged in multiple columns.
 % As a result, it includes elaborate support for many features not found in
 % most other packages, such as automatic column balancing, completely
@@ -3473,12 +3565,12 @@
 % beautiful scripture quotations to fill in gaps between songs.
 %
 % \paragraph{Indexes.}
-% Another major feature of the \Songs\ package is its support for a variety
+% Another major feature of the \Songs{} package is its support for a variety
 % of different index types, most notably indexes arranged by scripture
 % reference.
 % Scripture indexes can be invaluable for planning services around particular
 % sermons or topics.
-% The \Songs\ package allows book authors to specify the names and preferred
+% The \Songs{} package allows book authors to specify the names and preferred
 % ordering of books of the bible, and automatically handles complex issues
 % like overlapping verse ranges to produce an easy-to-read, compact, and
 % well-ordered index.
@@ -3486,7 +3578,7 @@
 % notable lines of lyrics.
 %
 % \paragraph{Automatic Transposition.}
-% The \Songs\ package has a facility for automatically transposing songs, and
+% The \Songs{} package has a facility for automatically transposing songs, and
 % even generating chord books that print the chords in multiple keys (e.g., so
 % that a pianist and guitarist using a capo can play together from the same
 % book).
@@ -3493,19 +3585,19 @@
 %
 % \bigskip
 %
-% The \Songs\ package was developed entirely independently of all other
-% \LaTeX\ song book packages.
-% I originally developed the set of \LaTeX\ macros that eventually became
-% the \Songs\ package in order to typeset a song book for the Graduate
+% The \Songs{} package was developed entirely independently of all other
+% \LaTeX{} song book packages.
+% I originally developed the set of \LaTeX{} macros that eventually became
+% the \Songs{} package in order to typeset a song book for the Graduate
 % Christian Fellowship (GCF) at Cornell University, and the Cornell
 % International Christian Fellowship (CICF).
 % Once I had fine-tuned my package to be sufficiently versatile, I decided
 % to release it for public use.
-% At that time I noticed the \Rath\ package and others, and wrote this
+% At that time I noticed the \Rath{} package and others, and wrote this
 % summary of the most prominent differences.
 %
 % For information on more song-typesetting resources for \LaTeX, I recommend
-% consulting the documentation provided with the \Rath\ package.
+% consulting the documentation provided with the \Rath{} package.
 % It includes an excellent list of other resources that might be of interest
 % to creators of song books.
 %
@@ -3780,7 +3872,7 @@
 % \clearpage
 % \section{Implementation}
 %
-% The following provides the verbatim implementation of the \Songs\ \LaTeX\ 
+% The following provides the verbatim implementation of the \Songs{} \LaTeX{} 
 % package, along with commentary on how it works.
 % In general, macro names that contain a |@| symbol are not intended to be
 % directly accessible by the outside world; they are for purely internal use.
@@ -3797,11 +3889,11 @@
 % challenging features:
 % \begin{itemize}
 % \item Putting chords above lyrics fully automatically requires building an
-% entire lyric-parser in \LaTeX\ (see \S\ref{sec:lyricscan}).
+% entire lyric-parser in \LaTeX{} (see \S\ref{sec:lyricscan}).
 % \item Avoiding page-turns within songs without prohibiting column-breaks
 % requires building a completely new page-breaking algorithm
 % (see \S\ref{sec:pagebuilder}).
-% \item The package must be able to generate an astonishing number of document
+% \item The package must be able to generate a daunting number of document
 % variants from a common source: lyric-only books, chorded books, digital
 % slides, transparency slides, selected song subsets, transposed songs, and
 % combinations of the above.
@@ -3812,13 +3904,13 @@
 %
 % \subsection{Initialization}
 %
-% The code in this section detects any \TeX\ versioning or configuration
+% The code in this section detects any \TeX{} versioning or configuration
 % settings that are relevant to the rest of the song book code.
 %
 % \begin{macro}{\ifSB at etex}
-% Numerous enhancements are possible when using an $\varepsilon$-\TeX\ 
+% Numerous enhancements are possible when using an $\varepsilon$-\TeX{}
 % compatible version of \LaTeX.
-% We start by checking to see if $\varepsilon$-\TeX\ primitives are
+% We start by checking to see whether $\varepsilon$-\TeX{} primitives are
 % available.
 %    \begin{macrocode}
 \newif\ifSB at etex
@@ -3825,7 +3917,9 @@
 \ifx\eTeXversion\undefined\else
   \ifx\eTeXversion\relax\else
     \SB at etextrue
-    \IfFileExists{etex.sty}{\RequirePackage{etex}}{}
+    \ifx\e at alloc\@undefined
+      \IfFileExists{etex.sty}{\RequirePackage{etex}}{}
+    \fi
   \fi
 \fi
 %    \end{macrocode}
@@ -3858,6 +3952,7 @@
 % \end{macro}
 %
 % \begin{macro}{\ifSB at test}
+% \begin{macro}{\ifSB at testii}
 % \begin{macro}{\SB at temp}
 % \begin{macro}{\SB at tempii}
 % \begin{macro}{\SB at tempiii}
@@ -3866,6 +3961,7 @@
 % Reserve some control sequence names for scratch use.
 %    \begin{macrocode}
 \newif\ifSB at test
+\newif\ifSB at testii
 \newcommand\SB at temp{}
 \newcommand\SB at tempii{}
 \newcommand\SB at tempiii{}
@@ -3878,7 +3974,32 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
+% \end{macro}
 %
+% \begin{macro}{\SB at newcount}
+% \begin{macro}{\SB at newdimen}
+% \begin{macro}{\SB at newbox}
+% \begin{macro}{\SB at newtoks}
+% \begin{macro}{\SB at newwrite}
+% Create macros for safely allocating count, dimen, box, token, and write
+% registers with detection for name-clashes.
+% For some reason, the default allocation macros provided by the \LaTeX{}
+% kernel do not detect name-clashes(!), which means that packages that use them
+% might accidentally overwrite our registers, causing all sorts of problems.
+% But at least we can do our best to avoid overwriting their registers.
+%    \begin{macrocode}
+\newcommand\SB at newcount[1]{\@ifdefinable#1{\newcount#1}}
+\newcommand\SB at newdimen[1]{\@ifdefinable#1{\newdimen#1}}
+\newcommand\SB at newbox[1]{\@ifdefinable#1{\newbox#1}}
+\newcommand\SB at newtoks[1]{\@ifdefinable#1{\newtoks#1}}
+\newcommand\SB at newwrite[1]{\@ifdefinable#1{\newwrite#1}}
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
 % \begin{macro}{\SB at dimen}
 % \begin{macro}{\SB at dimenii}
 % \begin{macro}{\SB at dimeniii}
@@ -3892,17 +4013,17 @@
 % \begin{macro}{\SB at skip}
 % Reserve some temp registers for various purposes.
 %    \begin{macrocode}
-\newdimen\SB at dimen
-\newdimen\SB at dimenii
-\newdimen\SB at dimeniii
-\newdimen\SB at dimeniv
-\newbox\SB at box
-\newbox\SB at boxii
-\newbox\SB at boxiii
-\newtoks\SB at toks
-\newcount\SB at cnt
-\newcount\SB at cntii
-\newskip\SB at skip
+\SB at newdimen\SB at dimen
+\SB at newdimen\SB at dimenii
+\SB at newdimen\SB at dimeniii
+\SB at newdimen\SB at dimeniv
+\SB at newbox\SB at box
+\SB at newbox\SB at boxii
+\SB at newbox\SB at boxiii
+\SB at newtoks\SB at toks
+\SB at newcount\SB at cnt
+\SB at newcount\SB at cntii
+\newlength\SB at skip
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -3916,12 +4037,30 @@
 % \end{macro}
 % \end{macro}
 %
+% \begin{macro}{\SB at envbox}
+% Also reserve a slightly less volatile box register for per-environment use.
+% In scripture environments it holds the scripture citation.
+% In indexes it holds the index title text.
+%    \begin{macrocode}
+\SB at newbox\SB at envbox
+%    \end{macrocode}
+% \end{macro}
+%
 % Load David Carlisle's |keyval| package for processing
-% \meta{key}=\meta{value} style macro arguments.
+% \Meta{key}=\Meta{value} style macro arguments.
 %    \begin{macrocode}
 \RequirePackage{keyval}
 %    \end{macrocode}
 %
+% \begin{macro}{\SB at app}
+% Utility macro: Append some text to the definition of another macro.
+%    \begin{macrocode}
+\newcommand\SB at app[3]{%
+  \expandafter#1\expandafter#2\expandafter{#2#3}%
+}
+%    \end{macrocode}
+% \end{macro}
+%
 % \subsection{Default Parameters}\label{sec:impparams}
 %
 % This section defines macros and lengths that will typically be executed or
@@ -3941,7 +4080,7 @@
 % Define the font style to use for formatting song titles.
 %    \begin{macrocode}
 \newcommand\stitlefont{%
-  \ifslides\sffamily\Huge\else\sffamily\slshape\Large\fi%
+  \sffamily\ifslides\Huge\else\slshape\Large\fi%
 }
 %    \end{macrocode}
 % \end{macro}
@@ -3949,18 +4088,35 @@
 % \begin{macro}{\versefont}\MainImpl{versefont}
 % \begin{macro}{\chorusfont}\MainImpl{chorusfont}
 % \begin{macro}{\notefont}\MainImpl{notefont}
+% \begin{macro}{\meterfont}\MainImpl{meterfont}
 % \changes{v2.1}{2007/08/02}{Added}
 % By default, verses, choruses, and textual notes just allow the |\lyricfont|
 % style to continue.
+% Meter numbers are in tiny, sans-serif, upright font.
+% Echo parts toggle slanted and upright fonts.
 %    \begin{macrocode}
 \newcommand\versefont{}
 \newcommand\chorusfont{}
 \newcommand\notefont{}
+\newcommand\meterfont{\tiny\sffamily\upshape}
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
 % \end{macro}
+% \end{macro}
 %
+% \begin{macro}{\echofont}\MainImpl{echofont}
+% \changes{v2.18}{2014/06/28}{Added}
+% Echo parts toggle between oblique and upright shapes like |\emph|, but we
+% use |\slshape| instead of |\itshape| because it tends to look nicer with the
+% larger fonts used in slides mode.
+%    \begin{macrocode}
+\newcommand\echofont{%
+  \ifdim\fontdimen\@ne\font>\z@\upshape\else\slshape\fi%
+}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\scripturefont}\MainImpl{scripturefont}
 % \changes{v1.13}{2005/05/12}{Added kerning correction for double-quote ligatures}
 % Define the font style to use for formatting scripture quotations
@@ -3986,7 +4142,7 @@
 % \begin{macro}{\idxbgcolor}\MainImpl{idxbgcolor}
 % Define the background color used for shaded boxes containing
 % song numbers, textual notes, and index section headers, respectively.
-% To turn off all shading for a box type, use |\def|\meta{macroname}|{}|.
+% To turn off all shading for a box type, use |\def|\Meta{macroname}|{}|.
 %    \begin{macrocode}
 \newcommand\snumbgcolor{SongbookShade}
 \newcommand\notebgcolor{SongbookShade}
@@ -4000,7 +4156,7 @@
 % \begin{macro}{\chorusjustify}\MainImpl{chorusjustify}
 % \changes{v2.1}{2007/08/02}{Added}
 % Verses and choruses are both left-justified with hanging indentation equal
-% to |\parindent|,
+% to |\parindent|.
 %    \begin{macrocode}
 \newcommand\versejustify{\justifyleft}
 \newcommand\chorusjustify{\justifyleft}
@@ -4119,8 +4275,8 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\chordlocals}\MainImpl{chordlocals}
 % \label{sec:chordlocals}
-% \begin{macro}{\chordlocals}\MainImpl{chordlocals}
 % This hook is expanded at the start of the scoping group that surrounds
 % every chord name.
 % Thus, it can be used to set any catcodes or definitions that should be
@@ -4136,7 +4292,7 @@
 % it by the end of the document preamble, it gets redefined to something
 % sensible based on other settings.
 %    \begin{macrocode}
-\newskip\versesep
+\newlength\versesep
 \versesep123456789sp\relax
 %    \end{macrocode}
 % \end{macro}
@@ -4147,9 +4303,9 @@
 % and postlude material from the body of the song by adjusting the following
 % two macros.
 %    \begin{macrocode}
-\newskip\afterpreludeskip
+\newlength\afterpreludeskip
 \afterpreludeskip=2\p@\@plus4\p@
-\newskip\beforepostludeskip
+\newlength\beforepostludeskip
 \beforepostludeskip=2\p@\@plus4\p@
 %    \end{macrocode}
 % \end{macro}
@@ -4161,7 +4317,7 @@
 % Setting this to zero accepts the default baseline distance computed by the
 % songs package.
 %    \begin{macrocode}
-\newskip\baselineadj
+\newlength\baselineadj
 \baselineadj\z at skip
 %    \end{macrocode}
 % \end{macro}
@@ -4267,11 +4423,11 @@
 % The default |\vbadness| setting starts issuing warnings at badness 1000,
 % so we set the penalties below to $1000-800=200$.
 %    \begin{macrocode}
-\newcount\vvpenalty\vvpenalty200
-\newcount\ccpenalty\ccpenalty200
-\newcount\vcpenalty\vcpenalty200
-\newcount\cvpenalty\cvpenalty200
-\newcount\brkpenalty\brkpenalty200
+\SB at newcount\vvpenalty\vvpenalty200
+\SB at newcount\ccpenalty\ccpenalty200
+\SB at newcount\vcpenalty\vcpenalty200
+\SB at newcount\cvpenalty\cvpenalty200
+\SB at newcount\brkpenalty\brkpenalty200
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -4287,7 +4443,7 @@
 % so for now it defaults to 100.
 % To start each song on a fresh column/page, set it to $-10000$ or below.
 %    \begin{macrocode}
-\newcount\spenalty\spenalty100
+\SB at newcount\spenalty\spenalty100
 %    \end{macrocode}
 % \end{macro}
 %
@@ -4296,9 +4452,9 @@
 % \begin{macro}{\versemark}\MainImpl{versemark}
 % \begin{macro}{\chorusmark}\MainImpl{chorusmark}
 % \changes{v2.1}{2007/08/02}{Added.}
-% The user can redefine the following macros to add \TeX\ marks for each
+% The user can redefine the following macros to add \TeX{} marks for each
 % song, each verse, or each chorus.
-% Such marks are used by \LaTeX\ to define page headers and footers.
+% Such marks are used by \LaTeX{} to define page headers and footers.
 %    \begin{macrocode}
 \newcommand\songmark{}
 \newcommand\versemark{}
@@ -4418,7 +4574,7 @@
 % Define the minimum number of fret rows that should appear in tablature
 % diagrams.
 %    \begin{macrocode}
-\newcount\minfrets\minfrets4
+\SB at newcount\minfrets\minfrets4
 %    \end{macrocode}
 % \end{macro}
 %
@@ -4428,7 +4584,7 @@
 % The user shouldn't set this one directly, but some users might want to
 % refer to it in calculations.
 %    \begin{macrocode}
-\newdimen\SB at colwidth
+\SB at newdimen\SB at colwidth
 %    \end{macrocode}
 % \end{macro}
 %
@@ -4512,7 +4668,7 @@
 % \begin{option}{unouter}\MainEnvImpl{unouter}
 % \begin{macro}{\SB at outer}
 % \optdef{off}
-% Several macros provided by the \Songs\ package are, by default, declared
+% Several macros provided by the \Songs{} package are, by default, declared
 % |\outer| to aid in debugging.
 % However, unusual documents may need to use these macros within larger
 % constructs.
@@ -4538,17 +4694,6 @@
 %    \end{macrocode}
 % \end{option}
 %
-% \begin{option}{nopdfindex}\MainEnvImpl{nopdfindex}
-% \optdef{off}
-% Inhibit the creation of the bookmark index in pdf files.
-% This option can only be set in the |\usepackage| line because initializing
-% the pdfbookmark library at all causes a (possibly empty) bookmark index to
-% be created.
-%    \begin{macrocode}
-\DeclareOption{nopdfindex}{\pdfindexfalse}
-%    \end{macrocode}
-% \end{option}
-%
 % \begin{option}{noshading}\MainEnvImpl{noshading}
 % \optdef{off}
 % Inhibit all shaded boxes (e.g., if the color package is unavailable).
@@ -4578,6 +4723,17 @@
 % \end{macro}
 % \end{option}
 %
+% \begin{option}{nopdfindex}\MainEnvImpl{nopdfindex}
+% \optdef{off}
+% Suppress creation of PDF bookmark entries and hyperlinks.
+%    \begin{macrocode}
+\DeclareOption{nopdfindex}{%
+  \let\songtarget\@gobbletwo%
+  \let\songlink\@secondoftwo%
+}
+%    \end{macrocode}
+% \end{option}
+%
 % \begin{macro}{\ifSB at measurespec}
 % \begin{macro}{\ifSB at chordedspec}
 % The |showmeasures| and |chorded| options interact in the sense that by
@@ -4585,9 +4741,9 @@
 % well.
 % However, if the user explicitly says that one should be on or off, then
 % switching the other shouldn't affect it.
-% To produce this behavior, we need two extra conditionals to remember if
-% each of these options has been explicitly specified by the user or if it
-% is still in a default state.
+% To produce this behavior, we need two extra conditionals to remember whether
+% each of these options has been explicitly specified by the user or whether
+% it is still in a default state.
 %    \begin{macrocode}
 \newif\ifSB at measurespec
 \newif\ifSB at chordedspec
@@ -4807,7 +4963,7 @@
 % \changes{v2.1}{2007/08/02}{Added.}
 % Using |\repchoruses| causes choruses to be automatically repeated on
 % subsequent pages of the song.
-% The feature requires $\varepsilon$-\TeX\ because the supporting code needs
+% The feature requires $\varepsilon$-\TeX{} because the supporting code needs
 % an extended mark register class.
 %    \begin{macrocode}
 \ifSB at etex
@@ -4875,7 +5031,7 @@
 % The most recently processed song (or scripture quotation) is stored in this
 % box.
 %    \begin{macrocode}
-\newbox\SB at songbox
+\SB at newbox\SB at songbox
 %    \end{macrocode}
 % \end{macro}
 %
@@ -4884,8 +5040,8 @@
 % Reserve two count registers to hold the total number of columns and the
 % current column number, respectively.
 %    \begin{macrocode}
-\newcount\SB at numcols\SB at numcols\tw@
-\newcount\SB at colnum
+\SB at newcount\SB at numcols\SB at numcols\tw@
+\SB at newcount\SB at colnum
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -4893,14 +5049,14 @@
 % \begin{macro}{\SB at colbox}
 % Reserve a box register to hold the current column in progress.
 %    \begin{macrocode}
-\newbox\SB at colbox
+\SB at newbox\SB at colbox
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}{\SB at colbox}
+% \begin{macro}{\SB at pgbox}
 % Reserve a box register to hold the current page in progress.
 %    \begin{macrocode}
-\newbox\SB at pgbox
+\SB at newbox\SB at pgbox
 %    \end{macrocode}
 % \end{macro}
 %
@@ -4908,7 +5064,7 @@
 % Reserve a box register to hold marks that migrate out of songs as they
 % get split into columns and pages.
 %    \begin{macrocode}
-\newbox\SB at mrkbox
+\SB at newbox\SB at mrkbox
 %    \end{macrocode}
 % \end{macro}
 %
@@ -4931,51 +5087,69 @@
 % process.
 % The macro arguments are:
 % \begin{enumerate}
-% \item the box $b$ to split (must not be |\SB at box|, which is used as a
-% temp register),
-% \item a count register $i$ equaling column index (zero or greater)
-% where the content of $b$ is to begin, and
+% \item an integer (positive or zero) indicating whether box $b$ should be
+% fully emptied and committed as columns (if positive), or whether its
+% final less-than-column-height remainder should be reserved as an in-progress
+% column (if zero);
+% \item the box $b$ to split;
+% \item a count register $i$ equaling the column index (zero or greater)
+% where the content of $b$ is to begin; and
 % \item the desired column height.
 % \end{enumerate}
 % Box $b$ is split and $i$ is incremented until $i$ reaches
-% |\SB at numcols| or $b$ is emptied, whichever occurs first.
-% If $b$ is emptied first, the final column is \emph{not} contributed;
-% instead it is left in $b$ and $i$ is left equal to the index
+% |\SB at numcols| or $b$ is emptied.
+% If $b$ is emptied and the first argument is 0, the final column is \emph{not}
+% contributed; instead it is left in $b$ and $i$ is left equal to the index
 % of the column that would have been added if $b$ had been emptied.
 % This allows the next call to reconsider whether to end the
 % current column here or add some or all of the next contribution to it.
+% Otherwise, if $b$ is emptied and the first argument is positive, the final
+% column is contributed and $i$ is set to one greater than the index of that
+% column.
+% (If $i$ reaches |\SB at numcols| before $b$ is emptied, the first argument is
+% ignored.)
+%
 % Box $b$ and count register $i$ are globally modified.
 % If |\SB at updatepage| is not redefined, boxes |\SB at pgbox| and |\SB at mrkbox|
 % are also globally modified based on the results of the split.
+%
+% The implementation takes two special steps to avoid pre-committing
+% in-progress columns (when the first macro argument is zero):
+% First, the final split that empties box $b$ is ``undone'' by reverting to a
+% backup copy made before each split.
+% Second, any underfull box warnings for this final split are suppressed by
+% temporarily adding infinite-stretch |\vfil| glue to the bottom of the box.
+% This strategy preserves underfull and overfull box warnings for the columns
+% that are actually committed, but suppresses faux warnings for the last split
+% that is undone.
 %    \begin{macrocode}
-\newcommand\SB at mkpage[3]{%
-  \begingroup%
+\newcommand\SB at mkpage[4]{%
+  \ifvoid#2\else\begingroup%
+    \edef\SB at temp{\ifnum#2=\SB at box\SB at boxii\else\SB at box\fi}%
+    \edef\SB at tempii{\ifnum#2=\SB at boxiii\SB at boxii\else\SB at boxiii\fi}%
     \splitmaxdepth\maxdepth\splittopskip\z at skip%
-    \global\setbox#1\vbox{%
-      \unvbox#1%
-      \nointerlineskip%
-      \null%
-      \vfil%
-    }%
-    \loop\ifnum#2<\SB at numcols%
-      \setbox\SB at box\vsplit#1to#3\relax%
-      \ifvoid#1%
-        #2\SB at numcols%
+    \ifnum#1=\z@\global\setbox#2\vbox{\unvbox#2\vfil}\fi%
+    \loop\ifnum#3<\SB at numcols%
+      \ifnum#1=\z@\setbox\SB at tempii\copy#2\fi%
+      \setbox\SB at temp\vsplit#2to#4\relax%
+      \ifvoid#2%
+        \ifnum#1=\z@%
+          \global\setbox#2\box\SB at tempii%
+        \else%
+          \SB at updatepage%
+          \global\advance#3\@ne%
+        \fi%
+        #3\SB at numcols%
       \else%
         \SB at updatepage%
-        \global\advance#2\@ne%
+        \global\advance#3\@ne%
         \ifrepchorus\ifvoid\SB at chorusbox\else%
-          \SB at insertchorus#1%
+          \SB at insertchorus#2%
         \fi\fi%
       \fi%
     \repeat%
-    \global\setbox#1\vbox{%
-      \unvbox\SB at box%
-      \unvbox#1%
-      \unskip%
-      \setbox\SB at box\lastbox%
-    }%
-  \endgroup%
+    \ifnum#1=\z@\global\setbox#2\vbox{\unvbox#2\unskip}\fi%
+  \endgroup\fi%
 }
 %    \end{macrocode}
 % \end{macro}
@@ -5009,7 +5183,7 @@
     \advance\SB at dimen-\wd\SB at pgbox%
     \unhbox\SB at pgbox%
     \ifdim\SB at dimen=\z@\else\hskip\SB at dimen\relax\fi%
-    \box\SB at box%
+    \box\SB at temp%
   }%
 }
 %    \end{macrocode}
@@ -5021,7 +5195,7 @@
 % This allows |\SB at mkpage| to be called by the song-positioning algorithm
 % as a trial run without outputting anything.
 %    \begin{macrocode}
-\newcommand\SB at droppage{\setbox\SB at box\box\voidb at x}
+\newcommand\SB at droppage{\setbox\SB at temp\box\voidb at x}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -5030,22 +5204,31 @@
 % It repeatedly calls |\SB at mkpage|, emitting pages as they are completed,
 % until the remaining content of box |\SB at colbox| is not enough to fill a
 % column.
-% This final, in-progress column is left unfinished, pending future
-% contributions.
+% If the macro argument is 0, this final, in-progress column is left
+% unfinished, pending future contributions.
+% If the argument is positive, the final material is committed as a column.
+% If the argument is two or greater, the entire in-progress page is also
+% committed and the column number reset.
 %    \begin{macrocode}
-\newcommand\SB at output{%
+\newcommand\SB at output[1]{%
   \ifnum\SB at numcols>\z@\begingroup%
     \loop%
       \SB at dimen\textheight%
       \ifinner\else\advance\SB at dimen-\pagetotal\fi%
-      \SB at mkpage\SB at colbox\SB at colnum\SB at dimen%
-      \ifnum\SB at colnum<\SB at numcols\else%
+      \SB at mkpage#1\SB at colbox\SB at colnum\SB at dimen%
+      \SB at testfalse\SB at testiitrue%
+      \ifnum#1>\@ne\ifvoid\SB at colbox\ifnum\SB at colnum>\z@%
+        \SB at testtrue\SB at testiifalse%
+      \fi\fi\fi%
+      \ifnum\SB at colnum<\SB at numcols\SB at testiifalse\else\SB at testtrue\fi%
+      \ifSB at test%
         \unvbox\SB at mrkbox%
         \ifinner\else\kern\z@\fi%
         \box\SB at pgbox%
         \ifinner\else\vfil\break\vskip\vsize\relax\fi%
         \global\SB at colnum\z@%
-    \repeat%
+      \fi%
+    \ifSB at testii\repeat%
   \endgroup\else%
     \unvbox\SB at colbox\unskip%
   \fi%
@@ -5056,7 +5239,7 @@
 % \begin{macro}{\SB at putboxes}
 % Create a vertical list consisting of the already committed contents of the
 % current column plus the most recently submitted song box.
-% The \LaTeX\ primitive that should be used to contribute each box is
+% The \LaTeX{} primitive that should be used to contribute each box is
 % specified in the first argument.
 %    \begin{macrocode}
 \newcommand\SB at putboxes[1]{%
@@ -5105,7 +5288,7 @@
           \advance\SB at cnt\m at ne%
         \repeat%
       }%
-      \SB at output%
+      \SB at output1%
     \else%
       \ifnum\lastpenalty=-\@M\null\fi%
       \break%
@@ -5145,7 +5328,7 @@
     \ifinner\else\advance\SB at dimen-\pagetotal\fi%
     \setbox\SB at boxii\vbox{\SB at putboxes\unvcopy}%
     \SB at cntii\SB at colnum%
-    \SB at mkpage\SB at boxii\SB at cntii\SB at dimen%
+    \SB at mkpage0\SB at boxii\SB at cntii\SB at dimen%
     \SB at spos%
     \global\SB at cnt\SB at cnt%
   \endgroup%
@@ -5161,7 +5344,7 @@
 \newcommand\SB at spbegnew{%
   \setbox\SB at boxiii\copy\SB at songbox%
   \SB at cntii\z@%
-  \SB at mkpage\SB at boxiii\SB at cntii\textheight%
+  \SB at mkpage0\SB at boxiii\SB at cntii\textheight%
 }
 %    \end{macrocode}
 % \end{macro}
@@ -5168,7 +5351,7 @@
 %
 % \begin{macro}{\SB at spextold}
 % Tentatively extend the song previously typeset on the current even page to
-% the next odd page to see if it fits on a double-page.
+% the next odd page to see whether it fits on a double-page.
 % If the current page is odd-numbered, do nothing since extending the song
 % to the next page would introduce a page-turn.
 %    \begin{macrocode}
@@ -5175,7 +5358,7 @@
 \newcommand\SB at spextold{%
   \ifodd\c at page\else%
     \SB at cntii\z@%
-    \SB at mkpage\SB at boxii\SB at cntii\textheight%
+    \SB at mkpage0\SB at boxii\SB at cntii\textheight%
   \fi%
 }
 %    \end{macrocode}
@@ -5183,11 +5366,11 @@
 %
 % \begin{macro}{\SB at spextnew}
 % Extend the trial typesetting started with |\SB at spbegnew| to a second
-% page to see if the song fits on a fresh double-page.
+% page to see whether the song fits on a fresh double-page.
 %    \begin{macrocode}
 \newcommand\SB at spextnew{%
   \SB at cntii\z@%
-  \SB at mkpage\SB at boxiii\SB at cntii\textheight%
+  \SB at mkpage0\SB at boxiii\SB at cntii\textheight%
 }
 %    \end{macrocode}
 % \end{macro}
@@ -5267,7 +5450,7 @@
     \advance\SB at cnt\@ne%
     \ifnum\SB at cnt<\SB at numcols%
       \setbox\SB at boxiii\copy\SB at songbox%
-      \SB at mkpage\SB at boxiii\SB at cnt\SB at dimen%
+      \SB at mkpage0\SB at boxiii\SB at cnt\SB at dimen%
       \advance\SB at cnt\m at ne%
     \fi%
     \ifnum\SB at cnt>\SB at colnum%
@@ -5319,14 +5502,21 @@
 % \begin{macro}{\SB at clearpage}
 % Output all contributed material as a new page unless there is no contributed
 % material. In that case do nothing (i.e., don't produce a blank page).
+% The |\SB at colbox| is tested for zero height and depth rather than voidness,
+% since sometimes it contains zero-length |\splittopskip| glue.
 %    \begin{macrocode}
 \newcommand\SB at clearpage{%
   \SB at testtrue%
-  \ifvoid\SB at pgbox\ifvoid\SB at colbox\SB at testfalse\fi\fi%
+  \ifvoid\SB at pgbox%
+    \ifdim\ht\SB at colbox=\z@\ifdim\dp\SB at colbox=\z@%
+      \SB at testfalse%
+    \fi\fi%
+  \fi%
   \ifSB at test%
     \SB at cnt\SB at numcols%
     \advance\SB at cnt-\SB at colnum%
     \SB at nextcol\SB at cnt\lastcolglue%
+    \SB at output2%
   \fi%
 }
 %    \end{macrocode}
@@ -5344,6 +5534,7 @@
   \SB at clearpage%
   \if at twoside\ifodd\c at page%
     \SB at nextcol\SB at numcols\@flushglue%
+    \SB at output2%
   \fi\fi%
 }
 %    \end{macrocode}
@@ -5371,7 +5562,7 @@
   \ifnum\SB at numcols>\z@%
     \SB at selectcol%
     \global\setbox\SB at colbox\vbox{\SB at putboxes\unvbox}%
-    \SB at output%
+    \SB at output0%
   \else%
     \unvbox\voidb at x%
     \SB at breakpoint\spenalty%
@@ -5387,15 +5578,26 @@
 % \begin{macro}{\SB at styppage}
 % Page-submissions go directly to the top of the nearest fresh page unless
 % the current page has all page-width material so far.
+%
+% Implementation notes:
+% The |\null| is needed because the page builder consults |\pagetotal|,
+% which isn't updated by \TeX{} until a box is contributed (|\unvbox| doesn't
+% count).
+% Both |\nointerlineskip|s are needed because |\unvbox| fails to update
+% |\prevdepth|, and it doesn't make sense to inherit its value from whatever
+% preceeded this contribution.
+% Authors who want interline glue must therefore insert it explicitly at the
+% bottom of their contributed text.
 %    \begin{macrocode}
 \newcommand\SB at styppage{%
   \ifnum\SB at numcols>\z@%
     \SB at clearpage%
     \unvbox\SB at songbox%
-    \null\nointerlineskip%
+    \nointerlineskip\null%
   \else%
     \unvbox\SB at songbox%
   \fi%
+  \nointerlineskip%
 }
 %    \end{macrocode}
 % \end{macro}
@@ -5420,10 +5622,30 @@
 % The song itself has number 0.
 % Environments that come after the song are numbered increasingly from 1.
 %    \begin{macrocode}
-\newcount\SB at groupcnt
+\SB at newcount\SB at groupcnt
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\SB at clearpboxes}
+% This dynamically constructed macro clears the content of all boxes created
+% by the workings of |\includeonlysongs|.
+%    \begin{macrocode}
+\newcommand\SB at clearpboxes{}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SB at partbox}
+% Save a box of full-song or chorus material for later output when producing
+% a partial list using |\includeonlysongs|.
+%    \begin{macrocode}
+\newcommand\SB at partbox[1]{%
+  \SB at newbox#1%
+  \SB at app\gdef\SB at clearpboxes{\setbox#1\box\voidb at x}%
+  \global\setbox#1\box%
+}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\SB at submitpart}
 % When a song completes and we're generating a partial list, save the song
 % in a box so that it can be submitted at the end of the section in the
@@ -5431,21 +5653,19 @@
 %    \begin{macrocode}
 \newcommand\SB at submitpart{%
   \ifx\SB at sgroup\@empty\else%
-    \@for\SB at temp:=\songlist\do{%
-      \ifx\SB at temp\SB at sgroup%
-        \edef\SB at tempii{\SB at sgroup @\the\SB at groupcnt}%
-        \expandafter\newbox\csname songbox@\SB at tempii\endcsname%
-        \global\expandafter\setbox
-          \csname songbox@\SB at tempii\endcsname\box\SB at songbox%
-        \global\expandafter\let%
-          \csname stype@\SB at tempii\endcsname\SB at stype%
-        \ifrepchorus\ifvoid\SB at chorusbox\else%
-          \expandafter\newbox\csname chbox@\SB at tempii\endcsname%
-          \global\expandafter\setbox%
-            \csname chbox@\SB at tempii\endcsname\box\SB at chorusbox%
-        \fi\fi%
-      \fi%
-    }%
+    \SB at testfalse
+    \@for\SB at temp:=\songlist\do{\ifx\SB at temp\SB at sgroup\SB at testtrue\fi}%
+    \ifSB at test%
+      \edef\SB at tempii{\SB at sgroup @\the\SB at groupcnt}%
+      \expandafter\SB at partbox
+        \csname songbox@\SB at tempii\endcsname\SB at songbox%
+      \global\expandafter\let%
+        \csname stype@\SB at tempii\endcsname\SB at stype%
+      \ifrepchorus\ifvoid\SB at chorusbox\else%
+        \expandafter\SB at partbox
+	  \csname chbox@\SB at tempii\endcsname\SB at chorusbox%
+      \fi\fi%
+    \fi%
     \global\advance\SB at groupcnt%
       \ifnum\SB at groupcnt<\z@\m at ne\else\@ne\fi%
   \fi%
@@ -5469,6 +5689,18 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\SB at submitenv}
+% Submit the |\SB at envbox| box as a page-width contribution.
+%    \begin{macrocode}
+\newcommand\SB at submitenv{%
+  \begingroup%
+    \let\SB at songbox\SB at envbox%
+    \SB at styppage%
+  \endgroup%
+}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\SB at songlistbrk}
 % \begin{macro}{\SB at songlistnc}
 % \begin{macro}{\SB at songlistcp}
@@ -5529,6 +5761,7 @@
         \fi\fi\fi\fi%
       }%
     \fi%
+    \SB at clearpboxes%
   \fi%
   \SB at clearpage%
 }
@@ -5544,13 +5777,13 @@
   \loop\edef\SB at tempii{\SB at temp @\the\SB at groupcnt}%
        \expandafter\ifx%
          \csname songbox@\SB at tempii\endcsname\relax\else%
-    \setbox\SB at songbox\expandafter\box%
+    \setbox\SB at songbox\expandafter\copy%
         \csname songbox@\SB at tempii\endcsname%
     \expandafter\ifx\csname chbox@\SB at tempii\endcsname\relax%
       \repchorusfalse%
     \else%
       \repchorustrue%
-      \setbox\SB at chorusbox\expandafter\box%
+      \setbox\SB at chorusbox\expandafter\copy%
         \csname chbox@\SB at tempii\endcsname%
     \fi%
     \csname stype@\SB at tempii\endcsname%
@@ -5701,8 +5934,8 @@
 % These registers hold the full list of titles for the current song and
 % the tail list of titles that has not yet been iterated over.
 %    \begin{macrocode}
-\newtoks\SB at titlelist
-\newtoks\SB at titletail
+\SB at newtoks\SB at titlelist
+\SB at newtoks\SB at titletail
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -5829,8 +6062,7 @@
 %    \begin{macrocode}
 \newcommand\SB at clearbskeys{}
 \newcommand\newsongkey[2]{%
-  \expandafter\gdef\expandafter\SB at clearbskeys\expandafter%
-    {\SB at clearbskeys#2}%
+  \SB at app\gdef\SB at clearbskeys{#2}%
   \define at key{beginsong}{#1}%
 }
 %    \end{macrocode}
@@ -5919,7 +6151,7 @@
     \nexttitle%
     \foreachtitle{\expandafter\SB at addtotitles\expandafter{\songtitle}}%
     \resettitles%
-    \lyricfont%
+    \lyricfont\relax%
     \SB at setbaselineskip%
 }
 %    \end{macrocode}
@@ -5949,42 +6181,47 @@
         \fi\fi%
       \fi\fi%
     \endgroup\egroup%
-    \setbox\SB at songbox\vbox{%
-      \songmark%
-      \unvbox\SB at songwrites%
-      \ifnum\SB at numcols>\z@\hsize\SB at colwidth\fi%
+    \begingroup%
+      \ifnum\SB at numcols>\z@%
+        \hsize\ifpagepreludes\textwidth\else\SB at colwidth\fi%
+      \fi%
       \leftskip\z at skip\rightskip\z at skip%
       \parfillskip\@flushglue\parskip\z at skip\parindent\z@%
-      \ifdim\sbarheight>\z@%
-        \hrule\@height\sbarheight\@width\hsize%
-        \nobreak\vskip5\p@%
-      \fi%
-      \ifpdfindex\begingroup%
-        \ifx\pdfbookmark\undefined\else%
-        \ifx\pdfbookmark\relax\else%
-          \resettitles%
-          \pdfbookmark[\ifnum\c at section=\z at 1\else2\fi]%
-            {\thesongnum. \songtitle}%
-            {song\theSB at songsnum-\thesongnum}%
+      \global\setbox\SB at envbox\vbox{%
+        \songmark%
+        \unvbox\SB at songwrites%
+        \ifpagepreludes\else\ifdim\sbarheight>\z@%
+          \hrule\@height\sbarheight\@width\hsize%
+          \nobreak\vskip5\p@\relax%
         \fi\fi%
-      \endgroup\fi%
-      \vbox{\makeprelude}%
-      \nobreak\vskip\SB at skip%
-      \vskip\afterpreludeskip\relax%
-      \unvbox\SB at songbox%
-      \nobreak\vskip\SB at skip%
-      \vskip\beforepostludeskip\relax%
-      \nointerlineskip%
-      \vbox{\makepostlude}%
-      \ifdim\sbarheight>\z@%
-        \nobreak\vskip2\p@\@plus\p@%
+        \resettitles%
+        \begingroup%
+          \songtarget{\ifnum\c at section=\z at 1\else2\fi}%
+                     {song\theSB at songsnum-\thesongnum}%
+        \endgroup%
+        \vbox{\makeprelude}%
+        \nobreak\vskip\SB at skip%
+        \vskip\afterpreludeskip\relax%
+      }%
+      \ifnum\SB at numcols>\z@\hsize\SB at colwidth\fi%
+      \global\setbox\SB at songbox\vbox{%
+        \ifpagepreludes\else\unvbox\SB at envbox\fi%
+        \unvbox\SB at songbox%
+        \nobreak\vskip\SB at skip%
+        \vskip\beforepostludeskip\relax%
         \nointerlineskip%
-        \hbox{\vrule\@height\sbarheight\@width\hsize}%
-      \fi%
-    }%
+        \vbox{\makepostlude}%
+        \ifdim\sbarheight>\z@%
+          \nobreak\vskip2\p@\@plus\p@%
+          \nointerlineskip%
+          \hbox{\vrule\@height\sbarheight\@width\hsize}%
+        \fi%
+      }%
+    \endgroup%
     \SB at insongfalse%
     \edef\SB at sgroup{\thesongnum}%
     \global\SB at groupcnt\z@%
+    \ifpagepreludes\SB at submitenv\fi%
     \SB at submitsong%
     \ifnum\SB at grouplvl=\z@\let\SB at sgroup\@empty\fi%
     \stepcounter{songnum}%
@@ -6031,7 +6268,7 @@
   \edef\SB at tempii{\the\versesep}%
   \ifx\SB at temp\SB at tempii%
     \begingroup%
-      \lyricfont%
+      \lyricfont\relax%
       \SB at dimen\f at size\p@%
       \ifchorded%
         \setbox\SB at box\hbox{{\printchord{ABCDEFG\shrp\flt/j7}}}%
@@ -6056,13 +6293,26 @@
 % Generate the material that begins each song.
 % This macro is invoked at |\endsong| so that its code can access song info
 % defined throughout the song.
+%
+% Note that if you are redefining |\makeprelude|, you can probably replace
+% everything below with something much simpler.
+% The code below is lengthy because it accommodates all of the many different
+% options that various authors may adjust to customize their books.
+% If you redefine it, you can replace all of this with smaller, more
+% specialized programming that just outputs the prelude format you desire. 
 %    \begin{macrocode}
 \newcommand\makeprelude{%
   \resettitles%
+%    \end{macrocode}
+% In slides mode, the title, references, and authors are simply centered on
+% the page with no song number.
+% Only the first of the song titles is included.
+% The references and authors only span the middle 50\% of the page, since
+% letting them span the whole page width stretches them out too much and makes
+% their fine print too hard to read.
+%    \begin{macrocode}
   \ifslides%
-    \hbox to\hsize{{%
-      \hfil\stitlefont\songtitle\hfil%
-    }}%
+    \hbox to\hsize{{\hfil\stitlefont\relax\songtitle\hfil}}%
     \vskip5\p@%
     \hbox to\hsize{%
       \hfil%
@@ -6073,6 +6323,16 @@
       \hfil%
     }%
   \else%
+%    \end{macrocode}
+% In non-slides mode, we write the song number in a shaded box to the left
+% (if |\songnumwidth| is positive) and everything else in left-justified
+% paragraphs to the right of it (or centered if |\pagepreludes| is on).
+% The height of the shaded box that contains the song number depends on
+% which is higher: the natural height of the song number, or everything else
+% that goes to the right of it.
+% To find out which is higher, we start by putting the song number in its
+% own box (|\SB at boxii|).
+%    \begin{macrocode}
     \ifdim\songnumwidth>\z@%
       \setbox\SB at boxii\hbox{{\SB at colorbox\snumbgcolor{%
         \hbox to\songnumwidth{%
@@ -6080,14 +6340,22 @@
         }%
       }}}%
     \fi%
+%    \end{macrocode}
+% Now we know the width $w$ of the song number box, so we typeset everything
+% else in a box (|\SB at box|) of width $c-w$, where $c$ is the column width.
+% (If |\pagepreludes| is on, we instead use width $c-2w$ so that the material
+% stays centered on the page.)
+%    \begin{macrocode}
     \setbox\SB at box\vbox{%
-      \ifnum\SB at numcols>\z@\hsize\SB at colwidth\fi%
       \ifdim\songnumwidth>\z@%
-        \advance\hsize-\wd\SB at boxii%
-        \advance\hsize-3\p@%
+        \SB at dimen\wd\SB at boxii%
+        \advance\SB at dimen3\p@%
+        \ifpagepreludes\multiply\SB at dimen\tw@\fi%
+        \advance\hsize-\SB at dimen%
       \fi%
-      \SB at raggedright\offinterlineskip\lineskip\p@%
-      {\stitlefont%
+      \ifpagepreludes\centering\else\SB at raggedright\fi%
+      \offinterlineskip\lineskip\p@%
+      {\stitlefont\relax%
        \songtitle\par%
        \nexttitle%
        \foreachtitle{(\songtitle)\par}}%
@@ -6096,6 +6364,16 @@
       \extendprelude%
       \kern\z@%
     }%
+%    \end{macrocode}
+% If the song number is being printed (i.e., |\songnumwidth| is positive),
+% and its height is greater than the height of the other material, then we
+% just put |\SB at boxii| and |\SB at box| side-by-side.
+% If the song number is being printed but its height is less, then we
+% re-typeset it at height equal to the other material, and place the boxes
+% side-by-side.
+% Finally, if the song number is not being printed at all, we just unbox
+% |\SB at box| onto the vertical list.
+%    \begin{macrocode}
     \ifdim\songnumwidth>\z@%
       \hbox{%
         \ifdim\ht\SB at boxii>\ht\SB at box%
@@ -6124,6 +6402,9 @@
 % \changes{v1.15}{2005/05/26}{Added to make song trailer format customizable.}
 % \changes{v2.0}{2007/06/18}{Arguments removed to support keyval syntax.}
 % Generate the material that ends each song.
+% The default implementation just prints the copyright and licensing
+% information (if any) as a single, left-justified, non-indentended paragraph
+% in fine print.
 %    \begin{macrocode}
 \newcommand\makepostlude{%
   \SB at raggedright\baselineskip\z at skip\parskip\z at skip\parindent\z@%
@@ -6133,7 +6414,11 @@
 % \end{macro}
 %
 % \begin{macro}{\showauthors}\MainImpl{showauthors}
-% Display the author line in the prelude.
+% Display the author information in the prelude.
+% This macro is only called by |\extendprelude|, which is only called by
+% |\makeprelude|; so if you redefine either of those, you don't need this.
+% The default implementation prints the authors in boldface and shortens the
+% spacing after periods so that they don't look like ends of sentences.
 %    \begin{macrocode}
 \newcommand\showauthors{%
   \setbox\SB at box\hbox{\bfseries\sfcode`.\@m\songauthors}%
@@ -6144,6 +6429,10 @@
 %
 % \begin{macro}{\showrefs}\MainImpl{showrefs}
 % Display the scripture references in the prelude.
+% This macro is only called by |\extendprelude|, which is only called by
+% |\makeprelude|; so if you redefine either of those, you don't need this.
+% The default implementation prints the scripture references in slanted
+% (oblique) font.
 %    \begin{macrocode}
 \newcommand\showrefs{%
   \setbox\SB at box\hbox{\slshape\songrefs\vphantom,}%
@@ -6183,7 +6472,7 @@
 % into a larger macro is harder than it seems:
 % If you write the following code but with an explicit control sequence
 % instead of |#1|, then the space immediately following the name will get
-% stripped by the \TeX\ parser.
+% stripped by the \TeX{} parser.
 % But invoking the following macro with a control sequence as an argument
 % works fine, because in that case the explicit space has already been
 % tokenized when this macro was first defined and won't be stripped as it
@@ -6204,36 +6493,41 @@
     \global\SB at titlelist{\\}%
     \SB at toks{}%
     \let\\\SB at titlesep%
-    \let\SB at dothis\SB at pthead%
-    \SB at ptstart#1\SB at endparse%
+    \SB at pthead#1\SB at endparse%
   \endgroup%
 }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}{\SB at ptstart}
-% The iterator of the title parser loop just scans the next token.
-%    \begin{macrocode}
-\newcommand\SB at ptstart{\futurelet\SB at next\SB at dothis}
-%    \end{macrocode}
-% \end{macro}
-%
 % \begin{macro}{\SB at pthead}
+% \begin{macro}{\SB@@pthead}
+% \begin{macro}{\SB@@@pthead}
 % While processing tokens at the head of a title, we skip over all spaces
 % until we reach a non-space token.
 %    \begin{macrocode}
-\newcommand\SB at pthead{%
-  \ifcat\noexpand\SB at next\noexpand\@sptoken%
-    \expandafter\SB at ptsp%
+\newcommand\SB at pthead{\futurelet\SB at next\SB@@pthead}
+\newcommand\SB@@pthead{%
+  \ifcat\noexpand\SB at next\@sptoken%
+    \expandafter\SB@@@pthead%
   \else%
-    \SB at toks{}%
-    \let\SB at dothis\SB at ptmain%
     \expandafter\SB at ptmain%
   \fi%
 }
+\newcommand\SB@@@pthead{%
+  \afterassignment\SB at pthead%
+  \let\SB at next= }
 %    \end{macrocode}
 % \end{macro}
+% \end{macro}
+% \end{macro}
 %
+% \begin{macro}{\SB at ptloop}
+% The iterator of the title parser loop just scans the next token.
+%    \begin{macrocode}
+\newcommand\SB at ptloop{\futurelet\SB at next\SB at ptmain}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\SB at ptmain}
 % Once we've reached a non-space token in the title, we consume the remainder
 % of the title as-is, except that space tokens should be trimmed from the end
@@ -6240,22 +6534,20 @@
 % of each title.
 %    \begin{macrocode}
 \newcommand\SB at ptmain{%
-  \ifcat\noexpand\SB at next\noexpand\@sptoken%
+  \ifcat\noexpand\SB at next\@sptoken%
     \let\SB at donext\SB at ptsp%
-  \else\ifcat\noexpand\SB at next\noexpand\bgroup%
+  \else\ifcat\noexpand\SB at next\bgroup%
     \let\SB at donext\SB at ptbg%
   \else\ifx\SB at next\SB at endparse%
     \global\SB at titlelist\expandafter{\the\SB at titlelist\\}%
     \let\SB at donext\@gobble%
+  \else\ifx\SB at next\\%
+    \SB at toks{}%
+    \def\SB at donext{\SB at ptstep\SB at pthead}%
   \else%
-    \ifx\SB at next\\%
-      \SB at toks{}%
-      \let\SB at dothis\SB at pthead%
-    \fi%
-    \let\SB at donext\SB at ptstep%
-  \fi\fi\fi%
-  \SB at donext%
-}
+    \def\SB at donext{\SB at ptstep\SB at ptloop}%
+  \fi\fi\fi\fi%
+  \SB at donext}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -6264,12 +6556,11 @@
 % title.
 % If any spaces preceded it, add those too.
 %    \begin{macrocode}
-\newcommand\SB at ptstep[1]{%
+\newcommand\SB at ptstep[2]{%
   \global\SB at titlelist\expandafter\expandafter\expandafter{%
-    \expandafter\the\expandafter\SB at titlelist\the\SB at toks#1}%
+    \expandafter\the\expandafter\SB at titlelist\the\SB at toks#2}%
   \SB at toks{}%
-  \SB at ptstart%
-}
+  #1}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -6278,7 +6569,7 @@
 % It should be balanced, so consume the entire group and add it (along with
 % its surrounding braces) as-is to the current title.
 %    \begin{macrocode}
-\newcommand\SB at ptbg[1]{\SB at ptstep{{#1}}}
+\newcommand\SB at ptbg[1]{\SB at ptstep\SB at ptloop{{#1}}}
 %    \end{macrocode}
 % \end{macro}
 %
@@ -6293,7 +6584,7 @@
 %    \begin{macrocode}
 \newcommand\SB at ptsp{
   \SB at appendsp\SB at toks%
-  \afterassignment\SB at ptstart%
+  \afterassignment\SB at ptloop%
   \let\SB at next= }
 %    \end{macrocode}
 % \end{macro}
@@ -6318,32 +6609,6 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}{\SB at testdigit}
-% \begin{macro}{\SB@@testdigit}
-% The following decides whether a token or |\let|-defined control sequence
-% is a digit and sets conditional |\ifSB at test| accordingly.
-%    \begin{macrocode}
-\newcommand\SB at testdigit[1]{%
-  \SB at testfalse%
-  \ifcat1\noexpand#1\SB@@testdigit#1\fi%
-}
-\newcommand\SB@@testdigit[1]{%
-  \ifx0#1\SB at testtrue\else%
-  \ifx1#1\SB at testtrue\else%
-  \ifx2#1\SB at testtrue\else%
-  \ifx3#1\SB at testtrue\else%
-  \ifx4#1\SB at testtrue\else%
-  \ifx5#1\SB at testtrue\else%
-  \ifx6#1\SB at testtrue\else%
-  \ifx7#1\SB at testtrue\else%
-  \ifx8#1\SB at testtrue\else%
-  \ifx9#1\SB at testtrue%
-  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi%
-}
-%    \end{macrocode}
-% \end{macro}
-% \end{macro}
-%
 % \begin{macro}{\SB at parsesrefs}
 % \changes{v1.14}{2005/05/15}{Added}
 % Assign the |\songrefs| macro a processed version of a scripture reference in
@@ -6363,10 +6628,10 @@
 % Unfortunately, the catcodes of everything in the text were set back when
 % the full keyval list was digested as an argument to |\beginsong|, so we
 % must unset and reset the catcodes.
-% One obvious solution is to use |\scantokens| from $\varepsilon$-\TeX\ to
+% One obvious solution is to use |\scantokens| from $\varepsilon$-\TeX{} to
 % do this, but that doesn't allow us to suppress the re-catcoding process
 % within groups, and we'd like to avoid intoducing features that require
-% $\varepsilon$-\TeX\ anyway for compatibility reasons.
+% $\varepsilon$-\TeX{} anyway for compatibility reasons.
 % Therefore, we build the following small scanner instead.
 %
 % The scanner walks through the text token by token, replacing each important
@@ -6402,13 +6667,13 @@
   \fi%
 }
 \newcommand\SB@@prstep{%
-  \ifcat\noexpand\SB at next\noexpand\@sptoken%
+  \ifcat\noexpand\SB at next\@sptoken%
     \let\SB at donext\SB at prspace%
   \else\ifx\SB at next-%
     \let\SB at donext\SB at prhyphen%
   \else\ifx\SB at next,%
     \let\SB at donext\SB at prcomma%
-  \else\ifx\SB at next\SB at endparse
+  \else\ifx\SB at next\SB at endparse%
     \let\SB at donext\@gobble%
   \else\ifcat\noexpand\SB at next\bgroup%
     \let\SB at donext\SB at prgr%
@@ -6644,13 +6909,13 @@
 % Unbox a vbox and follow it by vertical glue if its depth is unusually
 % shallow.
 % This ensures that verses and choruses will look equally spaced even if
-% one of them has a final line with no letters that dangle below the baseline.
+% one of them has a final line with no descenders.
 %    \begin{macrocode}
 \newcommand\SB at putbox[2]{%
   \begingroup%
     \SB at dimen\dp#2%
     #1#2%
-    \setbox\SB at box\hbox{{\lyricfont p}}%
+    \setbox\SB at box\hbox{{\lyricfont\relax p}}%
     \ifdim\SB at dimen<\dp\SB at box%
       \advance\SB at dimen-\dp\SB at box%
       \vskip-\SB at dimen%
@@ -6663,14 +6928,14 @@
 %
 % \begin{macro}{\SB at obeylines}
 % Within verses and choruses we would like to use |\obeylines| so that each
-% \meta{return} in the source file ends a paragraph without having to say
+% \Meta{return} in the source file ends a paragraph without having to say
 % |\par| explicitly.
-% The \LaTeX\ base code establishes the convention that short-term changes to
+% The \LaTeX{} base code establishes the convention that short-term changes to
 % |\par| will restore |\par| by setting it equal to |\@par|.
 % Long-term (i.e., environment-long) changes to |\par| should therefore
 % redefine |\@par| to restore the desired long-term definition.
 % The following code starts a long-term redefinition of |\par| adhering to
-% these conventions, and extends that definition to \meta{return} as well.
+% these conventions, and extends that definition to \Meta{return} as well.
 %    \begin{macrocode}
 \newcommand\SB at obeylines{%
   \let\par\SB at par%
@@ -6733,7 +6998,7 @@
 % \changes{v1.12}{2005/05/10}{Added}
 % Reserve a length to remember the current |\parindent|.
 %    \begin{macrocode}
-\newdimen\SB at parindent
+\SB at newdimen\SB at parindent
 %    \end{macrocode}
 % \end{macro}
 %
@@ -6797,7 +7062,7 @@
 % Begin a new verse.
 % This can be done by beginning a |verse| environment or by using the
 % |\beginverse| macro.
-% The latter must check for a trailing star to determine if this
+% The latter must check for a trailing star to determine whether this
 % verse should be numbered.
 % We use |\@ifstar| to scan ahead for the star, but this needs to be done
 % carefully because while scanning we might encounter tokens that
@@ -6858,15 +7123,13 @@
     \ifvnumbered%
       \protected at edef\@currentlabel{\p at versenum\theversenum}%
       \def\SB at everypar{%
-        \setbox\SB at box\hbox{{%
-          \printversenum{\theversenum}%
-        }}%
+        \setbox\SB at box\hbox{{\printversenum{\theversenum}}}%
         \ifdim\wd\SB at box<\versenumwidth%
           \setbox\SB at box%
           \hbox to\versenumwidth{\unhbox\SB at box\hfil}%
         \fi%
         \ifchorded\vrule\@height\baselineskip\@width\z@\@depth\z@\fi%
-        {\placeversenum\SB at box}%
+        \placeversenum\SB at box%
         \gdef\SB at everypar{}%
       }%
     \else%
@@ -6876,7 +7139,7 @@
       }%
     \fi%
     \everypar{\SB at everypar\everypar{}}%
-    \versefont\versejustify%
+    \versefont\relax\SB at setbaselineskip\versejustify%
     \SB at loadactives%
     \SB at obeylines%
     \penalty12345 %
@@ -6922,7 +7185,7 @@
 % When |\repchoruses| is used, the first sequence of consecutive choruses
 % is remembered in the following box register.
 %    \begin{macrocode}
-\newbox\SB at chorusbox
+\SB at newbox\SB at chorusbox
 %    \end{macrocode}
 % \end{macro}
 %
@@ -6942,7 +7205,7 @@
 % We use the |\newmarks| macro to allocate these classes, if it's
 % available.
 % If |\newmarks| doesn't exist, then that means the user has an
-% $\varepsilon$-\TeX\ compatible version of \LaTeX, but no |etex| style
+% $\varepsilon$-\TeX{} compatible version of \LaTeX, but no |etex| style
 % file to go with it;
 % we just have to pick two mark classes and hope that nobody else is
 % using them.
@@ -6973,8 +7236,8 @@
 % |\SB at cmark| is used to mark places where a chorus might be inserted between
 % verses, and |\SB at lastcmark| marks a place where a chorus might be inserted
 % after the last verse of the song.
-% Both marks are $\varepsilon$-\TeX\ marks of class |\SB at cmarkclass|,
-% to avoid disrupting the use of standard \TeX\ marks.
+% Both marks are $\varepsilon$-\TeX{} marks of class |\SB at cmarkclass|,
+% to avoid disrupting the use of standard \TeX{} marks.
 % Each time a chorus is automatically inserted, |\SB at nocmark| is inserted
 % with mark class |\SB at nocmarkclass| just above it (and at the top of each
 % additional page it spans).
@@ -7052,7 +7315,7 @@
       }%
       \everypar{\SB at everypar\everypar{}}%
     \fi%
-    \chorusfont\chorusjustify%
+    \chorusfont\relax\SB at setbaselineskip\chorusjustify%
     \SB at loadactives%
     \SB at obeylines%
     \penalty12345 %
@@ -7172,7 +7435,7 @@
 % \end{macro}
 %
 % \begin{macro}{\brk}\MainImpl{brk}
-% Placing |\brk| within a line in a verse or chorus tells \TeX\ to break the
+% Placing |\brk| within a line in a verse or chorus tells \TeX{} to break the
 % line at that point (if it needs to be broken at all).
 %
 % Placing |\brk| on a line by itself within a chorus stops the chorus (and its
@@ -7227,7 +7490,7 @@
 % justification.
 %    \begin{macrocode}
 \newcommand\SB at boxup[1]{%
-  \setbox\SB at box\hbox{{\notefont#1}}%
+  \setbox\SB at box\hbox{{\notefont\relax#1}}%
   \SB at dimen\wd\SB at box%
   \advance\SB at dimen6\p@%
   \advance\SB at dimen\leftskip%
@@ -7302,10 +7565,7 @@
 % \changes{v1.21}{2006/09/17}{Customized fonts now preserved.}
 % \changes{v2.1}{2007/08/02}{Toggles instead of forces slanted font.}
 % Typeset an echo part in the lyrics.
-% Echo parts will be oblique and parenthesized.
-% We toggle between oblique and upright shapes like |\emph|, but we
-% use |\slshape| instead of |\itshape| because it tends to look nicer
-% with the larger fonts used in slides mode.
+% Echo parts are in a user-customizable font and parenthesized.
 %
 % The |\echo| macro must be able to accept chords in its argument.
 % This complicates the implementation because chord macros should change
@@ -7314,9 +7574,9 @@
 % This would disallow chord name abbreviations like |#| and |&| within
 % |\echo| parts.
 %
-% If we're using $\varepsilon$-\TeX\ then the solution is easy: we use
+% If we're using $\varepsilon$-\TeX{} then the solution is easy: we use
 % |\scantokens| to re-scan the argument and thereby re-assign the catcodes.
-% (One subtlety: Whenever \LaTeX\ consumes an argument to a macro, it changes
+% (One subtlety: Whenever \LaTeX{} consumes an argument to a macro, it changes
 % |#| to |##| so that when the argument text is substituted into the body of
 % the macro, the replacement text will not contain unsubstituted parameters
 % (such as |#1|).
@@ -7325,7 +7585,7 @@
 % \emph{output}, which was not the intent.
 % To avoid this problem, we use |\@sanitize| before consuming the argument to
 % |\echo|, which sets the catcodes of most special tokens (including |#|) to
-% 12, so that \LaTeX\ will not recognize any of them as parameters and will
+% 12, so that \LaTeX{} will not recognize any of them as parameters and will
 % therefore not double any of them.)
 %    \begin{macrocode}
 \ifSB at etex
@@ -7333,7 +7593,7 @@
   \newcommand\SB at echo[1]{%
     \endgroup%
     \begingroup%
-      \ifdim\fontdimen\@ne\font>\z@\upshape\else\slshape\fi%
+      \echofont\relax%
       \endlinechar\m at ne%
       \scantokens{(#1)}%
     \endgroup%
@@ -7349,11 +7609,11 @@
 % chord name atop a short lyric, the closing parenthesis will float out away
 % from the lyric instead of being sucked under the chord.
 % I can find no solution to this problem, so to avoid it users must find a
-% version of \LaTeX\ that is $\varepsilon$-\TeX\ compatible.
+% version of \LaTeX{} that is $\varepsilon$-\TeX{} compatible.
 %    \begin{macrocode}
   \newcommand\echo{%
     \begingroup%
-      \ifdim\fontdimen\@ne\font>\z@\upshape\else\slshape\fi%
+      \echofont\relax%
       \afterassignment\SB at echo%
       \setbox\SB at box\hbox%
   }
@@ -7367,8 +7627,8 @@
 %
 % \begin{macro}{\rep}\MainImpl{rep}
 % \changes{v1.21}{2006/09/17}{Changed to avoid math mode.}
-% Place |\rep{|\meta{n}|}| at the end of a line to indicate that it should be
-% sung \meta{n} times.
+% Place |\rep{|\Meta{n}|}| at the end of a line to indicate that it should be
+% sung \Meta{n} times.
 %    \begin{macrocode}
 \newcommand\rep[1]{%
   (\raise.25ex\hbox{%
@@ -7405,7 +7665,7 @@
 % \begin{macro}{\SB at grouplvl}
 % Count the |songgroup| environment nesting depth.
 %    \begin{macrocode}
-\newcount\SB at grouplvl
+\SB at newcount\SB at grouplvl
 %    \end{macrocode}
 % \end{macro}
 %
@@ -7475,14 +7735,6 @@
 %    \end{macrocode}
 % \end{environment}
 %
-% \begin{macro}{\SB at srbox}
-% The following box register holds the citation information that is to be
-% typeset at the end of a scripture quotation.
-%    \begin{macrocode}
-\newbox\SB at srbox
-%    \end{macrocode}
-% \end{macro}
-%
 % \begin{environment}{scripture}\MainEnvImpl{scripture}
 % \begin{macro}{\beginscripture}
 % Begin a scripture quotation.
@@ -7495,13 +7747,13 @@
 \newcommand\beginscripture[1]{%
   \begin{intersong}%
     \SB at parsesrefs{#1}%
-    \setbox\SB at srbox\hbox{{\printscrcite\songrefs}}%
+    \setbox\SB at envbox\hbox{{\printscrcite\songrefs}}%
     \def\SB at closeall{\endscripture}%
     \nobreak\vskip5\p@%
     \SB at parindent\parindent\parindent\z@%
     \parskip\z at skip\parfillskip\@flushglue%
     \leftskip\SB at parindent\rightskip\SB at parindent\relax%
-    \scripturefont%
+    \scripturefont\relax%
     \baselineskip\f at size\p@\@plus\p@\relax%
     \advance\baselineskip\p@\relax%
     \emergencystretch.3em%
@@ -7530,7 +7782,7 @@
 % line, but at times the user might want to invoke this macro explicitly
 % at a more suitable point.
 % A good example is when something near the end of the scripture quotation
-% drops \TeX\ into vertical mode.
+% drops \TeX{} into vertical mode.
 % In such cases, it is often better to issue the citation before leaving
 % horizontal mode.
 %
@@ -7541,13 +7793,13 @@
 % However, if we're now in vertical mode, the problem is a little harder.
 % We do the best we can by using |\lastbox| to remove the last line, then
 % adding the reference and re-typesetting it.
-% This isn't as good as the horizontal mode solution because \TeX\ only
+% This isn't as good as the horizontal mode solution because \TeX{} only
 % gets to reevaluate the last line instead of the whole paragraph, but
 % usually the results are passable.
 %    \begin{macrocode}
 \newcommand\scitehere{%
   \ifSB at intersong%
-    \ifvoid\SB at srbox\else%
+    \ifvoid\SB at envbox\else%
       \ifvmode%
         \setbox\SB at box\lastbox%
         \nointerlineskip\noindent\hskip-\leftskip%
@@ -7554,7 +7806,7 @@
         \unhbox\SB at box\unskip%
       \fi%
       \unskip\nobreak\hfil\penalty50\hskip.8em\null\nobreak\hfil%
-      \box\SB at srbox\kern-\SB at parindent%
+      \box\SB at envbox\kern-\SB at parindent%
       {\parfillskip\z@\finalhyphendemerits2000\par}%
     \fi%
   \else%
@@ -7577,8 +7829,8 @@
 %
 % \begin{macro}{\SB at colon}
 % Begin a group of temporary definitions that will end at the next
-% \meta{return}.
-% The \meta{return} will end the paragraph and close the local scope.
+% \Meta{return}.
+% The \Meta{return} will end the paragraph and close the local scope.
 %    \begin{macrocode}
 \newcommand\SB at colon[2]{%
   \ifSB at intersong\else%
@@ -7714,7 +7966,7 @@
 % This counter identifies the requested number of halfsteps by which chords are
 % to be transposed (from $-11$ to $+11$).
 %    \begin{macrocode}
-\newcount\SB at transposefactor
+\SB at newcount\SB at transposefactor
 %    \end{macrocode}
 % \end{macro}
 %
@@ -7765,7 +8017,7 @@
 % \begin{macro}{\printnoteG}
 % These control sequences are what the transposition logic actually
 % outputs to denote each scale degree.
-% They can include any \LaTeX\ code that is legal in horizontal mode.
+% They can include any \LaTeX{} code that is legal in horizontal mode.
 %    \begin{macrocode}
 \newcommand\printnoteA{}
 \newcommand\printnoteB{}
@@ -7854,7 +8106,7 @@
 % The first chord seen is usually the best indicator of the key of the song.
 % (Even when the first chord isn't the tonic, it will often be the dominant
 % or subdominant, which usually has the same kind of accidental in its key
-% signatures as the actual key.) This conditional remembers if the current
+% signatures as the actual key.) This conditional remembers whether the current
 % chord is the first one seen in the song, and should therefore be used to
 % guess the key of the song.
 %    \begin{macrocode}
@@ -7931,7 +8183,7 @@
 %
 % \begin{macro}{\notrans}\MainImpl{notrans}
 % Suppress chord transposition without suppressing note name conversion.
-% When a |\notrans{|\meta{text}|}| macro appears within text undergoing
+% When a |\notrans{|\Meta{text}|}| macro appears within text undergoing
 % transposition, the |\notrans| macro and the group will be preserved
 % verbatim by the transposition parser.
 % When it is then expanded after parsing, we must therefore re-invoke
@@ -7982,7 +8234,7 @@
 % \end{macro}
 %
 % \begin{macro}{\SB at trmain}
-% Test to see if the token was a begin-brace, end-brace, or space.
+% Test to see whether the token was a begin-brace, end-brace, or space.
 % These tokens require special treatment because they cannot be
 % accepted as implicit arguments to macros.
 %    \begin{macrocode}
@@ -7992,7 +8244,7 @@
   \else\ifx\SB at next\egroup%
     \SB at toks\expandafter{\the\SB at toks\egroup}%
     \let\SB at donext\SB at trskip%
-  \else\ifcat\noexpand\SB at next\noexpand\@sptoken%
+  \else\ifcat\noexpand\SB at next\@sptoken%
     \SB at appendsp\SB at toks%
     \let\SB at donext\SB at trskip%
   \else%
@@ -8030,8 +8282,8 @@
 %
 % \begin{macro}{\SB at trstep}
 % A non-grouping token lies next in the input stream.
-% Consume it as an argument to this macro, and then test it to see if it's a
-% note letter or some other recognized item.
+% Consume it as an argument to this macro, and then test it to see whether
+% it's a note letter or some other recognized item.
 % If so, process it; otherwise just append it to the token list and continue
 % scanning.
 %    \begin{macrocode}
@@ -8057,8 +8309,8 @@
 % \begin{macro}{\SB at trnote}
 % We're in the midst of processing a sequence of uppercase letters that
 % might comprise a note name.
-% Check to see if the next token is an accidental (sharp or flat), or yet
-% another letter.
+% Check to see whether the next token is an accidental (sharp or flat),
+% or yet another letter.
 %    \begin{macrocode}
 \newcommand\SB at trnote{%
   \ifcat\noexpand\SB at next A%
@@ -8083,13 +8335,13 @@
 %
 % \begin{macro}{\SB at trnotestep}
 % The next token is a letter.
-% Consume it and test to see if it is an uppercase letter.
+% Consume it and test to see whether it is an uppercase letter.
 % If so, add it to the note name being assembled; otherwise reinsert it into
 % the input stream and jump directly to the transposition logic.
 %    \begin{macrocode}
 \newcommand\SB at trnotestep[1]{%
   \ifnum\uccode`#1=`#1%
-    \expandafter\def\expandafter\SB at temp\expandafter{\SB at temp#1}%
+    \SB at app\def\SB at temp{#1}%
     \expandafter\SB at trscan%
   \else%
     \SB at cnt\z@%
@@ -8246,8 +8498,8 @@
 % there needs to be a way to pull a vbox off the current list and determine
 % whether or not it is a box that contains a measure bar. The solution is to
 % insert a mark (|\SB at measuremark|) at the top of each measure bar vbox.
-% We can then see if this measure bar immediately follows another measure bar
-% by using |\vsplit| on |\lastbox|.
+% We can then see whether this measure bar immediately follows another measure
+% bar by using |\vsplit| on |\lastbox|.
 %    \begin{macrocode}
 \newcommand\SB at measuremark{SB at IsMeasure}
 %    \end{macrocode}
@@ -8284,16 +8536,9 @@
     \fi%
   \fi%
   \ifvmode\leavevmode\fi%
-  \setbox\SB at box\hbox{\tiny\sffamily{#1}}%
-  \setbox\SB at boxii\hbox{\tiny\sffamily{#2}}%
-  \ifdim\wd\SB at box>\wd\SB at boxii%
-    \SB at dimen\wd\SB at box\relax%
-  \else%
-    \SB at dimen\wd\SB at boxii\relax%
-  \fi%
-  \ifdim\SB at dimen<.5\p@%
-    \SB at dimen.5\p@%
-  \fi%
+  \setbox\SB at box\hbox{{\meterfont\relax#1}}%
+  \setbox\SB at boxii\hbox{{\meterfont\relax#2}}%
+  \SB at dimen\wd\ifdim\wd\SB at box>\wd\SB at boxii\SB at box\else\SB at boxii\fi%
   \SB at dimenii\baselineskip%
   \advance\SB at dimenii-2\p@%
   \advance\SB at dimenii-\ht\SB at box%
@@ -8300,6 +8545,13 @@
   \advance\SB at dimenii-\dp\SB at box%
   \advance\SB at dimenii-\ht\SB at boxii%
   \advance\SB at dimenii-\dp\SB at boxii%
+  \let\SB at temp\relax%
+  \ifdim\SB at dimen>\z@%
+    \advance\SB at dimenii-.75\p@%
+    \def\SB at temp{\kern.75\p@}%
+  \fi%
+  \SB at maxmin\SB at dimen<{.5\p@}%
+  \SB at maxmin\SB at dimenii<\z@%
   \vbox{%
     \mark{\SB at measuremark}%
     \hbox to\SB at dimen{%
@@ -8313,6 +8565,7 @@
       \box\SB at boxii%
       \hfil%
     }%
+    \SB at temp%
     \nointerlineskip%
     \hbox to\SB at dimen{%
       \hfil%
@@ -8320,6 +8573,7 @@
       \hfil%
     }%
   }%
+  \meter{}{}%
 }
 %    \end{macrocode}
 % \end{macro}
@@ -8339,7 +8593,6 @@
 %    \begin{macrocode}
 \newcommand\measurebar{%
   \mbar\SB at metertop\SB at meterbot%
-  \meter{}{}%
 }
 %    \end{macrocode}
 % \end{macro}
@@ -8391,15 +8644,15 @@
 %
 % The obvious way to create a chord macro is as a normal macro with
 % two arguments, one for the chord name and one for the lyrics to go
-% under the chord---e.g.~|\chord{|\meta{chordname}|{|\meta{lyric}|}|.
+% under the chord---e.g.~|\chord{|\Meta{chordname}|}{|\Meta{lyric}|}|.
 % However, in practice such a macro is extremely cumbersome and
 % difficult to use.
 % The problem is that in order to use such a macro properly, the user
 % must remember a bunch of complex style rules that govern what
-% part of the lyric text needs to go in the \meta{lyric} parameter and
+% part of the lyric text needs to go in the \Meta{lyric} parameter and
 % what part should be typed after the closing brace.
 % To avoid separating a word from its trailing punctuation, the
-% \meta{lyric} parameter must often include punctuation but not certain
+% \Meta{lyric} parameter must often include punctuation but not certain
 % special punctuation like hyphens, should include the rest of the
 % word but not if there's another chord in the word, should omit
 % measure bars but only if measure bars are being shown, etc.
@@ -8415,7 +8668,7 @@
 %
 % \begin{macro}{\ifSB at wordends}
 % \begin{macro}{\ifSB at brokenword}
-% Chord macros must look ahead in the input stream to see if this chord
+% Chord macros must look ahead in the input stream to see whether this chord
 % is immediately followed by whitespace or the remainder of a word.
 % If the latter, hyphenation might need to be introduced.
 % These macros keep track of the need for hyphenation, if any.
@@ -8430,7 +8683,7 @@
 % Lyrics appearing after a chord are scanned into the following token list
 % register.
 %    \begin{macrocode}
-\newtoks\SB at lyric
+\SB at newtoks\SB at lyric
 %    \end{macrocode}
 % \end{macro}
 %
@@ -8439,7 +8692,7 @@
 % The following counter counts the number of explicit hyphens ending
 % the lyric syllable that follows the current chord.
 %    \begin{macrocode}
-\newcount\SB at numhyps
+\SB at newcount\SB at numhyps
 %    \end{macrocode}
 % \end{macro}
 %
@@ -8447,7 +8700,7 @@
 % When a lyric syllable under a chord ends in exactly one hyphen, the
 % following token register is set to be the syllable without the hyphen.
 %    \begin{macrocode}
-\newtoks\SB at lyricnohyp
+\SB at newtoks\SB at lyricnohyp
 %    \end{macrocode}
 % \end{macro}
 %
@@ -8456,8 +8709,8 @@
 % The following two boxes hold the part of the lyric text that is to be
 % typeset under the chord, and the chord text that is to be typeset above.
 %    \begin{macrocode}
-\newbox\SB at lyricbox
-\newbox\SB at chordbox
+\SB at newbox\SB at lyricbox
+\SB at newbox\SB at chordbox
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -8475,6 +8728,7 @@
 % \begin{macro}{\SB at setchord}
 % \changes{v2.3}{2007/09/23}{Support replayed chords over ligatures}
 % \changes{v2.7}{2009/01/08}{Extend rather than replace chordbox}
+% \changes{v3.0}{2017/04/19}{Fix transposition of replayed chord over ligature}
 % The following macro typesets its argument as a chord and stores the
 % result in box |\SB at chordbox| for later placement into the document.
 % The hat token (|^|) is redefined so that outside of math mode it
@@ -8490,7 +8744,6 @@
 \newcommand\SB at setchord{}
 {
   \catcode`^\active
-  \catcode`!7
   \gdef\SB at setchord#1{%
     \SB at gettabindtrue\SB at nohattrue%
     \setbox\SB at chordbox\hbox{%
@@ -8497,11 +8750,9 @@
       \unhbox\SB at chordbox%
       \begingroup%
         \ifSB at trackch%
-          \def\SB at activehat{\ifmmode!\else\global\SB at nohatfalse\fi}%
+          \let\SB at activehat\SB at hat@tr%
         \else%
-          \def\SB at activehat{%
-            \ifmmode!\else\SB at lop\SB at ctail\SB at toks\the\SB at toks\fi%
-          }%
+          \let\SB at activehat\SB at hat@notr%
         \fi%
         \let^\SB at activehat%
         \printchord{%
@@ -8523,38 +8774,45 @@
 % \end{macro}
 %
 % \begin{macro}{\SB at outertest}
-% \begin{macro}{\SB@@outertest}
-% The lyric-scanning code must preemptively determine if the next token is
-% a macro declared |\outer| before it tries to accept that token as an
+% \begin{macro}{\SB at otesta}
+% \begin{macro}{\SB at otestb}
+% The lyric-scanning code must preemptively determine whether the next token
+% is a macro declared |\outer| before it tries to accept that token as an
 % argument.
-% Otherwise \TeX\ will abort with a parsing error.
-% Macros declared |\outer| are not allowed in arguments, so determining if a
-% token is |\outer| is a delicate process.
+% Otherwise \TeX{} will abort with a parsing error.
+% Macros declared |\outer| are not allowed in arguments, so determining
+% whether a token is |\outer| is a delicate process.
 % The following does so by consulting |\meaning|.
+% A macro can be identified as |\outer| if its meaning has the
+% word ``|\outer|'' before the first colon.
 %    \begin{macrocode}
-\newcommand\SB at outertest{}
-\edef\SB at outertest#1{%
-  \noexpand\SB@@outertest#1%
-  \string\outer%
-  \noexpand\SB@@outertest%
+\newcommand\SB at outertest{%
+  \expandafter\SB at otesta\meaning\SB at next:\SB at otesta%
 }
-\newcommand\SB@@outertest{}
-\expandafter\def\expandafter\SB@@outertest%
-\expandafter#\expandafter1\string\outer#2\SB@@outertest{%
+\newcommand\SB at otesta{}
+\edef\SB at otesta#1:#2\SB at otesta{%
+  \noexpand\SB at otestb%
+  #1\string\outer%
+  \noexpand\SB at otestb%
+}
+\newcommand\SB at otestb{}
+\expandafter\def\expandafter\SB at otestb%
+\expandafter#\expandafter1\string\outer#2\SB at otestb{%
   \def\SB at temp{#2}%
-  \ifx\SB at temp\@empty\else\SB at testtrue\fi%
+  \ifx\SB at temp\@empty\SB at testfalse\else\SB at testtrue\fi%
 }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
+% \end{macro}
 %
 % \begin{macro}{\SB at UTFtest}
-% \begin{macro}{\SB at two}
-% \begin{macro}{\SB at three}
-% \begin{macro}{\SB at four}
-% \begin{macro}{\SB at UTFtester}
+% \begin{macro}{\SB at U@two}
+% \begin{macro}{\SB at U@three}
+% \begin{macro}{\SB at U@four}
+% \begin{macro}{\SB@@UTFtest}
 % \changes{v1.22}{2007/05/15}{Added.}
-% To support UTF-8 encoded \LaTeX\ source files, we need to be able to
+% To support UTF-8 encoded \LaTeX{} source files, we need to be able to
 % identify multibyte characters during the lyric scanning process.
 % Alas, the |utf8.def| file provides no clean way of identifying the
 % macros it defines for this purpose.
@@ -8563,29 +8821,22 @@
 %    \begin{macrocode}
 \newcommand\SB at UTFtest{}
 \edef\SB at UTFtest#1{%
-  \noexpand\SB at UTFtester#1%
+  \noexpand\expandafter%
+  \noexpand\SB@@UTFtest%
+  \noexpand\meaning#1%
   \string\UTFviii at zero@octets%
-  \noexpand\SB at UTFtester%
+  \noexpand\SB@@UTFtest%
 }
-\begingroup
-  \escapechar\m at ne
-  \xdef\SB at two{\string\two}
-  \xdef\SB at three{\string\three}
-  \xdef\SB at four{\string\four}
-  \xdef\SB at temp{\string\@octets}
-\endgroup
-\edef\SB at temp{##1\string\UTFviii@##2\SB at temp##3}
-\expandafter\def\expandafter\SB at UTFtester\SB at temp\SB at UTFtester{%
-  \def\SB at temp{#2}%
-  \ifx\SB at temp\SB at two%
-    \SB at cnt\tw@%
-  \else\ifx\SB at temp\SB at three%
-    \SB at cnt\thr@@%
-  \else\ifx\SB at temp\SB at four%
-    \SB at cnt4 %
-  \else%
-    \SB at cnt\z@%
-  \fi\fi\fi%
+\newcommand\SB at U@two{\global\SB at cnt\tw@}
+\newcommand\SB at U@three{\global\SB at cnt\thr@@}
+\newcommand\SB at U@four{\global\SB at cnt4\relax}
+\newcommand\SB@@UTFtest{}
+{\escapechar\m at ne
+ \xdef\SB at temp{\string\@octets}}
+\edef\SB at temp{##1\string\UTFviii@##2\SB at temp}
+\expandafter\def\expandafter\SB@@UTFtest\SB at temp#3\SB@@UTFtest{%
+  \SB at cnt\z@%
+  {\csname SB at U@#2\endcsname}%
 }
 %    \end{macrocode}
 % \end{macro}
@@ -8610,30 +8861,26 @@
 %    \begin{macrocode}
 \newcommand\SB at declare[3]{%
   \afterassignment\iffalse\let\SB at next= #3\relax\fi%
-  \expandafter\SB at UTFtest\expandafter{\meaning\SB at next}%
+  \SB at UTFtest\SB at next%
   \ifcase\SB at cnt%
     \ifcat\noexpand#3\relax%
       \SB at addNtest\SB at macrotests#1#2#3%
     \else\ifcat\noexpand#3.%
-      \SB at addDtest\SB at othertests#1#2#3%
+      \SB at addDtest\SB at othertests#1#2%
     \else\ifcat\noexpand#3A%
-      \SB at addDtest\SB at lettertests#1#2#3%
+      \SB at addDtest\SB at lettertests#1#2%
     \else%
-      \SB at addDtest\relax0#2#3%
+      \SB at addDtest\relax0#2%
     \fi\fi\fi%
   \or%
     \SB at addNtest\SB at macrotests#1#2#3%
   \else%
-    \SB at addMtest\SB at multitests#1#2{#3}%
+    \SB at addMtest\SB at multitests#1#2#3\relax\relax\relax%
   \fi%
 }
 \newcommand\DeclareLyricChar{\SB at declare\SB at testtrue0}
-\newcommand\DeclareNonLyric{%
-  \SB at declare\SB at testfalse\SB at testfalse%
-}
-\newcommand\DeclareNoHyphen{%
-  \SB at declare\SB at testfalse\SB at testtrue%
-}
+\newcommand\DeclareNonLyric{\SB at declare\SB at testfalse\SB at testfalse}
+\newcommand\DeclareNoHyphen{\SB at declare\SB at testfalse\SB at testtrue}
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -8644,7 +8891,6 @@
 % \begin{macro}{\SB at macrotests}
 % \begin{macro}{\SB at multitests}
 % \begin{macro}{\SB at othertests}
-% \begin{macro}{\SB at hyphtests}
 % For speed, token tests introduced by |\DeclareLyricChar| and friends
 % are broken out into separate macros based on category codes.
 %    \begin{macrocode}
@@ -8652,13 +8898,11 @@
 \newcommand\SB at macrotests{}
 \newcommand\SB at multitests{}
 \newcommand\SB at othertests{}
-\newcommand\SB at hyphtests{}
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
 % \end{macro}
 % \end{macro}
-% \end{macro}
 %
 % The following macros add tests to the test macros defined above.
 % In each, \argp{1} is the test macro to which the test should be added,
@@ -8665,26 +8909,21 @@
 % \argp{2} and \argp{3} is the code to be executed at scanning-time and
 % at hyphenation-time if the test succeeds (or ``0'' if no action is to
 % be performed), and \argp{4} is the token to which the currently scanned
-% token should be compared to determine if it matches.
+% token should be compared to determine whether it matches.
 %
-% \begin{macro}{\SB at addtest}
-% Append the top-level expansion of \argp{2} to the control sequence name
-% given by \argp{1}.
-%    \begin{macrocode}
-\newcommand\SB at addtest[2]{%
-  \expandafter\gdef\expandafter#1\expandafter{#1#2}%
-}
-%    \end{macrocode}
-% \end{macro}
-%
 % \begin{macro}{\SB at addDtest}
-% A definition-test:  The test succeeds if the \emph{definition} at test-time
-% of the next lyric token matches the \emph{definition at test-time} of
-% the control sequence that was given to the |\Declare| macro.
+% A definition-test: The test succeeds if the next lyric token has the same
+% meaning (at test-time) of the non-macro, non-active character token that
+% was given to the |\Declare| macro.
 %    \begin{macrocode}
-\newcommand\SB at addDtest[4]{%
-  \ifx0#2\else\SB at addtest#1{\ifx\SB at next#4#2\fi}\fi%
-  \ifx0#3\else\SB at addtest\SB at hyphtests{\ifx\SB at next#4#3\fi}\fi%
+\newcommand\SB at addDtest[3]{%
+  \ifx0#2\else%
+    \def#1{{\csname SB@!\meaning\SB at next\endcsname}}%
+    \expandafter\def\csname SB@!\meaning\SB at next\endcsname{\global#2}%
+  \fi%
+  \ifx0#3\else%
+    \expandafter\def\csname SB at HT@\meaning\SB at next\endcsname{\global#3}%
+  \fi%
 }
 %    \end{macrocode}
 % \end{macro}
@@ -8697,14 +8936,11 @@
 %    \begin{macrocode}
 \newcommand\SB at addNtest[4]{%
   \ifx0#2\else%
-    \SB at addtest#1{%
-      \edef\SB at temp{\string#4}\ifx\SB at temp\SB at nextname#2\fi%
-    }%
+    \def#1{{\csname SB@!\SB at nextname\endcsname}}%
+    \expandafter\def\csname SB@!\string#4\endcsname{\global#2}%
   \fi%
   \ifx0#3\else%
-    \SB at addtest\SB at hyphtests{%
-      \edef\SB at temp{\string#4}\ifx\SB at temp\SB at nextname#3\fi%
-    }%
+    \expandafter\def\csname SB at HT@\string#4\endcsname{\global#3}%
   \fi%
 }
 %    \end{macrocode}
@@ -8715,12 +8951,19 @@
 % beginning of a UTF-8 encoded multibyte character sequence that matches
 % the multibyte sequence given to the |\Declare| macro.
 %    \begin{macrocode}
-\newcommand\SB at addMtest[4]{%
+\newcommand\SB at addMtest[7]{%
+  \edef\SB at temp{%
+    \string#4%
+    \ifx\relax#5\else\string#5\fi%
+    \ifx\relax#6\else\string#6\fi%
+    \ifx\relax#7\else\string#7\fi%
+  }%
   \ifx0#2\else%
-    \SB at addtest#1{\def\SB at temp{#4}\ifx\SB at next\SB at temp#2\fi}%
+    \def#1{{\csname SB@!\SB at nextname\endcsname}}%
+    \expandafter\def\csname SB@!\SB at temp\endcsname{\global#2}%
   \fi%
-  \ifx0#3\else\SB at addtest\SB at hyphtests{%
-    \def\SB at temp{#4}\ifx\SB at next\SB at temp#3\fi}%
+  \ifx0#3\else%
+    \expandafter\def\csname SB at HT@\SB at temp\endcsname{\global#3}%
   \fi%
 }
 %    \end{macrocode}
@@ -8727,7 +8970,7 @@
 % \end{macro}
 %
 % The following code declares the common intra-word macros provided by
-% \TeX\ (as listed on p.~52 of The \TeX book) to be lyric-continuing.
+% \TeX{} (as listed on p.~52 of The \TeX book) to be lyric-continuing.
 %    \begin{macrocode}
 \DeclareLyricChar\`
 \DeclareLyricChar\'
@@ -8795,9 +9038,9 @@
 % To accomplish this, |\SB at begincname| must always be invoked before any
 % macro whose argument is a chord name, and |\SB at endcname| must be invoked
 % at the start of the body of any macro whose argument is a chord name.
-% To aid in debugging, we also temporarily set \meta{return} characters and
+% To aid in debugging, we also temporarily set \Meta{return} characters and
 % chord macros |\outer|.
-% This will cause \TeX\ to halt with a runaway argument error on the correct
+% This will cause \TeX{} to halt with a runaway argument error on the correct
 % source line if the user forgets to type a closing end-brace (a common typo).
 % Colon characters are also set non-active to avoid a conflict between the
 % \textsf{Babel} French package and the |\gtab| macro.
@@ -8810,7 +9053,7 @@
      \catcode`:12\relax%
      \catcode`\^^M\active\SB at outer\def^^M{}%
      \SB at outer\def\[{}%
-     \chordlocals%
+     \chordlocals\relax%
   }
 }
 \newcommand\SB at endcname{}
@@ -8879,8 +9122,8 @@
 \newcommand\MultiwordChords{%
   \def\SB at spcinit{%
     \let\SB at chdone\SB at chlyrdone%
-    \def\SB at chimpspace{\let\SB at donext\SB at chdone}%
-    \def\SB at chexpspace{\let\SB at donext\SB at chdone}%
+    \let\SB at chimpspace\SB at chnxtdone%
+    \let\SB at chexpspace\SB at chnxtdone%
     \let\SB at chespace\SB at chendspace%
   }%
 }
@@ -8904,12 +9147,27 @@
 % \end{macro}
 % \end{macro}
 %
+% \begin{macro}{\SB at chnxtrelax}
+% \begin{macro}{\SB at chnxtstep}
+% \begin{macro}{\SB at chnxtdone}
+% To shorten the lyric parser macros that follow and thereby improve their
+% speed, we here define some abbreviations for common logic in untaken
+% branches.
+%    \begin{macrocode}
+\newcommand\SB at chnxtrelax{\let\SB at donext\relax}
+\newcommand\SB at chnxtstep{\let\SB at donext\SB at chstep}
+\newcommand\SB at chnxtdone{\let\SB at donext\SB at chdone}
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% 
 % Warning: In the lyric-scanner macros that follow, |\SB at next|
 % might be a macro declared |\outer|.
 % This means that it must \emph{never} be passed as an argument to
 % a macro and it must never explicitly appear in any untaken branch
 % of a conditional.
-% If it does, the \TeX\ parser will complain of a runaway argument
+% If it does, the \TeX{} parser will complain of a runaway argument
 % when it tries to skip over an |\outer| macro while consuming tokens
 % at high speed.
 %
@@ -8925,20 +9183,13 @@
 % left-quote or left-double-quote symbol, which looks better.
 %    \begin{macrocode}
 \newcommand\SB at chstart{%
-  \ifx\SB at next\[%
-    \let\SB at donext\relax%
-  \else\ifx\SB at next\SB at activehat%
-    \let\SB at donext\relax%
-  \else\ifx\SB at next\ch%
-    \let\SB at donext\relax%
-  \else\ifx\SB at next\mch%
-    \let\SB at donext\relax%
-  \else\ifx\SB at next`%
-    \let\SB at donext\SB at chstep%
-  \else\ifx\SB at next'%
-    \let\SB at donext\SB at chstep%
-  \else\ifx\SB at next"%
-    \let\SB at donext\SB at chstep%
+  \ifx\SB at next\[\SB at chnxtrelax%
+  \else\ifx\SB at next\SB at activehat\SB at chnxtrelax%
+  \else\ifx\SB at next\ch\SB at chnxtrelax%
+  \else\ifx\SB at next\mch\SB at chnxtrelax%
+  \else\ifx\SB at next`\SB at chnxtstep%
+  \else\ifx\SB at next'\SB at chnxtstep%
+  \else\ifx\SB at next"\SB at chnxtstep%
   \else%
     \the\SB at lyric%
     \SB at lyric{}%
@@ -8953,7 +9204,7 @@
 %
 % \begin{macro}{\SB at chnorm}
 % \changes{v2.0}{2007/06/18}{Rewritten for speed}
-% First, check to see if the lyric token is a letter.
+% First, check to see whether the lyric token is a letter.
 % Since that's the most common case, we do this check first for speed.
 %    \begin{macrocode}
 \newcommand\SB at chnorm{%
@@ -8960,9 +9211,9 @@
   \ifcat\noexpand\SB at next A%
     \SB at testtrue\SB at lettertests%
     \ifSB at test%
-      \SB at chespace\let\SB at donext\SB at chstep%
+      \SB at chespace\SB at chnxtstep%
     \else%
-      \let\SB at donext\SB at chdone%
+      \SB at chnxtdone%
     \fi%
   \else%
     \SB at chtrymacro%
@@ -8972,7 +9223,7 @@
 % \end{macro}
 %
 % \begin{macro}{\SB at chtrymacro}
-% Next, check to see if it's a macro or active character.
+% Next, check to see whether it's a macro or active character.
 % We do these checks next because these are the only cases when the
 % token might be |\outer|.
 % Once we eliminate that ugly possibility, we can write the rest of
@@ -8998,26 +9249,26 @@
 % we can be sure that the token isn't |\outer| at this point.
 %    \begin{macrocode}
 \newcommand\SB at chother{%
-  \ifcat\noexpand\SB at next\noexpand\@sptoken%
+  \ifcat\noexpand\SB at next\@sptoken%
     \SB at chexpspace%
-  \else\ifcat\noexpand\SB at next\noexpand\bgroup%
+  \else\ifcat\noexpand\SB at next\bgroup%
     \SB at chespace\let\SB at donext\SB at chbgroup%
-  \else\ifcat\noexpand\SB at next\noexpand\egroup%
+  \else\ifcat\noexpand\SB at next\egroup%
     \SB at chespace\let\SB at donext\SB at chegroup%
   \else\ifx\SB at next-%
     \SB at numhyps\@ne\relax%
     \SB at lyricnohyp\expandafter{\the\SB at lyric}%
     \let\SB at dothis\SB at chhyph%
-    \SB at chespace\let\SB at donext\SB at chstep%
+    \SB at chespace\SB at chnxtstep%
   \else\ifcat\noexpand\SB at next.%
     \SB at testtrue\SB at othertests%
     \ifSB at test%
-      \SB at chespace\let\SB at donext\SB at chstep%
+      \SB at chespace\SB at chnxtstep%
     \else%
-      \let\SB at donext\SB at chdone%
+      \SB at chnxtdone%
     \fi%
   \else%
-    \SB at chespace\let\SB at donext\SB at chstep%
+    \SB at chespace\SB at chnxtstep%
   \fi\fi\fi\fi\fi%
 }
 %    \end{macrocode}
@@ -9029,10 +9280,9 @@
 % If it's |\outer|, it should never be used in an argument, so stop here.
 %    \begin{macrocode}
 \newcommand\SB at chmacro{%
-  \SB at testfalse%
-  \expandafter\SB at outertest\expandafter{\meaning\SB at next}%
+  \SB at outertest%
   \ifSB at test%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \else%
     \let\SB at donext\SB at chgetname%
   \fi%
@@ -9055,16 +9305,16 @@
 % \begin{macro}{\SB@@chmacro}
 % The lyric-scanner has encountered a non-|\outer| macro or active character.
 % Its |\string|ified name has been stored in |\SB at nextname|.
-% Test to see if it's a known macro or the beginning of a multibyte-encoded
-% international character.
+% Test to see whether it's a known macro or the beginning of a
+% multibyte-encoded international character.
 % If the former, dispatch some macro-specific code to handle it.
 % If the latter, grab the full multibyte sequence and include it in the lyric.
 %    \begin{macrocode}
 \newcommand\SB@@chmacro{%
   \ifx\SB at next\SB at activehat%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \else\ifx\SB at next\SB at par%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \else\ifx\SB at next\measurebar%
     \SB at chmbar%
   \else\ifx\SB at next\mbar%
@@ -9078,7 +9328,7 @@
   \else\ifx\SB at next\SB at nbsp%
     \SB at chimpspace%
   \else%
-    \expandafter\SB at UTFtest\expandafter{\meaning\SB at next}%
+    \SB at UTFtest\SB at next%
     \ifcase\SB at cnt\SB at chothermac%
     \or\or\SB at chespace\let\SB at donext\SB at chsteptwo%
     \or\SB at chespace\let\SB at donext\SB at chstepthree%
@@ -9093,8 +9343,8 @@
 % The lyric-scanner has encountered a macro or active character that is
 % not |\outer|, not a known macro that requires special treatment,
 % and not a multibyte international character.
-% First, check the macro's name (stored in |\SB at nextname|) to see if it begins
-% with a non-escape character.
+% First, check the macro's name (stored in |\SB at nextname|) to see whether it
+% begins with a non-escape character.
 % If so, it's probably an accenting or punctuation character made active
 % by the |inputenc| or |babel| packages.
 % Most such characters should be included in the lyric, so include it by
@@ -9108,9 +9358,9 @@
   \ifnum\the\catcode\SB at cnt=\z@\else\SB at testtrue\fi%
   \SB at macrotests%
   \ifSB at test%
-    \SB at chespace\let\SB at donext\SB at chstep%
+    \SB at chespace\SB at chnxtstep%
   \else%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \fi%
 }
 %    \end{macrocode}
@@ -9133,12 +9383,16 @@
   \SB at lyric\expandafter{\the\SB at lyric#1}%
   \SB at chscan%
 }
-\newcommand\SB at chsteptwo[2]{\SB at chmulti{#1#2}}
-\newcommand\SB at chstepthree[3]{\SB at chmulti{#1#2#3}}
-\newcommand\SB at chstepfour[4]{\SB at chmulti{#1#2#3#4}}
-\newcommand\SB at chmulti[1]{%
+\newcommand\SB at chsteptwo[2]{\SB at chmulti{#1#2}{\string#1\string#2}}
+\newcommand\SB at chstepthree[3]{%
+  \SB at chmulti{#1#2#3}{\string#1\string#2\string#3}%
+}
+\newcommand\SB at chstepfour[4]{%
+  \SB at chmulti{#1#2#3#4}{\string#1\string#2\string#3\string#4}%
+}
+\newcommand\SB at chmulti[2]{%
   \def\SB at next{#1}%
-  \let\SB at nextname\relax%
+  \edef\SB at nextname{#2}%
   \SB at testtrue\SB at multitests%
   \ifSB at test%
     \SB at lyric\expandafter{\the\SB at lyric#1}%
@@ -9164,9 +9418,9 @@
 \newcommand\SB at chhyph{%
   \ifx\SB at next-%
     \advance\SB at numhyps\@ne\relax%
-    \let\SB at donext\SB at chstep%
+    \SB at chnxtstep%
   \else%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \fi%
 }
 %    \end{macrocode}
@@ -9178,8 +9432,10 @@
 % Normally this just ends the lyric, but if |\MultiwordChords| is
 % active, these macros both get redefined to process the space.
 %    \begin{macrocode}
-\newcommand\SB at chimpspace{\let\SB at donext\SB at chdone}
-\newcommand\SB at chexpspace{\let\SB at donext\SB at chdone}
+\newcommand\SB at chimpspace{}
+\let\SB at chimpspace\SB at chnxtdone
+\newcommand\SB at chexpspace{}
+\let\SB at chexpspace\SB at chnxtdone
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -9244,12 +9500,12 @@
   \ifdim\wd\SB at lyricbox<\wd\SB at chordbox%
     \let\SB at chbstok= \SB at next%
     \def\SB at chexpspace{\let\SB at donext\SB at chgetspace}%
-    \def\SB at chimpspace{\let\SB at donext\SB at chstep}%
+    \let\SB at chimpspace\SB at chnxtstep%
     \let\SB at chespace\SB at chendspace%
     \let\SB at chdone\SB at chspcdone%
   \else%
-    \def\SB at chimpspace{\let\SB at donext\SB at chdone}%
-    \def\SB at chexpspace{\let\SB at donext\SB at chdone}%
+    \let\SB at chimpspace\SB at chnxtdone%
+    \let\SB at chexpspace\SB at chnxtdone%
   \fi%
 }
 \newcommand\SB at chgetspace{%
@@ -9268,9 +9524,9 @@
 %    \begin{macrocode}
 \newcommand\SB at chmbar{%
   \ifmeasures%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \else%
-    \SB at chespace\let\SB at donext\SB at chstep%
+    \SB at chespace\SB at chnxtstep%
   \fi%
 }
 %    \end{macrocode}
@@ -9319,8 +9575,7 @@
   \fi%
 }
 \newcommand\SB at chegrpouter{%
-  \SB at testfalse%
-  \expandafter\SB at outertest\expandafter{\meaning\SB at next}%
+  \SB at outertest%
   \ifSB at test%
     \expandafter\SB at chegrpdone%
   \else%
@@ -9347,13 +9602,15 @@
 % Consume it and all of its arguments, and load them into some
 % registers for future processing.
 % (Part of the ligature might fall into this lyric text or might
-% not, depending on if we decide to add hyphenation.)
+% not, depending on whether we decide to add hyphenation.)
 % Then end the lyric text here.
 %    \begin{macrocode}
 \newcommand\SB at chlig[5]{%
   \gdef\SB at ligpre{{#3}}%
   \gdef\SB at ligpost{\[#2]{#4}}%
-  \gdef\SB at ligfull{\[\SB at noreplay{\hphantom{{\lyricfont#3}}}#2]{#5}}%
+  \gdef\SB at ligfull{%
+    \[\SB at noreplay{\hphantom{{\lyricfont\relax#3}}}#2]{#5}%
+  }%
   \SB at chdone%
 }
 \newcommand\SB at mchlig[5]{%
@@ -9446,16 +9703,18 @@
 \newcommand\SB at emitchord{%
   \ifSB at inverse\else\ifSB at inchorus\else\SB at errchord\fi\fi%
   \SB at testfalse%
-  \ifcat\noexpand\SB at next\noexpand\@sptoken\SB at testtrue\fi%
+  \ifcat\noexpand\SB at next\@sptoken\SB at testtrue\fi%
   \ifcat\noexpand\SB at next.\SB at testtrue\fi%
   \ifx\SB at next\SB at par\SB at testtrue\fi%
   \ifx\SB at next\egroup\SB at testtrue\fi%
   \ifx\SB at next\endgroup\SB at testtrue\fi%
-  \SB at hyphtests%
+  {\csname%
+     SB at HT@\ifx\SB at nextname\relax\meaning\SB at next\else\SB at nextname\fi%
+   \endcsname}%
   \ifSB at test\SB at wordendstrue\else\SB at wordendsfalse\fi%
 %    \end{macrocode}
 % Next, compare the width of the lyric to the width of the chord to
-% determine if hyphenation might be necessary.
+% determine whether hyphenation might be necessary.
 % The original lyric text might have ended in a string of one or more
 % explicit hyphens, enumerated by |\SB at numhyps|.
 % If it ended in exactly one, the lyric-scanning code suppresses that hyphen
@@ -9513,7 +9772,8 @@
         \advance\SB at dimen.5em%
         \hbox to\SB at dimen{\unhbox\SB at chordbox\hfil}%
         \hbox to\SB at dimen{%
-          \unhcopy\SB at lyricbox\hfil\char\hyphenchar\font\hfil%
+          \unhcopy\SB at lyricbox\hfil
+          \ifnum\hyphenchar\font>\m at ne\char\hyphenchar\font\hfil\fi%
         }%
         \global\SB at cnt\@m%
         \gdef\SB at temp{\expandafter\SB at clearlig\SB at ligpost}%
@@ -9534,7 +9794,7 @@
 % difficult to read.
 % If the chord has a lyric but it doesn't end on a word boundary, insert
 % an appropriate penalty to prevent linebreaking without hyphenation.
-% Also preserve the spacefactor in this case, which allows \LaTeX\ to
+% Also preserve the spacefactor in this case, which allows \LaTeX{} to
 % fine-tune the spacing between consecutive characters in the word that
 % contains the chord.
 %    \begin{macrocode}
@@ -9614,7 +9874,7 @@
 % do not render well in arbitrary sizes.
 % To solve the problem, we must therefore choose an appropriate size
 % individually for each possible base font size $b$.
-% This is the solution adopted by the rest of \LaTeX\ for such things.
+% This is the solution adopted by the rest of \LaTeX{} for such things.
 % For example, \LaTeX's |\DeclareMathSizes| macro defines an appropriate
 % superscript size for each possible base font size.
 % The macro below creates a similar macro that that defines an appropriate
@@ -9727,6 +9987,31 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\SB at hat@tr}
+% In verses/choruses where chords are being memorized, |\SB at activehat|
+% gets set to this definition, which marks the current chord as immune to
+% memorization.
+%    \begin{macrocode}
+\newcommand\SB at hat@tr{%
+  \ifmmode^\else\global\SB at nohatfalse\fi%
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\SB at hat@notr}
+% In verses/choruses where chords are being replayed, |\SB at activehat|
+% get set to the following, which replays the next memorized chord and
+% subjects it to any required transposition and/or note conversion.
+%    \begin{macrocode}
+\newcommand\SB at hat@notr{%
+  \ifmmode^\else%
+    \SB at lop\SB at ctail\SB at toks%
+    \expandafter\transposehere\expandafter{\the\SB at toks}%
+  \fi%
+}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\SB at loadactives}
 % It's cumbersome to have to type |\shrp|, |\flt|, and |\mbar| every time you
 % want a sharp, flat, or measure bar, so within verses and choruses we allow
@@ -9768,8 +10053,8 @@
 % \begin{macro}{\SB at cr@}
 % Reserve token registers to record a history of the chords seen in a verse.
 %    \begin{macrocode}
-\newtoks\SB at cr@
-\newtoks\SB at ctail
+\SB at newtoks\SB at cr@
+\SB at newtoks\SB at ctail
 %    \end{macrocode}
 % \end{macro}
 %
@@ -9787,7 +10072,7 @@
 %    \begin{macrocode}
 \newcommand\newchords[1]{%
   \@ifundefined{SB at cr@#1}{%
-    \expandafter\newtoks\csname SB at cr@#1\endcsname%
+    \expandafter\SB at newtoks\csname SB at cr@#1\endcsname%
     \global\csname SB at cr@#1\endcsname{\\}%
   }{\SB at errdup{#1}}%
 }
@@ -10010,7 +10295,7 @@
 % properly.
 %    \begin{macrocode}
 \newif\ifSB at gettabind\SB at gettabindfalse
-\newdimen\SB at tabindent
+\SB at newdimen\SB at tabindent
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -10020,8 +10305,8 @@
 % \begin{macro}{\SB at targfing}
 % Reserve some macro names in which to store the three pieces of the
 % second argument to the |\gtab| macro.
-% The first is for the fret number, the second is for the \meta{strings}
-% info, and the last is for the \meta{fingering} info.
+% The first is for the fret number, the second is for the \Meta{strings}
+% info, and the last is for the \Meta{fingering} info.
 %    \begin{macrocode}
 \newcommand\SB at targfret{}
 \newcommand\SB at targstr{}
@@ -10038,7 +10323,7 @@
 % $\varepsilon$-\TeX).
 %
 % We therefore adopt the alternative strategy of converting each token
-% in the \meta{strings} and \meta{fingering} arguments of a |\gtab| macro
+% in the \Meta{strings} and \Meta{fingering} arguments of a |\gtab| macro
 % into a control sequence (using |\csname|).
 % We can then temporarily assign meanings to those control sequences and
 % replay the arguments to achieve various effects.
@@ -10107,7 +10392,7 @@
 %
 % \begin{macro}{\SB at gtmax}
 % To compute the height of the tablature diagram, we must identify the
-% maximum fret number in the \meta{strings} argument.
+% maximum fret number in the \Meta{strings} argument.
 % This is accomplished by using the following macro in combination with
 % |\SB at gtset| above.
 %    \begin{macrocode}
@@ -10248,10 +10533,10 @@
 % \begin{macro}{\SB at ctoken}
 % Break the second argument to a |\gtab| macro into three sub-arguments.
 % The possible forms are:
-% (a) \meta{strings},
-% (b) \meta{fret}|:|\meta{strings},
-% (c) \meta{strings}|:|\meta{fingering}, or
-% (d) \meta{fret}|:|\meta{strings}|:|\meta{fingering}.
+% (a) \Meta{strings},
+% (b) \Meta{fret}|:|\Meta{strings},
+% (c) \Meta{strings}|:|\Meta{fingering}, or
+% (d) \Meta{fret}|:|\Meta{strings}|:|\Meta{fingering}.
 % To distinguish forms (b) and (c), we count the number of tokens before
 % the first colon.
 % If there is only one token, we assume it must be form (b), since frets
@@ -10375,10 +10660,10 @@
 % number of index entries when building a song index. The other two types are
 % designated by the user.
 %
-% As is typical of \LaTeX\ indexes, generation of song book indexes requires
+% As is typical of \LaTeX{} indexes, generation of song book indexes requires
 % two passes of document compilation. During the first pass, data files are
 % generated with song titles, authors, and scripture references. An external
-% program is then used to produce \LaTeX\ source files from those data files.
+% program is then used to produce \LaTeX{} source files from those data files.
 % During the second pass of document compilation, those source files are
 % imported to typeset all the indexes and display them in the document.
 %
@@ -10389,7 +10674,7 @@
 % the data is stored in a box of non-immediate write whatsit nodes.
 % \item The whatsits are migrated out to the top of the song box when
 % it is finalized at |\endsong|.
-% \item When the song box is shipped out to the output file, \TeX\ expands
+% \item When the song box is shipped out to the output file, \TeX{} expands
 % the whatsits, causing the data to be written to the |.sxc| auxiliary file.
 % \item At the |\end{document}| line, the |.sxc| is processed multiple
 % times---once for each index---to split the data into the respective
@@ -10398,7 +10683,7 @@
 % The first and second steps allow index references to point to the
 % beginning of the song no matter where the indexing commands appear
 % within the song.
-% The third step allows \TeX\ to drop index entries that refer to
+% The third step allows \TeX{} to drop index entries that refer to
 % songs that do not actually appear in the output (e.g., because of
 % |\includeonlysongs|).
 % It also allows index entries to refer to information that is only decided
@@ -10405,9 +10690,52 @@
 % at shipout time, such as page numbers.
 % The fourth step allows all indexing to be accomplished with at most one
 % write register.
-% \LaTeX\ provides extremely few write registers, so using as
+% \LaTeX{} provides extremely few write registers, so using as
 % few as possible is essential for supporting books with many indexes.
 %
+% \begin{macro}{\songtarget}\MainImpl{songtarget}
+% This macro is invoked by each \mac{beginsong} environment with two arguments:
+% (1) a suggested pdf bookmark index level, and
+% (2) a target name to which hyperlinks for this song in the index will refer.
+% The macro is expected to produce a suitable pdf bookmark entry and/or
+% link target.
+% The default definition tries to use |\pdfbookmark| if generating a PDF,
+% and resorts to |\hypertarget| (if it exists) otherwise.
+% The user can redefine the macro to customize how and whether bookmarks
+% and/or links are created.
+%    \begin{macrocode}
+\newcommand\songtarget[2]{%
+  \ifnum\@ne=0\ifSB at pdf\ifx\pdfbookmark\undefined\else%
+                       \ifx\pdfbookmark\relax\else1\fi\fi\fi\relax%
+    \pdfbookmark[#1]{\thesongnum. \songtitle}{#2}%
+  \else\ifx\hypertarget\undefined%
+  \else\ifx\hypertarget\relax\else%
+    \hypertarget{#2}{\relax}%
+  \fi\fi\fi%
+}
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\songlink}\MainImpl{songlink}
+% This macro is invoked by the index code to produce a link to a song target
+% created by \mac{songtarget}.
+% Its two arguments are:
+% (1) the target name (same as the second argument to \mac{songtarget}, and
+% (2) the text that is to be linked.
+% The default implementation uses |\hyperlink| if it exists; otherwise it
+% just leaves the text unlinked.
+%    \begin{macrocode}
+\newcommand\songlink{%
+  \ifnum\@ne=0\ifx\hyperlink\undefined\else%
+              \ifx\hyperlink\relax\else1\fi\fi\relax%
+    \expandafter\hyperlink%
+  \else%
+    \expandafter\@gobble%
+  \fi%
+}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\SB at indexlist}
 % This macro records the comma-separated list of the identifiers of indexes
 % associated with the current book section.
@@ -10447,7 +10775,7 @@
   }%
   \if at filesw%
     \ifx\SB at out\relax%
-      \newwrite\SB at out%
+      \SB at newwrite\SB at out%
       \immediate\openout\SB at out=\jobname.sxc\relax%
     \fi%
     \immediate\write\SB at out{\noexpand\SB at iwrite{#3}{#2}}%
@@ -10496,12 +10824,46 @@
 %    \begin{macrocode}
 \newcommand\SB at cwrite[2]{%
   \ifx\SB at out\relax\else%
-    \protected at write\SB at out{}{\protect\SB at iwrite{#1}{#2}}%
+    \protected at write\SB at out\SB at keepactive{\protect\SB at iwrite{#1}{#2}}%
   \fi%
 }
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\SB at keepactive}
+% By default, the |inputenc| package expands Unicode characters into macro
+% names when writing them to files.
+% This behavior must be inhibited when writing to the |.sxc| file, since
+% |songidx| needs the original Unicode characters for sorting.
+% To achieve this, we temporarily redefine most active characters so that
+% they expand to an unexpandable string version of themselves.
+%    \begin{macrocode}
+\newcommand\SB at keepactive{}
+{\catcode`\~\active
+ \catcode`\.12
+ \def\\#1#2{%
+   \endgroup
+   \SB at app\gdef\SB at keepactive{\def#1{#2}}%
+ }
+ \def\SB at temp#1#2{%
+   \SB at cnt#1\relax
+   \loop
+     \begingroup
+       \uccode`\~\SB at cnt
+       \uccode`\.\SB at cnt
+     \uppercase{\\~.}
+   \ifnum\SB at cnt<#2\relax
+     \advance\SB at cnt\@ne
+   \repeat
+ }
+ \SB at temp{1}{8}
+ \SB at temp{11}{11}
+ \SB at temp{14}{91}
+ \SB at temp{93}{255}
+}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\SB at iwrite}
 % The line contributed by |\SB at cwrite| to the |.sxc| file is an |\SB at iwrite|
 % macro that re-outputs the data to an appropriate |.sxd| file.
@@ -10545,7 +10907,7 @@
 % The following box register stores index data until it can be migrated to
 % the top of the song box currently under construction.
 %    \begin{macrocode}
-\newbox\SB at songwrites
+\SB at newbox\SB at songwrites
 %    \end{macrocode}
 % \end{macro}
 %
@@ -10674,7 +11036,7 @@
 % \begin{macro}{\authignoreword}\MainImpl{authignoreword}
 % \begin{macro}{\titleprefixword}\MainImpl{titleprefixword}
 % \changes{v2.0}{2007/06/18}{Added.}
-% The |songidx| index-generation program understands several different
+% The |songidx| index-generation script understands several different
 % directives that each dictate various aspects of how index entries are
 % parsed, sorted, and displayed.
 % Such directives should typically appear at the start of the |.sxd|
@@ -10681,12 +11043,14 @@
 % file just after the header line that identifies the type of index.
 %    \begin{macrocode}
 \newcommand\SB at idxcmd[3]{%
-  \ifx\SB at out\relax\else%
+  \ifx\SB at allindexes\@empty%
+    \SB at warnnoidx%
+  \else\ifx\SB at out\relax\else%
     \@for\SB at temp:=\SB at allindexes\do{%
       \csname SB at idxsel@\SB at temp\endcsname%
         {\SB@@idxcmd{#1}}{\SB@@idxcmd{#2}}{\SB@@idxcmd{#3}}%
     }%
-  \fi%
+  \fi\fi%
 }
 \newcommand\SB@@idxcmd[1]{%
   \def\SB at tempii{#1}%
@@ -10701,9 +11065,9 @@
 \newcommand\authignoreword[1]{}
 \newcommand\titleprefixword[1]{}
 {\catcode`\%=12
- \gdef\authsepword#1{\SB at idxcmd{}{%sep #1}{}}
- \gdef\authbyword#1{\SB at idxcmd{}{%after #1}{}}
- \gdef\authignoreword#1{\SB at idxcmd{}{%ignore #1}{}}
+ \gdef\authsepword#1{\SB at idxcmd{}{}{%sep #1}}
+ \gdef\authbyword#1{\SB at idxcmd{}{}{%after #1}}
+ \gdef\authignoreword#1{\SB at idxcmd{}{}{%ignore #1}}
  \gdef\titleprefixword#1{\SB at idxcmd{%prefix #1}{}{}}}
 \@onlypreamble\authsepword
 \@onlypreamble\authbyword
@@ -10717,13 +11081,6 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}{\SB at idxtitlebox}
-% Define a box to hold the index title.
-%    \begin{macrocode}
-\newbox\SB at idxtitlebox
-%    \end{macrocode}
-% \end{macro}
-%
 % \begin{macro}{\SB at idxlineskip}
 % Set the spacing between lines in an index.
 %    \begin{macrocode}
@@ -10736,7 +11093,7 @@
 % When rendering an index entry $X\ldots Y$ that is too long to fit on one
 % physical line, we must break text $X$ and/or $Y$ up into multiple lines.
 % Text $X$ should be typeset as a left-justified paragraph with a right
-% margin of about 2em; however, it's final line must not be so long that it
+% margin of about 2em; however, its final line must not be so long that it
 % cannot fit even the first item of list $Y$.
 % Text $Y$ should be typeset as a right-justified paragraph whose first line
 % begins on the last line of $X$.
@@ -10786,13 +11143,13 @@
 %
 % First, we must pre-compute the width $w_1$ of the final line of $X$ when
 % $X$ is typeset as a left-justified paragraph, storing it in |\SB at dimenii|.
-% This is necessary because in order to force \TeX\ to typeset the first
+% This is necessary because in order to force \TeX{} to typeset the first
 % line of $Y$ at some chosen width $w_2$, we must insert leaders of width
 % $c-w_1-w_2$ into the paragraph between $X$ and $Y$, where $c$ is the column
 % width.
 %
 % Computing this width $w_1$ is a bit tricky.
-% We must tell \TeX\ that the last line of $X$ must not be so long that it
+% We must tell \TeX{} that the last line of $X$ must not be so long that it
 % does not even have room for the first item of $Y$.
 % Thus, we must strip off the first item of $Y$ and add it (or a non-breaking
 % space of equivalent width) to the end of $X$ to typeset the paragraph.
@@ -11007,7 +11364,7 @@
 % \changes{v2.8}{2009/03/06}{Changed argument order}
 % Create an index with title \argp{2} and with \argp{1} columns (must be a
 % literal constant). Input the index contents from external file \argp{3},
-% which is expected to be a \TeX\ file.
+% which is expected to be a \TeX{} file.
 %    \begin{macrocode}
 \newcommand\SB at displayindex[3]{%
   \ifsongindexes\begingroup%
@@ -11015,7 +11372,7 @@
     \advance\SB at colwidth-#1\columnsep%
     \advance\SB at colwidth\columnsep%
     \divide\SB at colwidth#1%
-    \setbox\SB at idxtitlebox\vbox{%
+    \setbox\SB at envbox\vbox{%
       \let\SB at temp\songsection%
       \ifx\chapter\undefined\else%
         \ifx\chapter\relax\else%
@@ -11026,18 +11383,16 @@
     }%
 %    \end{macrocode}
 % The |.sbx| index file might not exist (e.g., if this is the first pass
-% through the \TeX\ compiler).
+% through the \TeX{} compiler).
 % If it exists, first try typesetting its content as a small index
 % (one column, centered, with no divisions).
 %    \begin{macrocode}
     \IfFileExists{\csname SB at idxfilename@#3\endcsname.sbx}{%
-      \ifx\hyperlink\undefined\let\hyperlink\@secondoftwo\fi%
-      \ifx\hyperlink\relax\let\hyperlink\@secondoftwo\fi%
       \ifsepindexes%
         \global\setbox\SB at box\vbox{%
           \null%
           \vfil%
-          \unvcopy\SB at idxtitlebox%
+          \unvcopy\SB at envbox%
           \vskip.5in\@minus.3in\relax%
           \hbox to\hsize{%
             \hfil%
@@ -11077,13 +11432,13 @@
       \ifsepindexes%
         \vbox to\textheight{%
           \vfil%
-          \unvbox\SB at idxtitlebox%
+          \unvbox\SB at envbox%
           \vskip1em\relax%
           \hbox to\hsize{\hfil[Index not yet generated.]\hfil}%
           \vskip\z@\@plus2fil\relax%
         }%
       \else%
-        \unvbox\SB at idxtitlebox%
+        \unvbox\SB at envbox%
         \hbox to\hsize{\hfil[Index not yet generated.]\hfil}%
       \fi%
     }%
@@ -11117,7 +11472,7 @@
     \advance\SB at cnt\m at ne%
   \repeat%
   \edef\SB at oneidxpage{\the\SB at toks}%
-  \unvbox\SB at idxtitlebox%
+  \unvbox\SB at envbox%
   \vskip.2in\relax%
   \nointerlineskip%
   \null%
@@ -11188,12 +11543,16 @@
 % \argp{3} is the index identifier (which was passed to |\newindex|).
 %    \begin{macrocode}
 \newcommand\SB at maketitleindex{%
-  \renewenvironment{SB at lgidx}[1]{
-    \hbox{\SB at colorbox\idxbgcolor{\vbox{%
-      \hbox to\idxheadwidth{{\idxheadfont\relax##1}\hfil}%
-    }}}%
-    \nobreak\vskip3\p@\@plus2\p@\@minus2\p@\nointerlineskip%
-  }{\penalty-50\vskip5\p@\@plus5\p@\@minus4\p@}%
+  \ifnum\idxheadwidth>\z@%
+    \renewenvironment{SB at lgidx}[1]{
+      \hbox{\SB at colorbox\idxbgcolor{\vbox{%
+        \hbox to\idxheadwidth{{\idxheadfont\relax##1}\hfil}%
+      }}}%
+      \nobreak\vskip3\p@\@plus2\p@\@minus2\p@\nointerlineskip%
+    }{\penalty-50\vskip5\p@\@plus5\p@\@minus4\p@}%
+  \else%
+    \renewenvironment{SB at lgidx}[1]{}{}%
+  \fi%
   \renewenvironment{SB at smidx}[1]{}{}%
   \renewcommand\idxentry[2]{%
     \SB at ellipspread{\idxtitlefont\relax\ignorespaces##1\unskip}%
@@ -11360,6 +11719,15 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\SB at warnnoidx}
+%    \begin{macrocode}
+\newcommand\SB at warnnoidx{%
+  \SB at Warn{Index command has no effect since no indexes are ye%
+  t declared}%
+}
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}{\SB at errboo}
 %    \begin{macrocode}
 \newcommand\SB at errboo{%
@@ -11721,9 +12089,9 @@
 % \begin{macro}{\iftranscapos}
 % \begin{macro}{\ifnolyrics}
 % \begin{macro}{\ifrawtext}
-% \begin{macro}{\ifpdfindex}
 % \begin{macro}{\ifsongindexes}
 % \begin{macro}{\ifsepindexes}
+% \begin{macro}{\ifpagepreludes}
 % \begin{macro}{\ifSB at colorboxes}
 % \begin{macro}{\ifSB at omitscrip}
 % Reserve conditionals for all of the various option settings.
@@ -11739,10 +12107,11 @@
 \newif\iftranscapos
 \newif\ifnolyrics
 \newif\ifrawtext
-\newif\ifpdfindex\pdfindextrue
 \newif\ifsongindexes\songindexestrue
 \newif\ifsepindexes\sepindexestrue
-\newif\ifSB at colorboxes\SB at colorboxestrue
+\newif\ifpagepreludes
+\newif\ifSB at colorboxes
+\IfFileExists{color.sty}\SB at colorboxestrue\SB at colorboxesfalse
 \newif\ifSB at omitscrip
 %    \end{macrocode}
 % \end{macro}
@@ -11761,12 +12130,16 @@
 % \end{macro}
 %
 % \begin{macro}{\nolyrics}
-% The |\nolyrics| macro is just shorthand for |\nolyricstrue|.
+% \begin{macro}{\pagepreludes}
+% The |\nolyrics| and |\pagepreludes| macros are just shorthand for
+% |\nolyricstrue| and |\pagepreludestrue|, respectively.
 %    \begin{macrocode}
 \newcommand\nolyrics{}
 \let\nolyrics\nolyricstrue
+\newcommand\pagepreludes{\pagepreludestrue\songpos0}
 %    \end{macrocode}
 % \end{macro}
+% \end{macro}
 %
 % Finally we're ready to process all of the package options.
 % This is delayed until near the end because the option processing code
@@ -11776,11 +12149,6 @@
 \ProcessOptions\relax
 %    \end{macrocode}
 %
-% If we're not generating a pdf, then don't generate the pdf index.
-%    \begin{macrocode}
-\ifSB at pdf\else\pdfindexfalse\fi
-%    \end{macrocode}
-%
 % \begin{macro}{\SB at colorbox}
 % Include the colors package and define colors, if requested.
 %    \begin{macrocode}
@@ -11815,7 +12183,7 @@
 % a text file.
 %    \begin{macrocode}
 \ifrawtext
-  \newwrite\SB at txtout
+  \SB at newwrite\SB at txtout
   \immediate\openout\SB at txtout=\jobname.txt
   \newif\ifSB at doEOL
   {\catcode`\^^M12 %

Modified: trunk/Master/texmf-dist/source/latex/songs/songs.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/songs/songs.ins	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/source/latex/songs/songs.ins	2017-06-10 21:36:05 UTC (rev 44553)
@@ -1,4 +1,4 @@
-%% Copyright (C) 2012 Kevin W. Hamlen
+%% Copyright (C) 2017 Kevin W. Hamlen
 %%
 %% This program is free software; you can redistribute it and/or
 %% modify it under the terms of the GNU General Public License
@@ -27,7 +27,7 @@
 
 This is a generated file.
 
-Copyright (C) 2012 by Kevin W. Hamlen
+Copyright (C) 2017 by Kevin W. Hamlen
 
 This file may be distributed and/or modified under the conditions of
 the LaTeX Project Public License, either version 1.3a of this license

Modified: trunk/Master/texmf-dist/tex/latex/songs/songs.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/songs/songs.sty	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/texmf-dist/tex/latex/songs/songs.sty	2017-06-10 21:36:05 UTC (rev 44553)
@@ -8,7 +8,7 @@
 %% 
 %% This is a generated file.
 %% 
-%% Copyright (C) 2012 by Kevin W. Hamlen
+%% Copyright (C) 2017 by Kevin W. Hamlen
 %% 
 %% This file may be distributed and/or modified under the conditions of
 %% the LaTeX Project Public License, either version 1.3a of this license
@@ -22,12 +22,14 @@
 %% 
 \NeedsTeXFormat{LaTeX2e}
 \ProvidesPackage{songs}
-  [2012/03/17 v2.14 Songs package]
+  [2017/06/05 v3.0 Songs package]
 \newif\ifSB at etex
 \ifx\eTeXversion\undefined\else
   \ifx\eTeXversion\relax\else
     \SB at etextrue
-    \IfFileExists{etex.sty}{\RequirePackage{etex}}{}
+    \ifx\e at alloc\@undefined
+      \IfFileExists{etex.sty}{\RequirePackage{etex}}{}
+    \fi
   \fi
 \fi
 \newif\ifSB at pdf\SB at pdffalse
@@ -41,30 +43,44 @@
 \newif\ifSB at preamble
 \SB at preambletrue
 \newif\ifSB at test
+\newif\ifSB at testii
 \newcommand\SB at temp{}
 \newcommand\SB at tempii{}
 \newcommand\SB at tempiii{}
 \newcommand\SB at tempiv{}
 \newcommand\SB at tempv{}
-\newdimen\SB at dimen
-\newdimen\SB at dimenii
-\newdimen\SB at dimeniii
-\newdimen\SB at dimeniv
-\newbox\SB at box
-\newbox\SB at boxii
-\newbox\SB at boxiii
-\newtoks\SB at toks
-\newcount\SB at cnt
-\newcount\SB at cntii
-\newskip\SB at skip
+\newcommand\SB at newcount[1]{\@ifdefinable#1{\newcount#1}}
+\newcommand\SB at newdimen[1]{\@ifdefinable#1{\newdimen#1}}
+\newcommand\SB at newbox[1]{\@ifdefinable#1{\newbox#1}}
+\newcommand\SB at newtoks[1]{\@ifdefinable#1{\newtoks#1}}
+\newcommand\SB at newwrite[1]{\@ifdefinable#1{\newwrite#1}}
+\SB at newdimen\SB at dimen
+\SB at newdimen\SB at dimenii
+\SB at newdimen\SB at dimeniii
+\SB at newdimen\SB at dimeniv
+\SB at newbox\SB at box
+\SB at newbox\SB at boxii
+\SB at newbox\SB at boxiii
+\SB at newtoks\SB at toks
+\SB at newcount\SB at cnt
+\SB at newcount\SB at cntii
+\newlength\SB at skip
+\SB at newbox\SB at envbox
 \RequirePackage{keyval}
+\newcommand\SB at app[3]{%
+  \expandafter#1\expandafter#2\expandafter{#2#3}%
+}
 \newcommand\lyricfont{\normalfont\normalsize}
 \newcommand\stitlefont{%
-  \ifslides\sffamily\Huge\else\sffamily\slshape\Large\fi%
+  \sffamily\ifslides\Huge\else\slshape\Large\fi%
 }
 \newcommand\versefont{}
 \newcommand\chorusfont{}
 \newcommand\notefont{}
+\newcommand\meterfont{\tiny\sffamily\upshape}
+\newcommand\echofont{%
+  \ifdim\fontdimen\@ne\font>\z@\upshape\else\slshape\fi%
+}
 \newcommand\scripturefont{%
   \usefont{OT1}{pzc}{mb}{it}%
   \shiftdblquotes{-1.1\p@}\z@{-2\p@}\z@%
@@ -102,13 +118,13 @@
 \newcommand\everychorus{}
 \newcommand\printchord[1]{\sffamily\slshape\large#1}
 \newcommand\chordlocals{}
-\newskip\versesep
+\newlength\versesep
 \versesep123456789sp\relax
-\newskip\afterpreludeskip
+\newlength\afterpreludeskip
 \afterpreludeskip=2\p@\@plus4\p@
-\newskip\beforepostludeskip
+\newlength\beforepostludeskip
 \beforepostludeskip=2\p@\@plus4\p@
-\newskip\baselineadj
+\newlength\baselineadj
 \baselineadj\z at skip
 \newcommand\clineparams{%
   \baselineskip\f at size\p@%
@@ -128,12 +144,12 @@
 \newlength\sbarheight
 \setlength\sbarheight\p@
 \interlinepenalty\@m
-\newcount\vvpenalty\vvpenalty200
-\newcount\ccpenalty\ccpenalty200
-\newcount\vcpenalty\vcpenalty200
-\newcount\cvpenalty\cvpenalty200
-\newcount\brkpenalty\brkpenalty200
-\newcount\spenalty\spenalty100
+\SB at newcount\vvpenalty\vvpenalty200
+\SB at newcount\ccpenalty\ccpenalty200
+\SB at newcount\vcpenalty\vcpenalty200
+\SB at newcount\cvpenalty\cvpenalty200
+\SB at newcount\brkpenalty\brkpenalty200
+\SB at newcount\spenalty\spenalty100
 \newcommand\songmark{}
 \newcommand\versemark{}
 \newcommand\chorusmark{}
@@ -151,8 +167,8 @@
 \let\colbotglue\z at skip
 \newcommand\lastcolglue{}
 \let\lastcolglue\@flushglue
-\newcount\minfrets\minfrets4
-\newdimen\SB at colwidth
+\SB at newcount\minfrets\minfrets4
+\SB at newdimen\SB at colwidth
 \DeclareOption{slides}{\slides}
 \newcommand\slides{%
   \slidestrue%
@@ -186,11 +202,14 @@
 \newcommand\SB at outer{\outer}
 \DeclareOption{unouter}{\let\SB at outer\relax}
 \DeclareOption{rawtext}{\rawtexttrue\indexesoff}
-\DeclareOption{nopdfindex}{\pdfindexfalse}
 \DeclareOption{noshading}{\SB at colorboxesfalse}
 \DeclareOption{noindexes}{\indexesoff}
 \newcommand\indexeson{\songindexestrue}
 \newcommand\indexesoff{\songindexesfalse}
+\DeclareOption{nopdfindex}{%
+  \let\songtarget\@gobbletwo%
+  \let\songlink\@secondoftwo%
+}
 \newif\ifSB at measurespec
 \newif\ifSB at chordedspec
 \DeclareOption{chorded}{\chordson}
@@ -310,41 +329,40 @@
   \songcolumns\SB at numcols
   \SB at preamblefalse
 }
-\newbox\SB at songbox
-\newcount\SB at numcols\SB at numcols\tw@
-\newcount\SB at colnum
-\newbox\SB at colbox
-\newbox\SB at pgbox
-\newbox\SB at mrkbox
+\SB at newbox\SB at songbox
+\SB at newcount\SB at numcols\SB at numcols\tw@
+\SB at newcount\SB at colnum
+\SB at newbox\SB at colbox
+\SB at newbox\SB at pgbox
+\SB at newbox\SB at mrkbox
 \newcommand\SB at maxmin[3]{\ifdim#1#2#3#1#3\fi}
-\newcommand\SB at mkpage[3]{%
-  \begingroup%
+\newcommand\SB at mkpage[4]{%
+  \ifvoid#2\else\begingroup%
+    \edef\SB at temp{\ifnum#2=\SB at box\SB at boxii\else\SB at box\fi}%
+    \edef\SB at tempii{\ifnum#2=\SB at boxiii\SB at boxii\else\SB at boxiii\fi}%
     \splitmaxdepth\maxdepth\splittopskip\z at skip%
-    \global\setbox#1\vbox{%
-      \unvbox#1%
-      \nointerlineskip%
-      \null%
-      \vfil%
-    }%
-    \loop\ifnum#2<\SB at numcols%
-      \setbox\SB at box\vsplit#1to#3\relax%
-      \ifvoid#1%
-        #2\SB at numcols%
+    \ifnum#1=\z@\global\setbox#2\vbox{\unvbox#2\vfil}\fi%
+    \loop\ifnum#3<\SB at numcols%
+      \ifnum#1=\z@\setbox\SB at tempii\copy#2\fi%
+      \setbox\SB at temp\vsplit#2to#4\relax%
+      \ifvoid#2%
+        \ifnum#1=\z@%
+          \global\setbox#2\box\SB at tempii%
+        \else%
+          \SB at updatepage%
+          \global\advance#3\@ne%
+        \fi%
+        #3\SB at numcols%
       \else%
         \SB at updatepage%
-        \global\advance#2\@ne%
+        \global\advance#3\@ne%
         \ifrepchorus\ifvoid\SB at chorusbox\else%
-          \SB at insertchorus#1%
+          \SB at insertchorus#2%
         \fi\fi%
       \fi%
     \repeat%
-    \global\setbox#1\vbox{%
-      \unvbox\SB at box%
-      \unvbox#1%
-      \unskip%
-      \setbox\SB at box\lastbox%
-    }%
-  \endgroup%
+    \ifnum#1=\z@\global\setbox#2\vbox{\unvbox#2\unskip}\fi%
+  \endgroup\fi%
 }
 \newcommand\SB at migrate[1]{%
   \SB at toks\expandafter{#1}%
@@ -364,23 +382,29 @@
     \advance\SB at dimen-\wd\SB at pgbox%
     \unhbox\SB at pgbox%
     \ifdim\SB at dimen=\z@\else\hskip\SB at dimen\relax\fi%
-    \box\SB at box%
+    \box\SB at temp%
   }%
 }
-\newcommand\SB at droppage{\setbox\SB at box\box\voidb at x}
-\newcommand\SB at output{%
+\newcommand\SB at droppage{\setbox\SB at temp\box\voidb at x}
+\newcommand\SB at output[1]{%
   \ifnum\SB at numcols>\z@\begingroup%
     \loop%
       \SB at dimen\textheight%
       \ifinner\else\advance\SB at dimen-\pagetotal\fi%
-      \SB at mkpage\SB at colbox\SB at colnum\SB at dimen%
-      \ifnum\SB at colnum<\SB at numcols\else%
+      \SB at mkpage#1\SB at colbox\SB at colnum\SB at dimen%
+      \SB at testfalse\SB at testiitrue%
+      \ifnum#1>\@ne\ifvoid\SB at colbox\ifnum\SB at colnum>\z@%
+        \SB at testtrue\SB at testiifalse%
+      \fi\fi\fi%
+      \ifnum\SB at colnum<\SB at numcols\SB at testiifalse\else\SB at testtrue\fi%
+      \ifSB at test%
         \unvbox\SB at mrkbox%
         \ifinner\else\kern\z@\fi%
         \box\SB at pgbox%
         \ifinner\else\vfil\break\vskip\vsize\relax\fi%
         \global\SB at colnum\z@%
-    \repeat%
+      \fi%
+    \ifSB at testii\repeat%
   \endgroup\else%
     \unvbox\SB at colbox\unskip%
   \fi%
@@ -417,7 +441,7 @@
           \advance\SB at cnt\m at ne%
         \repeat%
       }%
-      \SB at output%
+      \SB at output1%
     \else%
       \ifnum\lastpenalty=-\@M\null\fi%
       \break%
@@ -434,7 +458,7 @@
     \ifinner\else\advance\SB at dimen-\pagetotal\fi%
     \setbox\SB at boxii\vbox{\SB at putboxes\unvcopy}%
     \SB at cntii\SB at colnum%
-    \SB at mkpage\SB at boxii\SB at cntii\SB at dimen%
+    \SB at mkpage0\SB at boxii\SB at cntii\SB at dimen%
     \SB at spos%
     \global\SB at cnt\SB at cnt%
   \endgroup%
@@ -443,17 +467,17 @@
 \newcommand\SB at spbegnew{%
   \setbox\SB at boxiii\copy\SB at songbox%
   \SB at cntii\z@%
-  \SB at mkpage\SB at boxiii\SB at cntii\textheight%
+  \SB at mkpage0\SB at boxiii\SB at cntii\textheight%
 }
 \newcommand\SB at spextold{%
   \ifodd\c at page\else%
     \SB at cntii\z@%
-    \SB at mkpage\SB at boxii\SB at cntii\textheight%
+    \SB at mkpage0\SB at boxii\SB at cntii\textheight%
   \fi%
 }
 \newcommand\SB at spextnew{%
   \SB at cntii\z@%
-  \SB at mkpage\SB at boxiii\SB at cntii\textheight%
+  \SB at mkpage0\SB at boxiii\SB at cntii\textheight%
 }
 \newcommand\SB at spdblpg{%
   \ifnum\SB at cntii<\SB at numcols%
@@ -499,7 +523,7 @@
     \advance\SB at cnt\@ne%
     \ifnum\SB at cnt<\SB at numcols%
       \setbox\SB at boxiii\copy\SB at songbox%
-      \SB at mkpage\SB at boxiii\SB at cnt\SB at dimen%
+      \SB at mkpage0\SB at boxiii\SB at cnt\SB at dimen%
       \advance\SB at cnt\m at ne%
     \fi%
     \ifnum\SB at cnt>\SB at colnum%
@@ -531,11 +555,16 @@
 \songpos\thr@@
 \newcommand\SB at clearpage{%
   \SB at testtrue%
-  \ifvoid\SB at pgbox\ifvoid\SB at colbox\SB at testfalse\fi\fi%
+  \ifvoid\SB at pgbox%
+    \ifdim\ht\SB at colbox=\z@\ifdim\dp\SB at colbox=\z@%
+      \SB at testfalse%
+    \fi\fi%
+  \fi%
   \ifSB at test%
     \SB at cnt\SB at numcols%
     \advance\SB at cnt-\SB at colnum%
     \SB at nextcol\SB at cnt\lastcolglue%
+    \SB at output2%
   \fi%
 }
 \newcommand\SB at cleardpage{%
@@ -542,6 +571,7 @@
   \SB at clearpage%
   \if at twoside\ifodd\c at page%
     \SB at nextcol\SB at numcols\@flushglue%
+    \SB at output2%
   \fi\fi%
 }
 \newcommand\SB at stype{\SB at stypcol}
@@ -549,7 +579,7 @@
   \ifnum\SB at numcols>\z@%
     \SB at selectcol%
     \global\setbox\SB at colbox\vbox{\SB at putboxes\unvbox}%
-    \SB at output%
+    \SB at output0%
   \else%
     \unvbox\voidb at x%
     \SB at breakpoint\spenalty%
@@ -563,31 +593,36 @@
   \ifnum\SB at numcols>\z@%
     \SB at clearpage%
     \unvbox\SB at songbox%
-    \null\nointerlineskip%
+    \nointerlineskip\null%
   \else%
     \unvbox\SB at songbox%
   \fi%
+  \nointerlineskip%
 }
 \newcommand\SB at sgroup{}
 \let\SB at sgroup\@empty
-\newcount\SB at groupcnt
+\SB at newcount\SB at groupcnt
+\newcommand\SB at clearpboxes{}
+\newcommand\SB at partbox[1]{%
+  \SB at newbox#1%
+  \SB at app\gdef\SB at clearpboxes{\setbox#1\box\voidb at x}%
+  \global\setbox#1\box%
+}
 \newcommand\SB at submitpart{%
   \ifx\SB at sgroup\@empty\else%
-    \@for\SB at temp:=\songlist\do{%
-      \ifx\SB at temp\SB at sgroup%
-        \edef\SB at tempii{\SB at sgroup @\the\SB at groupcnt}%
-        \expandafter\newbox\csname songbox@\SB at tempii\endcsname%
-        \global\expandafter\setbox
-          \csname songbox@\SB at tempii\endcsname\box\SB at songbox%
-        \global\expandafter\let%
-          \csname stype@\SB at tempii\endcsname\SB at stype%
-        \ifrepchorus\ifvoid\SB at chorusbox\else%
-          \expandafter\newbox\csname chbox@\SB at tempii\endcsname%
-          \global\expandafter\setbox%
-            \csname chbox@\SB at tempii\endcsname\box\SB at chorusbox%
-        \fi\fi%
-      \fi%
-    }%
+    \SB at testfalse
+    \@for\SB at temp:=\songlist\do{\ifx\SB at temp\SB at sgroup\SB at testtrue\fi}%
+    \ifSB at test%
+      \edef\SB at tempii{\SB at sgroup @\the\SB at groupcnt}%
+      \expandafter\SB at partbox
+        \csname songbox@\SB at tempii\endcsname\SB at songbox%
+      \global\expandafter\let%
+        \csname stype@\SB at tempii\endcsname\SB at stype%
+      \ifrepchorus\ifvoid\SB at chorusbox\else%
+        \expandafter\SB at partbox
+  \csname chbox@\SB at tempii\endcsname\SB at chorusbox%
+      \fi\fi%
+    \fi%
     \global\advance\SB at groupcnt%
       \ifnum\SB at groupcnt<\z@\m at ne\else\@ne\fi%
   \fi%
@@ -597,6 +632,12 @@
 \newcommand\SB at submitsong{%
   \ifpartiallist\SB at submitpart\else\SB at stype\fi%
 }
+\newcommand\SB at submitenv{%
+  \begingroup%
+    \let\SB at songbox\SB at envbox%
+    \SB at styppage%
+  \endgroup%
+}
 \newcommand\SB at songlistbrk{}
 \def\SB at songlistbrk{brk}
 \newcommand\SB at songlistnc{}
@@ -631,6 +672,7 @@
         \fi\fi\fi\fi%
       }%
     \fi%
+    \SB at clearpboxes%
   \fi%
   \SB at clearpage%
 }
@@ -638,13 +680,13 @@
   \loop\edef\SB at tempii{\SB at temp @\the\SB at groupcnt}%
        \expandafter\ifx%
          \csname songbox@\SB at tempii\endcsname\relax\else%
-    \setbox\SB at songbox\expandafter\box%
+    \setbox\SB at songbox\expandafter\copy%
         \csname songbox@\SB at tempii\endcsname%
     \expandafter\ifx\csname chbox@\SB at tempii\endcsname\relax%
       \repchorusfalse%
     \else%
       \repchorustrue%
-      \setbox\SB at chorusbox\expandafter\box%
+      \setbox\SB at chorusbox\expandafter\copy%
         \csname chbox@\SB at tempii\endcsname%
     \fi%
     \csname stype@\SB at tempii\endcsname%
@@ -724,8 +766,8 @@
   \edef\SB at temp{\the#1}%
   \ifx\SB at temp\SB at emptylist#2\else#3\fi%
 }
-\newtoks\SB at titlelist
-\newtoks\SB at titletail
+\SB at newtoks\SB at titlelist
+\SB at newtoks\SB at titletail
 \newcommand\songtitle{}
 \newcommand\resettitles{%
   \global\SB at titletail\SB at titlelist%
@@ -758,8 +800,7 @@
 \newcommand\setlicense{\gdef\songlicense}
 \newcommand\SB at clearbskeys{}
 \newcommand\newsongkey[2]{%
-  \expandafter\gdef\expandafter\SB at clearbskeys\expandafter%
-    {\SB at clearbskeys#2}%
+  \SB at app\gdef\SB at clearbskeys{#2}%
   \define at key{beginsong}{#1}%
 }
 \newsongkey{sr}{\def\SB at rawrefs{}\gdef\songrefs{}}
@@ -811,7 +852,7 @@
     \nexttitle%
     \foreachtitle{\expandafter\SB at addtotitles\expandafter{\songtitle}}%
     \resettitles%
-    \lyricfont%
+    \lyricfont\relax%
     \SB at setbaselineskip%
 }
 \newcommand\SB at endsong{%
@@ -826,42 +867,47 @@
         \fi\fi%
       \fi\fi%
     \endgroup\egroup%
-    \setbox\SB at songbox\vbox{%
-      \songmark%
-      \unvbox\SB at songwrites%
-      \ifnum\SB at numcols>\z@\hsize\SB at colwidth\fi%
+    \begingroup%
+      \ifnum\SB at numcols>\z@%
+        \hsize\ifpagepreludes\textwidth\else\SB at colwidth\fi%
+      \fi%
       \leftskip\z at skip\rightskip\z at skip%
       \parfillskip\@flushglue\parskip\z at skip\parindent\z@%
-      \ifdim\sbarheight>\z@%
-        \hrule\@height\sbarheight\@width\hsize%
-        \nobreak\vskip5\p@%
-      \fi%
-      \ifpdfindex\begingroup%
-        \ifx\pdfbookmark\undefined\else%
-        \ifx\pdfbookmark\relax\else%
-          \resettitles%
-          \pdfbookmark[\ifnum\c at section=\z at 1\else2\fi]%
-            {\thesongnum. \songtitle}%
-            {song\theSB at songsnum-\thesongnum}%
+      \global\setbox\SB at envbox\vbox{%
+        \songmark%
+        \unvbox\SB at songwrites%
+        \ifpagepreludes\else\ifdim\sbarheight>\z@%
+          \hrule\@height\sbarheight\@width\hsize%
+          \nobreak\vskip5\p@\relax%
         \fi\fi%
-      \endgroup\fi%
-      \vbox{\makeprelude}%
-      \nobreak\vskip\SB at skip%
-      \vskip\afterpreludeskip\relax%
-      \unvbox\SB at songbox%
-      \nobreak\vskip\SB at skip%
-      \vskip\beforepostludeskip\relax%
-      \nointerlineskip%
-      \vbox{\makepostlude}%
-      \ifdim\sbarheight>\z@%
-        \nobreak\vskip2\p@\@plus\p@%
+        \resettitles%
+        \begingroup%
+          \songtarget{\ifnum\c at section=\z at 1\else2\fi}%
+                     {song\theSB at songsnum-\thesongnum}%
+        \endgroup%
+        \vbox{\makeprelude}%
+        \nobreak\vskip\SB at skip%
+        \vskip\afterpreludeskip\relax%
+      }%
+      \ifnum\SB at numcols>\z@\hsize\SB at colwidth\fi%
+      \global\setbox\SB at songbox\vbox{%
+        \ifpagepreludes\else\unvbox\SB at envbox\fi%
+        \unvbox\SB at songbox%
+        \nobreak\vskip\SB at skip%
+        \vskip\beforepostludeskip\relax%
         \nointerlineskip%
-        \hbox{\vrule\@height\sbarheight\@width\hsize}%
-      \fi%
-    }%
+        \vbox{\makepostlude}%
+        \ifdim\sbarheight>\z@%
+          \nobreak\vskip2\p@\@plus\p@%
+          \nointerlineskip%
+          \hbox{\vrule\@height\sbarheight\@width\hsize}%
+        \fi%
+      }%
+    \endgroup%
     \SB at insongfalse%
     \edef\SB at sgroup{\thesongnum}%
     \global\SB at groupcnt\z@%
+    \ifpagepreludes\SB at submitenv\fi%
     \SB at submitsong%
     \ifnum\SB at grouplvl=\z@\let\SB at sgroup\@empty\fi%
     \stepcounter{songnum}%
@@ -892,7 +938,7 @@
   \edef\SB at tempii{\the\versesep}%
   \ifx\SB at temp\SB at tempii%
     \begingroup%
-      \lyricfont%
+      \lyricfont\relax%
       \SB at dimen\f at size\p@%
       \ifchorded%
         \setbox\SB at box\hbox{{\printchord{ABCDEFG\shrp\flt/j7}}}%
@@ -911,9 +957,7 @@
 \newcommand\makeprelude{%
   \resettitles%
   \ifslides%
-    \hbox to\hsize{{%
-      \hfil\stitlefont\songtitle\hfil%
-    }}%
+    \hbox to\hsize{{\hfil\stitlefont\relax\songtitle\hfil}}%
     \vskip5\p@%
     \hbox to\hsize{%
       \hfil%
@@ -932,13 +976,15 @@
       }}}%
     \fi%
     \setbox\SB at box\vbox{%
-      \ifnum\SB at numcols>\z@\hsize\SB at colwidth\fi%
       \ifdim\songnumwidth>\z@%
-        \advance\hsize-\wd\SB at boxii%
-        \advance\hsize-3\p@%
+        \SB at dimen\wd\SB at boxii%
+        \advance\SB at dimen3\p@%
+        \ifpagepreludes\multiply\SB at dimen\tw@\fi%
+        \advance\hsize-\SB at dimen%
       \fi%
-      \SB at raggedright\offinterlineskip\lineskip\p@%
-      {\stitlefont%
+      \ifpagepreludes\centering\else\SB at raggedright\fi%
+      \offinterlineskip\lineskip\p@%
+      {\stitlefont\relax%
        \songtitle\par%
        \nexttitle%
        \foreachtitle{(\songtitle)\par}}%
@@ -990,69 +1036,50 @@
     \global\SB at titlelist{\\}%
     \SB at toks{}%
     \let\\\SB at titlesep%
-    \let\SB at dothis\SB at pthead%
-    \SB at ptstart#1\SB at endparse%
+    \SB at pthead#1\SB at endparse%
   \endgroup%
 }
-\newcommand\SB at ptstart{\futurelet\SB at next\SB at dothis}
-\newcommand\SB at pthead{%
-  \ifcat\noexpand\SB at next\noexpand\@sptoken%
-    \expandafter\SB at ptsp%
+\newcommand\SB at pthead{\futurelet\SB at next\SB@@pthead}
+\newcommand\SB@@pthead{%
+  \ifcat\noexpand\SB at next\@sptoken%
+    \expandafter\SB@@@pthead%
   \else%
-    \SB at toks{}%
-    \let\SB at dothis\SB at ptmain%
     \expandafter\SB at ptmain%
   \fi%
 }
+\newcommand\SB@@@pthead{%
+  \afterassignment\SB at pthead%
+  \let\SB at next= }
+\newcommand\SB at ptloop{\futurelet\SB at next\SB at ptmain}
 \newcommand\SB at ptmain{%
-  \ifcat\noexpand\SB at next\noexpand\@sptoken%
+  \ifcat\noexpand\SB at next\@sptoken%
     \let\SB at donext\SB at ptsp%
-  \else\ifcat\noexpand\SB at next\noexpand\bgroup%
+  \else\ifcat\noexpand\SB at next\bgroup%
     \let\SB at donext\SB at ptbg%
   \else\ifx\SB at next\SB at endparse%
     \global\SB at titlelist\expandafter{\the\SB at titlelist\\}%
     \let\SB at donext\@gobble%
+  \else\ifx\SB at next\\%
+    \SB at toks{}%
+    \def\SB at donext{\SB at ptstep\SB at pthead}%
   \else%
-    \ifx\SB at next\\%
-      \SB at toks{}%
-      \let\SB at dothis\SB at pthead%
-    \fi%
-    \let\SB at donext\SB at ptstep%
-  \fi\fi\fi%
-  \SB at donext%
-}
-\newcommand\SB at ptstep[1]{%
+    \def\SB at donext{\SB at ptstep\SB at ptloop}%
+  \fi\fi\fi\fi%
+  \SB at donext}
+\newcommand\SB at ptstep[2]{%
   \global\SB at titlelist\expandafter\expandafter\expandafter{%
-    \expandafter\the\expandafter\SB at titlelist\the\SB at toks#1}%
+    \expandafter\the\expandafter\SB at titlelist\the\SB at toks#2}%
   \SB at toks{}%
-  \SB at ptstart%
-}
-\newcommand\SB at ptbg[1]{\SB at ptstep{{#1}}}
+  #1}
+\newcommand\SB at ptbg[1]{\SB at ptstep\SB at ptloop{{#1}}}
 \newcommand\SB at ptsp{
   \SB at appendsp\SB at toks%
-  \afterassignment\SB at ptstart%
+  \afterassignment\SB at ptloop%
   \let\SB at next= }
 \newcommand\SB at titlesep{SB at titlesep}
 \newcommand\SB at endparse{%
   \SB at Error{Title parsing failed}{This error should not occur.}%
 }
-\newcommand\SB at testdigit[1]{%
-  \SB at testfalse%
-  \ifcat1\noexpand#1\SB@@testdigit#1\fi%
-}
-\newcommand\SB@@testdigit[1]{%
-  \ifx0#1\SB at testtrue\else%
-  \ifx1#1\SB at testtrue\else%
-  \ifx2#1\SB at testtrue\else%
-  \ifx3#1\SB at testtrue\else%
-  \ifx4#1\SB at testtrue\else%
-  \ifx5#1\SB at testtrue\else%
-  \ifx6#1\SB at testtrue\else%
-  \ifx7#1\SB at testtrue\else%
-  \ifx8#1\SB at testtrue\else%
-  \ifx9#1\SB at testtrue%
-  \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi%
-}
 \newcommand\SB at parsesrefs[1]{%
   \begingroup%
     \SB at toks{\begingroup\SB at sractives}%
@@ -1069,13 +1096,13 @@
   \fi%
 }
 \newcommand\SB@@prstep{%
-  \ifcat\noexpand\SB at next\noexpand\@sptoken%
+  \ifcat\noexpand\SB at next\@sptoken%
     \let\SB at donext\SB at prspace%
   \else\ifx\SB at next-%
     \let\SB at donext\SB at prhyphen%
   \else\ifx\SB at next,%
     \let\SB at donext\SB at prcomma%
-  \else\ifx\SB at next\SB at endparse
+  \else\ifx\SB at next\SB at endparse%
     \let\SB at donext\@gobble%
   \else\ifcat\noexpand\SB at next\bgroup%
     \let\SB at donext\SB at prgr%
@@ -1186,7 +1213,7 @@
   \begingroup%
     \SB at dimen\dp#2%
     #1#2%
-    \setbox\SB at box\hbox{{\lyricfont p}}%
+    \setbox\SB at box\hbox{{\lyricfont\relax p}}%
     \ifdim\SB at dimen<\dp\SB at box%
       \advance\SB at dimen-\dp\SB at box%
       \vskip-\SB at dimen%
@@ -1218,7 +1245,7 @@
   \fi%
 }
 \newcommand\SB@@par{\let\par\SB at par\par}
-\newdimen\SB at parindent
+\SB at newdimen\SB at parindent
 \newcommand\SB at everypar{}
 \newcommand\SB at raggedright{%
   \SB at parindent\parindent%
@@ -1273,15 +1300,13 @@
     \ifvnumbered%
       \protected at edef\@currentlabel{\p at versenum\theversenum}%
       \def\SB at everypar{%
-        \setbox\SB at box\hbox{{%
-          \printversenum{\theversenum}%
-        }}%
+        \setbox\SB at box\hbox{{\printversenum{\theversenum}}}%
         \ifdim\wd\SB at box<\versenumwidth%
           \setbox\SB at box%
           \hbox to\versenumwidth{\unhbox\SB at box\hfil}%
         \fi%
         \ifchorded\vrule\@height\baselineskip\@width\z@\@depth\z@\fi%
-        {\placeversenum\SB at box}%
+        \placeversenum\SB at box%
         \gdef\SB at everypar{}%
       }%
     \else%
@@ -1291,7 +1316,7 @@
       }%
     \fi%
     \everypar{\SB at everypar\everypar{}}%
-    \versefont\versejustify%
+    \versefont\relax\SB at setbaselineskip\versejustify%
     \SB at loadactives%
     \SB at obeylines%
     \penalty12345 %
@@ -1314,7 +1339,7 @@
   \fi%
 }
 \newif\ifSB at chorustop
-\newbox\SB at chorusbox
+\SB at newbox\SB at chorusbox
 \newif\ifSB at gotchorus
 \ifSB at etex
   \@ifundefined{newmarks}{
@@ -1374,7 +1399,7 @@
       }%
       \everypar{\SB at everypar\everypar{}}%
     \fi%
-    \chorusfont\chorusjustify%
+    \chorusfont\relax\SB at setbaselineskip\chorusjustify%
     \SB at loadactives%
     \SB at obeylines%
     \penalty12345 %
@@ -1484,7 +1509,7 @@
   \fi%
 }
 \newcommand\SB at boxup[1]{%
-  \setbox\SB at box\hbox{{\notefont#1}}%
+  \setbox\SB at box\hbox{{\notefont\relax#1}}%
   \SB at dimen\wd\SB at box%
   \advance\SB at dimen6\p@%
   \advance\SB at dimen\leftskip%
@@ -1533,7 +1558,7 @@
   \newcommand\SB at echo[1]{%
     \endgroup%
     \begingroup%
-      \ifdim\fontdimen\@ne\font>\z@\upshape\else\slshape\fi%
+      \echofont\relax%
       \endlinechar\m at ne%
       \scantokens{(#1)}%
     \endgroup%
@@ -1541,7 +1566,7 @@
 \else
   \newcommand\echo{%
     \begingroup%
-      \ifdim\fontdimen\@ne\font>\z@\upshape\else\slshape\fi%
+      \echofont\relax%
       \afterassignment\SB at echo%
       \setbox\SB at box\hbox%
   }
@@ -1563,7 +1588,7 @@
   \advance\SB at grouplvl\m at ne%
   \ifnum\SB at grouplvl=\z@\let\SB at sgroup\@empty\fi%
 }
-\newcount\SB at grouplvl
+\SB at newcount\SB at grouplvl
 \newenvironment{intersong}{%
   \ifSB at insong\SB at errbro\SB at closeall\fi%
   \ifSB at intersong\SB at errbrr\SB at closeall\fi%
@@ -1617,18 +1642,17 @@
     \ifSB at insong\SB at errero\SB at closeall\else\SB at errert\fi%
   \fi%
 }
-\newbox\SB at srbox
 \newenvironment{scripture}{\beginscripture}{\SB at endscripture}
 \newcommand\beginscripture[1]{%
   \begin{intersong}%
     \SB at parsesrefs{#1}%
-    \setbox\SB at srbox\hbox{{\printscrcite\songrefs}}%
+    \setbox\SB at envbox\hbox{{\printscrcite\songrefs}}%
     \def\SB at closeall{\endscripture}%
     \nobreak\vskip5\p@%
     \SB at parindent\parindent\parindent\z@%
     \parskip\z at skip\parfillskip\@flushglue%
     \leftskip\SB at parindent\rightskip\SB at parindent\relax%
-    \scripturefont%
+    \scripturefont\relax%
     \baselineskip\f at size\p@\@plus\p@\relax%
     \advance\baselineskip\p@\relax%
     \emergencystretch.3em%
@@ -1643,7 +1667,7 @@
 }
 \newcommand\scitehere{%
   \ifSB at intersong%
-    \ifvoid\SB at srbox\else%
+    \ifvoid\SB at envbox\else%
       \ifvmode%
         \setbox\SB at box\lastbox%
         \nointerlineskip\noindent\hskip-\leftskip%
@@ -1650,7 +1674,7 @@
         \unhbox\SB at box\unskip%
       \fi%
       \unskip\nobreak\hfil\penalty50\hskip.8em\null\nobreak\hfil%
-      \box\SB at srbox\kern-\SB at parindent%
+      \box\SB at envbox\kern-\SB at parindent%
       {\parfillskip\z@\finalhyphendemerits2000\par}%
     \fi%
   \else%
@@ -1738,7 +1762,7 @@
     \vadjust{}%
   }
 }
-\newcount\SB at transposefactor
+\SB at newcount\SB at transposefactor
 \newif\ifSB at convertnotes
 \newcommand\notenameA{}
 \newcommand\notenameB{}
@@ -1836,7 +1860,7 @@
   \else\ifx\SB at next\egroup%
     \SB at toks\expandafter{\the\SB at toks\egroup}%
     \let\SB at donext\SB at trskip%
-  \else\ifcat\noexpand\SB at next\noexpand\@sptoken%
+  \else\ifcat\noexpand\SB at next\@sptoken%
     \SB at appendsp\SB at toks%
     \let\SB at donext\SB at trskip%
   \else%
@@ -1887,7 +1911,7 @@
 }
 \newcommand\SB at trnotestep[1]{%
   \ifnum\uccode`#1=`#1%
-    \expandafter\def\expandafter\SB at temp\expandafter{\SB at temp#1}%
+    \SB at app\def\SB at temp{#1}%
     \expandafter\SB at trscan%
   \else%
     \SB at cnt\z@%
@@ -1997,16 +2021,9 @@
     \fi%
   \fi%
   \ifvmode\leavevmode\fi%
-  \setbox\SB at box\hbox{\tiny\sffamily{#1}}%
-  \setbox\SB at boxii\hbox{\tiny\sffamily{#2}}%
-  \ifdim\wd\SB at box>\wd\SB at boxii%
-    \SB at dimen\wd\SB at box\relax%
-  \else%
-    \SB at dimen\wd\SB at boxii\relax%
-  \fi%
-  \ifdim\SB at dimen<.5\p@%
-    \SB at dimen.5\p@%
-  \fi%
+  \setbox\SB at box\hbox{{\meterfont\relax#1}}%
+  \setbox\SB at boxii\hbox{{\meterfont\relax#2}}%
+  \SB at dimen\wd\ifdim\wd\SB at box>\wd\SB at boxii\SB at box\else\SB at boxii\fi%
   \SB at dimenii\baselineskip%
   \advance\SB at dimenii-2\p@%
   \advance\SB at dimenii-\ht\SB at box%
@@ -2013,6 +2030,13 @@
   \advance\SB at dimenii-\dp\SB at box%
   \advance\SB at dimenii-\ht\SB at boxii%
   \advance\SB at dimenii-\dp\SB at boxii%
+  \let\SB at temp\relax%
+  \ifdim\SB at dimen>\z@%
+    \advance\SB at dimenii-.75\p@%
+    \def\SB at temp{\kern.75\p@}%
+  \fi%
+  \SB at maxmin\SB at dimen<{.5\p@}%
+  \SB at maxmin\SB at dimenii<\z@%
   \vbox{%
     \mark{\SB at measuremark}%
     \hbox to\SB at dimen{%
@@ -2026,6 +2050,7 @@
       \box\SB at boxii%
       \hfil%
     }%
+    \SB at temp%
     \nointerlineskip%
     \hbox to\SB at dimen{%
       \hfil%
@@ -2033,11 +2058,11 @@
       \hfil%
     }%
   }%
+  \meter{}{}%
 }
 \newcommand\mbar{\SB at mbar}
 \newcommand\measurebar{%
   \mbar\SB at metertop\SB at meterbot%
-  \meter{}{}%
 }
 \newcommand\SB at repcolon{{%
   \usefont{OT1}{cmss}{m}{n}\selectfont%
@@ -2066,16 +2091,15 @@
 }
 \newif\ifSB at wordends
 \newif\ifSB at brokenword
-\newtoks\SB at lyric
-\newcount\SB at numhyps
-\newtoks\SB at lyricnohyp
-\newbox\SB at lyricbox
-\newbox\SB at chordbox
+\SB at newtoks\SB at lyric
+\SB at newcount\SB at numhyps
+\SB at newtoks\SB at lyricnohyp
+\SB at newbox\SB at lyricbox
+\SB at newbox\SB at chordbox
 \newcommand\SB at chbstok{}
 \newcommand\SB at setchord{}
 {
   \catcode`^\active
-  \catcode`!7
   \gdef\SB at setchord#1{%
     \SB at gettabindtrue\SB at nohattrue%
     \setbox\SB at chordbox\hbox{%
@@ -2082,11 +2106,9 @@
       \unhbox\SB at chordbox%
       \begingroup%
         \ifSB at trackch%
-          \def\SB at activehat{\ifmmode!\else\global\SB at nohatfalse\fi}%
+          \let\SB at activehat\SB at hat@tr%
         \else%
-          \def\SB at activehat{%
-            \ifmmode!\else\SB at lop\SB at ctail\SB at toks\the\SB at toks\fi%
-          }%
+          \let\SB at activehat\SB at hat@notr%
         \fi%
         \let^\SB at activehat%
         \printchord{%
@@ -2104,100 +2126,97 @@
     \let\SB at noreplay\@firstofone%
   }
 }
-\newcommand\SB at outertest{}
-\edef\SB at outertest#1{%
-  \noexpand\SB@@outertest#1%
-  \string\outer%
-  \noexpand\SB@@outertest%
+\newcommand\SB at outertest{%
+  \expandafter\SB at otesta\meaning\SB at next:\SB at otesta%
 }
-\newcommand\SB@@outertest{}
-\expandafter\def\expandafter\SB@@outertest%
-\expandafter#\expandafter1\string\outer#2\SB@@outertest{%
+\newcommand\SB at otesta{}
+\edef\SB at otesta#1:#2\SB at otesta{%
+  \noexpand\SB at otestb%
+  #1\string\outer%
+  \noexpand\SB at otestb%
+}
+\newcommand\SB at otestb{}
+\expandafter\def\expandafter\SB at otestb%
+\expandafter#\expandafter1\string\outer#2\SB at otestb{%
   \def\SB at temp{#2}%
-  \ifx\SB at temp\@empty\else\SB at testtrue\fi%
+  \ifx\SB at temp\@empty\SB at testfalse\else\SB at testtrue\fi%
 }
 \newcommand\SB at UTFtest{}
 \edef\SB at UTFtest#1{%
-  \noexpand\SB at UTFtester#1%
+  \noexpand\expandafter%
+  \noexpand\SB@@UTFtest%
+  \noexpand\meaning#1%
   \string\UTFviii at zero@octets%
-  \noexpand\SB at UTFtester%
+  \noexpand\SB@@UTFtest%
 }
-\begingroup
-  \escapechar\m at ne
-  \xdef\SB at two{\string\two}
-  \xdef\SB at three{\string\three}
-  \xdef\SB at four{\string\four}
-  \xdef\SB at temp{\string\@octets}
-\endgroup
-\edef\SB at temp{##1\string\UTFviii@##2\SB at temp##3}
-\expandafter\def\expandafter\SB at UTFtester\SB at temp\SB at UTFtester{%
-  \def\SB at temp{#2}%
-  \ifx\SB at temp\SB at two%
-    \SB at cnt\tw@%
-  \else\ifx\SB at temp\SB at three%
-    \SB at cnt\thr@@%
-  \else\ifx\SB at temp\SB at four%
-    \SB at cnt4 %
-  \else%
-    \SB at cnt\z@%
-  \fi\fi\fi%
+\newcommand\SB at U@two{\global\SB at cnt\tw@}
+\newcommand\SB at U@three{\global\SB at cnt\thr@@}
+\newcommand\SB at U@four{\global\SB at cnt4\relax}
+\newcommand\SB@@UTFtest{}
+{\escapechar\m at ne
+ \xdef\SB at temp{\string\@octets}}
+\edef\SB at temp{##1\string\UTFviii@##2\SB at temp}
+\expandafter\def\expandafter\SB@@UTFtest\SB at temp#3\SB@@UTFtest{%
+  \SB at cnt\z@%
+  {\csname SB at U@#2\endcsname}%
 }
 \newcommand\SB at declare[3]{%
   \afterassignment\iffalse\let\SB at next= #3\relax\fi%
-  \expandafter\SB at UTFtest\expandafter{\meaning\SB at next}%
+  \SB at UTFtest\SB at next%
   \ifcase\SB at cnt%
     \ifcat\noexpand#3\relax%
       \SB at addNtest\SB at macrotests#1#2#3%
     \else\ifcat\noexpand#3.%
-      \SB at addDtest\SB at othertests#1#2#3%
+      \SB at addDtest\SB at othertests#1#2%
     \else\ifcat\noexpand#3A%
-      \SB at addDtest\SB at lettertests#1#2#3%
+      \SB at addDtest\SB at lettertests#1#2%
     \else%
-      \SB at addDtest\relax0#2#3%
+      \SB at addDtest\relax0#2%
     \fi\fi\fi%
   \or%
     \SB at addNtest\SB at macrotests#1#2#3%
   \else%
-    \SB at addMtest\SB at multitests#1#2{#3}%
+    \SB at addMtest\SB at multitests#1#2#3\relax\relax\relax%
   \fi%
 }
 \newcommand\DeclareLyricChar{\SB at declare\SB at testtrue0}
-\newcommand\DeclareNonLyric{%
-  \SB at declare\SB at testfalse\SB at testfalse%
-}
-\newcommand\DeclareNoHyphen{%
-  \SB at declare\SB at testfalse\SB at testtrue%
-}
+\newcommand\DeclareNonLyric{\SB at declare\SB at testfalse\SB at testfalse}
+\newcommand\DeclareNoHyphen{\SB at declare\SB at testfalse\SB at testtrue}
 \newcommand\SB at lettertests{}
 \newcommand\SB at macrotests{}
 \newcommand\SB at multitests{}
 \newcommand\SB at othertests{}
-\newcommand\SB at hyphtests{}
-\newcommand\SB at addtest[2]{%
-  \expandafter\gdef\expandafter#1\expandafter{#1#2}%
+\newcommand\SB at addDtest[3]{%
+  \ifx0#2\else%
+    \def#1{{\csname SB@!\meaning\SB at next\endcsname}}%
+    \expandafter\def\csname SB@!\meaning\SB at next\endcsname{\global#2}%
+  \fi%
+  \ifx0#3\else%
+    \expandafter\def\csname SB at HT@\meaning\SB at next\endcsname{\global#3}%
+  \fi%
 }
-\newcommand\SB at addDtest[4]{%
-  \ifx0#2\else\SB at addtest#1{\ifx\SB at next#4#2\fi}\fi%
-  \ifx0#3\else\SB at addtest\SB at hyphtests{\ifx\SB at next#4#3\fi}\fi%
-}
 \newcommand\SB at addNtest[4]{%
   \ifx0#2\else%
-    \SB at addtest#1{%
-      \edef\SB at temp{\string#4}\ifx\SB at temp\SB at nextname#2\fi%
-    }%
+    \def#1{{\csname SB@!\SB at nextname\endcsname}}%
+    \expandafter\def\csname SB@!\string#4\endcsname{\global#2}%
   \fi%
   \ifx0#3\else%
-    \SB at addtest\SB at hyphtests{%
-      \edef\SB at temp{\string#4}\ifx\SB at temp\SB at nextname#3\fi%
-    }%
+    \expandafter\def\csname SB at HT@\string#4\endcsname{\global#3}%
   \fi%
 }
-\newcommand\SB at addMtest[4]{%
+\newcommand\SB at addMtest[7]{%
+  \edef\SB at temp{%
+    \string#4%
+    \ifx\relax#5\else\string#5\fi%
+    \ifx\relax#6\else\string#6\fi%
+    \ifx\relax#7\else\string#7\fi%
+  }%
   \ifx0#2\else%
-    \SB at addtest#1{\def\SB at temp{#4}\ifx\SB at next\SB at temp#2\fi}%
+    \def#1{{\csname SB@!\SB at nextname\endcsname}}%
+    \expandafter\def\csname SB@!\SB at temp\endcsname{\global#2}%
   \fi%
-  \ifx0#3\else\SB at addtest\SB at hyphtests{%
-    \def\SB at temp{#4}\ifx\SB at next\SB at temp#3\fi}%
+  \ifx0#3\else%
+    \expandafter\def\csname SB at HT@\SB at temp\endcsname{\global#3}%
   \fi%
 }
 \DeclareLyricChar\`
@@ -2241,7 +2260,7 @@
      \catcode`:12\relax%
      \catcode`\^^M\active\SB at outer\def^^M{}%
      \SB at outer\def\[{}%
-     \chordlocals%
+     \chordlocals\relax%
   }
 }
 \newcommand\SB at endcname{}
@@ -2269,8 +2288,8 @@
 \newcommand\MultiwordChords{%
   \def\SB at spcinit{%
     \let\SB at chdone\SB at chlyrdone%
-    \def\SB at chimpspace{\let\SB at donext\SB at chdone}%
-    \def\SB at chexpspace{\let\SB at donext\SB at chdone}%
+    \let\SB at chimpspace\SB at chnxtdone%
+    \let\SB at chexpspace\SB at chnxtdone%
     \let\SB at chespace\SB at chendspace%
   }%
 }
@@ -2279,21 +2298,17 @@
   \futurelet\SB at next\SB at chmain%
 }
 \newcommand\SB at chmain{\SB at dothis\SB at donext}
+\newcommand\SB at chnxtrelax{\let\SB at donext\relax}
+\newcommand\SB at chnxtstep{\let\SB at donext\SB at chstep}
+\newcommand\SB at chnxtdone{\let\SB at donext\SB at chdone}
 \newcommand\SB at chstart{%
-  \ifx\SB at next\[%
-    \let\SB at donext\relax%
-  \else\ifx\SB at next\SB at activehat%
-    \let\SB at donext\relax%
-  \else\ifx\SB at next\ch%
-    \let\SB at donext\relax%
-  \else\ifx\SB at next\mch%
-    \let\SB at donext\relax%
-  \else\ifx\SB at next`%
-    \let\SB at donext\SB at chstep%
-  \else\ifx\SB at next'%
-    \let\SB at donext\SB at chstep%
-  \else\ifx\SB at next"%
-    \let\SB at donext\SB at chstep%
+  \ifx\SB at next\[\SB at chnxtrelax%
+  \else\ifx\SB at next\SB at activehat\SB at chnxtrelax%
+  \else\ifx\SB at next\ch\SB at chnxtrelax%
+  \else\ifx\SB at next\mch\SB at chnxtrelax%
+  \else\ifx\SB at next`\SB at chnxtstep%
+  \else\ifx\SB at next'\SB at chnxtstep%
+  \else\ifx\SB at next"\SB at chnxtstep%
   \else%
     \the\SB at lyric%
     \SB at lyric{}%
@@ -2306,9 +2321,9 @@
   \ifcat\noexpand\SB at next A%
     \SB at testtrue\SB at lettertests%
     \ifSB at test%
-      \SB at chespace\let\SB at donext\SB at chstep%
+      \SB at chespace\SB at chnxtstep%
     \else%
-      \let\SB at donext\SB at chdone%
+      \SB at chnxtdone%
     \fi%
   \else%
     \SB at chtrymacro%
@@ -2322,33 +2337,32 @@
   \fi%
 }
 \newcommand\SB at chother{%
-  \ifcat\noexpand\SB at next\noexpand\@sptoken%
+  \ifcat\noexpand\SB at next\@sptoken%
     \SB at chexpspace%
-  \else\ifcat\noexpand\SB at next\noexpand\bgroup%
+  \else\ifcat\noexpand\SB at next\bgroup%
     \SB at chespace\let\SB at donext\SB at chbgroup%
-  \else\ifcat\noexpand\SB at next\noexpand\egroup%
+  \else\ifcat\noexpand\SB at next\egroup%
     \SB at chespace\let\SB at donext\SB at chegroup%
   \else\ifx\SB at next-%
     \SB at numhyps\@ne\relax%
     \SB at lyricnohyp\expandafter{\the\SB at lyric}%
     \let\SB at dothis\SB at chhyph%
-    \SB at chespace\let\SB at donext\SB at chstep%
+    \SB at chespace\SB at chnxtstep%
   \else\ifcat\noexpand\SB at next.%
     \SB at testtrue\SB at othertests%
     \ifSB at test%
-      \SB at chespace\let\SB at donext\SB at chstep%
+      \SB at chespace\SB at chnxtstep%
     \else%
-      \let\SB at donext\SB at chdone%
+      \SB at chnxtdone%
     \fi%
   \else%
-    \SB at chespace\let\SB at donext\SB at chstep%
+    \SB at chespace\SB at chnxtstep%
   \fi\fi\fi\fi\fi%
 }
 \newcommand\SB at chmacro{%
-  \SB at testfalse%
-  \expandafter\SB at outertest\expandafter{\meaning\SB at next}%
+  \SB at outertest%
   \ifSB at test%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \else%
     \let\SB at donext\SB at chgetname%
   \fi%
@@ -2359,9 +2373,9 @@
 }
 \newcommand\SB@@chmacro{%
   \ifx\SB at next\SB at activehat%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \else\ifx\SB at next\SB at par%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \else\ifx\SB at next\measurebar%
     \SB at chmbar%
   \else\ifx\SB at next\mbar%
@@ -2375,7 +2389,7 @@
   \else\ifx\SB at next\SB at nbsp%
     \SB at chimpspace%
   \else%
-    \expandafter\SB at UTFtest\expandafter{\meaning\SB at next}%
+    \SB at UTFtest\SB at next%
     \ifcase\SB at cnt\SB at chothermac%
     \or\or\SB at chespace\let\SB at donext\SB at chsteptwo%
     \or\SB at chespace\let\SB at donext\SB at chstepthree%
@@ -2389,9 +2403,9 @@
   \ifnum\the\catcode\SB at cnt=\z@\else\SB at testtrue\fi%
   \SB at macrotests%
   \ifSB at test%
-    \SB at chespace\let\SB at donext\SB at chstep%
+    \SB at chespace\SB at chnxtstep%
   \else%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \fi%
 }
 \newcommand\SB at chstep[1]{%
@@ -2398,12 +2412,16 @@
   \SB at lyric\expandafter{\the\SB at lyric#1}%
   \SB at chscan%
 }
-\newcommand\SB at chsteptwo[2]{\SB at chmulti{#1#2}}
-\newcommand\SB at chstepthree[3]{\SB at chmulti{#1#2#3}}
-\newcommand\SB at chstepfour[4]{\SB at chmulti{#1#2#3#4}}
-\newcommand\SB at chmulti[1]{%
+\newcommand\SB at chsteptwo[2]{\SB at chmulti{#1#2}{\string#1\string#2}}
+\newcommand\SB at chstepthree[3]{%
+  \SB at chmulti{#1#2#3}{\string#1\string#2\string#3}%
+}
+\newcommand\SB at chstepfour[4]{%
+  \SB at chmulti{#1#2#3#4}{\string#1\string#2\string#3\string#4}%
+}
+\newcommand\SB at chmulti[2]{%
   \def\SB at next{#1}%
-  \let\SB at nextname\relax%
+  \edef\SB at nextname{#2}%
   \SB at testtrue\SB at multitests%
   \ifSB at test%
     \SB at lyric\expandafter{\the\SB at lyric#1}%
@@ -2416,13 +2434,15 @@
 \newcommand\SB at chhyph{%
   \ifx\SB at next-%
     \advance\SB at numhyps\@ne\relax%
-    \let\SB at donext\SB at chstep%
+    \SB at chnxtstep%
   \else%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \fi%
 }
-\newcommand\SB at chimpspace{\let\SB at donext\SB at chdone}
-\newcommand\SB at chexpspace{\let\SB at donext\SB at chdone}
+\newcommand\SB at chimpspace{}
+\let\SB at chimpspace\SB at chnxtdone
+\newcommand\SB at chexpspace{}
+\let\SB at chexpspace\SB at chnxtdone
 \newcommand\SB at chespace{}
 \newcommand\SB at chendspace{%
   \let\SB at chdone\SB at chlyrdone%
@@ -2439,12 +2459,12 @@
   \ifdim\wd\SB at lyricbox<\wd\SB at chordbox%
     \let\SB at chbstok= \SB at next%
     \def\SB at chexpspace{\let\SB at donext\SB at chgetspace}%
-    \def\SB at chimpspace{\let\SB at donext\SB at chstep}%
+    \let\SB at chimpspace\SB at chnxtstep%
     \let\SB at chespace\SB at chendspace%
     \let\SB at chdone\SB at chspcdone%
   \else%
-    \def\SB at chimpspace{\let\SB at donext\SB at chdone}%
-    \def\SB at chexpspace{\let\SB at donext\SB at chdone}%
+    \let\SB at chimpspace\SB at chnxtdone%
+    \let\SB at chexpspace\SB at chnxtdone%
   \fi%
 }
 \newcommand\SB at chgetspace{%
@@ -2454,9 +2474,9 @@
   \let\SB at next= }
 \newcommand\SB at chmbar{%
   \ifmeasures%
-    \let\SB at donext\SB at chdone%
+    \SB at chnxtdone%
   \else%
-    \SB at chespace\let\SB at donext\SB at chstep%
+    \SB at chespace\SB at chnxtstep%
   \fi%
 }
 \newcommand\SB at chbgroup[1]{%
@@ -2478,8 +2498,7 @@
   \fi%
 }
 \newcommand\SB at chegrpouter{%
-  \SB at testfalse%
-  \expandafter\SB at outertest\expandafter{\meaning\SB at next}%
+  \SB at outertest%
   \ifSB at test%
     \expandafter\SB at chegrpdone%
   \else%
@@ -2494,7 +2513,9 @@
 \newcommand\SB at chlig[5]{%
   \gdef\SB at ligpre{{#3}}%
   \gdef\SB at ligpost{\[#2]{#4}}%
-  \gdef\SB at ligfull{\[\SB at noreplay{\hphantom{{\lyricfont#3}}}#2]{#5}}%
+  \gdef\SB at ligfull{%
+    \[\SB at noreplay{\hphantom{{\lyricfont\relax#3}}}#2]{#5}%
+  }%
   \SB at chdone%
 }
 \newcommand\SB at mchlig[5]{%
@@ -2534,12 +2555,14 @@
 \newcommand\SB at emitchord{%
   \ifSB at inverse\else\ifSB at inchorus\else\SB at errchord\fi\fi%
   \SB at testfalse%
-  \ifcat\noexpand\SB at next\noexpand\@sptoken\SB at testtrue\fi%
+  \ifcat\noexpand\SB at next\@sptoken\SB at testtrue\fi%
   \ifcat\noexpand\SB at next.\SB at testtrue\fi%
   \ifx\SB at next\SB at par\SB at testtrue\fi%
   \ifx\SB at next\egroup\SB at testtrue\fi%
   \ifx\SB at next\endgroup\SB at testtrue\fi%
-  \SB at hyphtests%
+  {\csname%
+     SB at HT@\ifx\SB at nextname\relax\meaning\SB at next\else\SB at nextname\fi%
+   \endcsname}%
   \ifSB at test\SB at wordendstrue\else\SB at wordendsfalse\fi%
   \SB at dimen\wd\SB at chordbox%
   \ifvmode\leavevmode\fi%
@@ -2575,7 +2598,8 @@
         \advance\SB at dimen.5em%
         \hbox to\SB at dimen{\unhbox\SB at chordbox\hfil}%
         \hbox to\SB at dimen{%
-          \unhcopy\SB at lyricbox\hfil\char\hyphenchar\font\hfil%
+          \unhcopy\SB at lyricbox\hfil
+          \ifnum\hyphenchar\font>\m at ne\char\hyphenchar\font\hfil\fi%
         }%
         \global\SB at cnt\@m%
         \gdef\SB at temp{\expandafter\SB at clearlig\SB at ligpost}%
@@ -2651,6 +2675,15 @@
 \newcommand\SB at activehat{%
   \ifmmode^\else\expandafter\SB at rechord\fi%
 }
+\newcommand\SB at hat@tr{%
+  \ifmmode^\else\global\SB at nohatfalse\fi%
+}
+\newcommand\SB at hat@notr{%
+  \ifmmode^\else%
+    \SB at lop\SB at ctail\SB at toks%
+    \expandafter\transposehere\expandafter{\the\SB at toks}%
+  \fi%
+}
 \newcommand\SB at loadactives{}
 {
   \catcode`&\active
@@ -2668,12 +2701,12 @@
   }
 }
 \newif\ifSB at trackch
-\newtoks\SB at cr@
-\newtoks\SB at ctail
+\SB at newtoks\SB at cr@
+\SB at newtoks\SB at ctail
 \newcommand\SB at creg{}
 \newcommand\newchords[1]{%
   \@ifundefined{SB at cr@#1}{%
-    \expandafter\newtoks\csname SB at cr@#1\endcsname%
+    \expandafter\SB at newtoks\csname SB at cr@#1\endcsname%
     \global\csname SB at cr@#1\endcsname{\\}%
   }{\SB at errdup{#1}}%
 }
@@ -2772,7 +2805,7 @@
   \SB at atopfret{\sffamily\fontsize\@vipt\@vipt\selectfont#1}%
 }
 \newif\ifSB at gettabind\SB at gettabindfalse
-\newdimen\SB at tabindent
+\SB at newdimen\SB at tabindent
 \newcommand\SB at targfret{}
 \newcommand\SB at targstr{}
 \newcommand\SB at targfing{}
@@ -2946,6 +2979,23 @@
   \SB at songsenvfalse%
 }
 \newcounter{SB at songsnum}
+\newcommand\songtarget[2]{%
+  \ifnum\@ne=0\ifSB at pdf\ifx\pdfbookmark\undefined\else%
+                       \ifx\pdfbookmark\relax\else1\fi\fi\fi\relax%
+    \pdfbookmark[#1]{\thesongnum. \songtitle}{#2}%
+  \else\ifx\hypertarget\undefined%
+  \else\ifx\hypertarget\relax\else%
+    \hypertarget{#2}{\relax}%
+  \fi\fi\fi%
+}
+\newcommand\songlink{%
+  \ifnum\@ne=0\ifx\hyperlink\undefined\else%
+              \ifx\hyperlink\relax\else1\fi\fi\relax%
+    \expandafter\hyperlink%
+  \else%
+    \expandafter\@gobble%
+  \fi%
+}
 \newcommand\SB at indexlist{}
 \newcommand\SB at allindexes{}
 \let\SB at allindexes\@empty
@@ -2960,7 +3010,7 @@
   }%
   \if at filesw%
     \ifx\SB at out\relax%
-      \newwrite\SB at out%
+      \SB at newwrite\SB at out%
       \immediate\openout\SB at out=\jobname.sxc\relax%
     \fi%
     \immediate\write\SB at out{\noexpand\SB at iwrite{#3}{#2}}%
@@ -2974,9 +3024,32 @@
 \@onlypreamble\newauthorindex
 \newcommand\SB at cwrite[2]{%
   \ifx\SB at out\relax\else%
-    \protected at write\SB at out{}{\protect\SB at iwrite{#1}{#2}}%
+    \protected at write\SB at out\SB at keepactive{\protect\SB at iwrite{#1}{#2}}%
   \fi%
 }
+\newcommand\SB at keepactive{}
+{\catcode`\~\active
+ \catcode`\.12
+ \def\\#1#2{%
+   \endgroup
+   \SB at app\gdef\SB at keepactive{\def#1{#2}}%
+ }
+ \def\SB at temp#1#2{%
+   \SB at cnt#1\relax
+   \loop
+     \begingroup
+       \uccode`\~\SB at cnt
+       \uccode`\.\SB at cnt
+     \uppercase{\\~.}
+   \ifnum\SB at cnt<#2\relax
+     \advance\SB at cnt\@ne
+   \repeat
+ }
+ \SB at temp{1}{8}
+ \SB at temp{11}{11}
+ \SB at temp{14}{91}
+ \SB at temp{93}{255}
+}
 \newcommand\SB at iwrite[2]{%
   \def\SB at tempii{#1}%
   \ifx\SB at temp\SB at tempii%
@@ -2999,7 +3072,7 @@
   \fi%
 }
 \AtEndDocument{\SB at uncombine}
-\newbox\SB at songwrites
+\SB at newbox\SB at songwrites
 \newcommand\SB at addtoindex[2]{%
   \protected at edef\SB at tempii{#2}%
   \ifx\SB at tempii\@empty\else%
@@ -3056,12 +3129,14 @@
     {\expandafter\renewcommand\csname SB at idxref@#1\endcsname}%
 }
 \newcommand\SB at idxcmd[3]{%
-  \ifx\SB at out\relax\else%
+  \ifx\SB at allindexes\@empty%
+    \SB at warnnoidx%
+  \else\ifx\SB at out\relax\else%
     \@for\SB at temp:=\SB at allindexes\do{%
       \csname SB at idxsel@\SB at temp\endcsname%
         {\SB@@idxcmd{#1}}{\SB@@idxcmd{#2}}{\SB@@idxcmd{#3}}%
     }%
-  \fi%
+  \fi\fi%
 }
 \newcommand\SB@@idxcmd[1]{%
   \def\SB at tempii{#1}%
@@ -3076,15 +3151,14 @@
 \newcommand\authignoreword[1]{}
 \newcommand\titleprefixword[1]{}
 {\catcode`\%=12
- \gdef\authsepword#1{\SB at idxcmd{}{%sep #1}{}}
- \gdef\authbyword#1{\SB at idxcmd{}{%after #1}{}}
- \gdef\authignoreword#1{\SB at idxcmd{}{%ignore #1}{}}
+ \gdef\authsepword#1{\SB at idxcmd{}{}{%sep #1}}
+ \gdef\authbyword#1{\SB at idxcmd{}{}{%after #1}}
+ \gdef\authignoreword#1{\SB at idxcmd{}{}{%ignore #1}}
  \gdef\titleprefixword#1{\SB at idxcmd{%prefix #1}{}{}}}
 \@onlypreamble\authsepword
 \@onlypreamble\authbyword
 \@onlypreamble\authignoreword
 \@onlypreamble\titleprefixword
-\newbox\SB at idxtitlebox
 \newcommand\SB at idxlineskip[1]{%
   \vskip#1\p@\@plus#1\p@\@minus#1\p@%
 }
@@ -3207,7 +3281,7 @@
     \advance\SB at colwidth-#1\columnsep%
     \advance\SB at colwidth\columnsep%
     \divide\SB at colwidth#1%
-    \setbox\SB at idxtitlebox\vbox{%
+    \setbox\SB at envbox\vbox{%
       \let\SB at temp\songsection%
       \ifx\chapter\undefined\else%
         \ifx\chapter\relax\else%
@@ -3217,13 +3291,11 @@
       \SB at temp{#2}%
     }%
     \IfFileExists{\csname SB at idxfilename@#3\endcsname.sbx}{%
-      \ifx\hyperlink\undefined\let\hyperlink\@secondoftwo\fi%
-      \ifx\hyperlink\relax\let\hyperlink\@secondoftwo\fi%
       \ifsepindexes%
         \global\setbox\SB at box\vbox{%
           \null%
           \vfil%
-          \unvcopy\SB at idxtitlebox%
+          \unvcopy\SB at envbox%
           \vskip.5in\@minus.3in\relax%
           \hbox to\hsize{%
             \hfil%
@@ -3254,13 +3326,13 @@
       \ifsepindexes%
         \vbox to\textheight{%
           \vfil%
-          \unvbox\SB at idxtitlebox%
+          \unvbox\SB at envbox%
           \vskip1em\relax%
           \hbox to\hsize{\hfil[Index not yet generated.]\hfil}%
           \vskip\z@\@plus2fil\relax%
         }%
       \else%
-        \unvbox\SB at idxtitlebox%
+        \unvbox\SB at envbox%
         \hbox to\hsize{\hfil[Index not yet generated.]\hfil}%
       \fi%
     }%
@@ -3284,7 +3356,7 @@
     \advance\SB at cnt\m at ne%
   \repeat%
   \edef\SB at oneidxpage{\the\SB at toks}%
-  \unvbox\SB at idxtitlebox%
+  \unvbox\SB at envbox%
   \vskip.2in\relax%
   \nointerlineskip%
   \null%
@@ -3327,12 +3399,16 @@
   }%
 }
 \newcommand\SB at maketitleindex{%
-  \renewenvironment{SB at lgidx}[1]{
-    \hbox{\SB at colorbox\idxbgcolor{\vbox{%
-      \hbox to\idxheadwidth{{\idxheadfont\relax##1}\hfil}%
-    }}}%
-    \nobreak\vskip3\p@\@plus2\p@\@minus2\p@\nointerlineskip%
-  }{\penalty-50\vskip5\p@\@plus5\p@\@minus4\p@}%
+  \ifnum\idxheadwidth>\z@%
+    \renewenvironment{SB at lgidx}[1]{
+      \hbox{\SB at colorbox\idxbgcolor{\vbox{%
+        \hbox to\idxheadwidth{{\idxheadfont\relax##1}\hfil}%
+      }}}%
+      \nobreak\vskip3\p@\@plus2\p@\@minus2\p@\nointerlineskip%
+    }{\penalty-50\vskip5\p@\@plus5\p@\@minus4\p@}%
+  \else%
+    \renewenvironment{SB at lgidx}[1]{}{}%
+  \fi%
   \renewenvironment{SB at smidx}[1]{}{}%
   \renewcommand\idxentry[2]{%
     \SB at ellipspread{\idxtitlefont\relax\ignorespaces##1\unskip}%
@@ -3415,6 +3491,10 @@
   \SB at Warn{The \protect\repchoruses\space feature will not wor%
   k when the number of columns is set to zero}%
 }
+\newcommand\SB at warnnoidx{%
+  \SB at Warn{Index command has no effect since no indexes are ye%
+  t declared}%
+}
 \newcommand\SB at errboo{%
   \SB at Error{Encountered \protect\beginsong\space without seein%
   g an \protect\endsong\space for the previous song}%
@@ -3614,16 +3694,17 @@
 \newif\iftranscapos
 \newif\ifnolyrics
 \newif\ifrawtext
-\newif\ifpdfindex\pdfindextrue
 \newif\ifsongindexes\songindexestrue
 \newif\ifsepindexes\sepindexestrue
-\newif\ifSB at colorboxes\SB at colorboxestrue
+\newif\ifpagepreludes
+\newif\ifSB at colorboxes
+\IfFileExists{color.sty}\SB at colorboxestrue\SB at colorboxesfalse
 \newif\ifSB at omitscrip
 \newcommand\nolyrics{}
 \let\nolyrics\nolyricstrue
+\newcommand\pagepreludes{\pagepreludestrue\songpos0}
 \SB at chordson
 \ProcessOptions\relax
-\ifSB at pdf\else\pdfindexfalse\fi
 \ifSB at colorboxes
   \RequirePackage{color}
   \definecolor{SongbookShade}{gray}{.80}
@@ -3646,7 +3727,7 @@
   }}
 \fi
 \ifrawtext
-  \newwrite\SB at txtout
+  \SB at newwrite\SB at txtout
   \immediate\openout\SB at txtout=\jobname.txt
   \newif\ifSB at doEOL
   {\catcode`\^^M12 %

Modified: trunk/Master/tlpkg/libexec/ctan2tds
===================================================================
--- trunk/Master/tlpkg/libexec/ctan2tds	2017-06-10 21:34:06 UTC (rev 44552)
+++ trunk/Master/tlpkg/libexec/ctan2tds	2017-06-10 21:36:05 UTC (rev 44553)
@@ -2765,6 +2765,7 @@
  'placeat'              => '\.lua$',
  'pst-pdf'		=> '\.bat(.w95)?$',
  'pythontex'            => '([23]|_.*)\.py$',
+ 'songs'		=> '\.lua$',
  'spelling'             => '\.lua$',
  'tex4ebook'		=> '\.lua$',
  'texosquery'		=> '\.jar$',



More information about the tex-live-commits mailing list