xref: /haiku/src/system/libroot/os/driver_settings.cpp (revision a2d9c45398ebcab924c89b2d4961bbebc2aeb3d6)
1 /*
2  * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de.
3  * This file may be used under the terms of the MIT License.
4  */
5 
6 /*!	\brief Implements the driver settings API
7 	This file is used by three different components with different needs:
8 	  1) the boot loader
9 		Buffers a list of settings files to move over to the kernel - the
10 		actual buffering is located in the boot loader directly, though.
11 		Creates driver_settings structures out of those on demand only.
12 	  2) the kernel
13 		Maintains a list of settings so that no disk access is required
14 		for known settings (such as those passed over from the boot
15 		loader).
16 	  3) libroot.so
17 		Exports the parser to userland applications, so that they can
18 		easily make use of driver_settings styled files.
19 
20 	The file has to be recompiled for every component separately, so that
21 	it properly exports the required functionality (which is specified by
22 	_BOOT_MODE for the boot loader, and _KERNEL_MODE for the kernel).
23 */
24 
25 // The boot loader is compiled with kernel rules, but we want to explicitely
26 // differentiate between the two here.
27 #ifdef _BOOT_MODE
28 #	undef _KERNEL_MODE
29 #endif
30 
31 #include <directories.h>
32 #include <driver_settings.h>
33 #include <FindDirectory.h>
34 #include <OS.h>
35 
36 #ifdef _KERNEL_MODE
37 #	include <KernelExport.h>
38 #	include <util/list.h>
39 #	include <lock.h>
40 #	include <kdriver_settings.h>
41 #	include <kernel.h>
42 #	include <boot/kernel_args.h>
43 #	include <boot_device.h>
44 #endif
45 #ifdef _BOOT_MODE
46 #	include <boot/kernel_args.h>
47 #	include <boot/stage2.h>
48 #else
49 #	include <find_directory_private.h>
50 #endif
51 
52 #include <stdlib.h>
53 #include <string.h>
54 #include <strings.h>
55 #include <unistd.h>
56 #include <fcntl.h>
57 #include <ctype.h>
58 
59 #ifndef B_BUFFER_OVERFLOW
60 #	define B_BUFFER_OVERFLOW B_ERROR
61 #endif
62 
63 #define SETTINGS_DIRECTORY "/kernel/drivers/"
64 #define SETTINGS_MAGIC		'DrvS'
65 
66 // Those maximum values are independent from the implementation - they
67 // have been chosen to make the code more robust against bad files
68 #define MAX_SETTINGS_SIZE	32768
69 #define MAX_SETTINGS_LEVEL	8
70 
71 #define CONTINUE_PARAMETER	1
72 #define NO_PARAMETER 2
73 
74 
75 typedef struct settings_handle {
76 #ifdef _KERNEL_MODE
77 	list_link	link;
78 	char		name[B_OS_NAME_LENGTH];
79 	int32		ref_count;
80 		// A negative ref_count means the node is not reference counted and not
81 		// stored in the list.
82 #endif
83 	int32		magic;
84 	struct		driver_settings settings;
85 	char		*text;
86 } settings_handle;
87 
88 
89 enum assignment_mode {
90 	NO_ASSIGNMENT,
91 	ALLOW_ASSIGNMENT,
92 	IGNORE_ASSIGNMENT
93 };
94 
95 
96 #ifdef _KERNEL_MODE
97 static struct list sHandles;
98 static mutex sLock = MUTEX_INITIALIZER("driver settings");
99 #endif
100 
101 
102 //	#pragma mark - private functions
103 
104 
105 /*!
106 	\brief Returns true for any characters that separate parameters
107 
108 	Those characters are ignored in the input stream and won't be added
109 	to any words.
110 */
111 static inline bool
112 is_parameter_separator(char c)
113 {
114 	return c == '\n' || c == ';';
115 }
116 
117 
118 /*! Indicates if "c" begins a new word or not.
119 */
120 static inline bool
121 is_word_break(char c)
122 {
123 	return isspace(c) || is_parameter_separator(c);
124 }
125 
126 
127 static inline bool
128 check_handle(settings_handle *handle)
129 {
130 	if (handle == NULL
131 		|| handle->magic != SETTINGS_MAGIC)
132 		return false;
133 
134 	return true;
135 }
136 
137 
138 static driver_parameter *
139 get_parameter(settings_handle *handle, const char *name)
140 {
141 	int32 i;
142 	for (i = handle->settings.parameter_count; i-- > 0;) {
143 		if (!strcmp(handle->settings.parameters[i].name, name))
144 			return &handle->settings.parameters[i];
145 	}
146 	return NULL;
147 }
148 
149 
150 /*!
151 	Returns the next word in the input buffer passed in via "_pos" - if
152 	this function returns, it will bump the input position after the word.
153 	It automatically cares about quoted strings and escaped characters.
154 	If "allowNewLine" is true, it reads over comments to get to the next
155 	word.
156 	Depending on the "assignmentMode" parameter, the '=' sign is either
157 	used as a work break, or not.
158 	The input buffer will be changed to contain the word without quotes
159 	or escaped characters and adds a terminating NULL byte. The "_word"
160 	parameter will be set to the beginning of the word.
161 	If the word is followed by a newline it will return B_OK, if white
162 	spaces follows, it will return CONTINUE_PARAMETER.
163 */
164 static status_t
165 get_word(char **_pos, char **_word, int32 assignmentMode, bool allowNewLine)
166 {
167 	char *pos = *_pos;
168 	char quoted = 0;
169 	bool newLine = false, end = false;
170 	int escaped = 0;
171 	bool charEscaped = false;
172 
173 	// Skip any white space and comments
174 	while (pos[0]
175 		&& ((allowNewLine && (isspace(pos[0]) || is_parameter_separator(pos[0])
176 				|| pos[0] == '#'))
177 			|| (!allowNewLine && (pos[0] == '\t' || pos[0] == ' '))
178 			|| (assignmentMode == ALLOW_ASSIGNMENT && pos[0] == '='))) {
179 		// skip any comment lines
180 		if (pos[0] == '#') {
181 			while (pos[0] && pos[0] != '\n')
182 				pos++;
183 		}
184 		pos++;
185 	}
186 
187 	if (pos[0] == '}' || pos[0] == '\0') {
188 		// if we just read some white space before an end of a
189 		// parameter, this is just no parameter at all
190 		*_pos = pos;
191 		return NO_PARAMETER;
192 	}
193 
194 	// Read in a word - might contain escaped (\) spaces, or it
195 	// might also be quoted (" or ').
196 
197 	if (pos[0] == '"' || pos[0] == '\'') {
198 		quoted = pos[0];
199 		pos++;
200 	}
201 	*_word = pos;
202 
203 	while (pos[0]) {
204 		if (charEscaped)
205 			charEscaped = false;
206 		else if (pos[0] == '\\') {
207 			charEscaped = true;
208 			escaped++;
209 		} else if ((!quoted && (is_word_break(pos[0])
210 				|| (assignmentMode != IGNORE_ASSIGNMENT && pos[0] == '=')))
211 			|| (quoted && pos[0] == quoted))
212 			break;
213 
214 		pos++;
215 	}
216 
217 	// "String exceeds line" - missing end quote
218 	if (quoted && pos[0] != quoted)
219 		return B_BAD_DATA;
220 
221 	// last character is a backslash
222 	if (charEscaped)
223 		return B_BAD_DATA;
224 
225 	end = pos[0] == '\0';
226 	newLine = is_parameter_separator(pos[0]) || end;
227 	pos[0] = '\0';
228 
229 	// Correct name if there were any escaped characters
230 	if (escaped) {
231 		char *word = *_word;
232 		int offset = 0;
233 		while (word <= pos) {
234 			if (word[0] == '\\') {
235 				offset--;
236 				word++;
237 			}
238 			word[offset] = word[0];
239 			word++;
240 		}
241 	}
242 
243 	if (end) {
244 		*_pos = pos;
245 		return B_OK;
246 	}
247 
248 	// Scan for next beginning word, open brackets, or comment start
249 
250 	pos++;
251 	while (true) {
252 		*_pos = pos;
253 		if (!pos[0])
254 			return B_NO_ERROR;
255 
256 		if (is_parameter_separator(pos[0])) {
257 			// an open bracket '{' could follow after the first
258 			// newline, but not later
259 			if (newLine)
260 				return B_NO_ERROR;
261 
262 			newLine = true;
263 		} else if (pos[0] == '{' || pos[0] == '}' || pos[0] == '#')
264 			return B_NO_ERROR;
265 		else if (!isspace(pos[0]))
266 			return newLine ? B_NO_ERROR : CONTINUE_PARAMETER;
267 
268 		pos++;
269 	}
270 }
271 
272 
273 static status_t
274 parse_parameter(struct driver_parameter *parameter, char **_pos, int32 level)
275 {
276 	char *pos = *_pos;
277 	status_t status;
278 
279 	// initialize parameter first
280 	memset(parameter, 0, sizeof(struct driver_parameter));
281 
282 	status = get_word(&pos, &parameter->name, NO_ASSIGNMENT, true);
283 	if (status == CONTINUE_PARAMETER) {
284 		while (status == CONTINUE_PARAMETER) {
285 			char **newArray, *value;
286 			status = get_word(&pos, &value, parameter->value_count == 0
287 				? ALLOW_ASSIGNMENT : IGNORE_ASSIGNMENT, false);
288 			if (status < B_OK)
289 				break;
290 
291 			// enlarge value array and save the value
292 
293 			newArray = (char**)realloc(parameter->values,
294 				(parameter->value_count + 1) * sizeof(char *));
295 			if (newArray == NULL)
296 				return B_NO_MEMORY;
297 
298 			parameter->values = newArray;
299 			parameter->values[parameter->value_count++] = value;
300 		}
301 	}
302 
303 	*_pos = pos;
304 	return status;
305 }
306 
307 
308 static status_t
309 parse_parameters(struct driver_parameter **_parameters, int *_count,
310 	char **_pos, int32 level)
311 {
312 	if (level > MAX_SETTINGS_LEVEL)
313 		return B_LINK_LIMIT;
314 
315 	while (true) {
316 		struct driver_parameter parameter;
317 		struct driver_parameter *newArray;
318 		status_t status;
319 
320 		status = parse_parameter(&parameter, _pos, level);
321 		if (status < B_OK)
322 			return status;
323 
324 		if (status != NO_PARAMETER) {
325 			driver_parameter *newParameter;
326 
327 			newArray = (driver_parameter*)realloc(*_parameters, (*_count + 1)
328 				* sizeof(struct driver_parameter));
329 			if (newArray == NULL)
330 				return B_NO_MEMORY;
331 
332 			memcpy(&newArray[*_count], &parameter, sizeof(struct driver_parameter));
333 			newParameter = &newArray[*_count];
334 
335 			*_parameters = newArray;
336 			(*_count)++;
337 
338 			// check for level beginning and end
339 			if (**_pos == '{') {
340 				// if we go a level deeper, just start all over again...
341 				(*_pos)++;
342 				status = parse_parameters(&newParameter->parameters,
343 							&newParameter->parameter_count, _pos, level + 1);
344 				if (status < B_OK)
345 					return status;
346 			}
347 		}
348 
349 		if ((**_pos == '}' && level > 0)
350 			|| (**_pos == '\0' && level == 0)) {
351 			// take the closing bracket from the stack
352 			(*_pos)++;
353 			return B_OK;
354 		}
355 
356 		// obviously, something has gone wrong
357 		if (**_pos == '}' || **_pos == '\0')
358 			return B_ERROR;
359 	}
360 }
361 
362 
363 static status_t
364 parse_settings(settings_handle *handle)
365 {
366 	char *text = handle->text;
367 
368 	memset(&handle->settings, 0, sizeof(struct driver_settings));
369 
370 	// empty settings are allowed
371 	if (text == NULL)
372 		return B_OK;
373 
374 	return parse_parameters(&handle->settings.parameters,
375 		&handle->settings.parameter_count, &text, 0);
376 }
377 
378 
379 static void
380 free_parameter(struct driver_parameter *parameter)
381 {
382 	int32 i;
383 	for (i = parameter->parameter_count; i-- > 0;)
384 		free_parameter(&parameter->parameters[i]);
385 
386 	free(parameter->parameters);
387 	free(parameter->values);
388 }
389 
390 
391 static void
392 free_settings(settings_handle *handle)
393 {
394 	int32 i;
395 	for (i = handle->settings.parameter_count; i-- > 0;)
396 		free_parameter(&handle->settings.parameters[i]);
397 
398 	free(handle->settings.parameters);
399 	free(handle->text);
400 	free(handle);
401 }
402 
403 
404 static settings_handle *
405 new_settings(char *buffer, const char *driverName)
406 {
407 	settings_handle *handle = (settings_handle*)malloc(sizeof(settings_handle));
408 	if (handle == NULL)
409 		return NULL;
410 
411 	handle->magic = SETTINGS_MAGIC;
412 	handle->text = buffer;
413 
414 #ifdef _KERNEL_MODE
415 	if (driverName != NULL) {
416 		handle->ref_count = 1;
417 		strlcpy(handle->name, driverName, sizeof(handle->name));
418 	} else {
419 		handle->ref_count = -1;
420 		handle->name[0] = 0;
421 	}
422 #endif
423 
424 	if (parse_settings(handle) == B_OK)
425 		return handle;
426 
427 	free(handle);
428 	return NULL;
429 }
430 
431 
432 static settings_handle *
433 load_driver_settings_from_file(int file, const char *driverName)
434 {
435 	struct stat stat;
436 
437 	// Allocate a buffer and read the whole file into it.
438 	// We will keep this buffer in memory, until the settings
439 	// are unloaded.
440 	// The driver_parameter::name field will point directly
441 	// to this buffer.
442 
443 	if (fstat(file, &stat) < B_OK)
444 		return NULL;
445 
446 	if (stat.st_size > B_OK && stat.st_size < MAX_SETTINGS_SIZE) {
447 		char *text = (char *)malloc(stat.st_size + 1);
448 		if (text != NULL && read(file, text, stat.st_size) == stat.st_size) {
449 			settings_handle *handle;
450 
451 			text[stat.st_size] = '\0';
452 				// make sure the string is null terminated
453 				// to avoid misbehaviour
454 
455 			handle = new_settings(text, driverName);
456 			if (handle != NULL) {
457 				// everything went fine!
458 				return handle;
459 			}
460 
461 			free(handle);
462 		}
463 		// "text" might be NULL here, but that's allowed
464 		free(text);
465 	}
466 
467 	return NULL;
468 }
469 
470 
471 static bool
472 put_string(char **_buffer, ssize_t *_bufferSize, char *string)
473 {
474 	size_t length, reserved, quotes;
475 	char *buffer = *_buffer, c;
476 	bool quoted;
477 
478 	if (string == NULL)
479 		return true;
480 
481 	for (length = reserved = quotes = 0; (c = string[length]) != '\0'; length++) {
482 		if (c == '"')
483 			quotes++;
484 		else if (is_word_break(c))
485 			reserved++;
486 	}
487 	quoted = reserved || quotes;
488 
489 	// update _bufferSize in any way, so that we can chain several
490 	// of these calls without having to check the return value
491 	// everytime
492 	*_bufferSize -= length + (quoted ? 2 + quotes : 0);
493 
494 	if (*_bufferSize <= 0)
495 		return false;
496 
497 	if (quoted)
498 		*(buffer++) = '"';
499 
500 	for (;(c = string[0]) != '\0'; string++) {
501 		if (c == '"')
502 			*(buffer++) = '\\';
503 
504 		*(buffer++) = c;
505 	}
506 
507 	if (quoted)
508 		*(buffer++) = '"';
509 
510 	buffer[0] = '\0';
511 
512 	// update the buffer position
513 	*_buffer = buffer;
514 
515 	return true;
516 }
517 
518 
519 static bool
520 put_chars(char **_buffer, ssize_t *_bufferSize, const char *chars)
521 {
522 	char *buffer = *_buffer;
523 	size_t length;
524 
525 	if (chars == NULL)
526 		return true;
527 
528 	length = strlen(chars);
529 	*_bufferSize -= length;
530 
531 	if (*_bufferSize <= 0)
532 		return false;
533 
534 	memcpy(buffer, chars, length);
535 	buffer += length;
536 	buffer[0] = '\0';
537 
538 	// update the buffer position
539 	*_buffer = buffer;
540 
541 	return true;
542 }
543 
544 
545 static bool
546 put_char(char **_buffer, ssize_t *_bufferSize, char c)
547 {
548 	char *buffer = *_buffer;
549 
550 	*_bufferSize -= 1;
551 
552 	if (*_bufferSize <= 0)
553 		return false;
554 
555 	buffer[0] = c;
556 	buffer[1] = '\0';
557 
558 	// update the buffer position
559 	*_buffer = buffer + 1;
560 
561 	return true;
562 }
563 
564 
565 static void
566 put_level_space(char **_buffer, ssize_t *_bufferSize, int32 level)
567 {
568 	while (level-- > 0)
569 		put_char(_buffer, _bufferSize, '\t');
570 }
571 
572 
573 static void
574 put_parameter(char **_buffer, ssize_t *_bufferSize,
575 	struct driver_parameter *parameter, int32 level, bool flat)
576 {
577 	int32 i;
578 
579 	if (!flat)
580 		put_level_space(_buffer, _bufferSize, level);
581 
582 	put_string(_buffer, _bufferSize, parameter->name);
583 	if (flat && parameter->value_count > 0)
584 		put_chars(_buffer, _bufferSize, " =");
585 
586 	for (i = 0; i < parameter->value_count; i++) {
587 		put_char(_buffer, _bufferSize, ' ');
588 		put_string(_buffer, _bufferSize, parameter->values[i]);
589 	}
590 
591 	if (parameter->parameter_count > 0) {
592 		put_chars(_buffer, _bufferSize, " {");
593 		if (!flat)
594 			put_char(_buffer, _bufferSize, '\n');
595 
596 		for (i = 0; i < parameter->parameter_count; i++) {
597 			put_parameter(_buffer, _bufferSize, &parameter->parameters[i],
598 				level + 1, flat);
599 
600 			if (parameter->parameters[i].parameter_count == 0)
601 				put_chars(_buffer, _bufferSize, flat ? "; " : "\n");
602 		}
603 
604 		if (!flat)
605 			put_level_space(_buffer, _bufferSize, level);
606 		put_chars(_buffer, _bufferSize, flat ? "}" : "}\n");
607 	}
608 }
609 
610 
611 //	#pragma mark - Kernel only functions
612 
613 
614 #ifdef _KERNEL_MODE
615 static settings_handle *
616 find_driver_settings(const char *name)
617 {
618 	settings_handle *handle = NULL;
619 
620 	ASSERT_LOCKED_MUTEX(&sLock);
621 
622 	while ((handle = (settings_handle*)list_get_next_item(&sHandles, handle))
623 			!= NULL) {
624 		if (!strcmp(handle->name, name))
625 			return handle;
626 	}
627 
628 	return NULL;
629 }
630 
631 
632 status_t
633 driver_settings_init(kernel_args *args)
634 {
635 	struct driver_settings_file *settings = args->driver_settings;
636 
637 	// Move the preloaded driver settings over to the kernel
638 
639 	list_init(&sHandles);
640 
641 	while (settings != NULL) {
642 		settings_handle *handle
643 			= (settings_handle*)malloc(sizeof(settings_handle));
644 		if (handle == NULL)
645 			return B_NO_MEMORY;
646 
647 		if (settings->size != 0) {
648 			handle->text = (char*)malloc(settings->size + 1);
649 			if (handle->text == NULL) {
650 				free(handle);
651 				return B_NO_MEMORY;
652 			}
653 
654 			memcpy(handle->text, settings->buffer, settings->size);
655 			handle->text[settings->size] = '\0';
656 				// null terminate the buffer
657 		} else
658 			handle->text = NULL;
659 
660 		strlcpy(handle->name, settings->name, sizeof(handle->name));
661 		handle->settings.parameters = NULL;
662 		handle->settings.parameter_count = 0;
663 		handle->magic = 0;
664 			// this triggers parsing the settings when they are actually used
665 
666 		if (!strcmp(handle->name, B_SAFEMODE_DRIVER_SETTINGS)) {
667 			// These settings cannot be reloaded, so we better don't throw
668 			// them away.
669 			handle->ref_count = 1;
670 		} else
671 			handle->ref_count = 0;
672 
673 		list_add_item(&sHandles, handle);
674 
675 		settings = settings->next;
676 	}
677 
678 	return B_OK;
679 }
680 #endif
681 
682 
683 //	#pragma mark - public API
684 
685 
686 status_t
687 unload_driver_settings(void *_handle)
688 {
689 	settings_handle *handle = (settings_handle *)_handle;
690 	if (!check_handle(handle))
691 		return B_BAD_VALUE;
692 
693 #ifdef _KERNEL_MODE
694 	mutex_lock(&sLock);
695 
696 	if (handle->ref_count > 0) {
697 		if (--handle->ref_count == 0 && gBootDevice > 0) {
698 			// don't unload an handle when /boot is not available
699 			list_remove_link(&handle->link);
700 		} else
701 			handle = NULL;
702 	}
703 	mutex_unlock(&sLock);
704 #endif
705 
706 	if (handle != NULL)
707 		free_settings(handle);
708 
709 	return B_OK;
710 }
711 
712 
713 void *
714 load_driver_settings(const char *driverName)
715 {
716 	settings_handle *handle;
717 	int file = -1;
718 
719 	if (driverName == NULL)
720 		return NULL;
721 
722 #ifdef _KERNEL_MODE
723 	// see if we already have these settings loaded
724 	mutex_lock(&sLock);
725 	handle = find_driver_settings(driverName);
726 	if (handle != NULL && handle->ref_count == 0 && gBootDevice > 0) {
727 		// A handle with a zero ref_count should be unloaded if /boot is
728 		// available.
729 		list_remove_link(&handle->link);
730 		free_settings(handle);
731 	} else if (handle != NULL) {
732 		handle->ref_count++;
733 
734 		// we got it, now let's see if it already has been parsed
735 		if (handle->magic != SETTINGS_MAGIC) {
736 			handle->magic = SETTINGS_MAGIC;
737 
738 			if (parse_settings(handle) != B_OK) {
739 				// no valid settings, let's cut down its memory requirements
740 				free(handle->text);
741 				handle->text = NULL;
742 				handle = NULL;
743 			}
744 		}
745 		mutex_unlock(&sLock);
746 		return handle;
747 	}
748 
749 	// we are allowed to call the driver settings pretty early in the boot process
750 	if (gKernelStartup) {
751 		mutex_unlock(&sLock);
752 		return NULL;
753 	}
754 #endif	// _KERNEL_MODE
755 #ifdef _BOOT_MODE
756 	// see if we already have these settings loaded
757 	{
758 		struct driver_settings_file *settings = gKernelArgs.driver_settings;
759 		while (settings != NULL) {
760 			if (!strcmp(settings->name, driverName)) {
761 				// we have it - since the buffer is clobbered, we have to
762 				// copy its contents, though
763 				char *text = (char*)malloc(settings->size + 1);
764 				if (text == NULL)
765 					return NULL;
766 
767 				memcpy(text, settings->buffer, settings->size + 1);
768 				return new_settings(text, driverName);
769 			}
770 			settings = settings->next;
771 		}
772 	}
773 #endif	// _BOOT_MODE
774 
775 	// open the settings from the standardized location
776 	if (driverName[0] != '/') {
777 		char path[B_FILE_NAME_LENGTH + 64];
778 
779 #ifdef _BOOT_MODE
780 		strcpy(path, kUserSettingsDirectory);
781 #else
782 		// TODO: use B_SYSTEM_SETTINGS_DIRECTORY instead!
783 		if (__find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, path,
784 				sizeof(path)) == B_OK)
785 #endif
786 		{
787 			strlcat(path, SETTINGS_DIRECTORY, sizeof(path));
788 			strlcat(path, driverName, sizeof(path));
789 		}
790 
791 		file = open(path, O_RDONLY);
792 	} else
793 		file = open(driverName, O_RDONLY);
794 
795 	if (file < B_OK) {
796 #ifdef _KERNEL_MODE
797 		mutex_unlock(&sLock);
798 #endif
799 		return NULL;
800 	}
801 
802 	handle = load_driver_settings_from_file(file, driverName);
803 
804 #ifdef _KERNEL_MODE
805 	if (handle != NULL)
806 		list_add_item(&sHandles, handle);
807 	mutex_unlock(&sLock);
808 #endif
809 
810 	close(file);
811 	return (void *)handle;
812 }
813 
814 
815 void*
816 load_driver_settings_file(int fd)
817 {
818 	return load_driver_settings_from_file(fd, NULL);
819 }
820 
821 
822 /*!
823 	Returns a new driver_settings handle that has the parsed contents
824 	of the passed string.
825 	You can get an empty driver_settings object when you pass NULL as
826 	the "settingsString" parameter.
827 */
828 void *
829 parse_driver_settings_string(const char *settingsString)
830 {
831 	char *text = NULL;
832 	if (settingsString != NULL) {
833 		// we simply copy the whole string to use it as our internal buffer
834 		text = strdup(settingsString);
835 		if (text == NULL)
836 			return NULL;
837 	}
838 
839 	settings_handle *handle = new_settings(text, NULL);
840 	if (handle == NULL)
841 		free(text);
842 	return handle;
843 }
844 
845 
846 /*!
847 	This function prints out a driver settings structure to a human
848 	readable string.
849 	It's either in standard style or the single line style speficied
850 	by the "flat" parameter.
851 	If the buffer is too small to hold the string, B_BUFFER_OVERFLOW
852 	is returned, and the needed amount of bytes if placed in the
853 	"_bufferSize" parameter.
854 	If the "handle" parameter is not a valid driver settings handle, or
855 	the "buffer" parameter is NULL, B_BAD_VALUE is returned.
856 */
857 status_t
858 get_driver_settings_string(void *_handle, char *buffer, ssize_t *_bufferSize,
859 	bool flat)
860 {
861 	settings_handle *handle = (settings_handle *)_handle;
862 	ssize_t bufferSize = *_bufferSize;
863 	int32 i;
864 
865 	if (!check_handle(handle) || !buffer || *_bufferSize == 0)
866 		return B_BAD_VALUE;
867 
868 	for (i = 0; i < handle->settings.parameter_count; i++) {
869 		put_parameter(&buffer, &bufferSize, &handle->settings.parameters[i],
870 			0, flat);
871 	}
872 
873 	*_bufferSize -= bufferSize;
874 	return bufferSize >= 0 ? B_OK : B_BUFFER_OVERFLOW;
875 }
876 
877 
878 /*!
879 	Matches the first value of the parameter matching "keyName" with a set
880 	of boolean values like 1/true/yes/on/enabled/...
881 	Returns "unknownValue" if the parameter could not be found or doesn't
882 	have any valid boolean setting, and "noArgValue" if the parameter
883 	doesn't have any values.
884 	Also returns "unknownValue" if the handle passed in was not valid.
885 */
886 bool
887 get_driver_boolean_parameter(void *_handle, const char *keyName,
888 	bool unknownValue, bool noArgValue)
889 {
890 	settings_handle *handle = (settings_handle*)_handle;
891 	driver_parameter *parameter;
892 	char *boolean;
893 
894 	if (!check_handle(handle))
895 		return unknownValue;
896 
897 	// check for the parameter
898 	if ((parameter = get_parameter(handle, keyName)) == NULL)
899 		return unknownValue;
900 
901 	// check for the argument
902 	if (parameter->value_count <= 0)
903 		return noArgValue;
904 
905 	boolean = parameter->values[0];
906 	if (!strcmp(boolean, "1")
907 		|| !strcasecmp(boolean, "true")
908 		|| !strcasecmp(boolean, "yes")
909 		|| !strcasecmp(boolean, "on")
910 		|| !strcasecmp(boolean, "enable")
911 		|| !strcasecmp(boolean, "enabled"))
912 		return true;
913 
914 	if (!strcmp(boolean, "0")
915 		|| !strcasecmp(boolean, "false")
916 		|| !strcasecmp(boolean, "no")
917 		|| !strcasecmp(boolean, "off")
918 		|| !strcasecmp(boolean, "disable")
919 		|| !strcasecmp(boolean, "disabled"))
920 		return false;
921 
922 	// if no known keyword is found, "unknownValue" is returned
923 	return unknownValue;
924 }
925 
926 
927 const char *
928 get_driver_parameter(void *_handle, const char *keyName,
929 	const char *unknownValue, const char *noArgValue)
930 {
931 	settings_handle* handle = (settings_handle*)_handle;
932 	struct driver_parameter *parameter;
933 
934 	if (!check_handle(handle))
935 		return unknownValue;
936 
937 	// check for the parameter
938 	if ((parameter = get_parameter(handle, keyName)) == NULL)
939 		return unknownValue;
940 
941 	// check for the argument
942 	if (parameter->value_count <= 0)
943 		return noArgValue;
944 
945 	return parameter->values[0];
946 }
947 
948 
949 const driver_settings *
950 get_driver_settings(void *handle)
951 {
952 	if (!check_handle((settings_handle*)handle))
953 		return NULL;
954 
955 	return &((settings_handle *)handle)->settings;
956 }
957 
958 
959 status_t
960 delete_driver_settings(void *_handle)
961 {
962 	return unload_driver_settings(_handle);
963 }
964