Précédent Suivant Index

Chapitre 3 :   Info

3.1   Description du format

Info est un programme pour lire de la documentation. Il regroupe des manuels de divers programmes et utilitaires unix. Il existe également un mode info sous Emacs, ce qui permet de consulter une documentation sous Emacs. On peut trouver d'autres méthodes de documentation, comme les pages de manuel `man' ou les fichiers d'aide `HOW-TO', mais le format info fournit en plus un système de références et de liens. On peut ainsi faire des menus et structurer le document, ce qui le rend beaucoup plus lisible. Un fichier info est alors un fichier formaté pour être lu par le programme info. Le format des fichiers info est ainsi un des premiers languages hypertexte, apparu bien avant l'HTML.

Il peut être utile de traduire LATEX vers ce format pour plusieurs raisons : Un fichier info est organisé en noeuds (nodes). Il permet de créer des menus dont les entrées seront liées à ces noeuds, ainsi que des références vers des noeuds. Le programme info n'affiche qu'un noeud à la fois, et permet de naviguer entre les différents noeuds en suivant les liens.

3.1.1   L'arborescence des noeuds

Chaque noeud est caractérisé par son nom, et contient des pointeurs vers d'autres noeuds référencés par leur nom. On y trouve ensuite le texte lisible par l'utilisateur.

Les noeuds sont organisés dans un arbre. Le programme info commence par afficher le père : ``Top''. On peut alors descendre dans l'arborescence. On trouve quelques champs facultatifs qui permettent le déplacement entre les différents noeuds:
Next
Nom du noeud suivant dans le niveau en cours
Previous
Nom du noeud précédant le noeud courant
Up
Nom du noeud parent du noeud courant
Les noeuds fils d'un même père sont alors liés par une liste doublement chaînée.

Pour descendre dans l'arbre des noeuds, on peut créer des menus qui font référence à tous les fils d'un noeud.

Voici la syntaxe d'un noeud dans le fichier info: Il doit commencer par un caractère `^_', qui sera invisible à l'utilisateur, et finir par un caractère `^_', `^L' ou la fin du fichier. Le `^_' commençant un nouveau noeud doit être suivi d'un retour à la ligne, éventuellemt précédé d'un `^L'.

La ligne suivante contient l'entête du noeud. Cette ligne doit contenir le nom du noeud, après le mot clé Node:, puis éventuellement les déclarations des champs Next, Previous et Up. Par exemple, voici l'entête du noeud de la section 5-3 du manuel de caml en info:
^_
File: caml.info-3, Node: Section 5-3, Next: Section 5-4,
                      Prev: Section 5-2, Up: Chapter 5
On remarquera qu'il y a en plus un champ donnant un nom de fichier. Cette information ne sera pas traîtée par le programme info, mais permet à l'utilisateur de savoir quel fichier est utilisé.

On voit aussi que ces noeuds vont correspondre en fait aux sections et chapitres de LATEX. Il est possible de faire autrement, mais c'est la structuration logique du document qui doit être respectée. Les noeuds seront toujours nommés ainsi par HEVEA.

D'après [1], un nom de noeud a la forme suivante: on trouve éventuellement au début le nom du fichier contenant le noeud entre parenthèses. Ensuite, il vient un caractère alphabétique, suivi de n'importe quelle suite de caractère. Il se termine lorsqu'on rencontre une tabulation, une virgule, un point ou un retour à la ligne. On a donc l'expression régulière suivante :
[(filename)]  ('a'-'z' | 'A'-'Z') [^'\t' '.' ',' '\n']+
En particulier, on peut mettre des espaces dans les noms de noeud.

Les menus sont déclarés par `* Menu:'. La fin de la ligne est ignorée. Ensuite, chaque ligne qui commence par un `* ' rajoute une entrée au menu. Suit alors le nom du sujet concerné, puis `:', puis le nom du noeud. Si le nom du noeud et le nom du sujet sont identiques, on peut utiliser le raccourci : `* NAME::'. C'est ce cas qui sera utilisé par HEVEA.

Ensuite, on peut placer à la fin du fichier une table des noeuds. Cette table regroupera tous les noeuds et donnera leur position dans le fichier. Cela permettra d'accélérer la recherche des noeuds, en évitant une recherche séquentielle sur l'ensemble du fichier. De plus, c'est indispensable si l'on veut faire référence à des noeuds qui sont dispersés sur plusieurs fichiers, sans avoir à mettre le nom du fichier à chaque fois qu'on fait référence à un noeud.

La table des noeud se définit comme sur cet exemple, extrait de la traduction du manuel Caml par HEVEA:
^_
Indirect:
caml.info-1: 94
caml.info-2: 57048

^_^L
Tag table:
(Indirect)
File: caml.info-1,        Node: Top^?94
File: caml.info-9,        Node: Section 17-12^?499222
File: caml.info-6,        Node: Subsection 13-4-3^?315447
Dans ce cas, on a plusieurs fichiers. Il faut définir les références à ces fichiers par la commande Indirect, suivie du nom de fichier et du numéro de caractère auxquel commence ce fichier. Vient ensuite la table de noeuds elle-même. Les noms de fichiers ne sont pas obligatoires dans cette table et ne sont pas interprétés par info. Il suffit de donner le nom du noeud, suivi du caractère `^?' puis de la position du caractère `^_' de début du noeud dans le fichier, en nombre de caractères.

On voit donc que l'on peut structurer la hiérarchie des noeuds du documents très facilement. En particulier, il est possible de donner aux noeuds n'importe quelle structure de graphe. Il ne faut cependant pas oublier de référencer les noeuds pour en faire une structure connexe, car sinon certains noeuds seraient inaccessibles. Par convention, le noeud principal d'un fichier est nommé `Top', et sera ouvert par défaut par le programme info.

L'utilisation des ces menus et liens se fait en cliquant sur les noms de noeuds si on est dans le mode info d'emacs. Sinon, on peut utiliser les touches `u' pour Up, `n' pour Next, `p' pour Previous, `m' pour accéder au menu, suivi du nom de l'entrée dans le menu. On peut également accéder directement aux cinq premiers éléments du menu grâce aux touches `1' à `5'.

3.1.2   Références

En plus des liens par les menus et de l'organisation en graphe des noeuds, on peut faire des liens entre deux noeuds d'une autre manière. Il existe en effet des pointeurs d'un autre type, les références. Ces liens peuvent être placés dans le courant du texte, contrairement aux menus qui doivent être à part.

Pour créer une référence, il faut utiliser la syntaxe : `* Note ' puis le nom de la référence, `:', et le nom du noeud. Ici aussi on peut utiliser le raccourci `noeud::' si les deux noms sont identiques. Par exemple, pour le manuel de caml, on trouve une note vers le chapitre 15 : `*Note Chapter 15::'. Cette référence apparaitra ainsi dans le texte, et on pourra y accéder soit en cliquant dessus si on est sous emacs, soit par la commande `f' suivie du nom de la référence.

3.2   Implémentation

Le module gérant le format info s'appuie sur le module Text, puisque tout le formatage doit être fait en mode texte. Les fonctions spécifiques à l'info sont dans le module InfoRef. Le module Info ne contient donc essentiellement que des indications de redirection des procédures vers le bon module.

Pour créer les noeuds, j'ai choisi de faire correspondre ceux-ci aux parties, chapitres, sections de LATEX, ce qui est le plus logique. Or, pour remplir l'entête d'un noeud, il faut connaître non seulement le noeud précédent, mais aussi le noeud suivant. Ce dernier est impossible à savoir puisqu'on lit le fichier source dans l'ordre, en écrivant le fichier de sortie en même temps. Il va donc falloir relire le fichier de sortie pour mettre au point les noeuds.

Etant donné qu'il est possible de faire des liens sur des noeuds localisés dans d'autres fichiers, j'ai limité la taille du fichier de sortie. Lors de la deuxième passe, on coupera donc le fichier en plusieurs s'il est trop gros. Le premier fichier contiendra uniquement la table des noeuds, et les autres contiendront les noeuds.

La première étape de la traduction consiste donc à formater le texte et ajouter les informations qui vont permettre la création des noeuds, menus et références. Le résultat sera sauvegardé dans un fichier temporaire qui va être ensuite réinterprété pour remplacer les indications de noeuds par les entêtes complets des noeuds.

3.2.1   Création des noeuds et références

En LATEX

Un noeud est crée par la commande interne d'HEVEA `\@infonode', qui prend trois arguments. Le premier est optionnel et donne le nom du menu dans lequel doit apparaitre le noeud. Le deuxieme argument est le nom du noeud et le troisième sa description.

Un menu est donné par `\@infomenu'. Cette commande a pour argument unique le nom du menu et va placer un menu. Les noms de menus, contrairement aux noms de noeuds, peuvent être identiques. En effet, on rajoutera des noeuds dans ce menu au fur et à mesure grâce à l'argument optionnel de `\@infonode'. Le noeud sera rajouté dans le menu crée à ce nom le plus récemment. Ceci permet de faire des menus appelés `section' sans ambiguïté sur les sections à rajouter dans ce menu : ce sont les sections qui suivent, jusqu'à la prochaine déclaration d'un menu `section'.

La définition et l'emplacement des menus et noeuds est entièrement gérée au niveau des fichiers de définition de style écrits en LATEX ``article.hva'', ``book.hva'', etc...Les menus sont crées dans l'environnement immédiatement supérieur.

Prenons pour exemple les sections dans un article. Lorsqu'on rencontre la première déclaration de section, le compteur section vaut 1 et on pose alors une commande `\@infomenu{section}'. Puis, à chaque nouvelle section, on aura juste une commande `\@infonode[section]{nom}{description}':
\newcommand{\sectionmenu}{\ifthenelse{\value{section}=1}%
{\@infomenu{section}}%
{}}%
\newcommand{\section}[2][]{%
\stepcounter{section}%
\sectionmenu%
\@infonode[section]{\sectionname\ \thesection}{#2}%
\@open{HEAD}{=}%%H2
\thesection\quad #2\\%
\@close{HEAD}%
}
Les références, elles, seront gérées par le mécanisme habituel de LATEX. La seule différence étant que l'on fera référence au noeud où est défini le label correspondant, par rapport à la page en LATEX ou à la ligne en HTML.

Organisation des données

Ces déclarations LATEX vont donner lieu à l'intérieur du programme à la création de structures représentant les noeuds, menus et références.

Les noeuds sont représentés par le type node_t:
type node_t = {
    mutable name : string;
    mutable comment : string;
    mutable file : string;
    mutable previous : node_t option;
    mutable next : node_t option;
    mutable up : node_t option;
    mutable pos : int;
  } 
;;
A la création d'un noeud, on commence par définir une nouvelle variable de type node_t, en vérifiant si le nom est bien conforme à un nom de noeud. Puis on vérifie si le nom du noeud n'est pas déjà défini. Ensuite, si le noeud est dans un menu, on le rajoute dans la liste définissant les entrées du menu concerné. Enfin, on sauvegarde les informations de ce noeud dans une table de hachage avec pour clé le nom du noeud.

Pour les menus, j'ai défini le type :
type menu_t = {
    mutable num : int;
    mutable nom : string;
    mutable nod : node_t option;
    mutable nodes : node_t list;
  }
;;
Comme les noms de menus ne sont pas uniques, on ne peut pas classer les menus sur leur nom. On leur affecte alors un numéro. nod est le noeud dans lequel se trouve le menu et nodes est la liste des noeuds appartenant au menu.

Dans le fichier temporaire, on va remplacer la commande \@infomenu par une ligne contenant \@menu suivi du numéro du menu. On initialise le champ nodes, puis on rajoute le menu dans la liste des menus.

Les références et les étiquettes se font grâce à une liste d'étiquettes ayant le type label_t:
type label_t = {
    mutable lab_name : string;
    mutable noeud : node_t option;
  };;
let labels_list = ref [];;
La pose d'une étiquette ne fait alors que rajouter un élément dans cette liste, si le nom n'est pas déjà utilisé. Ensuite, la référence se fait en écrivant dans le fichier temporaire la commande \@reference{<nom du label>}

3.2.2   Deuxième passe

Une fois le document entièrement formaté, le fichier temporaire est fermé et on ouvre le fichier de destination, au niveau de la fonction Dest.finalize qui vérifie l'état des piles avant la fin du programme, en mode HTML et texte.

Le document va alors être analysé par l'analyseur simplifié défini dans InfoRef. On va reconnaitre uniquement les commandes vues au-dessus, et deux commandes spéciales pour les notes de bas de page.

L'analyse du fichier va être précédée par une étape de remplissage des liens des noeuds pour créer l'arbre des noeuds en mémoire. Ceci se fait à partir des menus qui vont structurer le document. On va suivre l'algorithme 3.1 pour chaque menu, en partant par le menu de niveau le plus élevé, et en parcourant ensuite la liste des menus de manière à descendre l'arbre des noeuds. Cela revient à parcourir les menus dans l'ordre où ils ont été crées.



Figure 3.1 : Algorithme de remplissage des liens

On peut ensuite parcourir le fichier et remplacer chaque occurence de \@node par l'entête du noeud correspondant. On doit aussi vérifier le fichier dans lequel se trouve le noeud. Si ce fichier est différent du fichier en cours, alors il faut changer de fichier.

L'affichage des menus ne pose pas plus de problèmes, puisqu'on écrit au fur et à mesure toutes les entrées du menus, dans l'ordre. Les références donnent lieu à une recherche du noeud correspondant dans la table de hachage, puis on envoie ``*Note <noeud>::''.

Un traitement particulier est reservé aux notes de bas de page. En effet, Celles-ci sont créees au niveau du fichier de macros hevea.hva, par un système de liens doubles. Lorsqu'on pose une note de bas de page, il faut mettre un symbole distinctif 1 puis la référence. En info, cela peut se faire avec la syntaxe *Note <symbole>:<noeud>. Il faut donc commencer par écrire `*Note ', puis recopier le ou les symboles caractérisant la note, et ensuite poser le lien avec le nom du noeud correspondant, qui sera dans la liste des étiquettes comme les autres références.

Les commandes insérées dans le fichier temporaire pour ces notes sont d'abord `@@footNote{<label>}', puis le symbole, puis `@@footNoteEnd'. On ne peut pas utiliser de `\' pour commencer la commande car ce n'est pas une commande de l'analyseur principal d'HEVEA et cette macro va être interprétée par celui-ci.


1
Ca peut être un nombre ou *, #, %, §, ¶, ||, **, ## ou %%.

Précédent Suivant Index