parseutils: Make av_small_strptime public
[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 <time.h>
25
26 #include "avstring.h"
27 #include "avutil.h"
28 #include "common.h"
29 #include "eval.h"
30 #include "log.h"
31 #include "random_seed.h"
32 #include "time_internal.h"
33 #include "parseutils.h"
34
35 typedef struct VideoSizeAbbr {
36     const char *abbr;
37     int width, height;
38 } VideoSizeAbbr;
39
40 typedef struct VideoRateAbbr {
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         width = strtol(str, &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 ColorEntry {
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         double 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 || alpha < 0) {
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 (!av_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 const char *av_small_strptime(const char *p, const char *fmt, struct tm *dt)
404 {
405     int c, val;
406
407     while((c = *fmt++)) {
408         if (c != '%') {
409             if (av_isspace(c))
410                 for (; *p && av_isspace(*p); p++);
411             else if (*p != c)
412                 return NULL;
413             else p++;
414             continue;
415         }
416
417         c = *fmt++;
418         switch(c) {
419         case 'H':
420             val = date_get_num(&p, 0, 23, 2);
421             if (val == -1)
422                 return NULL;
423             dt->tm_hour = val;
424             break;
425         case 'M':
426             val = date_get_num(&p, 0, 59, 2);
427             if (val == -1)
428                 return NULL;
429             dt->tm_min = val;
430             break;
431         case 'S':
432             val = date_get_num(&p, 0, 59, 2);
433             if (val == -1)
434                 return NULL;
435             dt->tm_sec = val;
436             break;
437         case 'Y':
438             val = date_get_num(&p, 0, 9999, 4);
439             if (val == -1)
440                 return NULL;
441             dt->tm_year = val - 1900;
442             break;
443         case 'm':
444             val = date_get_num(&p, 1, 12, 2);
445             if (val == -1)
446                 return NULL;
447             dt->tm_mon = val - 1;
448             break;
449         case 'd':
450             val = date_get_num(&p, 1, 31, 2);
451             if (val == -1)
452                 return NULL;
453             dt->tm_mday = val;
454             break;
455         case 'T':
456             p = av_small_strptime(p, "%H:%M:%S", dt);
457             if (!p)
458                 return NULL;
459             break;
460         case '%':
461             if (*p++ != '%')
462                 return NULL;
463             break;
464         default:
465             return NULL;
466         }
467     }
468
469     return p;
470 }
471
472 time_t av_timegm(struct tm *tm)
473 {
474     time_t t;
475
476     int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
477
478     if (m < 3) {
479         m += 12;
480         y--;
481     }
482
483     t = 86400 *
484         (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469);
485
486     t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
487
488     return t;
489 }
490
491 int av_parse_time(int64_t *timeval, const char *timestr, int duration)
492 {
493     const char *p;
494     int64_t t;
495     struct tm dt = { 0 }, tmbuf;
496     int i;
497     static const char * const date_fmt[] = {
498         "%Y-%m-%d",
499         "%Y%m%d",
500     };
501     static const char * const time_fmt[] = {
502         "%H:%M:%S",
503         "%H%M%S",
504     };
505     const char *q;
506     int is_utc, len;
507     char lastch;
508     int negative = 0;
509
510     time_t now = time(0);
511
512     len = strlen(timestr);
513     if (len > 0)
514         lastch = timestr[len - 1];
515     else
516         lastch = '\0';
517     is_utc = (lastch == 'z' || lastch == 'Z');
518
519     p = timestr;
520     q = NULL;
521     if (!duration) {
522         if (!av_strncasecmp(timestr, "now", len)) {
523             *timeval = (int64_t) now * 1000000;
524             return 0;
525         }
526
527         /* parse the year-month-day part */
528         for (i = 0; i < FF_ARRAY_ELEMS(date_fmt); i++) {
529             q = av_small_strptime(p, date_fmt[i], &dt);
530             if (q) {
531                 break;
532             }
533         }
534
535         /* if the year-month-day part is missing, then take the
536          * current year-month-day time */
537         if (!q) {
538             if (is_utc) {
539                 dt = *gmtime_r(&now, &tmbuf);
540             } else {
541                 dt = *localtime_r(&now, &tmbuf);
542             }
543             dt.tm_hour = dt.tm_min = dt.tm_sec = 0;
544         } else {
545             p = q;
546         }
547
548         if (*p == 'T' || *p == 't' || *p == ' ')
549             p++;
550
551         /* parse the hour-minute-second part */
552         for (i = 0; i < FF_ARRAY_ELEMS(time_fmt); i++) {
553             q = av_small_strptime(p, time_fmt[i], &dt);
554             if (q) {
555                 break;
556             }
557         }
558     } else {
559         /* parse timestr as a duration */
560         if (p[0] == '-') {
561             negative = 1;
562             ++p;
563         }
564         /* parse timestr as HH:MM:SS */
565         q = av_small_strptime(p, time_fmt[0], &dt);
566         if (!q) {
567             char *o;
568             /* parse timestr as S+ */
569             dt.tm_sec = strtol(p, &o, 10);
570             if (o == p) {
571                 /* the parsing didn't succeed */
572                 *timeval = INT64_MIN;
573                 return AVERROR(EINVAL);
574             }
575             dt.tm_min = 0;
576             dt.tm_hour = 0;
577             q = o;
578         }
579     }
580
581     /* Now we have all the fields that we can get */
582     if (!q) {
583         *timeval = INT64_MIN;
584         return AVERROR(EINVAL);
585     }
586
587     if (duration) {
588         t = dt.tm_hour * 3600 + dt.tm_min * 60 + dt.tm_sec;
589     } else {
590         dt.tm_isdst = -1;       /* unknown */
591         if (is_utc) {
592             t = av_timegm(&dt);
593         } else {
594             t = mktime(&dt);
595         }
596     }
597
598     t *= 1000000;
599
600     /* parse the .m... part */
601     if (*q == '.') {
602         int val, n;
603         q++;
604         for (val = 0, n = 100000; n >= 1; n /= 10, q++) {
605             if (!av_isdigit(*q))
606                 break;
607             val += n * (*q - '0');
608         }
609         t += val;
610     }
611     *timeval = negative ? -t : t;
612     return 0;
613 }
614
615 int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
616 {
617     const char *p;
618     char tag[128], *q;
619
620     p = info;
621     if (*p == '?')
622         p++;
623     for(;;) {
624         q = tag;
625         while (*p != '\0' && *p != '=' && *p != '&') {
626             if ((q - tag) < sizeof(tag) - 1)
627                 *q++ = *p;
628             p++;
629         }
630         *q = '\0';
631         q = arg;
632         if (*p == '=') {
633             p++;
634             while (*p != '&' && *p != '\0') {
635                 if ((q - arg) < arg_size - 1) {
636                     if (*p == '+')
637                         *q++ = ' ';
638                     else
639                         *q++ = *p;
640                 }
641                 p++;
642             }
643         }
644         *q = '\0';
645         if (!strcmp(tag, tag1))
646             return 1;
647         if (*p != '&')
648             break;
649         p++;
650     }
651     return 0;
652 }
653
654 #ifdef TEST
655
656 int main(void)
657 {
658     printf("Testing av_parse_video_rate()\n");
659     {
660         int i;
661         static const char *const rates[] = {
662             "-inf",
663             "inf",
664             "nan",
665             "123/0",
666             "-123 / 0",
667             "",
668             "/",
669             " 123  /  321",
670             "foo/foo",
671             "foo/1",
672             "1/foo",
673             "0/0",
674             "/0",
675             "1/",
676             "1",
677             "0",
678             "-123/123",
679             "-foo",
680             "123.23",
681             ".23",
682             "-.23",
683             "-0.234",
684             "-0.0000001",
685             "  21332.2324   ",
686             " -21332.2324   ",
687         };
688
689         for (i = 0; i < FF_ARRAY_ELEMS(rates); i++) {
690             int ret;
691             AVRational q = { 0, 0 };
692             ret = av_parse_video_rate(&q, rates[i]);
693             printf("'%s' -> %d/%d %s\n",
694                    rates[i], q.num, q.den, ret ? "ERROR" : "OK");
695         }
696     }
697
698     printf("\nTesting av_parse_color()\n");
699     {
700         int i;
701         uint8_t rgba[4];
702         static const char *const color_names[] = {
703             "foo",
704             "red",
705             "Red ",
706             "RED",
707             "Violet",
708             "Yellow",
709             "Red",
710             "0x000000",
711             "0x0000000",
712             "0xff000000",
713             "0x3e34ff",
714             "0x3e34ffaa",
715             "0xffXXee",
716             "0xfoobar",
717             "0xffffeeeeeeee",
718             "#ff0000",
719             "#ffXX00",
720             "ff0000",
721             "ffXX00",
722             "red@foo",
723             "random@10",
724             "0xff0000@1.0",
725             "red@",
726             "red@0xfff",
727             "red@0xf",
728             "red@2",
729             "red@0.1",
730             "red@-1",
731             "red@0.5",
732             "red@1.0",
733             "red@256",
734             "red@10foo",
735             "red@-1.0",
736             "red@-0.0",
737         };
738
739         av_log_set_level(AV_LOG_DEBUG);
740
741         for (i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
742             if (av_parse_color(rgba, color_names[i], -1, NULL) >= 0)
743                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n",
744                        color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
745         }
746     }
747
748     return 0;
749 }
750
751 #endif /* TEST */