2 * command line and config file parser
3 * by Szabolcs Berecz <szabi@inf.elte.hu>
6 * subconfig support by alex
30 #define COMMAND_LINE 0
33 #define LIST_SEPARATOR ','
35 #define CONFIG_GLOBAL (1<<0)
36 #define CONFIG_RUNNING (1<<1)
38 #define SET_GLOBAL(c) (c->flags |= CONFIG_GLOBAL)
39 #ifdef GLOBAL_OPTIONS_ONLY
40 #define UNSET_GLOBAL(c)
42 #define UNSET_GLOBAL(c) (c->flags &= (!CONFIG_GLOBAL))
44 #define IS_GLOBAL(c) (c->flags & CONFIG_GLOBAL)
45 #define SET_RUNNING(c) (c->flags |= CONFIG_RUNNING)
46 #define IS_RUNNING(c) (c->flags & CONFIG_RUNNING)
48 #define MAX_RECURSION_DEPTH 8
54 #include "cfgparser.h"
57 static void m_config_list_options(m_config_t
*config
);
58 static void m_config_error(int err
,char* opt
,char* val
);
61 m_config_save_option(m_config_t
* config
, config_t
* conf
,char* opt
, char *param
) {
66 assert(config
!= NULL
);
67 assert(config
->cs_level
>= 0);
70 assert( ! (conf
->flags
& CONF_NOSAVE
));
74 case CONF_TYPE_PRINT
:
75 case CONF_TYPE_SUBCONFIG
:
81 mp_msg(MSGT_CFGPARSER
, MSGL_DBG2
,"Saving option %s\n",opt
);
83 save
= config
->config_stack
[config
->cs_level
];
86 for(sl
= 0; save
[sl
].opt
!= NULL
; sl
++){
87 // Check to not save the same arg two times
88 if(save
[sl
].opt
== conf
&& (save
[sl
].opt_name
== NULL
|| strcasecmp(save
[sl
].opt_name
,opt
) == 0))
95 save
= (config_save_t
*)realloc(save
,(sl
+2)*sizeof(config_save_t
));
97 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Can't allocate %d bytes of memory : %s\n",(sl
+2)*sizeof(config_save_t
),strerror(errno
));
100 memset(&save
[sl
],0,2*sizeof(config_save_t
));
104 case CONF_TYPE_FLAG
:
106 save
[sl
].param
.as_int
= *((int*)conf
->p
);
108 case CONF_TYPE_FLOAT
:
109 save
[sl
].param
.as_float
= *((float*)conf
->p
);
111 case CONF_TYPE_STRING
:
112 save
[sl
].param
.as_pointer
= *((char**)conf
->p
);
114 case CONF_TYPE_FUNC_FULL
:
115 if(strcasecmp(conf
->name
,opt
) != 0) save
->opt_name
= strdup(opt
);
116 case CONF_TYPE_FUNC_PARAM
:
118 save
->param
.as_pointer
= strdup(param
);
119 case CONF_TYPE_FUNC
:
121 case CONF_TYPE_STRING_LIST
:
122 save
[sl
].param
.as_pointer
= *((char***)conf
->p
);
124 case CONF_TYPE_POSITION
:
125 save
[sl
].param
.as_off_t
= *((off_t
*)conf
->p
);
128 mp_msg(MSGT_CFGPARSER
,MSGL_ERR
,"Should never append in m_config_save_option : conf->type=%d\n",conf
->type
);
131 config
->config_stack
[config
->cs_level
] = save
;
135 m_config_revert_option(m_config_t
* config
, config_save_t
* save
) {
137 config_save_t
* iter
=NULL
;
141 assert(config
!= NULL
);
142 assert(config
->cs_level
>= 0);
143 assert(save
!= NULL
);
147 arg
= save
->opt_name
? save
->opt_name
: save
->opt
->name
;
148 mp_msg(MSGT_CFGPARSER
, MSGL_DBG2
,"Reverting option %s\n",arg
);
150 if(save
->opt
->default_func
)
151 save
->opt
->default_func(save
->opt
,arg
);
153 switch(save
->opt
->type
) {
154 case CONF_TYPE_FLAG
:
156 *((int*)save
->opt
->p
) = save
->param
.as_int
;
158 case CONF_TYPE_FLOAT
:
159 *((float*)save
->opt
->p
) = save
->param
.as_float
;
161 case CONF_TYPE_STRING
:
162 *((char**)save
->opt
->p
) = save
->param
.as_pointer
;
164 case CONF_TYPE_STRING_LIST
:
165 *((char***)save
->opt
->p
) = save
->param
.as_pointer
;
167 case CONF_TYPE_FUNC_PARAM
:
168 case CONF_TYPE_FUNC_FULL
:
169 case CONF_TYPE_FUNC
:
170 if(config
->cs_level
> 0) {
171 for(i
= config
->cs_level
- 1 ; i
>= 0 ; i
--){
172 if(config
->config_stack
[i
] == NULL
) continue;
173 for(iter
= config
->config_stack
[i
]; iter
!= NULL
&& iter
->opt
!= NULL
; iter
++) {
174 if(iter
->opt
== save
->opt
&&
175 ((save
->param
.as_pointer
== NULL
|| iter
->param
.as_pointer
== NULL
) || strcasecmp(save
->param
.as_pointer
,iter
->param
.as_pointer
) == 0) &&
176 (save
->opt_name
== NULL
||
177 (iter
->opt_name
&& strcasecmp(save
->opt_name
,iter
->opt_name
)))) break;
181 free(save
->param
.as_pointer
);
182 if(save
->opt_name
) free(save
->opt_name
);
183 save
->opt_name
= save
->param
.as_pointer
= NULL
;
185 arg
= iter
->opt_name
? iter
->opt_name
: iter
->opt
->name
;
186 switch(iter
->opt
->type
) {
187 case CONF_TYPE_FUNC
:
188 if ((((cfg_func_t
) iter
->opt
->p
)(iter
->opt
)) < 0)
191 case CONF_TYPE_FUNC_PARAM
:
192 if (iter
->param
.as_pointer
== NULL
) {
193 mp_msg(MSGT_CFGPARSER
,MSGL_ERR
,"We lost param for option %s?\n",iter
->opt
->name
);
196 if ((((cfg_func_param_t
) iter
->opt
->p
)(iter
->opt
, (char*)iter
->param
.as_pointer
)) < 0)
199 case CONF_TYPE_FUNC_FULL
:
200 if (iter
->param
.as_pointer
!= NULL
&& ((char*)iter
->param
.as_pointer
)[0]=='-'){
201 if( ((cfg_func_arg_param_t
) iter
->opt
->p
)(iter
->opt
, arg
, NULL
) < 0)
204 if (((cfg_func_arg_param_t
) save
->opt
->p
)(iter
->opt
, arg
, (char*)iter
->param
.as_pointer
) < 0)
211 case CONF_TYPE_POSITION
:
212 *((off_t
*)save
->opt
->p
) = save
->param
.as_off_t
;
215 mp_msg(MSGT_CFGPARSER
,MSGL_WARN
,"Why do we reverse this : name=%s type=%d ?\n",save
->opt
->name
,save
->opt
->type
);
222 m_config_push(m_config_t
* config
) {
225 assert(config
!= NULL
);
226 assert(config
->cs_level
>= 0);
230 config
->config_stack
= (config_save_t
**)realloc(config
->config_stack
,sizeof(config_save_t
*)*(config
->cs_level
+1));
231 if(config
->config_stack
== NULL
) {
232 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Can't allocate %d bytes of memory : %s\n",sizeof(config_save_t
*)*(config
->cs_level
+1),strerror(errno
));
233 config
->cs_level
= -1;
236 config
->config_stack
[config
->cs_level
] = NULL
;
237 mp_msg(MSGT_CFGPARSER
, MSGL_DBG2
,"Config pushed level=%d\n",config
->cs_level
);
241 m_config_pop(m_config_t
* config
) {
246 assert(config
!= NULL
);
247 //assert(config->cs_level > 0);
250 if(config
->config_stack
[config
->cs_level
] != NULL
) {
251 cs
= config
->config_stack
[config
->cs_level
];
252 for(i
=0; cs
[i
].opt
!= NULL
; i
++ ) {
253 if (m_config_revert_option(config
,&cs
[i
]) < 0)
256 free(config
->config_stack
[config
->cs_level
]);
258 config
->config_stack
= (config_save_t
**)realloc(config
->config_stack
,sizeof(config_save_t
*)*config
->cs_level
);
260 if(config
->cs_level
> 0 && config
->config_stack
== NULL
) {
261 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Can't allocate %d bytes of memory : %s\n",sizeof(config_save_t
*)*config
->cs_level
,strerror(errno
));
262 config
->cs_level
= -1;
265 mp_msg(MSGT_CFGPARSER
, MSGL_DBG2
,"Config poped level=%d\n",config
->cs_level
);
270 m_config_new(play_tree_t
* pt
) {
277 config
= (m_config_t
*)calloc(1,sizeof(m_config_t
));
279 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Can't allocate %d bytes of memory : %s\n",sizeof(m_config_t
),strerror(errno
));
282 config
->config_stack
= (config_save_t
**)calloc(1,sizeof(config_save_t
*));
283 if(config
->config_stack
== NULL
) {
284 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Can't allocate %d bytes of memory : %s\n",sizeof(config_save_t
*),strerror(errno
));
288 SET_GLOBAL(config
); // We always start with global options
294 m_config_free(m_config_t
* config
) {
297 assert(config
!= NULL
);
300 free(config
->opt_list
);
301 free(config
->config_stack
);
306 static int init_conf(m_config_t
*config
, int mode
)
309 assert(config
!= NULL
);
310 assert(config
->pt
!= NULL
);
311 assert(config
->last_entry
== NULL
|| config
->last_entry
->parent
== config
->pt
);
313 if (mode
!= COMMAND_LINE
&& mode
!= CONFIG_FILE
) {
314 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "init_conf: wrong mode!\n");
318 config
->parser_mode
= mode
;
322 static int config_is_entry_option(m_config_t
*config
, char *opt
, char *param
) {
323 play_tree_t
* entry
= NULL
;
326 assert(config
->pt
!= NULL
);
329 if(strcasecmp(opt
,"playlist") == 0) { // We handle playlist here
331 return ERR_MISSING_PARAM
;
332 entry
= parse_playlist_file(param
);
334 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Playlist parsing failed: %s\n",param
);
339 if(! IS_RUNNING(config
)) {
340 if(strcasecmp(opt
,"vcd") == 0) {
343 return ERR_MISSING_PARAM
;
344 s
= (char*)malloc((strlen(param
) + 6 + 1)*sizeof(char));
345 sprintf(s
,"vcd://%s",param
);
346 entry
= play_tree_new();
347 play_tree_add_file(entry
,s
);
349 } else if(strcasecmp(opt
,"dvd") == 0) {
352 return ERR_MISSING_PARAM
;
353 s
= (char*)malloc((strlen(param
) + 6 + 1)*sizeof(char));
354 sprintf(s
,"dvd://%s",param
);
355 entry
= play_tree_new();
356 play_tree_add_file(entry
,s
);
358 } else if(strcasecmp(opt
,"tv") == 0) {
360 char *ps
,*pe
,*channel
=NULL
;
364 return ERR_MISSING_PARAM
;
366 pe
= strchr(param
,':');
367 pr
= prs
= (char*)malloc((strlen(param
)+1)*sizeof(char));
371 pe
= ps
+ strlen(ps
);
374 if(as
&& as
[1] != '\0' && pe
-as
> 0)
378 if( !as
&& pe
-ps
== 2 && strncasecmp("on",ps
,2) == 0 )
380 else if(as
&& as
-ps
== 8 && strncasecmp("channel",ps
,6) == 0 && pe
-as
> 0) {
381 channel
= (char*)realloc(channel
,(pe
-as
+1)*sizeof(char));
382 strncpy(channel
,as
,pe
-as
);
383 channel
[pe
-as
] = '\0';
384 } else if(pe
-ps
> 0) {
389 strncpy(prs
,ps
,pe
-ps
);
405 l
+= strlen(channel
);
406 s
= (char*) malloc((l
+1)*sizeof(char));
408 sprintf(s
,"tv://%s",channel
);
411 entry
= play_tree_new();
412 play_tree_add_file(entry
,s
);
414 play_tree_set_param(entry
,"tv",pr
);
425 if(config
->last_entry
)
426 play_tree_append_entry(config
->last_entry
,entry
);
428 play_tree_set_child(config
->pt
,entry
);
429 config
->last_entry
= entry
;
430 if(config
->parser_mode
== COMMAND_LINE
)
431 UNSET_GLOBAL(config
);
439 static int config_read_option(m_config_t
*config
,config_t
** conf_list
, char *opt
, char *param
)
451 assert(config
!= NULL
);
452 assert(conf_list
!= NULL
);
456 mp_msg(MSGT_CFGPARSER
, MSGL_DBG3
, "read_option: conf=%p opt='%s' param='%s'\n",
458 for(nconf
= 0 ; conf_list
[nconf
] != NULL
; nconf
++) {
459 conf
= conf_list
[nconf
];
460 for (i
= 0; conf
[i
].name
!= NULL
; i
++) {
462 /* allow 'aa*' in config.name */
463 namelength
=strlen(conf
[i
].name
);
464 if ( (conf
[i
].name
[namelength
-1]=='*') &&
465 !memcmp(opt
, conf
[i
].name
, namelength
-1))
467 if (!strcasecmp(opt
, conf
[i
].name
))
471 if (config
->parser_mode
== CONFIG_FILE
)
472 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "invalid option: %s\n",opt
);
473 ret
= ERR_NOT_AN_OPTION
;
476 mp_msg(MSGT_CFGPARSER
, MSGL_DBG3
, "read_option: name='%s' p=%p type=%d\n",
477 conf
[i
].name
, conf
[i
].p
, conf
[i
].type
);
479 if (conf
[i
].flags
& CONF_NOCFG
&& config
->parser_mode
== CONFIG_FILE
) {
480 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "this option can only be used on command line:\n", opt
);
481 ret
= ERR_NOT_AN_OPTION
;
484 if (conf
[i
].flags
& CONF_NOCMD
&& config
->parser_mode
== COMMAND_LINE
) {
485 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "this option can only be used in config file:\n", opt
);
486 ret
= ERR_NOT_AN_OPTION
;
489 ret
= config_is_entry_option(config
,opt
,param
);
494 if(! IS_RUNNING(config
) && ! IS_GLOBAL(config
) &&
495 ! (conf
[i
].flags
& CONF_GLOBAL
) && conf
[i
].type
!= CONF_TYPE_SUBCONFIG
)
496 m_config_push(config
);
497 if( !(conf
[i
].flags
& CONF_NOSAVE
) && ! (conf
[i
].flags
& CONF_GLOBAL
) )
498 m_config_save_option(config
,&conf
[i
],opt
,param
);
499 switch (conf
[i
].type
) {
501 /* flags need a parameter in config file */
502 if (config
->parser_mode
== CONFIG_FILE
) {
503 if (!strcasecmp(param
, "yes") || /* any other language? */
504 !strcasecmp(param
, "ja") ||
505 !strcasecmp(param
, "si") ||
506 !strcasecmp(param
, "igen") ||
507 !strcasecmp(param
, "y") ||
508 !strcasecmp(param
, "j") ||
509 !strcasecmp(param
, "i") ||
511 *((int *) conf
[i
].p
) = conf
[i
].max
;
512 else if (!strcasecmp(param
, "no") ||
513 !strcasecmp(param
, "nein") ||
514 !strcasecmp(param
, "nicht") ||
515 !strcasecmp(param
, "nem") ||
516 !strcasecmp(param
, "n") ||
518 *((int *) conf
[i
].p
) = conf
[i
].min
;
520 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "invalid parameter for flag: %s\n", param
);
521 ret
= ERR_OUT_OF_RANGE
;
525 } else { /* parser_mode == COMMAND_LINE */
526 *((int *) conf
[i
].p
) = conf
[i
].max
;
532 goto err_missing_param
;
534 tmp_int
= strtol(param
, &endptr
, 0);
536 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "parameter must be an integer: %s\n", param
);
537 ret
= ERR_OUT_OF_RANGE
;
541 if (conf
[i
].flags
& CONF_MIN
)
542 if (tmp_int
< conf
[i
].min
) {
543 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "parameter must be >= %d: %s\n", (int) conf
[i
].min
, param
);
544 ret
= ERR_OUT_OF_RANGE
;
548 if (conf
[i
].flags
& CONF_MAX
)
549 if (tmp_int
> conf
[i
].max
) {
550 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "parameter must be <= %d: %s\n", (int) conf
[i
].max
, param
);
551 ret
= ERR_OUT_OF_RANGE
;
555 *((int *) conf
[i
].p
) = tmp_int
;
558 case CONF_TYPE_FLOAT
:
560 goto err_missing_param
;
561 /* <olo@altkom.com.pl> Use portable C locale for parsing floats: */
563 setlocale(LC_NUMERIC
, "C");
565 tmp_float
= strtod(param
, &endptr
);
570 tmp_float
/= strtod(endptr
+1, &endptr
);
575 setlocale(LC_NUMERIC
, "");
579 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "parameter must be a floating point number"
580 " or a ratio (numerator[:/]denominator): %s\n", param
);
581 ret
= ERR_MISSING_PARAM
;
585 if (conf
[i
].flags
& CONF_MIN
)
586 if (tmp_float
< conf
[i
].min
) {
587 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "parameter must be >= %f: %s\n", conf
[i
].min
, param
);
588 ret
= ERR_OUT_OF_RANGE
;
592 if (conf
[i
].flags
& CONF_MAX
)
593 if (tmp_float
> conf
[i
].max
) {
594 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "parameter must be <= %f: %s\n", conf
[i
].max
, param
);
595 ret
= ERR_OUT_OF_RANGE
;
599 *((float *) conf
[i
].p
) = tmp_float
;
602 case CONF_TYPE_STRING
:
604 goto err_missing_param
;
606 if (conf
[i
].flags
& CONF_MIN
)
607 if (strlen(param
) < conf
[i
].min
) {
608 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "parameter must be >= %d chars: %s\n",
609 (int) conf
[i
].min
, param
);
610 ret
= ERR_OUT_OF_RANGE
;
614 if (conf
[i
].flags
& CONF_MAX
)
615 if (strlen(param
) > conf
[i
].max
) {
616 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "parameter must be <= %d chars: %s\n",
617 (int) conf
[i
].max
, param
);
618 ret
= ERR_OUT_OF_RANGE
;
621 *((char **) conf
[i
].p
) = strdup(param
);
624 case CONF_TYPE_STRING_LIST
:
626 goto err_missing_param
;
629 char *ptr
= param
, *last_ptr
, **res
;
631 while(ptr
[0] != '\0') {
633 ptr
= strchr(ptr
,LIST_SEPARATOR
);
635 // if(strlen(last_ptr) > 0)
643 goto err_missing_param
;
644 else if( (conf
[i
].flags
& CONF_MIN
&& n
< conf
[i
].min
) ||
645 (conf
[i
].flags
& CONF_MAX
&& n
> conf
[i
].max
) ) {
646 ret
= ERR_OUT_OF_RANGE
;
650 res
= malloc((n
+2)*sizeof(char*));
653 // while(ptr[0] != '\0') {
656 ptr
= strchr(ptr
,LIST_SEPARATOR
);
658 //if(strlen(last_ptr) > 0)
660 res
[n
] = strdup(last_ptr
);
665 len
= ptr
- last_ptr
;
666 res
[n
] = (char*)malloc(len
+ 1);
667 if(len
) strncpy(res
[n
],last_ptr
,len
);
673 *((char ***) conf
[i
].p
) = res
;
676 case CONF_TYPE_FUNC_PARAM
:
678 goto err_missing_param
;
679 if ((((cfg_func_param_t
) conf
[i
].p
)(conf
+ i
, param
)) < 0) {
685 case CONF_TYPE_FUNC_FULL
:
686 if (param
!=NULL
&& param
[0]=='-'){
687 ret
=((cfg_func_arg_param_t
) conf
[i
].p
)(conf
+ i
, opt
, NULL
);
689 /* if we return >=0: param is processed again (if there is any) */
691 ret
=((cfg_func_arg_param_t
) conf
[i
].p
)(conf
+ i
, opt
, param
);
692 /* if we return 0: need no param, precess it again */
693 /* if we return 1: accepted param */
697 if ((((cfg_func_t
) conf
[i
].p
)(conf
+ i
)) < 0) {
703 case CONF_TYPE_SUBCONFIG
:
709 config_t
*sublist
[] = { NULL
, NULL
};
714 goto err_missing_param
;
716 subparam
= malloc(strlen(param
)+1);
717 subopt
= malloc(strlen(param
)+1);
718 p
= strdup(param
); // In case that param is a static string (cf man strtok)
721 sublist
[0] = subconf
;
722 for (subconf_optnr
= 0; subconf
[subconf_optnr
].name
!= NULL
; subconf_optnr
++)
724 config
->sub_conf
= opt
;
725 token
= strtok(p
, (char *)&(":"));
730 subopt
[0] = subparam
[0] = 0;
732 sscanf_ret
= sscanf(token
, "%[^=]=%[^:]", subopt
, subparam
);
734 mp_msg(MSGT_CFGPARSER
, MSGL_DBG3
, "token: '%s', i=%d, subopt='%s', subparam='%s' (ret: %d)\n", token
, i
, subopt
, subparam
, sscanf_ret
);
740 if ((ret
= config_read_option(config
,sublist
, subopt
, subparam
)) < 0)
742 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Subconfig parsing returned error: %d in token: %s\n",
748 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Invalid subconfig argument! ('%s')\n", token
);
749 ret
= ERR_NOT_AN_OPTION
;
752 token
= strtok(NULL
, (char *)&(":"));
754 config
->sub_conf
= NULL
;
761 case CONF_TYPE_PRINT
:
762 mp_msg(MSGT_CFGPARSER
, MSGL_INFO
, "%s", (char *) conf
[i
].p
);
764 case CONF_TYPE_POSITION
:
766 goto err_missing_param
;
768 if (sscanf(param
, sizeof(off_t
) == sizeof(int) ?
769 "%d%c" : "%lld%c", &tmp_off
, (char *)&dummy
) != 1) {
770 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "parameter must be an integer: %s\n", param
);
771 ret
= ERR_OUT_OF_RANGE
;
775 if (conf
[i
].flags
& CONF_MIN
)
776 if (tmp_off
< conf
[i
].min
) {
777 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
,
778 (sizeof(off_t
) == sizeof(int) ?
779 "parameter must be >= %d: %s\n" :
780 "parameter must be >= %lld: %s\n"),
781 (off_t
) conf
[i
].min
, param
);
782 ret
= ERR_OUT_OF_RANGE
;
786 if (conf
[i
].flags
& CONF_MAX
)
787 if (tmp_off
> conf
[i
].max
) {
788 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
,
789 (sizeof(off_t
) == sizeof(int) ?
790 "parameter must be <= %d: %s\n" :
791 "parameter must be <= %lld: %s\n"),
792 (off_t
) conf
[i
].max
, param
);
793 ret
= ERR_OUT_OF_RANGE
;
797 *((off_t
*) conf
[i
].p
) = tmp_off
;
801 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Unknown config type specified in conf-mplayer.h!\n");
805 if(ret
>= 0 && ! IS_RUNNING(config
) && ! IS_GLOBAL(config
) && ! (conf
[i
].flags
& CONF_GLOBAL
) && conf
[i
].type
!= CONF_TYPE_SUBCONFIG
) {
806 play_tree_t
* dest
= config
->last_entry
? config
->last_entry
: config
->last_parent
;
809 assert(dest
!= NULL
);
811 if(config
->sub_conf
) {
812 o
= (char*)malloc((strlen(config
->sub_conf
) + 1 + strlen(opt
) + 1)*sizeof(char));
813 sprintf(o
,"%s:%s",config
->sub_conf
,opt
);
818 play_tree_set_param(dest
,o
,NULL
);
820 play_tree_set_param(dest
,o
,param
);
822 m_config_pop(config
);
826 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "missing parameter for option: %s\n", opt
);
827 ret
= ERR_MISSING_PARAM
;
831 int m_config_set_option(m_config_t
*config
,char *opt
, char *param
) {
834 assert(config
!= NULL
);
835 assert(config
->opt_list
!= NULL
);
838 mp_msg(MSGT_CFGPARSER
, MSGL_DBG2
, "Setting option %s=%s\n",opt
,param
);
840 if(e
&& e
[1] != '\0') {
842 config_t
* opt_list
[] = { NULL
, NULL
};
843 char* s
= (char*)malloc((e
-opt
+1)*sizeof(char));
844 strncpy(s
,opt
,e
-opt
);
846 opt_list
[0] = m_config_get_option_ptr(config
,s
);
848 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
,"m_config_set_option %s=%s : no %s subconfig\n",opt
,param
,s
);
850 return ERR_NOT_AN_OPTION
;
853 s
= (char*)realloc(s
,strlen(e
) + 1);
855 ret
= config_read_option(config
,opt_list
,s
,param
);
860 return config_read_option(config
,config
->opt_list
,opt
,param
);
863 int m_config_parse_config_file(m_config_t
*config
, char *conffile
)
865 #define PRINT_LINENUM mp_msg(MSGT_CFGPARSER,MSGL_INFO,"%s(%d): ", conffile, line_num)
866 #define MAX_LINE_LEN 10000
867 #define MAX_OPT_LEN 1000
868 #define MAX_PARAM_LEN 1000
871 char opt
[MAX_OPT_LEN
+ 1];
872 char param
[MAX_PARAM_LEN
+ 1];
873 char c
; /* for the "" and '' check */
876 int line_pos
; /* line pos */
877 int opt_pos
; /* opt pos */
878 int param_pos
; /* param pos */
883 assert(config
!= NULL
);
884 // assert(conf_list != NULL);
886 if (++config
->recursion_depth
> 1)
887 mp_msg(MSGT_CFGPARSER
,MSGL_INFO
,"Reading config file: %s", conffile
);
889 if (config
->recursion_depth
> MAX_RECURSION_DEPTH
) {
890 mp_msg(MSGT_CFGPARSER
,MSGL_ERR
,": too deep 'include'. check your configfiles\n");
895 if (init_conf(config
, CONFIG_FILE
) == -1) {
900 if ((line
= (char *) malloc(MAX_LINE_LEN
+ 1)) == NULL
) {
901 mp_msg(MSGT_CFGPARSER
,MSGL_FATAL
,"\ncan't get memory for 'line': %s", strerror(errno
));
906 if ((fp
= fopen(conffile
, "r")) == NULL
) {
907 if (config
->recursion_depth
> 1)
908 mp_msg(MSGT_CFGPARSER
,MSGL_ERR
,": %s\n", strerror(errno
));
913 if (config
->recursion_depth
> 1)
914 mp_msg(MSGT_CFGPARSER
,MSGL_INFO
,"\n");
916 while (fgets(line
, MAX_LINE_LEN
, fp
)) {
918 mp_msg(MSGT_CFGPARSER
,MSGL_FATAL
,"too many errors\n");
925 /* skip whitespaces */
926 while (isspace(line
[line_pos
]))
930 if (line
[line_pos
] == '\0' || line
[line_pos
] == '#')
934 for (opt_pos
= 0; isprint(line
[line_pos
]) &&
935 line
[line_pos
] != ' ' &&
936 line
[line_pos
] != '#' &&
937 line
[line_pos
] != '='; /* NOTHING */) {
938 opt
[opt_pos
++] = line
[line_pos
++];
939 if (opt_pos
>= MAX_OPT_LEN
) {
941 mp_msg(MSGT_CFGPARSER
,MSGL_ERR
,"too long option\n");
949 mp_msg(MSGT_CFGPARSER
,MSGL_ERR
,"parse error\n");
958 mp_msg(MSGT_CFGPARSER
,MSGL_INFO
,"option: %s\n", opt
);
961 /* skip whitespaces */
962 while (isspace(line
[line_pos
]))
966 if (line
[line_pos
++] != '=') {
968 mp_msg(MSGT_CFGPARSER
,MSGL_ERR
,"option without parameter\n");
975 while (isspace(line
[line_pos
]))
978 /* read the parameter */
979 if (line
[line_pos
] == '"' || line
[line_pos
] == '\'') {
982 for (param_pos
= 0; line
[line_pos
] != c
; /* NOTHING */) {
983 param
[param_pos
++] = line
[line_pos
++];
984 if (param_pos
>= MAX_PARAM_LEN
) {
986 mp_msg(MSGT_CFGPARSER
,MSGL_ERR
,"too long parameter\n");
992 line_pos
++; /* skip the closing " or ' */
994 for (param_pos
= 0; isprint(line
[line_pos
]) && !isspace(line
[line_pos
])
995 && line
[line_pos
] != '#'; /* NOTHING */) {
996 param
[param_pos
++] = line
[line_pos
++];
997 if (param_pos
>= MAX_PARAM_LEN
) {
999 mp_msg(MSGT_CFGPARSER
,MSGL_ERR
,"too long parameter\n");
1006 param
[param_pos
] = '\0';
1008 /* did we read a parameter? */
1009 if (param_pos
== 0) {
1011 mp_msg(MSGT_CFGPARSER
,MSGL_ERR
,"option without parameter\n");
1019 mp_msg(MSGT_CFGPARSER
,MSGL_INFO
,"parameter: %s\n", param
);
1022 /* now, check if we have some more chars on the line */
1024 while (isspace(line
[line_pos
]))
1028 if (line
[line_pos
] != '\0' && line
[line_pos
] != '#') {
1030 mp_msg(MSGT_CFGPARSER
,MSGL_WARN
,"extra characters on line: %s\n", line
+line_pos
);
1034 tmp
= m_config_set_option(config
, opt
, param
);
1036 case ERR_NOT_AN_OPTION
:
1037 case ERR_MISSING_PARAM
:
1038 case ERR_OUT_OF_RANGE
:
1041 mp_msg(MSGT_CFGPARSER
,MSGL_INFO
,"%s\n", opt
);
1054 --config
->recursion_depth
;
1058 int m_config_parse_command_line(m_config_t
*config
, int argc
, char **argv
)
1063 int no_more_opts
= 0;
1066 assert(config
!= NULL
);
1067 assert(config
->pt
!= NULL
);
1068 assert(argv
!= NULL
);
1072 if (init_conf(config
, COMMAND_LINE
) == -1)
1074 if(config
->last_parent
== NULL
)
1075 config
->last_parent
= config
->pt
;
1076 /* in order to work recursion detection properly in parse_config_file */
1077 ++config
->recursion_depth
;
1079 for (i
= 1; i
< argc
; i
++) {
1082 /* check for -- (no more options id.) except --help! */
1083 if ((*opt
== '-') && (*(opt
+1) == '-') && (*(opt
+2) != 'h'))
1088 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "You added '--' but no filenames presented!\n");
1093 if((opt
[0] == '{') && (opt
[1] == '\0'))
1095 play_tree_t
* entry
= play_tree_new();
1096 UNSET_GLOBAL(config
);
1097 if(config
->last_entry
== NULL
) {
1098 play_tree_set_child(config
->last_parent
,entry
);
1100 play_tree_append_entry(config
->last_entry
,entry
);
1101 config
->last_entry
= NULL
;
1103 config
->last_parent
= entry
;
1107 if((opt
[0] == '}') && (opt
[1] == '\0'))
1109 if( ! config
->last_parent
|| ! config
->last_parent
->parent
) {
1110 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "too much }-\n");
1113 config
->last_entry
= config
->last_parent
;
1114 config
->last_parent
= config
->last_entry
->parent
;
1118 if ((no_more_opts
== 0) && (*opt
== '-') && (*(opt
+1) != 0)) /* option */
1120 /* remove trailing '-' */
1123 mp_msg(MSGT_CFGPARSER
, MSGL_DBG3
, "this_opt = option: %s\n", opt
);
1124 // We handle here some specific option
1125 if(strcasecmp(opt
,"list-options") == 0) {
1126 m_config_list_options(config
);
1128 // Loop option when it apply to a group
1129 } else if(strcasecmp(opt
,"loop") == 0 &&
1130 (! config
->last_entry
|| config
->last_entry
->child
) ) {
1133 l
= strtol(argv
[i
+1],&end
,0);
1135 tmp
= ERR_OUT_OF_RANGE
;
1137 play_tree_t
* pt
= config
->last_entry
? config
->last_entry
: config
->last_parent
;
1138 l
= l
<= 0 ? -1 : l
;
1142 } else // All normal options
1143 tmp
= m_config_set_option(config
, opt
, argv
[i
+ 1]);
1146 case ERR_NOT_AN_OPTION
:
1147 case ERR_MISSING_PARAM
:
1148 case ERR_OUT_OF_RANGE
:
1150 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Error: ");
1151 m_config_error(tmp
,opt
,argv
[i
+1]);
1160 play_tree_t
* entry
= play_tree_new();
1161 mp_msg(MSGT_CFGPARSER
, MSGL_DBG2
,"Adding file %s\n",argv
[i
]);
1162 play_tree_add_file(entry
,argv
[i
]);
1163 if(strcasecmp(argv
[i
],"-") == 0)
1164 m_config_set_option(config
,"use-stdin",NULL
);
1165 /* opt is not an option -> treat it as a filename */
1166 UNSET_GLOBAL(config
); // We start entry specific options
1167 if(config
->last_entry
== NULL
)
1168 play_tree_set_child(config
->last_parent
,entry
);
1170 play_tree_append_entry(config
->last_entry
,entry
);
1171 config
->last_entry
= entry
;
1175 --config
->recursion_depth
;
1176 if(config
->last_parent
!= config
->pt
)
1177 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
,"Missing }- ?\n");
1178 config
->flags
&= (!CONFIG_GLOBAL
);
1179 SET_RUNNING(config
);
1183 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "can't allocate memory for filenames (%s)\n", strerror(errno
));
1186 --config
->recursion_depth
;
1187 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "command line: %s\n", argv
[i
]);
1192 m_config_register_options(m_config_t
*config
,config_t
*args
) {
1194 config_t
** conf_list
= config
->opt_list
;
1197 assert(config
!= NULL
);
1198 assert(args
!= NULL
);
1202 for ( ; conf_list
[list_len
] != NULL
; list_len
++)
1206 conf_list
= (config_t
**)realloc(conf_list
,sizeof(struct conf
*)*(list_len
+2));
1207 if(conf_list
== NULL
) {
1208 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
, "Can't allocate %d bytes of memory : %s\n",sizeof(struct conf
*)*(list_len
+2),strerror(errno
));
1211 conf_list
[list_len
] = args
;
1212 conf_list
[list_len
+1] = NULL
;
1214 config
->opt_list
= conf_list
;
1220 m_config_get_option(m_config_t
*config
, char* arg
) {
1224 config_t
**conf_list
;
1225 config_t
* cl
[] = { NULL
, NULL
};
1228 assert(config
!= NULL
);
1229 assert(arg
!= NULL
);
1232 e
= strchr(arg
,':');
1236 s
= (char*)malloc((e
-arg
+1)*sizeof(char));
1237 strncpy(s
,arg
,e
-arg
);
1239 cl
[0] = m_config_get_option(config
,s
);
1243 conf_list
= config
->opt_list
;
1246 for(j
= 0 ; conf_list
[j
] != NULL
; j
++) {
1247 conf
= conf_list
[j
];
1248 for(i
=0; conf
[i
].name
!= NULL
; i
++) {
1249 if(strcasecmp(conf
[i
].name
,arg
) == 0)
1258 m_config_get_option_ptr(m_config_t
*config
, char* arg
) {
1262 assert(config
!= NULL
);
1263 assert(arg
!= NULL
);
1266 conf
= m_config_get_option(config
,arg
);
1267 if(!conf
) return NULL
;
1272 m_config_get_int (m_config_t
*config
, char* arg
,int* err_ret
) {
1276 assert(config
!= NULL
);
1277 assert(arg
!= NULL
);
1280 ret
= m_config_get_option_ptr(config
,arg
);
1292 m_config_get_float (m_config_t
*config
, char* arg
,int* err_ret
) {
1296 assert(config
!= NULL
);
1297 assert(arg
!= NULL
);
1300 ret
= m_config_get_option_ptr(config
,arg
);
1311 #define AS_INT(c) (*((int*)c->p))
1314 m_config_set_int(m_config_t
*config
, char* arg
,int val
) {
1318 assert(config
!= NULL
);
1319 assert(arg
!= NULL
);
1322 opt
= m_config_get_option(config
,arg
);
1324 if(!opt
|| opt
->type
!= CONF_TYPE_INT
)
1325 return ERR_NOT_AN_OPTION
;
1327 if(opt
->flags
& CONF_MIN
&& val
< opt
->min
)
1328 return ERR_OUT_OF_RANGE
;
1329 if(opt
->flags
& CONF_MAX
&& val
> opt
->max
)
1330 return ERR_OUT_OF_RANGE
;
1332 m_config_save_option(config
,opt
,arg
,NULL
);
1339 m_config_set_float(m_config_t
*config
, char* arg
,float val
) {
1343 assert(config
!= NULL
);
1344 assert(arg
!= NULL
);
1347 opt
= m_config_get_option(config
,arg
);
1349 if(!opt
|| opt
->type
!= CONF_TYPE_FLOAT
)
1350 return ERR_NOT_AN_OPTION
;
1352 if(opt
->flags
& CONF_MIN
&& val
< opt
->min
)
1353 return ERR_OUT_OF_RANGE
;
1354 if(opt
->flags
& CONF_MAX
&& val
> opt
->max
)
1355 return ERR_OUT_OF_RANGE
;
1357 m_config_save_option(config
,opt
,arg
,NULL
);
1358 *((float*)opt
->p
) = val
;
1365 m_config_switch_flag(m_config_t
*config
, char* opt
) {
1369 assert(config
!= NULL
);
1370 assert(opt
!= NULL
);
1373 conf
= m_config_get_option(config
,opt
);
1374 if(!conf
|| conf
->type
!= CONF_TYPE_FLAG
) return 0;
1375 if( AS_INT(conf
) == conf
->min
) AS_INT(conf
) = conf
->max
;
1376 else if(AS_INT(conf
) == conf
->max
) AS_INT(conf
) = conf
->min
;
1383 m_config_set_flag(m_config_t
*config
, char* opt
, int state
) {
1387 assert(config
!= NULL
);
1388 assert(opt
!= NULL
);
1391 conf
= m_config_get_option(config
,opt
);
1392 if(!conf
|| conf
->type
!= CONF_TYPE_FLAG
) return 0;
1393 if(state
) AS_INT(conf
) = conf
->max
;
1394 else AS_INT(conf
) = conf
->min
;
1399 m_config_get_flag(m_config_t
*config
, char* opt
) {
1403 assert(config
!= NULL
);
1404 assert(opt
!= NULL
);
1407 conf
= m_config_get_option(config
,opt
);
1408 if(!conf
|| conf
->type
!= CONF_TYPE_FLAG
) return -1;
1409 if(AS_INT(conf
) == conf
->max
)
1411 else if(AS_INT(conf
) == conf
->min
)
1417 int m_config_is_option_set(m_config_t
*config
, char* arg
) {
1419 config_save_t
* save
;
1423 assert(config
!= NULL
);
1424 assert(arg
!= NULL
);
1427 opt
= m_config_get_option(config
,arg
);
1432 for(l
= config
->cs_level
; l
>= 0 ; l
--) {
1433 save
= config
->config_stack
[l
];
1436 for(i
= 0 ; save
[i
].opt
!= NULL
; i
++) {
1437 if(save
[i
].opt
== opt
)
1447 static void m_config_print_option_list(char* prefix
, config_t
* opt_list
) {
1450 char min
[50],max
[50],*type
;
1453 for(opt
= opt_list
; opt
->name
!= NULL
; opt
++) {
1454 if(opt
->type
== CONF_TYPE_SUBCONFIG
) {
1456 pf
= (char*)malloc(strlen(prefix
) + strlen(opt
->name
) + 1);
1457 sprintf(pf
,"%s:%s",prefix
,opt
->name
);
1459 pf
= strdup(opt
->name
);
1460 m_config_print_option_list(pf
,(config_t
*)opt
->p
);
1465 printf("%1.15s:",prefix
);
1466 if(opt
->flags
& CONF_MIN
)
1467 sprintf(min
,"%-8.0f",opt
->min
);
1470 if(opt
->flags
& CONF_MAX
)
1471 sprintf(max
,"%-8.0f",opt
->max
);
1475 case CONF_TYPE_FLAG
:
1481 case CONF_TYPE_FLOAT
:
1484 case CONF_TYPE_STRING
:
1487 case CONF_TYPE_FUNC
:
1488 case CONF_TYPE_FUNC_PARAM
:
1489 case CONF_TYPE_FUNC_FULL
:
1492 case CONF_TYPE_PRINT
:
1495 case CONF_TYPE_STRING_LIST
:
1496 type
= "String list";
1502 printf("%-*.15s %-13.13s %-10.10s %-10.10s %-3.3s %-3.3s %-3.3s\n",
1503 30 - (prefix
? strlen(prefix
) + 1 : 0),
1508 opt
->flags
& CONF_GLOBAL
? "Yes" : "No",
1509 opt
->flags
& CONF_NOCMD
? "No" : "Yes",
1510 opt
->flags
& CONF_NOCFG
? "No" : "Yes");
1516 static void m_config_list_options(m_config_t
*config
) {
1519 printf("\nName Type Min Max Glob CL Cfg\n\n");
1520 for(i
= 0; config
->opt_list
[i
] ; i
++)
1521 m_config_print_option_list(NULL
,config
->opt_list
[i
]);
1526 static void m_config_error(int err
,char* opt
,char* val
) {
1528 case ERR_NOT_AN_OPTION
:
1529 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
,"'%s' is not a mplayer/mencoder option\n",opt
);
1531 case ERR_MISSING_PARAM
:
1532 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
,"option '%s' need a parameter\n",opt
);
1534 case ERR_OUT_OF_RANGE
:
1535 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
,"value '%s' of option '%s' is out of range\n",val
,opt
);
1538 mp_msg(MSGT_CFGPARSER
, MSGL_ERR
,"while parsing option '%s'\n",opt
);