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