1/*
2 * copyright (c) 2009 Stefano Sabatini
3 * This file is part of FFmpeg.
4 *
5 * FFmpeg is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * FFmpeg is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with FFmpeg; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/**
21 * @file
22 * parsing utils
23 */
24
25#include <strings.h>
26#include "libavutil/avutil.h"
27#include "libavutil/random_seed.h"
28#include "parseutils.h"
29
30#define WHITESPACES " \n\t"
31
32char *av_get_token(const char **buf, const char *term)
33{
34    char *out = av_malloc(strlen(*buf) + 1);
35    char *ret= out, *end= out;
36    const char *p = *buf;
37    p += strspn(p, WHITESPACES);
38
39    while(*p && !strspn(p, term)) {
40        char c = *p++;
41        if(c == '\\' && *p){
42            *out++ = *p++;
43            end= out;
44        }else if(c == '\''){
45            while(*p && *p != '\'')
46                *out++ = *p++;
47            if(*p){
48                p++;
49                end= out;
50            }
51        }else{
52            *out++ = c;
53        }
54    }
55
56    do{
57        *out-- = 0;
58    }while(out >= end && strspn(out, WHITESPACES));
59
60    *buf = p;
61
62    return ret;
63}
64
65typedef struct {
66    const char *name;            ///< a string representing the name of the color
67    uint8_t     rgba_color[4];   ///< RGBA values for the color
68} ColorEntry;
69
70static ColorEntry color_table[] = {
71    { "AliceBlue",            { 0xF0, 0xF8, 0xFF } },
72    { "AntiqueWhite",         { 0xFA, 0xEB, 0xD7 } },
73    { "Aqua",                 { 0x00, 0xFF, 0xFF } },
74    { "Aquamarine",           { 0x7F, 0xFF, 0xD4 } },
75    { "Azure",                { 0xF0, 0xFF, 0xFF } },
76    { "Beige",                { 0xF5, 0xF5, 0xDC } },
77    { "Bisque",               { 0xFF, 0xE4, 0xC4 } },
78    { "Black",                { 0x00, 0x00, 0x00 } },
79    { "BlanchedAlmond",       { 0xFF, 0xEB, 0xCD } },
80    { "Blue",                 { 0x00, 0x00, 0xFF } },
81    { "BlueViolet",           { 0x8A, 0x2B, 0xE2 } },
82    { "Brown",                { 0xA5, 0x2A, 0x2A } },
83    { "BurlyWood",            { 0xDE, 0xB8, 0x87 } },
84    { "CadetBlue",            { 0x5F, 0x9E, 0xA0 } },
85    { "Chartreuse",           { 0x7F, 0xFF, 0x00 } },
86    { "Chocolate",            { 0xD2, 0x69, 0x1E } },
87    { "Coral",                { 0xFF, 0x7F, 0x50 } },
88    { "CornflowerBlue",       { 0x64, 0x95, 0xED } },
89    { "Cornsilk",             { 0xFF, 0xF8, 0xDC } },
90    { "Crimson",              { 0xDC, 0x14, 0x3C } },
91    { "Cyan",                 { 0x00, 0xFF, 0xFF } },
92    { "DarkBlue",             { 0x00, 0x00, 0x8B } },
93    { "DarkCyan",             { 0x00, 0x8B, 0x8B } },
94    { "DarkGoldenRod",        { 0xB8, 0x86, 0x0B } },
95    { "DarkGray",             { 0xA9, 0xA9, 0xA9 } },
96    { "DarkGreen",            { 0x00, 0x64, 0x00 } },
97    { "DarkKhaki",            { 0xBD, 0xB7, 0x6B } },
98    { "DarkMagenta",          { 0x8B, 0x00, 0x8B } },
99    { "DarkOliveGreen",       { 0x55, 0x6B, 0x2F } },
100    { "Darkorange",           { 0xFF, 0x8C, 0x00 } },
101    { "DarkOrchid",           { 0x99, 0x32, 0xCC } },
102    { "DarkRed",              { 0x8B, 0x00, 0x00 } },
103    { "DarkSalmon",           { 0xE9, 0x96, 0x7A } },
104    { "DarkSeaGreen",         { 0x8F, 0xBC, 0x8F } },
105    { "DarkSlateBlue",        { 0x48, 0x3D, 0x8B } },
106    { "DarkSlateGray",        { 0x2F, 0x4F, 0x4F } },
107    { "DarkTurquoise",        { 0x00, 0xCE, 0xD1 } },
108    { "DarkViolet",           { 0x94, 0x00, 0xD3 } },
109    { "DeepPink",             { 0xFF, 0x14, 0x93 } },
110    { "DeepSkyBlue",          { 0x00, 0xBF, 0xFF } },
111    { "DimGray",              { 0x69, 0x69, 0x69 } },
112    { "DodgerBlue",           { 0x1E, 0x90, 0xFF } },
113    { "FireBrick",            { 0xB2, 0x22, 0x22 } },
114    { "FloralWhite",          { 0xFF, 0xFA, 0xF0 } },
115    { "ForestGreen",          { 0x22, 0x8B, 0x22 } },
116    { "Fuchsia",              { 0xFF, 0x00, 0xFF } },
117    { "Gainsboro",            { 0xDC, 0xDC, 0xDC } },
118    { "GhostWhite",           { 0xF8, 0xF8, 0xFF } },
119    { "Gold",                 { 0xFF, 0xD7, 0x00 } },
120    { "GoldenRod",            { 0xDA, 0xA5, 0x20 } },
121    { "Gray",                 { 0x80, 0x80, 0x80 } },
122    { "Green",                { 0x00, 0x80, 0x00 } },
123    { "GreenYellow",          { 0xAD, 0xFF, 0x2F } },
124    { "HoneyDew",             { 0xF0, 0xFF, 0xF0 } },
125    { "HotPink",              { 0xFF, 0x69, 0xB4 } },
126    { "IndianRed",            { 0xCD, 0x5C, 0x5C } },
127    { "Indigo",               { 0x4B, 0x00, 0x82 } },
128    { "Ivory",                { 0xFF, 0xFF, 0xF0 } },
129    { "Khaki",                { 0xF0, 0xE6, 0x8C } },
130    { "Lavender",             { 0xE6, 0xE6, 0xFA } },
131    { "LavenderBlush",        { 0xFF, 0xF0, 0xF5 } },
132    { "LawnGreen",            { 0x7C, 0xFC, 0x00 } },
133    { "LemonChiffon",         { 0xFF, 0xFA, 0xCD } },
134    { "LightBlue",            { 0xAD, 0xD8, 0xE6 } },
135    { "LightCoral",           { 0xF0, 0x80, 0x80 } },
136    { "LightCyan",            { 0xE0, 0xFF, 0xFF } },
137    { "LightGoldenRodYellow", { 0xFA, 0xFA, 0xD2 } },
138    { "LightGrey",            { 0xD3, 0xD3, 0xD3 } },
139    { "LightGreen",           { 0x90, 0xEE, 0x90 } },
140    { "LightPink",            { 0xFF, 0xB6, 0xC1 } },
141    { "LightSalmon",          { 0xFF, 0xA0, 0x7A } },
142    { "LightSeaGreen",        { 0x20, 0xB2, 0xAA } },
143    { "LightSkyBlue",         { 0x87, 0xCE, 0xFA } },
144    { "LightSlateGray",       { 0x77, 0x88, 0x99 } },
145    { "LightSteelBlue",       { 0xB0, 0xC4, 0xDE } },
146    { "LightYellow",          { 0xFF, 0xFF, 0xE0 } },
147    { "Lime",                 { 0x00, 0xFF, 0x00 } },
148    { "LimeGreen",            { 0x32, 0xCD, 0x32 } },
149    { "Linen",                { 0xFA, 0xF0, 0xE6 } },
150    { "Magenta",              { 0xFF, 0x00, 0xFF } },
151    { "Maroon",               { 0x80, 0x00, 0x00 } },
152    { "MediumAquaMarine",     { 0x66, 0xCD, 0xAA } },
153    { "MediumBlue",           { 0x00, 0x00, 0xCD } },
154    { "MediumOrchid",         { 0xBA, 0x55, 0xD3 } },
155    { "MediumPurple",         { 0x93, 0x70, 0xD8 } },
156    { "MediumSeaGreen",       { 0x3C, 0xB3, 0x71 } },
157    { "MediumSlateBlue",      { 0x7B, 0x68, 0xEE } },
158    { "MediumSpringGreen",    { 0x00, 0xFA, 0x9A } },
159    { "MediumTurquoise",      { 0x48, 0xD1, 0xCC } },
160    { "MediumVioletRed",      { 0xC7, 0x15, 0x85 } },
161    { "MidnightBlue",         { 0x19, 0x19, 0x70 } },
162    { "MintCream",            { 0xF5, 0xFF, 0xFA } },
163    { "MistyRose",            { 0xFF, 0xE4, 0xE1 } },
164    { "Moccasin",             { 0xFF, 0xE4, 0xB5 } },
165    { "NavajoWhite",          { 0xFF, 0xDE, 0xAD } },
166    { "Navy",                 { 0x00, 0x00, 0x80 } },
167    { "OldLace",              { 0xFD, 0xF5, 0xE6 } },
168    { "Olive",                { 0x80, 0x80, 0x00 } },
169    { "OliveDrab",            { 0x6B, 0x8E, 0x23 } },
170    { "Orange",               { 0xFF, 0xA5, 0x00 } },
171    { "OrangeRed",            { 0xFF, 0x45, 0x00 } },
172    { "Orchid",               { 0xDA, 0x70, 0xD6 } },
173    { "PaleGoldenRod",        { 0xEE, 0xE8, 0xAA } },
174    { "PaleGreen",            { 0x98, 0xFB, 0x98 } },
175    { "PaleTurquoise",        { 0xAF, 0xEE, 0xEE } },
176    { "PaleVioletRed",        { 0xD8, 0x70, 0x93 } },
177    { "PapayaWhip",           { 0xFF, 0xEF, 0xD5 } },
178    { "PeachPuff",            { 0xFF, 0xDA, 0xB9 } },
179    { "Peru",                 { 0xCD, 0x85, 0x3F } },
180    { "Pink",                 { 0xFF, 0xC0, 0xCB } },
181    { "Plum",                 { 0xDD, 0xA0, 0xDD } },
182    { "PowderBlue",           { 0xB0, 0xE0, 0xE6 } },
183    { "Purple",               { 0x80, 0x00, 0x80 } },
184    { "Red",                  { 0xFF, 0x00, 0x00 } },
185    { "RosyBrown",            { 0xBC, 0x8F, 0x8F } },
186    { "RoyalBlue",            { 0x41, 0x69, 0xE1 } },
187    { "SaddleBrown",          { 0x8B, 0x45, 0x13 } },
188    { "Salmon",               { 0xFA, 0x80, 0x72 } },
189    { "SandyBrown",           { 0xF4, 0xA4, 0x60 } },
190    { "SeaGreen",             { 0x2E, 0x8B, 0x57 } },
191    { "SeaShell",             { 0xFF, 0xF5, 0xEE } },
192    { "Sienna",               { 0xA0, 0x52, 0x2D } },
193    { "Silver",               { 0xC0, 0xC0, 0xC0 } },
194    { "SkyBlue",              { 0x87, 0xCE, 0xEB } },
195    { "SlateBlue",            { 0x6A, 0x5A, 0xCD } },
196    { "SlateGray",            { 0x70, 0x80, 0x90 } },
197    { "Snow",                 { 0xFF, 0xFA, 0xFA } },
198    { "SpringGreen",          { 0x00, 0xFF, 0x7F } },
199    { "SteelBlue",            { 0x46, 0x82, 0xB4 } },
200    { "Tan",                  { 0xD2, 0xB4, 0x8C } },
201    { "Teal",                 { 0x00, 0x80, 0x80 } },
202    { "Thistle",              { 0xD8, 0xBF, 0xD8 } },
203    { "Tomato",               { 0xFF, 0x63, 0x47 } },
204    { "Turquoise",            { 0x40, 0xE0, 0xD0 } },
205    { "Violet",               { 0xEE, 0x82, 0xEE } },
206    { "Wheat",                { 0xF5, 0xDE, 0xB3 } },
207    { "White",                { 0xFF, 0xFF, 0xFF } },
208    { "WhiteSmoke",           { 0xF5, 0xF5, 0xF5 } },
209    { "Yellow",               { 0xFF, 0xFF, 0x00 } },
210    { "YellowGreen",          { 0x9A, 0xCD, 0x32 } },
211};
212
213static int color_table_compare(const void *lhs, const void *rhs)
214{
215    return strcasecmp(lhs, ((const ColorEntry *)rhs)->name);
216}
217
218int av_parse_color(uint8_t *rgba_color, const char *color_string, void *log_ctx)
219{
220    if (!strcasecmp(color_string, "random") || !strcasecmp(color_string, "bikeshed")) {
221        int rgba = ff_random_get_seed();
222        rgba_color[0] = rgba >> 24;
223        rgba_color[1] = rgba >> 16;
224        rgba_color[2] = rgba >> 8;
225        rgba_color[3] = rgba;
226    } else
227    if (!strncmp(color_string, "0x", 2)) {
228        char *tail;
229        int len = strlen(color_string);
230        unsigned int rgba = strtoul(color_string, &tail, 16);
231
232        if (*tail || (len != 8 && len != 10)) {
233            av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string);
234            return -1;
235        }
236        if (len == 10) {
237            rgba_color[3] = rgba;
238            rgba >>= 8;
239        }
240        rgba_color[0] = rgba >> 16;
241        rgba_color[1] = rgba >> 8;
242        rgba_color[2] = rgba;
243    } else {
244        const ColorEntry *entry = bsearch(color_string,
245                                          color_table,
246                                          FF_ARRAY_ELEMS(color_table),
247                                          sizeof(ColorEntry),
248                                          color_table_compare);
249        if (!entry) {
250            av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string);
251            return -1;
252        }
253        memcpy(rgba_color, entry->rgba_color, 4);
254    }
255
256    return 0;
257}
258
259/**
260 * Stores the value in the field in ctx that is named like key.
261 * ctx must be an AVClass context, storing is done using AVOptions.
262 *
263 * @param buf the string to parse, buf will be updated to point at the
264 * separator just after the parsed key/value pair
265 * @param key_val_sep a 0-terminated list of characters used to
266 * separate key from value
267 * @param pairs_sep a 0-terminated list of characters used to separate
268 * two pairs from each other
269 * @return 0 if the key/value pair has been successfully parsed and
270 * set, or a negative value corresponding to an AVERROR code in case
271 * of error:
272 * AVERROR(EINVAL) if the key/value pair cannot be parsed,
273 * the error code issued by av_set_string3() if the key/value pair
274 * cannot be set
275 */
276static int parse_key_value_pair(void *ctx, const char **buf,
277                                const char *key_val_sep, const char *pairs_sep)
278{
279    char *key = av_get_token(buf, key_val_sep);
280    char *val;
281    int ret;
282
283    if (*key && strspn(*buf, key_val_sep)) {
284        (*buf)++;
285        val = av_get_token(buf, pairs_sep);
286    } else {
287        av_log(ctx, AV_LOG_ERROR, "Missing key or no key/value separator found after key '%s'\n", key);
288        av_free(key);
289        return AVERROR(EINVAL);
290    }
291
292    av_log(ctx, AV_LOG_DEBUG, "Setting value '%s' for key '%s'\n", val, key);
293
294    ret = av_set_string3(ctx, key, val, 1, NULL);
295    if (ret == AVERROR(ENOENT))
296        av_log(ctx, AV_LOG_ERROR, "Key '%s' not found.\n", key);
297
298    av_free(key);
299    av_free(val);
300    return ret;
301}
302
303int av_set_options_string(void *ctx, const char *opts,
304                          const char *key_val_sep, const char *pairs_sep)
305{
306    int ret, count = 0;
307
308    while (*opts) {
309        if ((ret = parse_key_value_pair(ctx, &opts, key_val_sep, pairs_sep)) < 0)
310            return ret;
311        count++;
312
313        if (*opts)
314            opts++;
315    }
316
317    return count;
318}
319
320#ifdef TEST
321
322#undef printf
323
324typedef struct TestContext
325{
326    const AVClass *class;
327    int num;
328    int toggle;
329    char *string;
330    int flags;
331    AVRational rational;
332} TestContext;
333
334#define OFFSET(x) offsetof(TestContext, x)
335
336#define TEST_FLAG_COOL 01
337#define TEST_FLAG_LAME 02
338#define TEST_FLAG_MU   04
339
340static const AVOption test_options[]= {
341{"num",      "set num",        OFFSET(num),      FF_OPT_TYPE_INT,      0,              0,        100                 },
342{"toggle",   "set toggle",     OFFSET(toggle),   FF_OPT_TYPE_INT,      0,              0,        1                   },
343{"rational", "set rational",   OFFSET(rational), FF_OPT_TYPE_RATIONAL, 0,              0,        10                  },
344{"string",   "set string",     OFFSET(string),   FF_OPT_TYPE_STRING,   0,              CHAR_MIN, CHAR_MAX            },
345{"flags",    "set flags",      OFFSET(flags),    FF_OPT_TYPE_FLAGS,    0,              0,        INT_MAX, 0, "flags" },
346{"cool",     "set cool flag ", 0,                FF_OPT_TYPE_CONST,    TEST_FLAG_COOL, INT_MIN,  INT_MAX, 0, "flags" },
347{"lame",     "set lame flag ", 0,                FF_OPT_TYPE_CONST,    TEST_FLAG_LAME, INT_MIN,  INT_MAX, 0, "flags" },
348{"mu",       "set mu flag ",   0,                FF_OPT_TYPE_CONST,    TEST_FLAG_MU,   INT_MIN,  INT_MAX, 0, "flags" },
349{NULL},
350};
351
352static const char *test_get_name(void *ctx)
353{
354    return "test";
355}
356
357static const AVClass test_class = {
358    "TestContext",
359    test_get_name,
360    test_options
361};
362
363int main(void)
364{
365    int i;
366
367    const char *strings[] = {
368        "''",
369        "",
370        ":",
371        "\\",
372        "'",
373        "    ''    :",
374        "    ''  ''  :",
375        "foo   '' :",
376        "'foo'",
377        "foo     ",
378        "foo\\",
379        "foo':  blah:blah",
380        "foo\\:  blah:blah",
381        "foo\'",
382        "'foo :  '  :blahblah",
383        "\\ :blah",
384        "     foo",
385        "      foo       ",
386        "      foo     \\ ",
387        "foo ':blah",
388        " foo   bar    :   blahblah",
389        "\\f\\o\\o",
390        "'foo : \\ \\  '   : blahblah",
391        "'\\fo\\o:': blahblah",
392        "\\'fo\\o\\:':  foo  '  :blahblah"
393    };
394
395    for (i=0; i < FF_ARRAY_ELEMS(strings); i++) {
396        const char *p= strings[i];
397        printf("|%s|", p);
398        printf(" -> |%s|", av_get_token(&p, ":"));
399        printf(" + |%s|\n", p);
400    }
401
402    printf("\nTesting av_parse_color()\n");
403    {
404        uint8_t rgba[4];
405        const char *color_names[] = {
406            "bikeshed",
407            "RaNdOm",
408            "foo",
409            "red",
410            "Red ",
411            "RED",
412            "Violet",
413            "Yellow",
414            "Red",
415            "0x000000",
416            "0x0000000",
417            "0xff000000",
418            "0x3e34ff",
419            "0x3e34ffaa",
420            "0xffXXee",
421            "0xfoobar",
422            "0xffffeeeeeeee",
423        };
424
425        av_log_set_level(AV_LOG_DEBUG);
426
427        for (int i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
428            if (av_parse_color(rgba, color_names[i], NULL) >= 0)
429                printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
430        }
431    }
432
433    printf("\nTesting av_set_options_string()\n");
434    {
435        TestContext test_ctx;
436        const char *options[] = {
437            "",
438            ":",
439            "=",
440            "foo=:",
441            ":=foo",
442            "=foo",
443            "foo=",
444            "foo",
445            "foo=val",
446            "foo==val",
447            "toggle=:",
448            "string=:",
449            "toggle=1 : foo",
450            "toggle=100",
451            "toggle==1",
452            "flags=+mu-lame : num=42: toggle=0",
453            "num=42 : string=blahblah",
454            "rational=0 : rational=1/2 : rational=1/-1",
455            "rational=-1/0",
456        };
457
458        test_ctx.class = &test_class;
459        av_opt_set_defaults2(&test_ctx, 0, 0);
460        test_ctx.string = av_strdup("default");
461
462        av_log_set_level(AV_LOG_DEBUG);
463
464        for (i=0; i < FF_ARRAY_ELEMS(options); i++) {
465            av_log(&test_ctx, AV_LOG_DEBUG, "Setting options string '%s'\n", options[i]);
466            if (av_set_options_string(&test_ctx, options[i], "=", ":") < 0)
467                av_log(&test_ctx, AV_LOG_ERROR, "Error setting options string: '%s'\n", options[i]);
468            printf("\n");
469        }
470    }
471
472    return 0;
473}
474
475#endif
476