core: always allow volume setting with single-channel pa_cvolume
[platform/upstream/pulseaudio.git] / src / pulsecore / database-simple.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2009 Nokia Corporation
5   Contact: Maemo Multimedia <multimedia@maemo.org>
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as
9   published by the Free Software Foundation; either version 2.1 of the
10   License, or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public
18   License along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <stdio.h>
33
34 #include <pulse/xmalloc.h>
35 #include <pulsecore/core-util.h>
36 #include <pulsecore/log.h>
37 #include <pulsecore/core-error.h>
38 #include <pulsecore/hashmap.h>
39
40 #include "database.h"
41
42
43 typedef struct simple_data {
44     char *filename;
45     char *tmp_filename;
46     pa_hashmap *map;
47     pa_bool_t read_only;
48 } simple_data;
49
50 typedef struct entry {
51     pa_datum key;
52     pa_datum data;
53 } entry;
54
55 void pa_datum_free(pa_datum *d) {
56     pa_assert(d);
57
58     pa_xfree(d->data);
59     d->data = NULL;
60     d->size = 0;
61 }
62
63 static int compare_func(const void *a, const void *b) {
64     const pa_datum *aa, *bb;
65
66     aa = (const pa_datum*)a;
67     bb = (const pa_datum*)b;
68
69     if (aa->size != bb->size)
70         return aa->size > bb->size ? 1 : -1;
71
72     return memcmp(aa->data, bb->data, aa->size);
73 }
74
75 /* pa_idxset_string_hash_func modified for our use */
76 static unsigned hash_func(const void *p) {
77     const pa_datum *d;
78     unsigned hash = 0;
79     const char *c;
80     unsigned i;
81
82     d = (const pa_datum*)p;
83     c = d->data;
84
85     for (i = 0; i < d->size; i++) {
86         hash = 31 * hash + (unsigned) *c;
87         c++;
88     }
89
90     return hash;
91 }
92
93 static entry* new_entry(const pa_datum *key, const pa_datum *data) {
94     entry *e;
95
96     pa_assert(key);
97     pa_assert(data);
98
99     e = pa_xnew0(entry, 1);
100     e->key.data = key->size > 0 ? pa_xmemdup(key->data, key->size) : NULL;
101     e->key.size = key->size;
102     e->data.data = data->size > 0 ? pa_xmemdup(data->data, data->size) : NULL;
103     e->data.size = data->size;
104     return e;
105 }
106
107 static void free_entry(entry *e) {
108     if (e) {
109         if (e->key.data)
110             pa_xfree(e->key.data);
111         if (e->data.data)
112             pa_xfree(e->data.data);
113         pa_xfree(e);
114     }
115 }
116
117 static int read_uint(FILE *f, uint32_t *res) {
118     size_t items = 0;
119     uint8_t values[4];
120     uint32_t tmp;
121     int i;
122
123     items = fread(&values, sizeof(values), sizeof(uint8_t), f);
124
125     if (feof(f)) /* EOF */
126         return 0;
127
128     if (ferror(f))
129         return -1;
130
131     for (i = 0; i < 4; ++i) {
132         tmp = values[i];
133         *res += (tmp << (i*8));
134     }
135
136     return items;
137 }
138
139 static int read_data(FILE *f, void **data, ssize_t *length) {
140     size_t items = 0;
141     uint32_t data_len = 0;
142
143     pa_assert(f);
144
145     *data = NULL;
146     *length = 0;
147
148     if ((items = read_uint(f, &data_len)) <= 0)
149         return -1;
150
151     if (data_len > 0) {
152         *data = pa_xmalloc0(data_len);
153         items = fread(*data, data_len, 1, f);
154
155         if (feof(f)) /* EOF */
156             goto reset;
157
158         if (ferror(f))
159             goto reset;
160
161         *length = data_len;
162
163     } else { /* no data? */
164         return -1;
165     }
166
167     return 0;
168
169 reset:
170     pa_xfree(*data);
171     *data = NULL;
172     *length = 0;
173     return -1;
174 }
175
176 static int fill_data(simple_data *db, FILE *f) {
177     pa_datum key;
178     pa_datum data;
179     void *d = NULL;
180     ssize_t l = 0;
181     pa_bool_t append = FALSE;
182     enum { FIELD_KEY = 0, FIELD_DATA } field = FIELD_KEY;
183
184     pa_assert(db);
185     pa_assert(db->map);
186
187     errno = 0;
188
189     key.size = 0;
190     key.data = NULL;
191
192     while (!read_data(f, &d, &l)) {
193
194         switch (field) {
195             case FIELD_KEY:
196                 key.data = d;
197                 key.size = l;
198                 field = FIELD_DATA;
199                 break;
200             case FIELD_DATA:
201                 data.data = d;
202                 data.size = l;
203                 append = TRUE;
204                 break;
205         }
206
207         if (append) {
208             entry *e = pa_xnew0(entry, 1);
209             e->key.data = key.data;
210             e->key.size = key.size;
211             e->data.data = data.data;
212             e->data.size = data.size;
213             pa_hashmap_put(db->map, &e->key, e);
214             append = FALSE;
215             field = FIELD_KEY;
216         }
217     }
218
219     if (ferror(f)) {
220         pa_log_warn("read error. %s", pa_cstrerror(errno));
221         pa_database_clear((pa_database*)db);
222     }
223
224     if (field == FIELD_DATA && d)
225         pa_xfree(d);
226
227     return pa_hashmap_size(db->map);
228 }
229
230 pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
231     FILE *f;
232     char *path;
233     simple_data *db;
234
235     pa_assert(fn);
236
237     path = pa_sprintf_malloc("%s."CANONICAL_HOST".simple", fn);
238     errno = 0;
239
240     f = fopen(path, "r");
241
242     if (f || errno == ENOENT) { /* file not found is ok */
243         db = pa_xnew0(simple_data, 1);
244         db->map = pa_hashmap_new(hash_func, compare_func);
245         db->filename = pa_xstrdup(path);
246         db->tmp_filename = pa_sprintf_malloc(".%s.tmp", db->filename);
247         db->read_only = !for_write;
248
249         if (f) {
250             fill_data(db, f);
251             fclose(f);
252         }
253     } else {
254         if (errno == 0)
255             errno = EIO;
256         db = NULL;
257     }
258
259     pa_xfree(path);
260
261     return (pa_database*) db;
262 }
263
264 void pa_database_close(pa_database *database) {
265     simple_data *db = (simple_data*)database;
266     pa_assert(db);
267
268     pa_database_sync(database);
269     pa_database_clear(database);
270     pa_xfree(db->filename);
271     pa_xfree(db->tmp_filename);
272     pa_hashmap_free(db->map, NULL, NULL);
273     pa_xfree(db);
274 }
275
276 pa_datum* pa_database_get(pa_database *database, const pa_datum *key, pa_datum* data) {
277     simple_data *db = (simple_data*)database;
278     entry *e;
279
280     pa_assert(db);
281     pa_assert(key);
282     pa_assert(data);
283
284     e = pa_hashmap_get(db->map, key);
285
286     if (!e)
287         return NULL;
288
289     data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
290     data->size = e->data.size;
291
292     return data;
293 }
294
295 int pa_database_set(pa_database *database, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite) {
296     simple_data *db = (simple_data*)database;
297     entry *e;
298     int ret = 0;
299
300     pa_assert(db);
301     pa_assert(key);
302     pa_assert(data);
303
304     if (db->read_only)
305         return -1;
306
307     e = new_entry(key, data);
308
309     if (pa_hashmap_put(db->map, &e->key, e) < 0) {
310         /* entry with same key exists in hashmap */
311         entry *r;
312         if (overwrite) {
313             r = pa_hashmap_remove(db->map, key);
314             pa_hashmap_put(db->map, &e->key, e);
315         } else {
316             /* wont't overwrite, so clean new entry */
317             r = e;
318             ret = -1;
319         }
320
321         free_entry(r);
322     }
323
324     return ret;
325 }
326
327 int pa_database_unset(pa_database *database, const pa_datum *key) {
328     simple_data *db = (simple_data*)database;
329     entry *e;
330
331     pa_assert(db);
332     pa_assert(key);
333
334     e = pa_hashmap_remove(db->map, key);
335     if (!e)
336         return -1;
337
338     free_entry(e);
339
340     return 0;
341 }
342
343 int pa_database_clear(pa_database *database) {
344     simple_data *db = (simple_data*)database;
345     entry *e;
346
347     pa_assert(db);
348
349     while ((e = pa_hashmap_steal_first(db->map)))
350         free_entry(e);
351
352     return 0;
353 }
354
355 signed pa_database_size(pa_database *database) {
356     simple_data *db = (simple_data*)database;
357     pa_assert(db);
358
359     return (signed) pa_hashmap_size(db->map);
360 }
361
362 pa_datum* pa_database_first(pa_database *database, pa_datum *key, pa_datum *data) {
363     simple_data *db = (simple_data*)database;
364     entry *e;
365
366     pa_assert(db);
367     pa_assert(key);
368
369     e = pa_hashmap_first(db->map);
370
371     if (!e)
372         return NULL;
373
374     key->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
375     key->size = e->key.size;
376
377     if (data) {
378         data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
379         data->size = e->data.size;
380     }
381
382     return key;
383 }
384
385 pa_datum* pa_database_next(pa_database *database, const pa_datum *key, pa_datum *next, pa_datum *data) {
386     simple_data *db = (simple_data*)database;
387     entry *e;
388     entry *search;
389     void *state;
390     pa_bool_t pick_now;
391
392     pa_assert(db);
393     pa_assert(next);
394
395     if (!key)
396         return pa_database_first(database, next, data);
397
398     search = pa_hashmap_get(db->map, key);
399
400     state = NULL;
401     pick_now = FALSE;
402
403     while ((e = pa_hashmap_iterate(db->map, &state, NULL))) {
404         if (pick_now)
405             break;
406
407         if (search == e)
408             pick_now = TRUE;
409     }
410
411     if (!pick_now || !e)
412         return NULL;
413
414     next->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
415     next->size = e->key.size;
416
417     if (data) {
418         data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
419         data->size = e->data.size;
420     }
421
422     return next;
423 }
424
425 static int write_uint(FILE *f, const uint32_t num) {
426     size_t items;
427     uint8_t values[4];
428     int i;
429     errno = 0;
430
431     for (i = 0; i < 4; i++)
432          values[i] = (num >> (i*8)) & 0xFF;
433
434     items = fwrite(&values, sizeof(values), sizeof(uint8_t), f);
435
436     if (ferror(f))
437         return -1;
438
439     return items;
440 }
441
442 static int write_data(FILE *f, void *data, const size_t length) {
443     size_t items;
444     uint32_t len;
445
446     len = length;
447     if ((items = write_uint(f, len)) <= 0)
448         return -1;
449
450     items = fwrite(data, length, 1, f);
451
452     if (ferror(f) || items != 1)
453         return -1;
454
455     return 0;
456 }
457
458 static int write_entry(FILE *f, const entry *e) {
459     pa_assert(f);
460     pa_assert(e);
461
462     if (write_data(f, e->key.data, e->key.size) < 0)
463         return -1;
464     if (write_data(f, e->data.data, e->data.size) < 0)
465         return -1;
466
467     return 0;
468 }
469
470 int pa_database_sync(pa_database *database) {
471     simple_data *db = (simple_data*)database;
472     FILE *f;
473     void *state;
474     entry *e;
475
476     pa_assert(db);
477
478     if (db->read_only)
479         return 0;
480
481     errno = 0;
482
483     f = fopen(db->tmp_filename, "w");
484
485     if (!f)
486         goto fail;
487
488     state = NULL;
489     while((e = pa_hashmap_iterate(db->map, &state, NULL))) {
490         if (write_entry(f, e) < 0) {
491             pa_log_warn("error while writing to file. %s", pa_cstrerror(errno));
492             goto fail;
493         }
494     }
495
496     fclose(f);
497     f = NULL;
498
499     if (rename(db->tmp_filename, db->filename) < 0) {
500         pa_log_warn("error while renaming file. %s", pa_cstrerror(errno));
501         goto fail;
502     }
503
504     return 0;
505
506 fail:
507     if (f)
508         fclose(f);
509     return -1;
510 }