/*
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;
}