1 /*
2 * Copyright (C) 2006-2009 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * A generic settings object. (This module should be 100% indepedent of GXemul
29 * and hence easily reusable.) It is basically a tree structure of nodes,
30 * where each node has a name and a few properties. The main property is
31 * a pointer, which can either point to other settings ("sub-settings"),
32 * or to a variable in memory.
33 *
34 * Appart from the pointer, the other properties are a definition of the
35 * type being pointed to (int, int32_t, int64_t, char*, etc), how it should be
36 * presented (e.g. it may be an int value in memory, but it should be
37 * presented as a boolean "true/false" value), and a flag which tells us
38 * whether the setting is directly writable or not.
39 *
40 * If UNSTABLE_DEVEL is defined, then warnings are printed when
41 * settings_destroy() is called if individual settings have not yet been
42 * deleted. (This is to help making sure that code which uses the settings
43 * subsystem correctly un-initializes stuff.)
44 */
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49
50 /* Including misc.h should ONLY be necessary to work around the fact that
51 many systems don't have PRIx64 etc defined. */
52 #include "misc.h"
53
54 #include "settings.h"
55
56
57 struct settings {
58 struct settings *parent;
59 char *name_in_parent;
60
61 int n_settings;
62
63 /*
64 * Each setting has a name, a writable flag, a storage type, a
65 * presentation format, and a pointer.
66 *
67 * For subsettings, the pointer points to the subsettings object;
68 * for other settings, the pointer points to a variable.
69 *
70 * These pointers point to simple linear arrays, containing n_settings
71 * entries each.
72 */
73
74 char **name;
75 int *writable;
76 int *storage_type;
77 int *presentation_format;
78 void **ptr;
79 };
80
81
82 /*
83 * settings_new():
84 *
85 * Create a new settings object. Return value is a pointer to the newly
86 * created object. The function does not return on failure.
87 */
settings_new(void)88 struct settings *settings_new(void)
89 {
90 struct settings *settings;
91
92 CHECK_ALLOCATION(settings = (struct settings *) malloc(sizeof(struct settings)));
93 memset(settings, 0, sizeof(struct settings));
94
95 return settings;
96 }
97
98
99 /*
100 * settings_destroy():
101 *
102 * Frees all resources occupied by a settings object. Also, if this settings
103 * object has a parent, then remove it from the parent.
104 */
settings_destroy(struct settings * settings)105 void settings_destroy(struct settings *settings)
106 {
107 int i;
108
109 if (settings == NULL) {
110 fprintf(stderr, "settings_destroy(): internal error, "
111 "settings = NULL!\n");
112 exit(1);
113 }
114
115 #ifdef UNSTABLE_DEVEL
116 if (settings->n_settings > 0)
117 printf("settings_destroy(): there are remaining settings!\n");
118 #endif
119
120 if (settings->name != NULL) {
121 for (i=0; i<settings->n_settings; i++) {
122 if (settings->name[i] != NULL) {
123 #ifdef UNSTABLE_DEVEL
124 printf("settings_destroy(): setting '%s'"
125 " was not properly deleted before "
126 "exiting!\n", settings->name[i]);
127 #endif
128 free(settings->name[i]);
129 }
130 }
131
132 free(settings->name);
133 } else if (settings->n_settings != 0) {
134 fprintf(stderr, "settings_destroy(): internal error, "
135 "settings->name = NULL, but there were settings?"
136 " (n_settings = %i)\n", settings->n_settings);
137 exit(1);
138 }
139
140 if (settings->writable != NULL)
141 free(settings->writable);
142
143 if (settings->storage_type != NULL)
144 free(settings->storage_type);
145
146 if (settings->presentation_format != NULL)
147 free(settings->presentation_format);
148
149 if (settings->ptr != NULL)
150 free(settings->ptr);
151
152 if (settings->parent != NULL) {
153 settings_remove(settings->parent, settings->name_in_parent);
154 free(settings->name_in_parent);
155 }
156
157 free(settings);
158 }
159
160
161 /*
162 * settings_read():
163 *
164 * Used internally by settings_access() and settings_debugdump().
165 */
settings_read(struct settings * settings,int i,uint64_t * valuep)166 static int settings_read(struct settings *settings, int i, uint64_t *valuep)
167 {
168 *valuep = 0;
169
170 switch (settings->storage_type[i]) {
171 case SETTINGS_TYPE_INT:
172 *valuep = *((int *) settings->ptr[i]);
173 break;
174 case SETTINGS_TYPE_INT8:
175 *valuep = *((int8_t *) settings->ptr[i]);
176 break;
177 case SETTINGS_TYPE_INT16:
178 *valuep = *((int16_t *) settings->ptr[i]);
179 break;
180 case SETTINGS_TYPE_INT32:
181 *valuep = *((int32_t *) settings->ptr[i]);
182 break;
183 case SETTINGS_TYPE_INT64:
184 *valuep = *((int64_t *) settings->ptr[i]);
185 break;
186 case SETTINGS_TYPE_UINT:
187 *valuep = *((uint *) settings->ptr[i]);
188 break;
189 case SETTINGS_TYPE_UINT8:
190 *valuep = *((uint8_t *) settings->ptr[i]);
191 break;
192 case SETTINGS_TYPE_UINT16:
193 *valuep = *((uint16_t *) settings->ptr[i]);
194 break;
195 case SETTINGS_TYPE_UINT32:
196 *valuep = *((uint32_t *) settings->ptr[i]);
197 break;
198 case SETTINGS_TYPE_UINT64:
199 *valuep = *((uint64_t *) settings->ptr[i]);
200 break;
201 case SETTINGS_TYPE_STRING:
202 /* Note: Strings cannot be read like this. */
203 break;
204 default:printf("settings_read(): FATAL ERROR! Unknown storage type"
205 ": %i\n", settings->storage_type[i]);
206 exit(1);
207 }
208
209 return SETTINGS_OK;
210 }
211
212
213 /*
214 * settings_write():
215 *
216 * Used internally by settings_access().
217 */
settings_write(struct settings * settings,int i,uint64_t * valuep)218 static int settings_write(struct settings *settings, int i, uint64_t *valuep)
219 {
220 if (!settings->writable[i])
221 return SETTINGS_READONLY;
222
223 switch (settings->storage_type[i]) {
224 case SETTINGS_TYPE_INT:
225 case SETTINGS_TYPE_UINT:
226 *((int *) settings->ptr[i]) = *valuep;
227 break;
228 case SETTINGS_TYPE_INT8:
229 case SETTINGS_TYPE_UINT8:
230 *((int8_t *) settings->ptr[i]) = *valuep;
231 break;
232 case SETTINGS_TYPE_INT16:
233 case SETTINGS_TYPE_UINT16:
234 *((int16_t *) settings->ptr[i]) = *valuep;
235 break;
236 case SETTINGS_TYPE_INT32:
237 case SETTINGS_TYPE_UINT32:
238 *((int32_t *) settings->ptr[i]) = *valuep;
239 break;
240 case SETTINGS_TYPE_INT64:
241 case SETTINGS_TYPE_UINT64:
242 *((int64_t *) settings->ptr[i]) = *valuep;
243 break;
244 case SETTINGS_TYPE_STRING:
245 /* Note: Strings cannot be read like this. */
246 printf("settings_write(): ERROR! Strings cannot be "
247 "written like this.\n");
248 break;
249 default:printf("settings_read(): FATAL ERROR! Unknown storage type"
250 ": %i\n", settings->storage_type[i]);
251 exit(1);
252 }
253
254 return SETTINGS_OK;
255 }
256
257
258 /*
259 * settings_debugdump():
260 *
261 * Dump settings in a settings object to stdout.
262 * If recurse is non-zero, all subsetting objects are also dumped.
263 */
settings_debugdump(struct settings * settings,const char * prefix,int recurse)264 void settings_debugdump(struct settings *settings, const char *prefix,
265 int recurse)
266 {
267 size_t name_buflen = strlen(prefix) + 100;
268 char *name;
269 int i;
270 uint64_t value = 0;
271
272 CHECK_ALLOCATION(name = (char *) malloc(name_buflen));
273
274 for (i=0; i<settings->n_settings; i++) {
275 snprintf(name, name_buflen, "%s.%s", prefix, settings->name[i]);
276
277 if (settings->storage_type[i] == SETTINGS_TYPE_SUBSETTINGS) {
278 /* Subsettings: */
279 if (recurse)
280 settings_debugdump((struct settings *)settings->ptr[i], name, 1);
281 } else {
282 /* Normal value: */
283 printf("%s = ", name);
284
285 settings_read(settings, i, &value);
286
287 switch (settings->presentation_format[i]) {
288 case SETTINGS_FORMAT_DECIMAL:
289 printf("%" PRIi64, value);
290 break;
291 case SETTINGS_FORMAT_HEX8:
292 printf("0x%02" PRIx8, (int8_t) value);
293 break;
294 case SETTINGS_FORMAT_HEX16:
295 printf("0x%04" PRIx16, (int16_t) value);
296 break;
297 case SETTINGS_FORMAT_HEX32:
298 printf("0x%08" PRIx32, (int32_t) value);
299 break;
300 case SETTINGS_FORMAT_HEX64:
301 printf("0x%016" PRIx64, (int64_t) value);
302 break;
303 case SETTINGS_FORMAT_BOOL:
304 printf(value? "true" : "false");
305 break;
306 case SETTINGS_FORMAT_YESNO:
307 printf(value? "yes" : "no");
308 break;
309 case SETTINGS_FORMAT_STRING:
310 printf("\"%s\"", *((char **)settings->ptr[i]));
311 break;
312 default:printf("FATAL ERROR! Unknown presentation "
313 "format: %i\n",
314 settings->presentation_format[i]);
315 exit(1);
316 }
317
318 if (!settings->writable[i])
319 printf(" (R/O)");
320
321 printf("\n");
322 }
323 }
324
325 free(name);
326 }
327
328
329 /*
330 * settings_add():
331 *
332 * Add a setting to a settings object.
333 */
settings_add(struct settings * settings,const char * name,int writable,int type,int format,void * ptr)334 void settings_add(struct settings *settings, const char *name, int writable,
335 int type, int format, void *ptr)
336 {
337 int i;
338
339 for (i=0; i<settings->n_settings; i++) {
340 if (strcmp(settings->name[i], name) == 0)
341 break;
342 }
343
344 if (i < settings->n_settings) {
345 fprintf(stderr, "settings_add(): name '%s' is already"
346 " in use\n", name);
347 exit(1);
348 }
349
350 settings->n_settings ++;
351
352 CHECK_ALLOCATION(settings->name = (char **) realloc(settings->name,
353 settings->n_settings * sizeof(char *)));
354 CHECK_ALLOCATION(settings->writable = (int *) realloc(settings->writable,
355 settings->n_settings * sizeof(int)));
356 CHECK_ALLOCATION(settings->storage_type = (int *) realloc(
357 settings->storage_type, settings->n_settings * sizeof(int)));
358 CHECK_ALLOCATION(settings->presentation_format = (int *) realloc(settings->
359 presentation_format, settings->n_settings * sizeof(int)));
360 CHECK_ALLOCATION(settings->ptr = (void **) realloc(settings->ptr,
361 settings->n_settings * sizeof(void *)));
362
363 CHECK_ALLOCATION(settings->name[settings->n_settings - 1] =
364 strdup(name));
365 settings->writable[settings->n_settings - 1] = writable;
366 settings->storage_type[settings->n_settings - 1] = type;
367 settings->presentation_format[settings->n_settings - 1] = format;
368 settings->ptr[settings->n_settings - 1] = ptr;
369
370 if (type == SETTINGS_TYPE_SUBSETTINGS) {
371 ((struct settings *)ptr)->parent = settings;
372 CHECK_ALLOCATION( ((struct settings *)ptr)->name_in_parent =
373 strdup(name) );
374 }
375 }
376
377
378 /*
379 * settings_remove():
380 *
381 * Remove a setting from a settings object.
382 */
settings_remove(struct settings * settings,const char * name)383 void settings_remove(struct settings *settings, const char *name)
384 {
385 int i, m;
386
387 for (i=0; i<settings->n_settings; i++) {
388 if (strcmp(settings->name[i], name) == 0)
389 break;
390 }
391
392 if (i >= settings->n_settings) {
393 #ifdef UNSTABLE_DEVEL
394 fprintf(stderr, "settings_remove(): attempting to remove"
395 " non-existant setting '%s'\n", name);
396 #endif
397 return;
398 }
399
400 /* Check subsettings specifically: */
401 if (settings->storage_type[i] == SETTINGS_TYPE_SUBSETTINGS &&
402 settings->ptr[i] != NULL) {
403 struct settings *subsettings = (struct settings *) settings->ptr[i];
404 if (subsettings->n_settings != 0) {
405 fprintf(stderr, "settings_remove(): attempting to "
406 "remove non-emtpy setting '%s'\n", name);
407 fprintf(stderr, "Remaining settings are:\n");
408 for (i=0; i<subsettings->n_settings; i++)
409 fprintf(stderr, "\t%s\n", subsettings->name[i]);
410 exit(1);
411 }
412 }
413
414 settings->n_settings --;
415 free(settings->name[i]);
416
417 m = settings->n_settings - i;
418 if (m == 0)
419 return;
420
421 memmove(&settings->name[i], &settings->name[i+1],
422 m * sizeof(settings->name[0]));
423 memmove(&settings->writable[i], &settings->writable[i+1],
424 m * sizeof(settings->writable[0]));
425 memmove(&settings->storage_type[i], &settings->storage_type[i+1],
426 m * sizeof(settings->storage_type[0]));
427 memmove(&settings->presentation_format[i],
428 &settings->presentation_format[i+1],
429 m * sizeof(settings->presentation_format[0]));
430 memmove(&settings->ptr[i], &settings->ptr[i+1],
431 m * sizeof(settings->ptr[0]));
432 }
433
434
435 /*
436 * settings_remove_all():
437 *
438 * Remove all (level-1) settings from a settings object. By level-1, I mean
439 * all settings that do not contain subsettings.
440 */
settings_remove_all(struct settings * settings)441 void settings_remove_all(struct settings *settings)
442 {
443 while (settings->n_settings > 0)
444 settings_remove(settings, settings->name[0]);
445 }
446
447
448 /*
449 * settings_access():
450 *
451 * Read or write a setting. fullname may be something like "settings.x.y".
452 * When writing a value, valuebuf should point to a uint64_t containing the
453 * new value (note: always a uint64_t). When reading a value, valuebuf should
454 * point to a uint64_t where the value will be stored.
455 *
456 * The return value is one of the following:
457 *
458 * SETTINGS_OK
459 * The value was read or written.
460 *
461 * SETTINGS_NAME_NOT_FOUND
462 * The name was not found in the settings object.
463 *
464 * SETTINGS_READONLY
465 * The name was found, but it was marked as read-only, and
466 * an attempt was made to write to it.
467 */
settings_access(struct settings * settings,const char * fullname,int writeflag,uint64_t * valuep)468 int settings_access(struct settings *settings, const char *fullname,
469 int writeflag, uint64_t *valuep)
470 {
471 int i;
472
473 /* printf("settings_access(fullname='%s')\n", fullname); */
474
475 if (strncmp(fullname, GLOBAL_SETTINGS_NAME".",
476 strlen(GLOBAL_SETTINGS_NAME) + 1) == 0)
477 fullname += strlen(GLOBAL_SETTINGS_NAME) + 1;
478
479 for (i=0; i<settings->n_settings; i++) {
480 size_t settings_name_len = strlen(settings->name[i]);
481
482 if (strncmp(fullname, settings->name[i],
483 settings_name_len) != 0)
484 continue;
485
486 /* Found the correct setting? */
487 if (fullname[settings_name_len] == '\0') {
488 if (writeflag)
489 return settings_write(settings, i, valuep);
490 else
491 return settings_read(settings, i, valuep);
492 }
493
494 /* Found a setting which has sub-settings? */
495 if (fullname[settings_name_len] == '.') {
496 /* Recursive search: */
497 return settings_access(
498 (struct settings *)settings->ptr[i],
499 fullname + settings_name_len + 1,
500 writeflag, valuep);
501 }
502 }
503
504 return SETTINGS_NAME_NOT_FOUND;
505 }
506
507