Merge svn changes up to r30876
[mplayer/kovensky.git] / m_config.c
blobdaac69018da1444e4fef18f498781e955005bb4a
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"
36 #include "help_mp.h"
38 #define MAX_PROFILE_DEPTH 20
40 static int
41 parse_profile(const m_option_t *opt, const char *name, const char *param, void *dst, int src);
43 static void
44 set_profile(const m_option_t *opt, void* dst, const void* src);
46 static int
47 show_profile(m_option_t *opt, char* name, char *param);
49 static void
50 m_config_add_option(m_config_t *config, const m_option_t *arg, const char* prefix);
52 static int
53 list_options(m_option_t *opt, char* name, char *param);
55 static void m_option_save(const m_config_t *config, const m_option_t *opt,
56 void *dst)
58 if (opt->type->save) {
59 const void *src = opt->new ? (char*)config->optstruct + opt->offset : opt->p;
60 opt->type->save(opt, dst, src);
64 static void m_option_set(const m_config_t *config, const m_option_t *opt,
65 const void *src)
67 if (opt->type->set) {
68 void *dst = opt->new ? (char*)config->optstruct + opt->offset : opt->p;
69 opt->type->set(opt, dst, src);
75 m_config_t *m_config_new(void *optstruct,
76 int includefunc(m_option_t *conf, char *filename))
78 m_config_t* config;
79 static int initialized = 0;
80 static m_option_type_t profile_opt_type;
81 static const m_option_t ref_opts[] = {
82 { "profile", NULL, &profile_opt_type, CONF_NOSAVE, 0, 0, NULL },
83 { "show-profile", show_profile, CONF_TYPE_PRINT_FUNC, CONF_NOCFG, 0, 0, NULL },
84 { "list-options", list_options, CONF_TYPE_PRINT_FUNC, CONF_NOCFG, 0, 0, NULL },
85 { NULL, NULL, NULL, 0, 0, 0, NULL }
87 int i;
89 config = talloc_zero(NULL, m_config_t);
90 config->lvl = 1; // 0 Is the defaults
91 if(!initialized) {
92 initialized = 1;
93 profile_opt_type = m_option_type_string_list;
94 profile_opt_type.parse = parse_profile;
95 profile_opt_type.set = set_profile;
97 m_option_t *self_opts = talloc_memdup(config, ref_opts, sizeof(ref_opts));
98 for (i = 0; self_opts[i].name; i++)
99 self_opts[i].priv = config;
100 m_config_register_options(config, self_opts);
101 if (includefunc) {
102 struct m_option *p = talloc_ptrtype(config, p);
103 *p = (struct m_option){"include", includefunc, CONF_TYPE_FUNC_PARAM,
104 CONF_NOSAVE, 0, 0, config};
105 m_config_add_option(config, p, NULL);
107 config->optstruct = optstruct;
109 return config;
112 void m_config_free(m_config_t* config)
114 m_config_option_t *opt;
115 for (opt = config->opts; opt; opt = opt->next) {
116 if (opt->flags & M_CFG_OPT_ALIAS)
117 continue;
118 m_config_save_slot_t *sl;
119 for (sl = opt->slots; sl; sl = sl->prev)
120 m_option_free(opt->opt, sl->data);
122 talloc_free(config);
125 void
126 m_config_push(m_config_t* config) {
127 m_config_option_t *co;
128 m_config_save_slot_t *slot;
130 #ifdef MP_DEBUG
131 assert(config != NULL);
132 assert(config->lvl > 0);
133 #endif
135 config->lvl++;
137 for(co = config->opts ; co ; co = co->next ) {
138 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
139 continue;
140 if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
141 continue;
142 if((co->opt->flags & M_OPT_OLD) && !(co->flags & M_CFG_OPT_SET))
143 continue;
144 if(co->flags & M_CFG_OPT_ALIAS)
145 continue;
147 // Update the current status
148 m_option_save(config, co->opt, co->slots->data);
150 // Allocate a new slot
151 slot = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
152 co->opt->type->size);
153 slot->lvl = config->lvl;
154 slot->prev = co->slots;
155 co->slots = slot;
156 m_option_copy(co->opt,co->slots->data,co->slots->prev->data);
157 // Reset our set flag
158 co->flags &= ~M_CFG_OPT_SET;
161 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config pushed level is now %d\n",config->lvl);
164 void
165 m_config_pop(m_config_t* config) {
166 m_config_option_t *co;
167 m_config_save_slot_t *slot;
169 #ifdef MP_DEBUG
170 assert(config != NULL);
171 assert(config->lvl > 1);
172 #endif
174 for(co = config->opts ; co ; co = co->next ) {
175 int pop = 0;
176 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
177 continue;
178 if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
179 continue;
180 if(co->flags & M_CFG_OPT_ALIAS)
181 continue;
182 if(co->slots->lvl > config->lvl)
183 mp_tmsg(MSGT_CFGPARSER, MSGL_WARN,"Save slot found from lvl %d is too old: %d !!!\n",config->lvl,co->slots->lvl);
185 while(co->slots->lvl >= config->lvl) {
186 m_option_free(co->opt,co->slots->data);
187 slot = co->slots;
188 co->slots = slot->prev;
189 talloc_free(slot);
190 pop++;
192 if(pop) // We removed some ctx -> set the previous value
193 m_option_set(config, co->opt, co->slots->data);
196 config->lvl--;
197 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config poped level=%d\n",config->lvl);
200 static void
201 m_config_add_option(m_config_t *config, const m_option_t *arg, const char* prefix) {
202 m_config_option_t *co;
203 m_config_save_slot_t* sl;
205 #ifdef MP_DEBUG
206 assert(config != NULL);
207 assert(config->lvl > 0);
208 assert(arg != NULL);
209 #endif
211 // Allocate a new entry for this option
212 co = talloc_zero_size(config, sizeof(m_config_option_t) + arg->type->size);
213 co->opt = arg;
215 // Fill in the full name
216 if(prefix && strlen(prefix) > 0) {
217 co->name = talloc_asprintf(co, "%s:%s", prefix, arg->name);
218 } else
219 co->name = arg->name;
221 // Option with children -> add them
222 if(arg->type->flags & M_OPT_TYPE_HAS_CHILD) {
223 const m_option_t *ol = arg->p;
224 int i;
225 co->slots = NULL;
226 for(i = 0 ; ol[i].name != NULL ; i++)
227 m_config_add_option(config,&ol[i], co->name);
228 } else {
229 m_config_option_t *i;
230 // Check if there is already an option pointing to this address
231 if(arg->p || arg->new && arg->offset >= 0) {
232 for(i = config->opts ; i ; i = i->next ) {
233 if (arg->new ? (i->opt->new && i->opt->offset == arg->offset)
234 : (!i->opt->new && i->opt->p == arg->p)) {
235 // So we don't save the same vars more than 1 time
236 co->slots = i->slots;
237 co->flags |= M_CFG_OPT_ALIAS;
238 break;
242 if(!(co->flags & M_CFG_OPT_ALIAS)) {
243 // Allocate a slot for the defaults
244 sl = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
245 arg->type->size);
246 m_option_save(config, arg, sl->data);
247 // Hack to avoid too much trouble with dynamically allocated data :
248 // We always use a dynamic version
249 if ((arg->type->flags & M_OPT_TYPE_DYNAMIC)) {
250 char **hackptr = arg->new ? (char*)config->optstruct + arg->offset
251 : arg->p;
252 if (hackptr && *hackptr) {
253 *hackptr = NULL;
254 m_option_set(config, arg, sl->data);
257 sl->lvl = 0;
258 sl->prev = NULL;
259 co->slots = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
260 arg->type->size);
261 co->slots->prev = sl;
262 co->slots->lvl = config->lvl;
263 m_option_copy(co->opt, co->slots->data, sl->data);
266 co->next = config->opts;
267 config->opts = co;
271 m_config_register_options(m_config_t *config, const m_option_t *args) {
272 int i;
274 #ifdef MP_DEBUG
275 assert(config != NULL);
276 assert(config->lvl > 0);
277 assert(args != NULL);
278 #endif
280 for(i = 0 ; args[i].name != NULL ; i++)
281 m_config_add_option(config,&args[i],NULL);
283 return 1;
286 static m_config_option_t*
287 m_config_get_co(m_config_t *config, char* arg) {
288 m_config_option_t *co;
290 for(co = config->opts ; co ; co = co->next ) {
291 int l = strlen(co->name) - 1;
292 if((co->opt->type->flags & M_OPT_TYPE_ALLOW_WILDCARD) &&
293 (co->name[l] == '*')) {
294 if(strncasecmp(co->name,arg,l) == 0)
295 return co;
296 } else if(strcasecmp(co->name,arg) == 0)
297 return co;
299 return NULL;
302 static int
303 m_config_parse_option(m_config_t *config, char* arg, char* param,int set) {
304 m_config_option_t *co;
305 int r = 0;
307 #ifdef MP_DEBUG
308 assert(config != NULL);
309 assert(config->lvl > 0);
310 assert(arg != NULL);
311 #endif
313 co = m_config_get_co(config,arg);
314 if(!co){
315 // mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Unknown option: %s\n",arg);
316 return M_OPT_UNKNOWN;
319 #ifdef MP_DEBUG
320 // This is the only mandatory function
321 assert(co->opt->type->parse);
322 #endif
324 // Check if this option isn't forbidden in the current mode
325 if((config->mode == M_CONFIG_FILE) && (co->opt->flags & M_OPT_NOCFG)) {
326 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"The %s option can't be used in a config file.\n",arg);
327 return M_OPT_INVALID;
329 if((config->mode == M_COMMAND_LINE) && (co->opt->flags & M_OPT_NOCMD)) {
330 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"The %s option can't be used on the command line.\n",arg);
331 return M_OPT_INVALID;
333 // During command line preparse set only pre-parse options
334 // Otherwise only set pre-parse option if they were not already set.
335 if(((config->mode == M_COMMAND_LINE_PRE_PARSE) &&
336 !(co->opt->flags & M_OPT_PRE_PARSE)) ||
337 ((config->mode != M_COMMAND_LINE_PRE_PARSE) &&
338 (co->opt->flags & M_OPT_PRE_PARSE) && (co->flags & M_CFG_OPT_SET)))
339 set = 0;
341 // Option with children are a bit different to parse
342 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD) {
343 char** lst = NULL;
344 int i,sr;
345 // Parse the child options
346 r = m_option_parse(co->opt,arg,param,&lst,M_COMMAND_LINE);
347 // Set them now
348 if(r >= 0)
349 for(i = 0 ; lst && lst[2*i] ; i++) {
350 int l = strlen(co->name) + 1 + strlen(lst[2*i]) + 1;
351 if(r >= 0) {
352 // Build the full name
353 char n[l];
354 sprintf(n,"%s:%s",co->name,lst[2*i]);
355 sr = m_config_parse_option(config,n,lst[2*i+1],set);
356 if(sr < 0){
357 if(sr == M_OPT_UNKNOWN){
358 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"Error: option '%s' has no suboption '%s'.\n",co->name,lst[2*i]);
359 r = M_OPT_INVALID;
360 } else
361 if(sr == M_OPT_MISSING_PARAM){
362 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"Error: suboption '%s' of '%s' must have a parameter!\n",lst[2*i],co->name);
363 r = M_OPT_INVALID;
364 } else
365 r = sr;
368 free(lst[2*i]);
369 free(lst[2*i+1]);
371 if(lst) free(lst);
372 } else
373 r = m_option_parse(co->opt,arg,param,set ? co->slots->data : NULL,config->mode);
375 // Parsing failed ?
376 if(r < 0)
377 return r;
378 // Set the option
379 if(set) {
380 m_option_set(config, co->opt, co->slots->data);
381 co->flags |= M_CFG_OPT_SET;
384 return r;
388 m_config_set_option(m_config_t *config, char* arg, char* param) {
389 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Setting %s=%s\n",arg,param);
390 return m_config_parse_option(config,arg,param,1);
394 m_config_check_option(m_config_t *config, char* arg, char* param) {
395 int r;
396 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Checking %s=%s\n",arg,param);
397 r=m_config_parse_option(config,arg,param,0);
398 if(r==M_OPT_MISSING_PARAM){
399 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"Error: option '%s' must have a parameter!\n",arg);
400 return M_OPT_INVALID;
402 return r;
406 const m_option_t*
407 m_config_get_option(m_config_t *config, char* arg) {
408 m_config_option_t *co;
410 #ifdef MP_DEBUG
411 assert(config != NULL);
412 assert(config->lvl > 0);
413 assert(arg != NULL);
414 #endif
416 co = m_config_get_co(config,arg);
417 if(co)
418 return co->opt;
419 else
420 return NULL;
423 void
424 m_config_print_option_list(m_config_t *config) {
425 char min[50],max[50];
426 m_config_option_t* co;
427 int count = 0;
429 if(!config->opts) return;
431 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "\n Name Type Min Max Global CL Cfg\n\n");
432 for(co = config->opts ; co ; co = co->next) {
433 const m_option_t* opt = co->opt;
434 if(opt->type->flags & M_OPT_TYPE_HAS_CHILD) continue;
435 if(opt->flags & M_OPT_MIN)
436 sprintf(min,"%-8.0f",opt->min);
437 else
438 strcpy(min,"No");
439 if(opt->flags & M_OPT_MAX)
440 sprintf(max,"%-8.0f",opt->max);
441 else
442 strcpy(max,"No");
443 mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %-20.20s %-15.15s %-10.10s %-10.10s %-3.3s %-3.3s %-3.3s\n",
444 co->name,
445 co->opt->type->name,
446 min,
447 max,
448 opt->flags & CONF_GLOBAL ? "Yes" : "No",
449 opt->flags & CONF_NOCMD ? "No" : "Yes",
450 opt->flags & CONF_NOCFG ? "No" : "Yes");
451 count++;
453 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "\nTotal: %d options\n",count);
456 m_profile_t*
457 m_config_get_profile(m_config_t* config, char* name) {
458 m_profile_t* p;
459 for(p = config->profiles ; p ; p = p->next)
460 if(!strcmp(p->name,name)) return p;
461 return NULL;
464 m_profile_t*
465 m_config_add_profile(m_config_t* config, char* name) {
466 m_profile_t* p = m_config_get_profile(config,name);
467 if(p) return p;
468 p = talloc_zero(config, m_profile_t);
469 p->name = talloc_strdup(p, name);
470 p->next = config->profiles;
471 config->profiles = p;
472 return p;
475 void
476 m_profile_set_desc(m_profile_t* p, char* desc) {
477 talloc_free(p->desc);
478 p->desc = talloc_strdup(p, desc);
482 m_config_set_profile_option(m_config_t* config, m_profile_t* p,
483 char* name, char* val) {
484 int i = m_config_check_option(config,name,val);
485 if(i < 0) return i;
486 p->opts = talloc_realloc(p, p->opts, char *, 2*(p->num_opts+2));
487 p->opts[p->num_opts*2] = talloc_strdup(p, name);
488 p->opts[p->num_opts*2+1] = talloc_strdup(p, val);
489 p->num_opts++;
490 p->opts[p->num_opts*2] = p->opts[p->num_opts*2+1] = NULL;
491 return 1;
494 void
495 m_config_set_profile(m_config_t* config, m_profile_t* p) {
496 int i;
497 if(config->profile_depth > MAX_PROFILE_DEPTH) {
498 mp_tmsg(MSGT_CFGPARSER, MSGL_WARN, "WARNING: Profile inclusion too deep.\n");
499 return;
501 config->profile_depth++;
502 for(i = 0 ; i < p->num_opts ; i++)
503 m_config_set_option(config,p->opts[2*i],p->opts[2*i+1]);
504 config->profile_depth--;
507 static int
508 parse_profile(const m_option_t *opt, const char *name, const char *param, void *dst, int src)
510 m_config_t* config = opt->priv;
511 char** list = NULL;
512 int i,r;
513 if(param && !strcmp(param,"help")) {
514 m_profile_t* p;
515 if(!config->profiles) {
516 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "No profiles have been defined.\n");
517 return M_OPT_EXIT-1;
519 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "Available profiles:\n");
520 for(p = config->profiles ; p ; p = p->next)
521 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\t%s\t%s\n",p->name,
522 p->desc ? p->desc : "");
523 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
524 return M_OPT_EXIT-1;
527 r = m_option_type_string_list.parse(opt,name,param,&list,src);
528 if(r < 0) return r;
529 if(!list || !list[0]) return M_OPT_INVALID;
530 for(i = 0 ; list[i] ; i++)
531 if(!m_config_get_profile(config,list[i])) {
532 mp_tmsg(MSGT_CFGPARSER, MSGL_WARN, "Unknown profile '%s'.\n",
533 list[i]);
534 r = M_OPT_INVALID;
536 if(dst)
537 m_option_copy(opt,dst,&list);
538 else
539 m_option_free(opt,&list);
540 return r;
543 static void
544 set_profile(const m_option_t *opt, void *dst, const void *src) {
545 m_config_t* config = opt->priv;
546 m_profile_t* p;
547 char** list = NULL;
548 int i;
549 if(!src || !*(char***)src) return;
550 m_option_copy(opt,&list,src);
551 for(i = 0 ; list[i] ; i++) {
552 p = m_config_get_profile(config,list[i]);
553 if(!p) continue;
554 m_config_set_profile(config,p);
556 m_option_free(opt,&list);
559 static int
560 show_profile(m_option_t *opt, char* name, char *param) {
561 m_config_t* config = opt->priv;
562 m_profile_t* p;
563 int i,j;
564 if(!param) return M_OPT_MISSING_PARAM;
565 if(!(p = m_config_get_profile(config,param))) {
566 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR, "Unknown profile '%s'.\n", param);
567 return M_OPT_EXIT-1;
569 if(!config->profile_depth)
570 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "Profile %s: %s\n", param,
571 p->desc ? p->desc : "");
572 config->profile_depth++;
573 for(i = 0 ; i < p->num_opts ; i++) {
574 char spc[config->profile_depth+1];
575 for(j = 0 ; j < config->profile_depth ; j++)
576 spc[j] = ' ';
577 spc[config->profile_depth] = '\0';
579 mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s%s=%s\n", spc,
580 p->opts[2*i], p->opts[2*i+1]);
583 if(config->profile_depth < MAX_PROFILE_DEPTH &&
584 !strcmp(p->opts[2*i],"profile")) {
585 char* e,*list = p->opts[2*i+1];
586 while((e = strchr(list,','))) {
587 int l = e-list;
588 char tmp[l+1];
589 if(!l) continue;
590 memcpy(tmp,list,l);
591 tmp[l] = '\0';
592 show_profile(opt,name,tmp);
593 list = e+1;
595 if(list[0] != '\0')
596 show_profile(opt,name,list);
599 config->profile_depth--;
600 if(!config->profile_depth) mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
601 return M_OPT_EXIT-1;
604 static int
605 list_options(m_option_t *opt, char* name, char *param) {
606 m_config_t* config = opt->priv;
607 m_config_print_option_list(config);
608 return M_OPT_EXIT;