Mode d'emploi de l'interpréteur de commandes CI
Dernière mise à jour : 18/06/2006 23:17 clib
Construction de l'interpréteur de commandes CI
1 - Construire un environnement de développement pour la bibliothèque CLIB
.../clib/ed/inc .../clib/ed/src .../clib/ed/lib
2 - Récupérer les fichiers
Dans .../inc :
- ansi.itm - ascii.h - assert.h - bits.h - ci.h - ci_err.utm - cnt.h - cnt_err.itm - fstr.h - fstr_err.itm - io.h - pc_dbg.h - str.h - sys.h - sysalloc.h - tok.h - tok_err.itm - types.h
Dans .../src :
- ascii.c - assert.c - ci.c - cnt.c - fstr.c - io.c - str.c - sysalloc.c - tok.c
Les 9 fichiers sources servent éventuellement à générer la bibliothèque.
Nota : Bien sûr, on est pas obligé de générer la bibliothèque, on peut aussi ajouter ces fichiers au projet.
3 - Créer la bibliothèque
Le chemin des fichiers inclus est
.../clib
Le chemin des fichiers sources (.c) est
.../clib/ed/src
Le chemin de la sortie est
.../clib/ed/lib
La macro DBG_SYSALLOC doit être définie globalement. Typiquement (Borland C, gcc) :
... -DDBG_SYSALLOC
Générer la bibliothèque (Les détails dépendent de l'implémentation).
4 - Créer une application de test
Celle-ci est composée de 3 fichiers. Le programme principal main.c et un ensemble de fonctions appelées par le shell (ainsi que le fichier d'interface qui va avec).
/* main.c */ #include <stdio.h> #include <stdlib.h> #include "ed/inc/sysalloc.h" #include "ed/inc/sys.h" #include "ed/inc/io.h" #include "app.h" /* macros ============================================================== */ /* constants =========================================================== */ /* types =============================================================== */ /* structures ========================================================== */ /* private variables =================================================== */ /* private functions =================================================== */ /* --------------------------------------------------------------------- --------------------------------------------------------------------- */ static int cb_out (void *p_user, char const *s) { int err = 0; FILE *fp = p_user; if (fp != NULL) { fprintf (fp, "%s", s); fflush (fp); } else { err = 1; } return err; } /* --------------------------------------------------------------------- --------------------------------------------------------------------- */ static void print_prompt (void) { printf ("\n> "); fflush (stdout); } /* --------------------------------------------------------------------- --------------------------------------------------------------------- */ static void print_err (ci_err_e err) { printf ("CI.ERR: %s\n", ci_serr (err)); } /* --------------------------------------------------------------------- --------------------------------------------------------------------- */ static void test (void) { ci_s ci; static ci_cfg_s const a_cfg[] = { {"date", date_man, date_cde}, {"time", time_man, time_cde}, }; char s_user[] = "User"; ci_err_e err = ci_init (&ci, a_cfg, NELEM (a_cfg)); if (err == CI_OK) { ci_install_out (&ci, cb_out, stdout); { int end = 0; while (!end) { print_prompt (); { char *s_line = get_line (); if (s_line != NULL) { err = ci_in (&ci, s_line, s_user); end = err == CI_ERR_QUIT; FREE (s_line); if (err && !end) { print_err (err); } } else { end = 1; } } } } } else { print_err (err); } } /* --------------------------------------------------------------------- --------------------------------------------------------------------- */ static int main_ (void) { test (); return 0; } /* entry points ======================================================== */ /* --------------------------------------------------------------------- --------------------------------------------------------------------- */ int main (void) { int ret; static char Trace[1 << 8]; SYS_INIT (Trace, OFF); ret = main_ (); sys_mem_trace (); return ret; }
/* app.h */ #ifndef H_ED_APP_20050311102737 #define H_ED_APP_20050311102737 /* app.h */ #ifdef __cplusplus extern "C" { #endif #include "ed/inc/ci.h" /* macros ============================================================== */ /* constants =========================================================== */ /* types =============================================================== */ /* structures ========================================================== */ /* internal public data ================================================ */ /* internal public functions =========================================== */ /* entry points ======================================================== */ /* commands */ ci_cde_f date_cde; ci_cde_f time_cde; /* public data ========================================================= */ /* helps */ extern char const date_man[]; extern char const time_man[]; #ifdef __cplusplus } #endif #endif /* guard */ /* Guards added by GUARD (c) ED 2000-2005 Feb 07 2005 Ver. 1.7 */
/* app.c */ /* app.c */ #include "app.h" #include <stdio.h> #include <time.h> /* macros ============================================================== */ /* constants =========================================================== */ /* types =============================================================== */ /* structures ========================================================== */ /* private data ======================================================== */ /* private functions =================================================== */ /* internal public data ================================================ */ /* internal public functions =========================================== */ /* entry points ======================================================== */ /* --------------------------------------------------------------------- --------------------------------------------------------------------- */ int date_cde (int argc, char const **argv, void *p_user) { int ret = 0; if (argc == 1) { time_t now = time (NULL); struct tm tm = *localtime (&now); char s[64]; char const *s_user = p_user ? p_user : ""; strftime (s, sizeof s, "%A %d-%m-%Y (week %W)", &tm); printf ("[%s] today is %s\n", s_user, s); } else { printf ("date change ...\n"); } return ret; } /* --------------------------------------------------------------------- --------------------------------------------------------------------- */ int time_cde (int argc, char const **argv, void *p_user) { int ret = 0; if (argc == 1) { time_t now = time (NULL); struct tm tm = *localtime (&now); char s[16]; char const *s_user = p_user ? p_user : ""; strftime (s, sizeof s, "%H:%M:%S", &tm); printf (" %s\n", s); printf ("[%s] it's now %s\n", s_user, s); } else { printf ("time change ...\n"); } return ret; } /* public data ========================================================= */ char const date_man[] = { "[yyyy[ mm[ dd]]]\n" }; char const time_man[] = { "[hh[ mm[ ss]]]\n" };
La macro DBG_SYSALLOC doit être définie globalement.
Une fois lancé, ce code produit :
SYSALLOC Overload (10 rec) SYSALLOC Successful initialization: 10 records available >
On peut ensuite passer des commandes :
SYSALLOC Overload (10 rec) SYSALLOC Successful initialization: 10 records available > help command list help quit date time > date [User] today is Friday 16-06-2006 (week 24) > time 11:18:56 [User] it's now 11:18:56 > quit SYSALLOC min=4294967295 max=4294967295 delta=0 SYSALLOC All-matched SYSALLOC Released Memory Press ENTER to continue.
Utilisation de l'interpréteur de commandes CI
Fonctionnement
Un interpréteur de commande est un mécanisme qui exécute une action en fonction d'une commande fournie sous forme textuelle. La ligne de commande est une chaine de caractères qui peut comporter la commande suivie ou non de paramètres séparés par un espace.
"commande" "commande parametre ..."
L'action est implémentée par un appel de fonction au format classique et universel :
int action (int argc, char **argv);
Le principe est de définir une liste de commandes composée
- du nom de la commande en minuscules
- d'une chaine d'aide
- du nom de fonction associée
static ci_cfg_s const a_cfg[] = { {"date", date_man, date_cde}, {"time", time_man, time_cde}, };
Le système a 2 commandes internes préétablies : "quit" et "help".
Restrictions
Les commandes préétablies ne sont pas configurables.
Construire une application pas à pas
Le code minimum
Ce code minimum montre comment on construit l'interpreteur de commande avec ses 2 commandes intégrées 'help' et 'quit'. Dans un premier temps, il suffit de créer une structure ci_s et de l'initialiser avec ci_init() :
/* ci_01.c */ #include "ed/inc/ci.h" #include <stdio.h> /* macro permettant l'affichage des erreurs */ #define PERR(e)\ do\ {\ if (err != CI_OK)\ {\ fprintf (stderr,\ "ERR : %s(%d) at %s:%d\n"\ , ci_serr(err)\ , err\ , __FILE__\ , __LINE__\ );\ }\ }\ while (0) int main (void) { ci_s ci; ci_err_e err; err = ci_init (&, NULL, 0); PERR(err); return 0; }
Ce n'est pas très spectaculaire, mais ça fonctionne. Comme il n'y a pas de boucle, le programme s'arrête tout de suite. Normal.
On ajoute maintenant une application minimaliste comprenant :
- Une boucle applicative incluant :
- L'affichage d'un 'prompt'
- Une lecture de ligne (saisie de la commande)
- un appel à la fonction de traitement de la commande
/* ci_02.c */ #include "ed/inc/ci.h" #include "ed/inc/io.h" #include "ed/inc/sysalloc.h" /* free */ #include <stdio.h> /* macro permettant l'affichage des erreurs */ #define PERR(e)\ do\ {\ if (err != CI_OK)\ {\ fprintf (stderr,\ "ERR : %s(%d) at %s:%d\n"\ , ci_serr(err)\ , err\ , __FILE__\ , __LINE__\ );\ }\ }\ while (0) int main (void) { ci_s ci; ci_err_e err; /* initialisation */ err = ci_init (&, NULL, 0); PERR(err); /* boucle applicative */ { int end = 0; do { /* prompt */ printf ("> "); fflush (stdout); /* lecture d'une ligne */ { char *line = get_line(); if (line != NULL) { /* passage de la ligne à l'interpreteur de commande */ err = ci_in(&ci, line, NULL); PERR(err); /* detection de la fin */ end = err == CI_ERR_QUIT; } free (line), line = NULL; } } while (!end); } return 0; }
avec un petit exemple d'exécution :
> sdsdsd > help > quit ERR : quit command(5) at ci_02.c:49 Press ENTER to continue.
On constate que la commande inconnue ne provoque pas de reaction, de même que la commande d'aide (help). C'est du au fait que le module CI n'a pas, en interne, de fonction de sortie. C'est à l'utilisateur de fournir cette fonction au module. Ceci permet de répondre à tous les cas (la sortie ne se fait pas forcément sur stdout, elle peut se faire dans un port série, un socket etc.).
Nous allons donc compléter le code avec une fonction de sortie sur stdout :
/* ci_03.c */ #include "ed/inc/ci.h" #include "ed/inc/io.h" #include "ed/inc/sysalloc.h" /* free */ #include <stdio.h> /* macro permettant l'affichage des erreurs */ #define PERR(e)\ do\ {\ if (err != CI_OK)\ {\ fprintf (stderr,\ "ERR : %s(%d) at %s:%d\n"\ , ci_serr(err)\ , err\ , __FILE__\ , __LINE__\ );\ }\ }\ while (0) /* definition d'une fonction de sortie de type ci_out_f */ static int ci_out_cb (void *p_user, char const *s) { int ret = 0; printf ("%s", s); fflush (stdout); return ret; } int main (void) { ci_s ci; ci_err_e err; /* initialisation */ err = ci_init (&ci, NULL, 0); PERR(err); /* installation de la fonction de sortie */ err = ci_install_out (&, ci_out_cb, NULL); PERR(err); /* boucle applicative */ { int end = 0; do { printf ("> "); fflush (stdout); { char *line = get_line(); if (line != NULL) { err = ci_in(&ci, line, NULL); PERR(err); end = err == CI_ERR_QUIT; } free (line), line = NULL; } } while (!end); } return 0; }
avec un petit exemple d'exécution :
> sdsdsd > help command list help quit > quit ERR : quit command(5) at ci_03.c:58 Press ENTER to continue.
C'est terminé pour la partie 'minimale'. Nous allons ensuite aborder pas à pas le cas d'une application réelle.
Une application réelle
Nous allons définir une commande qui montre la version de l'application. Si on ajoute un paramètre 'lib', elle montre en plus la version des bibliothèques :
Commande ver[ lib] Paramètres lib : optionnel Comportement Affiche la version de l'application Le paramètre 'lib' provoque en plus l'affichage des bibliothèques.
Dans un premier temps, création d'une chaine "mode d'emploi" ou "aide" qui reprend l'essentiel de la spécification de la commande...
/* definition du mode d'emploi de la commande 'ver'.*/ static char const help_ver[] = "ver[ lib]\n" ;
puis création de la fonction de traitement. Un peu de code est placé dedans pour montrer le comportement des paramètres.
/* definition d'une fonction de traitement de commande de type ci_cde_f pour traiter la commande 'ver' */ static int cde_ver_cb (int argc, char const **argv, void *p_user) { int ret = 0; /* quelques lignes pour monter le comportement des parametres (debug) */ printf ("argc = %d\n", argc); { int i; for (i = 0; i < argc; i++) { printf ("argv[%d] = '%s'\n", i, argv[i]); } } return ret; }
Enfin, integration de ces deux éléments dans le tableau de configuration. Son adresse et son nombre d'elements sont alors passés à ci_init() à la place de NULL et 0 :
/* ci_04.c */ #include "ed/inc/ci.h" #include "ed/inc/io.h" #include "ed/inc/sysalloc.h" /* free */ #include "ed/inc/sys.h" /* NELEM */ #include <stdio.h> #include <string.h> /* version de l'application */ #define VER "0.4" /* macro permettant l'affichage des erreurs */ #define PERR(e)\ do\ {\ if (err != CI_OK)\ {\ fprintf (stderr,\ "ERR : %s(%d) at %s:%d\n"\ , ci_serr(err)\ , err\ , __FILE__\ , __LINE__\ );\ }\ }\ while (0) /* definition du mode d'emploi de la commande 'ver'. La premiere ligne affiche automatiquement le nom de la commande. On peut ajouter les parametres et une ou des lignes ligne supplementaires pour illustrer... */ static char const help_ver[] = "[lib]\n" "Donne la version de l'application\n" "lib : liste aussi les versions des bibliotheques\n" ; /* definition d'une fonction de traitement de commande de type ci_cde_f pour traiter la commande 'ver' */ static int cde_ver_cb (int argc, char const **argv, void *p_user) { int ret = 0; /* quelques lignes pour montrer le comportement des parametres (debug) */ printf ("argc = %d\n", argc); { int i; for (i = 0; i < argc; i++) { printf ("argv[%d] = '%s'\n", i, argv[i]); } } return ret; } /* definition d'une fonction de sortie de type ci_out_f */ static int ci_out_cb (void *p_user, char const *s) { int ret = 0; printf ("%s", s); fflush (stdout); return ret; } int main (void) { ci_s ci; ci_err_e err; /* tableau de structure de configuration (pour le moment, un seule commande) */ static ci_cfg_s const a_cfg[] = { {"ver", help_ver, cde_ver_cb}, }; /* initialisation */ err = ci_init (&ci, a_cfg, NELEM(a_cfg)); PERR(err); err = ci_install_out (&ci, ci_out_cb, NULL); PERR(err); /* boucle applicative */ { int end = 0; do { printf ("> "); fflush (stdout); { char *line = get_line(); if (line != NULL) { err = ci_in(&ci, line, NULL); PERR(err); end = err == CI_ERR_QUIT; } free (line), line = NULL; } } while (!end) ; } return 0; }
Nous pouvons constater que la commande est maintenant prise en compte dans la liste
> help command list help quit ver >
Nous pouvons aussi demander une aide spécifique :
> help ver USAGE ver [lib] Donne la version de l'application lib : liste aussi les versions des bibliotheques >
Voici le résultat de quelques essais de commande 'ver' et de paramètres'
> ver argc = 1 argv[0] = 'ver' > ver xx argc = 2 argv[0] = 'ver' argv[1] = 'xx' > ver xx yy argc = 3 argv[0] = 'ver' argv[1] = 'xx' argv[2] = 'yy' >
D'autre part, maintenant qu'un fichier de configuration est installé, le module réagit de façon plus explicite à certaines erreurs :
> xxx ERR : unknown command(8) at ci_04.c:95 > ERR : no command(6) at ci_04.c:95 >
Nous allons maintenant modifier la fonction de traitement pour qu'elle fasse exactement ce qu'on attend d'elle :
/* ci_05.c */ #include "ed/inc/ci.h" #include "ed/inc/io.h" #include "ed/inc/sysalloc.h" /* free */ #include "ed/inc/sys.h" /* NELEM */ #include <stdio.h> #include <string.h> /* version de l'application */ #define VER "0.5" /* macro permettant l'affichage des erreurs */ #define PERR(e)\ do\ {\ if (err != CI_OK)\ {\ fprintf (stderr,\ "ERR : %s(%d) at %s:%d\n"\ , ci_serr(err)\ , err\ , __FILE__\ , __LINE__\ );\ }\ }\ while (0) /* definition du mode d'emploi de la commande 'ver'. La premiere ligne affiche automatiquement le nom de la commande. On peut ajouter les parametres et une ou des lignes ligne supplementaires pour illustrer... */ static char const help_ver[] = "[lib]\n" "Donne la version de l'application\n" "lib : liste aussi les versions des bibliotheques\n" ; /* definition d'une fonction de traitement de commande de type ci_cde_f pour traiter la commande 'ver' */ static int cde_ver_cb (int argc, char const **argv, void *p_user) { int ret = 0; /* afficher la version de l'application */ printf ("VER = %s\n", VER); /* si il y a un parametre */ if (argc > 1) { /* et que ce parametre est 'lib' */ if (strcmp (argv[1], "lib") == 0) { printf (" bibliotheques :\n"); printf (" %-20s%s\n", ci_sid(), ci_sver()); printf (" %-20s%s\n", io_sid(), io_sver()); } } return ret; } /* definition d'une fonction de sortie de type ci_out_f */ static int ci_out_cb (void *p_user, char const *s) { int ret = 0; printf ("%s", s); fflush (stdout); return ret; } int main (void) { ci_s ci; ci_err_e err; /* tableau de structure de configuration (pour le moment, un seule commande) */ static ci_cfg_s const a_cfg[] = { {"ver", help_ver, cde_ver_cb}, }; /* initialisation */ err = ci_init (&ci, a_cfg, NELEM(a_cfg)); PERR(err); err = ci_install_out (&ci, ci_out_cb, NULL); PERR(err); /* boucle applicative */ { int end = 0; do { printf ("> "); fflush (stdout); { char *line = get_line(); if (line != NULL) { err = ci_in(&ci, line, NULL); PERR(err); end = err == CI_ERR_QUIT; } free (line), line = NULL; } } while (!end) ; } return 0; }
Ce qui donne :
> ver VER = 0.5 >
et :
> ver lib VER = 0.5 bibliotheques : CI 1.4 IO (c) ED 2003-2005 1.2 >
Nota : la cas du paramètre inconnu n'est pas signalé. Une modification triviale est faisable. Il suffit d'ajouter le traitement adéquant dans la bonne branche. On en profite pour modifier le compte rendu en 1 pour signifier 'erreur de parametre'.
/* ci_06.c */ #include "ed/inc/ci.h" #include "ed/inc/io.h" #include "ed/inc/sysalloc.h" /* free */ #include "ed/inc/sys.h" /* NELEM */ #include <stdio.h> #include <string.h> /* version de l'application */ #define VER "0.6" /* macro permettant l'affichage des erreurs */ #define PERR(e)\ do\ {\ if (err != CI_OK)\ {\ fprintf (stderr,\ "ERR : %s(%d) at %s:%d\n"\ , ci_serr(err)\ , err\ , __FILE__\ , __LINE__\ );\ }\ }\ while (0) /* definition du mode d'emploi de la commande 'ver'. La premiere ligne affiche automatiquement le nom de la commande. On peut ajouter les parametres et une ou des lignes ligne supplementaires pour illustrer... */ static char const help_ver[] = "[lib]\n" "Donne la version de l'application\n" "lib : liste aussi les versions des bibliotheques\n" ; /* definition d'une fonction de traitement de commande de type ci_cde_f pour traiter la commande 'ver' */ static int cde_ver_cb (int argc, char const **argv, void *p_user) { int ret = 0; /* afficher la version de l'application */ printf ("VER = %s\n", VER); /* si il y a un parametre */ if (argc > 1) { /* et que ce parametre est 'lib' */ if (strcmp (argv[1], "lib") == 0) { printf (" bibliotheques :\n"); printf (" %-20s%s\n", ci_sid(), ci_sver()); printf (" %-20s%s\n", io_sid(), io_sver()); } else { printf ("parametre inconnu\n"); ret = 1; } } return ret; } /* definition d'une fonction de sortie de type ci_out_f */ static int ci_out_cb (void *p_user, char const *s) { int ret = 0; printf ("%s", s); fflush (stdout); return ret; } int main (void) { ci_s ci; ci_err_e err; /* tableau de structure de configuration (pour le moment, un seule commande) */ static ci_cfg_s const a_cfg[] = { {"ver", help_ver, cde_ver_cb}, }; /* initialisation */ err = ci_init (&ci, a_cfg, NELEM(a_cfg)); PERR(err); err = ci_install_out (&ci, ci_out_cb, NULL); PERR(err); /* boucle applicative */ { int end = 0; do { printf ("> "); fflush (stdout); { char *line = get_line(); if (line != NULL) { err = ci_in(&ci, line, NULL); PERR(err); end = err == CI_ERR_QUIT; } free (line), line = NULL; } } while (!end) ; } return 0; }
Ce qui donne :
> ver xxx VER = 0.6 parametre inconnu ERR : command parameter error(7) at ci_06.c:111 >
© Emmanuel Delahaye 1995-2004 | emmanuel dot delahaye at gmail dot com | Haut | Up | Home | Forum | Livre d'or