Sommaire Carte Index Recherche Nouvelles Archives Liens A propos
[Barre Superieure]
[Barre Inferieure]
Philipp Gühring
par

L´auteur:

Philipp vient juste de passer son bac scientifique à HTL Wiener Neustadt, une école technique supérieure en informatique. Il a maintenant du temps à consacrer à son équipe de développement appelée Futureware 2001. Il est également fan de Linux et membre actif du Linux User Group Austria

Sommaire:

Dialog - un langage de programmation de dialogue.

[Illustration]

Résumé:

Dialog est un langage de programmation spécialisé dans les dialogues avec l'utilisateur. Il a été utilisé dans le logiciel de simulation commerciale Würstelstand. Cet article décrit le développement de Dialog et ses applications.



 

Introduction

Würstelstand est un logiciel de simulation commerciale autrichienne en allemand qui propose au joueur de gérer un stand de hot-dogs, et dans lequel le contact avec les clients devient presque un jeu d'aventure. C'est précisément pour ce contact-client que j'ai développé le langage Dialog, qui répond aux besoins suivants : Lorsqu'il fallut écrire les conversations télephoniques, je réutilisai le moteur de dialog et l'étendis. Le joueur contrôle le personnage Leni, qui est le propriétaire du stand, avec un système à choix multiples. Les clients sont simulés par l'ordinateur. L'objectif du joueur est de conseiller aux clients de lui acheter quelquechose et de bavarder avec eux. Les clients apparaissent automatiquement lorsqu'ils ont faim et qu'ils ont le temps de venir. De plus, le joueur a la possibilité d'être actif, et peut appeller plusieurs numéros de son téléphone.
 

Le Langage Dialog

Les dialogues sont stockés dans des fichiers ASCII, et sont interprétés ligne par ligne. Il est également possible d'avancer à une ligne déterminée à tout moment. Ces fichiers sont simplement créés avec un éditeur de texte. Le nom de fichier est Name.BAT (i.e. : HALR.BAT). Quand le joueur (Leni) parle, cela donne quelque chose comme :
Leni: Text
Leni: Bonjour, monsieur ! 
      Que souhaitez vous aujourd'hui ?
Leni: Regardez-moi ces jeunes !
      Incroyable !
Leni a la parole
Regardez-moi ces jeunes ! Incroyable !
Etes-vous tombé du lit ?
Le client a la parole
Deux Francfort avec du pain et deux Colas.
Allons, laisses tomber, vieil homme !

Quand l'interlocuteur dit quelque chose: (Kunde signifie client en Allemand, et Telefon signifie téléphone.)

Kunde: Text
Kunde: Deux Francfort avec du pain et deux Cola.
       Allons, laisses tomber, vieil homme !
Telefon: Futureware 2001, Philipp Gühring.
         Que puis-je pour vous ?
         

Tous les dialogues habituels se terminent par

Ende
(Ende signifie fin en Allemand)

Un exemple simple :
Leni: Bonjour! Que souhaitez-vous ce matin ?
Kunde: Salut ! Un Käsekrainer s'il-vous-plaît !
Leni: Un instant.
Leni: Et voilà.
Kunde: Merci beaucoup. Au revoir.
Leni: Au revoir !
Ende

Les étiquettes de saut sont définies par un caractère "deux points" au début d'une ligne, qui est suivi immédiatement par le nom de l'étiquette. On peut atteindre une étiquette avec la commande Sprung (Sprung signifie saut en Allemand) :

:Target
//Suit un exemple de saut :
Sprung Target

Exemple
...
Leni: 1
//Commençons par ceci
SPRUNG MENU_0
//Je reviendrai !
...
//Ces commandes ne seront pas exécutées.
Leni: 2
:MENU_0
//Je suis de retour ! 
Leni: 3
Que fait l'interpréteur dans cet exemple ? Tout d'abord, il trouve la commande Leni: et affiche le texte 1. Ensuite il y a un commentaire sur la ligne suivante (Commençons par ceci) qui sera ignoré. Après celà, il trouve la commande Sprung. L'interpréteur recherche alors l'étiquette MENU_0 dans tout le dialogue, la trouve quelques lignes plus loin, et effectue un saut à cette position. De nouveau, on trouve un commentaire (Je suis de retour !). Enfin, la dernière commande est Leni:, qui affiche 3 à l'écran. En fin de compte, la commande Leni: 2 a été laissée de coté dans l'exemple et Leni ne dit pas 2.

Comme nous l'avons vu, une ligne peut :

Les commentaires commencent par ;(point virgule),//(double slash), (espace),*(astérisque) Ils permettent de documenter et de clarifier le dialogue et sont complètement ignorés par l'interpréteur. par exemple :
// Ceci est un commentaire
**************************************************
*On peut faire aussi des commentaires comme celà.*
**************************************************
Les commentaires ne peuvent pas être sur la même ligne qu'une commande :
Leni: Je ne comprends plus rien.  // SANS COMMENTAIRE
L'interpreteur va reconnaître Je ne comprends plus rien. // SANS COMMENTAIRE comme le texte à afficher !  

Le Système de Choix Multiples

Multiple-Choice
  • Que faites-vous dans la vie ?
  • Est-ce que mon Würstelstand vous plaît ?
  • Que puis-je faire pour vous ?
Dialog propose le mécanisme suivant : Le système gère une liste, dans laquelle sont insérées les entrées correspondantes aux possibilités offertes à l'utilisateur. En temps utile, le menu est affiché à l'écran et l'utilisateur fait son choix. L'interpréteur poursuit l'exécution par la partie du programme qui gère l'entrée choisie.

Tout d'abord, les entrées sont insérées dans la liste par les commandes NEU et ALT. Ces deux commandes sont suivies de l'étiquette cible pour le saut et du texte de l'entrée. Le texte peut être très long, dans ce cas, le système insère automatiquement des sauts de lignes. Avec la commande MENÜ, toute la liste est affichée et le joueur peut alors choisir une des propositions.



Voyons maintenant les trois types de questions.  

Thèmes indépendant du contexte

Ce premier type sert pour les sujets de discussions :
Neu acheter, un bien chaud, comme d'hab, ou pas ?
Neu travail, Comment ca va au travail ?
Neu langue, Suivez vous toujours le cours de langues à WIFI ?
Neu family, Comment va la famille ?
Neu weather, Vous profitez du beau temps ?
Menü
De cette façon, le joueur a la possibilité de choisir chacune des entrées dans un ordre quelconque. Les choix peuvent être répétés. Les entrées qui ne sont pas choisies restent dans la liste et pourront être sélectionnées plus tard. Ainsi, si nous choisissons travail dans l'exemple précédent :
:travail
Leni: Comment ca va le travail ?
Client: Trop de choses à faire, comme toujours.
Menü
Comme prévu, seule la ligne choisie disparait. Dans le menu suivant, restent les sujets : Maintenant, il reste deux autres formes de choix :  

Options, réponses contextuelles

Client: Combien vous en voulez ?
Alt un lot, 10 Pièces
Alt deux lots, 20 Pièces
Alt dix lots, 100 Pièces
Menü

:un lot
//Nous continuons ici, quand l'utilisateur choisit 10 pièces.

:deux lots
...

:dix lots
...
Comme cela n'a pas de sens de conserver les entrées choisies dans la liste, toutes les entrées seront automatiquement effacées après le choix de l'utilisateur. Si par exemple celui-ci choisit deux lots, l'interpréteur saute à l'étiquette deux lots:
:deux lots
Client: Etes-vous sûr ?
Leni: Oui, je veux 20 pièces.
Client: Pour quand les voulez-vous ?
Alt 1, Demain
Alt 2, Après demain
Alt 3, Plus tard
Menü
Finalement, nous allons associer ces deux types :  

Contexte, Changement de sujet

Nous rencontrons une nouvelle subtilité des dialogues : Quand on a discuté d'un sujet avec son interlocuteur, et qu'il nous reste une remarque en tête, on peut choisir d'exprimer cette remarque ou de changer de sujet. Si l'on change de sujet, la remarque devient hors-contexte et inutile. On a donc la possibilité de continuer sur un thème ou de passer à un autre. Ceci a été implanté dans Dialog de la façon suivante : La remarque est insérée comme une option normale dans la liste, alors que celle-ci contient déjà des thèmes. Toutes les options suivant le choix de l'utilisateur seront supprimés, alors que les thèmes non choisis seront conservés.
Kunde: Rappelles-toi le bon vieux temps.
Alt Memoire, Ah oui, je viens juste de me souvenir, que ...
MENÜ
 

Implantation

Vous vous demandez sans doute maintenant comme on met en pratique ces trois différents concepts. Vous avez sans doute réalisé, à ce stade, que la distinction s'effectue par l'usage de NEU ou de ALT. Si l'on insert un sujet dans la liste avec l commande NEU, alors il restera dans la liste jusqu'à ce qu'il soit choisi. Inversement, si l'on insère une option dans la liste avec la commande ALT, elle sera automatiquement supprimée, choisie ou pas.  

Listes multiples

Que se passe-t-il par exemple, si l'on veut effectuer un choix alors que l'on discute un thème, et cela sans afficher les autres thèmes dans le menu ? Dans ce but, j'ai développé, non pas une, mais trois listes :

La liste 0 est recommandée pour les options. La liste 1 est destinée aux sujets généraux, par exemple famille, travail, loisirs, cuisine... Si l'on veut parler travail, et qu'il y a d'autres sujets à l'intérieur du sujet travail, on choisira la liste 2. Nous avons vu un exemple avec le dialogue avec Hale. Dans le cas ou un plus grand nombre de listes seraient nécéssaires, il faudra changer la constante dans le source de l'interpréteur.  

Comment utiliser les différentes listes ?

Au démarrage, la liste 1 est la liste courante. Avec la commande LISTE, on peut sélectionner une autre liste comme liste courante.
LISTE 0
LISTE 1
LISTE 2
Evidemment, les entrées de chaque liste ne sont pas modifiées par cette action. Les commandes NEU, ALT, MENÜ et LÖSCHEN s'appliquent toujours à la liste courante.  

Version précédente de Dialog

Dans la version précédente de Dialog, qui a été utilisée pour Würstelstand, le système de choix était légèrement différent : A la place de la commande ALT, nous ajoutions un paragraphe à la commande NEU après la virgule :
Neu Memoire, Ah oui, je viens juste de me souvenir, que ... 
La liste 0 était automatiquement effacée après le menu. Elle n'était donc utile que pour des options, et non pour les thèmes.

Je vous recommande maintenant de consulter les exemples HALE.BAT et PETER.BAT, dans lesquels les listes sont très largement utilisées.

LÖSCHEN  étiquette
efface toutes les entrées de la liste courante pointant vers étiquette. Par exemple:
LÖSCHEN famille
Pour effacer toutes les entrées de la liste courante, on ajoute un astérisque :
LÖSCHEN *
(Si désiré, le support des expressions régulières peut être ajouté ;-)

Menü affiche toutes les entrées de la liste courante, et permet à l'utilisateur de faire son choix parmi celles-ci. Après celà, l'entrée choisie et toutes les options ( ajoutées avec ALT) seront supprimées. Enfin l'interpréteur effectue un saut vers l'étiquette correspondante. Dans le cas où il ne reste plus qu'une seule entrée dans la liste, cette entrée est supposée être choisie et donc aucun choix n'est proposé. Si la liste est vide, ou si la cible est introuvable, l'interpréteur continuera à la ligne suivant MENÜ.  

Interfaces

Comment est ce que le dialogue peut réagir à son environnement, l'influencer et échanger des données avec d'autres dialogues?  

Memoire et Registres

Dans Würstelstand, chaque dialogue a accès à 256 registres. Chacun de ces registres contient un nombre compris entre -2 milliard et +3 milliard. Les 256 registres se répartissent en trois groupes :

Registres systèmes

Les 100 premiers registres (de 0 à à 99) sont résérvés par le système. Leur valeurs sont définies avant le démarrage d'un dialogue et lui permette de réagir à son environnement. Les registres marqués par //S seront présentés et utilisés à la fin du dialogue. Ils constituent le résultat du dialogue. Voici la liste des registres système de Würstelstand :
1 Event;   //Numéro d'évênement (voir texte.h)
2 geliefert; //S //0-10 nombre de dixièmes fournis
3 wtag;   //jour de la semaine
4 tag;   //jour du mois
5 monat;   //mois
6 jahr;   //année
7 Datum;   //code du jour (1.1.1997 = 0)
8 wetter;   //la météo du jour
9 konto; //S //compte bancaire
10 kapital; //S //cash
11 ausgaben; //S //dépenses du jour
12 einnahmen; //S //revenus du jour
13 sterne; //S // évaluation de la qualité du stand (0-5 étoiles)
14 wverkauf;   //Nombre de produit vendus cette semaine
15 weinnahmen;   //revenu de la semaine
16 wausgaben;   //dépense de la semaine
17 0; //S //nouveau revenu/dépense (conséquence du dialogue)
18 Nachrichtenserie;   //quelle série d'infos(0=Elch,1=...)
19 Nachricht;   //quelle info dans la série courante (0=1.Tag,1=2...)
20 LottoNr[0];   //combien de numeros cochés dans la grille(0-6)
21 LottoErgebnis[0];   //combien de numéros corrects
22 LottoGewinn[LottoErgebnis[0]];   //gains de Leni
23 S.Image; //S //Image de Leni
24 S.Override; //S //Evênement masque
25 S.wverkauf[1];   //produits vendus la semaine dernière
26 S.weinnahmen[1];   //revenus de la semaine dernière
27 S.wausgaben[1];   //dépenses de la semaine dernière
28 S.wverkauf[2];   //produits vendus il y a deux semaine
29 S.weinnahmen[2];   //revenus d'il y a deux semaine
30 S.wausgaben[2];   //dépenses d'il y a deux semaines
31 S.NOverride; //S //evênement masque de demain
32 S.wetter_bericht;   // prévision météo
33 Gesamtwert();   //Valeur totale dy stand
34 Wetterbericht[S.wetter_bericht].Ereignis;   //Evênement météo
35 Tageszeit;   //Heure du jour en minutes
70..79 Lagermenge   //stocks
80..89 Verkaufspreis //S //prix de vente
90..99 Kaufmenge //S //quantité commandée

Registre de Dialogue

Les 100 registres suivants (de 100 à 199) sont propres à chaque dialogue. Ils sont initialisés à zéro au démarrage du jeu, et leur valeur est persistente pendant toute la durée du jeu. Ces registres sont accessibles seulement par leur dialogue respectifs et sont sauvés dans les sauvegardes du jeu. Le système ne peut ni lire ni écrire dans les registres de dialogue. Il est utile de documenter au début de chaque dialogue les registres qu'il utilise et leur usage.
batch.cpp

// Client: Peter Hinzing 
// 
// Registres utilisés
//[100] Combien de fois il est venu
//[101] Argent de poche
//[102] Différents évênements
//[103] Nombre aléatoire: commande
//[104] Nombre aléatore: réponse à la commande
//[105] dialogues différents : thème travail à partir du 5ème jour
//[106] Vente
//[107] Le jeu commence. Jeu après choix.
//[108] Jeu.mise.type
//[109] Jeu.mise.quantite
//[110] Jeu.choix.Peter
//[111] Jeu.choix.Leni
//[112] Activation Hobby
//[113] Activation Maison
//[114] Dialogue à propos de Würstelstand 
//[115] stock total coke
//[116] trop cher ?*************************
//* à implanter
Dans le regisre [100] Pierre se souvient combien de fois il est déjà venu. Quand il arrive la première fois, il se présente. Lors de sa dixième arrivée, il utilise le tutoiement En [101] il gère son argent de poche,...

Mémoire partagée

Les 56 derniers registres (ils pourraient être plus nombreux, le nombre exact n'est pas important) constituent une mémoire partagée entre tous les dialogues.

Tous les dialogues voient les mêmes valeurs et tous peuvent y écrire. Il devrait y avoir un système pour réguler l'usage de ces registres. Les trois registres suivants sont utilisés par des dialogues de Würstelstand (documentés dans daten.h) :

[200]: Leni peut aller au bureau de l'immigration avec Hale.
[201]: Leni lit la circulaire sur les chiens recherchés
[202]: Leni a joué à papier caillou ciseau avec Peter. (terrible !)
 

Evênements

Un systême d'évênements a été dévéloppé pour Würstelstand. chaque évênement a un numéro unique, qui doit être défini dans un fichier de référence. Les évênements peuvent correspondre à des choses comme : Comment ça marche ?
Pour les dialogues produits, clients, téléphone et les séries d'informations, il suffit d'insérer le numéro de l'évênement dans le fichier de données correspondant et la valeur de départ/fin.

Comment déclencher des évênements ?

Aktion expression
// activation des Cheats:
Aktion 3
// activating de l'évênement, qui a été calculé dans le registre 100 :
Aktion [100]
Que faire de ces évênements ? Voici un extrait de la liste d'évênements de Würstelstand :
0 Erreur/Jamais L'Evênement 0 a été reçu sans pouvoir être géré.
1 Initialisation est déclenché au démarrage et active de nombreux produits clients, ...
2 Fin devrait être déclenché à la fin du jeu.
3 activation FW-Cheat Qui a codé ça ?!?
4 désactivation FW-Cheat Gardez le secret !
5 Leni.competition.journal L'image de Leni est suffisament bonne -> activation de l''article de journal sur la prochaine compétition
6 Leni.competition.journal->No Téléphone L'article du journal active le numéro de téléphone pour s'inscrire à la compétition.
7 Leni.competition.deactivation No Téléphone Après l'appel téléphonque et tout étant réglé, le numéro est désactivé.
8 désactivation de Hale Hale se désactive, car Leni l'a offensé.
9 Hale recommande Josi Hale active Josi dès qu'il en a parlé. (Smalltalk est important!)
10 deactivation Josi Josi se désactive
11 deactivation Peter Peter se désactive
12 Info de Sepp, sans accord de Leni Sepp offre une production illégale, Leni refuse, toute l'histoire devient publique.
13 Info de Sepp, avec accord de Leni Leni accepte la production illégale et les problèmes suivent
14 jeu perdu Le postier Gottfried annonce la fin du jeu
15 jeu gagné Gottfried réalise la valeur du stand de hot-dog et Leni gagne
16 Hale.info article droit d'asile activation Leni parle à Hale de sa famille, ce qui suscite un article de journal sur le droit d'asile.
17 Hale.info article->No téléphone activation Le journal publie le numéro qui peut maintenant être utilisé.
18 Hale-> article->No téléphone desactivation La conversation désactivates le numéro de téléphone
19 Hale->Famille activation La famille de Hale reçoit le droit d'asile.
20 activation de l'espion Leni devrait se voir recommandé un détective, mais cela n'a jamais été mis en pratique..
33 Nouveau produit 1 (Nouveau fournisseur) Cet évênement étend la gamme produit.
100 competition gagnée Leni remporte le concours, les clients vont maintenant en parler, ...
101 compétion perdue
102 Prix de Loterie Leni a gagné à la loterie
Comme on peut le voir, les évênements sont un outil très puissant pour implanter la logique du jeu.  

Calculs

Avec la commande Rechne (Rechne signifie calcule en Allemand) vous pouvez évaluer des expressions mathématiques et placer le résultat dans un registre. Par example :
Rechne [100]: 20 + [30] * 10
Le contenu du registre 30, multiplié par 10, ajouté à 20 est placé dans le registre 100.

Les opérations suivantes sont disponibles :

Opération Notation Example Solution
Parenthèse (a) (10+20)*30 900
Registre [a] [20] Le contenu du registre 20
Multiplication a*b 3*4 12
Division a/b 10/5 2
Modulo a%b 10%3 1
Addition a+b 1+1 2
Soustraction a-b 1-1 0
Accès mémoire [a]:b [10]:20 Place la valeur 20 à l'emplacement 10.
Test booléen a?b Oui(1) ou Non(0)
Test égalite a=b 10=20 Non(0)
Test inférieur a<b 10<20 Oui(1)
Test supérieur a>b [10]>[20]
ET a&b 1=1 & 2=2 Si 1 égale 1 ET 2 égale 2
OU a|b 1=1 | 2=2 Si 1 égale 1 OU 2 égale 2 ist
Nombre aléatoire a Z b 1 Z 6 retourne un nombre aléatoire entre 1 et 6

Résultat des comparaisons en nombre : 1 pour VRAI et 0 pour FAUX. Ils peuvent être également placés dans les registres. Les espaces ont autorisés dans les expressions ( 10 + 20 ), mais pas obligatoires (10+10).

Le plus grand challenge a été le développement de l'évaluateur mathématique, qui est maintenant capable de gérer des expressions de la forme :

Hypothèse : [100]=5, [24]=14, 1Z6=2

[[100]+1]:((1Z6)*([24]>3)+10/2-10%5)
[5    +1]:((2  )*(14  >3)+10/2-10%5)
[6      ]:(2    *(1        )+5   -0   )
[6      ]:(2    *1          +5        )
[6      ]:(7                          )
[6      ]:7
Solution: [6]:7 LA valeur 7 est sauvée dans le registre 6.

 

Suivi des registres

Avec la commande Wenn (Wenn signifie si en Allemand):
Wenn condition
then
on peut faire des comparaisons, par exemple :
Wenn [100+1]>10
Client: Le nombre dans le registre 101 est supérieur à 10 !
Wenn 1>1
Client: ERREUR !
Si la condition est vraie, l'interpréteur passe à la ligne suivante, sinon il passe une ligne. Cette instruction peut être utilisée en association avec des sauts:
Wenn [102]<10
Sprung PLUSPETIT
Wenn [102]=10
Sprung EGAL
Wenn [102]>10
Sprung PLUSGRAND
...
:PLUSPETIT
...
:EGAL
...
:PLUSGRAND
 

Visualiser des images

BILD expression
(Bild signifie image en allemand) Si par example
Bild 5
est contenu dans HALE.BAT, alors HALE5.DAT (un format d'image spécial) est affiché. Un click souris est attendu puis le dialogue se poursuit.  

Référence des commandes

Pour avoir une meilleure vue d'ensemble, j'ai réalisé un index des commandes :
// commentaire COMMAND REFERENCE

Client: text Le client dit quelquechose
Tel: text L'interlocuteur dit quelquechose.
Leni: text Leni dit quelquechose
:target Etiquette à laquelle l'interpréteur peut sauter
Liste number Faire d'une liste la liste courante.
Löschen * Effacer toutes les entrées de la liste courante.
Löschen target Effacer toutes les entrées de la liste courante qui pointent vers target.
Aktion number Déclenche un évênement
Ende Fin du dialogue
Bild number Affiche l'image dans le fichier NameNumber.dat
Sprung target Effectue un saut vers target
Neu target,Text Insère un nouveau sujet dans la liste courante
Alt target,Text Insère un nouveau choix dans la liste courante
Menü Affiche le menu, fait choisir le joueur, ...
Wenn condition Comparaison (voir plus loin)
//then Si vraie, l'interpréteur exécute la ligne suivante.
//else Si fausse, l'interpréteur passe la ligne suivante.
Rechne expression Evalue l'expressions dans un registre
Bild expression Affiche une image, attends un click souris
 

Désavantages du système à choix multiples.

 

Dialog Maker

Markus Muntaneau a développé un programme Delphi appelé Dialog-Maker, avec lequel le développement de dialogue devrait être simplifié. Malheureusement il n'a jamais été terminé (Il reste des bugs) et de ce fait n'est pas très utile. Je recommande cependant aux développeurs d'y jeter un coup d'oeil.

 

Astuces de programmation

Comme l'ensemble du projet Würstelstand comprend à peu près 10 000 lignes de code C(++), et que les temps de compilations étaient encore acceptables, je n'ai pas modularisé proprement le code (ok, j'admet que j'ai été paresseux). A la place, j'ai développé le système Test-Include : Le code du module est intégré dans un fichier .c, qui est exécutable séparément, et qui offre en même temps un programme de test pour les routines des modules. Ceci peut être désactivé par un #ifdef s'il est inclus à partir du module principal. Ainsi, on peut économiser des fichiers d'en-tête. ;-)

batch.cpp
#ifndef _DIALOG_H 
#define _DIALOG_H 
 
#ifndef MAIN_MODULE
  #define DIALOG_TEXT 
  #define DEBUG 
  //Ici sont les fichiers include nécessaires
  #include <stdio.h>
  //... 
#endif 
 
//Ici sont les routines du dialogue
//..
S2 Dialog(char *Filename, TYP Array[])
{
  //...
}

#ifndef MAIN_MODULE
 //Ici tout dont a besoin le programme de test
TYP Feld[256]; 
int main(short argc,char *argv[]) 
{ 
  // Programme de test
  Dialog(Filename,Feld);
} 
#endif
wurst.cpp
#define MAIN_MODULE
#include "batch.cpp"
TYP Felder[10][256];
int main(short argc,char *argv[]) 
{ 
  Dialog(Filename,Felder[i]);
}

 

Remarques finales

La version Linux de la simulation Würstelstand est disponible sur Futureware (http://poboxes.com/futureware). La version 1.1 de dialog peut être téléchargée ici (dialog-1.1.tar.gz).
En cas d'importante demande (envoyer vos emails à l'auteur), d'autres articles sur des examples pratiques de dialogues suivront.
Site Web maintenu par l´équipe d´édition LinuxFocus
© Philipp Gühring
LinuxFocus 1999
Translation information:
de -> --
de -> fr

1999-11-22, generated by lfparser version 0.7