/************************************************************************/
/*   File:	Index.c            					*/
/************************************************************************/
/*   Developer:	TeXplorators Inc., Houston, Texas		        */
/*   PRODUCT  :	Index: Sort TeX index entries alphabetically		*/
/*   Module   :	index							*/
/*   Notes    :	This source code is compiled with Microsoft C 5.1       */
/************************************************************************/
/*
			SOFTWARE LICENSE AGREEMENT

1. PURPOSE
	This agreement recites the terms and conditions under which 
The TeXplorators Corporation ("TEXPLORATORS") agrees to grant you a 
free, nonexclusive, transferable license to use the INDEX software.

2. UNDERTAKINGS BY TEXPLORATORS
	2A. Ownership: Ownership of the INDEX software remains 
exclusively in TEXPLORATORS.
	2B. License Fee: TEXPLORATORS makes the INDEX software 
available to you free of any license fee.
	2C. Disclaimers: TEXPLORATORS DISCLAIMS ALL EXPRESS OR IMPLIED
WARRANTIES OF INDEX'S MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR
PURPOSE.
	2D. Liability: TEXPLORATORS' LIABILITY, IF ANY, IS LIMITED TO 
THE DISTRIBUTION FEE YOU PAID, AND DOES NOT INCLUDE SPECIAL AND/OR
CONSEQUENTIAL DAMAGES ARISING OUT OF, OR IN CONNECTION WITH, THE USE 
OF INDEX.  SINCE SOME STATES DISALLOW LIMITATION OR EXCLUSION OF 
LIABILITY, THIS LIMITATION OR EXCLUSION MAY NOT APPLY TO YOU.

3. UNDERTAKINGS BY YOU.
	3A. Proper Use: You agree to use the INDEX software in
compliance with the terms and conditions hereof.
	3B. Notices on Authorized Copies: You agree to take reasonable
precautions to preserve the propriety of the INDEX software by
duplicating verbatim, on all copies thereof, the notices of TEXPLORATORS'
interests recited thereon.  You agree to note that source code is 
available upon request, if you do not distribute source code with the
INDEX software.
	3C. Distribution: You are encouraged to freely distribute the
INDEX software, including source code, on or through any common media
including diskettes and electronic bulletin board systems.  You agree to
distribute this license with each and every copy so distributed, and to 
either distribute source code therewith or make source code available upon
request. You may charge a distribution fee to cover the cost of diskettes,
handling and shipping.
	3D. Modifications: You are authorized to make modifications to
INDEX, but these modifications must be conspicuously noted and dated
therewith.
	3E. License Fee: You may not charge a license fee for the
INDEX software, either in its original or modified form.  However, you
may charge a license fee for your software which is broader in scope
than INDEX, whereby INDEX is not a primary functional unit thereof.

4. SCOPE
	This Software License Agreement constitutes the entire agreement
between TEXPLORATORS and you regarding the INDEX software, and shall
be construed under the laws of the State of TEXAS.

					The TeXplorators Corporation
					By: Michael Spivak, President

Page*/
/*
  HISTORY:
   [03/07/1991] - Index now operates on a single .ndx file - there will be
                  .npg file. An error message will be issued if a
                  \Page{...}{...}{...} does not follow each entry.
                  Each entry buffer size is expanded to 300 symbols.
*/

/* Configuration and defines for Unix 05/08/1991, P. A. MacKay */

/* System-dependent defines */
#include "config.h"

/*------------------------------*/
/*	includes		*/
/*------------------------------*/
#include  <stdio.h>
#include  <ctype.h>
#include  <malloc.h>	
#include  <string.h>
#ifdef ANSI
#include  <stdlib.h>
#endif
#include  <memory.h>

/*------------------------------*/
/*	defines			*/
/*------------------------------*/

#define TRUE        1
#define FALSE       0

#define MAX_STRING    5120
#define MAX_ENTRIES   15000
#define MAX_DEFINES   200
#define MAX_ABBREVS   200
#define MAX_PAGEORDER 200
#define MAX_PAGEENTRY 500

#define SPACE       32
#define COMMA       ','
#define STAR        '*'
#define DQUOTE      34
#define BAR         '|'
#define LBRACE      123
#define RBRACE      125
#define RSLASH	    '\\'
#define MINUS       '-'
#define PLUS        '+'
#define NL	    '\n'
#define LBRACKET    '['
#define RBRACKET    ']'
#define LPAREN      '('
#define RPAREN      ')'
#define QUOTE       39
#define DOLLAR      '$'
#define ATSIGN      '@'
#define TILDE       '~'
#define EXCLAIM     '!'
#define CARET       '^'
#define USCORE      '_'
#define EQUAL       '='
#define COLON       ':'
#define SEMI        ';'
#define LQUOTE      '`'
#define GREATER     '>'
#define LESS        '<'
#define QUESTION    '?'
#define SLASH       '/'
#define PERIOD      '.'

#define ZERO        '0'
#define ONE         '1'
#define TWO	    '2'
#define THREE       '3'
#define FOUR        '4'
#define FIVE        '5'

#define ENTRY1       1
#define ENTRY2       4
#define ENTRY3       7
#define ENTRY4      10
#define ENTRY5      13

#define PRE_TYPE     0
#define POST_TYPE    1
#define ALIAS_TYPE   2
#define ENTRY_TYPE   3
#define XREF_TYPE    4
#define CROSS_TYPE   5
#define OTHER_TYPE   6

#define LEVEL0        0
#define LEVEL1        1
#define LEVEL2        2
#define LEVEL3        3
#define LEVEL4        4
#define LEVEL5        5

#define E1            1
#define E1_PRE        2
#define E1_POST       3
#define E1_ALIAS     23
#define E1_CS        28

#define E2            4
#define E2_PRE        5
#define E2_POST       6
#define E2_ALIAS     24
#define E2_CS        29

#define E3            7
#define E3_PRE        8
#define E3_POST       9
#define E3_ALIAS     25
#define E3_CS        30

#define E4           10
#define E4_PRE       11
#define E4_POST      12
#define E4_ALIAS     26
#define E4_CS        31

#define E5           13
#define E5_PRE       14
#define E5_POST      15
#define E5_ALIAS     27
#define E5_CS        32

#define XREF         16
#define NUMBER       17
#define CSNAME       18
#define PREFIX       19
#define POSTFIX      20
#define PAGE_SPAN    21
#define LARGEST      22
#define CTRLSEQ      33
#define CROSS_REF    34

/*------------------------------*/
/*	typedefs		*/
/*------------------------------*/
#ifdef MICROSOFT_C
typedef unsigned int   WORD;
#else
typedef unsigned short   WORD;
#endif
typedef unsigned char  BYTE;

typedef struct
{
  char *fields[35];
} FieldType;

typedef struct
{
  FieldType *item;
} EntryType;

typedef struct
{
  char *astr;
  char *xstr;
} AbbrvType;

typedef struct
{
  char *style;
  char *pre;
  char *post;
  BYTE order;
  BYTE nstyle;
  BYTE npre;
  BYTE npost;
} PageOrderType;

/*------------------------------*/
/*	data structures		*/
/*------------------------------*/
EntryType Entry[MAX_ENTRIES];	/* entries		    */
char      *define[MAX_DEFINES];	/* defines		    */
AbbrvType *abbrev[MAX_ABBREVS];	/* abbrevs		    */
PageOrderType *PO[MAX_PAGEORDER];/*page orders		    */
int       SubPageOrder[MAX_PAGEENTRY];
int       SortingOrder[35];     /* sorting order indices    */
int       FirstOrder;		/* First Sorting Order entry*/
int       LastOrder;		/* Last Sorting Order entry */
int       PageOrder[10];        /* Page order indices       */
int       PageOrderFirst;	/* Fist page order entry    */
int       PageOrderLast;	/* Last page order entry    */
char      PageType;		
int       PageIndex;
int       PageSpan;
int       Tindex;		/* Topage Index 	    */
char	  string[300];		/* working string buffer    */
FILE      *ndxfile;		/* index file		    */
FILE      *xdxfile;		/* sorted file		    */
FILE      *xxxfile;		/* temporary file	    */
char      ndxname[80];		/* .ndx file name	    */
char      xdxname[80];		/* .xdx file name	    */
char      tmpname[80];		/* .@d@ file name	    */
char	  idxname[80];		/* .idx file name	    */
BYTE      alias;		/* alias flag		    */
BYTE      xref;			/* xref flag		    */
BYTE      crossref;		/* crossref flag            */
BYTE      curEntry;		/* type of entry appears    */
WORD      nitems;		/* number of input lines    */
WORD      ndef; 		/* number of defs	    */
WORD      nabb;			/* number of abbrevs	    */
WORD      npo;			/* number of pageorders	    */
char      curLetter;		/* current letter	    */
char      preLetter;		/* previous letter	    */
BYTE      FirstEntry;		/* first entry flag	    */
char      pagenum[80];		/* page number		    */
char      prevnum[80];		/* previous page number	    */
BYTE      skippage;		/* skip page flag	    */
char      *nullstr="";		/* NULL string		    */
char      *NULLPTR;		/* NULL pointer		    */
char      *delstr="D";		/* Delete string	    */
char      *DELPTR;		/* DEL pointer		    */
BYTE      idxfile;              /* make idxfile		    */
char      msgbuf[1024];         /* message buffer	    */
int       curIndex;		/* current index	    */
char      arg1[300], arg2[300], arg3[300], arg4[300], arg5[300];

/*------------------------------*/
/*	functions prototypes	*/
/*------------------------------*/
#ifdef ANSI /* Use ANSI C prototypes */
void  beep(void);
void  BuildProc(void);
void  BuildString(int);

char *CheckAbbrev(char s[], char *q);
void  CheckAlias(int);
BYTE  CheckLevel(int cur, int idx, BYTE level);
int   CompareStrings(int i,int j);
int   CompareStringsPO(int i,int j);
void  copyString(int,int,char *);

void  doNDX(int,char *);
void  doNPG(int,char *);
void  DoNormal(int idx,char arg[]);
void  DoVariant(int idx, char arg[]);
int   DupPageFormat(int idx, char arg[]);
int   DupEntry(int idx, int cur);
int   DupXref(int idx, int cur);

void  error(char *);
void  exit(int);

int   FindLastT( int idx );
void  FreeEntry( int idx );
void  FreeMem(void);

void  GetAbbrevSpace(int);
void  GetArg(int idx,char arg[],BYTE pre,BYTE cs,BYTE entry,
             BYTE post,BYTE alias);
void  GetArgs(int idx,char *arg1,char *arg2,char *arg3,
              char *arg4,char *arg5);
BYTE  GetD2(int cur,int idx);
void  GetFieldSpace(int);
BYTE  GetPageNum(int idx);
int   GetPageOrder(int);
void  GetPageOrderSpace(int);
char *GetStringSpace(char *);
int   GetSubLevel(int, int);

void  InitProc(void);

void  OutputProc(int idx,char *arg1,char *arg2,char *arg3,
                 char *arg4,char *arg5);

int  main(int argc, char *argv[]);
void  MoveEntry( int from, int to );

void  PreProc(void);
void  printMSG(char *);
void  PrintErrorMSG(void);
void  PutPageNum(int idx);
void  PutByte( char c, FILE *f );
void  PutString( char string[], FILE *f );

void  ReduceSpaces(char *);
void  RemoveDuplicates( void );
void  RemoveEmptySet(char *str);
void  RemoveSpaces(char *);
void  RemoveTrailingSpace(char *str);

void  ScanEntry( char line[] );
char *SkipSpaces(char *p);
void  SkipSpacesReverse(int len, char *p);
void  SortPageNumber( int beg, int end );
void  SortProc(void);
void  strCopy(char *,char *,int);
void  SwapStrings(int cur, int nxt);
void  syntax_error(char *);

void  TeXOut(void);
void  TeXPut(int idx);

void  xref_proc(int idx);
void  xxref_proc(int idx);
void  other_proc(int idx);
#else
/* Use old-style K&R function declarations */
void  beep();
void  BuildProc();
void  BuildString();

char *CheckAbbrev();
void  CheckAlias();
BYTE  CheckLevel();
int   CompareStrings();
int   CompareStringsPO();
void  copyString();

void  doNDX();
void  doNPG();
void  DoNormal();
void  DoVariant();
int   DupPageFormat();
int   DupEntry();
int   DupXref();

void  error();
void  exit();

int   FindLastT();
void  FreeEntry();
void  FreeMem();

void  GetAbbrevSpace();
void  GetArg();
void  GetArgs();
BYTE  GetD2();
void  GetFieldSpace();
BYTE  GetPageNum();
int   GetPageOrder();
void  GetPageOrderSpace();
char *GetStringSpace();
int   GetSubLevel();

void  InitProc();

void  OutputProc();

int main();
void  MoveEntry();

void  PreProc();
void  printMSG();
void  PrintErrorMSG();
void  PutPageNum();
void  PutByte();
void  PutString();

void  ReduceSpaces();
void  RemoveDuplicates();
void  RemoveEmptySet();
void  RemoveSpaces();
void  RemoveTrailingSpace();

void  ScanEntry();
char *SkipSpaces();
void  SkipSpacesReverse();
void  SortPageNumber();
void  SortProc();
void  strCopy();
void  SwapStrings();
void  syntax_error();

void  TeXOut();
void  TeXPut();

void  xref_proc();
void  xxref_proc();
void  other_proc();
#endif

/*------------------------------*/
/*	functions 		*/
/*------------------------------*/

void beep()
{
   printf("\007\b");
   return;
}

void FreeMem()
{
int i,j;
   for (i=0; i<=nitems; i++)
   {
     if ( Entry[i].item!=NULL )
     {
       for (j=E1; j<=CROSS_REF; j++)
       {
	if (Entry[i].item->fields[j]!=NULLPTR )
	    free( Entry[i].item->fields[j] );
       }
     }
   }
   for (i=0; i<MAX_DEFINES; i++)
	if ( define[i]!=NULL )
	     free( define[i] );
   for (i=0; i<MAX_ABBREVS; i++)
	if ( abbrev[i]!=NULL )
	{
	     if ( abbrev[i]->astr != NULL ) free( abbrev[i]->astr );
	     if ( abbrev[i]->xstr != NULL ) free( abbrev[i]->xstr );
	     free( abbrev[i] );
	}
	
   for (i=0; i<MAX_PAGEORDER; i++)
	if ( PO[i]!=NULL )
	{
	     if ( PO[i]->style !=NULL ) free( PO[i]->style );
	     if ( PO[i]->pre   !=NULL ) free( PO[i]->pre );
	     if ( PO[i]->post  !=NULL ) free( PO[i]->post );
	     free( PO[i] );
        }

   return;
}

void error(s)
char *s;
{
   beep();	
   printf("\n%s\n",s);
   FreeMem();
   exit(0);
}

void syntax_error(s)
char *s;
{ 
  sprintf(string,"syntax error! -> %s",s);
  error(string); 
}


void printMSG(s)
char *s;
{
   int i;
   printf("\r");
   for (i=0; i<78; i++)
       printf(" ");
   printf("\r");
   printf("%s",s);
   return;
}


void PutByte(c,f)
 char c; 
 FILE *f;
{
  fputc( c, f );
  if ( ferror(f) )
       error("! File write error - out of disk space");
} /* end - PutByte */


void PutString(s,f)
 char *s; 
 FILE *f;
{
   fputs( s, f );
   if ( ferror(f) )
       error("! File write error - out of disk space");
} /* end  - PutString */

/* Get Field Space */
void GetFieldSpace(idx)
int  idx;
{
int  i;
   
   /* Check for boundary conditions */
   if ( idx >= MAX_ENTRIES )
   {
      sprintf(string,"! number of entries (%d) exceeded MAXIMUM allowed (%d)",
	      idx,MAX_ENTRIES);
      error( string );
   }
   
   Entry[idx].item = (FieldType *) malloc( sizeof(FieldType) );
   if ( Entry[idx].item==NULL )
	error("! out of fields space");
   for (i=0; i<35; i++)
	Entry[idx].item->fields[i]=NULLPTR;
   return;
}


/* Get Abbrev Space */
void GetAbbrevSpace(idx)
int  idx;
{
   /* Check for boundary conditions */
   if ( idx >= MAX_ABBREVS )
   {
      sprintf(string,"! number of abbrevs (%d) exceeded MAXIMUM allowed (%d)",
	      idx,MAX_ABBREVS);
      error( string );
   }
   
   abbrev[idx] = (AbbrvType *) malloc( sizeof(AbbrvType) );
   if ( abbrev[idx]==NULL )
	error("! out of space");
   abbrev[idx]->astr = NULL;
   abbrev[idx]->xstr = NULL;
   return;
}

/* Get PageOrder Space */
void GetPageOrderSpace(idx)
int  idx;
{

   /* Check for boundary conditions */
   if ( idx >= MAX_PAGEORDER )
   {
      sprintf(string,
      "! number of pageorders directives (%d) exceeded MAXIMUM allowed (%d)",
	      idx,MAX_PAGEORDER);
      error( string );
   }
  
   PO[idx] = (PageOrderType *) malloc( sizeof(PageOrderType) );
   if ( PO[idx]==NULL )
	error("! out of space");
   PO[idx]->style = NULL;
   PO[idx]->pre   = NULL;
   PO[idx]->post  = NULL;
   PO[idx]->order    = 0;
   PO[idx]->nstyle   = 0;
   PO[idx]->npre     = 0;
   PO[idx]->npost    = 0;
   return;
}


/* Get String Space */
char * GetStringSpace(str)
char *str;
{
char *p;
int  i;
   i = strlen(str);
   p = (char *) malloc( i+1 );
   if ( p==NULL )
	error("! out of strings space");
   return(p);
}


void InitProc()
{
  int i;
  for (i=0; i<MAX_ENTRIES; i++)
       Entry[i].item = NULL;
  for (i=0; i<MAX_DEFINES; i++)
       define[i] = NULL;
  for (i=0; i<MAX_ABBREVS; i++)
       abbrev[i] = NULL;
  for (i=0; i<MAX_PAGEORDER; i++)
       PO[i] = NULL;
  FirstEntry = TRUE;	
  NULLPTR    = &nullstr[0];
  PageType   = 0;
  PageIndex  = 0;
  PageSpan   = FALSE;
  Tindex     = 0;
  
  SortingOrder[1] =E1;
  SortingOrder[2] =E1_ALIAS;
  SortingOrder[3] =E1_PRE;
  SortingOrder[4] =E1_POST;
  
  SortingOrder[5] =E2;
  SortingOrder[6] =E2_ALIAS;
  SortingOrder[7] =E2_PRE;
  SortingOrder[8] =E2_POST;
  
  SortingOrder[9] =E3;
  SortingOrder[10]=E3_ALIAS; 
  SortingOrder[11]=E3_PRE;
  SortingOrder[12]=E3_POST;
  
  SortingOrder[13]=E4;  
  SortingOrder[14]=E4_ALIAS;
  SortingOrder[15]=E4_PRE;
  SortingOrder[16]=E4_POST;
  
  SortingOrder[17]=E5;
  SortingOrder[18]=E5_ALIAS;  
  SortingOrder[19]=E5_PRE;  
  SortingOrder[20]=E5_POST;
  SortingOrder[21]=CROSS_REF;
 
  FirstOrder = 1;
  LastOrder  = 21;

  PageOrder[0] = CSNAME;
  PageOrder[1] = PREFIX;
  PageOrder[2] = NUMBER;
  PageOrder[3] = POSTFIX;
 
  PageOrderFirst = 0;
  PageOrderLast  = 3;
  
  return;
}

void ReduceSpaces(str)
char *str;
{
   char *p;
   char *q;

   p = str; q = str;
   while ( *p != 0 )
   {
      if ( (*p == SPACE) && (*(p+1) == SPACE) ) p++;
      else
          *q++ = *p++;
   }
   *q = *p;
   return;
}


void BuildString(idx)
int idx;
{
  int  i;
  char cbuf[300];
  char str[300];

  string[0] = 0;
  for (i=E1; i<NUMBER; i++)
  {
    switch( i )
    {
      case 2: case 5: case 8: case 11: case 14:
      case 3: case 6: case 9: case 12: case 15:	
	   if ( Entry[idx].item->fields[i]!=NULLPTR )
	   {
                memcpy(cbuf,Entry[idx].item->fields[i],
	               strlen(Entry[idx].item->fields[i])+1);
	   }
	   else cbuf[0]=0;
	   sprintf(str,"%s %c",cbuf,BAR);
	   strcat(string,str);
	   break;
      default:
	   if ( Entry[idx].item->fields[i]!=NULLPTR )
                memcpy(cbuf,Entry[idx].item->fields[i],
		       strlen(Entry[idx].item->fields[i])+1);
	   else cbuf[0] = 0;
	   sprintf(str,"%s %c",cbuf,BAR);
	   ReduceSpaces(&str[0]);    
	   strcat(string,str);
	   break;
      }
  }
  
  for (i=NUMBER; i<=CROSS_REF; i++)
  {
    if ( Entry[idx].item->fields[i]!=NULLPTR )
         memcpy(cbuf,Entry[idx].item->fields[i], 
	        strlen(Entry[idx].item->fields[i])+1);
    else cbuf[0]=0;
    sprintf(str,"%s%c",cbuf,BAR);
    ReduceSpaces(&str[0]);    
    strcat(string,str);
  }

  return;
}

int GetSubLevel(n,op)
int n,op;
{
int  cur;	
   switch ( op )
   {
     case 'a':   cur = 22 + n; curEntry = ALIAS_TYPE; alias = TRUE; 
          break;
     case MINUS: cur = n*3 -1; curEntry = PRE_TYPE; 
          break;
     case PLUS:  cur = n*3;    curEntry = POST_TYPE; 
          break;
     case 'e':   cur = 27 + n; curEntry = ENTRY_TYPE; 
          break;
     default:    cur = 0; 
          break;
   }
  return(cur);
}

void CheckAlias(idx)
int idx;
{
char *p;

   if ( Entry[idx].item->fields[E1_ALIAS]!=NULLPTR )
   {
       p = Entry[idx].item->fields[E1];
       Entry[idx].item->fields[E1] = Entry[idx].item->fields[E1_ALIAS];
       Entry[idx].item->fields[E1_ALIAS] = p;
   }

   if ( Entry[idx].item->fields[E2_ALIAS]!=NULLPTR )
   {
       p = Entry[idx].item->fields[E2];
       Entry[idx].item->fields[E2] = Entry[idx].item->fields[E2_ALIAS];
       Entry[idx].item->fields[E2_ALIAS] = p;
   }

   if ( Entry[idx].item->fields[E3_ALIAS]!=NULLPTR )
   {
       p = Entry[idx].item->fields[E3];
       Entry[idx].item->fields[E3] = Entry[idx].item->fields[E3_ALIAS];
       Entry[idx].item->fields[E3_ALIAS] = p;
   }

   if ( Entry[idx].item->fields[E4_ALIAS]!=NULLPTR )
   {
       p = Entry[idx].item->fields[E4];
       Entry[idx].item->fields[E4] = Entry[idx].item->fields[E4_ALIAS];
       Entry[idx].item->fields[E4_ALIAS] = p;
   }

   if ( Entry[idx].item->fields[E5_ALIAS]!=NULLPTR )
   {
       p = Entry[idx].item->fields[E5];
       Entry[idx].item->fields[E5] = Entry[idx].item->fields[E5_ALIAS];
       Entry[idx].item->fields[E5_ALIAS] = p;
   }
   return;	
}


void strCopy(d,s,len)
char *d, *s;
int  len;
{
int  i;
   for (i=0; i<=len; i++)
        *(d+i) = *(s+i);
   return;	
}

void copyString(idx,cur,str)
int  idx,cur;
char *str;
{
 if ( cur == 0 ) return;	
    Entry[idx].item->fields[cur] = GetStringSpace( str );
    strcpy(Entry[idx].item->fields[cur], str );
    return;	
}

char *SkipSpaces(p)
char *p;
{
   while ( *p==SPACE && *p!=0 ) p++;	
   return(p);	
}

void SkipSpacesReverse(len,q)
int  len;
char *q;
{
   char *r;

   r = q-2;
   while ( *r==SPACE && len>0 ) 
   { r--; len--; }
   r++; *r = 0;
   
   return;	
}

void RemoveSpaces(str)
char *str;
{
   char *p;
   char *q;

   p = q = str; 
   while ( *p != 0 )
   {
      if ( (*p == SPACE) ) p++;
      else
          *q++ = *p++;
   }
   *q = *p;
   return;
}

void RemoveEmptySet(str)
char *str;
{
   char *p;
   char *q;   

   if ( strlen(str) <= 2 ) return;
   p = str; q = str;
   while ( *p != 0 )
   {
      if ( (*p == LBRACE) && (*(p+1) == RBRACE) ) p+=2;
      else
          *q++ = *p++;
   }
   *q = *p;
   return;
}

void RemoveTrailingSpace(str)
char *str;
{
   char *p;
   char *q;

   p = str; q = str;
   while ( *p != 0 )
   {
      if ( (*p == SPACE) && (*(p+1) == RBRACE) ) p++;
      else
          *q++ = *p++;
   }
   *q = *p;
   return;
}

void doNDX(idx,str)
int  idx;
char str[];
{
   int  i,j;
   char *p, *q, *r;
   int  cur,e;
   BYTE done;

   j = strlen(str);
   p = &str[0]; p++; q = r = &string[0];
   i = 0; e = 1; cur = E1;
   xref = FALSE; alias = FALSE; crossref = FALSE;
   done = FALSE; curEntry = OTHER_TYPE;
   while ( !done && i<j )
   {
      switch ( *p )
      {
	case DQUOTE:
	     if ( *(p-1)==RSLASH ) 
                  *q++ = *p;		
	     else
		 done = TRUE;
	     break;
        case NL:
	     done = TRUE;
	     break;
        case STAR:
             *q++ = 0; 
             if ( cur!=0 )
	     {
               SkipSpacesReverse(j,q);
	       copyString(idx,cur,string); 
	       cur = 0;
             }
	     q = r; 
	     p++; i++;
             switch ( *p )
	     {
	       case ONE: case TWO: case THREE: case FOUR: case FIVE:
		    cur = GetSubLevel( (*p)-ZERO,*(p+1) ); p++; i++;
	            q = r;
		    break;
               case 'a':  
		    p++; i++; q = r;
		    curEntry = ALIAS_TYPE;
		    alias = TRUE;
		    cur = E1_ALIAS;
		    break;
 	       case MINUS:
		    p++; i++; q = r;
	            curEntry = PRE_TYPE;
		    cur = E1_PRE;
		    break;
 	       case PLUS:
		     p++; i++; q = r;
	             curEntry = POST_TYPE;
		     cur = E1_POST;
		     break;
	       case 'e': 
		    q = r;
		    curEntry = ENTRY_TYPE;
		    cur = E1_CS;
		    break;
	       case 'p': 
		    q = r;
	            cur = CTRLSEQ;
		    break;
	       case 'f':  
	       case 'F':
	       case 't': 
		    q = r;
		    *q++ = *p; *q++ = 0;
		    copyString(idx,PAGE_SPAN,string);
		    q = r;
		    break;
	       case 'x': 
		    if ( crossref )
		    {
		       sprintf(msgbuf,"%s\n *x and *X can't occur together\n",
			       str);
		       syntax_error(msgbuf);
	            }
	            strcpy(string,"x");    copyString(idx,XREF,string);
		    strcpy(string,"99999"); copyString(idx,NUMBER,string);
		    curEntry = XREF_TYPE; xref = TRUE;
		    q = r;
		    cur = CROSS_REF;
		    break;
	      case 'X':
		    if ( xref )
		    {
		       sprintf(msgbuf,"%s\n *x and *X can't occur together\n",
			       str);
		       syntax_error(msgbuf);
	            }
                   strcpy(string,"X");  copyString(idx,XREF,string);
		   crossref = TRUE;
	           cur = CROSS_REF; curEntry = XREF_TYPE;
		   q = r;
		   break;
	      case RSLASH:
		    cur = 0; break;
	      default: 
		    syntax_error(str);
		    cur = 0;
	            break;
	     }
	     break;
        case COMMA:
             p++; i++; *q++ = 0;
             copyString(idx,cur,string);
	     q = r;
	     e++; cur = e*3 - 2;
	     break;
       case LBRACE:
	    switch ( curEntry )
	    {
		case ALIAS_TYPE:	
		case PRE_TYPE:
		case POST_TYPE:
		case ENTRY_TYPE:
		case XREF_TYPE:
		case CROSS_TYPE:			
		     break;
	        default:
		     *q++ = *p; break;
	    }
	    break;
       case RBRACE: 
	    switch ( curEntry )
	    {
		case ALIAS_TYPE:	
		     alias = FALSE; 
		case PRE_TYPE:
		case POST_TYPE:
		case ENTRY_TYPE:
		case XREF_TYPE:
		case CROSS_TYPE:			
                     *q++ = 0; 
                     ReduceSpaces(string);
	             copyString(idx,cur,string); 
	             q = r; 
		     curEntry = OTHER_TYPE; cur = 0;
		     break;
	        default:
		     *q++ = *p; break;
	    }
	    break;
       case RSLASH:
	    if ( *(p+1) == COMMA)
            {
	       p++; i++; *q++ = *p;
	    }
	    else
	      *q++ = *p;
            break;
       default:
	    *q++ = *p;
	    break;
       }
       p++; i++;
    }
   
   *q++ = 0;
   if ( cur!=0 )
   {
      SkipSpacesReverse(j,q);
      copyString(idx,cur,string); 
      cur = 0;
   }
 
   /* post processing of ndxfile */
   /* largest j with <entry_j>    */
   sprintf(string,"%d",e);
   copyString(idx,LARGEST,string);

   CheckAlias(idx);
   
   return;	
}


void doNPG(idx,str)
int  idx;
char str[];
{
   int  i,j,k;
   char *p, *q, *r;
   int  cur;
   char num[10],outstr[10];

   j = strlen(str);
   p = &str[0]; p++; q = r = &string[0]; i = 0; 
   while ( *p != RBRACE && i<j ) 
   {
      num[i++] = *p++;
   }
   num[i] = 0;
   strcpy(outstr,"0000");
   for (k=i; k>=0; k--)
   outstr[4-i+k] = num[k];
   for (k=0; k<5; k++)
   outstr[k];
   copyString(idx,NUMBER,outstr);

   p++; i++; cur = CSNAME; 
   while ( i<j && *p != NL )
   {
      switch ( *p )
      {
       case LBRACE:
	    if ( *(p+1) != RBRACE )
	         q = r;
	    else { p++; i++; cur++; }
	    break;
       case RBRACE: 
	    *q++ = 0;
	    copyString(idx,cur,string);
	    cur++;
	    break;
       default:
	    *q++ = *p;
	    break;
       }
       p++; i++;
   }
   return;	
}

char * CheckAbbrev(s,q)
char s[];
char *q;
{
  int  i,j,len;
  BYTE found;
  char *p;
  
  for (i=0, found=FALSE; i<nabb && !found; i++)
  {
    if ( stricmp(s,abbrev[i]->astr)==0 )
    {
	found = TRUE; p = abbrev[i]->xstr; len = strlen(abbrev[i]->xstr); 
	j = 0;
	while ( *p!=0 && j<len) 
	{
	   *q++ = *p++; j++;
	}
    }
  }
  if ( !found )
  { sprintf(string,"syntax error! '%s' - not an \\abbrev",s); error(string); }
  return ( q );
}



/* preprocess NDX file for defines, abbrevs, page orders */
void PreProc()
{
   char buf[300],str[300];
   int	i,j,len;
   char *p, *q, *r;
   BYTE done, status;

   printMSG("preprocessing...");
   ndef = nabb = npo = 0 ;
   fgets(buf,300,ndxfile);
   while ( !feof(ndxfile) )
   {  
     len = strlen(buf);
     p = &buf[0];
     if ( strnicmp(p,"\\define",7)==0 )
     {
       buf[len-1]=0;
       if ( ndef >= MAX_DEFINES )
       {
	  sprintf(string,
		  "! number of defines (%d) exceeded MAXIMUM allowed (%d)",
		  ndef,MAX_DEFINES);
	  error( string );
       }
       define[ndef] = GetStringSpace(p);
       strcpy(define[ndef],p);
       ndef++;
     }
     else
     {
	if ( strnicmp(p,"\\abbrev",7)==0 )
	{
	   buf[len-1]=0; p+=7;
	   GetAbbrevSpace(nabb);
	   j=0; q  = &buf[len-1];

	   i=0;  /* remove spaces */
	   while ( *p==SPACE && i<len )
	   { p++; i++; }

	   while ( *(p+j)!=LBRACE && *(p+j)!=SPACE && (p+j)<q ) j++; 
	   *(p+j) = 0;
	   abbrev[nabb]->astr = GetStringSpace(p);
	   strcpy(abbrev[nabb]->astr,p);

	   p += j+1; j = len-1;
	   i=0;  /* remove spaces */
	   while ( *p==SPACE && i<len )
	   { p++; i++; }

	   if ( *p==LBRACE ) p++;
	   
	   while ( *q!=RBRACE && q>p ) q--;
	   *q = 0;
	   abbrev[nabb]->xstr = GetStringSpace(p);
	   strcpy(abbrev[nabb]->xstr,p);
	   nabb++;
	}
	else
	{
           i=0;  /* remove spaces */
	   while ( *p==SPACE && i<len )
	   { p++; i++; }
	 
	   if ( strnicmp(p,"\\pageorder",10)==0 )
	   {
	     buf[len-1]=0; 
	     p+=10;
	     RemoveSpaces(p);
	     GetPageOrderSpace(npo);
	     /* get style */
	     j=0; q  = &buf[len-1];
	     while ( *(p+j)!=LBRACE && (p+j)<q ) 
	     {   j++; }
	     if ( (p+j) >= q )
	       syntax_error(buf);
	     *(p+j) = 0;
	     PO[npo]->style = GetStringSpace(p);
	     strcpy(PO[npo]->style,p);
	     PO[npo]->nstyle = (BYTE) strlen(p);

	     /* get pre page material */
	     p += j+1; j=0;
	     while ( *(p+j)!=RBRACE && (p+j)<q ) j++; 
	     if ( (p+j) >= q )
		syntax_error(buf);
	     *(p+j) = 0;
	     if ( j==0 )
	     { PO[npo]->pre = NULLPTR;  }
	     else
	     { PO[npo]->pre = GetStringSpace(p); strcpy(PO[npo]->pre,p); }
	     PO[npo]->npre = (BYTE) strlen(p);

	     /* get post page material */
	     p += j+2; j=0;
	     while ( *(p+j)!=RBRACE && (p+j)<q ) j++; 
	     if ( (p+j) >= q )
		syntax_error(buf);
	     *(p+j) = 0;
	     if ( j==0 )
	     { PO[npo]->post = NULLPTR; }
	     else
	     { PO[npo]->post = GetStringSpace(p); strcpy(PO[npo]->post,p); }
	     PO[npo]->npost = (BYTE) strlen(p);
	     
	     /* get page order */
	     p += j+1; 
	     if ( *p==LBRACE ) 
	     {	p++; q--; *q=0;  }
	     PO[npo]->order =(BYTE) atoi(p);
	     npo++;
           } /* endif page order */
	   else
	   {
	    if ( *p!=NL && *p!=SPACE )
	    {
	     q = &string[0];
	     done = FALSE; status = FALSE; i = 0;
	     *q++ = *p++;
	     while ( i<len )
	     {
	     switch ( *p )
	     {
	       case STAR:
		    if ( *(p+1)==RSLASH )
		    {
		      r = &str[0]; done = FALSE;
		      *r++ = *p++; *r++ = *p++;
		      while ( !done )
		      {
		       switch ( *p )
	               {
			case SPACE: case DQUOTE: case COMMA: case LBRACE:
			case RBRACE: case LBRACKET: case RBRACKET:
			case LPAREN: case RPAREN: case QUOTE: case DOLLAR:
			case TILDE: case EXCLAIM: case CARET:
			case MINUS: case USCORE: case PLUS: case EQUAL:
			case COLON: case SEMI: case LQUOTE: case GREATER:
			case LESS: case QUESTION: case SLASH: case BAR:
			case STAR: case RSLASH: case PERIOD:
			     *r++=0;
			     done = TRUE;
			     break;
		        default:
			     *r++ = *p++; i++;
		             break;
	               } /* switch */
	              }  /* while */
		      q=CheckAbbrev(str,q);
		    } /* if */
		    else
		    {  *q++ = *p++; i++; }
		    break;
	       default:
		    *q++ = *p++; i++;
		    break;
	     } /* switch */
          } /* while */
	  *q++ = 0;
	  PutString( string, xxxfile );
          } /* not NL or SPACE */
        } /* others */
       } /* else pageorders */
     } /* else abbrevs */
     fgets(buf,300,ndxfile);
   } /* while */
   return;	
}




/**********************************************************************/
/* BuildProc: read in contents of .ndx file into internal data buffers*/
/* Inputs   : .ndx file handle                                        */
/* Output   : Entries                                                 */
/**********************************************************************/
#ifndef SEEK_END
#define SEEK_END 2
#define SEEK_CUR 1
#define SEEK_SET 0
#endif
void BuildProc( void )
{
   char abuf[300];
   char bbuf[300];
   int  i;

   fseek(xxxfile, 0L, SEEK_SET);
   nitems = 0;
   fgets(abuf,300,xxxfile);
   while( !feof(xxxfile) )
   {
      nitems++;
      fgets( abuf,300,xxxfile );
   }

   nitems/=2;
   fseek(xxxfile, 0L, SEEK_SET);
   GetFieldSpace(0);

   for (i=1; i<=nitems; i++ )
   {
     fgets(abuf,300,xxxfile);
     fgets(bbuf,300,xxxfile);
     
     if ( bbuf[0]!='{' )
     {
          sprintf( msgbuf,"\n! Entry ->\n%s does not have a following \"{...}{...}{...}\" line.", abuf );
	  error( msgbuf );
     }
     
     GetFieldSpace(i);
     ScanEntry( abuf );
     doNDX(i,abuf);	
     doNPG(i,bbuf);
     
   };

   return;
}



/**********************************************************************/
/* ScanEntry: scan entry line and delete entry delimiters             */
/* Inputs   : line                                                    */
/* Output   : line                                                    */
/**********************************************************************/
void ScanEntry( char line[] )
{
int   len,i;
  if ( line[0] == RSLASH )
  {
     sprintf( msgbuf,"Entry ->\n%s begins with a '\\'.", line );
     error( msgbuf );
  }
  else
    line[0] = SPACE;
  
  len = strlen( line );
  i = strlen(line) - 2;
  while( i>0 && line[i] == SPACE )
  {
    i--;
  }
  line[i] = SPACE;
  line[i] = NL;
  return;
  
}



int PageOrderSort(i,j)
int i, j;
{
int stat,n,k;
int i_order, j_order;

    i_order=GetPageOrder(i);
    j_order=GetPageOrder(j);

    stat = 0;
    for (n=PageOrderFirst; n<=PageOrderLast && stat==0; n++)
    {
	 k = PageOrder[n];
	 stat=strcmp(Entry[i].item->fields[k],Entry[j].item->fields[k]);
	 switch ( k )
	 {	
           case CSNAME:
	   case PREFIX:
	     if ( stat!= 0 ) 
	     {
	       if ( i_order < j_order ) return( -1 );
	       if ( i_order > j_order ) return(  1 );
	       return ( stat );
             }
             break;
	   case NUMBER:
	     if ( stat != 0 ) 
	     {
		  if ( (i_order == j_order) && (i_order == MAX_PAGEORDER) )
		       return( stat );
		  if ( (i_order == MAX_PAGEORDER) && (j_order < i_order) )
		       return( 1 );
	          if ( (j_order == MAX_PAGEORDER) && (i_order < j_order) )
		       return( -1 );
		  return( stat );
	     }
	     else
	     {
	        if ( i_order < j_order ) return( -1 );
	        if ( i_order > j_order ) return(  1 );
	     }
	     break;
	   case POSTFIX:
	     if ( stat!= 0 ) 
	     {
	       if ( i_order < j_order ) return( -1 );
	       if ( i_order > j_order ) return(  1 );
	       return ( stat );
             }
             break;
	   default:
             break;
        } /* switch */

     } /* for */
   return( stat );	
}

int CompareStrings(i,j)
int  i,j;
{
int n,k;	
int stat;

   stat=0;
   for (n=FirstOrder; n<=LastOrder && stat==0; n++)
   {
     k = SortingOrder[n];
     stat=stricmp(Entry[i].item->fields[k],Entry[j].item->fields[k]);
   }

   if ( stat==0 )
   {
      stat = PageOrderSort(i,j);
   } /* if */
   
   return( stat );
}


int GetPageOrder(idx)
int idx;
{
int i,done;	
int order;
   order = MAX_PAGEORDER;
   for (i=0,done=FALSE; i<npo && !done; i++)
   {
     if (  strncmp(Entry[idx].item->fields[CSNAME],PO[i]->style,
	            PO[i]->nstyle)==0
	&& strncmp(Entry[idx].item->fields[PREFIX],PO[i]->pre,
		    PO[i]->npre)==0
	&& strncmp(Entry[idx].item->fields[POSTFIX],PO[i]->post,
		    PO[i]->npost)==0 )
     {
       order = PO[i]->order; done = TRUE;
     }
   }
   return(order);
}


void SwapStrings(cur,nxt)
int  cur,nxt;
{
   int   k;
   char *p;
   for (k=E1; k<=CROSS_REF; k++)
   {
	p = Entry[cur].item->fields[k];
	Entry[cur].item->fields[k] = Entry[nxt].item->fields[k];
	Entry[nxt].item->fields[k] = p;
   }
   return;
}

void SortProc()
{
int  i,j,n;
FILE *f;
int  gap;

  printMSG("sorting...");
  
  /* Shell sort */
  n = nitems+1;
  for ( gap=n/2; gap>0; gap/=2 )
  {
     for ( i=gap; i<n; i++ )
     {
	for ( j=i-gap; j>0 && CompareStrings(j,j+gap) > 0; j-=gap )
	{
	    SwapStrings(j,j+gap);
	}
     }
  }
  
  /* write out sorted list - this is for debugging only */
  if ( idxfile )
  {
    f = fopen(idxname,"w");
    if ( f==NULL )
    { sprintf(string,"File write error %s",idxname); error(string); }
    for (i=1; i<=nitems; i++)
    { 
	BuildString(i); 
	PutString( string, f ); PutByte( NL, f);
    }
    fclose(f);
  }

  return;	
}


int CompareStringsPO(i,j)
int  i,j;
{
int stat;
   stat = PageOrderSort(i,j);
   return( stat );
}


/**********************************************************************/
/* Sort Page Numbers		                                      */
/* Inputs: beginning entry, ending entry                              */
/* Output: Sorted list of page numbers entries                        */
/**********************************************************************/
void SortPageNumber(beg,end)
 int beg, end;
{
int  i,j,k,n;
int  change,status;

  /* Check for boundary conditions */
  if ( ( end - beg )  >= MAX_PAGEENTRY )
  {
     sprintf(string,"! page line entries (%d) exceeded MAXIMUM allowed (%d)",
	     end-beg,MAX_PAGEENTRY );
     error( string );
  }
  
  n = 0;
  for (i=beg; i<end; i++)
  {
     if ( *(Entry[i].item->fields[XREF])!='x' )
     {
       SubPageOrder[n] = i; n++;
     }
  }

  /* Refined Bubble sort - for simplicity  
     Note: we only need to sort a subset of the entries. 
           Sorting is done using an array of indices without
	   physically swapping entries. */
  change = TRUE;	
  for (i=0; i<n && change; i++)
  {
     change = FALSE;
     for (j=0;j<n-1;j++)
     {
       status = CompareStringsPO(SubPageOrder[j],SubPageOrder[j+1]);
       if ( status>0 )
       { 
	 k = SubPageOrder[j+1];
	 SubPageOrder[j+1] = SubPageOrder[j];
	 SubPageOrder[j] = k;
	 change=TRUE; }
     }
  }

  return;
}


BYTE CheckLevel(cur,idx,level)
int  cur,idx;
BYTE level;
{
BYTE entry_i, pre_i, post_i, alias_i, cs_i;

  switch ( level )
  {
     case LEVEL1:
          entry_i = E1; pre_i = E1_PRE; post_i = E1_POST;
          alias_i = E1_ALIAS; cs_i = E1_CS;
	  break;
     case LEVEL2:
          entry_i = E2; pre_i = E2_PRE; post_i = E2_POST;
          alias_i = E2_ALIAS; cs_i = E2_CS;
	  break;
     case LEVEL3:
          entry_i = E3; pre_i = E3_PRE; post_i = E3_POST;
          alias_i = E3_ALIAS; cs_i = E3_CS;
	  break;
     case LEVEL4:
          entry_i = E4; pre_i = E4_PRE; post_i = E4_POST;
          alias_i = E4_ALIAS; cs_i = E4_CS;
	  break;
     case LEVEL5:
          entry_i = E5; pre_i = E5_PRE; post_i = E5_POST;
          alias_i = E5_ALIAS; cs_i = E5_CS;
	  break;
  }
 
  if (  strcmp(Entry[cur].item->fields[entry_i],
	Entry[idx].item->fields[entry_i])==0
     && strcmp(Entry[cur].item->fields[pre_i],
	Entry[idx].item->fields[pre_i])  ==0
     && strcmp(Entry[cur].item->fields[post_i],
	Entry[idx].item->fields[post_i]) ==0
     && strcmp(Entry[cur].item->fields[alias_i],
	Entry[idx].item->fields[alias_i])==0
     && strcmp(Entry[cur].item->fields[cs_i],
	Entry[idx].item->fields[cs_i])   ==0
     )
     return(TRUE); 
  else
     return(FALSE);
}


BYTE GetD2(cur,idx)
int  cur;
int  idx;
{
  if ( CheckLevel( cur,idx,LEVEL1 ) )
  {
      if ( CheckLevel( cur,idx,LEVEL2 ) )	
      {
          if ( CheckLevel( cur,idx,LEVEL3)  )
	  {
	     if ( CheckLevel( cur,idx,LEVEL4 ) )
	     {
		 if ( CheckLevel ( cur,idx,LEVEL5 ) )
		      return(LEVEL5);
	         else return(LEVEL4);
	     }
	     else return(LEVEL3);
	  }
	  else return(LEVEL2);
      }
      else return(LEVEL1);
   }
   else return(LEVEL0);

   return(LEVEL0);	
}




void GetArg(idx,arg,pre,cs,entry,post,alias)
int  idx;
char arg[];
BYTE pre,cs,entry,post,alias;
{
   if ( Entry[idx].item->fields[alias]!=NULLPTR )
	 entry = alias;

   if ( Entry[idx].item->fields[cs]==NULLPTR )
   {
       if ( Entry[idx].item->fields[pre]==NULLPTR
	 && Entry[idx].item->fields[post]==NULLPTR )
            sprintf(arg,"{%s}",Entry[idx].item->fields[entry]);
       else
           sprintf(arg,"{{%s}{%s}{%s}}",Entry[idx].item->fields[pre],
	       Entry[idx].item->fields[entry],Entry[idx].item->fields[post]);
   }
   else
   {
       sprintf(arg,"{{%s}{%s{%s}}{%s}}",Entry[idx].item->fields[pre],
	       Entry[idx].item->fields[cs],
	       Entry[idx].item->fields[entry],Entry[idx].item->fields[post]);
   }

   RemoveEmptySet(arg);

   return;
}


void DoNormal(idx,arg)
int  idx;
char arg[];
{
char *p;
char str[80];
int  pn;

   pn = atoi(Entry[idx].item->fields[NUMBER]);
   strcpy( str, Entry[idx].item->fields[CSNAME] );
   p = &str[0];
   if ( strncmp(p,"\\arabic",7)==0 ) *p=0;

   if ( Entry[idx].item->fields[CTRLSEQ]==NULLPTR )
   {
      if ( Entry[idx].item->fields[PREFIX]==NULLPTR
	&& Entry[idx].item->fields[POSTFIX]==NULLPTR )
      {
	  if ( *p==0 )
  	       sprintf(arg,"{%d}",pn);
          else
  	       sprintf(arg,"{%s{%d}}",p,pn);		  
      }
      else
      {  
	  if ( *p==0 )
              sprintf(arg,"{{%s}{%d}{%s}}",
	              Entry[idx].item->fields[PREFIX],pn,
		      Entry[idx].item->fields[POSTFIX]);
	  else
              sprintf(arg,"{{%s}{%s{%d}}{%s}}",
                      Entry[idx].item->fields[PREFIX],p,pn,
		      Entry[idx].item->fields[POSTFIX]);
      }
   }
   else
   {
      if ( Entry[idx].item->fields[PREFIX]==NULLPTR 
	&& Entry[idx].item->fields[POSTFIX]==NULLPTR )
      {
	  if ( *p == 0 )
               sprintf(arg,"{%s{%d}}",Entry[idx].item->fields[CTRLSEQ],pn);
          else
               sprintf(arg,"{%s{%s{%d}}}",Entry[idx].item->fields[CTRLSEQ],
		       p,pn);
      }
      else
      {
	  if ( *p == 0 )
              sprintf(arg,"{%s{{%s}{%d}{%s}}}",
	              Entry[idx].item->fields[CTRLSEQ],
                      Entry[idx].item->fields[PREFIX],pn,
		      Entry[idx].item->fields[POSTFIX]);
	  else
             sprintf(arg,"{%s{{%s}{%s{%d}}{%s}}}",
		     Entry[idx].item->fields[CTRLSEQ],
                     Entry[idx].item->fields[PREFIX],p,pn,
		     Entry[idx].item->fields[POSTFIX]);
      }
   }
   return;
}


void DoVariant(idx,arg)
int  idx;
char arg[];
{
char *p;
int  pn;
char str[80];

   pn = atoi(Entry[idx].item->fields[NUMBER]);
   strcpy(str,Entry[idx].item->fields[CSNAME]);
   p = &str[0];
   if ( strncmp(p,"\\arabic",7)==0 ) *p=0;

   if ( Entry[idx].item->fields[PREFIX]==NULLPTR 
     && Entry[idx].item->fields[POSTFIX]==NULLPTR )
   {
      if ( *p==0 )
  	   sprintf(arg,"{%s }{%d}",Entry[idx].item->fields[CTRLSEQ],pn);
      else
  	   sprintf(arg,"{%s }{%s{%d}}",Entry[idx].item->fields[CTRLSEQ],p,pn);
   }
   else
   {  
      if ( *p==0 )
           sprintf(arg,"{%s }{{%s}{%d}{%s}}",
	           Entry[idx].item->fields[CTRLSEQ],
                   Entry[idx].item->fields[PREFIX],pn,
	           Entry[idx].item->fields[POSTFIX]);
      else
           sprintf(arg,"{%s }{{%s}{%s{%d}}{%s}}",
	           Entry[idx].item->fields[CTRLSEQ],
		   Entry[idx].item->fields[PREFIX],
	           p,pn,Entry[idx].item->fields[POSTFIX]);
   }
   return;
}


void  PrintErrorMSG()
{
int i;

   PutString( "%% ",xdxfile );
   for (i=0; i<72; i++) 
	PutByte('/',xdxfile);
   PutByte( NL, xdxfile );
   PutString("%%\n",xdxfile);
   PutString( msgbuf,xdxfile );
   PutString("%%\n",xdxfile);
   PutString("%% ",xdxfile); 
   for (i=0; i<72; i++) 
	PutByte('\\',xdxfile);
   PutByte( NL, xdxfile);

   return;
}


int DupPageFormat(idx,arg)
int idx;
char arg[];
{
int  i,level;
BYTE done;
char tmpbuf[300];

  for (i=idx-1, done=FALSE; i>0 && !done; i--)
  {
    level = GetD2(i,idx);
    if ( level==LEVEL5 ) 
    {
      if ( strcmp(Entry[idx].item->fields[NUMBER],
	        Entry[i].item->fields[NUMBER])==0 
	&&  strcmp(Entry[idx].item->fields[CTRLSEQ],
	           Entry[i].item->fields[CTRLSEQ])!=0 )
      {
       RemoveEmptySet(arg);
       sprintf(msgbuf,"%%  \\Page %s\n",arg);
       sprintf(tmpbuf,
	    "%%  Duplicate page number formatted differently\n");
       strcat(msgbuf,tmpbuf);
       PrintErrorMSG();
       done = TRUE;
       return( FALSE );
     } /* if */
   } /* if - level */
   else 
      done = TRUE;
  } /* for */
  return(TRUE);
}


/**********************************************************************/
/* Find The Last matching t entry                                     */
/* Inputs: index into current entry                                   */
/* Output: TRUE if found, FALSE if not                                */
/**********************************************************************/
int FindLastT(idx)
int idx;
{
int i,level,done;

  Tindex = 0; done = FALSE; 
  level = GetD2(idx-1,idx);
  for ( i=idx-1;
        ( i>0 ) && ( level==LEVEL5 ) && !done ;
	i-- )
  {
    done = TRUE;
    if (   strcmp(Entry[idx].item->fields[NUMBER],
                 Entry[i].item->fields[NUMBER])==0 
	&& strcmp(Entry[idx].item->fields[PAGE_SPAN],
                 Entry[i].item->fields[PAGE_SPAN])==0 )
    {
       Tindex = i; done = FALSE;
    }
    level = GetD2(i-1,idx);
    
  }

  if ( Tindex != 0 )
       return( TRUE );
  else
       return( FALSE );

}



BYTE GetPageNum(idx)
int  idx;
{
char type;
char arg[300];
int  status;
char tmpbuf[300];

   type = *(Entry[idx].item->fields[PAGE_SPAN]); skippage = FALSE;
   switch ( type )
   {
     case 'F':
	DoVariant(idx,arg);
	if ( PageType=='f' || PageType=='F' )
	{
          sprintf(msgbuf,"%%  \\Pagespan %s\n",arg);
	  sprintf(tmpbuf,
	          "%%  Page span starting at %s within span starting at %d\n",
		  arg,atoi(Entry[PageIndex].item->fields[NUMBER]));	
	  strcat(msgbuf,tmpbuf);
	  PrintErrorMSG();
	  skippage = TRUE;
	  return(FALSE);
	}
	
	PageType = type ; PageIndex = idx;
        PageSpan = TRUE;
    break;
  case 'f': 
      DoNormal(idx,arg);	  
      if ( PageType=='f' || PageType=='F' )
      {
       sprintf(msgbuf,"%%  \\Pagespan %s\n",arg);
       sprintf(tmpbuf,
	       "%%  Page span starting at %s within span starting at %d\n",
	        arg,atoi(Entry[PageIndex].item->fields[NUMBER]));	
       strcat(msgbuf,tmpbuf);
       PrintErrorMSG();
       skippage = TRUE;
       return(FALSE);
      }
      
      PageType = type ; PageIndex = idx;
      PageSpan = TRUE;
    break;
  case 't':
    switch ( PageType ) 
    {
      case 'F': 
	DoVariant(idx,arg); 
        break;
      case 'f': 
	DoNormal(idx,arg); 
        break;
      default: 
	DoNormal(idx,arg); 

        if ( FindLastT(idx)  )
	{
	 sprintf(msgbuf,"%%  \\Topage %s\n",arg);
	 sprintf(tmpbuf,
	        "%%  Span ending at %s also ending at {%s {%d}}\n",
		arg,
		Entry[Tindex].item->fields[CTRLSEQ],
		atoi(Entry[Tindex].item->fields[NUMBER]));
	 strcat(msgbuf,tmpbuf);
	 PrintErrorMSG();
	 skippage = TRUE;
         PageType = 0; PageIndex = 0; PageSpan = FALSE;
	 return(FALSE); 
	}
	
	sprintf(msgbuf,"%%  \\Topage %s\n",arg);
	sprintf(tmpbuf,
	        "%%  Page span ending at %s has no starting page\n",
		arg);
	strcat(msgbuf,tmpbuf);
	PrintErrorMSG();
	skippage = TRUE;
        PageType = 0; PageIndex = 0; PageSpan = FALSE;	   
	return(FALSE); 
      break;
    } /* switch */

    break;
  default: 
     DoNormal(idx,arg); 
     if ( PageSpan )
     {
       /* Drop it if within page span */
       status = stricmp(Entry[idx].item->fields[CTRLSEQ],
	                Entry[PageIndex].item->fields[CTRLSEQ]);
       if ( status == 0 )
	  return( FALSE );
       else
       {
         sprintf(msgbuf,
	         "%%  Page %s%s within span starting at {%s {%d}}\n",
		 Entry[idx].item->fields[CTRLSEQ],arg,
		 Entry[PageIndex].item->fields[CTRLSEQ],
		 atoi(Entry[PageIndex].item->fields[NUMBER]));
	PrintErrorMSG();
	skippage = TRUE;	
	return( FALSE );
       } /* else */
     } /* if */
     status = DupPageFormat(idx,arg);
     if ( !status )
	   return( FALSE );
     break;
  } /* switch */
   
  RemoveEmptySet(arg);

  strcpy(prevnum,pagenum);
  strcpy(pagenum,arg);
   
  return(TRUE);
 
}


void PutPageNum(idx)
int  idx;
{
char c;
char str[80];

   if ( skippage ) return;
   
   c =  *(Entry[idx].item->fields[PAGE_SPAN]) ;
   switch ( c )
   {
      case 'f':
	   sprintf(str,"\\Pagespan %s\n",pagenum);
	   PutString( str, xdxfile );
	   break;
      case 'F':
	   sprintf(str,"\\PageSpan %s\n",pagenum);
	   PutString( str, xdxfile );
	   break;
      case 't':
	   sprintf(str,"\\Topage %s\n",pagenum);
	   PutString( str, xdxfile );
	   if ( strcmp( Entry[idx].item->fields[CTRLSEQ],
		        Entry[PageIndex].item->fields[CTRLSEQ] ) !=0 )
	   {
		sprintf(msgbuf,"%%  \\Page span from {%s {%d}} to %s\n",
			Entry[PageIndex].item->fields[CTRLSEQ],
			atoi(Entry[PageIndex].item->fields[NUMBER]),
			pagenum);
		PrintErrorMSG();
	   }
           PageType = 0; PageIndex = 0; PageSpan = FALSE;
	   break;	      
        default:	      
	   sprintf(str,"\\Page %s\n",pagenum);
	   PutString( str, xdxfile );
           break;
   }
   return;
}


void GetArgs(idx,arg1,arg2,arg3,arg4,arg5)
int  idx;
char *arg1, *arg2, *arg3, *arg4, *arg5;
{
   GetArg(idx,arg1,E1_PRE,E1_CS,E1,E1_POST,E1_ALIAS);
   GetArg(idx,arg2,E2_PRE,E2_CS,E2,E2_POST,E2_ALIAS);
   GetArg(idx,arg3,E3_PRE,E3_CS,E3,E3_POST,E3_ALIAS);
   GetArg(idx,arg4,E4_PRE,E4_CS,E4,E4_POST,E4_ALIAS);
   GetArg(idx,arg5,E5_PRE,E5_CS,E5,E5_POST,E5_ALIAS);
   return;
}


int DupEntry( idx, cur )
int idx,cur;
{
  int i,k,stat;
  
 
  for (i=cur; i<idx; i++)
  {
    stat=0;
    
    for (k=E1; k<=CROSS_REF && stat==0; k++)
    {
     stat=stricmp(Entry[i].item->fields[k],Entry[idx].item->fields[k]);
    }
    
    if ( stat == 0 )
	 return( TRUE );
    
  } /* for */
  
  if ( stat == 0 )
       return( TRUE );
  else return ( FALSE );
  
} /* end - DupEntry */


int DupXref( idx, cur )
int idx,cur;
{
  int i;
  for (i=idx; i<cur; i++)
  {
     if ( stricmp( Entry[i].item->fields[CROSS_REF], 
	           Entry[cur].item->fields[CROSS_REF] ) == 0 )
	  return( TRUE );
  }
  
  return ( FALSE );
  
} /* end - DupXref */


void xxref_proc(idx)
int  idx;
{
int  i,d2,nxt,level,n;
BYTE done;
char xtype;
int  status;

   /* put out 1 cross reference */
   GetArgs(idx,arg1,arg2,arg3,arg4,arg5);
   d2 = GetD2(idx,idx-1);
   sprintf(msgbuf,"\\Entry %s%d%s%s%s%s%s\n",
           Entry[idx].item->fields[LARGEST],d2,arg1,arg2,arg3,arg4,arg5);
   PutString( msgbuf, xdxfile );
   
   sprintf(msgbuf,"\\Xref {\\See {%s}}\n",
	   Entry[idx].item->fields[CROSS_REF]);
   PutString( msgbuf, xdxfile );
   
   
   /* Gather all the cross references */
   nxt = 1; done = FALSE;
   while ( !done && (idx+nxt)<=nitems )
   {
     level = GetD2(idx,idx+nxt);
     xtype = (char) *(Entry[idx+nxt].item->fields[XREF]);
     if ( level==LEVEL5 && ( xtype=='X' || xtype=='x' ) )
     {
       if ( !DupXref(idx, idx+nxt) )
       {
            sprintf(msgbuf,"\\Morexref {\\See {%s}}\n",
	            Entry[idx+nxt].item->fields[CROSS_REF]);
	    PutString( msgbuf, xdxfile );
       }
       nxt++;
     }
     else
     {
        done = TRUE;
     }
   }

   SortPageNumber(idx,idx+nxt);
   n = 0;
   for ( i=idx; i<idx+nxt; i++)
   {
     xtype = (char) *(Entry[i].item->fields[XREF]);
     if ( xtype != 'x' )
     {
       status = GetPageNum(SubPageOrder[n]);
       if ( status ) PutPageNum(SubPageOrder[n]);
       n++;
     }
   }

   /* put out cross references again */
   sprintf(msgbuf,"\\Xref {\\See {%s}}\n",
           Entry[idx].item->fields[CROSS_REF]);
   PutString( msgbuf, xdxfile );
     
     
   for ( i=idx+1; i<idx+nxt; i++)
   {
     level = GetD2(i,idx);
     xtype = (char) *(Entry[i].item->fields[XREF]);
     if ( level==LEVEL5 && ( xtype=='X' || xtype=='x' ) )
     {
       if ( !DupXref(idx,i) )
       {
            sprintf(msgbuf,"\\Morexref {\\See {%s}}\n",
	            Entry[i].item->fields[CROSS_REF]);
	    PutString( msgbuf, xdxfile );
       }
     } /* if */
   } /* for */
   
   curIndex = idx + nxt;
   return;
} /* xxref_proc */


void xref_proc(idx)
int  idx;
{
int  nxt,level,d2,i;
BYTE done;
char xtype;
BYTE ctype;
   
   /* CASE I or CASE II */
   nxt = 1; done = FALSE; ctype = 1;
   while ( !done && (idx+nxt)<=nitems )
   {
     level = GetD2(idx+nxt,idx);
     xtype = (char) *(Entry[idx+nxt].item->fields[XREF]);
     if ( level==LEVEL5 && ( xtype=='X' || xtype =='x') )
     {
       if ( xtype == 'X' )
            ctype = 2;
       nxt++;
     }
     else
     {
        done = TRUE;
     }
   } /* while */

 /* CASE I */
 if ( ctype == 1 )
 {
   /* put out 1 cross reference */
   GetArgs(idx,arg1,arg2,arg3,arg4,arg5);
   d2 = GetD2(idx,idx-1);
   
   sprintf(msgbuf,"\\Entryxref %s%d%s%s%s%s%s{\\See {%s}}\n",
           Entry[idx].item->fields[LARGEST],d2,arg1,arg2,arg3,arg4,arg5,
 	   Entry[idx].item->fields[CROSS_REF]); 
   PutString( msgbuf, xdxfile );
   

   /* Gather up all the cross references */
   for ( i=idx+1; i<idx+nxt; i++ )
   {
     level = GetD2(i,idx);
     xtype = (char) *(Entry[i].item->fields[XREF]);
     if ( level==LEVEL5 && xtype=='x' )
     {
       if ( !DupXref(idx,i) )
       {
               sprintf(msgbuf,"\\Morexref {\\See {%s}}\n",
		       Entry[i].item->fields[CROSS_REF]);
	       PutString( msgbuf, xdxfile );
       }
     }
     else
     {
        done = TRUE;
     }
   } /* while */
   
   curIndex = idx + nxt;
   
 } /* if */
 
 /* CASE II */
 else
 {
   xxref_proc( idx );
 }
 
   return;
} /* end  - xref_proc */


void OutputProc(idx,arg1,arg2,arg3,arg4,arg5)
int  idx;
char *arg1, *arg2, *arg3, *arg4, *arg5;
{
int d2, status;

   d2 = GetD2(idx,idx-1);
   if ( d2==LEVEL5 )
   {
      if ( !DupEntry( idx, idx-1 ) )
      {
        status = GetPageNum(idx);
        if ( status )  PutPageNum(idx);
      }
   }
   else
   {
     sprintf(msgbuf,"\\Entry %s%d%s%s%s%s%s\n",
             Entry[idx].item->fields[LARGEST],d2,arg1,arg2,arg3,arg4,arg5);
     PutString( msgbuf, xdxfile );
     status = GetPageNum(idx);
     if ( status ) PutPageNum(idx);
   }
   return;	
} /* end - OutputProc */


void other_proc(idx)
int  idx;
{
int  i,d2,nxt,level,n;
BYTE done,first;
char xtype,ctype;
int  status;


   /* this is the last line */
   if ( idx+1 > nitems )
   {
     /* finish up and exits */
     GetArgs(idx,arg1,arg2,arg3,arg4,arg5);
     OutputProc(idx,arg1,arg2,arg3,arg4,arg5);
     curIndex++;
     return;
   }

   /* Get all the LEVEL5 entries */
   nxt = 1; done = FALSE; ctype = FALSE;
   while ( !done && (idx+nxt)<=nitems )
   {
     level = GetD2(idx+nxt,idx);
     xtype = (char) *(Entry[idx+nxt].item->fields[XREF]);
     if ( level==LEVEL5 )
     {
	if ( xtype == 'X' || xtype == 'x' )
	   ctype = TRUE;
        nxt++;
     }
     else
     {
        done = TRUE;
     }
   }

   if ( !ctype )
   {
     /* Not a cross reference, so we output line and return */
     GetArgs(idx,arg1,arg2,arg3,arg4,arg5);
     OutputProc(idx,arg1,arg2,arg3,arg4,arg5);
     curIndex++;
     return;
   }
   
   /* put out entry first */
   GetArgs(idx,arg1,arg2,arg3,arg4,arg5);
   d2 = GetD2(idx,idx-1);
   sprintf(msgbuf,"\\Entry %s%d%s%s%s%s%s\n",
           Entry[idx].item->fields[LARGEST],d2,arg1,arg2,arg3,arg4,arg5);
   PutString( msgbuf, xdxfile );

   /* Gather all the cross references */
   first = TRUE;
   for (i=idx+1; i<idx+nxt; i++ )
   {
     level = GetD2(i,idx);
     xtype = (char) *(Entry[i].item->fields[XREF]);
     if ( level==LEVEL5 && ( xtype=='X' || xtype=='x' ) )
     {
       if ( !DupXref(idx,i) )
       {
         if ( first )
         {
	   first = FALSE;
           sprintf(msgbuf,"\\Xref {\\See {%s}}\n",
	           Entry[i].item->fields[CROSS_REF]);
	   PutString( msgbuf, xdxfile );
         } /* if */
         else
	 {
           sprintf(msgbuf,"\\Morexref {\\See {%s}}\n",
	           Entry[i].item->fields[CROSS_REF]);
	   PutString( msgbuf, xdxfile );
         } /* else */
       } /* if - dupXref */
     } /* if - level */
   } /* for */


   /* We need to sort a subset of the entries base on page order,
      viz, entries with LEVEL5 */
   SortPageNumber(idx,idx+nxt);
   
   n = 0;
   for ( i=idx; i<idx+nxt; i++)
   {
     xtype = (char) *(Entry[i].item->fields[XREF]);
     switch ( xtype )
     {
      case 'x':
	   break;
      case 'X':
      default:
        status = GetPageNum(SubPageOrder[n]);
        if ( status ) PutPageNum(SubPageOrder[n]);
	n++;
	break;
    } /* switch */
   } /* for */

   /* put out cross references again */
   first  = TRUE;
   for ( i=idx+1; i<idx+nxt; i++)
   {
     level = GetD2(i,idx);
     xtype = (char) *(Entry[i].item->fields[XREF]);
     if ( level==5 && ( xtype=='X' || xtype=='x' ) )
     {
       if ( !DupXref(idx,i) )
       {
         if ( first )
         {
          sprintf(msgbuf,"\\Xref {\\See {%s}}\n",
	          Entry[i].item->fields[CROSS_REF]);
	  PutString( msgbuf, xdxfile );
	  first = FALSE;
         } /* if */
         else
	 {
          sprintf(msgbuf,"\\Morexref {\\See {%s}}\n",
	          Entry[i].item->fields[CROSS_REF]);
	  PutString( msgbuf, xdxfile );
         } /* else */
       } /* if */
      } /* if */
   } /* for */

   curIndex = idx + nxt;
   return;
} /* end - other_proc */


void TeXPut(idx)
int  idx;
{
char xtype;

    curLetter = (char) toupper(*(Entry[idx].item->fields[E1]));
    if ( curLetter != preLetter )
    {
         sprintf(msgbuf,"\n\\LETTER %c\n\n",curLetter);
	 PutString( msgbuf, xdxfile );
         preLetter = curLetter;
    }

    xtype = (char) *(Entry[idx].item->fields[XREF]);
    
    switch( xtype )
    {
       case 'x':
	    xref_proc(idx);
	    break;
       case 'X':
	    xxref_proc(idx);
	    break;
       default:
	    other_proc(idx);
	    break;
    }
    
    return;
} /* end - TeXPut */



void TeXOut()
{
  int  i;
  
  printMSG("writing output...");
  preLetter = 0;
  for (i=0; i<ndef; i++)
  {
     sprintf(msgbuf,"%s\n",define[i]);
     PutString( msgbuf, xdxfile );
  } /* for */

  curIndex = 1;
  while ( curIndex <= nitems )
  {
     TeXPut( curIndex ); 
  }
  return;
} /* end - TexOut */


/**********************************************************************/
/* Move Entries			                                      */
/* Inputs: from, to			                              */
/* Output: none					                      */
/**********************************************************************/
void MoveEntry(from,to)
 int from,to;
{
int k;

  /* return if both are the same entry */
  if ( from == to )
       return;

  for (k=E1; k<=CROSS_REF; k++)
  {
     Entry[to].item->fields[k] = Entry[from].item->fields[k];
  }
  
  return;
} /* end - MoveEntry */


/**********************************************************************/
/* Free Entry			                                      */
/* Inputs: idx				                              */
/* Output: none					                      */
/**********************************************************************/
void FreeEntry(idx)
 int idx;
{
int j;

  if ( Entry[idx].item!=NULL )
  {
    for ( j=E1; j<=CROSS_REF; j++ )
    {
     if ( Entry[idx].item->fields[j]!=NULLPTR )
	  free( Entry[idx].item->fields[j] );
     Entry[idx].item->fields[j] = NULLPTR;
    }
  }
  
  return;
  
} /* end - FreeEntry */


/**********************************************************************/
/* Remove Duplicates Entries	                                      */
/* Inputs: none				                              */
/* Output: modified nitems			                      */
/**********************************************************************/
void RemoveDuplicates()
{
  int i,j,cur,level;

  cur = 1;
  for ( i=2; i<=nitems; i++ )
  {
     if ( DupEntry( i, cur ) )
     {
	Entry[i].item->fields[0] = DELPTR;
     }
     level = GetD2(i,cur);
     if ( level!=LEVEL5 )
	  cur = i;
  }

  for ( i=1,j=1; i<=nitems; i++ )
  {
     if ( Entry[i].item->fields[0]==DELPTR )
     {
	FreeEntry(i);
     }
     else
     {
	MoveEntry(i,j);
	j++;
     }
  }
  
  nitems = --j;
  
  return;
} /* end - RemoveDuplicates */

/* make main() an int so it returns something predictable to the OS */
int main(argc, argv)
int  argc;
char *argv[];
{
	
  if ( argc<2 )
   {
      printf("\n\tUsage: index filename\n");
      exit(0);
   }
 
   printf("\n\tnewindex 1.10    All Rights Reserved.");
   printf("\n\tCopyright (c) 1989-91 The TeXplorators Corporation.\n");
      
   strcpy(ndxname,argv[1]); strcat(ndxname,".ndx");
   strcpy(tmpname,argv[1]); strcat(tmpname,".@d@");
   strcpy(xdxname,argv[1]); strcat(xdxname,".xdx");
   strcpy(idxname,argv[1]); strcat(idxname,".idx");

   idxfile = FALSE;
   if ( argc > 2 )
   {
      if ( strnicmp(argv[2],"-i",2)==0 )
	   idxfile = TRUE;
   }
    
   if ( ( ndxfile = fopen(ndxname,"r") )==NULL )
   {
      printf("File %s not found\r\n",ndxname);
      beep();
      exit(0);
   }
  
   if ( ( xxxfile = fopen(tmpname,"w+") )==NULL )
   {
     printf("File %s cannot be opened\r\n",tmpname);
     beep();
     exit(0);
   }

   if ( ( xdxfile = fopen(xdxname,"w") )==NULL )
   {
     printf("File %s cannot be opened\r\n",xdxname);
     beep();
     exit(0);
   }
   
   InitProc();

   PreProc();

   BuildProc();
   
   fclose(ndxfile);

   SortProc();
   
   RemoveDuplicates();

   TeXOut();
   
   fclose(xdxfile);
   fclose(xxxfile);
#ifdef MICROSOFTC
   fcloseall();
#endif

   unlink(tmpname);
   FreeMem();
   
   printMSG("");
   printf("\r\n");
   
   return(0);
}


/* ############################### END OF FILE ########################### */
