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