#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"heap.h"
#include"library.h"

#define PARSERIMPLEMENTATION
#include"parse.h"

#define RINGSIZE    15
#define MAXSTRING   256
#define MAXWORT     1022
#define MODESTACK   100
#define MINDESTLAENGE  1

#ifdef  DEBUG
#define storemode( mode )  { ringindex++; ringindex %= RINGSIZE; modering[ringindex] = mode; printf("%d ",modering[ringindex]);}
#else
#define storemode( mode )  { ringindex++; ringindex %= RINGSIZE; modering[ringindex] = mode;}
#endif

#define min(a,b)            ((a)<(b) ? (a) : (b))
#define is_a_letter(c)  ((('A'<=(c))&&((c)<='Z'))||(('a'<=(c))&&((c)<='z')))
#define islower( c )        ( ('a'<= ( c ))&&(( c ) <= 'z') )
#define isdigit( c )        ( ('0'<= ( c ))&&(( c ) <= '9') )
#define isupper( c )        ( ('A'<= ( c ))&&(( c ) <= 'Z') )
#define preprevious_mode    ( modering[ ringindex > 0 ? ringindex-1 : RINGSIZE-1 ] )
#define previous_mode       ( modering[ ringindex ] )
#define old_mode( age )     ( modering[ ringindex - age => 0 ? ringindex-age : RINGSIZE + ringindex - age ] )

enum catcode {
 catcode_escape,
 catcode_begingroup,
 catcode_endgroup,
 catcode_mathshift,
 catcode_alignmenttab,
 catcode_endofline,
 catcode_space,
 catcode_letter,
 catcode_other,
 catcode_active,
 catcode_comment,
 catcode_ignored,
 catcode_eof
};

enum parsemode {
 no_mode,          /* kein mode im mode-ring                   */
 common_mode,      /* normale buchstaben                       */
 escape_mode,      /* befehle                                  */
 parameter_mode,   /* untersuchung von environment-parametern  */
 space_mode,       /* skippen von leerzeichen                  */
 digit_mode,       /* behandeln von zahlen (mit dezimalpunkt)  */
 other_mode,       /* skippen von others ( z.b. '(' )          */
 skip_mode,        /* $$ werden geskipt und aehnliches         */
 comment_mode,     /* behandeln von kommentaren                */
 active_mode,      /* untersuchen von active-characters        */
 end_deletion_mode /* skippen der parameter von \end           */
};

struct token {
 enum catcode catcode;
 unsigned char token;
};


struct active_expansion{
 unsigned char of_what;
 unsigned char *what;
};


struct active_definition{
 struct active_definition *next;
 unsigned char character;
 int anzahl;
 struct active_expansion expansion[1];
};


/* Modulglobale variablen */

static enum parsemode modering[RINGSIZE];
static int ringindex;
static unsigned char punctuation_chars[50] = ".,:;!?";  /* characters, die hinter sich, aber nicht vor sich ein space wollen */
static unsigned char textbegingroup_chars[20] = "(";   /* characters, die vor sich, aber nicht hinter sich ein space wollen */
static unsigned char textendgroup_chars[20] = ")";   /* characters, die vor sich, aber nicht hinter sich ein space wollen */
static unsigned char sentenceend_chars[20]=".!?";
static int zeilenoffset;
static struct active_expansion default_expansion;
static struct active_definition *active_defs;
static struct active_definition *escape_def;
static struct active_definition escape_default;
static heap *active_heap;
static enum parsemode mode_stack[MODESTACK];
static enum parsemode *mode;                  /* pointer auf den mode_stack */
static struct token actualtoken;
static FILE *source;
static FILE *outputfile;
static const unsigned char *texname;
static struct token catcodes[256];
static library *skipped_env, *skipped_com, *delimiter_com;
static library *weird_capital;

/* prototypen  */

static struct token next_token(void);
static void exit_parser( void );

/* code */

void init_parser(FILE *infofile, FILE *sourcefile, FILE *outfile, const unsigned char *sourcename )
{

 unsigned char zeile[MAXSTRING];
 unsigned char keyword[MAXSTRING];
 outputfile = outfile;
 source = sourcefile;
 texname = sourcename;
 zeilennummer = 1;
 zeilenoffset = 0;


 /* initialisierung des mode-rings auf den jeweils die alten modes kommen */

 for ( ringindex = 0; ringindex < RINGSIZE; ringindex++ )
   modering[ringindex] = no_mode;
 ringindex = 0;

 escape_default.anzahl = 1;
 escape_default.next = NULL;
 default_expansion.of_what = '-';
 default_expansion.what = "\\-@";
 escape_default.expansion[0] = default_expansion;

 skipped_env = library_create( 500 );
 skipped_com = library_create( 500 );
 delimiter_com = library_create( 500 );
 weird_capital = library_create( 500 );  /* 1.11.91 */
 active_heap = heap_create( 500 );
 atexit( exit_parser );
 escape_def = &escape_default;
 active_defs = NULL;
 { /* init catcodes */
   unsigned char c;
   for ( c = 255; c >= ' '; c-- )
     {
       catcodes[c].catcode = catcode_other;
       catcodes[c].token = c;
     }
   for ( c = 1; c < ' '; c++ )
     catcodes[c].catcode = catcode_ignored;

   catcodes['\\'].catcode = catcode_escape;
   catcodes['{'].catcode = catcode_begingroup;
   catcodes['}'].catcode = catcode_endgroup;
   catcodes['$'].catcode = catcode_mathshift;
   catcodes['&'].catcode = catcode_alignmenttab;
   catcodes['\n'].catcode = catcode_endofline;
   catcodes['\n'].token = '\n';
   catcodes[' '].catcode = catcode_space;
   catcodes['\t'].catcode = catcode_space;
   catcodes['%'].catcode = catcode_comment;
   for ( c = 'A'; c <= 'Z'; c++ )
     catcodes[c].catcode = catcode_letter;
   for ( c = 'a'; c <= 'z'; c++ )
     catcodes[c].catcode = catcode_letter;
   catcodes[0].catcode = catcode_eof;
 } /* init catcodes */

 /* infofile-syntax
    <keyword>=<string>
    <keyword> = catcodes / skipped_environment / skipped_commands */

 while( fscanf( infofile, "%s\t%s\n", keyword, zeile ))
   {
     unsigned char *zeilepointer;
     zeilepointer = zeile;

     if (!strcmp(keyword,"escape"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_escape;
     else if (!strcmp(keyword,"begingroup"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_begingroup;
     else   if (!strcmp(keyword,"endgroup"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_endgroup;
     else  if (!strcmp(keyword,"mathshift"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_mathshift;
     else  if (!strcmp(keyword,"alignmenttab"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_alignmenttab;
     else  if (!strcmp(keyword,"endofline"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_endofline;
     else   if (!strcmp(keyword,"space"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_space;
     else  if (!strcmp(keyword,"letter"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_letter;
     else  if (!strcmp(keyword,"active"))
       {
         int i,anzahl;
         struct active_definition *ad;
     unsigned char expansion[80];
         catcodes[*zeilepointer++].catcode = catcode_active;
         anzahl = atoi( zeilepointer );
         ad = heap_allocate( active_heap, sizeof( struct active_definition ) + sizeof( struct active_expansion ) * ( anzahl - 1 ));
         ad->character = *zeile;
         ad->anzahl = anzahl;
         ad->next = active_defs;
         active_defs = ad;

         for ( i = 0; i < anzahl; i++ )
           {
             fscanf( infofile, "%c\t%s\n", &( ((ad->expansion)[i]).of_what), expansion );
             ((ad->expansion)[i]).what = heap_allocate( active_heap, strlen( expansion ) + 1 );
             strcpy( ((ad->expansion)[i]).what, expansion );
           }          /* for i < anzahl */
       }
     else  if (!strcmp(keyword,"escape_def"))
       {
         int i,anzahl;
     unsigned char expansion[80];
         anzahl = atoi( zeilepointer );
         escape_def = heap_allocate( active_heap, sizeof( struct active_definition ) + sizeof( struct active_expansion ) * ( anzahl - 1 ));
         escape_def->character = *zeile;
         escape_def->anzahl = anzahl;
         escape_def->next = NULL;

         for ( i = 0; i < anzahl; i++ )
           {
             fscanf( infofile, "%c\t%s\n", &( ((escape_def->expansion)[i]).of_what), expansion );
             ((escape_def->expansion)[i]).what = heap_allocate( active_heap, strlen( expansion ) + 1 );
             strcpy( ((escape_def->expansion)[i]).what, expansion );
           }          /* for i < anzahl */
       }
     else   if (!strcmp(keyword,"other"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_other;
     else  if (!strcmp(keyword,"ignored"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_ignored;
     else  if (!strcmp(keyword,"comment"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_comment;
     else  if (!strcmp(keyword,"escape"))
       while ( *zeilepointer ) catcodes[*zeilepointer++].catcode = catcode_escape;
     else  if (!strcmp(keyword,"skipped_command"))
       library_enter_entry( skipped_com, &zeile[1], 1 );
     else  if (!strcmp(keyword,"punctuation"))
       strcpy( punctuation_chars, zeile );
     else  if (!strcmp(keyword,"sentenceend"))
       strcpy( sentenceend_chars, zeile );
     else  if (!strcmp(keyword,"text_begingroup"))
       strcpy( textbegingroup_chars, zeile );
     else  if (!strcmp(keyword,"text_endgroup"))
       strcpy( textendgroup_chars, zeile );
     else  if (!strcmp(keyword,"capital"))  /* 1.11.91 */
       library_enter_entry( weird_capital, zeile, 1 );
     else  if (!strcmp(keyword,"skipped_environment"))
       library_enter_entry( skipped_env, zeile, 1 );
     else  if (!strcmp(keyword,"delimiter_command"))
       library_enter_entry( delimiter_com, &zeile[1], 1 );
     else if (!strcmp(keyword,"end_of_file"))break;
     else
       printf( "Invalid command %s ignored; check infofile\n\b", keyword);
   }  /* while fgets */
 actualtoken = next_token();
 mode = mode_stack+1;
 *mode = common_mode;
} /* init_parser */

static struct token next_token(void)
{
 unsigned char zeichen;
 for (;;)
  {
     zeichen = getc( source );
     if (feof(source)) break;
     if ( zeichen == '\n' ) zeilenoffset++;
     if (catcodes[zeichen].catcode != catcode_ignored ) return ( catcodes[zeichen] );
   }             /* while zeichen != EOF */
 return( catcodes[0] );
} /* next_token */



unsigned char *next_word( int *end_of_sentence )
{
 static unsigned char wortbuffer[MAXWORT + 2];
 unsigned char *wort;
 unsigned char *wortzeiger;
 unsigned char escapebuffer[MAXSTRING];
 unsigned char *escapezeiger;
 unsigned char parameterbuffer[MAXSTRING];
 unsigned char *parameterzeiger;
 unsigned char endbuffer[MAXSTRING];
 unsigned char *endzeiger;
 int wortlaenge = 0;

 escapezeiger = escapebuffer;
 end_of_sentence = &new_sentence;
 wort = &wortbuffer[2];
 wortzeiger = wort;
 wortlaenge = 0;
 zeilennummer += zeilenoffset;
 zeilenoffset = 0;

 while( 1 )
   {
     if ( wortzeiger < wort)
       {
         wortzeiger = wort;
         printf("%s:%d:wortunderflow\n",texname, zeilennummer );
       }
     if ( wortzeiger >= &wort[MAXWORT-1])
       {
         printf("%s:%d:wortoverflow\n",texname, zeilennummer );
         return(wort);
       }
     if ( mode <= mode_stack ) /* stack underflow verhindern */
       {
         zeilennummer += zeilenoffset;
         zeilenoffset = 0;
         fprintf( outputfile, "%s:%d: stack underflow\n",
                  texname, zeilennummer );
         mode = mode_stack + 1;
         *mode = common_mode;
       };
     if ( mode >= &mode_stack[ MODESTACK - 1] ) /* stack overflow */
       {
         zeilennummer += zeilenoffset;
         zeilenoffset = 0;
         fprintf( outputfile, "%s:%d: stack overflow\n",
                  texname, zeilennummer );
         mode = mode_stack + 1;
         *mode = common_mode;
       };


     switch( *mode )
       {
       case common_mode:      /* normale buchstaben */
         while( actualtoken.catcode == catcode_letter )
           {
             if (wortzeiger - wort >= MAXWORT) return(wort);
             *wortzeiger++ = actualtoken.token;
             wortlaenge++;
             actualtoken = next_token();
           }
         switch( actualtoken.catcode )
           {
           case catcode_escape:
             storemode( *mode );
             *++mode = escape_mode;   /* mode auf den stack  */
             if (wortzeiger - wort >= MAXWORT) return(wort);
             *wortzeiger++ = actualtoken.token;
             actualtoken = next_token();
             escapezeiger = escapebuffer; /* initialisierung des escapebuffers */
             break;
           case catcode_begingroup:
             storemode( *mode );
             *++mode = common_mode;
             actualtoken = next_token();
             break;
           case catcode_endgroup:
             storemode( *mode );
             --mode;
             actualtoken = next_token();
             break;
           case catcode_mathshift:
             /*                  *wortzeiger++ = actualtoken.token; */ /* mshift nicht an wort dran */
             actualtoken = next_token();
             break;
           case catcode_endofline:
           case catcode_space:
             storemode( *mode );
             *++mode = space_mode;
             *wortzeiger = 0;
             if ( wortlaenge > MINDESTLAENGE )
               return( wort );
             if ( actualtoken.token == '\n' )
               {
                 zeilennummer += zeilenoffset;
                 zeilenoffset = 0;
               }
             wortzeiger = wort; wortlaenge = 0;
             break;
           case catcode_alignmenttab:
           case catcode_other:
             storemode( *mode );
             *++mode = other_mode;
             *wortzeiger = 0;
             if ( wortlaenge > MINDESTLAENGE )
               return( wort );
             wortzeiger = wort; wortlaenge = 0;
             break;
           case catcode_active:
             storemode( *mode );
             *++mode = active_mode;
             break;
           case catcode_comment:
             storemode( *mode );
             *++mode = comment_mode;
             break;
           case catcode_ignored:
             ;
             break;
           case catcode_eof:
             return( NULL );

           }           /* local switch */
         break;


       case escape_mode:      /* befehle                                  */
         switch( actualtoken.catcode )
           {
           case catcode_space:
             *--wortzeiger = 0;          /* \\ = \n wortzeiger eins runter, um alten backslash zu tilgen  */
             storemode( *mode );
             *mode = space_mode;
             if ( wortlaenge > MINDESTLAENGE )
               return( wort );
             wortzeiger = wort; wortlaenge = 0;         /* neues wort anfangen  */
             break;
           case catcode_escape:
             *--wortzeiger = 0;          /* \\ = \n wortzeiger eins runter, um alten backslash zu tilgen  */
             actualtoken = next_token(); /* naechstes token holen */
             storemode( *mode );
             mode--;                     /* alten mode vom stack holen  */
             if ( wortlaenge > MINDESTLAENGE )
               return( wort );
             wortzeiger = wort; wortlaenge = 0;         /* neues wort anfangen  */
             break;
           case catcode_other:
             {
               int i = 0;
               while ( (((escape_def->expansion)[i]).of_what != actualtoken.token ) && ( i < escape_def->anzahl )) i++;

               /* jetzt haben wir die makroexpansion und schauen ob sie geskippt werden muss */

               if ( i == escape_def->anzahl ) ;
               else
                 {
           unsigned char string[80];
                   storemode( *mode );
                   mode--;
                   actualtoken = next_token();
                   strcpy( string,&(((escape_def->expansion)[i]).what[1])); /* kopieren ohne backslash */
                   string[strlen( string )-1] = 0;
                   if( library_find_entry( delimiter_com, string ))
                     {
                       *--wortzeiger = 0; /* eingefuegt 14.11.91 */
                       if ( wortlaenge > MINDESTLAENGE )
                         return( wort );
                       wortzeiger = wort; wortlaenge = 0;
                       break;
                     }
                   if ( library_find_entry( skipped_com, string ))
                     {
                       *--wortzeiger = 0; /* backslash wegwerfen */
                     }
                   else
                     {
                       wortzeiger--;
                       strcpy (wortzeiger, (( escape_def->expansion)[i] ).what);
                       wortzeiger += strlen((( escape_def->expansion)[i] ).what);
                       wortlaenge++;
                     }

                   break;
                 }
             }
           case catcode_begingroup:      /* zeichen fuer geschweifte klammer auf */
           case catcode_endgroup:        /* zeichen fuer geschweifte klammer zu */
           case catcode_mathshift:       /* dollarzeichen */
           case catcode_alignmenttab:    /* ampersand     */
           case catcode_active:
           case catcode_comment:
             *wortzeiger++ = actualtoken.token;
             actualtoken = next_token();
             storemode( *mode );
             mode--;
             break;
           case catcode_endofline:
             if ( wortlaenge > MINDESTLAENGE )
               return( wort );
             *wortzeiger = 0;
             actualtoken = next_token();
             break;
           case catcode_letter:
             escapezeiger = escapebuffer;
             do{
               *escapezeiger++ = actualtoken.token;
               actualtoken = next_token();
             }while( actualtoken.catcode == catcode_letter );
             *escapezeiger = 0;
             if(!strcmp(escapebuffer,"begin"))
               {
                 storemode( *mode );
                 *mode = parameter_mode;
                 if( actualtoken.catcode == catcode_comment )
                   {
                     storemode(*mode);
                     *++mode = comment_mode;
                   }
                 break;
               }
             if(!strcmp(escapebuffer,"end"))
               {
                 storemode( *mode );
                 *mode = end_deletion_mode;
                 if( actualtoken.catcode == catcode_comment )
                   {
                     storemode( *mode );
                     *++mode = comment_mode;
                   }
                 break;
               }
             if(library_find_entry( delimiter_com, escapebuffer ))  /* escapesequenz  wortdelimiter (wie \quad)*/
               {
                 storemode( *mode );
                 mode--;
                 *--wortzeiger = 0;
                 if ( wortlaenge > MINDESTLAENGE )
                   return( wort );
                 wortzeiger = wort; wortlaenge = 0;
               }
             else if ( library_find_entry( skipped_com, escapebuffer ))
               {
                 storemode( *mode );
                 *mode = space_mode; /* damit der delimiting space ueberlesen wird, eingeb. 20.11.91 */
                 *--wortzeiger = 0;
               }
             else
               {
                 strcpy( wortzeiger,escapebuffer );
                 wortzeiger += ( escapezeiger - escapebuffer );
                 *wortzeiger++ = '@';       /* statt eines leerzeichens in der library fuer z.B. stra\ss e */
                 storemode( *mode );
                 mode--;
                 while ( actualtoken.catcode == catcode_space ) actualtoken = next_token();
               }
             break;

           case catcode_ignored:
             ;
             break;
           case catcode_eof:
             return( NULL );

           }           /* local switch */

         break;

       case parameter_mode:   /* untersuchung von environment-parametern  */
         parameterzeiger = parameterbuffer;
         while ( actualtoken.token == '[' )
           while ( actualtoken.token != ']' )
             {
               actualtoken = next_token();
               if ( actualtoken.catcode == catcode_eof ) return( NULL );
             }
         while ( actualtoken.catcode == catcode_begingroup )
           actualtoken = next_token();
         while ( actualtoken.catcode == catcode_letter )
           {
             *parameterzeiger++ = actualtoken.token;
             actualtoken = next_token();
           }
         *parameterzeiger = 0;
         if( library_find_entry( skipped_env, parameterbuffer ))
           {
             storemode( *mode );
             *mode = skip_mode;
             break;
           }
         else if ( actualtoken.token == '*' ) actualtoken = next_token();
         while ( actualtoken.catcode == catcode_endgroup ) actualtoken = next_token();
         storemode( *mode );
         mode--;
         break;

       case other_mode:       /* behandlung von others, insbes. interpunktion   */
         if ( punctuation_check )
           {
             if ( strchr( punctuation_chars, actualtoken.token ))
               {
         unsigned char mishandled_token = actualtoken.token;
                 actualtoken = next_token();
                 if ( actualtoken.catcode == catcode_eof ) return( NULL );
                 if ( strchr(sentenceend_chars, mishandled_token) &&
                      !isdigit( actualtoken.token )) *end_of_sentence = 1;
                 if ( ( actualtoken.catcode != catcode_space )&&
                      ( actualtoken.catcode != catcode_endofline )&&
                      !isdigit( actualtoken.token )&&
                      ( actualtoken.catcode != catcode_alignmenttab)&&
                      ( actualtoken.catcode != catcode_endgroup  ))
                   {
                     fprintf(outputfile , "%s:%d: missing space after %c\n",
                             texname, zeilennummer, mishandled_token );
                   }
               }
             else    /* if actualtoken.token is punctuation_char */
               {
                 actualtoken = next_token();
               }
           }
         else  actualtoken = next_token(); /* if punctuation_check */
         storemode( *mode );
         mode--;
         break;

       case space_mode:       /* skippen von leerzeichen                  */
         while (( actualtoken.catcode == catcode_space )||( actualtoken.catcode == catcode_endofline))
           actualtoken = next_token();
         if ( actualtoken.catcode == catcode_eof ) return( NULL );
         if ( punctuation_check )
           if ( strchr( punctuation_chars, actualtoken.token ))
             fprintf( outputfile , "%s:%d: extra space before %c\n", texname, zeilennummer, actualtoken.token );
         storemode( *mode );
         mode--;
         break;
       case skip_mode:        /* environments{parameterbuffer} werden geskipt    */
         do {
           do {
             do {
               do {
                 do {
                   if( actualtoken.catcode == catcode_eof ) return( NULL );
                   while ( actualtoken.catcode != catcode_escape )actualtoken = next_token();
                   if( actualtoken.catcode == catcode_eof ) return( NULL );
                   actualtoken = next_token();
                 }while( actualtoken.token != 'e' );
                 actualtoken = next_token();
               }while( actualtoken.token != 'n' );
               actualtoken = next_token();
             }while( actualtoken.token != 'd' );
             actualtoken = next_token();
           }while( actualtoken.catcode != catcode_begingroup );
           actualtoken = next_token();
           endzeiger = endbuffer;
           while( actualtoken.catcode == catcode_letter )
             {
               *endzeiger++ = actualtoken.token;
               actualtoken = next_token();
             }
           *endzeiger = 0;
         }while( 0 != strcmp( endbuffer,parameterbuffer ));
         actualtoken = next_token();
         if( actualtoken.catcode == catcode_eof ) return( NULL );
         storemode( *mode );
         mode--;
         break;
       case comment_mode:     /* behandeln von kommentaren                */
         while ( actualtoken.catcode != catcode_endofline )
           {
             actualtoken = next_token();
             if ( actualtoken.catcode == catcode_eof ) return( NULL );
           }
         if ( actualtoken.token == '\n' )
           {
             zeilennummer += zeilenoffset;
             zeilenoffset = 0;
           }
         storemode( *mode );  /* 1.11.91 */
         mode--;              /* 1.11.91 */
         actualtoken = next_token();
         break;


       case active_mode:       /* untersuchen von active-characters  */
         {
       unsigned char wrong_token;
           struct active_definition *def;
           int i=0;

           storemode( *mode );
           mode--;
           def = active_defs;
           while ( def->character != actualtoken.token ) def = def->next;
           actualtoken = next_token();
           while ( (((def->expansion)[i]).of_what != actualtoken.token ) && ( i < def->anzahl )) i++;

           /* jetzt haben wir die makroexpansion und schauen ob sie geskippt werden muss */

           wrong_token = actualtoken.token;
           actualtoken = next_token();
           if ( i == def->anzahl )
             {
               fprintf( outputfile, "%s:%d: active  %c with unknown parameter %c\n",
                        texname, zeilennummer, def->character,wrong_token );
             }
           else
             {
       unsigned char string[80];
               strcpy( string,&(((def->expansion)[i]).what[1])); /* kopieren ohne backslash */
               string[strlen( string )-1] = 0;
               if( library_find_entry( delimiter_com, string ))
                 {
                   if ( wortlaenge > MINDESTLAENGE )
                     return( wort );
                   wortzeiger = wort; wortlaenge = 0;
                 }
               if ( library_find_entry( skipped_com, string ))
                 {
                   ;
                 }
               else
                 {
                   strcpy (wortzeiger, ((def->expansion)[i]).what);
                   wortzeiger += strlen(((def->expansion)[i]).what);
                   wortlaenge++;
                 }
             }
         }
         break;


       case end_deletion_mode:
         storemode( *mode );
         mode--;
         if ( actualtoken.catcode == catcode_begingroup )
           while ( actualtoken.catcode != catcode_endgroup )
             {
               actualtoken = next_token();
               if ( actualtoken.catcode == catcode_eof ) return ( NULL );
             }
         actualtoken = next_token();
         break;
       default:
         ;
         break;
       }   /* switch */
   }      /* while 1 */
}          /* next_word */

static void exit_parser( void )   /* wirft die nebemlibraries weg */
{
 library_delete( skipped_com );
 library_delete( skipped_env );
 library_delete( delimiter_com );
 library_delete( weird_capital );
 heap_delete( active_heap );
}


int check_first_char( unsigned char *word )  /* prueft den ersten buchstaben */
{                                                                                               /* 1 = gross, 0 = klein         */
 unsigned char *hilfspointer;
 while(word)
   {
     unsigned char string[MAXSTRING];
     while( !is_a_letter( *word ))
       {
         if( *word  == '\\' )
           break;
         word++;
       }
     if ( islower( *word ) ) return( 0 );
     if ( isupper( *word ) ) return( 1 );

     /* wir brauchen noch weird_capital  fuer \L@ \AE@ etc. */

     hilfspointer = word;
     while( ( word )&&( *word != '@' ))  /* bis zum @ kopieren */
       word++;                    /* von hilfspointer bis word */
     /* ist der bereich als capital definiert? 1 : continue; */
     word++;   /* affenschwanz mitnehmen */
     strncpy( string, hilfspointer, min(word - hilfspointer,MAXSTRING) );
     string[ min(word-hilfspointer,MAXSTRING-1)  ] = 0;
     if ( library_find_entry( weird_capital, string )) return( 1 );
   }
 return(0);
}