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