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