Change some filename-handling code to use mp_basename()
[mplayer/greg.git] / m_config.c
blob266ee1032378c86e524a78466e3021c6ee3df8b1
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 = opt->new ? (char*)config->optstruct + opt->offset : opt->p;
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 = opt->new ? (char*)config->optstruct + opt->offset : opt->p;
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->opt->flags & M_OPT_OLD) && !(co->flags & M_CFG_OPT_SET))
240 continue;
241 if(co->flags & M_CFG_OPT_ALIAS)
242 continue;
244 // Update the current status
245 m_option_save(config, co->opt, co->slots->data);
247 // Allocate a new slot
248 slot = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
249 co->opt->type->size);
250 slot->lvl = config->lvl;
251 slot->prev = co->slots;
252 co->slots = slot;
253 m_option_copy(co->opt,co->slots->data,co->slots->prev->data);
254 // Reset our set flag
255 co->flags &= ~M_CFG_OPT_SET;
258 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config pushed level is now %d\n",config->lvl);
261 void
262 m_config_pop(m_config_t* config) {
263 m_config_option_t *co;
264 m_config_save_slot_t *slot;
266 #ifdef MP_DEBUG
267 assert(config != NULL);
268 assert(config->lvl > 1);
269 #endif
271 for(co = config->opts ; co ; co = co->next ) {
272 int pop = 0;
273 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
274 continue;
275 if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
276 continue;
277 if(co->flags & M_CFG_OPT_ALIAS)
278 continue;
279 if(co->slots->lvl > config->lvl)
280 mp_tmsg(MSGT_CFGPARSER, MSGL_WARN,"Save slot found from lvl %d is too old: %d !!!\n",config->lvl,co->slots->lvl);
282 while(co->slots->lvl >= config->lvl) {
283 m_option_free(co->opt,co->slots->data);
284 slot = co->slots;
285 co->slots = slot->prev;
286 talloc_free(slot);
287 pop++;
289 if(pop) // We removed some ctx -> set the previous value
290 m_option_set(config, co->opt, co->slots->data);
293 config->lvl--;
294 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config poped level=%d\n",config->lvl);
297 static void
298 m_config_add_option(m_config_t *config, const m_option_t *arg, const char* prefix) {
299 m_config_option_t *co;
300 m_config_save_slot_t* sl;
302 #ifdef MP_DEBUG
303 assert(config != NULL);
304 assert(config->lvl > 0);
305 assert(arg != NULL);
306 #endif
308 // Allocate a new entry for this option
309 co = talloc_zero_size(config, sizeof(m_config_option_t) + arg->type->size);
310 co->opt = arg;
312 // Fill in the full name
313 if(prefix && strlen(prefix) > 0) {
314 co->name = talloc_asprintf(co, "%s:%s", prefix, arg->name);
315 } else
316 co->name = arg->name;
318 // Option with children -> add them
319 if(arg->type->flags & M_OPT_TYPE_HAS_CHILD) {
320 const m_option_t *ol = arg->p;
321 int i;
322 co->slots = NULL;
323 for(i = 0 ; ol[i].name != NULL ; i++)
324 m_config_add_option(config,&ol[i], co->name);
325 } else {
326 m_config_option_t *i;
327 // Check if there is already an option pointing to this address
328 if(arg->p || arg->new && arg->offset >= 0) {
329 for(i = config->opts ; i ; i = i->next ) {
330 if (arg->new ? (i->opt->new && i->opt->offset == arg->offset)
331 : (!i->opt->new && i->opt->p == arg->p)) {
332 // So we don't save the same vars more than 1 time
333 co->slots = i->slots;
334 co->flags |= M_CFG_OPT_ALIAS;
335 break;
339 if(!(co->flags & M_CFG_OPT_ALIAS)) {
340 // Allocate a slot for the defaults
341 sl = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
342 arg->type->size);
343 m_option_save(config, arg, sl->data);
344 // Hack to avoid too much trouble with dynamically allocated data :
345 // We always use a dynamic version
346 if ((arg->type->flags & M_OPT_TYPE_DYNAMIC)) {
347 char **hackptr = arg->new ? (char*)config->optstruct + arg->offset
348 : arg->p;
349 if (hackptr && *hackptr) {
350 *hackptr = NULL;
351 m_option_set(config, arg, sl->data);
354 sl->lvl = 0;
355 sl->prev = NULL;
356 co->slots = talloc_zero_size(co, sizeof(m_config_save_slot_t) +
357 arg->type->size);
358 co->slots->prev = sl;
359 co->slots->lvl = config->lvl;
360 m_option_copy(co->opt, co->slots->data, sl->data);
363 co->next = config->opts;
364 config->opts = co;
368 m_config_register_options(m_config_t *config, const m_option_t *args) {
369 int i;
371 #ifdef MP_DEBUG
372 assert(config != NULL);
373 assert(config->lvl > 0);
374 assert(args != NULL);
375 #endif
377 for(i = 0 ; args[i].name != NULL ; i++)
378 m_config_add_option(config,&args[i],NULL);
380 return 1;
383 static m_config_option_t*
384 m_config_get_co(const m_config_t *config, char *arg) {
385 m_config_option_t *co;
387 for(co = config->opts ; co ; co = co->next ) {
388 int l = strlen(co->name) - 1;
389 if((co->opt->type->flags & M_OPT_TYPE_ALLOW_WILDCARD) &&
390 (co->name[l] == '*')) {
391 if(strncasecmp(co->name,arg,l) == 0)
392 return co;
393 } else if(strcasecmp(co->name,arg) == 0)
394 return co;
396 return NULL;
399 static int
400 m_config_parse_option(const m_config_t *config, char *arg, char *param, int set) {
401 m_config_option_t *co;
402 int r = 0;
404 #ifdef MP_DEBUG
405 assert(config != NULL);
406 assert(config->lvl > 0);
407 assert(arg != NULL);
408 #endif
410 co = m_config_get_co(config,arg);
411 if(!co){
412 // mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Unknown option: %s\n",arg);
413 return M_OPT_UNKNOWN;
416 #ifdef MP_DEBUG
417 // This is the only mandatory function
418 assert(co->opt->type->parse);
419 #endif
421 // Check if this option isn't forbidden in the current mode
422 if((config->mode == M_CONFIG_FILE) && (co->opt->flags & M_OPT_NOCFG)) {
423 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"The %s option can't be used in a config file.\n",arg);
424 return M_OPT_INVALID;
426 if((config->mode == M_COMMAND_LINE) && (co->opt->flags & M_OPT_NOCMD)) {
427 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"The %s option can't be used on the command line.\n",arg);
428 return M_OPT_INVALID;
430 // During command line preparse set only pre-parse options
431 // Otherwise only set pre-parse option if they were not already set.
432 if(((config->mode == M_COMMAND_LINE_PRE_PARSE) &&
433 !(co->opt->flags & M_OPT_PRE_PARSE)) ||
434 ((config->mode != M_COMMAND_LINE_PRE_PARSE) &&
435 (co->opt->flags & M_OPT_PRE_PARSE) && (co->flags & M_CFG_OPT_SET)))
436 set = 0;
438 // Option with children are a bit different to parse
439 if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD) {
440 char** lst = NULL;
441 int i,sr;
442 // Parse the child options
443 r = m_option_parse(co->opt,arg,param,&lst,M_COMMAND_LINE);
444 // Set them now
445 if(r >= 0)
446 for(i = 0 ; lst && lst[2*i] ; i++) {
447 int l = strlen(co->name) + 1 + strlen(lst[2*i]) + 1;
448 if(r >= 0) {
449 // Build the full name
450 char n[l];
451 sprintf(n,"%s:%s",co->name,lst[2*i]);
452 sr = m_config_parse_option(config,n,lst[2*i+1],set);
453 if(sr < 0){
454 if(sr == M_OPT_UNKNOWN){
455 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"Error: option '%s' has no suboption '%s'.\n",co->name,lst[2*i]);
456 r = M_OPT_INVALID;
457 } else
458 if(sr == M_OPT_MISSING_PARAM){
459 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"Error: suboption '%s' of '%s' must have a parameter!\n",lst[2*i],co->name);
460 r = M_OPT_INVALID;
461 } else
462 r = sr;
465 free(lst[2*i]);
466 free(lst[2*i+1]);
468 free(lst);
469 } else
470 r = m_option_parse(co->opt,arg,param,set ? co->slots->data : NULL,config->mode);
472 // Parsing failed ?
473 if(r < 0)
474 return r;
475 // Set the option
476 if(set) {
477 m_option_set(config, co->opt, co->slots->data);
478 co->flags |= M_CFG_OPT_SET;
481 return r;
485 m_config_set_option(m_config_t *config, char* arg, char* param) {
486 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Setting %s=%s\n",arg,param);
487 return m_config_parse_option(config,arg,param,1);
491 m_config_check_option(const m_config_t *config, char *arg, char *param) {
492 int r;
493 mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Checking %s=%s\n",arg,param);
494 r=m_config_parse_option(config,arg,param,0);
495 if(r==M_OPT_MISSING_PARAM){
496 mp_tmsg(MSGT_CFGPARSER, MSGL_ERR,"Error: option '%s' must have a parameter!\n",arg);
497 return M_OPT_INVALID;
499 return r;
503 const m_option_t*
504 m_config_get_option(const m_config_t *config, char *arg) {
505 m_config_option_t *co;
507 #ifdef MP_DEBUG
508 assert(config != NULL);
509 assert(config->lvl > 0);
510 assert(arg != NULL);
511 #endif
513 co = m_config_get_co(config,arg);
514 if(co)
515 return co->opt;
516 else
517 return NULL;
520 void
521 m_config_print_option_list(const m_config_t *config) {
522 char min[50],max[50];
523 m_config_option_t* co;
524 int count = 0;
526 if(!config->opts) return;
528 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "\n Name Type Min Max Global CL Cfg\n\n");
529 for(co = config->opts ; co ; co = co->next) {
530 const m_option_t* opt = co->opt;
531 if(opt->type->flags & M_OPT_TYPE_HAS_CHILD) continue;
532 if(opt->flags & M_OPT_MIN)
533 sprintf(min,"%-8.0f",opt->min);
534 else
535 strcpy(min,"No");
536 if(opt->flags & M_OPT_MAX)
537 sprintf(max,"%-8.0f",opt->max);
538 else
539 strcpy(max,"No");
540 mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %-20.20s %-15.15s %-10.10s %-10.10s %-3.3s %-3.3s %-3.3s\n",
541 co->name,
542 co->opt->type->name,
543 min,
544 max,
545 opt->flags & CONF_GLOBAL ? "Yes" : "No",
546 opt->flags & CONF_NOCMD ? "No" : "Yes",
547 opt->flags & CONF_NOCFG ? "No" : "Yes");
548 count++;
550 mp_tmsg(MSGT_CFGPARSER, MSGL_INFO, "\nTotal: %d options\n",count);
553 m_profile_t*
554 m_config_get_profile(const m_config_t *config, char *name) {
555 m_profile_t* p;
556 for(p = config->profiles ; p ; p = p->next)
557 if(!strcmp(p->name,name)) return p;
558 return NULL;
561 m_profile_t*
562 m_config_add_profile(m_config_t* config, char* name) {
563 m_profile_t* p = m_config_get_profile(config,name);
564 if(p) return p;
565 p = talloc_zero(config, m_profile_t);
566 p->name = talloc_strdup(p, name);
567 p->next = config->profiles;
568 config->profiles = p;
569 return p;
572 void
573 m_profile_set_desc(m_profile_t* p, char* desc) {
574 talloc_free(p->desc);
575 p->desc = talloc_strdup(p, desc);
579 m_config_set_profile_option(m_config_t* config, m_profile_t* p,
580 char* name, char* val) {
581 int i = m_config_check_option(config,name,val);
582 if(i < 0) return i;
583 p->opts = talloc_realloc(p, p->opts, char *, 2*(p->num_opts+2));
584 p->opts[p->num_opts*2] = talloc_strdup(p, name);
585 p->opts[p->num_opts*2+1] = talloc_strdup(p, val);
586 p->num_opts++;
587 p->opts[p->num_opts*2] = p->opts[p->num_opts*2+1] = NULL;
588 return 1;
591 void
592 m_config_set_profile(m_config_t* config, m_profile_t* p) {
593 int i;
594 if(config->profile_depth > MAX_PROFILE_DEPTH) {
595 mp_tmsg(MSGT_CFGPARSER, MSGL_WARN, "WARNING: Profile inclusion too deep.\n");
596 return;
598 int prev_mode = config->mode;
599 config->mode = M_CONFIG_FILE;
600 config->profile_depth++;
601 for(i = 0 ; i < p->num_opts ; i++)
602 m_config_set_option(config,p->opts[2*i],p->opts[2*i+1]);
603 config->profile_depth--;
604 config->mode = prev_mode;