diff options
author | Karl Berry <karl@freefriends.org> | 2017-06-10 21:36:05 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2017-06-10 21:36:05 +0000 |
commit | f4304ad1f80aab94e5657360f8c1348366c9d1b9 (patch) | |
tree | b3f1c78224e27482ebcddf0f6a3e7200cdb7db2e /Master/texmf-dist/doc/latex/songs/songidx | |
parent | 0a543d44f49029704fe666953858d028b000d5d2 (diff) |
songs (10jun17)
git-svn-id: svn://tug.org/texlive/trunk@44553 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/doc/latex/songs/songidx')
17 files changed, 1075 insertions, 2907 deletions
diff --git a/Master/texmf-dist/doc/latex/songs/songidx/Makefile b/Master/texmf-dist/doc/latex/songs/songidx/Makefile deleted file mode 100644 index d8cd7cf226a..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -srcdir = . -CC = cc -CFLAGS = -I$(srcdir) -LDFLAGS = - -all: songidx - -.SUFFIXES: -.SUFFIXES: .c .o - -songidx: *.h $(patsubst %.c,%.o,$(wildcard *.c)) - $(CC) $(CFLAGS) $(LDFLAGS) $(filter %.o,$^) -o $@ - -clean: - -rm $(DESTDIR)*.o $(DESTDIR)songidx - diff --git a/Master/texmf-dist/doc/latex/songs/songidx/authidx.c b/Master/texmf-dist/doc/latex/songs/songidx/authidx.c deleted file mode 100644 index 2f6a7e1e00b..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/authidx.c +++ /dev/null @@ -1,488 +0,0 @@ -/* Copyright (C) 2012 Kevin W. Hamlen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - * - * The latest version of this program can be obtained from - * http://songs.sourceforge.net. - */ - - -/* Create a LaTeX author index file from an author .dat file. */ - -#if HAVE_CONFIG_H -# include "config.h" -#else -# include "vsconfig.h" -#endif - -#include "chars.h" -#include "songidx.h" -#include "fileio.h" - -#if HAVE_STRING_H -# include <string.h> -#elif HAVE_STRINGS_H -# include <strings.h> -#endif - -typedef struct wordlist -{ - struct wordlist *next; - WCHAR *w; -} -WORDLIST; - -WORDLIST wl_and = { NULL, ws_lit("and") }; -WORDLIST wl_by = { NULL, ws_lit("by") }; -WORDLIST wl_unknown = { NULL, ws_lit("unknown") }; - -/* wl_insert(<wordlist>,<word>) - * Insert a word onto the head of a wordlist. */ -static void -wl_insert(wl,w) - WORDLIST **wl; - const WCHAR *w; -{ - WORDLIST *head = (WORDLIST *) malloc(sizeof(WORDLIST)); - head->next = ((*wl==&wl_and) || (*wl==&wl_by) || (*wl==&wl_unknown)) ? - NULL : *wl; - head->w = (WCHAR *) calloc(ws_strlen(w)+1, sizeof(WCHAR)); - ws_strcpy(head->w, w); - *wl = head; -} - -/* matchany(<string>,<wordlist>) - * If a word in <wordlist> case-insensitively matches to a prefix of <string>, - * and if the match concludes with whitespace, then return the length of the - * match plus 1. Otherwise return 0. - */ -static size_t -matchany(s,wl) - const WCHAR *s; - WORDLIST *wl; -{ - const WCHAR *s2, *w2; - - for (; wl; wl=wl->next) - { - for (s2=s, w2=wl->w; *w2; ++s2, ++w2) - if (wc_tolower(*s2)!=wc_tolower(*w2)) break; - if ((*w2 == wc_null) && wc_isspace(*s2)) return s2-s+1; - } - return 0; -} - -/* matchwithin(<string>,<wordlist>) - * Return 1 if any word in <wordlist> appears in <string> (case insensitive - * match). */ -static char -matchwithin(s,wl) - const WCHAR *s; - WORDLIST *wl; -{ - const WCHAR *s2, *w2; - - for (; wl; wl=wl->next) - { - for (; *s; ++s) - { - for (s2=s, w2=wl->w; *w2; ++s2, ++w2) - if (wc_tolower(*s2)!=wc_tolower(*w2)) break; - if (*w2 == wc_null) return 1; - } - } - return 0; -} - -/* partofword(<char>) - * Return 0 if <char> is a word-delimiter and 1 otherwise. Word-delimiters - * are anything other than alphabetic characters, hyphens, apostrophes, - * backslashes, and braces. - */ -static int -partofword(c) - WCHAR c; -{ - return (wc_isalpha(c) || (c==wc_apostrophe) || (c==wc_backquote) - || (c==wc_backslash) || (c==wc_lbrace) || (c==wc_rbrace)); -} - -/* isjrsuffix(<string>) - * Return 1 if <string> points to the name suffix "Jr" and 0 otherwise. */ -static int -isjrsuffix(s) - const WCHAR *s; -{ - WCHAR buf[3]; - - if (!partofword(*s) || !partofword(*(s+1)) || partofword(*(s+2))) - return 0; - buf[0] = *s; - buf[1] = *(s+1); - buf[2] = wc_null; - return !ws_coll(buf, ws_lit("Jr")); -} - -/* grabauthor(<string>,<seplist>,<afterlist>,<ignorelist>) - * Return a string of the form "Sirname, Restofname" denoting the full name - * of the first author found in <string>; or return NULL if no author name - * can be found. Set <string> to point to the suffix of <string> that was - * not parsed. The string returned (if any) is static, so successive calls - * will overwrite it. - * - * Heuristics: - * * Names are separated by punctuation (other than hyphens, periods, - * apostrophes, or backslashes) or by the word "and" (or whatever words - * are in seplist). - * Special case: If a comma is followed by the abbreviation "Jr" or by a - * roman numeral, then the comma does NOT end the author's name. - * * If a name contains the word "by" (or anything in afterlist), then - * everything before it is not considered part of the name. (Let's hope - * nobody is named "By".) - * * The author's last name is always the last capitalized word in the - * name unless the last capitalized word is "Jr." or a roman numeral. - * In that case the author's last name is the second-last capitalized - * word. - * * If an author appears to have only a first name, or if the last name - * found according to the above heuristics is an abbreviation (ending in - * a period), look ahead in <string> until we find someone with a last - * name and use that one. This allows us to identify the first author in - * a string like "Joe, Billy E., and Bob Smith" to be "Joe Smith". - * * If the resultant name contains the word "unknown" (or any word in - * unknownlist), it's probably not a real name. Recursively attempt to - * parse the next author. */ -static WCHAR * -grabauthor(authline,seplist,afterlist,unknownlist) - const WCHAR **authline; - WORDLIST *seplist; - WORDLIST *afterlist; - WORDLIST *unknownlist; -{ - static WCHAR buf[MAXLINELEN+2], *bp; - const WCHAR *first, *last, *suffix, *next, *scanahead, *endp; - size_t len; - - /* Point "first" to the first character of the name, "last" to the first - * character of the sirname, "suffix" to any suffix like "Jr." or "III" - * (or NULL if there is none), and "next" to the first character beyond the - * end of the name. */ - if (!authline) return NULL; - for (first=*authline; (*first!=wc_null) && !partofword(*first); ++first) ; - if (((len = matchany(first,seplist)) > 0) || - ((len = matchany(first,afterlist)) > 0)) - first += len; - if (*first==wc_null) return NULL; - last=suffix=NULL; - for (next=first; *next; ++next) - { - skipesc(&next); - if (*next==wc_comma) - { - for (scanahead = next+1; *scanahead==wc_space; ++scanahead) ; - if (isjrsuffix(scanahead)) continue; - if (ws_strchr(ws_lit("XVI"), *scanahead) && - !partofword(*(scanahead+ws_strspn(scanahead, ws_lit("XVI"))))) - continue; - *authline = next+1; - break; - } - else if (wc_ispunct(*next) && (*next!=wc_hyphen) && (*next!=wc_period) && - (*next!=wc_apostrophe) && (*next!=wc_backquote) && - (*next!=wc_backslash)) - { - *authline = next+1; - break; - } - if (*next == wc_space) - { - for (++next; *next==wc_space; ++next) ; - if ((len = matchany(next,seplist)) > 0) - { - *authline=next+len; - break; - } - if ((len = matchany(next,afterlist)) > 0) - { - first = next+len; - next += len-1; - last = suffix = NULL; - continue; - } - if (isjrsuffix(next)) - suffix=next; - else if (ws_strchr(ws_lit("XVI"), *next) && - !partofword(*(next+ws_strspn(next, ws_lit("XVI"))))) - suffix=next; - else - { - scanahead = next; - skipesc(&scanahead); - if (wc_isupper(*scanahead)) - { - last = next; - suffix = NULL; - } - } - --next; - } - } - if (*next==wc_null) *authline = next; - - /* Put the sirname into the buffer first. */ - endp = NULL; - if (last) - { - endp = (suffix ? suffix : next)-1; - if (endp<last) abort(); - for (; (endp>last) && ((*endp==wc_space) || (*endp==wc_comma)); --endp) ; - } - if (!endp || (*endp==wc_period)) - { - /* Here's where it gets tough. We either have a single-word name, or the - * last name ends in a "." which means maybe it's just a middle initial or - * other abbreviation. We could be dealing with a line like, "Billy, - * Joe E., and Bob Smith", in which case we have to go searching for the - * real last name. To handle this case, we will try a recursive call. */ - scanahead = *authline; - if (grabauthor(&scanahead,seplist,afterlist,unknownlist) && - ((bp = ws_strchr(buf, wc_comma)) != NULL)) - /* got it! Make our old last name part of the first name. */ - last = NULL; - else if (last) - { - /* Couldn't find a last name. Just use this one and hope it's okay. */ - ws_strncpy(buf, last, endp-last+1); - bp = buf+(endp-last+1); - } - else - bp = buf; - } - else - { - ws_strncpy(buf, last, endp-last+1); - bp = buf+(endp-last+1); - } - - /* Next, put the first name into the buffer. */ - endp = (last ? last : (suffix ? suffix : next))-1; - if (endp<first) abort(); - for (; (endp>=first) && ((*endp==wc_space) || (*endp==wc_comma)); --endp) ; - ++endp; - if (endp>first) - { - if (bp > buf) - { - *bp++ = wc_comma; - *bp++ = wc_space; - } - ws_strncpy(bp, first, endp-first); - bp += endp-first; - } - - /* Finally, put the suffix (if any) into the buffer. */ - if (suffix) - { - for (endp=next-1; - (endp>=suffix) && ((*endp==wc_space) || (*endp==wc_comma)); - --endp) ; - ++endp; - if (endp>suffix) - { - if (bp > buf) *bp++ = wc_space; - ws_strncpy(bp, suffix, endp-suffix); - bp += endp-suffix; - } - } - - *bp = wc_null; - if (matchwithin(buf, unknownlist)) - return grabauthor(authline,seplist,afterlist,unknownlist); - return buf; -} - -/* genauthorindex(<file>,<inname>,<outname>) - * Read author data from file handle <file> and generate from it a LaTeX - * author index file named <outname>. - * Return 0 on success, 1 on warnings, or 2 on error. */ -int -genauthorindex(fs,outname) - FSTATE *fs; - const char *outname; -{ - FILE *f; - int eof = 0; - int arraysize, numauthors, i; - WORDLIST *seplist=&wl_and, *afterlist=&wl_by, *ignorelist=&wl_unknown; - SONGENTRY **authors; - WCHAR authorbuf[MAXLINELEN], *bp; - WCHAR songnumbuf[MAXLINELEN], linknamebuf[MAXLINELEN]; - WCHAR *thisnum, *thislink; - const WCHAR *auth; - - fprintf(stderr, "songidx: Parsing author index data file %s...\n", - fs->filename); - - eof = 0; - authors = NULL; - for (arraysize=numauthors=i=0; !eof; ++i) - { - if (!filereadln(fs,authorbuf,&eof)) return 2; - if (eof) break; - if (authorbuf[0] == wc_percent) - { - if (!ws_strncmp(authorbuf, ws_lit("%sep "), 5)) - wl_insert(&seplist, authorbuf + 5); - else if (!ws_strncmp(authorbuf, ws_lit("%after "), 7)) - wl_insert(&afterlist, authorbuf + 7); - else if (!ws_strncmp(authorbuf, ws_lit("%ignore "), 8)) - wl_insert(&ignorelist, authorbuf + 8); - --i; - continue; - } - if (!filereadln(fs,songnumbuf,&eof)) - { - fileclose(fs); - return 2; - } - if (eof) - { - fprintf(stderr, "songidx:%s:%d: incomplete author entry" - " (orphan byline)\n", fs->filename, fs->lineno); - fileclose(fs); - return 2; - } - if (!filereadln(fs,linknamebuf,&eof)) - { - fileclose(fs); - return 2; - } - if (eof) - { - fprintf(stderr, "songidx:%s:%d: incomplete author entry" - " (missing hyperlink)\n", fs->filename, fs->lineno); - fileclose(fs); - return 2; - } - if (((thisnum = (WCHAR *) calloc(ws_strlen(songnumbuf)+1, - sizeof(WCHAR))) == NULL) || - ((thislink = (WCHAR *) calloc(ws_strlen(linknamebuf)+1, - sizeof(WCHAR))) == NULL)) - { - fprintf(stderr, "songidx:%s:%d: song number/link too long" - " (out of memory)\n", fs->filename, fs->lineno); - return 2; - } - ws_strcpy(thisnum, songnumbuf); - ws_strcpy(thislink, linknamebuf); - - for (bp=authorbuf; - (auth=grabauthor((const WCHAR **) &bp, seplist, afterlist, - ignorelist)) != NULL; - ++numauthors) - { - if (numauthors >= arraysize) - { - SONGENTRY **temp; - arraysize *= 2; - if (arraysize==0) arraysize=64; - temp = (SONGENTRY **) realloc(authors,arraysize*sizeof(SONGENTRY *)); - if (!temp) - { - fprintf(stderr, "songidx:%s:%d: too many song authors" - " (out of memory)\n", fs->filename, fs->lineno); - return 2; - } - authors = temp; - } - if ((authors[numauthors] = (SONGENTRY *) - malloc(sizeof(SONGENTRY))) == NULL) - { - fprintf(stderr, "songidx:%s:%d: too many song authors" - " (out of memory)\n", fs->filename, fs->lineno); - return 2; - } - if ((authors[numauthors]->title = - (WCHAR *) calloc(ws_strlen(auth)+1, sizeof(WCHAR))) == NULL) - { - fprintf(stderr, "songidx:%s:%d: author name too long (out of memory)\n", - fs->filename, fs->lineno); - return 2; - } - ws_strcpy(authors[numauthors]->title, auth); - authors[numauthors]->num = thisnum; - authors[numauthors]->linkname = thislink; - authors[numauthors]->idx = i; - } - } - fileclose(fs); - - /* Sort the array by author. */ - qsort(authors, numauthors, sizeof(*authors), songcmp); - - /* Generate an author index LaTeX file from the sorted data. - * Combine any entries with the same author name into a single index entry. */ - fprintf(stderr, "songidx: Generating author index TeX file %s...\n", outname); - if (strcmp(outname,"-")) - { - if ((f = fopen(outname, "w")) == NULL) - { - fprintf(stderr, "songidx: Unable to open %s for writing.\n", outname); - return 2; - } - } - else - { - f = stdout; - outname = "stdout"; - } - -#define TRYWRITE(x) \ - if (!(x)) \ - { \ - fprintf(stderr, "songidx:%s: write error\n", outname); \ - if (f == stdout) fflush(f); else fclose(f); \ - return 2; \ - } - - TRYWRITE(ws_fputs(ws_lit("\\begin{idxblock}{"), f) >= 0) - for (i=0; i<numauthors; ++i) - { - if ((i>0) && !ws_coll(authors[i]->title, authors[i-1]->title)) - { - TRYWRITE(ws_fputs(ws_lit("\\\\"), f) >= 0) - } - else - { - TRYWRITE((ws_fputs(ws_lit("}\n\\idxentry{"), f) >= 0) && - (ws_fputs(authors[i]->title, f) >= 0) && - (ws_fputs(ws_lit("}{"), f) >= 0)) - } - TRYWRITE((ws_fputs(ws_lit("\\hyperlink{"), f) >= 0) && - (ws_fputs(authors[i]->linkname, f) >= 0) && - (ws_fputs(ws_lit("}{"), f) >= 0) && - (ws_fputs(authors[i]->num, f) >= 0) && - (ws_fputs(ws_lit("}"), f) >= 0)) - } - TRYWRITE(ws_fputs(ws_lit("}\n\\end{idxblock}\n"), f) >= 0) - -#undef TRYWRITE - - if (f == stdout) - fflush(f); - else - fclose(f); - return 0; -} diff --git a/Master/texmf-dist/doc/latex/songs/songidx/bible.can b/Master/texmf-dist/doc/latex/songs/songidx/bible.can index ed2059ec2e6..e88b292e8c4 100644 --- a/Master/texmf-dist/doc/latex/songs/songidx/bible.can +++ b/Master/texmf-dist/doc/latex/songs/songidx/bible.can @@ -1,4 +1,4 @@ -# Copyright (C) 2012 Kevin W. Hamlen +# Copyright (C) 2017 Kevin W. Hamlen # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/Master/texmf-dist/doc/latex/songs/songidx/catholic.can b/Master/texmf-dist/doc/latex/songs/songidx/catholic.can index 19b23f87729..d39988b5d3b 100644 --- a/Master/texmf-dist/doc/latex/songs/songidx/catholic.can +++ b/Master/texmf-dist/doc/latex/songs/songidx/catholic.can @@ -1,4 +1,4 @@ -# Copyright (C) 2012 Kevin W. Hamlen +# Copyright (C) 2017 Kevin W. Hamlen # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/Master/texmf-dist/doc/latex/songs/songidx/chars.h b/Master/texmf-dist/doc/latex/songs/songidx/chars.h deleted file mode 100644 index 375b0a0b4b1..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/chars.h +++ /dev/null @@ -1,137 +0,0 @@ -/* Copyright (C) 2012 Kevin W. Hamlen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - * - * The latest version of this program can be obtained from - * http://songs.sourceforge.net. - */ - -#ifndef CHARS_H -#define CHARS_H - -#if HAVE_CONFIG_H -# include "config.h" -#else -# include "vsconfig.h" -#endif - -#if HAVE_STDLIB_H -# include <stdlib.h> -#endif -#if HAVE_WCHAR_H -# include <wchar.h> -#endif -#if HAVE_WCTYPE_H -# include <wctype.h> -#endif - -#if HAVE_WCHAR_T - -typedef wchar_t WCHAR; - -#define wc_lit(c) (L ## c) -#define wc_isalpha(c) iswalpha((c)) -#define wc_ispunct(c) iswpunct((c)) -#define wc_isupper(c) iswupper((c)) -#define wc_isspace(c) iswspace((c)) -#define wc_isdigit(c) iswdigit((c)) -#define wc_tolower(c) towlower((c)) -#define wc_toupper(c) towupper((c)) - -#define ws_lit(s) ws_lit2(s) -#define ws_lit2(s) (L ## s) -#define ws_strlen(p) wcslen((p)) -#define ws_strcmp(p1,p2) wcscmp((p1),(p2)) -#define ws_strncmp(p1,p2,n) wcsncmp((p1),(p2),(n)) -#define ws_strcpy(p1,p2) wcscpy((p1),(p2)) -#define ws_strncpy(p1,p2,n) wcsncpy((p1),(p2),(n)) -#define ws_memmove(p1,p2,n) wmemmove((p1),(p2),(n)) -#define ws_strchr(p,c) wcschr((p),(c)) -#define ws_strspn(p1,p2) wcsspn((p1),(p2)) -#define ws_strpbrk(p1,p2) wcspbrk((p1),(p2)) -#define ws_strtol(p1,p2,n) wcstol((p1),(p2),(n)) -#define ws_coll(p1,p2) wcscoll((p1),(p2)) -#define ws_fprintf1(fh,fs,x) fwprintf((fh),(fs),(x)) -#define ws_fprintf2(fh,fs,x,y) fwprintf((fh),(fs),(x),(y)) -#define ws_fprintf3(fh,fs,x,y,z) fwprintf((fh),(fs),(x),(y),(z)) -#define ws_fgets(p,n,fh) fgetws((p),(n),(fh)) -#define ws_fputs(s,fh) fputws((s),(fh)) -#define ws_fputc(c,fh) fputwc((c),(fh)) - -#else - -#include <ctype.h> -#if HAVE_STRING_H -# include <string.h> -#elif HAVE_STRINGS_H -# include <strings.h> -#endif - -typedef char WCHAR; - -#define wc_lit(c) (c) -#define wc_isalpha(c) isalpha((c)) -#define wc_ispunct(c) ispunct((c)) -#define wc_isupper(c) isupper((c)) -#define wc_isspace(c) isspace((c)) -#define wc_isdigit(c) isdigit((c)) -#define wc_tolower(c) tolower((c)) -#define wc_toupper(c) toupper((c)) - -#define ws_lit(s) (s) -#define ws_strlen(p) strlen((p)) -#define ws_strcmp(p1,p2) strcmp((p1),(p2)) -#define ws_strncmp(p1,p2,n) strncmp((p1),(p2),(n)) -#define ws_strcpy(p1,p2) strcpy((p1),(p2)) -#define ws_strncpy(p1,p2,n) strncpy((p1),(p2),(n)) -#define ws_memmove(p1,p2,n) memmove((p1),(p2),(n)) -#define ws_strchr(p,c) strchr((p),(c)) -#define ws_strspn(p1,p2) strspn((p1),(p2)) -#define ws_strpbrk(p1,p2) strpbrk((p1),(p2)) -#define ws_strtol(p1,p2,n) strtol((p1),(p2),(n)) -#define ws_coll(p1,p2) strcoll((p1),(p2)) -#define ws_fprintf1(fh,fs,x) fprintf((fh),(fs),(x)) -#define ws_fprintf2(fh,fs,x,y) fprintf((fh),(fs),(x),(y)) -#define ws_fprintf3(fh,fs,x,y,z) fprintf((fh),(fs),(x),(y),(z)) -#define ws_fgets(p,n,fh) fgets((p),(n),(fh)) -#define ws_fputs(s,fh) fputs((s),(fh)) -#define ws_fputc(c,fh) fputc((c),(fh)) - -#endif - -#define wc_null wc_lit('\0') -#define wc_hyphen wc_lit('-') -#define wc_apostrophe wc_lit('\'') -#define wc_backquote wc_lit('`') -#define wc_backslash wc_lit('\\') -#define wc_lbrace wc_lit('{') -#define wc_rbrace wc_lit('}') -#define wc_comma wc_lit(',') -#define wc_period wc_lit('.') -#define wc_space wc_lit(' ') -#define wc_newline wc_lit('\n') -#define wc_cr wc_lit('\r') -#define wc_asterisk wc_lit('*') -#define wc_hash wc_lit('#') -#define wc_pipe wc_lit('|') -#define wc_colon wc_lit(':') -#define wc_semicolon wc_lit(';') -#define wc_capA wc_lit('A') -#define wc_percent wc_lit('%') -#define wc_tilda wc_lit('~') - -#endif - diff --git a/Master/texmf-dist/doc/latex/songs/songidx/fileio.c b/Master/texmf-dist/doc/latex/songs/songidx/fileio.c deleted file mode 100644 index 26bf56fcf37..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/fileio.c +++ /dev/null @@ -1,122 +0,0 @@ -/* Copyright (C) 2012 Kevin W. Hamlen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - * - * The latest version of this program can be obtained from - * http://songs.sourceforge.net. - */ - -#if HAVE_CONFIG_H -# include "config.h" -#else -# include "vsconfig.h" -#endif - -#include "chars.h" -#include "songidx.h" -#include "fileio.h" - -#if HAVE_STRING_H -# include <string.h> -#endif - -/* fileopen(<fstate>,<filename>) - * Open <filename> for reading, storing the file handle in <fstate>. Return - * 1 on success or 0 on failure. - */ -int -fileopen(fs,fnam) - FSTATE *fs; - const char *fnam; -{ - if (strcmp(fnam,"-")) - { - fs->f = fopen(fnam, "r"); - if (!fs->f) - { - fprintf(stderr, "songidx: Unable to open %s for reading.\n", fnam); - return 0; - } - } - else - { - fs->f = stdin; - fnam = "stdin"; - } - fs->filename = (char *) calloc(strlen(fnam)+1, sizeof(char)); - if (!fs->filename) - { - fprintf(stderr, "songidx: Out of memory!\n"); - fclose(fs->f); - return 0; - } - strcpy(fs->filename, fnam); - fs->lineno = 0; - return 1; -} - -/* fileclose(<fstate>) - * Close file handle <fstate>. - */ -void -fileclose(fs) - FSTATE *fs; -{ - if (fs->f != stdin) fclose(fs->f); - free(fs->filename); - fs->f = NULL; - fs->filename = NULL; - fs->lineno = 0; -} - -/* filereadln(<fstate>,<buffer>,<eof flag>) - * Read a line of output into <buffer>, which should be at least MAXLINELEN - * wide-characters in size. If the line is too long to fit into the buffer, - * close the file and report an error. Eliminate the trailing \n from the - * returned line. Return 1 on success or end-of-line, or 0 on failure. If - * end-of-line, set <eof flag> to 1. */ -int -filereadln(fs,buf,eof) - FSTATE *fs; - WCHAR *buf; - int *eof; -{ - size_t n; - - ++fs->lineno; - if (!ws_fgets(buf, MAXLINELEN, fs->f)) - { - if (!ferror(fs->f)) - { - *eof=1; - return 1; - } - fprintf(stderr, "songidx:%s:%d: read error\n", fs->filename, fs->lineno); - fileclose(fs); - return 0; - } - n = ws_strlen(buf); - if ((n==0) || ((buf[n-1] != wc_newline) && (buf[n-1] != wc_cr))) - { - fprintf(stderr, "songidx:%s:%d: line too long\n", fs->filename, fs->lineno); - fileclose(fs); - return 0; - } - buf[n-1] = wc_null; - if ((n>=2) && ((buf[n-2] == wc_newline) || (buf[n-2] == wc_cr))) - buf[n-2] = wc_null; - return 1; -} diff --git a/Master/texmf-dist/doc/latex/songs/songidx/fileio.h b/Master/texmf-dist/doc/latex/songs/songidx/fileio.h deleted file mode 100644 index 327f5d59581..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/fileio.h +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright (C) 2012 Kevin W. Hamlen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - * - * The latest version of this program can be obtained from - * http://songs.sourceforge.net. - */ - -#ifndef FILEIO_H -#define FILEIO_H - -#if HAVE_CONFIG_H -# include "config.h" -#else -# include "vsconfig.h" -#endif - -#include "chars.h" - -#if HAVE_STDIO_H -# include <stdio.h> -#endif - -/* Define the maximum length of lines found in the input file */ -#define MAXLINELEN 1024 - -/* FSTATE structures model the state of a FILE */ -typedef struct -{ - FILE *f; - char *filename; - int lineno; -} -FSTATE; - -/* The following functions are in fileio.c */ -extern int fileopen(FSTATE *fs, const char *fnam); -extern void fileclose(FSTATE *fs); -extern int filereadln(FSTATE *fs, WCHAR *buf, int *eof); - -#endif diff --git a/Master/texmf-dist/doc/latex/songs/songidx/greek.can b/Master/texmf-dist/doc/latex/songs/songidx/greek.can index 289d5e2d21c..93b85c4146b 100644 --- a/Master/texmf-dist/doc/latex/songs/songidx/greek.can +++ b/Master/texmf-dist/doc/latex/songs/songidx/greek.can @@ -1,4 +1,4 @@ -# Copyright (C) 2012 Kevin W. Hamlen +# Copyright (C) 2017 Kevin W. Hamlen # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/Master/texmf-dist/doc/latex/songs/songidx/protestant.can b/Master/texmf-dist/doc/latex/songs/songidx/protestant.can index fab28005f98..f86cce40e4c 100644 --- a/Master/texmf-dist/doc/latex/songs/songidx/protestant.can +++ b/Master/texmf-dist/doc/latex/songs/songidx/protestant.can @@ -1,4 +1,4 @@ -# Copyright (C) 2012 Kevin W. Hamlen +# Copyright (C) 2017 Kevin W. Hamlen # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/Master/texmf-dist/doc/latex/songs/songidx/scripidx.c b/Master/texmf-dist/doc/latex/songs/songidx/scripidx.c deleted file mode 100644 index 1a2189ac617..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/scripidx.c +++ /dev/null @@ -1,1105 +0,0 @@ -/* Copyright (C) 2012 Kevin W. Hamlen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - * - * The latest version of this program can be obtained from - * http://songs.sourceforge.net. - */ - - -/* Create a LaTeX scripture index file from a scripture index .dat file. */ - -#if HAVE_CONFIG_H -# include "config.h" -#else -# include "vsconfig.h" -#endif - -#include "chars.h" -#include "songidx.h" -#include "fileio.h" - -#if HAVE_STRING_H -# include <string.h> -#else -# if HAVE_STRINGS_H -# include <strings.h> -# endif -#endif - -/* The following constant should be a large integer (but not greater than - * INT_MAX) limiting the maximum number of verses in a single chapter of the - * bible. */ -#define MAXVERSES 999 - -/* A BBOOK struct describes a book of the Bible, including: - * name: a \0-separated string of names (the first one is the name used when - * printing the scripture index) - * verses: an array of numbers giving the number of verses in each chapter - * aliases: the number of names in the name field - * chapters: the number of chapters in the book */ -typedef struct -{ - WCHAR *name; - int *verses; - int aliases; - int chapters; -} -BBOOK; - -/* A VERSE struct refers to a verse in a book of the Bible. - * chapter: the integral chapter number of the verse (1 if the book has only - * one chapter) - * verse: the integral verse number */ -typedef struct -{ - int chapter, verse; -} -VERSE; - -/* A SONGLIST is a list of songs - * num: a string denoting the song number (e.g. "3" or "H10") - * link: a string denoting the hyperlink label for the song - * int: an integer key used to order songs when sorting - * next: next pointer */ -typedef struct songliststruct -{ - WCHAR *num; - WCHAR *link; - int key; - struct songliststruct *next; -} -SONGLIST; - -/* A VERSELIST is the list of verses in a particular book of the Bible at which - * the set of songs referencing that verse differs from the set of songs - * referencing the previous verse in the book. Such a list allows us to - * sparsely represent the set of songs referencing each verse in the book - * without having to create a distinct list entry for each verse in the entire - * book. For example, if song "H1" references verses 3:1-10, that would - * represented by a two-element list: - * [ {v={chapter=3, verse=1}, adds=[H1], drops=[]}; - * {v={chapter=3, verse=10}, adds=[], drops=[H1]} ] - * This is more reasonable than creating ten list items to refer to such a - * range. - * v: the verse at which the set of song references is changing - * adds: a list of songs that refer to this verse but not the previous one - * drops: a list of songs that refer to this verse but not the next one - * next: pointer to next verse at which the set of references changes */ -typedef struct verseliststruct -{ - VERSE v; - SONGLIST *adds, *drops; - struct verseliststruct *next; -} -VERSELIST; - -/* The bible global variable stores the array of bible books being used to - * generate the scripture index. The numbooks variable stores the number of - * elements in the bible array. */ -BBOOK *bible = NULL; -int numbooks = 0; - -/* free_bible() - * Free the bible array, set it to NULL, and set numbooks to 0. */ -static void -free_bible() -{ - int i; - - if (!bible) return; - for (i=0; i<numbooks; ++i) - { - if (bible[i].name) free(bible[i].name); - if (bible[i].verses) free(bible[i].verses); - } - free(bible); - bible = NULL; - numbooks = 0; -} - -/* free_songlist(<slist>) - * Free song list <slist>. */ -static void -free_songlist(sl) - SONGLIST *sl; -{ - SONGLIST *next; - - for (; sl; sl=next) - { - if (sl->num) free(sl->num); - if (sl->link) free(sl->link); - next = sl->next; - free(sl); - } -} - -/* free_verselist(<vlist>) - * Free verse list <vlist>. */ -static void -free_verselist(vl) - VERSELIST *vl; -{ - VERSELIST *next; - - for (; vl; vl=next) - { - free_songlist(vl->adds); - free_songlist(vl->drops); - next = vl->next; - free(vl); - } -} - -/* freeidxarray(<array>,<len>) - * Free the array of verselists given by <array>. The array should have - * length <len>. */ -static void -freeidxarray(idx,len) - VERSELIST **idx; - int len; -{ - int i; - - if (!idx) return; - for (i=0; i<len; ++i) free_verselist(idx[i]); - free(idx); -} - -/* readbible(<filename>) - * Read bible data file <filename> into the bible array and set numbooks to - * the number of books read. Return 0 on error or 1 on success. */ -static int -readbible(filename) - const char *filename; -{ - FSTATE fs; - int eof = 0; - WCHAR buf[MAXLINELEN]; - int verses[MAXLINELEN/2+1]; - int arraysize, j; - WCHAR *p1, *p2; - - if (!fileopen(&fs,filename)) return 0; - - bible = NULL; - eof = 0; - for (arraysize=numbooks=0; !eof; ++numbooks) - { - if (!filereadln(&fs,buf,&eof)) - { - free_bible(); - return 0; - } - if (eof) break; - if (*buf == wc_hash) - { - --numbooks; - continue; - } - if (numbooks >= arraysize) - { - BBOOK *temp; - arraysize *= 2; - if (arraysize==0) arraysize=64; - temp = (BBOOK *) realloc(bible,arraysize*sizeof(BBOOK)); - if (!temp) - { - fprintf(stderr, "songidx:%s:%d: too many bible books (out of memory)\n", - fs.filename, fs.lineno); - free_bible(); - return 0; - } - bible = temp; - } - if ((bible[numbooks].name = - (WCHAR *) calloc(ws_strlen(buf)+1,sizeof(WCHAR))) == NULL) - { - fprintf(stderr, "songidx:%s:%d: bible book name too large" - " (out of memory)\n", fs.filename, fs.lineno); - fileclose(&fs); - free_bible(); - return 0; - } - for(j=1, p1=buf, p2=bible[numbooks].name; *p1!=wc_null; ++p1, ++p2) - { - if (*p1 == wc_pipe) - { - ++j; - *p2=wc_null; - } - else *p2=*p1; - } - *p2 = wc_null; - if (j == 0) - { - fprintf(stderr, "songidx:%s:%d: bible book has no name\n", - fs.filename, fs.lineno); - free(bible[numbooks].name); - fileclose(&fs); - free_bible(); - return 0; - } - bible[numbooks].aliases = j; - do - { - if (!filereadln(&fs,buf,&eof)) - { - free_bible(); - return 0; - } - if (eof) - { - fprintf(stderr, "songidx:%s:%d: incomplete bible book entry (book" - " title with no verses)\n", fs.filename, fs.lineno); - free_bible(); - return 0; - } - } - while (*buf == wc_hash); - for (j=0, p1=buf; *p1!=wc_null; ++j) - { - long v = ws_strtol(p1, &p1, 10); - if ((v<=0) || (v>MAXVERSES)) - { - fprintf(stderr, "songidx:%s:%d: illegal verse count for chapter %d\n", - fs.filename, fs.lineno, j+1); - fileclose(&fs); - free_bible(); - return 0; - } - verses[j] = (int) v; - } - if (j == 0) - { - fprintf(stderr, "songidx:%s:%d: book ", fs.filename, fs.lineno); - ws_fputs(bible[numbooks].name, stderr); - fprintf(stderr, " has no chapters\n"); - fileclose(&fs); - free_bible(); - return 0; - } - bible[numbooks].chapters = j; - if ((bible[numbooks].verses = (int *) calloc(j, sizeof(int))) == NULL) - { - fprintf(stderr, "songidx:%s:%d: too many verses (out of memory)\n", - fs.filename, fs.lineno); - fileclose(&fs); - free_bible(); - return 0; - } - for (j=0; j<bible[numbooks].chapters; ++j) - bible[numbooks].verses[j] = verses[j]; - } - - fileclose(&fs); - return 1; -} - -/* ws_stricmp(<string1>,<string2>) - * Case-insensitive, wide-string comparison function. (Some standard C libs - * already have one of these, but not all do; for compatibility I have to - * write my own.) */ -static int -ws_stricmp(p1,p2) - const WCHAR *p1; - const WCHAR *p2; -{ - for (;;) - { - int diff = wc_tolower(*p1) - wc_tolower(*p2); - if (diff != 0) return diff; - if (*p1 == wc_null) return 0; - ++p1; ++p2; - } -} - -/* parseref(<string>,<book>,<verse>) - * Interpret <string> as a scripture reference, and store the NUMBER of the - * book of the Bible it refers to in <book>, and the chapter and verse that - * it refers to in <verse>. Return a pointer into <string> to the suffix of - * <string> that was not parsed. The <book> and <verse> should hold the book - * and verse of the PREVIOUS scripture reference (if any) on entry to the - * function. If book or chapter information is missing, they will be drawn - * from this info. That way, successive calls can correctly parse a run-on - * string like "Philippians 3:1,5; 4:3", inferring that "5" refers to - * "Philippians 3" and "4:3" refers to "Philippians". If the parser - * encounters an error in processing the book name (e.g., a book name was - * specified but not recognized), <book> is set to numbooks. If no chapter - * or no verse is provided (e.g., the reference is just "Philippians" or - * "Philippians 3") then the chapter and/or verse fields of <verse> are set - * to -1. */ -static WCHAR * -parseref(r,book,v) - WCHAR *r; - int *book; - VERSE *v; -{ - WCHAR *p; - WCHAR c; - int i; - const WCHAR *a; - - while (*r==wc_space) ++r; - p = ws_strpbrk(r, ws_lit(",;-")); - if (!p) p=r+ws_strlen(r); - if (p>r) --p; - while ((p>r) && (*p==wc_space)) --p; - while ((p>r) && (wc_isdigit(*p) || (*p==wc_colon))) --p; - if (p>r) - { - if (*p==wc_space) - { - *p++ = wc_null; - for (*book=0; *book<numbooks; ++(*book)) - { - for (i=0, a=bible[*book].name; - i<bible[*book].aliases; - ++i, a+=ws_strlen(a)+1) - if (!ws_stricmp(r,a)) break; - if (i<bible[*book].aliases) break; - } - } - else - { - *book=numbooks; - } - r=p; - v->chapter = v->verse = -1; - } - while (wc_isdigit(*p)) ++p; - if (*p==wc_colon) - { - *p++ = wc_null; - v->chapter = ws_strtol(r,NULL,10); - r=p; - while (wc_isdigit(*p)) ++p; - } - if (r==p) return NULL; - c = *p; - *p = wc_null; - if ((v->chapter < 0) && (*book>=0) && (*book<numbooks) && - (bible[*book].chapters == 1)) - { - /* Special case: This book only has one chapter. */ - v->chapter = 1; - } - if (v->chapter < 0) - v->chapter = ws_strtol(r,NULL,10); - else - v->verse = ws_strtol(r,NULL,10); - *p = c; - while (*p==wc_space) ++p; - if (*p && (*p!=wc_comma) && (*p!=wc_semicolon) && (*p!=wc_hyphen)) - return NULL; - return p; -} - -/* vlt(<v1>,<v2>) - * Return 1 if verse <v1> precedes <v2> and 0 otherwise. */ -static int -vlt(v1,v2) - const VERSE v1; - const VERSE v2; -{ - return ((v1.chapter == v2.chapter) ? (v1.verse < v2.verse) - : (v1.chapter < v2.chapter)); -} - -/* vcpy(<vdest>,<vsrc>) - * Copy the data from verse <vsrc> into verse <vdest>. */ -static void -vcpy(vdest,vsrc) - VERSE *vdest; - const VERSE vsrc; -{ - vdest->chapter = vsrc.chapter; - vdest->verse = vsrc.verse; -} - -/* getvcount(<book>,<chapter>) - * Return the number of verses in chapter <chapter> of book number <book> of - * the Bible. */ -static int -getvcount(book,chapter) - int book; - int chapter; -{ - if ((book<0) || (book>=numbooks)) - { - /* This should never happen. */ - fprintf(stderr, "songidx: Internal error (getvcount):" - " Book %d out of range.\n", book); - return 0; - } - if ((chapter<1) || (chapter > bible[book].chapters)) - return 0; - return bible[book].verses[chapter-1]; -} - -/* vinc(<book>,<verse>) - * Return a reference to the verse immediately following <verse> in book - * number <book> of the Bible. The verse returned is static, so successive - * calls will overwrite it. If <verse> is the last verse in <book>, the - * return value will reference verse 1 of a non-existant chapter. */ -static VERSE -vinc(book,v) - int book; - const VERSE v; -{ - static VERSE vret; - int cnt; - - vret.chapter = v.chapter; - vret.verse = v.verse + 1; - cnt = getvcount(book, vret.chapter); - if (cnt <= 0) - { - /* This should never happen. */ - fprintf(stderr, "songidx: Internal Error (vinc): Book "); - ws_fputs(bible[book].name, stderr); - fprintf(stderr, " has no chapter %d.\n", vret.chapter); - } - else if (vret.verse > cnt) - { - ++vret.chapter; - vret.verse = 1; - } - return vret; -} - -/* vdec(<book>,<verse>) - * Return a reference to the verse immediately preceding <verse> in book - * number <book> of the Bible. The verse returned is static, so successive - * calls will overwrite it. If <verse> is the first verse in <book>, the - * return value will reference chapter -1 and an error message will be - * printed to stderr. */ -static VERSE -vdec(book,v) - int book; - const VERSE v; -{ - static VERSE vret; - vret.chapter = v.chapter; - vret.verse = v.verse - 1; - if (vret.verse < 1) - { - --vret.chapter; - vret.verse = getvcount(book, vret.chapter); - if (vret.verse <= 0) - { - /* This should never happen. */ - fprintf(stderr, "songidx: Internal Error (vdec): Book "); - ws_fputs(bible[book].name, stderr); - fprintf(stderr, " has no chapter %d.\n", vret.chapter); - vret.verse = -1; - } - } - return vret; -} - -/* addref(<reflist>,<song>,<link>,<key>) - * Add a reference to song (<song>,<link>,<key>) to REFLIST <reflist>. The - * <reflist> is assumed to be sorted by <key>. If a reference with the same - * <key> already exists, <reflist> is left unchanged. Both <song> and - * <link> are COPIED into the list. Return 1 on success or 0 on failure. */ -static int -addref(sl,n,l,key) - SONGLIST **sl; - const WCHAR *n; - const WCHAR *l; - int key; -{ - SONGLIST *newsong; - - for (; *sl; sl=&((*sl)->next)) - if ((*sl)->key >= key) break; - if (*sl && ((*sl)->key == key)) return 1; - - if ((newsong = (SONGLIST *) malloc(sizeof(SONGLIST))) == NULL) - { - fprintf(stderr, "songidx: too many scripture references (out of memory)\n"); - return 0; - } - newsong->num = newsong->link = NULL; - if (((newsong->num = (WCHAR *) calloc(ws_strlen(n)+1, - sizeof(WCHAR))) == NULL) || - ((newsong->link = (WCHAR *) calloc(ws_strlen(l)+1, - sizeof(WCHAR))) == NULL)) - { - fprintf(stderr, "songidx: too many scripture references (out of memory)\n"); - if (newsong->num) free(newsong->num); - if (newsong->link) free(newsong->link); - free(newsong); - return 0; - } - ws_strcpy(newsong->num, n); - ws_strcpy(newsong->link, l); - newsong->key = key; - newsong->next = *sl; - *sl = newsong; - return 1; -} - -/* insertref(<type>,<verselist>,<verse>,<song>,<link>,<key>) - * If <type> is INSERT_ADD, then insert song (<song>,<link>,<key>) into the - * set of "adds" for verse <verse> in <verselist>. If <type> is INSERT_DROP, - * then insert the song into the set of "drops" for verse <verse>. In either - * case, create a new entry for <verse> in <verselist> if it doesn't already - * exist. Return 1 on success or 0 on failure. */ -#define INSERT_ADD 1 -#define INSERT_DROP 0 - -static int -insertref(type,vpp,v,n,l,key) - char type; - VERSELIST **vpp; - const VERSE v; - const WCHAR *n; - const WCHAR *l; - int key; -{ - for (;;) - { - if (!*vpp || vlt(v, (*vpp)->v)) - { - VERSELIST *vnew = NULL; - SONGLIST *sl = NULL; - if (((vnew = (VERSELIST *) malloc(sizeof(VERSELIST))) == NULL) || - ((sl = (SONGLIST *) malloc(sizeof(SONGLIST))) == NULL)) - { - fprintf(stderr, "songidx: too many scripture references" - " (out of memory)\n"); - if (vnew) free(vnew); - if (sl) free(sl); - return 0; - } - sl->num = sl->link = NULL; - if (((sl->num = (WCHAR *) calloc(ws_strlen(n)+1, - sizeof(WCHAR))) == NULL) || - ((sl->link = (WCHAR *) calloc(ws_strlen(l)+1, - sizeof(WCHAR))) == NULL)) - { - fprintf(stderr, "songidx: too many scripture references" - " (out of memory)\n"); - if (sl->num) free(sl->num); - if (sl->link) free(sl->link); - free(vnew); - free(sl); - return 0; - } - ws_strcpy(sl->num, n); - ws_strcpy(sl->link, l); - sl->key = key; - sl->next = NULL; - vcpy(&(vnew->v), v); - if (type) - { - vnew->adds = sl; - vnew->drops = NULL; - } - else - { - vnew->adds = NULL; - vnew->drops = sl; - } - vnew->next = *vpp; - *vpp = vnew; - return 1; - } - else if (!vlt((*vpp)->v, v)) - { - return addref(type ? &((*vpp)->adds) : &((*vpp)->drops), n, l, key); - } - vpp=&((*vpp)->next); - } -} - -/* addrefs(<dest>,<src>) - * Take the union of SONGLIST <dest> and SONGLIST <src>, storing the result - * in <dest>. Return 1 on success, or 0 on failure. */ -static int -addrefs(dest,src) - SONGLIST **dest; - const SONGLIST *src; -{ - for (; src; src=src->next) - if (!addref(dest, src->num, src->link, src->key)) return 0; - return 1; -} - -/* removerefs(<dest>,<src>) - * Take the set difference between SONGLIST <dest> and SONGLIST <src>, - * storing the result in <dest>. */ -static void -removerefs(dest,src) - SONGLIST **dest; - const SONGLIST *src; -{ - SONGLIST **sl; - - for (; src; src=src->next) - { - for (sl=dest; *sl; sl=&((*sl)->next)) - if ((*sl)->key >= src->key) break; - if (*sl && ((*sl)->key == src->key)) - { - SONGLIST *temp = (*sl)->next; - free((*sl)->num); - free((*sl)->link); - free(*sl); - *sl = temp; - } - } -} - -/* slcmp(<slist1>,<slist2>) - * Compare SONGLIST's <slist1> and <slist2>, returning 1 if they are - * different and 0 if they are the same. */ -static int -slcmp(s1,s2) - const SONGLIST *s1; - const SONGLIST *s2; -{ - for (; s1 && s2; s1=s1->next, s2=s2->next) - if (s1->key != s2->key) return 1; - return (s1 || s2); -} - -/* print_vrange(<file>,<book>,<verse1>,<verse2>,<lastchapter>) - * Output LaTeX material to file handle <file> for verse range - * <verse1>-<verse2> of book number <book>. Depending on <lastchapter>, - * the outputted material might be the start of a new index entry or the - * continuation of a previous entry. If <lastchapter> is 0, start a new - * index entry. If <lastchapter> is >0, continue the previous entry and - * print the chapter of <verse1> only if it differs from <lastchapter>. - * If <lastchapter> is <0, continue the previous entry and always print the - * chapter number of <verse1>. Return 0 on success or -1 on failure. */ -static int -print_vrange(f,book,v1,v2,lastchapter) - FILE *f; - int book; - const VERSE v1; - const VERSE v2; - int lastchapter; -{ - if (ws_fputs(lastchapter ? ws_lit(",") : ws_lit("\\idxentry{"), f) < 0) - return -1; - if (v1.verse <= 0) - { - if (ws_fprintf1(f, lastchapter ? ws_lit("\\thinspace %d") : ws_lit("%d"), - v1.chapter) < 0) - return -1; - } - else if ((book>=0) && (book<numbooks) && (bible[book].chapters == 1)) - { - /* This book has only one chapter. */ - if (ws_fprintf1(f, lastchapter ? ws_lit("\\thinspace %d") : ws_lit("%d"), - v1.verse) < 0) - return -1; - } - else if ((lastchapter<=0) || (lastchapter!=v1.chapter) || - (v1.chapter!=v2.chapter)) - { - if (ws_fprintf2(f, lastchapter ? ws_lit(" %d:%d") : ws_lit("%d:%d"), - v1.chapter, v1.verse) < 0) - return -1; - } - else - { - if (ws_fprintf1(f, lastchapter ? ws_lit("\\thinspace %d") : ws_lit("%d"), - v1.verse) < 0) - return -1; - } - - if (vlt(v1,v2)) - { - if (v2.verse <= 0) - { - if (ws_fprintf1(f, ws_lit("--%d"), v2.chapter) < 0) return -1; - } - else if (v1.chapter!=v2.chapter) - { - if (ws_fprintf2(f, ws_lit("--%d:%d"), v2.chapter, v2.verse) < 0) - return -1; - } - else - { - if (ws_fprintf1(f, ws_lit("--%d"), v2.verse) < 0) return -1; - } - } - return 0; -} - -/* print_reflist(<file>,<slist>) - * Output the list of song references given by <slist> to file handle <file>. - * Return 0 on success or -1 on failure. */ -static int -print_reflist(f,sp) - FILE *f; - const SONGLIST *sp; -{ - char first; - for (first=1; sp; sp=sp->next) - { - if (!first) - { - if (ws_fputs(ws_lit("\\\\"), f) < 0) return -1; - } - first = 0; - if ((ws_fputs(ws_lit("\\hyperlink{"), f) < 0) || - (ws_fputs(sp->link, f) < 0) || - (ws_fputs(ws_lit("}{"), f) < 0) || - (ws_fputs(sp->num, f) < 0) || - (ws_fputs(ws_lit("}"), f) < 0)) - return -1; - } - return 0; -} - -/* genscriptureindex(<fstate>,<outname>,<biblename>) - * Generate a LaTeX file named <outname> containing material suitable to - * typeset the scripture index data found in input file handle <file>. - * Input Bible data from an ascii file named <biblename>. Return 0 on - * success, 1 if there were warnings, and 2 if there was a fatal error. */ -int -genscriptureindex(fs,outname,biblename) - FSTATE *fs; - const char *outname; - const char *biblename; -{ - FILE *f; - int eof = 0; - VERSELIST **idx; - int book, book2; - WCHAR ref[MAXLINELEN], songnum[MAXLINELEN], linkname[MAXLINELEN]; - int songcount; - WCHAR *p; - VERSE v1, v2; - char hadwarnings = 0; - - fprintf(stderr, "songidx: Parsing scripture index data file %s...\n", - fs->filename); - - /* Read the bible data file into the bible array. */ - if (!readbible(biblename)) return 2; - if ((idx = (VERSELIST **) calloc(numbooks, sizeof(VERSELIST *))) == NULL) - { - fprintf(stderr, "songidx: too many books of the bible (out of memory)\n"); - free_bible(); - return 2; - } - for (book=0; book<numbooks; ++book) idx[book] = NULL; - - /* Walk through the input file and construct a VERSELIST for each book - * of the Bible. Each VERSELIST represents the set of verses in that book - * referred to by songs in the song book. */ - songcount = 0; - book = -1; - v1.chapter = -1; - v1.verse = -1; - for (;;) - { - /* Get the next song data record. */ - if (!filereadln(fs,ref,&eof)) - { - freeidxarray(idx,numbooks); - free_bible(); - return 2; - } - if (eof) break; - if (!filereadln(fs,songnum,&eof) || eof || - !filereadln(fs,linkname,&eof) || eof) - { - if (eof) - { - fprintf(stderr, "songidx:%s:%d: incomplete entry\n", - fs->filename, fs->lineno); - fileclose(fs); - } - freeidxarray(idx,numbooks); - free_bible(); - return 2; - } - - /* Add all scripture references for this song to the VERSELIST array */ - p = ref; - while (*p != wc_null) - { - /* Parse the next reference. */ - p = parseref(p, &book, &v1); - if (!p) - { - fprintf(stderr, "songidx:%s:%d: WARNING: Malformed scripture reference" - " for song ", fs->filename, fs->lineno); - ws_fputs(songnum, stderr); - fprintf(stderr, ". Ignoring it.\n"); - hadwarnings = 1; - break; - } - if (book<0) - { - fprintf(stderr, "songidx:%s:%d: WARNING: Scripture reference for song ", - fs->filename, fs->lineno); - ws_fputs(songnum, stderr); - fprintf(stderr, " doesn't include a book name. Ignoring it.\n"); - hadwarnings = 1; - break; - } - if (book>=numbooks) - { - fprintf(stderr, "songidx:%s:%d: WARNING: Scripture reference for song ", - fs->filename, fs->lineno); - ws_fputs(songnum, stderr); - fprintf(stderr, " references unknown book. Ignoring it.\n"); - hadwarnings = 1; - break; - } - if (v1.chapter < 1) - { - fprintf(stderr, "songidx:%s:%d: WARNING: Scripture reference for song ", - fs->filename, fs->lineno); - ws_fputs(songnum, stderr); - fprintf(stderr, " has no chapter or verse. Ignoring it.\n"); - hadwarnings = 1; - break; - } - /* Unless the reference ended in a "-", it consists of just one verse. */ - v2.chapter = v1.chapter; - v2.verse = v1.verse; - if (*p==wc_hyphen) - { - /* This reference ended in a "-", which means it starts a range. - * Parse the next reference to find the range's end. */ - book2 = book; - p = parseref(++p, &book2, &v2); - if (!p) - { - fprintf(stderr, "songidx:%s:%d: WARNING: Malformed scripture" - " reference for song ", fs->filename, fs->lineno); - ws_fputs(songnum, stderr); - fprintf(stderr, ". Ignoring it.\n"); - hadwarnings = 1; - break; - } - if (book2!=book) - { - fprintf(stderr, "songidx:%s:%d: WARNING: Scripture reference for" - " song ", fs->filename, fs->lineno); - ws_fputs(songnum, stderr); - fprintf(stderr, " appears to span books! Ignoring it.\n"); - hadwarnings = 1; - break; - } - } - if (*p!=wc_null) ++p; - /* If either the start or end references of the range were incomplete - * (e.g., the range was something like "Philippians 3", where the verse - * numbers are not explicit), try to fill in the missing information - * based on how many verses are in the chapter and how many chapters are - * in the book. */ - if (v1.verse <= 0) v1.verse = 1; - if (v2.verse <= 0) - { - v2.verse = getvcount(book,v2.chapter); - if (v2.verse <= 0) - { - fprintf(stderr, "songidx:%s:%d: WARNING: Scripture reference for " - " song ", fs->filename, fs->lineno); - ws_fputs(songnum, stderr); - fprintf(stderr, " refers implicitly to chapter %d of ", v2.chapter); - ws_fputs(bible[book].name, stderr); - fprintf(stderr, ", which doesn't exist. Ignoring it.\n"); - hadwarnings = 1; - break; - } - } - if (v1.verse > getvcount(book,v1.chapter)) - { - fprintf(stderr, "songidx:%s:%d: WARNING: Song ", - fs->filename, fs->lineno); - ws_fputs(songnum, stderr); - fprintf(stderr, " references "); - ws_fputs(bible[book].name, stderr); - fprintf(stderr, " %d:%d, which doesn't exist! Ignoring it.\n", - v1.chapter, v1.verse); - hadwarnings = 1; - break; - } - if (v2.verse > getvcount(book,v2.chapter)) - { - fprintf(stderr, "songidx:%s:%d: WARNING: Song ", - fs->filename, fs->lineno); - ws_fputs(songnum, stderr); - fprintf(stderr, " references "); - ws_fputs(bible[book].name, stderr); - fprintf(stderr, " %d:%d, which doesn't exist! Ignoring it.\n", - v2.chapter, v2.verse); - hadwarnings = 1; - break; - } - - /* Add the range to the SONGLIST array. */ - if (!insertref(INSERT_ADD, &(idx[book]), v1, songnum, linkname, - songcount) || - !insertref(INSERT_DROP, &(idx[book]), v2, songnum, linkname, - songcount)) - { - fileclose(fs); - freeidxarray(idx,numbooks); - free_bible(); - return 2; - } - } - /* finished adding all refs for this song */ - ++songcount; - } - /* finished adding all refs for all songs */ - fileclose(fs); - - /* Now create the index .sbx file */ - fprintf(stderr, "songidx: Generating scripture index TeX file %s...\n", - outname); - if (strcmp(outname,"-")) - { - if ((f = fopen(outname, "w")) == NULL) - { - fprintf(stderr, "songidx: Unable to open %s for writing.\n", outname); - freeidxarray(idx,numbooks); - free_bible(); - return 2; - } - } - else - { - f = stdout; - outname = "stdout"; - } - -#define TRYWRITE(x) \ - if (!(x)) \ - { \ - fprintf(stderr, "songidx:%s: write error\n", outname); \ - if (f == stdout) fflush(f); else fclose(f); \ - freeidxarray(idx,numbooks); \ - free_bible(); \ - return 2; \ - } - - /* For each book of the Bible that has songs that reference it, go through - * its VERSELIST and generate a sequence of index entries. Wherever - * possible, compact adjacent entries that have identical SONGLISTS so that - * we never have two consecutive index entries with identical right-hand - * sides. */ - for (book=0; book<numbooks; ++book) - if (idx[book]) - { - VERSE vrstart; - VERSELIST *vp; - SONGLIST *sl = NULL; - int lastchapter; /* 0=none, -1=force printing of chapter */ - - TRYWRITE((ws_fputs(ws_lit("\\begin{idxblock}{"), f) >=0) - && (ws_fputs(bible[book].name, f) >= 0) - && (ws_fputs(ws_lit("}\n"), f) >=0)) - lastchapter = 0; - vcpy(&vrstart, idx[book]->v); - for (vp=idx[book]; vp; vp=vp->next) - { - if (!addrefs(&sl, vp->adds)) - { - fclose(f); - freeidxarray(idx,numbooks); - free_bible(); - return 2; - } - if (!slcmp(vp->drops, vp->next ? vp->next->adds : NULL)) - { - /* Set of drops here equals set of adds next time. There's at least - * a chance that we can combine this item and the next one into a - * single index entry. */ - if (vp->next && !vlt(vinc(book,vp->v), vp->next->v)) - { - /* If the next item is adjacent to this one, do nothing. Just let - * the range in progress be extended. We'll output a single entry - * for all of these adjacent verses when we reach the end. */ - continue; - } - else if (vp->next && !slcmp(sl, vp->drops)) - { - /* Otherwise, if the next item is not adjacent but all refs are - * dropped here, then print a partial entry to be continued with a - * comma next time. */ - TRYWRITE(print_vrange(f, book, vrstart, vp->v, lastchapter) >= 0) - lastchapter = (vrstart.chapter != vp->v.chapter) ? - -1 : vp->v.chapter; - vcpy(&vrstart, vp->next->v); - continue; - } - } - if (vp->drops) - { - /* Some songs get dropped here, and either the next item is not - * adjacent to this one, or it's adjacent and the set of adds is not - * the same. In either case, that means the set of refs changes at - * this point, so we need to output a full entry (or finish the one - * in progress). */ - TRYWRITE((print_vrange(f, book, vrstart, vp->v, lastchapter) >= 0) && - (ws_fputs(ws_lit("}{"), f) >= 0) && - (print_reflist(f, sl) >= 0) && - (ws_fputs(ws_lit("}\n"), f) >= 0)) - removerefs(&sl, vp->drops); - lastchapter = 0; - vcpy(&vrstart, (!sl && vp->next) ? vp->next->v : vinc(book,vp->v)); - } - if (sl && vp->next && vp->next->adds && vlt(vrstart, vp->next->v)) - { - /* There are verses between this item and the next which have refs, - * but the refs change at the beginning of the next item. Make an - * entry for the intermediate block of verses. */ - VERSE vrend; - vcpy(&vrend, vdec(book,vp->next->v)); - TRYWRITE((print_vrange(f, book, vrstart, vrend, lastchapter) >= 0) && - (ws_fputs(ws_lit("}{"), f) >= 0) && - (print_reflist(f, sl) >= 0) && - (ws_fputs(ws_lit("}\n"), f) >= 0)) - lastchapter = 0; - vcpy(&vrstart, vp->next->v); - } - } - if (sl) - { - fprintf(stderr, "songidx: scripture references in "); - ws_fputs(bible[book].name, stderr); - fprintf(stderr, " appear to extend past the end of the book!" - " Aborting.\n"); - fclose(f); - freeidxarray(idx,numbooks); - free_bible(); - return 2; - } - TRYWRITE(ws_fputs(ws_lit("\\end{idxblock}\n"), f) >= 0) - } - -#undef TRYWRITE - - fclose(f); - freeidxarray(idx,numbooks); - free_bible(); - - return hadwarnings; -} diff --git a/Master/texmf-dist/doc/latex/songs/songidx/songidx.c b/Master/texmf-dist/doc/latex/songs/songidx/songidx.c deleted file mode 100644 index 20c08e62df8..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/songidx.c +++ /dev/null @@ -1,270 +0,0 @@ -/* Copyright (C) 2012 Kevin W. Hamlen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - * - * The latest version of this program can be obtained from - * http://songs.sourceforge.net. - * - * - * This program generates an index.sbx file from an index.sxd file. Index.sxd - * files are generated by the song book LaTeX files while you compile a song - * book. They contain lists of all the song titles, song numbers, and - * scripture references. Once the .sxd files exist, run this program to - * convert them in to .sbx files. This program will sort the song titles - * alphabetically, group all the scripture entries into the appropriate - * chunks, etc. The TeX files that it creates are then used by the LaTeX - * files next time you compile the song book. - * - * The syntax for running the program is: - * songidx input.sxd output.sbx - * or, if you're compiling a scripture index: - * songidx -b bible.can input.sxd output.sbx - * - * The program should compile on unix with - * - * cc *.c -o songidx - * - * On Windows, you'll need to find a C compiler in order to compile it, - * or go to http://songs.sourceforge.net to obtain a Windows self-installer - * with pre-compiled binaries. - */ - -#if HAVE_CONFIG_H -# include "config.h" -#else -# include "vsconfig.h" -#endif - -#include "chars.h" -#include "songidx.h" -#include "fileio.h" - -#if HAVE_STRING_H -# include <string.h> -#elif HAVE_STRINGS_H -# include <strings.h> -#endif - -#if HAVE_SETLOCALE -#if HAVE_LOCALE_H -# include <locale.h> -#endif -#endif - -extern int gentitleindex(FSTATE *fs, const char *outname); -extern int genscriptureindex(FSTATE *fs, const char *outname, - const char *biblename); -extern int genauthorindex(FSTATE *fs, const char *outname); - -#define BIBLEDEFAULT "bible.can" - -#if HAVE_STRRCHR -# define STRRCHR(x,y) strrchr((x),(y)) -#else -# define STRRCHR(x,y) local_strrchr((x),(y)) -char * -local_strrchr(s,c) - const char *s; - int c; -{ - const char *t; - if (!s) return NULL; - for (t=s; *t; ++s) ; - for (; t>=s; --s) - if (*t == c) return t; - return NULL; -} -#endif - -int -main(argc,argv) - int argc; - char *argv[]; -{ - FSTATE fs; - int eof = 0; - WCHAR buf[MAXLINELEN]; - int retval; - const char *bible, *inname, *outname, *locale; - int i; - - bible = inname = outname = locale = NULL; - for (i=1; i<argc; ++i) - { - if (!strcmp(argv[i],"-v") || !strcmp(argv[i],"--version")) - { - printf("songidx " VERSION "\n" - "Copyright (C) 2012 Kevin W. Hamlen\n" - "License GPLv2: GNU GPL version 2 or later" - " <http://gnu.org/licenses/gpl.html>\n" - "This is free software: you are free to change and redistribute it.\n" - "There is NO WARRANTY, to the extent permitted by law.\n"); - return 0; - } - else if (!strcmp(argv[i],"-h") || !strcmp(argv[i],"--help")) - { - printf("Syntax: %s [options] input.sxd [output.sbx]\n", argv[0]); - printf( - "Available options:\n" - " -b FILE Sets the bible format when generating a\n" - " --bible FILE scripture index (default: " BIBLEDEFAULT ")\n" - "\n" - " -l LOCALE Overrides the default system locale (affects\n" - " --locale LOCALE (how non-English characters are parsed).\n" - " See local system help for valid LOCALE's.\n" - "\n" - " -h Displays this help file and stops.\n" - " --help\n" - "\n" - " -o FILE Sets the output filename.\n" - " --output FILE\n" - "\n" - " -v Print version information and stop.\n" - " --version\n" - "\n" - "If omitted, [output.sbx] defaults to the input filename but with\n" - "the file extension renamed to '.sbx'. To read or write to stdin or\n" - "stdout, use '-' in place of input.sxd or output.sbx.\n" - "\n" - "See http://songs.sourceforge.net for support.\n"); - return 0; - } - else if (!strcmp(argv[i],"-b") || !strcmp(argv[i],"--bible")) - { - if (bible) - { - fprintf(stderr, "songidx: multiple bible files specified\n"); - return 2; - } - else if (++i < argc) - bible = argv[i]; - else - { - fprintf(stderr, "songidx: %s option requires an argument\n", argv[i-1]); - return 2; - } - } - else if (!strcmp(argv[i],"-l") || !strcmp(argv[i],"--locale")) - { - if (locale) - { - fprintf(stderr, "songidx: multiple locales specified\n"); - return 2; - } - else if (++i < argc) - locale = argv[i]; - else - { - fprintf(stderr, "songidx: %s option requires an argument\n", argv[i-1]); - return 2; - } - } - else if (!strcmp(argv[i],"-o") || !strcmp(argv[i],"--output")) - { - if (outname) - { - fprintf(stderr, "songidx: multiple output files specified\n"); - return 2; - } - else if (++i < argc) - outname = argv[i]; - else - { - fprintf(stderr, "songidx: %s option requires an argument\n", argv[i-1]); - return 2; - } - } - else if ((argv[i][0] == '-') && (argv[i][1] != '\0')) - { - fprintf(stderr, "songidx: unknown option %s\n", argv[i]); - return 2; - } - else if (!inname) inname=argv[i]; - else if (!outname) outname=argv[i]; - else - { - fprintf(stderr, "songidx: too many command line arguments\n"); - return 2; - } - } - -#if HAVE_SETLOCALE - if (!locale) - (void) setlocale(LC_ALL, ""); - else if (!setlocale(LC_ALL, locale)) - { - fprintf(stderr, "songidx: invalid locale: %s\n", locale); - return 2; - } -#endif - - if (!inname) - { - fprintf(stderr, "songidx: no input file specified\n"); - return 2; - } - if (!outname) - { - if (!strcmp(inname,"-")) - outname = "-"; - else - { - char *b; - const char *p1 = STRRCHR(inname,'.'); - if ((p1 < STRRCHR(inname,'/')) || (p1 < STRRCHR(inname,'\\'))) - p1 = inname+strlen(inname); - b = (char *) malloc(p1-inname+5); - strncpy(b,inname,p1-inname); - strcpy(b+(p1-inname),".sbx"); - outname = b; - } - } - if (!bible) bible = BIBLEDEFAULT; - - if (!fileopen(&fs, inname)) return 2; - - /* The first line of the input file is a header line that dictates which kind - * of index the data file is for. */ - if (!filereadln(&fs, buf, &eof)) return 2; - retval = 2; - if (eof) - { - fprintf(stderr, "songidx:%s: file is empty\n", fs.filename); - fileclose(&fs); - } - else if (!ws_strcmp(buf, ws_lit("TITLE INDEX DATA FILE"))) - retval = gentitleindex(&fs, outname); - else if (!ws_strcmp(buf, ws_lit("SCRIPTURE INDEX DATA FILE"))) - retval = genscriptureindex(&fs, outname, bible); - else if (!ws_strcmp(buf, ws_lit("AUTHOR INDEX DATA FILE"))) - retval = genauthorindex(&fs, outname); - else - { - fprintf(stderr, "songidx:%s:%d: file has unrecognized format\n", - fs.filename, fs.lineno); - fileclose(&fs); - } - - /* Report the outcome to the user. */ - if (retval == 0) - fprintf(stderr, "songidx: Done!\n"); - else if (retval == 1) - fprintf(stderr, "songidx: COMPLETED WITH ERRORS. SEE ABOVE.\n"); - else - fprintf(stderr, "songidx: FAILED. SEE ERROR MESSAGES ABOVE.\n"); - - return retval; -} diff --git a/Master/texmf-dist/doc/latex/songs/songidx/songidx.h b/Master/texmf-dist/doc/latex/songs/songidx/songidx.h deleted file mode 100644 index 42dd130b1cd..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/songidx.h +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright (C) 2012 Kevin W. Hamlen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - * - * The latest version of this program can be obtained from - * http://songs.sourceforge.net. - */ - -#ifndef SONGIDX_H -#define SONGIDX_H - -#if HAVE_CONFIG_H -# include "config.h" -#else -# include "vsconfig.h" -#endif - -#include "chars.h" - -/* A SONGENTRY struct consists of three fields: - * title: a string representing the title of the song - * num: a string representing the song's number in the book (might be - * something like "H10") - * linkname: a string denoting the internal hyperlink label for this song - * idx: a unique integer key that should be used to sort songs with - * identical titles */ -typedef struct -{ - WCHAR *title; - WCHAR *num; - WCHAR *linkname; - int idx; -} -SONGENTRY; - -/* The following functions are in songsort.c */ -extern void skipesc(const WCHAR **p); -extern int songcmp(const void *s1, const void *s2); - -#endif diff --git a/Master/texmf-dist/doc/latex/songs/songidx/songidx.lua b/Master/texmf-dist/doc/latex/songs/songidx/songidx.lua new file mode 100644 index 00000000000..bc50b597c45 --- /dev/null +++ b/Master/texmf-dist/doc/latex/songs/songidx/songidx.lua @@ -0,0 +1,1070 @@ +-- Copyright (C) 2017 Kevin W. Hamlen +-- +-- This program is free software; you can redistribute it and/or +-- modify it under the terms of the GNU General Public License +-- as published by the Free Software Foundation; either version 2 +-- of the License, or (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program; if not, write to the Free Software +-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +-- MA 02110-1301, USA. +-- +-- The latest version of this program can be obtained from +-- http://songs.sourceforge.net. + + +VERSION = "3.0" +BIBLEDEFAULT = "bible.can" + +-- fileopen(<filename>) +-- Open <filename> for reading, returning a filestate table on success or +-- nil on failure. +function fileopen(fnam) + local handle + if fnam ~= "-" then + local msg,errno + handle,msg,errno = io.open(fnam, "r") + if not handle then + io.stderr:write("songidx: Unable to open ",fnam," for reading.\n", + "Error ",errno,": ",msg,"\n") + return nil + end + else + handle = io.stdin + fnam = "stdin" + end + return { f=handle, filename=fnam, lineno=0 } +end + +-- filereadln(<fstate>) +-- Read a line of input, returning the string or nil if at file-end. +function filereadln(fs) + fs["lineno"] = fs["lineno"] + 1 + local s = fs["f"]:read() + if not s then return nil end + return s +end + +-- closeout(<handle>) +-- Close an output file <handle>. +function closeout(handle) + if handle == io.stdout then handle:flush() + else handle:close() end +end + +-- errorout(<handle>) +-- Respond to a write failure to <handle>. +function errorout(handle) + closeout(handle) + io.stderr:write("songidx: Error writing to output file. Aborting.\n") + return 2 + end + +-- fileclose(<fstate>) +-- Close file table <fstate>. +function fileclose(fs) + if (fs["f"] ~= io.stdin) then fs["f"]:close() end + fs["f"] = nil + fs["filename"] = nil + fs["lineno"] = 0 +end + +-- numprefix(<string>) +-- Find the longest prefix of <string> that represents a number (according to +-- the current locale) and contains no whitespace. Return the number and the +-- suffix of <string> not parsed. If there is no such prefix, return false +-- and the original <string>. +function numprefix(s) + local _, len = unicode.utf8.find(s,"^%S*") + for i=len, 1, -1 do + local n = tonumber(s:sub(1,i)) + if n then return n, s:sub(i+1) end + end + return false, s +end + +-- cleantitle(<song>) +-- Remove macros and braces from <song>'s title, and convert it to uppercase. +-- Macro-spaces ("\ ") are converted to regular spaces (" "). Cache the +-- result to avoid re-cleaning during sorting. +function cleantitle(s) + if not s["clean"] then + local t = s["title"]:gsub("\\[^%a%s]","") + :gsub("\\(%s)","%1") + :gsub("\\%a+%s*","") + :gsub("{%s*","") + :gsub("}","") + s["clean"] = unicode.utf8.upper(t) + end + return s["clean"] +end + +-- songcmp(<song1>,<song2>) +-- Return true if <song1> is less than <song2>, and false otherwise. The +-- ordering is first by title, then by index. This function is suitable for +-- use with table.sort(). +function songcmp(s1,s2) + local t1 = cleantitle(s1) + local t2 = cleantitle(s2) + + while true do + -- Find the next word or number in each string. + t1, t2 = unicode.utf8.match(t1,"%w.*"), unicode.utf8.match(t2,"%w.*") + + -- If there is no next word/number in both, sort by index. If there is + -- no next word/number in one but not the other, sort the shorter string + -- before the longer one. + if not t1 then + if not t2 then break end + return true + elseif not t2 then return false end + + -- If one is a number, sort the number before the word. If both are + -- numbers (and are not equal), then sort in numerical order. + local n1, n2 + n1, t1 = numprefix(t1) + n2, t2 = numprefix(t2) + if n1 then + if not n2 or n1 < n2 then return true end + if n1 > n2 then return false end + elseif n2 then return false + else + -- Otherwise, both are words. Lexicographically compare the words + -- according to the current locale's collation conventions. If the + -- locale considers them "equal" (i.e., w1<w2 is false but w1<=w2 is true) + -- then continue the loop to consider the next pair of words. + local w1, w2 = unicode.utf8.match(t1,"^[%a'`]*"), + unicode.utf8.match(t2,"^[%a'`]*") + local diff = w1 < w2 + if diff or not (w1 <= w2) then return diff end + t1, t2 = t1:sub(#w1+1), t2:sub(#w2+1) + end + end + + -- If all corresponding words/numbers are equal, then sort alternate-form + -- entries (e.g., lyrics) after normal entries (e.g., titles) + local alt1, alt2 = s1["title"]:sub(1,1), s2["title"]:sub(1,1) + if alt1 == "*" and alt2 ~= "*" then return false end + if alt1 ~= "*" and alt2 == "*" then return true end + + -- If everything is the same, sort by the right-hand sides of the index + -- entries (e.g., the song or page numbers). + return s1["idx"] < s2["idx"] +end + +-- setstartchars(<array>) +-- Try to find canonical "start characters" for each block of songs in a +-- sorted <array> of songs. Since Lua doesn't currently have any means of +-- imparting a locale's alphabet, we adopt the following strategy: Extract +-- the first unicode character from each title in the sorted list of titles +-- until reaching one that the locale's collation algorithm says is "bigger" +-- than the last. Find the "smallest" first character in that set using +-- NON-UNICODE lexicographic sort. This tends to be the most canonical one, +-- since unicode tends to put canonical (e.g., no-accent) glyphs at lower +-- code points than non-canonical (e.g., accented) ones. (Unfortunately, if +-- none of the titles in the block start with the desired canonical glyph, +-- there's no way to guess it; we just use the best one available.) +function setstartchars(songs) + local start = 1 + local best + + for i=1, #songs do + local c = unicode.utf8.match(cleantitle(songs[i]),"%w") + if c then + c = unicode.utf8.upper(c) + if not best then + songs[start]["newblk"], start, best = "\\#", i, c + elseif best < c then + songs[start]["newblk"], start, best = best, i, c + elseif #c < #best then best = c + elseif #c == #best then + for j=1,#c do + if c:byte(j) < best:byte(j) then best = c; break + elseif c:byte(j) > best:byte(j) then break end + end + end + elseif best then + songs[start]["newblk"], start, best = best, i, nil + end + end + + if start <= #songs then + if best then + songs[start]["newblk"] = best + else + songs[start]["newblk"] = "\\#" + end + end +end + +prelist = { A=true, THE=true } +wt_and = { AND=true } +wt_by = { BY=true } +wt_unknown = { UNKNOWN=true } + +-- rotate(<title>) +-- If the first word of <title> is any word in prelist, then shift that word +-- to the end of the string, preceded by a comma and a space. So for example, +-- if prelist contains "The", then rotate("The title") returns "Title, The". +-- Words in prelist are matched case-insensitively, and the new first word +-- becomes capitalized. If <title> begins with the marker character '*', +-- that character is ignored and left unchanged. +function rotate(s) + local t = unicode.utf8.upper(s) + local n = 0 + if s:sub(1,1) == "*" then n = 1 end + for pre in pairs(prelist) do + if t:sub(1+n,n+#pre) == pre and + unicode.utf8.find(t, "^%s+%S", n+#pre+1) then + local len = unicode.utf8.len(pre) + local x, y, z = unicode.utf8.match(unicode.utf8.sub(s,n+len+1),"^%s+(%W*)(%w?)(.*)$") + return s:sub(1,n) .. x .. unicode.utf8.upper(y) .. z:gsub("\\%s","%0\1"):match("^(.-)%s*$"):gsub("\1","") .. ",~" .. unicode.utf8.sub(s,1+n,n+len) + end + end + return s +end + +-- matchany(<string>,<init>,<wordtable>) +-- If a word in <wordtable> case-insensitively matches to <string> starting +-- at index <init>, and if the match concludes with whitespace, then return +-- the index of the whitespace; otherwise return <init>. +function matchany(s,init,wt) + local t = s:sub(init) + local u = unicode.utf8.upper(t) + for w,_ in pairs(wt) do + if u:sub(1,#w) == w and + (#w == #u or unicode.utf8.find(u, "^%s", #w+1)) then + return init + #(unicode.utf8.sub(t,1,unicode.utf8.len(w))) + end + end + return init +end + +-- issuffix(<string>,<init>) +-- Return true if the abbreviation "Jr" or a roman numeral (followed by +-- nothing, space, period, comma, or semicolon) appears at position <init> +-- within <string>. Return false otherwise. +function issuffix(s,i) + return unicode.utf8.find(s, "^Jr$", i) or + unicode.utf8.find(s, "^Jr[%s,;%.]", i) or + unicode.utf8.find(s, "^[IVX]+$", i) or + unicode.utf8.find(s, "^[IVX]+[%s,;%.]", i) +end + +-- grabauthor(<string>) +-- Return a string of the form "Sirname, Restofname" denoting the full name +-- of the first author found in <string>; or return nil if no author name +-- can be found. Also return an index to the suffix of <string> that was +-- not parsed, and just the "Sirname" part as a stand-alone string. +-- +-- Precondition: Caller must first sanitize <string> as follows: +-- <string>:gsub("[\1\2\3]","") +-- :gsub("\\\\","\\\1"):gsub("\\{","\\\2"):gsub("\\}","\\\3") +-- +-- Postcondition: Caller must then unsanitize the returned <string> with: +-- <string>:gsub("\1","\\"):gsub("\2","{"):gsub("\3","}") +-- +-- This is to allow grabauthor() to safely use Lua's %b pattern to find +-- balanced brace pairs without getting confused by escaped braces. +-- +-- Heuristics: +-- * Names are separated by punctuation (other than hyphens, periods, +-- apostrophes, or backslashes) or by the word "and" (or whatever words +-- are in wt_and). +-- Special case: If a comma is followed by the abbreviation "Jr" or by a +-- roman numeral, then the comma does NOT end the author's name. +-- * If a name contains the word "by" (or anything in wt_by), then +-- everything before it is not considered part of the name. (Let's hope +-- nobody is named "By".) +-- * The author's last name is always the last capitalized word in the +-- name unless the last capitalized word is "Jr." or a roman numeral. +-- In that case the author's last name is the penultimate captialized +-- word. +-- * If an author appears to have only a first name, or if the last name +-- found according to the above heuristics is an abbreviation (ending in +-- a period), look ahead in <string> until we find someone with a last +-- name and use that one. This allows us to identify the first author in +-- a string like "Joe, Billy E., and Bob Smith" to be "Joe Smith". +-- * If the resultant name contains the word "unknown" (or any word in +-- wt_unknown), it's probably not a real name. Recursively attempt +-- to parse the next author. +function grabauthor(authline,i) + i = unicode.utf8.find(authline, "[^%s,;]", i) + if not i then return nil end + i = unicode.utf8.find(authline, "%S", matchany(authline,i,wt_and)) + if not i then return nil end + i = unicode.utf8.find(authline, "%S", matchany(authline,i,wt_by)) + if not i then return nil end + local skip = (matchany(authline,i,wt_unknown) > i) + + -- Set "first" to the index of the start of the first name, "last" to the + -- index of the start of the sirname, "suffix" to the index of any suffix + -- like "Jr." or "III" (or nil if there is none), and i to the index of the + -- first character beyond the end of this author's name. + local first,last,suffix = i,nil,nil + while i <= #authline do + while true do + local j = select(2,authline:find("^\\%A", i)) or + select(2,authline:find("^\\%a+%s*", i)) or + select(2,authline:find("^%b{}", i)) + if not j then break end + i = j + 1 + end + if i > #authline then break + elseif authline:sub(i,i) == "," then + local j = unicode.utf8.find(authline, "%S", i+1) + if j and issuffix(authline,j) then i = i + 1 + else break end + elseif authline:sub(i,i) == ";" then break + elseif unicode.utf8.find(authline, "^%s", i) then + i = unicode.utf8.find(authline, "%S", i) + if not i then i = #authline + 1; break end + if matchany(authline, i, wt_and) > i then break end + skip = skip or (matchany(authline, i, wt_unknown) > i) + local j = matchany(authline, i, wt_by) + if j > i then + j = unicode.utf8.find(authline, "%S", j) + if not j then last = i; break end -- last name of "By"? + i,first,last,suffix = j,j,nil,nil + elseif issuffix(authline,i) then + suffix = i + elseif unicode.utf8.find(authline:sub(i):gsub("\\%A",""):gsub("\\%a+%s*",""),"^[%s{}'`\"]*%u") then + last,suffix = i,nil + end + else + i = select(2, unicode.utf8.find(authline, ".", i)) + 1 + end + end + + -- If an "unknown" word appeared, skip this entry and parse the next. + if skip then return grabauthor(authline,i) end + + -- Find the sirname. + local sirname, fullname + if last then + sirname = unicode.utf8.gsub(authline:sub(last,(suffix or i)-1), + "([^%s,;\\])[%s,;]+$", "%1") + end + if not sirname or unicode.utf8.find(sirname, "%a%.$") then + -- Here's where it gets tough. We either have a single-word name, or the + -- last name ends in a "." which means maybe it's just a middle initial or + -- other abbreviation. We could be dealing with a line like, "Billy, + -- Joe E., and Bob Smith", in which case we have to go searching for the + -- real last name. To handle this case, we will try a recursive call. + local _,_,r = grabauthor(authline,i) + if r or not sirname then + fullname = unicode.utf8.gsub(authline:sub(first,i-1), + "([^%s,;\\])[%s,;]+$", "%1") + if r then return (r .. ", " .. fullname), i, r + else return fullname, i, nil end + end + end + + -- Add the first name. + fullname = sirname .. ", " .. + unicode.utf8.gsub(authline:sub(first,(last or suffix or i)-1), + "([^%s,;\\])[%s,;]+$", "%1") + + -- Add the suffix, if any. + if suffix then + fullname = fullname .. " " .. + unicode.utf8.gsub(authline:sub(suffix,i-1), "([^%s,;\\])[%s,;]+$", "%1") + end + + return fullname, i, sirname +end + +-- genindex(<fstate>,<outname>) +-- Reads a title (if <authorindex>=false) or author (if <authorindex>=true) +-- index data file from file table <fstate> and generates a new file named +-- <outfile> containing a LaTeX title/author index. +-- Return Value: 0 on success, 1 on warnings, or 2 on failure +function genindex(fs,outname,authorindex) + local songs = {} + local seen = {} + local wt = { wt_sep, wt_after, wt_prefix, wt_ignore } + local typ = authorindex and "author" or "title" + + io.stderr:write("songidx: Parsing ",typ," index data file ",fs["filename"],"...\n") + + while true do + local buf = filereadln(fs) + if not buf then break end + if buf:sub(1,1) == "%" then + local j = buf:match("^()%%sep ") or + buf:match("^%%()after ") or + buf:match("^%%p()refix ") or + buf:match("^%%ig()nore ") + if j then + if not seen[j] then wt[j], seen[j] = {}, true end + wt[j][unicode.utf8.upper(buf:sub(buf:find(" ")+1))] = true + end + else + local snum = filereadln(fs) + if not snum then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": incomplete song entry (orphan ",typ,")\n") + return 2 + end + local link = filereadln(fs) + if not link then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": incomplete song entry (missing hyperlink)\n") + return 2 + end + if authorindex then + local i,a = 1 + buf = buf:gsub("[\1\2\3]",""):gsub("\\\\","\\\1") + :gsub("\\{","\\\2"):gsub("\\}","\\\3") + while true do + a,i = grabauthor(buf, i) + if not a then break end + a = a:gsub("\1","\\"):gsub("\2","{"):gsub("\3","}") + table.insert(songs, {title=a, num=snum, linkname=link, idx=#songs}) + end + else + buf = rotate(unicode.utf8.gsub(unicode.utf8.gsub(buf,"([^%s\\])%s+$","%1"),"^(%*?)%s+","%1")) + table.insert(songs, {title=buf, num=snum, linkname=link, idx=#songs}) + end + end + end + fileclose(fs) + + -- Sort the song table. + table.sort(songs, songcmp) + -- Find the index blocks. + setstartchars(songs) + + -- Write the sorted data out to the output file. + io.stderr:write("songidx: Generating ",typ," index TeX file ",outname,"...\n") + local f,msg,errno + if outname == "-" then + f, outname = io.stdout, "stdout" + else + f,msg,errno = io.open(outname, "w") + if not f then + io.stderr:write("songidx: Unable to open ",outname," for writing.\n", + "Error ",errno,": ",msg,"\n") + return 2 + end + end + for i=1, #songs do + if i>1 and songs[i]["title"] == songs[i-1]["title"] then + if not f:write("\\\\\\songlink{",songs[i]["linkname"],"}{",songs[i]["num"],"}") then return errorout(f) end + else + if songs[i]["newblk"] then + if i>1 then + if not f:write("}\n\\end{idxblock}\n") then return errorout(f) end + end + if not f:write("\\begin{idxblock}{",songs[i]["newblk"]) then return errorout(f) end + end + if songs[i]["title"]:find("^%*") then + if not f:write("}\n\\idxaltentry{",songs[i]["title"]:sub(2)) then return errorout(f) end + else + if not f:write("}\n\\idxentry{",songs[i]["title"]) then return errorout(f) end + end + if not f:write("}{\\songlink{",songs[i]["linkname"],"}{",songs[i]["num"],"}") then return errorout(f) end + end + end + if #songs > 0 then + if not f:write("}\n\\end{idxblock}\n") then return errorout(f) end + end + + return 0 +end + +bible = {} +chapX = 0 + +-- readbible(<filename>) +-- Read bible data file <filename> into the bible table. Return nil on error +-- or true on success. +function readbible(filename) + local fs = fileopen(filename) + if not fs then return nil end + bible = {} + + while true do + local buf = filereadln(fs) + if not buf then break end + if buf:sub(1,1) ~= "#" and buf:find("%S") then + local t, vbuf = { name = buf:match("^[^|]*"), + aliases = "|" .. unicode.utf8.upper(buf) .. "|" } + repeat + vbuf = filereadln(fs) + if not vbuf then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": incomplete bible book entry (book title with no verses)\n") + fileclose(fs) + return nil + end + until vbuf:sub(1,1) ~= "#" and buf:find("%S") + if vbuf:find("[^%d%s]") then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": verse count includes a non-digit\n") + fileclose(fs) + return nil + end + for n in vbuf:gmatch("%d+") do + local i = tonumber(n) + if not i then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": invalid number ",n,"\n") + fileclose(fs) + return nil + end + i = math.floor(i) + if chapX < i+1 then chapX = i+1 end + table.insert(t, i) + end + table.insert(bible, t) + end + end + + fileclose(fs) + return true +end + +-- parseref(<string>,<init>,<book>,<chapter>) +-- Interpret the characters starting at index <init> of <string> as a +-- scripture reference, and return four values: (1) the index of the first +-- character after <init> not parsed, (2) the book number parsed, (3) the +-- chapter number parsed (or 1 if the book has only verses), and (4) the +-- verse number parsed. Arguments <book> and <chapter> are the PREVIOUS book +-- number and chapter parsed, or -1 if none. If book or chapter information +-- is missing from <string>, they will be drawn from <book> and <chapter>. +-- That way, successive calls can correctly parse a run-on string like +-- "Philippians 3:1,5; 4:3", infering that "5" refers to "Philippians 3" and +-- "4:3" refers to "Philippians". If the parser encounters an error in +-- processing the book name (e.g., a book name was specified but not +-- recognized), then #bible+1 is returned for the book. If no chapter or no +-- verse is provided (e.g., the reference is just "Philippians" or +-- "Philippians 3") then the chapter and/or verse are returned as -1. +function parseref(s,i,book,ch) + local v = -1 + i = unicode.utf8.find(s,"%S",i) + if not i then return nil end + local j = unicode.utf8.find(s,"[%d:]*%s*[,;%-]",i) or + unicode.utf8.find(s,"[%d:]*%s*$",i) + local bookname = "|" .. unicode.utf8.upper(unicode.utf8.match(s:sub(i,j-1), "(.-)%s*$")) .. "|" + i = j + if bookname ~= "||" then + book, ch = #bible+1, -1 + for b,t in pairs(bible) do + if t["aliases"]:find(bookname, 1, true) then book = b; break end + end + end + j = unicode.utf8.find(s,"%D",i) or (#s+1) + if s:sub(j,j) == ":" then + ch, i = (tonumber(s:sub(i,j-1)) or -1), j+1 + j = unicode.utf8.find(s,"%D",i) or (#s+1) + end + if ch<=0 and book>0 and #bible[book] == 1 then + -- Special case: This book has only one chapter. + ch = 1 + end + if ch <= 0 then + ch = tonumber(s:sub(i,j-1)) or -1 + else + v = tonumber(s:sub(i,j-1)) or -1 + end + i = unicode.utf8.find(s,"%S",j) + if not i then i = #s+1 + elseif not s:find("^[,;%-]",i) then return nil end + return i, book, ch, v +end + +-- vlt(<chapter1>,<verse1>,<chapter2>,<verse2>) +-- Return true if <chapter1>:<verse1> precedes <chapter2>:<verse2> and false +-- otherwise. +function vlt(ch1,v1,ch2,v2) + return ch1 < ch2 or (ch1 == ch2 and v1 < v2) +end + +-- vinc(<book>,<chapter>,<verse>) +-- Return the chapter,verse pair of the verse immediately following +-- <chapter>:<verse> in <book>. If <chapter>:<verse> is the last verse in +-- <book>, the returned chapter will not exist in <book>. +function vinc(book,ch,v) + if v < bible[book][ch] then return ch, v+1 end + return ch+1, 1 +end + +-- vdec(<book>,<chapter>,<verse>) +-- Return the chapter,verse pair of the verse immediately preceding +-- <chapter>:<verse> in <book>. If <chapter> and <verse> are both 1, +-- then 0,nil will be returned. +function vdec(book,ch,v) + if v > 1 then return ch, v-1 end + return ch-1, bible[book][ch-1] +end + +-- unpack_cv(<cv>) +-- Decompose a chapter-verse key (computed as cv*chapX+v) into the original +-- chapter and verse numbers. +function unpack_cv(cv) + local v = cv % chapX + return (cv-v)/chapX, v +end + +-- eqdom(<table1>,<table2>) +-- Return true if two tables have identical domains; false otherwise. +function eqdom(t1,t2) + for k,_ in pairs(t1) do if not t2[k] then return false end end + for k,_ in pairs(t2) do if not t1[k] then return false end end + return true +end + +-- insertref(<is_add>,<changeset>,<chapter>,<verse>,<song>,<link>,<key>) +-- Insert song <song>,<link>,<key> into the set of "adds" (if <is_add>=true) +-- or "drops" (if <is_add>=false) for verse <chapter>:<verse> in <changeset>. +-- A <changeset> is a table that maps cv's to {adds=<refset>, drops=<refset>} +-- tables, where cv's are chapter-verse pairs encoded as chapter*chapX+verse. +-- Each such entry denotes a verse where the set of songs that reference it +-- changes. The "adds" field lists the songs that refer to this verse but +-- not the previous one. The "drops" field lists the songs that refer to +-- this verse but not the next. This formulation allows us to efficiently +-- represent range-references (e.g., "Psalms 1:1-8") without creating a +-- separate table entry for each verse in the range. +-- Create a new entry for <chapter>:<verse> in <changeset> if it doesn't +-- already exist. Return the new <changeset>. +function insertref(is_add,set,ch,v,n,l,k) + if not set then set = {} end + local cv = ch*chapX+v + if not set[cv] then set[cv] = { adds={}, drops={} } end + set[cv][is_add and "adds" or "drops"][k] = { num=n, link=l } + return set +end + +-- print_vrange(<file>,<book>,<ch1>,<v1>,<ch2>,<v2>,<lastchapter>) +-- Output LaTeX material to file <file> for verse range <ch1>:<v1>--<ch2>:<v2> +-- of book number <book>. Depending on <lastchapter>, the outputted material +-- might be the start of a new index entry or the continuation of a previous +-- entry. If <lastchapter> is positive, continue the previous entry and +-- print the chapter of <ch1>:<v1> only if it differs from <lastchapter>. If +-- <lastchapter> is negative, continue the previous entry and always print +-- the chapter number of <ch1>:<v1>. +function print_vrange(f,b,ch1,v1,ch2,v2,lch) + local r = f:write(lch == 0 and "\\idxentry{" or ",") + + if v1 <= 0 then + if lch ~= 0 then r = r and f:write("\\thinspace ") end + r = r and f:write(ch1) + elseif 0 <= b and b < #bible and #bible[b] == 1 then + -- This book has only one chapter. + if lch ~= 0 then r = r and f:write("\\thinspace ") end + r = r and f:write(v1) + elseif lch <= 0 or lch ~= ch1 or ch1 ~= ch2 then + if lch ~= 0 then r = r and f:write(" ") end + r = r and f:write(ch1,":",v1) + else + if lch ~= 0 then r = r and f:write("\\thinspace ") end + r = r and f:write(v1) + end + + if vlt(ch1,v1,ch2,v2) then + if v2 <= 0 then + r = r and f:write("--",ch2) + elseif ch1 ~= ch2 then + r = r and f:write("--",ch2,":",v2) + else + r = r and f:write("--",v2) + end + end + + return r +end + +-- print_reflist(<file>,<refset>) +-- Output the list of song references given by <refset> in sorted order +-- to file <file>. +function print_reflist(f,t) + local r = true + local s = {} + for k,_ in pairs(t) do table.insert(s,k) end + table.sort(s) + + local first = true + for _,k in ipairs(s) do + if first then first = false else r = r and f:write("\\\\") end + r = r and f:write("\\songlink{",t[k]["link"],"}{",t[k]["num"],"}") + end + return r +end + +function debug_print_reflist(r) + local first = true + io.stderr:write("{") + for k,_ in pairs(r) do + if first then first=false else io.stderr:write(",") end + io.stderr:write(k) + end + io.stderr:write("}") +end + +function debug_print_changeset(x) + if not x then io.stderr:write("nil") else + for cv,r in pairs(x) do + local ch,v = unpack_cv(cv) + io.stderr:write("{",ch,":",v," --> adds=") + debug_print_reflist(r["adds"]) + io.stderr:write(", drops=") + debug_print_reflist(r["drops"]) + io.stderr:write("}") + end + end +end + +-- genscriptureindex(<fstate>,<outname>,<biblename>) +-- Generate a LaTeX file named <outname> containing material suitable to +-- typeset the scripture index data found in input file <fstate>. Input +-- bible data from an ascii file named <biblename>. Return 0 on success, +-- 1 if there were warnings, and 2 if there was a fatal error. +function genscriptureindex(fs,outname,biblename) + local hadwarnings = 0 + local idx = {} + + io.stderr:write("songidx: Parsing scripture index data file ",fs["filename"],"...\n") + + -- Read the bible data file into the bible array. + if not readbible(biblename) then return 2 end + + -- Walk through the input file and construct a <changeset> for each book + -- of the bible. Each changeset represents the set of verses in that book + -- referred to by songs in the song book. + local key = 0 + while true do + local ref = filereadln(fs) + if not ref then break end + local n = filereadln(fs) + if not n then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": incomplete song entry (orphan reference line)\n") + fileclose(fs) + return 2 + end + local l = filereadln(fs) + if not l then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": incomplete song entry (missing hyperlink)\n") + fileclose(fs) + return 2 + end + key = key + 1 + + local i = 1 + local book, ch1, v1, ch2, v2 = -1, -1, -1, -1, -1 + while i <= #ref do + i,book,ch1,v1 = parseref(ref,i,book,ch1) + ch2,v2 = ch1,v1 + if not i then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Malformed scripture reference for song ",n,". Ignoring it.\n") + hadwarnings = 1 + break + end + if book < 1 then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," doesn't include a book name. Ignoring it.\n") + hadwarnings = 1 + break + end + if book > #bible then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," references unknown book. Ignoring it.\n") + hadwarnings = 1 + break + end + if ch1 < 1 then ch1 = 1 end + if ch1 > #bible[book] then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," refers to ",bible[book]["name"]," ",ch1,", which doesn't exist. Ignoring it.\n") + hadwarnings = 1 + break + end + if v1 < 1 then v1 = 1 end + if v1 > bible[book][ch1] then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," refers to ",bible[book]["name"]," ",ch1,":",v1,", which doesn't exist. Ignoring it.\n") + hadwarnings = 1 + break + end + + if ref:sub(i,i) == "-" then + -- If the reference ends in a "-", it starts an explicit range. + -- Parse the next reference to find the range's end. + i = unicode.utf8.find(ref, "[^%s%-]", i) + if not i then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," has range with no limit. Ignoring it.\n") + hadwarnings = 1 + break + end + local book2 + i,book2,ch2,v2 = parseref(ref,i,book,ch1) + if not i then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Malformed scripture reference for song ",n,". Ignoring it.\n") + hadwarnings = 1 + break + end + if book2 ~= book then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," appears to span books! Ignoring it.\n") + hadwarnings = 1 + break + end + end + if ch2 < 1 then ch2 = #bible[book] end + if ch2 > #bible[book] then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," refers implicitly to ",bible[book]["name"]," ",ch2,", which doesn't exist. Ignoring it.\n") + hadwarnings = 1 + break + end + if v2 < 1 then v2 = bible[book][ch2] + elseif v2 > bible[book][ch2] then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," refers implicitly to chapter ",ch2," of ",bible[book]["name"],", which doesn't exist. Ignoring it.\n") + hadwarnings = 1 + break + end + if vlt(ch2,v2,ch1,v1) then + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": WARNING: Scripture reference for song ",n," contains backwards range ",bible[book]["name"]," ",ch1,":",v1,"-",ch2,":",v2,". Ignoring it.\n") + hadwarnings = 1 + break + end + if i < #ref then i = i + 1 end + + idx[book] = insertref(true, idx[book], ch1, v1, n, l, key) + idx[book] = insertref(false, idx[book], ch2, v2, n, l, key) + end + end + fileclose(fs) + + -- Now create the index .sbx file. + io.stderr:write("songidx: Generating scripture index TeX file ",outname,"...\n") + local f,msg,errno + if outname == "-" then + f, outname = io.stdout, "stdout" + else + f,msg,errno = io.open(outname, "w") + if not f then + io.stderr:write("songidx: Unable to open ",outname," for writing.\n", + "Error ",errno,": ",msg,"\n") + return 2 + end + end + + -- For each book of the bible the has songs that reference it, go through its + -- <changeset> and generate a sequence of index entries. Wherever possible, + -- compact adjacent entries that have identical <refset>'s so that we never + -- have two consecutive index entries with identical right-hand sides. + for b=1,#bible do + local x = idx[b] + if x then + -- io.stderr:write("idx[",b,"] = ") + -- debug_print_changeset(x) + local s, t = {}, {} + for cv,_ in pairs(x) do table.insert(s,cv) end + table.sort(s) + local lch = 0 -- 0=none, -1=force printing of chapter + local cv1 = s[1] + local ch1,v1 = unpack_cv(cv1) + if not f:write("\\begin{idxblock}{",bible[b]["name"],"}\n") then return errorout(f) end + for i,cv in ipairs(s) do + local this = x[cv] + local ch,v = unpack_cv(cv) + local ncv,nxt,nch,nv = s[i+1] + if ncv then + nxt,nch,nv = x[ncv], unpack_cv(ncv) + end + for k,r in pairs(this["adds"]) do t[k] = r end + local skip = false + if ncv and eqdom(this["drops"], nxt["adds"]) then + -- Set of drops here equals set of adds next time. There's at least + -- a chance that we can combine this item and the next one into a + -- single index entry. + local ch2, v2 = vinc(b,ch,v) + if not vlt(ch2,v2, nch,nv) then + -- If the next item is adjacent to this one, do nothing. Just let + -- the range in progress be extended. We'll output a single entry + -- for all of these adjacent verses when we reach the end. + skip = true + elseif eqdom(t, this["drops"]) then + -- Otherwise, if the next item is not adjacent but all refs are + -- dropped here, then print a partial entry to be continued with a + -- comma next time. + if not print_vrange(f,b,ch1,v1,ch,v,lch) then return errorout(f) end + lch = (ch1 == ch) and ch or -1 + ch1, v1 = nch, nv + skip = true + end + end + if not skip then + if next(this["drops"]) then + -- Some songs get dropped here, and either the next item is not + -- adjacent to this one, or it's adjacent and the set of adds is not + -- the same. In either case, that means the set of refs changes at + -- this point, so we need to output a full entry (or finish the one + -- in progress). + if not (print_vrange(f,b,ch1,v1,ch,v,lch) and + f:write("}{") and + print_reflist(f,t) and + f:write("}\n")) then return errorout(f) end + for k,_ in pairs(this["drops"]) do t[k] = nil end + lch = 0 + if not next(t) and ncv then + ch1, v1 = nch, nv + else + ch1, v1 = vinc(b,ch,v) + end + end + if next(t) and ncv and next(nxt["adds"]) and vlt(ch1,v1,nch,nv) then + -- There are verses between this item and the next which have refs, + -- but the refs change at the beginning of the next item. Make an + -- entry for the intermediate block of verses. + local ch2, v2 = vdec(b,nch,nv) + if not (print_vrange(f,b,ch1,v1,ch2,v2,lch) and + f:write("}{") and + print_reflist(f,t) and + f:write("}\n")) then return errorout(f) end + lch, ch1, v1 = 0, nch, nv + end + end + end + if not f:write("\\end{idxblock}\n") then return errorout(f) end + end + end + + closeout(f) + return hadwarnings +end + +-- Main program entry point +function main() + local fs, biblename, inname, outname, locale + + local i = 1 + while arg[i] do + if arg[i] == "-v" or arg[i] == "--version" then + io.write("songidx ", VERSION, "\n", + "Copyright (C) 2017 Kevin W. Hamlen\n", + "License GPLv2: GNU GPL version 2 or later", + " <http://gnu.org/licenses/gpl.html>\n", + "This is free software: you are free to change and redistribute it.\n", + "There is NO WARRANTY, to the extent permitted by law.\n") + return 0 + elseif arg[i] == "-h" or arg[i] == "--help" then + io.write("Syntax: ",arg[-1]," ",arg[0]," [options] input.sxd [output.sbx]\n", +"Available options:\n", +" -b FILE Set the bible format when generating a scripture index\n", +" --bible FILE (default: ", BIBLEDEFAULT, ")\n", +"\n", +" -l LOCALE Override the default system locale (affecting how non-\n", +" --locale LOCALE English characters are sorted). See your system help\n", +" for valid LOCALEs.\n", +"\n", +" -h Display this help file and stop.\n", +" --help\n", +"\n", +" -v Print version information and stop.\n", +" --version\n", +"\n", +"If omitted, [output.sbx] defaults to the input filename but with the file\n", +"extension renamed to '.sbx'. To read or write to stdin or stdout, use '-'\n", +"in place of input.sxd or output.sbx.\n", +"\n", +"See http://songs.sourceforge.net for support.\n") + return 0 + elseif arg[i] == "-b" or arg[i] == "--bible" then + if biblename then + io.stderr:write("songidx: multiple bible files specified\n") + return 2 + end + i = i + 1 + if arg[i] then + biblename = arg[i] + else + io.stderr:write("songidx: ",arg[i-1]," option requires an argument\n") + return 2 + end + elseif arg[i] == "-l" or arg[i] == "--locale" then + if locale then + io.stderr:write("songidx: multiple locales specified\n") + return 2 + end + i = i + 1 + if arg[i] then + locale = arg[i] + else + io.stderr:write("songidx: ",arg[i-1]," requires an argument\n") + return 2 + end + elseif arg[i] == "-o" or arg[i] == "--output" then + if outname then + io.stderr:write("songidx: multiple output files specified\n") + return 2 + end + i = i + 1 + if arg[i] then + outname = arg[i] + else + io.stderr:write("songidx: ",arg[i-1]," option requires an argument\n") + return 2 + end + elseif arg[i]:sub(1,1) == "-" and arg[i] ~= "-" then + io.stderr:write("songidx: unknown option ",arg[i],"\n") + return 2 + elseif not inname then inname = arg[i] + elseif not outname then outname = arg[i] + else + io.stderr:write("songidx: too many command line arguments\n") + return 2 + end + i = i + 1 + end + + if not locale then + os.setlocale("") + elseif not os.setlocale(locale) then + io.stderr:write("songidx: invalid locale: ",locale,"\n") + return 2 + end + + if not inname then + io.stderr:write("songidx: no input file specified\n") + return 2 + end + if not outname then + if inname == "-" then + outname = "-" + else + local n + outname,n = inname:gsub("%.[^%./\\]*$", ".sbx") + if n == 0 then outname = inname .. ".sbx" end + end + end + if not biblename then biblename = BIBLEDEFAULT end + + fs = fileopen(inname) + if not fs then return 2 end + + local retval = 2 + local buf = filereadln(fs) + if not buf then + io.stderr:write("songidx:",fs["filename"],": file is empty\n") + fileclose(fs) + elseif buf == "TITLE INDEX DATA FILE" then + retval = genindex(fs,outname,false) + elseif buf == "SCRIPTURE INDEX DATA FILE" then + retval = genscriptureindex(fs,outname,biblename) + elseif buf == "AUTHOR INDEX DATA FILE" then + retval = genindex(fs,outname,true) + else + io.stderr:write("songidx:",fs["filename"],":",fs["lineno"],": file has unrecognized format\n") + fileclose(fs) + end + + if retval == 0 then + io.stderr:write("songidx: Done!\n") + elseif retval == 1 then + io.stderr:write("songidx: COMPLETED WITH ERRORS. SEE ABOVE.\n") + else + io.stderr:write("songidx: FAILED. SEE ERROR MESSAGES ABOVE.\n") + end + + return retval +end + +os.exit(main()) + diff --git a/Master/texmf-dist/doc/latex/songs/songidx/songsort.c b/Master/texmf-dist/doc/latex/songs/songidx/songsort.c deleted file mode 100644 index 22825733727..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/songsort.c +++ /dev/null @@ -1,162 +0,0 @@ -/* Copyright (C) 2012 Kevin W. Hamlen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - * - * The latest version of this program can be obtained from - * http://songs.sourceforge.net. - */ - - -#if HAVE_CONFIG_H -# include "config.h" -#else -# include "vsconfig.h" -#endif - -#include "chars.h" -#include "songidx.h" -#include "fileio.h" - -/* skipesc_sp(<ptr>,<flag>) - * Walk <ptr> past any LaTeX macros or braces until we reach the next "real" - * character. If <flag> is 1, then make an exception for space-macros. - * The space macro "\ " causes p to stop on the space. */ -void -skipesc_sp(p, stop_on_space) - const WCHAR **p; - int stop_on_space; -{ - for (;;) - { - if (**p == wc_backslash) - { - ++(*p); - if (stop_on_space && wc_isspace(**p)) return; - if (wc_isalpha(**p)) while (wc_isalpha(**p)) ++(*p); - else if (**p) ++(*p); - while (wc_isspace(**p)) ++(*p); - } - else if (**p == wc_lbrace) - { - ++(*p); - while (wc_isspace(**p)) ++(*p); - } - else if (**p == wc_rbrace) - ++(*p); - else - break; - } -} - -/* skipesc(<ptr>) - * Walk <ptr> past any LaTeX macros or braces until we reach the next "real" - * character. */ -void -skipesc(p) - const WCHAR **p; -{ - skipesc_sp(p,0); -} - -/* inword(<char>) - * Return 0 if <char> is a word-delimiter (for sorting purposes) and 1 - * otherwise. Other than TeX macros (which are handled by skipesc above), - * words only have alphabetics and apostrophes. */ -static int -inword(c) - WCHAR c; -{ - return (wc_isalpha(c) || (c==wc_apostrophe) || (c==wc_backquote)); -} - -/* songcmp(<song1>,<song2>) - * Return a negative number if <song1> is less than <song2>, a positive - * number if <song1> is greater than <song2>, and 0 if <song1> and <song2> - * are equal. The ordering is first by title, then by index. This function - * is suitable for use with qsort(). */ -int -songcmp(s1,s2) - const void *s1; - const void *s2; -{ - static WCHAR buf1[MAXLINELEN+1], *bp1; - static WCHAR buf2[MAXLINELEN+1], *bp2; - const WCHAR *t1 = (*((const SONGENTRY **) s1))->title; - const WCHAR *t2 = (*((const SONGENTRY **) s2))->title; - int diff; - - for (;;) - { - /* Find the next word or number in each string. */ - skipesc(&t1); - while(*t1 && !wc_isalpha(*t1) && !wc_isdigit(*t1)) - { - ++t1; - skipesc(&t1); - } - skipesc(&t2); - while(*t2 && !wc_isalpha(*t2) && !wc_isdigit(*t2)) - { - ++t2; - skipesc(&t2); - } - - /* If there is no next word/number in one or both, sort the shorter - * string before the longer one. */ - if ((*t1==wc_null) || (*t2==wc_null)) - { - if ((*t1==wc_null) && (*t2==wc_null)) break; - return (t1!=wc_null) ? 1 : -1; - } - - /* If one is a number, sort the number before the word. If both are - * numbers, sort in numerical order. */ - if (wc_isdigit(*t1) || wc_isdigit(*t2)) - { - long n1, n2; - WCHAR *p1, *p2; - if (!wc_isdigit(*t1)) return 1; - if (!wc_isdigit(*t2)) return -1; - n1 = ws_strtol(t1, &p1, 10); - t1 = p1; - n2 = ws_strtol(t2, &p2, 10); - t2 = p2; - if (n1 == n2) continue; - return n1-n2; - } - - /* Otherwise, both are words. Copy the words into scratch buffers, - * omitting any macros or braces. Then lexographically compare the buffer - * contents to determine how the original strings should be sorted. */ - for (bp1=buf1; inword(*t1); skipesc_sp(&t1,1)) *bp1++=wc_tolower(*t1++); - for (bp2=buf2; inword(*t2); skipesc_sp(&t2,1)) *bp2++=wc_tolower(*t2++); - *bp1 = *bp2 = wc_null; - if ((diff = ws_coll(buf1,buf2)) != 0) return diff; - } - - /* If each corresponding word/number is identical, then sort alternate- - * form entries (e.g., lyrics) after normal entries (e.g., titles). */ - if (((*((const SONGENTRY **) s1))->title[0] == wc_asterisk) - && ((*((const SONGENTRY **) s2))->title[0] != wc_asterisk)) - return 1; - if (((*((const SONGENTRY **) s1))->title[0] != wc_asterisk) - && ((*((const SONGENTRY **) s2))->title[0] == wc_asterisk)) - return -1; - - /* If everything is the same, sort by the right-hand sides of the index - * entries (e.g., the song or page numbers). */ - return (*((const SONGENTRY **) s1))->idx - (*((const SONGENTRY **) s2))->idx; -} diff --git a/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can b/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can index 6a3053c94eb..4e941a1fe1a 100644 --- a/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can +++ b/Master/texmf-dist/doc/latex/songs/songidx/tanakh.can @@ -1,4 +1,4 @@ -# Copyright (C) 2012 Kevin W. Hamlen +# Copyright (C) 2017 Kevin W. Hamlen # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/Master/texmf-dist/doc/latex/songs/songidx/titleidx.c b/Master/texmf-dist/doc/latex/songs/songidx/titleidx.c deleted file mode 100644 index cde08511a0c..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/titleidx.c +++ /dev/null @@ -1,379 +0,0 @@ -/* Copyright (C) 2012 Kevin W. Hamlen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - * - * The latest version of this program can be obtained from - * http://songs.sourceforge.net. - */ - - -/* Create a LaTeX title index file from a title .sxd file. */ - -#if HAVE_CONFIG_H -# include "config.h" -#else -# include "vsconfig.h" -#endif - -#include "chars.h" -#include "songidx.h" -#include "fileio.h" - -#if HAVE_STRING_H -# include <string.h> -#elif HAVE_STRINGS_H -# include <strings.h> -#endif - -/* By convention, some word prefixes always get moved to the end of a title - * before it is indexed. For example, in English "The Song Title" becomes - * "Song Title, The". The following structure stores the list of these prefix - * words. The default is "The" and "A". */ -typedef struct prefix -{ - struct prefix *next; - WCHAR *s; -} -PREFIX; - -PREFIX pre_A = { NULL, ws_lit("A") }; -PREFIX pre_defaults = { &pre_A, ws_lit("The") }; - -/* freeprefixes(<list>) - * Free prefix list <list>. */ -static void -freeprefixes(pl) - PREFIX *pl; -{ - if (pl == &pre_defaults) return; - while (pl) - { - PREFIX *nxt = pl->next; - free(pl->s); - free(pl); - pl = nxt; - } -} - -/* rotate(<title>, <prelist>) - * If the first word of <title> is any word in list <prelist>, then <title> - * is modified in-place so that the word is shifted to the end of the string - * and preceded by a comma and a space. So for example, rotate("The Title", - * <prelist>) where <prelist> is the default results in "Title, The". Words - * in <prelist> are matched case-insensitively. If <title> begins with the - * marker character '*', that character is ignored and left unchanged. Note - * that <title> MUST be allocated with ONE EXTRA WCHAR OF STORAGE in order - * to leave room for the extra comma. - * Return Value: 1 if <title> was modified and 0 otherwise. */ -static int -rotate(s,pl) - WCHAR *s; - PREFIX *pl; -{ - const WCHAR *s2, *w2; - - w2 = NULL; - if (*s==wc_asterisk) ++s; - for (; pl; pl=pl->next) - { - for (s2=s, w2=pl->s; *w2; ++s2, ++w2) - if (wc_tolower(*s2)!=wc_tolower(*w2)) break; - if ((*w2 == wc_null) && (*s2 != wc_null) && !wc_isalpha(*s2)) break; - } - if (pl) - { - WCHAR buf[MAXLINELEN]; - size_t slen = ws_strlen(s); - size_t wlen = w2 - pl->s; - ws_strncpy(buf, s, wlen); - buf[wlen] = wc_null; - ws_memmove(s, s+wlen+1, slen-wlen-1); - *s = wc_toupper(*s); - *(s+slen-wlen-1) = wc_comma; - *(s+slen-wlen) = wc_tilda; - ws_strcpy(s+slen-wlen+1, buf); - return 1; - } - return 0; -} - -/* getstartchar(<string>,<default>) - * Returns the first non-escaped, alphabetic character of <string>. If - * <string> has no non-escaped, alphabetic characters, <default> is returned - * instead. */ -static WCHAR -getstartchar(s,def) - const WCHAR *s; - WCHAR def; -{ - if (!s) return def; - skipesc(&s); - while ((*s!=wc_null) && !wc_isalpha(*s)) - { - ++s; - skipesc(&s); - } - return (*s!=wc_null) ? wc_toupper(*s) : def; -} - -/* freesongarray(<array>,<len>) - * Free song array <array>, which should have length <len>. */ -static void -freesongarray(songs,len) - SONGENTRY **songs; - int len; -{ - int i; - - if (!songs) return; - for (i=0; i<len; ++i) - { - if (songs[i]) - { - if (songs[i]->title) free(songs[i]->title); - if (songs[i]->num) free(songs[i]->num); - if (songs[i]->linkname) free(songs[i]->linkname); - free(songs[i]); - } - } - free(songs); -} - -/* gentitleindex(<file>,<outname>) - * Reads a title index data file from file handle <file> and generates a new - * file named <outfile> containing a LaTeX title index. - * Return Value: 0 on success, 1 on warnings, or 2 on failure */ -int -gentitleindex(fs,outname) - FSTATE *fs; - const char *outname; -{ - FILE *f; - int eof = 0; - int numsongs, arraysize, i; - PREFIX *prelist = &pre_defaults; - SONGENTRY **songs; - WCHAR buf[MAXLINELEN], *bp; - WCHAR startchar; - - fprintf(stderr, "songidx: Parsing title index data file %s...\n", - fs->filename); - - songs = NULL; - eof = 0; - for (arraysize=numsongs=0; !eof; ++numsongs) - { - if (!filereadln(fs,buf,&eof)) - { - freesongarray(songs,numsongs); - return 2; - } - if (eof) break; - if (buf[0] == wc_percent) - { - if (!ws_strncmp(buf, wc_lit("%prefix "), 8)) - { - PREFIX *temp = (prelist==&pre_defaults) ? NULL : prelist; - prelist = (PREFIX *) malloc(sizeof(PREFIX)); - prelist->next = temp; - prelist->s = (WCHAR *) calloc(ws_strlen(buf)-7, sizeof(WCHAR)); - ws_strcpy(prelist->s, buf+8); - } - --numsongs; - continue; - } - if (numsongs >= arraysize) - { - SONGENTRY **temp; - arraysize *= 2; - if (arraysize==0) arraysize=64; - temp = (SONGENTRY **) realloc(songs,arraysize*sizeof(SONGENTRY *)); - if (!temp) - { - fprintf(stderr, "songidx:%s:%d: too many songs (out of memory)\n", - fs->filename, fs->lineno); - freeprefixes(prelist); - freesongarray(songs,numsongs); - return 2; - } - songs = temp; - } - if ((songs[numsongs] = (SONGENTRY *) malloc(sizeof(SONGENTRY))) == NULL) - { - fprintf(stderr, "songidx:%s:%d: too many songs (out of memory)\n", - fs->filename, fs->lineno); - freeprefixes(prelist); - freesongarray(songs,numsongs); - return 2; - } - songs[numsongs]->title = songs[numsongs]->num = - songs[numsongs]->linkname = NULL; - for (bp=buf+ws_strlen(buf)-1; (bp > buf) && wc_isspace(*bp); --bp) ; - if (wc_isspace(*bp)) *bp = wc_null; - for (bp=buf; wc_isspace(*bp); ++bp) ; - /* The following allocates one extra char of storage because we might add - * a comma later. */ - if ((songs[numsongs]->title = - (WCHAR *) calloc(ws_strlen(bp)+2, sizeof(WCHAR))) == NULL) - { - fprintf(stderr, "songidx:%s:%d: too many songs (out of memory)\n", - fs->filename, fs->lineno); - freeprefixes(prelist); - freesongarray(songs,numsongs+1); - return 2; - } - ws_strcpy(songs[numsongs]->title, bp); - rotate(songs[numsongs]->title, prelist); - if (!filereadln(fs,buf,&eof)) - { - freeprefixes(prelist); - freesongarray(songs,numsongs+1); - return 2; - } - if (eof) - { - fprintf(stderr, "songidx:%s:%d: incomplete song entry (orphan title)\n", - fs->filename, fs->lineno); - freeprefixes(prelist); - freesongarray(songs,numsongs+1); - return 2; - } - if ((songs[numsongs]->num = - (WCHAR *) calloc(ws_strlen(buf)+1, sizeof(WCHAR))) == NULL) - { - fprintf(stderr, "songidx:%s:%d: too many songs (out of memory)\n", - fs->filename, fs->lineno); - freeprefixes(prelist); - freesongarray(songs,numsongs+1); - return 2; - } - ws_strcpy(songs[numsongs]->num, buf); - if (!filereadln(fs,buf,&eof)) - { - freeprefixes(prelist); - freesongarray(songs,numsongs+1); - return 2; - } - if (eof) - { - fprintf(stderr, "songidx:%s:%d: incomplete song entry" - " (missing hyperlink)\n", fs->filename, fs->lineno); - freeprefixes(prelist); - freesongarray(songs,numsongs+1); - return 2; - } - if ((songs[numsongs]->linkname = - (WCHAR *) calloc(ws_strlen(buf)+1, sizeof(WCHAR))) == NULL) - { - fprintf(stderr, "songidx:%s:%d: too many songs (out of memory)\n", - fs->filename, fs->lineno); - freeprefixes(prelist); - freesongarray(songs,numsongs); - return 2; - } - ws_strcpy(songs[numsongs]->linkname, buf); - songs[numsongs]->idx = numsongs; - } - fileclose(fs); - freeprefixes(prelist); - - /* Sort the song array */ - qsort(songs, numsongs, sizeof(*songs), songcmp); - - /* Write the sorted data out to the output file. */ - fprintf(stderr, "songidx: Generating title index TeX file %s...\n", outname); - if (strcmp(outname,"-")) - { - if ((f = fopen(outname, "w")) == NULL) - { - fprintf(stderr, "songidx: Unable to open %s for writing.\n", outname); - return 2; - } - } - else - { - f = stdout; - outname = "stdout"; - } - -#define TRYWRITE(x) \ - if (!(x)) \ - { \ - fprintf(stderr, "songidx:%s: write error\n", outname); \ - if (f == stdout) fflush(f); else fclose(f); \ - freesongarray(songs,numsongs); \ - return 2; \ - } - - startchar = 'A'; - if (numsongs>0) - { - startchar = getstartchar(songs[0]->title, wc_capA); - TRYWRITE((ws_fputs(ws_lit("\\begin{idxblock}{"), f) >= 0) && - (ws_fputc(startchar, f)==startchar)) - } - for (i=0; i<numsongs; ++i) - { - WCHAR c = getstartchar(songs[i]->title, startchar); - if ((i>0) && !ws_coll(songs[i]->title, songs[i-1]->title)) - { - TRYWRITE((ws_fputs(ws_lit("\\\\\\hyperlink{"), f) >= 0) && - (ws_fputs(songs[i]->linkname, f) >= 0) && - (ws_fputs(ws_lit("}{"), f) >= 0) && - (ws_fputs(songs[i]->num, f) >= 0) && - (ws_fputs(ws_lit("}"), f) >= 0)) - continue; - } - else - { - TRYWRITE(ws_fputs(ws_lit("}\n"), f) >= 0) - } - if (startchar != c) - { - startchar = c; - TRYWRITE((ws_fputs(ws_lit("\\end{idxblock}\n\\begin{idxblock}{"), f) >= 0) - && (ws_fputc(startchar, f)==startchar) - && (ws_fputs(ws_lit("}\n"), f) >= 0)) - } - if (songs[i]->title[0] == wc_asterisk) - { - TRYWRITE((ws_fputs(ws_lit("\\idxaltentry{"), f) >= 0) && - (ws_fputs(songs[i]->title+1, f) >= 0) && - (ws_fputs(ws_lit("}{\\hyperlink{"), f) >= 0) && - (ws_fputs(songs[i]->linkname, f) >= 0) && - (ws_fputs(ws_lit("}{"), f) >= 0) && - (ws_fputs(songs[i]->num, f) >= 0) && - (ws_fputs(ws_lit("}"), f) >= 0)) - } - else - { - TRYWRITE((ws_fputs(ws_lit("\\idxentry{"), f) >= 0) && - (ws_fputs(songs[i]->title, f) >= 0) && - (ws_fputs(ws_lit("}{\\hyperlink{"), f) >= 0) && - (ws_fputs(songs[i]->linkname, f) >= 0) && - (ws_fputs(ws_lit("}{"), f) >= 0) && - (ws_fputs(songs[i]->num, f) >= 0) && - (ws_fputs(ws_lit("}"), f) >= 0)) - } - } - TRYWRITE(ws_fputs(ws_lit("}\n\\end{idxblock}\n"), f) >= 0) - -#undef TRYWRITE - - if (f == stdout) fflush(f); else fclose(f); - freesongarray(songs,numsongs); - return 0; -} diff --git a/Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h b/Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h deleted file mode 100644 index f8793af2fa4..00000000000 --- a/Master/texmf-dist/doc/latex/songs/songidx/vsconfig.h +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright (C) 2012 Kevin W. Hamlen - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - * - * The latest version of this program can be obtained from - * http://songs.sourceforge.net. - */ - -/* This configuration file should work as-is for any system that has standard - * C libraries. It has been tested successfully on Visual Studio and Linux. - * If your system is non-standard, follow the directions below to customize it - * for your system. */ - -#ifndef CONFIG_H -#define CONFIG_H - - -/* Define to 1 if you have the <inttypes.h> header file. */ -/* #undef HAVE_INTTYPES_H */ - -/* Define to 1 if you have the <locale.h> header file. */ -#define HAVE_LOCALE_H 1 - -/* Define to 1 if you have the <memory.h> header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if your system has a GNU libc compatible `realloc' function, - and to 0 otherwise. */ -#define HAVE_REALLOC 1 - -/* Define to 1 if you have the `setlocale' function. */ -#define HAVE_SETLOCALE 1 - -/* Define to 1 if you have the <stdint.h> header file. */ -/* #undef HAVE_STDINT_H */ - -/* Define to 1 if you have the <stdlib.h> header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the <stdio.h> header file. */ -#define HAVE_STDIO_H 1 - -/* Define to 1 if you have the <strings.h> header file. */ -/* #undef HAVE_STRINGS_H */ - -/* Define to 1 if you have the <string.h> header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the `strrchr' function. */ -#define HAVE_STRRCHR 1 - -/* Define to 1 if you have the <sys/stat.h> header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the <sys/types.h> header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the <unistd.h> header file. */ -/* #undef HAVE_UNISTD_H */ - -/* Define to 1 if you have the <wchar.h> header file. */ -#define HAVE_WCHAR_H 1 - -/* Define to 1 if you have the wchar_t type. */ -#define HAVE_WCHAR_T 1 - -/* Define to 1 if you have the <wctype.h> header file. */ -#define HAVE_WCTYPE_H 1 - -/* Name of package */ -#define PACKAGE "songs" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "songs" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "songs 2.14" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "songs" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "2.14" - -/* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Version number of package */ -#define VERSION "2.14" - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ - -/* Define to rpl_realloc if the replacement function should be used. */ -/* #undef realloc */ - -/* Define to `unsigned int' if <sys/types.h> does not define. */ -/* #undef size_t */ - -#endif |