aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-07-19 09:27:19 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-07-19 09:27:19 +0000
commit2e157ddf9ecd9d58864425f0e87409ddc218df94 (patch)
tree18f415d4847897a12d790b545f94c32ae68c199c
parentbd28f6bf7f53ede8df39112d40cb52f2a3d00177 (diff)
downloadbusybox-w32-2e157ddf9ecd9d58864425f0e87409ddc218df94.tar.gz
busybox-w32-2e157ddf9ecd9d58864425f0e87409ddc218df94.tar.bz2
busybox-w32-2e157ddf9ecd9d58864425f0e87409ddc218df94.zip
libbb: updated config_parse() from Vladimir
function old new delta config_read 385 460 +75 runsvdir_main 1701 1716 +15 readit 331 338 +7 passwd_main 1049 1053 +4 parse_command 1504 1507 +3 decode_format_string 822 824 +2 bb__parsespent 117 119 +2 udhcp_get_option 221 222 +1 changepath 196 194 -2 parse_inittab 400 396 -4 nameif_main 683 679 -4 make_device 1176 1172 -4 config_open 48 40 -8 expand_main 698 689 -9 readcmd 1012 1002 -10 config_free_data 37 21 -16 SynchronizeFile 683 643 -40 sleep_main 474 362 -112 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 8/10 up/down: 109/-209) Total: -100 bytes
-rw-r--r--Config.in7
-rw-r--r--include/applets.h1
-rw-r--r--include/libbb.h14
-rw-r--r--include/usage.h5
-rw-r--r--init/init.c4
-rw-r--r--libbb/parse_config.c148
-rw-r--r--miscutils/crond.c13
-rw-r--r--networking/nameif.c2
-rwxr-xr-xtestsuite/parse.tests64
-rw-r--r--util-linux/mdev.c2
10 files changed, 206 insertions, 54 deletions
diff --git a/Config.in b/Config.in
index 1cbdf6197..8de667804 100644
--- a/Config.in
+++ b/Config.in
@@ -479,6 +479,13 @@ config INCLUDE_SUSv2
479 will be supported in head, tail, and fold. (Note: should 479 will be supported in head, tail, and fold. (Note: should
480 affect renice too.) 480 affect renice too.)
481 481
482config PARSE
483 bool "Uniform config file parser debugging applet: parse"
484
485config FEATURE_PARSE_COPY
486 bool "Keep a copy of current line"
487 depends on PARSE
488
482endmenu 489endmenu
483 490
484menu 'Installation Options' 491menu 'Installation Options'
diff --git a/include/applets.h b/include/applets.h
index aff9070bb..5dd485ab4 100644
--- a/include/applets.h
+++ b/include/applets.h
@@ -268,6 +268,7 @@ USE_NOHUP(APPLET(nohup, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
268USE_NSLOOKUP(APPLET(nslookup, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 268USE_NSLOOKUP(APPLET(nslookup, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
269USE_OD(APPLET(od, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 269USE_OD(APPLET(od, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
270USE_OPENVT(APPLET(openvt, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 270USE_OPENVT(APPLET(openvt, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
271USE_PARSE(APPLET(parse, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
271USE_PASSWD(APPLET(passwd, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS)) 272USE_PASSWD(APPLET(passwd, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS))
272USE_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 273USE_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
273USE_PGREP(APPLET(pgrep, _BB_DIR_USR_BIN, _BB_SUID_NEVER)) 274USE_PGREP(APPLET(pgrep, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
diff --git a/include/libbb.h b/include/libbb.h
index 14af1368c..af6c1385d 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -988,16 +988,22 @@ int bb_ask_confirmation(void) FAST_FUNC;
988int bb_parse_mode(const char* s, mode_t* theMode) FAST_FUNC; 988int bb_parse_mode(const char* s, mode_t* theMode) FAST_FUNC;
989 989
990/* 990/*
991 * Uniform config file parser helpers 991 * Config file parser
992 */ 992 */
993#define PARSE_DONT_REDUCE 0x00010000 // do not treat consecutive delimiters as one
994#define PARSE_DONT_TRIM 0x00020000 // do not trim line of leading and trailing delimiters
995#define PARSE_LAST_IS_GREEDY 0x00040000 // last token takes whole remainder of the line
996//#define PARSE_DONT_NULL 0x00080000 // do not set tokens[] to NULL
993typedef struct parser_t { 997typedef struct parser_t {
994 FILE *fp; 998 FILE *fp;
995 char *line, *data; 999 char *line;
1000 USE_FEATURE_PARSE_COPY(char *data;)
996 int lineno; 1001 int lineno;
997} parser_t; 1002} parser_t;
998parser_t* config_open(const char *filename) FAST_FUNC; 1003parser_t* config_open(const char *filename) FAST_FUNC;
999/* TODO: add define magic to collapse ntokens/mintokens/comment into one int param */ 1004int config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims) FAST_FUNC;
1000int config_read(parser_t *parser, char **tokens, int ntokens, int mintokens, const char *delims, char comment) FAST_FUNC; 1005#define config_read(parser, tokens, max, min, str, flags) \
1006 config_read(parser, tokens, ((flags) | (((min) & 0xFF) << 8) | ((max) & 0xFF)), str)
1001void config_close(parser_t *parser) FAST_FUNC; 1007void config_close(parser_t *parser) FAST_FUNC;
1002 1008
1003/* Concatenate path and filename to new allocated buffer. 1009/* Concatenate path and filename to new allocated buffer.
diff --git a/include/usage.h b/include/usage.h
index f9a993a21..61c5c8ee3 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -2903,6 +2903,11 @@
2903#define openvt_example_usage \ 2903#define openvt_example_usage \
2904 "openvt 2 /bin/ash\n" 2904 "openvt 2 /bin/ash\n"
2905 2905
2906#define parse_trivial_usage \
2907 "[-n maxtokens] [-m mintokens] [-d delims] [-f flags] file ..."
2908#define parse_full_usage "\n\n" \
2909 "[-n maxtokens] [-m mintokens] [-d delims] [-f flags] file ..."
2910
2906#define passwd_trivial_usage \ 2911#define passwd_trivial_usage \
2907 "[OPTION] [name]" 2912 "[OPTION] [name]"
2908#define passwd_full_usage "\n\n" \ 2913#define passwd_full_usage "\n\n" \
diff --git a/init/init.c b/init/init.c
index 9637589ce..0e4a8f1e5 100644
--- a/init/init.c
+++ b/init/init.c
@@ -808,7 +808,7 @@ static void parse_inittab(void)
808 /* optional_tty:ignored_runlevel:action:command 808 /* optional_tty:ignored_runlevel:action:command
809 * Delims are not to be collapsed and need exactly 4 tokens 809 * Delims are not to be collapsed and need exactly 4 tokens
810 */ 810 */
811 while (config_read(parser, token, -4, 0, ":", '#') >= 0) { 811 while (config_read(parser, token, 4, 0, "#:", PARSE_DONT_TRIM|PARSE_DONT_REDUCE|PARSE_LAST_IS_GREEDY)) {
812 int action; 812 int action;
813 char *tty = token[0]; 813 char *tty = token[0];
814 814
@@ -828,7 +828,7 @@ static void parse_inittab(void)
828 free(tty); 828 free(tty);
829 continue; 829 continue;
830 bad_entry: 830 bad_entry:
831 message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", parser->line); 831 message(L_LOG | L_CONSOLE, "Bad inittab entry at line %d", parser->lineno);
832 } 832 }
833 config_close(parser); 833 config_close(parser);
834#endif 834#endif
diff --git a/libbb/parse_config.c b/libbb/parse_config.c
index 5f6dbbde1..3945501ad 100644
--- a/libbb/parse_config.c
+++ b/libbb/parse_config.c
@@ -9,23 +9,51 @@
9 9
10#include "libbb.h" 10#include "libbb.h"
11 11
12#if ENABLE_PARSE
13int parse_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
14int parse_main(int argc UNUSED_PARAM, char **argv)
15{
16 const char *delims = "# \t";
17 unsigned flags = 0;
18 int mintokens = 0, ntokens = 128;
19 opt_complementary = "-1:n+:m+:f+";
20 getopt32(argv, "n:m:d:f:", &ntokens, &mintokens, &delims, &flags);
21 //argc -= optind;
22 argv += optind;
23 while (*argv) {
24 parser_t *p = config_open(*argv);
25 if (p) {
26 int n;
27 char **t = xmalloc(sizeof(char *) * ntokens);
28 while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) > 0) {
29 for (int i = 0; i < n; ++i)
30 printf("[%s]", t[i]);
31 puts("");
32 }
33 config_close(p);
34 }
35 argv++;
36 }
37 return EXIT_SUCCESS;
38}
39#endif
40
12/* 41/*
13 42
14Typical usage: 43Typical usage:
15 44
16----- CUT ----- 45----- CUT -----
17 char *t[3]; // tokens placeholder 46 char *t[3]; // tokens placeholder
18 parser_t p; // parser structure 47 parser_t *p = config_open(filename);
19 // open file 48 if (p) {
20 if (config_open(filename, &p)) {
21 // parse line-by-line 49 // parse line-by-line
22 while (*config_read(&p, t, 3, 0, delimiters, comment_char) >= 0) { // 0..3 tokens 50 while (config_read(p, t, 3, 0, delimiters, flags)) { // 1..3 tokens
23 // use tokens 51 // use tokens
24 bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]); 52 bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]);
25 } 53 }
26 ... 54 ...
27 // free parser 55 // free parser
28 config_close(&p); 56 config_close(p);
29 } 57 }
30----- CUT ----- 58----- CUT -----
31 59
@@ -35,44 +63,69 @@ parser_t* FAST_FUNC config_open(const char *filename)
35{ 63{
36 parser_t *parser = xzalloc(sizeof(parser_t)); 64 parser_t *parser = xzalloc(sizeof(parser_t));
37 /* empty file configures nothing */ 65 /* empty file configures nothing */
38 parser->fp = fopen_or_warn(filename, "r"); 66 parser->fp = fopen_or_warn_stdin(filename);
39 if (parser->fp) 67 if (parser->fp)
40 return parser; 68 return parser;
41 config_close (parser);
42 if (ENABLE_FEATURE_CLEAN_UP) 69 if (ENABLE_FEATURE_CLEAN_UP)
43 free(parser); 70 free(parser);
44 return NULL; 71 return NULL;
45} 72}
46 73
47static void config_free_data(parser_t *const parser) 74static void config_free_data(parser_t *const parser)
48{ 75{
49 free(parser->line); 76 free(parser->line);
50 free(parser->data); 77 parser->line = NULL;
51 parser->line = parser->data = NULL; 78 USE_FEATURE_PARSE_COPY(
79 free(parser->data);
80 parser->data = NULL;
81 )
52} 82}
83
53void FAST_FUNC config_close(parser_t *parser) 84void FAST_FUNC config_close(parser_t *parser)
54{ 85{
55 config_free_data(parser); 86 config_free_data(parser);
56 fclose(parser->fp); 87 fclose(parser->fp);
57} 88}
58 89
59int FAST_FUNC config_read(parser_t *parser, char **tokens, int ntokens, int mintokens, const char *delims, char comment) 90/*
911. Read a line from config file. If nothing to read then bail out returning 0.
92 Handle continuation character. Advance lineno for each physical line. Cut comments.
932. if PARSE_DONT_TRIM is not set (default) skip leading and cut trailing delimiters, if any.
943. If resulting line is empty goto 1.
954. Look for first delimiter. If PARSE_DONT_REDUCE or PARSE_DONT_TRIM is set then pin empty token.
965. Else (default) if number of seen tokens is equal to max number of tokens (token is the last one)
97 and PARSE_LAST_IS_GREEDY is set then pin the remainder of the line as the last token.
98 Else (token is not last or PARSE_LAST_IS_GREEDY is not set) just replace first delimiter with '\0'
99 thus delimiting token and pin it.
1006. Advance line pointer past the end of token. If number of seen tokens is less than required number
101 of tokens then goto 4.
1027. Control the number of seen tokens is not less the min number of tokens. Die if condition is not met.
1038. Return the number of seen tokens.
104
105mintokens > 0 make config_read() exit with error message if less than mintokens
106(but more than 0) are found. Empty lines are always skipped (not warned about).
107*/
108#undef config_read
109int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims)
60{ 110{
61 char *line, *q; 111 char *line, *q;
62 int ii, seen; 112 char comment = *delims++;
63 /* do not treat consecutive delimiters as one delimiter */ 113 int ii;
64 bool noreduce = (ntokens < 0); 114 int ntokens = flags & 0xFF;
65 if (noreduce) 115 int mintokens = (flags & 0xFF00) >> 8;
66 ntokens = -ntokens; 116
67 117 /*
68 memset(tokens, 0, sizeof(tokens[0]) * ntokens); 118 // N.B. this could only be used in read-in-one-go version, or when tokens use xstrdup(). TODO
119 if (!parser->lineno || !(flags & PARSE_DONT_NULL))
120 */
121 memset(tokens, 0, sizeof(tokens[0]) * ntokens);
69 config_free_data(parser); 122 config_free_data(parser);
70 123
71 while (1) { 124 while (1) {
72//TODO: speed up xmalloc_fgetline by internally using fgets, not fgetc 125//TODO: speed up xmalloc_fgetline by internally using fgets, not fgetc
73 line = xmalloc_fgetline(parser->fp); 126 line = xmalloc_fgetline(parser->fp);
74 if (!line) 127 if (!line)
75 return -1; 128 return 0;
76 129
77 parser->lineno++; 130 parser->lineno++;
78 // handle continuations. Tito's code stolen :) 131 // handle continuations. Tito's code stolen :)
@@ -98,12 +151,22 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, int ntokens, int mint
98 *q = '\0'; 151 *q = '\0';
99 ii = q - line; 152 ii = q - line;
100 } 153 }
101 // skip leading delimiters 154 // skip leading and trailing delimiters
102 seen = strspn(line, delims); 155 if (!(flags & PARSE_DONT_TRIM)) {
103 if (seen) { 156 // skip leading
104 ii -= seen; 157 int n = strspn(line, delims);
105 strcpy(line, line + seen); 158 if (n) {
159 ii -= n;
160 strcpy(line, line + n);
161 }
162 // cut trailing
163 if (ii) {
164 while (strchr(delims, line[--ii]))
165 continue;
166 line[++ii] = '\0';
167 }
106 } 168 }
169 // if something still remains -> return it
107 if (ii) 170 if (ii)
108 break; 171 break;
109 172
@@ -112,36 +175,39 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, int ntokens, int mint
112 free(line); 175 free(line);
113 } 176 }
114 177
115 // non-empty line found, parse and return 178 // non-empty line found, parse and return the number of tokens
116 179
117 // store line 180 // store line
118 parser->line = line = xrealloc(line, ii + 1); 181 parser->line = line = xrealloc(line, ii + 1);
119 parser->data = xstrdup(line); 182 USE_FEATURE_PARSE_COPY(
183 parser->data = xstrdup(line);
184 )
120 185
121 /* now split line to tokens */ 186 /* now split line to tokens */
122 ii = noreduce ? seen : 0;
123 ntokens--; // now it's max allowed token no 187 ntokens--; // now it's max allowed token no
124 while (1) { 188 // N.B, non-empty remainder is also a token,
189 // so if ntokens <= 1, we just return the whole line
190 // N.B. if PARSE_LAST_IS_GREEDY is set the remainder of the line is stuck to the last token
191 for (ii = 0; *line && ii <= ntokens; ) {
192 //bb_info_msg("L[%s]", line);
125 // get next token 193 // get next token
126 if (ii == ntokens) 194 // at the last token and need greedy token ->
127 break; 195 if ((flags & PARSE_LAST_IS_GREEDY) && (ii == ntokens)) {
128 q = line + strcspn(line, delims); 196 // ... don't cut the line
129 if (!*q) 197 q = line + strlen(line);
130 break; 198 } else {
199 // vanilla token. cut the line at the first delim
200 q = line + strcspn(line, delims);
201 *q++ = '\0';
202 }
131 // pin token 203 // pin token
132 *q++ = '\0'; 204 if ((flags & (PARSE_DONT_REDUCE|PARSE_DONT_TRIM)) || *line) {
133 if (noreduce || *line) { 205 //bb_info_msg("N[%d] T[%s]", ii, line);
134 tokens[ii++] = line; 206 tokens[ii++] = line;
135//bb_info_msg("L[%d] T[%s]\n", ii, line);
136 } 207 }
137 line = q; 208 line = q;
138 } 209 }
139 210
140 // non-empty remainder is also a token,
141 // so if ntokens <= 1, we just return the whole line
142 if (noreduce || *line)
143 tokens[ii++] = line;
144
145 if (ii < mintokens) 211 if (ii < mintokens)
146 bb_error_msg_and_die("bad line %u: %d tokens found, %d needed", 212 bb_error_msg_and_die("bad line %u: %d tokens found, %d needed",
147 parser->lineno, ii, mintokens); 213 parser->lineno, ii, mintokens);
diff --git a/miscutils/crond.c b/miscutils/crond.c
index d8423cf4f..154243c78 100644
--- a/miscutils/crond.c
+++ b/miscutils/crond.c
@@ -469,12 +469,15 @@ static void SynchronizeFile(const char *fileName)
469 file->cf_User = xstrdup(fileName); 469 file->cf_User = xstrdup(fileName);
470 pline = &file->cf_LineBase; 470 pline = &file->cf_LineBase;
471 471
472 while (--maxLines && (n=config_read(parser, tokens, 6, 0, " \t", '#')) >= 0) { 472 while (--maxLines
473 && (n = config_read(parser, tokens, 6, 1, "# \t", PARSE_LAST_IS_GREEDY))
474 ) {
473 CronLine *line; 475 CronLine *line;
474 476
475 if (DebugOpt) { 477 USE_FEATURE_PARSE_COPY(
476 crondlog(LVL5 "user:%s entry:%s", fileName, parser->data); 478 if (DebugOpt)
477 } 479 crondlog(LVL5 "user:%s entry:%s", fileName, parser->data);
480 )
478 481
479 /* check if line is setting MAILTO= */ 482 /* check if line is setting MAILTO= */
480 if (0 == strncmp(tokens[0], "MAILTO=", 7)) { 483 if (0 == strncmp(tokens[0], "MAILTO=", 7)) {
@@ -485,7 +488,7 @@ static void SynchronizeFile(const char *fileName)
485 continue; 488 continue;
486 } 489 }
487 /* check if a minimum of tokens is specified */ 490 /* check if a minimum of tokens is specified */
488 if (n < 5) 491 if (n < 6)
489 continue; 492 continue;
490 *pline = line = xzalloc(sizeof(CronLine)); 493 *pline = line = xzalloc(sizeof(CronLine));
491 /* parse date ranges */ 494 /* parse date ranges */
diff --git a/networking/nameif.c b/networking/nameif.c
index 291780a28..76a8cb7df 100644
--- a/networking/nameif.c
+++ b/networking/nameif.c
@@ -163,7 +163,7 @@ int nameif_main(int argc, char **argv)
163 struct parser_t *parser = config_open(fname); 163 struct parser_t *parser = config_open(fname);
164 if (parser) { 164 if (parser) {
165 char *tokens[2]; 165 char *tokens[2];
166 while (config_read(parser, tokens, 2, 2, " \t", '#') >= 0) 166 while (config_read(parser, tokens, 2, 2, "# \t", 0))
167 prepend_new_eth_table(&clist, tokens[0], tokens[1]); 167 prepend_new_eth_table(&clist, tokens[0], tokens[1]);
168 config_close(parser); 168 config_close(parser);
169 } 169 }
diff --git a/testsuite/parse.tests b/testsuite/parse.tests
new file mode 100755
index 000000000..1b43f9c9f
--- /dev/null
+++ b/testsuite/parse.tests
@@ -0,0 +1,64 @@
1#!/bin/sh
2
3# Copyright 2008 by Denys Vlasenko <vda.linux@googlemail.com>
4# Licensed under GPL v2, see file LICENSE for details.
5
6. testing.sh
7
8NO_REDUCE=65536
9NO_TRIM=131072
10GREEDY=262144
11
12# testing "description" "command" "result" "infile" "stdin"
13
14testing "mdev.conf" \
15 "parse -n 4 -m 3 -f $GREEDY -" \
16 "[sda][0:0][644][@echo @echo TEST]\n" \
17 "-" \
18 " sda 0:0 644 @echo @echo TEST # echo trap\n"
19
20testing "notrim" \
21 "parse -n 4 -m 3 -f $(($GREEDY+$NO_TRIM)) -" \
22 "[][sda][0:0][644 @echo @echo TEST ]\n" \
23 "-" \
24 " sda 0:0 644 @echo @echo TEST \n"
25
26FILE=__parse.fstab
27cat >$FILE <<EOF
28#
29# Device Point System Options
30#_______________________________________________________________
31/dev/hdb3 / ext2 defaults 1 0
32 /dev/hdb1 /dosc hpfs ro 1 0
33 /dev/fd0 /dosa vfat rw,user,noauto,nohide 0 0
34 /dev/fd1 /dosb vfat rw,user,noauto,nohide 0 0
35#
36 /dev/cdrom /cdrom iso9660 ro,user,noauto,nohide 0 0
37/dev/hdb5 /redhat ext2 rw,root,noauto,nohide 0 0 #sssd
38 /dev/hdb6 /win2home ntfs rw,root,noauto,nohide 0 0# ssdsd
39/dev/hdb7 /win2skul ntfs rw,root,noauto,nohide none 0 0
40none /dev/pts devpts gid=5,mode=620 0 0
41 none /proc proc defaults 0 0
42EOF
43
44cat >$FILE.res <<EOF
45[/dev/hdb3][/][ext2][defaults][1][0]
46[/dev/hdb1][/dosc][hpfs][ro][1][0]
47[/dev/fd0][/dosa][vfat][rw,user,noauto,nohide][0][0]
48[/dev/fd1][/dosb][vfat][rw,user,noauto,nohide][0][0]
49[/dev/cdrom][/cdrom][iso9660][ro,user,noauto,nohide][0][0]
50[/dev/hdb5][/redhat][ext2][rw,root,noauto,nohide][0][0]
51[/dev/hdb6][/win2home][ntfs][rw,root,noauto,nohide][0][0]
52[/dev/hdb7][/win2skul][ntfs][rw,root,noauto,nohide][none][0]
53[none][/dev/pts][devpts][gid=5,mode=620][0][0]
54[none][/proc][proc][defaults][0][0]
55EOF
56
57testing "polluted fstab" \
58 "parse -n 6 -m 6 $FILE" \
59 "`cat $FILE.res`\n" \
60 "" \
61 ""
62rm -f $FILE $FILE.res
63
64exit $FAILCOUNT
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index f83dd6adf..7ad55c8af 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -101,7 +101,7 @@ static void make_device(char *path, int delete)
101 if (!parser) 101 if (!parser)
102 goto end_parse; 102 goto end_parse;
103 103
104 while (config_read(parser, tokens, 4, 3, " \t", '#') >= 0) { 104 while (config_read(parser, tokens, 4, 3, "# \t", PARSE_LAST_IS_GREEDY)) {
105 regmatch_t off[1+9*ENABLE_FEATURE_MDEV_RENAME_REGEXP]; 105 regmatch_t off[1+9*ENABLE_FEATURE_MDEV_RENAME_REGEXP];
106 char *val; 106 char *val;
107 107