/*
Fichier messages.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-en.txt , licence-es.txt , licence-pt.txt ,
licence-eo.txt , licence-eo-utf.txt
Droits d'utilisation également sur la page web :
http://cyloop.tuxfamily.org/voir.php?page=droits
Bibliothèque de fonctions utilisant des fichiers de données
pour dialoguer avec l'utilisateur dans sa langue.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "messages.h"
// #define DEBUG
#define szlig 120 // longueur maximale des lignes des fichiers de données
/* variables globales au source */
char *ncom; // nom de (ou chemin d'accès à) la commande en cours d'exécution
char repert_com [szchemin] = ""; // répertoire où est implantée cette commande
/* mémorise le nom ou le chemin d'accès de la commande en cours d'exécution */
void memcom (char *arg0)
{
ncom = arg0;
// si le chemin d'accès à la commande est un chemin relatif
if (*ncom != '/')
// chercher le chemin d'accès absolu avant un éventuel
// chdir dans le programme, on évitera ainsi une erreur
// d'ouverture ultérieure du fichier des messages
dircom ();
}
/* retourne le nom ou le chemin d'accès de la commande en cours d'exécution */
char *nomcom ()
{
return (ncom);
}
/* retourne le nom du répertoire où est la commande en cours d'exécution */
char *dircom ()
{
int derslash; // position du dernier slash rencontré dans ncom
char *path; // variable d'environnement PATH
char chemcom [szchemin]; // chemin d'accès complet à la commande
int i, j; // indices pour parcourir et recopier ncom et path
// si le nom de ce répertoire n'est pas encore mémorisé
if (! *repert_com)
{
// initialisation
derslash = 0;
i = 0;
j = 0;
// recopier ncom dans repert_com et chercher la position du dernier /
while (ncom [i])
{
// on saute les ./ trouvés pendant la recopie
while (ncom [i] == '.' && ncom [i+1] == '/')
i = i + 2;
// recopier un nom de répertoire (ou le nom du fichier de commande)
while (ncom [i] != '/' && ncom [i])
repert_com [j++] = ncom [i++];
// si on arrive sur un /
if (ncom [i] == '/')
{
// mémoriser la position du / dans repert_com
derslash = j;
// et le recopier
repert_com [j++] = ncom [i++];
}
}
// on ne conserve dans repert_com que le nom
// du répertoire où est implantée la commande
repert_com [derslash] = '\0';
// si aucun nom de répertoire n'a été trouvé
if (! *repert_com)
{
// on va tester les chemins de la variable d'environnement PATH
path = getenv ("PATH");
i = 0;
do
{
j = 0;
// recopier l'un de ces chemins
while (path [i] != ':' && path [i])
{
repert_com [j++] = path [i++];
}
// terminer la chaine de caractères
repert_com [j] = '\0';
// générer le chemin d'accès à la commande
sprintf (chemcom, "%s/%s", repert_com, ncom);
}
// terminé si ce chemin d'accès est le bon
while (access (chemcom, X_OK) != 0 && path [i++]);
}
// si le chemin d'accès trouvé est un chemin d'accès relatif
// on va le transformer en chemin d'accès absolu.
// la transformation optimisée est complexe, mais nécessaire
// pour le cas ou le programme fait un chdir avant d'ouvrir
// les fichier des messages ou un autre fichier de données
if (*repert_com != '/')
{
// récupérer le nom du répertoire courant
strcpy (chemcom, repert_com);
getcwd (repert_com, szchemin);
// on va y accoler le chemin d'accès relatif au
// répertoire trouvé en traitant les ../
// initialisation
i = 0;
j = strlen (repert_com);
// on rajoute / au répertoire courant
repert_com [j++] = '/';
repert_com [j] = '\0';
while (chemcom [i])
{
// si on a le répertoire ..
if (chemcom [i] == '.' && chemcom [i+1] == '.'
&& (chemcom [i+2] == '/' || chemcom [i+2] == '\0'))
{
// se positionner après le .. terminal
i = i + 2;
// ou après le ../
if (chemcom [i] == '/')
i++;
// remonter d'un répertoire dans repert_com
do
j--;
while (j > 1 && repert_com [j-1] != '/');
// on ne supprime pas le premier /
if (j == 0)
j++;
}
// sinon (cas général)
else
{
// recopier un nom de répertoire
while (chemcom [i] != '/' && chemcom [i] && j < szchemin)
repert_com [j++] = chemcom [i++];
repert_com [j++] = chemcom [i++];
}
}
}
j = strlen (repert_com);
// enlever les /. terminaux
while (j > 1 && repert_com [j-1] == '.' && repert_com [j-2] == '/')
j = j - 2;
repert_com [j] = '\0';
}
// retourner le nom du répertoire
return (repert_com);
}
/* retourne la langue de l'utilisateur dans une chaine de 2 caractères */
char *getlang ()
{
char *langue; // contenu de la variable d'environnement $LANG
static char retour [3]; // chaine de caractères retournée
// récupérer la variable d'environnement $LANG
langue = (char *) getenv ("LANG");
// si cette variable d'environnement a été déclarée
if (langue)
{
// on retournera les 2 premiers caractères de cette variable
retour [0] = langue [0];
retour [1] = langue [1];
retour [2] = '\0';
}
// sinon
else
// on retournera une chaine vide
*retour = '\0';
// retourner le résultat
return (retour);
}
/* vérifie si le jeu de caractères UTF-8 est utilisé */
int util_utf8 ()
{
static int testacces; // mémorise si on a déjà appelé cette fonction
static int restest; // résultat de la vérification
char *langue; // caractère de la variable d'environnement $LANG
int lg_langue; // longueur de la chaine $LANG
// si on appelle cette fonction pour la première fois
if (! testacces)
{
// récupérer la variable d'environnement $LANG
langue = (char *) getenv ("LANG");
// initialisation
restest = 0;
// si cette variable d'environnement est initialisée et se
// termine par UTF-8, ou UTF8 en majuscules ou minuscules
// alors on utilise ce jeu de caractères
if (langue) // && strlen (langue) > 5)
{
// compter le nombre de caractères de $LANG
lg_langue = 0;
while (*langue)
{
langue ++;
lg_langue ++;
}
// et se positionner sur le dernier
langue --;
// si $LANG assez long et se termine par '8'
if (lg_langue > 5 && *langue == '8')
{
// sauter le '-' précédent éventuel
if (*(--langue) == '-')
langue --;
// vérifier et mémoriser si chaine "utf" ou "UTF"
if ((*(langue-2) | 0x20) == 'u' && (*(langue-1) | 0x20) == 't'
&& (*langue | 0x20) == 'f')
restest = 1;
}
}
// on ne repassera plus dans ce bloc d'instructions
testacces = 1;
}
// retourner le résultat du test
return restest;
}
/* retourne le chemin d'accès à un fichier de données
après avoir choisi le fichier le mieux adapté compte tenu :
- de la langue de l'utilisateur
- de l'indicateur de prise en compte du jeu de caractères utf-8
- de la présence ou non du fichier deflang-cyloop
*/
char *chemfichlang (char *nomfic, int utf8)
{
static char chemfic [szchemin]; // chemin d'accès au fichier de données
char ficdeflang [szchemin]; // chemin d'accès au fichier "deflang-..."
char *pos_suffixe; // posit 1er caract du suffixe dans chemfic
char langue [3]; // langue de l'utilisateur
int sans_suff, avec_suff; // indicateur de présence des fichiers
// générer le chemin d'accès au fichier sans suffixe
// si cyloop est implanté localement
if (strcmp (dircom (), "/usr/bin") != 0)
// fichiers de données dans le même répertoire que les commandes
sprintf (chemfic, "%s/%s", dircom (), nomfic);
// sinon, les commandes de cyloop sont dans /usr/bin
else
// et les fichiers de données dans /usr/share/cyloop
sprintf (chemfic, "/usr/share/cyloop/%s", nomfic);
// récupérer la langue de l'utilisateur
strcpy (langue, getlang ());
// si une langue a été définie
if (*langue)
{
// mémoriser la possibilité d'accès en lecture du fichier sans suffixe
sans_suff = (access (chemfic, R_OK) == 0);
// chercher la position où rajouter un suffixe au fichier
pos_suffixe = chemfic + strlen (chemfic);
// si jeu de caractères UTF-8 utilisé et à prendre en compte
if (utf8)
{
// rajouter au chemin d'accès la langue suivie de "-utf"
sprintf (pos_suffixe, ".%s-utf", langue);
// si ce fichier n'existe pas ou n'est pas accessible en lecture
if (access (chemfic, R_OK) < 0)
// supprimer le "-utf" à la fin du chemin d'accès
pos_suffixe [3] = '\0';
}
// sinon
else
// rajouter au chemin d'accès l'indication de la langue
sprintf (pos_suffixe, ".%s", langue);
// mémoriser la possibilité d'accès en lecture du fichier avec suffixe
avec_suff = (access (chemfic, R_OK) == 0);
// si les fichiers avec et sans suffixe sont tous
// deux accessibles en lecture, ou si aucun ne l'est
if (sans_suff == avec_suff)
{
// créer le chemin d'accès au fichier "deflang-cyloop"
sprintf (ficdeflang, "%s/deflang-cyloop", dircom ());
// si ce fichier existe
if (access (ficdeflang, F_OK) == 0)
// revenir au nom de fichier sans suffixe
*pos_suffixe = '\0';
// sinon, on utilisera le nom de fichier avec suffixe
}
// sinon si le fichier sans suffixe est le seul accessible en lecture
else if (sans_suff && (! avec_suff))
// revenir au nom de fichier sans suffixe
*pos_suffixe = '\0';
// sinon le fichier avec suffixe est le seul accessible en lecture
// son nom est déjà mémorisé dans chemfic
}
// retourner le chemin d'accès au fichier
return chemfic;
}
/* sortie du programme sur impossibilité d'afficher un message */
void erreur_mess (int numerr, char *info)
{
// messages mémorisés en dur dans le logiciel dans des tableaux
// statiques pour éviter allocation mémoire et initialisation
static char *fichmanquant [] =
{
"fr Fichier %s manquant ou protégé en lecture",
// "de In Lektüre geschützte oder fehlende Kartei %s",
"en File %s missing or read protected",
"eo Dosiero %s mankante au protektita en lego",
"es Fichero %s que falta o protegido en lectura",
// "it Archivio %s che manca o protetto in lettura",
// "nl Bestand dat %s of dat in lezing wordt beschermd gebrek heeft aan",
"pt Ficheiro %s que falta ou protegido em leitura",
NULL // pour terminer la liste
};
static char *mnemomanquant [] =
{
"fr Mnémonique MNEMO_ABSENT manquant dans le fichier %s",
// "de MNEMO_ABSENT Mnemonik, die in der Kartei %s fehlt",
"en Mnemonic MNEMO_ABSENT missing in the file %s",
"eo Mnemoniko MNEMO_ABSENT mankante en la dosiero %s",
"es Mnemotecnia MNEMO_ABSENT que falta en el fichero %s",
// "it Mnémonique MNEMO_ABSENT che manca nell'archivio %s",
// "nl Mnémonique MNEMO_ABSENT die in het bestand %s gebrek heeft aan",
"pt Mnémonique MNEMO_ABSENT que falta no ficheiro %s",
NULL // pour terminer la liste
};
char **listemessages; // pour sélectionner une liste de messages
char langue [3]; // langue de l'utilisateur
char mess_err [szlig]; // contenu du message récupéré
int i; // compteur dans la liste des messages
// sélectionner la bonne liste de messages
if (numerr == 1)
listemessages = fichmanquant;
else
listemessages = mnemomanquant;
// récupérer la langue de l'utilisateur
strcpy (langue, getlang ());
// rechercher le message dans la bonne langue
i = 0;
while (listemessages [i] && (listemessages [i][0] != langue [0]
|| listemessages [i][1] != langue [1]))
i++;
// si le message a été trouvé
if (listemessages [i])
{
// le mettre en forme
sprintf (mess_err, listemessages [i] + 3, info);
// si on utilise l'encodage UTF-8
if (util_utf8 ())
// convertir les caractères accentués du message
conv_iso_utf8 (mess_err);
// afficher le message d'erreur
fprintf (stderr, "\n%s\n", mess_err);
}
// sinon
else
{
// on va afficher le message dans toutes les langues disponibles
i = 0;
while (listemessages [i])
{
// récupérer un message et le mettre en forme
sprintf (mess_err, listemessages [i++] + 3, info);
// si on utilise l'encodage UTF-8 convertir les caractères accentués
if (util_utf8 ())
conv_iso_utf8 (mess_err);
// afficher le message d'erreur
fprintf (stderr, "\n%s\n", mess_err);
}
}
// sortir du programme
exit (-1);
}
/* retourne le message associé au mnémonique passé en paramètre */
char *message (char *mnemonique)
{
static int testacces = 0; // mémorise si on a déjà appelé cette fonction
static char ficmes [szchemin]; // chemin d'accès au fichier mess-cyloop
static FILE *desc_ficmes; // descripteur du fichier mess-cyloop
static char mess_lu [szlig]; // message récupéré dans le fichier
char ligne [szlig]; // contenu d'une ligne du fichier
int retourdeb; // mémorise si retour en début de fichier
int i, j; // indices pour traitements chaines caractères
#ifdef DEBUG
printf ("Appel de message (%s)\n", mnemonique);
sleep (1);
#endif
// si on appelle cette fonction pour la première fois
if (! testacces)
{
// générer le chemin d'accès au fichier mess-cyloop
// en tenant compte de la langue et du jeu de caractères
strcpy (ficmes, chemfichlang ("mess-cyloop", util_utf8 ()));
// tenter de l'ouvrir en lecture
desc_ficmes = fopen (ficmes, "r");
// si le fichier mess-cyloop n'a pas pu être ouvert
if (!desc_ficmes)
// message d'erreur fatale
// "Fichier %s manquant ou protégé en lecture"
erreur_mess (1, ficmes);
// on ne repassera plus dans ce bloc d'instructions
testacces = 1;
// on est au début du fichier
retourdeb = 1;
}
// sinon
else
// on va commencer par tester la chaine qui suit la dernière trouvée
retourdeb = 0;
// le fichier ficmes-cyloop a pu être ouvert
// on va l'explorer pour trouver le mnémonique
// premier essai à partir de la ligne qui suit celle du message précédent
fgets (ligne, szlig, desc_ficmes);
do
{
// comparaison du mnémonique de la ligne
// lue avec celui passé en paramètre
i = 0;
while (ligne [i] == mnemonique [i])
i++;
// si mnémonique trouvé
if (mnemonique [i] == '\0' && (ligne [i] == ' ' || ligne [i] == '\t'))
{
// se positionner sur le message correspondant
while (ligne [i] != '"' && ligne [i] != '\n' && ligne [i])
i++;
j = 0;
i++;
// on va recopier le message
while (ligne [i] != '"' && ligne [i] != '\n' && ligne [i])
{
// si \ dans le message, on convertit le caractère spécial
if (ligne [i] == '\\')
{
switch (ligne [++i])
{
case 'n' : mess_lu [j++] = '\n';
break;
case 'r' : mess_lu [j++] = '\r';
break;
case 't' : mess_lu [j++] = '\t';
break;
case '\\': mess_lu [j++] = '\\';
break;
default : mess_lu [j++] = '\\';
mess_lu [j++] = ligne [i];
}
// et on passe au caractère suivant
i++;
}
// sinon, simple copie du caractère
else
mess_lu [j++] = ligne [i++];
}
// terminer le message
mess_lu [j] = '\0';
// si on utilise l'encodage UTF-8
if (util_utf8 ())
// convertir les caractères accentués du message
conv_iso_utf8 (mess_lu);
// retourner le message trouvé
return (mess_lu);
}
// si on vient de tester sans succès la chaine du fichier
// qui suit celle du message demandé juste auparavant
if (! retourdeb)
{
// reprendre la recherche en début de fichier
rewind (desc_ficmes);
// on explorera le fichier jusqu'à la fin si nécessaire
retourdeb = 1;
}
}
// continuer la recherche ligne par ligne jusqu'en fin de fichier
while (fgets (ligne, szlig, desc_ficmes));
// mnémonique non trouvé dans le fichier mess-cyloop
if (strcmp ("MNEMO_ABSENT", mnemonique) != 0)
// sortie sur message d'erreur
err_fatale_arg ("MNEMO_ABSENT", mnemonique);
else
// "Mnémonique MNEMO_ABSENT manquant dans le fichier des messages"
erreur_mess (2, ficmes);
}
/* convertit les caractères spéciaux du message
passé en paramètre dans le format UTF-8 */
void conv_iso_utf8 (char *mess)
{
int i, j; // compteurs de caractères
// initialisation
i = 0;
j = 0;
// compter les caractères spéciaux
while (mess [i])
{
if (mess [i++] & 0x80)
j++;
}
// terminé si pas de caractères spéciaux
if (j == 0)
return;
// décaler la chaine à convertir de j caractères
j = j + i;
while (i >= 0)
mess [j--] = mess [i--];
// remettre les caractères à la bonne place
// en convertissant les caractères spéciaux
do
{
// si caractère spécial
if (mess [++j] & 0x80)
{
// on le remplace par les 2 caractères de l'encodage UTF-8
if (mess [j] & 0x40)
{
mess [++i] = 'Ã';
mess [++i] = mess [j] ^ 0x40;
}
else
{
mess [++i] = 'Â';
mess [++i] = mess [j];
}
}
// sinon
else
// recopier le caractère sans conversion
mess [++i] = mess [j];
}
while (mess [j]);
}
/* affichage d'un texte puis rester en fin de ligne */
void affiche_msg (char *mnemonique)
{
printf (message (mnemonique));
}
/* affichage d'une ligne de texte puis passage à la ligne */
void affiche_ligne (char *mnemonique)
{
puts (message (mnemonique));
}
/* affichage d'un message d'erreur simple */
void affiche_err (char *mnemonique)
{
fputs (message (mnemonique), stderr);
fputc ('\n', stderr);
}
/* affichage d'un message d'erreur avec argument */
void aff_err_arg (char *mnemonique, char *argument)
{
fprintf (stderr, message (mnemonique), argument);
fputc ('\n', stderr);
}
/* affichage d'un message d'erreur avec argument numérique */
void aff_err_argnum (char *mnemonique, int argument)
{
fprintf (stderr, message (mnemonique), argument);
fputc ('\n', stderr);
}
/* sortie du programme sur erreur fatale (message simple) */
void err_fatale (char *mnemonique)
{
// écrire le message d'erreur
affiche_err (mnemonique);
// sortir du programme
exit (-1);
}
/* sortie du programme sur erreur fatale (message avec argument) */
void err_fatale_arg (char *mnemonique, char *argument)
{
// écrire le message d'erreur
aff_err_arg (mnemonique, argument);
// sortir du programme
exit (-1);
}
/* sortie du programme en rappellant la syntaxe d'une commande */
void psyntaxe (char *mnemonique)
{
err_fatale_arg (mnemonique, nomcom ());
}
/* sortie du programme en rappellant la syntaxe d'une commande sur 2 lignes */
void psyntaxe2 (char *mnemonique1, char *mnemonique2)
{
aff_err_arg (mnemonique1, nomcom ());
err_fatale (mnemonique2);
}