/* xlatex.c
*
* xlatex est une application X facilitant l'utilisation des produits de
* composition de texte (tels que TeX et LaTeX) librement distribuable.
* Mario Dragone en a un Copyright (1990). Vous pouvez modifier et
* utiliser ce programme, a condition de lui envoyer les modifications. Il
* peut etre inclus dans toute distribution, commerciale ou non, a
* condition que cette notice et la banniere definie ci-dessous ne soient
* pas modifies (sauf pour le numero de version) et que cette banniere
* soit affichee a l'appel du programme. Toute contribution, correction
* de bogue et commentaires doivent etre envoyes a :
*
* Mario Dragone,    IUT Vannes
*                   rue Montaigne
*                   BP 1104
*                   56014 Vannes -  France
*                   Tel: 99.46.31.06
*
* e-mail: [email protected]
*
* [email protected] 11/06/93 modifie pour HP-UX
* [email protected] 08/09/93 modifie pour IRIX
*                                   message fin dvips
* [email protected]         23/09/93 modifie pour Solaris 2
* [email protected] 11/08/94 ressource et widget => pilote
* [email protected] 25/08/94 #define ALPR et ALPQ
* [email protected] 08/09/94 waitpid sur tous les fils en Solaris 2
*                                   sinon la terminaison de "xterm emacs"
*                                   terminait aussi xlatex !
* [email protected] 08/09/94 LG_NOM mis a 128 au lieu de 32
*                                   LG_PATH mis a MAXPATHLENGTH au lieu de 128
*/

#define VERSION "3.3c"
#define BANNER "xlatex, version %s -- Copyright(c) 1990-1994 CICB\n"

/************************************************************************
* Application X concue autour des Athena Widgets pour faciliter
* l'utilisation des commandes associees a TeX et LaTeX. Appel :
*
* xlatex [options] [document] [toolkit_options]
*
*
* L'utilisateur dispose des boutons de commandes:
*
*      Faconner        faconnage du document courant
*      Visionner       epreuvage sur ecran
*      PostScript      production d'un document PostScript
*      Imprimer        impression du document sur la station courante. Une
*                      fenetre ``popup'' apparait pour positionner les options
*                      telles que:
*                              - commande d'impression et ses arguments
*                              - station de sortie
*                              - pages a imprimer
*                              - nb. de copies
*      File d'attente  etat de l'imprimante
*      Editer          session edition de texte
*      Quitter         fin de l'application
*
* Les champs suivants sont egalement definis:
*
*      visionneur      utilitaire de visualisation sur ecran du document
*      repertoire      repertoire courant de travail
*      document        nom du document (correspond au nom du fichier sans
*                      suffixe 'tex'
*      faconnage       utilitaire de faconnage de documents
*      editeur         editeur de texte
*      pilote          pilote de conversion en PostScript
*      interaction     c'est une fenetre 'ascenseur' qui recoit toutes les
*                      sorties 'ecran' des commandes generees par selection
*                      des boutons de commande. Elle permet egalement a
*                      l'utilisateur d'introduire du texte pour notamment
*                      dialoguer avec la commande latex.
*
* Voici la correspondance 'shell-like' des actions realisees par les boutons de
* commande :
*
*      faconner        <faconnage> <document> &
*      imprimer        <pilote> [-p page -n nbpages] [-c copies]
*                             -o '!<impression>
*                             -P<imprimante>' <document> & (BSD)
*                             -d<imprimante>' <document> & (System V)
*      visionner       <visionneur> <document> &
*      file attente    lpq -P<imprimante> &    (BSD)
*                      lpstat -o<imprimante> & (System V)
*      editeur         xterm -e <editeur> <document>.tex &
*
* ***************************************************************************
*/

/*
* Notes :
* - xlatex est prevu pour utiliser les versions 5.4 et ulterieures de dvips
*/

/*
* Versions :
* 3.0   03/06/91: premiere version a diffusion restreinte X11R4
* 3.1   26/06/91: version diffusable
* 3.2   24/09/91: adaptation X11R5, meilleure designation des champs
*                 et possibilite de deplacement par tabulation
* 3.3   29/01/92: ajout du bouton PostScript et possibilite d'epreuver
*                 le PostScript si le mode choisi est PostScript
* 3.3a  03/12/92: ajout bouton xfig et amenagements mineurs
* 3.3b  10/12/93: adaptations pour differentes plate-formes
* 3.3c  11/08/94: ajout ressource et widget => pilote
*/

#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <search.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/param.h>  /* pour MAXPATHLEN */

#include <X11/Xos.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Toggle.h>
#include <X11/Xaw/Dialog.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SmeLine.h>
#include <X11/Xaw/MenuButton.h>

#if (defined(SVR4) || defined(SYSV))
#include <sys/types.h>
#include <fcntl.h>
#if ! defined(HPUX) && ! defined(AIXV3) && ! defined(IRIX)
#include <sys/filio.h>  /* pour FIOREAD */
#endif
#endif

#include "icone.h"

#define LG_NOM    128
#define LG_FICH   MAXPATHLEN
#define LG_CHEMIN MAXPATHLEN
#define DVIPS   "dvips"
#if (defined(SVR4) || defined(SYSV)) && ! defined(BSD_COMP)
#define LPR   "lp"
#define ALPR   "-d"
#define ALPQ   "-o"
#define LPQ     "lpstat"
#else
#define LPR     "lpr"
#define ALPR   "-P"
#define ALPQ   "-P"
#define LPQ     "lpq"
#endif

#define POINT_INSERTION 999999


#if defined(SVR4)
char *sys_errlist[0230];
#else
char *sys_errlist[];
#endif
int errno;

extern char
#ifndef AIXV3
              *strrchr (),
#endif
#if (defined(SVR4) || defined(SYSV)) && ! defined(IRIX)
              *getcwd (),
#else
              *getwd (),
#endif
              *strtok (),
              *getenv ();
extern ENTRY   *hsearch ();


/* --- STATIQUES ---*/

/*
* References en avant
*/

static          XtInputCallbackProc
               Procedure_entreeINP ();
static void     FaconnerCB (),
               VisionnerCB (),
               DessinerCB (),
               PostScriptCB (),
               ImprimerCB (),
               StatutCB (),
               EditerCB (),
               QuitterCB (),
               MenuVisionneursCB (),
               MenuVisionneursPSCB (),
               Toutes_pagesCB (),
               De_la_pageCB (),
               OkCB (),
               AnnulerCB ();
static          XtActionProc
               FocaliserACT (),
               ChampSuivantACT (),
               ControlACT (),
               InsertACT (),
               DeleteACT (),
               SendACT ();
static void     Connecter_pipes (),
               Reinitialiser_fenetre_interaction (),
               Supprimer_suffixe_tex ();

/*
* Implementation des groupes de tabulation des champs de saisie
* Chaque entree du tableau designe un widget et son suivant (chainage boucle)
* Pour acceder a une entree par le nom d'un widget, on utilise la fonction
* de hash-coding hsearch
*/
typedef struct {
   char           *nom;
   int             suivant;
   Widget          wg;
}               _groupeT;

static _groupeT TabGroup[15];
static int      IGroup = 0;
static ENTRY    TabElem;

/*
* Tableau des widgets boutons de commande et leur fonction de CallBack
*/
typedef void    (*fptr) ();
typedef struct {
   char           *nom;
   fptr            CB;
   Widget          wg;
}               _commandesT;

static _commandesT Commandes[] = {
   {"editer", EditerCB},
   {"dessiner", DessinerCB},
   {"faconner", FaconnerCB},
   {"visionner", VisionnerCB},
   {"postscript", PostScriptCB},
   {"imprimer", ImprimerCB},
   {"statut", StatutCB},
   {"quitter", QuitterCB}
};


/*
* Description des ressources de l'application Xlatex
*/

typedef struct _AppRessources {
   String          repertoire_initial,
                   dessin,
                   faconneur,
                   visionneurs,
                   visionneursPS,
                   pilote,
                   impression,
                   editeur;
   Boolean         editeur_avec_xterm;
   /* ressources maintenues pour compatibilite avec la version anterieure */
   String          repertoire;
}               RessourcesXlatex;

static RessourcesXlatex Ressources_xlatex;

static XtResource Ressources[] = {
   {"repertoireInitial", "RepertoireInitial", XtRString, sizeof (String),
       XtOffset (RessourcesXlatex *, repertoire_initial), XtRString, NULL},
   {"faconneur", "Faconneur", XtRString, sizeof (String),
       XtOffset (RessourcesXlatex *, faconneur), XtRString, "latex"},
   {"dessin", "Dessin", XtRString, sizeof (String),
       XtOffset (RessourcesXlatex *, dessin), XtRString, "xfig"},
   {"visionneurs", "Visionneurs", XtRString, sizeof (String),
       XtOffset (RessourcesXlatex *, visionneurs), XtRString, "*xdvi texx"},
   {"visionneursPS", "VisionneursPS", XtRString, sizeof (String),
       XtOffset (RessourcesXlatex *, visionneursPS), XtRString, "ghostview gs"},
   {"editeur", "Editeur", XtRString, sizeof (String),
       XtOffset (RessourcesXlatex *, editeur), XtRString, NULL},
   {"pilote", "Pilote", XtRString, sizeof (String),
       XtOffset (RessourcesXlatex *, pilote), XtRString, NULL},
   {"impression", "Impression", XtRString, sizeof (String),
       XtOffset (RessourcesXlatex *, impression), XtRString, LPR},
   {"editeurAvecXterm", "EditeurAvecXterm", XtRBoolean, sizeof (Boolean),
       XtOffset (RessourcesXlatex *, editeur_avec_xterm), XtRString, NULL},
};

static String   Ressources_defaut[] = {
   NULL,
};


/*
* Table des actions
*/
static XtActionsRec Table_actions[] = {
   {"Focaliser", (XtActionProc) FocaliserACT},
   {"ChampSuivant", (XtActionProc) ChampSuivantACT},
   {"Control", (XtActionProc) ControlACT},
   {"Insert", (XtActionProc) InsertACT},
   {"Delete", (XtActionProc) DeleteACT},
   {"Send", (XtActionProc) SendACT},
   {"faconner", (XtActionProc) FaconnerCB},
   {"editer", (XtActionProc) EditerCB},
   {"dessiner", (XtActionProc) DessinerCB},
   {"visionner", (XtActionProc) VisionnerCB},
   {"postscript", (XtActionProc) PostScriptCB},
   {"imprimer", (XtActionProc) ImprimerCB},
   {"statut", (XtActionProc) StatutCB},
   {"quitter", (XtActionProc) QuitterCB},
   {NULL, NULL}
};

static Widget   Toplevel,
               Document_wg,
               Imprimer_wg,
               Form_wg,
               Popup_wg,
               Repertoire_wg,
               Visionneurs_wg,
               Faconneur_wg,
               Editeur_wg,
               Dessin_wg,
               Interaction_wg,
               Imprimante_wg,
               Impression_wg,
               Pilote_wg,
               ToutesLab_wg,
               DeLab_wg,
               De_wg,
               ALab_wg,
               A_wg,
               CopiesLab_wg,
               Copies_wg;
Widget          Focus = (Widget) NULL;

XtAppContext    Xlatex_app_con;
Display        *Dpy;

static int      Pipe_entree[2],
               Pipe_sortie[2];

static char     Faconneur[LG_NOM] = "",
               Editeur[LG_NOM] = "",
               Dessin[LG_NOM] = "",
               Impression[LG_NOM] = "",
               Visionneur[LG_NOM] = "",
               Document[LG_FICH] = "",
               Repertoire[LG_CHEMIN] = "",
               Pilote[LG_NOM] = "",
               Imprimante[LG_NOM] = "";
static Boolean  PostScript = False;

static int      FaconneurPid = -9999;
static Boolean  FaconneurActif = False;
static int      ImpressionPid = -9999;
static Boolean  ImpressionActive = False;
static int      PostScriptPid = -9999;
static Boolean  PostScriptActif = False;
static int      Pgid;
static char     TamponEntree[256] = "";
static int      TamponEntree_lg = 0;

static Boolean  ToutesPages = True,
               DeLaPage = False;

static char     PageDebut[5] = "0",
               PageFin[5] = "0",
               Pages[5] = "0",
               Copies[5] = "1";

static Pixel    CouleurDeBordure,
               CouleurDeFond;

static char    *TabArg[15];

static char    *Visionneurs[10],
              *VisionneursPS[10];

static char     Programme[] = "Xlatex";


/* --- INTERNES --- */

#if (defined(SVR4) || defined(SYSV))
 killpg(ProcessGroup, Signal)
 int ProcessGroup;
 int Signal;
{
if (ProcessGroup < 0)
 {
    errno = ESRCH;
     return (-1);
 }
 return (kill(-ProcessGroup, Signal));
 }
#endif

       /************************************************************
         Changer_Repertoire

               - modification de la variable d'environnement PATH
               - changement de repertoire
        ************************************************************/

static int      Changer_repertoire (repertoire)
/****************************************/
   char           *repertoire;
{
   extern char   **environ;
   static char   **nouvelEnviron = NULL;
   static char     pwdEnv[256];
   static Boolean  nouvelEnvCree = False;
   int             nbVarEnv;
   char          **newPtr,
                 **oldPtr;

   if (nouvelEnvCree == False) {

       /* Enumeration des variables d'environnement */
       for (oldPtr = environ; *oldPtr; oldPtr++);
       nbVarEnv = (oldPtr - environ);

       nouvelEnviron = (char **) XtMalloc ((nbVarEnv + 2) * sizeof (char **));
       if (!nouvelEnviron) {
           fprintf (stderr,
              "%s:  echec allocation de %d pointeurs de l'environnement\n",
                    Programme, nbVarEnv + 2);
           exit (1);
       }
       /* placer PWD=repertoire en premiere position */
       strcpy (pwdEnv, "PWD=");
       newPtr = nouvelEnviron;
       *newPtr++ = pwdEnv;

       /* copie des autres variables de l'environnement */
       for (oldPtr = environ; *oldPtr; oldPtr++) {
           if (strncmp (*oldPtr, "PWD=", 4) != 0)
               *newPtr++ = *oldPtr;
       }
       *newPtr = NULL;
       environ = nouvelEnviron;
       nouvelEnvCree = True;
   }
   /* Changement de repertoire */
   strcpy (pwdEnv + 4, repertoire);
   return (chdir (repertoire));
}


       /************************************************************
        Connecter_pipes

        On se situe dans le cadre d'un processus FILS.
        Les organes d'E/S standard sont connectes aux pipes Pipe_entree
        et Pipe_sortie :

        0      recevra les caracteres en provenance du PERE
        1,2    enverront les caracteres au PERE qui activera la procedure
               Procedure_entreeINP

                               PERE            FILS
                               [1] Pipe_entree [0] == 0
        Procedure_entreeINP    [0] Pipe_sortie [1] == 1, 2

        Ce processus est affilie au 'process group' de son pere ce qui
        permet a ce dernier d'envoyer un signal de terminaison a
        l'ensemble du groupe en fin d'application
        ************************************************************/

static void     Connecter_pipes ()
/****************************************/
{
   long            arg;
   char            buf[32];
   int             len;

   /*
      On se situe ici dans le cadre d'un processus qui s'est cree mais avant
      qu'il ne realise l'execvp. on connecte 0, 1 et 2 aux pipes d'E/S
   */
   dup2 (Pipe_entree[0], 0);
   dup2 (Pipe_sortie[1], 1);
   dup2 (Pipe_sortie[1], 2);

   close (Pipe_entree[0]);
   close (Pipe_entree[1]);
   close (Pipe_sortie[0]);
   close (Pipe_sortie[1]);

   /* Purge des entrees anticipees */
   ioctl (0, FIONREAD, &arg);
#ifndef IRIX
   while (arg > 0) {
       len = read (0, buf, sizeof (buf));
       ioctl (0, FIONREAD, &arg);
   }
#endif
}


       /************************************************************
        Afficher

        Affichage d'un message suivi d'un retour a la ligne
        dans le widget Interaction.
        ************************************************************/

static int      Afficher (msg)
/****************************************/
   char           *msg;
{
   char            aLaLigne = '\n';
   XawTextBlock    text;

   /* On affiche le message dans la fenetre d'interaction */
   text.firstPos = 0;
   text.ptr = msg;
   text.format = FMT8BIT;
   text.length = strlen (msg);

   XawTextReplace (Interaction_wg, POINT_INSERTION, POINT_INSERTION, &text);

   text.ptr = &aLaLigne;
   text.length = 1;

   XawTextReplace (Interaction_wg, POINT_INSERTION, POINT_INSERTION, &text);
   XawTextSetInsertionPoint (Interaction_wg, POINT_INSERTION);

   XFlush (Dpy);
}


       /************************************************************
        Reinitialiser_fenetre_interaction

        Effacement du contenu du widget Interaction, generalement
        provoque a chaque faconnage
        ************************************************************/

static void     Reinitialiser_fenetre_interaction ()
/****************************************/
{
   XawTextBlock    text;
   char            car = '\00';

   text.firstPos = 0;
   text.ptr = &car;
   text.format = FMT8BIT;
   text.length = 0;

   XawTextReplace (Interaction_wg, (XawTextPosition) 0, POINT_INSERTION, &text);
   XawTextSetInsertionPoint (Interaction_wg, POINT_INSERTION);
}


       /************************************************************
        Supprimer_suffixe_tex

        Suppression du suffixe .tex du champ Document. Un suffixe
        approprie est ajoute ulterieurement.
        ************************************************************/

static void     Supprimer_suffixe_tex ()
/****************************************/
{
   char           *ptex;
   if ((ptex = rindex (Document, '.')) == NULL)
       return;
   if (strcmp (ptex, ".tex") == 0) {
       *ptex = '\00';
       XtVaSetValues (Document_wg, XtNstring, Document, NULL);
   }
}


       /************************************************************
        Positionner_arguments

        Analyse une ligne de commande et positionne le tableau
        d'arguments TabArg
        ************************************************************/

static void     Positionner_arguments (commande)
/****************************************/
   char           *commande;
{
   int             i = 0;
   TabArg[i] = strtok (commande, " ");
   while ((TabArg[++i] = strtok (NULL, " ")) != NULL);
}


       /************************************************************
        Acquitter_processus

        Reaction au signal de terminaison SIGCHLD.
        Dans le cas du processus de faconnage, d'impression et de
        generation PoscScript, on memorise et signale
        sa terminaison.
        ************************************************************/

static void     Acquitter_processus ()
/****************************************/
{
   int             status;
   int             pid;

#if defined(SVR4)
   while ((pid = waitpid ((pid_t) -1, &status, WNOHANG)) > 0) {
#else
#if defined(SYSV) && ! defined(IRIX)
   while ((pid = waitpid ((pid_t) 0, &status, WNOHANG)) > 0) {
#else
   while ((pid = wait3 (&status, WNOHANG, 0)) > 0) {
#endif
#endif
       if (pid == FaconneurPid) {
           char            msg[256];
           FaconneurActif = False;
           sprintf (msg, "--- Exit %s ---", Faconneur);
           Afficher (msg);
       }
       if (pid == ImpressionPid) {
           char            msg[256];
           ImpressionActive = False;
           sprintf (msg, "--- Exit %s ---", Impression);
           Afficher (msg);
       }
       if (pid == PostScriptPid) {
           char            msg[256];
           PostScriptActif = False;
           sprintf (msg, "--- Exit %s ---", Pilote);
           Afficher (msg);
       }
   }
   signal (SIGCHLD, Acquitter_processus);
}


       /************************************************************
        Initialiser_pipes

        Creation des pipes Pipe_entree et Pipe_sortie
        ************************************************************/

static void     Initialiser_pipes ()
/****************************************/
{
   pipe (Pipe_entree);
   pipe (Pipe_sortie);
#if (defined(SVR4) || defined(SYSV))
   fcntl (Pipe_entree[1], F_SETFL, O_NDELAY);
   fcntl (Pipe_sortie[0], F_SETFL, O_NDELAY);
#else
   fcntl (Pipe_entree[1], F_SETFL, FNDELAY);
   fcntl (Pipe_sortie[0], F_SETFL, FNDELAY);
#endif
}


       /************************************************************
        Initialiser_Xt

        Creation de l'application Xlatex, traitement des ressources
        et initialisation de l'environnement de travail
        ************************************************************/

static void     Initialiser_Xt (argc, argv)
/****************************************/
   int             argc;
   char           *argv[];
{
   char            msg[256];
   char            titre[256];
   char           *rep,
                  *doc,
                  *imp,
                  *edi;
   int             iv;
   char           *vp;

   /* Initialisation du processus de hash-coding : on prevoit 15 entrees max */
   hcreate (15);

   /*
      Interception du signal de changement d'etat (en l'occurence leur
      terminaison) des processus fils pour eviter leur etat <defunct>
   */
   signal (SIGCHLD, Acquitter_processus);


   /* Demarrage de l'application X */
   Toplevel = XtAppInitialize (&Xlatex_app_con, Programme, NULL,
                       0, &argc, argv,
                       Ressources_defaut, NULL, 0);

   Dpy = XtDisplay (Toplevel);

   XtAppAddActions (Xlatex_app_con, Table_actions, XtNumber (Table_actions));
   XtAppAddInput (Xlatex_app_con, Pipe_sortie[0], (XtPointer) XtInputReadMask,
                       (XtInputCallbackProc) Procedure_entreeINP, NULL);

   /* Obtention des ressources de l'application */
   XtGetApplicationResources (Toplevel, (XtPointer) & Ressources_xlatex,
                       Ressources, XtNumber (Ressources), NULL, 0);

   /* Fabrication des listes des visionneurs */
   Visionneur[0] = '\0';
   iv = 0;
   vp = strtok (Ressources_xlatex.visionneurs, " ,");
   while (vp) {
       if (*vp == '*') {
          vp++;
          strcpy (Visionneur, vp);
          PostScript = False;
       }
       Visionneurs[iv++]=vp;
       vp = strtok (NULL, " ,");
   }
   iv = 0;
   vp = strtok (Ressources_xlatex.visionneursPS, " ,");
   while (vp) {
       if (*vp == '*') {
          vp++;
          strcpy (Visionneur, vp);
          PostScript = True;
       }
       VisionneursPS[iv++]=vp;
       vp = strtok (NULL, " ,");
   }

   if (! Visionneur[0]) {
       strcpy (Visionneur, Visionneurs[0]);
       PostScript = False;
   }

   /* Prise en compte des anciens noms de ressources */
   if (Ressources_xlatex.repertoire != NULL)
       Ressources_xlatex.repertoire_initial = Ressources_xlatex.repertoire;

   /* Fabrication de l'icone */
   xlatex_bitmap = XCreateBitmapFromData (Dpy,
                       RootWindow (Dpy, DefaultScreen (Dpy)),
                       xlatex_bits, xlatex_width, xlatex_height);
   sprintf (titre, "%s %s", Programme, VERSION);
   XtVaSetValues (Toplevel,
                       XtNiconPixmap, xlatex_bitmap,
                       XtNtitle, titre,
                       NULL);

   /*
      Determination du nom du document. S'il est fourni en parametre : son
      chemin d'acces est expanse et on en deduit le repertoire et son nom
      sinon si la ressource repertoireInitial est positionnee : elle devient
      le repertoire courant de travail sinon le repertoire courant est pris
   */

   if (argc > 1) {
       doc = strrchr (argv[1], '/');
       if (doc == NULL)
           strcpy (Document, argv[1]);
       else {
           strcpy (Document, doc + 1);
           *doc = '\00';
           if (Changer_repertoire (argv[1]) == -1) {
               sprintf (msg, "chdir %s", argv[1]);
               perror (msg);
               exit (1);
           }
       }
   } else {
       strcpy (Document, "document");
       if (Ressources_xlatex.repertoire_initial != NULL) {
           if (Changer_repertoire (Ressources_xlatex.repertoire_initial) == -1) {
               sprintf (msg, "chdir %s", Ressources_xlatex.repertoire_initial);
               perror (msg);
               exit (1);
           }
       }
   };
#if (defined(SVR4) ||defined(SYSV)) && ! defined(IRIX)
   rep = getcwd (Repertoire, MAXPATHLEN);
#else
   rep = getwd (Repertoire);
#endif

   /*
      Obtention du nom de l'imprimante et de l'editeur par les variables
      d'environnement.
   */
#if (defined(SVR4) || defined(SYSV)) && ! defined(BSD_COMP)
   imp = getenv ("LPDEST");
#else
   imp = getenv ("PRINTER");
#endif
   if (imp != NULL)
       strcpy (Imprimante, imp);
   if (Ressources_xlatex.editeur == NULL) {
       edi = getenv ("EDITOR");
       if (edi != NULL)
           strcpy (Editeur, edi);
       else
           strcpy (Editeur, "emacs");
   } else
       strcpy (Editeur, Ressources_xlatex.editeur);
   if (Ressources_xlatex.pilote != NULL)
       strcpy (Pilote, Ressources_xlatex.pilote);
   else
       strcpy (Pilote, DVIPS);
   strcpy (Dessin, Ressources_xlatex.dessin);
   strcpy (Faconneur, Ressources_xlatex.faconneur);
   strcpy (Impression, Ressources_xlatex.impression);
}



       /************************************************************
        Creer_popup_imprimer

        Creation du formulaire d'impression
        Creation du groupe secondaire de tabulation pour ce formulaire.
        IGroup designe la derniere entree occupee dans TabGroup
        ************************************************************/

static void     Creer_popup_imprimer ()
/****************************************/
{
   Widget          form_wg,
                   impressionLab_wg,
                   imprimanteLab_wg,
                   toutesTog_wg,
                   deTog_wg,
                   ok_wg,
                   annuler_wg;
   Widget          widgets[30];
   Cardinal        nwg = 0;
   int             debut;

   Popup_wg = XtVaCreatePopupShell ("popup", transientShellWidgetClass, Form_wg, NULL);
   form_wg = XtVaCreateManagedWidget ("form", formWidgetClass, Popup_wg, NULL);

   widgets[nwg++] = impressionLab_wg = XtVaCreateWidget ("impressionLab", labelWidgetClass, form_wg,
                               NULL);

   debut = ++IGroup;
   TabGroup[IGroup].suivant = IGroup + 1;
   TabGroup[IGroup].nom = "impression";
   TabGroup[IGroup].wg = widgets[nwg++] = Impression_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, form_wg,
                               XtNstring, Impression,
                               XtNlength, LG_NOM,
                               XtNborderColor, CouleurDeFond,
                               NULL);

   widgets[nwg++] = imprimanteLab_wg = XtVaCreateWidget ("imprimanteLab", labelWidgetClass, form_wg,
                               NULL);

   IGroup++;
   TabGroup[IGroup].suivant = IGroup + 1;
   TabGroup[IGroup].nom = "imprimante";
   TabGroup[IGroup].wg = widgets[nwg++] = Imprimante_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, form_wg,
                               XtNstring, Imprimante,
                               XtNlength, LG_NOM,
                               XtNborderColor, CouleurDeFond,
                               NULL);

   widgets[nwg++] = toutesTog_wg = XtVaCreateWidget ("toutesTog", toggleWidgetClass, form_wg,
                               XtNstate, ToutesPages,
                               NULL);
   XtAddCallback (toutesTog_wg, XtNcallback, Toutes_pagesCB, NULL);

   widgets[nwg++] = ToutesLab_wg = XtVaCreateWidget ("toutesLab", labelWidgetClass, form_wg,
                               XtNsensitive, ToutesPages,
                               NULL);

   widgets[nwg++] = deTog_wg = XtVaCreateWidget ("deTog", toggleWidgetClass, form_wg,
                               XtNradioGroup, toutesTog_wg,
                               XtNstate, DeLaPage,
                               NULL);
   XtAddCallback (deTog_wg, XtNcallback, De_la_pageCB, NULL);

   widgets[nwg++] = DeLab_wg = XtVaCreateWidget ("deLab", labelWidgetClass, form_wg,
                               XtNsensitive, DeLaPage,
                               NULL);

   IGroup++;
   TabGroup[IGroup].suivant = IGroup + 1;
   TabGroup[IGroup].nom = "de";
   TabGroup[IGroup].wg = widgets[nwg++] = De_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, form_wg,
                               XtNstring, PageDebut,
                               XtNlength, sizeof (PageDebut),
                               XtNwidth, 35,
                               XtNsensitive, DeLaPage,
                               XtNborderColor, CouleurDeFond,
                               NULL);


   widgets[nwg++] = ALab_wg = XtVaCreateWidget ("aLab", labelWidgetClass, form_wg,
                               XtNsensitive, DeLaPage,
                               NULL);

   IGroup++;
   TabGroup[IGroup].suivant = IGroup + 1;
   TabGroup[IGroup].nom = "a";
   TabGroup[IGroup].wg = widgets[nwg++] = A_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, form_wg,
                               XtNstring, PageFin,
                               XtNlength, sizeof (PageFin),
                               XtNwidth, 35,
                               XtNsensitive, DeLaPage,
                               XtNborderColor, CouleurDeFond,
                               NULL);

   widgets[nwg++] = ok_wg = XtVaCreateWidget ("ok", commandWidgetClass, form_wg,
                               NULL);

   XtAddCallback (ok_wg, XtNcallback, OkCB, NULL);

   widgets[nwg++] = annuler_wg = XtVaCreateWidget ("annuler", commandWidgetClass, form_wg,
                               NULL);
   XtAddCallback (annuler_wg, XtNcallback, AnnulerCB, NULL);

   widgets[nwg++] = CopiesLab_wg = XtVaCreateWidget ("copiesLab", labelWidgetClass, form_wg,
                               NULL);

   IGroup++;
   TabGroup[IGroup].suivant = debut;
   TabGroup[IGroup].nom = "copies";
   TabGroup[IGroup].wg = widgets[nwg++] = Copies_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, form_wg,
                               XtNstring, Copies,
                               XtNlength, sizeof (Copies),
                               XtNwidth, 35,
                               XtNborderColor, CouleurDeFond,
                               NULL);

   XtManageChildren (widgets, nwg);


   /* Hash-coding du groupe de tabulation cree */

   {
       int     i;
       for (i=debut; i<= IGroup; i++) {
               TabElem.key = TabGroup[i].nom;
               TabElem.data= (char *) &TabGroup[i].suivant;
               hsearch (TabElem, ENTER);
       }
   }

}


       /************************************************************
        Creer_widgets

        Creation des widgets de l'application Xlatex
        Creation du groupe principal de tabulation dans TabGroup
        ************************************************************/

static void     Creer_widgets ()
/****************************************/
{
   Widget          repertoireLab_wg,
                   modeLab_wg,
                   documentLab_wg,
                   dessinLab_wg,
                   visionneursLab_wg,
                   menuVisionneurs_wg,
                   faconneurLab_wg,
                   piloteLab_wg,
                   editeurLab_wg;
   Widget          widgets[32];
   WidgetList      bouttons;
   Widget          wg;
   int             iv, i;
   Cardinal        nwg = 0;

   Form_wg = XtVaCreateManagedWidget ("form", formWidgetClass, Toplevel, NULL);

   for (i = 0; i < XtNumber (Commandes); i++) {
       widgets[nwg++] = Commandes[i].wg = XtVaCreateWidget (
                               Commandes[i].nom, commandWidgetClass, Form_wg,
                               XtNfromHoriz, (i == 0 ? NULL : Commandes[i - 1].wg),
                               NULL);
       XtAddCallback (Commandes[i].wg, XtNcallback, Commandes[i].CB, NULL);
   }
   widgets[nwg++] = repertoireLab_wg = XtVaCreateWidget ("repertoireLab", labelWidgetClass, Form_wg,
                               NULL);
   IGroup = 0;
   TabGroup[IGroup].suivant = IGroup + 1;
   TabGroup[IGroup].nom = "repertoire";
   TabGroup[IGroup].wg = widgets[nwg++] = Repertoire_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, Form_wg,
                               XtNstring, Repertoire,
                               XtNlength, LG_CHEMIN,
                               NULL);

   XtVaGetValues (Repertoire_wg,
                               XtNbackground, &CouleurDeFond,
                               XtNborderColor, &CouleurDeBordure,
                               NULL);
   XtVaSetValues (Repertoire_wg,
                               XtNborderColor, CouleurDeFond,
                               NULL);

   widgets[nwg++] = dessinLab_wg = XtVaCreateWidget ("dessinLab", labelWidgetClass, Form_wg,
                               NULL);

   IGroup++;
   TabGroup[IGroup].suivant = IGroup + 1;
   TabGroup[IGroup].nom = "dessin";
   TabGroup[IGroup].wg = widgets[nwg++] = Dessin_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, Form_wg,
                               XtNstring, Dessin,
                               XtNlength, LG_NOM,
                               XtNborderColor, CouleurDeFond,
                               NULL);

   widgets[nwg++] = documentLab_wg = XtVaCreateWidget ("documentLab", labelWidgetClass, Form_wg,
                               NULL);

   IGroup++;
   TabGroup[IGroup].suivant = IGroup + 1;
   TabGroup[IGroup].nom = "document";
   TabGroup[IGroup].wg = widgets[nwg++] = Document_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, Form_wg,
                               XtNstring, Document,
                               XtNlength, LG_NOM,
                               XtNborderColor, CouleurDeFond,
                               NULL);

   widgets[nwg++] = visionneursLab_wg = XtVaCreateWidget ("visionneursLab", labelWidgetClass, Form_wg,
                               NULL);

   widgets[nwg++] = Visionneurs_wg = XtVaCreateManagedWidget ("visionneurs", menuButtonWidgetClass, Form_wg,
                               XtNmenuName, "menuVisionneurs",
                               XtNlabel, Visionneur,
                               NULL);

   menuVisionneurs_wg = XtVaCreatePopupShell ("menuVisionneurs", simpleMenuWidgetClass, Visionneurs_wg, NULL);

   iv = 0;
   while (Visionneurs[iv]) {
       wg = XtVaCreateManagedWidget (Visionneurs[iv], smeBSBObjectClass, menuVisionneurs_wg,
                               NULL);
       XtAddCallback (wg, XtNcallback, MenuVisionneursCB, (XtPointer) iv);
       iv++;
   }
   XtVaCreateManagedWidget ("separateur", smeLineObjectClass, menuVisionneurs_wg, NULL);
   iv = 0;
   while (VisionneursPS[iv]) {
       wg = XtVaCreateManagedWidget (VisionneursPS[iv], smeBSBObjectClass, menuVisionneurs_wg,
                               NULL);
       XtAddCallback (wg, XtNcallback, MenuVisionneursPSCB, (XtPointer) iv);
       iv++;
   }


   widgets[nwg++] = faconneurLab_wg = XtVaCreateWidget ("faconneurLab", labelWidgetClass, Form_wg,
                               NULL);

   IGroup++;
   TabGroup[IGroup].suivant = IGroup + 1;
   TabGroup[IGroup].nom = "faconneur";
   TabGroup[IGroup].wg = widgets[nwg++] = Faconneur_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, Form_wg,
                               XtNstring, Faconneur,
                               XtNlength, LG_NOM,
                               XtNborderColor, CouleurDeFond,
                               NULL);

   widgets[nwg++] = editeurLab_wg = XtVaCreateWidget ("editeurLab", labelWidgetClass, Form_wg,
                               NULL);

   IGroup++;
   TabGroup[IGroup].suivant = IGroup + 1;
   TabGroup[IGroup].nom = "editeur";
   TabGroup[IGroup].wg = widgets[nwg++] = Editeur_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, Form_wg,
                               XtNstring, Editeur,
                               XtNlength, LG_NOM,
                               XtNborderColor, CouleurDeFond,
                               NULL);

   widgets[nwg++] = piloteLab_wg = XtVaCreateWidget ("piloteLab", labelWidgetClass, Form_wg,
                               NULL);

   IGroup++;
   TabGroup[IGroup].suivant = 0;
   TabGroup[IGroup].nom = "pilote";
   TabGroup[IGroup].wg = widgets[nwg++] = Pilote_wg = XtVaCreateWidget (TabGroup[IGroup].nom, asciiTextWidgetClass, Form_wg,
                               XtNstring, Pilote,
                               XtNlength, LG_NOM,
                               XtNborderColor, CouleurDeFond,
                               NULL);

   widgets[nwg++] = Interaction_wg = XtVaCreateWidget ("interaction", asciiTextWidgetClass, Form_wg,
                               XtNtop, XtChainTop,
                               XtNright, XtChainRight,
                               XtNleft, XtChainLeft,
                               XtNbottom, XtChainBottom,
                               XtNuseStringInPlace, False,
                               NULL);

   XtManageChildren (widgets, nwg);

   /* Ajustement de la taille de la fenetre d'interaction */

   {
       Dimension       largeur;
       XFontStruct    *police;
       XtVaGetValues (Interaction_wg, XtNfont, &police, NULL);
       largeur = (police->max_bounds.width + police->min_bounds.width) / 2;
       XtVaSetValues (Interaction_wg, XtNwidth, largeur * 80 + 05,
                               XtNheight, (police->max_bounds.ascent + police->max_bounds.descent) * 20 + 10,
                               NULL);
   }

   Supprimer_suffixe_tex ();

   /* Hash-coding du groupe de tabulation */

   {
       int     i;
       for (i=0; i<= IGroup; i++) {
               TabElem.key = TabGroup[i].nom;
               TabElem.data= (char *) &TabGroup[i].suivant;
               hsearch (TabElem, ENTER);
       }
   }

   Focus = Repertoire_wg;
}


       /************************************************************
        Installer_accelerateurs

        Mise en place des accelerateurs
        ************************************************************/

static void     Installer_accelerateurs ()
/****************************************/
{
   XtInstallAllAccelerators (Toplevel, Toplevel);
   XtInstallAllAccelerators (Repertoire_wg, Toplevel);
   XtInstallAllAccelerators (Document_wg, Toplevel);
   XtInstallAllAccelerators (Visionneurs_wg, Toplevel);
   XtInstallAllAccelerators (Dessin_wg, Toplevel);
   XtInstallAllAccelerators (Faconneur_wg, Toplevel);
   XtInstallAllAccelerators (Pilote_wg, Toplevel);
   XtInstallAllAccelerators (Editeur_wg, Toplevel);
   XtInstallAllAccelerators (Interaction_wg, Toplevel);
   XtInstallAllAccelerators (Imprimante_wg, Toplevel);
   XtInstallAllAccelerators (Impression_wg, Toplevel);
}


/* --- INPUT READ PROCEDURE --- */

       /************************************************************
        Procedure_entreeINP

        Procedure activee lors de la presence de caracteres dans le
        pipe Pipe_sortie[0].
        ************************************************************/

static XtInputCallbackProc Procedure_entreeINP (cd, s, id)
/****************************************/
   caddr_t         cd;
   int            *s;
   XtInputId      *id;
{
   char            tamp[100];
   XawTextBlock    text;

   text.firstPos = 0;
   text.ptr = tamp;
   text.format = FMT8BIT;

   /* Boucle de transfert */
   while ((text.length = read (*s, tamp, sizeof (tamp))) > 0)
       XawTextReplace (Interaction_wg, POINT_INSERTION, POINT_INSERTION, &text);

   XawTextSetInsertionPoint (Interaction_wg, POINT_INSERTION);

   return (0);
}


/* --- CALLBACKS --- */

       /************************************************************
        OkCB

        Confirmation de l'impression. La commande d'impression est
        preparee puis executee.
        ************************************************************/

static void     OkCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   int             pd;
   int             pf;
   int             cp;
   int             etat;
   char            msg[256];

   /* Calcul du nombre de pages a sortir dans Pages */
   pd = atoi (PageDebut);
   pf = atoi (PageFin);

   sprintf (Pages, "%d", (pf - pd) + 1);

   if (DeLaPage == True)
       if (pd > pf) {
           Afficher ("Mauvais parametres de pages. DEB > FIN !");
           return;
       }
   cp = atoi (Copies);
   if (cp < 1) {
       Afficher ("Nombre de copies errone !");
       return;
   }
   if (Changer_repertoire (Repertoire) == -1) {
       sprintf (msg, "%s: %s", Repertoire, sys_errlist[errno]);
       Afficher (msg);
       return;
   }
   Reinitialiser_fenetre_interaction ();
   Supprimer_suffixe_tex ();
   if (DeLaPage == True)
       if (cp > 1)
           sprintf (msg, "%s -c %s -p %s -n %s -o '!%s %s%s' %s", Pilote,
                         Copies, PageDebut, Pages, Impression, ALPR,
                         Imprimante, Document);
       else
           sprintf (msg, "%s -p %s -n %s -o '!%s %s%s' %s", Pilote, PageDebut,
                    Pages, Impression, ALPR, Imprimante, Document);
   else
       if (cp > 1)
           sprintf (msg, "%s -c %s -o '!%s %s%s' %s", Pilote, Copies,
                    Impression, ALPR, Imprimante, Document);
       else
           sprintf (msg, "%s -o '!%s %s%s' %s", Pilote, Impression,
                    ALPR, Imprimante, Document);
   Afficher (msg);
   ImpressionActive = True;
   if ((ImpressionPid = fork ()) == 0) {
       sprintf (msg, "!%s %s%s", Impression, ALPR, Imprimante);
       Connecter_pipes ();
       if (DeLaPage == True)
           if (cp > 1)
               etat = execlp (Pilote, Pilote, "-c", Copies, "-p", PageDebut,
                              "-n", Pages, "-o", msg, Document, '\00');
           else
               etat = execlp (Pilote, Pilote, "-p", PageDebut,
                              "-n", Pages, "-o", msg, Document, '\00');
       else
           if (cp > 1)
               etat = execlp (Pilote, Pilote, "-c", Copies, "-o", msg, Document,
                          '\00');
           else
               etat = execlp (Pilote, Pilote, "-o", msg, Document, '\00');
       if (etat == -1) {
           printf ("%s est inaccessible.\n", Pilote);
           exit (-1);
       }
   }
   else if (ImpressionPid == -1) {
       sprintf (msg, "fork: %s", sys_errlist[errno]);
       Afficher (msg);
       ImpressionActive = False;
   }
   XtPopdown (Popup_wg);
}


       /************************************************************
        MenuVisionneursCB

        Selection d'un visionneur
        ************************************************************/

static void     MenuVisionneursCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   int             iv = (int) client_data;
   strcpy (Visionneur, Visionneurs[iv]);
   PostScript = False;
   XtVaSetValues (Visionneurs_wg, XtNlabel, Visionneur, NULL);
}


       /************************************************************
        MenuVisionneursPSCB

        Selection d'un visionneur PostScript
        ************************************************************/

static void     MenuVisionneursPSCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   int             iv = (int) client_data;
   strcpy (Visionneur, VisionneursPS[iv]);
   PostScript = True;
   XtVaSetValues (Visionneurs_wg, XtNlabel, Visionneur, NULL);
}


       /************************************************************
        AnnulerCB

        Abandon de l'impression
        ************************************************************/

static void     AnnulerCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   XtPopdown (Popup_wg);
}


       /************************************************************
        De_la_pageCB

        Selection de l'option 'de la page nn a mm', desensibilisation
        de l'option 'toutes les pages'
        ************************************************************/

static void     De_la_pageCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   Boolean         etat;

   XtVaGetValues (w, XtNstate, &etat, NULL);
   if (etat == True) {
       DeLaPage = True;
       XtVaSetValues (DeLab_wg, XtNsensitive, True, NULL);
       XtVaSetValues (De_wg, XtNsensitive, True, NULL);
       XtVaSetValues (ALab_wg, XtNsensitive, True, NULL);
       XtVaSetValues (A_wg, XtNsensitive, True, NULL);
   } else {
       DeLaPage = False;
       XtVaSetValues (DeLab_wg, XtNsensitive, False, NULL);
       XtVaSetValues (De_wg, XtNsensitive, False, NULL);
       XtVaSetValues (ALab_wg, XtNsensitive, False, NULL);
       XtVaSetValues (A_wg, XtNsensitive, False, NULL);
   }
}


       /************************************************************
        Toutes_pagesCB

        Selection de l'option 'toutes les pages' et desensibilisation
        de l'option 'de la page nn a mm'
        ************************************************************/

static void     Toutes_pagesCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   Boolean         etat;

   XtVaGetValues (w, XtNstate, &etat, NULL);
   if (etat == True) {
       ToutesPages = True;
       XtVaSetValues (ToutesLab_wg, XtNsensitive, True, NULL);
   } else {
       ToutesPages = False;
       XtVaSetValues (ToutesLab_wg, XtNsensitive, False, NULL);
   }
}


       /************************************************************
        ImprimerCB

        Apparition du formulaire d'impression
        ************************************************************/

static void     ImprimerCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   Position        x,
                   y;
   Dimension       largeur,
                   hauteur;

   /* Calcul des coordonnees en fonction de celles de l'application */
   XtVaGetValues (Toplevel,
                  XtNwidth, &largeur,
                  XtNheight, &hauteur,
                  NULL);
   XtTranslateCoords (Form_wg, (Position) (largeur / 3), (Position) (hauteur / 3), &x, &y);

   XtVaSetValues (Popup_wg,
                  XtNx, x,
                  XtNy, y,
                  NULL);

   XtPopup (Popup_wg, XtGrabNone);
   XRaiseWindow (Dpy, XtWindow (Popup_wg));
}


       /************************************************************
        VisionnerCB

        Execution de la commande de visionnage
        ************************************************************/

static void     VisionnerCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   char            msg[255];
   int             pid;

   if (Changer_repertoire (Repertoire) == -1) {
       sprintf (msg, "%s: %s", Repertoire, sys_errlist[errno]);
       Afficher (msg);
       return;
   }
   Supprimer_suffixe_tex ();
   if (PostScript == True) {
       struct stat dvi,
                   ps;
       char        fdvi[LG_CHEMIN],
                   fps[LG_CHEMIN];
       sprintf (fdvi, "%s.dvi", Document);
       sprintf (fps , "%s.ps" , Document);
       if (stat (fdvi, &dvi) == 0) {
           if (stat (fps, &ps) == 0) {
               if (dvi.st_mtime > ps.st_mtime) {
                   sprintf (msg, "PSST !!  %s.ps n'est pas a jour ...");
                   Afficher (msg);
               }
           }
       }
       sprintf (msg, "%s %s.ps", Visionneur, Document);
   }
   else
       sprintf (msg, "%s %s.dvi", Visionneur, Document);
   Afficher (msg);
   Positionner_arguments (msg);
   if ((pid = fork ()) == 0) {
       Connecter_pipes ();
       if (execvp (TabArg[0], TabArg) == -1) {
           printf ("%s n'a pas marche.\n", msg);
           exit (-1);
       }

   }
   else if (pid == -1) {
       sprintf (msg, "fork: %s", sys_errlist[errno]);
       Afficher (msg);
   }
}


       /************************************************************
        DessinerCB

        Execution de la commande de dessin
        ************************************************************/

static void     DessinerCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   char            msg[255];
   int             pid;

   if (Changer_repertoire (Repertoire) == -1) {
       sprintf (msg, "%s: %s", Repertoire, sys_errlist[errno]);
       Afficher (msg);
       return;
   }
   sprintf (msg, "%s", Dessin);
   Afficher (msg);
   Positionner_arguments (msg);
   if ((pid = fork ()) == 0) {
       Connecter_pipes ();
       if (execvp (TabArg[0], TabArg) == -1) {
           printf ("%s n'a pas marche.\n", msg);
           exit (-1);
       }

   }
   else if (pid == -1) {
       sprintf (msg, "fork: %s", sys_errlist[errno]);
       Afficher (msg);
   }
}


       /************************************************************
        PostScriptCB

        Production d'un document PostScript
        ************************************************************/

static void     PostScriptCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   char            msg[255];

   if (Changer_repertoire (Repertoire) == -1) {
       sprintf (msg, "%s: %s", Repertoire, sys_errlist[errno]);
       Afficher (msg);
       return;
   }
   if (PostScriptActif == True) {
       sprintf (msg, "%s (PID=%d) encore actif !", Pilote, PostScriptPid);
       Afficher (msg);
       return;
   }
   Supprimer_suffixe_tex ();
   sprintf (msg, "%s -o %s.ps %s", Pilote, Document, Document);
   Afficher (msg);
   Positionner_arguments (msg);
   PostScriptActif = True;
   if ((PostScriptPid = fork ()) == 0) {
       Connecter_pipes ();
       if (execvp (TabArg[0], TabArg) == -1) {
           printf ("%s n'a pas marche.\n", msg);
           exit (-1);
       }

   }
   else if (PostScriptPid == -1) {
       sprintf (msg, "fork: %s", sys_errlist[errno]);
       Afficher (msg);
       PostScriptActif = False;
   }
}


       /************************************************************
        FaconnerCB

        Execution de la commande de faconnage. Plusieurs processus
        pouvant etre lances, un groupe de processus est cree
        permettant d'agir globalement avec les signaux
        ************************************************************/

static void     FaconnerCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   char            msg[255];

   if (Changer_repertoire (Repertoire) == -1) {
       sprintf (msg, "%s: %s", Repertoire, sys_errlist[errno]);
       Afficher (msg);
       return;
   }
   if (FaconneurActif == True) {
       sprintf (msg, "%s (PID=%d) encore actif !", Faconneur, FaconneurPid);
       Afficher (msg);
       return;
   }
   Reinitialiser_fenetre_interaction ();
   Supprimer_suffixe_tex ();
   sprintf (msg, "%s %s", Faconneur, Document);
   Afficher (msg);
   Positionner_arguments (msg);
   FaconneurActif = True;
   if ((FaconneurPid = fork ()) == 0) {
       Connecter_pipes ();
       if (execvp (TabArg[0], TabArg) == -1) {
           printf ("%s est inaccessible.\n", Faconneur);
           exit (-1);
       }
   }
   else if (FaconneurPid == -1) {
       sprintf (msg, "fork: %s", sys_errlist[errno]);
       Afficher (msg);
       FaconneurActif = False;
   }
}


       /************************************************************
        StatutCB

        Interrogation de la file d'attente de l'imprimante
        ************************************************************/

static void     StatutCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   int             pid;
   char            msg[255],
                   arg[255];

   sprintf (msg, "%s %s%s", LPQ, ALPQ, Imprimante);
   Afficher (msg);

   sprintf (arg, "%s%s", ALPQ, Imprimante);

   if ((pid = fork ()) == 0) {
       Connecter_pipes ();
       if (execlp (LPQ, LPQ, arg, '\00') == -1) {
           printf ("%s: %s.\n", msg, sys_errlist[errno]);
           exit (-1);
       }
   }
   else if (pid == -1) {
       sprintf (msg, "fork: %s", sys_errlist[errno]);
       Afficher (msg);
   }
}


       /************************************************************
        EditerCB

        Execution de la commande d'edition de texte
        ************************************************************/

static void     EditerCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   char            msg[255];
   int             pid;

   if (Changer_repertoire (Repertoire) == -1) {
       sprintf (msg, "%s: %s", Repertoire, sys_errlist[errno]);
       Afficher (msg);
       return;
   }
   Supprimer_suffixe_tex ();
   if (Ressources_xlatex.editeur_avec_xterm == True)
       sprintf (msg, "xterm -e %s %s.tex", Editeur, Document);
   else
       sprintf (msg, "%s %s.tex", Editeur, Document);
   Afficher (msg);
   Positionner_arguments (msg);
   if ((pid = fork ()) == 0) {
       Connecter_pipes ();
       if (execvp (TabArg[0], TabArg) == -1) {
           printf ("%s: %s.\n", msg, sys_errlist[errno]);
           exit (-1);
       }
   }
   else if (pid == -1) {
       sprintf (msg, "fork: %s", sys_errlist[errno]);
       Afficher (msg);
   }
}


       /************************************************************
        QuitterCB

        Fin de l'application Xlatex.
        Tous les processus lances encore actifs sont termines.
        ************************************************************/

static void     QuitterCB (w, client_data, call_data)
/****************************************/
   Widget          w;
   XtPointer       client_data,
                   call_data;
{
   signal (SIGTERM, SIG_IGN);
   killpg (Pgid, SIGTERM);
   XtDestroyWidget (Toplevel);
   exit (0);
}

/* --- ACTIONS --- */

       /************************************************************
        FocaliserACT

        Entree de la souris dans un champ d'entree de texte.
        Demarquage du pourtour du champ precedent
        Marquage du pourtour du champ courant
        Positionnement du focus
        ************************************************************/

static XtActionProc FocaliserACT (wg, event, params, num_params)
/****************************************/
   Widget          wg;
   XEvent      *event;
   String         *params;
   Cardinal       *num_params;
{
   if (XtIsSensitive(wg)) {
       XtVaSetValues (Focus, XtNborderColor, CouleurDeFond, NULL);
       if (*num_params == 0) {
           XtVaSetValues (wg, XtNborderColor, CouleurDeBordure, NULL);
           Focus = wg;
       }
       XtSetKeyboardFocus (XtParent (wg), wg);
   }
   else
       ChampSuivantACT(wg, event, params, num_params);

   return (0);
}


       /************************************************************
        ChampSuivantACT

        Recherche du champ suivant dans le groupe de tabulation
        Une fois trouve, focalisation sur celui-ci
        ************************************************************/

static XtActionProc ChampSuivantACT (wg, event, params, num_params)
/****************************************/
   Widget          wg;
   XEvent      *event;
   String         *params;
   Cardinal       *num_params;
{
   ENTRY           elem,
                   *elem_cherche;
   char            *nom;
   int              i;

   nom = XtName(wg);
   elem.key=nom;
   elem_cherche=hsearch(elem,FIND);
   i = *(int *)elem_cherche->data;
   FocaliserACT (TabGroup[i].wg,event,params,num_params);
   return (0);
}


       /***********************************************************
        ControlACT

        Entree d'une sequence d'interruption.
               ^C -> envoi du signal SIGINT aux processus de faconnage
               ^D -> envoi du signal SIGTERM aux processus de faconnage
        ************************************************************/

static XtActionProc ControlACT (wg, event, params, num_params)
/****************************************/
   Widget          wg;
   XKeyEvent      *event;
   String         *params;
   Cardinal       *num_params;
{
   char            car;
   int             lg;

   /* Obtention du caractere frappe */
   lg = XLookupString (event, &car, 1, NULL, NULL);

   switch (car) {
   case 3:                     /* Ctrl-C */
       kill (FaconneurPid, SIGINT);
       break;
   case 4:                     /* Ctrl-D */
       kill (FaconneurPid, SIGTERM);
       break;
   }

   TamponEntree_lg = 0;
   XawTextSetInsertionPoint (Interaction_wg, POINT_INSERTION);

   return (0);
}


       /************************************************************
        InsertACT

        Entree d'un caractere dans la fenetre d'interaction.
        Le caractere est memorise dans le tampon TamponEntree
        ************************************************************/

static XtActionProc InsertACT (wg, event, params, num_params)
/****************************************/
   Widget          wg;
   XKeyEvent      *event;
   String         *params;
   Cardinal       *num_params;
{
   char            str[32];
   int             lg;
   register int    i;

   /* Obtention des caracteres frappes */
   lg = XLookupString (event, str, sizeof (str), NULL, NULL);

   strncat (TamponEntree, str, lg);
   TamponEntree_lg += lg;

   XawTextSetInsertionPoint (Interaction_wg, POINT_INSERTION);

   return (0);
}


       /************************************************************
        DeleteACT

        Correction de frappe dans la fenetre d'interaction.
        Le tampon TamponEntree est mis a jour.
        ************************************************************/

static XtActionProc DeleteACT (wg, event, params, num_params)
/****************************************/
   Widget          wg;
   XEvent      *event;
   String         *params;
   Cardinal       *num_params;
{
   /*
      On veille a ne pas corriger au dela des caracteres precedemment
      introduits.
   */
   if (TamponEntree_lg > 0)
       TamponEntree[--TamponEntree_lg] = '\00';

   return (0);
}


       /************************************************************
        SendACT

        Frappe d'un retour a la ligne dans la fenetre d'interaction.
        Le contenu du tampon TamponEntree est envoye dans le pipe
        Pipe_entree[1].
        ************************************************************/

static XtActionProc SendACT (wg, event, params, num_params)
/****************************************/
   Widget          wg;
   XEvent      *event;
   String         *params;
   Cardinal       *num_params;
{
   /* Envoi de la commande */
   strcat (TamponEntree, "\n");
   TamponEntree_lg++;

   (void) write (Pipe_entree[1], TamponEntree, TamponEntree_lg);

   /*
      Reinitialisation de TamponEntree et mise a jour de la position dans la
      fenetre d'interaction
   */
   TamponEntree[0] = '\00';
   TamponEntree_lg = 0;

   return (0);
}


/*
* ---------------------------------------------------------------------------
*                              PROGRAMME PRINCIPAL
* ---------------------------------------------------------------------------
*/
main (argc, argv)
/****************************************/
   int             argc;
   char           *argv[];
{
/*    fprintf (stderr, BANNER, VERSION);*/

   if ((Pgid = fork ()))
       exit (0);

/*
* La, c'est un peu galere ! Il faut assurer que ce processus est leader
* du groupe qu'il va dominer
* Sur Sun (SunOS 4.x), setsid() ou setpgrp(0,0) serait tout indique ...
*/

#ifdef gould
   setpgrp (0, Pgid);
#else
   setsid ();
#endif /* gould */

   Initialiser_pipes ();
   Initialiser_Xt (argc, argv);

   Creer_widgets ();
   Creer_popup_imprimer ();

   Installer_accelerateurs ();

   XtRealizeWidget (Toplevel);

   XtAppMainLoop (Xlatex_app_con);
}