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