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