/* windex.c
** Author: Ronald L. Rivest
** Helpful modifications from Carl Manning, Ron Greenberg.
** Documentation: see windex.doc
** Version: 1/26/90
** Basic functionality:
** (1) input the file windex.tex into your tex source
** (2) put index entries into your Tex document (e.g. foo.tex)
** These are written out into file foo.idx by Tex.
** (3) Run `windex foo' to compile index.
** Output is now in file foo.index
** (4) Include in foo.tex the line
** \makeatletter\@input{book.index}\makeatother
** where you want the index to go.
*/
/* Miscellaneous constants */
#define TRUE 1
#define FALSE 0
#define DEBUG 0
#define CR '\r'
#define LF '\n'
#define EOS 0 /* end of string */
/* WINDEX constants */
/* String constants */
#define WINDEX_BANNER "WINDEX index-creation program. Version 1/26/90.\n"
#define INPUT_EXTENSION ".idx"
#define OUTPUT_EXTENSION ".index"
#define MAX_STRING_LENGTH 300
#define DEFAULT_FORMAT "0" /* just print the page number */
char NULL_FORMAT[] = ""; /* null format string */
#define NULL_SORT_CHARS "-" /* nonblank characters to ignore when sorting */
#define UNKNOWN -1
/* Character constants: changing these constants changes syntax of windex */
#define SLASH '/' /* separates levels in sort key */
#define LANGLE '<' /* marks entry to start range */
#define RANGLE '>' /* marks entry to end range */
#define LBRACE '{' /* marks start of print value */
#define RBRACE '}' /* marks end of print value */
#define LBRACKET '[' /* marks start of format string */
#define RBRACKET ']' /* marks end of format string */
#define ZERO '0' /* marks position of page or range in format */
#define COLON ':' /* marks beginning of page number */
#define BLANK ' ' /* whitespace to ignore at beginning */
/* Windex policy switches */
#define MAKE_RANGES TRUE /* create ranges for adjacent page entries */
#define IGNORE_SMALL_WORDS TRUE /* ignore initial small words in subitems */
/* when sorting them into order */
/* TEX constants */
#define TEX_BEGIN_GROUP '{'
#define TEX_END_GROUP '}'
#define TEX_EMPTY_GROUP "{}"
#define TEX_BEGIN_INDEX "\\begin{theindex}\n"
#define TEX_END_INDEX "\n\n\\end{theindex}\n"
#define TEX_MAX_LEVEL 2
#define TEX_BEGIN_LEVEL0 "\n\\item "
#define TEX_BEGIN_LEVEL1 "\n \\subitem "
#define TEX_BEGIN_LEVEL2 "\n \\subsubitem "
#define TEX_AFTER_ENTRY ", " /* string to print after print value */
#define TEX_BETWEEN_PAGES ", " /* string to print between page entries */
#define TEX_BETWEEN_RANGE "--"
/* end of TEX constants */
FILE *instream; /* lines as produced by \index commands */
FILE *outstream; /* actual latex commands to make index */
typedef /* for a page or a range of pages */
struct pageref {
int level; /* 0...TEX_MAX_LEVEL */
char *sortkey[TEX_MAX_LEVEL+1]; /* values to sort/compare keys by */
char *pv; /* print value or else NULL */
char *format; /* format or replacement for page reference */
int firstpageno; /* first page number in range or UNKNOWN */
int lastpageno; /* last page number in range or UNKNOWN */
struct pageref *next; /* link to next page reference */
} entry;
entry elist;
entry *elast;
/* mymalloc(n)
** allocate n bytes but check for out of memory condition
*/
char *mymalloc(n)
int n;
{ char *p = malloc((unsigned)n);
if (p == NULL)
{ printf("\nERROR: Windex ran out of memory.\n");
exit(0);
}
return(p);
}
/* mystrdup(s)
** copy string s but check for out of memory condition
*/
char *mystrdup(s)
char *s;
{ char *p = mymalloc(strlen(s)+1);
strcpy(p,s);
return(p);
}
/* uppercase(c)
** convert character to uppercase
*/
char uppercase(c)
char c;
{ if (islower(c)) return(toupper(c));
return(c);
}
/* printspage(s)
** TRUE if s is a format string that contains a spot to print a pageno
*/
printspage(s)
char *s;
{ if (*s == EOS) return(FALSE);
if (*s == ZERO) return(TRUE);
return(printspage(s+1));
}
/* sortpageno(e)
** return page to sort entry e by
*/
sortpageno(e)
entry *e;
{ if (e->firstpageno != UNKNOWN) return(e->firstpageno);
return(e->lastpageno);
}
/* entrylesseq(e1,e2,format_first)
** Returns TRUE if entry e1 should come before (or is equal to) entry e2
** in the sorted list of entries. This is the basic comparison operation
** for sorting the entries into order.
** if format_first == TRUE then order by key, format, then page
** if format_first == FALSE then order by key, page, then format
*/
entrylesseq(e1,e2,format_first)
entry *e1,*e2;
int format_first;
{ int i,r;
/* if e1 doesn't exist, then e2 comes first */
if (e1 == NULL) return(FALSE);
/* if e2 doesn't exist, then e1 comes first */
if (e2 == NULL) return(TRUE);
/* compare sort keys and return TRUE if e1 sorts first,
** FALSE if e2 sorts first.
*/
for (i=0;i<=e1->level && i<=e2->level;i++)
{ r = keycmp(i,e1,e2);
if (r<0) return(TRUE);
if (r>0) return(FALSE);
/* level i keys are the same */
if (e1->level == i && e2->level > i) return(TRUE);
if (e1->level > i && e2->level == i) return(FALSE);
}
/* The sort keys are the same. */
if (format_first)
{
r = strcmp(e1->format,e2->format);
if (r<0) return(TRUE);
if (r>0) return(FALSE);
/* formats are the same */
if (sortpageno(e1) <= sortpageno(e2)) return(TRUE);
return(FALSE);
}
else /* sort by pageno, then format */
{
if (sortpageno(e1)<sortpageno(e2)) return(TRUE);
if (sortpageno(e1)>sortpageno(e2)) return(FALSE);
/* sort pages are the same */
if (strcmp(e1->format,e2->format)<=0) return(TRUE);
return(FALSE);
}
}
/* skipspace(p)
** advance p beyond any space characters (space, tabs, CRs, etc.)
*/
char *skipspace(p)
char *p;
{ while (*p != EOS && isspace(*p)) p++;
return(p);
}
/* skipnonsortletters(p)
** advance p beyond any non-sorting letters: whitespace or '-'s.
*/
char *skipnonsortletters(p)
char *p;
{ char *q;
if (*p == EOS) return(p);
if (isspace(*p)) return(skipnonsortletters(p+1));
q = NULL_SORT_CHARS;
while (*q && *p != *q) q++;
if (*q) return(skipnonsortletters(p+1));
return(p);
}
/* skipsmallwords(p)
** advances p over any small words in the small word list.
** capitalization is ignored.
** small word must be followed by a blank
*/
char *skipsmallwords(p)
char *p;
{ int i;
char *pp, *q;
p = skipnonsortletters(p);
for (i=0;strcmp("***",smallword[i]);i++)
{ q = smallword[i];
pp = p;
while (*pp && *q && uppercase(*pp) == uppercase(*q))
{ pp++;
q++;
};
if (*q==EOS && isspace(*pp))
return(skipsmallwords(pp));
}
return(p);
}
/* keycmp(i,e1,e2)
** compare e1->sortkey[i] with e2->sortkey[i]
** return -1 if e1 sorts first
** 0 if they are the same
** 1 if e2 sorts first
*/
keycmp(i,e1,e2)
int i;
entry *e1, *e2;
{ char *p1, *p2;
int r;
/* ignore initial small words when sorting subitems */
if (i>0 && IGNORE_SMALL_WORDS)
{ p1 = skipsmallwords(p1);
p2 = skipsmallwords(p2);
}
/* find position of their first difference, if any (ignoring case,
** blanks, hyphens)
*/
p1 = skipnonsortletters(p1);
p2 = skipnonsortletters(p2);
while (*p1!=EOS && *p2!=EOS && uppercase(*p1) == uppercase(*p2))
{ p1 = skipnonsortletters(p1+1);
p2 = skipnonsortletters(p2+1);
}
if ( *p1 == EOS && *p2 != EOS )
return(-1); /* e1 a prefix of e2 --> -1 */
if ( *p2 == EOS && *p1 != EOS )
return(1); /* e2 a prefix of e1 --> FALSE */
/* sort alphabetically by uppercase version of letters, if possible */
if (uppercase(*p1) < uppercase(*p2)) return(-1);
if (uppercase(*p2) < uppercase(*p1)) return(1);
/* now the uppercase version of the entries are known to be the same */
/* redo the comparison, paying attention to case */
/* sort upper case entry AFTER lower case entry */
/* get sort keys */
p1 = e1->sortkey[i];
p2 = e2->sortkey[i];
while (*p1!=EOS && *p2!=EOS && *p1 == *p2)
{ p1 = skipnonsortletters(p1+1);
p2 = skipnonsortletters(p2+1);
}
/* sort alphabetically by differing cases of same letter, if possible */
if (isupper(*p1) && islower(*p2)) return(1);
if (isupper(*p2) && islower(*p1)) return(-1);
/* keys are totally identical */
/* try to sort by blanks and hyphens ??? */
r = strcmp(e1->sortkey[i],e2->sortkey[i]);
if (r!=0)
printf("\n----- WARNING: Keys `%s' and `%s' are almost identical.\n",
e1->sortkey[i],e2->sortkey[i]);
return(r);
}
/* mergelists(e1,e2,format_first)
** This is the basic merge operation for a merge-sort.
** The entries e1 and e2 are the heads of two disjoint lists of entries.
** The result is the merged list of entries.
** The lists are destructively modified in this merge by modification
** of the `next' entries.
** The `entrylesseq' operator is used for comparisons between entries.
** if format_first == TRUE then merge by key, format, then page
** if format_first == FALSE then merge by key, page, then format
*/
entry *mergelists(e1,e2,format_first)
entry *e1, *e2;
int format_first;
{ entry head, *last = &head;
head.next = NULL;
while (TRUE) {
if (e1 == NULL) { last->next = e2; return(head.next); }
if (e2 == NULL) { last->next = e1; return(head.next); }
if (entrylesseq(e1,e2,format_first))
{
if (DEBUG)
{ printf("\n [");
printsortkey(e1);
printf("] <= [");
printsortkey(e2);
printf("]");
}
last->next = e1; last = e1; e1 = e1->next;
}
else
{
if (DEBUG)
{ printf("\n [");
printsortkey(e1);
printf("] > [");
printsortkey(e2);
printf("]");
}
last->next = e2; last = e2; e2 = e2->next;
}
}
}
/* sortlist(e,format_first)
** This a the merge-sort operator on list e of entries.
** The sort is a ``stable sort'', so entries with the same key stay
** in the same relative order.
** if format_first == TRUE then sort by key, format, then page
** if format_first == FALSE then sort by key, page, then format
*/
entry *sortlist(e,format_first)
entry *e;
int format_first;
{ entry *f,*g;
int i,len;
if (e == NULL || e->next == NULL) return(e);
if (DEBUG)
{ printf("\n\nSorting:");
printlist(e);
}
/* let f point to entry half-way down list */
f = e; len = 0; while (f != NULL) {len++; f = f->next;}
for (f=e,i=0; i<len/2-1; i++,f=f->next) ;
/* now split list into two */
g = f->next;
f->next = NULL;
/* recurse and merge */
e = sortlist(e,format_first); /* first half of list */
g = sortlist(g,format_first); /* second half of list */
e = mergelists(e,g,format_first);
if (DEBUG)
{ printf("\n\nSorting result:");
printlist(e);
}
return(e);
}
/* needsparent(e, f)
** Here e and f are entries in the sorted list: f = e->next.
** The question is whether or not an entry should be added to precede f
** in the list, because f is a multi-level entry but no entry for the super-
** entries are present. Since e is the predecessor of f in the list, we
** don't need to add a parent (superentry) if
** f is at level 0
** (e.g. f = "FOO"), or
** The f->level - 1 first keys of e are the same as f's:
** (e.g. e = "A/B", f = "A/B/C"),
** (e.g. e = "A/B/C/D", f = "A/B/E")
*/
needsparent(e, f)
entry *e, *f;
{ int i;
if (f->level == 0) return(FALSE);
/* e is the prefix of f --> FALSE */
for (i=0;i<=f->level-1;i++)
if (strcmp(e->sortkey[i],f->sortkey[i])) return(TRUE);
return(FALSE);
}
/* addparentifnecessary(e)
** Add a new entry after e if the entry f following e is multi-level and
** it's superentries are not already in list. This can be determined
** by examining e and f together, assuming the list entries are processed
** in order.
*/
addparentifnecessary(e)
entry *e;
{ entry *f = e->next;
int i;
if (samesortkey(e,f)) return; /* same sortkey --> do nothing */
if (f->level == 0) return; /* f at level 0 --> do nothing */
if (needsparent(e,f))
{ /* create a new entry for f's parent */
entry *g = (entry *)mymalloc(sizeof(entry));
g->level = f->level - 1;
for (i=0;i<=TEX_MAX_LEVEL;i++) g->sortkey[i] = NULL;
for (i=0;i<=g->level;i++) g->sortkey[i] = mystrdup(f->sortkey[i]);
g->pv = NULL;
g->format = NULL_FORMAT; /* no page numbers for super-entries */
g->firstpageno = 0;
g->lastpageno = 0;
g->next = f; /* splice g into list: e-->g-->f */
e->next = g;
addparentifnecessary(e); /* redo in case g now needs a parent */
}
}
/* addparents()
** add all superentries that are needed to the entry list `elist',
** using the `addparentifnecessary' routine on each entry.
*/
addparents()
{ entry *e = &elist;
while (e->next != NULL)
{ addparentifnecessary(e);
e = e->next;
}
}
/* samesortkey(e,f)
** returns TRUE if and only if entries e and f have the same sort key
*/
samesortkey(e,f)
entry *e, *f;
{ int i;
if (e->level != f->level) return(FALSE);
for (i=0;i<=e->level;i++)
if (strcmp(e->sortkey[i],f->sortkey[i])) return(FALSE);
return(TRUE);
}
/* spliceout(e,f)
** splice out the entry f. e is f's predecessor.
*/
spliceout(e,f)
entry *e,*f;
{
e->next = f->next;
/* save print value of f if e doesn't have a print value */
if (e->pv == NULL && f->pv != NULL)
e->pv = f->pv;
else if (e->pv != NULL &&
f->pv != NULL &&
strcmp(e->pv,f->pv) &&
strcmp(f->pv,f->sortkey[f->level])
)
{ printf("\n----- WARNING: Print value %s overridden for entry:\n",f->pv);
printentry(f);
printf("\n");
}
/* save format string (assumption: e's is the same or NULL_FORMAT */
e->format = f->format;
}
/* compressentrylist()
** Loop through list `elist' of index entries, and combine adjacent
** entries to make page ranges whenever possible.
*/
compressentrylist()
{ entry *e = elist.next,*f;
while (e != NULL && e->next != NULL)
{ f = e->next;
if (!samesortkey(e,f))
{
e=f; /* can't combine, move on to f */
}
else if (e->format == NULL_FORMAT) /* Note NULL_FORMATs sort earlier */
{ spliceout(e,f);
e->firstpageno = f->firstpageno;
e->lastpageno = f->lastpageno;
}
else if (strcmp(e->format,f->format))
{ /* different formats */
e=f; /* can't combine, move on to f */
}
else if (e->lastpageno == UNKNOWN && f->firstpageno == UNKNOWN)
{ spliceout(e,f); /* combine start and end of range */
e->lastpageno = f->lastpageno;
/* reprocess e */
}
else if (e->lastpageno == UNKNOWN && f->firstpageno != UNKNOWN)
{ spliceout(e,f); /* delete page in middle of range */
}
else if (f->firstpageno == UNKNOWN)
{ /* ERROR -- no start of range */
e = f; /* (will be reported later) */
}
else if ( e->lastpageno == f->firstpageno ||
e->lastpageno +1 == f->firstpageno)
{ spliceout(e,f); /* remove f */
e->lastpageno = f->lastpageno;
/* reprocess e */
}
else
{
e = f; /* process f next */
}
}
}
/* main(argc,argv)
** requires one command-line argument: the name of the file (no extension)
*/
main(argc,argv)
int argc;
char **argv;
{ char fname[MAX_STRING_LENGTH];
int i;
/* If no arguments given, instruct user */
if (argc == 1)
{ printf("\nUsage: windex filename\n");
exit(0);
}
/* print banner */
printf(WINDEX_BANNER);
/* Open input and output files */
strcpy(fname,argv[1]);
strcat(fname,INPUT_EXTENSION); /* input file = foo.idx */
instream = fopen(fname,"r");
if (instream == NULL)
{ printf("\nERROR: input file `%s' can not be opened.\n",fname);
exit(0);
}
strcpy(fname,argv[1]);
strcat(fname,OUTPUT_EXTENSION); /* output file = foo.index */
outstream = fopen(fname,"w");
if (outstream == NULL)
{ printf("\nERROR: output file `%s' can not be opened.\n",fname);
exit(0);
}
/* Initialize entry list to one entry, consisting of a null string */
/* (The null string should sort first) */
elist.level = 0;
elist.next = NULL;
for (i=0;i<TEX_MAX_LEVEL;i++)
elist.sortkey[i] = "";
elast = &elist;
/* Read the input file foo.idx */
if (DEBUG) printf("\n\nInput: ");
inputfile();
/* Sort the entries into order, by key, format then page */
elist.next = sortlist(elist.next,TRUE);
/* Debug printout */
if (DEBUG)
{ printf("\n\nSorted:"); printlist(elist.next); }
/* Add parent superentries for those entries that need them */
addparents();
/* Debug printout */
if (DEBUG)
{ printf("\n\nSorted with parents:"); printlist(elist.next); }
/* Combine pages into ranges as appropriate */
compressentrylist();
/* Debug printout */
if (DEBUG)
{ printf("\n\nSorted with parents and ranges:"); printlist(elist.next); }
/* Sort the entries into order, by key, page, then format */
elist.next = sortlist(elist.next,FALSE);
/* Debug printout */
if (DEBUG)
{ printf("\n\nSorted:"); printlist(elist.next); }
/* print out the output file */
makeindex();
fclose(instream);
fclose(outstream);
}
/* scanbalance(bufferp,out)
** input: bufferp is a pointer to an input buffer
** the first character of bufferp is LBRACE or LBRACKET
** out is a pointer to an output buffer
** The procedure copies bufferp up to the matching delimiter into out.
** The outermost delimeters are converted to TEX_BEGIN_GROUP..TEX_END_GROUP
** (So if there is nothing between the delimiters, we get TEX_EMPTY_GROUP.)
** This procedure returns a pointer to the character after the final delimiter.
*/
char *scanbalance(bufferp,out)
char *bufferp;
char *out;
{ int n = 0;
char c, *q = out;
top: c = *q++ = *bufferp++;
if (c == LBRACKET || c == LBRACE) n++; /* left delimiter */
if (c == RBRACKET || c == RBRACE) n--; /* right delimiter */
if (c == EOS || c == CR || c == LF || n == 0)
/* end of string or all matched */
{ /* convert outermost delimiters to TEX_BEGIN_GROUP..TEX_END_GROUP */
out[0] = TEX_BEGIN_GROUP;
*(q-1) = TEX_END_GROUP;
*q = EOS; /* terminate output string */
return(bufferp);
}
goto top;
}
/* printsortkey(e)
** print out the sort key of entry e in form similar to input form
*/
printsortkey(e)
entry *e;
{ printf("%s",e->sortkey[0]);
if (e->level>=1) printf("/%s",e->sortkey[1]);
if (e->level>=2) printf("/%s",e->sortkey[2]);
}
/* printentry(e)
** print out the entry e in form similar to input form.
*/
printentry(e)
entry *e;
{ printf("\n");
if (e==NULL) printf("NULL");
printsortkey(e);
if (e->pv != NULL) printf("%s",e->pv);
/* else printf("{%s}",e->sortkey[e->level]); */
if (e->lastpageno == UNKNOWN) printf("%c",LANGLE);
if (e->firstpageno == UNKNOWN) printf("%c",RANGLE);
if (e->format != NULL) printf("[%s]",e->format);
printf(":%d",sortpageno(e));
fflush(stdout);
}
/* printlist(e)
** print out the list of entries starting at e
*/
printlist(e)
entry *e;
{ while (e != NULL) { printentry(e); e = e->next; }
}
/* inputfile()
** read the input file a line at a time and process each line.
** print a warning if any line begins with ``\indexentry{''
*/
inputfile()
{ char buffer[MAX_STRING_LENGTH];
int oldstylewarn; /* flag: only give the warning once */
oldstylewarn = FALSE;
while (NULL != fgets(buffer,MAX_STRING_LENGTH-1,instream))
{ if ((!oldstylewarn) && oldstyle(buffer))
{ oldstylewarn = TRUE;
printf("\n----- WARNING: You appear not to have \\input windex.tex.");
printf("\n Proceeding anyway.\n");
};
inputline(buffer);
}
}
/* oldstyle(buffer)
** true if buffer starts with "\indexentry{"
*/
oldstyle(buffer)
char *buffer;
{ int i = 0;
while ((buffer[i] == "\\indexentry{"[i]) && (++i < 12)) ;
return(i == 12);
}
/* inputline(buffer)
** Scan an input line and create an entry for it.
*/
inputline(buffer)
char *buffer;
{ char key[MAX_STRING_LENGTH]; /* sort key */
char pv[MAX_STRING_LENGTH]; /* print value */
char format[MAX_STRING_LENGTH]; /* format string for page number */
char *bufferp,*keyp,*pvp;
entry *e;
int pvseen; /* flag if print-value has been seen */
int i,pageno;
/* Create a new empty entry for this line */
e = (entry *)mymalloc(sizeof(entry));
e->level = 0;
for (i=0;i<TEX_MAX_LEVEL;i++) e->sortkey[i] = NULL;
e->pv = NULL;
e->format = DEFAULT_FORMAT;
e->firstpageno = 0;
e->lastpageno = 0;
/* scan line */
bufferp = buffer; /* input buffer pointer */
keyp = key; /* sort key output pointer */
key[0] = EOS; /* sort key is null string */
pvp = pv; /* print value output pointer */
pvseen = FALSE; /* print value not seen yet */
strcpy(format,DEFAULT_FORMAT);
while (*bufferp==BLANK) bufferp++; /* skip leading blanks */
while (*bufferp && *bufferp != CR && *bufferp != LF)
{ switch (*bufferp) {
case LANGLE: /* LANGLE (<) is flag to start a page range */
e->lastpageno = UNKNOWN;
bufferp++;
break;
case RANGLE: /* RANGLE (>) is flag to end a page range */
e->firstpageno = UNKNOWN;
bufferp++;
break;
case LBRACE: /* LBRACE ({) marks beginning of print value string */
bufferp = scanbalance(bufferp,pv); /* copy string into pv */
pvp = pv + strlen(pv);
pvseen = TRUE;
break;
case LBRACKET: /* LBRACKET ([) marks start of format string */
bufferp = scanbalance(bufferp,format);
break;
case COLON: /* COLON (:) marks beginning of page number */
pageno = atoi(++bufferp);
if (e->firstpageno != UNKNOWN) e->firstpageno = pageno;
if (e->lastpageno != UNKNOWN) e->lastpageno = pageno;
goto saveline; /* and break out of loop */
case SLASH: /* SLASH marks superentry divisions */
bufferp++; /* skip over the slash */
*keyp = EOS; /* terminate sort key */
e->sortkey[e->level] = mystrdup(key);
keyp = key;
e->level = e->level + 1;
if (e->level > TEX_MAX_LEVEL)
printf("\n----- WARNING: Too many levels in entry: %s",
buffer);
break;
default: /* Copy character into sort key */
*keyp++ = *bufferp++;
}
}
saveline:
/* If we haven't seen a sort key, this is probably a blank line.
** Don't save this entry on the list.
*/
if (strlen(key)==0) return;
/* Copy print value into newly allocated string */
if (pvseen) e->pv = mystrdup(pv);
/* Copy format into newly allocated string, if format exists */
if (strcmp(format,TEX_EMPTY_GROUP)==0) /* empty format means no page */
{ e->format = NULL_FORMAT; /* (used for super-entry) */
}
else if (format[0] != EOS)
{ e->format = mystrdup(format);
}
/* If format doesn't contain a spot for pageno, then make pageno = 0 */
/* so it sorts at front */
if (!printspage(e->format))
e->firstpageno = e->lastpageno = 0;
/* Append new entry to end of list. elast points to end of list */
e->next = NULL;
elast->next = e;
elast = e;
if (DEBUG) printentry(e);
}
/* printvalueaux(f,e)
** returns NULL if sort key of e is the different than f's
** otherwise returns printvalue of e, if it exists
** otherwise returns printvalueaux of e's successor
*/
char *printvalueaux(f,e)
entry *f, *e;
{
if (e == NULL) return(NULL);
if (!samesortkey(f,e)) return(NULL);
if (e->pv != NULL) return(e->pv);
return(printvalueaux(f,e->next));
}
/* printvalue(f)
** returns print value for entry f, or sort key if no print value given
** checks succeeding entries with same sort key to see if any of them
** have a print value given, and if so, uses that
*/
char *printvalue(f)
entry *f;
{ char *pv;
pv = printvalueaux(f,f);
if (pv != NULL) return(pv);
return(f->sortkey[f->level]);
}
/* makeindex()
** Runs through the list elist, and prints out the output file foo.index.
*/
makeindex()
{ entry *e = &elist;
entry *f;
int n = 0; /* number of entries printed */
int entry_printed_last = FALSE; /* TRUE if entry printed last,
FALSE if page printed last */
char *q;
if (e->next == NULL)
{
printf("No output produced (no entries found in input file).\n",n);
return;
}
fprintf(outstream,TEX_BEGIN_INDEX);
while (e->next != NULL)
{ f = e->next; /* entry to print is f */
n++;
/* Check for unbalanced page ranges and print warnings */
if (f->lastpageno == UNKNOWN)
{ printf("\n----- WARNING: closing entry not found to match following starting %c entry:",LANGLE);
printentry(f);
printf("\n");
}
if (f->firstpageno == UNKNOWN)
{ printf("\n----- WARNING: starting entry not found to match following ending %c entry:",RANGLE);
printentry(f);
printf("\n");
}
/* Print entry text if necessary */
if (!samesortkey(e,f))
{ /* Key of f is different than key of predecessor e */
/* print appropriately indented entry with subitems, etc. */
if (f->level == 0) fprintf(outstream,TEX_BEGIN_LEVEL0);
else if (f->level == 1) fprintf(outstream,TEX_BEGIN_LEVEL1);
else if (f->level == 2) fprintf(outstream,TEX_BEGIN_LEVEL2);
/* print ``print value'' for f */
fprintf(outstream,"%s",printvalue(f));
entry_printed_last = TRUE;
}
/* print TEX_AFTER_ENTRY or TEX_BETWEEN_PAGES if necessary */
if (strcmp(f->format,NULL_FORMAT)==0)
{ ; /* no page entry to be printed, so no separator */
}
else if (entry_printed_last)
fprintf(outstream,TEX_AFTER_ENTRY);
else if (entry_printed_last == FALSE)
fprintf(outstream,TEX_BETWEEN_PAGES);
/* Print page number in its format */
if (strcmp(f->format,NULL_FORMAT))
{ /* check for no page number printed */
if (!printspage(f->format))
fprintf(outstream,"%s",f->format);
else /* insert page or page range */
{ /* print first part of format */
q = f->format;
while (*q && *q != ZERO)
{ fprintf(outstream,"%c",*q); q++; }
/* print first page number */
fprintf(outstream,"%d",f->firstpageno);
/* if necessary, print range */
if (f->lastpageno != f->firstpageno)
{
fprintf(outstream,"%s",TEX_BETWEEN_RANGE);
fprintf(outstream,"%d",f->lastpageno);
}
q++; /* skip the zero */
while (*q) fprintf(outstream,"%c",*q++);
}
entry_printed_last = FALSE;
}
/* go on to next entry */
e = e->next;
}
fprintf(outstream,TEX_END_INDEX);
printf("Done. %d entries processed.\n",n);
}