/*************************************************************************/
/*                                                                       */
/*               COMPC.C Compression de fichiers source C.               */
/*              V1.00 FREEWARE Christian BRUNON 20-08-1993               */
/*                                                                       */
/*             +-----------------------------------------+               */
/*             | Logiciel placé dans le domaine FREEWARE |               */
/*             +-----------------------------------------+               */
/* L'utilisation et la diffusion de ce programme, des fichiers sources   */
/* et de la documentation sont entièrement libres.                       */
/*                                                                       */
/*                           Christian BRUNON                            */
/*                        30 Rue Georges Brassens                        */
/*                      43140 LA SEAUVE SUR SEMENE                       */
/*                                FRANCE                                 */
/*                                                                       */
/*            Fichier source Lattice C AmigaDOS Version 5.04             */
/*                                                                       */
/*************************************************************************/

/* Fichier INCLUDE */
#include "CPRG:CompC.H"

/* MACROS */
#define IsValide(C) (((C)=='_') || ((UpChar(C)>'/') && (UpChar(C)<'[')))
#define IsInitiale(C) (((C)=='#') || (IsValide(C)))

/* Autre constante d'erreur */
#define CompError 8

/* Autre constante */
#define MaxLongLigne 255

UBYTE Ligne[MaxLongLigne],     /* Ligne de texte à compresser */
      LigneComp[MaxLongLigne], /* Ligne de texte compressée   */
      NuCar;                   /* Numéro du caractère inséré dans la chaîne Ligne */

typedef struct {
                UWORD CodeMot;
                UBYTE Mot[MaxLongMot];
               } StructMot;

static StructMot ListeMots[MaxMots];

static UWORD PosMot;  /* Position du nouveau mot à insérer */

static ULONG LongFicOut;

const UBYTE *ListeErreurs[] =
     {
      "\n",
      "Utilisation : CompC [-DEST] Fichier1 [-DEST] [Fichier2] [...]\n",
      "\aFichier inexistant\n",
      "\aErreur en création\n",
      "\aErreur en écriture\n",
      "\aErreur en lecture\n",
      "\aPas assez de mémoire\n",
      "\a***BREAK\n",
      "\aCompression inefficace\n"
     };

extern int (*_ONBREAK)();

/**************************************************************************/

int CXBRK()
{
 CodeErreur=BreakError;

 return(0);
}

/**************************************************************************/

#ifdef OOOOOOOOO
/* INUTILISE car provoquant des compressions moins importantes */
int IsValide(UBYTE C)
{
 return((C=='_') | (C=='ç') | (C=='é') | (C=='è') | (C=='ê') |
        (C=='à') | (C=='ù') | (C=='ä') | (C=='ë') | (C=='ï') |
        (C=='ö') | (C=='ü') | (C=='â') | (C=='î') | (C=='ô') |
        (C=='û') | (C=='\'') | ((UpChar(C)>'/') && (UpChar(C)<'[')));
}
#endif

/**************************************************************************/

UBYTE InsererMot()
{
 register UWORD i;

 if(NbMots==0)
   {
    strcpy(ListeMots[0].Mot,Mot);
    ListeMots[0].CodeMot=0;
    NbMots=1;
   }
 else if(NbMots<MaxMots)
   {
    /* Décaler la partie supérieure du tableau pour libérer une place */
    if(PosMot<NbMots) for(i=NbMots;i>PosMot;i--) ListeMots[i]=ListeMots[i-1];

    /* Insertion du mot */
    strcpy(ListeMots[PosMot].Mot,Mot);
    ListeMots[PosMot].CodeMot=NbMots;
    NbMots++;
   }
 /* Le dictionnaire est plein */
 else return(CodeErreur=MemoryError);

 return(0);
}

/**************************************************************************/
/* Recherche un mot dans la liste de ceux déjà enregistrés.               */
/* Si le mot ne s'y trouve pas il est inséré dans la liste par appel à la */
/* fonction InsererMot().                                                 */
/* Elle insère le nouveau mot à la position PosMot si la liste n'est pas  */
/* vide.                                                                  */
/* Si elle est vide, le mot est inséré à la première place                */
/* Cette fonction renvoie 1 si le mot est dans la liste.                  */
/* Sinon, elle l'insère et renvoie 0.                                     */
/**************************************************************************/

UBYTE TrouverMot()
{
 /* Le mot est peut-être dans la liste */
 if(NbMots)
     {
      /* Le mot est-il inférieur au dernier mot de la liste ? */
      if(strcmp(ListeMots[NbMots-1].Mot,Mot)>=0)
          {
           PosMot=0;

           /* Recherche sur le premier caractère de la chaîne */
           while(ListeMots[PosMot].Mot[0]<Mot[0]) PosMot++;
           /* Recherche sur les autres caractères */
           while(strcmp(ListeMots[PosMot].Mot,Mot)<0) PosMot++;

           if(strcmp(ListeMots[PosMot].Mot,Mot)==0)
                {
                 /* Le mot est déjà dans la liste */
                 CodeMot=ListeMots[PosMot].CodeMot;
                 return(1);
                }
           else CodeErreur=InsererMot(); /* Le mot n'est pas dans la liste */
          }
      else{
           /* Le mot est plus grand que le dernier mot de la liste */
           /* On l'insère en dernière position.                    */
           PosMot=NbMots;
           CodeErreur=InsererMot();
          } /* if(strcmp(ListeMots[NbMots-1].Mot,Mot)>=0) */

     }
 else CodeErreur=InsererMot(); /* Mot non trouvé (NbMots=0) */

 return(0);
}

/**************************************************************************/

UBYTE CompresserLigne()
{
 Ligne[NuCar]='\0';

 /* Effacement des espaces se trouvant en fin de chaîne */
 while(NuCar && (Ligne[NuCar-1]==' ' || Ligne[NuCar-1]=='\t')) Ligne[--NuCar]='\0';

 if(NuCar==0){
              if(fputc(LigneVide,FOut)==EOF) return(WriteError);
             }
 else{
      NuCar=0;

      /* Calcul du nombre d'espaces en début de ligne dans NuCar */
      while(Ligne[NuCar]==' ' || Ligne[NuCar]=='\t') NuCar++;

      /* Suppression du nombre d'espaces en début de ligne */
      if(NuCar) strcpy(Ligne,Ligne+NuCar);

      /* Ecriture du nombre d'espaces en début de ligne */
      if(fputc(NuCar,FOut)==EOF) return(WriteError);

      for(NuCar=0 ; NuCar<strlen(Ligne) && CodeErreur==0;)
         {
          /* Un mot détecté */
          if(IsInitiale(Ligne[NuCar]))
            {
             /* Copie de l'initiale du mot dans le premier car de mot */
             Mot[0]=Ligne[NuCar++];
             LongMot=1;

             /* Copie du reste du mot */
             while(IsValide(Ligne[NuCar])) Mot[LongMot++]=Ligne[NuCar++];
             /* Ajout de la parenthèse ouverte ou du crochet ouvert si il suit */
             if(Ligne[NuCar]=='(' || Ligne[NuCar]=='[') Mot[LongMot++]=Ligne[NuCar++];

             Mot[LongMot]=0;

             /* Ecriture du mot ou de son code */
             if(LongMot<3)
                 {
                  if(fputs(Mot,FOut)) return(WriteError);
                 }
             else{
                  /* Mot répertorié ? */
                  if(TrouverMot())
                      {
                       /* Le mot existe, on écrit son code */
                       if(fprintf(FOut,"%c%c%c",MotExistant,CodeMot&255,CodeMot>>8)==-1) return(WriteError);
                      }
                  /* Nouveau mot */
                  else{
                       /* Le mot est nouveau, on l'écrit encadré par */
                       /* les marques de nouveau mot                 */
                       if(fprintf(FOut,"%c%s%c",NouveauMot,Mot,NouveauMot)==-1) return(WriteError);
                      }
                 } /* if(LongMot<3) */

            } /* if(IsInitiale(Ligne[NuCar])) */

        /* Début de commentaire */
        else if(Ligne[NuCar]=='/' && Ligne[NuCar+1]=='*')
               {
                /* Début de commentaire + espace */
                if(Ligne[NuCar+2]==' ')
                    {
                     NuCar+=3;
                     if(fputc(Commentaire2On,FOut)==EOF) return(WriteError);
                    }
                else{
                     NuCar+=2;
                     if(fputc(Commentaire1On,FOut)==EOF) return(WriteError);
                    }
               }

        /* Fin de commentaire */
        else if(Ligne[NuCar]=='*' && Ligne[NuCar+1]=='/')
               {
                NuCar+=2;
                if(fputc(Commentaire1Off,FOut)==EOF) return(WriteError);
               }

        /* Espace + fin de commentaire */
        else if(Ligne[NuCar]==' ' && Ligne[NuCar+1]=='*' && Ligne[NuCar+2]=='/')
               {
                NuCar+=3;
                if(fputc(Commentaire2Off,FOut)==EOF) return(WriteError);
               }

        /* Autre caractère */
        else while(!IsInitiale(Ligne[NuCar]) && Ligne[NuCar])
             if(fputc(Ligne[NuCar++],FOut)==EOF) return(WriteError);

         } /* for(NuCar=0;NuCar<strlen(Ligne);) */

      if(fputc('\n',FOut)==EOF) return(WriteError);

      /* Affichage du nombre d'octets lus */
      if(!ComptLignes++) printf("%7u\b\b\b\b\b\b\b",ftell(FIn));

      ComptLignes%=10;
      NuCar=0;
     }

 return(CodeErreur);
}

/**************************************************************************/

UBYTE CompresserFichier()
{
 /* VARIABLE LOCALE */
 register int Car; /* Caractère lu dans le fichier */

 /* Le fichier est-il vide ?                                     */
 /* if(((Car=fgetc(FIn))==EOF)&&(feof(FIn)))                     */
 /*        {                                                     */
 /*         printf("Fichier vide.\n");                           */
 /*         return(1);                                           */
 /*        }                                                     */
 /*                                                              */
 /* Fichier non vide : on replace le caractère lu dans le buffer */
 /* ungetc(Car,FIn);                                             */

 printf("Création du fichier %s -> %7u -> ",NomFicOut,LongFicIn);

 LongFicOut=0L;
 if((FOut=fopen(NomFicOut,"wb"))==NULL) return(CreateError);

 Car=fgetc(FIn);

 ComptLignes=NuCar=NbMots=0;

 while(FIn->_flag==_IOREAD && CodeErreur==0)
      {
       if(Car=='\n') CodeErreur=CompresserLigne();
       else Ligne[NuCar++]=Car;

       Car=fgetc(FIn);
      }

 /* Erreur en lecture ou fin normal de fichier ? */
 if(FIn->_flag != (_IOREAD|_IOEOF)) return(ReadError);

 if(CodeErreur==0 && NuCar) CodeErreur=CompresserLigne();

 return(CodeErreur);
}

/**************************************************************************/

int main(int argc,char *argv[])
{
 /* Déclarations et fonctions de Exec.Library */
 extern struct ExecBase *SysBase;
 extern void FreeMem(void *,long);
 extern void *AllocMem(long,long);
 #pragma syscall AllocMem c6 1002
 #pragma syscall FreeMem d2 902

 register struct FileInfoBlock *PtrFIB;

 /* Déclarations de variables externes de la DOS.Library */
 extern struct DosLibrary *DOSBase;
 extern BPTR CreateDir(char *);
 extern long DeleteFile(char *);
 extern long SetProtection(char *, ULONG);
 extern LONG SetComment(char *, char *);
 extern LONG Examine(BPTR, struct FileInfoBlock *);
 extern BPTR Lock(char *, LONG);
 extern void UnLock(BPTR);
 #pragma libcall DOSBase CreateDir 78 101
 #pragma libcall DOSBase DeleteFile 48 101
 #pragma libcall DOSBase SetProtection ba 2102
 #pragma libcall DOSBase SetComment b4 2102
 #pragma libcall DOSBase Examine 66 2102
 #pragma libcall DOSBase Lock 54 2102
 #pragma libcall DOSBase UnLock 5a 101

 /* VARIABLES LOCALES */
 register UBYTE NuArg,
                FlagNewDir=0,  /* Changer le répertoire de destination ? */
                CreerChemin=0, /* Créer le nouveau répertoire ?          */
                NewChemin[80],
                FileName[108],
                Commentaire[80];

 register BPTR Clef; /* Paramètre utilisé pour Examine() et CreateDir() */

 register ULONG Protection;

 /*********************** DEBUT DU CODE DE main() **************************/

 _ONBREAK=CXBRK;

 printf("CompC V1.00 FREEWARE Christian BRUNON 20-08-1993\n");

 /* Pas de paramètre ou paramètre ? */
 if(argc==1 || (argc==2 && argv[1][0]=='?' && argv[1][1]=='\0'))
   {
    printf("%s",ListeErreurs[1]);
    return(1);
   }

 if((PtrFIB=(struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),0L))==NULL)
   {
    printf("%s",ListeErreurs[MemoryError]);
    return(MemoryError);
   }

 for(NuArg=1;NuArg<argc;NuArg++)
 if(argv[NuArg][0]=='-')
     /* Le paramètre lu est un nouveau chemin */
     {
      FlagNewDir=(UBYTE)1;
      strcpy(NewChemin,&argv[NuArg][1]);

      if(NewChemin[0]) CreerChemin=LastChar(NewChemin)!=':' && LastChar(NewChemin)!='/';

      if(CreerChemin && NewChemin[0])
        {
         if(Clef=CreateDir(NewChemin)) UnLock(Clef);

         strcat(NewChemin,"/");
        }
     }
 /* Le paramètre lu est un fichier à compresser */
 else{
      CodeErreur=0;
      strcpy(NomFicIn,argv[NuArg]);

      if(Clef=Lock(NomFicIn,ACCESS_READ))
          {
           /* Test existence et test type = fichier */
           if(Examine(Clef,PtrFIB) && PtrFIB->fib_DirEntryType<0)
               {
                strcpy(FileName,PtrFIB->fib_FileName);
                LongFicIn=PtrFIB->fib_Size;
                Protection=(ULONG)PtrFIB->fib_Protection;
                strcpy(Commentaire,PtrFIB->fib_Comment);
               }
           else CodeErreur=FileOpenReadError;

           UnLock(Clef);
          }
      else CodeErreur=FileOpenReadError;

      if(CodeErreur){
                     printf("%s %s",NomFicIn,ListeErreurs[CodeErreur]);
                     continue;
                    }

      /* Détermination du nom du fichier compressé */
      if(FlagNewDir)
          {
           strcpy(NomFicOut,NewChemin);
           strcat(NomFicOut,FileName);
          }
      else strcpy(NomFicOut,NomFicIn);

      /* Rajout de l'extension ".K" */
      strcat(NomFicOut,".K");

      printf("Ouverture du fichier %s\r",NomFicIn);

      if(FIn=fopen(NomFicIn,"rb"))
        {
         CodeErreur=CompresserFichier();

         /* Affichage du taux de compression ou du texte de l'erreur */
         if(CodeErreur==0)
             {
              printf("%7u => ",LongFicOut=ftell(FOut));

              if(LongFicIn && LongFicOut<=LongFicIn)
                printf("%u %%\n",(((100*(LongFicIn-LongFicOut))/LongFicIn)*10+5)/10);
              else CodeErreur=CompError;
             }
         else printf("\n%s",ListeErreurs[CodeErreur]);

         if(FIn)  fclose(FIn);
         if(FOut) fclose(FOut);

         if(CodeErreur) DeleteFile(NomFicOut);
         else /* Recopie des bits d'attributs de fichier et du commentaire */
             {
              SetProtection(NomFicOut,Protection);
              SetComment(NomFicOut,Commentaire);
             }
        }
      /* Fichier inexistant ou illisible */
      else printf("\n%s",ListeErreurs[CodeErreur=FileOpenReadError]);
     }

 if(PtrFIB) FreeMem(PtrFIB,sizeof(struct FileInfoBlock));

 /* Renvoie le code d'erreur du dernier fichier compressé */
 return(CodeErreur);
}
