/* Copyright (C) 2009 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. */ #ifdef HAVE_CONFIG_H # include "config.h" #else # include "vsconfig.h" #endif #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STDIO_H # include #endif #ifdef HAVE_STRING_H # include #else # ifdef HAVE_STRINGS_H # include # endif #endif #include "chars.h" #include "songidx.h" #include "fileio.h" /* 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) * Free song list . */ 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() * Free verse list . */ 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(,) * Free the array of verselists given by . The array should have length . */ static void freeidxarray(idx,len) VERSELIST **idx; int len; { int i; if (!idx) return; for (i=0; i) * Read bible data file 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,) * 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(,,) * Interpret as a scripture reference, and store the NUMBER of the book of the Bible * it refers to in , and the chapter and verse that it refers to in . Return * a pointer into to the suffix of that was not parsed. * and 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), 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 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; *bookchapter = 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) && (*bookchapter = 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(,) * Return 1 if verse precedes 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(,) * Copy the data from verse into verse . */ static void vcpy(vdest,vsrc) VERSE *vdest; const VERSE vsrc; { vdest->chapter = vsrc.chapter; vdest->verse = vsrc.verse; } /* getvcount(,) * Return the number of verses in chapter of book number 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(,) * Return a reference to the verse immediately following in book number * of the Bible. The verse returned is static, so successive calls will overwrite it. * If is the last verse in , 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(,) * Return a reference to the verse immediately preceding in book number * of the Bible. The verse returned is static, so successive calls will overwrite it. * If is the first verse in , 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(,,,) * Add a reference to song (,,) to REFLIST . is * assumed to be sorted by . If a reference with the same already exists, * is left unchanged. Both and 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(,,,,,) * If is INSERT_ADD, then insert song (,,) into the set of "adds" for * verse in . If is INSERT_DROP, then insert the song into the set * of "drops" for verse . In either case, create a new entry for in * 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(,) * Take the union of SONGLIST and SONGLIST , storing the result in . * 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(,) * Take the set difference between SONGLIST and SONGLIST , storing the * result in . */ 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(,) * Compare SONGLIST's and , 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(,,,,) * Output LaTeX material to file handle for verse range - * of book number . Depending on , the outputted material * might be the start of a new index entry or the continuation of a previous * entry. If is 0, start a new index entry. If is >0, * continue the previous entry and print the chapter of only if it differs * from . If is <0, continue the previous entry and * always print the chapter number of . 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,) * Output the list of song references given by to file handle . * 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(,,) * Generate a LaTeX file named containing material suitable to typeset * the scripture index data found in input file handle . Input Bible data * from an ascii file named . * 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; bookfilename, 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=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; }