xref: /haiku/src/system/libroot/os/driver_settings.cpp (revision e7d5c75dce28921de0dc981ed840205a67a0c0e5)
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 bool
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 	return *_bufferSize >= 0;
599 }
600 
601 
602 //	#pragma mark - Kernel only functions
603 
604 
605 #ifdef _KERNEL_MODE
606 static settings_handle *
607 find_driver_settings(const char *name)
608 {
609 	settings_handle *handle = NULL;
610 
611 	ASSERT_LOCKED_MUTEX(&sLock);
612 
613 	while ((handle = (settings_handle*)list_get_next_item(&sHandles, handle))
614 			!= NULL) {
615 		if (!strcmp(handle->name, name))
616 			return handle;
617 	}
618 
619 	return NULL;
620 }
621 
622 
623 status_t
624 driver_settings_init(kernel_args *args)
625 {
626 	struct driver_settings_file *settings = args->driver_settings;
627 
628 	// Move the preloaded driver settings over to the kernel
629 
630 	list_init(&sHandles);
631 
632 	while (settings != NULL) {
633 		settings_handle *handle
634 			= (settings_handle*)malloc(sizeof(settings_handle));
635 		if (handle == NULL)
636 			return B_NO_MEMORY;
637 
638 		if (settings->size != 0) {
639 			handle->text = (char*)malloc(settings->size + 1);
640 			if (handle->text == NULL) {
641 				free(handle);
642 				return B_NO_MEMORY;
643 			}
644 
645 			memcpy(handle->text, settings->buffer, settings->size);
646 			handle->text[settings->size] = '\0';
647 				// null terminate the buffer
648 		} else
649 			handle->text = NULL;
650 
651 		strlcpy(handle->name, settings->name, sizeof(handle->name));
652 		handle->settings.parameters = NULL;
653 		handle->settings.parameter_count = 0;
654 		handle->magic = 0;
655 			// this triggers parsing the settings when they are actually used
656 
657 		if (!strcmp(handle->name, B_SAFEMODE_DRIVER_SETTINGS)) {
658 			// These settings cannot be reloaded, so we better don't throw
659 			// them away.
660 			handle->ref_count = 1;
661 		} else
662 			handle->ref_count = 0;
663 
664 		list_add_item(&sHandles, handle);
665 
666 		settings = settings->next;
667 	}
668 
669 	return B_OK;
670 }
671 #endif
672 
673 
674 //	#pragma mark - public API
675 
676 
677 status_t
678 unload_driver_settings(void *_handle)
679 {
680 	settings_handle *handle = (settings_handle *)_handle;
681 	if (!check_handle(handle))
682 		return B_BAD_VALUE;
683 
684 #ifdef _KERNEL_MODE
685 	mutex_lock(&sLock);
686 	if (--handle->ref_count == 0 && gBootDevice > 0) {
687 		// don't unload an handle when /boot is not available
688 		list_remove_link(&handle->link);
689 	} else
690 		handle = NULL;
691 	mutex_unlock(&sLock);
692 #endif
693 
694 	if (handle != NULL)
695 		free_settings(handle);
696 
697 	return B_OK;
698 }
699 
700 
701 void *
702 load_driver_settings(const char *driverName)
703 {
704 	settings_handle *handle;
705 	int file = -1;
706 
707 	if (driverName == NULL)
708 		return NULL;
709 
710 #ifdef _KERNEL_MODE
711 	// see if we already have these settings loaded
712 	mutex_lock(&sLock);
713 	handle = find_driver_settings(driverName);
714 	if (handle != NULL && handle->ref_count == 0 && gBootDevice > 0) {
715 		// A handle with a zero ref_count should be unloaded if /boot is
716 		// available.
717 		list_remove_link(&handle->link);
718 		free_settings(handle);
719 	} else if (handle != NULL) {
720 		handle->ref_count++;
721 
722 		// we got it, now let's see if it already has been parsed
723 		if (handle->magic != SETTINGS_MAGIC) {
724 			handle->magic = SETTINGS_MAGIC;
725 
726 			if (parse_settings(handle) != B_OK) {
727 				// no valid settings, let's cut down its memory requirements
728 				free(handle->text);
729 				handle->text = NULL;
730 				handle = NULL;
731 			}
732 		}
733 		mutex_unlock(&sLock);
734 		return handle;
735 	}
736 
737 	// we are allowed to call the driver settings pretty early in the boot process
738 	if (gKernelStartup) {
739 		mutex_unlock(&sLock);
740 		return NULL;
741 	}
742 #endif	// _KERNEL_MODE
743 #ifdef _BOOT_MODE
744 	// see if we already have these settings loaded
745 	{
746 		struct driver_settings_file *settings = gKernelArgs.driver_settings;
747 		while (settings != NULL) {
748 			if (!strcmp(settings->name, driverName)) {
749 				// we have it - since the buffer is clobbered, we have to
750 				// copy its contents, though
751 				char *text = (char*)malloc(settings->size + 1);
752 				if (text == NULL)
753 					return NULL;
754 
755 				memcpy(text, settings->buffer, settings->size + 1);
756 				return new_settings(text, driverName);
757 			}
758 			settings = settings->next;
759 		}
760 	}
761 #endif	// _BOOT_MODE
762 
763 	// open the settings from the standardized location
764 	if (driverName[0] != '/') {
765 		char path[B_FILE_NAME_LENGTH + 64];
766 
767 #ifdef _BOOT_MODE
768 		// TODO: for now the boot loader does not support find_directory()
769 		//		(it might get a simplified version of it)
770 		strcpy(path, "/boot/home/config/settings");
771 #else
772 		// TODO: use B_COMMON_SETTINGS_DIRECTORY instead!
773 		if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, path,
774 				sizeof(path)) == B_OK)
775 #endif
776 		{
777 			strlcat(path, SETTINGS_DIRECTORY, sizeof(path));
778 			strlcat(path, driverName, sizeof(path));
779 		}
780 
781 		file = open(path, O_RDONLY);
782 	} else
783 		file = open(driverName, O_RDONLY);
784 
785 	if (file < B_OK) {
786 #ifdef _KERNEL_MODE
787 		mutex_unlock(&sLock);
788 #endif
789 		close(file);
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 /** Loads a driver settings file using the full path, instead of
807  *	only defining the leaf name (as load_driver_settings() does).
808  *	I am not sure if this function is really necessary - I would
809  *	probably prefer something like a search order (if it's not
810  *	an absolute path):
811  *		~/config/settings/kernel/driver
812  *		current directory
813  *	That would render this function useless.
814  */
815 
816 #if 0
817 void *
818 load_driver_settings_from_path(const char *path)
819 {
820 	settings_handle *handle;
821 	int file;
822 
823 	if (path == NULL)
824 		return NULL;
825 
826 	file = open(path, O_RDONLY);
827 	if (file < B_OK)
828 		return NULL;
829 
830 	handle = load_driver_settings_from_file(file);
831 
832 	close(file);
833 	return (void *)handle;
834 }
835 #endif
836 
837 
838 /*!
839 	Returns a new driver_settings handle that has the parsed contents
840 	of the passed string.
841 	You can get an empty driver_settings object when you pass NULL as
842 	the "settingsString" parameter.
843 */
844 void *
845 parse_driver_settings_string(const char *settingsString)
846 {
847 	// we simply copy the whole string to use it as our internal buffer
848 	char *text = strdup(settingsString);
849 	if (settingsString == NULL || text != NULL) {
850 		settings_handle *handle
851 			= (settings_handle*)malloc(sizeof(settings_handle));
852 		if (handle != NULL) {
853 			handle->magic = SETTINGS_MAGIC;
854 			handle->text = text;
855 
856 			if (parse_settings(handle) == B_OK)
857 				return handle;
858 
859 			free(handle);
860 		}
861 		free(text);
862 	}
863 
864 	return NULL;
865 }
866 
867 
868 /*!
869 	This function prints out a driver settings structure to a human
870 	readable string.
871 	It's either in standard style or the single line style speficied
872 	by the "flat" parameter.
873 	If the buffer is too small to hold the string, B_BUFFER_OVERFLOW
874 	is returned, and the needed amount of bytes if placed in the
875 	"_bufferSize" parameter.
876 	If the "handle" parameter is not a valid driver settings handle, or
877 	the "buffer" parameter is NULL, B_BAD_VALUE is returned.
878 */
879 status_t
880 get_driver_settings_string(void *_handle, char *buffer, size_t *_bufferSize,
881 	bool flat)
882 {
883 	settings_handle *handle = (settings_handle *)_handle;
884 	size_t bufferSize = *_bufferSize;
885 	int32 i;
886 
887 	if (!check_handle(handle) || !buffer || *_bufferSize == 0)
888 		return B_BAD_VALUE;
889 
890 	for (i = 0; i < handle->settings.parameter_count; i++) {
891 		put_parameter(&buffer, &bufferSize, &handle->settings.parameters[i],
892 			0, flat);
893 	}
894 
895 	*_bufferSize -= bufferSize;
896 	return bufferSize >= 0 ? B_OK : B_BUFFER_OVERFLOW;
897 }
898 
899 
900 /*!
901 	Matches the first value of the parameter matching "keyName" with a set
902 	of boolean values like 1/true/yes/on/enabled/...
903 	Returns "unknownValue" if the parameter could not be found or doesn't
904 	have any valid boolean setting, and "noArgValue" if the parameter
905 	doesn't have any values.
906 	Also returns "unknownValue" if the handle passed in was not valid.
907 */
908 bool
909 get_driver_boolean_parameter(void *_handle, const char *keyName,
910 	bool unknownValue, bool noArgValue)
911 {
912 	settings_handle *handle = (settings_handle*)_handle;
913 	driver_parameter *parameter;
914 	char *boolean;
915 
916 	if (!check_handle(handle))
917 		return unknownValue;
918 
919 	// check for the parameter
920 	if ((parameter = get_parameter(handle, keyName)) == NULL)
921 		return unknownValue;
922 
923 	// check for the argument
924 	if (parameter->value_count <= 0)
925 		return noArgValue;
926 
927 	boolean = parameter->values[0];
928 	if (!strcmp(boolean, "1")
929 		|| !strcasecmp(boolean, "true")
930 		|| !strcasecmp(boolean, "yes")
931 		|| !strcasecmp(boolean, "on")
932 		|| !strcasecmp(boolean, "enable")
933 		|| !strcasecmp(boolean, "enabled"))
934 		return true;
935 
936 	if (!strcmp(boolean, "0")
937 		|| !strcasecmp(boolean, "false")
938 		|| !strcasecmp(boolean, "no")
939 		|| !strcasecmp(boolean, "off")
940 		|| !strcasecmp(boolean, "disable")
941 		|| !strcasecmp(boolean, "disabled"))
942 		return false;
943 
944 	// if no known keyword is found, "unknownValue" is returned
945 	return unknownValue;
946 }
947 
948 
949 const char *
950 get_driver_parameter(void *_handle, const char *keyName,
951 	const char *unknownValue, const char *noArgValue)
952 {
953 	settings_handle* handle = (settings_handle*)_handle;
954 	struct driver_parameter *parameter;
955 
956 	if (!check_handle(handle))
957 		return unknownValue;
958 
959 	// check for the parameter
960 	if ((parameter = get_parameter(handle, keyName)) == NULL)
961 		return unknownValue;
962 
963 	// check for the argument
964 	if (parameter->value_count <= 0)
965 		return noArgValue;
966 
967 	return parameter->values[0];
968 }
969 
970 
971 const driver_settings *
972 get_driver_settings(void *handle)
973 {
974 	if (!check_handle((settings_handle*)handle))
975 		return NULL;
976 
977 	return &((settings_handle *)handle)->settings;
978 }
979 
980 
981 // this creates an alias of the above function
982 // unload_driver_settings() is the same as delete_driver_settings()
983 extern "C" __typeof(unload_driver_settings) delete_driver_settings
984 	__attribute__((alias ("unload_driver_settings")));
985 
986