]> code.delx.au - pulseaudio/blob - src/pulse/proplist.c
add pa_proplist_size() and pa_proplist_isempty()
[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(pa_proplist *p) {
263 const char *key;
264 void *state = NULL;
265 pa_strbuf *buf;
266
267 pa_assert(p);
268
269 buf = pa_strbuf_new();
270
271 while ((key = pa_proplist_iterate(p, &state))) {
272
273 const char *v;
274
275 if ((v = pa_proplist_gets(p, key)))
276 pa_strbuf_printf(buf, "%s = \"%s\"\n", key, v);
277 else {
278 const void *value;
279 size_t nbytes;
280 char *c;
281
282 pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0);
283 c = pa_xmalloc(nbytes*2+1);
284 pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
285
286 pa_strbuf_printf(buf, "%s = hex:%s\n", key, c);
287 pa_xfree(c);
288 }
289 }
290
291 return pa_strbuf_tostring_free(buf);
292 }
293
294 /* Remove all whitepsapce from the beginning and the end of *s. *s may
295 * be modified. (from conf-parser.c) */
296 #define WHITESPACE " \t\n"
297 #define in_string(c,s) (strchr(s,c) != NULL)
298
299 static char *strip(char *s) {
300 char *b = s+strspn(s, WHITESPACE);
301 char *e, *l = NULL;
302
303 for (e = b; *e; e++)
304 if (!in_string(*e, WHITESPACE))
305 l = e;
306
307 if (l)
308 *(l+1) = 0;
309
310 return b;
311 }
312
313 pa_proplist *pa_proplist_from_string(const char *str) {
314 pa_proplist *p;
315 char *s, *v, *k, *e;
316
317 pa_assert(str);
318 pa_assert_se(p = pa_proplist_new());
319 pa_assert_se(s = strdup(str));
320
321 for (k = s; *k; k = e) {
322 k = k+strspn(k, WHITESPACE);
323
324 if (!*k)
325 break;
326
327 if (!(v = strchr(k, '='))) {
328 pa_log("Missing '='.");
329 break;
330 }
331
332 *v++ = '\0';
333 k = strip(k);
334
335 v = v+strspn(v, WHITESPACE);
336 if (*v == '"') {
337 v++;
338 if (!(e = strchr(v, '"'))) { /* FIXME: handle escape */
339 pa_log("Missing '\"' at end of string value.");
340 break;
341 }
342 *e++ = '\0';
343 pa_proplist_sets(p, k, v);
344 } else {
345 uint8_t *blob;
346
347 if (*v++ != 'h' || *v++ != 'e' || *v++ != 'x' || *v++ != ':') {
348 pa_log("Value must be a string or \"hex:\"");
349 break;
350 }
351
352 e = v;
353 while (in_string(*e, "0123456789abcdefABCDEF"))
354 ++e;
355
356 if ((e - v) % 2) {
357 pa_log("Invalid \"hex:\" value data");
358 break;
359 }
360
361 blob = pa_xmalloc((size_t)(e-v)/2);
362 if (pa_parsehex(v, blob, (e-v)/2) != ((e-v)/2)) {
363 pa_log("Invalid \"hex:\" value data");
364 pa_xfree(blob);
365 break;
366 }
367
368 pa_proplist_set(p, k, blob, (e-v)/2);
369 pa_xfree(blob);
370 }
371 }
372
373 pa_xfree(s);
374
375 return p;
376 }
377
378 int pa_proplist_contains(pa_proplist *p, const char *key) {
379 pa_assert(p);
380 pa_assert(key);
381
382 if (!property_name_valid(key))
383 return -1;
384
385 if (!(pa_hashmap_get(MAKE_HASHMAP(p), key)))
386 return 0;
387
388 return 1;
389 }
390
391 void pa_proplist_clear(pa_proplist *p) {
392 struct property *prop;
393 pa_assert(p);
394
395 while ((prop = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
396 property_free(prop);
397 }
398
399 pa_proplist* pa_proplist_copy(pa_proplist *template) {
400 pa_proplist *p;
401
402 pa_assert_se(p = pa_proplist_new());
403
404 if (template)
405 pa_proplist_update(p, PA_UPDATE_REPLACE, template);
406
407 return p;
408 }
409
410 unsigned pa_proplist_size(pa_proplist *p) {
411 pa_assert(p);
412
413 return pa_hashmap_size(MAKE_HASHMAP(p));
414 }
415
416 int pa_proplist_isempty(pa_proplist *p) {
417 pa_assert(p);
418
419 return pa_hashmap_isempty(MAKE_HASHMAP(p));
420 }