/*
    Fichier vmailfic.c
    Auteur Bernard Chardonneau

    Logiciel libre, droits d'utilisation précisés en français
    dans le fichier : licence-fr.txt

    Traductions des droits d'utilisation dans les fichiers :
    licence-de.txt , licence-en.txt , licence-es.txt ,
    licence-it.txt , licence-nl.txt , licence-pt.txt ,
    licence-eo.txt , licence-eo-utf.txt

    Droits d'utilisation également sur la page web :
    http://libremail.tuxfamily.org/voir.php?page=droits


    Ce programme affiche le contenu d'un fichier mail passé en paramètre
    Le chemin d'accès au fichier peut être absolu ou relatif.

    On peut utiliser les flèches et les autres touches de déplacement
    pour parcourir le contenu du mail si celui ci tient sur plus d'une
    page.

    Cet outil peut être lancé automatiquement par l'outil vmailsj
    (lui même lancé par vmaildir) lorsqu'on sélectionne un répertoire
    de l'arborescence des mails, puis un mail particulier.
*/


#define appli   // pour la déclaration de variables globales à l'application

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include "messages.h"
#include "buflect.h"
#include "fmail.h"
#include "encodage.h"
#include "trtentete.h"
#include "trtligne.h"
#include "groupeligne.h"
#include "trtbordure.h"
#include "trtsection.h"
#include "modepage.h"
#include "carspe.h"
#include "szchemin.h"
#include "tradmail.h"


#define maxligini     500  // nombre max initial de lignes de texte pour le mail


/* prototypes */
void mem_entete ();
void memchamp (long pos_deblig, int conversion);
void mem_corpsmail ();
void memo_mail (int typetexte);
void memo_texte ();
void genligne (char *buffer);
int  ajoutlignepossible ();
void rechargemail (char *nomfic, int mode_affich);
void navigation (char *nomfic);
void affligne (int numlig);
void cop_mail ();
void recherche (int sens);
int  dansligne (char *chaine, int numlig);


/* variable globale au source
   (pour éviter des tonnes de passages de paramètres) */


char **ligne;    // les lignes de texte du mail
int  maxlig;     // nombre maximum de lignes mémorisées
int  colonsaut;  // nombre de colonnes avant un passage à la ligne souhaitable
int  opthb = 0;  // options d'affichage relatives aux balises HTML
int lig_nonvide; // mémorise s'il y a des caractères significatifs dans la ligne


/* programme principal */

int main (int nbarg, char *varg[])
{
    // récupération du nom de l'exécutable
    memcom (*varg);

    // controle du nombre d'arguments
    if (--nbarg == 1)
    {
        // ouvrir le fichier mail
        fmail = fopen (varg [1], "r");

        // si le fichier a pu être ouvert
        if (fmail)
        {
            // fixer la taille initiale de la liste des fichiers mail
            maxlig = maxligini;

            // allouer la liste des fichiers mail
            ligne = malloc (maxlig * sizeof (char *));

            // vérification allocation
            if (! ligne)
                // "Manque de place mémoire, le logiciel %s ne peut fonctionner"
                errfatale ("MANQUE_MEMOIRE", nomcom ());

            // mémoriser l'entête du mail
            mem_entete ();

            // puis son contenu
            mem_corpsmail (TextPlain);

            // fermer ce fichier
            fclose (fmail);

            // afficher le mail
            navigation (varg [1]);
        }
        else
            // "Fichier %s non trouvé"
            aff_err_arg ("FICH_ABSENT", varg [1]);
    }
    else
        // "Syntaxe : %s nom_fichier_mail"
        psyntaxe ("SYNT_GENE_FICMAIL");

    // pour faire plaisir à gcc qui veut une fonction main de type int
    return (0);
}


/* lit le message choisi et mémorise les champs importants de son entête */

void mem_entete ()
{
    long pos_deblig;    // position dans le fichier en début de ligne
    // position dans le fichier mail des principaux champs de l'entête
    long posDate, posFrom, posXorig, posTo, posCc, posReply, posSubject,
         posContent;
    char *varenv_xorig; // variable d'environnement libremail_xorig
    char xorig;         // information utile de libremail_xorig


    // initialisation
    posDate    = -1;
    posFrom    = -1;
    posXorig   = -1;
    posTo      = -1;
    posCc      = -1;
    posReply   = -1;
    posSubject = -1;
    posContent = -1;
    nb_lignes  =  0;

    // récupérer la partie significative de la variable libremail_xorig
    varenv_xorig = getenv ("libremail_xorig");

    if (varenv_xorig)
        xorig = tolower (*varenv_xorig);
    else
        xorig = '\0';

    // déterminer la largeur d'affichage
    lig_col ();

    // et la largeur après laquelle un passage à la ligne est souhaitable
    if (colonpage < defaut_colon)
        colonsaut = colonpage;
    else
        colonsaut = defaut_colon;

    // lecture de l'entête et mémorisation de la position des champs importants
    do
    {
        // récupérer la position en début de ligne
        pos_deblig = ftell (fmail);

        // lire une ligne de l'entête du message
        lire_fmail ();

        // repérage des champs importants et mémorisation de leur position
        if (start ("X-Mailer") || start ("User-Agent"))  // pour le fun
            genligne (buf_lect);  // le mailer est mémorisé directement
        else if (start ("Date"))
            posDate = pos_deblig;
        else if (start ("From"))
            posFrom = pos_deblig;
        else if (start ("X-Original-From") && xorig != 'n')
            posXorig = pos_deblig;
        else if (start ("To"))
            posTo = pos_deblig;
        else if (start ("Cc"))
            posCc = pos_deblig;
        else if (start ("Reply-To"))
            posReply = pos_deblig;
        else if (start ("Subject"))
            posSubject = pos_deblig;
        else if (start ("Content-Type"))
            posContent = pos_deblig;
        else if (start ("Content-Transfer-Encoding"))
            mem_encodage ();
    }
    while (buf_lect [0] != '\0');  // lecture entête terminée si ligne vide

    // affichage ordonné des champs principaux de l'entête
    if (posDate >= 0)
        memchamp (posDate, 0);
    if (posFrom >= 0 && (posXorig < 0 || xorig == '2'))
        memchamp (posFrom, 1);
    if (posXorig >= 0)
        memchamp (posXorig, 1);
    if (posTo >= 0)
        memchamp (posTo, 1);
    if (posCc >= 0)
        memchamp (posCc, 1);
    if (posReply >= 0)
        memchamp (posReply, 1);
    if (posSubject >= 0)
        memchamp (posSubject, 1);

    // récupérer le type principal du message et ses caractéristiques
    recup_typeorig (posContent);

    // revenir au début du corps du message
    fseek (fmail, pos_deblig, SEEK_SET);
}


/* mémorise un champ de l'entête d'une ou plusieurs lignes
   repéré à partir de sa position dans le fichier mail */


void memchamp (long pos_deblig, int conversion)
{
    // se positionner sur la ligne contenant le champ
    fseek (fmail, pos_deblig, SEEK_SET);

    // lire cette ligne
    lire_fmail ();

    // interpréter si nécessaire les caractères spéciaux
    if (conversion)
        majlignentete ();

    // forcer le premier caractère de la ligne en majuscules
    // bien qu'il doit l'être déjà
    *buf_lect = *buf_lect & 0x5F;

    // et afficher la ligne
    genligne (buf_lect);

    // lire la ligne suivante
    lire_fmail ();

    // tant qu'elle fait partie du même champ
    while (*buf_lect == ' ' || *buf_lect == '\t')
    {
        // interpréter si nécessaire les caractères spéciaux
        if (conversion)
            majlignentete ();

        // et afficher la ligne
        genligne (buf_lect);

        // lire la ligne suivante
        lire_fmail ();
    }
}


/* lit le corps du mail et mémorise son contenu
   dans un mail multi section, c'est la section du type passée
   en paramètre (text/plain si appel depuis le programme
   principal) qui est affichée */


void mem_corpsmail (int typesection)
{
    // sauter la ligne blanche entre l'entête et le corps du mail
    lire_fmail ();

    // mémorisation du contenu du mail
    if (ctypeorig & Multipart)
        // il faudra analyser les sections
        memo_mail (typesection);
    else
        // une simple mémorisation du texte suffit
        memo_texte ();

    // si aucune ligne utile mémorisée
    if (nb_lignes < 2)
        // "Aucun texte récupéré : ce n'est pas un fichier mail"
        genligne (message ("NON_FICMAIL"));
}


/* suppression des balises html et affichage du reste */

void filtre_balhtm ()
{
    // supprimer les balises html
    sup_balhtm ();

    // réduit à un les blancs multiples et les supprime en fin de ligne
    sup_multiblancs ();

    // et convertir les caractère sous la forme &...;
    conv_carhtm ();

    // si la dernière ligne lue n'est pas vide
    if (*buf_lect && *buf_lect != '\n')
    {
        // l'afficher
        genligne (buf_lect);

        // et mémoriser le fait qu'elle n'est pas vide
        lig_nonvide = 1;
    }
    // sinon si la précédente n'était pas vide ou pas d'option -H
    else if (lig_nonvide)
    {
        // l'afficher
        genligne ("");

        // et mémoriser le fait qu'elle est vide
        lig_nonvide = 0;
    }
}


/* mémorisation du contenu d'un mail en mode multipart */

void memo_mail (int typetexte)
{
    // mémorisation des modes multipart trouvés
    int multipmixed = 0, multipalter = 0, multiprel = 0, multiprep = 0;


    // mémoriser tous les modes multipart imbriqués
    do
    {
        switch (ctype)
        {
            case MultipMixed : multipmixed = 1;
                               break;

            case MultipAlter : multipalter = 1;
                               break;

            case MultipRep   : multiprep   = 1;
                               break;

            case MultipRel   : multiprel   = 1;
        }

        // passer à la section suivante
        prochaine_section ();

        // récupérer ses caractéristiques
        recup_infos_section ();
    }
    while ((ctype & Multipart) && lire_fmail ());

    // indiquer si le mail peut contenir des pièces jointes
    if (multipmixed)
        // "Pièce(s) jointes(s) probable(s)"
        genligne (message ("PJ_PROBABLE"));

    // si une section multipart/alternative a été trouvée, se positionner
    // (si l'on n'y est pas) sur la section text/plain ou text/html du mail
    if (multipalter)
        posit_section (typetexte);

    // générer une ligne de séparation avec l'entête
    genligne ("");

    // si structure du mail non conforme, message d'erreur
    if (! lire_fmail ())
    {
        if (typetexte == TextPlain)
            // "Pas de zone texte dans ce mail !!!"
            genligne (message ("MANQUE_ZONE_TEXTE"));
        else
            // "Pas de zone texte html dans ce mail !!!"
            genligne (message ("MANQUE_ZONE_HTML"));
    }

    // si on doit supprimer la partie html avant <body
    if (opthb & 4)
        // le mémoriser
        avantbody = 1;
    else
        avantbody = 0;

    // lecture et mémorisation du corps du message
    do
    {
        // mise en forme de la dernière ligne lue
        majligne ();

        // si on est dans l'entête html qu'on doit supprimer
        if (avantbody)
            // le faire
            supavantbody ();

        // si option de suppression des balises html
        if (opthb & 2)
            // les supprimer
            filtre_balhtm ();
        // sinon
        else
        {
            // on pourra éventuellement regrouper des lignes
            if (buf_lect [strlen (buf_lect)-1] == '\n')
                genligne (buf_lect);
            else
                regroupeligne (buf_lect);
        }

        // et lecture de la suivante
        if (! lire_fmail ())
            return;   // on sort en fin de fichier

        // si mode multipart/report, on saute les entêtes de section
        if (multiprep && nbordures && surbordure ())
        {
            // aller à la prochaine ligne vide
            while (lire_fmail () && *buf_lect)
                ;
        }
    }
    // on s'arrête en fin de fichier, si trop de lignes
    // ont été mémorisées ou sur la prochaine bordure
    while (nb_lignes < maxlig && (nbordures == 0 || ! surbordure ()));

    // si mode multipart mixed on va lister les pièces jointes
    if (multipmixed)
        liste_pj ();
}



/* lecture et mémorisation d'un mail de type text/plain */

void memo_texte ()
{
    // si texte encodé base 64
    if (encodage_texte == Base64)
        // générer une ligne de séparation avec l'entête
        genligne ("");

    // si on doit supprimer la partie html avant <body
    if (opthb & 4)
        // le mémoriser
        avantbody = 1;
    else
        avantbody = 0;

    // répéter
    do
    {
        // mise en forme de la dernière ligne lue
        majligne ();

        // si on est dans l'entête html qu'on doit supprimer
        if (avantbody)
            // le faire
            supavantbody ();

        // si option de suppression des balises html
        if (opthb & 2)
            // les supprimer
            filtre_balhtm ();
        // sinon
        else
        {
            // on pourra éventuellement regrouper des lignes
            if (buf_lect [strlen (buf_lect)-1] == '\n')
                genligne (buf_lect);
            else
                regroupeligne (buf_lect);
        }

        // et lecture de la suivante
    }
    // on s'arrête en fin de fichier ou si trop de lignes ont été mémorisées
    while (lire_fmail () && nb_lignes < maxlig);
}



/* mémorise une ligne du mail */

void genligne (char *buffer)
{
    char * nouvligne;  // tableau alloué dynamiquement pour mémoriser la ligne
    static char pb_alloc [50]; // message d'erreur si allocation impossible
    int  taille_ligne; // longueur de la ligne mémorisée
    int  taille_utile; // longueur de la ligne sans le \n éventuel
    int  i;            // compteur


    // récupérer la longueur de la ligne
    taille_ligne = strlen (buffer);

    // si ligne longue
    if (taille_ligne > colonsaut)
    {
        // on tente de la tronquer à moins de 'colonsaut' car
        i = colonsaut;

        // chercher un blanc pour passer à la ligne
        while (i > 0 && buffer [i] != ' ')
            i--;

        // si pas de blanc trouvé
        if (i == 0)
        {
            // on en cherche un dans l'autre sens
            i = colonsaut;

            while (i < taille_ligne && i < colonpage && buffer [i] != ' ')
                i++;
        }

        // mémoriser la taille de la ligne tronquée
        taille_ligne = i;
    }

    // tenir compte d'un '\n' éventuel en cours de ligne !
    // (dans le cas d'un encodage quoted-printable non conforme
    // ou d'un encodage base64)
    for (i = 0; i < taille_ligne - 1; i++)
        if (buffer [i] == '\n')
            taille_ligne = i;

    // ne pas compter le passage à la ligne en fin de buffer
    if (taille_ligne && buffer [taille_ligne - 1] == '\n')
        taille_utile = taille_ligne - 1;
    else
        taille_utile = taille_ligne;

    // allouer un tableau pour mémoriser la ligne
    nouvligne = (char *) malloc (taille_utile + 1);

    // si le tableau a pu être alloué et on peut mémoriser cette ligne de texte
    if (nouvligne && ajoutlignepossible ())
    {
        // recopier les données dans ce tableau
        for (i = 0; i < taille_utile; i++)
            nouvligne [i] = buffer [i];

        // terminer la chaine
        nouvligne [taille_utile] = '\0';

        // rajouter la ligne mémorisée dans le texte
        ligne [nb_lignes++] = nouvligne;

        // si la ligne a été tronquée, traiter la suite
        if (taille_ligne < strlen (buffer) && nb_lignes < maxlig)
            genligne (buffer + taille_ligne + 1);
    }
    else
    {
        // sinon avertir d'un manque de mémoire
        // "** MANQUE D'ESPACE MEMOIRE ==> TEXTE TRONQUE **"
        strcpy (pb_alloc, message ("TEXTE_TRONQUE"));

        ligne [nb_lignes++] = "";
        ligne [nb_lignes++] = pb_alloc;

        // et aller en fin de fichier pour arrêter sa lecture
        fseek (fmail, 0, SEEK_END);
    }
}


/* vérifie si l'on peut insérer un élément de plus dans le tableau
   ligne, et redimensionne ce tableau si nécessaire */


int ajoutlignepossible ()
{
    char **nouvtableau; // adresse du tableau de remplacement
    int  nouvtaille;    // et sa taille
    int  element;       // compteur : numéro d'élément dans les tableaux


    // cas simple : il reste au moins une place de libre dans le tableau
    if (nb_lignes + 1 < maxlig)
        return (1);

    // calculer la nouvelle taille du tableau
    // l'augmentation est alternativement de 50 % ou 33 % de manière
    // à ce que la taille double après 2 réallocations
    if (maxlig % 3)
        nouvtaille = maxlig + (maxlig / 2);
    else
        nouvtaille = maxlig + (maxlig / 3);

    // allocation mémoire
    nouvtableau = malloc (nouvtaille * sizeof (char *));

    // vérification allocation
    if (nouvtableau)
    {
        // copie du contenu de l'ancien tableau dans le nouveau
        for (element = 0; element < maxlig; element ++)
            nouvtableau [element] = ligne [element];

        // destruction de l'ancien tableau
        free (ligne);

        // que l'on remplace par le nouveau
        ligne = nouvtableau;
        maxlig = nouvtaille;
    }

    // retourne le résultat de la possibilité d'insertion d'éléments
    return (nb_lignes + 1 < maxlig);
}


/* recharge le mail en mémoire
   Cette fonction prend en compte un changement de la
   longueur maximale des lignes, ou du mode d'affichage */


void rechargemail (char *nomfic, int mode_affich)
{
    // libérer la mémoire pour les lignes mémorisées
    while (nb_lignes > 0)
        free (ligne [--nb_lignes]);

    // on va relire le fichier mail
    fmail = fopen (nomfic, "r");

    // mémoriser l'entête du mail
    mem_entete ();

    // avec le nouveau mode d'affichage
    mem_corpsmail (mode_affich);

    // lecture terminée
    fclose (fmail);

    // si numéro de ligne courant trop grand
    if (lignecour >= nb_lignes)
        // le corriger
        lignecour = nb_lignes - 1;

    // idem pour la position à l'écran
    if (lignecran > nb_lignes)
        lignecran = nb_lignes;
}


/* affiche la liste des mails du répertoire et permet de
   la parcourir même si elle tient sur plusieurs pages */


void navigation (char *nomfic)
{
    int  car;         // caractère tapé au clavier
    int  erreurs;     // nombre frappes caractères inconnus comme commandes
    char curdir [szchemin];       // répertoire courant
    char nouvnom [szchemin + 12]; // pour rebaptiser le fichier après première
                                  // visualisation ou le mettre à la poubelle
    char ficimpr [20];            // fichier d'impression
    char fic_mailtrad [20] = "";      // fichier contenant le mail traduit
    FILE *tmpfic;     // descripteur du fichier d'impression
    char *editeur;    // éditeur utilisé pour modifier les mails
    int  colonprec;   // nombre de colonnes d'affichage avant un ^L
    int  i;           // compteur de lignes (pour générer fichier d'impression)
    int  opthb_prec;  // valeur précédente de opthb
    int  mode_affich; // mémorise le mode d'affichage de la section texte
    int  traduit;     // mémorise si mail affiché dans langue origine ou traduit


    // configurer la liaison clavier pour lecture directe avec timeout
    mode_raw ();

    // initialisation
    mode_affich = TextPlain;
    lignecran = 1;
    lignecour = 0;
    erreurs = 0;
    traduit = 0;
    affpage ();

    // récupérer aussi le répertoire courant
    getcwd (curdir, szchemin);

    do
    {
        // lire et traiter une touche du clavier
        car = leccar ();

        switch (car)
        {                  // déplacement dans le texte du mail
            case MONTE   : monte (1);
                           erreurs = 0;
                           break;

            case DESCEND : descend (1);
                           erreurs = 0;
                           break;

            case PAGEUP  : monte (lignepage + lignecran - 2);
                           erreurs = 0;
                           break;

            case PAGEDOWN: descend (2 * lignepage - lignecran - 2);
                           erreurs = 0;
                           break;

            case HOME    :
            case HOMEg   : monte (lignecour);
                           erreurs = 0;
                           break;

            case FIN     :
            case FINg    : descend (nb_lignes - lignecour - 1);
                           erreurs = 0;
                           break;

            case 0       : // aide si trop d'erreurs ou à la demande
            case F1      : effpage ();
                           // "Touches utilisables :\n"
                           // "flèches Pageup, Pagedown, Home et Fin"
                           // "pour se déplacer d'une ou plusieurs lignes"
                           // "i pour Identifier le fichier mail"
                           // "r pour Répondre au mail, t pour le Transférer"
                           // "s pour le Supprimer, m pour Modifier avant envoi"
                           // "u pour restaurer un mail dans la poubelle"
                           // "j pour Joindre des fichiers à un mail à envoyer"
                           // "c pour Copier le mail ou l'adresse d'expéditeur"
                           // "/ ou ? pour rechercher une chaine dans le mail"
                           // "p pour l'imPrimer"
                           // "h pour afficher la section Html sans modification"
                           // "H pour afficher la section Html sans les balises Html"
            // B pour afficher à partir de la balise <Body ...> sans les balises Html"
                           // "l pour spécifier une Langue, T pour Traduire le mail"
                           // "entrée pour récupérer les fichiers joints"
                           // "Control L pour réafficher la page"
                           affiche_msg ("AIDE_CHOIX_ADR-1");
                           affiche_msg ("AIDE_CHOIX_ADR-2");
                           affiche_msg ("AIDE_CHOIX_ADR-3");
                           affiche_msg ("AIDE_VMAILFIC_SJ1");
                           affiche_msg ("AIDE_VMAILFIC-1");
                           affiche_msg ("AIDE_VMAILFIC-2");
                           affiche_msg ("AIDE_VMAILFIC-3");
                           affiche_msg ("AIDE_VMAILFIC_SJ2");
                           affiche_msg ("AIDE_VMAILFIC-4");
                           affiche_msg ("AIDE_VMAILFIC-5");
                           affiche_msg ("AIDE_VMAILFIC-6");
                           affiche_msg ("AIDE_VMAILFIC-7");
                           affiche_msg ("AIDE_VMAILFIC-8");
                           affiche_msg ("AIDE_VMAILFIC-9");
                           affiche_msg ("AIDE_VMAILFIC-10");
                           affiche_msg ("AIDE_VMAILFIC-11");
                           affiche_msg ("AIDE_VMAILFIC-12");
                           affiche_msg ("AIDE_CHOIX_ADR-7");

                           // variante pour la touche Esc
                           if (util_systemd ())
                               // "q ou Esc (2 fois) pour Quitter ce programme"
                               affiche_msg ("AIDE_VMAIL2");
                           else
                               // "q ou Esc pour Quitter ce programme"
                               affiche_msg ("AIDE_VMAIL");

                           // "Appuyer sur une touche pour continuer"
                           affiche_msg ("ATTENTE_CLAVIER");
                           leccar ();
                           affpage ();
                           erreurs = 0;
                           break;

                           // réaffiche la page en tenant compte d'un
                           // éventuel redimentionnement de la fenêtre
                           // mémoriser la lageur précédente de la page
            case CTRL    : colonprec = colonpage;

                           // déterminer la nouvelle largeur d'affichage
                           lig_col ();

                           // si elle a changé
                           if (colonpage != colonprec)
                           {
                               // recalculer la largeur après laquelle un
                               // passage à la ligne est souhaitable
                               if (colonpage < defaut_colon)
                                   colonsaut = colonpage;
                               else
                                   colonsaut = defaut_colon;

                               // recharger le mail
                               rechargemail (nomfic, mode_affich);
                           }

                           // réafficher la page
                           affpage ();
                           erreurs = 0;
                           break;

                           // identifie le fichier mail (donne son nom)
            case 'i'     : effpage ();
                           // "\nFichier : %s/%s\n\n"
                           // "Appuyer sur une touche pour continuer"
                           printf (message ("AFF_CHEMFICH"), curdir, nomfic);
                           affiche_msg_nocr ("ATTENTE_CLAVIER");
                           leccar ();
                           affpage ();
                           erreurs = 0;
                           break;

                           // réponse à un mail
            case 'r'     : execom ("repmail", nomfic);

                           // on revient à la liste des mails
                           car = ESC;
                           break;

                           // transfert d'un mail
            case 't'     : execom ("trsfmail", nomfic);

                           // on revient à la liste des mails
                           car = ESC;
                           break;

                           // impression du mail
            case 'p'     : // on crée un fichier tampon dans le répertoire
                           // /tmp pour éviter les problèmes lorsque le
                           // répertoire courant est protégé en écriture
                           sprintf (ficimpr, "/tmp/impr-mail-%04X", getpid ());

                           // ouvrir en écriture un fichier tampon
                           tmpfic = fopen (ficimpr, "w");

                           // remplir de fichier d'impression
                           for (i = 0; i < nb_lignes; i++)
                               fprintf (tmpfic, "%s\n", ligne [i]);

                           fclose (tmpfic);

                           // imprimer le fichier généré
                           execom ("lp", ficimpr);

                           // et le supprimer
                           unlink (ficimpr);

                           // l'appel d'execom suppose de réafficher la page
                           // Note : ça crée un léger clignottement qui permet
                           // de visualiser la prise en compte de l'impression
                           affpage ();

                           erreurs = 0;
                           break;

                           // affichage de la section text/html si elle existe
            case 'h'     :
            case 'H'     :
            case 'b'     :
            case 'B'     : effpage (); // on efface la page

                           // si mail multisection ou mail en HTML pur
                           if (ctypeorig != TextPlain)
                           {
                               // récupérer la valeur précédente de opthb
                               opthb_prec = opthb;

                               // prendre en compte la demande de changement
                               switch (car)
                               {
                                   case 'h' : opthb = 1;
                                              break;

                                   case 'H' : opthb = 3;
                                              break;

                                   case 'b' : opthb = 5;
                                              break;

                                   case 'B' : opthb = 7;
                                              break;
                               }

                               // si on n'a pas changé d'option d'affichage
                               if (opthb == opthb_prec)
                               {
                                   // revenir à un  affichage en mode texte
                                   opthb = 0;

                                   // "Retour de l'affichage en mode texte"
                                   affiche_msg_nocr ("AFFICH_TEXTE");
                                   attendre (1);

                                   // on affichera la section text/plain
                                   mode_affich = TextPlain;
                               }
                               // sinon
                               else
                                   // on affichera la section text/html
                                   mode_affich = TextHtml;

                               // recharger le mail
                               rechargemail (nomfic, mode_affich);
                           }
                           // sinon message d'erreur
                           else
                           {
                    // "Ce mail ne contient qu'une section, commande sans effet"
                               affiche_msg_nocr ("MAIL_MONOSECTION");
                               attendre (2);
                           }

                           // réafficher la page
                           affpage ();
                           erreurs = 0;
                           break;

                           // choix d'une langue pour traduction
            case 'l'     : effpage ();
                           fflush (stdout);

                           // un fichier de traduction a des chances d'exister
                           if (*fic_mailtrad)
                               // le détruire s'il existe (traduction à refaire)
                               unlink (fic_mailtrad);

                           // selectionner la langue à traduire
                           choixlangue (1);

                           // réafficher la page
                           affpage ();
                           erreurs = 0;
                           break;

                           // traduction du mail
            case 'T'     : // si on affichait la version traduite du mail
                           if (traduit)
                           {
                               // recharger le mail d'origine
                               rechargemail (nomfic, mode_affich);
                               traduit = 0;
                           }
                           // sinon faire la traduction et la mémoriser
                           else
                               traduit = trad_mail (nomfic, mode_affich,
                                                       fic_mailtrad, ligne);

                           // réafficher la page
                           affpage ();
                           erreurs = 0;
                           break;

            case SUPR    : // suppression d'un mail
            case 's'     : // récupérer le répertoire des mails supprimés
                           if (getenv ("mailpoub"))
                               strcpy (nouvnom, getenv ("mailpoub"));
                           else
                               *nouvnom = '\0';

                           // si on est dans ce répertoire
                           if (strcmp (curdir, nouvnom) == 0)
                           {
                               // supprimer le fichier mail
                               unlink (nomfic);

                               // on revient à la liste des mails
                               car = ESC;
                           }
                           // sinon (cas général)
                           else if (*nouvnom)
                           {
                               // créer le répertoire poubelle si nécessaire
                               mkdir (nouvnom, 0755);

                               // et y mettre le fichier mail
                               strcat (nouvnom, "/");
                               strcat (nouvnom, nomfic);

                               // sans l'extention des fichiers à lire
                                 if (nouvnom [strlen (nouvnom) - 2] == '.')
                                     nouvnom [strlen (nouvnom) - 2] = '\0';

                               rename (nomfic, nouvnom);

                               // on revient à la liste des mails
                               car = ESC;
                           }
                           // sinon (racine des mails inconnue)
                           else
                           {
                               effpage ();

                            // "Racine des mails inconnue, pas de suppression"
                               affiche_msg_nocr ("REP_RACINE_INCONNU");

                               attendre (2);
                               affpage ();
                           }
                           erreurs = 0;
                           break;

                           // restauration d'un mail
            case 'u'     : // si on est dans le répertoire poubelle
                           if (strcmp (curdir, getenv ("mailpoub")) == 0)
                           {
                               // déplacer le fichier mail dans entree
                               sprintf (nouvnom, "../%s/%s",
                                        ficdir ("DIR_ENTREE"), nomfic);
                               rename (nomfic, nouvnom);

                               // on revient à la liste des mails
                               car = ESC;
                           }
                           // sinon, message d'erreur
                           else
                           {
                               effpage ();

                    // "Seuls les mails dans la poubelle peuvent être restaurés"
                               affiche_msg_nocr ("MAIL_NON_RESTAUR");

                               attendre (2);
                           }

                           affpage ();
                           erreurs = 0;
                           break;

                           // modification d'un mail à envoyer
                           // ou rajout de pièces jointes au mail
            case 'm'     :
            case 'j'     : effpage ();
                           fflush (stdout);

                           // si on est dans le répertoire des mails à envoyer
                           if (getenv ("mailenv") &&
                               strcmp (curdir, getenv ("mailenv")) == 0)
                           {
                               // traitement en fonction de la touche tapée
                               if (car == 'm')
                               {
                                   // modifier le mail
                                   editeur = getenv ("EDITOR");

                                   if (editeur)
                                       execom (editeur, nomfic);
                                   else
                                       execom ("vi", nomfic);
                               }
                               else
                                   // joindre des fichiers
                                   execom ("joindre", nomfic);

                               // on revient à la liste des mails
                               car = ESC;
                           }
                           else
                           {
                               // sinon, message d'erreur
                   // "Seuls les mails en attente d'envoi peuvent être modifiés"
                               affiche_msg_nocr ("MAIL_NON_MODIF");

                               attendre (2);
                               affpage ();
                           }
                           erreurs = 0;
                           break;

                           // copie de tout ou partie du mail
            case 'c'     : effpage ();
                           fflush (stdout);
                           cop_mail ();
                           erreurs = 0;
                           affpage ();
                           break;

                           // extraction des pièces jointes
                           // "-> Fichier joint : "
            case '\n'    : if (memcmp (ligne [lignecour], message ("FICJOINT")
                                        , strlen (message ("FICJOINT"))) == 0)
                               execom ("recuppj", nomfic);
                           else
                           {
                               effpage ();

                            // "Vous n'êtes pas positionné sur une pièce jointe"
                               affiche_msg_nocr ("NON_POS_PJ");

                               attendre (2);
                           }

                           affpage ();
                           erreurs = 0;
                           break;

            case '/'     : // recherche d'une chaine de caractères
            case '?'     : recherche (car);
                           erreurs = 0;

            case 'q'     : // sortie du programme
            case ESC     : break;

                           // sortie aussi sur :q
            case ':'     : car = leccar ();

                           if (car == 'q')
                           {
                               car = ESC;
                               break;
                           }

            default      : putchar (7);  // bip

                           // si trop d'erreurs, afficher l'aide
                           if (++erreurs == 5)
                               ungetc (0, stdin);  // on ira sur l'aide
        }
    }
    while (car != 'q' && car != ESC);

    // si le fichier n'avait pas encore été visualisé
    if (nomfic[strlen (nomfic) - 2] == '.')
    {
        // on enlèvera le .n de l'ancien nom
        strcpy (nouvnom, nomfic);
        nouvnom [strlen (nouvnom) - 2] = '\0';

        // et voila !
        rename (nomfic, nouvnom);
    }

    // si on a appelé ce programme directement
    if (! getenv ("mailenv"))
    {
        // descendre en bas de page
        while (lignecran++ < lignepage && lignecour++ < nb_lignes)
            putchar ('\n');

        // retour à la configuration standard du clavier
        mode_normal ();
    }

    // si un fichier de traduction du mail a été créé (ou si son nom existe)
    if (*fic_mailtrad)
        // détruire le fichier s'il est présent
        unlink (fic_mailtrad);
}


/* affiche une ligne du mail */

void affligne (int numlig)
{
    char *maligne; // pour éviter de trop manipuler des indices de tableau
    int i;         // simple compteur


    // initialisation
    i = 0;
    maligne = ligne [numlig];

    // copie de la ligne sans déborder
    while (*maligne && i < colonpage)
    {
        // cas général, on recopie le caractère courant
        if (*maligne != '\t')
            putchar (*maligne);
        // si tabulation
        else
            // écrire des blancs
            do
            {
                putchar (' ');
                i++;
            }
            // sans déborder de la fenêtre d'affichage
            while (i % 8 && i < colonpage);

        // passer au caractère suivant
        maligne ++;
    }
}



/* copie de tout ou partie du fichier mail */

void cop_mail ()
{
    char nomfic [szchemin]; // nom du fichier résultat
    FILE *fcopy;            // descripteur fichier utilisé pour une copie
    int  choix;             // choix de la zone à copier
    int  ligFrom, ligXorig; // lignes du champ expéditeur
    int  i;                 // compteur (pour copier le mail dans un fichier)


    // choix de la zone à copier
    // "1) copie du mail complet"
    // "2) copie du message seul"
    // "3) copie de l'adresse de l'expéditeur"
    // "4) rajout de l'expéditeur dans le carnet d'adresse"
    // "5) rajout de l'expéditeur dans les adresses refusées"
    // "6) rajout du nom de domaine de l'expéditeur dans les adresses refusées"
    affiche_msg ("MENU_COPIE-1");
    affiche_msg ("MENU_COPIE-2");
    affiche_msg ("MENU_COPIE-3");
    affiche_msg ("MENU_COPIE-4");
    affiche_msg ("MENU_COPIE-5");
    affiche_msg ("MENU_COPIE-6");

    do
    {
        // "Votre choix ? "
        affiche_msg_nocr ("VOTRE_CHOIX");
        choix = leccar () - '0';
    }
    while (choix < 1 || choix > 6);

    // si rajout dans le carnet d'adresse ou la liste des expéditeurs refusés
    if (choix >= 4 && getenv ("mailenv"))
    {
        // rechercher le répertoire racine de la messagerie
        strcpy (nomfic, getenv ("mailenv"));

        // à partir de la variable $mailenv on remonte au dernier /
        i = strlen (nomfic);

        while (nomfic [--i] != '/' && i)
            ; // ne rien faire, tout est dans la condition

        if (choix == 4)
            // rajout dans le carnet d'adresse
            strcpy (nomfic + i + 1, ficdir ("FIC_CARNET-ADR"));
        else
            // rajout dans la liste des expéditeurs refusés
            strcpy (nomfic + i + 1, ficdir ("FIC_REFUS_ADR"));

        // l'ouvrir en écriture en fin de fichier
        fcopy = fopen (nomfic, "a");
    }
    // sinon : copie dans un nouveau fichier
    else
    {
        // afficher le répertoire courant
        getcwd (nomfic, szchemin);
        // "Répertoire courant : %s\n"
        printf (message ("REPERT_COURANT"), nomfic);

        // choix du fichier par l'opérateur
        // "Chemin d'accès au fichier qui contiendra la copie ? "
        affiche_msg_nocr ("CHEM_FIC_COPIE");
        mode_normal ();
        fgets (nomfic, sizeof (nomfic), stdin);

        // suppression du '\n' parasite
        nomfic [strlen (nomfic) - 1] = '\0';

        // création du fichier destinataire de la copie
        fcopy = fopen (nomfic, "w");
    }

    // si ouverture du fichier réussie
    if (fcopy)
    {
        // on se positionne en début de mail
        i = 0;

        // si copie du message (avec ou sans entête)
        if (choix < 3)
        {
            // saut de l'entête ?
            if (choix == 2)
            {
                while (*ligne [i])
                    i++;

                i++;
            }

            // copie du mail complet ou du message
            while (i < nb_lignes)
            {
                fputs (ligne [i++], fcopy);
                fputc ('\n', fcopy);
            }
        }
        // copie de l'adresse de l'expéditeur
        else
        {
            // initialisation
            ligFrom  = 0;
            ligXorig = 0;

            // recherche de la ligne contenant l'adresse
            do
            {
                strcpy (buf_lect, ligne [i]);

                if (start ("From"))
                    ligFrom = i;
                else if (start ("X-Original-From"))
                    ligXorig = i;

                i++;
            }
            while (!start ("To") && *buf_lect);

            // si expéditeur trouvé
            if (ligXorig || ligFrom)
            {
                // choisir le meilleur entre From: et X-Original-From:
                if (ligXorig)
                {
                    strcpy (buf_lect, ligne [ligXorig]);
                    i = 17;
                }
                else
                {
                    strcpy (buf_lect, ligne [ligFrom]);
                    i = 6;
                }

                // si ajout dans la liste des adresses refusées
                if (choix > 4)
                {
                    // rechercher l'adresse dans le champ expéditeur
                    while (buf_lect [i] != '@' && buf_lect [i])
                        i++;

                    // si refus de l'adresse (et pas du nom de domaine complet)
                    if (choix == 5)
                    {
                        // se positionner en début d'adresse
                        while (buf_lect [i] != '<' && buf_lect [i] != ' ')
                            i--;

                        i++;
                    }
                }

                // copier l'adresse dans le fichier
                do
                {
                    // pour la lisibilité du carnet d'adresse
                    if (buf_lect [i] == '<')
                        buf_lect [i] = ' ';

                    // copie d'un caractère et passage au suivant
                    fputc (buf_lect [i++], fcopy);
                }
                while (buf_lect [i] != '>' && buf_lect [i]);

                // terminer la ligne
                fputc ('\n', fcopy);
            }
            else
            {
                // sinon message d'erreur
                // "Expéditeur non trouvé"
                affiche_msg_nocr ("EXPED_ABSENT");
                attendre (2);
            }
        }

        // terminé, on ferme le fichier
        fclose (fcopy);
    }
    else
    {
        // sinon, message d'erreur
        // "Impossible de créer le fichier %s\n"
        printf (message ("IMPOS_CRE_FICH"), nomfic);
        montecurs ();
        attendre (2);
    }

    // reconfigurer la liaison clavier pour lecture directe avec timeout
    mode_raw ();
}



/* recherche d'une chaine de caractères dans le mail */

void recherche (int sens)
{
    // chaine à rechercher, on utilise une variable statique
    // pour garder sa valeur aux appels suivants de la fonction
    static char chaine [120];
    int car;    // caractère lu au clavier
    int limite; // ligne marquant une extrémité du mail
    int trouve; // indicateur : ligne contenant la chaine trouvée
    int i, j;   // compteurs


    // descendre en bas de page et effacer la dernière ligne
    baspage ();
    effligne ();

    // saisir la chaine à rechercher
    putchar (sens);
    car = leccar ();

    // si chaine vide, on conserve la précédente
    if (car != '\n')
    {
        // sinon on mémorise la nouvelle chaine
        i = 0;

        do
        {
            // cas particulier : caractère d'effacement
            if (car == RECULE || car == EFFCAR)
            {
                if (i > 0)
                {
                    putchar (CTRH);
                    putchar (' ');
                    putchar (CTRH);
                    i--;
                }
            }
            // cas général : affichage et mémorisation d'un caractère
            else if (i < sizeof (chaine))
            {
                putchar (car);
                chaine [i++] = car;
            }

            // lire le caractère suivant
            car = leccar ();
        }
        // jusqu'à ce que chaine entièrement saisie
        while (car != '\n');

        // terminer la chaine
        chaine [i] = '\0';
    }

    // initialisation variables pour recherche
    if (sens == '/')
    {
        sens = 1;
        limite = nb_lignes - 1;
    }
    else
    {
        sens = -1;
        limite = 0;
    }

    // début de la recherche à partir d'une ligne voisine de la ligne courante
    i = lignecour;

    // répeter
    do
    {
        // si on n'a pas atteint l'extrémité du mail
        if (i != limite)
            // descendre ou remonter d'une ligne selon le sens de recherche
            i = i + sens;
        else
            // sinon aller à l'autre extrémité du mail
            i = nb_lignes - 1 - limite;

        // chercher si la chaine est présente dans la nouvelle ligne
        trouve = dansligne (chaine, i);
    }
    // jusque chaine de caractères trouvée ou tout le mail exploré
    while (! dansligne (chaine, i) && i != lignecour);

    // si chaine non trouvée
    if (! trouve)
    {
        // afficher un message d'erreur
        // " Chaine non trouvée"
        affiche_msg_nocr ("CHAINE_ABSENTE");
        putchar (7);
    }

    // revenir en début de ligne
    putchar ('\r');

    // remonter à la ligne sur laquelle on était
    j = lignepage;

    while (j-- > lignecran)
        montecurs ();

    // si chaine trouvée
    if (trouve)
    {
        // se positionner sur la ligne de la chaine s'il on n'y est pas déjà
        if (i < lignecour)
            monte (lignecour - i);
        else if (i > lignecour)
            descend (i - lignecour);

        // réafficher la ligne contenant la chaine en surbrillance
        clair ();
        affligne (lignecour);
        putchar ('\r');
        lumnor ();
    }
}



/* vérification de la présence d'une chaine dans la numligième ligne */

int dansligne (char *chaine, int numlig)
{
    char *maligne; // pour éviter de trop manipuler des indices de tableau
    int i, j;      // simples compteurs


    // initialisation
    maligne = ligne [numlig];
    i = 0;

    // tantque ligne non explorée en entier
    while (maligne [i])
    {
        // si caractère courant = premier caractère de la chaine
        if (maligne [i] == *chaine)
        {
            // vérifier la concordance des caractères suivants
            j = 1;

            while (chaine [j] && (chaine [j] == maligne [i+j]))
                j++;

            // si chaine trouvée, sortir de la fonction
            if (! chaine [j])
                return 1;
        }

        // sinon, essayer avec la suite de la ligne
        i++;
    }

    // chaine non trouvée dans la ligne
    return 0;
}