/*
Fichier vmailsj.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 les caractéristique de chaque mail
mémorisé dans un fichier.
Pour chaque fichier mail, il y a une ligne d'affichage de la forme :
expéditeur sujet date et heure d'envoi
destinataire sujet date et heure d'envoi
selon qu'il s'agisse d'un mail reçu ou envoyé (ou à envoyer)
On peut naviguer dans la liste avec les flèches pour sélectionner
le mail à visualiser.
Une fois sélectionné, on pourra répondre à ce mail ou le transférer.
Par défaut, on analyse les mails du répertoire courant.
On peut aussi indiquer un autre répertoire de recherche.
Cet outil peut être lancé automatiquement par la commande
vmaildir lorsqu'on sélectionne un répertoire de l'arborescence
des mails.
Il lance à son tour vmailfic pour la visalisation des mails.
*/
#define appli // pour la déclaration de variables globales à l'application
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include "messages.h"
#include "buflect.h"
#include "fmail.h"
#include "trtentete.h"
#include "modepage.h"
#include "carspe.h"
#include "szchemin.h"
// mettez le #define qui suit en commentaire si vous ne
// voulez aucune correction du fuseau horaire sur les mails
#define correct_fh
#define max_nbfic_ini 500 // nombre max initial de fichiers mail
#define largeurmax 120 // largeur maximale d'une ligne affichée
// largeur ou largeur maximum des différents champs d'une ligne
#define sz_date 14
#define szmax_nom ((largeurmax / 4) - 1)
#define szmax_sujet (largeurmax - szmax_nom - sz_date - 2)
#define an_pivot 90 // l'année des mails les plus vieux sur 2 chiffres
/* type de données */
typedef struct
{
char nomfic [12];
long ordre;
char nom [szmax_nom];
char sujet [szmax_sujet];
char date [sz_date];
} descmail;
/* prototypes */
void lister_mails ();
int memfic (char *nomfic);
void lect_fuseau_h ();
void corrige_fh (int *jour, int *mois, int *an, int *heure,
int *min, int decal);
int ajoutlistepossible ();
void tridate ();
void tridate_inv ();
void navigation ();
void affligne (int numlig);
void suprefmail ();
void majliste_mails ();
void recherche (int sens);
int dansligne (char *chaine, int numlig);
/* variable globale au source
(pour éviter des tonnes de passages de paramètres) */
descmail **fichmail; // noms des fichiers mail
int nbmaxfic; // nombre maximun de fichiers mails mémorisés
int fh_loc; // fuseau horaire local
int test_xorig; // traitement ou non du champ X-Original-From
/* programme principal */
int main (int nbarg, char *varg[])
{
int opt_inv; // option d'inversion de l'ordre d'affichage
char fic_cmd_trad [20]; // nom fichier qui peut avoir été créé par vmailfic
// récupération du nom de l'exécutable
memcom (*varg);
// mémorisation de l'option -i (ou -r) éventuelle
if (--nbarg > 0 && ((strcmp (varg [1], "-i") == 0)
|| (strcmp (varg [1], "-r") == 0)))
{
opt_inv = 1;
// passage à l'argument suivant
varg ++;
nbarg --;
}
else
opt_inv = 0;
// récupération éventuelle du nom du répertoire des mails
if (nbarg == 1)
{
// positionnement dans ce répertoire
if (chdir (varg [1]) < 0)
{
// "Répertoire %s inexistant"
aff_err_arg ("REPERT_INEXISTANT", varg [1]);
return (0);
}
}
// si aucune erreur dans la liste des paramètres
if (nbarg < 2)
{
// fixer la taille initiale de la liste des fichiers mail
nbmaxfic = max_nbfic_ini;
// allouer la liste des fichiers mail
fichmail = malloc (nbmaxfic * sizeof (descmail *));
// vérification allocation
if (! fichmail)
// "Manque de place mémoire, le logiciel %s ne peut fonctionner"
errfatale ("MANQUE_MEMOIRE", nomcom ());
#ifdef correct_fh
// récupérer et mémoriser le fuseau horaire local
lect_fuseau_h ();
#endif
// récupération de la liste des fichiers mail
lister_mails ();
// si des fichiers mails ont été trouvés
if (nb_lignes > 0)
{
// les ordonner par date
if (opt_inv)
tridate_inv ();
else
tridate ();
// générer le nom d'un fichier destiné à contenir une commande de
// traduction. Il pourra être créé lors d'un appel de vmailfic
// envoyer ce nom de fichier aux processus fils via une variable
// d'environement.
sprintf (fic_cmd_trad, "/tmp/cmd-trad-%04X", getpid ());
setenv ("libremail-cmd-trad", fic_cmd_trad, 1);
// remarque : on procède ainsi pour pouvoir réutiliser le même
// choix de traduction pour tous les mails du répertoire.
// avec une version de gnu/linux assez ancienne, on pouvait
// fabriquer ce nom de fichier dans les processus fils en utilisant
// la fonction getppid, mais ça ne fonctionne plus sur les
// distributions récentes.
// afficher le caractéristiques des mails et naviguer dans la liste
navigation ();
// détruire le fichier fic_cmd_trad s'il a été créé
unlink (fic_cmd_trad);
}
else
// sinon, pas nécessaire de rester dans l'outil
// "pas de mails dans ce répertoire"
affiche_msg ("REPERT_SANS_MAIL");
}
else
// "Syntaxe : %s [-i] [répertoire]"
psyntaxe ("SYNT_VMAILSJ");
// pour faire plaisir à gcc qui veut une fonction main de type int
return (0);
}
/* récupère les caractéristiques des mails */
void lister_mails ()
{
DIR *repert;
struct dirent *entree;
struct stat descfic;
int errmem;
char *varenv_xorig;
// initialisation
nb_lignes = 0;
errmem = 0;
// déterminer si on va traiter les champs X-Original-From
varenv_xorig = getenv ("libremail_xorig");
if (varenv_xorig)
test_xorig = (tolower (*varenv_xorig) != 'n');
else
test_xorig = 1;
// accéder au répertoire
repert = opendir (".");
// lire un nom de fichier
entree = readdir (repert);
// tant qu'il y a des noms de fichier à lire et qu'on peut les traiter
while (entree && ! errmem)
{
// mémoriser le fichier si son nom convient
if (strlen (entree->d_name) == 8 ||
(strlen (entree->d_name) == 10 && entree->d_name [8] == '.'))
{
// et si ce n'est pas un répertoire
stat (entree->d_name, &descfic);
if ((descfic.st_mode & S_IFMT) == S_IFREG)
errmem = memfic (entree->d_name);
}
// et lire le nom du fichier suivant
entree = readdir (repert);
}
// avertissement si on a atteint les limites de stockage
if (errmem)
{
effpage ();
// "Manque de place mémoire, liste des mails tronquée"
affiche_msg_nocr ("DEBORD_MEM_TRONCAT");
attendre (3);
}
}
/* mémorisation des caractéristiques d'un mail
retourne 1 si problème d'espace mémoire, 0 dans tous les autres cas */
int memfic (char *nomfic)
{
// buffers pour mémoriser les caractéristiques principales du message
char bufFromTo [120], bufSubject [120], bufDate [120];
// les noms de mois dans les champs Date: des mails
static char *mois [] = { "XXX", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
descmail *mailcourant; // la description en cours de génération
int jour, numois, an, heure, min; // date et heure du mail
int fhoraire, decalh; // fuseau horaire et décalage horaire
int affichedest; // indique si on doit afficher le champ destinataire
int i, j; // simples compteurs
// ouverture du fichier mail
fmail = fopen (nomfic, "r");
// sortie si erreur imprévue
if (fmail == 0)
{
affpage ();
// "Fichier %s protégé en lecture"
aff_err_arg ("ACCES_FICH_LECT", nomfic);
montecurs ();
attendre (2);
return (0); // erreur non remontée
}
// déterminer le champ à afficher entre expéditeur et destinataire
affichedest = (tolower (*nomfic) == 'e') || (tolower (*nomfic) == 's');
// initialisation
bufFromTo [0] = '\0';
bufSubject [0] = '\0';
bufDate [0] = '\0';
// tantqu'on peut lire une ligne de l'entête du mail
while (lire_fmail () && buf_lect [0] != '\0')
{
// si la ligne correspond au champ sujet
if (start ("Subject"))
{
// mémoriser la ligne
memconvbuf (bufSubject);
// on va tester la ligne qui suit
lire_fmail ();
majlignentete ();
// si c'est encore une ligne du sujet
if (*buf_lect == ' ' || *buf_lect == '\t')
{
// initialisation compteurs
i = 1;
j = strlen (bufSubject);
// compléter le sujet du mail
while (buf_lect [i] && j < sizeof (bufSubject) - 1)
bufSubject [j++] = buf_lect [i++];
// terminer la chaine de caractères
bufSubject [j] = '\0';
}
}
// mémorisation des autres caractéristiques importantes
if (start ("Date"))
membuf (bufDate);
else if (start ("From") && ! affichedest && ! *bufFromTo)
memconvbuf (bufFromTo);
else if (start ("X-Original-From") && test_xorig)
memconvbuf (bufFromTo);
else if (start ("To") && affichedest)
memconvbuf (bufFromTo);
}
// libérer le fichier mail
fclose (fmail);
// si l'on n'a pas trouvé de ligne Date: From: ou Subject:
if (! *bufDate && ! *bufFromTo && ! *bufSubject)
return (0); // ce n'est pas un fichier mail
// si on ne peut pas mémoriser la description dans la liste
if (! ajoutlistepossible ())
return (1); // on travaillera avec une liste partielle
// allouer de l'espace mémoire pour stocker les caractéristiques du mail
mailcourant = (descmail *) malloc (sizeof (descmail));
// si manque de place mémoire
if (! mailcourant)
return (1); // on travaillera avec une liste partielle
// mémoriser le nom du fichier
strcpy (mailcourant->nomfic, nomfic);
// mémorisation du nom de l'expéditeur ou du destinataire du message
j = 0;
if (*bufFromTo)
{
// sauter le mot clé From: , To: ou X-Original-From:
if (affichedest)
i = 4;
else if (tolower (*bufFromTo) == 'x')
i = 16;
else
i = 6;
// se positionner sur le 1er caractère significatif du nom
while (bufFromTo [i] == '"' || bufFromTo [i] == ' ')
i++;
// mémorisation du nom
mailcourant->nom [j++] = bufFromTo [i++];
while (j < szmax_nom && bufFromTo [i] && bufFromTo [i] != '<'
&& bufFromTo [i] != '"' && bufFromTo [i] != '=')
mailcourant->nom [j++] = bufFromTo [i++];
}
// compléter le nom par des blancs
while (j < szmax_nom)
mailcourant->nom [j++] = ' ';
// mémorisation du sujet du message
j = 0;
if (*bufSubject)
{
// sauter le mot clé Subject:
i = 8;
// se positionner sur le 1er caractère significatif du sujet
while (bufSubject [i] == ' ')
i++;
// mémoriser le sujet
while (j < szmax_sujet && bufSubject [i])
mailcourant->sujet [j++] = bufSubject [i++];
}
// compléter le sujet par des blancs
while (j < szmax_sujet)
mailcourant->sujet [j++] = ' ';
// mémorisation de la date et de l'heure du message
if (*bufDate)
{
// sauter le mot clé Date:
i = 6;
// récupérer le jour
while (bufDate [i] < '0' || bufDate [i] > '9')
i++;
jour = atoi (bufDate + i);
// se positionner sur le mois
while (bufDate [i] > ' ')
i++;
i++;
// récupérer le numéro du mois
numois = 12;
while (numois > 0 && (toupper (bufDate [i]) != mois [numois][0]
|| tolower (bufDate [i+1]) != mois [numois][1]
|| tolower (bufDate [i+2]) != mois [numois][2]))
numois--;
// récupérer l'année sur 2 chiffres
while (bufDate [i] < '0' || bufDate [i] > '9')
i++;
an = atoi (bufDate + i) % 100;
// récupérer l'heure et les minutes
while (bufDate [i] > ' ')
i++;
heure = atoi (bufDate + i);
min = atoi (bufDate + i + 4);
#ifdef correct_fh
// se positionner sur le fuseau horaire
while (bufDate [i] && bufDate [i] != '+' && bufDate [i] != '-')
i++;
// récupérer le fuseau horaire
if (bufDate [i])
{
// on traitera les décalages sur les heures et les minutes
fhoraire = atoi (bufDate + i);
// s'il faut faire une correction de fuseau horaire
if (fhoraire != fh_loc && fh_loc > -2400)
// la faire
corrige_fh (&jour, &numois, &an, &heure, &min,
fh_loc - fhoraire);
}
#endif
// mémoriser la date sous forme de chaine de caractères
sprintf (mailcourant->date, "%02d/%02d/%02d %02d:%02d",
jour, numois, an, heure, min);
// les années 90 à 99 sont considérées du siècle passé
if (an >= an_pivot)
an = an - 100;
// mémoriser aussi la date sous forme numérique pour tri ultérieur
/* on tient compte du réglage le plus fréquent sur les ordinateurs
européens : l'ordinateur est réglé sur l'heure locale */
mailcourant->ordre = ((((((((long) an * 12) + numois) * 31)
+ jour) * 24) + heure) * 60) + min;
}
else
{
mailcourant->date [0] = '\0';
mailcourant->ordre = 0;
}
// mémoriser la description dans la liste
fichmail [nb_lignes++] = mailcourant;
// tout s'est bien déroulé
return (0);
}
/* récupérer et mémoriser le fuseau horaire local */
void lect_fuseau_h ()
{
char commande [26];
char *nomfic;
FILE *fcaract;
// initialisation avec une valeur "impossible"
fh_loc = -9999;
// récupérer le décalage de l'heure locale par rapport au temps universel
sprintf (commande, "date +%%z > /tmp/fich%04X", getpid ());
system (commande);
// ouvrir le fichier créé par la commande date
nomfic = commande + 11;
fcaract = fopen (nomfic, "r");
// si ouverture réussie
if (fcaract)
{
// lire le fuseau horaire sous forme numérique
fscanf (fcaract, "%d", &fh_loc);
// fermer et détruire le fichier
fclose (fcaract);
unlink (nomfic);
}
}
/* corriger le décalage horaire d'un mail */
void corrige_fh (int *jour, int *mois, int *an, int *heure, int *min, int decal)
{
// nombre de jours des différents mois de l'année
// le mois 0 correspond à décembre de l'année précédente
int dureemois [] = {31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// correction des minutes
*min = *min + (decal % 100);
// changement d'heure éventuel
if (*min < 0)
{
*min = *min + 60;
(*heure) --;
}
else if (*min >= 60)
{
*min = *min - 60;
(*heure) ++;
}
// correction de l'heure
*heure = *heure + (decal / 100);
// si retour au jour précédent
if (*heure < 0)
{
// corriger l'heure et le jour du mois
*heure = *heure + 24;
*jour = *jour - 1;
// si retour au mois précédent
if (*jour < 1)
{
// éviter un plantage si le mois n'a pas pu être décodé
if (*mois < 1)
return;
// corriger le mois et le jour
*mois = *mois - 1;
*jour = dureemois [*mois];
// si retour à l'année précédente
if (! *mois)
{
// corriger le mois et l'année
*mois = 12;
*an = *an - 1;
}
// sinon si mois de février d'une année bisextile
else if (*mois == 2 && (*an % 4) == 0)
// corriger le jour
*jour = *jour + 1;
}
}
// sinon si passage au jour suivant
else if (*heure > 23)
{
// corriger l'heure et le jour du mois
*heure = *heure - 24;
*jour = *jour + 1;
// si passage au mois suivant
if (*jour > dureemois [*mois])
{
// test suplémentaire par rapport au 29 février
if (*mois != 2 || (*an % 4) || *jour > 29)
{
// corriger le mois et le jour
*mois = *mois + 1;
*jour = 1;
// si changement d'année
if (*mois > 12)
{
// corriger le mois et l'année
*mois = 1;
*an = *an + 1;
}
}
}
}
}
/* vérifie si l'on peut insérer un élément de plus dans le tableau
fichmail, et redimmensionne ce tableau si nécessaire */
int ajoutlistepossible ()
{
descmail **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 < nbmaxfic)
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 (nbmaxfic % 3)
nouvtaille = nbmaxfic + (nbmaxfic / 2);
else
nouvtaille = nbmaxfic + (nbmaxfic / 3);
// allocation mémoire
nouvtableau = malloc (nouvtaille * sizeof (descmail *));
// vérification allocation
if (nouvtableau)
{
// copie du contenu de l'ancien tableau dans le nouveau
for (element = 0; element < nbmaxfic; element ++)
nouvtableau [element] = fichmail [element];
// destruction de l'ancien tableau
free (fichmail);
// que l'on remplace par le nouveau
fichmail = nouvtableau;
nbmaxfic = nouvtaille;
}
// retourne le résultat de la possibilité d'insertion d'éléments
return (nb_lignes + 1 < nbmaxfic);
}
/* tri des mails par date et heure croissantes */
void tridate ()
{
descmail *tampon; // adresse d'une description de mail pour permutation
int i; // indice dans la liste
// initialisation
i = 0;
while (i < nb_lignes - 1)
{
// on compare 2 éléments consécutifs
if (fichmail [i]->ordre > fichmail [i+1]-> ordre)
{
// interversion de 2 enregistrements
tampon = fichmail [i];
fichmail [i] = fichmail [i+1];
fichmail [i+1] = tampon;
// ce qui peut supposer de comparer un nom déplacé avec le précédent
if (i > 0)
i--;
else
i++;
}
else
i++; // on progresse dans la liste
}
}
/* tri des mails par date et heure décroissantes */
void tridate_inv ()
{
descmail *tampon; // adresse d'une description de mail pour permutation
int i; // indice dans la liste
// initialisation
i = 0;
while (i < nb_lignes - 1)
{
// on compare 2 éléments consécutifs
if (fichmail [i]->ordre < fichmail [i+1]-> ordre)
{
// interversion de 2 enregistrements
tampon = fichmail [i];
fichmail [i] = fichmail [i+1];
fichmail [i+1] = tampon;
// ce qui peut supposer de comparer un nom déplacé avec le précédent
if (i > 0)
i--;
else
i++;
}
else
i++; // on progresse dans la liste
}
}
/* affiche la liste des mails du répertoire et permet de
la parcourir même si elle tient sur plusieurs pages */
void navigation ()
{
int car; // caractère tapé au clavier
int erreurs; // nombre frappes caractères inconnus comme commandes
int choix; // choix de ce qu'on veut imprimer
char curdir [szchemin]; // répertoire courant
int dans_mailenv; // indicateur : on est dans le répertoire d'envoi
char *editeur; // éditeur utilisé pour modifier les mails
// chaine de caractères utilisée pour rebaptiser,
// mettre à la poubelle ou imprimer un fichier mail
char tampon [szchemin + 16];
// configurer la liaison clavier pour lecture directe avec timeout
mode_raw ();
// initialisation
lignecran = 1;
lignecour = 0;
erreurs = 0;
affpage ();
// récupérer le nom du répertoire courant
getcwd (curdir, szchemin);
// tester si on est dans le répertoire d'envoi des mails
if (getenv ("mailenv"))
dans_mailenv = (strcmp (curdir, getenv ("mailenv")) == 0);
else
dans_mailenv = 0;
do
{
// lire et traiter une touche du clavier
car = leccar ();
switch (car)
{ // déplacement dans la liste des mails
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"
// "v ou l ou entrée pour Voir (Lire) un mail"
// "r pour y Répondre, 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"
// "n ou Inser pour en créer un Nouveau"
// "o pour voir le fichier Original (non converti)"
// "i pour Identifier le fichier mail"
// "p pour imPrimer un mail (ou la liste complète)"
// "/ ou ? pour rechercher une chaine dans la liste"
// "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_VMAILSJ-1");
affiche_msg ("AIDE_VMAILSJ-2");
affiche_msg ("AIDE_VMAILFIC-2");
affiche_msg ("AIDE_VMAILFIC-3");
affiche_msg ("AIDE_VMAILFIC_SJ2");
affiche_msg ("AIDE_VMAILDIR_SJ");
affiche_msg ("AIDE_VMAILSJ-3");
affiche_msg ("AIDE_VMAILFIC_SJ1");
affiche_msg ("AIDE_VMAILSJ-4");
affiche_msg ("AIDE_VMAILSJ-5");
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;
case 'l' :
case 'v' : // on va visualiser un mail
case '\n' : execom ("vmailfic", fichmail [lignecour]->nomfic);
// pas de surbrillance pour les mails déjà visualisés
// (le test de la lettre n comme 10ème caractère
// n'est utile que pour assurer la compatibilité en
// lecture avec Micro$oft Internet mail)
if (fichmail [lignecour]->nomfic [8] == '.'
&& tolower(fichmail[lignecour]->nomfic[9])== 'n'
&& access (fichmail [lignecour]->nomfic, 0) < 0)
fichmail [lignecour]->nomfic [8] = '\0';
// si le mail a été supprimé
if (access (fichmail [lignecour]->nomfic, 0) < 0)
// on l'enlève de la liste à afficher
suprefmail ();
// on peut réafficher la page
case CTRL : affpage ();
erreurs = 0;
break;
// identifie le fichier mail (donne son nom)
case 'i' : effpage ();
// "\nFichier : %s/%s\n\n"
printf (message ("AFF_CHEMFICH"), curdir,
fichmail [lignecour]->nomfic);
// "Appuyer sur une touche pour continuer"
affiche_msg_nocr ("ATTENTE_CLAVIER");
leccar ();
affpage ();
erreurs = 0;
break;
// voir le fichier mail original sans conversion
case 'o' : execom ("less -r", fichmail [lignecour]->nomfic);
affpage ();
erreurs = 0;
break;
case INSERT : // création d'un nouveau mail
case 'n' : execom ("cremail", "");
// si on est dans le répertoire des mails à envoyer
if (dans_mailenv)
// réactualiser la liste des mails affichés
majliste_mails ();
affpage ();
erreurs = 0;
break;
// réponse à un mail
case 'r' : execom ("repmail", fichmail [lignecour]->nomfic);
// si on est dans le répertoire des mails à envoyer
if (dans_mailenv)
// réactualiser la liste des mails affichés
majliste_mails ();
affpage ();
erreurs = 0;
break;
// transfert d'un mail
case 't' : execom ("trsfmail", fichmail [lignecour]->nomfic);
// si on est dans le répertoire des mails à envoyer
if (dans_mailenv)
// réactualiser la liste des mails affichés
majliste_mails ();
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 (tampon, getenv ("mailpoub"));
else
*tampon = '\0';
// si c'est le répertoire courant
if (strcmp (curdir, tampon) == 0)
{
// supprimer le fichier mail
unlink (fichmail [lignecour]->nomfic);
// supprimer le mail de la liste à afficher
suprefmail ();
}
// sinon (cas général)
else if (*tampon)
{
// créer le répertoire poubelle si nécessaire
mkdir (tampon, 0755);
// et y mettre le fichier mail
strcat (tampon, "/");
strcat (tampon, fichmail [lignecour]->nomfic);
rename (fichmail [lignecour]->nomfic, tampon);
// supprimer le mail de la liste à afficher
suprefmail ();
}
// 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 (tampon, "../%s/%s",
ficdir ("DIR_ENTREE"), fichmail [lignecour]->nomfic);
rename (fichmail [lignecour]->nomfic, tampon);
// supprimer le mail de la liste à afficher
suprefmail ();
}
// 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
case 'm' : // si on est dans le répertoire des mails à envoyer
if (dans_mailenv)
{
// modifier le mail
editeur = getenv ("EDITOR");
if (editeur)
execom (editeur,fichmail[lignecour]->nomfic);
else
execom ("vi", fichmail [lignecour]->nomfic);
}
else
{
// sinon, message d'erreur
effpage ();
// "Seuls les mails en attente d'envoi peuvent être modifiés"
affiche_msg_nocr ("MAIL_NON_MODIF");
attendre (2);
}
affpage ();
erreurs = 0;
break;
// impression de la liste ou du mail courant
case 'p' : // choix de ce qu'on veut imprimer
putchar ('\n');
effpage ();
// "1) impression de la liste des mails"
// "2) impression du mail de la ligne courante"
// "Votre choix ? "
affiche_msg ("CHOIX_IMPR-1");
affiche_msg ("CHOIX_IMPR-2");
affiche_msg_nocr ("VOTRE_CHOIX");
choix = leccar () - '0';
// lancer la commande d'impression correspondante
if (choix == 1)
system ("sjfmails . | lp");
else if (choix == 2)
{
sprintf (tampon, "voirfmail %s | lp",
fichmail [lignecour]->nomfic);
system (tampon);
}
else
{
// "Choix erronné"
affiche_msg_nocr ("CHOIX_IMPR_ERR");
attendre (2);
}
affpage ();
erreurs = 0;
break;
// rajout de fichiers joints à un mail à envoyer
case 'j' : // si on est dans le répertoire des mails à envoyer
if (dans_mailenv)
// rajouter des fichiers joints
execom ("joindre", fichmail [lignecour]->nomfic);
else
{
// sinon, message d'erreur
effpage ();
// "Seuls les mails en attente d'envoi peuvent être modifiés"
affiche_msg_nocr ("MAIL_NON_MODIF");
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 && nb_lignes > 0);
// descendre en bas de page (pour sortir proprement du programme)
while (lignecran++ < lignepage && lignecour++ < nb_lignes)
putchar ('\n');
// retour à la configuration standard du clavier
mode_normal ();
}
/* affiche les caractéristiques d'un mail */
void affligne (int numlig)
{
// la largeur des différents champs affichés est calculée
// à partir de la largeur de la fenêtre d'affichage
// on utilise des variables statiques pour ne faire le
// calcul qu'en cas de nécessité
static int old_colonpage = -1; // pour détecter changement largeur de page
static int sz_nom, sz_sujet; // longueur des différentes zones affichées
static char car_nonlu = 0; // caractère indiquant les mails non lus
int tot; // pour calcul
int i, j; // simples compteurs
// si la largeur de la zone d'affichage a changé
if (colonpage != old_colonpage)
{
// répartir la largeur d'affichage entre les champs
sz_nom = (colonpage / 4) - 1;
if (sz_nom > szmax_nom)
sz_nom = szmax_nom;
sz_sujet = colonpage - sz_date - sz_nom - 2;
if (sz_sujet > szmax_sujet)
sz_sujet = szmax_sujet;
// cette mémorisation évitera de refaire le calcul à chaque ligne
old_colonpage = colonpage;
// si la variable d'environnement libremail_new est utilisée
if (getenv ("libremail_new"))
{
// mémoriser ce caractère
car_nonlu = *getenv ("libremail_new");
// corriger la largeur d'affichage des 2 premiers champs
sz_nom --;
sz_sujet --;
}
}
// afficher en surbrillance les mails non lus
if (fichmail [numlig]->nomfic [8] == '.' &&
tolower (fichmail [numlig]->nomfic [9]) == 'n')
{
clair ();
// si nécessaire avec le caractère d'identification des mails non lus
if (car_nonlu)
{
putchar (car_nonlu);
putchar (' ');
}
}
else if (car_nonlu)
{
putchar (' ');
putchar (' ');
}
// si affichage avec un jeu de caractères ISO-8859
if (! util_utf8 ())
{
// affichage du nom de l'expéditeur
for (i = 0; i < sz_nom; i++)
putchar (fichmail [numlig]->nom [i]);
putchar (' ');
// affichage du sujet
for (i = 0; i < sz_sujet; i++)
{
// si présence d'une tabulation
if (fichmail [numlig]->sujet [i] == '\t')
// afficher un blanc pour une meilleure mise en page
putchar (' ');
else
// sinon affichage du caractère
putchar (fichmail [numlig]->sujet [i]);
}
}
// sinon (affichage avec le jeu de caractères UTF-8)
else
{
// initialisation
j = 0;
// affichage du nom de l'expéditeur
for (i = 0; i < sz_nom + j && i < szmax_nom; i++)
{
putchar (fichmail [numlig]->nom [i]);
// mais certains caractères ne seront pas comptés
/* il serait plus logique de ne pas compter les caractères
entre 0x80 et 0xBF, mais certains caractères UTF8 codés
sur 3 (ou 4 ?) octets s'affichent en largeur double, ce
qui décale les autres colonnes et génère un saut de ligne
intempestif après la date */
if ((fichmail [numlig]->nom [i] & 0xC0) == 0xC0)
j++;
}
// complété par des blancs
while (i++ <= sz_nom + j)
putchar (' ');
// réinitialisation
j = 0;
// affichage du sujet
for (i = 0; i < sz_sujet + j && i < szmax_sujet; i++)
{
// si présence d'une tabulation
if (fichmail [numlig]->sujet [i] == '\t')
// afficher un blanc pour une meilleure mise en page
putchar (' ');
// sinon
else
{
// affichage du caractère
putchar (fichmail [numlig]->sujet [i]);
// mais certains caractères ne seront pas comptés
// même remarque que pour le nom
if ((fichmail [numlig]->sujet [i] & 0xC0) == 0xC0)
j++;
}
}
// complété par des blancs
while (i++ < sz_sujet + j)
putchar (' ');
}
putchar (' ');
// affichage de la date et de l'heure (sans fuseau horaire)
printf ("%s", fichmail [numlig]->date);
// retour à l'affichage normal
lumnor ();
}
/* supprime de la liste un mail que l'on vient de détruire */
void suprefmail ()
{
int i; // compteur
// on l'enlève de la liste à afficher
for (i = lignecour + 1; i < nb_lignes; i++)
fichmail [i - 1] = fichmail [i];
nb_lignes --;
// on reste positionné sur un mail voisin
if (lignecour == nb_lignes)
monte (1);
}
/* réactualiser l'affichage de la liste des mails
Cette fonction ne sert que dans le cas particulier d'un
mail rajouté alors qu'on est dans le répertoire d'envoi.
La méthode de mise à jour n'a pas besoin d'être optimisée.
On se contente d'effacer l'ancienne liste et de la recréer. */
void majliste_mails ()
{
// libérer la mémoire occupée par l'ancienne liste
while (nb_lignes > 0)
free (fichmail [--nb_lignes]);
// récupérer la liste des fichiers mail à envoyer
lister_mails ();
// et les trier par date
tridate ();
}
/* recherche d'une chaine de caractères dans la liste des mails */
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é de la liste
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é de la liste des mails
if (i != limite)
// descendre ou remonter d'une ligne selon le sens de recherche
i = i + sens;
else
// sinon aller à l'autre extrémité de la liste
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 toute la liste explorée
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 y a lieu
if (i < lignecour)
monte (lignecour - i);
else if (i > lignecour)
descend (i - lignecour);
// réafficher la ligne contenant la chaine dans une autre couleur
putseq ("33m");
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
i = 0;
// le nom de l'expéditeur/destinataire, le sujet et la
// date sont regroupés sur une même chaine de caractères
maligne = (char*) &fichmail [numlig]->nom;
// 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;
}