/*
    SC LINK SMALLCODE SMALLDATA VERBOSE OPTIMIZE CPU=ANY PARMS=REG MvPSF
    lc -Lcdv -O -ma -rr -ignu:os-include MvPSF
    apf -v gcc -g MvPSF.c -o MvPSF_pure -noixemul -lauto
    gcc020 -o MvPSF_gcc -noixemul -m68030 MvPSF.c -lauto -g -v
    gcc020 -DAPURIFY -g MvPSF.c -o MvPSF.s -m68030 -noixemul -lauto -S
    Apurify -tb MvPSF.s -o MvPSFPure.s
    gcc020 -g MvPSFPure.s -o MvPSF_pure -m68030 -noixemul -lauto -lAPur
*/

#define __MA_VERSION__ "$Revision: 1.5 $ $Date: 1997/09/25 20:55:09 $"
static char MvPSF_RCS_Main[] = "$Id: MvPSF.c,v 1.5 1997/09/25 20:55:09 labouiss Exp $";

/* ====== Section : Includes === */

#include <string.h>
#include <ctype.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <proto/dos.h>
#include <exec/exec.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <exec/memory.h>
#include <proto/exec.h>
#include <clib/alib_protos.h>
#include <utility/tagitem.h>
#ifdef __GNUC__
#include <inline/exec.h>
#include <inline/dos.h>
#include <inline/alib.h>
#else
#include <proto/exec.h>
#include <proto/dos.h>
#endif

/* --- */

/* ====== Section : Types === */

typedef enum {TYF_ERR = -1,TYF_NON = 0,TYF_AFM,TYF_PFA,TYF_PFB,TYF_AMFM} TYPE_FICHIER;


typedef struct
{
    struct MinNode UF_Node;
    int PosDebut;       /* début de la partie textuelle */
    char fontname[32];  /* chaîne de reconnaissance de FontName */
    char finhead[32];   /* chaîne indiquant la fin de l'en-tête */
    char type[16];      /* Extension du fichier */
    char nom[108];      /* Nom du fichier */
    BPTR repertoire;    /* Un "Lock" vers le répertoire où se trouve le fichier */
} Un_Fichier;


typedef struct
{
    char **fichiers;
    LONG verbose,all,stat;
} MvPSF_Args;


typedef struct
{
    BOOL verbose,all,stat;
    LONG nb_fic_lus,nb_dir_lues;
    LONG nb_AFM;
    LONG nb_AMFM;
    LONG nb_PFB;
    LONG nb_PFA;
    LONG nb_fic_mv;
    APTR mpool;
    struct MinList ma_liste;
    char *nom_prog;
} MvPSF_Glo;

/* --- */

/* ====== Section : Consts & Defines === */

/* Fichiers AFM */
const char  AFMHeader[] = "StartFontMetrics";
#define AFMHeadlong ((unsigned long)strlen(AFMHeader))
const int PosAFM = 0;
const char AFMFNStr[] = "FontName";
const char AFMFHStr[] = "StartCharMetrics";


/* Fichiers AMFM */
const char  AMFMHeader[] = "StartMasterFontMetrics";
#define AMFMHeadlong ((unsigned long)strlen(AMFMHeader))
const int PosAMFM = 0;
const char AMFMFNStr[] = "FontName";
const char AMFMFHStr[] = "StartAxis";


/* Fichiers PFB */
const char  PFBHeader[] = "\x80\x01";
#define PFBHeadlong ((unsigned long)strlen(PFBHeader))
const int PosPFB = 6;
const char PFBFNStr[] = "/FontName";
const char PFBFHStr[] = "eexec";


/* Fichiers PFA */
const char  PFA1Header[] = "%!PS-AdobeFont";
#define PFA1Headlong ((unsigned long)strlen(PFA1Header))
const char  PFA2Header[] = "%!FontType";
#define PFA2Headlong ((unsigned long)strlen(PFA2Header))
const int PosPFA = 0;
const char PFAFNStr[] = "/FontName";
const char PFAFHStr[] = "eexec";


/* Memory Pool */
const int NB_POOL=64;
const int NB_PUDDLE=20;


/* Divers  */

#ifdef __GNUC__
const int LONG_BUFFER = 64; /* Le nombre d'octets lus au début de chaque fichier */
const int LONGUEUR_NOM = 31; /* La longueur max du nom du programme */
#else
#define LONG_BUFFER 64
#define LONGUEUR_NOM 31
#endif
char Template[] = "FILE/M/A,VERBOSE/S,ALL/S,STAT/S";

/* --- */

/* ====== Section : Prototypes === */

void traiter_fichier(MvPSF_Glo *contexte,Un_Fichier *fic);
BOOL lecture_repertoire(MvPSF_Glo *contexte,struct FileInfoBlock *info,BPTR rep);
BOOL ajoute_fichier(MvPSF_Glo *contexte,char *nom,Un_Fichier *fic);
TYPE_FICHIER type_fichier(MvPSF_Glo *contexte,BPTR fic);
BOOL recup_mot(MvPSF_Glo *contexte,BPTR fh,char *chaine,int longueur);

/* --- */

/* ================ main =============

Le programme principal.

*/

int main(void)
{
    struct FileInfoBlock *info;
    char mon_nom[32];
    MvPSF_Args arguments;
    MvPSF_Glo *contexte;
    struct RDArgs *RDArgs;

    if (!GetProgramName(mon_nom,LONGUEUR_NOM))
    {
        strcpy(mon_nom,"MvPSF");
    }

    Printf("\n\033[1m%s\033[m ©1996 Christophe Labouisse %s\n\n",mon_nom,__MA_VERSION__);

    arguments.verbose = FALSE;
    arguments.all = FALSE;
    arguments.stat = FALSE;

    RDArgs = ReadArgs(Template,(LONG *)&arguments,NULL);

    if (RDArgs)
    {

        struct AnchorPath *AnchorPath;

        AnchorPath = (struct AnchorPath *)AllocVec(sizeof(struct AnchorPath),MEMF_PUBLIC|MEMF_CLEAR);

        if (AnchorPath)
        {
            AnchorPath->ap_BreakBits=SIGBREAKF_CTRL_C;
            AnchorPath->ap_Strlen=0;

            info = AllocDosObjectTags(DOS_FIB,TAG_DONE,NULL);

            if (info)
            {
                contexte = (MvPSF_Glo *)AllocVec(sizeof(MvPSF_Glo),MEMF_CLEAR);

                if (contexte)
                {
                    char **les_fichiers;
                    BPTR ancien_rep;

                    contexte->all = arguments.all;
                    contexte->verbose = arguments.verbose;
                    contexte->stat = arguments.stat;
                    contexte->nom_prog = mon_nom;

                    /* Allocation de la 'memory pool' pour la suite */
                    contexte->mpool = CreatePool(MEMF_CLEAR,sizeof(Un_Fichier)*NB_POOL,sizeof(Un_Fichier)*NB_PUDDLE);

                    if (contexte->mpool)
                    {
                        struct MinNode *node;

                        les_fichiers = arguments.fichiers;

                        while (*les_fichiers)
                        {
                            LONG RetVal;

                            NewList((struct List *)&contexte->ma_liste);

                            if (contexte->verbose)
                            {
                                Printf("Processing argument « \033[1m%s\033[m »\n",*les_fichiers);
                            }


                            for (RetVal = MatchFirst(*les_fichiers,AnchorPath) ; RetVal == 0 ; RetVal = MatchNext(AnchorPath))
                            {
                                BPTR rep_courant;

                                rep_courant = AnchorPath->ap_Current->an_Lock;

                                if (AnchorPath->ap_Info.fib_DirEntryType > 0)
                                {
                                    /* C'est un répertoire */


                                    if (contexte->all)
                                    {
                                        if (!(AnchorPath->ap_Flags & APF_DIDDIR))
                                        {
                                            contexte->nb_dir_lues++;

                                            AnchorPath->ap_Flags |= APF_DODIR;

                                            if (contexte->verbose)
                                            {
                                                Printf("Entering directory \033[1m%s\033[m\n",AnchorPath->ap_Info.fib_FileName);
                                            }

                                        }
                                        else if (contexte->verbose)
                                        {
                                            Printf("Exiting directory \033[1m%s\033[m\n",AnchorPath->ap_Info.fib_FileName);
                                        }


                                        AnchorPath->ap_Flags &= ~APF_DIDDIR;

                                    }
                                    else
                                    {
                                        BOOL rv_lr = FALSE;
                                        BPTR arg_courant;

                                        /* On modifie AnchorPath pour qu'il ne rentre pas dans le répertoire */
                                        AnchorPath->ap_Flags |= APF_DIDDIR;
                                        AnchorPath->ap_Flags &= ~APF_DODIR;

                                        ancien_rep = CurrentDir(rep_courant);

                                        arg_courant = Lock(AnchorPath->ap_Info.fib_FileName,ACCESS_READ);

                                        if (arg_courant)
                                        {
                                            CurrentDir(arg_courant);

                                            if (Examine(arg_courant,info))
                                            {
                                                contexte->nb_dir_lues++;
                                                rv_lr = lecture_repertoire(contexte,info,arg_courant);
                                            }
                                            else
                                            {
                                                Printf("%s : Can't examine %s - ",mon_nom,AnchorPath->ap_Current->an_Info.fib_FileName);
                                                PrintFault(IoErr(),NULL);
                                            }

                                            UnLock(arg_courant);
                                        }
                                        else
                                        {
                                            Printf("%s : Can't lock %s - ",mon_nom,AnchorPath->ap_Current->an_Info.fib_FileName);
                                            PrintFault(IoErr(),NULL);
                                        }

                                        CurrentDir(ancien_rep);

                                        if (!rv_lr)
                                        {
                                            break;
                                        }

                                    }

                                }
                                else
                                {
                                    /* Traitement d'un fichier */
                                    Un_Fichier *f_tmp;

                                    f_tmp = AllocPooled(contexte->mpool,sizeof(Un_Fichier));

                                    if (f_tmp)
                                    {

                                        ancien_rep = CurrentDir(rep_courant);

                                        if (ajoute_fichier(contexte,AnchorPath->ap_Info.fib_FileName,f_tmp))
                                        {
                                            f_tmp->repertoire = DupLock(AnchorPath->ap_Current->an_Lock);
                                        }
                                        else
                                        {
                                            FreePooled(contexte->mpool,f_tmp,sizeof(Un_Fichier));
                                        }

                                        CurrentDir(ancien_rep);

                                    }
                                    else
                                    {
                                        PrintFault(ERROR_NO_FREE_STORE,mon_nom);
                                        break;
                                    }

                                }

                            } /* for (RetVal ...) */

                            MatchEnd(AnchorPath);

                            if (RetVal != ERROR_NO_MORE_ENTRIES)
                            {
                                Printf("%s : %s - ",mon_nom,*les_fichiers);
                                PrintFault(RetVal,NULL);

                                /* Lorsqu'un fichier ou un répertoire n'existe pas
                                 * on passe au suivant. Dans les autres cas on sort */
                                if ((RetVal != ERROR_OBJECT_NOT_FOUND) &&
                                    (RetVal != ERROR_DEVICE_NOT_MOUNTED))
                                {
                                    break;
                                }

                            }

                            les_fichiers++;

                            for (node = contexte->ma_liste.mlh_Head ; node->mln_Succ ; node = node->mln_Succ)
                            {
                                Un_Fichier *f_tmp;

                                f_tmp = (Un_Fichier *)node;
                                traiter_fichier(contexte,f_tmp);
                            }

                        } /* while (*les_fichiers) */

                        if (contexte->stat)
                        {
                            Printf("\n%s statistics :\n",contexte->nom_prog);
                            Printf("\t%ld Directory(ies) entered\n",contexte->nb_dir_lues);
                            Printf("\t%ld File(s) processed\n",contexte->nb_fic_lus);
                            Printf("\t%ld AFM File(s) found\n",contexte->nb_AFM);
                            Printf("\t%ld AMFM File(s) found\n",contexte->nb_AMFM);
                            Printf("\t%ld PFA File(s) found\n",contexte->nb_PFA);
                            Printf("\t%ld PFB File(s) found\n",contexte->nb_PFB);
                            Printf("\t%ld File(s) renamed\n\n",contexte->nb_fic_mv);
                        }


                        DeletePool(contexte->mpool);
                    }
                    else
                    {
                        Printf("%s : Can't Create memory pool - ",mon_nom);
                        PrintFault(ERROR_NO_FREE_STORE,NULL);
                    }

                    FreeVec(contexte);
                }
                else
                {
                    PrintFault(ERROR_NO_FREE_STORE,mon_nom);
                }


                FreeDosObject(DOS_FIB,info);

            } /* FileInfoBlock */
            else
            {
                Printf("%s : FileInfoBlock's Allocation - ",mon_nom);
                PrintFault(ERROR_NO_FREE_STORE,NULL);
            }

            FreeVec((APTR)AnchorPath);

        } /* AnchorPath */
        else
        {
            Printf("%s : AnchorPath's Allocation - ",mon_nom);
            PrintFault(ERROR_NO_FREE_STORE,NULL);
        }

        FreeArgs(RDArgs);

    }
    else
    {
        PrintFault(IoErr(),mon_nom);
    }

}

/* --- main() --- */



/* ============================== traiter_fichier ==============================

 Commentaire: Traitement d'un fichier. Récupération du nom et renommage si nécessaire

*/

void traiter_fichier(MvPSF_Glo *contexte,Un_Fichier *fic)
{
    BPTR fh;
    BPTR ancien_rep;

    ancien_rep = CurrentDir(fic->repertoire);

    fh = Open(fic->nom,MODE_OLDFILE);

    if (fh)
    {
        char chaine[256];

        Seek(fh,fic->PosDebut,OFFSET_BEGINNING);

        do  /* Recherche du nom de la fonte dans le fichier */
        {
            if (recup_mot(contexte,fh,chaine,sizeof(chaine)))
            {
                if (!strcmp(chaine,fic->fontname))
                {
                    if (recup_mot(contexte,fh,chaine,sizeof(chaine)))
                    {
                        char tmp_nom[108];
                        int lchaine = strlen(chaine)-1;

                        /* On filtre le dernier caractère si c'est ) */
                        if (chaine[lchaine] == ')')
                        {
                            chaine[lchaine] = '\0';
                        }


                        /* On filtre le premier caractère si c'est ( ou / */
                        if ((*chaine == '/') || (*chaine == '('))
                        {
                            strcpy(tmp_nom,chaine+1);
                        }
                        else
                        {
                            strcpy(tmp_nom,chaine);
                        }

                        strcat(tmp_nom,fic->type);

                        if (strncmp(fic->nom,tmp_nom,LONGUEUR_NOM - 1))
                        {
                            if (Rename(fic->nom,tmp_nom))
                            {
                                contexte->nb_fic_mv++;
                                Printf("Renaming \033[33m%s\033[m to \033[33m%s\033[m\n",fic->nom,tmp_nom);
                            }
                            else
                            {
                                Printf("%s : %s - ",contexte->nom_prog,fic->nom);
                                PrintFault(IoErr(),NULL);
                            }

                        }
                        else
                        {
                            Printf("%s \033[1mOK\033[m\n",fic->nom);
                        }

                        break;
                    }
                    else
                    {
                        Printf("%s : %s - ",contexte->nom_prog,fic->nom);
                        PrintFault(IoErr(),NULL);
                        break;
                    }

                }
                else
                {
                    if (!strcmp(chaine,fic->finhead))
                    {
                        Printf("Can't find \033[1m%s\033[m in \033[33m%s\033[m\n",fic->fontname,fic->nom);
                        break;
                    }

                }

            }
            else
            {
                LONG erreur = IoErr();

                if (erreur)
                {
                    Printf("%s : %s - ",contexte->nom_prog,fic->nom);
                    PrintFault(IoErr(),NULL);
                }
                else
                {
                    Printf("Can't find %s in %s\n",fic->fontname,fic->nom);
                }

                break;
            }

        } while(1);

        Close(fh);
    }
    else
    {
        Printf("%s : %s - ",contexte->nom_prog,fic->nom);
        PrintFault(IoErr(),NULL);
    }

    CurrentDir(ancien_rep); /* On revient au répertoire initial */
    UnLock(fic->repertoire);/* On libère le "Lock" au répertoire du fichier */

}
/* --- traiter_fichier() --- */



/* ================================= recup_mot =================================

 Commentaire: Récupère le mot suivant du fichier fh.

 fh : fichier.
 chaine : buffer dans lequel sera mis le mot.
 longueur : longueur de 'chaine'.

*/

BOOL recup_mot(MvPSF_Glo *contexte,BPTR fh,char *chaine,int longueur)
{
    BOOL rv = FALSE;
    int courant = 0;
    longueur--;

    do
    {
        LONG buffer = FGetC(fh);

        if (buffer < 0)
        {
            rv = FALSE;
            break;
        }

        if (isspace(buffer))
        {
            chaine[courant] = '\0';
            rv = TRUE;
            break;
        }
        else
        {
            chaine[courant] = buffer;
            courant++;

            if (courant >= longueur)
            {
                chaine[longueur] = '\0';
                Printf("Error while parsing line to long\n\t-> (%s)\n",chaine);
                rv = FALSE;
                break;
            }
        }
    } while(1);

    return rv;

}

/* --- recup_mot() --- */



/* ================================= lecture_repertoire =================================

 Commentaire: Ajoute les fichiers du répertoire à la liste.

*/

BOOL lecture_repertoire(MvPSF_Glo *contexte,struct FileInfoBlock *info,BPTR rep)
{
    BOOL rv;

    Un_Fichier *f_tmp = AllocPooled(contexte->mpool,sizeof(Un_Fichier));

    if (f_tmp)
    {
        do
        {

            if (!ExNext(rep,info))
            {
                long erreur;

                erreur = IoErr();

                if (erreur != ERROR_NO_MORE_ENTRIES)
                {
                    Printf("%s : While examining directory - ",contexte->nom_prog);
                    PrintFault(erreur,NULL);
                }

                break;
            }

            if (info->fib_DirEntryType < 0)
            {
                if (ajoute_fichier(contexte,info->fib_FileName,f_tmp))
                {
                    /* On ajoute le répertoire au fichier qui viens d'être ajouté */
                    f_tmp->repertoire = DupLock(rep);

                    f_tmp = AllocPooled(contexte->mpool, sizeof(Un_Fichier));

                    if (!f_tmp)
                    {
                        PrintFault(ERROR_NO_FREE_STORE,contexte->nom_prog);
                        break;
                    }

                }

            }
            else if (contexte->verbose)
            {
                Printf("\t\033[1m%s\033[m is a directory\n",info->fib_FileName);
            }

        } while(1);
    }
    else
    {
        PrintFault(ERROR_NO_FREE_STORE,contexte->nom_prog);
    }

    if (f_tmp)
    {
        FreePooled(contexte->mpool,f_tmp,sizeof(Un_Fichier));
        rv = TRUE;
    }
    else
    {
        rv = FALSE;
    }

    return rv;
}

/* --- lecture_repertoire --- */



/* ================================= ajoute_fichier =================================

 Commentaire: Renvoie TRUE si le fichier et d'un type connu et rempli la structure
 fournie. Renvoie FALSE sinon.
 Ajoute le fichier à la liste.

*/

BOOL ajoute_fichier(MvPSF_Glo *contexte,char *nom,Un_Fichier *fic)
{
    BOOL rv = TRUE;
    BPTR fichier;

    if (contexte->verbose)
    {
        Printf("\tProcessing \033[1m%s\033[m\n",nom);
    }

    fichier = Open(nom,MODE_OLDFILE);

    if (fichier)
    {

        contexte->nb_fic_lus++;

        switch (type_fichier(contexte,fichier))
        {
            case (TYF_AFM) :
            {
                fic->PosDebut = PosAFM;
                strcpy(fic->fontname,AFMFNStr);
                strcpy(fic->type,".AFM");
                strcpy(fic->finhead,AFMFHStr);

                if (contexte->verbose)
                {
                    Printf("\t\t\033[1m%s\033[m is a AFM file\n",nom);
                }

                contexte->nb_AFM++;
                break;
            }

            case (TYF_AMFM) :
            {
                fic->PosDebut = PosAMFM;
                strcpy(fic->fontname,AMFMFNStr);
                strcpy(fic->type,".AMFM");
                strcpy(fic->finhead,AMFMFHStr);

                if (contexte->verbose)
                {
                    Printf("\t\t\033[1m%s\033[m is a AMFM file\n",nom);
                }

                contexte->nb_AMFM++;
                break;
            }

            case (TYF_PFA) :
            {
                fic->PosDebut = PosPFA;
                strcpy(fic->fontname,PFAFNStr);
                strcpy(fic->type,".PFA");
                strcpy(fic->finhead,PFAFHStr);

                if (contexte->verbose)
                {
                    Printf("\t\t\033[1m%s\033[m is a PFA file\n",nom);
                }

                contexte->nb_PFA++;
                break;
            }

            case (TYF_PFB) :
            {
                fic->PosDebut = PosPFB;
                strcpy(fic->fontname,PFBFNStr);
                strcpy(fic->type,".PFB");
                strcpy(fic->finhead,PFBFHStr);

                if (contexte->verbose)
                {
                    Printf("\t\t\033[1m%s\033[m is a PFB file\n",nom);
                }

                contexte->nb_PFB++;
                break;
            }

            case (TYF_NON) :
            {
                if (contexte->verbose)
                {
                    Printf("\t\t\033[1m%s\033[m doesn't seem to belong to any known type\n",nom);
                }

                rv = FALSE;
                break;
            }

            case (TYF_ERR) :
            {
                LONG code_erreur = IoErr();

                /* Si le code erreur est nul c'est probablement que le fichier est de longueur nulle */
                if (code_erreur)
                {
                    Printf("%s : Error reading file %s - ",contexte->nom_prog,nom);
                    PrintFault(IoErr(),NULL);
                }
                else if (contexte->verbose)
                {
                    Printf("\t\t\033[1m%s\033[m doesn't seem to belong to any known type\n",nom);
                }

                rv = FALSE;
                break;
            }
        }

        if (rv)
        {
            strcpy(fic->nom,nom);
            AddTail((struct List *)&contexte->ma_liste,(struct Node *)fic);
        }

        Close(fichier);

    }
    else
    {
        Printf("%s : Error can't open file %s - ",contexte->nom_prog,nom);
        PrintFault(IoErr(),NULL);
        rv = FALSE;
    }

    return rv;

}

/* --- ajoute_fichier --- */



/* ================================= type_fichier =================================

 Commentaire: Détermine le type du fichier.

*/

TYPE_FICHIER type_fichier(MvPSF_Glo *contexte,BPTR fichier)
{
    char buffer[LONG_BUFFER];
    TYPE_FICHIER ret_val;

    if (Read(fichier,buffer,sizeof(buffer)) > 0)
    {
        if (!memcmp(buffer,AFMHeader,AFMHeadlong))
        {
            ret_val = TYF_AFM;
        }
        else if (!memcmp(buffer,AMFMHeader,AMFMHeadlong))
        {
            ret_val = TYF_AMFM;
        }
        else if (!memcmp(buffer,PFBHeader,PFBHeadlong) &&
                 (!memcmp(buffer+PosPFB,PFA1Header,PFA1Headlong) ||
                  !memcmp(buffer+PosPFB,PFA2Header,PFA2Headlong)))
        {
            ret_val = TYF_PFB;
        }
        else if (!memcmp(buffer,PFA1Header,PFA1Headlong) ||
                 !memcmp(buffer,PFA2Header,PFA2Headlong))
        {
            ret_val = TYF_PFA;
        }
        else
        {
            ret_val = TYF_NON;
        }

    }
    else
    {
        ret_val = TYF_ERR;
    }

    return ret_val;
}

/* --- type_fichier --- */


/* ====== Log ===
 * $Log: MvPSF.c,v $
 * Revision 1.5  1997/09/25  20:55:09  labouiss
 * Ajout du traitement des fichiers AMFM (metriques pour les fonts
 * Multiple Master).
 *
 * Revision 1.4  1996/04/10  21:30:16  Christophe_Labouisse
 * Correction RCS Version -> Revision
 *
 * Revision 1.3  1996/04/10  21:25:37  Christophe_Labouisse
 * Ajout des statistiques.
 * Utilisation de RCS pour le numéro de version.
 *
 * Revision 1.2  1996/04/07  14:47:27  LABOUISSE_Christophe
 * Ajout des mots clés VERBOSE et ALL
 * Nouvelle utilisation des arguments permettant maintenant d'avoir plusieurs
 * fichiers ou répertoires traités en une seule commande.
 *
 * Revision 1.1  1996/03/23  22:46:43  LABOUISSE_Christophe
 * Initial revision
 *

/* --- */

