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