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