• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
36 struct 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 
pa_proplist_key_valid(const char * key)46 int 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 
property_free(struct property * prop)57 static 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 
pa_proplist_new(void)65 pa_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 
pa_proplist_free(pa_proplist * p)69 void 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 */
pa_proplist_sets(pa_proplist * p,const char * key,const char * value)76 int 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 */
proplist_setn(pa_proplist * p,const char * key,size_t key_length,const char * value,size_t value_length)104 static 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 */
pa_proplist_setp(pa_proplist * p,const char * pair)141 int 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 
proplist_sethex(pa_proplist * p,const char * key,size_t key_length,const char * value,size_t value_length)155 static 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 */
pa_proplist_setf(pa_proplist * p,const char * key,const char * format,...)205 int 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 
240 fail:
241     pa_xfree(v);
242     return -1;
243 }
244 
pa_proplist_set(pa_proplist * p,const char * key,const void * data,size_t nbytes)245 int 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 
pa_proplist_gets(const pa_proplist * p,const char * key)275 const 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 
pa_proplist_get(const pa_proplist * p,const char * key,const void ** data,size_t * nbytes)302 int 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 
pa_proplist_update(pa_proplist * p,pa_update_mode_t mode,const pa_proplist * other)322 void 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 
pa_proplist_unset(pa_proplist * p,const char * key)342 int 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 
pa_proplist_unset_many(pa_proplist * p,const char * const keys[])355 int 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 
pa_proplist_iterate(const pa_proplist * p,void ** state)373 const 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 
pa_proplist_to_string_sep(const pa_proplist * p,const char * sep)382 char *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 
pa_proplist_to_string(const pa_proplist * p)440 char *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 
pa_proplist_from_string(const char * s)450 pa_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 
627 success:
628     return MAKE_PROPLIST(pl);
629 
630 fail:
631     pa_proplist_free(pl);
632     return NULL;
633 }
634 
pa_proplist_contains(const pa_proplist * p,const char * key)635 int 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 
pa_proplist_clear(pa_proplist * p)648 void pa_proplist_clear(pa_proplist *p) {
649     pa_assert(p);
650 
651     pa_hashmap_remove_all(MAKE_HASHMAP(p));
652 }
653 
pa_proplist_copy(const pa_proplist * p)654 pa_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 
pa_proplist_size(const pa_proplist * p)665 unsigned pa_proplist_size(const pa_proplist *p) {
666     pa_assert(p);
667 
668     return pa_hashmap_size(MAKE_HASHMAP_CONST(p));
669 }
670 
pa_proplist_isempty(const pa_proplist * p)671 int pa_proplist_isempty(const pa_proplist *p) {
672     pa_assert(p);
673 
674     return pa_hashmap_isempty(MAKE_HASHMAP_CONST(p));
675 }
676 
pa_proplist_equal(const pa_proplist * a,const pa_proplist * b)677 int 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