suboption parser for vo and ao modules
authoral <al@b3059339-0415-0410-9bf9-f77b7e298cf2>
Fri, 31 Dec 2004 11:11:24 +0000 (11:11 +0000)
committeral <al@b3059339-0415-0410-9bf9-f77b7e298cf2>
Fri, 31 Dec 2004 11:11:24 +0000 (11:11 +0000)
git-svn-id: svn://git.mplayerhq.hu/mplayer/trunk@14282 b3059339-0415-0410-9bf9-f77b7e298cf2

Makefile
subopt-helper.c [new file with mode: 0644]
subopt-helper.h [new file with mode: 0644]

index 795a885..9f06097 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@ endif
 
 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) 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
diff --git a/subopt-helper.c b/subopt-helper.c
new file mode 100644 (file)
index 0000000..7ae5512
--- /dev/null
@@ -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;
+}
diff --git a/subopt-helper.h b/subopt-helper.h
new file mode 100644 (file)
index 0000000..5298016
--- /dev/null
@@ -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