/* >>>>>>>>>>>>>>>>>>>>>>>>>>> Token Corrector <<<<<<<<<<<<<<<<<<<<<<<<<< *
 *                                                                        *
 * Corregge un file Modula II sostituendo alle parole con le maiuscole    *
 * sbagliate le parole corrette.                                          *
 *                                                                        *
 * Il file "Token", contenente le parole chiave del Modula II scritte     *
 * correttamente, deve essere presente nella directory corrente.          *
 *                                                                        *
 *                                        ..... 1987 by Emilio Orione.    *
 *                                                                        *
 * >>>>>>>>>>>>>>>>>>>>>>>>>>> Token Corrector <<<<<<<<<<<<<<<<<<<<<<<<<< */

#include "stdio.h"
#include "ctype.h"

#define MAXL 255    /* Lunghezza massima dell'input */
#define HTSIZE 127  /* Ampiezza della hashtable,deve essere un numero primo
                    */
#define VOID int
#define BOOL int
#define TRUE 1
#define FALSE 0
#define EQUALS 0

char  hashtable[ HTSIZE ][ MAXL ] ;

VOID stripnewlines (stringtoprocess) /* Elimina '\n' dalla stringa */
char *stringtoprocess ;
{
    while (*stringtoprocess && *stringtoprocess != '\n')
       stringtoprocess++ ;
    *stringtoprocess = '\0' ;
}

VOID lowerstring (value) /* Rende minuscola una stringa */
char *value ;
{
   for ( ; *value != '\0' ; value++)
       *value = tolower(*value) ;
}

int transformid(word)
char word[] ;
/*
   Converte una stringa in un intero che,modulo HTSIZE,fornisce l'indice
   perla tabella hash.
*/
{
   int term ;
   int wordindex ;
   
   for (term = 0 , wordindex = strlen(word) -1 ; wordindex > -1 ; wordindex--)
       term = (257*term) + word[wordindex] ;
   term = (term < 0) ? -term : term ;
   return(term % HTSIZE) ;
}

int generatenewindex(originalkey,probenumber)

int originalkey ;
int probenumber ;
/*
   Genera un nuovo indice per la tabella hash in base all'indice precedente
   per risolvere una collisione.Viene usato il quadrato dell'indice
   precedente per riempire la tabella in modo piu' uniforme.
*/
{
   return( (originalkey + probenumber * probenumber) % HTSIZE) ;
}

int hash(word)
char *word ;
/*
   Retistuisce un indice per word sicuramente libero da collisioni.
   Se la tabella e' piena restituisce -1.
*/
{
   int htindex ;
   int idlen ;
   int inithtindex ;
   int probecounter = 0 ;
   
   if (!(idlen = strlen(word)))
       printf("Hash : Word of no lenght\n") ;
       
   /* Calcolo dell'indice iniziale per word */
   
   htindex = inithtindex = transformid(word) ;
   
   /* Se la tabella e' vuota siamo a posto */
   
   if (*hashtable[ htindex ] == '\0')
       ;
   else
   /* Una collisione si cerca un altro indice */
   
       for (probecounter = 0 ; probecounter < (HTSIZE / 2) ;
                   probecounter++) {
           htindex = generatenewindex(inithtindex,probecounter) ;
           if (*hashtable[htindex] == '\0') /* Trovato */
               break ;
       }
   
   /* Sono stati fatti troppi tentativi,la tabella e' piena.
      L'errore e' segnalato con -1.
   */
   
   if (probecounter >= (HTSIZE / 2))
       return(-1) ;
   return(htindex) ;
}

BOOL insertword(indx,word)
char *word ;
char *indx ;
/*
   Inserisce una nuova parola nella tabella hash.
   Ritorna TRUE se tutto e' a posto,FALSE se la tabella e' piena.
*/
{
   int htindex ;
   
   if ((htindex = hash(indx)) == -1)
       return(FALSE) ;
   if (*hashtable[htindex] == '\0')
       strcpy(hashtable[htindex],word) ;
   return(TRUE) ;
}

VOID setupht()
/*
   La funzione inserisce i Token (parole corrette) nella tabella hash.
   Si suppone che il numero di parole sia il 70% di HTSIZE.
   Le parole sono lette dal file Token preso nella directory corrente.
*/
{
   int   counter ;
   FILE  *infile ;
   char  instring[ MAXL ] ;
   char  indice[ MAXL ] ;
   
   for (counter = 0 ; counter < HTSIZE ; counter++)
       hashtable[counter][0] = '\0' ; /* Inizializza tabella */
   
   if ( !( infile = fopen("Token","r"))) { /* Apre il file Token */
       printf("Dov' e' il file 'Token'\n") ;
       exit(0) ;
   }
   
   while ( fgets(instring,MAXL,infile) ) {
       stripnewlines(instring) ;
       strcpy(indice,instring) ;
       lowerstring(indice) ;
       if (!(insertword(indice,instring))) {
           printf("Hash Table Full!\n") ;
           exit(0) ;
       }
   }
   fclose(infile) ;
}

int getcstripped(whichfile)

FILE *whichfile ;
/*
   Restituisce un carattere letto da whichfile eliminando (se non si tratta
   di EOF) l'ottavo bit.
*/
{
   int temp ;
   
   if ((temp = getc(whichfile)) != EOF)
       return(temp & 0x7f) ;
   return(EOF) ;
}

int getword(infile,outfile,word)

FILE *infile ;
FILE *outfile ;
char *word ;
/*
   Legge le parole da infile e le pone in word.
   Se incontra EOF lo ritorna.
   Se legge caratteri che non fanno parte di una parola li riscrive in
   outfile.
*/
{  
   int curchar ;
   
   while ((curchar = getcstripped(infile)) != EOF)
       if (isalpha(curchar))
           break ;
       else
           putc(curchar,outfile) ;
   
   do {
       if (!isalnum(curchar))
           break ;
       *word++ = curchar ;
      } while ((curchar = getcstripped(infile)) != EOF) ;
   
   *word = '\0' ;
   return (curchar) ;
}

BOOL controlla(word)

char *word ;
/*
   Controlla se word e' presente nella hashtable e nel caso la sostituisce
   con la versione corretta.
   L'indice viene sempre generato con la parola tutta minuscola per evitare
   che un carattere maiuscolo falsi il riconiscemento.
   Se vi sono troppe collisioni e la parola non si trova ritorna
   FALSE,viceversa ritorna TRUE.
*/
{
   int htindex ;
   int inithtindex ;
   int probecounter = 0 ;
   char indice[ MAXL ] ;
   char flag[ MAXL ] ;
   
   strcpy(indice,word) ;
   lowerstring(indice) ;
   htindex = inithtindex = transformid(indice) ;
   if (*hashtable[htindex] != '\0') { /* La parola puo'essere presente */
       strcpy(flag,hashtable[htindex]) ;
       lowerstring(flag) ;
       if (strcmp(flag,indice) == EQUALS) /* L'abbiamo trovata */
           strcpy(word,hashtable[htindex]) ;
       else 
           for (probecounter = 0 ; probecounter < (HTSIZE / 2) ;
                   probecounter++) {
           htindex = generatenewindex(inithtindex,probecounter) ;
           if (*hashtable[htindex] == '\0')
               break ; /* Non e' presente */
           else {
               strcpy(flag,hashtable[htindex]) ;
               lowerstring(flag) ;
               if (strcmp(flag,indice) == EQUALS) /* Trovata */
                       {
                           strcpy(word,hashtable[htindex]) ;
                           break ;
                       }
           }
           }
   }
   if (probecounter >= (HTSIZE / 2))/* Troppe collisioni parola non trovata */
      return(FALSE) ;
   return(TRUE) ;
}

main(argc,argv)
int  argc ;
char *argv[] ;
{
   char m2filename[ MAXL ] ; /* Nome del file ModulaII da correggere */
   char m2outfilename[ MAXL ] ; /* Nome del file corretto */
   char curword[ MAXL ] ;
   int  curchar = FALSE ;
   FILE *m2file ; /* File da correggere */
   FILE *goodm2 ; /* File corretto */
   
   setupht() ;
   if (argc < 2) {
       printf("Nome del file da correggere ? ") ;
       fgets(m2filename,MAXL,stdin) ;
   }
   else
       strcpy(m2filename,argv[1]) ;
   stripnewlines(m2filename) ;
   
   if (!(m2file = fopen(m2filename,"r"))) {
       printf("File not found.\n") ;
       exit(0) ;
   }
   
   strcpy(m2outfilename,m2filename) ;
/*
   Modificare la linea seguente se non si desidera che al file corretto sia
   aggiunto il suffisso ".mod".Ricordare pero' che un suffisso e' necessario
   per differenziare il nome del programma da correggere da quello corretto
*/
   strcat(m2outfilename,".mod") ; /* Nome del file corretto sara'
                                               "m2filename.mod" */
   
   if (!(goodm2 = fopen(m2outfilename,"w"))) {
       printf("Can't open %s for output.\n",m2outfilename) ;
       exit(0) ;
   }
   printf("Scrivo il file %s\n",m2outfilename) ;
   
   while (curchar != EOF) {
       curchar = getword(m2file,goodm2,curword) ;
           if (curchar != EOF) {
               if (!controlla(curword)) {
                   printf("Collisione nella Hash Table per la parola %s\n",
                                                               curword) ;
                   printf("Controllo non effettuato su %s\n",curword) ;
               }
               fputs(curword,goodm2) ;
               putc(curchar,goodm2) ;
       }
       strcpy(curword,"") ;
   }
   fclose(m2file) ;
   fclose(goodm2) ;
   printf("Fatto!\n") ;
}