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
34typedef struct {
35    const char *abbr;
36    int width, height;
37} VideoSizeAbbr;
38
39typedef struct {
40    const char *abbr;
41    AVRational rate;
42} VideoRateAbbr;
43
44static 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
84static 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
95int 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
123int 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
146typedef 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
151static 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
294static 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
301int 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. */
379static 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
403static 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
464time_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
483int 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
608int 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
651int 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 */
748