Merge remote-tracking branch 'qatar/master'
[ffmpeg.git] / libavutil / parseutils.c
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 /**
20  * @file
21  * misc parsing utilities
22  */
23
24 #include <strings.h>
25 #include <sys/time.h>
26 #include <time.h>
27
28 #include "avstring.h"
29 #include "avutil.h"
30 #include "eval.h"
31 #include "log.h"
32 #include "random_seed.h"
33 #include "parseutils.h"
34
35 typedef struct {
36     const char *abbr;
37     int width, height;
38 } VideoSizeAbbr;
39
40 typedef struct {
41     const char *abbr;
42     AVRational rate;
43 } VideoRateAbbr;
44
45 static const VideoSizeAbbr video_size_abbrs[] = {
46     { "ntsc",      720, 480 },
47     { "pal",       720, 576 },
48     { "qntsc",     352, 240 }, /* VCD compliant NTSC */
49     { "qpal",      352, 288 }, /* VCD compliant PAL */
50     { "sntsc",     640, 480 }, /* square pixel NTSC */
51     { "spal",      768, 576 }, /* square pixel PAL */
52     { "film",      352, 240 },
53     { "ntsc-film", 352, 240 },
54     { "sqcif",     128,  96 },
55     { "qcif",      176, 144 },
56     { "cif",       352, 288 },
57     { "4cif",      704, 576 },
58     { "16cif",    1408,1152 },
59     { "qqvga",     160, 120 },
60     { "qvga",      320, 240 },
61     { "vga",       640, 480 },
62     { "svga",      800, 600 },
63     { "xga",      1024, 768 },
64     { "uxga",     1600,1200 },
65     { "qxga",     2048,1536 },
66     { "sxga",     1280,1024 },
67     { "qsxga",    2560,2048 },
68     { "hsxga",    5120,4096 },
69     { "wvga",      852, 480 },
70     { "wxga",     1366, 768 },
71     { "wsxga",    1600,1024 },
72     { "wuxga",    1920,1200 },
73     { "woxga",    2560,1600 },
74     { "wqsxga",   3200,2048 },
75     { "wquxga",   3840,2400 },
76     { "whsxga",   6400,4096 },
77     { "whuxga",   7680,4800 },
78     { "cga",       320, 200 },
79     { "ega",       640, 350 },
80     { "hd480",     852, 480 },
81     { "hd720",    1280, 720 },
82     { "hd1080",   1920,1080 },
83 };
84
85 static const VideoRateAbbr video_rate_abbrs[]= {
86     { "ntsc",      { 30000, 1001 } },
87     { "pal",       {    25,    1 } },
88     { "qntsc",     { 30000, 1001 } }, /* VCD compliant NTSC */
89     { "qpal",      {    25,    1 } }, /* VCD compliant PAL */
90     { "sntsc",     { 30000, 1001 } }, /* square pixel NTSC */
91     { "spal",      {    25,    1 } }, /* square pixel PAL */
92     { "film",      {    24,    1 } },
93     { "ntsc-film", { 24000, 1001 } },
94 };
95
96 int av_parse_video_size(int *width_ptr, int *height_ptr, const char *str)
97 {
98     int i;
99     int n = FF_ARRAY_ELEMS(video_size_abbrs);
100     char *p;
101     int width = 0, height = 0;
102
103     for (i = 0; i < n; i++) {
104         if (!strcmp(video_size_abbrs[i].abbr, str)) {
105             width  = video_size_abbrs[i].width;
106             height = video_size_abbrs[i].height;
107             break;
108         }
109     }
110     if (i == n) {
111         p = str;
112         width = strtol(p, &p, 10);
113         if (*p)
114             p++;
115         height = strtol(p, &p, 10);
116     }
117     if (width <= 0 || height <= 0)
118         return AVERROR(EINVAL);
119     *width_ptr  = width;
120     *height_ptr = height;
121     return 0;
122 }
123
124 int av_parse_video_rate(AVRational *rate, const char *arg)
125 {
126     int i, ret;
127     int n = FF_ARRAY_ELEMS(video_rate_abbrs);
128     double res;
129
130     /* First, we check our abbreviation table */
131     for (i = 0; i < n; ++i)
132         if (!strcmp(video_rate_abbrs[i].abbr, arg)) {
133             *rate = video_rate_abbrs[i].rate;
134             return 0;
135         }
136
137     /* Then, we try to parse it as fraction */
138     if ((ret = av_expr_parse_and_eval(&res, arg, NULL, NULL, NULL, NULL, NULL, NULL,
139                                       NULL, 0, NULL)) < 0)
140         return ret;
141     *rate = av_d2q(res, 1001000);
142     if (rate->num <= 0 || rate->den <= 0)
143         return AVERROR(EINVAL);
144     return 0;
145 }
146
147 typedef struct {
148     const char *name;            ///< a string representing the name of the color
149     uint8_t     rgb_color[3];    ///< RGB values for the color
150 } ColorEntry;
151
152 static ColorEntry color_table[] = {
153     { "AliceBlue",            { 0xF0, 0xF8, 0xFF } },
154     { "AntiqueWhite",         { 0xFA, 0xEB, 0xD7 } },
155     { "Aqua",                 { 0x00, 0xFF, 0xFF } },
156     { "Aquamarine",           { 0x7F, 0xFF, 0xD4 } },
157     { "Azure",                { 0xF0, 0xFF, 0xFF } },
158     { "Beige",                { 0xF5, 0xF5, 0xDC } },
159     { "Bisque",               { 0xFF, 0xE4, 0xC4 } },
160     { "Black",                { 0x00, 0x00, 0x00 } },
161     { "BlanchedAlmond",       { 0xFF, 0xEB, 0xCD } },
162     { "Blue",                 { 0x00, 0x00, 0xFF } },
163     { "BlueViolet",           { 0x8A, 0x2B, 0xE2 } },
164     { "Brown",                { 0xA5, 0x2A, 0x2A } },
165     { "BurlyWood",            { 0xDE, 0xB8, 0x87 } },
166     { "CadetBlue",            { 0x5F, 0x9E, 0xA0 } },
167     { "Chartreuse",           { 0x7F, 0xFF, 0x00 } },
168     { "Chocolate",            { 0xD2, 0x69, 0x1E } },
169     { "Coral",                { 0xFF, 0x7F, 0x50 } },
170     { "CornflowerBlue",       { 0x64, 0x95, 0xED } },
171     { "Cornsilk",             { 0xFF, 0xF8, 0xDC } },
172     { "Crimson",              { 0xDC, 0x14, 0x3C } },
173     { "Cyan",                 { 0x00, 0xFF, 0xFF } },
174     { "DarkBlue",             { 0x00, 0x00, 0x8B } },
175     { "DarkCyan",             { 0x00, 0x8B, 0x8B } },
176     { "DarkGoldenRod",        { 0xB8, 0x86, 0x0B } },
177     { "DarkGray",             { 0xA9, 0xA9, 0xA9 } },
178     { "DarkGreen",            { 0x00, 0x64, 0x00 } },
179     { "DarkKhaki",            { 0xBD, 0xB7, 0x6B } },
180     { "DarkMagenta",          { 0x8B, 0x00, 0x8B } },
181     { "DarkOliveGreen",       { 0x55, 0x6B, 0x2F } },
182     { "Darkorange",           { 0xFF, 0x8C, 0x00 } },
183     { "DarkOrchid",           { 0x99, 0x32, 0xCC } },
184     { "DarkRed",              { 0x8B, 0x00, 0x00 } },
185     { "DarkSalmon",           { 0xE9, 0x96, 0x7A } },
186     { "DarkSeaGreen",         { 0x8F, 0xBC, 0x8F } },
187     { "DarkSlateBlue",        { 0x48, 0x3D, 0x8B } },
188     { "DarkSlateGray",        { 0x2F, 0x4F, 0x4F } },
189     { "DarkTurquoise",        { 0x00, 0xCE, 0xD1 } },
190     { "DarkViolet",           { 0x94, 0x00, 0xD3 } },
191     { "DeepPink",             { 0xFF, 0x14, 0x93 } },
192     { "DeepSkyBlue",          { 0x00, 0xBF, 0xFF } },
193     { "DimGray",              { 0x69, 0x69, 0x69 } },
194     { "DodgerBlue",           { 0x1E, 0x90, 0xFF } },
195     { "FireBrick",            { 0xB2, 0x22, 0x22 } },
196     { "FloralWhite",          { 0xFF, 0xFA, 0xF0 } },
197     { "ForestGreen",          { 0x22, 0x8B, 0x22 } },
198     { "Fuchsia",              { 0xFF, 0x00, 0xFF } },
199     { "Gainsboro",            { 0xDC, 0xDC, 0xDC } },
200     { "GhostWhite",           { 0xF8, 0xF8, 0xFF } },
201     { "Gold",                 { 0xFF, 0xD7, 0x00 } },
202     { "GoldenRod",            { 0xDA, 0xA5, 0x20 } },
203     { "Gray",                 { 0x80, 0x80, 0x80 } },
204     { "Green",                { 0x00, 0x80, 0x00 } },
205     { "GreenYellow",          { 0xAD, 0xFF, 0x2F } },
206     { "HoneyDew",             { 0xF0, 0xFF, 0xF0 } },
207     { "HotPink",              { 0xFF, 0x69, 0xB4 } },
208     { "IndianRed",            { 0xCD, 0x5C, 0x5C } },
209     { "Indigo",               { 0x4B, 0x00, 0x82 } },
210     { "Ivory",                { 0xFF, 0xFF, 0xF0 } },
211     { "Khaki",                { 0xF0, 0xE6, 0x8C } },
212     { "Lavender",             { 0xE6, 0xE6, 0xFA } },
213     { "LavenderBlush",        { 0xFF, 0xF0, 0xF5 } },
214     { "LawnGreen",            { 0x7C, 0xFC, 0x00 } },
215     { "LemonChiffon",         { 0xFF, 0xFA, 0xCD } },
216     { "LightBlue",            { 0xAD, 0xD8, 0xE6 } },
217     { "LightCoral",           { 0xF0, 0x80, 0x80 } },
218     { "LightCyan",            { 0xE0, 0xFF, 0xFF } },
219     { "LightGoldenRodYellow", { 0xFA, 0xFA, 0xD2 } },
220     { "LightGrey",            { 0xD3, 0xD3, 0xD3 } },
221     { "LightGreen",           { 0x90, 0xEE, 0x90 } },
222     { "LightPink",            { 0xFF, 0xB6, 0xC1 } },
223     { "LightSalmon",          { 0xFF, 0xA0, 0x7A } },
224     { "LightSeaGreen",        { 0x20, 0xB2, 0xAA } },
225     { "LightSkyBlue",         { 0x87, 0xCE, 0xFA } },
226     { "LightSlateGray",       { 0x77, 0x88, 0x99 } },
227     { "LightSteelBlue",       { 0xB0, 0xC4, 0xDE } },
228     { "LightYellow",          { 0xFF, 0xFF, 0xE0 } },
229     { "Lime",                 { 0x00, 0xFF, 0x00 } },
230     { "LimeGreen",            { 0x32, 0xCD, 0x32 } },
231     { "Linen",                { 0xFA, 0xF0, 0xE6 } },
232     { "Magenta",              { 0xFF, 0x00, 0xFF } },
233     { "Maroon",               { 0x80, 0x00, 0x00 } },
234     { "MediumAquaMarine",     { 0x66, 0xCD, 0xAA } },
235     { "MediumBlue",           { 0x00, 0x00, 0xCD } },
236     { "MediumOrchid",         { 0xBA, 0x55, 0xD3 } },
237     { "MediumPurple",         { 0x93, 0x70, 0xD8 } },
238     { "MediumSeaGreen",       { 0x3C, 0xB3, 0x71 } },
239     { "MediumSlateBlue",      { 0x7B, 0x68, 0xEE } },
240     { "MediumSpringGreen",    { 0x00, 0xFA, 0x9A } },
241     { "MediumTurquoise",      { 0x48, 0xD1, 0xCC } },
242     { "MediumVioletRed",      { 0xC7, 0x15, 0x85 } },
243     { "MidnightBlue",         { 0x19, 0x19, 0x70 } },
244     { "MintCream",            { 0xF5, 0xFF, 0xFA } },
245     { "MistyRose",            { 0xFF, 0xE4, 0xE1 } },
246     { "Moccasin",             { 0xFF, 0xE4, 0xB5 } },
247     { "NavajoWhite",          { 0xFF, 0xDE, 0xAD } },
248     { "Navy",                 { 0x00, 0x00, 0x80 } },
249     { "OldLace",              { 0xFD, 0xF5, 0xE6 } },
250     { "Olive",                { 0x80, 0x80, 0x00 } },
251     { "OliveDrab",            { 0x6B, 0x8E, 0x23 } },
252     { "Orange",               { 0xFF, 0xA5, 0x00 } },
253     { "OrangeRed",            { 0xFF, 0x45, 0x00 } },
254     { "Orchid",               { 0xDA, 0x70, 0xD6 } },
255     { "PaleGoldenRod",        { 0xEE, 0xE8, 0xAA } },
256     { "PaleGreen",            { 0x98, 0xFB, 0x98 } },
257     { "PaleTurquoise",        { 0xAF, 0xEE, 0xEE } },
258     { "PaleVioletRed",        { 0xD8, 0x70, 0x93 } },
259     { "PapayaWhip",           { 0xFF, 0xEF, 0xD5 } },
260     { "PeachPuff",            { 0xFF, 0xDA, 0xB9 } },
261     { "Peru",                 { 0xCD, 0x85, 0x3F } },
262     { "Pink",                 { 0xFF, 0xC0, 0xCB } },
263     { "Plum",                 { 0xDD, 0xA0, 0xDD } },
264     { "PowderBlue",           { 0xB0, 0xE0, 0xE6 } },
265     { "Purple",               { 0x80, 0x00, 0x80 } },
266     { "Red",                  { 0xFF, 0x00, 0x00 } },
267     { "RosyBrown",            { 0xBC, 0x8F, 0x8F } },
268     { "RoyalBlue",            { 0x41, 0x69, 0xE1 } },
269     { "SaddleBrown",          { 0x8B, 0x45, 0x13 } },
270     { "Salmon",               { 0xFA, 0x80, 0x72 } },
271     { "SandyBrown",           { 0xF4, 0xA4, 0x60 } },
272     { "SeaGreen",             { 0x2E, 0x8B, 0x57 } },
273     { "SeaShell",             { 0xFF, 0xF5, 0xEE } },
274     { "Sienna",               { 0xA0, 0x52, 0x2D } },
275     { "Silver",               { 0xC0, 0xC0, 0xC0 } },
276     { "SkyBlue",              { 0x87, 0xCE, 0xEB } },
277     { "SlateBlue",            { 0x6A, 0x5A, 0xCD } },
278     { "SlateGray",            { 0x70, 0x80, 0x90 } },
279     { "Snow",                 { 0xFF, 0xFA, 0xFA } },
280     { "SpringGreen",          { 0x00, 0xFF, 0x7F } },
281     { "SteelBlue",            { 0x46, 0x82, 0xB4 } },
282     { "Tan",                  { 0xD2, 0xB4, 0x8C } },
283     { "Teal",                 { 0x00, 0x80, 0x80 } },
284     { "Thistle",              { 0xD8, 0xBF, 0xD8 } },
285     { "Tomato",               { 0xFF, 0x63, 0x47 } },
286     { "Turquoise",            { 0x40, 0xE0, 0xD0 } },
287     { "Violet",               { 0xEE, 0x82, 0xEE } },
288     { "Wheat",                { 0xF5, 0xDE, 0xB3 } },
289     { "White",                { 0xFF, 0xFF, 0xFF } },
290     { "WhiteSmoke",           { 0xF5, 0xF5, 0xF5 } },
291     { "Yellow",               { 0xFF, 0xFF, 0x00 } },
292     { "YellowGreen",          { 0x9A, 0xCD, 0x32 } },
293 };
294
295 static int color_table_compare(const void *lhs, const void *rhs)
296 {
297     return strcasecmp(lhs, ((const ColorEntry *)rhs)->name);
298 }
299
300 #define ALPHA_SEP '@'
301
302 int av_parse_color(uint8_t *rgba_color, const char *color_string, int slen,
303                    void *log_ctx)
304 {
305     char *tail, color_string2[128];
306     const ColorEntry *entry;
307     int len, hex_offset = 0;
308
309     if (color_string[0] == '#') {
310         hex_offset = 1;
311     } else if (!strncmp(color_string, "0x", 2))
312         hex_offset = 2;
313
314     if (slen < 0)
315         slen = strlen(color_string);
316     av_strlcpy(color_string2, color_string + hex_offset,
317                FFMIN(slen-hex_offset+1, sizeof(color_string2)));
318     if ((tail = strchr(color_string2, ALPHA_SEP)))
319         *tail++ = 0;
320     len = strlen(color_string2);
321     rgba_color[3] = 255;
322
323     if (!strcasecmp(color_string2, "random") || !strcasecmp(color_string2, "bikeshed")) {
324         int rgba = av_get_random_seed();
325         rgba_color[0] = rgba >> 24;
326         rgba_color[1] = rgba >> 16;
327         rgba_color[2] = rgba >> 8;
328         rgba_color[3] = rgba;
329     } else if (hex_offset ||
330                strspn(color_string2, "0123456789ABCDEFabcdef") == len) {
331         char *tail;
332         unsigned int rgba = strtoul(color_string2, &tail, 16);
333
334         if (*tail || (len != 6 && len != 8)) {
335             av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string2);
336             return AVERROR(EINVAL);
337         }
338         if (len == 8) {
339             rgba_color[3] = rgba;
340             rgba >>= 8;
341         }
342         rgba_color[0] = rgba >> 16;
343         rgba_color[1] = rgba >> 8;
344         rgba_color[2] = rgba;
345     } else {
346         entry = bsearch(color_string2,
347                         color_table,
348                         FF_ARRAY_ELEMS(color_table),
349                         sizeof(ColorEntry),
350                         color_table_compare);
351         if (!entry) {
352             av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string2);
353             return AVERROR(EINVAL);
354         }
355         memcpy(rgba_color, entry->rgb_color, 3);
356     }
357
358     if (tail) {
359         unsigned long int alpha;
360         const char *alpha_string = tail;
361         if (!strncmp(alpha_string, "0x", 2)) {
362             alpha = strtoul(alpha_string, &tail, 16);
363         } else {
364             alpha = 255 * strtod(alpha_string, &tail);
365         }
366
367         if (tail == alpha_string || *tail || alpha > 255) {
368             av_log(log_ctx, AV_LOG_ERROR, "Invalid alpha value specifier '%s' in '%s'\n",
369                    alpha_string, color_string);
370             return AVERROR(EINVAL);
371         }
372         rgba_color[3] = alpha;
373     }
374
375     return 0;
376 }
377
378 /* get a positive number between n_min and n_max, for a maximum length
379    of len_max. Return -1 if error. */
380 static int date_get_num(const char **pp,
381                         int n_min, int n_max, int len_max)
382 {
383     int i, val, c;
384     const char *p;
385
386     p = *pp;
387     val = 0;
388     for(i = 0; i < len_max; i++) {
389         c = *p;
390         if (!isdigit(c))
391             break;
392         val = (val * 10) + c - '0';
393         p++;
394     }
395     /* no number read ? */
396     if (p == *pp)
397         return -1;
398     if (val < n_min || val > n_max)
399         return -1;
400     *pp = p;
401     return val;
402 }
403
404 /* small strptime for ffmpeg */
405 static
406 const char *small_strptime(const char *p, const char *fmt,
407                            struct tm *dt)
408 {
409     int c, val;
410
411     for(;;) {
412         c = *fmt++;
413         if (c == '\0') {
414             return p;
415         } else if (c == '%') {
416             c = *fmt++;
417             switch(c) {
418             case 'H':
419                 val = date_get_num(&p, 0, 23, 2);
420                 if (val == -1)
421                     return NULL;
422                 dt->tm_hour = val;
423                 break;
424             case 'M':
425                 val = date_get_num(&p, 0, 59, 2);
426                 if (val == -1)
427                     return NULL;
428                 dt->tm_min = val;
429                 break;
430             case 'S':
431                 val = date_get_num(&p, 0, 59, 2);
432                 if (val == -1)
433                     return NULL;
434                 dt->tm_sec = val;
435                 break;
436             case 'Y':
437                 val = date_get_num(&p, 0, 9999, 4);
438                 if (val == -1)
439                     return NULL;
440                 dt->tm_year = val - 1900;
441                 break;
442             case 'm':
443                 val = date_get_num(&p, 1, 12, 2);
444                 if (val == -1)
445                     return NULL;
446                 dt->tm_mon = val - 1;
447                 break;
448             case 'd':
449                 val = date_get_num(&p, 1, 31, 2);
450                 if (val == -1)
451                     return NULL;
452                 dt->tm_mday = val;
453                 break;
454             case '%':
455                 goto match;
456             default:
457                 return NULL;
458             }
459         } else {
460         match:
461             if (c != *p)
462                 return NULL;
463             p++;
464         }
465     }
466 }
467
468 static time_t mktimegm(struct tm *tm)
469 {
470     time_t t;
471
472     int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
473
474     if (m < 3) {
475         m += 12;
476         y--;
477     }
478
479     t = 86400 *
480         (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
481
482     t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
483
484     return t;
485 }
486
487 int av_parse_time(int64_t *timeval, const char *timestr, int duration)
488 {
489     const char *p;
490     int64_t t;
491     struct tm dt;
492     int i;
493     static const char * const date_fmt[] = {
494         "%Y-%m-%d",
495         "%Y%m%d",
496     };
497     static const char * const time_fmt[] = {
498         "%H:%M:%S",
499         "%H%M%S",
500     };
501     const char *q;
502     int is_utc, len;
503     char lastch;
504     int negative = 0;
505
506 #undef time
507     time_t now = time(0);
508
509     len = strlen(timestr);
510     if (len > 0)
511         lastch = timestr[len - 1];
512     else
513         lastch = '\0';
514     is_utc = (lastch == 'z' || lastch == 'Z');
515
516     memset(&dt, 0, sizeof(dt));
517
518     p = timestr;
519     q = NULL;
520     if (!duration) {
521         if (!strncasecmp(timestr, "now", len)) {
522             *timeval = (int64_t) now * 1000000;
523             return 0;
524         }
525
526         /* parse the year-month-day part */
527         for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
528             q = small_strptime(p, date_fmt[i], &dt);
529             if (q) {
530                 break;
531             }
532         }
533
534         /* if the year-month-day part is missing, then take the
535          * current year-month-day time */
536         if (!q) {
537             if (is_utc) {
538                 dt = *gmtime(&now);
539             } else {
540                 dt = *localtime(&now);
541             }
542             dt.tm_hour = dt.tm_min = dt.tm_sec = 0;
543         } else {
544             p = q;
545         }
546
547         if (*p == 'T' || *p == 't' || *p == ' ')
548             p++;
549
550         /* parse the hour-minute-second part */
551         for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
552             q = small_strptime(p, time_fmt[i], &dt);
553             if (q) {
554                 break;
555             }
556         }
557     } else {
558         /* parse timestr as a duration */
559         if (p[0] == '-') {
560             negative = 1;
561             ++p;
562         }
563         /* parse timestr as HH:MM:SS */
564         q = small_strptime(p, time_fmt[0], &dt);
565         if (!q) {
566             /* parse timestr as S+ */
567             dt.tm_sec = strtol(p, (char **)&q, 10);
568             if (q == p) {
569                 /* the parsing didn't succeed */
570                 *timeval = INT64_MIN;
571                 return AVERROR(EINVAL);
572             }
573             dt.tm_min = 0;
574             dt.tm_hour = 0;
575         }
576     }
577
578     /* Now we have all the fields that we can get */
579     if (!q) {
580         *timeval = INT64_MIN;
581         return AVERROR(EINVAL);
582     }
583
584     if (duration) {
585         t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
586     } else {
587         dt.tm_isdst = -1;       /* unknown */
588         if (is_utc) {
589             t = mktimegm(&dt);
590         } else {
591             t = mktime(&dt);
592         }
593     }
594
595     t *= 1000000;
596
597     /* parse the .m... part */
598     if (*q == '.') {
599         int val, n;
600         q++;
601         for (val = 0, n = 100000; n >= 1; n /= 10, q++) {
602             if (!isdigit(*q))
603                 break;
604             val += n * (*q - '0');
605         }
606         t += val;
607     }
608     *timeval = negative ? -t : t;
609     return 0;
610 }
611
612 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
613 {
614     const char *p;
615     char tag[128], *q;
616
617     p = info;
618     if (*p == '?')
619         p++;
620     for(;;) {
621         q = tag;
622         while (*p != '\0' && *p != '=' && *p != '&') {
623             if ((q - tag) < sizeof(tag) - 1)
624                 *q++ = *p;
625             p++;
626         }
627         *q = '\0';
628         q = arg;
629         if (*p == '=') {
630             p++;
631             while (*p != '&' && *p != '\0') {
632                 if ((q - arg) < arg_size - 1) {
633                     if (*p == '+')
634                         *q++ = ' ';
635                     else
636                         *q++ = *p;
637                 }
638                 p++;
639             }
640         }
641         *q = '\0';
642         if (!strcmp(tag, tag1))
643             return 1;
644         if (*p != '&')
645             break;
646         p++;
647     }
648     return 0;
649 }
650
651 #ifdef TEST
652
653 #undef printf
654
655 int main(void)
656 {
657     printf("Testing av_parse_video_rate()\n");
658     {
659         int i;
660         const char *rates[] = {
661             "-inf",
662             "inf",
663             "nan",
664             "123/0",
665             "-123 / 0",
666             "",
667             "/",
668             " 123  /  321",
669             "foo/foo",
670             "foo/1",
671             "1/foo",
672             "0/0",
673             "/0",
674             "1/",
675             "1",
676             "0",
677             "-123/123",
678             "-foo",
679             "123.23",
680             ".23",
681             "-.23",
682             "-0.234",
683             "-0.0000001",
684             "  21332.2324   ",
685             " -21332.2324   ",
686         };
687
688         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
689             int ret;
690             AVRational q = (AVRational){0, 0};
691             ret = av_parse_video_rate(&q, rates[i]),
692             printf("'%s' -> %d/%d ret:%d\n",
693                    rates[i], q.num, q.den, ret);
694         }
695     }
696
697     printf("\nTesting av_parse_color()\n");
698     {
699         int i;
700         uint8_t rgba[4];
701         const char *color_names[] = {
702             "bikeshed",
703             "RaNdOm",
704             "foo",
705             "red",
706             "Red ",
707             "RED",
708             "Violet",
709             "Yellow",
710             "Red",
711             "0x000000",
712             "0x0000000",
713             "0xff000000",
714             "0x3e34ff",
715             "0x3e34ffaa",
716             "0xffXXee",
717             "0xfoobar",
718             "0xffffeeeeeeee",
719             "#ff0000",
720             "#ffXX00",
721             "ff0000",
722             "ffXX00",
723             "red@foo",
724             "random@10",
725             "0xff0000@1.0",
726             "red@",
727             "red@0xfff",
728             "red@0xf",
729             "red@2",
730             "red@0.1",
731             "red@-1",
732             "red@0.5",
733             "red@1.0",
734             "red@256",
735             "red@10foo",
736             "red@-1.0",
737             "red@-0.0",
738         };
739
740         av_log_set_level(AV_LOG_DEBUG);
741
742         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
743             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
744                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
745         }
746     }
747
748     return 0;
749 }
750
751 #endif /* TEST */