Command processor
- typedef list shell;
shell
is a list of commands.
- extern shell shell_new( void );
- extern void shell_free( shell this );
- extern shell shell_help( shell this, char *input );
- extern void shell_inputline( shell this, char *input_line );
- extern void shell_input( shell this, char *prompt );
- extern shell shell_addcmd( shell this, char *name, char *help, void (*f)( int argc, char **argv ) );
- extern shell shell_delcmd( shell this, char *name );
shell_new
returns a new instance of the type shell.
shell_free
deallocates the memory space occupied by a shell.
shell_help
displays a summary of all the available commands.
shell_inputline
interprets a line of text and runs the corresponding function.
shell_input
reads a line of text, passes it to shell_inputline
and displays a message prompt.
shell_addcmd
adds a command to the processor.
shell_delcmd
removes a command from the processor.
- typedef struct _shell_keyword {
- char *name, *help;
- void (*f)( int argc, char **argv );
- } *shell_keyword;
The type shell_keyword
defines a command. name
defines its name, how it's called. help
is the command usage which is displayed by shell_help
. f
is a pointer to the function called to run the command.
- } *shell_keyword;
- static shell_keyword shell_keyword_alloc( void ) {
- return (shell_keyword)calloc( 1, sizeof ( struct _shell_keyword ));
- }
- static shell_keyword shell_keyword_init( shell_keyword this, char *name, char *help, void (*f)( int argc, char **argv ) ) {
- this->name = name;
- this->help = help;
- this->f = f;
- return this;
- }
- static shell_keyword shell_keyword_new( char *name, char *help, void (*f)( int argc, char **argv ) ) {
- return shell_keyword_init( shell_keyword_alloc(), name, help, f );
- }
- static void shell_keyword_free( shell_keyword this ) {
- free( this );
- }
- shell shell_alloc( void ) {
- return list_alloc();
- }
- shell shell_init( shell this ) {
- return this;
- }
- shell shell_new( void ) {
- return shell_init( shell_alloc() );
- }
- void shell_free( shell this ) {
- int i, len = list_length( this );
- for ( i = 0; i < len; i++ )
- shell_keyword_free( list_get( this, i ));
- list_free( this );
- }
- shell shell_help( shell this, char *input ) {
- shell_keyword kw;
- int i, len = list_length( this );
- unsigned lin = input ? strlen( input ) : 0;
- for ( i = 0; i < len; i++ ) {
- kw = (shell_keyword)list_get( this, i );
- if ( !lin || strncmp( kw->name, input, lin ) == 0 )
- fprintf( stderr, "%s\n", kw->help );
- }
- return this;
- }
- static shell_keyword shell_match( shell this, char *input ) {
- shell_keyword kw, found = 0;
- int i, len = list_length( this );
- unsigned lin = input ? strlen( input ) : 0;
- for ( i = 0; i < len; i++ ) {
- kw = (shell_keyword)list_get( this, i );
- if ( strncmp( kw->name, input, lin ) == 0 ) {
- if ( !found )
- found = kw;
- else { /* ambiguous input */
- shell_help( this, input );
- return 0 ;
- }
- }
- }
- if ( !found )
- shell_help( this, 0 ); /* help user */
- return found;
- }
- static int parse( char *s, char ***argv ) {
- register char *p;
- int argc;
- int i;
- /* count tokens */
- for ( argc = 0, p = s; *p; ) {
- while ( isspace( *p ))
- p++; /* skip spaces between tokens */
- if ( *p == '\0' )
- break; /* end of line */
- argc++; /* one more token */
- while ( *p && !isspace( *p ))
- p++; /* goto to end of token */
- }
- /* return 0 if empty */
- if ( argc == 0 )
- return 0;
- /* allocate space for array of arg pointers */
- *argv = (char **)calloc( argc + 1, sizeof( char * ));
- /* fill array of pointers */
- for ( i = 0, p = s; *p; ) {
- while ( isspace( *p ))
- p++; /* skip spaces between tokens */
- if ( *p == '\0' )
- break;
- (*argv)[ i++ ] = p; /* put token address in argv */
- while ( *p && !isspace( *p ))
- p++; /* go to end of token */
- if ( *p )
- *p++ = '\0';
- }
- return argc;
- }
- void shell_inputline( shell this, char *input_line ) {
- shell_keyword kw;
- int argc;
- char **argv;
- if ( (argc = parse( input_line, &argv )) ) {
- if ( (kw = shell_match( this, argv[ 0 ] ))) {
- argv[ 0 ] = kw->name; /* full name */
- (kw->f)( argc, argv );
- }
- free( argv );
- }
- }
- void shell_input( shell this, char *prompt ) {
- char *input_line;
- if ( (input_line = readline( prompt )) ) {
- shell_inputline( this, input_line );
- free( input_line );
- }
- }
- void shell_input( shell this, char *prompt ) {
- char input_line[ 4096 ];
- if ( fgets( input_line, sizeof ( input_line ), stdin )) {
- shell_inputline( this, input_line );
- if ( prompt ) {
- fprintf( stdout, "%s", prompt );
- fflush( stdout );
- }
- }
- }
- shell shell_addcmd( shell this, char *name, char *help, void (*f)( int argc, char **argv ) ) {
- list_put( this, -1, shell_keyword_new( name, help, f ));
- return this;
- }
- shell shell_delcmd( shell this, char *name ) {
- shell_keyword kw;
- int i, len = list_length( this );
- for ( i = 0; i < len; i++ ) {
- kw = (shell_keyword)list_get( this, i );
- if ( strcmp( kw->name, name ) == 0 ) {
- list_delete( this, i );
- break;
- }
- }
- return this;
- }
- #if defined( STANDALONE )
- #include <time.h>
- struct _app {
- shell sh;
- } app;
- static void cmd_echo( int argc, char **argv ) {
- while ( --argc )
- printf( argc > 1 ? "%s " : "%s\n", *++argv );
- }
- static void cmd_date( int argc, char **argv ) {
- time_t clock = time( (time_t *)0 );
- fprintf( stdout, "%s", ctime( &clock ));
- }
- static void cmd_time( int argc, char **argv ) {
- time_t clock = time( (time_t *)0 );
- struct tm *tm = localtime( &clock );
- fprintf( stdout, "%02d:%02d:%02d\n", tm->tm_hour, tm->tm_min, tm->tm_sec );
- }
- static void cmd_quit( int argc, char **argv ) {
- shell_free( app.sh );
- exit( 0 );
- }
- int main( void ) {
- char *prompt = "? ";
- app.sh = shell_new( );
- shell_addcmd( app.sh, "echo", "echo word...", cmd_echo );
- shell_addcmd( app.sh, "date", "date", cmd_date );
- shell_addcmd( app.sh, "time", "time", cmd_time );
- shell_addcmd( app.sh, "quit", "quit | exit", cmd_quit );
- shell_addcmd( app.sh, "exit", "exit | quit", cmd_quit );
- setbuf( stdout, 0 ); /* interactive */
- #ifndef READLINE
- fprintf( stdout, "%s", prompt );
- #endif
- for ( ;; ) /* exit with a command */
- shell_input( app.sh, prompt );
- }
- #endif
Install the libreadline
library if necessary:
$ apt-get install libreadline-dev
NOTE: If you don't want to use readline
, delete the line which defines READLINE in the file shell.c and don't add the option -lreadline
when linking a program with the toolkit.
To build the test program, compile list.c separately then shell.c with -DSTANDALONE
:
$ gcc -Wall -c list.o
$ gcc -Wall -DDEBUG -DSTANDALONE -c shell.c -o test-shell.o
$ gcc test-shell.o list.o -o test-shell -lreadline
Try the program:
$ ./test-shell
?
Ask for help:
? ?
echo word...
date
time
quit | exit
exit | quit
?
Display the date and the time:
? date
Wed May 26 13:02:48 2010
? time
13:02:50
?
Try a command with a variable number of arguments:
? echo Hello my dear Frasq!
Hello my dear Frasq!
?
Check what happens when the name of a command is ambiguous:
? e
echo word...
exit | quit
?
Terminate the program:
? q
$
Comments