mirror of
https://github.com/mpv-player/mpv
synced 2024-12-26 17:12:36 +00:00
bb12a33f9a
The option system has a hack that converts default values (potentially constants) of dynamically allocated options to allocated ones when the options are first added to the config system, so that all values can be equally freed later. Make this work with new-style options in the option struct too.
607 lines
16 KiB
C
607 lines
16 KiB
C
|
|
/// \file
|
|
/// \ingroup Config
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include "talloc.h"
|
|
#ifdef MP_DEBUG
|
|
#include <assert.h>
|
|
#endif
|
|
|
|
#include "m_config.h"
|
|
#include "m_option.h"
|
|
#include "mp_msg.h"
|
|
#include "help_mp.h"
|
|
|
|
#define MAX_PROFILE_DEPTH 20
|
|
|
|
static int
|
|
parse_profile(const m_option_t *opt, const char *name, char *param, void *dst, int src);
|
|
|
|
static void
|
|
set_profile(const m_option_t *opt, void* dst, void* src);
|
|
|
|
static int
|
|
show_profile(m_option_t *opt, char* name, char *param);
|
|
|
|
static void
|
|
m_config_add_option(m_config_t *config, const m_option_t *arg, const char* prefix);
|
|
|
|
static int
|
|
list_options(m_option_t *opt, char* name, char *param);
|
|
|
|
static void m_option_save(const m_config_t *config, const m_option_t *opt,
|
|
void *dst)
|
|
{
|
|
if (opt->type->save) {
|
|
void *src = opt->new ? (char*)config->optstruct + opt->offset : opt->p;
|
|
opt->type->save(opt, dst, src);
|
|
}
|
|
}
|
|
|
|
static void m_option_set(const m_config_t *config, const m_option_t *opt,
|
|
void *src)
|
|
{
|
|
if (opt->type->set) {
|
|
void *dst = opt->new ? (char*)config->optstruct + opt->offset : opt->p;
|
|
opt->type->set(opt, dst, src);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
m_config_t *m_config_new(void *optstruct,
|
|
int includefunc(m_option_t *conf, char *filename))
|
|
{
|
|
m_config_t* config;
|
|
static int initialized = 0;
|
|
static m_option_type_t profile_opt_type;
|
|
static const m_option_t ref_opts[] = {
|
|
{ "profile", NULL, &profile_opt_type, CONF_NOSAVE, 0, 0, NULL },
|
|
{ "show-profile", show_profile, CONF_TYPE_PRINT_FUNC, CONF_NOCFG, 0, 0, NULL },
|
|
{ "list-options", list_options, CONF_TYPE_PRINT_FUNC, CONF_NOCFG, 0, 0, NULL },
|
|
{ NULL, NULL, NULL, 0, 0, 0, NULL }
|
|
};
|
|
int i;
|
|
|
|
config = talloc_zero(NULL, m_config_t);
|
|
config->lvl = 1; // 0 Is the defaults
|
|
if(!initialized) {
|
|
initialized = 1;
|
|
profile_opt_type = m_option_type_string_list;
|
|
profile_opt_type.parse = parse_profile;
|
|
profile_opt_type.set = set_profile;
|
|
}
|
|
m_option_t *self_opts = talloc_memdup(config, ref_opts, sizeof(ref_opts));
|
|
for (i = 0; self_opts[i].name; i++)
|
|
self_opts[i].priv = config;
|
|
m_config_register_options(config, self_opts);
|
|
if (includefunc) {
|
|
struct m_option *p = talloc_ptrtype(config, p);
|
|
*p = (struct m_option){"include", includefunc, CONF_TYPE_FUNC_PARAM,
|
|
CONF_NOSAVE, 0, 0, config};
|
|
m_config_add_option(config, p, NULL);
|
|
}
|
|
config->optstruct = optstruct;
|
|
|
|
return config;
|
|
}
|
|
|
|
void m_config_free(m_config_t* config)
|
|
{
|
|
m_config_option_t *opt;
|
|
for (opt = config->opts; opt; opt = opt->next) {
|
|
if (opt->flags & M_CFG_OPT_ALIAS)
|
|
continue;
|
|
m_config_save_slot_t *sl;
|
|
for (sl = opt->slots; sl; sl = sl->prev)
|
|
m_option_free(opt->opt, sl->data);
|
|
}
|
|
talloc_free(config);
|
|
}
|
|
|
|
void
|
|
m_config_push(m_config_t* config) {
|
|
m_config_option_t *co;
|
|
m_config_save_slot_t *slot;
|
|
|
|
#ifdef MP_DEBUG
|
|
assert(config != NULL);
|
|
assert(config->lvl > 0);
|
|
#endif
|
|
|
|
config->lvl++;
|
|
|
|
for(co = config->opts ; co ; co = co->next ) {
|
|
if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
|
|
continue;
|
|
if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
|
|
continue;
|
|
if((co->opt->flags & M_OPT_OLD) && !(co->flags & M_CFG_OPT_SET))
|
|
continue;
|
|
if(co->flags & M_CFG_OPT_ALIAS)
|
|
continue;
|
|
|
|
// Update the current status
|
|
m_option_save(config, co->opt, co->slots->data);
|
|
|
|
// Allocate a new slot
|
|
slot = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
|
|
co->opt->type->size);
|
|
slot->lvl = config->lvl;
|
|
slot->prev = co->slots;
|
|
co->slots = slot;
|
|
m_option_copy(co->opt,co->slots->data,co->slots->prev->data);
|
|
// Reset our set flag
|
|
co->flags &= ~M_CFG_OPT_SET;
|
|
}
|
|
|
|
mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config pushed level is now %d\n",config->lvl);
|
|
}
|
|
|
|
void
|
|
m_config_pop(m_config_t* config) {
|
|
m_config_option_t *co;
|
|
m_config_save_slot_t *slot;
|
|
|
|
#ifdef MP_DEBUG
|
|
assert(config != NULL);
|
|
assert(config->lvl > 1);
|
|
#endif
|
|
|
|
for(co = config->opts ; co ; co = co->next ) {
|
|
int pop = 0;
|
|
if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
|
|
continue;
|
|
if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
|
|
continue;
|
|
if(co->flags & M_CFG_OPT_ALIAS)
|
|
continue;
|
|
if(co->slots->lvl > config->lvl)
|
|
mp_msg(MSGT_CFGPARSER, MSGL_WARN,MSGTR_SaveSlotTooOld,config->lvl,co->slots->lvl);
|
|
|
|
while(co->slots->lvl >= config->lvl) {
|
|
m_option_free(co->opt,co->slots->data);
|
|
slot = co->slots;
|
|
co->slots = slot->prev;
|
|
talloc_free(slot);
|
|
pop++;
|
|
}
|
|
if(pop) // We removed some ctx -> set the previous value
|
|
m_option_set(config, co->opt, co->slots->data);
|
|
}
|
|
|
|
config->lvl--;
|
|
mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config poped level=%d\n",config->lvl);
|
|
}
|
|
|
|
static void
|
|
m_config_add_option(m_config_t *config, const m_option_t *arg, const char* prefix) {
|
|
m_config_option_t *co;
|
|
m_config_save_slot_t* sl;
|
|
|
|
#ifdef MP_DEBUG
|
|
assert(config != NULL);
|
|
assert(config->lvl > 0);
|
|
assert(arg != NULL);
|
|
#endif
|
|
|
|
// Allocate a new entry for this option
|
|
co = talloc_zero_size(config, sizeof(m_config_option_t) + arg->type->size);
|
|
co->opt = arg;
|
|
|
|
// Fill in the full name
|
|
if(prefix && strlen(prefix) > 0) {
|
|
co->name = talloc_asprintf(co, "%s:%s", prefix, arg->name);
|
|
} else
|
|
co->name = arg->name;
|
|
|
|
// Option with children -> add them
|
|
if(arg->type->flags & M_OPT_TYPE_HAS_CHILD) {
|
|
const m_option_t *ol = arg->p;
|
|
int i;
|
|
co->slots = NULL;
|
|
for(i = 0 ; ol[i].name != NULL ; i++)
|
|
m_config_add_option(config,&ol[i], co->name);
|
|
} else {
|
|
m_config_option_t *i;
|
|
// Check if there is already an option pointing to this address
|
|
if(arg->p || arg->new && arg->offset >= 0) {
|
|
for(i = config->opts ; i ; i = i->next ) {
|
|
if (arg->new ? (i->opt->new && i->opt->offset == arg->offset)
|
|
: (!i->opt->new && i->opt->p == arg->p)) {
|
|
// So we don't save the same vars more than 1 time
|
|
co->slots = i->slots;
|
|
co->flags |= M_CFG_OPT_ALIAS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!(co->flags & M_CFG_OPT_ALIAS)) {
|
|
// Allocate a slot for the defaults
|
|
sl = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
|
|
arg->type->size);
|
|
m_option_save(config, arg, sl->data);
|
|
// Hack to avoid too much trouble with dynamically allocated data :
|
|
// We always use a dynamic version
|
|
if ((arg->type->flags & M_OPT_TYPE_DYNAMIC)) {
|
|
char **hackptr = arg->new ? (char*)config->optstruct + arg->offset
|
|
: arg->p;
|
|
if (hackptr && *hackptr) {
|
|
*hackptr = NULL;
|
|
m_option_set(config, arg, sl->data);
|
|
}
|
|
}
|
|
sl->lvl = 0;
|
|
sl->prev = NULL;
|
|
co->slots = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
|
|
arg->type->size);
|
|
co->slots->prev = sl;
|
|
co->slots->lvl = config->lvl;
|
|
m_option_copy(co->opt, co->slots->data, sl->data);
|
|
}
|
|
}
|
|
co->next = config->opts;
|
|
config->opts = co;
|
|
}
|
|
|
|
int
|
|
m_config_register_options(m_config_t *config, const m_option_t *args) {
|
|
int i;
|
|
|
|
#ifdef MP_DEBUG
|
|
assert(config != NULL);
|
|
assert(config->lvl > 0);
|
|
assert(args != NULL);
|
|
#endif
|
|
|
|
for(i = 0 ; args[i].name != NULL ; i++)
|
|
m_config_add_option(config,&args[i],NULL);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static m_config_option_t*
|
|
m_config_get_co(m_config_t *config, char* arg) {
|
|
m_config_option_t *co;
|
|
|
|
for(co = config->opts ; co ; co = co->next ) {
|
|
int l = strlen(co->name) - 1;
|
|
if((co->opt->type->flags & M_OPT_TYPE_ALLOW_WILDCARD) &&
|
|
(co->name[l] == '*')) {
|
|
if(strncasecmp(co->name,arg,l) == 0)
|
|
return co;
|
|
} else if(strcasecmp(co->name,arg) == 0)
|
|
return co;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
m_config_parse_option(m_config_t *config, char* arg, char* param,int set) {
|
|
m_config_option_t *co;
|
|
int r = 0;
|
|
|
|
#ifdef MP_DEBUG
|
|
assert(config != NULL);
|
|
assert(config->lvl > 0);
|
|
assert(arg != NULL);
|
|
#endif
|
|
|
|
co = m_config_get_co(config,arg);
|
|
if(!co){
|
|
// mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Unknown option: %s\n",arg);
|
|
return M_OPT_UNKNOWN;
|
|
}
|
|
|
|
#ifdef MP_DEBUG
|
|
// This is the only mandatory function
|
|
assert(co->opt->type->parse);
|
|
#endif
|
|
|
|
// Check if this option isn't forbidden in the current mode
|
|
if((config->mode == M_CONFIG_FILE) && (co->opt->flags & M_OPT_NOCFG)) {
|
|
mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_InvalidCfgfileOption,arg);
|
|
return M_OPT_INVALID;
|
|
}
|
|
if((config->mode == M_COMMAND_LINE) && (co->opt->flags & M_OPT_NOCMD)) {
|
|
mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_InvalidCmdlineOption,arg);
|
|
return M_OPT_INVALID;
|
|
}
|
|
// During command line preparse set only pre-parse options
|
|
// Otherwise only set pre-parse option if they were not already set.
|
|
if(((config->mode == M_COMMAND_LINE_PRE_PARSE) &&
|
|
!(co->opt->flags & M_OPT_PRE_PARSE)) ||
|
|
((config->mode != M_COMMAND_LINE_PRE_PARSE) &&
|
|
(co->opt->flags & M_OPT_PRE_PARSE) && (co->flags & M_CFG_OPT_SET)))
|
|
set = 0;
|
|
|
|
// Option with children are a bit different to parse
|
|
if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD) {
|
|
char** lst = NULL;
|
|
int i,sr;
|
|
// Parse the child options
|
|
r = m_option_parse(co->opt,arg,param,&lst,M_COMMAND_LINE);
|
|
// Set them now
|
|
if(r >= 0)
|
|
for(i = 0 ; lst && lst[2*i] ; i++) {
|
|
int l = strlen(co->name) + 1 + strlen(lst[2*i]) + 1;
|
|
if(r >= 0) {
|
|
// Build the full name
|
|
char n[l];
|
|
sprintf(n,"%s:%s",co->name,lst[2*i]);
|
|
sr = m_config_parse_option(config,n,lst[2*i+1],set);
|
|
if(sr < 0){
|
|
if(sr == M_OPT_UNKNOWN){
|
|
mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_InvalidSuboption,co->name,lst[2*i]);
|
|
r = M_OPT_INVALID;
|
|
} else
|
|
if(sr == M_OPT_MISSING_PARAM){
|
|
mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_MissingSuboptionParameter,lst[2*i],co->name);
|
|
r = M_OPT_INVALID;
|
|
} else
|
|
r = sr;
|
|
}
|
|
}
|
|
free(lst[2*i]);
|
|
free(lst[2*i+1]);
|
|
}
|
|
if(lst) free(lst);
|
|
} else
|
|
r = m_option_parse(co->opt,arg,param,set ? co->slots->data : NULL,config->mode);
|
|
|
|
// Parsing failed ?
|
|
if(r < 0)
|
|
return r;
|
|
// Set the option
|
|
if(set) {
|
|
m_option_set(config, co->opt, co->slots->data);
|
|
co->flags |= M_CFG_OPT_SET;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int
|
|
m_config_set_option(m_config_t *config, char* arg, char* param) {
|
|
mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Setting %s=%s\n",arg,param);
|
|
return m_config_parse_option(config,arg,param,1);
|
|
}
|
|
|
|
int
|
|
m_config_check_option(m_config_t *config, char* arg, char* param) {
|
|
int r;
|
|
mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Checking %s=%s\n",arg,param);
|
|
r=m_config_parse_option(config,arg,param,0);
|
|
if(r==M_OPT_MISSING_PARAM){
|
|
mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_MissingOptionParameter,arg);
|
|
return M_OPT_INVALID;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
const m_option_t*
|
|
m_config_get_option(m_config_t *config, char* arg) {
|
|
m_config_option_t *co;
|
|
|
|
#ifdef MP_DEBUG
|
|
assert(config != NULL);
|
|
assert(config->lvl > 0);
|
|
assert(arg != NULL);
|
|
#endif
|
|
|
|
co = m_config_get_co(config,arg);
|
|
if(co)
|
|
return co->opt;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
const void*
|
|
m_config_get_option_ptr(m_config_t *config, char* arg) {
|
|
const m_option_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;
|
|
}
|
|
|
|
void
|
|
m_config_print_option_list(m_config_t *config) {
|
|
char min[50],max[50];
|
|
m_config_option_t* co;
|
|
int count = 0;
|
|
|
|
if(!config->opts) return;
|
|
|
|
mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_OptionListHeader);
|
|
for(co = config->opts ; co ; co = co->next) {
|
|
const m_option_t* opt = co->opt;
|
|
if(opt->type->flags & M_OPT_TYPE_HAS_CHILD) continue;
|
|
if(opt->flags & M_OPT_MIN)
|
|
sprintf(min,"%-8.0f",opt->min);
|
|
else
|
|
strcpy(min,"No");
|
|
if(opt->flags & M_OPT_MAX)
|
|
sprintf(max,"%-8.0f",opt->max);
|
|
else
|
|
strcpy(max,"No");
|
|
mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %-20.20s %-15.15s %-10.10s %-10.10s %-3.3s %-3.3s %-3.3s\n",
|
|
co->name,
|
|
co->opt->type->name,
|
|
min,
|
|
max,
|
|
opt->flags & CONF_GLOBAL ? "Yes" : "No",
|
|
opt->flags & CONF_NOCMD ? "No" : "Yes",
|
|
opt->flags & CONF_NOCFG ? "No" : "Yes");
|
|
count++;
|
|
}
|
|
mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_TotalOptions,count);
|
|
}
|
|
|
|
m_profile_t*
|
|
m_config_get_profile(m_config_t* config, char* name) {
|
|
m_profile_t* p;
|
|
for(p = config->profiles ; p ; p = p->next)
|
|
if(!strcmp(p->name,name)) return p;
|
|
return NULL;
|
|
}
|
|
|
|
m_profile_t*
|
|
m_config_add_profile(m_config_t* config, char* name) {
|
|
m_profile_t* p = m_config_get_profile(config,name);
|
|
if(p) return p;
|
|
p = talloc_zero(config, m_profile_t);
|
|
p->name = talloc_strdup(p, name);
|
|
p->next = config->profiles;
|
|
config->profiles = p;
|
|
return p;
|
|
}
|
|
|
|
void
|
|
m_profile_set_desc(m_profile_t* p, char* desc) {
|
|
talloc_free(p->desc);
|
|
p->desc = talloc_strdup(p, desc);
|
|
}
|
|
|
|
int
|
|
m_config_set_profile_option(m_config_t* config, m_profile_t* p,
|
|
char* name, char* val) {
|
|
int i = m_config_check_option(config,name,val);
|
|
if(i < 0) return i;
|
|
p->opts = talloc_realloc(p, p->opts, char *, 2*(p->num_opts+2));
|
|
p->opts[p->num_opts*2] = talloc_strdup(p, name);
|
|
p->opts[p->num_opts*2+1] = talloc_strdup(p, val);
|
|
p->num_opts++;
|
|
p->opts[p->num_opts*2] = p->opts[p->num_opts*2+1] = NULL;
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
m_config_set_profile(m_config_t* config, m_profile_t* p) {
|
|
int i;
|
|
if(config->profile_depth > MAX_PROFILE_DEPTH) {
|
|
mp_msg(MSGT_CFGPARSER, MSGL_WARN, MSGTR_ProfileInclusionTooDeep);
|
|
return;
|
|
}
|
|
config->profile_depth++;
|
|
for(i = 0 ; i < p->num_opts ; i++)
|
|
m_config_set_option(config,p->opts[2*i],p->opts[2*i+1]);
|
|
config->profile_depth--;
|
|
}
|
|
|
|
static int
|
|
parse_profile(const m_option_t *opt, const char *name, char *param, void *dst, int src)
|
|
{
|
|
m_config_t* config = opt->priv;
|
|
char** list = NULL;
|
|
int i,r;
|
|
if(param && !strcmp(param,"help")) {
|
|
m_profile_t* p;
|
|
if(!config->profiles) {
|
|
mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_NoProfileDefined);
|
|
return M_OPT_EXIT-1;
|
|
}
|
|
mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_AvailableProfiles);
|
|
for(p = config->profiles ; p ; p = p->next)
|
|
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\t%s\t%s\n",p->name,
|
|
p->desc ? p->desc : "");
|
|
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
|
|
return M_OPT_EXIT-1;
|
|
}
|
|
|
|
r = m_option_type_string_list.parse(opt,name,param,&list,src);
|
|
if(r < 0) return r;
|
|
if(!list || !list[0]) return M_OPT_INVALID;
|
|
for(i = 0 ; list[i] ; i++)
|
|
if(!m_config_get_profile(config,list[i])) {
|
|
mp_msg(MSGT_CFGPARSER, MSGL_WARN, MSGTR_UnknownProfile,
|
|
list[i]);
|
|
r = M_OPT_INVALID;
|
|
}
|
|
if(dst)
|
|
m_option_copy(opt,dst,&list);
|
|
else
|
|
m_option_free(opt,&list);
|
|
return r;
|
|
}
|
|
|
|
static void
|
|
set_profile(const m_option_t *opt, void *dst, void *src) {
|
|
m_config_t* config = opt->priv;
|
|
m_profile_t* p;
|
|
char** list = NULL;
|
|
int i;
|
|
if(!src || !*(char***)src) return;
|
|
m_option_copy(opt,&list,src);
|
|
for(i = 0 ; list[i] ; i++) {
|
|
p = m_config_get_profile(config,list[i]);
|
|
if(!p) continue;
|
|
m_config_set_profile(config,p);
|
|
}
|
|
m_option_free(opt,&list);
|
|
}
|
|
|
|
static int
|
|
show_profile(m_option_t *opt, char* name, char *param) {
|
|
m_config_t* config = opt->priv;
|
|
m_profile_t* p;
|
|
int i,j;
|
|
if(!param) return M_OPT_MISSING_PARAM;
|
|
if(!(p = m_config_get_profile(config,param))) {
|
|
mp_msg(MSGT_CFGPARSER, MSGL_ERR, MSGTR_UnknownProfile, param);
|
|
return M_OPT_EXIT-1;
|
|
}
|
|
if(!config->profile_depth)
|
|
mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_Profile, param,
|
|
p->desc ? p->desc : "");
|
|
config->profile_depth++;
|
|
for(i = 0 ; i < p->num_opts ; i++) {
|
|
char spc[config->profile_depth+1];
|
|
for(j = 0 ; j < config->profile_depth ; j++)
|
|
spc[j] = ' ';
|
|
spc[config->profile_depth] = '\0';
|
|
|
|
mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s%s=%s\n", spc,
|
|
p->opts[2*i], p->opts[2*i+1]);
|
|
|
|
|
|
if(config->profile_depth < MAX_PROFILE_DEPTH &&
|
|
!strcmp(p->opts[2*i],"profile")) {
|
|
char* e,*list = p->opts[2*i+1];
|
|
while((e = strchr(list,','))) {
|
|
int l = e-list;
|
|
char tmp[l+1];
|
|
if(!l) continue;
|
|
memcpy(tmp,list,l);
|
|
tmp[l] = '\0';
|
|
show_profile(opt,name,tmp);
|
|
list = e+1;
|
|
}
|
|
if(list[0] != '\0')
|
|
show_profile(opt,name,list);
|
|
}
|
|
}
|
|
config->profile_depth--;
|
|
if(!config->profile_depth) mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
|
|
return M_OPT_EXIT-1;
|
|
}
|
|
|
|
static int
|
|
list_options(m_option_t *opt, char* name, char *param) {
|
|
m_config_t* config = opt->priv;
|
|
m_config_print_option_list(config);
|
|
return M_OPT_EXIT;
|
|
}
|