]> code.delx.au - pulseaudio/blob - src/pulse/proplist.c
add pa_proplist_to_string_sep()
[pulseaudio] / 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
28 #include <pulse/xmalloc.h>
29 #include <pulse/utf8.h>
30 #include <pulse/i18n.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 static pa_bool_t property_name_valid(const char *key) {
48
49 if (!pa_utf8_valid(key))
50 return FALSE;
51
52 if (strlen(key) <= 0)
53 return FALSE;
54
55 return TRUE;
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 pa_init_i18n();
68
69 return MAKE_PROPLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func));
70 }
71
72 void pa_proplist_free(pa_proplist* p) {
73 pa_assert(p);
74
75 pa_proplist_clear(p);
76 pa_hashmap_free(MAKE_HASHMAP(p), NULL, NULL);
77 }
78
79 /** Will accept only valid UTF-8 */
80 int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
81 struct property *prop;
82 pa_bool_t add = FALSE;
83
84 pa_assert(p);
85 pa_assert(key);
86
87 if (!property_name_valid(key) || !pa_utf8_valid(value))
88 return -1;
89
90 if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
91 prop = pa_xnew(struct property, 1);
92 prop->key = pa_xstrdup(key);
93 add = TRUE;
94 } else
95 pa_xfree(prop->value);
96
97 prop->value = pa_xstrdup(value);
98 prop->nbytes = strlen(value)+1;
99
100 if (add)
101 pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
102
103 return 0;
104 }
105
106 /** Will accept only valid UTF-8 */
107 int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) {
108 va_list ap;
109 int r;
110 char *t;
111
112 pa_assert(p);
113 pa_assert(key);
114
115 if (!property_name_valid(key) || !pa_utf8_valid(format))
116 return -1;
117
118 va_start(ap, format);
119 t = pa_vsprintf_malloc(format, ap);
120 va_end(ap);
121
122 r = pa_proplist_sets(p, key, t);
123
124 pa_xfree(t);
125 return r;
126 }
127
128 int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) {
129 struct property *prop;
130 pa_bool_t add = FALSE;
131
132 pa_assert(p);
133 pa_assert(key);
134
135 if (!property_name_valid(key))
136 return -1;
137
138 if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
139 prop = pa_xnew(struct property, 1);
140 prop->key = pa_xstrdup(key);
141 add = TRUE;
142 } else
143 pa_xfree(prop->value);
144
145 prop->value = pa_xmemdup(data, nbytes);
146 prop->nbytes = nbytes;
147
148 if (add)
149 pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
150
151 return 0;
152 }
153
154 const char *pa_proplist_gets(pa_proplist *p, const char *key) {
155 struct property *prop;
156
157 pa_assert(p);
158 pa_assert(key);
159
160 if (!property_name_valid(key))
161 return NULL;
162
163 if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key)))
164 return NULL;
165
166 if (prop->nbytes <= 0)
167 return NULL;
168
169 if (((char*) prop->value)[prop->nbytes-1] != 0)
170 return NULL;
171
172 if (strlen((char*) prop->value) != prop->nbytes-1)
173 return NULL;
174
175 if (!pa_utf8_valid((char*) prop->value))
176 return NULL;
177
178 return (char*) prop->value;
179 }
180
181 int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t *nbytes) {
182 struct property *prop;
183
184 pa_assert(p);
185 pa_assert(key);
186
187 if (!property_name_valid(key))
188 return -1;
189
190 if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key)))
191 return -1;
192
193 *data = prop->value;
194 *nbytes = prop->nbytes;
195
196 return 0;
197 }
198
199 void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, pa_proplist *other) {
200 struct property *prop;
201 void *state = NULL;
202
203 pa_assert(p);
204 pa_assert(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE);
205 pa_assert(other);
206
207 if (mode == PA_UPDATE_SET)
208 pa_proplist_clear(p);
209
210 while ((prop = pa_hashmap_iterate(MAKE_HASHMAP(other), &state, NULL))) {
211
212 if (mode == PA_UPDATE_MERGE && pa_proplist_contains(p, prop->key))
213 continue;
214
215 pa_assert_se(pa_proplist_set(p, prop->key, prop->value, prop->nbytes) == 0);
216 }
217 }
218
219 int pa_proplist_unset(pa_proplist *p, const char *key) {
220 struct property *prop;
221
222 pa_assert(p);
223 pa_assert(key);
224
225 if (!property_name_valid(key))
226 return -1;
227
228 if (!(prop = pa_hashmap_remove(MAKE_HASHMAP(p), key)))
229 return -2;
230
231 property_free(prop);
232 return 0;
233 }
234
235 int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]) {
236 const char * const * k;
237 int n = 0;
238
239 pa_assert(p);
240 pa_assert(keys);
241
242 for (k = keys; *k; k++)
243 if (!property_name_valid(*k))
244 return -1;
245
246 for (k = keys; *k; k++)
247 if (pa_proplist_unset(p, *k) >= 0)
248 n++;
249
250 return n;
251 }
252
253 const char *pa_proplist_iterate(pa_proplist *p, void **state) {
254 struct property *prop;
255
256 if (!(prop = pa_hashmap_iterate(MAKE_HASHMAP(p), state, NULL)))
257 return NULL;
258
259 return prop->key;
260 }
261
262 char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) {
263 const char *key;
264 void *state = NULL;
265 pa_strbuf *buf;
266
267 pa_assert(p);
268 pa_assert(sep);
269
270 buf = pa_strbuf_new();
271
272 while ((key = pa_proplist_iterate(p, &state))) {
273 const char *v;
274
275 if (!pa_strbuf_isempty(buf))
276 pa_strbuf_puts(buf, sep);
277
278 if ((v = pa_proplist_gets(p, key)))
279 pa_strbuf_printf(buf, "%s = \"%s\"", key, v);
280 else {
281 const void *value;
282 size_t nbytes;
283 char *c;
284
285 pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0);
286 c = pa_xmalloc(nbytes*2+1);
287 pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
288
289 pa_strbuf_printf(buf, "%s = hex:%s", key, c);
290 pa_xfree(c);
291 }
292 }
293
294 return pa_strbuf_tostring_free(buf);
295 }
296
297 char *pa_proplist_to_string(pa_proplist *p) {
298 char *s, *t;
299
300 s = pa_proplist_to_string_sep(p, "\n");
301 t = pa_sprintf_malloc("%s\n", s);
302 pa_xfree(s);
303
304 return t;
305 }
306
307 /* Remove all whitepsapce from the beginning and the end of *s. *s may
308 * be modified. (from conf-parser.c) */
309 #define WHITESPACE " \t\n"
310 #define in_string(c,s) (strchr(s,c) != NULL)
311
312 static char *strip(char *s) {
313 char *b = s+strspn(s, WHITESPACE);
314 char *e, *l = NULL;
315
316 for (e = b; *e; e++)
317 if (!in_string(*e, WHITESPACE))
318 l = e;
319
320 if (l)
321 *(l+1) = 0;
322
323 return b;
324 }
325
326 pa_proplist *pa_proplist_from_string(const char *str) {
327 pa_proplist *p;
328 char *s, *v, *k, *e;
329
330 pa_assert(str);
331 pa_assert_se(p = pa_proplist_new());
332 pa_assert_se(s = strdup(str));
333
334 for (k = s; *k; k = e) {
335 k = k+strspn(k, WHITESPACE);
336
337 if (!*k)
338 break;
339
340 if (!(v = strchr(k, '='))) {
341 pa_log("Missing '='.");
342 break;
343 }
344
345 *v++ = '\0';
346 k = strip(k);
347
348 v = v+strspn(v, WHITESPACE);
349 if (*v == '"') {
350 v++;
351 if (!(e = strchr(v, '"'))) { /* FIXME: handle escape */
352 pa_log("Missing '\"' at end of string value.");
353 break;
354 }
355 *e++ = '\0';
356 pa_proplist_sets(p, k, v);
357 } else {
358 uint8_t *blob;
359
360 if (*v++ != 'h' || *v++ != 'e' || *v++ != 'x' || *v++ != ':') {
361 pa_log("Value must be a string or \"hex:\"");
362 break;
363 }
364
365 e = v;
366 while (in_string(*e, "0123456789abcdefABCDEF"))
367 ++e;
368
369 if ((e - v) % 2) {
370 pa_log("Invalid \"hex:\" value data");
371 break;
372 }
373
374 blob = pa_xmalloc((size_t)(e-v)/2);
375 if (pa_parsehex(v, blob, (e-v)/2) != (size_t)((e-v)/2)) {
376 pa_log("Invalid \"hex:\" value data");
377 pa_xfree(blob);
378 break;
379 }
380
381 pa_proplist_set(p, k, blob, (e-v)/2);
382 pa_xfree(blob);
383 }
384 }
385
386 pa_xfree(s);
387
388 return p;
389 }
390
391 int pa_proplist_contains(pa_proplist *p, const char *key) {
392 pa_assert(p);
393 pa_assert(key);
394
395 if (!property_name_valid(key))
396 return -1;
397
398 if (!(pa_hashmap_get(MAKE_HASHMAP(p), key)))
399 return 0;
400
401 return 1;
402 }
403
404 void pa_proplist_clear(pa_proplist *p) {
405 struct property *prop;
406 pa_assert(p);
407
408 while ((prop = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
409 property_free(prop);
410 }
411
412 pa_proplist* pa_proplist_copy(pa_proplist *template) {
413 pa_proplist *p;
414
415 pa_assert_se(p = pa_proplist_new());
416
417 if (template)
418 pa_proplist_update(p, PA_UPDATE_REPLACE, template);
419
420 return p;
421 }
422
423 unsigned pa_proplist_size(pa_proplist *p) {
424 pa_assert(p);
425
426 return pa_hashmap_size(MAKE_HASHMAP(p));
427 }
428
429 int pa_proplist_isempty(pa_proplist *p) {
430 pa_assert(p);
431
432 return pa_hashmap_isempty(MAKE_HASHMAP(p));
433 }