view cfgparser.c @ 8870:7538bf7e728d

halfpack now takes an optional argument, field, which tells it to keep only even lines (if 0) or only odd lines (if 1) rather than averaging pairs of lines. this may be useful for slightly improving performance or for dirty deinterlacing.
author rfelker
date Fri, 10 Jan 2003 14:12:07 +0000
parents 7a7980b874f5
children b0141de527df
line wrap: on
line source

/*
 * command line and config file parser
 * by Szabolcs Berecz <szabi@inf.elte.hu>
 * (C) 2001
 *
 * subconfig support by alex
 */

//#define DEBUG

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <math.h>

#include "config.h"

#ifndef NEW_CONFIG

#ifdef USE_SETLOCALE
#include <locale.h>
#endif

#include "mp_msg.h"

#define COMMAND_LINE		0
#define CONFIG_FILE		1

#define LIST_SEPARATOR ','

#define CONFIG_GLOBAL (1<<0)
#define CONFIG_RUNNING (1<<1)

#define SET_GLOBAL(c)  (c->flags |= CONFIG_GLOBAL)
#ifdef GLOBAL_OPTIONS_ONLY
#define UNSET_GLOBAL(c)
#else
#define UNSET_GLOBAL(c) (c->flags &= (!CONFIG_GLOBAL))
#endif
#define IS_GLOBAL(c) (c->flags & CONFIG_GLOBAL)
#define SET_RUNNING(c) (c->flags |= CONFIG_RUNNING)
#define IS_RUNNING(c) (c->flags & CONFIG_RUNNING)

#define MAX_RECURSION_DEPTH	8

#ifdef MP_DEBUG
#include <assert.h>
#endif

#include "cfgparser.h"
#include "playtree.h"

static void m_config_list_options(m_config_t *config);
static void m_config_error(int err,char* opt,char* val);

static void
m_config_save_option(m_config_t* config, config_t* conf,char* opt, char *param) {
  config_save_t* save;
  int sl=0;
 
#ifdef MP_DEBUG
  assert(config != NULL);
  assert(config->cs_level >= 0);
  assert(conf != NULL);
  assert(opt != NULL);
  assert( ! (conf->flags & CONF_NOSAVE));
#endif

  switch(conf->type) {
  case CONF_TYPE_PRINT :
  case CONF_TYPE_SUBCONFIG :
    return;
  default :
    ;
  }

  mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Saving option %s\n",opt);

  save = config->config_stack[config->cs_level];

  if(save) {
    for(sl = 0; save[sl].opt != NULL; sl++){
      // Check to not save the same arg two times
      if(save[sl].opt == conf && (save[sl].opt_name == NULL || strcasecmp(save[sl].opt_name,opt) == 0))
	break;
    }
    if(save[sl].opt)
      return;
  }

  save = (config_save_t*)realloc(save,(sl+2)*sizeof(config_save_t));
  if(save == NULL) {
    mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Can't allocate %d bytes of memory : %s\n",(sl+2)*sizeof(config_save_t),strerror(errno));
    return;
  }
  memset(&save[sl],0,2*sizeof(config_save_t));
  save[sl].opt = conf;
  
  switch(conf->type) {
  case CONF_TYPE_FLAG :
  case CONF_TYPE_INT :
    save[sl].param.as_int = *((int*)conf->p);
    break;
  case CONF_TYPE_FLOAT :
    save[sl].param.as_float = *((float*)conf->p);
    break;
  case CONF_TYPE_STRING :
    save[sl].param.as_pointer = *((char**)conf->p);
    break;   
  case CONF_TYPE_FUNC_FULL :
    if(strcasecmp(conf->name,opt) != 0) save->opt_name = strdup(opt);
  case CONF_TYPE_FUNC_PARAM :
    if(param)
      save->param.as_pointer = strdup(param);
  case CONF_TYPE_FUNC :    
    break;
  case CONF_TYPE_STRING_LIST :
    save[sl].param.as_pointer = *((char***)conf->p);
    break;
  case CONF_TYPE_POSITION :
    save[sl].param.as_off_t = *((off_t*)conf->p);
    break;
  default :
    mp_msg(MSGT_CFGPARSER,MSGL_ERR,"Should never append in m_config_save_option : conf->type=%d\n",conf->type);
  }

  config->config_stack[config->cs_level] = save;
}

static int
m_config_revert_option(m_config_t* config, config_save_t* save) {
  char* arg = NULL;
  config_save_t* iter=NULL;
  int i=-1;

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(config->cs_level >= 0);
  assert(save != NULL);
#endif


  arg = save->opt_name ? save->opt_name : save->opt->name;
  mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Reverting option %s\n",arg);

  if(save->opt->default_func)
    save->opt->default_func(save->opt,arg);

  switch(save->opt->type) {
  case CONF_TYPE_FLAG :
  case CONF_TYPE_INT :
    *((int*)save->opt->p) = save->param.as_int;
    break;
  case CONF_TYPE_FLOAT :
    *((float*)save->opt->p) = save->param.as_float;
    break;
  case CONF_TYPE_STRING :
    *((char**)save->opt->p) = save->param.as_pointer;
    break;
  case CONF_TYPE_STRING_LIST :
    *((char***)save->opt->p) = save->param.as_pointer;
    break;
  case CONF_TYPE_FUNC_PARAM :
  case CONF_TYPE_FUNC_FULL :
  case CONF_TYPE_FUNC :
    if(config->cs_level > 0) {
      for(i = config->cs_level - 1 ; i >= 0 ; i--){
	if(config->config_stack[i] == NULL) continue;
	for(iter = config->config_stack[i]; iter != NULL && iter->opt != NULL ; iter++) {
	  if(iter->opt == save->opt && 
	     ((save->param.as_pointer == NULL || iter->param.as_pointer == NULL) || strcasecmp(save->param.as_pointer,iter->param.as_pointer) == 0) && 
	     (save->opt_name == NULL || 
	      (iter->opt_name && strcasecmp(save->opt_name,iter->opt_name)))) break;
	}
      }
    }
    free(save->param.as_pointer);
    if(save->opt_name) free(save->opt_name);
    save->opt_name = save->param.as_pointer = NULL;
    if(i < 0) break;
    arg = iter->opt_name ? iter->opt_name : iter->opt->name;
    switch(iter->opt->type) {
    case CONF_TYPE_FUNC :
      if ((((cfg_func_t) iter->opt->p)(iter->opt)) < 0)
	return -1;
      break;
    case CONF_TYPE_FUNC_PARAM :
      if (iter->param.as_pointer == NULL) {
	mp_msg(MSGT_CFGPARSER,MSGL_ERR,"We lost param for option %s?\n",iter->opt->name);
	return -1;
      } 
      if ((((cfg_func_param_t) iter->opt->p)(iter->opt, (char*)iter->param.as_pointer)) < 0)
	return -1;
      break;
    case CONF_TYPE_FUNC_FULL :
      if (iter->param.as_pointer != NULL && ((char*)iter->param.as_pointer)[0]=='-'){
	if( ((cfg_func_arg_param_t) iter->opt->p)(iter->opt, arg, NULL) < 0)
	  return -1;
      }else {
	if (((cfg_func_arg_param_t) save->opt->p)(iter->opt, arg, (char*)iter->param.as_pointer) < 0) 
	  return -1;
	  
      }
      break;
    }
    break;
  case CONF_TYPE_POSITION :
    *((off_t*)save->opt->p) = save->param.as_off_t;
    break;
  default :
    mp_msg(MSGT_CFGPARSER,MSGL_WARN,"Why do we reverse this : name=%s type=%d ?\n",save->opt->name,save->opt->type);
  }
			
  return 1;
}

void
m_config_push(m_config_t* config) {

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(config->cs_level >= 0);
#endif

  config->cs_level++;
  config->config_stack = (config_save_t**)realloc(config->config_stack ,sizeof(config_save_t*)*(config->cs_level+1));
  if(config->config_stack == NULL) {
    mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Can't allocate %d bytes of memory : %s\n",sizeof(config_save_t*)*(config->cs_level+1),strerror(errno));
    config->cs_level = -1;
    return;
  }
  config->config_stack[config->cs_level] = NULL;
  mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config pushed level=%d\n",config->cs_level);
}

int
m_config_pop(m_config_t* config) {
  int i,ret= 1;
  config_save_t* cs;
  
#ifdef MP_DEBUG
  assert(config != NULL);
  //assert(config->cs_level > 0);
#endif

  if(config->config_stack[config->cs_level] != NULL) {
    cs = config->config_stack[config->cs_level];
    for(i=0; cs[i].opt != NULL ; i++ ) {
      if (m_config_revert_option(config,&cs[i]) < 0)
	ret = -1;
    }
    free(config->config_stack[config->cs_level]);
  }
  config->config_stack = (config_save_t**)realloc(config->config_stack ,sizeof(config_save_t*)*config->cs_level);
  config->cs_level--;
  if(config->cs_level > 0 && config->config_stack == NULL) {
    mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Can't allocate %d bytes of memory : %s\n",sizeof(config_save_t*)*config->cs_level,strerror(errno));
    config->cs_level = -1;
    return -1;
  }
  mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config poped level=%d\n",config->cs_level);
  return ret;
}

m_config_t*
m_config_new(play_tree_t* pt) {
  m_config_t* config;

#ifdef MP_DEBUG
  assert(pt != NULL);
#endif

  config = (m_config_t*)calloc(1,sizeof(m_config_t));
  if(config == NULL) {
    mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Can't allocate %d bytes of memory : %s\n",sizeof(m_config_t),strerror(errno));
    return NULL;
  }
  config->config_stack = (config_save_t**)calloc(1,sizeof(config_save_t*));
  if(config->config_stack == NULL) {
    mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Can't allocate %d bytes of memory : %s\n",sizeof(config_save_t*),strerror(errno));
    free(config);
    return NULL;
  }
  SET_GLOBAL(config); // We always start with global options
  config->pt = pt;
  return config;
}

void
m_config_free(m_config_t* config) {

#ifdef MP_DEBUG
  assert(config != NULL);
#endif

  free(config->opt_list);
  free(config->config_stack);
  free(config);
}


static int init_conf(m_config_t *config, int mode)
{
#ifdef MP_DEBUG
	assert(config != NULL);
	assert(config->pt != NULL);
	assert(config->last_entry == NULL || config->last_entry->parent == config->pt);

	if (mode != COMMAND_LINE && mode != CONFIG_FILE) {
		mp_msg(MSGT_CFGPARSER, MSGL_ERR, "init_conf: wrong mode!\n");
		return -1;
	}
#endif
	config->parser_mode = mode;
	return 1;
}

static int config_is_entry_option(m_config_t *config, char *opt, char *param) {
  play_tree_t* entry = NULL;

#ifdef MP_DEBUG
  assert(config->pt != NULL);
#endif

  if(strcasecmp(opt,"playlist") == 0) { // We handle playlist here
    if(!param)
      return ERR_MISSING_PARAM;
    entry = parse_playlist_file(param);
    if(!entry) {
      mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Playlist parsing failed: %s\n",param);
      return 1;
    }
  }

  if(! IS_RUNNING(config)) {
    if(strcasecmp(opt,"vcd") == 0) {
      char* s;
      if(!param)
	return ERR_MISSING_PARAM;
      s = (char*)malloc((strlen(param) + 6 + 1)*sizeof(char));
      sprintf(s,"vcd://%s",param);
      entry = play_tree_new();
      play_tree_add_file(entry,s);
      free(s);
    } else if(strcasecmp(opt,"dvd") == 0) {
      char* s;
      if(!param)
	return ERR_MISSING_PARAM;
      s = (char*)malloc((strlen(param) + 6 + 1)*sizeof(char));
      sprintf(s,"dvd://%s",param);
      entry = play_tree_new();
      play_tree_add_file(entry,s);
      free(s);
    } else if(strcasecmp(opt,"tv") == 0) {
      char *s,*pr,*prs;
      char *ps,*pe,*channel=NULL;
      char *as;
      int on=0;
      if(!param)
	return ERR_MISSING_PARAM;
      ps = param;
      pe = strchr(param,':');
      pr = prs = (char*)malloc((strlen(param)+1)*sizeof(char));
      pr[0] = '\0';
      while(ps) {
	if(!pe)
	  pe = ps + strlen(ps);

	as = strchr(ps,'=');
	if(as && as[1] != '\0' && pe-as > 0)
	  as++;
	else
	  as = NULL;
	if( !as && pe-ps == 2 &&  strncasecmp("on",ps,2) == 0 )
	  on = 1;
	else if(as  && as-ps == 8  && strncasecmp("channel",ps,6) == 0 && pe-as > 0) {
	  channel = (char*)realloc(channel,(pe-as+1)*sizeof(char));
	  strncpy(channel,as,pe-as);
	  channel[pe-as] = '\0';
	} else if(pe-ps > 0) {
	  if(prs != pr) {
	    prs[0] = ':';
	    prs++;
	  }
	  strncpy(prs,ps,pe-ps);
	  prs += pe-ps;
	  prs[0] = '\0';
	}

	if(pe[0] != '\0') {
	  ps = pe+1;
	  pe = strchr(ps,':');
	} else
	  ps = NULL;
      }

      if(on) {
	int l=5;
	
	if(channel)
	  l += strlen(channel);
	s = (char*) malloc((l+1)*sizeof(char));
	if(channel)
	  sprintf(s,"tv://%s",channel);
	else
	  sprintf(s,"tv://");
	entry = play_tree_new();
	play_tree_add_file(entry,s);
	if(strlen(pr) > 0)
	  play_tree_set_param(entry,"tv",pr);
	free(s);
      }
      free(pr);
      if(channel)
	free(channel);
	  
    }
  }

  if(entry) {
    if(config->last_entry)
      play_tree_append_entry(config->last_entry,entry);
    else
      play_tree_set_child(config->pt,entry);
    config->last_entry = entry;
    if(config->parser_mode == COMMAND_LINE)
      UNSET_GLOBAL(config);
    return 1;
  } else
    return 0;
}



static int config_read_option(m_config_t *config,config_t** conf_list, char *opt, char *param)
{
	int i=0,nconf = 0;
	long tmp_int;
	off_t tmp_off;
	double tmp_float;
	int dummy;
	int ret = -1;
	char *endptr;
	config_t* conf=NULL;

#ifdef MP_DEBUG
	assert(config != NULL);
	assert(conf_list != NULL);
	assert(opt != NULL);
#endif

	mp_msg(MSGT_CFGPARSER, MSGL_DBG3, "read_option: conf=%p opt='%s' param='%s'\n",
	    conf, opt, param);
	for(nconf = 0 ;  conf_list[nconf] != NULL; nconf++) {
	  conf = conf_list[nconf];
		for (i = 0; conf[i].name != NULL; i++) {
			int namelength;
			/* allow 'aa*' in config.name */
			namelength=strlen(conf[i].name);
			if ( (conf[i].name[namelength-1]=='*') && 
				    !memcmp(opt, conf[i].name, namelength-1))
			  goto option_found;
			if (!strcasecmp(opt, conf[i].name))
			  goto option_found;
		}
	}
	if (config->parser_mode == CONFIG_FILE)
	  mp_msg(MSGT_CFGPARSER, MSGL_ERR, "invalid option: %s\n",opt);
	ret = ERR_NOT_AN_OPTION;
	goto out;	
	option_found :
	mp_msg(MSGT_CFGPARSER, MSGL_DBG3, "read_option: name='%s' p=%p type=%d\n",
	    conf[i].name, conf[i].p, conf[i].type);

	if (conf[i].flags & CONF_NOCFG && config->parser_mode == CONFIG_FILE) {
		mp_msg(MSGT_CFGPARSER, MSGL_ERR, "this option can only be used on command line:\n", opt);
		ret = ERR_NOT_AN_OPTION;
		goto out;
	}
	if (conf[i].flags & CONF_NOCMD && config->parser_mode == COMMAND_LINE) {
		mp_msg(MSGT_CFGPARSER, MSGL_ERR, "this option can only be used in config file:\n", opt);
		ret = ERR_NOT_AN_OPTION;
		goto out;
	}
	ret = config_is_entry_option(config,opt,param);
	if(ret != 0)
	  return ret;
	else
	  ret = -1;
	if(! IS_RUNNING(config) && ! IS_GLOBAL(config) && 
	   ! (conf[i].flags & CONF_GLOBAL)  && conf[i].type != CONF_TYPE_SUBCONFIG  )
	  m_config_push(config);
	if( !(conf[i].flags & CONF_NOSAVE) && ! (conf[i].flags & CONF_GLOBAL) )
	  m_config_save_option(config,&conf[i],opt,param);
	switch (conf[i].type) {
		case CONF_TYPE_FLAG:
			/* flags need a parameter in config file */
			if (config->parser_mode == CONFIG_FILE) {
				if (!strcasecmp(param, "yes") ||	/* any other language? */
				    !strcasecmp(param, "ja") ||
				    !strcasecmp(param, "si") ||
				    !strcasecmp(param, "igen") ||
				    !strcasecmp(param, "y") ||
				    !strcasecmp(param, "j") ||
				    !strcasecmp(param, "i") ||
				    !strcmp(param, "1"))
					*((int *) conf[i].p) = conf[i].max;
				else if (!strcasecmp(param, "no") ||
				    !strcasecmp(param, "nein") ||
				    !strcasecmp(param, "nicht") ||
				    !strcasecmp(param, "nem") ||
				    !strcasecmp(param, "n") ||
				    !strcmp(param, "0"))
					*((int *) conf[i].p) = conf[i].min;
				else {
					mp_msg(MSGT_CFGPARSER, MSGL_ERR, "invalid parameter for flag: %s\n", param);
					ret = ERR_OUT_OF_RANGE;
					goto out;
				}
				ret = 1;
			} else {	/* parser_mode == COMMAND_LINE */
				*((int *) conf[i].p) = conf[i].max;
				ret = 0;
			}
			break;
		case CONF_TYPE_INT:
			if (param == NULL)
				goto err_missing_param;

			tmp_int = strtol(param, &endptr, 0);
			if (*endptr) {
				mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parameter must be an integer: %s\n", param);
				ret = ERR_OUT_OF_RANGE;
				goto out;
			}

			if (conf[i].flags & CONF_MIN)
				if (tmp_int < conf[i].min) {
					mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parameter must be >= %d: %s\n", (int) conf[i].min, param);
					ret = ERR_OUT_OF_RANGE;
					goto out;
				}

			if (conf[i].flags & CONF_MAX)
				if (tmp_int > conf[i].max) {
					mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parameter must be <= %d: %s\n", (int) conf[i].max, param);
					ret = ERR_OUT_OF_RANGE;
					goto out;
				}

			*((int *) conf[i].p) = tmp_int;
			ret = 1;
			break;
		case CONF_TYPE_FLOAT:
			if (param == NULL)
				goto err_missing_param;
			/* <olo@altkom.com.pl> Use portable C locale for parsing floats: */
#ifdef USE_SETLOCALE
			setlocale(LC_NUMERIC, "C");
#endif
			tmp_float = strtod(param, &endptr);

			switch(*endptr) {
			    case ':':
			    case '/':
				tmp_float /= strtod(endptr+1, &endptr);
			    default:
				break;
			}
#ifdef USE_SETLOCALE
			setlocale(LC_NUMERIC, "");
#endif

			if (*endptr) {
				mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parameter must be a floating point number"
				       " or a ratio (numerator[:/]denominator): %s\n", param);
				ret = ERR_MISSING_PARAM;
				goto out;
			}

			if (conf[i].flags & CONF_MIN)
				if (tmp_float < conf[i].min) {
					mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parameter must be >= %f: %s\n", conf[i].min, param);
					ret = ERR_OUT_OF_RANGE;
					goto out;
				}

			if (conf[i].flags & CONF_MAX)
				if (tmp_float > conf[i].max) {
					mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parameter must be <= %f: %s\n", conf[i].max, param);
					ret = ERR_OUT_OF_RANGE;
					goto out;
				}

			*((float *) conf[i].p) = tmp_float;
			ret = 1;
			break;
		case CONF_TYPE_STRING:
			if (param == NULL)
				goto err_missing_param;

			if (conf[i].flags & CONF_MIN)
				if (strlen(param) < conf[i].min) {
					mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parameter must be >= %d chars: %s\n",
							(int) conf[i].min, param);
					ret = ERR_OUT_OF_RANGE;
					goto out;
				}

			if (conf[i].flags & CONF_MAX)
				if (strlen(param) > conf[i].max) {
					mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parameter must be <= %d chars: %s\n",
							(int) conf[i].max, param);
					ret = ERR_OUT_OF_RANGE;
					goto out;
				}
			*((char **) conf[i].p) = strdup(param);
			ret = 1;
			break;
		case CONF_TYPE_STRING_LIST:
			if (param == NULL)
				goto err_missing_param;
			else {
			  int n = 0,len;
			  char *ptr = param, *last_ptr, **res;

			  while(ptr[0] != '\0') {
			    last_ptr = ptr;
			    ptr = strchr(ptr,LIST_SEPARATOR);
			    if(!ptr) {
//			      if(strlen(last_ptr) > 0)
				n++;
			      break;
			    }
			    ptr++;
			    n++;
			  }
			  if(n == 0)
			    goto err_missing_param;
			  else if( (conf[i].flags & CONF_MIN && n < conf[i].min) || 
				   (conf[i].flags & CONF_MAX && n > conf[i].max) ) {
			    ret = ERR_OUT_OF_RANGE;
			    goto out;
			  }
			  ret = 1;
			  res = malloc((n+2)*sizeof(char*));
			  ptr = param;
			  n = 0;
//			  while(ptr[0] != '\0') {
			  while(1) {
			    last_ptr = ptr;
			    ptr = strchr(ptr,LIST_SEPARATOR);
			     if(!ptr) {
			       //if(strlen(last_ptr) > 0) 
			       {
				 res[n] = strdup(last_ptr);
				 n++;
			       }
			       break;
			     }
			     len = ptr - last_ptr;
			     res[n] = (char*)malloc(len + 1);
			     if(len) strncpy(res[n],last_ptr,len);
			     res[n][len] = '\0';
			     ptr++;
			     n++;
			  }
			  res[n] = NULL;
			  *((char ***) conf[i].p) = res;
			}
			break;
		case CONF_TYPE_FUNC_PARAM:
			if (param == NULL)
				goto err_missing_param;
			if ((((cfg_func_param_t) conf[i].p)(conf + i, param)) < 0) {
				ret = ERR_FUNC_ERR;
				goto out;
			}
			ret = 1;
			break;
		case CONF_TYPE_FUNC_FULL:
			if (param!=NULL && param[0]=='-'){
			    ret=((cfg_func_arg_param_t) conf[i].p)(conf + i, opt, NULL);
			    if (ret>=0) ret=0;
			    /* if we return >=0: param is processed again (if there is any) */
			}else{
			    ret=((cfg_func_arg_param_t) conf[i].p)(conf + i, opt, param);
			    /* if we return 0: need no param, precess it again */
			    /* if we return 1: accepted param */
			}
			break;
		case CONF_TYPE_FUNC:
			if ((((cfg_func_t) conf[i].p)(conf + i)) < 0) {
				ret = ERR_FUNC_ERR;
				goto out;
			}
			ret = 0;
			break;
		case CONF_TYPE_SUBCONFIG:
		    {
			char *subparam;
			char *subopt;
			int subconf_optnr;
			config_t *subconf;
			config_t *sublist[] = { NULL , NULL };
			char *token;
			char *p;

			if (param == NULL)
				goto err_missing_param;

			subparam = malloc(strlen(param)+1);
			subopt = malloc(strlen(param)+1);
			p = strdup(param); // In case that param is a static string (cf man strtok)

			subconf = conf[i].p;
			sublist[0] = subconf;
			for (subconf_optnr = 0; subconf[subconf_optnr].name != NULL; subconf_optnr++)
			    /* NOTHING */;
			config->sub_conf = opt;
			token = strtok(p, (char *)&(":"));
			while(token)
			{
			    int sscanf_ret;
			    /* clear out */
			    subopt[0] = subparam[0] = 0;
			    
			    sscanf_ret = sscanf(token, "%[^=]=%[^:]", subopt, subparam);

			    mp_msg(MSGT_CFGPARSER, MSGL_DBG3, "token: '%s', i=%d, subopt='%s', subparam='%s' (ret: %d)\n", token, i, subopt, subparam, sscanf_ret);
			    switch(sscanf_ret)
			    {
				case 1:
				    subparam[0] = 0;
				case 2:
				    if ((ret = config_read_option(config,sublist, subopt, subparam)) < 0)
				    {
					mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Subconfig parsing returned error: %d in token: %s\n",
					    ret, token);
					goto out;
				    }
				    break;
				default:
				    mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid subconfig argument! ('%s')\n", token);
				    ret = ERR_NOT_AN_OPTION;
				    goto out;
			    }
			    token = strtok(NULL, (char *)&(":"));
			}
			config->sub_conf = NULL;
			free(subparam);
			free(subopt);
			free(p);
			ret = 1;
			break;
		    }
		case CONF_TYPE_PRINT:
			mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s", (char *) conf[i].p);
			exit(1);
		case CONF_TYPE_POSITION:
			if (param == NULL)
				goto err_missing_param;

			if (sscanf(param, sizeof(off_t) == sizeof(int) ?
			"%d%c" : "%lld%c", &tmp_off, (char *)&dummy) != 1) {
				mp_msg(MSGT_CFGPARSER, MSGL_ERR, "parameter must be an integer: %s\n", param);
				ret = ERR_OUT_OF_RANGE;
				goto out;
			}

			if (conf[i].flags & CONF_MIN)
				if (tmp_off < conf[i].min) {
					mp_msg(MSGT_CFGPARSER, MSGL_ERR,
					(sizeof(off_t) == sizeof(int) ?
					"parameter must be >= %d: %s\n" :
					"parameter must be >= %lld: %s\n"),
					(off_t) conf[i].min, param);
					ret = ERR_OUT_OF_RANGE;
					goto out;
				}

			if (conf[i].flags & CONF_MAX)
				if (tmp_off > conf[i].max) {
					mp_msg(MSGT_CFGPARSER, MSGL_ERR,
					(sizeof(off_t) == sizeof(int) ?
					"parameter must be <= %d: %s\n" :
					"parameter must be <= %lld: %s\n"),
					(off_t) conf[i].max, param);
					ret = ERR_OUT_OF_RANGE;
					goto out;
				}

			*((off_t *) conf[i].p) = tmp_off;
			ret = 1;
			break;
		default:
			mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Unknown config type specified in conf-mplayer.h!\n");
			break;
	}
out:
	if(ret >= 0 && ! IS_RUNNING(config) && ! IS_GLOBAL(config) && ! (conf[i].flags & CONF_GLOBAL) && conf[i].type != CONF_TYPE_SUBCONFIG ) {
	  play_tree_t* dest = config->last_entry ? config->last_entry : config->last_parent;
	  char* o;
#ifdef MP_DEBUG
	  assert(dest != NULL);
#endif
	  if(config->sub_conf) {
	    o = (char*)malloc((strlen(config->sub_conf) + 1 + strlen(opt) + 1)*sizeof(char));
	    sprintf(o,"%s:%s",config->sub_conf,opt);
	  } else
	    o =strdup(opt);

	  if(ret == 0)
	    play_tree_set_param(dest,o,NULL);
	  else if(ret > 0)
	    play_tree_set_param(dest,o,param);
	  free(o);
	  m_config_pop(config); 
	}
	return ret;
err_missing_param:
	mp_msg(MSGT_CFGPARSER, MSGL_ERR, "missing parameter for option: %s\n", opt);
	ret = ERR_MISSING_PARAM;
	goto out;
}

int m_config_set_option(m_config_t *config,char *opt, char *param) {
  char *e;
#ifdef MP_DEBUG
  assert(config != NULL);
  assert(config->opt_list != NULL);
  assert(opt != NULL);
#endif
  mp_msg(MSGT_CFGPARSER, MSGL_DBG2, "Setting option %s=%s\n",opt,param);
  e = strchr(opt,':');
  if(e && e[1] != '\0') {
    int ret;
    config_t* opt_list[] = { NULL, NULL };
    char* s = (char*)malloc((e-opt+1)*sizeof(char));
    strncpy(s,opt,e-opt);
    s[e-opt] = '\0';
    opt_list[0] = m_config_get_option_ptr(config,s);
    if(!opt_list[0]) {
      mp_msg(MSGT_CFGPARSER, MSGL_ERR,"m_config_set_option %s=%s : no %s subconfig\n",opt,param,s);
      free(s);
      return ERR_NOT_AN_OPTION;
    }
    e++;
    s = (char*)realloc(s,strlen(e) + 1);
    strcpy(s,e);
    ret = config_read_option(config,opt_list,s,param);
    free(s);
    return ret;
  }

  return config_read_option(config,config->opt_list,opt,param);
}

int m_config_parse_config_file(m_config_t *config, char *conffile)
{
#define PRINT_LINENUM	mp_msg(MSGT_CFGPARSER,MSGL_INFO,"%s(%d): ", conffile, line_num)
#define MAX_LINE_LEN	1000
#define MAX_OPT_LEN	100
#define MAX_PARAM_LEN	100
	FILE *fp;
	char *line;
	char opt[MAX_OPT_LEN + 1];
	char param[MAX_PARAM_LEN + 1];
	char c;		/* for the "" and '' check */
	int tmp;
	int line_num = 0;
	int line_pos;	/* line pos */
	int opt_pos;	/* opt pos */
	int param_pos;	/* param pos */
	int ret = 1;
	int errors = 0;

#ifdef MP_DEBUG
	assert(config != NULL);
	//	assert(conf_list != NULL);
#endif
	if (++config->recursion_depth > 1)
		mp_msg(MSGT_CFGPARSER,MSGL_INFO,"Reading config file: %s", conffile);

	if (config->recursion_depth > MAX_RECURSION_DEPTH) {
		mp_msg(MSGT_CFGPARSER,MSGL_ERR,": too deep 'include'. check your configfiles\n");
		ret = -1;
		goto out;
	}

	if (init_conf(config, CONFIG_FILE) == -1) {
		ret = -1;
		goto out;
	}

	if ((line = (char *) malloc(MAX_LINE_LEN + 1)) == NULL) {
		mp_msg(MSGT_CFGPARSER,MSGL_FATAL,"\ncan't get memory for 'line': %s", strerror(errno));
		ret = -1;
		goto out;
	}

	if ((fp = fopen(conffile, "r")) == NULL) {
		if (config->recursion_depth > 1)
			mp_msg(MSGT_CFGPARSER,MSGL_ERR,": %s\n", strerror(errno));
		free(line);
		ret = 0;
		goto out;
	}
	if (config->recursion_depth > 1)
		mp_msg(MSGT_CFGPARSER,MSGL_INFO,"\n");

	while (fgets(line, MAX_LINE_LEN, fp)) {
		if (errors >= 16) {
			mp_msg(MSGT_CFGPARSER,MSGL_FATAL,"too many errors\n");
			goto out;
		}

		line_num++;
		line_pos = 0;

		/* skip whitespaces */
		while (isspace(line[line_pos]))
			++line_pos;

		/* EOL / comment */
		if (line[line_pos] == '\0' || line[line_pos] == '#')
			continue;

		/* read option. */
		for (opt_pos = 0; isprint(line[line_pos]) &&
				line[line_pos] != ' ' &&
				line[line_pos] != '#' &&
				line[line_pos] != '='; /* NOTHING */) {
			opt[opt_pos++] = line[line_pos++];
			if (opt_pos >= MAX_OPT_LEN) {
				PRINT_LINENUM;
				mp_msg(MSGT_CFGPARSER,MSGL_ERR,"too long option\n");
				errors++;
				ret = -1;
				goto nextline;
			}
		}
		if (opt_pos == 0) {
			PRINT_LINENUM;
			mp_msg(MSGT_CFGPARSER,MSGL_ERR,"parse error\n");
			ret = -1;
			errors++;
			continue;
		}
		opt[opt_pos] = '\0';

#ifdef MP_DEBUG
		PRINT_LINENUM;
		mp_msg(MSGT_CFGPARSER,MSGL_INFO,"option: %s\n", opt);
#endif

		/* skip whitespaces */
		while (isspace(line[line_pos]))
			++line_pos;

		/* check '=' */
		if (line[line_pos++] != '=') {
			PRINT_LINENUM;
			mp_msg(MSGT_CFGPARSER,MSGL_ERR,"option without parameter\n");
			ret = -1;
			errors++;
			continue;
		}

		/* whitespaces... */
		while (isspace(line[line_pos]))
			++line_pos;

		/* read the parameter */
		if (line[line_pos] == '"' || line[line_pos] == '\'') {
			c = line[line_pos];
			++line_pos;
			for (param_pos = 0; line[line_pos] != c; /* NOTHING */) {
				param[param_pos++] = line[line_pos++];
				if (param_pos >= MAX_PARAM_LEN) {
					PRINT_LINENUM;
					mp_msg(MSGT_CFGPARSER,MSGL_ERR,"too long parameter\n");
					ret = -1;
					errors++;
					goto nextline;
				}
			}
			line_pos++;	/* skip the closing " or ' */
		} else {
			for (param_pos = 0; isprint(line[line_pos]) && !isspace(line[line_pos])
					&& line[line_pos] != '#'; /* NOTHING */) {
				param[param_pos++] = line[line_pos++];
				if (param_pos >= MAX_PARAM_LEN) {
					PRINT_LINENUM;
					mp_msg(MSGT_CFGPARSER,MSGL_ERR,"too long parameter\n");
					ret = -1;
					errors++;
					goto nextline;
				}
			}
		}
		param[param_pos] = '\0';

		/* did we read a parameter? */
		if (param_pos == 0) {
			PRINT_LINENUM;
			mp_msg(MSGT_CFGPARSER,MSGL_ERR,"option without parameter\n");
			ret = -1;
			errors++;
			continue;
		}

#ifdef MP_DEBUG
		PRINT_LINENUM;
		mp_msg(MSGT_CFGPARSER,MSGL_INFO,"parameter: %s\n", param);
#endif

		/* now, check if we have some more chars on the line */
		/* whitespace... */
		while (isspace(line[line_pos]))
			++line_pos;

		/* EOL / comment */
		if (line[line_pos] != '\0' && line[line_pos] != '#') {
			PRINT_LINENUM;
			mp_msg(MSGT_CFGPARSER,MSGL_WARN,"extra characters on line: %s\n", line+line_pos);
			ret = -1;
		}

		tmp = m_config_set_option(config, opt, param);
		switch (tmp) {
		case ERR_NOT_AN_OPTION:
		case ERR_MISSING_PARAM:
		case ERR_OUT_OF_RANGE:
		case ERR_FUNC_ERR:
			PRINT_LINENUM;
			mp_msg(MSGT_CFGPARSER,MSGL_INFO,"%s\n", opt);
			ret = -1;
			errors++;
			continue;
			/* break */
		}	
nextline:
		;
	}

	free(line);
	fclose(fp);
out:
	--config->recursion_depth;
	return ret;
}

int m_config_parse_command_line(m_config_t *config, int argc, char **argv)
{
	int i;
	int tmp;
	char *opt;
	int no_more_opts = 0;

#ifdef MP_DEBUG
	assert(config != NULL);
	assert(config->pt != NULL);
	assert(argv != NULL);
	assert(argc >= 1);
#endif
	
	if (init_conf(config, COMMAND_LINE) == -1)
		return -1;	
	if(config->last_parent == NULL)
	  config->last_parent = config->pt;
	/* in order to work recursion detection properly in parse_config_file */
	++config->recursion_depth;

	for (i = 1; i < argc; i++) {
	  //next:
		opt = argv[i];
		/* check for -- (no more options id.) except --help! */
		if ((*opt == '-') && (*(opt+1) == '-') && (*(opt+2) != 'h'))
		{
			no_more_opts = 1;
			if (i+1 >= argc)
			{
			    mp_msg(MSGT_CFGPARSER, MSGL_ERR, "You added '--' but no filenames presented!\n");
			    goto err_out;
			}
			continue;
		}
		if((opt[0] == '{') && (opt[1] == '\0'))
		  {
		    play_tree_t* entry = play_tree_new();
		    UNSET_GLOBAL(config);		    
		    if(config->last_entry == NULL) {
		      play_tree_set_child(config->last_parent,entry);
		    } else {
		      play_tree_append_entry(config->last_entry,entry);
		      config->last_entry = NULL;
		    }
		    config->last_parent = entry;
		    continue;
		  }

		if((opt[0] == '}') && (opt[1] == '\0'))
		  {
		    if( ! config->last_parent || ! config->last_parent->parent) {
		      mp_msg(MSGT_CFGPARSER, MSGL_ERR, "too much }-\n");
		      goto err_out;
		    }
		    config->last_entry = config->last_parent;
		    config->last_parent = config->last_entry->parent;
		    continue;
		  }
			
		if ((no_more_opts == 0) && (*opt == '-') && (*(opt+1) != 0)) /* option */
		{
		    /* remove trailing '-' */
		    opt++;

		    mp_msg(MSGT_CFGPARSER, MSGL_DBG3, "this_opt = option: %s\n", opt);
		    // We handle here some specific option
		    if(strcasecmp(opt,"list-options") == 0) {
		      m_config_list_options(config);
		      exit(1);
		      // Loop option when it apply to a group
		    } else if(strcasecmp(opt,"loop") == 0 &&
			      (! config->last_entry || config->last_entry->child) ) {
		      int l;
		      char* end;
		      l = strtol(argv[i+1],&end,0);
		      if(!end)
			tmp = ERR_OUT_OF_RANGE;
		      else {
			play_tree_t* pt = config->last_entry ? config->last_entry : config->last_parent;
			l = l <= 0 ? -1 : l;
			pt->loop = l;
			tmp = 1;
		      }
		    } else // All normal options
		      tmp = m_config_set_option(config, opt, argv[i + 1]);

		    switch (tmp) {
		    case ERR_NOT_AN_OPTION:
		    case ERR_MISSING_PARAM:
		    case ERR_OUT_OF_RANGE:
		    case ERR_FUNC_ERR:
		      mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Error: ");
		      m_config_error(tmp,opt,argv[i+1]);
			goto err_out;
		    default:
			i += tmp;
			break;
		    }
		}
		else /* filename */
		{
		    play_tree_t* entry = play_tree_new();
		    mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Adding file %s\n",argv[i]);
		    play_tree_add_file(entry,argv[i]);
		    if(strcasecmp(argv[i],"-") == 0)
		      m_config_set_option(config,"use-stdin",NULL);
		    /* opt is not an option -> treat it as a filename */
		    UNSET_GLOBAL(config); // We start entry specific options
		    if(config->last_entry == NULL)
		      play_tree_set_child(config->last_parent,entry);		      
		    else 
		      play_tree_append_entry(config->last_entry,entry);
		    config->last_entry = entry;
		}
	}

	--config->recursion_depth;
	if(config->last_parent != config->pt)
	  mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Missing }- ?\n");
	config->flags &= (!CONFIG_GLOBAL);
	SET_RUNNING(config);
	return 1; 
#if 0
err_out_mem:
	mp_msg(MSGT_CFGPARSER, MSGL_ERR, "can't allocate memory for filenames (%s)\n", strerror(errno));
#endif
err_out:
	--config->recursion_depth;
	mp_msg(MSGT_CFGPARSER, MSGL_ERR, "command line: %s\n", argv[i]);
	return -1;
}

int
m_config_register_options(m_config_t *config,config_t *args) {
  int list_len = 0;
  config_t** conf_list = config->opt_list;

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(args != NULL);
#endif
  
  if(conf_list) {
    for ( ; conf_list[list_len] != NULL; list_len++)
      /* NOTHING */;
  }
  
  conf_list = (config_t**)realloc(conf_list,sizeof(struct conf*)*(list_len+2));
  if(conf_list == NULL) {
    mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Can't allocate %d bytes of memory : %s\n",sizeof(struct conf*)*(list_len+2),strerror(errno));
    return 0;
  }
  conf_list[list_len] = args;
  conf_list[list_len+1] = NULL;

  config->opt_list = conf_list;

  return 1;
}

config_t*
m_config_get_option(m_config_t *config, char* arg) {
  int i,j;
  char *e;
  config_t *conf;
  config_t **conf_list;
  config_t* cl[] = { NULL, NULL };

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(arg != NULL);
#endif

  e = strchr(arg,':');

  if(e) {
    char *s;
    s = (char*)malloc((e-arg+1)*sizeof(char));
    strncpy(s,arg,e-arg);
    s[e-arg] = '\0';
    cl[0] = m_config_get_option(config,s);
    conf_list = cl;
    free(s);
  } else
    conf_list = config->opt_list;

  if(conf_list) {
    for(j = 0 ; conf_list[j] != NULL ; j++) {
      conf = conf_list[j];
      for(i=0; conf[i].name != NULL; i++) {
	if(strcasecmp(conf[i].name,arg) == 0)
	  return &conf[i];
      }
    }
  }
  return NULL;
}

void*
m_config_get_option_ptr(m_config_t *config, char* arg) {
  config_t* conf;

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(arg != NULL);
#endif

  conf = m_config_get_option(config,arg);
  if(!conf) return NULL;
  return conf->p;
}

int
m_config_get_int (m_config_t *config, char* arg,int* err_ret) {
  int *ret;

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(arg != NULL);
#endif

  ret = m_config_get_option_ptr(config,arg);
  if(err_ret)
    *err_ret = 0;
  if(!ret) {
    if(err_ret)
      *err_ret = 1;
    return -1;
  } else
    return (*ret);
}

float
m_config_get_float (m_config_t *config, char* arg,int* err_ret) {
  float *ret;

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(arg != NULL);
#endif

  ret = m_config_get_option_ptr(config,arg);
  if(err_ret)
    *err_ret = 0;
  if(!ret) {
    if(err_ret)
      *err_ret = 1;
    return -1;
  } else
    return (*ret);
}

#define AS_INT(c) (*((int*)c->p))

int
m_config_set_int(m_config_t *config, char* arg,int val) {
  config_t* opt;

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(arg != NULL);
#endif

  opt = m_config_get_option(config,arg);

  if(!opt || opt->type != CONF_TYPE_INT)
    return ERR_NOT_AN_OPTION;

  if(opt->flags & CONF_MIN && val < opt->min)
    return ERR_OUT_OF_RANGE;
  if(opt->flags & CONF_MAX && val > opt->max)
    return ERR_OUT_OF_RANGE;

  m_config_save_option(config,opt,arg,NULL);
  AS_INT(opt) = val;

  return 1;
}

int
m_config_set_float(m_config_t *config, char* arg,float val) {
  config_t* opt;

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(arg != NULL);
#endif

  opt = m_config_get_option(config,arg);

  if(!opt || opt->type != CONF_TYPE_FLOAT)
    return ERR_NOT_AN_OPTION;

  if(opt->flags & CONF_MIN && val < opt->min)
    return ERR_OUT_OF_RANGE;
  if(opt->flags & CONF_MAX && val > opt->max)
    return ERR_OUT_OF_RANGE;

  m_config_save_option(config,opt,arg,NULL);
  *((float*)opt->p) = val;

  return 1;
}


int
m_config_switch_flag(m_config_t *config, char* opt) {
  config_t *conf;
 
#ifdef MP_DEBUG
  assert(config != NULL);
  assert(opt != NULL);
#endif
 
  conf = m_config_get_option(config,opt);
  if(!conf || conf->type != CONF_TYPE_FLAG) return 0;
  if( AS_INT(conf) == conf->min) AS_INT(conf) = conf->max;
  else if(AS_INT(conf) == conf->max) AS_INT(conf) = conf->min;
  else return 0;

  return 1;
}
    
int
m_config_set_flag(m_config_t *config, char* opt, int state) {
  config_t *conf;

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(opt != NULL);
#endif

  conf = m_config_get_option(config,opt);
  if(!conf || conf->type != CONF_TYPE_FLAG) return 0;
  if(state) AS_INT(conf) = conf->max;
  else AS_INT(conf) = conf->min;
  return 1;
}

int
m_config_get_flag(m_config_t *config, char* opt) {
  config_t *conf;

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(opt != NULL);
#endif

  conf = m_config_get_option(config,opt);
  if(!conf || conf->type != CONF_TYPE_FLAG) return -1;
  if(AS_INT(conf) == conf->max)
    return 1;
  else if(AS_INT(conf) == conf->min)
    return 0;
  else
    return -1;
}

int m_config_is_option_set(m_config_t *config, char* arg) {
  config_t* opt;
  config_save_t* save;
  int l,i;

#ifdef MP_DEBUG
  assert(config != NULL);
  assert(arg != NULL);
#endif

  opt = m_config_get_option(config,arg);

  if(!opt) 
    return -1;

  for(l = config->cs_level ; l >= 0 ; l--) {
    save = config->config_stack[l];
    if(!save) 
      continue;
    for(i = 0 ; save[i].opt != NULL ; i++) {
      if(save[i].opt == opt)
	return 1;
    }
  }

  return 0;
}

#undef AS_INT

static void m_config_print_option_list(char* prefix, config_t* opt_list) {
  char* pf = NULL;
  config_t* opt;
  char min[50],max[50],*type;

  
  for(opt = opt_list ; opt->name != NULL ; opt++) {
    if(opt->type == CONF_TYPE_SUBCONFIG) {
      if(prefix) {
	pf = (char*)malloc(strlen(prefix) + strlen(opt->name) + 1);
	sprintf(pf,"%s:%s",prefix,opt->name);
      } else
	pf = strdup(opt->name);
      m_config_print_option_list(pf,(config_t*)opt->p);
      free(pf);
      continue;
    }
    if(prefix)
      printf("%1.15s:",prefix);
    if(opt->flags & CONF_MIN)
      sprintf(min,"%-8.0f",opt->min);
    else
      strcpy(min,"No");
    if(opt->flags & CONF_MAX)
      sprintf(max,"%-8.0f",opt->max);
    else
      strcpy(max,"No");
    switch(opt->type) {
    case  CONF_TYPE_FLAG:
      type = "Flag";
      break;
    case CONF_TYPE_INT:
      type = "Integer";
      break;
    case CONF_TYPE_FLOAT:
      type = "Float";
      break;
    case CONF_TYPE_STRING:
      type = "String";
      break;
    case CONF_TYPE_FUNC:
    case CONF_TYPE_FUNC_PARAM:
    case CONF_TYPE_FUNC_FULL:
      type = "Function";
      break;
    case CONF_TYPE_PRINT:
      type = "Print";
      break;
    case CONF_TYPE_STRING_LIST:
      type = "String list";
      break;
    default:
      type = "";
      break;
    }
    printf("%-*.15s  %-13.13s %-10.10s %-10.10s %-3.3s %-3.3s %-3.3s\n",
	   30 - (prefix ? strlen(prefix) + 1 : 0),
	   opt->name,
	   type,
	   min,
	   max,
	   opt->flags & CONF_GLOBAL ? "Yes" : "No",
	   opt->flags & CONF_NOCMD ? "No" : "Yes",
	   opt->flags & CONF_NOCFG ? "No" : "Yes");
  }

}


static void m_config_list_options(m_config_t *config) {
  int i;
                                                                         
  printf("\nName                            Type          Min        Max       Glob CL  Cfg\n\n");
  for(i = 0; config->opt_list[i] ; i++)
    m_config_print_option_list(NULL,config->opt_list[i]);
}
  


static void m_config_error(int err,char* opt,char* val) {
  switch(err) {
  case ERR_NOT_AN_OPTION:
    mp_msg(MSGT_CFGPARSER, MSGL_ERR,"'%s' is not a mplayer/mencoder option\n",opt);
    break;
  case ERR_MISSING_PARAM:
    mp_msg(MSGT_CFGPARSER, MSGL_ERR,"option '%s' need a parameter\n",opt);
    break;
  case ERR_OUT_OF_RANGE:
    mp_msg(MSGT_CFGPARSER, MSGL_ERR,"value '%s' of option '%s' is out of range\n",val,opt);
    break;
  case ERR_FUNC_ERR:
    mp_msg(MSGT_CFGPARSER, MSGL_ERR,"while parsing option '%s'\n",opt);
    break;
  }
}

#endif