DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T i

⟦b4d4d6ef7⟧ TextFile

    Length: 40909 (0x9fcd)
    Types: TextFile
    Names: »index.y«

Derivation

└─⟦52210d11f⟧ Bits:30007239 EUUGD2: TeX 3 1992-12
    └─⟦c319c2751⟧ »unix3.0/TeX3.0.tar.Z« 
        └─⟦036c765ac⟧ 
            └─⟦this⟧ »TeX3.0/LaTeX/LaTeXindex/index.y« 
└─⟦060c9c824⟧ Bits:30007080 DKUUG TeX 2/12/89
    └─⟦this⟧ »./tex82/LaTeX/LaTeXindex/index.y« 
└─⟦52210d11f⟧ Bits:30007239 EUUGD2: TeX 3 1992-12
    └─⟦63303ae94⟧ »unix3.14/TeX3.14.tar.Z« 
        └─⟦c58930e5c⟧ 
            └─⟦this⟧ »TeX3.14/LaTeX/LaTeXindex/index.y« 

TextFile

/* $Header: index.y,v 1.5 88/01/25 17:37:20 root Exp $ */
/* Yacc parser for LaTeX index processor */
/* Roman numeral code written by John Renner (adobe!renner@decwrl.dec.com) */
%{
#include <stdio.h>
#include <local/standard.h>
#include <local/profile.h>
#include <ctype.h>
#include <pwd.h>
#define TABLEINCREMENT	50		/* Number of additional entries added when expanding a table */
#define ODD(i)		(((i) % 2) == 1)
#define ITEMDEPTH	3		/* Number of nestings of \item's, \subitem's, \subsubitem's, etc. */
char *ItemRep[] = { "\\item", "\\subitem", "\\subsubitem", NULL }; /* and their representation */
char	*calloc(), *realloc();
enum TokenType	{controlword, controlsymbol, string, integer, roman, comma, obrace, cbrace, whitespace};
struct IndexEntry {
    char		literal[81];	/* Literal representation of index entry */
    char		alphabetic[81];	/* Alphabetic representation for sorting of index entry */
    struct Token	*tokenlist;	/* Doubly linked token list */
    struct IndexEntry	*subitem;	/* Pointer to subitem table, subsubitem table, etc */
    int			subitemcount;	/* Number of items in subitem table */
    int			subitemtabsize;	/* Subitem table size currently allocated */
    struct PageNoTable	*pagenos;	/* List of page numbers */
    int			pagetablecount;	/* Number of items in page number table */
    int			pagetablesize;	/* Size of page number table currently allocated */
};
struct Token {
    enum TokenType	type;		/* Token type */
    char		lexeme[81];	/* Representation of all the token types */
    struct Token	*prev, *next;
};
struct PageNoTable {
    int			number;		/* Page number */
    Boolean		range;		/* True if this is the beginning of a range */
    Boolean		roman;	/* True if this was a roman numeral */
};
struct IndexEntry	*IndexTable = NULL;	/* Table of primary index entries */
int			IndexTableCount = 0;	/* Count of number of elements used in index table */
int			IndexTableSize = 0;	/* Current allocated size of index table */
int			ExitStatus = SUCCEED;	/* Guess */
int			LineNo = 1;		/* Line number at start of token */
int			EndLineNo = 1;		/* Line number at end of token */
Boolean			Label = FALSE;		/* True if -l option given */
Boolean			Range;			/* True if this \indexentry is a range */
PROFILE_STANZA		*SortStanza = NULL;	/* Alphabetize stanza */
extern int		optind;			/* From getopt(3) */
extern char		*optarg;
char			*Whoami;		/* argv[0] */
char			*Usage = "Usage: %s [-l] [-f alphabetizefile] [file...]\n";
char			*Marker[] = { "alphabetize", NULL };	/* Markers for alphabetize stanza */
char			IdxFileName[81];	/* .idx file name */
char			Literal[81];		/* Literal string of key */
char			Alphabetic[81];		/* Alphabetic string of key */
FILE			*InputFile;		/* Current input file */
FILE			*OutputFile;		/* Current output file */
struct Token		*CurKey;		/* Current key we are constructing */
struct IndexEntry	**CurSearchTable;	/* Current table to search for match */
struct IndexEntry	*CurEntry;		/* Current table entry */
struct IndexEntry	*PrevEntry;		/* Previous Entry */
%}
%union {
    char		value[81];
    struct Token	*t;
}
%token <value> ROMAN CONTROLSEQUENCE INTEGER WHITESPACE STRING INDEXENTRY
%type <t> noncommaelement anyelement anyelements
%%
indexfile :
    optwhitespace
    indexentries
	{
	    sort(IndexTable, IndexTableCount);
	    fprintf(OutputFile, "\\begin{theindex}\n");
	    if (Label) {
		fprintf(OutputFile,
 "\\newcommand{\\largeletter}[1]{{\\pagebreak[2]\\Large\\hspace{-.5in}\\parbox[t]{.5in}{\\makebox[.35in][r]");
		fprintf(OutputFile, "{\\uppercase{#1}}}\\nopagebreak[4]\\vspace{-1.5ex}}}\n");
	    }
	    printindexentries(IndexTable, IndexTableCount, 1);
	    fprintf(OutputFile, "\\end{theindex}\n");
	}
    ;

indexentries :
    indexentries
    indexentry
    |
    indexentry
    ;

indexentry :
    INDEXENTRY
	{
	    CurSearchTable = &IndexTable, PrevEntry = NULL;
	    CurKey = NULL;
	    Range = FALSE;
	}
    optwhitespace
    '{'
    keys
    '}'
    optwhitespace
    '{'
    optwhitespace
    anumber
    optwhitespace
    '}'
    optwhitespace
    ;

anumber : 
    INTEGER
	{
	    struct PageNoTable	*p;

	    if (!(p = findpage(CurEntry->pagenos, CurEntry->pagetablecount, atoi($1), FALSE))) {
		if (CurEntry->pagetablecount >= CurEntry->pagetablesize) {
		    if (!(CurEntry->pagenos = (struct PageNoTable *)reallocate(CurEntry->pagenos, CurEntry->pagetablesize,
		    TABLEINCREMENT, sizeof(struct PageNoTable)))) {
			yyerror("memory allocation failure");
			exit(FAIL);
		    }
		    CurEntry->pagetablesize += TABLEINCREMENT;
		}
		CurEntry->pagenos[CurEntry->pagetablecount].number = atoi($1);
		CurEntry->pagenos[CurEntry->pagetablecount].roman = FALSE;
		CurEntry->pagenos[CurEntry->pagetablecount].range = Range;
		CurEntry->pagetablecount++;
	    } else
		p->range = Range;
	}
      |
      ROMAN
	{
	    struct PageNoTable	*p;

	    if (!(p = findpage(CurEntry->pagenos, CurEntry->pagetablecount, rmtoi($1), TRUE))) {
		if (CurEntry->pagetablecount >= CurEntry->pagetablesize) {
		    if (!(CurEntry->pagenos = (struct PageNoTable *)reallocate(CurEntry->pagenos, CurEntry->pagetablesize,
		    TABLEINCREMENT, sizeof(struct PageNoTable)))) {
			yyerror("memory allocation failure");
			exit(FAIL);
		    }
		    CurEntry->pagetablesize += TABLEINCREMENT;
		}
		CurEntry->pagenos[CurEntry->pagetablecount].number = rmtoi($1);
		CurEntry->pagenos[CurEntry->pagetablecount].roman = TRUE;
		CurEntry->pagenos[CurEntry->pagetablecount].range = Range;
		CurEntry->pagetablecount++;
	    } else
		p->range = Range;
	}
	
	
keys :
    multiplekeys
    key
    {
	struct Token	*t;

	for (t = CurKey; t->next; t = t->next)
	    ;
	if (t->type == string)
	    if (t->lexeme[strlen(t->lexeme) - 1] == '-') {
		t->lexeme[strlen(t->lexeme) - 1] = '\0';
		Range = TRUE;
	    }
	goto installkey;
    }
    ;

multiplekeys :
    multiplekeys
    key
    ','
	{
	    struct Token	*t;

installkey: strcpy(Literal, literalstring(CurKey));
	    strcpy(Alphabetic, alphabetizestring(CurKey, SortStanza));
	    if (!*CurSearchTable) {
		if (!(*CurSearchTable = (struct IndexEntry *)reallocate(*CurSearchTable, 0, TABLEINCREMENT,
		sizeof(struct IndexEntry)))) {
		    yyerror("memory allocation failure");
		    exit(FAIL);
		}
		if (!PrevEntry)
		    IndexTableSize = TABLEINCREMENT;
		else
		    PrevEntry->subitemtabsize = TABLEINCREMENT;
	    }
	    if (!(CurEntry = findentry(*CurSearchTable, PrevEntry ? PrevEntry->subitemcount : IndexTableCount, Literal))) {
		if (!PrevEntry) {
		    if (IndexTableCount >= IndexTableSize) {
			if (!(*CurSearchTable = (struct IndexEntry *)reallocate(*CurSearchTable, IndexTableSize, TABLEINCREMENT,
			sizeof(struct IndexEntry)))) {
			    yyerror("memory allocation failure");
			    exit(FAIL);
			}
			IndexTableSize += TABLEINCREMENT;
		    }
		    CurEntry = (*CurSearchTable + IndexTableCount);
		    IndexTableCount++;
		} else {
		    if (PrevEntry->subitemcount >= PrevEntry->subitemtabsize) {
			if (!(*CurSearchTable = (struct IndexEntry *)reallocate(*CurSearchTable, PrevEntry->subitemtabsize,
			TABLEINCREMENT, sizeof(struct IndexEntry)))) {
			    yyerror("memory allocation failure");
			    exit(FAIL);
			}
			PrevEntry->subitemtabsize += TABLEINCREMENT;
		    }
		    CurEntry = (*CurSearchTable + PrevEntry->subitemcount);
		    PrevEntry->subitemcount++;
		}
		strcpy(CurEntry->literal, Literal);
		strcpy(CurEntry->alphabetic, Alphabetic);
		CurKey->prev = CurEntry->tokenlist, CurEntry->tokenlist = CurKey;
		CurEntry->subitem = NULL, CurEntry->subitemcount = CurEntry->subitemtabsize = 0;
		CurEntry->pagenos = NULL, CurEntry->pagetablecount = CurEntry->pagetablesize = 0;
	    }
	    CurSearchTable = &CurEntry->subitem;
	    PrevEntry = CurEntry;
	    CurKey = NULL;
	}
    |
    /* epsilon */
    ;

key :
    key
    noncommaelement
    |
    noncommaelement
    ;

noncommaelement :
    CONTROLSEQUENCE
	{
	    if (!($$ = (struct Token *)calloc(1, sizeof(struct Token)))) {
		yyerror("memory allocation failure");
		exit(FAIL);
	    }
	    $$->type = isalpha($1[1]) ? controlword : controlsymbol;
	    strcpy($$->lexeme, $1);
	    $$->next = NULL;
	    if (!CurKey)
		$$->prev = CurKey, CurKey = $$;
	    else {
		struct Token *p;
		
		for (p = CurKey; p->next; p = p->next)
		    ;
		p->next = $$, $$->prev = p;
	    }
	    $$ = CurKey;
	}
    |
    ROMAN
	{
	    if (!($$ = (struct Token *)calloc(1, sizeof(struct Token)))) {
		yyerror("memory allocation failure");
		exit(FAIL);
	    }
	    $$->type = roman;
	    strcpy($$->lexeme, $1);
	    $$->next = NULL;
	    if (!CurKey)
		$$->prev = CurKey, CurKey = $$;
	    else {
		struct Token *p;
		
		for (p = CurKey; p->next; p = p->next)
		    ;
		p->next = $$, $$->prev = p;
	    }
	    $$ = CurKey;
	}
    |
    INTEGER
	{
	    if (!($$ = (struct Token *)calloc(1, sizeof(struct Token)))) {
		yyerror("memory allocation failure");
		exit(FAIL);
	    }
	    $$->type = integer;
	    strcpy($$->lexeme, $1);
	    $$->next = NULL;
	    if (!CurKey)
		$$->prev = CurKey, CurKey = $$;
	    else {
		struct Token *p;
		
		for (p = CurKey; p->next; p = p->next)
		    ;
		p->next = $$, $$->prev = p;
	    }
	    $$ = CurKey;
	}
    |
    WHITESPACE
	{
	    if (!($$ = (struct Token *)calloc(1, sizeof(struct Token)))) {
		yyerror("memory allocation failure");
		exit(FAIL);
	    }
	    $$->type = whitespace;
	    strcpy($$->lexeme, $1);
	    $$->next = NULL;
	    if (!CurKey)
		$$->prev = CurKey, CurKey = $$;
	    else {
		struct Token *p;
		
		for (p = CurKey; p->next; p = p->next)
		    ;
		p->next = $$, $$->prev = p;
	    }
	    $$ = CurKey;
	}
    |
    STRING
	{
	    if (!($$ = (struct Token *)calloc(1, sizeof(struct Token)))) {
		yyerror("memory allocation failure");
		exit(FAIL);
	    }
	    $$->type = string;
	    strcpy($$->lexeme, $1);
	    $$->next = NULL;
	    if (!CurKey)
		$$->prev = CurKey, CurKey = $$;
	    else {
		struct Token *p;
		
		for (p = CurKey; p->next; p = p->next)
		    ;
		p->next = $$, $$->prev = p;
	    }
	    $$ = CurKey;
	}
    |
    '{'
	{
	    if (!($$ = (struct Token *)calloc(1, sizeof(struct Token)))) {
		yyerror("memory allocation failure");
		exit(FAIL);
	    }
	    $$->type = obrace;
	    strcpy($$->lexeme, "{");
	    $$->next = NULL;
	    if (!CurKey)
		$$->prev = CurKey, CurKey = $$;
	    else {
		struct Token *p;

		for (p = CurKey; p->next; p = p->next)
		    ;
		p->next = $$, $$->prev = p;
	    }
	}
    anyelements
    '}'
	{
	    if (!($$ = (struct Token *)calloc(1, sizeof(struct Token)))) {
		yyerror("memory allocation failure");
		exit(FAIL);
	    }
	    $$->type = cbrace;
	    strcpy($$->lexeme, "}");
	    $$->next = NULL;
	    if (!CurKey)
		$$->prev = CurKey, CurKey = $$;
	    else {
		struct Token *p;
		
		for (p = CurKey; p->next; p = p->next)
		    ;
		p->next = $$, $$->prev = p;
	    }
	    $$ = CurKey;
	}
    ;

anyelements :
    anyelements
    anyelement
	{
	    $$ = $2;
	}
    |
	/* epsilon */
	{
	    ;	/* Do nothing */
	}
    ;

anyelement :
    noncommaelement 	/* Default action is $$ = $1 */
    |
    ','
	{
	    if (!($$ = (struct Token *)calloc(1, sizeof(struct Token)))) {
		yyerror("memory allocation failure");
		exit(FAIL);
	    }
	    $$->type = comma;
	    strcpy($$->lexeme, ",");
	    $$->next = NULL;
	    if (!CurKey)
		$$->prev = CurKey, CurKey = $$;
	    else {
		struct Token *p;
		
		for (p = CurKey; p->next; p = p->next)
		    ;
		p->next = $$, $$->prev = p;
	    }
	    $$ = CurKey;
	}
    ;

optwhitespace :
    WHITESPACE
    |
    ;
    
%%
#include "indexlex.c"

main(argc, argv)
int	argc;
char	*argv[];
{
    int			c;
    Boolean		sortfilegiven = FALSE;
    char		sortfilename[81];
    char		indfilename[81];
    struct passwd	*pwentry;
    FILE		*stanzafileptr;

    Whoami = argv[0];
    pwentry = getpwuid(geteuid());
    sprintf(sortfilename, "%s/.alphabetize", pwentry->pw_dir);
    while ((c = getopt(argc, argv, "f:l")) != EOF)
	switch (c) {
	case 'l':
	    Label = TRUE;
	    break;
	case 'f':
	    strcpy(sortfilename, optarg);
	    sortfilegiven = TRUE;
	    break;
	case '?':
	    fprintf(stderr, Usage, Whoami);
	    exit(FAIL);
	}
    stanzafileptr = fopen(sortfilename, "r");
    if (sortfilegiven && !stanzafileptr) {
	fprintf(stderr, "%s: cannot open alphabetization file %s\n", Whoami, sortfilename);
	exit(FAIL);
    }
    if (stanzafileptr) {
	if (!(SortStanza = profile_read_profile(stanzafileptr))) {
	    fprintf(stderr, "%s: file %s is not in stanza format\n", Whoami, sortfilename);
	    fclose(stanzafileptr);
	    exit(FAIL);
	}
	if (!(SortStanza = profile_has_stanza(SortStanza, Marker))) {
	    fprintf(stderr, "%s: file %s does not contain a stanza with marker %s\n", Whoami, sortfilename, Marker[0]);
	    fclose(stanzafileptr);
	    exit(FAIL);
	}
	fclose(stanzafileptr);
    }
    checkstanza(SortStanza);
    if (optind == argc) {
	InputFile = stdin;
	OutputFile = stdout;
	strcpy(IdxFileName, "stdin");
    }
    do {
	if (InputFile != stdin) {
	    strcpy(IdxFileName, argv[optind]);
	    if (!(InputFile = fopen(argv[optind], "r"))) {
		strcpy(IdxFileName, argv[optind]);
		strcat(IdxFileName, ".idx");
		if (!(InputFile = fopen(IdxFileName, "r"))) {
		    fprintf(stderr, "%s: cannot open %s\n", Whoami, IdxFileName);
		    ExitStatus = FAIL;
		    continue;
		}
	    }
	    if (strlen(IdxFileName) >= 4 && EQ(&IdxFileName[strlen(IdxFileName)-4], ".idx"))
		sprintf(indfilename, "%.*s.ind", strlen(IdxFileName)-4, IdxFileName);
	    else
		sprintf(indfilename, "%s.ind", IdxFileName);
	    if (!(OutputFile = fopen(indfilename, "w"))) {
		fprintf(stderr, "%s: cannot open output file %s\n", Whoami, indfilename);
		fclose(InputFile);
		ExitStatus = FAIL;
		continue;
	    }
	} else
	    strcpy(IdxFileName, "stdin");
	if (yyparse() != 0)
	    ExitStatus = FAIL;
	fclose(InputFile);
	fclose(OutputFile);
	freetables(IndexTable, IndexTableCount);
	IndexTable = NULL, IndexTableCount = IndexTableSize = 0, LineNo = EndLineNo = 1;
	yysptr = yysbuf;	/* Resets Lex lookahead buffer */
    } while (++optind < argc);
    exit(ExitStatus);
}

yyerror(s)
char *s;
{
    fprintf(stderr, "\"%s\", line %d: %s\n", IdxFileName, LineNo, s);
}

/* Allocates additional space for tables. Returns NULL if memory allocation failure or inconsistent parameters */
char *reallocate(table, current, increment, elementsize)
char	*table;			/* pointer to current table */
int	current;		/* current size of table */
int	increment;		/* additional entries to add */
int	elementsize;		/* size of an element in the table */
{
    char	*calloc(), *realloc();
    char	*p;

    if ((!table && current > 0) || current < 0 || increment < 0 || elementsize < 0)
	return NULL;
    if (increment == 0 || elementsize == 0)
	return table;
    if (current == 0)
	if (!(p = calloc(increment, elementsize)))
	    return NULL;
	else
	    return p;
    else
	if (!(p = realloc(table, (current + increment) * elementsize)))
	    return NULL;
	else
	    return p;
}

/* Frees the space allocated for all the tables */
freetables(index, noentries)
struct IndexEntry	*index;		/* index table */
int			noentries;	/* number of entries in table */
{
    struct Token	*t, *ttemp;
    int			i;

    if (!index || noentries == 0)
	return;
    for (i = 0; i < noentries; i++)
    if (index[i].subitem)
	freetables(index[i].subitem, index[i].subitemcount);	/* recursion! */
    for (t = index[i].tokenlist; t; t = ttemp)
	ttemp = t->next, free(t);
    if (index[i].pagenos)
	free(index[i].pagenos);
    free(index);
}

/* Checks alphabetize stanza for validity */
checkstanza(ps)
PROFILE_STANZA	*ps;
{
    PROFILE_BINDING	*pb;
    PROFILE_VALUE	*pv;
    int			count;

    if (!ps)
	return;
    if (pb = profile_has_binding(ps, "skipchars"))
	for (pv = pb->value; pv; pv = pv->next)
	    if (pv->class != PROFILE_CHARACTER)
		switch (pv->class) {
		case PROFILE_INTEGER:
		    fprintf(stderr, "%s: illegal integer constant %d in skipchars binding\n", Whoami, pv->value.i);
		    break;
		case PROFILE_HEX:
		    fprintf(stderr, "%s: illegal hex constant 0x%x in skipchars binding\n", Whoami, pv->value.i);
		    break;
		case PROFILE_OCTAL:
		    fprintf(stderr, "%s: illegal octal constant 0%o in skipchars binding\n", Whoami, pv->value.i);
		    break;
		case PROFILE_FLOAT:
		    fprintf(stderr, "%s: illegal float constant %f in skipchars binding\n", Whoami, pv->value.f);
		    break;
		case PROFILE_STRING:
		case PROFILE_OTHER:
		    fprintf(stderr, "%s: illegal string constant %s in skipchars binding\n", Whoami, pv->value.s);
		    break;
		}
    if (pb = profile_has_binding(ps, "mapctrlsequence")) {
	for (count = 0, pv = pb->value; pv; pv = pv->next, count++)
	    if (pv->class != PROFILE_OTHER || pv->class != PROFILE_STRING)
		switch (pv->class) {
		case PROFILE_INTEGER:
		    fprintf(stderr, "%s: illegal integer constant %d in mapctrlsequence binding\n", Whoami, pv->value.i);
		    break;
		case PROFILE_HEX:
		    fprintf(stderr, "%s: illegal hex constant 0x%x in mapctrlsequence binding\n", Whoami, pv->value.i);
		    break;
		case PROFILE_OCTAL:
		    fprintf(stderr, "%s: illegal octal constant 0%o in mapctrlsequence binding\n", Whoami, pv->value.i);
		    break;
		case PROFILE_FLOAT:
		    fprintf(stderr, "%s: illegal float constant %f in mapctrlsequence binding\n", Whoami, pv->value.f);
		    break;
		case PROFILE_CHARACTER:
		    fprintf(stderr, "%s: illegal character constant %c in mapctrlsequence binding\n", Whoami, pv->value.c);
		    break;
		}
	if (ODD(count))
	    fprintf(stderr, "%s: must have an even number of string values for mapctrlsequence binding\n", Whoami);
    }
    if (pb = profile_has_binding(ps, "mapindexentry")) {
	for (count = 0, pv = pb->value; pv; pv = pv->next, count++)
	    if (pv->class != PROFILE_OTHER || pv->class != PROFILE_STRING)
		switch (pv->class) {
		case PROFILE_INTEGER:
		    fprintf(stderr, "%s: illegal integer constant %d in mapindexentry binding\n", Whoami, pv->value.i);
		    break;
		case PROFILE_HEX:
		    fprintf(stderr, "%s: illegal hex constant 0x%x in mapindexentry binding\n", Whoami, pv->value.i);
		    break;
		case PROFILE_OCTAL:
		    fprintf(stderr, "%s: illegal octal constant 0%o in mapindexentry binding\n", Whoami, pv->value.i);
		    break;
		case PROFILE_FLOAT:
		    fprintf(stderr, "%s: illegal float constant %f in mapindexentry binding\n", Whoami, pv->value.f);
		    break;
		case PROFILE_CHARACTER:
		    fprintf(stderr, "%s: illegal character constant %c in mapindexentry binding\n", Whoami, pv->value.c);
		    break;
		}
	if (ODD(count))
	    fprintf(stderr, "%s: must have an even number of string values for mapindexentry binding\n", Whoami);
    }
}

/* Returns the literal string of a token list */
char	*literalstring(t)
struct Token	*t;
{
    static char	literal[81];

    strcpy(literal, "");
    for (t = CurKey; t; t = t->next)
	    strcat(literal, t->lexeme);
    return literal;
}

/* Returns alphabetization string for a token list and a stanza */
char	*alphabetizestring(tokenlist, stanza)
struct Token	*tokenlist;
PROFILE_STANZA	*stanza;
{
    char		litstring[81];
    char		ctrlstring[21];
    char		c;
    static char		alphastring[81];
    int			i;
    Boolean		add;
    struct Token	*t;
    PROFILE_BINDING	*pb;
    PROFILE_VALUE	*pv;
    
    if (!tokenlist)
	return NULL;
    strcpy(alphastring, "");
    if (!stanza) {
	for (t = tokenlist; t; t = t->next)
	    switch (t->type) {
	    case string:
	    case integer:
	    case roman:
	    case comma:
	    case obrace:
	    case cbrace:
		strcat(alphastring, t->lexeme);
		break;
	    }
	return alphastring;
    } else {
	strcpy(litstring, literalstring(tokenlist));
	for (pb = stanza->binding; pb; pb = pb->next)
	    if (EQ(pb->name, "mapindexentry"))
	        for (pv = pb->value; pv && pv->next; pv = pv->next, pv = pv->next)
		    if ((pv->class == PROFILE_STRING || pv->class == PROFILE_OTHER) && (pv->next->class == PROFILE_STRING ||
		    pv->next->class == PROFILE_OTHER))
		        if (EQ(litstring, pv->value.s)) {
			    strcpy(alphastring, pv->next->value.s);
			    return alphastring;
		        }
	for (t = tokenlist; t; t = t->next)
	    switch (t->type) {
	    case controlword:
	    case controlsymbol:
		for (pb = stanza->binding; pb; pb = pb->next)
		    if (EQ(pb->name, "mapctrlsequence"))
		        for (pv = pb->value; pv && pv->next; pv = pv->next, pv = pv->next)
			    if ((pv->class == PROFILE_STRING || pv->class == PROFILE_OTHER) && (pv->next->class == 
			    PROFILE_STRING || pv->next->class == PROFILE_OTHER))
			        if (strlen(pv->value.s) > 0) {
				    if (pv->value.s[0] != '\\')
				        sprintf(ctrlstring, "\\%s", pv->value.s);
				    else
				        strcpy(ctrlstring, pv->value.s);
				    if (EQ(ctrlstring, t->lexeme))
				        strcat(alphastring, pv->next->value.s);
				}
		break;
	    case string:
	    case integer:
	    case roman:
		for (i = 0; t->lexeme[i]; i++) {
		    add = TRUE;
		    for (pb = stanza->binding; pb; pb = pb->next)
		        if (EQ(pb->name, "skipchars"))
			    for (pv = pb->value; pv; pv = pv->next)
			        if (pv->class == PROFILE_CHARACTER)
				    if (pv->value.c == t->lexeme[i]) {
				        add = FALSE;
					goto breaktwice1;
				    }
breaktwice1:	    if (add)
			alphastring[strlen(alphastring) + 1] = '\0', alphastring[strlen(alphastring)] = t->lexeme[i];
		}
		break;
	    case comma:
		c = ',';
		goto insert;
	    case obrace:
		c = '{';
		goto insert;
	    case cbrace:
		c = '}';
insert:		add = TRUE;
		for (pb = stanza->binding; pb; pb = pb->next)
		    if (EQ(pb->name, "skipchars"))
		        for (pv = pb->value; pv; pv = pv->next)
			    if (pv->class == PROFILE_CHARACTER)
			        if (pv->value.c == c) {
				    add = FALSE;
				    goto breaktwice2;
				}
breaktwice2:	if (add)
		    alphastring[strlen(alphastring) + 1] = '\0', alphastring[strlen(alphastring)] = c;
		break;
	    }
	return alphastring;
    }
}

/* Finds an entry in a table. Returns NULL if not found. */
struct IndexEntry	*findentry(table, noentries, string)
struct IndexEntry 	*table;
int			noentries;
char			*string;
{
    int	i;

    if (noentries <= 0)
	return NULL;
    for (i = 0; i < noentries; i++)
	if (EQ(string, table[i].literal))
	    return &table[i];
    return NULL;
}

/* Returns pointer to page number if found, NULL otherwise */
struct PageNoTable	*findpage(pagearray, elements, pageno, aromannum)
struct PageNoTable	*pagearray;
int			elements;
int			pageno;
Boolean			aromannum;
{
    int	i;

    if (!pagearray)
	return NULL;
    for (i = 0; i < elements; i++)
	if ((pagearray[i].number == pageno) &&
	    (pagearray[i].roman == aromannum))
	    return &pagearray[i];
    return NULL;
}

/* Sorts the entries in the structures */
sort(base, numberelements)
struct IndexEntry	*base;
int			numberelements;
{
    int	i;
    int numericcompare();
    int alphacompare();

    for (i = 0; i < numberelements; i++) {
	if (base[i].pagenos)
	    qsort(base[i].pagenos, base[i].pagetablecount, sizeof(struct PageNoTable), numericcompare);
	if (base[i].subitem)
	    sort(base[i].subitem, base[i].subitemcount);	/* recursion! */
    }
    qsort(base, numberelements, sizeof(struct IndexEntry), alphacompare);
}

int	numericcompare(e1, e2)
struct PageNoTable	*e1, *e2;
{
    if ((e1->roman == TRUE) && (e2->roman == FALSE))
        return -1;
    if ((e1->roman == FALSE) && (e2->roman == TRUE))
        return 1;
   		/* else either both roman or both integers */
    if (e1->number == e2->number)
	return 0;
    else if (e1->number < e2->number)
	return -1;
    else
	return 1;
}

int alphacompare(e1, e2)
struct IndexEntry	*e1, *e2;
{
    char	s1[81], s2[81];

    strcpy(s1, e1->alphabetic), strcpy(s2, e2->alphabetic);
    return(strcmp(string_downshift(s1), string_downshift(s2)));
}


int rmtoi (romanstr)
char *romanstr;
{
	register char  *p = romanstr;
	register int    w;
	register int    prevw = (-1);
	register int    result = 0;
	int             romanwt();

	while (*p) {
		if ((w = romanwt(*p)) == (-1)) {
			fprintf(stderr, "illegal char in roman string:'%c'\n", (*p));
			return (-1);
		} else {
			if (prevw > 0) {	/* check for subtractive
						 * notation */
				if (w > prevw) { /* e.g., the case "ix" */
					result += (w - prevw) - prevw;
				} else
					result += w;
			} else {
				result += w;
			}
		}
		prevw = w;
		p++;
	}
	return (result);
}

static int romanwt (c)
register char c;
{
	static char     romanlett[7] = {'m', 'd', 'c', 'l', 'x', 'v', 'i'};
	static int      weight[7] = {1000, 500, 100, 50, 10, 5, 1};

	register char  *pt;

	if (isupper(c))
		c = tolower(c);
	pt = romanlett;
	while (*pt) {
		if (*pt == c) {
			return (weight[(int) (pt - romanlett)]);
		} else
			pt++;
	}
	return (-1);		/* roman letter not found */
}

/* Used for storing state of previous page number */
struct State {
    Boolean	roman;		/* Roman/arabic */
    Boolean	range;		/* Range or single page number */
    Boolean	rangecomplete;	/* True if there has been an end to the range */
    int		pagerange[2];	/* 0 element for single page or 0 and 1 for a page range */
} State;

/* Prints out the contents of the state structure */
printstate(s, itemrep, item, comma)
struct State	s;
char		*itemrep;	/* \item, \subitem, \subsubitem, etc. */
char		*item;		/* Actual name of item */
Boolean		comma;		/* Whether to print out a comma or a newline */
{
    if (s.range && !s.rangecomplete) {
	fprintf(stderr, "%s: file %s, %s %s ends with a range\n", Whoami, IdxFileName, itemrep, item);
	ExitStatus = FAIL;
    }
    if (!s.range) {
	if (s.roman)
	    fprintf(OutputFile, "{\\romannumeral %d}", s.pagerange[0]);
        else
	    fprintf(OutputFile, "%d", s.pagerange[0]);
    } else
        if (s.pagerange[0] == s.pagerange[1] - 1) {
	    if (s.roman)
		fprintf(OutputFile, "{\\romannumeral %d}, {\\romannumeral %d}", s.pagerange[0], s.pagerange[1]);
            else
		fprintf(OutputFile, "%d, %d", s.pagerange[0], s.pagerange[1]);
	} else {
	    if (s.roman)
	        fprintf(OutputFile, "{\\romannumeral %d}--{\\romannumeral %d}", s.pagerange[0], s.pagerange[1]);
	    else
	        fprintf(OutputFile, "%d--%d", s.pagerange[0], s.pagerange[1]);
	}
    if (comma)
	fprintf(OutputFile, ", ");
    else
	fprintf(OutputFile, "\n");
}

/* Prints out the index entries.

   						Action Table

cur pg--->||    1.    !range            |    2.   !range             |    3.   range              |    4.   range
prev pg   ||          roman             |         !roman             |         roman              |         !roman
===V======||============================|============================|============================|=============================
1. none   ||(1,1)                       |(1,2)                       |(1,3)                       |(1,4)
          ||set roman TRUE              |set roman FALSE             |set roman TRUE              |set roman FALSE
          ||set range FALSE             |set range FALSE             |set range TRUE              |set range TRUE
          ||set page number             |set page number             |set rangecomplete FALSE     |set rangecomplete FALSE
          ||                            |                            |set start page=end page     |set start page=end page
----------||----------------------------|----------------------------|----------------------------|-----------------------------
2. !range ||(2,1)                       |(2,2)                       |(2,3)                       |(2,4)
   roman  ||if prev pg+1=cur pg&&there  |print out prev pg           |if prev pg+1=cur pg&&there  |print out previous page
          ||is a next pg&&next pg-1=cur |set roman FALSE             |is a next pg&&next pg is    |set roman FALSE
          ||pg&&next pg is roman,       |set range FALSE             |roman,                      |set range TRUE
          ||  make a range with         |set page number             |  make a range with         |set rangecomplete FALSE
          ||  rangecomplete set to next |                            |  rangecomplete FALSE       |set start pg=end pg
          ||  pg and skip over next pg  |                            |else                        |
          ||else                        |                            |  print out previous page   |
          ||  print out prev pg         |                            |  set roman TRUE            |
          ||  set roman TRUE            |                            |  set range TRUE            |
          ||  set range FALSE           |                            |  set rangecomplete FALSE   |
          ||  set page number           |                            |  set start pg=end pg       |
----------||----------------------------|----------------------------|----------------------------|-----------------------------
3. !range ||(3,1)                       |(3,2)                       |(3,3)                       |(3,4)
   !roman ||cannot happen               |if prev pg+1=cur pg&&there  |cannot happen               |if prev pg+1=cur pg&&there
          ||                            |is a next pg&&next pg-1=    |                            |is a next pg
          ||                            |cur pg,                     |                            |  make a range with
          ||                            |  make a range with         |                            |  rangecomplete FALSE
          ||                            |  rangecomplete set to next |                            |else
          ||                            |  pg and skip over next pg  |                            |  print out prev pg
          ||                            |else                        |                            |  set roman FALSE
          ||                            |  print out prev pg         |                            |  set range TRUE
          ||                            |  set roman FALSE           |                            |  set rangecomplete FALSE
          ||                            |  set range FALSE           |                            |
          ||                            |  set page number           |                            |
----------||----------------------------|----------------------------|----------------------------|-----------------------------
4. range  ||(4,1)                       |(4,2)                       |(4,3)                       |(4,4)
   roman  ||if prev pg rangecomplete    |write out prev pg           |if range !complete || last  |write out prev pg
          ||  if prev pg+1=cur pg       |set roman FALSE             |pg of prev range+1=this pg  |set roman FALSE
          ||    adjust new end pg       |set range FALSE             |  set range !complete       |set range TRUE
          ||  else                      |set page number             |else                        |set rangecomplete FALSE
          ||    write out prev pg       |                            |  print out prev pg         |set start pg=end pg
          ||    set roman TRUE          |                            |  set roman TRUE            |
          ||    set range FALSE         |                            |  set range TRUE            |
          ||  endif                     |                            |  set rangecomplete FALSE   |
          ||else                        |                            |  set start pg=end pg       |
          ||  set prev pg rangecomplete |                            |                            |
          ||  set end pg                |                            |                            |
----------||----------------------------|----------------------------|----------------------------|-----------------------------
5. range  ||(5,1)                       |(5,2)                       |(5,3)                       |(5,4)
   !roman ||cannot happen               |if prev pg rangecomplete    |cannot happen               |if range !complete or last pg
          ||                            |  if prev pg+1=cur pg       |                            |of prev range+1=this pg,
          ||                            |    adjust new end pg       |                            |  set range !complete
          ||                            |  else                      |                            |else
          ||                            |    write out prev pg       |                            |  print out prev range
          ||                            |    set roman FALSE         |                            |  set roman FALSE
          ||                            |    set range FALSE         |                            |  set range TRUE         
          ||                            |  endif                     |                            |  set rangecomplete FALSE
          ||                            |else                        |                            |  set start pg=end pg
          ||                            |  set prev pg rangecomplete |                            |
          ||                            |  set end pg                |                            |
----------||----------------------------|----------------------------|----------------------------|-----------------------------
*/
printindexentries(base, noelements, level)
struct IndexEntry	*base;
int			noelements;
int			level;
{
    int             i, j;
    Boolean         prevoutput = FALSE;
    char            c;
    char            letter = '\0';

    if (level > ITEMDEPTH)
	return;
    for (i = 0; i < noelements; i++) {
	if (level == 1)
	    if (strlen(base[i].alphabetic) > 0)
		if (isalpha(base[i].alphabetic[0])) {
		    if (isupper(c = base[i].alphabetic[0]))
			c = tolower(c);
		    if (!letter) {
			if (Label) {
			    fprintf(OutputFile, "\\indexspace\n");
			    fprintf(OutputFile, "\\largeletter{%c}\n", c);
			} else if (prevoutput)
			    fprintf(OutputFile, "\\indexspace\n");
		    } else if (letter != c) {
			fprintf(OutputFile, "\\indexspace\n");
			if (Label)
			    fprintf(OutputFile, "\\largeletter{%c}\n", c);
		    }
		    letter = c;
		}
	prevoutput = TRUE;
	for (j = 1; j < level; j++)
	    fprintf(OutputFile, "  ");
	fprintf(OutputFile, "%s %s ", ItemRep[level - 1], base[i].literal);
	j = 0;
	if (base[i].pagenos)
	    for (j = 0; j < base[i].pagetablecount; j++) {
		if (j == 0)
		    if (!base[i].pagenos[j].range)
		        if (base[i].pagenos[j].roman) {
			    /* (1,1) */
			    State.roman = TRUE;
			    State.range = FALSE;
			    State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
			} else {
			    /* (1,2) */
			    State.roman = State.range = FALSE;
			    State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
			}
	            else
		        if (base[i].pagenos[j].roman) {
			    /* (1,3) */
			    State.roman = State.range = TRUE;
			    State.rangecomplete = FALSE;
			    State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
			} else {
			    /* (1,4) */
			    State.roman = State.rangecomplete = FALSE;
			    State.range = TRUE;
			    State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
			}
		else if (!State.range)
		    if (State.roman)
		        if (!base[i].pagenos[j].range)
			    if (base[i].pagenos[j].roman) {
				/* (2,1) */
				if (State.pagerange[0] + 1 == base[i].pagenos[j].number && j < base[i].pagetablecount - 1 &&
				base[i].pagenos[j].number == base[i].pagenos[j+1].number - 1 && base[i].pagenos[j+1].roman) {
				    State.roman = State.range = TRUE;
				    State.rangecomplete = ! base[i].pagenos[j+1].range;
				    State.pagerange[1] = base[i].pagenos[j+1].number;
				    j++;
				} else {
				    printstate(State, ItemRep[level-1], base[i].literal, TRUE);
				    State.roman = TRUE;
				    State.range = FALSE;
				    State.pagerange[0] = base[i].pagenos[j].number;
				}
			    } else {
				/* (2,2) */
				printstate(State, ItemRep[level-1], base[i].literal, TRUE);
				State.roman = State.range = FALSE;
				State.pagerange[0] = base[i].pagenos[j].number;
			    }
		        else
			    if (base[i].pagenos[j].roman) {
				/* (2,3) */
				if (State.pagerange[0] + 1 == base[i].pagenos[j].number && j < base[i].pagetablecount - 1 &&
				base[i].pagenos[j+1].roman) {
				    State.roman = State.range = TRUE;
				    State.rangecomplete = FALSE;
				    State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j-1].number;
				} else {
				    printstate(State, ItemRep[level-1], base[i].literal, TRUE);
				    State.roman = State.range = TRUE;
				    State.rangecomplete = FALSE;
				    State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
				}
			    } else {
				/* (2,4) */
				printstate(State, ItemRep[level-1], base[i].literal, TRUE);
				State.roman = State.rangecomplete = FALSE;
				State.range = TRUE;
				State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
			    }
		    else
		        if (!base[i].pagenos[j].range)
			    if (base[i].pagenos[j].roman)
			        /* (3,1) */ ;
		            else {
				/* (3,2) */
				if (State.pagerange[0]+1 == base[i].pagenos[j].number && j < base[i].pagetablecount-1 &&
			        base[i].pagenos[j].number == base[i].pagenos[j+1].number-1) {
				    State.roman = FALSE;
				    State.range = TRUE;
				    State.rangecomplete = ! base[i].pagenos[j+1].range;
				    State.pagerange[1] = base[i].pagenos[j+1].number;
				    j++;
				} else {
				    printstate(State, ItemRep[level-1], base[i].literal, TRUE);
				    State.roman = State.range = FALSE;
				    State.pagerange[0] = base[i].pagenos[j].number;
				}
			    }
		        else
			    if (base[i].pagenos[j].roman)
			        /* (3,3) */ ;
		            else {
				/* (3,4) */
				if (State.pagerange[0]+1 == base[i].pagenos[j].number && j < base[i].pagetablecount-1) {
				    State.roman = State.rangecomplete = FALSE;
				    State.range = TRUE;
				    State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j-1].number;
				} else {
				    printstate(State, ItemRep[level-1], base[i].literal, TRUE);
				    State.range = TRUE;
				    State.rangecomplete = State.roman = FALSE;
				    State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
				}
			    }
		else
		    if (State.roman)
		        if (!base[i].pagenos[j].range)
			    if (base[i].pagenos[j].roman) {
				/* (4,1) */
				if (State.rangecomplete)
				    if (State.pagerange[1]+1 == base[i].pagenos[j].number)
				        State.pagerange[1]++;
				    else {
					printstate(State, ItemRep[level-1], base[i].literal, TRUE);
					State.roman = TRUE;
					State.range = FALSE;
					State.pagerange[0] = base[i].pagenos[j].number;
				    }
				else {
				    State.rangecomplete = TRUE;
				    State.pagerange[1] = base[i].pagenos[j].number;
				}
			    } else {
				/* (4,2) */
				printstate(State, ItemRep[level-1], base[i].literal, TRUE);
				State.roman = State.range = FALSE;
				State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
			    }
		        else
			    if (base[i].pagenos[j].roman) {
				/* (4,3) */
				if (!State.rangecomplete || State.pagerange[1]+1 == base[i].pagenos[j].number)
				    State.rangecomplete = FALSE;
				else {
				    printstate(State, ItemRep[level-1], base[i].literal, TRUE);
				    State.roman = State.range = TRUE;
				    State.rangecomplete = FALSE;
				    State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
				}
			    } else {
				/* (4,4) */
				printstate(State, ItemRep[level-1], base[i].literal, TRUE);
				State.roman = State.rangecomplete = FALSE;
				State.range = TRUE;
				State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
			    }
		    else
		        if (!base[i].pagenos[j].range)
			    if (base[i].pagenos[j].roman)
			        /* (5,1) */ ;
		            else {
				/* (5,2) */
				if (State.rangecomplete)
				    if (State.pagerange[1]+1 == base[i].pagenos[j].number)
				        State.pagerange[1]++;
				    else {
					printstate(State, ItemRep[level-1], base[i].literal, TRUE);
					State.roman = State.range = FALSE;
					State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
				    }
				else {
				    State.rangecomplete = TRUE;
				    State.pagerange[1] = base[i].pagenos[j].number;
				}
			    }
			else
			    if (base[i].pagenos[j].roman)
			        /* (5,3) */ ;
			    else {
				if (!State.rangecomplete || State.pagerange[1]+1 == base[i].pagenos[j].number)
				    /* (5,4) */
				    State.rangecomplete = FALSE;
				else {
				    printstate(State, ItemRep[level-1], base[i].literal, TRUE);
				    State.roman = State.rangecomplete = FALSE;
				    State.range = TRUE;
				    State.pagerange[0] = State.pagerange[1] = base[i].pagenos[j].number;
				}
			    }
	    }
        if (j > 0)
            printstate(State, ItemRep[level-1], base[i].literal, FALSE);
        else
            fprintf(OutputFile, "\n");
        if (base[i].subitem)
            printindexentries(base[i].subitem, base[i].subitemcount, level+1);   /* recursion */
    }
}