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