summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/doc/latex/songs/songidx
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2013-04-17 23:19:43 +0000
committerKarl Berry <karl@freefriends.org>2013-04-17 23:19:43 +0000
commitaaaec2f57c537ee18ea0a59c2cfd7534d5b2b188 (patch)
treed54aac03a0b78c2e01c9f8ebb68302ebccb3e641 /Master/texmf-dist/doc/latex/songs/songidx
parent0c5f06400403c02946d916560fc5cea65e1abe4b (diff)
songs (per hamlen mail 16 Apr 2013 23:01:06)
git-svn-id: svn://tug.org/texlive/trunk@30005 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/doc/latex/songs/songidx')
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/Makefile16
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/authidx.c488
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/bible.can200
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/catholic.can174
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/chars.h137
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/fileio.c122
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/fileio.h54
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/greek.can187
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/protestant.can160
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/scripidx.c1105
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/songidx.c270
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/songidx.h53
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/songsort.c162
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/tanakh.can100
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/titleidx.c379
-rw-r--r--Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h116
16 files changed, 3723 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/Makefile b/Master/texmf-dist/doc/latex/songs/songidx/Makefile
new file mode 100644
index 00000000000..d8cd7cf226a
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/Makefile
@@ -0,0 +1,16 @@
+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
+
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/authidx.c b/Master/texmf-dist/doc/latex/songs/songidx/authidx.c
new file mode 100644
index 00000000000..2f6a7e1e00b
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/authidx.c
@@ -0,0 +1,488 @@
+/* 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;
+}
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/bible.can b/Master/texmf-dist/doc/latex/songs/songidx/bible.can
new file mode 100644
index 00000000000..ed2059ec2e6
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/bible.can
@@ -0,0 +1,200 @@
+# 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.
+#
+#
+# Filename: bible.can
+# Last modified: 2006/06/12
+#
+# The following Canon data file describes a composite of the Protestant,
+# Catholic, and Greek Orthodox Christian bibles. All books of all bibles
+# are represented. Where two bibles have a different number of chapters
+# or a different number of verses, the larger is used. The result is a
+# Canon file that should be adequate to generate scripture indexes for
+# songs referencing any of these biblical canons.
+#
+# File Format: In what follows, each book of the bible is represented by a
+# pair of lines: the first line lists the book title(s), and the second lists
+# the number of verses in each of the book's chapters. The first line may
+# list more than one title, separated by | characters. The first title listed
+# is the one used to generate the scripture index. The other titles, if any,
+# are recognized as aliases when they appear in "sr=" keyvals in the
+# \beginsong macro. Any of the titles may contain arbitrary LaTeX macros.
+# The order that books appear in this file dictates the order that they appear
+# when sorting the scripture index.
+Genesis
+031 025 024 026 032 022 024 022 029 032 032 020 018 024 021 016 027 033 038 018 034 024 020 067 034 035 046 022 035 043 055 032 020 031 029 043 036 030 023 023 057 038 034 034 028 034 031 022 033 026
+Exodus
+022 025 022 031 023 030 025 032 035 029 010 051 022 031 027 036 016 027 025 026 036 031 033 018 040 037 021 043 046 038 018 035 023 035 035 038 029 031 043 038
+Leviticus
+017 016 017 035 019 030 038 036 024 020 047 008 059 057 033 034 016 030 037 027 024 033 044 023 055 046 034
+Numbers
+054 034 051 049 031 027 089 026 023 036 035 016 033 045 041 050 013 032 022 029 035 041 030 025 018 065 023 031 040 016 054 042 056 029 034 013
+Deuteronomy
+046 037 029 049 033 025 026 020 029 022 032 032 018 029 023 022 020 022 021 020 023 030 025 022 019 019 026 068 029 020 030 052 029 012
+Joshua
+018 024 017 024 015 027 026 035 027 043 023 024 033 015 063 010 018 028 051 009 045 034 016 033
+Judges
+036 023 031 024 031 040 025 035 057 018 040 015 025 020 020 031 013 031 030 048 025
+Ruth
+022 023 018 022
+1 Samuel|1 Kingdoms
+028 036 021 022 012 021 017 022 027 027 015 025 023 052 035 023 058 030 024 042 015 023 029 022 044 025 012 025 011 031 013
+2 Samuel|2 Kingdoms
+027 032 039 012 025 023 029 018 013 019 027 031 039 033 037 023 029 033 043 026 022 051 039 025
+1 Kings|3 Kingdoms
+053 046 028 034 018 038 051 066 028 029 043 033 034 031 034 034 024 046 021 043 029 053
+2 Kings|4 Kingdoms
+018 025 027 044 027 033 020 029 037 036 021 021 025 029 038 020 041 037 037 021 026 020 037 020 030
+1 Chronicles
+054 055 024 043 026 081 040 040 044 014 047 040 014 017 029 043 027 017 019 008 030 019 032 031 031 032 034 021 030
+2 Chronicles
+017 018 017 022 014 042 022 018 031 019 023 016 022 015 019 014 019 034 011 037 020 012 021 027 028 023 009 027 036 027 021 033 025 033 027 023
+1 Esdras
+058 030 024 063 073 034 015 096 055
+2 Esdras
+011 070 013 024 017 022 028 036 015 044 011 020 032 023 019 019 073 018 038 039 036 047 031
+Ezra
+011 070 013 024 017 022 028 036 015 044
+Nehemiah
+011 020 032 023 019 019 073 018 038 039 036 047 031
+Tobit|Tobias
+025 023 025 023 028 022 020 024 012 013 021 022 023 017
+Judith
+012 018 015 016 029 021 025 034 019 020 021 020 031 018 015 031
+Esther
+022 023 015 017 014 014 010 017 032 013 012 006 018 019 019 024
+1 Macabees|1 Maccabees
+067 070 060 061 068 063 050 032 073 089 074 054 054 049 041 024
+2 Macabees|2 Maccabees
+036 033 040 050 027 031 042 036 029 038 038 046 026 046 040
+3 Macabees|3 Maccabees
+029 033 030 021 051 041 023
+4 Macabees|4 Maccabees
+035 024 021 026 038 035 023 028 032 021 027 019 027 020 032 025 024 024
+Job
+022 013 026 021 027 030 021 022 035 022 020 025 028 022 035 022 016 021 029 029 034 030 017 025 006 014 023 028 025 031 040 022 033 037 016 033 024 041 030 024 034 017
+Psalms|Psalm
+006 012 008 008 012 010 017 009 020 018 007 008 006 007 005 011 015 050 014 009 013 031 006 010 022 012 014 009 011 012 024 011 022 022 028 012 040 022 013 017 013 011 005 026 017 011 009 014 020 023 019 009 006 007 023 013 011 011 017 012 008 012 011 010 013 020 007 035 036 005 024 020 028 023 010 012 020 072 013 019 016 008 018 012 013 017 007 018 052 017 016 015 005 023 011 013 012 009 009 005 008 028 022 035 045 048 043 013 031 007 010 010 009 008 018 019 002 029 176 007 008 009 004 008 005 006 005 006 008 008 003 018 003 003 021 026 009 008 024 013 010 007 012 015 021 010 020 014 009 006 007
+Odes
+019 043 010 018 012 008 020 037 034 009 010 015 004 046
+Proverbs
+033 022 035 027 023 035 027 036 018 032 031 028 025 035 033 033 028 024 029 030 031 029 035 034 028 028 027 028 027 033 031
+Ecclesiastes
+018 026 022 016 020 012 029 017 018 020 010 014
+Song of Songs|Song of Solomon
+017 017 011 016 016 013 013 014
+Wisdom|Wisdom of Solomon
+016 025 019 020 024 027 030 021 019 021 027 027 019 031 019 029 020 025 020
+Sirach|Ecclesiasticus
+040 023 034 036 018 037 040 022 025 034 036 019 032 027 022 031 031 033 028 033 031 033 038 047 036 028 033 030 034 027 042 028 033 031 026 028 034 039 041 032 028 026 037 027 031 023 031 028 019 031 038
+Psalms of Solomon|Psalm of Solomon
+008 037 012 025 019 006 010 034 011 008 009 006 012 010 013 015 046 012
+Isaiah
+031 022 026 006 030 013 025 022 021 034 016 006 022 032 009 014 014 007 025 006 017 025 018 023 012 021 013 029 024 033 009 020 024 017 010 022 038 022 008 031 029 025 028 028 025 013 015 022 026 011 023 015 012 017 013 012 021 014 021 022 011 012 019 012 025 024
+Jeremiah
+019 037 025 031 031 030 034 022 026 025 023 017 027 022 021 021 027 023 015 018 014 030 040 010 038 024 022 017 032 024 040 044 026 022 019 032 021 028 018 016 018 022 013 030 005 028 007 047 039 046 064 034
+Lamentations
+022 022 066 022 022
+Baruch
+022 035 038 037 009 073
+Letter of Jeremiah
+073
+Ezekiel
+028 010 027 017 017 014 027 018 011 022 025 028 023 023 008 063 024 032 014 049 032 031 049 027 017 021 036 026 021 026 018 032 033 031 015 038 028 023 029 049 026 020 027 031 025 024 023 035
+Daniel
+021 049 100 034 031 028 028 027 027 021 045 013 065 042
+Hosea
+011 023 005 019 015 011 016 014 017 015 012 014 016 009
+Joel
+020 032 021
+Amos
+015 016 015 013 027 014 017 014 015
+Obadiah
+021
+Jonah
+017 010 010 011
+Micah
+016 013 012 013 015 016 020
+Nahum
+015 013 019
+Habakkuk
+017 020 019
+Zephaniah
+018 015 020
+Haggai
+015 023
+Zechariah
+021 013 010 014 011 015 014 023 017 012 017 014 009 021
+Malachi
+014 017 018 006
+Matthew
+025 023 017 025 048 034 029 034 038 042 030 050 058 036 039 028 027 035 030 034 046 046 039 051 046 075 066 020
+Mark
+045 028 035 041 043 056 037 038 050 052 033 044 037 072 047 020
+Luke
+080 052 038 044 039 049 050 056 062 042 054 059 035 035 032 031 037 043 048 047 038 071 056 053
+John
+051 025 036 054 047 071 053 059 041 042 057 050 038 031 027 033 026 040 042 031 025
+Acts
+026 047 026 037 042 015 060 040 043 048 030 025 052 028 041 040 034 028 041 038 040 030 035 027 027 032 044 031
+Romans
+032 029 031 025 021 023 025 039 033 021 036 021 014 026 033 025
+1 Corinthians
+031 016 023 021 013 020 040 013 027 033 034 031 013 040 058 024
+2 Corinthians
+024 017 018 018 021 018 016 024 015 018 033 021 014
+Galatians
+024 021 029 031 026 018
+Ephesians
+023 022 021 032 033 024
+Philippians
+030 030 021 023
+Colossians
+029 023 025 018
+1 Thessalonians
+010 020 013 018 028
+2 Thessalonians
+012 017 018
+1 Timothy
+020 015 016 016 025 021
+2 Timothy
+018 026 017 022
+Titus
+016 015 015
+Philemon
+025
+Hebrews
+014 018 019 016 014 020 028 013 028 039 040 029 025
+James
+027 026 018 017 020
+1 Peter
+025 025 022 019 014
+2 Peter
+021 022 018
+1 John
+010 029 024 021 021
+2 John
+013
+3 John
+014
+Jude
+025
+Revelation
+020 029 022 011 014 017 017 013 021 011 019 017 018 020 008 021 018 024 021 015 027 021
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/catholic.can b/Master/texmf-dist/doc/latex/songs/songidx/catholic.can
new file mode 100644
index 00000000000..19b23f87729
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/catholic.can
@@ -0,0 +1,174 @@
+# 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.
+#
+#
+# Filename: catholic.can
+# Last modified: 2006/06/12
+#
+# The following Canon data file describes the layout of the Catholic Bible.
+# The deuterocanonical books are included, along with the additional verses
+# in Esther and Daniel.
+#
+Genesis
+031 025 024 026 032 022 024 022 029 032 032 020 018 024 021 016 027 033 038 018 034 024 020 067 034 035 046 022 035 043 055 032 020 031 029 043 036 030 023 023 057 038 034 034 028 034 031 022 033 026
+Exodus
+022 025 022 031 023 030 025 032 035 029 010 051 022 031 027 036 016 027 025 026 036 031 033 018 040 037 021 043 046 038 018 035 023 035 035 038 029 031 043 038
+Leviticus
+017 016 017 035 019 030 038 036 024 020 047 008 059 057 033 034 016 030 037 027 024 033 044 023 055 046 034
+Numbers
+054 034 051 049 031 027 089 026 023 036 035 016 033 045 041 050 013 032 022 029 035 041 030 025 018 065 023 031 040 016 054 042 056 029 034 013
+Deuteronomy
+046 037 029 049 033 025 026 020 029 022 032 032 018 029 023 022 020 022 021 020 023 030 025 022 019 019 026 068 029 020 030 052 029 012
+Joshua
+018 024 017 024 015 027 026 035 027 043 023 024 033 015 063 010 018 028 051 009 045 034 016 033
+Judges
+036 023 031 024 031 040 025 035 057 018 040 015 025 020 020 031 013 031 030 048 025
+Ruth
+022 023 018 022
+1 Samuel
+028 036 021 022 012 021 017 022 027 027 015 025 023 052 035 023 058 030 024 042 015 023 029 022 044 025 012 025 011 031 013
+2 Samuel
+027 032 039 012 025 023 029 018 013 019 027 031 039 033 037 023 029 033 043 026 022 051 039 025
+1 Kings
+053 046 028 034 018 038 051 066 028 029 043 033 034 031 034 034 024 046 021 043 029 053
+2 Kings
+018 025 027 044 027 033 020 029 037 036 021 021 025 029 038 020 041 037 037 021 026 020 037 020 030
+1 Chronicles
+054 055 024 043 026 081 040 040 044 014 047 040 014 017 029 043 027 017 019 008 030 019 032 031 031 032 034 021 030
+2 Chronicles
+017 018 017 022 014 042 022 018 031 019 023 016 022 015 019 014 019 034 011 037 020 012 021 027 028 023 009 027 036 027 021 033 025 033 027 023
+Ezra
+011 070 013 024 017 022 028 036 015 044
+Nehemiah
+011 020 032 023 019 019 073 018 038 039 036 047 031
+Tobit|Tobias
+025 023 025 023 028 022 020 024 012 013 021 022 023 017
+Judith
+012 018 015 016 029 021 025 034 019 020 021 020 031 018 015 031
+Esther
+022 023 015 017 014 014 010 017 032 013 012 006 018 019 019 024
+1 Macabees
+067 070 060 061 068 063 050 032 073 089 074 054 054 049 041 024
+2 Macabees
+036 033 040 050 027 031 042 036 029 038 038 046 026 046 040
+Job
+022 013 026 021 027 030 021 022 035 022 020 025 028 022 035 022 016 021 029 029 034 030 017 025 006 014 023 028 025 031 040 022 033 037 016 033 024 041 030 024 034 017
+Psalms|Psalm
+006 012 008 008 012 010 017 009 020 018 007 008 006 007 005 011 015 050 014 009 013 031 006 010 022 012 014 009 011 012 024 011 022 022 028 012 040 022 013 017 013 011 005 026 017 011 009 014 020 023 019 009 006 007 023 013 011 011 017 012 008 012 011 010 013 020 007 035 036 005 024 020 028 023 010 012 020 072 013 019 016 008 018 012 013 017 007 018 052 017 016 015 005 023 011 013 012 009 009 005 008 028 022 035 045 048 043 013 031 007 010 010 009 008 018 019 002 029 176 007 008 009 004 008 005 006 005 006 008 008 003 018 003 003 021 026 009 008 024 013 010 007 012 015 021 010 020 014 009 006
+Proverbs
+033 022 035 027 023 035 027 036 018 032 031 028 025 035 033 033 028 024 029 030 031 029 035 034 028 028 027 028 027 033 031
+Ecclesiastes
+018 026 022 016 020 012 029 017 018 020 010 014
+Song of Songs|Song of Solomon
+017 017 011 016 016 013 013 014
+Wisdom|Wisdom of Solomon
+016 025 019 020 024 027 030 021 019 021 027 027 019 031 019 029 020 025 020
+Sirach|Ecclesiasticus
+040 023 034 036 018 037 040 022 025 034 036 019 032 027 022 031 031 033 028 033 031 033 038 047 036 028 033 030 034 027 042 028 033 031 026 028 034 039 041 032 028 026 037 027 031 023 031 028 019 031 038
+Isaiah
+031 022 026 006 030 013 025 022 021 034 016 006 022 032 009 014 014 007 025 006 017 025 018 023 012 021 013 029 024 033 009 020 024 017 010 022 038 022 008 031 029 025 028 028 025 013 015 022 026 011 023 015 012 017 013 012 021 014 021 022 011 012 019 012 025 024
+Jeremiah
+019 037 025 031 031 030 034 022 026 025 023 017 027 022 021 021 027 023 015 018 014 030 040 010 038 024 022 017 032 024 040 044 026 022 019 032 021 028 018 016 018 022 013 030 005 028 007 047 039 046 064 034
+Lamentations
+022 022 066 022 022
+Baruch
+022 035 038 037 009 073
+Ezekiel
+028 010 027 017 017 014 027 018 011 022 025 028 023 023 008 063 024 032 014 049 032 031 049 027 017 021 036 026 021 026 018 032 033 031 015 038 028 023 029 049 026 020 027 031 025 024 023 035
+Daniel
+021 049 100 034 031 028 028 027 027 021 045 013 065 042
+Hosea
+011 023 005 019 015 011 016 014 017 015 012 014 016 009
+Joel
+020 032 021
+Amos
+015 016 015 013 027 014 017 014 015
+Obadiah
+021
+Jonah
+017 010 010 011
+Micah
+016 013 012 013 015 016 020
+Nahum
+015 013 019
+Habakkuk
+017 020 019
+Zephaniah
+018 015 020
+Haggai
+015 023
+Zechariah
+021 013 010 014 011 015 014 023 017 012 017 014 009 021
+Malachi
+014 017 018 006
+Matthew
+025 023 017 025 048 034 029 034 038 042 030 050 058 036 039 028 027 035 030 034 046 046 039 051 046 075 066 020
+Mark
+045 028 035 041 043 056 037 038 050 052 033 044 037 072 047 020
+Luke
+080 052 038 044 039 049 050 056 062 042 054 059 035 035 032 031 037 043 048 047 038 071 056 053
+John
+051 025 036 054 047 071 053 059 041 042 057 050 038 031 027 033 026 040 042 031 025
+Acts
+026 047 026 037 042 015 060 040 043 048 030 025 052 028 041 040 034 028 041 038 040 030 035 027 027 032 044 031
+Romans
+032 029 031 025 021 023 025 039 033 021 036 021 014 026 033 025
+1 Corinthians
+031 016 023 021 013 020 040 013 027 033 034 031 013 040 058 024
+2 Corinthians
+024 017 018 018 021 018 016 024 015 018 033 021 014
+Galatians
+024 021 029 031 026 018
+Ephesians
+023 022 021 032 033 024
+Philippians
+030 030 021 023
+Colossians
+029 023 025 018
+1 Thessalonians
+010 020 013 018 028
+2 Thessalonians
+012 017 018
+1 Timothy
+020 015 016 016 025 021
+2 Timothy
+018 026 017 022
+Titus
+016 015 015
+Philemon
+025
+Hebrews
+014 018 019 016 014 020 028 013 028 039 040 029 025
+James
+027 026 018 017 020
+1 Peter
+025 025 022 019 014
+2 Peter
+021 022 018
+1 John
+010 029 024 021 021
+2 John
+013
+3 John
+014
+Jude
+025
+Revelation
+020 029 022 011 014 017 017 013 021 011 019 017 018 020 008 021 018 024 021 015 027 021
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/chars.h b/Master/texmf-dist/doc/latex/songs/songidx/chars.h
new file mode 100644
index 00000000000..375b0a0b4b1
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/chars.h
@@ -0,0 +1,137 @@
+/* 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
+
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/fileio.c b/Master/texmf-dist/doc/latex/songs/songidx/fileio.c
new file mode 100644
index 00000000000..26bf56fcf37
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/fileio.c
@@ -0,0 +1,122 @@
+/* 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;
+}
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/fileio.h b/Master/texmf-dist/doc/latex/songs/songidx/fileio.h
new file mode 100644
index 00000000000..327f5d59581
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/fileio.h
@@ -0,0 +1,54 @@
+/* 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
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/greek.can b/Master/texmf-dist/doc/latex/songs/songidx/greek.can
new file mode 100644
index 00000000000..289d5e2d21c
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/greek.can
@@ -0,0 +1,187 @@
+# 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.
+#
+#
+# Filename: greek.can
+# Last modified: 2006/06/12
+#
+# The following Canon data file describes the layout of the Greek Orthodox
+# Christian Bible. It includes the deuterocanonical books along with
+# 1 Esdras, 3 & 4 Maccabees, and Odes. Greek Orthodox names for the books
+# are used, including 1-4 Kingdoms, 2 Esdras, and Letter of Jeremiah. The
+# extra verses of Esther and Daniel are also included, as well as the 151st
+# Psalm.
+#
+Genesis
+031 025 024 026 032 022 024 022 029 032 032 020 018 024 021 016 027 033 038 018 034 024 020 067 034 035 046 022 035 043 055 032 020 031 029 043 036 030 023 023 057 038 034 034 028 034 031 022 033 026
+Exodus
+022 025 022 031 023 030 025 032 035 029 010 051 022 031 027 036 016 027 025 026 036 031 033 018 040 037 021 043 046 038 018 035 023 035 035 038 029 031 043 038
+Leviticus
+017 016 017 035 019 030 038 036 024 020 047 008 059 057 033 034 016 030 037 027 024 033 044 023 055 046 034
+Numbers
+054 034 051 049 031 027 089 026 023 036 035 016 033 045 041 050 013 032 022 029 035 041 030 025 018 065 023 031 040 016 054 042 056 029 034 013
+Deuteronomy
+046 037 029 049 033 025 026 020 029 022 032 032 018 029 023 022 020 022 021 020 023 030 025 022 019 019 026 068 029 020 030 052 029 012
+Joshua
+018 024 017 024 015 027 026 035 027 043 023 024 033 015 063 010 018 028 051 009 045 034 016 033
+Judges
+036 023 031 024 031 040 025 035 057 018 040 015 025 020 020 031 013 031 030 048 025
+Ruth
+022 023 018 022
+1 Kingdoms|1 Samuel
+028 036 021 022 012 021 017 022 027 027 015 025 023 052 035 023 058 030 024 042 015 023 029 022 044 025 012 025 011 031 013
+2 Kingdoms|2 Samuel
+027 032 039 012 025 023 029 018 013 019 027 031 039 033 037 023 029 033 043 026 022 051 039 025
+3 Kingdoms|1 Kings
+053 046 028 034 018 038 051 066 028 029 043 033 034 031 034 034 024 046 021 043 029 053
+4 Kingdoms|2 Kings
+018 025 027 044 027 033 020 029 037 036 021 021 025 029 038 020 041 037 037 021 026 020 037 020 030
+1 Chronicles
+054 055 024 043 026 081 040 040 044 014 047 040 014 017 029 043 027 017 019 008 030 019 032 031 031 032 034 021 030
+2 Chronicles
+017 018 017 022 014 042 022 018 031 019 023 016 022 015 019 014 019 034 011 037 020 012 021 027 028 023 009 027 036 027 021 033 025 033 027 023
+1 Esdras
+058 030 024 063 073 034 015 096 055
+2 Esdras
+011 070 013 024 017 022 028 036 015 044 011 020 032 023 019 019 073 018 038 039 036 047 031
+Tobit|Tobias
+025 023 025 023 028 022 020 024 012 013 021 022 023 017
+Judith
+012 018 015 016 029 021 025 034 019 020 021 020 031 018 015 031
+Esther
+022 023 015 017 014 014 010 017 032 013 012 006 018 019 019 024
+1 Macabees|1 Maccabees
+067 070 060 061 068 063 050 032 073 089 074 054 054 049 041 024
+2 Macabees|2 Maccabees
+036 033 040 050 027 031 042 036 029 038 038 046 026 046 040
+3 Macabees|3 Maccabees
+029 033 030 021 051 041 023
+4 Macabees|4 Maccabees
+035 024 021 026 038 035 023 028 032 021 027 019 027 020 032 025 024 024
+Job
+022 013 026 021 027 030 021 022 035 022 020 025 028 022 035 022 016 021 029 029 034 030 017 025 006 014 023 028 025 031 040 022 033 037 016 033 024 041 030 024 034 017
+Psalms|Psalm
+006 012 008 008 012 010 017 009 020 018 007 008 006 007 005 011 015 050 014 009 013 031 006 010 022 012 014 009 011 012 024 011 022 022 028 012 040 022 013 017 013 011 005 026 017 011 009 014 020 023 019 009 006 007 023 013 011 011 017 012 008 012 011 010 013 020 007 035 036 005 024 020 028 023 010 012 020 072 013 019 016 008 018 012 013 017 007 018 052 017 016 015 005 023 011 013 012 009 009 005 008 028 022 035 045 048 043 013 031 007 010 010 009 008 018 019 002 029 176 007 008 009 004 008 005 006 005 006 008 008 003 018 003 003 021 026 009 008 024 013 010 007 012 015 021 010 020 014 009 006 007
+Odes
+019 043 010 018 012 008 020 037 034 009 010 015 004 046
+Proverbs
+033 022 035 027 023 035 027 036 018 032 031 028 025 035 033 033 028 024 029 030 031 029 035 034 028 028 027 028 027 033 031
+Ecclesiastes
+018 026 022 016 020 012 029 017 018 020 010 014
+Song of Songs|Song of Solomon
+017 017 011 016 016 013 013 014
+Wisdom|Wisdom of Solomon
+016 025 019 020 024 027 030 021 019 021 027 027 019 031 019 029 020 025 020
+Sirach|Ecclesiasticus
+040 023 034 036 018 037 040 022 025 034 036 019 032 027 022 031 031 033 028 033 031 033 038 047 036 028 033 030 034 027 042 028 033 031 026 028 034 039 041 032 028 026 037 027 031 023 031 028 019 031 038
+Psalms of Solomon|Psalm of Solomon
+008 037 012 025 019 006 010 034 011 008 009 006 012 010 013 015 046 012
+Isaiah
+031 022 026 006 030 013 025 022 021 034 016 006 022 032 009 014 014 007 025 006 017 025 018 023 012 021 013 029 024 033 009 020 024 017 010 022 038 022 008 031 029 025 028 028 025 013 015 022 026 011 023 015 012 017 013 012 021 014 021 022 011 012 019 012 025 024
+Jeremiah
+019 037 025 031 031 030 034 022 026 025 023 017 027 022 021 021 027 023 015 018 014 030 040 010 038 024 022 017 032 024 040 044 026 022 019 032 021 028 018 016 018 022 013 030 005 028 007 047 039 046 064 034
+Lamentations
+022 022 066 022 022
+Baruch
+022 035 038 037 009
+Letter of Jeremiah
+073
+Ezekiel
+028 010 027 017 017 014 027 018 011 022 025 028 023 023 008 063 024 032 014 049 032 031 049 027 017 021 036 026 021 026 018 032 033 031 015 038 028 023 029 049 026 020 027 031 025 024 023 035
+Daniel
+021 049 100 034 031 028 028 027 027 021 045 013 065 042
+Hosea
+011 023 005 019 015 011 016 014 017 015 012 014 016 009
+Joel
+020 032 021
+Amos
+015 016 015 013 027 014 017 014 015
+Obadiah
+021
+Jonah
+017 010 010 011
+Micah
+016 013 012 013 015 016 020
+Nahum
+015 013 019
+Habakkuk
+017 020 019
+Zephaniah
+018 015 020
+Haggai
+015 023
+Zechariah
+021 013 010 014 011 015 014 023 017 012 017 014 009 021
+Malachi
+014 017 018 006
+Matthew
+025 023 017 025 048 034 029 034 038 042 030 050 058 036 039 028 027 035 030 034 046 046 039 051 046 075 066 020
+Mark
+045 028 035 041 043 056 037 038 050 052 033 044 037 072 047 020
+Luke
+080 052 038 044 039 049 050 056 062 042 054 059 035 035 032 031 037 043 048 047 038 071 056 053
+John
+051 025 036 054 047 071 053 059 041 042 057 050 038 031 027 033 026 040 042 031 025
+Acts
+026 047 026 037 042 015 060 040 043 048 030 025 052 028 041 040 034 028 041 038 040 030 035 027 027 032 044 031
+Romans
+032 029 031 025 021 023 025 039 033 021 036 021 014 026 033 025
+1 Corinthians
+031 016 023 021 013 020 040 013 027 033 034 031 013 040 058 024
+2 Corinthians
+024 017 018 018 021 018 016 024 015 018 033 021 014
+Galatians
+024 021 029 031 026 018
+Ephesians
+023 022 021 032 033 024
+Philippians
+030 030 021 023
+Colossians
+029 023 025 018
+1 Thessalonians
+010 020 013 018 028
+2 Thessalonians
+012 017 018
+1 Timothy
+020 015 016 016 025 021
+2 Timothy
+018 026 017 022
+Titus
+016 015 015
+Philemon
+025
+Hebrews
+014 018 019 016 014 020 028 013 028 039 040 029 025
+James
+027 026 018 017 020
+1 Peter
+025 025 022 019 014
+2 Peter
+021 022 018
+1 John
+010 029 024 021 021
+2 John
+013
+3 John
+014
+Jude
+025
+Revelation
+020 029 022 011 014 017 017 013 021 011 019 017 018 020 008 021 018 024 021 015 027 021
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/protestant.can b/Master/texmf-dist/doc/latex/songs/songidx/protestant.can
new file mode 100644
index 00000000000..fab28005f98
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/protestant.can
@@ -0,0 +1,160 @@
+# 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.
+#
+#
+# Filename: protestant.can
+# Last modified: 2006/06/12
+#
+# The following Canon data file describes the layout of the Protestant Bible.
+# Books of the Apocrypha are omitted, as are the extra chapters of Esther
+# and Daniel and the 151st Psalm.
+#
+Genesis
+031 025 024 026 032 022 024 022 029 032 032 020 018 024 021 016 027 033 038 018 034 024 020 067 034 035 046 022 035 043 055 032 020 031 029 043 036 030 023 023 057 038 034 034 028 034 031 022 033 026
+Exodus
+022 025 022 031 023 030 025 032 035 029 010 051 022 031 027 036 016 027 025 026 036 031 033 018 040 037 021 043 046 038 018 035 023 035 035 038 029 031 043 038
+Leviticus
+017 016 017 035 019 030 038 036 024 020 047 008 059 057 033 034 016 030 037 027 024 033 044 023 055 046 034
+Numbers
+054 034 051 049 031 027 089 026 023 036 035 016 033 045 041 050 013 032 022 029 035 041 030 025 018 065 023 031 040 016 054 042 056 029 034 013
+Deuteronomy
+046 037 029 049 033 025 026 020 029 022 032 032 018 029 023 022 020 022 021 020 023 030 025 022 019 019 026 068 029 020 030 052 029 012
+Joshua
+018 024 017 024 015 027 026 035 027 043 023 024 033 015 063 010 018 028 051 009 045 034 016 033
+Judges
+036 023 031 024 031 040 025 035 057 018 040 015 025 020 020 031 013 031 030 048 025
+Ruth
+022 023 018 022
+1 Samuel
+028 036 021 022 012 021 017 022 027 027 015 025 023 052 035 023 058 030 024 042 015 023 029 022 044 025 012 025 011 031 013
+2 Samuel
+027 032 039 012 025 023 029 018 013 019 027 031 039 033 037 023 029 033 043 026 022 051 039 025
+1 Kings
+053 046 028 034 018 038 051 066 028 029 043 033 034 031 034 034 024 046 021 043 029 053
+2 Kings
+018 025 027 044 027 033 020 029 037 036 021 021 025 029 038 020 041 037 037 021 026 020 037 020 030
+1 Chronicles
+054 055 024 043 026 081 040 040 044 014 047 040 014 017 029 043 027 017 019 008 030 019 032 031 031 032 034 021 030
+2 Chronicles
+017 018 017 022 014 042 022 018 031 019 023 016 022 015 019 014 019 034 011 037 020 012 021 027 028 023 009 027 036 027 021 033 025 033 027 023
+Ezra
+011 070 013 024 017 022 028 036 015 044
+Nehemiah
+011 020 032 023 019 019 073 018 038 039 036 047 031
+Esther
+022 023 015 017 014 014 010 017 032 003
+Job
+022 013 026 021 027 030 021 022 035 022 020 025 028 022 035 022 016 021 029 029 034 030 017 025 006 014 023 028 025 031 040 022 033 037 016 033 024 041 030 024 034 017
+Psalms|Psalm
+006 012 008 008 012 010 017 009 020 018 007 008 006 007 005 011 015 050 014 009 013 031 006 010 022 012 014 009 011 012 024 011 022 022 028 012 040 022 013 017 013 011 005 026 017 011 009 014 020 023 019 009 006 007 023 013 011 011 017 012 008 012 011 010 013 020 007 035 036 005 024 020 028 023 010 012 020 072 013 019 016 008 018 012 013 017 007 018 052 017 016 015 005 023 011 013 012 009 009 005 008 028 022 035 045 048 043 013 031 007 010 010 009 008 018 019 002 029 176 007 008 009 004 008 005 006 005 006 008 008 003 018 003 003 021 026 009 008 024 013 010 007 012 015 021 010 020 014 009 006
+Proverbs
+033 022 035 027 023 035 027 036 018 032 031 028 025 035 033 033 028 024 029 030 031 029 035 034 028 028 027 028 027 033 031
+Ecclesiastes
+018 026 022 016 020 012 029 017 018 020 010 014
+Song of Songs|Song of Solomon
+017 017 011 016 016 013 013 014
+Isaiah
+031 022 026 006 030 013 025 022 021 034 016 006 022 032 009 014 014 007 025 006 017 025 018 023 012 021 013 029 024 033 009 020 024 017 010 022 038 022 008 031 029 025 028 028 025 013 015 022 026 011 023 015 012 017 013 012 021 014 021 022 011 012 019 012 025 024
+Jeremiah
+019 037 025 031 031 030 034 022 026 025 023 017 027 022 021 021 027 023 015 018 014 030 040 010 038 024 022 017 032 024 040 044 026 022 019 032 021 028 018 016 018 022 013 030 005 028 007 047 039 046 064 034
+Lamentations
+022 022 066 022 022
+Ezekiel
+028 010 027 017 017 014 027 018 011 022 025 028 023 023 008 063 024 032 014 049 032 031 049 027 017 021 036 026 021 026 018 032 033 031 015 038 028 023 029 049 026 020 027 031 025 024 023 035
+Daniel
+021 049 030 037 031 028 028 027 027 021 045 013
+Hosea
+011 023 005 019 015 011 016 014 017 015 012 014 016 009
+Joel
+020 032 021
+Amos
+015 016 015 013 027 014 017 014 015
+Obadiah
+021
+Jonah
+017 010 010 011
+Micah
+016 013 012 013 015 016 020
+Nahum
+015 013 019
+Habakkuk
+017 020 019
+Zephaniah
+018 015 020
+Haggai
+015 023
+Zechariah
+021 013 010 014 011 015 014 023 017 012 017 014 009 021
+Malachi
+014 017 018 006
+Matthew
+025 023 017 025 048 034 029 034 038 042 030 050 058 036 039 028 027 035 030 034 046 046 039 051 046 075 066 020
+Mark
+045 028 035 041 043 056 037 038 050 052 033 044 037 072 047 020
+Luke
+080 052 038 044 039 049 050 056 062 042 054 059 035 035 032 031 037 043 048 047 038 071 056 053
+John
+051 025 036 054 047 071 053 059 041 042 057 050 038 031 027 033 026 040 042 031 025
+Acts
+026 047 026 037 042 015 060 040 043 048 030 025 052 028 041 040 034 028 041 038 040 030 035 027 027 032 044 031
+Romans
+032 029 031 025 021 023 025 039 033 021 036 021 014 026 033 025
+1 Corinthians
+031 016 023 021 013 020 040 013 027 033 034 031 013 040 058 024
+2 Corinthians
+024 017 018 018 021 018 016 024 015 018 033 021 014
+Galatians
+024 021 029 031 026 018
+Ephesians
+023 022 021 032 033 024
+Philippians
+030 030 021 023
+Colossians
+029 023 025 018
+1 Thessalonians
+010 020 013 018 028
+2 Thessalonians
+012 017 018
+1 Timothy
+020 015 016 016 025 021
+2 Timothy
+018 026 017 022
+Titus
+016 015 015
+Philemon
+025
+Hebrews
+014 018 019 016 014 020 028 013 028 039 040 029 025
+James
+027 026 018 017 020
+1 Peter
+025 025 022 019 014
+2 Peter
+021 022 018
+1 John
+010 029 024 021 021
+2 John
+013
+3 John
+014
+Jude
+025
+Revelation
+020 029 022 011 014 017 017 013 021 011 019 017 018 020 008 021 018 024 021 015 027 021
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/scripidx.c b/Master/texmf-dist/doc/latex/songs/songidx/scripidx.c
new file mode 100644
index 00000000000..1a2189ac617
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/scripidx.c
@@ -0,0 +1,1105 @@
+/* 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;
+}
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/songidx.c b/Master/texmf-dist/doc/latex/songs/songidx/songidx.c
new file mode 100644
index 00000000000..20c08e62df8
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/songidx.c
@@ -0,0 +1,270 @@
+/* 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;
+}
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/songidx.h b/Master/texmf-dist/doc/latex/songs/songidx/songidx.h
new file mode 100644
index 00000000000..42dd130b1cd
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/songidx.h
@@ -0,0 +1,53 @@
+/* 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
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/songsort.c b/Master/texmf-dist/doc/latex/songs/songidx/songsort.c
new file mode 100644
index 00000000000..22825733727
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/songsort.c
@@ -0,0 +1,162 @@
+/* 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;
+}
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can b/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can
new file mode 100644
index 00000000000..6a3053c94eb
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can
@@ -0,0 +1,100 @@
+# 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.
+#
+#
+# Filename: tanakh.can
+# Last modified: 2006/06/12
+#
+# The following Canon data file describes the layout of the Jewish Tanakh.
+# This includes the books of the Protestant Old Testament, but in a different
+# order and with Chronicles as one book (spanning the Protestant books of
+# 1 & 2 Chronicles) and Ezra as one book (spanning the Protestant books of
+# Ezra and Nehemiah).
+#
+Genesis
+031 025 024 026 032 022 024 022 029 032 032 020 018 024 021 016 027 033 038 018 034 024 020 067 034 035 046 022 035 043 055 032 020 031 029 043 036 030 023 023 057 038 034 034 028 034 031 022 033 026
+Exodus
+022 025 022 031 023 030 025 032 035 029 010 051 022 031 027 036 016 027 025 026 036 031 033 018 040 037 021 043 046 038 018 035 023 035 035 038 029 031 043 038
+Leviticus
+017 016 017 035 019 030 038 036 024 020 047 008 059 057 033 034 016 030 037 027 024 033 044 023 055 046 034
+Numbers
+054 034 051 049 031 027 089 026 023 036 035 016 033 045 041 050 013 032 022 029 035 041 030 025 018 065 023 031 040 016 054 042 056 029 034 013
+Deuteronomy
+046 037 029 049 033 025 026 020 029 022 032 032 018 029 023 022 020 022 021 020 023 030 025 022 019 019 026 068 029 020 030 052 029 012
+Joshua
+018 024 017 024 015 027 026 035 027 043 023 024 033 015 063 010 018 028 051 009 045 034 016 033
+Judges
+036 023 031 024 031 040 025 035 057 018 040 015 025 020 020 031 013 031 030 048 025
+Samuel
+028 036 021 022 012 021 017 022 027 027 015 025 023 052 035 023 058 030 024 042 015 023 029 022 044 025 012 025 011 031 013 027 032 039 012 025 023 029 018 013 019 027 031 039 033 037 023 029 033 043 026 022 051 039 025
+Kings
+053 046 028 034 018 038 051 066 028 029 043 033 034 031 034 034 024 046 021 043 029 053 018 025 027 044 027 033 020 029 037 036 021 021 025 029 038 020 041 037 037 021 026 020 037 020 030
+Isaiah
+031 022 026 006 030 013 025 022 021 034 016 006 022 032 009 014 014 007 025 006 017 025 018 023 012 021 013 029 024 033 009 020 024 017 010 022 038 022 008 031 029 025 028 028 025 013 015 022 026 011 023 015 012 017 013 012 021 014 021 022 011 012 019 012 025 024
+Jeremiah
+019 037 025 031 031 030 034 022 026 025 023 017 027 022 021 021 027 023 015 018 014 030 040 010 038 024 022 017 032 024 040 044 026 022 019 032 021 028 018 016 018 022 013 030 005 028 007 047 039 046 064 034
+Ezekiel
+028 010 027 017 017 014 027 018 011 022 025 028 023 023 008 063 024 032 014 049 032 031 049 027 017 021 036 026 021 026 018 032 033 031 015 038 028 023 029 049 026 020 027 031 025 024 023 035
+Hosea
+011 023 005 019 015 011 016 014 017 015 012 014 016 009
+Joel
+020 032 021
+Amos
+015 016 015 013 027 014 017 014 015
+Obadiah
+021
+Jonah
+017 010 010 011
+Micah
+016 013 012 013 015 016 020
+Nahum
+015 013 019
+Habakkuk
+017 020 019
+Zephaniah
+018 015 020
+Haggai
+015 023
+Zechariah
+021 013 010 014 011 015 014 023 017 012 017 014 009 021
+Malachi
+014 017 018 006
+Psalms|Psalm
+006 012 008 008 012 010 017 009 020 018 007 008 006 007 005 011 015 050 014 009 013 031 006 010 022 012 014 009 011 012 024 011 022 022 028 012 040 022 013 017 013 011 005 026 017 011 009 014 020 023 019 009 006 007 023 013 011 011 017 012 008 012 011 010 013 020 007 035 036 005 024 020 028 023 010 012 020 072 013 019 016 008 018 012 013 017 007 018 052 017 016 015 005 023 011 013 012 009 009 005 008 028 022 035 045 048 043 013 031 007 010 010 009 008 018 019 002 029 176 007 008 009 004 008 005 006 005 006 008 008 003 018 003 003 021 026 009 008 024 013 010 007 012 015 021 010 020 014 009 006
+Proverbs
+033 022 035 027 023 035 027 036 018 032 031 028 025 035 033 033 028 024 029 030 031 029 035 034 028 028 027 028 027 033 031
+Job
+022 013 026 021 027 030 021 022 035 022 020 025 028 022 035 022 016 021 029 029 034 030 017 025 006 014 023 028 025 031 040 022 033 037 016 033 024 041 030 024 034 017
+Song of Songs|Song of Solomon
+017 017 011 016 016 013 013 014
+Ruth
+022 023 018 022
+Lamentations
+022 022 066 022 022
+Ecclesiastes
+018 026 022 016 020 012 029 017 018 020 010 014
+Esther
+022 023 015 017 014 014 010 017 032 003
+Daniel
+021 049 030 037 031 028 028 027 027 021 045 013
+Ezra
+011 070 013 024 017 022 028 036 015 044 011 020 032 023 019 019 073 018 038 039 036 047 031
+Chronicles
+054 055 024 043 026 081 040 040 044 014 047 040 014 017 029 043 027 017 019 008 030 019 032 031 031 032 034 021 030 017 018 017 022 014 042 022 018 031 019 023 016 022 015 019 014 019 034 011 037 020 012 021 027 028 023 009 027 036 027 021 033 025 033 027 023
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/titleidx.c b/Master/texmf-dist/doc/latex/songs/songidx/titleidx.c
new file mode 100644
index 00000000000..cde08511a0c
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/titleidx.c
@@ -0,0 +1,379 @@
+/* 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;
+}
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h b/Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h
new file mode 100644
index 00000000000..f8793af2fa4
--- /dev/null
+++ b/Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h
@@ -0,0 +1,116 @@
+/* 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