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