1 /*****************************************************************************
2 * intf_cmd.c: interface commands parsing and executions functions
3 * This file implements the interface commands execution functions. It is used
4 * by command-line oriented interfaces and scripts. The commands themselves are
5 * implemented in intf_ctrl.
6 *****************************************************************************
7 * Copyright (C) 1998, 1999, 2000 VideoLAN
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public
22 * License along with this program; if not, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
32 #include <errno.h> /* errno */
33 #include <stdio.h> /* FILE */
34 #include <stdlib.h> /* strtod(), strtol() */
35 #include <string.h> /* strerror() */
43 #include "interface.h"
46 #include "intf_ctrl.h"
52 static int ParseCommandArguments ( char *psz_argv[INTF_MAX_ARGS], char *psz_cmd );
53 static int CheckCommandArguments ( intf_arg_t argv[INTF_MAX_ARGS], int i_argc,
54 char *psz_argv[INTF_MAX_ARGS], char *psz_format );
55 static void ParseFormatString ( intf_arg_t format[INTF_MAX_ARGS], char *psz_format );
56 static int ConvertArgument ( intf_arg_t *p_arg, int i_flags, char *psz_str );
58 /*****************************************************************************
59 * intf_ExecCommand: parse and execute a command
60 *****************************************************************************
61 * This function is called when a command needs to be executed. It parse the
62 * command line, build an argument array, find the command in control commands
63 * array and run the command. It returns the return value of the command, or
64 * EINVAL if no command could be executed. Command line is modified by this
66 * Note that this function may terminate abruptly the program or signify it's
67 * end to the interface thread.
68 *****************************************************************************/
69 int intf_ExecCommand( char *psz_cmd )
71 char * psz_argv[INTF_MAX_ARGS]; /* arguments pointers */
72 intf_arg_t argv[INTF_MAX_ARGS]; /* converted arguments */
73 int i_argc; /* number of arguments */
74 int i_index; /* multi-purposes index */
75 int i_return; /* command return value */
77 intf_DbgMsg("command `%s'\n", psz_cmd);
79 /* Parse command line (separate arguments). If nothing has been found,
80 * the function returns without error */
81 i_argc = ParseCommandArguments( psz_argv, psz_cmd );
87 /* Find command. Command is always the first token on the line */
89 control_command[i_index].psz_name && strcmp( psz_argv[0], control_command[i_index].psz_name );
94 if( !control_command[i_index].psz_name ) /* unknown command */
96 /* Print error message */
97 intf_IntfMsg( "error: unknown command `%s'. Try `help'", psz_argv[0] );
98 return( INTF_USAGE_ERROR );
101 /* Check arguments validity */
102 if( CheckCommandArguments( argv, i_argc, psz_argv, control_command[i_index].psz_format ) )
104 /* The given arguments does not match the format string. An error message has
105 * already been displayed, so only the usage string is printed */
106 intf_IntfMsg( "usage: %s", control_command[i_index].psz_usage );
107 return( INTF_USAGE_ERROR );
110 /* Execute command */
111 i_return = control_command[i_index].function( i_argc, argv );
113 /* Manage special error codes */
116 case INTF_FATAL_ERROR: /* fatal error */
117 /* Print message and terminates the interface thread */
118 intf_ErrMsg( "fatal error in command `%s'\n", psz_argv[0] );
119 p_main->p_intf->b_die = 1;
122 case INTF_CRITICAL_ERROR: /* critical error */
123 /* Print message, flush messages queue and exit. Note that this
124 * error should be very rare since it does not even try to cancel other
126 intf_ErrMsg("critical error in command `%s'. Please report this error !\n", psz_argv[0] );
128 exit( INTF_CRITICAL_ERROR );
131 case INTF_USAGE_ERROR: /* usage error */
132 /* Print error message and usage */
133 intf_IntfMsg( "usage: %s", control_command[i_index].psz_usage );
137 /* Return error code */
141 /*****************************************************************************
142 * intf_ExecScript: parse and execute a command script
143 *****************************************************************************
144 * This function, based on ExecCommand read a file and tries to execute each
145 * of its line as a command. It returns 0 if everything succeeded, a negative
146 * number if the script could not be executed and a positive one if an error
147 * occured during execution.
148 *****************************************************************************/
149 int intf_ExecScript( char *psz_filename )
151 FILE * p_file; /* file */
152 char psz_line[INTF_MAX_CMD_SIZE]; /* line */
153 char * psz_index; /* index in string */
154 int i_err; /* error indicator */
158 p_file = fopen( psz_filename, "r" );
161 intf_ErrMsg("warning: %s: %s\n", psz_filename, strerror(errno));
165 /* For each line: read and execute */
166 while( fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL )
168 /* If line begins with a '#', it is a comment and shoule be ignored,
169 * else, execute it */
170 if( psz_line[0] != '#' )
172 /* The final '\n' needs to be removed before execution */
173 for( psz_index = psz_line; *psz_index && (*psz_index != '\n'); psz_index++ )
177 if( *psz_index == '\n' )
182 /* Execute command */
183 i_err |= intf_ExecCommand( psz_line );
186 if( !feof( p_file ) )
188 intf_ErrMsg("error: %s: %s\n", psz_filename, strerror(errno));
194 return( i_err != 0 );
197 /* following functions are local */
199 /*****************************************************************************
200 * ParseCommandArguments: isolate arguments in a command line
201 *****************************************************************************
202 * This function modify the original command line, adding '\0' and completes
203 * an array of pointers to beginning of arguments. It return the number of
205 *****************************************************************************/
206 static int ParseCommandArguments( char *psz_argv[INTF_MAX_ARGS], char *psz_cmd )
208 int i_argc; /* number of arguments */
209 char * psz_index; /* index */
210 boolean_t b_block; /* block (argument) indicator */
212 /* Initialize parser state */
213 b_block = 0; /* we start outside a block to remove spaces at beginning */
216 /* Go through command until end has been reached or maximal number of
217 * arguments has been reached */
218 for( psz_index = psz_cmd; *psz_index && (i_argc < INTF_MAX_ARGS); psz_index++ )
220 /* Inside a block, end of blocks are marked by spaces */
223 if( *psz_index == ' ' )
225 *psz_index = '\0'; /* mark the end of argument */
226 b_block = 0; /* exit the block */
230 /* Outside a block, beginning of blocks are marked by any character
231 * different from space */
234 if( *psz_index != ' ' )
236 psz_argv[i_argc++] = psz_index; /* store argument */
237 b_block = 1; /* enter the block */
242 /* Return number of arguments found */
246 /*****************************************************************************
247 * CheckCommandArguments: check arguments agains format
248 *****************************************************************************
249 * This function parse each argument and tries to find a match in the format
250 * string. It fills the argv array.
251 * If all arguments have been sucessfuly identified and converted, it returns
252 * 0, else, an error message is issued and non 0 is returned.
253 * Note that no memory is allocated by this function, but that the arguments
255 *****************************************************************************/
256 static int CheckCommandArguments( intf_arg_t argv[INTF_MAX_ARGS], int i_argc,
257 char *psz_argv[INTF_MAX_ARGS], char *psz_format )
259 intf_arg_t format[INTF_MAX_ARGS]; /* parsed format indicators */
260 int i_arg; /* argument index */
261 int i_format; /* format index */
262 char * psz_index; /* string index */
263 char * psz_cmp_index; /* string comparaison index */
264 int i_index; /* generic index */
265 boolean_t b_found; /* `argument found' flag */
268 /* Build format array */
269 ParseFormatString( format, psz_format );
271 /* Initialize parser: i_format must be the first non named formatter */
272 for( i_format = 0; ( i_format < INTF_MAX_ARGS )
273 && (format[i_format].i_flags & INTF_NAMED_ARG);
279 /* Scan all arguments */
280 for( i_arg = 1; i_arg < i_argc; i_arg++ )
284 /* Test if argument can be taken as a named argument: try to find a
285 * '=' in the string */
286 for( psz_index = psz_argv[i_arg]; *psz_index && ( *psz_index != '=' ); psz_index++ )
290 if( *psz_index == '=' ) /* '=' found */
292 /* Browse all named arguments to check if there is one matching */
293 for( i_index = 0; (i_index < INTF_MAX_ARGS)
294 && ( format[i_index].i_flags & INTF_NAMED_ARG )
298 /* Current format string is named... compare start of two
299 * names. A local inline ntation of a strcmp is used since
300 * string isn't ended by '\0' but by '=' */
301 for( psz_index = psz_argv[i_arg], psz_cmp_index = format[i_index].ps_name;
302 (*psz_index == *psz_cmp_index) && (*psz_index != '=') && (*psz_cmp_index != '=');
303 psz_index++, psz_cmp_index++ )
307 if( *psz_index == *psz_cmp_index ) /* the names match */
309 /* The argument is a named argument which name match the
310 * named argument i_index. To be valid, the argument should
311 * not have been already encountered and the type must
312 * match. Before going further, the '=' is replaced by
316 /* Check unicity. If the argument has already been encountered,
317 * print an error message and return. */
318 if( format[i_index].i_flags & INTF_PRESENT_ARG )/* present */
320 intf_IntfMsg("error: `%s' has already been encountered", psz_argv[i_arg] );
324 /* Register argument and prepare exit */
326 format[i_index].i_flags |= INTF_PRESENT_ARG;
327 argv[i_arg].i_flags = INTF_NAMED_ARG;
328 argv[i_arg].i_index = i_index;
329 argv[i_arg].ps_name = psz_argv[i_arg];
331 /* Check type and store value */
333 if( ConvertArgument( &argv[i_arg], format[i_index].i_flags, psz_index ) )
335 /* An error occured during conversion */
336 intf_IntfMsg( "error: invalid type for `%s'", psz_index );
342 /* If argument is not a named argument, the format string will
343 * be browsed starting from last position until the argument is
344 * found or an error occurs. */
347 /* Reset type indicator */
348 argv[i_arg].i_flags = 0;
350 /* If argument is not a named argument, the format string will
351 * be browsed starting from last position until the argument is
352 * found, an error occurs or the last format argument is
354 while( !b_found && (i_format < INTF_MAX_ARGS) && format[i_format].i_flags )
356 /* Try to convert argument */
357 if( !ConvertArgument( &argv[i_arg], format[i_format].i_flags, psz_argv[i_arg] ) )
359 /* Matching format has been found */
361 format[i_format].i_flags |= INTF_PRESENT_ARG;
362 argv[i_arg].i_index = i_format;
364 /* If argument is repeatable, dot not increase format counter */
365 if( !(format[i_format].i_flags & INTF_REP_ARG) )
372 /* Argument does not match format. This can be an error, or
373 * just a missing optionnal parameter, or the end of a
374 * repeated argument */
375 if( (format[i_format].i_flags & INTF_OPT_ARG)
376 || (format[i_format].i_flags & INTF_PRESENT_ARG) )
378 /* This is not an error */
383 /* The present format argument is mandatory and does
384 * not match the argument */
385 intf_IntfMsg("error: missing argument before `%s'", psz_argv[i_arg] );
392 /* If argument is not a named argument and hasn't been found in
393 * format string, then it is an usage error and the function can
397 intf_IntfMsg("error: `%s' does not match any argument", psz_argv[i_arg] );
401 intf_DbgMsg("argument flags=0x%x (index=%d) name=%s str=%s int=%d float=%f\n",
404 (argv[i_arg].i_flags & INTF_NAMED_ARG) ? argv[i_arg].ps_name : "NA",
405 (argv[i_arg].i_flags & INTF_STR_ARG) ? argv[i_arg].psz_str : "NA",
406 (argv[i_arg].i_flags & INTF_INT_ARG) ? argv[i_arg].i_num : 0,
407 (argv[i_arg].i_flags & INTF_FLOAT_ARG) ? argv[i_arg].f_num : 0);
410 /* Parse all remaining format specifier to verify they are all optionnal */
411 for( ; (i_format < INTF_MAX_ARGS) && format[i_format].i_flags ; i_format++ )
413 if( !(( format[i_format].i_flags & INTF_OPT_ARG)
414 || ( format[i_format].i_flags & INTF_PRESENT_ARG)) )
416 /* Format has not been used and is neither optionnal nor multiple
418 intf_IntfMsg("error: missing argument(s)\n");
423 /* If an error occured, the function already exited, so if this point is
424 * reached, everything is fine */
428 /*****************************************************************************
429 * ConvertArgument: try to convert an argument to a given type
430 *****************************************************************************
431 * This function tries to convert the string argument given in psz_str to
432 * a type specified in i_flags. It updates p_arg and returns O on success,
433 * or 1 on error. No error message is issued.
434 *****************************************************************************/
435 static int ConvertArgument( intf_arg_t *p_arg, int i_flags, char *psz_str )
437 char *psz_end; /* end pointer for conversion functions */
439 if( i_flags & INTF_STR_ARG ) /* string */
441 /* A conversion from a string to a string will always succeed... */
442 p_arg->psz_str = psz_str;
443 p_arg->i_flags |= INTF_STR_ARG;
445 else if( i_flags & INTF_INT_ARG ) /* integer */
447 p_arg->i_num = strtol( psz_str, &psz_end, 0 ); /* convert string */
448 /* If the conversion failed, return 1 and do not modify argument
449 * flags. Else, add 'int' flag and continue. */
450 if( !*psz_str || *psz_end )
454 p_arg->i_flags |= INTF_INT_ARG;
456 else if( i_flags & INTF_FLOAT_ARG ) /* float */
458 p_arg->f_num = strtod( psz_str, &psz_end ); /* convert string */
459 /* If the conversion failed, return 1 and do not modify argument
460 * flags. Else, add 'float' flag and continue. */
461 if( !*psz_str || *psz_end )
465 p_arg->i_flags |= INTF_FLOAT_ARG;
468 else /* error: missing type specifier */
470 intf_ErrMsg("error: missing type specifier for `%s' (0x%x)\n", psz_str, i_flags);
478 /*****************************************************************************
479 * ParseFormatString: parse a format string (ok ?)
480 *****************************************************************************
481 * This function read a format string, as specified in the control_command
482 * array, and fill a format array, to allow easier argument identification.
483 * Note that no memory is allocated by this function, but that, in a named
484 * argument, the name field does not end with a '\0' but with an '='.
485 * See command.h for format string specifications.
486 * Note that this function is designed to be efficient, not to check everything
487 * in a format string, which should be entered by a developper and therefore
488 * should be correct (TRUST !).
489 *****************************************************************************/
490 static void ParseFormatString( intf_arg_t format[INTF_MAX_ARGS], char *psz_format )
492 char * psz_index; /* format string index */
493 char * psz_start; /* argument format start */
494 char * psz_item; /* item index */
495 int i_index; /* format index */
497 /* Initialize parser */
499 psz_start = psz_format;
501 /* Reset first format indicator */
502 format[ 0 ].i_flags = 0;
504 /* Parse format string */
505 for( psz_index = psz_format; *psz_index && (i_index < INTF_MAX_ARGS) ; psz_index++ )
507 /* A space is always an item terminator */
508 if( *psz_index == ' ' )
510 /* Parse format item. Items are parsed from end to beginning or to
512 for( psz_item = psz_index - 1;
513 (psz_item >= psz_start) && !( format[i_index].i_flags & INTF_NAMED_ARG);
518 case 's': /* string */
519 format[i_index].i_flags |= INTF_STR_ARG;
521 case 'i': /* integer */
522 format[i_index].i_flags |= INTF_INT_ARG;
524 case 'f': /* float */
525 format[i_index].i_flags |= INTF_FLOAT_ARG;
527 case '*': /* can be repeated */
528 format[i_index].i_flags |= INTF_REP_ARG;
530 case '?': /* optionnal argument */
531 format[i_index].i_flags |= INTF_OPT_ARG;
533 case '=': /* name argument */
534 format[i_index].i_flags |= INTF_NAMED_ARG;
535 format[i_index].ps_name = psz_start;
538 default:/* error which should never happen: incorrect format */
539 intf_DbgMsg("error: incorrect format string `%s'\n", psz_format);
545 /* Mark next item start, increase items counter and reset next
546 * format indicator, if it wasn't the last one. */
548 psz_start = psz_index + 1;
549 if( i_index != INTF_MAX_ARGS ) /* end of array not reached */
551 format[ i_index ].i_flags = 0;