/*
    Fichier vmailfic.c
    Auteur Bernard Chardonneau

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

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


    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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "messages.h"
#include "buflect.h"
#include "fmail.h"
#include "trtentete.h"
#include "trtligne.h"
#include "trtbordure.h"
#include "trtsection.h"
#include "modepage.h"
#include "carspe.h"
#include "szchemin.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 memo_mail (int typetexte);
void memo_texte ();
void genligne (char *buffer);
void regroupeligne (char *buffer);
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  optH = 0;     // indique si affichage ou non des balises HTML


/* 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 puis son contenu
            mem_entete (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 le mémorise
   cette fonction traite essentiellement l'entête du mail
   puis appelle memo_mail pour l'affichage du corps du mail
   Pour un mail multi section, c'st la section du type passé
   en paramètre (text/plain si appel depuis le programme
   principal) qui est affichée */


void mem_entete (int typesection)
{
    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, posTo, posCc, posReply, posSubject, posContent;


    // initialisation
    posDate    = -1;
    posFrom    = -1;
    posTo      = -1;
    posCc      = -1;
    posReply   = -1;
    posSubject = -1;
    posContent = -1;
    nb_lignes  =  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 ("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)
        memchamp (posFrom, 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
    typeorig (posContent);

    // revenir au début du corps du message
    fseek (fmail, pos_deblig, SEEK_SET);
    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"));
}


/* 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 ();

    // 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 ();
    }
}


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

void memo_mail (int typetexte)
{
    // si mode multipart/mixed ou multipart/related, mémoriser
    // le type et la bordure éventuelle de la section suivante
    if ((ctypeorig == MultipMixed) || (ctypeorig == MultipRel))
        mem_soustype ();

    // se positionner (si l'on n'y est pas) sur la section
    // texte ou text/html du mail
    posit_section (typetexte);

    // message d'avertissement éventuel
    test_encode ();

    // 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"));
    }

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

        // si traitement des balises html
        if (optH)
        {
            // supprimer les balises html
            sup_balhtm ();

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

            // et mémoriser la ligne
            genligne (buf_lect);
        }
        // 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 (ctypeorig == MultipRep && nbordures && surbordure ())
        {
            do
                lire_fmail ();
            while (*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 (ctypeorig == MultipMixed)
        liste_pj ();
}



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

void memo_texte ()
{
    // message d'avertissement éventuel
    test_encode ();

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

        // si traitement des balises html
        if (optH)
        {
            // supprimer les balises html
            sup_balhtm ();

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

            // et mémoriser la ligne
            genligne (buf_lect);
        }
        // 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 redimmensionne 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);
}


/* regroupe 2 lignes du fichier mail lorsque le passage
   à la ligne doit être supprimé à l'affichage */


void regroupeligne (char *buffer)
{
    char * lignefusion;


    // allouer un buffer pour contenir les 2 lignes
    lignefusion = (char *) malloc (strlen (buffer) + sizeof (buf_lect) + 1);

    // si espace mémoire suffisant
    if (lignefusion)
    {
        // recopier la dernière ligne lue dans ce buffer
        strcpy (lignefusion, buffer);

        // lire et convertir la suivante et la recopier à la suite
        lire_fmail ();
        majligne ();
        strcat (lignefusion, buf_lect);

        // mémoriser l'ensemble
        if (lignefusion [strlen (lignefusion) - 1] != '\n')
            regroupeligne (lignefusion);
        else
            genligne (lignefusion);

        // libérer le buffer de travail
        free (lignefusion);
    }
    // sinon, on va juste mémoriser la ligne passée en paramètre
    else
        genligne (buffer);
}


/* 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 [szchemin];      // fichier d'impression
    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  mode_affich; // mémorise le mode d'affichage de la section texte


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

    // initialisation
    mode_affich = TextPlain;
    lignecran = 1;
    lignecour = 0;
    erreurs = 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 ou revenir au texte"
                   // "H pour afficher la section Html en supprimant les balises"
                           // "entrée pour récupérer les fichiers joints"
                           // "Control L pour réafficher la page"
                           // "Esc pour sortir de ce programme\n"
                           // "Appuyer sur une touche pour continuer"
                           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_CHOIX_ADR-7");
                           affiche_msg ("AIDE_VMAIL");
                           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;

                               // 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");

                               // avec le nouveau mode d'affichage
                               mem_entete (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;
                               colonsaut = colonpage;
                           }

                           // 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);
                           printf (message ("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%d", 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 ("lpr", ficimpr);

                           // et le supprimer
                           unlink (ficimpr);

                           //le lancement 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'     : effpage (); // on efface la page

                           // si mail multisection ou mail en HTML pur
                           if (ctypeorig != TextPlain)
                           {
                               // si le mail est multi section
                               if (ctypeorig & Multipart)
                               {
                                   // si commande h
                                   if (car == 'h')
                                   {
                                       // si mode HTML, affichage des balises
                                       optH = 0;

                                       // changement du mode d'affichage
                                       if (mode_affich == TextPlain)
                                           mode_affich = TextHtml;
                                       else
                                       {
                                           mode_affich = TextPlain;

                                           // avertissement si retour en mode texte
                                           // "Retour de l'affichage en mode texte"
                                           affiche_msg ("AFFICH_TEXTE");
                                           sleep (1);
                                       }
                                   }
                                   // sinon commande H
                                   else
                                   {
                                       // changement du mode d'affichage
                                       if (mode_affich == TextHtml && optH)
                                       {
                                           mode_affich = TextPlain;
                                           optH = 0;

                                           // avertissement si retour en mode texte
                                           // "Retour de l'affichage en mode texte"
                                           affiche_msg ("AFFICH_TEXTE");
                                           sleep (1);
                                       }
                                       else
                                       {
                                           // mode HTML sans affichage des balises
                                           mode_affich = TextHtml;
                                           optH = 1;
                                       }
                                   }
                               }
                               // sinon, mail en HTML pur
                               else
                                   optH = 1 - optH;

                               // 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");

                               // avec le nouveau mode d'affichage
                               mem_entete (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;
                           }
                           // sinon message d'erreur
                           else
                           {
                    // "Ce mail ne contient qu'une section, commande sans effet"
                               affiche_msg ("MAIL_MONOSECTION");
                               sleep (2);
                           }

                           // 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 ("REP_RACINE_INCONNU");
                               sleep (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 ("MAIL_NON_RESTAUR");
                               sleep (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 ("MAIL_NON_MODIF");
                               sleep (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 ("NON_POS_PJ");
                               sleep (2);
                           }

                           affpage ();
                           erreurs = 0;
                           break;

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

                           // 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 != ESC);

    // si le fichier n'avait pas encore été visualisé
    if (nomfic[strlen (nomfic) - 2] == '.')
    {
        // on enlèvera le .r 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 ();
    }
}


/* 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  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"
    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");

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

    // si rajout dans le carnet d'adresse
    if (choix == 4 && getenv ("mailenv"))
    {
        // trouver le nom du fichier carnet d'adresse
        strcpy (nomfic, getenv ("mailenv"));
        strcpy (nomfic + strlen (nomfic) - 6, ficdir ("FIC_CARNET-ADR"));

        // l'ouvrir en écriture en fin de fichier
        fcopy = fopen (nomfic, "a");
    }
    // sinon si rajout dans la liste des expéditeurs refusés
    else if (choix == 5 && getenv ("mailenv"))
    {
        // trouver le nom du fichier refus d'adresse
        strcpy (nomfic, getenv ("mailenv"));
        strcpy (nomfic + strlen (nomfic) - 6, 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 ? "
        printf (message ("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
        {
            // recherche de la ligne contenant l'adresse
            do
                strcpy (buf_lect, ligne [i++]);
            while (!start ("From") && *buf_lect);

            // si expéditeur trouvé
            if (*buf_lect)
            {
                // sauter le From:
                i = 6;

                // si ajout dans la liste des adresses refusées
                if (choix == 5)
                {
                    // se positionner en début d'adresse
                    while (buf_lect [i] != '@' && buf_lect [i])
                        i++;

                    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 ("EXPED_ABSENT");
                sleep (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);
        sleep (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 [80];
    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"
        printf (message ("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;
}