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