options: fix -ar default back to enabled if available
[mplayer.git] / m_config.c
blob2cbe777d6884be95b1207bac3e785546173dba66
2 /// \file
3 /// \ingroup Config
5 #include "config.h"
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include <string.h>
11 #include "talloc.h"
12 #ifdef MP_DEBUG
13 #include <assert.h>
14 #endif
16 #include "m_config.h"
17 #include "m_option.h"
18 #include "mp_msg.h"
19 #include "help_mp.h"
21 #define MAX_PROFILE_DEPTH 20
23 static int
24 parse_profile(const m_option_t *opt, const char *name, char *param, void *dst, int src);
26 static void
27 set_profile(const m_option_t *opt, void* dst, void* src);
29 static int
30 show_profile(m_option_t *opt, char* name, char *param);
32 static void
33 m_config_add_option(m_config_t *config, const m_option_t *arg, const char* prefix);
35 static int
36 list_options(m_option_t *opt, char* name, char *param);
38 static void m_option_save(const m_config_t *config, const m_option_t *opt,
39 void *dst)
41 if (opt->type->save) {
42 void *src = opt->new ? (char*)config->optstruct + opt->offset : opt->p;
43 opt->type->save(opt, dst, src);
47 static void m_option_set(const m_config_t *config, const m_option_t *opt,
48 void *src)
50 if (opt->type->set) {
51 void *dst = opt->new ? (char*)config->optstruct + opt->offset : opt->p;
52 opt->type->set(opt, dst, src);
58 m_config_t *m_config_new(void *optstruct,
59 int includefunc(m_option_t *conf, char *filename))
61 m_config_t* config;
62 static int initialized = 0;
63 static m_option_type_t profile_opt_type;
64 static const m_option_t ref_opts[] = {
65 { "profile", NULL, &profile_opt_type, CONF_NOSAVE, 0, 0, NULL },
66 { "show-profile", show_profile, CONF_TYPE_PRINT_FUNC, CONF_NOCFG, 0, 0, NULL },
67 { "list-options", list_options, CONF_TYPE_PRINT_FUNC, CONF_NOCFG, 0, 0, NULL },
68 { NULL, NULL, NULL, 0, 0, 0, NULL }
70 int i;
72 config = talloc_zero(NULL, m_config_t);
73 config->lvl = 1; // 0 Is the defaults
74 if(!initialized) {
75 initialized = 1;
76 profile_opt_type = m_option_type_string_list;
77 profile_opt_type.parse = parse_profile;
78 profile_opt_type.set = set_profile;
80 m_option_t *self_opts = talloc_memdup(config, ref_opts, sizeof(ref_opts));
81 for (i = 0; self_opts[i].name; i++)
82 self_opts[i].priv = config;
83 m_config_register_options(config, self_opts);
84 if (includefunc) {
85 struct m_option *p = talloc_ptrtype(config, p);
86 *p = (struct m_option){"include", includefunc, CONF_TYPE_FUNC_PARAM,
87 CONF_NOSAVE, 0, 0, config};
88 m_config_add_option(config, p, NULL);
90 config->optstruct = optstruct;
92 return config;
95 void m_config_free(m_config_t* config)
97 m_config_option_t *opt;
98 for (opt = config->opts; opt; opt = opt->next) {
99 if (opt->flags & M_CFG_OPT_ALIAS)
100 continue;
101 m_config_save_slot_t *sl;
102 for (sl = opt->slots; sl; sl = sl->prev)
103 m_option_free(opt->opt, sl->data);
105 talloc_free(config);
108 void
109 m_config_push(m_config_t* config) {
110 m_config_option_t *co;
111 m_config_save_slot_t *slot;
113 #ifdef MP_DEBUG
114 assert(config != NULL);
115 assert(config->lvl > 0);
116 #endif
118 config->lvl++;
120 for(co = config->opts ; co ; co = co->next ) {
121 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
122 continue;
123 if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
124 continue;
125 if((co->opt->flags & M_OPT_OLD) && !(co->flags & M_CFG_OPT_SET))
126 continue;
127 if(co->flags & M_CFG_OPT_ALIAS)
128 continue;
130 // Update the current status
131 m_option_save(config, co->opt, co->slots->data);
133 // Allocate a new slot
134 slot = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
135 co->opt->type->size);
136 slot->lvl = config->lvl;
137 slot->prev = co->slots;
138 co->slots = slot;
139 m_option_copy(co->opt,co->slots->data,co->slots->prev->data);
140 // Reset our set flag
141 co->flags &= ~M_CFG_OPT_SET;
144 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config pushed level is now %d\n",config->lvl);
147 void
148 m_config_pop(m_config_t* config) {
149 m_config_option_t *co;
150 m_config_save_slot_t *slot;
152 #ifdef MP_DEBUG
153 assert(config != NULL);
154 assert(config->lvl > 1);
155 #endif
157 for(co = config->opts ; co ; co = co->next ) {
158 int pop = 0;
159 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
160 continue;
161 if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
162 continue;
163 if(co->flags & M_CFG_OPT_ALIAS)
164 continue;
165 if(co->slots->lvl > config->lvl)
166 mp_msg(MSGT_CFGPARSER, MSGL_WARN,MSGTR_SaveSlotTooOld,config->lvl,co->slots->lvl);
168 while(co->slots->lvl >= config->lvl) {
169 m_option_free(co->opt,co->slots->data);
170 slot = co->slots;
171 co->slots = slot->prev;
172 talloc_free(slot);
173 pop++;
175 if(pop) // We removed some ctx -> set the previous value
176 m_option_set(config, co->opt, co->slots->data);
179 config->lvl--;
180 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config poped level=%d\n",config->lvl);
183 static void
184 m_config_add_option(m_config_t *config, const m_option_t *arg, const char* prefix) {
185 m_config_option_t *co;
186 m_config_save_slot_t* sl;
188 #ifdef MP_DEBUG
189 assert(config != NULL);
190 assert(config->lvl > 0);
191 assert(arg != NULL);
192 #endif
194 // Allocate a new entry for this option
195 co = talloc_zero_size(config, sizeof(m_config_option_t) + arg->type->size);
196 co->opt = arg;
198 // Fill in the full name
199 if(prefix && strlen(prefix) > 0) {
200 co->name = talloc_asprintf(co, "%s:%s", prefix, arg->name);
201 } else
202 co->name = arg->name;
204 // Option with children -> add them
205 if(arg->type->flags & M_OPT_TYPE_HAS_CHILD) {
206 const m_option_t *ol = arg->p;
207 int i;
208 co->slots = NULL;
209 for(i = 0 ; ol[i].name != NULL ; i++)
210 m_config_add_option(config,&ol[i], co->name);
211 } else {
212 m_config_option_t *i;
213 // Check if there is already an option pointing to this address
214 if(arg->p || arg->new && arg->offset >= 0) {
215 for(i = config->opts ; i ; i = i->next ) {
216 if (arg->new ? (i->opt->new && i->opt->offset == arg->offset)
217 : (!i->opt->new && i->opt->p == arg->p)) {
218 // So we don't save the same vars more than 1 time
219 co->slots = i->slots;
220 co->flags |= M_CFG_OPT_ALIAS;
221 break;
225 if(!(co->flags & M_CFG_OPT_ALIAS)) {
226 // Allocate a slot for the defaults
227 sl = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
228 arg->type->size);
229 m_option_save(config, arg, sl->data);
230 // Hack to avoid too much trouble with dynamically allocated data :
231 // We always use a dynamic version
232 if ((arg->type->flags & M_OPT_TYPE_DYNAMIC)) {
233 char **hackptr = arg->new ? (char*)config->optstruct + arg->offset
234 : arg->p;
235 if (hackptr && *hackptr) {
236 *hackptr = NULL;
237 m_option_set(config, arg, sl->data);
240 sl->lvl = 0;
241 sl->prev = NULL;
242 co->slots = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
243 arg->type->size);
244 co->slots->prev = sl;
245 co->slots->lvl = config->lvl;
246 m_option_copy(co->opt, co->slots->data, sl->data);
249 co->next = config->opts;
250 config->opts = co;
254 m_config_register_options(m_config_t *config, const m_option_t *args) {
255 int i;
257 #ifdef MP_DEBUG
258 assert(config != NULL);
259 assert(config->lvl > 0);
260 assert(args != NULL);
261 #endif
263 for(i = 0 ; args[i].name != NULL ; i++)
264 m_config_add_option(config,&args[i],NULL);
266 return 1;
269 static m_config_option_t*
270 m_config_get_co(m_config_t *config, char* arg) {
271 m_config_option_t *co;
273 for(co = config->opts ; co ; co = co->next ) {
274 int l = strlen(co->name) - 1;
275 if((co->opt->type->flags & M_OPT_TYPE_ALLOW_WILDCARD) &&
276 (co->name[l] == '*')) {
277 if(strncasecmp(co->name,arg,l) == 0)
278 return co;
279 } else if(strcasecmp(co->name,arg) == 0)
280 return co;
282 return NULL;
285 static int
286 m_config_parse_option(m_config_t *config, char* arg, char* param,int set) {
287 m_config_option_t *co;
288 int r = 0;
290 #ifdef MP_DEBUG
291 assert(config != NULL);
292 assert(config->lvl > 0);
293 assert(arg != NULL);
294 #endif
296 co = m_config_get_co(config,arg);
297 if(!co){
298 // mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Unknown option: %s\n",arg);
299 return M_OPT_UNKNOWN;
302 #ifdef MP_DEBUG
303 // This is the only mandatory function
304 assert(co->opt->type->parse);
305 #endif
307 // Check if this option isn't forbidden in the current mode
308 if((config->mode == M_CONFIG_FILE) && (co->opt->flags & M_OPT_NOCFG)) {
309 mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_InvalidCfgfileOption,arg);
310 return M_OPT_INVALID;
312 if((config->mode == M_COMMAND_LINE) && (co->opt->flags & M_OPT_NOCMD)) {
313 mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_InvalidCmdlineOption,arg);
314 return M_OPT_INVALID;
316 // During command line preparse set only pre-parse options
317 // Otherwise only set pre-parse option if they were not already set.
318 if(((config->mode == M_COMMAND_LINE_PRE_PARSE) &&
319 !(co->opt->flags & M_OPT_PRE_PARSE)) ||
320 ((config->mode != M_COMMAND_LINE_PRE_PARSE) &&
321 (co->opt->flags & M_OPT_PRE_PARSE) && (co->flags & M_CFG_OPT_SET)))
322 set = 0;
324 // Option with children are a bit different to parse
325 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD) {
326 char** lst = NULL;
327 int i,sr;
328 // Parse the child options
329 r = m_option_parse(co->opt,arg,param,&lst,M_COMMAND_LINE);
330 // Set them now
331 if(r >= 0)
332 for(i = 0 ; lst && lst[2*i] ; i++) {
333 int l = strlen(co->name) + 1 + strlen(lst[2*i]) + 1;
334 if(r >= 0) {
335 // Build the full name
336 char n[l];
337 sprintf(n,"%s:%s",co->name,lst[2*i]);
338 sr = m_config_parse_option(config,n,lst[2*i+1],set);
339 if(sr < 0){
340 if(sr == M_OPT_UNKNOWN){
341 mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_InvalidSuboption,co->name,lst[2*i]);
342 r = M_OPT_INVALID;
343 } else
344 if(sr == M_OPT_MISSING_PARAM){
345 mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_MissingSuboptionParameter,lst[2*i],co->name);
346 r = M_OPT_INVALID;
347 } else
348 r = sr;
351 free(lst[2*i]);
352 free(lst[2*i+1]);
354 if(lst) free(lst);
355 } else
356 r = m_option_parse(co->opt,arg,param,set ? co->slots->data : NULL,config->mode);
358 // Parsing failed ?
359 if(r < 0)
360 return r;
361 // Set the option
362 if(set) {
363 m_option_set(config, co->opt, co->slots->data);
364 co->flags |= M_CFG_OPT_SET;
367 return r;
371 m_config_set_option(m_config_t *config, char* arg, char* param) {
372 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Setting %s=%s\n",arg,param);
373 return m_config_parse_option(config,arg,param,1);
377 m_config_check_option(m_config_t *config, char* arg, char* param) {
378 int r;
379 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Checking %s=%s\n",arg,param);
380 r=m_config_parse_option(config,arg,param,0);
381 if(r==M_OPT_MISSING_PARAM){
382 mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_MissingOptionParameter,arg);
383 return M_OPT_INVALID;
385 return r;
389 const m_option_t*
390 m_config_get_option(m_config_t *config, char* arg) {
391 m_config_option_t *co;
393 #ifdef MP_DEBUG
394 assert(config != NULL);
395 assert(config->lvl > 0);
396 assert(arg != NULL);
397 #endif
399 co = m_config_get_co(config,arg);
400 if(co)
401 return co->opt;
402 else
403 return NULL;
406 void
407 m_config_print_option_list(m_config_t *config) {
408 char min[50],max[50];
409 m_config_option_t* co;
410 int count = 0;
412 if(!config->opts) return;
414 mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_OptionListHeader);
415 for(co = config->opts ; co ; co = co->next) {
416 const m_option_t* opt = co->opt;
417 if(opt->type->flags & M_OPT_TYPE_HAS_CHILD) continue;
418 if(opt->flags & M_OPT_MIN)
419 sprintf(min,"%-8.0f",opt->min);
420 else
421 strcpy(min,"No");
422 if(opt->flags & M_OPT_MAX)
423 sprintf(max,"%-8.0f",opt->max);
424 else
425 strcpy(max,"No");
426 mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %-20.20s %-15.15s %-10.10s %-10.10s %-3.3s %-3.3s %-3.3s\n",
427 co->name,
428 co->opt->type->name,
429 min,
430 max,
431 opt->flags & CONF_GLOBAL ? "Yes" : "No",
432 opt->flags & CONF_NOCMD ? "No" : "Yes",
433 opt->flags & CONF_NOCFG ? "No" : "Yes");
434 count++;
436 mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_TotalOptions,count);
439 m_profile_t*
440 m_config_get_profile(m_config_t* config, char* name) {
441 m_profile_t* p;
442 for(p = config->profiles ; p ; p = p->next)
443 if(!strcmp(p->name,name)) return p;
444 return NULL;
447 m_profile_t*
448 m_config_add_profile(m_config_t* config, char* name) {
449 m_profile_t* p = m_config_get_profile(config,name);
450 if(p) return p;
451 p = talloc_zero(config, m_profile_t);
452 p->name = talloc_strdup(p, name);
453 p->next = config->profiles;
454 config->profiles = p;
455 return p;
458 void
459 m_profile_set_desc(m_profile_t* p, char* desc) {
460 talloc_free(p->desc);
461 p->desc = talloc_strdup(p, desc);
465 m_config_set_profile_option(m_config_t* config, m_profile_t* p,
466 char* name, char* val) {
467 int i = m_config_check_option(config,name,val);
468 if(i < 0) return i;
469 p->opts = talloc_realloc(p, p->opts, char *, 2*(p->num_opts+2));
470 p->opts[p->num_opts*2] = talloc_strdup(p, name);
471 p->opts[p->num_opts*2+1] = talloc_strdup(p, val);
472 p->num_opts++;
473 p->opts[p->num_opts*2] = p->opts[p->num_opts*2+1] = NULL;
474 return 1;
477 void
478 m_config_set_profile(m_config_t* config, m_profile_t* p) {
479 int i;
480 if(config->profile_depth > MAX_PROFILE_DEPTH) {
481 mp_msg(MSGT_CFGPARSER, MSGL_WARN, MSGTR_ProfileInclusionTooDeep);
482 return;
484 config->profile_depth++;
485 for(i = 0 ; i < p->num_opts ; i++)
486 m_config_set_option(config,p->opts[2*i],p->opts[2*i+1]);
487 config->profile_depth--;
490 static int
491 parse_profile(const m_option_t *opt, const char *name, char *param, void *dst, int src)
493 m_config_t* config = opt->priv;
494 char** list = NULL;
495 int i,r;
496 if(param && !strcmp(param,"help")) {
497 m_profile_t* p;
498 if(!config->profiles) {
499 mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_NoProfileDefined);
500 return M_OPT_EXIT-1;
502 mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_AvailableProfiles);
503 for(p = config->profiles ; p ; p = p->next)
504 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\t%s\t%s\n",p->name,
505 p->desc ? p->desc : "");
506 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
507 return M_OPT_EXIT-1;
510 r = m_option_type_string_list.parse(opt,name,param,&list,src);
511 if(r < 0) return r;
512 if(!list || !list[0]) return M_OPT_INVALID;
513 for(i = 0 ; list[i] ; i++)
514 if(!m_config_get_profile(config,list[i])) {
515 mp_msg(MSGT_CFGPARSER, MSGL_WARN, MSGTR_UnknownProfile,
516 list[i]);
517 r = M_OPT_INVALID;
519 if(dst)
520 m_option_copy(opt,dst,&list);
521 else
522 m_option_free(opt,&list);
523 return r;
526 static void
527 set_profile(const m_option_t *opt, void *dst, void *src) {
528 m_config_t* config = opt->priv;
529 m_profile_t* p;
530 char** list = NULL;
531 int i;
532 if(!src || !*(char***)src) return;
533 m_option_copy(opt,&list,src);
534 for(i = 0 ; list[i] ; i++) {
535 p = m_config_get_profile(config,list[i]);
536 if(!p) continue;
537 m_config_set_profile(config,p);
539 m_option_free(opt,&list);
542 static int
543 show_profile(m_option_t *opt, char* name, char *param) {
544 m_config_t* config = opt->priv;
545 m_profile_t* p;
546 int i,j;
547 if(!param) return M_OPT_MISSING_PARAM;
548 if(!(p = m_config_get_profile(config,param))) {
549 mp_msg(MSGT_CFGPARSER, MSGL_ERR, MSGTR_UnknownProfile, param);
550 return M_OPT_EXIT-1;
552 if(!config->profile_depth)
553 mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_Profile, param,
554 p->desc ? p->desc : "");
555 config->profile_depth++;
556 for(i = 0 ; i < p->num_opts ; i++) {
557 char spc[config->profile_depth+1];
558 for(j = 0 ; j < config->profile_depth ; j++)
559 spc[j] = ' ';
560 spc[config->profile_depth] = '\0';
562 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s%s=%s\n", spc,
563 p->opts[2*i], p->opts[2*i+1]);
566 if(config->profile_depth < MAX_PROFILE_DEPTH &&
567 !strcmp(p->opts[2*i],"profile")) {
568 char* e,*list = p->opts[2*i+1];
569 while((e = strchr(list,','))) {
570 int l = e-list;
571 char tmp[l+1];
572 if(!l) continue;
573 memcpy(tmp,list,l);
574 tmp[l] = '\0';
575 show_profile(opt,name,tmp);
576 list = e+1;
578 if(list[0] != '\0')
579 show_profile(opt,name,list);
582 config->profile_depth--;
583 if(!config->profile_depth) mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
584 return M_OPT_EXIT-1;
587 static int
588 list_options(m_option_t *opt, char* name, char *param) {
589 m_config_t* config = opt->priv;
590 m_config_print_option_list(config);
591 return M_OPT_EXIT;