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