14

Processeur de commandes

  1. typedef list shell;

Un type shell est une liste de commandes.

  1. extern shell shell_new( void );
  2. extern void shell_free( shell this );
  3. extern shell shell_help( shell this, char *input );
  4. extern void shell_inputline( shell this, char *input_line );
  5. extern void shell_input( shell this, char *prompt );
  6. extern shell shell_addcmd( shell this, char *name, char *help, void (*f)( int argc, char **argv ) );
  7. extern shell shell_delcmd( shell this, char *name );

shell_new retourne une nouvelle instance du type shell.
shell_free désalloue l'espace mémoire occupé par le shell.
shell_help affiche un résumé de toutes les commandes disponibles.
shell_inputline interprète une ligne de texte et exécute la fonction correspondante.
shell_input lit une ligne de texte, la passe à shell_inputline et affiche un message d'invite.
shell_addcmd ajoute une commande au processeur.
shell_delcmd retire une commande du processeur.

  1. typedef struct _shell_keyword {
  2.     char *name, *help;
  3.     void (*f)( int argc, char **argv );
  4. } *shell_keyword;

Le type shell_keyword définit une commande. name spécifie son nom, comment elle est appelée. help donne l'usage de la commande qui est affiché par shell_help. f est un pointeur sur la fonction appelée pour exécuter la commande.

  1. static shell_keyword shell_keyword_alloc( void ) {
  2.     return (shell_keyword)calloc( 1, sizeof ( struct _shell_keyword ));
  3. }
  4.  
  5. static shell_keyword shell_keyword_init( shell_keyword this, char *name, char *help, void (*f)( int argc, char **argv ) ) {
  6.     this->name = name;
  7.     this->help = help;
  8.     this->f = f;
  9.  
  10.     return this;
  11. }
  12.  
  13. static shell_keyword shell_keyword_new( char *name, char *help, void (*f)( int argc, char **argv ) ) {
  14.     return shell_keyword_init( shell_keyword_alloc(), name, help, f );
  15. }
  16.  
  17. static void shell_keyword_free( shell_keyword this ) {
  18.     free( this );
  19. }
  1. shell shell_alloc( void ) {
  2.     return list_alloc();
  3. }
  4.  
  5. shell shell_init( shell this ) {
  6.     return this;
  7. }
  8.  
  9. shell shell_new( void ) {
  10.     return shell_init( shell_alloc() );
  11. }
  12.  
  13. void shell_free( shell this ) {
  14.     int i, len = list_length( this );
  15.  
  16.     for ( i = 0; i < len; i++ )
  17.         shell_keyword_free( list_get( this, i ));
  18.  
  19.     list_free( this );
  20. }
  1. shell shell_help( shell this, char *input ) {
  2.     shell_keyword kw;
  3.     int i, len = list_length( this );
  4.    
  5.     unsigned lin = input ? strlen( input ) : 0;
  6.  
  7.     for ( i = 0; i < len; i++ ) {
  8.         kw = (shell_keyword)list_get( this, i );
  9.         if ( !lin || strncmp( kw->name, input, lin ) == 0 )
  10.             fprintf( stderr, "%s\n", kw->help );
  11.     }
  12.     return this;
  13. }
  1. static shell_keyword shell_match( shell this, char *input ) {
  2.     shell_keyword kw, found = 0;
  3.     int i, len = list_length( this );
  4.  
  5.     unsigned lin = input ? strlen( input ) : 0;
  6.  
  7.     for ( i = 0; i < len; i++ ) {
  8.         kw = (shell_keyword)list_get( this, i );
  9.         if ( strncmp( kw->name, input, lin ) == 0 ) {
  10.             if ( !found )
  11.                 found = kw;
  12.             else { /* ambiguous input */
  13.                 shell_help( this, input );
  14.                 return 0 ;
  15.             }
  16.         }
  17.     }
  18.     if ( !found )
  19.         shell_help( this, 0 ); /* help user */
  20.  
  21.     return found;
  22. }
  1. static int parse( char *s, char ***argv ) {
  2.     register char *p;
  3.  
  4.     int argc;
  5.     int i;
  6.  
  7.     /* count tokens */
  8.     for ( argc = 0, p = s; *p; ) {
  9.         while ( isspace( *p ))
  10.             p++;    /* skip spaces between tokens */
  11.  
  12.         if ( *p == '\0' )
  13.             break;  /* end of line */
  14.  
  15.         argc++; /* one more token */
  16.  
  17.         while ( *p && !isspace( *p ))
  18.             p++;    /* goto to end of token */
  19.     }
  20.  
  21.     /* return 0 if empty */
  22.     if ( argc == 0 )
  23.         return 0;
  24.  
  25.     /* allocate space for array of arg pointers */
  26.     *argv = (char **)calloc( argc + 1, sizeof( char * ));
  27.  
  28.     /* fill array of pointers */
  29.     for ( i = 0, p = s; *p; ) {
  30.         while ( isspace( *p ))
  31.             p++;    /* skip spaces between tokens */
  32.  
  33.         if ( *p == '\0' )
  34.             break;
  35.  
  36.         (*argv)[ i++ ] = p; /* put token address in argv */
  37.  
  38.         while ( *p && !isspace( *p ))
  39.             p++;    /* go to end of token */
  40.  
  41.         if ( *p )
  42.             *p++ = '\0';
  43.     }
  44.  
  45.     return argc;
  46. }
  1. void shell_inputline( shell this, char *input_line ) {
  2.     shell_keyword kw;
  3.  
  4.     int argc;
  5.     char **argv;
  6.  
  7.     if ( (argc = parse( input_line, &argv )) ) {
  8.         if ( (kw = shell_match( this, argv[ 0 ] ))) {
  9.             argv[ 0 ] = kw->name;   /* full name */
  10.             (kw->f)( argc, argv );
  11.         }
  12.         free( argv );
  13.     }
  14. }
  1. void shell_input( shell this, char *prompt ) {
  2.     char *input_line;
  3.  
  4.     if ( (input_line = readline( prompt )) )  {
  5.         shell_inputline( this, input_line );
  6.         free( input_line );
  7.     }
  8. }
  1. void shell_input( shell this, char *prompt ) {
  2.     char input_line[ 4096 ];
  3.  
  4.     if ( fgets( input_line, sizeof ( input_line ), stdin )) {
  5.         shell_inputline( this, input_line );
  6.  
  7.         if ( prompt ) {
  8.             fprintf( stdout, "%s", prompt );
  9.             fflush( stdout );
  10.         }
  11.     }
  12. }
  1. shell shell_addcmd( shell this, char *name, char *help, void (*f)( int argc, char **argv ) ) {
  2.     list_put( this, -1, shell_keyword_new( name, help, f ));
  3.     return this;
  4. }
  5.  
  6. shell shell_delcmd( shell this, char *name ) {
  7.     shell_keyword kw;
  8.     int i, len = list_length( this );
  9.  
  10.     for ( i = 0; i < len; i++ ) {
  11.         kw = (shell_keyword)list_get( this, i );
  12.         if ( strcmp( kw->name, name ) == 0 ) {
  13.             list_delete( this, i );
  14.             break;
  15.         }
  16.     }
  17.     return this;
  18. }
  1. #if defined( STANDALONE )
  2.  
  3. #include <time.h>
  4.  
  5. struct _app {
  6.     shell sh;
  7. } app;
  8.  
  9. static void cmd_echo( int argc, char **argv ) {
  10.     while ( --argc )
  11.         printf( argc > 1 ? "%s " : "%s\n", *++argv );
  12. }
  13.  
  14. static void cmd_date( int argc, char **argv ) {
  15.     time_t clock = time( (time_t *)0 );
  16.  
  17.     fprintf( stdout, "%s", ctime( &clock ));
  18. }
  19.  
  20. static void cmd_time( int argc, char **argv ) {
  21.     time_t clock = time( (time_t *)0 );
  22.     struct tm *tm = localtime( &clock );
  23.  
  24.     fprintf( stdout, "%02d:%02d:%02d\n", tm->tm_hour, tm->tm_min, tm->tm_sec );
  25. }
  26.  
  27. static void cmd_quit( int argc, char **argv ) {
  28.     shell_free( app.sh );
  29.     exit( 0 );
  30. }
  31.  
  32. int main( void ) {
  33.     char *prompt = "? ";
  34.  
  35.     app.sh = shell_new( );
  36.  
  37.     shell_addcmd( app.sh, "echo",   "echo word...", cmd_echo );
  38.     shell_addcmd( app.sh, "date",   "date",         cmd_date );
  39.     shell_addcmd( app.sh, "time",   "time",         cmd_time );
  40.     shell_addcmd( app.sh, "quit",   "quit | exit",  cmd_quit );
  41.     shell_addcmd( app.sh, "exit",   "exit | quit",  cmd_quit );
  42.  
  43.     setbuf( stdout, 0 );    /* interactive */
  44. #ifndef READLINE
  45.     fprintf( stdout, "%s", prompt );
  46. #endif
  47.     for ( ;; )  /* exit with a command */
  48.         shell_input( app.sh, prompt );
  49. }
  50.  
  51. #endif

Installez la librairie libreadline si nécessaire :

$ apt-get install libreadline-dev

NOTE : Si vous ne voulez pas utiliser readline, supprimez la ligne qui définit READLINE dans le fichier shell.c et n'ajoutez pas l'option -lreadline quand vous liez un programme avec le toolkit.

Pour fabriquer le programme de test, compilez list.c séparément puis shell.c avec -DSTANDALONE :

$ gcc -Wall -DDEBUG -c list.o
$ gcc -Wall -DDEBUG -DSTANDALONE -c shell.c -o test-shell.o
$ gcc test-shell.o list.o -o test-shell -lreadline

Essayez le programme :

$ ./test-shell
?

Demandez de l'aide :

? ?
echo word...
date
time
quit | exit
exit | quit
? 

Affichez la date et l'heure :

? date
Wed May 26 13:02:48 2010
? time
13:02:50
? 

Essayez une commande avec un nombre variable d'arguments :

? echo Hello my dear Frasq!
Hello my dear Frasq!
? 

Vérifiez ce qui se passe quand le nom d'une commande est ambigu :

? e
echo word...
exit | quit
? 

Quittez le programme :

? q
$ 

Commentaires

Votre commentaire :
[p] [b] [i] [u] [s] [quote] [pre] [br] [code] [url] [email] strip aide 2000

Entrez un maximum de 2000 caractères.
Améliorez la présentation de votre texte avec les balises de formatage suivantes :
[p]paragraphe[/p], [b]gras[/b], [i]italique[/i], [u]souligné[/u], [s]barré[/s], [quote]citation[/quote], [pre]tel quel[/pre], [br]à la ligne,
[url]http://www.izend.org[/url], [url=http://www.izend.org]site[/url], [email]izend@izend.org[/email], [email=izend@izend.org]izend[/email],
[code]commande[/code], [code=langage]code source en c, java, php, html, javascript, xml, css, sql, bash, dos, make, etc.[/code].