[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