Copyright, LICENSE: change binary license to GPL 3
[mplayer.git] / m_config.c
blob0f5151aed57326b4e2a97ed9c3b129d80e6a02d2
1 /*
2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 /// \file
20 /// \ingroup Config
22 #include "config.h"
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include <string.h>
28 #include "talloc.h"
29 #ifdef MP_DEBUG
30 #include <assert.h>
31 #endif
33 #include "m_config.h"
34 #include "m_option.h"
35 #include "mp_msg.h"
37 #define MAX_PROFILE_DEPTH 20
39 static int parse_profile(const m_option_t *opt, const char *name,
40 const char *param, void *dst, int src)
42 m_config_t *config = opt->priv;
43 char **list = NULL;
44 int i, r;
45 if (param && !strcmp(param, "help")) {
46 m_profile_t *p;
47 if (!config->profiles) {
48 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "No profiles have been defined.\n");
49 return M_OPT_EXIT-1;
51 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "Available profiles:\n");
52 for (p = config->profiles; p; p = p->next)
53 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\t%s\t%s\n", p->name,
54 p->desc ? p->desc : "");
55 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
56 return M_OPT_EXIT-1;
59 r = m_option_type_string_list.parse(opt, name, param, &list, src);
60 if (r < 0)
61 return r;
62 if (!list || !list[0])
63 return M_OPT_INVALID;
64 for (i = 0; list[i]; i++)
65 if (!m_config_get_profile(config,list[i])) {
66 mp_tmsg(MSGT_CFGPARSER, MSGL_WARN, "Unknown profile '%s'.\n",
67 list[i]);
68 r = M_OPT_INVALID;
70 if (dst)
71 m_option_copy(opt, dst, &list);
72 else
73 m_option_free(opt, &list);
74 return r;
77 static void set_profile(const m_option_t *opt, void *dst, const void *src)
79 m_config_t *config = opt->priv;
80 m_profile_t *p;
81 char **list = NULL;
82 int i;
83 if (!src || !*(char***)src)
84 return;
85 m_option_copy(opt, &list, src);
86 for (i = 0; list[i]; i++) {
87 p = m_config_get_profile(config, list[i]);
88 if (!p)
89 continue;
90 m_config_set_profile(config, p);
92 m_option_free(opt, &list);
95 static int show_profile(m_option_t *opt, char* name, char *param)
97 m_config_t *config = opt->priv;
98 m_profile_t *p;
99 int i, j;
100 if (!param)
101 return M_OPT_MISSING_PARAM;
102 if (!(p = m_config_get_profile(config, param))) {
103 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR, "Unknown profile '%s'.\n", param);
104 return M_OPT_EXIT - 1;
106 if (!config->profile_depth)
107 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "Profile %s: %s\n", param,
108 p->desc ? p->desc : "");
109 config->profile_depth++;
110 for (i = 0; i < p->num_opts; i++) {
111 char spc[config->profile_depth + 1];
112 for (j = 0; j < config->profile_depth; j++)
113 spc[j] = ' ';
114 spc[config->profile_depth] = '\0';
116 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s%s=%s\n", spc,
117 p->opts[2 * i], p->opts[2 * i + 1]);
119 if (config->profile_depth < MAX_PROFILE_DEPTH
120 && !strcmp(p->opts[2*i], "profile")) {
121 char *e, *list = p->opts[2 * i + 1];
122 while ((e = strchr(list, ','))) {
123 int l = e-list;
124 char tmp[l+1];
125 if (!l)
126 continue;
127 memcpy(tmp, list, l);
128 tmp[l] = '\0';
129 show_profile(opt, name, tmp);
130 list = e+1;
132 if (list[0] != '\0')
133 show_profile(opt, name, list);
136 config->profile_depth--;
137 if (!config->profile_depth)
138 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
139 return M_OPT_EXIT - 1;
142 static int list_options(m_option_t *opt, char *name, char *param)
144 m_config_t *config = opt->priv;
145 m_config_print_option_list(config);
146 return M_OPT_EXIT;
149 static void m_option_save(const m_config_t *config, const m_option_t *opt,
150 void *dst)
152 if (opt->type->save) {
153 const void *src = m_option_get_ptr(opt, config->optstruct);
154 opt->type->save(opt, dst, src);
158 static void m_option_set(const m_config_t *config, const m_option_t *opt,
159 const void *src)
161 if (opt->type->set) {
162 void *dst = m_option_get_ptr(opt, config->optstruct);
163 opt->type->set(opt, dst, src);
169 static void
170 m_config_add_option(m_config_t *config, const m_option_t *arg, const char* prefix);
172 m_config_t *m_config_new(void *optstruct,
173 int includefunc(m_option_t *conf, char *filename))
175 m_config_t* config;
176 static int initialized = 0;
177 static m_option_type_t profile_opt_type;
178 static const m_option_t ref_opts[] = {
179 { "profile", NULL, &profile_opt_type, CONF_NOSAVE, 0, 0, NULL },
180 { "show-profile", show_profile, CONF_TYPE_PRINT_FUNC, CONF_NOCFG, 0, 0, NULL },
181 { "list-options", list_options, CONF_TYPE_PRINT_FUNC, CONF_NOCFG, 0, 0, NULL },
182 { NULL, NULL, NULL, 0, 0, 0, NULL }
184 int i;
186 config = talloc_zero(NULL, m_config_t);
187 config->lvl = 1; // 0 Is the defaults
188 if(!initialized) {
189 initialized = 1;
190 profile_opt_type = m_option_type_string_list;
191 profile_opt_type.parse = parse_profile;
192 profile_opt_type.set = set_profile;
194 m_option_t *self_opts = talloc_memdup(config, ref_opts, sizeof(ref_opts));
195 for (i = 0; self_opts[i].name; i++)
196 self_opts[i].priv = config;
197 m_config_register_options(config, self_opts);
198 if (includefunc) {
199 struct m_option *p = talloc_ptrtype(config, p);
200 *p = (struct m_option){"include", includefunc, CONF_TYPE_FUNC_PARAM,
201 CONF_NOSAVE, 0, 0, config};
202 m_config_add_option(config, p, NULL);
204 config->optstruct = optstruct;
206 return config;
209 void m_config_free(m_config_t* config)
211 m_config_option_t *opt;
212 for (opt = config->opts; opt; opt = opt->next) {
213 if (opt->flags & M_CFG_OPT_ALIAS)
214 continue;
215 m_config_save_slot_t *sl;
216 for (sl = opt->slots; sl; sl = sl->prev)
217 m_option_free(opt->opt, sl->data);
219 talloc_free(config);
222 void
223 m_config_push(m_config_t* config) {
224 m_config_option_t *co;
225 m_config_save_slot_t *slot;
227 #ifdef MP_DEBUG
228 assert(config != NULL);
229 assert(config->lvl > 0);
230 #endif
232 config->lvl++;
234 for(co = config->opts ; co ; co = co->next ) {
235 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
236 continue;
237 if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
238 continue;
239 if(co->flags & M_CFG_OPT_ALIAS)
240 continue;
242 // Update the current status
243 m_option_save(config, co->opt, co->slots->data);
245 // Allocate a new slot
246 slot = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
247 co->opt->type->size);
248 slot->lvl = config->lvl;
249 slot->prev = co->slots;
250 co->slots = slot;
251 m_option_copy(co->opt,co->slots->data,co->slots->prev->data);
252 // Reset our set flag
253 co->flags &= ~M_CFG_OPT_SET;
256 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config pushed level is now %d\n",config->lvl);
259 void
260 m_config_pop(m_config_t* config) {
261 m_config_option_t *co;
262 m_config_save_slot_t *slot;
264 #ifdef MP_DEBUG
265 assert(config != NULL);
266 assert(config->lvl > 1);
267 #endif
269 for(co = config->opts ; co ; co = co->next ) {
270 int pop = 0;
271 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
272 continue;
273 if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
274 continue;
275 if(co->flags & M_CFG_OPT_ALIAS)
276 continue;
277 if(co->slots->lvl > config->lvl)
278 mp_tmsg(MSGT_CFGPARSER, MSGL_WARN,"Save slot found from lvl %d is too old: %d !!!\n",config->lvl,co->slots->lvl);
280 while(co->slots->lvl >= config->lvl) {
281 m_option_free(co->opt,co->slots->data);
282 slot = co->slots;
283 co->slots = slot->prev;
284 talloc_free(slot);
285 pop++;
287 if(pop) // We removed some ctx -> set the previous value
288 m_option_set(config, co->opt, co->slots->data);
291 config->lvl--;
292 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config poped level=%d\n",config->lvl);
295 static void
296 m_config_add_option(m_config_t *config, const m_option_t *arg, const char* prefix) {
297 m_config_option_t *co;
298 m_config_save_slot_t* sl;
300 #ifdef MP_DEBUG
301 assert(config != NULL);
302 assert(config->lvl > 0);
303 assert(arg != NULL);
304 #endif
306 // Allocate a new entry for this option
307 co = talloc_zero_size(config, sizeof(m_config_option_t) + arg->type->size);
308 co->opt = arg;
310 // Fill in the full name
311 if(prefix && strlen(prefix) > 0) {
312 co->name = talloc_asprintf(co, "%s:%s", prefix, arg->name);
313 } else
314 co->name = arg->name;
316 // Option with children -> add them
317 if(arg->type->flags & M_OPT_TYPE_HAS_CHILD) {
318 const m_option_t *ol = arg->p;
319 int i;
320 co->slots = NULL;
321 for(i = 0 ; ol[i].name != NULL ; i++)
322 m_config_add_option(config,&ol[i], co->name);
323 } else {
324 m_config_option_t *i;
325 // Check if there is already an option pointing to this address
326 if(arg->p || arg->new && arg->offset >= 0) {
327 for(i = config->opts ; i ; i = i->next ) {
328 if (arg->new ? (i->opt->new && i->opt->offset == arg->offset)
329 : (!i->opt->new && i->opt->p == arg->p)) {
330 // So we don't save the same vars more than 1 time
331 co->slots = i->slots;
332 co->flags |= M_CFG_OPT_ALIAS;
333 break;
337 if(!(co->flags & M_CFG_OPT_ALIAS)) {
338 // Allocate a slot for the defaults
339 sl = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
340 arg->type->size);
341 m_option_save(config, arg, sl->data);
342 // Hack to avoid too much trouble with dynamically allocated data :
343 // We always use a dynamic version
344 if ((arg->type->flags & M_OPT_TYPE_DYNAMIC)) {
345 char **hackptr = arg->new ? (char*)config->optstruct + arg->offset
346 : arg->p;
347 if (hackptr && *hackptr) {
348 *hackptr = NULL;
349 m_option_set(config, arg, sl->data);
352 sl->lvl = 0;
353 sl->prev = NULL;
354 co->slots = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
355 arg->type->size);
356 co->slots->prev = sl;
357 co->slots->lvl = config->lvl;
358 m_option_copy(co->opt, co->slots->data, sl->data);
361 co->next = config->opts;
362 config->opts = co;
366 m_config_register_options(m_config_t *config, const m_option_t *args) {
367 int i;
369 #ifdef MP_DEBUG
370 assert(config != NULL);
371 assert(config->lvl > 0);
372 assert(args != NULL);
373 #endif
375 for(i = 0 ; args[i].name != NULL ; i++)
376 m_config_add_option(config,&args[i],NULL);
378 return 1;
381 static m_config_option_t*
382 m_config_get_co(const m_config_t *config, char *arg) {
383 m_config_option_t *co;
385 for(co = config->opts ; co ; co = co->next ) {
386 int l = strlen(co->name) - 1;
387 if((co->opt->type->flags & M_OPT_TYPE_ALLOW_WILDCARD) &&
388 (co->name[l] == '*')) {
389 if(strncasecmp(co->name,arg,l) == 0)
390 return co;
391 } else if(strcasecmp(co->name,arg) == 0)
392 return co;
394 return NULL;
397 static int
398 m_config_parse_option(const m_config_t *config, char *arg, char *param, int set) {
399 m_config_option_t *co;
400 int r = 0;
402 #ifdef MP_DEBUG
403 assert(config != NULL);
404 assert(config->lvl > 0);
405 assert(arg != NULL);
406 #endif
408 co = m_config_get_co(config,arg);
409 if(!co){
410 // mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Unknown option: %s\n",arg);
411 return M_OPT_UNKNOWN;
414 #ifdef MP_DEBUG
415 // This is the only mandatory function
416 assert(co->opt->type->parse);
417 #endif
419 // Check if this option isn't forbidden in the current mode
420 if((config->mode == M_CONFIG_FILE) && (co->opt->flags & M_OPT_NOCFG)) {
421 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"The %s option can't be used in a config file.\n",arg);
422 return M_OPT_INVALID;
424 if((config->mode == M_COMMAND_LINE) && (co->opt->flags & M_OPT_NOCMD)) {
425 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"The %s option can't be used on the command line.\n",arg);
426 return M_OPT_INVALID;
428 // During command line preparse set only pre-parse options
429 // Otherwise only set pre-parse option if they were not already set.
430 if(((config->mode == M_COMMAND_LINE_PRE_PARSE) &&
431 !(co->opt->flags & M_OPT_PRE_PARSE)) ||
432 ((config->mode != M_COMMAND_LINE_PRE_PARSE) &&
433 (co->opt->flags & M_OPT_PRE_PARSE) && (co->flags & M_CFG_OPT_SET)))
434 set = 0;
436 // Option with children are a bit different to parse
437 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD) {
438 char** lst = NULL;
439 int i,sr;
440 // Parse the child options
441 r = m_option_parse(co->opt,arg,param,&lst,M_COMMAND_LINE);
442 // Set them now
443 if(r >= 0)
444 for(i = 0 ; lst && lst[2*i] ; i++) {
445 int l = strlen(co->name) + 1 + strlen(lst[2*i]) + 1;
446 if(r >= 0) {
447 // Build the full name
448 char n[l];
449 sprintf(n,"%s:%s",co->name,lst[2*i]);
450 sr = m_config_parse_option(config,n,lst[2*i+1],set);
451 if(sr < 0){
452 if(sr == M_OPT_UNKNOWN){
453 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"Error: option '%s' has no suboption '%s'.\n",co->name,lst[2*i]);
454 r = M_OPT_INVALID;
455 } else
456 if(sr == M_OPT_MISSING_PARAM){
457 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"Error: suboption '%s' of '%s' must have a parameter!\n",lst[2*i],co->name);
458 r = M_OPT_INVALID;
459 } else
460 r = sr;
463 free(lst[2*i]);
464 free(lst[2*i+1]);
466 free(lst);
467 } else
468 r = m_option_parse(co->opt,arg,param,set ? co->slots->data : NULL,config->mode);
470 // Parsing failed ?
471 if(r < 0)
472 return r;
473 // Set the option
474 if(set) {
475 m_option_set(config, co->opt, co->slots->data);
476 co->flags |= M_CFG_OPT_SET;
479 return r;
483 m_config_set_option(m_config_t *config, char* arg, char* param) {
484 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Setting %s=%s\n",arg,param);
485 return m_config_parse_option(config,arg,param,1);
489 m_config_check_option(const m_config_t *config, char *arg, char *param) {
490 int r;
491 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Checking %s=%s\n",arg,param);
492 r=m_config_parse_option(config,arg,param,0);
493 if(r==M_OPT_MISSING_PARAM){
494 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"Error: option '%s' must have a parameter!\n",arg);
495 return M_OPT_INVALID;
497 return r;
501 const m_option_t*
502 m_config_get_option(const m_config_t *config, char *arg) {
503 m_config_option_t *co;
505 #ifdef MP_DEBUG
506 assert(config != NULL);
507 assert(config->lvl > 0);
508 assert(arg != NULL);
509 #endif
511 co = m_config_get_co(config,arg);
512 if(co)
513 return co->opt;
514 else
515 return NULL;
518 void
519 m_config_print_option_list(const m_config_t *config) {
520 char min[50],max[50];
521 m_config_option_t* co;
522 int count = 0;
524 if(!config->opts) return;
526 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "\n Name Type Min Max Global CL Cfg\n\n");
527 for(co = config->opts ; co ; co = co->next) {
528 const m_option_t* opt = co->opt;
529 if(opt->type->flags & M_OPT_TYPE_HAS_CHILD) continue;
530 if(opt->flags & M_OPT_MIN)
531 sprintf(min,"%-8.0f",opt->min);
532 else
533 strcpy(min,"No");
534 if(opt->flags & M_OPT_MAX)
535 sprintf(max,"%-8.0f",opt->max);
536 else
537 strcpy(max,"No");
538 mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %-20.20s %-15.15s %-10.10s %-10.10s %-3.3s %-3.3s %-3.3s\n",
539 co->name,
540 co->opt->type->name,
541 min,
542 max,
543 opt->flags & CONF_GLOBAL ? "Yes" : "No",
544 opt->flags & CONF_NOCMD ? "No" : "Yes",
545 opt->flags & CONF_NOCFG ? "No" : "Yes");
546 count++;
548 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "\nTotal: %d options\n",count);
551 m_profile_t*
552 m_config_get_profile(const m_config_t *config, char *name) {
553 m_profile_t* p;
554 for(p = config->profiles ; p ; p = p->next)
555 if(!strcmp(p->name,name)) return p;
556 return NULL;
559 m_profile_t*
560 m_config_add_profile(m_config_t* config, char* name) {
561 m_profile_t* p = m_config_get_profile(config,name);
562 if(p) return p;
563 p = talloc_zero(config, m_profile_t);
564 p->name = talloc_strdup(p, name);
565 p->next = config->profiles;
566 config->profiles = p;
567 return p;
570 void
571 m_profile_set_desc(m_profile_t* p, char* desc) {
572 talloc_free(p->desc);
573 p->desc = talloc_strdup(p, desc);
577 m_config_set_profile_option(m_config_t* config, m_profile_t* p,
578 char* name, char* val) {
579 int i = m_config_check_option(config,name,val);
580 if(i < 0) return i;
581 p->opts = talloc_realloc(p, p->opts, char *, 2*(p->num_opts+2));
582 p->opts[p->num_opts*2] = talloc_strdup(p, name);
583 p->opts[p->num_opts*2+1] = talloc_strdup(p, val);
584 p->num_opts++;
585 p->opts[p->num_opts*2] = p->opts[p->num_opts*2+1] = NULL;
586 return 1;
589 void
590 m_config_set_profile(m_config_t* config, m_profile_t* p) {
591 int i;
592 if(config->profile_depth > MAX_PROFILE_DEPTH) {
593 mp_tmsg(MSGT_CFGPARSER, MSGL_WARN, "WARNING: Profile inclusion too deep.\n");
594 return;
596 int prev_mode = config->mode;
597 config->mode = M_CONFIG_FILE;
598 config->profile_depth++;
599 for(i = 0 ; i < p->num_opts ; i++)
600 m_config_set_option(config,p->opts[2*i],p->opts[2*i+1]);
601 config->profile_depth--;
602 config->mode = prev_mode;