1
/* Copyright (C) 1995 Bjoern Beutel. */
3
/* Description. =============================================================*/
5
/* Tools for a command line interpreter. */
7
/* Includes. ================================================================*/
24
/* Types. ===================================================================*/
26
typedef struct /* An alias. */
30
string_t line; /* Command line this alias stands for. */
33
/* Global variables. ========================================================*/
36
/* The options that can be set by the set command.
37
* This has to be set before executing "set_command". */
39
bool_t leave_program; /* Set to TRUE if program is to leave. */
40
bool_t leave_command_loop; /* Set to TRUE if command loop is to leave. */
42
/* Variables. ===============================================================*/
44
static command_t **commands_local;
45
/* Local copy of command table for "help_command". */
47
static list_t alias_list;
49
/* Functions for user interrupts. ===========================================*/
53
set_user_break( int signal_number )
54
/* Signal handler for notification that user wants to interrupt a command. */
56
user_break_requested = TRUE;
57
signal( SIGINT, set_user_break );
63
set_user_break( DWORD signal_type )
64
/* Signal handler for notification that user wants to interrupt a command. */
66
if (signal_type == CTRL_C_EVENT)
68
user_break_requested = TRUE;
75
/*---------------------------------------------------------------------------*/
78
check_user_break( void )
79
/* Abort command execution if user has sent an interrupt signal. */
81
if (user_break_requested)
83
user_break_requested = FALSE;
84
complain( "User interrupt." );
88
/* Functions for reading and executing commands. ============================*/
91
find_command( string_t command_name, command_t *commands[] )
92
/* Find the entry for COMMAND_NAME in COMMANDS and return it.
93
* Return NULL if COMMAND_NAME can't be found. */
99
for (i = 0; commands[i] != NULL; i++)
101
names = commands[i]->names;
102
while (*names != EOS)
104
name = parse_word( &names );
105
found_command = (strcmp_no_case( command_name, name ) == 0);
114
/*---------------------------------------------------------------------------*/
117
execute_command( string_t command_line, string_t command_type,
118
command_t *commands[], bool_t use_aliases )
119
/* Execute COMMAND_LINE using COMMANDS.
120
* If there is no such command, print an error.
121
* COMMAND_TYPE may be "command" or "option" and is used for error messages.
122
* If USE_ALIASES == TRUE, look for the command in the list of aliases. */
124
string_t line, command_name;
125
string_t alias_line, line_p, name_readable;
127
bool_t command_found;
130
/* Divide command line into strings COMMAND and ARGUMENTS. */
131
line = command_name = NULL;
134
line_p = line = replace_vars_in_string( command_line );
135
parse_whitespace( &line_p );
138
command_name = parse_word( &line_p );
139
command_found = FALSE;
140
user_break_requested = FALSE;
141
commands_local = commands;
142
command = find_command( command_name, commands );
143
if (command != NULL) /* Execute the command. */
145
command_found = TRUE;
146
command->command( line_p );
148
else if (use_aliases)
150
FOREACH( alias, alias_list )
152
if (strcmp_no_case( alias->name, command_name ) == 0)
154
command_found = TRUE;
155
alias_line = replace_arguments( alias->line, "a", line_p );
156
execute_command( alias_line, command_type, commands, FALSE );
157
free_mem( &alias_line );
164
name_readable = new_string_readable( command_name, NULL );
165
free_mem( &command_name );
166
command_name = name_readable;
167
complain( "Unknown %s %s.", command_type, command_name );
173
free_mem( &command_name );
179
/*---------------------------------------------------------------------------*/
182
execute_command_file( string_t command_file, string_t line_header,
183
command_t *commands[] )
184
/* Execute commands in COMMAND_FILE using COMMANDS.
185
* If LINE_HEADER != NULL, command lines must start with LINE_HEADER. */
187
FILE *command_stream;
188
string_t command_line, include_file, header, command_line_p;
189
bool_t is_command_line;
190
volatile int_t line_count;
191
volatile bool_t leave_loop;
193
command_stream = open_stream( command_file, "r" );
198
command_line = header = NULL;
201
/* Read a command line */
202
command_line = read_line( command_stream );
203
if (command_line == NULL)
207
cut_comment( command_line );
209
command_line_p = command_line;
210
if (*command_line_p == EOS)
211
is_command_line = FALSE;
212
else if (line_header == NULL)
213
is_command_line = TRUE;
216
/* Check if current line begins with LINE_HEADER. */
217
header = parse_word( &command_line_p );
218
is_command_line = FALSE;
219
if (strcmp_no_case( header, line_header ) == 0)
220
is_command_line = TRUE;
221
else if (strcmp_no_case( header, "include:" ) == 0)
223
include_file = parse_absolute_path( &command_line_p,
225
parse_end( &command_line_p );
226
execute_command_file( include_file, line_header, commands );
227
free_mem( &include_file );
231
execute_command( command_line_p, "command", commands, TRUE );
236
print_text( error_text, " (\"%s\", line %d)",
237
name_in_path( command_file ), line_count );
242
free_mem( &command_line );
246
close_stream( &command_stream, command_file );
249
/*---------------------------------------------------------------------------*/
252
command_loop( string_t prompt, command_t *commands[] )
253
/* Read user commands, look for them in COMMANDS and execute them
254
* until a command function returns FALSE. */
256
string_t command_line;
258
/* Install a Ctrl-C handler. */
260
signal( SIGINT, set_user_break );
263
static bool_t user_break_installed;
265
if (! user_break_installed)
266
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) set_user_break, TRUE );
267
user_break_installed = TRUE;
270
/* Repeat the command loop. */
271
while (! leave_command_loop && ! leave_program)
272
{ /* Read command line. */
276
printf( "%s> ", prompt );
277
command_line = read_line( stdin );
278
if (command_line != NULL)
279
execute_command( command_line, "command", commands, TRUE );
283
printf( "%s\n", error_text->buffer );
287
free_mem( &command_line );
290
leave_command_loop = FALSE;
293
/*---------------------------------------------------------------------------*/
297
/* Free all currently defined aliases. */
301
FOREACH_FREE( alias, alias_list )
303
free_mem( &alias->name );
304
free_mem( &alias->line );
308
/* General commands. ========================================================*/
311
print_help_summary( command_t *commands[] )
312
/* Print names of all commands in COMMANDS. */
315
string_t names, name;
317
for (i = 0; commands[i] != NULL; i++)
319
names = commands[i]->names;
320
name = parse_word( &names );
321
if (i > 0 && i % 5 == 0)
323
printf( "%s", name );
324
for (j = strlen( name ); j < 15; j++)
331
/*---------------------------------------------------------------------------*/
334
print_help( string_t command_type, command_t *command )
335
/* Print help about COMMAND,
336
* which is of COMMAND_TYPE (either "command" or "option"). */
338
string_t names, name;
340
names = command->names;
342
/* Print full name. */
343
name = parse_word( &names );
344
printf( "%s \"%s\"", command_type, name );
347
/* Print shortcuts. */
348
while (*names != EOS)
350
name = parse_word( &names );
351
printf( ", \"%s\"", name );
355
/* Print help text. */
356
printf( ":\n%s", command->help );
359
/*---------------------------------------------------------------------------*/
362
do_help( string_t arguments )
363
/* Give help on commands. */
365
string_t command_name;
366
command_t *command, *option;
368
if (*arguments == EOS)
370
printf( "Commands available: "
371
"(Use \"help COMMAND\" to get help about COMMAND.)\n" );
372
print_help_summary( commands_local );
373
printf( "\nOptions available: "
374
"(Use \"help OPTION\" to get help about OPTION.)\n" );
375
print_help_summary( options );
379
command_name = parse_word( &arguments );
380
if (strcmp_no_case( command_name, "command" ) == 0)
382
free_mem( &command_name );
383
command_name = parse_word( &arguments );
384
parse_end( &arguments );
385
command = find_command( command_name, commands_local );
388
complain( "\"%s\" is no command.", command_name );
390
else if (strcmp_no_case( command_name, "option" ) == 0)
392
free_mem( &command_name );
393
command_name = parse_word( &arguments );
395
option = find_command( command_name, options );
397
complain( "\"%s\" is no option.", command_name );
401
command = find_command( command_name, commands_local );
402
option = find_command( command_name, options );
403
if (command != NULL && option != NULL)
405
complain( "Use \"help command %s\" or \"help option %s\",",
406
command_name, command_name );
408
else if (command == NULL && option == NULL)
409
complain( "\"%s\" is neither a command nor an option.", command_name );
411
parse_end( &arguments );
413
print_help( "Command", command );
415
print_help( "Option", option );
416
free_mem( &command_name );
420
command_t help_command =
424
" help command COMMAND -- Print help about COMMAND.\n"
425
" help option OPTION -- Print help about OPTION.\n"
426
" help COMMAND_OR_OPTION -- Print help about COMMAND_OR_OPTION if unique.\n"
427
" help -- get a list of all available commands and options\n"
430
/*---------------------------------------------------------------------------*/
433
do_quit( string_t arguments )
434
/* Quit the program. */
436
parse_end( &arguments );
437
leave_program = TRUE;
440
command_t quit_command =
442
"quit exit q", do_quit,
443
"Leave the program.\n"
447
/*---------------------------------------------------------------------------*/
450
do_get( string_t arguments )
451
/* Get setting for ARGUMENTS. */
456
if (*arguments == EOS)
458
for (i = 0; options[i] != NULL; i++)
459
options[i]->command( "" );
463
option = parse_word( &arguments );
464
parse_end( &arguments );
465
execute_command( option, "option", options, FALSE );
470
command_t get_command =
473
"Query program settings.\n"
475
" get OPTION -- Print the setting of OPTION.\n"
476
" get -- Print all settings.\n"
479
/*---------------------------------------------------------------------------*/
482
do_set( string_t arguments )
485
string_t option_arguments, option;
487
option_arguments = arguments;
488
option = parse_word( &arguments );
489
if (*arguments == EOS)
490
complain( "Missing arguments to set \"%s\".", option );
491
execute_command( option_arguments, "option", options, FALSE );
495
command_t set_command =
498
"Change program settings.\n"
500
" set OPTION ARGUMENT -- Change value of OPTION to ARGUMENT.\n"
503
/*---------------------------------------------------------------------------*/
505
static command_t *set_commands[] = {&set_command, NULL};
506
/* The commands that can be called during initialisation. */
509
execute_set_commands( string_t file_name, string_t prefix )
510
/* Execute set commands in file FILE_NAME that are prefixed with PREFIX. */
512
execute_command_file( file_name, prefix, set_commands );
515
/*---------------------------------------------------------------------------*/
518
do_alias_option( string_t arguments )
523
if (*arguments == EOS)
525
if (alias_list.first == NULL)
526
printf( "alias: (none)\n" );
529
FOREACH( alias, alias_list )
531
line = new_string_readable( alias->line, NULL );
532
printf( "alias \"%s\": %s\n", alias->name, line );
539
name = parse_word( &arguments );
541
/* Find the alias NAME. */
542
FOREACH( alias, alias_list )
544
if (strcmp_no_case( alias->name, name ) == 0)
548
if (*arguments == EOS)
550
/* Delete the alias. */
552
complain( "Alias \"%s\" not defined." );
553
free_mem( &alias->name );
554
free_mem( &alias->line );
555
free_node( &alias_list, (list_node_t *) alias );
559
/* Define an alias. */
560
line = parse_word( &arguments );
561
parse_end( &arguments );
562
if (alias == NULL) /* Create alias entry if it doesn't exist. */
564
alias = new_node( &alias_list, sizeof( alias_t ), LIST_END );
565
alias->name = new_string( name, NULL );
568
free_mem( &alias->line );
575
command_t alias_option =
577
"alias", do_alias_option,
578
"Define command line aliases.\n"
580
" alias NAME LINE -- Define alias NAME for LINE.\n"
581
" alias NAME -- Undefine alias NAME.\n"
582
"The LINE may contain the following special sequence:\n"
583
" %a -- The command line arguments when invoking the alias.\n"
587
/* End of file. =============================================================*/