/*
    Fichier modepage.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


    Bibliothèque de fonctions permettant de gérer l'affichage en mode page

    Certaines de ces fonctions sont des macros de modepage.h

    On peut remarquer que les fonctions de déplacement dans le texte
    et d'affichage d'une page ne manipulent que des indicateurs
    de position dans le texte.

    La fonction affligne reste dans le fichier source principal de
    chaque application, ce qui permet d'avoir un affichage adapté
    aux données à traiter.
*/


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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "messages.h"
#include "modepage.h"
#include "carspe.h"
#include "genentete.h"



/* teste si l'ordinateur a été démarré avec init ou avec systemd */
int util_systemd ()
{
// si libremail compilé seulement pour init ou pour systemd
#ifdef systemd
    // retourner directement le résultat
    return (systemd > 0);
// sinon la détection du système de démarrage est nécessaire
#else
    static int sysinit = 0; // mémorise si le système de démarrage a été détecté
    static int resultat;    // résultat du test
    FILE   *descfic; // descripteur du fichier indiquant le système de démarrage
    int    car;      // caractère du fichier indiquant le système de démarrage

    // si le système de démarrage n'a pas encore été détecté
    if (! sysinit)
    {
        // lancer la commande de recherche du processus d'initialisation
        system ("ps 1 > /tmp/sys_init");

        // ouvrir en lecture le fichier contenant l'information
        descfic = fopen ("/tmp/sys_init", "r");

        // si l'ouverture s'est bien passée
        if (descfic)
        {
            // lire les caractères jusqu'à trouver un :
            do
                car = getc (descfic);
            while (car != ':' && car != EOF);

            // continuer jusqu'à trouver une voyelle minuscule
            while (car < 'a' && car != EOF)
                car = getc (descfic);

            // terminé avec la lecture du fichier
            fclose (descfic);
        }
        // sinon message d'erreur
        else
        {
            affiche_err ("PB_ACCES_TMP");
            sleep (2);
        }

        // test simplifié pour savoir si démarré avec init
        // dans tous les autres cas, on considère que démarré avec systemd
        resultat = (car != 'i');

        // on a détecté le système de démarrage
        sysinit = 1;

        // on peut supprimer le fichier qui a servi à la détection
        unlink ("/tmp/sys_init");
    }

    // retourner le résultat du test
    return (resultat);
#endif
}



/* lecture du clavier en mode raw (caractère par caractère sans écho) */
void mode_raw ()
{
// si libremail compilé seulement pour init ou pour systemd
#ifdef systemd
    // exécuter la commande stty la mieux adaptée
    #if systemd > 0
        system (cmd_mode_raw_systemd);
    #else
        system (cmd_mode_raw_init);
    #endif
// sinon
#else
    // selon le système de démarrage détecté, exécuter la bonne commande stty
    if (util_systemd ())
        system (cmd_mode_raw_systemd);
    else
        system (cmd_mode_raw_init);
#endif
}



/* lecture du clavier en mode normal (ligne par ligne avec écho) */
void mode_normal ()
{
    system (cmd_mode_normal);
}



/* affichage en surbrillance */
void clair ()
{
    static char sequence [8] = ""; // séquence de changement de couleur
    char *valenv;   // contenu de la variable d'environnement libremail_bright
    int  valcoul;   // couleur pour la séquence d'échappement


    // si aucune séquence d'échappement déja mémorisée
    if (! *sequence)
    {
        // tenter de récupérer la variable d'environnement libremail_bright
        valenv = getenv ("libremail_bright");

        // si la variable d'environnement existe
        if (valenv)
        {
            // convertir le contenu de la variable en un numéro de couleur
            valcoul = (valenv [0] & 1) + ((valenv [1] & 1) * 2)
                                       + ((valenv [2] & 1) * 4);

            // et générer la séquence d'échappement avec couleur en surbrillance
            sprintf (sequence, "%c[%d;1m", ESC, valcoul + 30);
        }
        else
            // sinon générer la séquence d'échappement pour passer surbrillance
            sprintf (sequence, "%c[1m", ESC);
    }

    // envoyer la séquence d'échappement
    printf ("%s", sequence);
}



/* affichage en bleu foncé par défaut */
void sombre ()
{
    static char sequence [6] = ""; // séquence de changement de couleur
    char *valenv;   // contenu de la variable d'environnement libremail_dark
    int  valcoul;   // couleur pour la séquence d'échappement


    // si aucune séquence d'échappement déja mémorisée
    if (! *sequence)
    {
        // tenter de récupérer la variable d'environnement libremail_dark
        valenv = getenv ("libremail_dark");

        // si variable d'environnement utilisée
        if (valenv)
        {
            // convertir le contenu de la variable en un numéro de couleur
            valcoul = (valenv [0] & 1) + ((valenv [1] & 1) * 2)
                                       + ((valenv [2] & 1) * 4);

            // et générer la séquence d'échappement
            sprintf (sequence, "%c[%dm", ESC, valcoul + 30);
        }
        else
            // sinon générer la séquence d'échappement pour passer en bleu foncé
            sprintf (sequence, "%c[34m", ESC);
    }

    // envoyer la séquence d'échappement
    printf ("%s", sequence);
}



/* réaffiche la page d'écran courante en tenant
   compte du changement de taille de la fenêtre */


void affpage ()
{
    int i, j;


    // récupérer la taille de la fenêtre (qui peut avoir changé)
    lig_col ();

    // se positionner en haut d'écran et l'effacer
    debpage ();
    effpage ();

    // initialisation
    i = 1;
    j = lignecour - lignecran + 1;

    // réafficher toutes les lignes de la page
    while (i++ < lignepage && j < nb_lignes)
    {
        affligne (j++);
        putchar ('\n');
    }

    // remonter à la ligne sur laquelle on était
    while (--i > lignecran)
        montecurs ();
}



/* remonte de n lignes (max) dans le texte affiché */

void monte (int n)
{
    // limiter le déplacement pour ne pas sortir du texte
    if (n > lignecour)
        n = lignecour;

    // tant qu'on n'est pas en haut de page et déplacement non terminé
    while (n > 0 && lignecran > 1)
    {
        // remonter d'une ligne
        montecurs ();

        // et mettre à jour les variables
        lignecran --;
        lignecour --;
        n--;
    }

    // si déplacement de plus d'une page
    if (n > lignepage)
    {
        // on n'affichera pas les lignes intermédiaires
        lignecour = lignecour + lignepage - n;
        n = lignepage;
    }

    // tant que déplacement non terminé
    while (n-- > 0)
    {
        // descendre le texte affiché d'une ligne
        insligne ();

        // et afficher la ligne à rajouter en haut d'écran
        affligne (--lignecour);
        putchar ('\r');
    }
}



/* descend de n lignes (max) dans le texte affiché */

void descend (int n)
{
    // limiter le déplacement pour ne pas sortir du texte
    if (n > nb_lignes - lignecour - 1)
        n = nb_lignes - lignecour - 1;

    // tant qu'on n'est pas en bas de page et déplacement non terminé
    while (n > 0 && lignecran < lignepage - 1)
    {
        // descendre d'une ligne
        putchar ('\n');

        // et mettre à jour les variables
        lignecran ++;
        lignecour ++;
        n--;
    }

    // si déplacement de plus d'une page
    if (n > lignepage)
    {
        // on n'affichera pas les lignes intermédiaires
        lignecour = lignecour + n - lignepage;
        n = lignepage;
    }

    // tant que déplacement non terminé
    while (n-- > 0)
    {
        // afficher la ligne à rajouter en bas de page
        // et remonter tout le texte d'une ligne
        putchar ('\n');
        affligne (++lignecour);
        putchar ('\n');

        // remonter sur la ligne insérée
        montecurs ();
    }
}



/* récupère le nombre de lignes et de colonnes d'affichage */

void lig_col ()
{
    char commande [28];
    FILE *fcaract;


    // récupérer les caractéristiques du terminal
    sprintf (commande, "stty size > /tmp/fich%X", getpid ());
    system (commande);

    // ouvrir le fichier créé par la commande  stty
    fcaract = fopen (commande + 12, "r");

    // si ouverture réussie
    if (fcaract)
    {
        // lire le nombre de lignes et de colonnes
        fscanf (fcaract, "%d%d", &lignepage, &colonpage);

        // fermer et détruire le fichier
        fclose (fcaract);
        unlink (commande + 12);
    }
    // cas très particulier : problème de droits d'accès à /tmp
    else
    {
        // message d'avertissement
     // "Droit d'accès insuffisants à /tmp, le fonctionnement peut être dégradé"
        affiche_msg_nocr ("PB_ACCES_TMP");
        attendre (3);

        // on prend les valeurs par défaut
        lignepage = defaut_ligne;
        colonpage = defaut_colon;
    }

    // corriger avec des valeurs par défaut si l'on a récupéré des 0
    // (par exemple depuis une fenêtre telnet)
    if (! lignepage)
        lignepage = defaut_ligne;

    if (! colonpage)
        colonpage = defaut_colon;
}



/* lit un caractère au clavier et le traite si caractère spécial */

int leccar ()
{
    int car, carsuiv;

    // attendre qu'un caractère soit tapé
    do
        car = getchar ();
    while (car == -1);

    // si caractère ordinaire, on le retourne
    if (car != ESC)
        return (car);

    // lire le caractère qui suit le ESC
    car = getchar ();

    // traitement du caractère ESC et des séquences d'échappement
    if (car == '[' || car == 'O')
    {
        car = getchar ();

        if ('1' <= car && car <= '9')
        {
            // séquence  ESC[n~  où  n  est une valeur numérique
            carsuiv = getchar ();
            car = car & 0x0F;

            // récupérer la valeur numérique
            while ('0' <= carsuiv && carsuiv <= '9')
            {
                car = (car * 10) + (carsuiv & 0x0F);
                carsuiv = getchar ();
            }

            if (carsuiv == '~')
                return (CARSPE | car);
            else
                // "Caractère spécial non traité"
                affiche_msg ("CARSPE_INVALIDE");
        }
        else if (car == '[')
        {
            // séquence  ESC[[n  (certaines touches de fonction)
            car = getchar ();
            return (CARSPE + 0x0F + car);
        }
        else
            // séquence  ESC[lettre
            return (CARSPE | car);
    }

    // caractère ESC classique
    else
    {
        // si après le ESC on a eu un autre caractère de suite
        // le remettre dans le buffer
        if (car != -1)
            ungetc (car, stdin);

        // retour du caractère ESCape
        return (ESC);
    }
}



/* exécute une commande, puis repasse en mode page */

void execom (char *outil, char *fichier)
{
    char commande [130];    // chaine contenant la commande à exécuter
    unsigned long heuredeb; // pour détecter problème exécution commandes


    // effacer le bas de page
    effpage ();
    fflush (stdout);

    // fabriquer la commande à éxécuter
    sprintf (commande, "%s %s", outil, fichier);

    // récupérer l'heure courante
    heuredeb = time (0);

    // lancer la commande
    system (commande);

    // si la commande a rendu la main trop rapidement
    // et que c'est une commande interactive
    if ((time (0) <= heuredeb + 1) && (strcmp (outil, "lp") != 0)
                                   && (strcmp (outil, "recuppj") != 0))
    {
        // attendre lecture du message d'erreur
        montecurs ();
        attendre (2);
    }

    // reconfigurer la voie que commande a remit en mode normal en se terminant
    mode_raw ();
}



/* attente en seconde pour l'affichage d'un message qui sera ensuite effacé */

void attendre (int duree)
{
    char *varenv;   // contenu variable d'environnement libremail_errtimeout


    // si la variable d'environnement libremail_errtimeout est utilisée
    varenv = getenv ("libremail_errtimeout");

    if (varenv)
    {
        // récupérer la durée de l'attente qu'elle contient
        duree = atoi (varenv);

        // positionner le curseur en début de ligne
        putchar ('\r');
    }

    // si attente pour une durée limitée
    if (duree)
    {
        // forcer l'affichage du texte en attente
        fflush (stdout);

        // attendre pour la durée demandée
        sleep (duree);
    }
    // sinon attendre que l'utilisateur tape un caractère
    else
        leccar ();
}