1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2007 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <string.h> 25#include <ctype.h> 26 27#include <pulse/xmalloc.h> 28#include <pulse/utf8.h> 29 30#include <pulsecore/hashmap.h> 31#include <pulsecore/strbuf.h> 32#include <pulsecore/core-util.h> 33 34#include "proplist.h" 35 36struct property { 37 char *key; 38 void *value; 39 size_t nbytes; 40}; 41 42#define MAKE_HASHMAP(p) ((pa_hashmap*) (p)) 43#define MAKE_HASHMAP_CONST(p) ((const pa_hashmap*) (p)) 44#define MAKE_PROPLIST(p) ((pa_proplist*) (p)) 45 46int pa_proplist_key_valid(const char *key) { 47 48 if (!pa_ascii_valid(key)) 49 return 0; 50 51 if (strlen(key) <= 0) 52 return 0; 53 54 return 1; 55} 56 57static void property_free(struct property *prop) { 58 pa_assert(prop); 59 60 pa_xfree(prop->key); 61 pa_xfree(prop->value); 62 pa_xfree(prop); 63} 64 65pa_proplist* pa_proplist_new(void) { 66 return MAKE_PROPLIST(pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, (pa_free_cb_t) property_free)); 67} 68 69void pa_proplist_free(pa_proplist* p) { 70 pa_assert(p); 71 72 pa_hashmap_free(MAKE_HASHMAP(p)); 73} 74 75/** Will accept only valid UTF-8 */ 76int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) { 77 struct property *prop; 78 bool add = false; 79 80 pa_assert(p); 81 pa_assert(key); 82 pa_assert(value); 83 84 if (!pa_proplist_key_valid(key) || !pa_utf8_valid(value)) 85 return -1; 86 87 if (!(prop = pa_hashmap_get(MAKE_HASHMAP_CONST(p), key))) { 88 prop = pa_xnew(struct property, 1); 89 prop->key = pa_xstrdup(key); 90 add = true; 91 } else 92 pa_xfree(prop->value); 93 94 prop->value = pa_xstrdup(value); 95 prop->nbytes = strlen(value)+1; 96 97 if (add) 98 pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); 99 100 return 0; 101} 102 103/** Will accept only valid UTF-8 */ 104static int proplist_setn(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) { 105 struct property *prop; 106 bool add = false; 107 char *k, *v; 108 109 pa_assert(p); 110 pa_assert(key); 111 pa_assert(value); 112 113 k = pa_xstrndup(key, key_length); 114 v = pa_xstrndup(value, value_length); 115 116 if (!pa_proplist_key_valid(k) || !pa_utf8_valid(v)) { 117 pa_xfree(k); 118 pa_xfree(v); 119 return -1; 120 } 121 122 if (!(prop = pa_hashmap_get(MAKE_HASHMAP_CONST(p), k))) { 123 prop = pa_xnew(struct property, 1); 124 prop->key = k; 125 add = true; 126 } else { 127 pa_xfree(prop->value); 128 pa_xfree(k); 129 } 130 131 prop->value = v; 132 prop->nbytes = strlen(v)+1; 133 134 if (add) 135 pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); 136 137 return 0; 138} 139 140/** Will accept only valid UTF-8 */ 141int pa_proplist_setp(pa_proplist *p, const char *pair) { 142 const char *t; 143 144 pa_assert(p); 145 pa_assert(pair); 146 147 if (!(t = strchr(pair, '='))) 148 return -1; 149 150 return proplist_setn(p, 151 pair, t - pair, 152 t + 1, strchr(pair, 0) - t - 1); 153} 154 155static int proplist_sethex(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) { 156 struct property *prop; 157 bool add = false; 158 char *k, *v; 159 uint8_t *d; 160 size_t dn; 161 162 pa_assert(p); 163 pa_assert(key); 164 pa_assert(value); 165 166 k = pa_xstrndup(key, key_length); 167 168 if (!pa_proplist_key_valid(k)) { 169 pa_xfree(k); 170 return -1; 171 } 172 173 v = pa_xstrndup(value, value_length); 174 d = pa_xmalloc(value_length*2+1); 175 176 if ((dn = pa_parsehex(v, d, value_length*2)) == (size_t) -1) { 177 pa_xfree(k); 178 pa_xfree(v); 179 pa_xfree(d); 180 return -1; 181 } 182 183 pa_xfree(v); 184 185 if (!(prop = pa_hashmap_get(MAKE_HASHMAP_CONST(p), k))) { 186 prop = pa_xnew(struct property, 1); 187 prop->key = k; 188 add = true; 189 } else { 190 pa_xfree(prop->value); 191 pa_xfree(k); 192 } 193 194 d[dn] = 0; 195 prop->value = d; 196 prop->nbytes = dn; 197 198 if (add) 199 pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); 200 201 return 0; 202} 203 204/** Will accept only valid UTF-8 */ 205int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) { 206 struct property *prop; 207 bool add = false; 208 va_list ap; 209 char *v; 210 211 pa_assert(p); 212 pa_assert(key); 213 pa_assert(format); 214 215 if (!pa_proplist_key_valid(key) || !pa_utf8_valid(format)) 216 return -1; 217 218 va_start(ap, format); 219 v = pa_vsprintf_malloc(format, ap); 220 va_end(ap); 221 222 if (!pa_utf8_valid(v)) 223 goto fail; 224 225 if (!(prop = pa_hashmap_get(MAKE_HASHMAP_CONST(p), key))) { 226 prop = pa_xnew(struct property, 1); 227 prop->key = pa_xstrdup(key); 228 add = true; 229 } else 230 pa_xfree(prop->value); 231 232 prop->value = v; 233 prop->nbytes = strlen(v)+1; 234 235 if (add) 236 pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); 237 238 return 0; 239 240fail: 241 pa_xfree(v); 242 return -1; 243} 244 245int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) { 246 struct property *prop; 247 bool add = false; 248 249 pa_assert(p); 250 pa_assert(key); 251 pa_assert(data || nbytes == 0); 252 253 if (!pa_proplist_key_valid(key)) 254 return -1; 255 256 if (!(prop = pa_hashmap_get(MAKE_HASHMAP_CONST(p), key))) { 257 prop = pa_xnew(struct property, 1); 258 prop->key = pa_xstrdup(key); 259 add = true; 260 } else 261 pa_xfree(prop->value); 262 263 prop->value = pa_xmalloc(nbytes+1); 264 if (nbytes > 0) 265 memcpy(prop->value, data, nbytes); 266 ((char*) prop->value)[nbytes] = 0; 267 prop->nbytes = nbytes; 268 269 if (add) 270 pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); 271 272 return 0; 273} 274 275const char *pa_proplist_gets(const pa_proplist *p, const char *key) { 276 struct property *prop; 277 278 pa_assert(p); 279 pa_assert(key); 280 281 if (!pa_proplist_key_valid(key)) 282 return NULL; 283 284 if (!(prop = pa_hashmap_get(MAKE_HASHMAP_CONST(p), key))) 285 return NULL; 286 287 if (prop->nbytes <= 0) 288 return NULL; 289 290 if (((char*) prop->value)[prop->nbytes-1] != 0) 291 return NULL; 292 293 if (strlen((char*) prop->value) != prop->nbytes-1) 294 return NULL; 295 296 if (!pa_utf8_valid((char*) prop->value)) 297 return NULL; 298 299 return (char*) prop->value; 300} 301 302int pa_proplist_get(const pa_proplist *p, const char *key, const void **data, size_t *nbytes) { 303 struct property *prop; 304 305 pa_assert(p); 306 pa_assert(key); 307 pa_assert(data); 308 pa_assert(nbytes); 309 310 if (!pa_proplist_key_valid(key)) 311 return -1; 312 313 if (!(prop = pa_hashmap_get(MAKE_HASHMAP_CONST(p), key))) 314 return -1; 315 316 *data = prop->value; 317 *nbytes = prop->nbytes; 318 319 return 0; 320} 321 322void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other) { 323 struct property *prop; 324 void *state = NULL; 325 326 pa_assert(p); 327 pa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE); 328 pa_assert(other); 329 330 if (mode == PA_UPDATE_SET) 331 pa_proplist_clear(p); 332 333 while ((prop = pa_hashmap_iterate(MAKE_HASHMAP_CONST(other), &state, NULL))) { 334 335 if (mode == PA_UPDATE_MERGE && pa_proplist_contains(p, prop->key)) 336 continue; 337 338 pa_assert_se(pa_proplist_set(p, prop->key, prop->value, prop->nbytes) == 0); 339 } 340} 341 342int pa_proplist_unset(pa_proplist *p, const char *key) { 343 pa_assert(p); 344 pa_assert(key); 345 346 if (!pa_proplist_key_valid(key)) 347 return -1; 348 349 if (pa_hashmap_remove_and_free(MAKE_HASHMAP(p), key) < 0) 350 return -2; 351 352 return 0; 353} 354 355int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) { 356 const char * const * k; 357 int n = 0; 358 359 pa_assert(p); 360 pa_assert(keys); 361 362 for (k = keys; *k; k++) 363 if (!pa_proplist_key_valid(*k)) 364 return -1; 365 366 for (k = keys; *k; k++) 367 if (pa_proplist_unset(p, *k) >= 0) 368 n++; 369 370 return n; 371} 372 373const char *pa_proplist_iterate(const pa_proplist *p, void **state) { 374 struct property *prop; 375 376 if (!(prop = pa_hashmap_iterate(MAKE_HASHMAP_CONST(p), state, NULL))) 377 return NULL; 378 379 return prop->key; 380} 381 382char *pa_proplist_to_string_sep(const pa_proplist *p, const char *sep) { 383 const char *key; 384 void *state = NULL; 385 pa_strbuf *buf; 386 387 pa_assert(p); 388 pa_assert(sep); 389 390 buf = pa_strbuf_new(); 391 392 while ((key = pa_proplist_iterate(p, &state))) { 393 const char *v; 394 395 if (!pa_strbuf_isempty(buf)) 396 pa_strbuf_puts(buf, sep); 397 398 if ((v = pa_proplist_gets(p, key))) { 399 const char *t; 400 401 pa_strbuf_printf(buf, "%s = \"", key); 402 403 for (t = v;;) { 404 size_t h; 405 406 h = strcspn(t, "\""); 407 408 if (h > 0) 409 pa_strbuf_putsn(buf, t, h); 410 411 t += h; 412 413 if (*t == 0) 414 break; 415 416 pa_assert(*t == '"'); 417 pa_strbuf_puts(buf, "\\\""); 418 419 t++; 420 } 421 422 pa_strbuf_puts(buf, "\""); 423 } else { 424 const void *value; 425 size_t nbytes; 426 char *c; 427 428 pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0); 429 c = pa_xmalloc(nbytes*2+1); 430 pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1); 431 432 pa_strbuf_printf(buf, "%s = hex:%s", key, c); 433 pa_xfree(c); 434 } 435 } 436 437 return pa_strbuf_to_string_free(buf); 438} 439 440char *pa_proplist_to_string(const pa_proplist *p) { 441 char *s, *t; 442 443 s = pa_proplist_to_string_sep(p, "\n"); 444 t = pa_sprintf_malloc("%s\n", s); 445 pa_xfree(s); 446 447 return t; 448} 449 450pa_proplist *pa_proplist_from_string(const char *s) { 451 enum { 452 WHITESPACE, 453 KEY, 454 AFTER_KEY, 455 VALUE_START, 456 VALUE_SIMPLE, 457 VALUE_DOUBLE_QUOTES, 458 VALUE_DOUBLE_QUOTES_ESCAPE, 459 VALUE_TICKS, 460 VALUE_TICKS_ESCAPED, 461 VALUE_HEX 462 } state; 463 464 pa_proplist *pl; 465 const char *p, *key = NULL, *value = NULL; 466 size_t key_len = 0, value_len = 0; 467 468 pa_assert(s); 469 470 pl = pa_proplist_new(); 471 472 state = WHITESPACE; 473 474 for (p = s;; p++) { 475 switch (state) { 476 477 case WHITESPACE: 478 if (*p == 0) 479 goto success; 480 else if (*p == '=') 481 goto fail; 482 else if (!isspace((unsigned char)*p)) { 483 key = p; 484 state = KEY; 485 key_len = 1; 486 } 487 break; 488 489 case KEY: 490 if (*p == 0) 491 goto fail; 492 else if (*p == '=') 493 state = VALUE_START; 494 else if (isspace((unsigned char)*p)) 495 state = AFTER_KEY; 496 else 497 key_len++; 498 break; 499 500 case AFTER_KEY: 501 if (*p == 0) 502 goto fail; 503 else if (*p == '=') 504 state = VALUE_START; 505 else if (!isspace((unsigned char)*p)) 506 goto fail; 507 break; 508 509 case VALUE_START: 510 if (*p == 0) 511 goto fail; 512 else if (strncmp(p, "hex:", 4) == 0) { 513 state = VALUE_HEX; 514 value = p+4; 515 value_len = 0; 516 p += 3; 517 } else if (*p == '\'') { 518 state = VALUE_TICKS; 519 value = p+1; 520 value_len = 0; 521 } else if (*p == '"') { 522 state = VALUE_DOUBLE_QUOTES; 523 value = p+1; 524 value_len = 0; 525 } else if (!isspace((unsigned char)*p)) { 526 state = VALUE_SIMPLE; 527 value = p; 528 value_len = 1; 529 } 530 break; 531 532 case VALUE_SIMPLE: 533 if (*p == 0 || isspace((unsigned char)*p)) { 534 if (proplist_setn(pl, key, key_len, value, value_len) < 0) 535 goto fail; 536 537 if (*p == 0) 538 goto success; 539 540 state = WHITESPACE; 541 } else 542 value_len++; 543 break; 544 545 case VALUE_DOUBLE_QUOTES: 546 if (*p == 0) 547 goto fail; 548 else if (*p == '"') { 549 char *v; 550 551 v = pa_unescape(pa_xstrndup(value, value_len)); 552 553 if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) { 554 pa_xfree(v); 555 goto fail; 556 } 557 558 pa_xfree(v); 559 state = WHITESPACE; 560 } else if (*p == '\\') { 561 state = VALUE_DOUBLE_QUOTES_ESCAPE; 562 value_len++; 563 } else 564 value_len++; 565 break; 566 567 case VALUE_DOUBLE_QUOTES_ESCAPE: 568 if (*p == 0) 569 goto fail; 570 else { 571 state = VALUE_DOUBLE_QUOTES; 572 value_len++; 573 } 574 break; 575 576 case VALUE_TICKS: 577 if (*p == 0) 578 goto fail; 579 else if (*p == '\'') { 580 char *v; 581 582 v = pa_unescape(pa_xstrndup(value, value_len)); 583 584 if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) { 585 pa_xfree(v); 586 goto fail; 587 } 588 589 pa_xfree(v); 590 state = WHITESPACE; 591 } else if (*p == '\\') { 592 state = VALUE_TICKS_ESCAPED; 593 value_len++; 594 } else 595 value_len++; 596 break; 597 598 case VALUE_TICKS_ESCAPED: 599 if (*p == 0) 600 goto fail; 601 else { 602 state = VALUE_TICKS; 603 value_len++; 604 } 605 break; 606 607 case VALUE_HEX: 608 if ((*p >= '0' && *p <= '9') || 609 (*p >= 'A' && *p <= 'F') || 610 (*p >= 'a' && *p <= 'f')) { 611 value_len++; 612 } else if (*p == 0 || isspace((unsigned char)*p)) { 613 614 if (proplist_sethex(pl, key, key_len, value, value_len) < 0) 615 goto fail; 616 617 if (*p == 0) 618 goto success; 619 620 state = WHITESPACE; 621 } else 622 goto fail; 623 break; 624 } 625 } 626 627success: 628 return MAKE_PROPLIST(pl); 629 630fail: 631 pa_proplist_free(pl); 632 return NULL; 633} 634 635int pa_proplist_contains(const pa_proplist *p, const char *key) { 636 pa_assert(p); 637 pa_assert(key); 638 639 if (!pa_proplist_key_valid(key)) 640 return -1; 641 642 if (!(pa_hashmap_get(MAKE_HASHMAP_CONST(p), key))) 643 return 0; 644 645 return 1; 646} 647 648void pa_proplist_clear(pa_proplist *p) { 649 pa_assert(p); 650 651 pa_hashmap_remove_all(MAKE_HASHMAP(p)); 652} 653 654pa_proplist* pa_proplist_copy(const pa_proplist *p) { 655 pa_proplist *copy; 656 657 pa_assert_se(copy = pa_proplist_new()); 658 659 if (p) 660 pa_proplist_update(copy, PA_UPDATE_REPLACE, p); 661 662 return copy; 663} 664 665unsigned pa_proplist_size(const pa_proplist *p) { 666 pa_assert(p); 667 668 return pa_hashmap_size(MAKE_HASHMAP_CONST(p)); 669} 670 671int pa_proplist_isempty(const pa_proplist *p) { 672 pa_assert(p); 673 674 return pa_hashmap_isempty(MAKE_HASHMAP_CONST(p)); 675} 676 677int pa_proplist_equal(const pa_proplist *a, const pa_proplist *b) { 678 const void *key = NULL; 679 struct property *a_prop = NULL; 680 struct property *b_prop = NULL; 681 void *state = NULL; 682 683 pa_assert(a); 684 pa_assert(b); 685 686 if (a == b) 687 return 1; 688 689 if (pa_proplist_size(a) != pa_proplist_size(b)) 690 return 0; 691 692 while ((a_prop = pa_hashmap_iterate(MAKE_HASHMAP_CONST(a), &state, &key))) { 693 if (!(b_prop = pa_hashmap_get(MAKE_HASHMAP_CONST(b), key))) 694 return 0; 695 696 if (a_prop->nbytes != b_prop->nbytes) 697 return 0; 698 699 if (memcmp(a_prop->value, b_prop->value, a_prop->nbytes) != 0) 700 return 0; 701 } 702 703 return 1; 704} 705