1 /*
2 deadbeef config file manager
3 http://deadbeef.sourceforge.net
4
5 Copyright (C) 2009-2013 Alexey Yakovenko
6
7 This software is provided 'as-is', without any express or implied
8 warranty. In no event will the authors be held liable for any damages
9 arising from the use of this software.
10
11 Permission is granted to anyone to use this software for any purpose,
12 including commercial applications, and to alter it and redistribute it
13 freely, subject to the following restrictions:
14
15 1. The origin of this software must not be misrepresented; you must not
16 claim that you wrote the original software. If you use this software
17 in a product, an acknowledgment in the product documentation would be
18 appreciated but is not required.
19 2. Altered source versions must be plainly marked as such, and must not be
20 misrepresented as being the original software.
21 3. This notice may not be removed or altered from any source distribution.
22
23 Alexey Yakovenko waker@users.sourceforge.net
24 */
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <limits.h>
33 #include <inttypes.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #if HAVE_SYS_CDEFS_H
37 #include <sys/cdefs.h>
38 #endif
39 #include <sys/stat.h>
40 #include "conf.h"
41 #include "threading.h"
42 #include "common.h"
43
44 #define min(x,y) ((x)<(y)?(x):(y))
45
46 static DB_conf_item_t *conf_items;
47 static int changed = 0;
48 static uintptr_t mutex;
49
50 void
conf_init(void)51 conf_init (void) {
52 mutex = mutex_create ();
53 }
54
55 void
conf_lock(void)56 conf_lock (void) {
57 mutex_lock (mutex);
58 }
59
60 void
conf_unlock(void)61 conf_unlock (void) {
62 mutex_unlock (mutex);
63 }
64
65 void
conf_free(void)66 conf_free (void) {
67 mutex_lock (mutex);
68 DB_conf_item_t *next = NULL;
69 for (DB_conf_item_t *it = conf_items; it; it = next) {
70 next = it->next;
71 conf_item_free (it);
72 }
73 conf_items = NULL;
74 changed = 0;
75 mutex_free (mutex);
76 mutex = 0;
77 }
78
79 int
conf_load(void)80 conf_load (void) {
81 size_t l = strlen (dbconfdir);
82 const char configfile[] = "/config";
83 char fname[l + sizeof(configfile)];
84 memcpy (fname, dbconfdir, l);
85 memcpy (fname + l, configfile, sizeof (configfile));
86 FILE *fp = fopen (fname, "rt");
87 if (!fp) {
88 fprintf (stderr, "failed to load config file\n");
89 fp = fopen (fname, "w+b");
90 if (!fp) {
91 return -1;
92 }
93 fprintf (stderr, "created an empty config\n");
94 fclose (fp);
95 return 0;
96 }
97 conf_lock ();
98 int line = 0;
99
100 fseek (fp, 0, SEEK_END);
101 l = ftell (fp);
102 rewind (fp);
103
104 uint8_t *buffer = malloc (l+1);
105 if (l != fread (buffer, 1, l, fp)) {
106 free (buffer);
107 fprintf (stderr, "failed to read entire config file to memory\n");
108 return -1;
109 }
110 buffer[l] = 0;
111 fclose (fp);
112 fp = NULL;
113
114 uint8_t *str = buffer;
115
116 while (*str) {
117 line++;
118 uint8_t *estr = str;
119 while (*estr >= 0x20) {
120 estr++;
121 }
122 *estr = 0;
123
124 if (str[0] == '#' || str[0] <= 0x20) {
125 str = estr+1;
126 continue;
127 }
128 uint8_t *p = (uint8_t *)str;
129 while (*p && *p > 0x20) {
130 p++;
131 }
132 if (!*p) {
133 fprintf (stderr, "error in config file line %d\n", line);
134 str = estr+1;
135 continue;
136 }
137 *p = 0;
138 p++;
139 // skip whitespace
140 while (*p && *p <= 0x20) {
141 p++;
142 }
143 uint8_t *value = p;
144 // remove trailing trash
145 while (*p && *p >= 0x20) {
146 p++;
147 }
148 *p = 0;
149 // new items are appended, to preserve order
150 conf_set_str ((const char *)str, (const char *)value);
151 str = estr+1;
152 }
153 changed = 0;
154 free (buffer);
155 conf_unlock ();
156 return 0;
157 }
158
159 int
conf_save(void)160 conf_save (void) {
161 char tempfile[PATH_MAX];
162 char str[PATH_MAX];
163 FILE *fp;
164 int err;
165
166 if (!changed) {
167 return 0;
168 }
169
170 snprintf (tempfile, sizeof (tempfile), "%s/config.tmp", dbconfdir);
171 snprintf (str, sizeof (str), "%s/config", dbconfdir);
172
173 conf_lock ();
174 changed = 0;
175 fp = fopen (tempfile, "w+t");
176 if (!fp) {
177 fprintf (stderr, "failed to open config file for writing\n");
178 conf_unlock ();
179 return -1;
180 }
181 for (DB_conf_item_t *it = conf_items; it; it = it->next) {
182 if (fprintf (fp, "%s %s\n", it->key, it->value) < 0) {
183 fprintf (stderr, "failed to write to file %s (%s)\n", tempfile, strerror (errno));
184 fclose (fp);
185 conf_unlock ();
186 return -1;
187 }
188 }
189 fclose (fp);
190 err = rename (tempfile, str);
191 if (err != 0) {
192 fprintf (stderr, "config rename %s -> %s failed: %s\n", tempfile, str, strerror (errno));
193 }
194 else {
195 chmod (str, 0600);
196 }
197 conf_unlock ();
198 return 0;
199 }
200
201 void
conf_item_free(DB_conf_item_t * it)202 conf_item_free (DB_conf_item_t *it) {
203 conf_lock ();
204 if (it) {
205 if (it->key) {
206 free (it->key);
207 }
208 if (it->value) {
209 free (it->value);
210 }
211 free (it);
212 }
213 conf_unlock ();
214 }
215
216 const char *
conf_get_str_fast(const char * key,const char * def)217 conf_get_str_fast (const char *key, const char *def) {
218 for (DB_conf_item_t *it = conf_items; it; it = it->next) {
219 if (!strcasecmp (key, it->key)) {
220 return it->value;
221 }
222 }
223 return def;
224 }
225
226 void
conf_get_str(const char * key,const char * def,char * buffer,int buffer_size)227 conf_get_str (const char *key, const char *def, char *buffer, int buffer_size) {
228 conf_lock ();
229 const char *out = conf_get_str_fast (key, def);
230 if (out) {
231 size_t n = strlen (out)+1;
232 n = min (n, buffer_size);
233 memcpy (buffer, out, n);
234 buffer[buffer_size-1] = 0;
235 }
236 else {
237 *buffer = 0;
238 }
239 conf_unlock ();
240 }
241
242 float
conf_get_float(const char * key,float def)243 conf_get_float (const char *key, float def) {
244 conf_lock ();
245 const char *v = conf_get_str_fast (key, NULL);
246 conf_unlock ();
247 return v ? atof (v) : def;
248 }
249
250 int
conf_get_int(const char * key,int def)251 conf_get_int (const char *key, int def) {
252 conf_lock ();
253 const char *v = conf_get_str_fast (key, NULL);
254 conf_unlock ();
255 return v ? atoi (v) : def;
256 }
257
258 int64_t
conf_get_int64(const char * key,int64_t def)259 conf_get_int64 (const char *key, int64_t def) {
260 conf_lock ();
261 const char *v = conf_get_str_fast (key, NULL);
262 conf_unlock ();
263 return v ? atoll (v) : def;
264 }
265
266 DB_conf_item_t *
conf_find(const char * group,DB_conf_item_t * prev)267 conf_find (const char *group, DB_conf_item_t *prev) {
268 size_t l = strlen (group);
269 for (DB_conf_item_t *it = prev ? prev->next : conf_items; it; it = it->next) {
270 if (!strncasecmp (group, it->key, l)) {
271 return it;
272 }
273 }
274 return NULL;
275 }
276
277 void
conf_set_str(const char * key,const char * val)278 conf_set_str (const char *key, const char *val) {
279 conf_lock ();
280 DB_conf_item_t *prev = NULL;
281 for (DB_conf_item_t *it = conf_items; it; it = it->next) {
282 int cmp = strcasecmp (key, it->key);
283 if (!cmp) {
284 if (!strcmp (it->value, val)) {
285 conf_unlock ();
286 return;
287 }
288 free (it->value);
289 it->value = strdup (val);
290 conf_unlock ();
291 changed = 1;
292 return;
293 }
294 else if (cmp < 0) {
295 break;
296 }
297 prev = it;
298 }
299 if (!val) {
300 conf_unlock ();
301 return;
302 }
303 DB_conf_item_t *it = malloc (sizeof (DB_conf_item_t));
304 memset (it, 0, sizeof (DB_conf_item_t));
305 it->key = strdup (key);
306 it->value = strdup (val);
307 changed = 1;
308 if (prev) {
309 DB_conf_item_t *next = prev->next;
310 prev->next = it;
311 it->next = next;
312 }
313 else {
314 it->next = conf_items;
315 conf_items = it;
316 }
317 conf_unlock ();
318 }
319
320 void
conf_set_int(const char * key,int val)321 conf_set_int (const char *key, int val) {
322 char s[10];
323 snprintf (s, sizeof (s), "%d", val);
324 conf_set_str (key, s);
325 }
326
327 void
conf_set_int64(const char * key,int64_t val)328 conf_set_int64 (const char *key, int64_t val) {
329 char s[20];
330 snprintf (s, sizeof (s), "%"PRId64, val);
331 conf_set_str (key, s);
332 }
333
334 void
conf_set_float(const char * key,float val)335 conf_set_float (const char *key, float val) {
336 char s[10];
337 snprintf (s, sizeof (s), "%0.7f", val);
338 conf_set_str (key, s);
339 }
340
341 int
conf_ischanged(void)342 conf_ischanged (void) {
343 return changed;
344 }
345
346 void
conf_setchanged(int c)347 conf_setchanged (int c) {
348 changed = c;
349 }
350
351 void
conf_remove_items(const char * key)352 conf_remove_items (const char *key) {
353 size_t l = strlen (key);
354 conf_lock ();
355 DB_conf_item_t *prev = NULL;
356 DB_conf_item_t *it;
357 for (it = conf_items; it; prev = it, it = it->next) {
358 if (!strncasecmp (key, it->key, l)) {
359 break;
360 }
361 }
362 DB_conf_item_t *next = NULL;
363 while (it) {
364 next = it->next;
365 conf_item_free (it);
366 it = next;
367 if (!it || strncasecmp (key, it->key, l)) {
368 break;
369 }
370 }
371 if (prev) {
372 prev->next = next;
373 }
374 else {
375 conf_items = next;
376 }
377 conf_unlock ();
378 }
379