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