[MPlayer-dev-eng] [PATCH] subopt parser
Alexander Strasser
eclipse7 at gmx.net
Sat Dec 11 02:05:01 CET 2004
Hi,
as i wanted to add a suboption to one of the vos lately
i again recognized the somewhat chaotic parsing of the
suboptions distributed all over the vo/ao modules. So
i decided to write a suboption parser, it is inspired
by the suboption parser implemented in vo gl.
The attached patch contains the parser + a demonstration
of its usage in vo gl.
Alex (beastd)
-------------- next part --------------
Index: libvo/vo_gl.c
===================================================================
RCS file: /cvsroot/mplayer/main/libvo/vo_gl.c,v
retrieving revision 1.59
diff -u -r1.59 vo_gl.c
--- libvo/vo_gl.c 20 Nov 2004 10:57:39 -0000 1.59
+++ libvo/vo_gl.c 11 Dec 2004 00:49:43 -0000
@@ -24,6 +24,7 @@
#include "gl_common.h"
#include "x11_common.h"
#include "aspect.h"
+#include "subopt-helper.h"
#ifdef HAVE_NEW_GUI
#include "Gui/interface.h"
#endif
@@ -543,63 +544,33 @@
vo_x11_uninit();
}
+static int test_slice_height( void * sh )
+{
+ if ( *((int *)sh) < 0 ) { return 0; }
+
+ return 1;
+}
static uint32_t preinit(const char *arg)
{
- int parse_err = 0;
- unsigned int parse_pos = 0;
+ opt_t subopts[] =
+ {
+ /* name arg var test func */
+ { "manyfmts", OPT_ARG_BOOL, &many_fmts, NULL },
+ { "osd", OPT_ARG_BOOL, &use_osd, NULL },
+ { "scaled-osd", OPT_ARG_BOOL, &scaled_osd, NULL },
+ { "aspect", OPT_ARG_BOOL, &use_aspect, NULL },
+ { "slice-height",OPT_ARG_INT, &slice_height,test_slice_height },
+ { NULL } //terminator
+ };
+
+ /* set defaults */
many_fmts = 0;
use_osd = 1;
scaled_osd = 0;
use_aspect = 1;
slice_height = 4;
- if(arg)
- {
- while (arg[parse_pos] && !parse_err) {
- if (strncmp (&arg[parse_pos], "manyfmts", 8) == 0) {
- parse_pos += 8;
- many_fmts = 1;
- } else if (strncmp (&arg[parse_pos], "nomanyfmts", 10) == 0) {
- parse_pos += 10;
- many_fmts = 0;
- } else if (strncmp (&arg[parse_pos], "osd", 3) == 0) {
- parse_pos += 3;
- use_osd = 1;
- } else if (strncmp (&arg[parse_pos], "noosd", 5) == 0) {
- parse_pos += 5;
- use_osd = 0;
- } else if (strncmp (&arg[parse_pos], "scaled-osd", 10) == 0) {
- parse_pos += 10;
- scaled_osd = 1;
- } else if (strncmp (&arg[parse_pos], "noscaled-osd", 12) == 0) {
- parse_pos += 12;
- scaled_osd = 0;
- } else if (strncmp (&arg[parse_pos], "aspect", 6) == 0) {
- parse_pos += 6;
- use_aspect = 1;
- } else if (strncmp (&arg[parse_pos], "noaspect", 8) == 0) {
- parse_pos += 8;
- use_aspect = 0;
- } else if (strncmp (&arg[parse_pos], "slice-height=", 13) == 0) {
- int val;
- char *end;
- parse_pos += 13;
- val = strtol(&arg[parse_pos], &end, 0);
- if (val < 0) parse_err = 1;
- else {
- slice_height = val;
- parse_pos = end - arg;
- }
- }
- if (arg[parse_pos] == ':') parse_pos++;
- else if (arg[parse_pos]) parse_err = 1;
- }
- }
- if (parse_err) {
- unsigned int i;
- mp_msg(MSGT_VO, MSGL_FATAL, "Could not parse arguments:\n%s\n", arg);
- for (i = 0; i < parse_pos; i++)
- mp_msg(MSGT_VO, MSGL_FATAL, " ");
- mp_msg(MSGT_VO, MSGL_FATAL, "^\n");
+
+ if ( subopt_parse( arg, subopts ) != 0 ) {
mp_msg(MSGT_VO, MSGL_FATAL,
"\n-vo gl command line help:\n"
"Example: mplayer -vo gl:slice-height=4\n"
Index: Makefile
===================================================================
RCS file: /cvsroot/mplayer/main/Makefile,v
retrieving revision 1.311
diff -u -r1.311 Makefile
--- Makefile 5 Nov 2004 14:02:40 -0000 1.311
+++ Makefile 11 Dec 2004 00:49:43 -0000
@@ -21,7 +21,7 @@
SRCS_COMMON = cpudetect.c codec-cfg.c spudec.c playtree.c playtreeparser.c asxparser.c vobsub.c subreader.c sub_cc.c find_sub.c m_config.c m_option.c parser-cfg.c m_struct.c edl.c
SRCS_MENCODER = mencoder.c mp_msg-mencoder.c $(SRCS_COMMON) libao2/afmt.c divx4_vbr.c libvo/aclib.c libvo/osd.c libvo/sub.c libvo/font_load.c libvo/font_load_ft.c xvid_vbr.c parser-mecmd.c
-SRCS_MPLAYER = mplayer.c mp_msg.c $(SRCS_COMMON) mixer.c parser-mpcmd.c
+SRCS_MPLAYER = mplayer.c mp_msg.c $(SRCS_COMMON) mixer.c parser-mpcmd.c subopt-helper.c
ifeq ($(UNRARLIB),yes)
SRCS_COMMON += unrarlib.c
--- /dev/null 2004-11-03 10:47:25.000000000 +0100
+++ subopt-helper.h 2004-12-11 01:43:36.000000000 +0100
@@ -0,0 +1,39 @@
+#ifndef SUBOPT_HELPER_H
+#define SUBOPT_HELPER_H
+
+/**
+ * \file subopt-helper.h
+ *
+ * \brief Datatype and functions declarations for usage
+ * of the suboption parser.
+ *
+ */
+
+#define OPT_ARG_BOOL 0
+#define OPT_ARG_INT 1
+#define OPT_ARG_STR 2
+
+/** simple structure for defining the option name, type and storage location */
+typedef struct opt_s
+{
+ char * name; ///< string that identifies the option
+ int type; ///< option type as defined in subopt-helper.h
+ void * valp; ///< pointer to the mem where the value should be stored
+ int (* test)(void *); ///< argument test func ( optional )
+ int set; ///< Is set internally by the parser if the option was found.
+ ///< Don't use it at initialization of your opts, it will be
+ ///< overriden anyway!
+} opt_t;
+
+/** parses the string for the options specified in opt */
+int subopt_parse( char const * const str, opt_t * opts );
+
+
+/*------------------ arg specific types and declaration -------------------*/
+typedef struct strarg_s
+{
+ unsigned char len; ///< length of the string determined by the parser
+ char const * str; ///< pointer to position inside the parse string
+} strarg_t;
+
+#endif
--- /dev/null 2004-11-03 10:47:25.000000000 +0100
+++ subopt-helper.c 2004-12-11 00:47:54.000000000 +0100
@@ -0,0 +1,256 @@
+/**
+ * \file subopt-helper.c
+ *
+ * \brief Compensates the suboption parsing code duplication a bit.
+ *
+ * The routines defined below are there to help you with the
+ * suboption parsing. Meaning extracting the options and their
+ * values for you and also outputting generic help message if
+ * a parse error is encountered.
+ *
+ * Most stuff happens in the subopt_parse function: if you call it
+ * it parses for the passed opts in the passed string. It calls some
+ * extra functions for explicit argument parsing ( where the option
+ * itself isn't the argument but a value given after the argument
+ * delimiter ('='). It also calls your test function if you supplied
+ * one.
+ *
+ */
+
+#include "subopt-helper.h"
+#include "mp_msg.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifndef MPDEBUG
+ #define NDEBUG
+#endif
+
+/* prototypes for argument parsing */
+static char const * parse_int( char const * const str, int * const valp );
+static char const * parse_str( char const * const str, strarg_t * const valp );
+
+/**
+ * \brief Try to parse all options in str and fail if it was not possible.
+ *
+ * \param str Pointer to the zero terminated string to be parsed.
+ * \param opts Pointer to a options array. The array must be terminated
+ * with an element having set name to NULL in its opt_t structure.
+ *
+ * \return The return value is zero if the string could be parsed
+ * else a non-zero value is returned.
+ *
+ */
+int subopt_parse( char const * const str, opt_t * opts )
+{
+ int parse_err = 0, idx;
+ unsigned int parse_pos = 0;
+
+ /* Initialize set member to false. *
+ * It is set to true if it was found in str */
+ for ( idx=0; opts[idx].name; ++idx )
+ {
+ opts[idx].set = 0;
+ }
+
+ if ( str )
+ {
+ while ( str[parse_pos] && !parse_err )
+ {
+ int next = 0;
+
+ idx = 0; // reset index for the below loop
+ while ( opts[idx].name )
+ {
+ int opt_len;
+ int substr_len;
+
+ // get length of the option we test against */
+ opt_len = strlen( opts[idx].name );
+
+ // get length of the current substring of str */
+ {
+ char * delim, * arg_delim;
+
+ /* search nearest delimiter ( option or argument delimiter ) */
+ delim = strchr( &str[parse_pos], ':' );
+ arg_delim = strchr( &str[parse_pos], '=' );
+
+ if ( ( delim && arg_delim && delim > arg_delim ) ||
+ delim == NULL )
+ {
+ delim = strchr( &str[parse_pos], '=' );
+ }
+
+ substr_len = delim ? // is a delim present
+ delim - &str[parse_pos] : // yes
+ strlen( &str[parse_pos] ); // no, end of string
+ }
+
+ //printf( "substr_len=%d, opt_len=%d\n", substr_len, opt_len );
+
+ /* Check if the length of the current option matches the *
+ * length of the option we want to test again. */
+ if ( substr_len == opt_len )
+{
+ /* check if option was activated/deactivated */
+ if( strncmp( &str[parse_pos], opts[idx].name, opt_len ) == 0 )
+ {
+ /* option was found */
+ opts[idx].set = 1; next = 1;
+
+ assert( opts[idx].valp && "Need a pointer to store the arg!" );
+
+ /* type specific code */
+ if ( opts[idx].type == OPT_ARG_BOOL )
+ {
+ /* Handle OPT_ARG_BOOL seperately so *
+ * the others can share code. */
+
+ /* set option to true */
+ *((int *)(opts[idx].valp)) = 1;
+
+ /* increment position */
+ parse_pos += opt_len;
+ }
+ else
+ {
+ /* Type is not OPT_ARG_BOOL, means we have to parse *
+ * for the arg delimiter character and eventually *
+ * call a test function. */
+ char const * last;
+
+ /* increment position to check for arg */
+ parse_pos += opt_len;
+
+ if ( str[parse_pos] != '=' )
+ {
+ parse_err = 1; break;
+ }
+
+ /* '=' char was there, so let's move after it */
+ ++parse_pos;
+
+ switch ( opts[idx].type )
+ {
+ case OPT_ARG_INT:
+ last = parse_int( &str[parse_pos],
+ (int *)opts[idx].valp );
+
+ break;
+ case OPT_ARG_STR:
+ last = parse_str( &str[parse_pos],
+ (strarg_t *)opts[idx].valp );
+ break;
+ default:
+ assert( 0 && "Arg type of suboption doesn't exist!" );
+ last = NULL; // break parsing!
+ }
+
+ /* was the conversion succesful? */
+ if ( !last )
+ {
+ parse_err = 1; break;
+ }
+
+ /* make test if supplied */
+ if ( opts[idx].test && !opts[idx].test( opts[idx].valp ) )
+ {
+ parse_err = 1; break;
+ }
+
+ /* we succeded, set position */
+ parse_pos = last - str;
+ }
+ }
+}
+else if ( substr_len == opt_len+2 )
+{
+ if ( opts[idx].type == OPT_ARG_BOOL && // check for no<opt>
+ strncmp( &str[parse_pos], "no", 2 ) == 0 &&
+ strncmp( &str[parse_pos+2], opts[idx].name, opt_len ) == 0 )
+ {
+ /* option was found but negated */
+ opts[idx].set = 1; next = 1;
+
+ /* set arg to false */
+ *((int *)(opts[idx].valp)) = 0;
+
+ /* increment position */
+ parse_pos += opt_len+2;
+ }
+}
+
+ ++idx; // test against next option
+
+ /* break out of the loop, if this subopt is processed */
+ if ( next ) { break; }
+ }
+
+ /* if we had a valid suboption the current pos should *
+ * equal the delimiter char, which should be ':' for *
+ * suboptions. */
+ if ( !parse_err && str[parse_pos] == ':' ) { ++parse_pos; }
+ else if ( str[parse_pos] ) { parse_err = 1; }
+ }
+ }
+
+ /* if an error was encountered */
+ if (parse_err)
+ {
+ unsigned int i;
+ mp_msg( MSGT_VO, MSGL_FATAL, "Could not parse arguments at the position indicated below:\n%s\n", str );
+ for ( i = 0; i < parse_pos; ++i )
+ {
+ mp_msg(MSGT_VO, MSGL_FATAL, " ");
+ }
+ mp_msg(MSGT_VO, MSGL_FATAL, "^\n");
+
+ return -1;
+ }
+
+ /* we could parse everything */
+ return 0;
+}
+
+static char const * parse_int( char const * const str, int * const valp )
+{
+ char * endp;
+
+ assert( str && "parse_int(): str == NULL" );
+
+ *valp = (int)strtol( str, &endp, 0 );
+
+ /* nothing was converted */
+ if ( str == endp ) { return NULL; }
+
+ return endp;
+}
+
+static char const * parse_str( char const * const str, strarg_t * const valp )
+{
+ char const * match = strchr( str, ':' );
+
+ if ( !match )
+ {
+ if ( str[1] != '\0' )
+ {
+ int len = strlen( &str[1] );
+ match = str + 1 + len;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+ valp->len = match - str;
+ valp->str = str;
+
+ /* if the length is zero, indicate error */
+ if ( valp->len == 0 ) { return NULL; }
+
+ return match;
+}
More information about the MPlayer-dev-eng
mailing list