diff options
Diffstat (limited to 'Master/texmf-dist/doc/latex/songs/songidx/titleidx.c')
-rw-r--r-- | Master/texmf-dist/doc/latex/songs/songidx/titleidx.c | 379 |
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; +} |