diff -Naur busybox.orig/include/libbb.h busybox/include/libbb.h
--- busybox.orig/include/libbb.h	2008-07-11 20:23:31 +0000
+++ busybox/include/libbb.h	2008-07-13 23:48:59 +0000
@@ -983,6 +983,30 @@
 
 extern int bb_parse_mode(const char* s, mode_t* theMode) FAST_FUNC;
 
+/*
+ * Uniform config file parser helpers
+ */
+#define CONFIG_PARSER_STDIO_BASED 1
+
+#if !CONFIG_PARSER_STDIO_BASED
+typedef struct parser_t {
+	char *data;
+	char *line;
+	int lineno;
+} parser_t;
+
+extern char* config_open(parser_t *parser, const char *filename) FAST_FUNC;
+#else
+typedef struct parser_t {
+	FILE *fp;
+	char *line;
+	int lineno;
+} parser_t;
+extern FILE* config_open(parser_t *parser, const char *filename) FAST_FUNC;
+#endif
+extern char* config_read(parser_t *parser, char comment, const char *delims, int ntokens, char **tokens, const char *format) FAST_FUNC;
+extern void config_close(parser_t *parser) FAST_FUNC;
+
 /* Concatenate path and filename to new allocated buffer.
  * Add "/" only as needed (no duplicate "//" are produced).
  * If path is NULL, it is assumed to be "/".
diff -Naur busybox.orig/libbb/Kbuild busybox/libbb/Kbuild
--- busybox.orig/libbb/Kbuild	2008-07-12 21:22:13 +0000
+++ busybox/libbb/Kbuild	2008-07-13 16:15:43 +0000
@@ -63,6 +63,7 @@
 lib-y += mtab_file.o
 lib-y += obscure.o
 lib-y += parse_mode.o
+lib-y += parse_config.o
 lib-y += perror_msg.o
 lib-y += perror_msg_and_die.o
 lib-y += perror_nomsg.o
diff -Naur busybox.orig/libbb/parse_config.c busybox/libbb/parse_config.c
--- busybox.orig/libbb/parse_config.c	1970-01-01 00:00:00 +0000
+++ busybox/libbb/parse_config.c	2008-07-13 23:55:06 +0000
@@ -0,0 +1,222 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * config file parser helper
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/*
+
+Typical usage:
+
+----- CUT -----
+	char *t[3];	// tokens placeholder
+	PARSER p;	// parser structure
+	// open file
+	if (config_open(filename, &p)) {
+		// parse line-by-line
+		while (*config_read(&p, comment_char, delimiters, 3, t)) { // 3 tokens at most
+			// use tokens
+			bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]);
+		}
+		...
+		// free parser
+		config_close(&p);
+	}
+----- CUT -----
+
+*/
+
+#if !CONFIG_PARSER_STDIO_BASED
+
+char* FAST_FUNC config_open(parser_t *parser, const char *filename)
+{
+	// empty file configures nothing!
+	char *data = xmalloc_open_read_close(filename, NULL);
+	if (!data)
+		return data;
+
+	// convert 0x5c 0x0a (backslashes at the very end of line) to 0x20 0x20 (spaces)
+	for (char *s = data; (s = strchr(s, '\\')) != NULL; ++s)
+		if ('\n' == s[1]) {
+			s[0] = s[1] = ' ';
+		}
+
+	// init parser
+	parser->line = parser->data = data;
+	parser->lineno = 0;
+
+	return data;
+}
+
+void FAST_FUNC config_close(parser_t *parser)
+{
+	// for now just free config data
+	free(parser->data);
+}
+
+char* FAST_FUNC config_read(parser_t *parser, char comment, const char *delims, int ntokens, char **tokens, const char *format)
+{
+	char *ret, *line;
+	int noreduce = (ntokens<0); // do not treat subsequent delimiters as one delimiter
+	if (ntokens < 0)
+		ntokens = -ntokens;
+	ret = line = parser->line;
+	// nullify tokens
+	memset(tokens, 0, sizeof(void *) * ntokens);
+	// now split to lines
+	while (*line) {
+		int token_num = 0;
+		// limit the line
+		char *ptr = strchrnul(line, '\n');
+		*ptr++ = '\0';
+		// line number
+		parser->lineno++;
+		// comments mean EOLs
+		if (comment)
+			*strchrnul(line, comment) = '\0';
+		// skip leading delimiters
+		while (*line && strchr(delims, *line))
+			line++;
+		// skip empty lines
+		if (*line) {
+			char *s;
+			// now split line to tokens
+			s = line;
+			while (s) {
+				char *p;
+				// get next token
+				if (token_num+1 >= ntokens)
+					break;
+				p = s;
+				while (*p && !strchr(delims, *p))
+					p++;
+				if (!*p)
+					break;
+				*p++ = '\0';
+				// pin token
+				if (noreduce || *s) {
+					tokens[token_num++] = s;
+//bb_error_msg("L[%d] T[%s]", token_num, s);
+				}
+				s = p;
+	 		}
+			// non-empty remainder is also a token. So if ntokens == 0, we just return the whole line
+			if (s && (noreduce || *s))
+				tokens[token_num++] = s;
+			// sanity check: have we got all required tokens?
+			if (token_num < strlen(format))
+				bb_error_msg_and_die("bad line %u, %d tokens found, %d needed", parser->lineno, token_num, strlen(format));// FIXME: lineno);
+			// advance data for the next call
+			line = ptr;
+			break;
+		}
+		// line didn't contain any token -> try next line
+		ret = line = ptr;
+ 	}
+	parser->line = line;
+
+	// return current line. caller must check *ret to determine whether to continue
+	return ret;
+}
+
+#else // stdio-based
+
+FILE* FAST_FUNC config_open(parser_t *parser, const char *filename)
+{
+	// empty file configures nothing!
+	parser->fp = xfopen(filename, "r");
+	if (!parser->fp)
+		return parser->fp;
+
+	// init parser
+	parser->line = NULL;
+	parser->lineno = 0;
+
+	return parser->fp;
+}
+
+void FAST_FUNC config_close(parser_t *parser)
+{
+	fclose(parser->fp);
+}
+
+char* FAST_FUNC config_read(parser_t *parser, char comment, const char *delims, int ntokens, char **tokens, const char *format)
+{
+	char *line;
+
+	int noreduce = (ntokens<0); // do not treat subsequent delimiters as one delimiter
+	if (ntokens < 0)
+		ntokens = -ntokens;
+
+	// nullify tokens
+	memset(tokens, 0, sizeof(void *) * ntokens);
+
+	// free used line
+	free(parser->line);
+
+	// get fresh line
+	while ((line = xmalloc_fgetline(parser->fp))) {
+		int token_num = 0;
+		char *q;
+		parser->lineno++;
+		// handle continuations. Tito's code stolen :)
+		while ((q = last_char_is(line, '\\')) != NULL) {
+			/* Multi-line object */
+			char *next_line = xmalloc_fgetline(parser->fp);
+			parser->lineno++;
+			*q = '\0'; /* Remove '\' */
+			line = xasprintf("%s%s", line, (next_line) ? next_line : "");
+			free(next_line);
+		}
+		// store line
+		parser->line = line;
+		// comments mean EOLs
+		if (comment)
+			*strchrnul(line, comment) = '\0';
+		// skip leading delimiters
+		while (*line && strchr(delims, *line))
+			line++;
+		// skip empty lines
+		if (*line) {
+			char *s;
+			// now split line to tokens
+			s = line;
+			while (s) {
+				char *p;
+				// get next token
+				if (token_num+1 >= ntokens)
+					break;
+				p = s;
+				while (*p && !strchr(delims, *p))
+					p++;
+				if (!*p)
+					break;
+				*p++ = '\0';
+				// pin token
+				if (noreduce || *s) {
+					tokens[token_num++] = s;
+//bb_error_msg("L[%d] T[%s]", token_num, s);
+				}
+				s = p;
+	 		}
+			// non-empty remainder is also a token. So if ntokens == 0, we just return the whole line
+			if (s && (noreduce || *s))
+				tokens[token_num++] = s;
+			// sanity check: have we got all required tokens?
+			if (token_num < strlen(format))
+				bb_error_msg_and_die("bad line %u, %d tokens found, %d needed", parser->lineno, token_num, strlen(format));// FIXME: lineno);
+			break;
+		}
+		// line didn't contain any token -> try next line
+		free(line);
+ 	}
+
+	return line;
+}
+
+#endif
diff -Naur busybox.orig/util-linux/mdev.c busybox/util-linux/mdev.c
--- busybox.orig/util-linux/mdev.c	2008-07-12 21:22:18 +0000
+++ busybox/util-linux/mdev.c	2008-07-13 23:49:29 +0000
@@ -25,16 +25,6 @@
 /* We use additional 64+ bytes in make_device() */
 #define SCRATCH_SIZE 80
 
-static char *next_field(char *s)
-{
-	char *end = skip_non_whitespace(s);
-	s = skip_whitespace(end);
-	*end = '\0';
-	if (*s == '\0')
-		s = NULL;
-	return s;
-}
-
 /* Builds an alias path.
  * This function potentionally reallocates the alias parameter.
  */
@@ -104,33 +94,26 @@
 	        type = S_IFBLK;
 
 	if (ENABLE_FEATURE_MDEV_CONF) {
-		FILE *fp;
-		char *line, *val, *next;
-		unsigned lineno = 0;
+		parser_t parser;
+		char *tokens[5];
 
 		/* If we have config file, look up user settings */
-		fp = fopen_or_warn("/etc/mdev.conf", "r");
-		if (!fp)
+		if (!config_open(&parser, "/etc/mdev.conf"))
 			goto end_parse;
 
-		while ((line = xmalloc_fgetline(fp)) != NULL) {
+		while (config_read(&parser, '#', " \t", 5, tokens, "sss")) {
 			regmatch_t off[1+9*ENABLE_FEATURE_MDEV_RENAME_REGEXP];
-
-			++lineno;
-			trim(line);
-			if (!line[0])
-				goto next_line;
+			char **val = tokens;
 
 			/* Fields: regex uid:gid mode [alias] [cmd] */
 
 			/* 1st field: regex to match this device */
-			next = next_field(line);
 			{
 				regex_t match;
 				int result;
 
 				/* Is this it? */
-				xregcomp(&match, line, REG_EXTENDED);
+				xregcomp(&match, *val++, REG_EXTENDED);
 				result = regexec(&match, device_name, ARRAY_SIZE(off), off, 0);
 				regfree(&match);
 
@@ -147,7 +130,7 @@
 				if (result || off[0].rm_so
 				 || ((int)off[0].rm_eo != (int)strlen(device_name))
 				) {
-					goto next_line;
+					continue;
 				}
 			}
 
@@ -155,15 +138,11 @@
 			 * after parsing the rest of fields */
 
 			/* 2nd field: uid:gid - device ownership */
-			if (!next) /* field must exist */
-				bb_error_msg_and_die("bad line %u", lineno);
-			val = next;
-			next = next_field(val);
 			{
 				struct passwd *pass;
 				struct group *grp;
-				char *str_uid = val;
-				char *str_gid = strchrnul(val, ':');
+				char *str_uid = *val++;
+				char *str_gid = strchrnul(str_uid, ':');
 
 				if (*str_gid)
 					*str_gid++ = '\0';
@@ -182,33 +161,27 @@
 			}
 
 			/* 3rd field: mode - device permissions */
-			if (!next) /* field must exist */
-				bb_error_msg_and_die("bad line %u", lineno);
-			val = next;
-			next = next_field(val);
-			mode = strtoul(val, NULL, 8);
+			mode = strtoul(*val, NULL, 8);
 
 			/* 4th field (opt): >alias */
 #if ENABLE_FEATURE_MDEV_RENAME
-			if (!next)
+			if (!*++val)
 				break;
-			if (*next == '>' || *next == '=') {
+			aliaslink = *val[0];
+			if (aliaslink == '>' || aliaslink == '=') {
 #if ENABLE_FEATURE_MDEV_RENAME_REGEXP
 				char *s, *p;
 				unsigned i, n;
 
-				aliaslink = *next;
-				val = next;
-				next = next_field(val);
 				/* substitute %1..9 with off[1..9], if any */
 				n = 0;
-				s = val;
+				s = *val;
 				while (*s)
 					if (*s++ == '%')
 						n++;
 
-				p = alias = xzalloc(strlen(val) + n * strlen(device_name));
-				s = val + 1;
+				p = alias = xzalloc(strlen(*val) + n * strlen(device_name));
+				s = *val + 1;
 				while (*s) {
 					*p = *s;
 					if ('%' == *s) {
@@ -224,24 +197,20 @@
 					s++;
 				}
 #else
-				aliaslink = *next;
-				val = next;
-				next = next_field(val);
-				alias = xstrdup(val + 1);
+				alias = xstrdup(*val + 1);
 #endif
 			}
 #endif /* ENABLE_FEATURE_MDEV_RENAME */
 
 			/* The rest (opt): command to run */
-			if (!next)
+			if (!*++val)
 				break;
-			val = next;
 			if (ENABLE_FEATURE_MDEV_EXEC) {
 				const char *s = "@$*";
-				const char *s2 = strchr(s, *val);
+				const char *s2 = strchr(s, *val[0]);
 
 				if (!s2)
-					bb_error_msg_and_die("bad line %u", lineno);
+					bb_error_msg_and_die("bad line %u", parser.lineno);
 
 				/* Correlate the position in the "@$*" with the delete
 				 * step so that we get the proper behavior:
@@ -250,17 +219,14 @@
 				 * *cmd: run on both
 				 */
 				if ((s2 - s + 1) /*1/2/3*/ & /*1/2*/ (1 + delete)) {
-					command = xstrdup(val + 1);
+					command = xstrdup(*val + 1);
 				}
 			}
 			/* end of field parsing */
 			break; /* we found matching line, stop */
- next_line:
-			free(line);
 		} /* end of "while line is read from /etc/mdev.conf" */
 
-		free(line); /* in case we used "break" to get here */
-		fclose(fp);
+		config_close(&parser);
 	}
  end_parse:
 
