Fix style: "if(" -> "if (".
[libav.git] / libavfilter / parseutils.c
CommitLineData
d11dbf09
MN
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/**
ba87f080 21 * @file
d11dbf09
MN
22 * parsing utils
23 */
24
b69b622c 25#include <strings.h>
d11dbf09 26#include "libavutil/avutil.h"
a52e2c3a 27#include "libavutil/avstring.h"
4fa61d1e 28#include "libavutil/random_seed.h"
d11dbf09
MN
29#include "parseutils.h"
30
31#define WHITESPACES " \n\t"
32
33char *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;
5ba949fe 38 if (!out) return NULL;
d11dbf09
MN
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
a3015225
SS
67typedef struct {
68 const char *name; ///< a string representing the name of the color
8e094dd6 69 uint8_t rgb_color[3]; ///< RGB values for the color
a3015225
SS
70} ColorEntry;
71
72static 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
215static int color_table_compare(const void *lhs, const void *rhs)
216{
b69b622c 217 return strcasecmp(lhs, ((const ColorEntry *)rhs)->name);
a3015225
SS
218}
219
a52e2c3a
SS
220#define ALPHA_SEP '@'
221
a3015225
SS
222int av_parse_color(uint8_t *rgba_color, const char *color_string, void *log_ctx)
223{
a52e2c3a
SS
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")) {
576fb48e 232 int rgba = av_get_random_seed();
4fa61d1e
RP
233 rgba_color[0] = rgba >> 24;
234 rgba_color[1] = rgba >> 16;
235 rgba_color[2] = rgba >> 8;
236 rgba_color[3] = rgba;
9e74966b 237 } else if (!strncmp(color_string2, "0x", 2)) {
a3015225 238 char *tail;
a52e2c3a
SS
239 int len = strlen(color_string2);
240 unsigned int rgba = strtoul(color_string2, &tail, 16);
a3015225
SS
241
242 if (*tail || (len != 8 && len != 10)) {
a52e2c3a 243 av_log(log_ctx, AV_LOG_ERROR, "Invalid 0xRRGGBB[AA] color string: '%s'\n", color_string2);
dc0eaef3 244 return AVERROR(EINVAL);
a3015225
SS
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 {
a52e2c3a 254 entry = bsearch(color_string2,
4cf0b43a
SS
255 color_table,
256 FF_ARRAY_ELEMS(color_table),
257 sizeof(ColorEntry),
258 color_table_compare);
a3015225 259 if (!entry) {
a52e2c3a 260 av_log(log_ctx, AV_LOG_ERROR, "Cannot find color '%s'\n", color_string2);
dc0eaef3 261 return AVERROR(EINVAL);
a3015225 262 }
8e094dd6 263 memcpy(rgba_color, entry->rgb_color, 3);
a3015225
SS
264 }
265
a52e2c3a
SS
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
a3015225
SS
287 return 0;
288}
289
c1ec75b5 290/**
49bd8e4b 291 * Store the value in the field in ctx that is named like key.
c1ec75b5
SS
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 */
307static 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);
62096b99
SS
326 if (ret == AVERROR(ENOENT))
327 av_log(ctx, AV_LOG_ERROR, "Key '%s' not found.\n", key);
c1ec75b5
SS
328
329 av_free(key);
330 av_free(val);
331 return ret;
332}
333
334int 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
d11dbf09
MN
351#ifdef TEST
352
353#undef printf
354
c1ec75b5
SS
355typedef 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
371static 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
383static const char *test_get_name(void *ctx)
384{
385 return "test";
386}
387
388static const AVClass test_class = {
389 "TestContext",
390 test_get_name,
391 test_options
392};
393
2e032b32 394int main(void)
d11dbf09
MN
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
a3015225
SS
433 printf("\nTesting av_parse_color()\n");
434 {
435 uint8_t rgba[4];
436 const char *color_names[] = {
4fa61d1e 437 "bikeshed",
3d3bd64d 438 "RaNdOm",
a3015225
SS
439 "foo",
440 "red",
441 "Red ",
442 "RED",
443 "Violet",
444 "Yellow",
445 "Red",
446 "0x000000",
447 "0x0000000",
7d132c54 448 "0xff000000",
a3015225
SS
449 "0x3e34ff",
450 "0x3e34ffaa",
451 "0xffXXee",
452 "0xfoobar",
453 "0xffffeeeeeeee",
a52e2c3a
SS
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",
a3015225
SS
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
c1ec75b5
SS
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
d11dbf09
MN
518 return 0;
519}
520
521#endif