1 /*
2  * This file is part of MPlayer.
3  *
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.
8  *
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.
13  *
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.
17  */
18 
19 /// \file
20 /// \ingroup Config
21 
22 #include "config.h"
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include <string.h>
28 #ifdef MP_DEBUG
29 #include <assert.h>
30 #endif
31 
32 #include "libavutil/common.h"
33 #include "libavutil/avstring.h"
34 #include "m_config.h"
35 #include "m_option.h"
36 #include "mp_msg.h"
37 #include "help_mp.h"
38 
39 #define MAX_PROFILE_DEPTH 20
40 
41 
parse_profile(const m_option_t * opt,const char * name,const char * param,void * dst,int src)42 static int parse_profile(const m_option_t *opt, const char *name,
43                          const char *param, void *dst, int src)
44 {
45     m_config_t *config = opt->priv;
46     char **list = NULL;
47     int i, r;
48     if (param && !strcmp(param, "help")) {
49         m_profile_t *p;
50         if (!config->profiles) {
51             mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_NoProfileDefined);
52             return M_OPT_EXIT-1;
53         }
54         mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_AvailableProfiles);
55         for (p = config->profiles; p; p = p->next)
56             mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\t%s\t%s\n", p->name,
57                    p->desc ? p->desc : "");
58         mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
59         return M_OPT_EXIT-1;
60     }
61 
62     r = m_option_type_string_list.parse(opt, name, param, &list,src);
63     if (r < 0)
64         return r;
65     if (!list || !list[0])
66         return M_OPT_INVALID;
67     for (i = 0; list[i]; i++)
68         if (!m_config_get_profile(config,list[i])) {
69             mp_msg(MSGT_CFGPARSER, MSGL_WARN, MSGTR_UnknownProfile, list[i]);
70             r = M_OPT_INVALID;
71         }
72     if (dst)
73         m_option_copy(opt, dst, &list);
74     else
75         m_option_free(opt, &list);
76     return r;
77 }
78 
79 static void
set_profile(const m_option_t * opt,void * dst,const void * src)80 set_profile(const m_option_t *opt, void *dst, const void *src)
81 {
82     m_config_t *config = opt->priv;
83     m_profile_t *p;
84     char **list = NULL;
85     int i;
86     if (!src || !*(char***)src)
87         return;
88     m_option_copy(opt,&list,src);
89     for (i = 0; list[i]; i++) {
90         p = m_config_get_profile(config, list[i]);
91         if (!p)
92             continue;
93         m_config_set_profile(config, p);
94     }
95     m_option_free(opt, &list);
96 }
97 
show_profile(m_option_t * opt,char * name,char * param)98 static int show_profile(m_option_t *opt, char* name, char *param)
99 {
100     m_config_t *config = opt->priv;
101     m_profile_t *p;
102     int i, j;
103     if (!param)
104         return M_OPT_MISSING_PARAM;
105     if (!(p = m_config_get_profile(config, param))) {
106         mp_msg(MSGT_CFGPARSER, MSGL_ERR, MSGTR_UnknownProfile, param);
107         return M_OPT_EXIT - 1;
108     }
109     if (!config->profile_depth)
110         mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_Profile, param,
111                p->desc ? p->desc : "");
112     config->profile_depth++;
113     for (i = 0; i < p->num_opts; i++) {
114         char spc[MAX_PROFILE_DEPTH + 1];
115         memset(spc, ' ', MAX_PROFILE_DEPTH);
116         spc[FFMIN(config->profile_depth, MAX_PROFILE_DEPTH)] = '\0';
117 
118         mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s%s=%s\n", spc,
119                p->opts[2 * i], p->opts[2 * i + 1]);
120 
121         if (config->profile_depth < MAX_PROFILE_DEPTH &&
122             !strcmp(p->opts[2*i],"profile")) {
123             char *e, *list = p->opts[2 * i + 1];
124             while ((e = strchr(list, ','))) {
125                 int l = e-list;
126                 if (!l)
127                     continue;
128                 char *tmp = av_strndup(list, l);
129                 show_profile(opt, name, tmp);
130                 av_freep(&tmp);
131                 list = e+1;
132             }
133             if (list[0] != '\0')
134                 show_profile(opt, name, list);
135         }
136     }
137     config->profile_depth--;
138     if (!config->profile_depth)
139         mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
140     return M_OPT_EXIT - 1;
141 }
142 
list_options(m_option_t * opt,char * name,char * param)143 static int list_options(m_option_t *opt, char* name, char *param)
144 {
145     m_config_t *config = opt->priv;
146     m_config_print_option_list(config);
147     return M_OPT_EXIT;
148 }
149 
150 m_config_t*
m_config_new(void)151 m_config_new(void) {
152   m_config_t* config;
153   static int initialized = 0;
154   static m_option_type_t profile_opt_type;
155   static const m_option_t ref_opts[] = {
156     { "profile", NULL, &profile_opt_type, CONF_NOSAVE, 0, 0, NULL },
157     { "show-profile", show_profile, CONF_TYPE_PRINT_FUNC, CONF_NOCFG, 0, 0, NULL },
158     { "list-options", list_options, CONF_TYPE_PRINT_FUNC, CONF_NOCFG, 0, 0, NULL },
159     { NULL, NULL, NULL, 0, 0, 0, NULL }
160   };
161   int i;
162 
163   config = calloc(1,sizeof(m_config_t));
164   if (!config) return NULL;
165   config->lvl = 1; // 0 Is the defaults
166   if(!initialized) {
167     initialized = 1;
168     profile_opt_type = m_option_type_string_list;
169     profile_opt_type.parse = parse_profile;
170     profile_opt_type.set = set_profile;
171   }
172   config->self_opts = malloc(sizeof(ref_opts));
173   if (!config->self_opts) {
174     free(config);
175     return NULL;
176   }
177   memcpy(config->self_opts,ref_opts,sizeof(ref_opts));
178   for(i = 0 ; config->self_opts[i].name ; i++)
179     config->self_opts[i].priv = config;
180   m_config_register_options(config,config->self_opts);
181 
182   return config;
183 }
184 
185 void
m_config_free(m_config_t * config)186 m_config_free(m_config_t* config) {
187   m_config_option_t *i = config->opts, *ct;
188   m_config_save_slot_t *sl,*st;
189   m_profile_t *p,*pn;
190   int j;
191 
192 #ifdef MP_DEBUG
193   assert(config != NULL);
194 #endif
195 
196   while(i) {
197     if (i->flags & M_CFG_OPT_ALIAS)
198       sl = NULL;
199     else
200       sl = i->slots;
201     while(sl) {
202       m_option_free(i->opt,sl->data);
203       st = sl->prev;
204       free(sl);
205       sl = st;
206     }
207     if(i->name != i->opt->name)
208       free(i->name);
209     if(i->opt->p && (i->opt->type->flags & M_OPT_TYPE_DYNAMIC))
210       m_option_free(i->opt, i->opt->p);
211     ct = i->next;
212     free(i);
213     i = ct;
214   }
215   for(p = config->profiles ; p ; p = pn) {
216     pn = p->next;
217     free(p->name);
218     free(p->desc);
219     for(j = 0 ; j < p->num_opts ; j++) {
220       free(p->opts[2*j]);
221       free(p->opts[2*j + 1]);
222     }
223     free(p->opts);
224     free(p);
225   }
226   free(config->self_opts);
227   free(config);
228 }
229 
230 void
m_config_push(m_config_t * config)231 m_config_push(m_config_t* config) {
232   m_config_option_t *co;
233   m_config_save_slot_t *slot;
234 
235 #ifdef MP_DEBUG
236   assert(config != NULL);
237   assert(config->lvl > 0);
238 #endif
239 
240   config->lvl++;
241 
242   for(co = config->opts ; co ; co = co->next ) {
243     if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
244       continue;
245     if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
246       continue;
247     if((co->opt->flags & M_OPT_OLD) && !(co->flags & M_CFG_OPT_SET))
248       continue;
249     if(co->flags & M_CFG_OPT_ALIAS)
250       continue;
251 
252     // Update the current status
253     m_option_save(co->opt,co->slots->data,co->opt->p);
254 
255     // Allocate a new slot
256     slot = calloc(1,sizeof(m_config_save_slot_t) + co->opt->type->size);
257     slot->lvl = config->lvl;
258     slot->prev = co->slots;
259     co->slots = slot;
260     m_option_copy(co->opt,co->slots->data,co->slots->prev->data);
261     // Reset our set flag
262     co->flags &= ~M_CFG_OPT_SET;
263   }
264 
265   mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config pushed level is now %d\n",config->lvl);
266 }
267 
268 void
m_config_pop(m_config_t * config)269 m_config_pop(m_config_t* config) {
270   m_config_option_t *co;
271   m_config_save_slot_t *slot;
272 
273 #ifdef MP_DEBUG
274   assert(config != NULL);
275   assert(config->lvl > 1);
276 #endif
277 
278   for(co = config->opts ; co ; co = co->next ) {
279     int pop = 0;
280     if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD)
281       continue;
282     if(co->opt->flags & (M_OPT_GLOBAL|M_OPT_NOSAVE))
283       continue;
284     if(co->flags & M_CFG_OPT_ALIAS)
285       continue;
286     if(co->slots->lvl > config->lvl)
287       mp_msg(MSGT_CFGPARSER, MSGL_WARN,MSGTR_SaveSlotTooOld,config->lvl,co->slots->lvl);
288 
289     while(co->slots->lvl >= config->lvl) {
290       m_option_free(co->opt,co->slots->data);
291       slot = co->slots;
292       co->slots = slot->prev;
293       free(slot);
294       pop++;
295     }
296     if(pop) // We removed some ctx -> set the previous value
297       m_option_set(co->opt,co->opt->p,co->slots->data);
298   }
299 
300   config->lvl--;
301   mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Config poped level=%d\n",config->lvl);
302 }
303 
304 static void
m_config_add_option(m_config_t * config,const m_option_t * arg,const char * prefix)305 m_config_add_option(m_config_t *config, const m_option_t *arg, const char* prefix) {
306   m_config_option_t *co;
307   m_config_save_slot_t* sl;
308 
309 #ifdef MP_DEBUG
310   assert(config != NULL);
311   assert(config->lvl > 0);
312   assert(arg != NULL);
313 #endif
314 
315   // Allocate a new entry for this option
316   co = calloc(1,sizeof(m_config_option_t) + arg->type->size);
317   co->opt = arg;
318 
319   // Fill in the full name
320   if(prefix && strlen(prefix) > 0) {
321     int l = strlen(prefix) + 1 + strlen(arg->name) + 1;
322     co->name = malloc(l);
323     sprintf(co->name,"%s:%s",prefix,arg->name);
324   } else
325     co->name = arg->name;
326 
327   // Option with children -> add them
328   if(arg->type->flags & M_OPT_TYPE_HAS_CHILD) {
329     const m_option_t *ol = arg->p;
330     int i;
331     co->slots = NULL;
332     for(i = 0 ; ol[i].name != NULL ; i++)
333       m_config_add_option(config,&ol[i], co->name);
334   } else {
335     m_config_option_t *i;
336     // Check if there is already an option pointing to this address
337     if(arg->p) {
338       for(i = config->opts ; i ; i = i->next ) {
339 	if(i->opt->p == arg->p) { // So we don't save the same vars more than 1 time
340 	  co->slots = i->slots;
341 	  co->flags |= M_CFG_OPT_ALIAS;
342 	  break;
343 	}
344       }
345     }
346     if(!(co->flags & M_CFG_OPT_ALIAS)) {
347     // Allocate a slot for the defaults
348     sl = calloc(1,sizeof(m_config_save_slot_t) + arg->type->size);
349     m_option_save(arg,sl->data,(void**)arg->p);
350     // Hack to avoid too much trouble with dynamically allocated data :
351     // We always use a dynamic version
352     if((arg->type->flags & M_OPT_TYPE_DYNAMIC) && arg->p && (*(void**)arg->p)) {
353       *(void**)arg->p = NULL;
354       m_option_set(arg,arg->p,sl->data);
355     }
356     sl->lvl = 0;
357     sl->prev = NULL;
358     co->slots = calloc(1,sizeof(m_config_save_slot_t) + arg->type->size);
359     co->slots->prev = sl;
360     co->slots->lvl = config->lvl;
361     m_option_copy(co->opt,co->slots->data,sl->data);
362     } // !M_OPT_ALIAS
363   }
364   co->next = config->opts;
365   config->opts = co;
366 }
367 
368 int
m_config_register_options(m_config_t * config,const m_option_t * args)369 m_config_register_options(m_config_t *config, const m_option_t *args) {
370   int i;
371 
372 #ifdef MP_DEBUG
373   assert(config != NULL);
374   assert(config->lvl > 0);
375   assert(args != NULL);
376 #endif
377 
378   for(i = 0 ; args[i].name != NULL ; i++)
379     m_config_add_option(config,&args[i],NULL);
380 
381   return 1;
382 }
383 
384 static m_config_option_t*
m_config_get_co(const m_config_t * config,char * arg)385 m_config_get_co(const m_config_t *config, char *arg) {
386   m_config_option_t *co;
387 
388   for(co = config->opts ; co ; co = co->next ) {
389     int l = strlen(co->name) - 1;
390     if((co->opt->type->flags & M_OPT_TYPE_ALLOW_WILDCARD) &&
391        (co->name[l] == '*')) {
392       if(av_strncasecmp(co->name,arg,l) == 0)
393 	return co;
394     } else if(av_strcasecmp(co->name,arg) == 0)
395       return co;
396   }
397   return NULL;
398 }
399 
400 static int
m_config_parse_option(const m_config_t * config,char * arg,char * param,int set)401 m_config_parse_option(const m_config_t *config, char *arg, char *param, int set) {
402   m_config_option_t *co;
403   int r = 0;
404 
405 #ifdef MP_DEBUG
406   assert(config != NULL);
407   assert(config->lvl > 0);
408   assert(arg != NULL);
409 #endif
410 
411   co = m_config_get_co(config,arg);
412   if(!co){
413 //    mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Unknown option: %s\n",arg);
414     return M_OPT_UNKNOWN;
415   }
416 
417 #ifdef MP_DEBUG
418   // This is the only mandatory function
419   assert(co->opt->type->parse);
420 #endif
421 
422   // Check if this option isn't forbidden in the current mode
423   if((config->mode == M_CONFIG_FILE) && (co->opt->flags & M_OPT_NOCFG)) {
424     mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_InvalidCfgfileOption,arg);
425     return M_OPT_INVALID;
426   }
427   if((config->mode == M_COMMAND_LINE) && (co->opt->flags & M_OPT_NOCMD)) {
428     mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_InvalidCmdlineOption,arg);
429     return M_OPT_INVALID;
430   }
431   // During command line preparse set only pre-parse options
432   // Otherwise only set pre-parse option if they were not already set.
433   if(((config->mode == M_COMMAND_LINE_PRE_PARSE) &&
434       !(co->opt->flags & M_OPT_PRE_PARSE)) ||
435      ((config->mode != M_COMMAND_LINE_PRE_PARSE) &&
436       (co->opt->flags & M_OPT_PRE_PARSE) && (co->flags & M_CFG_OPT_SET)))
437     set = 0;
438 
439   // Option with children are a bit different to parse
440   if(co->opt->type->flags & M_OPT_TYPE_HAS_CHILD) {
441     char** lst = NULL;
442     int i,sr;
443     // Parse the child options
444     r = m_option_parse(co->opt,arg,param,&lst,M_COMMAND_LINE);
445     // Set them now
446     if(r >= 0)
447     for(i = 0 ; lst && lst[2*i] ; i++) {
448       int l = strlen(co->name) + 1 + strlen(lst[2*i]) + 1;
449       if(r >= 0) {
450 	// Build the full name
451 	char *n = av_asprintf("%s:%s",co->name,lst[2*i]);
452 	sr = m_config_parse_option(config,n,lst[2*i+1],set);
453 	av_freep(&n);
454 	if(sr < 0){
455 	  if(sr == M_OPT_UNKNOWN){
456 	    mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_InvalidSuboption,co->name,lst[2*i]);
457 	    r = M_OPT_INVALID;
458 	  } else
459 	  if(sr == M_OPT_MISSING_PARAM){
460 	    mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_MissingSuboptionParameter,lst[2*i],co->name);
461 	    r = M_OPT_INVALID;
462 	  } else
463 	    r = sr;
464 	}
465       }
466       free(lst[2*i]);
467       free(lst[2*i+1]);
468     }
469     free(lst);
470   } else
471     r = m_option_parse(co->opt,arg,param,set ? co->slots->data : NULL,config->mode);
472 
473   // Parsing failed ?
474   if(r < 0)
475     return r;
476   // Set the option
477   if(set) {
478     m_option_set(co->opt,co->opt->p,co->slots->data);
479     co->flags |= M_CFG_OPT_SET;
480   }
481 
482   return r;
483 }
484 
485 int
m_config_set_option(m_config_t * config,char * arg,char * param)486 m_config_set_option(m_config_t *config, char* arg, char* param) {
487   mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Setting %s=%s\n",arg,param);
488   return m_config_parse_option(config,arg,param,1);
489 }
490 
491 int
m_config_check_option(const m_config_t * config,char * arg,char * param)492 m_config_check_option(const m_config_t *config, char *arg, char *param) {
493   int r;
494   mp_msg(MSGT_CFGPARSER, MSGL_DBG2,"Checking %s=%s\n",arg,param);
495   r=m_config_parse_option(config,arg,param,0);
496   if(r==M_OPT_MISSING_PARAM){
497     mp_msg(MSGT_CFGPARSER, MSGL_ERR,MSGTR_MissingOptionParameter,arg);
498     return M_OPT_INVALID;
499   }
500   return r;
501 }
502 
503 
504 const m_option_t*
m_config_get_option(const m_config_t * config,char * arg)505 m_config_get_option(const m_config_t *config, char *arg) {
506   m_config_option_t *co;
507 
508 #ifdef MP_DEBUG
509   assert(config != NULL);
510   assert(config->lvl > 0);
511   assert(arg != NULL);
512 #endif
513 
514   co = m_config_get_co(config,arg);
515   if(co)
516     return co->opt;
517   else
518     return NULL;
519 }
520 
521 
522 void
m_config_print_option_list(const m_config_t * config)523 m_config_print_option_list(const m_config_t *config) {
524   char min[50],max[50];
525   m_config_option_t* co;
526   int count = 0;
527 
528   if(!config->opts) return;
529 
530   mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_OptionListHeader);
531   for(co = config->opts ; co ; co = co->next) {
532     const m_option_t* opt = co->opt;
533     if(opt->type->flags & M_OPT_TYPE_HAS_CHILD) continue;
534     if(opt->flags & M_OPT_MIN)
535       sprintf(min,"%-8.0f",opt->min);
536     else
537       strcpy(min,"No");
538     if(opt->flags & M_OPT_MAX)
539       sprintf(max,"%-8.0f",opt->max);
540     else
541       strcpy(max,"No");
542     mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %-20.20s %-15.15s %-10.10s %-10.10s %-3.3s   %-3.3s   %-3.3s\n",
543 	   co->name,
544 	   co->opt->type->name,
545 	   min,
546 	   max,
547 	   opt->flags & CONF_GLOBAL ? "Yes" : "No",
548 	   opt->flags & CONF_NOCMD ? "No" : "Yes",
549 	   opt->flags & CONF_NOCFG ? "No" : "Yes");
550     count++;
551   }
552   mp_msg(MSGT_CFGPARSER, MSGL_INFO, MSGTR_TotalOptions,count);
553 }
554 
555 m_profile_t*
m_config_get_profile(const m_config_t * config,char * name)556 m_config_get_profile(const m_config_t *config, char *name) {
557   m_profile_t* p;
558   for(p = config->profiles ; p ; p = p->next)
559     if(!strcmp(p->name,name)) return p;
560   return NULL;
561 }
562 
563 m_profile_t*
m_config_add_profile(m_config_t * config,char * name)564 m_config_add_profile(m_config_t* config, char* name) {
565   m_profile_t* p = m_config_get_profile(config,name);
566   if(p) return p;
567   p = calloc(1,sizeof(m_profile_t));
568   p->name = strdup(name);
569   p->next = config->profiles;
570   config->profiles = p;
571   return p;
572 }
573 
574 void
m_profile_set_desc(m_profile_t * p,char * desc)575 m_profile_set_desc(m_profile_t* p, char* desc) {
576   free(p->desc);
577   p->desc = desc ? strdup(desc) : NULL;
578 }
579 
580 int
m_config_set_profile_option(m_config_t * config,m_profile_t * p,char * name,char * val)581 m_config_set_profile_option(m_config_t* config, m_profile_t* p,
582 			    char* name, char* val) {
583   int i = m_config_check_option(config,name,val);
584   if(i < 0) return i;
585   if(p->opts) p->opts = realloc(p->opts,2*(p->num_opts+2)*sizeof(char*));
586   else p->opts = malloc(2*(p->num_opts+2)*sizeof(char*));
587   p->opts[p->num_opts*2] = strdup(name);
588   p->opts[p->num_opts*2+1] = val ? strdup(val) : NULL;
589   p->num_opts++;
590   p->opts[p->num_opts*2] = p->opts[p->num_opts*2+1] = NULL;
591   return 1;
592 }
593 
594 void
m_config_set_profile(m_config_t * config,m_profile_t * p)595 m_config_set_profile(m_config_t* config, m_profile_t* p) {
596   int i;
597   if(config->profile_depth > MAX_PROFILE_DEPTH) {
598     mp_msg(MSGT_CFGPARSER, MSGL_WARN, MSGTR_ProfileInclusionTooDeep);
599     return;
600   }
601   config->profile_depth++;
602   for(i = 0 ; i < p->num_opts ; i++)
603     m_config_set_option(config,p->opts[2*i],p->opts[2*i+1]);
604   config->profile_depth--;
605 }
606