Cope with OSD changes.
[vaapi:sewalliniusms-mplayer.git] / m_option.c
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 Options
21
22 #include "config.h"
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <math.h>
27 #include <stdio.h>
28 #include <stdarg.h>
29 #include <inttypes.h>
30 #include <unistd.h>
31
32 #include "m_option.h"
33 //#include "m_config.h"
34 #include "mp_msg.h"
35 #include "stream/url.h"
36 #include "libavutil/avstring.h"
37
38 // Don't free for 'production' atm
39 #ifndef MP_DEBUG
40 //#define NO_FREE
41 #endif
42
43 const m_option_t* m_option_list_find(const m_option_t* list,const char* name) {
44   int i;
45
46   for(i = 0 ; list[i].name ; i++) {
47     int l = strlen(list[i].name) - 1;
48     if((list[i].type->flags & M_OPT_TYPE_ALLOW_WILDCARD) &&
49        (l > 0) && (list[i].name[l] == '*')) {
50       if(strncasecmp(list[i].name,name,l) == 0)
51         return &list[i];
52     } else if(strcasecmp(list[i].name,name) == 0)
53       return &list[i];
54   }
55   return NULL;
56 }
57
58 // Default function that just does a memcpy
59
60 static void copy_opt(const m_option_t* opt,void* dst,const void* src) {
61   if(dst && src)
62     memcpy(dst,src,opt->type->size);
63 }
64
65 // Helper for the print funcs (from man printf)
66 static char* dup_printf(const char *fmt, ...) {
67   /* Guess we need no more than 50 bytes. */
68   int n, size = 50;
69   char *p;
70   va_list ap;
71   if ((p = malloc (size)) == NULL)
72     return NULL;
73   while (1) {
74     /* Try to print in the allocated space. */
75     va_start(ap, fmt);
76     n = vsnprintf (p, size, fmt, ap);
77     va_end(ap);
78     /* If that worked, return the string. */
79     if (n > -1 && n < size)
80       return p;
81     /* Else try again with more space. */
82     if (n > -1)    /* glibc 2.1 */
83       size = n+1; /* precisely what is needed */
84     else           /* glibc 2.0 */
85       size *= 2;  /* twice the old size */
86     if ((p = realloc (p, size)) == NULL)
87       return NULL;
88   }
89 }
90
91
92 // Flag
93
94 #define VAL(x) (*(int*)(x))
95
96 static int parse_flag(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
97   if (src == M_CONFIG_FILE) {
98     if(!param) return M_OPT_MISSING_PARAM;
99     if (!strcasecmp(param, "yes") ||    /* any other language? */
100         !strcasecmp(param, "on") ||
101         !strcasecmp(param, "ja") ||
102         !strcasecmp(param, "si") ||
103         !strcasecmp(param, "igen") ||
104         !strcasecmp(param, "y") ||
105         !strcasecmp(param, "j") ||
106         !strcasecmp(param, "i") ||
107         !strcasecmp(param, "tak") ||
108         !strcasecmp(param, "ja") ||
109         !strcasecmp(param, "true") ||
110         !strcmp(param, "1")) {
111       if(dst) VAL(dst) = opt->max;
112     } else if (!strcasecmp(param, "no") ||
113                !strcasecmp(param, "off") ||
114                !strcasecmp(param, "nein") ||
115                !strcasecmp(param, "nicht") ||
116                !strcasecmp(param, "nem") ||
117                !strcasecmp(param, "n") ||
118                !strcasecmp(param, "nie") ||
119                !strcasecmp(param, "nej") ||
120                !strcasecmp(param, "false") ||
121                !strcmp(param, "0")) {
122       if(dst) VAL(dst) = opt->min;
123     } else {
124       mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid parameter for %s flag: %s\n",name, param);
125       return M_OPT_INVALID;
126     }
127     return 1;
128   } else {
129     if(dst) VAL(dst) = opt->max;
130     return 0;
131   }
132 }
133
134 static char* print_flag(const m_option_t* opt,  const void* val) {
135   if(VAL(val) == opt->min)
136     return strdup("no");
137   else
138     return strdup("yes");
139 }
140
141 const m_option_type_t m_option_type_flag = {
142   "Flag",
143   "need yes or no in config files",
144   sizeof(int),
145   0,
146   parse_flag,
147   print_flag,
148   copy_opt,
149   copy_opt,
150   NULL,
151   NULL
152 };
153
154 // Integer
155
156 static int parse_int(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
157   long long tmp_int;
158   char *endptr;
159   src = 0;
160
161   if (param == NULL)
162     return M_OPT_MISSING_PARAM;
163
164   tmp_int = strtoll(param, &endptr, 10);
165   if (*endptr)
166     tmp_int = strtoll(param, &endptr, 0);
167   if (*endptr) {
168     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "The %s option must be an integer: %s\n",name, param);
169     return M_OPT_INVALID;
170   }
171
172   if ((opt->flags & M_OPT_MIN) && (tmp_int < opt->min)) {
173     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "The %s option must be >= %d: %s\n", name, (int) opt->min, param);
174     return M_OPT_OUT_OF_RANGE;
175   }
176
177   if ((opt->flags & M_OPT_MAX) && (tmp_int > opt->max)) {
178     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "The %s option must be <= %d: %s\n",name, (int) opt->max, param);
179     return M_OPT_OUT_OF_RANGE;
180   }
181
182   if(dst) {
183     if (opt->type->size == sizeof(int64_t))
184       *(int64_t *)dst = tmp_int;
185     else
186       VAL(dst) = tmp_int;
187   }
188
189   return 1;
190 }
191
192 static char* print_int(const m_option_t* opt,  const void* val) {
193   if (opt->type->size == sizeof(int64_t))
194     return dup_printf("%"PRId64, *(const int64_t *)val);
195   return dup_printf("%d",VAL(val));
196 }
197
198 const m_option_type_t m_option_type_int = {
199   "Integer",
200   "",
201   sizeof(int),
202   0,
203   parse_int,
204   print_int,
205   copy_opt,
206   copy_opt,
207   NULL,
208   NULL
209 };
210
211 const m_option_type_t m_option_type_int64 = {
212   "Integer64",
213   "",
214   sizeof(int64_t),
215   0,
216   parse_int,
217   print_int,
218   copy_opt,
219   copy_opt,
220   NULL,
221   NULL
222 };
223
224 // Float
225
226 #undef VAL
227 #define VAL(x) (*(double*)(x))
228
229 static int parse_double(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
230   double tmp_float;
231   char* endptr;
232   src = 0;
233
234   if (param == NULL)
235     return M_OPT_MISSING_PARAM;
236
237   tmp_float = strtod(param, &endptr);
238
239   switch(*endptr) {
240   case ':':
241   case '/':
242     tmp_float /= strtod(endptr+1, &endptr);
243     break;
244   case '.':
245   case ',':
246     /* we also handle floats specified with
247      * non-locale decimal point ::atmos
248      */
249     if(tmp_float<0)
250       tmp_float -= 1.0/pow(10,strlen(endptr+1)) * strtod(endptr+1, &endptr);
251     else
252       tmp_float += 1.0/pow(10,strlen(endptr+1)) * strtod(endptr+1, &endptr);
253     break;
254   }
255
256   if (*endptr) {
257     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "The %s option must be a floating point "
258            "number or a ratio (numerator[:/]denominator): %s\n",name, param);
259     return M_OPT_INVALID;
260   }
261
262   if (opt->flags & M_OPT_MIN)
263     if (tmp_float < opt->min) {
264       mp_msg(MSGT_CFGPARSER, MSGL_ERR, "The %s option must be >= %f: %s\n", name, opt->min, param);
265       return M_OPT_OUT_OF_RANGE;
266     }
267
268   if (opt->flags & M_OPT_MAX)
269     if (tmp_float > opt->max) {
270       mp_msg(MSGT_CFGPARSER, MSGL_ERR, "The %s option must be <= %f: %s\n", name, opt->max, param);
271       return M_OPT_OUT_OF_RANGE;
272     }
273
274   if(dst) VAL(dst) = tmp_float;
275   return 1;
276 }
277
278 static char* print_double(const m_option_t* opt,  const void* val) {
279   opt = NULL;
280   return dup_printf("%f",VAL(val));
281 }
282
283 const m_option_type_t m_option_type_double = {
284   "Double",
285   "double precision floating point number or ratio (numerator[:/]denominator)",
286   sizeof(double),
287   0,
288   parse_double,
289   print_double,
290   copy_opt,
291   copy_opt,
292   NULL,
293   NULL
294 };
295
296 #undef VAL
297 #define VAL(x) (*(float*)(x))
298
299 static int parse_float(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
300     double tmp;
301     int r= parse_double(opt, name, param, &tmp, src);
302     if(r==1 && dst) VAL(dst) = tmp;
303     return r;
304 }
305
306 static char* print_float(const m_option_t* opt,  const void* val) {
307   opt = NULL;
308   return dup_printf("%f",VAL(val));
309 }
310
311 const m_option_type_t m_option_type_float = {
312   "Float",
313   "floating point number or ratio (numerator[:/]denominator)",
314   sizeof(float),
315   0,
316   parse_float,
317   print_float,
318   copy_opt,
319   copy_opt,
320   NULL,
321   NULL
322 };
323
324 ///////////// Position
325 #undef VAL
326 #define VAL(x) (*(off_t*)(x))
327
328 static int parse_position(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
329   off_t tmp_off;
330   char dummy;
331
332   if (param == NULL)
333     return M_OPT_MISSING_PARAM;
334   if (sscanf(param, sizeof(off_t) == sizeof(int) ?
335              "%d%c" : "%"PRId64"%c", &tmp_off, &dummy) != 1) {
336     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "The %s option must be an integer: %s\n",opt->name,param);
337     return M_OPT_INVALID;
338   }
339
340   if (opt->flags & M_OPT_MIN)
341     if (tmp_off < opt->min) {
342       mp_msg(MSGT_CFGPARSER, MSGL_ERR,
343               "The %s option must be >= %"PRId64": %s\n",
344              name, (int64_t) opt->min, param);
345       return M_OPT_OUT_OF_RANGE;
346     }
347
348   if (opt->flags & M_OPT_MAX)
349     if (tmp_off > opt->max) {
350       mp_msg(MSGT_CFGPARSER, MSGL_ERR,
351               "The %s option must be <= %"PRId64": %s\n",
352              name, (int64_t) opt->max, param);
353       return M_OPT_OUT_OF_RANGE;
354     }
355
356   if(dst)
357     VAL(dst) = tmp_off;
358   return 1;
359 }
360
361 static char* print_position(const m_option_t* opt,  const void* val) {
362   return dup_printf("%"PRId64,(int64_t)VAL(val));
363 }
364
365 const m_option_type_t m_option_type_position = {
366   "Position",
367   "Integer (off_t)",
368   sizeof(off_t),
369   0,
370   parse_position,
371   print_position,
372   copy_opt,
373   copy_opt,
374   NULL,
375   NULL
376 };
377
378
379 ///////////// String
380
381 #undef VAL
382 #define VAL(x) (*(char**)(x))
383
384 static int parse_str(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
385
386
387   if (param == NULL)
388       return M_OPT_MISSING_PARAM;
389
390   if ((opt->flags & M_OPT_MIN) && (strlen(param) < opt->min)) {
391     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Parameter must be >= %d chars: %s\n",
392            (int) opt->min, param);
393     return M_OPT_OUT_OF_RANGE;
394   }
395
396   if ((opt->flags & M_OPT_MAX) && (strlen(param) > opt->max)) {
397     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Parameter must be <= %d chars: %s\n",
398            (int) opt->max, param);
399     return M_OPT_OUT_OF_RANGE;
400   }
401
402   if(dst) {
403     free(VAL(dst));
404     VAL(dst) = strdup(param);
405   }
406
407   return 1;
408
409 }
410
411 static char* print_str(const m_option_t* opt,  const void* val) {
412   return (val && VAL(val) && strlen(VAL(val)) > 0) ? strdup(VAL(val)) : NULL;
413 }
414
415 static void copy_str(const m_option_t* opt,void* dst, const void* src) {
416   if(dst && src) {
417 #ifndef NO_FREE
418     free(VAL(dst)); //FIXME!!!
419 #endif
420     VAL(dst) = VAL(src) ? strdup(VAL(src)) : NULL;
421   }
422 }
423
424 static void free_str(void* src) {
425   if(src && VAL(src)){
426 #ifndef NO_FREE
427     free(VAL(src)); //FIXME!!!
428 #endif
429     VAL(src) = NULL;
430   }
431 }
432
433 const m_option_type_t m_option_type_string = {
434   "String",
435   "",
436   sizeof(char*),
437   M_OPT_TYPE_DYNAMIC,
438   parse_str,
439   print_str,
440   copy_str,
441   copy_str,
442   copy_str,
443   free_str
444 };
445
446 //////////// String list
447
448 #define LIST_SEPARATOR ','
449 #undef VAL
450 #define VAL(x) (*(char***)(x))
451
452 #define OP_NONE 0
453 #define OP_ADD 1
454 #define OP_PRE 2
455 #define OP_DEL 3
456 #define OP_CLR 4
457
458 static void free_str_list(void* dst) {
459   char** d;
460   int i;
461
462   if(!dst || !VAL(dst)) return;
463   d = VAL(dst);
464
465 // FIXME!!!
466 #ifndef NO_FREE
467   for(i = 0 ; d[i] != NULL ; i++)
468     free(d[i]);
469   free(d);
470 #endif
471   VAL(dst) = NULL;
472 }
473
474 static int str_list_add(char** add, int n,void* dst,int pre) {
475   char** lst = VAL(dst);
476   int ln;
477
478   if(!dst) return M_OPT_PARSER_ERR;
479   lst = VAL(dst);
480
481   for(ln = 0 ; lst && lst[ln] ; ln++)
482     /**/;
483
484   lst = realloc(lst,(n+ln+1)*sizeof(char*));
485
486   if(pre) {
487     memmove(&lst[n],lst,ln*sizeof(char*));
488     memcpy(lst,add,n*sizeof(char*));
489   } else
490     memcpy(&lst[ln],add,n*sizeof(char*));
491   // (re-)add NULL-termination
492   lst[ln+n] = NULL;
493
494   free(add);
495
496   VAL(dst) = lst;
497
498   return 1;
499 }
500
501 static int str_list_del(char** del, int n,void* dst) {
502   char **lst,*ep,**d;
503   int i,ln,s;
504   long idx;
505
506   if(!dst) return M_OPT_PARSER_ERR;
507   lst = VAL(dst);
508
509   for(ln = 0 ; lst && lst[ln] ; ln++)
510     /**/;
511   s = ln;
512
513   for(i = 0 ; del[i] != NULL ; i++) {
514     idx = strtol(del[i], &ep, 0);
515     if(*ep) {
516       mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid index: %s\n",del[i]);
517       free(del[i]);
518       continue;
519     }
520     free(del[i]);
521     if(idx < 0 || idx >= ln) {
522       mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Index %ld is out of range.\n",idx);
523       continue;
524     } else if(!lst[idx])
525       continue;
526     free(lst[idx]);
527     lst[idx] = NULL;
528     s--;
529   }
530   free(del);
531
532   if(s == 0) {
533     free(lst);
534     VAL(dst) = NULL;
535     return 1;
536   }
537
538   d = calloc(s+1,sizeof(char*));
539   for(i = 0, n = 0 ; i < ln ; i++) {
540     if(!lst[i]) continue;
541     d[n] = lst[i];
542     n++;
543   }
544   d[s] = NULL;
545
546   free(lst);
547   VAL(dst) = d;
548
549   return 1;
550 }
551
552 static char *get_nextsep(char *ptr, char sep, int modify) {
553     char *last_ptr = ptr;
554     for(;;){
555         ptr = strchr(ptr, sep);
556         if(ptr && ptr>last_ptr && ptr[-1]=='\\'){
557             if (modify) memmove(ptr-1, ptr, strlen(ptr)+1);
558             else ptr++;
559         }else
560             break;
561     }
562     return ptr;
563 }
564
565 static int parse_str_list(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
566   int n = 0,len = strlen(opt->name);
567   char *str;
568   char *ptr = (char *)param, *last_ptr, **res;
569   int op = OP_NONE;
570
571   if(opt->name[len-1] == '*' && ((int)strlen(name) > len - 1)) {
572     const char* n = &name[len-1];
573     if(strcasecmp(n,"-add") == 0)
574       op = OP_ADD;
575     else if(strcasecmp(n,"-pre") == 0)
576       op = OP_PRE;
577     else if(strcasecmp(n,"-del") == 0)
578       op = OP_DEL;
579     else if(strcasecmp(n,"-clr") == 0)
580       op = OP_CLR;
581     else
582       return M_OPT_UNKNOWN;
583   }
584
585   // Clear the list ??
586   if(op == OP_CLR) {
587     if(dst)
588       free_str_list(dst);
589     return 0;
590   }
591
592   // All other ops need a param
593   if (param == NULL || strlen(param) == 0)
594       return M_OPT_MISSING_PARAM;
595
596
597   while(ptr[0] != '\0') {
598     ptr = get_nextsep(ptr, LIST_SEPARATOR, 0);
599     if(!ptr) {
600       n++;
601       break;
602     }
603     ptr++;
604     n++;
605   }
606   if(n == 0)
607     return M_OPT_INVALID;
608   if( ((opt->flags & M_OPT_MIN) && (n < opt->min)) ||
609       ((opt->flags & M_OPT_MAX) && (n > opt->max)) )
610     return M_OPT_OUT_OF_RANGE;
611
612   if(!dst) return 1;
613
614   res = malloc((n+2)*sizeof(char*));
615   ptr = str = strdup(param);
616   n = 0;
617
618   while(1) {
619     last_ptr = ptr;
620     ptr = get_nextsep(ptr, LIST_SEPARATOR, 1);
621     if(!ptr) {
622       res[n] = strdup(last_ptr);
623       n++;
624       break;
625     }
626     len = ptr - last_ptr;
627     res[n] = malloc(len + 1);
628     if(len) strncpy(res[n],last_ptr,len);
629     res[n][len] = '\0';
630     ptr++;
631     n++;
632   }
633   res[n] = NULL;
634   free(str);
635
636   switch(op) {
637   case OP_ADD:
638     return str_list_add(res,n,dst,0);
639   case OP_PRE:
640     return str_list_add(res,n,dst,1);
641   case OP_DEL:
642     return str_list_del(res,n,dst);
643   }
644
645   if(VAL(dst))
646     free_str_list(dst);
647   VAL(dst) = res;
648
649   return 1;
650 }
651
652 static void copy_str_list(const m_option_t* opt,void* dst, const void* src) {
653   int n;
654   char **d,**s;
655
656   if(!(dst && src)) return;
657   s = VAL(src);
658
659   if(VAL(dst))
660     free_str_list(dst);
661
662   if(!s) {
663     VAL(dst) = NULL;
664     return;
665   }
666
667   for(n = 0 ; s[n] != NULL ; n++)
668     /* NOTHING */;
669   d = malloc((n+1)*sizeof(char*));
670   for( ; n >= 0 ; n--)
671     d[n] = s[n] ? strdup(s[n]) : NULL;
672
673   VAL(dst) = d;
674 }
675
676 static char* print_str_list(const m_option_t* opt, const void* src) {
677   char **lst = NULL;
678   char *ret = NULL,*last = NULL;
679   int i;
680
681   if(!(src && VAL(src))) return NULL;
682   lst = VAL(src);
683
684   for(i = 0 ; lst[i] ; i++) {
685     if(last) {
686       ret = dup_printf("%s,%s",last,lst[i]);
687       free(last);
688     } else
689       ret = strdup(lst[i]);
690     last = ret;
691   }
692   if(last && last != ret) free(last);
693   return ret;
694 }
695
696 const m_option_type_t m_option_type_string_list = {
697   "String list",
698   "A list of strings separated by ','\n"
699   "Option with a name ending in an * permits using the following suffix: \n"
700   "\t-add: Add the given parameters at the end of the list.\n"
701   "\t-pre: Add the given parameters at the beginning of the list.\n"
702   "\t-del: Remove the entry at the given indices.\n"
703   "\t-clr: Clear the list.\n"
704   "e.g: -vf-add flip,mirror -vf-del 2,5\n",
705   sizeof(char**),
706   M_OPT_TYPE_DYNAMIC | M_OPT_TYPE_ALLOW_WILDCARD,
707   parse_str_list,
708   print_str_list,
709   copy_str_list,
710   copy_str_list,
711   copy_str_list,
712   free_str_list
713 };
714
715
716 ///////////////////  Func based options
717
718 static int parse_call_func(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
719   int res = ((m_opt_func_param_t) opt->p)(opt,param);
720   if (res < 0)
721     return res;
722   return 1;
723 }
724
725 // special variant, will not have a history/be able to
726 // be used as per-file option etc.
727 const m_option_type_t m_option_type_func_param_immediate = {
728   "Func param once",
729   "",
730   0,
731   M_OPT_TYPE_INDIRECT,
732   parse_call_func,
733   NULL,
734   NULL, // Nothing to do on save
735   NULL,
736   NULL,
737   NULL
738 };
739
740 // A chained list to save the various calls for func_param and func_full
741 typedef struct m_func_save m_func_save_t;
742 struct m_func_save {
743   m_func_save_t* next;
744   char* name;
745   char* param;
746 };
747
748 #undef VAL
749 #define VAL(x) (*(m_func_save_t**)(x))
750
751 static void free_func_pf(void* src) {
752   m_func_save_t *s,*n;
753
754   if(!src) return;
755
756   s = VAL(src);
757
758   while(s) {
759     n = s->next;
760     free(s->name);
761     free(s->param);
762     free(s);
763     s = n;
764   }
765   VAL(src) = NULL;
766 }
767
768 // Parser for func_param and func_full
769 static int parse_func_pf(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
770   m_func_save_t *s,*p;
771
772   if(!dst)
773     return 1;
774
775   s = calloc(1,sizeof(m_func_save_t));
776   s->name = strdup(name);
777   s->param = param ? strdup(param) : NULL;
778
779   p = VAL(dst);
780   if(p) {
781     for( ; p->next != NULL ; p = p->next)
782       /**/;
783     p->next = s;
784   } else
785     VAL(dst) = s;
786
787   return 1;
788 }
789
790 static void copy_func_pf(const m_option_t* opt,void* dst, const void* src) {
791   m_func_save_t *d = NULL, *s,* last = NULL;
792
793   if(!(dst && src)) return;
794   s = VAL(src);
795
796   if(VAL(dst))
797     free_func_pf(dst);
798
799   while(s) {
800     d = calloc(1,sizeof(m_func_save_t));
801     d->name = strdup(s->name);
802     d->param = s->param ? strdup(s->param) : NULL;
803     if(last)
804       last->next = d;
805     else
806       VAL(dst) = d;
807     last = d;
808     s = s->next;
809   }
810
811
812 }
813
814 /////////////////// Func_param
815
816 static void set_func_param(const m_option_t* opt, void* dst, const void* src) {
817   m_func_save_t* s;
818
819   if(!src) return;
820   s = VAL(src);
821
822   if(!s) return;
823
824   // Revert if needed
825   if(opt->priv) ((m_opt_default_func_t)opt->priv)(opt,opt->name);
826   for( ; s != NULL ; s = s->next)
827     ((m_opt_func_param_t) opt->p)(opt,s->param);
828 }
829
830 const m_option_type_t m_option_type_func_param = {
831   "Func param",
832   "",
833   sizeof(m_func_save_t*),
834   M_OPT_TYPE_INDIRECT,
835   parse_func_pf,
836   NULL,
837   NULL, // Nothing to do on save
838   set_func_param,
839   copy_func_pf,
840   free_func_pf
841 };
842
843 /////////////////// Func_full
844
845 static void set_func_full(const m_option_t* opt, void* dst, const void* src) {
846   m_func_save_t* s;
847
848   if(!src) return;
849
850   for(s = VAL(src) ; s ; s = s->next) {
851     // Revert if needed
852     if(opt->priv) ((m_opt_default_func_t)opt->priv)(opt,s->name);
853     ((m_opt_func_full_t) opt->p)(opt,s->name,s->param);
854   }
855 }
856
857 const m_option_type_t m_option_type_func_full = {
858   "Func full",
859   "",
860   sizeof(m_func_save_t*),
861   M_OPT_TYPE_ALLOW_WILDCARD|M_OPT_TYPE_INDIRECT,
862   parse_func_pf,
863   NULL,
864   NULL, // Nothing to do on save
865   set_func_full,
866   copy_func_pf,
867   free_func_pf
868 };
869
870 /////////////// Func
871
872 #undef VAL
873 #define VAL(x) (*(int*)(x))
874
875 static int parse_func(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
876   if(dst)
877     VAL(dst) += 1;
878   return 0;
879 }
880
881 static void set_func(const m_option_t* opt,void* dst, const void* src) {
882   int i;
883   if(opt->priv) ((m_opt_default_func_t)opt->priv)(opt,opt->name);
884   for(i = 0 ; i < VAL(src) ; i++)
885     ((m_opt_func_t) opt->p)(opt);
886 }
887
888 const m_option_type_t m_option_type_func = {
889   "Func",
890   "",
891   sizeof(int),
892   M_OPT_TYPE_INDIRECT,
893   parse_func,
894   NULL,
895   NULL, // Nothing to do on save
896   set_func,
897   NULL,
898   NULL
899 };
900
901 /////////////////// Print
902
903 static int parse_print(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
904   if(opt->type == CONF_TYPE_PRINT_INDIRECT)
905     mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s", *(char **) opt->p);
906   else if(opt->type == CONF_TYPE_PRINT_FUNC)
907     return ((m_opt_func_full_t) opt->p)(opt,name,param);
908   else
909     mp_msg(MSGT_CFGPARSER, MSGL_INFO, "%s", (char *) opt->p);
910
911   if(opt->priv == NULL)
912     return M_OPT_EXIT;
913   if(opt->priv == PRIV_NO_EXIT)
914     return 0;
915   return 1;
916 }
917
918 const m_option_type_t m_option_type_print = {
919   "Print",
920   "",
921   0,
922   0,
923   parse_print,
924   NULL,
925   NULL,
926   NULL,
927   NULL,
928   NULL
929 };
930
931 const m_option_type_t m_option_type_print_indirect = {
932   "Print",
933   "",
934   0,
935   0,
936   parse_print,
937   NULL,
938   NULL,
939   NULL,
940   NULL,
941   NULL
942 };
943
944 const m_option_type_t m_option_type_print_func = {
945   "Print",
946   "",
947   0,
948   M_OPT_TYPE_ALLOW_WILDCARD,
949   parse_print,
950   NULL,
951   NULL,
952   NULL,
953   NULL,
954   NULL
955 };
956
957
958 /////////////////////// Subconfig
959 #undef VAL
960 #define VAL(x) (*(char***)(x))
961
962 static int parse_subconf(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
963   char *subparam;
964   char *subopt;
965   int nr = 0,i,r;
966   const m_option_t *subopts;
967   const char *p;
968   char** lst = NULL;
969
970   if (param == NULL || strlen(param) == 0)
971     return M_OPT_MISSING_PARAM;
972
973   subparam = malloc(strlen(param)+1);
974   subopt = malloc(strlen(param)+1);
975   p = param;
976
977   subopts = opt->p;
978
979   while(p[0])
980     {
981       int sscanf_ret = 1;
982       int optlen = strcspn(p, ":=");
983       /* clear out */
984       subopt[0] = subparam[0] = 0;
985       av_strlcpy(subopt, p, optlen + 1);
986       p = &p[optlen];
987       if (p[0] == '=') {
988         sscanf_ret = 2;
989         p = &p[1];
990         if (p[0] == '"') {
991           p = &p[1];
992           optlen = strcspn(p, "\"");
993           av_strlcpy(subparam, p, optlen + 1);
994           p = &p[optlen];
995           if (p[0] != '"') {
996             mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Terminating '\"' missing for '%s'\n", subopt);
997             return M_OPT_INVALID;
998           }
999           p = &p[1];
1000         } else if (p[0] == '%') {
1001           p = &p[1];
1002           optlen = (int)strtol(p, (char**)&p, 0);
1003           if (!p || p[0] != '%' || (optlen > strlen(p) - 1)) {
1004             mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Invalid length %i for '%s'\n", optlen, subopt);
1005             return M_OPT_INVALID;
1006           }
1007           p = &p[1];
1008           av_strlcpy(subparam, p, optlen + 1);
1009           p = &p[optlen];
1010         } else {
1011           optlen = strcspn(p, ":");
1012           av_strlcpy(subparam, p, optlen + 1);
1013           p = &p[optlen];
1014         }
1015       }
1016       if (p[0] == ':')
1017         p = &p[1];
1018       else if (p[0]) {
1019         mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Incorrect termination for '%s'\n", subopt);
1020         return M_OPT_INVALID;
1021       }
1022
1023       switch(sscanf_ret)
1024         {
1025         case 1:
1026           subparam[0] = 0;
1027         case 2:
1028           for(i = 0 ; subopts[i].name ; i++) {
1029             if(!strcmp(subopts[i].name,subopt)) break;
1030           }
1031           if(!subopts[i].name) {
1032             mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Unknown suboption %s\n",name,subopt);
1033             return M_OPT_UNKNOWN;
1034           }
1035           r = m_option_parse(&subopts[i],subopt,
1036                              subparam[0] == 0 ? NULL : subparam,NULL,src);
1037           if(r < 0) return r;
1038           if(dst) {
1039             lst = realloc(lst,2 * (nr+2) * sizeof(char*));
1040             lst[2*nr] = strdup(subopt);
1041             lst[2*nr+1] = subparam[0] == 0 ? NULL : strdup(subparam);
1042             memset(&lst[2*(nr+1)],0,2*sizeof(char*));
1043             nr++;
1044           }
1045           break;
1046         }
1047     }
1048
1049   free(subparam);
1050   free(subopt);
1051   if(dst)
1052     VAL(dst) = lst;
1053
1054   return 1;
1055 }
1056
1057 const m_option_type_t m_option_type_subconfig = {
1058   "Subconfig",
1059   "The syntax is -option opt1=foo:flag:opt2=blah",
1060   sizeof(int),
1061   M_OPT_TYPE_HAS_CHILD,
1062   parse_subconf,
1063   NULL,
1064   NULL,
1065   NULL,
1066   NULL,
1067   NULL
1068 };
1069
1070 #include "libmpcodecs/img_format.h"
1071
1072 /* FIXME: snyc with img_format.h */
1073 static struct {
1074   const char* name;
1075   unsigned int fmt;
1076 } mp_imgfmt_list[] = {
1077   {"444p16le", IMGFMT_444P16_LE},
1078   {"444p16be", IMGFMT_444P16_BE},
1079   {"444p10le", IMGFMT_444P10_LE},
1080   {"444p10be", IMGFMT_444P10_BE},
1081   {"444p9le",  IMGFMT_444P9_LE},
1082   {"444p9be",  IMGFMT_444P9_BE},
1083   {"422p16le", IMGFMT_422P16_LE},
1084   {"422p16be", IMGFMT_422P16_BE},
1085   {"422p10le", IMGFMT_422P10_LE},
1086   {"422p10be", IMGFMT_422P10_BE},
1087   {"422p9le",  IMGFMT_422P9_LE},
1088   {"422p9be",  IMGFMT_422P9_BE},
1089   {"420p16le", IMGFMT_420P16_LE},
1090   {"420p16be", IMGFMT_420P16_BE},
1091   {"420p10le", IMGFMT_420P10_LE},
1092   {"420p10be", IMGFMT_420P10_BE},
1093   {"420p9le",  IMGFMT_420P9_LE},
1094   {"420p9be",  IMGFMT_420P9_BE},
1095   {"444p16",   IMGFMT_444P16},
1096   {"422p16",   IMGFMT_422P16},
1097   {"422p10",   IMGFMT_422P10},
1098   {"420p16",   IMGFMT_420P16},
1099   {"420p10",   IMGFMT_420P10},
1100   {"420p9",    IMGFMT_420P9},
1101   {"420a", IMGFMT_420A},
1102   {"444p", IMGFMT_444P},
1103   {"422p", IMGFMT_422P},
1104   {"411p", IMGFMT_411P},
1105   {"440p", IMGFMT_440P},
1106   {"yuy2", IMGFMT_YUY2},
1107   {"yvyu", IMGFMT_YVYU},
1108   {"uyvy", IMGFMT_UYVY},
1109   {"yvu9", IMGFMT_YVU9},
1110   {"if09", IMGFMT_IF09},
1111   {"yv12", IMGFMT_YV12},
1112   {"i420", IMGFMT_I420},
1113   {"iyuv", IMGFMT_IYUV},
1114   {"clpl", IMGFMT_CLPL},
1115   {"hm12", IMGFMT_HM12},
1116   {"y800", IMGFMT_Y800},
1117   {"y8",   IMGFMT_Y8},
1118   {"nv12", IMGFMT_NV12},
1119   {"nv21", IMGFMT_NV21},
1120   {"bgr24", IMGFMT_BGR24},
1121   {"bgr32", IMGFMT_BGR32},
1122   {"bgr16", IMGFMT_BGR16},
1123   {"bgr15", IMGFMT_BGR15},
1124   {"bgr12", IMGFMT_BGR12},
1125   {"bgr8", IMGFMT_BGR8},
1126   {"bgr4", IMGFMT_BGR4},
1127   {"bg4b", IMGFMT_BG4B},
1128   {"bgr1", IMGFMT_BGR1},
1129   {"rgb48be", IMGFMT_RGB48BE},
1130   {"rgb48le", IMGFMT_RGB48LE},
1131   {"rgb48ne", IMGFMT_RGB48NE},
1132   {"rgb24", IMGFMT_RGB24},
1133   {"rgb32", IMGFMT_RGB32},
1134   {"rgb16", IMGFMT_RGB16},
1135   {"rgb15", IMGFMT_RGB15},
1136   {"rgb12", IMGFMT_RGB12},
1137   {"rgb8", IMGFMT_RGB8},
1138   {"rgb4", IMGFMT_RGB4},
1139   {"rg4b", IMGFMT_RG4B},
1140   {"rgb1", IMGFMT_RGB1},
1141   {"rgba", IMGFMT_RGBA},
1142   {"argb", IMGFMT_ARGB},
1143   {"bgra", IMGFMT_BGRA},
1144   {"abgr", IMGFMT_ABGR},
1145   {"gbr24p", IMGFMT_GBR24P},
1146   {"mjpeg", IMGFMT_MJPEG},
1147   {"mjpg", IMGFMT_MJPEG},
1148   { NULL, 0 }
1149 };
1150
1151 static int parse_imgfmt(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
1152   uint32_t fmt = 0;
1153   int i;
1154
1155   if (param == NULL || strlen(param) == 0)
1156     return M_OPT_MISSING_PARAM;
1157
1158   if(!strcmp(param,"help")) {
1159     mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available formats:");
1160     for(i = 0 ; mp_imgfmt_list[i].name ; i++)
1161       mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %s",mp_imgfmt_list[i].name);
1162     mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
1163     return M_OPT_EXIT - 1;
1164   }
1165
1166   if (sscanf(param, "0x%x", &fmt) != 1)
1167   {
1168   for(i = 0 ; mp_imgfmt_list[i].name ; i++) {
1169     if(!strcasecmp(param,mp_imgfmt_list[i].name)) {
1170       fmt=mp_imgfmt_list[i].fmt;
1171       break;
1172     }
1173   }
1174   if(!mp_imgfmt_list[i].name) {
1175     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: unknown format name: '%s'\n",name,param);
1176     return M_OPT_INVALID;
1177   }
1178   }
1179
1180   if(dst)
1181     *((uint32_t*)dst) = fmt;
1182
1183   return 1;
1184 }
1185
1186 const m_option_type_t m_option_type_imgfmt = {
1187   "Image format",
1188   "Please report any missing colorspaces.",
1189   sizeof(uint32_t),
1190   0,
1191   parse_imgfmt,
1192   NULL,
1193   copy_opt,
1194   copy_opt,
1195   NULL,
1196   NULL
1197 };
1198
1199 #include "libaf/af_format.h"
1200
1201 /* FIXME: snyc with af_format.h */
1202 static struct {
1203   const char* name;
1204   unsigned int fmt;
1205 } mp_afmt_list[] = {
1206   // SPECIAL
1207   {"mulaw", AF_FORMAT_MU_LAW},
1208   {"alaw", AF_FORMAT_A_LAW},
1209   {"mpeg2", AF_FORMAT_MPEG2},
1210   {"ac3le", AF_FORMAT_AC3_LE},
1211   {"ac3be", AF_FORMAT_AC3_BE},
1212   {"ac3ne", AF_FORMAT_AC3_NE},
1213   {"imaadpcm", AF_FORMAT_IMA_ADPCM},
1214   // ORIDNARY
1215   {"u8", AF_FORMAT_U8},
1216   {"s8", AF_FORMAT_S8},
1217   {"u16le", AF_FORMAT_U16_LE},
1218   {"u16be", AF_FORMAT_U16_BE},
1219   {"u16ne", AF_FORMAT_U16_NE},
1220   {"s16le", AF_FORMAT_S16_LE},
1221   {"s16be", AF_FORMAT_S16_BE},
1222   {"s16ne", AF_FORMAT_S16_NE},
1223   {"u24le", AF_FORMAT_U24_LE},
1224   {"u24be", AF_FORMAT_U24_BE},
1225   {"u24ne", AF_FORMAT_U24_NE},
1226   {"s24le", AF_FORMAT_S24_LE},
1227   {"s24be", AF_FORMAT_S24_BE},
1228   {"s24ne", AF_FORMAT_S24_NE},
1229   {"u32le", AF_FORMAT_U32_LE},
1230   {"u32be", AF_FORMAT_U32_BE},
1231   {"u32ne", AF_FORMAT_U32_NE},
1232   {"s32le", AF_FORMAT_S32_LE},
1233   {"s32be", AF_FORMAT_S32_BE},
1234   {"s32ne", AF_FORMAT_S32_NE},
1235   {"floatle", AF_FORMAT_FLOAT_LE},
1236   {"floatbe", AF_FORMAT_FLOAT_BE},
1237   {"floatne", AF_FORMAT_FLOAT_NE},
1238   { NULL, 0 }
1239 };
1240
1241 static int parse_afmt(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
1242   uint32_t fmt = 0;
1243   int i;
1244
1245   if (param == NULL || strlen(param) == 0)
1246     return M_OPT_MISSING_PARAM;
1247
1248   if(!strcmp(param,"help")) {
1249     mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available formats:");
1250     for(i = 0 ; mp_afmt_list[i].name ; i++)
1251       mp_msg(MSGT_CFGPARSER, MSGL_INFO, " %s",mp_afmt_list[i].name);
1252     mp_msg(MSGT_CFGPARSER, MSGL_INFO, "\n");
1253     return M_OPT_EXIT - 1;
1254   }
1255
1256   if (sscanf(param, "0x%x", &fmt) != 1)
1257   {
1258   for(i = 0 ; mp_afmt_list[i].name ; i++) {
1259     if(!strcasecmp(param,mp_afmt_list[i].name)) {
1260       fmt=mp_afmt_list[i].fmt;
1261       break;
1262     }
1263   }
1264   if(!mp_afmt_list[i].name) {
1265     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: unknown format name: '%s'\n",name,param);
1266     return M_OPT_INVALID;
1267   }
1268   }
1269
1270   if(dst)
1271     *((uint32_t*)dst) = fmt;
1272
1273   return 1;
1274 }
1275
1276 const m_option_type_t m_option_type_afmt = {
1277   "Audio format",
1278   "Please report any missing formats.",
1279   sizeof(uint32_t),
1280   0,
1281   parse_afmt,
1282   NULL,
1283   copy_opt,
1284   copy_opt,
1285   NULL,
1286   NULL
1287 };
1288
1289
1290 int parse_timestring(const char *str, double *time, char endchar)
1291 {
1292   int a, b, len;
1293   double d;
1294   *time = 0; /* ensure initialization for error cases */
1295   if (sscanf(str, "%d:%d:%lf%n", &a, &b, &d, &len) >= 3)
1296     *time = 3600*a + 60*b + d;
1297   else if (sscanf(str, "%d:%lf%n", &a, &d, &len) >= 2)
1298     *time = 60*a + d;
1299   else if (sscanf(str, "%lf%n", &d, &len) >= 1)
1300     *time = d;
1301   else
1302     return 0; /* unsupported time format */
1303   if (str[len] && str[len] != endchar)
1304     return 0; /* invalid extra characters at the end */
1305   return len;
1306 }
1307
1308
1309 static int parse_time(const m_option_t* opt,const char *name, const char *param, void* dst, int src)
1310 {
1311   double time;
1312
1313   if (param == NULL || strlen(param) == 0)
1314     return M_OPT_MISSING_PARAM;
1315
1316   if (!parse_timestring(param, &time, 0)) {
1317     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: invalid time: '%s'\n",
1318            name,param);
1319     return M_OPT_INVALID;
1320   }
1321
1322   if (dst)
1323     *(double *)dst = time;
1324   return 1;
1325 }
1326
1327 const m_option_type_t m_option_type_time = {
1328   "Time",
1329   "",
1330   sizeof(double),
1331   0,
1332   parse_time,
1333   print_double,
1334   copy_opt,
1335   copy_opt,
1336   NULL,
1337   NULL
1338 };
1339
1340
1341 // Time or size (-endpos)
1342
1343 static int parse_time_size(const m_option_t* opt,const char *name, const char *param, void* dst, int src) {
1344   m_time_size_t ts;
1345   char unit[4];
1346   double end_at;
1347
1348   if (param == NULL || strlen(param) == 0)
1349     return M_OPT_MISSING_PARAM;
1350
1351   ts.pos=0;
1352   /* End at size parsing */
1353   if(sscanf(param, "%lf%3s", &end_at, unit) == 2) {
1354     ts.type = END_AT_SIZE;
1355     if(!strcasecmp(unit, "b"))
1356       ;
1357     else if(!strcasecmp(unit, "kb"))
1358       end_at *= 1024;
1359     else if(!strcasecmp(unit, "mb"))
1360       end_at *= 1024*1024;
1361     else if(!strcasecmp(unit, "gb"))
1362       end_at *= 1024*1024*1024;
1363     else
1364       ts.type = END_AT_NONE;
1365
1366     if (ts.type == END_AT_SIZE) {
1367       ts.pos  = end_at;
1368       goto out;
1369     }
1370   }
1371
1372   /* End at time parsing. This has to be last because the parsing accepts
1373    * even a number followed by garbage */
1374   if (!parse_timestring(param, &end_at, 0)) {
1375     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: invalid time or size: '%s'\n",
1376            name,param);
1377     return M_OPT_INVALID;
1378   }
1379
1380   ts.type = END_AT_TIME;
1381   ts.pos  = end_at;
1382 out:
1383   if(dst)
1384     *(m_time_size_t *)dst = ts;
1385   return 1;
1386 }
1387
1388 const m_option_type_t m_option_type_time_size = {
1389   "Time or size",
1390   "",
1391   sizeof(m_time_size_t),
1392   0,
1393   parse_time_size,
1394   NULL,
1395   copy_opt,
1396   copy_opt,
1397   NULL,
1398   NULL
1399 };
1400
1401
1402 //// Objects (i.e. filters, etc) settings
1403
1404 #include "m_struct.h"
1405
1406 #undef VAL
1407 #define VAL(x) (*(m_obj_settings_t**)(x))
1408
1409 static int find_obj_desc(const char* name,const m_obj_list_t* l,const m_struct_t** ret) {
1410   int i;
1411   char* n;
1412
1413   for(i = 0 ; l->list[i] ; i++) {
1414     n = M_ST_MB(char*,l->list[i],l->name_off);
1415     if(!strcmp(n,name)) {
1416       *ret = M_ST_MB(m_struct_t*,l->list[i],l->desc_off);
1417       return 1;
1418     }
1419   }
1420   return 0;
1421 }
1422
1423 static int get_obj_param(const char* opt_name,const char* obj_name, const m_struct_t* desc,
1424                          char* str,int* nold,int oldmax,char** dst) {
1425   char* eq;
1426   const m_option_t* opt;
1427   int r;
1428
1429   eq = strchr(str,'=');
1430   if(eq && eq == str)
1431     eq = NULL;
1432
1433   if(eq) {
1434     char* p = eq + 1;
1435     if(p[0] == '\0') p = NULL;
1436     eq[0] = '\0';
1437     opt = m_option_list_find(desc->fields,str);
1438     if(!opt) {
1439       mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: %s doesn't have a %s parameter.\n",opt_name,obj_name,str);
1440       return M_OPT_UNKNOWN;
1441     }
1442     r = m_option_parse(opt,str,p,NULL,M_CONFIG_FILE);
1443     if(r < 0) {
1444       if(r > M_OPT_EXIT)
1445         mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Error while parsing %s parameter %s (%s)\n",opt_name,obj_name,str,p);
1446       eq[0] = '=';
1447       return r;
1448     }
1449     if(dst) {
1450       dst[0] = strdup(str);
1451       dst[1] = p ? strdup(p) : NULL;
1452     }
1453     eq[0] = '=';
1454   } else {
1455     if((*nold) >= oldmax) {
1456       mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: %s has only %d params, so you can't give more than %d unnamed params.\n",
1457              opt_name,obj_name,oldmax,oldmax);
1458       return M_OPT_OUT_OF_RANGE;
1459     }
1460     opt = &desc->fields[(*nold)];
1461     r = m_option_parse(opt,opt->name,str,NULL,M_CONFIG_FILE);
1462     if(r < 0) {
1463       if(r > M_OPT_EXIT)
1464         mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Error while parsing %s parameter %s (%s)\n",opt_name,obj_name,opt->name,str);
1465       return r;
1466     }
1467     if(dst) {
1468       dst[0] = strdup(opt->name);
1469       dst[1] = strdup(str);
1470     }
1471     (*nold)++;
1472   }
1473   return 1;
1474 }
1475
1476 static int get_obj_params(const char* opt_name, const char* name,char* params,
1477                           const m_struct_t* desc,char separator, char*** _ret) {
1478   int n = 0,nold = 0, nopts,r;
1479   char* ptr,*last_ptr = params;
1480   char** ret;
1481
1482   if(!strcmp(params,"help")) { // Help
1483     char min[50],max[50];
1484     if(!desc->fields) {
1485       printf("%s doesn't have any options.\n\n",name);
1486       return M_OPT_EXIT - 1;
1487     }
1488     printf("\n Name                 Type            Min        Max\n\n");
1489     for(n = 0 ; desc->fields[n].name ; n++) {
1490       const m_option_t* opt = &desc->fields[n];
1491       if(opt->type->flags & M_OPT_TYPE_HAS_CHILD) continue;
1492       if(opt->flags & M_OPT_MIN)
1493         sprintf(min,"%-8.0f",opt->min);
1494       else
1495         strcpy(min,"No");
1496       if(opt->flags & M_OPT_MAX)
1497         sprintf(max,"%-8.0f",opt->max);
1498       else
1499         strcpy(max,"No");
1500       printf(" %-20.20s %-15.15s %-10.10s %-10.10s\n",
1501              opt->name,
1502              opt->type->name,
1503              min,
1504              max);
1505     }
1506     printf("\n");
1507     return M_OPT_EXIT - 1;
1508   }
1509
1510   for(nopts = 0 ; desc->fields[nopts].name ; nopts++)
1511     /* NOP */;
1512
1513   // TODO : Check that each opt can be parsed
1514   r = 1;
1515   while(last_ptr && last_ptr[0] != '\0') {
1516     ptr = strchr(last_ptr,separator);
1517     if(!ptr) {
1518       r = get_obj_param(opt_name,name,desc,last_ptr,&nold,nopts,NULL);
1519       n++;
1520       break;
1521     }
1522     if(ptr == last_ptr) { // Empty field, count it and go on
1523       nold++;
1524       last_ptr = ptr+1;
1525       continue;
1526     }
1527     ptr[0] = '\0';
1528     r = get_obj_param(opt_name,name,desc,last_ptr,&nold,nopts,NULL);
1529     ptr[0] = separator;
1530     if(r < 0) break;
1531     n++;
1532     last_ptr = ptr+1;
1533   }
1534   if(r < 0) return r;
1535   if (!last_ptr[0]) // count an empty field at the end, too
1536     nold++;
1537   if (nold > nopts) {
1538     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Too many options for %s\n", name);
1539     return M_OPT_OUT_OF_RANGE;
1540   }
1541   if(!_ret) // Just test
1542     return 1;
1543   if (n == 0) // No options or only empty options
1544     return 1;
1545
1546   ret = malloc((n+2)*2*sizeof(char*));
1547   n = nold = 0;
1548   last_ptr = params;
1549
1550   while(last_ptr && last_ptr[0] != '\0') {
1551     ptr = strchr(last_ptr,separator);
1552     if(!ptr) {
1553       get_obj_param(opt_name,name,desc,last_ptr,&nold,nopts,&ret[n*2]);
1554       n++;
1555       break;
1556     }
1557     if(ptr == last_ptr) { // Empty field, count it and go on
1558       last_ptr = ptr+1;
1559       nold++;
1560       continue;
1561     }
1562     ptr[0] = '\0';
1563     get_obj_param(opt_name,name,desc,last_ptr,&nold,nopts,&ret[n*2]);
1564     n++;
1565     last_ptr = ptr+1;
1566   }
1567   ret[n*2] = ret[n*2+1] = NULL;
1568   *_ret = ret;
1569
1570   return 1;
1571 }
1572
1573 static int parse_obj_params(const m_option_t* opt,const char *name,
1574                             const char *param, void* dst, int src) {
1575   char** opts;
1576   int r;
1577   m_obj_params_t* p = opt->priv;
1578   const m_struct_t* desc;
1579   char* cpy;
1580
1581   // We need the object desc
1582   if(!p)
1583     return M_OPT_INVALID;
1584
1585   desc = p->desc;
1586   cpy = strdup(param);
1587   r = get_obj_params(name,desc->name,cpy,desc,p->separator,dst ? &opts : NULL);
1588   free(cpy);
1589   if(r < 0)
1590     return r;
1591   if(!dst)
1592     return 1;
1593   if (!opts) // no arguments given
1594     return 1;
1595
1596   for(r = 0 ; opts[r] ; r += 2)
1597     m_struct_set(desc,dst,opts[r],opts[r+1]);
1598
1599   return 1;
1600 }
1601
1602
1603 const m_option_type_t m_option_type_obj_params = {
1604   "Object params",
1605   "",
1606   0,
1607   0,
1608   parse_obj_params,
1609   NULL,
1610   NULL,
1611   NULL,
1612   NULL,
1613   NULL
1614 };
1615
1616 /// Some predefined types as a definition would be quite lengthy
1617
1618 /// Span arguments
1619 static const m_span_t m_span_params_dflts = { -1, -1 };
1620 static const m_option_t m_span_params_fields[] = {
1621   {"start", M_ST_OFF(m_span_t,start), CONF_TYPE_INT, M_OPT_MIN, 1 ,0, NULL},
1622   {"end", M_ST_OFF(m_span_t,end), CONF_TYPE_INT, M_OPT_MIN , 1 ,0, NULL},
1623   { NULL, NULL, 0, 0, 0, 0,  NULL }
1624 };
1625 static const struct m_struct_st m_span_opts = {
1626   "m_span",
1627   sizeof(m_span_t),
1628   &m_span_params_dflts,
1629   m_span_params_fields
1630 };
1631 const m_obj_params_t m_span_params_def = {
1632   &m_span_opts,
1633   '-'
1634 };
1635
1636 static int parse_obj_settings(const char* opt,char* str,const m_obj_list_t* list,
1637                               m_obj_settings_t **_ret, int ret_n) {
1638   int r;
1639   char *param,**plist = NULL;
1640   const m_struct_t* desc;
1641   m_obj_settings_t *ret = _ret ? *_ret : NULL;
1642
1643
1644   // Now check that the object exists
1645   param = strchr(str,'=');
1646   if(param) {
1647     param[0] = '\0';
1648     param++;
1649     if(strlen(param) <= 0)
1650       param = NULL;
1651   }
1652
1653
1654   if(!find_obj_desc(str,list,&desc)) {
1655     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: %s doesn't exist.\n",opt,str);
1656     return M_OPT_INVALID;
1657   }
1658
1659   if(param) {
1660     if(!desc && _ret) {
1661       if(!strcmp(param,"help")) {
1662         mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Option %s: %s have no option description.\n",opt,str);
1663         return M_OPT_EXIT - 1;
1664       }
1665       plist = calloc(4,sizeof(char*));
1666       plist[0] = strdup("_oldargs_");
1667       plist[1] = strdup(param);
1668     } else if(desc) {
1669       r = get_obj_params(opt,str,param,desc,':',_ret ? &plist : NULL);
1670       if(r < 0)
1671         return r;
1672     }
1673   }
1674   if(!_ret)
1675     return 1;
1676
1677   ret = realloc(ret,(ret_n+2)*sizeof(m_obj_settings_t));
1678   memset(&ret[ret_n],0,2*sizeof(m_obj_settings_t));
1679   ret[ret_n].name = strdup(str);
1680   ret[ret_n].attribs = plist;
1681
1682   *_ret = ret;
1683   return 1;
1684 }
1685
1686 static int obj_settings_list_del(const char *opt_name,const char *param,void* dst, int src) {
1687   char** str_list = NULL;
1688   int r,i,idx_max = 0;
1689   char* rem_id = "_removed_marker_";
1690   const m_option_t list_opt = {opt_name , NULL, CONF_TYPE_STRING_LIST,
1691                            0, 0, 0, NULL };
1692   m_obj_settings_t* obj_list = dst ? VAL(dst) : NULL;
1693
1694   if(dst && !obj_list) {
1695     mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Option %s: the list is empty.\n",opt_name);
1696     return 1;
1697   } else if(obj_list) {
1698     for(idx_max = 0 ; obj_list[idx_max].name != NULL ; idx_max++)
1699       /* NOP */;
1700   }
1701
1702   r = m_option_parse(&list_opt,opt_name,param,&str_list,src);
1703   if(r < 0 || !str_list)
1704     return r;
1705
1706   for(r = 0 ; str_list[r] ; r++) {
1707     int id;
1708     char* endptr;
1709     id = strtol(str_list[r],&endptr,0);
1710     if(endptr == str_list[r]) {
1711       mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: invalid parameter. We need a list of integers which are the indices of the elements to remove.\n",opt_name);
1712       m_option_free(&list_opt,&str_list);
1713       return M_OPT_INVALID;
1714     }
1715     if(!obj_list) continue;
1716     if(id >= idx_max || id < -idx_max) {
1717       mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Option %s: Index %d is out of range.\n",opt_name,id);
1718       continue;
1719     }
1720     if(id < 0)
1721       id = idx_max + id;
1722     free(obj_list[id].name);
1723     free_str_list(&(obj_list[id].attribs));
1724     obj_list[id].name = rem_id;
1725   }
1726
1727   if(!dst) {
1728     m_option_free(&list_opt,&str_list);
1729     return 1;
1730   }
1731
1732   for(i = 0 ; obj_list[i].name ; i++) {
1733     while(obj_list[i].name == rem_id) {
1734       memmove(&obj_list[i],&obj_list[i+1],sizeof(m_obj_settings_t)*(idx_max - i));
1735       idx_max--;
1736     }
1737   }
1738   obj_list = realloc(obj_list,sizeof(m_obj_settings_t)*(idx_max+1));
1739   VAL(dst) = obj_list;
1740
1741   return 1;
1742 }
1743
1744 static void free_obj_settings_list(void* dst) {
1745   int n;
1746   m_obj_settings_t *d;
1747
1748   if (!dst || !VAL(dst)) return;
1749
1750   d = VAL(dst);
1751 #ifndef NO_FREE
1752   for (n = 0 ; d[n].name ; n++) {
1753     free(d[n].name);
1754     free_str_list(&(d[n].attribs));
1755   }
1756   free(d);
1757 #endif
1758   VAL(dst) = NULL;
1759 }
1760
1761 static int parse_obj_settings_list(const m_option_t* opt,const char *name,
1762                                    const char *param, void* dst, int src) {
1763   int n = 0,r,len = strlen(opt->name);
1764   char *str;
1765   char *ptr, *last_ptr;
1766   m_obj_settings_t *res = NULL,*queue = NULL,*head = NULL;
1767   int op = OP_NONE;
1768
1769   // We need the objects list
1770   if(!opt->priv)
1771     return M_OPT_INVALID;
1772
1773   if(opt->name[len-1] == '*' && ((int)strlen(name) > len - 1)) {
1774     const char* n = &name[len-1];
1775     if(strcasecmp(n,"-add") == 0)
1776       op = OP_ADD;
1777     else if(strcasecmp(n,"-pre") == 0)
1778       op = OP_PRE;
1779     else if(strcasecmp(n,"-del") == 0)
1780       op = OP_DEL;
1781     else if(strcasecmp(n,"-clr") == 0)
1782       op = OP_CLR;
1783     else {
1784       char prefix[len];
1785       strncpy(prefix,opt->name,len-1);
1786       prefix[len-1] = '\0';
1787       mp_msg(MSGT_VFILTER,MSGL_ERR, "Option %s: unknown postfix %s\n"
1788              "Supported postfixes are:\n"
1789              "  %s-add\n"
1790              " Append the given list to the current list\n\n"
1791              "  %s-pre\n"
1792              " Prepend the given list to the current list\n\n"
1793              "  %s-del x,y,...\n"
1794              " Remove the given elements. Take the list element index (starting from 0).\n"
1795              " Negative index can be used (i.e. -1 is the last element)\n\n"
1796              "  %s-clr\n"
1797              " Clear the current list.\n",name,n,prefix,prefix,prefix,prefix);
1798
1799       return M_OPT_UNKNOWN;
1800     }
1801   }
1802
1803   // Clear the list ??
1804   if(op == OP_CLR) {
1805     if(dst)
1806       free_obj_settings_list(dst);
1807     return 0;
1808   }
1809
1810   if (param == NULL || strlen(param) == 0)
1811     return M_OPT_MISSING_PARAM;
1812
1813   switch(op) {
1814   case OP_ADD:
1815     if(dst) head = VAL(dst);
1816     break;
1817   case OP_PRE:
1818     if(dst) queue = VAL(dst);
1819      break;
1820   case OP_DEL:
1821     return obj_settings_list_del(name,param,dst,src);
1822   case OP_NONE:
1823     if(dst && VAL(dst))
1824       free_obj_settings_list(dst);
1825     break;
1826   default:
1827     mp_msg(MSGT_VFILTER,MSGL_ERR, "Option %s: FIXME\n",name);
1828     return M_OPT_UNKNOWN;
1829   }
1830
1831   if(!strcmp(param,"help")) {
1832     m_obj_list_t* ol = opt->priv;
1833     mp_msg(MSGT_VFILTER,MSGL_INFO,"Available video filters:\n");
1834     mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_VIDEO_FILTERS\n");
1835     for(n = 0 ; ol->list[n] ; n++)
1836       mp_msg(MSGT_VFILTER,MSGL_INFO,"  %-15s: %s\n",
1837              M_ST_MB(char*,ol->list[n],ol->name_off),
1838              M_ST_MB(char*,ol->list[n],ol->info_off));
1839     mp_msg(MSGT_VFILTER,MSGL_INFO,"\n");
1840     return M_OPT_EXIT - 1;
1841   }
1842   ptr = str = strdup(param);
1843
1844   while(ptr[0] != '\0') {
1845     last_ptr = ptr;
1846     ptr = get_nextsep(ptr, LIST_SEPARATOR, 1);
1847
1848     if(!ptr) {
1849       r = parse_obj_settings(name,last_ptr,opt->priv,dst ? &res : NULL,n);
1850       if(r < 0) {
1851         free(str);
1852         return r;
1853       }
1854       n++;
1855       break;
1856     }
1857     ptr[0] = '\0';
1858     r = parse_obj_settings(name,last_ptr,opt->priv,dst ? &res : NULL,n);
1859     if(r < 0) {
1860       free(str);
1861       return r;
1862     }
1863     ptr++;
1864     n++;
1865   }
1866   free(str);
1867   if(n == 0)
1868     return M_OPT_INVALID;
1869
1870   if( ((opt->flags & M_OPT_MIN) && (n < opt->min)) ||
1871       ((opt->flags & M_OPT_MAX) && (n > opt->max)) )
1872     return M_OPT_OUT_OF_RANGE;
1873
1874   if(dst) {
1875     if(queue) {
1876       int qsize;
1877       for(qsize = 0 ; queue[qsize].name ; qsize++)
1878         /* NOP */;
1879       res = realloc(res,(qsize+n+1)*sizeof(m_obj_settings_t));
1880       memcpy(&res[n],queue,(qsize+1)*sizeof(m_obj_settings_t));
1881       n += qsize;
1882       free(queue);
1883     }
1884     if(head) {
1885       int hsize;
1886       for(hsize = 0 ; head[hsize].name ; hsize++)
1887         /* NOP */;
1888       head = realloc(head,(hsize+n+1)*sizeof(m_obj_settings_t));
1889       memcpy(&head[hsize],res,(n+1)*sizeof(m_obj_settings_t));
1890       free(res);
1891       res = head;
1892     }
1893     VAL(dst) = res;
1894   }
1895   return 1;
1896 }
1897
1898 static void copy_obj_settings_list(const m_option_t* opt,void* dst, const void* src) {
1899   m_obj_settings_t *d,*s;
1900   int n;
1901
1902   if(!(dst && src))
1903     return;
1904
1905   s = VAL(src);
1906
1907   if(VAL(dst))
1908     free_obj_settings_list(dst);
1909   if(!s) return;
1910
1911
1912
1913   for(n = 0 ; s[n].name ; n++)
1914     /* NOP */;
1915   d = malloc((n+1)*sizeof(m_obj_settings_t));
1916   for(n = 0 ; s[n].name ; n++) {
1917     d[n].name = strdup(s[n].name);
1918     d[n].attribs = NULL;
1919     copy_str_list(NULL,&(d[n].attribs),&(s[n].attribs));
1920   }
1921   d[n].name = NULL;
1922   d[n].attribs = NULL;
1923   VAL(dst) = d;
1924 }
1925
1926 const m_option_type_t m_option_type_obj_settings_list = {
1927   "Object settings list",
1928   "",
1929   sizeof(m_obj_settings_t*),
1930   M_OPT_TYPE_DYNAMIC|M_OPT_TYPE_ALLOW_WILDCARD,
1931   parse_obj_settings_list,
1932   NULL,
1933   copy_obj_settings_list,
1934   copy_obj_settings_list,
1935   copy_obj_settings_list,
1936   free_obj_settings_list,
1937 };
1938
1939
1940
1941 static int parse_obj_presets(const m_option_t* opt,const char *name,
1942                             const char *param, void* dst, int src) {
1943   m_obj_presets_t* obj_p = (m_obj_presets_t*)opt->priv;
1944   m_struct_t *in_desc,*out_desc;
1945   int s,i;
1946   unsigned char* pre;
1947   char* pre_name = NULL;
1948
1949   if(!obj_p) {
1950     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Presets need a pointer to a m_obj_presets_t in the priv field.\n",name);
1951     return M_OPT_PARSER_ERR;
1952   }
1953
1954   if(!param)
1955     return M_OPT_MISSING_PARAM;
1956
1957   pre = obj_p->presets;
1958   in_desc = obj_p->in_desc;
1959   out_desc = obj_p->out_desc ? obj_p->out_desc : obj_p->in_desc;
1960   s = in_desc->size;
1961
1962   if(!strcmp(param,"help")) {
1963     mp_msg(MSGT_CFGPARSER, MSGL_INFO, "Available presets for %s->%s:",out_desc->name,name);
1964     for(pre = obj_p->presets;(pre_name = M_ST_MB(char*,pre,obj_p->name_off)) ;
1965         pre +=  s)
1966       mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s",pre_name);
1967     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n");
1968     return M_OPT_EXIT - 1;
1969   }
1970
1971   for(pre_name = M_ST_MB(char*,pre,obj_p->name_off) ; pre_name ;
1972       pre +=  s, pre_name = M_ST_MB(char*,pre,obj_p->name_off)) {
1973     if(!strcmp(pre_name,param)) break;
1974   }
1975   if(!pre_name) {
1976     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: There is no preset named %s\n"
1977            "Available presets are:",name,param);
1978     for(pre = obj_p->presets;(pre_name = M_ST_MB(char*,pre,obj_p->name_off)) ;
1979         pre +=  s)
1980       mp_msg(MSGT_CFGPARSER, MSGL_ERR, " %s",pre_name);
1981     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "\n");
1982     return M_OPT_INVALID;
1983   }
1984
1985   if(!dst) return 1;
1986
1987   for(i = 0 ; in_desc->fields[i].name ; i++) {
1988     const m_option_t* out_opt = m_option_list_find(out_desc->fields,
1989                                              in_desc->fields[i].name);
1990     if(!out_opt) {
1991       mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Unable to find the target option for field %s.\nPlease report this to the developers.\n",name,in_desc->fields[i].name);
1992       return M_OPT_PARSER_ERR;
1993     }
1994     m_option_copy(out_opt,M_ST_MB_P(dst,out_opt->p),M_ST_MB_P(pre,in_desc->fields[i].p));
1995   }
1996   return 1;
1997 }
1998
1999
2000 const m_option_type_t m_option_type_obj_presets = {
2001   "Object presets",
2002   "",
2003   0,
2004   0,
2005   parse_obj_presets,
2006   NULL,
2007   NULL,
2008   NULL,
2009   NULL,
2010   NULL
2011 };
2012
2013 static int parse_custom_url(const m_option_t* opt,const char *name,
2014                             const char *url, void* dst, int src) {
2015   int pos1, pos2, r, v6addr = 0;
2016   char *ptr1=NULL, *ptr2=NULL, *ptr3=NULL, *ptr4=NULL;
2017   m_struct_t* desc = opt->priv;
2018
2019   if(!desc) {
2020     mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Custom URL needs a pointer to a m_struct_t in the priv field.\n",name);
2021     return M_OPT_PARSER_ERR;
2022   }
2023
2024   // extract the protocol
2025   ptr1 = strstr(url, "://");
2026   if( ptr1==NULL ) {
2027     // Filename only
2028     if(m_option_list_find(desc->fields,"filename")) {
2029       m_struct_set(desc,dst,"filename",url);
2030       return 1;
2031     }
2032     mp_msg(MSGT_CFGPARSER, MSGL_ERR,"Option %s: URL doesn't have a valid protocol!\n",name);
2033     return M_OPT_INVALID;
2034   }
2035   if(m_option_list_find(desc->fields,"string")) {
2036     if(strlen(ptr1)>3) {
2037       m_struct_set(desc,dst,"string",ptr1+3);
2038       return 1;
2039     }
2040   }
2041   pos1 = ptr1-url;
2042   if(dst && m_option_list_find(desc->fields,"protocol")) {
2043     ptr1[0] = '\0';
2044     r = m_struct_set(desc,dst,"protocol",url);
2045     ptr1[0] = ':';
2046     if(r < 0) {
2047       mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Error while setting protocol.\n",name);
2048       return r;
2049     }
2050   }
2051
2052   // jump the "://"
2053   ptr1 += 3;
2054   pos1 += 3;
2055
2056   // check if a username:password is given
2057   ptr2 = strstr(ptr1, "@");
2058   ptr3 = strstr(ptr1, "/");
2059   if( ptr3!=NULL && ptr3<ptr2 ) {
2060     // it isn't really a username but rather a part of the path
2061     ptr2 = NULL;
2062   }
2063   if( ptr2!=NULL ) {
2064
2065     // We got something, at least a username...
2066     if(!m_option_list_find(desc->fields,"username")) {
2067       mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Option %s: This URL doesn't have a username part.\n",name);
2068       // skip
2069     } else {
2070       ptr3 = strstr(ptr1, ":");
2071       if( ptr3!=NULL && ptr3<ptr2 ) {
2072         // We also have a password
2073         if(!m_option_list_find(desc->fields,"password")) {
2074           mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Option %s: This URL doesn't have a password part.\n",name);
2075           // skip
2076         } else { // Username and password
2077           if(dst) {
2078             ptr3[0] = '\0';
2079             r = m_struct_set(desc,dst,"username",ptr1);
2080             ptr3[0] = ':';
2081             if(r < 0) {
2082               mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Error while setting username.\n",name);
2083               return r;
2084             }
2085             ptr2[0] = '\0';
2086             r = m_struct_set(desc,dst,"password",ptr3+1);
2087             ptr2[0] = '@';
2088             if(r < 0) {
2089               mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Error while setting password.\n",name);
2090               return r;
2091             }
2092           }
2093         }
2094       } else { // User name only
2095         ptr2[0] = '\0';
2096         r = m_struct_set(desc,dst,"username",ptr1);
2097         ptr2[0] = '@';
2098         if(r < 0) {
2099           mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Error while setting username.\n",name);
2100           return r;
2101         }
2102       }
2103     }
2104     ptr1 = ptr2+1;
2105     pos1 = ptr1-url;
2106   }
2107
2108   // before looking for a port number check if we have an IPv6 type numeric address
2109   // in an IPv6 URL the numeric address should be inside square braces.
2110   ptr2 = strstr(ptr1, "[");
2111   ptr3 = strstr(ptr1, "]");
2112   // If the [] is after the first it isn't the hostname
2113   ptr4 = strstr(ptr1, "/");
2114   if( ptr2!=NULL && ptr3!=NULL && (ptr2 < ptr3) && (!ptr4 || ptr4 > ptr3)) {
2115     // we have an IPv6 numeric address
2116     ptr1++;
2117     pos1++;
2118     ptr2 = ptr3;
2119     v6addr = 1;
2120   } else {
2121     ptr2 = ptr1;
2122   }
2123
2124   // look if the port is given
2125   ptr2 = strstr(ptr2, ":");
2126   // If the : is after the first / it isn't the port
2127   ptr3 = strstr(ptr1, "/");
2128   if(ptr3 && ptr3 - ptr2 < 0) ptr2 = NULL;
2129   if( ptr2==NULL ) {
2130     // No port is given
2131     // Look if a path is given
2132     if( ptr3==NULL ) {
2133       // No path/filename
2134       // So we have an URL like http://www.hostname.com
2135       pos2 = strlen(url);
2136     } else {
2137       // We have an URL like http://www.hostname.com/file.txt
2138       pos2 = ptr3-url;
2139     }
2140   } else {
2141     // We have an URL beginning like http://www.hostname.com:1212
2142     // Get the port number
2143     if(!m_option_list_find(desc->fields,"port")) {
2144       mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Option %s: This URL doesn't have a port part.\n",name);
2145       // skip
2146     } else {
2147       if(dst) {
2148         int p = atoi(ptr2+1);
2149         char tmp[100];
2150         snprintf(tmp,99,"%d",p);
2151         r = m_struct_set(desc,dst,"port",tmp);
2152         if(r < 0) {
2153           mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Error while setting port.\n",name);
2154           return r;
2155         }
2156       }
2157     }
2158     pos2 = ptr2-url;
2159   }
2160   if( v6addr ) pos2--;
2161   // Get the hostname
2162   if(pos2-pos1 > 0) {
2163     if(!m_option_list_find(desc->fields,"hostname")) {
2164       mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Option %s: This URL doesn't have a hostname part.\n",name);
2165       // skip
2166     } else {
2167       char tmp[pos2-pos1+1];
2168       strncpy(tmp,ptr1, pos2-pos1);
2169       tmp[pos2-pos1] = '\0';
2170       r = m_struct_set(desc,dst,"hostname",tmp);
2171       if(r < 0) {
2172         mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Error while setting hostname.\n",name);
2173         return r;
2174       }
2175     }
2176   }
2177   // Look if a path is given
2178   ptr2 = strstr(ptr1, "/");
2179   if( ptr2!=NULL ) {
2180     // A path/filename is given
2181     // check if it's not a trailing '/'
2182     if( strlen(ptr2)>1 ) {
2183       // copy the path/filename in the URL container
2184       if(!m_option_list_find(desc->fields,"filename")) {
2185         mp_msg(MSGT_CFGPARSER, MSGL_WARN, "Option %s: This URL doesn't have a hostname part.\n",name);
2186         // skip
2187       } else {
2188         if(dst) {
2189           int l = strlen(ptr2+1) + 1;
2190           char* fname = ptr2+1;
2191           if(l > 1) {
2192             fname = malloc(l);
2193             url_unescape_string(fname,ptr2+1);
2194           }
2195           r = m_struct_set(desc,dst,"filename",fname);
2196           if(fname != ptr2+1)
2197             free(fname);
2198           if(r < 0) {
2199             mp_msg(MSGT_CFGPARSER, MSGL_ERR, "Option %s: Error while setting filename.\n",name);
2200             return r;
2201           }
2202         }
2203       }
2204     }
2205   }
2206   return 1;
2207 }
2208
2209 /// TODO : Write the other needed funcs for 'normal' options
2210 const m_option_type_t m_option_type_custom_url = {
2211   "Custom URL",
2212   "",
2213   0,
2214   0,
2215   parse_custom_url,
2216   NULL,
2217   NULL,
2218   NULL,
2219   NULL,
2220   NULL
2221 };