Mercurial > mplayer.hg
view cfgparser.c @ 7732:328bbac6224c
Fixes:
- missing check in init
- missing brackets causing failure
- nas_aformat_to_auformat not working properly
- fix hang that was finally reproducible with high disk activity
- don't cut of audio on uninit(), wait for buffer to empty
It also simplifies the event_handler, making it more readable and
implements Sidik Isani's suggestion to make the buffer size dependent on
bytes per second. I've been using it for two days and found no further
problems.
patch by Tobias Diedrich <td@sim.uni-hannover.de>
author | arpi |
---|---|
date | Sun, 13 Oct 2002 22:00:15 +0000 |
parents | 411886c03c54 |
children | b571ae470520 |
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" #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" 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; tmp_float = strtod(param, &endptr); switch(*endptr) { case ':': case '/': tmp_float /= strtod(endptr+1, &endptr); break; case '.': case ',': /* we also handle floats specified with * non-locale decimal point ::atmos */ if(tmp_float<0) tmp_float -= 1.0/pow(10,strlen(endptr+1)) * strtod(endptr+1, &endptr); else tmp_float += 1.0/pow(10,strlen(endptr+1)) * strtod(endptr+1, &endptr); break; } 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+1)*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, &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; } }