]> git.sesse.net Git - ffmpeg/blob - libavfilter/parseutils.c
Fix grammar errors in documentation
[ffmpeg] / 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 if (!strncmp(color_string2, "0x", 2)) {
238         char *tail;
239         int len = strlen(color_string2);
240         unsigned int rgba = strtoul(color_string2, &tail, 16);
241
242         if (*tail || (len != 8 && len != 10)) {
243             av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string2);
244             return AVERROR(EINVAL);
245         }
246         if (len == 10) {
247             rgba_color[3] = rgba;
248             rgba >>= 8;
249         }
250         rgba_color[0] = rgba >> 16;
251         rgba_color[1] = rgba >> 8;
252         rgba_color[2] = rgba;
253     } else {
254         entry = bsearch(color_string2,
255                         color_table,
256                         FF_ARRAY_ELEMS(color_table),
257                         sizeof(ColorEntry),
258                         color_table_compare);
259         if (!entry) {
260             av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string2);
261             return AVERROR(EINVAL);
262         }
263         memcpy(rgba_color, entry->rgb_color, 3);
264     }
265
266     if (tail) {
267         unsigned long int alpha;
268         const char *alpha_string = tail;
269         if (!strncmp(alpha_string, "0x", 2)) {
270             alpha = strtoul(alpha_string, &tail, 16);
271         } else {
272             alpha = strtoul(alpha_string, &tail, 10);
273             if (*tail) {
274                 double d = strtod(alpha_string, &tail);
275                 alpha = d * 255;
276             }
277         }
278
279         if (tail == alpha_string || *tail || alpha > 255) {
280             av_log(log_ctx, AV_LOG_ERROR, "Invalid alpha value specifier '%s' in '%s'\n",
281                    alpha_string, color_string);
282             return AVERROR(EINVAL);
283         }
284         rgba_color[3] = alpha;
285     }
286
287     return 0;
288 }
289
290 /**
291  * Store the value in the field in ctx that is named like key.
292  * ctx must be an AVClass context, storing is done using AVOptions.
293  *
294  * @param buf the string to parse, buf will be updated to point at the
295  * separator just after the parsed key/value pair
296  * @param key_val_sep a 0-terminated list of characters used to
297  * separate key from value
298  * @param pairs_sep a 0-terminated list of characters used to separate
299  * two pairs from each other
300  * @return 0 if the key/value pair has been successfully parsed and
301  * set, or a negative value corresponding to an AVERROR code in case
302  * of error:
303  * AVERROR(EINVAL) if the key/value pair cannot be parsed,
304  * the error code issued by av_set_string3() if the key/value pair
305  * cannot be set
306  */
307 static int parse_key_value_pair(void *ctx, const char **buf,
308                                 const char *key_val_sep, const char *pairs_sep)
309 {
310     char *key = av_get_token(buf, key_val_sep);
311     char *val;
312     int ret;
313
314     if (*key && strspn(*buf, key_val_sep)) {
315         (*buf)++;
316         val = av_get_token(buf, pairs_sep);
317     } else {
318         av_log(ctx, AV_LOG_ERROR, "Missing key or no key/value separator found after key '%s'\n", key);
319         av_free(key);
320         return AVERROR(EINVAL);
321     }
322
323     av_log(ctx, AV_LOG_DEBUG, "Setting value '%s' for key '%s'\n", val, key);
324
325     ret = av_set_string3(ctx, key, val, 1, NULL);
326     if (ret == AVERROR(ENOENT))
327         av_log(ctx, AV_LOG_ERROR, "Key '%s' not found.\n", key);
328
329     av_free(key);
330     av_free(val);
331     return ret;
332 }
333
334 int av_set_options_string(void *ctx, const char *opts,
335                           const char *key_val_sep, const char *pairs_sep)
336 {
337     int ret, count = 0;
338
339     while (*opts) {
340         if ((ret = parse_key_value_pair(ctx, &opts, key_val_sep, pairs_sep)) < 0)
341             return ret;
342         count++;
343
344         if (*opts)
345             opts++;
346     }
347
348     return count;
349 }
350
351 #ifdef TEST
352
353 #undef printf
354
355 typedef struct TestContext
356 {
357     const AVClass *class;
358     int num;
359     int toggle;
360     char *string;
361     int flags;
362     AVRational rational;
363 } TestContext;
364
365 #define OFFSET(x) offsetof(TestContext, x)
366
367 #define TEST_FLAG_COOL 01
368 #define TEST_FLAG_LAME 02
369 #define TEST_FLAG_MU   04
370
371 static const AVOption test_options[]= {
372 {"num",      "set num",        OFFSET(num),      FF_OPT_TYPE_INT,      0,              0,        100                 },
373 {"toggle",   "set toggle",     OFFSET(toggle),   FF_OPT_TYPE_INT,      0,              0,        1                   },
374 {"rational", "set rational",   OFFSET(rational), FF_OPT_TYPE_RATIONAL, 0,              0,        10                  },
375 {"string",   "set string",     OFFSET(string),   FF_OPT_TYPE_STRING,   0,              CHAR_MIN, CHAR_MAX            },
376 {"flags",    "set flags",      OFFSET(flags),    FF_OPT_TYPE_FLAGS,    0,              0,        INT_MAX, 0, "flags" },
377 {"cool",     "set cool flag ", 0,                FF_OPT_TYPE_CONST,    TEST_FLAG_COOL, INT_MIN,  INT_MAX, 0, "flags" },
378 {"lame",     "set lame flag ", 0,                FF_OPT_TYPE_CONST,    TEST_FLAG_LAME, INT_MIN,  INT_MAX, 0, "flags" },
379 {"mu",       "set mu flag ",   0,                FF_OPT_TYPE_CONST,    TEST_FLAG_MU,   INT_MIN,  INT_MAX, 0, "flags" },
380 {NULL},
381 };
382
383 static const char *test_get_name(void *ctx)
384 {
385     return "test";
386 }
387
388 static const AVClass test_class = {
389     "TestContext",
390     test_get_name,
391     test_options
392 };
393
394 int main(void)
395 {
396     int i;
397
398     const char *strings[] = {
399         "''",
400         "",
401         ":",
402         "\\",
403         "'",
404         "    ''    :",
405         "    ''  ''  :",
406         "foo   '' :",
407         "'foo'",
408         "foo     ",
409         "foo\\",
410         "foo':  blah:blah",
411         "foo\\:  blah:blah",
412         "foo\'",
413         "'foo :  '  :blahblah",
414         "\\ :blah",
415         "     foo",
416         "      foo       ",
417         "      foo     \\ ",
418         "foo ':blah",
419         " foo   bar    :   blahblah",
420         "\\f\\o\\o",
421         "'foo : \\ \\  '   : blahblah",
422         "'\\fo\\o:': blahblah",
423         "\\'fo\\o\\:':  foo  '  :blahblah"
424     };
425
426     for (i=0; i < FF_ARRAY_ELEMS(strings); i++) {
427         const char *p= strings[i];
428         printf("|%s|", p);
429         printf(" -> |%s|", av_get_token(&p, ":"));
430         printf(" + |%s|\n", p);
431     }
432
433     printf("\nTesting av_parse_color()\n");
434     {
435         uint8_t rgba[4];
436         const char *color_names[] = {
437             "bikeshed",
438             "RaNdOm",
439             "foo",
440             "red",
441             "Red ",
442             "RED",
443             "Violet",
444             "Yellow",
445             "Red",
446             "0x000000",
447             "0x0000000",
448             "0xff000000",
449             "0x3e34ff",
450             "0x3e34ffaa",
451             "0xffXXee",
452             "0xfoobar",
453             "0xffffeeeeeeee",
454             "red@foo",
455             "random@10",
456             "0xff0000@1.0",
457             "red@",
458             "red@0xfff",
459             "red@0xf",
460             "red@2",
461             "red@0.1",
462             "red@-1",
463             "red@0.5",
464             "red@1.0",
465             "red@256",
466             "red@10foo",
467             "red@-1.0",
468             "red@-0.0",
469         };
470
471         av_log_set_level(AV_LOG_DEBUG);
472
473         for (int i = 0;  i < FF_ARRAY_ELEMS(color_names); i++) {
474             if (av_parse_color(rgba, color_names[i], NULL) >= 0)
475                 printf("%s -> R(%d) G(%d) B(%d) A(%d)\n", color_names[i], rgba[0], rgba[1], rgba[2], rgba[3]);
476         }
477     }
478
479     printf("\nTesting av_set_options_string()\n");
480     {
481         TestContext test_ctx;
482         const char *options[] = {
483             "",
484             ":",
485             "=",
486             "foo=:",
487             ":=foo",
488             "=foo",
489             "foo=",
490             "foo",
491             "foo=val",
492             "foo==val",
493             "toggle=:",
494             "string=:",
495             "toggle=1 : foo",
496             "toggle=100",
497             "toggle==1",
498             "flags=+mu-lame : num=42: toggle=0",
499             "num=42 : string=blahblah",
500             "rational=0 : rational=1/2 : rational=1/-1",
501             "rational=-1/0",
502         };
503
504         test_ctx.class = &test_class;
505         av_opt_set_defaults2(&test_ctx, 0, 0);
506         test_ctx.string = av_strdup("default");
507
508         av_log_set_level(AV_LOG_DEBUG);
509
510         for (i=0; i < FF_ARRAY_ELEMS(options); i++) {
511             av_log(&test_ctx, AV_LOG_DEBUG, "Setting options string '%s'\n", options[i]);
512             if (av_set_options_string(&test_ctx, options[i], "=", ":") < 0)
513                 av_log(&test_ctx, AV_LOG_ERROR, "Error setting options string: '%s'\n", options[i]);
514             printf("\n");
515         }
516     }
517
518     return 0;
519 }
520
521 #endif