Prehtml
Charger l'ensemble du projet (sources, exécutable pour linux, et
mini-documentation).
Origine du projet
Des difficultés pour maintenir une liste de dépêches
d'information sur un site consacré au vélo comme moyen de
transport dans la région de Montpellier.
Initialement, chaque fois que je rajoutais une information, il fallait
que je modifie une page "actualites.html", en écrivant le texte
de la nouvelle information. Puis je rajoutais une cible <A NAME="pistes_etroites">
sur l'article, puis je recopiais le titre en haut de la page d'actualités
avec un lien vers cette info <A HREF="#pistes_etroites"> pour retrouver
facilement les dernières infos, et enfin je rajoutais cette info
sur la page d'accueil "index.html" pour signaler la nouveauté <A
HREF="actualites.html#pistes_etroites">. C'était beaucoup de travail
à chaque fois, sans compter le travail d'archivage des informations
un peu périmées.
Ensuite, j'ai été remplacé par un jeune génie
des maths et de l'informatique, qui a automatisé tout le processus
avec des scripts Perl et shell dans tous les sens, et quelques utilitaires
Unix. Brillant, il faut le dire, mais incompréhensible: j'aime bien
le Perl, mais à chaque fois que je m'y remets 3 mois après
la dernière fois, j'ai oublié toute la syntaxe. Et le système
rendait impossible ou presque l'utilisation d'éditeurs HTML un peu
évolués comme Netscape et équivalents.
J'ai donc décidé de faire mon propre système, en
me basant sur le langage qui me paraissait le plus simple et le plus connu:
le Bourne-Shell, c'est-à-dire le langage de la console d'Unix.
Est-ce que ça a marché? Essayez toujours de jeter un oeil
à www.chez.com/velocite
ou montpellier.fubicy.org.
Pour ma part, je suis plutôt content du résultat, d'autant
que moins d'une semaine après l'idée de départ, le
programme fonctionnait déjà et, pire, je l'utilisais déjà
avec parfaite satisfaction.
Aperçu général
Plusieurs cas se présentent:
-
Je crée une page toute simple: aucun problème, elle s'appellera
page.html (ou page.htm chez certains adeptes de Microsoft, pour cause de
limitation du MS-DOS à 8+3 caractères). J'utilise alors un
éditeur HTML quelconque et mon programme ne sert à rien.
-
Je crée une page qui doit être modifiée automatiquement
en fonction de certains paramètres: je crée une page nommée
page2.phtml (l'extension .phtm est également possible), qui est
une page normale mais contenant des commandes spéciales. Cette page
sera lue par prehtml et les commandes spéciales seront décodées:
à la fin du traitement, une page HTML sera créée par
mon programme (elle s'appellera page2.html, ou bien page2.htm pour les
microsots).
-
Je veux crée une série de pages HTML (par exemple une page
d'archives pour chaque année). J'écris alors une page nommée
page3.phtml, qui contient les commandes nécessaires pour décrire
comment les pages doivent être créées (en général,
ceci comprends l'inclusion d'autres pages). Le programme prehtml va exécuter
les commandes et créer toutes les pages demandées (mais toujours
basées sur page3.phtml), par contre il ne créera pas de page3.html.
Principe de prehtml
Le principe est fort simple: au début du programme, un "shell" démarre
en parallèle de prehtml. Les commandes trouvées dans les
fichiers .phtml (fichiers source) sont décodées avec l'aide
du shell et/ou sous la forme de commandes shell. Le même shell tournant
du début à la fin du traitement de la page, il est capable
de stocker des variables, ce qui est très utile.
Si vous connaissez le shell, prehtml vous paraîtra évident.
Sinon, vous avez le choix: soit apprendre le shell, soit vous passer de
prehtml et trouver une autre solution à vos problèmes. Pour
l'instant, je suis à ma connaissance le seul et unique utilisateur
de ce programme.
Prehtml est écrit en C standard. Le C++ serait plus adapté
mais j'aurais passé plus de temps à m'y remettre qu'à
tout faire en C (le langage utilisé dans les microcontrôleurs...).
On m'a fait remarqué qu'il aurait été plus simple
d'utiliser des analyseurs syntaxiques tout faits: c'est juste, mais je
ne connais pas du tout ces choses-là. Et que Perl sait faire tout
cela ou presque: c'est exact, mais comme je l'ai signalé, ma mémoire
n'arrive pas à retenir la syntaxe du Perl: à force de vouloir
englobler toutes les syntaxes existantes dans le monde de la programmation,
je trouve que ça perd un peu sa logique. Et je trouve le Perl très
difficile à déboguer (peut-être par manque d'habitude).
Syntaxe
Les fonctions sont écrites sous une forme qui ressemble à
des balises HTML. Par exemple <SHELL titre="Le vélo">. Bien sûr,
ce sont des balises inconnues du HTML! Mais la différence est que
si l'on utilise un éditeur HTML, cette balise risque fort de devenir
ceci ">SHELL titre="Le vélo">" (c'est
ce que fournit Netscape Composer). Mais ce n'est pas grave, prehtml est
prévu pour décoder ce charabia et exécuter la commande
tout aussi bien. Cela dit, si la commande est tapée à la
main, donc réellement sous la forme <SHELL titre="Le vélo">,
ça marche également.
Les commandes prehtml fonctionnent en minuscules ou en majuscules. Mais
attention car le shell, lui, fait la différence! "ls" donne la liste
des fichiers, mais pas "LS", et si vous définissez "titre", alors
"Titre" n'est pas défini.
On lance le programme en tapant "prehtml <liste_de_fichiers.phtml>".
Pour chaque fichier .phtml, un fichier .html est créé (sauf
cas spécial mentionné plus bas). Si le fichier source s'appelle
.phtm, le fichier créé s'appelle .htm. Si on donne plusieurs
fichiers, le programme fonctionne comme si on le lançait successivement
avec chaque fichier. Si le fichier qui doit être créé
existe déjà et est exactement identique à celui qui
devrait être créé, il n'est pas modifié (sa
date n'est pas affectée, ce qui est utile pour des programmes automatiques
comme Makefile ou sitecopy, utilisables pour créer ou pour télécharger
le site).
Trêve de baratin, voici la liste des commandes, leur syntaxe,
et des exemples:
-
<SHELL commande_shell>: Permet d'exécuter une commande
shell discrètement, c'est-à-dire sans sortie dans
le fichier .html créé. Exemples:
-
<SHELL titre="Des pistes cyclables sur le trottoir">: permet de définir
une variable pour la suite
-
<SHELL numero_info=`expr $numero_info + 1`>: permet d'incrémenter
une variable.
-
<SHELL ls infos/*.html | tee liste.txt>: crée un fichier utilisable
par la suite. Cet exemple sert surtout à signaler une limitation:
il est impossible de mettre le caractère ">" dans une commande prehtml,
car il signale la fin d'une commande. Il n'y a pas de code d'échappement,
peut-être serait-ce utile? Mais pour une simple redirection vers
un fichier, le programme "tee" fonctionne très bien.
-
<ECHO texte>: permet d'écrire ce qu'on veut dans le fichier
créé, comme la commande "echo" du shell:
-
Info n°<ECHO $numero_info>: <ECHO $titre>.
Notez que le fait de créer un lien n'empêche pas la fonction
de marcher, puisque le lien se situe à l'extérieur de cette
fonction. Ça ne marcherait pas (avec erreur possible) si le lien
coupait le fonction en deux (comme dans <ECHO $titre>).
-
Pour la raison expliquée plus haut, on ne peut pas mettre de balise
HTML dans une commande Prehtml. Mais le contraire est possible et même
très utile: dans un lien, ou dans l'adresse d'une cible, c'est très
pratique. Regardez de près l'exemple suivant:
-
<shell ref="sans_bagnoles">: ceci définit une variable
Cliquez ici pour lire l'info : l'adresse
indiquée pour ce lien est <echo #$ref>, ce qui sera remplacé
par #sans_bagnoles
La journée du 22 septembre a été
une réussite...: juste au début de cette ligne se trouve
une cible (champ <A NAME>), dont le nom est <ECHO $ref>: le nom deviendra
"sans_bagnoles" après passage par prehtml.
-
<SHELLECHO commande_shell>: comme <SHELL>, sauf que le résultat
est écrit dans le fichier créé. Si la commande donne
plusieurs lignes, des sauts de lignes sont insérés entre
chaque ligne (NB: globalement le HTML n'est pas sensible aux sauts de ligne),
mais ni au début, ni à la fin. Il serait peut-être
plus rusé d'insérer des <P> entre chaque ligne, mais j'attends
vos suggestions pour savoir ce qui est le mieux
-
Dernière mise à jour le <SHELLECHO date "+%A %e %B
%Y à %Hh%M">. Notez que:
-
je peux mettre toute la commande en gras pour faire ressortir le résultat,
ou bien plus que la commande, mais pas une fraction de la commande (sous
peine d'erreurs imprévisibles)
-
le "à" inclus dans la commande (éventuellement sous la forme
à qui est décodée avant d'être envoyée
au shell) est recodé au format HTML (à)avant d'être
écrit dans le fichier de sortie. C'est également le comportement
de <ECHO>
-
Et pour finir philosophons sur ceci: <SHELLECHO fortune>.
-
<ECHONOESC>: comme <ECHO>, sauf que la sortie n'est pas recodée
en HTML: ce n'est sans doute pas très utile, mais sait-on jamais.
"NOESC" signifie ici "no escape", pas d'échappement des caractères
spéciaux pour le HTML.
-
<shell nom="El Señor Julier">
<meta name="Author" content="<ECHONOESC $nom>">
-
<SHELLECHONOESC>: comme <SHELLECHO>, sauf que la sortie n'est
pas recodée en HTML. Utile si vous faîtes marcher une commande
qui produit réellement du HTML. Un exemple relativement simple:
-
Voici la liste des fichiers HTML:
<SHELLECHONOESC ls *.html | awk $'{print $0"\074P\076"}'>: remarquez
l'échappement $'....' (compris par le shell) qui petmet de transformer
\074 en < et \076 en >. La commande envoyée à awk est
donc '{print $0"<P>"}', ce qui lui dit d'écrire chaque ligne
de l'entrée avec <P> à la fin. L'ensemble permet de sortir
la liste des fichiers avec un passage de ligne au format HTML entre chaque.
-
<INCLUDE fichiers>: inclut un ou des fichiers dans le fichier
principal.
Remarque 1: quand je dis inclure, je veux dire que les fichiers
inclus seront traités comme s'ils étaient dans le fichier
source. Les fonctions prehtml des fichiers inclus sont exécutées.
Remarque 2: on peut inclure plusieurs fichiers avec une seule
commande, c'est la syntaxe de la commande "cat"
Remarque 3: cette commande (comme presque toutes les commandes
de prehtml) est récursive: les sous-fichiers peuvent en inclure
à leur tour... Ils peuvent même s'inclure eux-mêmes,
mais ça ne doit pas boucler indéfiniment!
Remarque 4: si on inclut un fichier créé avec un éditeur
HTML, celui-ci contiendra certainement des balises telles que <HTML><HEAD></HEAD><BODY>....</BODY></HTML>.
L'insertion des fichiers commence après le champ <BODY> (s'il
existe) et se termine avant </BODY> (s'il existe), ce qui permet de
l'inclure dans un autre fichier HTML sans se poser de questions.<INCLUDE
bandeau_haut.htm>: pratique pour définir un entête, que l'on
peut changer en même temps dans toutes les pages du site.
-
Info n°<echo $numero_info>:
<INCLUDE depeche_${numero_info}.html>: dois-je vraiment détailler
l'utilité de ceci?
-
<INCLUDE definitionscommunes/*.htm>: on peut faire ça si l'on
veut.
-
<NOTINCLUDE fichiers>: comme <INCLUDE>, sauf que les fichiers
inclus et traités ne sont pas retransmis dans la sortie. Pourtant,
toutes les opérations sont faites (même les sous-inclusions
de fichiers, etc...). Principale utilité: connaître des variables
qui vont être définies dans un fichier inclus. Exemple:
-
<shell fichier=depeche_${annee}_${numero_info}.htm>
<NOTINCLUDE $fichier>
(Le fichier "depeche_2001_1.htm" contient la commande suivante: <shell
titre="Tous à vélo!">)
N°<echo "$numero_info : $titre">
(produit: "N°1 : Tous à vélo!")
-
<IF condition>... [<ELSE>...] <FI>: permet de faire des
conditions. La syntaxe de l'instruction "if" du shell, mais sans le point-virgule
";" à la fin. Note: il n'y a pas de "then". La commande <ELSE>
est facultative. Bien sûr, les <IF> peuvent être imbriqués
entre eux, ce qui est souvent utile. La condition peut être une commande
normale, l'instruction "test", ou bien une expression entre crochets [
] (ne pas oublier les espaces obligatoires), ce qui revient au même
que "test", je crois.
-
<IF [ $file = index.phtml ]>ACCUEIL<ELSE>Accueil<FI>:
pratique pour faire des bandeaux qui s'adaptent à chaque page.
-
<FOR variable IN liste>...... <DONE>: comme en shell: la variable
designée par valoir successivement chaque mot de la liste. Ce qui
est entre <FOR> et <DONE> va être lu autant de fois qu'il y
a de mots dans la liste. C'est une instruction indispensables pour faire
des pages un peu dynamiques, car elle permet de faire des boucles. On peut
imbriquer les <FOR>, mais le <DONE> doit être dans le même
fichier que le <FOR> (le contraire sera d'ailleurs très bizarre).
-
<FOR fichier IN depeches/depeche*.htm>
<notinclude $fichier>
- <echo $titre>
<DONE>: ceci permet de générer une liste de titres toute
simple
-
<FOR fichier IN $liste_de_fichiers>
<include $fichier><DONE>: ceci permet de lister joliment
le contenu de plusieurs fichiers.
-
<WHILE condition>....<DONE>: exécute la partie entre
<WHILE> et <DONE> tant que la condition est vraie (et donc aucune
fois si la condition est fausse dès le départ. Je suppose
qu'il y a des cas où c'est plus utile que <FOR>. On peut également
imbriquer les <WHILE> entre eux (ou bien avec les <FOR>).
-
<shell nb=1>
<WHILE [ -f depeche_${nb}.htm ]> (fait la boucle tant qu'il existe
un fichier du nom demandé)
<include depeche_${nb}.htm>
<shell nb=`expr $nb + 1`>
<DONE>
-
<GENERATE nom_de_fichier>.....</GENERATE>: commande spéciale
qui demande à prehtml de ne pas créer le fichier normalement
prévu, mais celui qui est indiqué dans la commande. La seule
utilisation intéressante est à l'intérieur d'une boucle
<FOR> ou <WHILE>, auquel cas les instructions <GENERATE> et </GENERATE>
sont rencontrées plusieurs fois avec un nom différent à
chaque fois.
Un fichier est créé pour chaque nom (le même nom ne
peut venir qu'une seule fois). Le début et la fin du fichier sont
communs à tous les fichiers créés, et correspondent
respectivement à la partie avant <GENERATE> et à la partie
après </GENERATE>. Par contre, la partie variable correspond
à la partie qui se trouve entre ces deux commandes.
Remarque 1: après un </GENERATE> et avant un <GENERATE>
suivant, il ne doit y avoir que des caractères "blancs" (espaces,
tabulations, sauts de ligne "\n"), car autrement le programme ne saurait
pas où envoyer ces caractères. Attention, les caractères
blancs, ça ne comprends pas les <BR> ou les <P> et </P>
que peut inclure un éditeur HTML si on tape sur ENTRÉE.
Remarque 2: contrairement aux autres instructions, il n'est pas
possible d'imbriquer les <GENERATE>.
Exemple:
-
<for annee in 19?? 20??>
<GENERATE archives_${annee}.html>Archives de l'année <echo
$annee>
<for depeche in ${annee}/depeche*.htm>
<include $depeche>
<done>
</GENERATE>
<done>
-
<CHUT>... </CHUT>: supprime momentanément la sortie
dans un fichier. On peut remarquer que <NOTINCLUDE> est à peu
près équivalent à <CHUT><INCLUDE></CHUT>.
On peut imbriquer les <CHUT>.
Cette balise peut servir à mettre des commentaires, mais sa véritable
utilité est due aux éditeurs HTML. Si l'on veut qu'une boucle
génère un tableau ou une liste, par exemple, il faut d'abord
générer le début du tableau (ou de la liste), puis
chaque ligne, puis la fin. Or un éditeur HTML type "Wysiwyg" ne
permet pas de faire un début de tableau sans mettre aussi la fin.
La façon la plus simple de contourner le problème est de
mettre <CHUT> à l'endroit qui est inutile. Attention car si on
ne fait pas attention, on peut générer du HTML incorrect
(NB: il existe des programmes pour vérifier la qualité du
HTML: par exemple "weblint", programme gratuit, au moins sur Linux). Exemple
d'utilisation pour un tableau:
-
On génère d'abord le début du tableau (on s'arrête
juste à la fin de la dernière cellule de la 1ère ligne)
Titre d'une colonne |
Titre de l'autre colonne<CHUT> |
-
Pour chaque élément, on reprend à la fin d'une ligne,
et on génère la ligne suivante, puis on s'arrête juste
à la fin
<FOR variable IN liste>
|
</CHUT> |
dans une ligne: une cellule |
et une autre cellule<CHUT> |
<DONE>
-
Puis on termine la dernière ligne et le tableau lui-même
-
Bref, on a créé un tableau sans même savoir quel est
le nom des balises HTML pour cela! Il est cependant utile de connaître
un peu le HTML. Par exemple pour les couleurs: on peut définir la
couleur du tableau (juste au début), la couleur de chaque ligne,
et la couleur de chaque cellule. Par exemple, dans l'exemple indiqué,
le début est indiqué avec le "fond du tableau" jaune, donc
tout le tableau apparaîtra en jaune, sauf la première ligne,
dont le "fond de ligne" est en bleu clair: la première ligne apparaîtra
en bleu clair, car la couleur de ligne est prioritaire sur la couleur de
fond.
Bugs et limitations
-
Le système qui communique avec le shell n'est pas très élégant,
mais je ne sais pas faire plus joli, et ça marche quand même
très bien. Voir le fichier shell.c si vous avez des suggestions
à faire.
-
Le shell est potentiellement dangereux, comme tout langage complet: ne
traitez pas un fichier .phtml d'origine inconnue, si jamais il contenait
<SHELL rm -R \>, vous pourriez effacer tout le disque.
-
On peut bloquer le système en envoyer une commande shell incomplète,
par exemple un guillemet non refermé comme par exemple <ECHO
"texte>.
-
On ne peut pas mettre ">" dans une commande prehtml puisque c'est le signal
de fin de commande. Pour contourner, il y a "tee" pour les redirections,
ou bien les codes d'échappement du shell en $'\074....\076'
-
Si on veut réellement écrire une commande prehtml dans le
fichier HTML, il est difficile de faire en sorte qu'elle ne soit pas décodée
par prehtml. Mieux vaut écrire la page directement en HTML (comme
celle-ci par exemple), ou bien inclure directement une page HTML (par exemple
avec <SHELLECHONOESC cat un_fichier.html>). Si vous voulez réellement
taper une commande, vous pouvez toujours inclure quelque chose qui va empêcher
prehtml de repérer la commande: par exemple <<ECHO>WHILE>
donne <WHILE>, puisque <ECHO> tout seul ne donne aucune sortie.
-
Une commande prehtml (partie entre "<" et ">") ne peut pas dépasser
une certaine taille, actuellement 160 caractères (environ), définie
par #define LONGUEUR_LIGNE dans le fichier prehtml.h. Cette limite est
la taille de ce qu'on a tapé: on peut taper <for fichier in depeches/20*/depeche*.html>,
qui est une commande courte, même si les fichiers sont très
nombreux, et que leurs noms pris ensemble dépassent de loin cette
limite. Par contre, chaque fichier doit avoir un nom plus court que cette
limite (y compris chemin d'accès).
-
C'est pour les amateurs de shell, mais à tout prendre, il est plus
utile d'apprendre le shell que d'apprendre un langage que l'on ne retrouvera
jamais.
-
Serait-il utile d'avoir une version de <SHELLECHO> qui inclue des <P>
à la fin de chaque ligne? Ça n'a rien de difficile.
-
Les caractères accentués non terminés par ";" ne sont
pas décodés (exemple: é au lieu de é),
je crois que ceci est une entorse à la norme HTML, mais les éditeurs
HTML incluent toujours le point-virgule.
-
Ce système n'est prévu que pour le jeu de caractères
iso-8859-1 (iso-latin-1, européen de l'ouest), ça risque
de faire n'importe quoi si on met des caractères orientaux ou d'Europe
centrale.
-
Il arrive que des fichiers temporaires restent en cas d'erreur: c'est un
bug à corriger. Il y en a dans /tmp/ (qui s'accumulent), et d'autres
dans le répertoire courant (ce sont des .html.tmp, mais ils disparaissent
quand l'erreur est corrigée).
Trucs et astuces
Pour m'en sortir entre les extensions, j'utile ces conventions:
-
.phtml: fichiers qui doivent être traités par prehtml
-
.html: fichiers écrits directement sous la forme définitive,
et fichiers générés par prehtml
-
.htm: fichiers inclus par prehtml
Pour lancer prehtml, il y a au moins deux bonnes méthodes:
-
si tous les fichiers sont dans le même répertoire, et qu'ils
ne sont pas trop gros (question de vitesse), on peut lancer:
prehtml *.phtml (ce qui traite l'ensemble des fichiers)
-
utiliser l'utilitaire "make", avec un fichier nommé "Makefile" qui
contient les lignes suivantes:
faitprehtml.htm : *.phtml
prehtml $?
touch faitprehtml.htm
Le fichier "faitprehtml.htm", dans cet exemple, sert juste à sauvegarder
la date du dernier traitement par prehtml. Les fichiers *.phtml plus récents
sont alors traités avec prehtml.
Pour transférer les fichier sur le serveur j'utilise "sitecopy"
(un programme gratuit). Pour ne transférer que les bons fichiers,
le fichier de configuration ~/.sitecopyrc contient les lignes suivantes:
exclude "*.htm"
exclude "*.phtml"
exclude "*.tmp"
exclude "Makefile"
Adresse de cette page: http://mjulier.free.fr/prehtml.html