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