summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-07-15 21:09:30 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-07-15 21:09:30 +0000
commite559e0a75738044403e9a43f54ccb0ac3091cd9a (patch)
tree9216b28c5e34aa5fed658d7893201947eec861b2
parent0ed090e184739b6722a87acab644a8df24be09d4 (diff)
downloadbusybox-w32-e559e0a75738044403e9a43f54ccb0ac3091cd9a.tar.gz
busybox-w32-e559e0a75738044403e9a43f54ccb0ac3091cd9a.tar.bz2
busybox-w32-e559e0a75738044403e9a43f54ccb0ac3091cd9a.zip
libbb: unified config parser (By Vladimir Dronnikov)
mdev: use it function old new delta config_read - 400 +400 config_open - 43 +43 config_close - 9 +9 qrealloc 33 36 +3 compare_keys 735 737 +2 xstrtoull_range_sfx 296 295 -1 qgravechar 109 106 -3 get_address 181 178 -3 next_token 928 923 -5 sv_main 1228 1222 -6 find_main 418 406 -12 next_field 32 - -32 make_device 1269 1184 -85 ------------------------------------------------------------------------------ (add/remove: 3/1 grow/shrink: 2/7 up/down: 457/-147) Total: 310 bytes
-rw-r--r--include/libbb.h22
-rw-r--r--libbb/Kbuild1
-rw-r--r--libbb/parse_config.c247
-rw-r--r--util-linux/mdev.c81
4 files changed, 295 insertions, 56 deletions
diff --git a/include/libbb.h b/include/libbb.h
index 2bd614c71..0db1658f4 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -987,6 +987,28 @@ int bb_ask_confirmation(void) FAST_FUNC;
987 987
988extern int bb_parse_mode(const char* s, mode_t* theMode) FAST_FUNC; 988extern int bb_parse_mode(const char* s, mode_t* theMode) FAST_FUNC;
989 989
990/*
991 * Uniform config file parser helpers
992 */
993#define PARSER_STDIO_BASED 1
994#if !PARSER_STDIO_BASED
995typedef struct parser_t {
996 char *data;
997 char *line;
998 int lineno;
999} parser_t;
1000extern char* config_open(parser_t *parser, const char *filename) FAST_FUNC;
1001#else
1002typedef struct parser_t {
1003 FILE *fp;
1004 char *line;
1005 int lineno;
1006} parser_t;
1007extern FILE* config_open(parser_t *parser, const char *filename) FAST_FUNC;
1008#endif
1009extern char* config_read(parser_t *parser, char **tokens, int ntokens, int mintokens, const char *delims, char comment) FAST_FUNC;
1010extern void config_close(parser_t *parser) FAST_FUNC;
1011
990/* Concatenate path and filename to new allocated buffer. 1012/* Concatenate path and filename to new allocated buffer.
991 * Add "/" only as needed (no duplicate "//" are produced). 1013 * Add "/" only as needed (no duplicate "//" are produced).
992 * If path is NULL, it is assumed to be "/". 1014 * If path is NULL, it is assumed to be "/".
diff --git a/libbb/Kbuild b/libbb/Kbuild
index ab85ffc9e..726200675 100644
--- a/libbb/Kbuild
+++ b/libbb/Kbuild
@@ -63,6 +63,7 @@ lib-y += mode_string.o
63lib-y += mtab_file.o 63lib-y += mtab_file.o
64lib-y += obscure.o 64lib-y += obscure.o
65lib-y += parse_mode.o 65lib-y += parse_mode.o
66lib-y += parse_config.o
66lib-y += perror_msg.o 67lib-y += perror_msg.o
67lib-y += perror_msg_and_die.o 68lib-y += perror_msg_and_die.o
68lib-y += perror_nomsg.o 69lib-y += perror_nomsg.o
diff --git a/libbb/parse_config.c b/libbb/parse_config.c
new file mode 100644
index 000000000..6612db367
--- /dev/null
+++ b/libbb/parse_config.c
@@ -0,0 +1,247 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * config file parser helper
4 *
5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 */
9
10#include "libbb.h"
11
12/*
13
14Typical usage:
15
16----- CUT -----
17 char *t[3]; // tokens placeholder
18 parser_t p; // parser structure
19 // open file
20 if (config_open(filename, &p)) {
21 // parse line-by-line
22 while (*config_read(&p, t, 3, 0, delimiters, comment_char)) { // 0..3 tokens
23 // use tokens
24 bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]);
25 }
26 ...
27 // free parser
28 config_close(&p);
29 }
30----- CUT -----
31
32*/
33
34#if !PARSER_STDIO_BASED
35
36char* FAST_FUNC config_open(parser_t *parser, const char *filename)
37{
38 // empty file configures nothing!
39 char *data = xmalloc_open_read_close(filename, NULL);
40 if (!data)
41 return data;
42
43 // convert 0x5c 0x0a (backslashes at the very end of line) to 0x20 0x20 (spaces)
44 for (char *s = data; (s = strchr(s, '\\')) != NULL; ++s)
45 if ('\n' == s[1]) {
46 s[0] = s[1] = ' ';
47 }
48
49 // init parser
50 parser->line = parser->data = data;
51 parser->lineno = 0;
52
53 return data;
54}
55
56void FAST_FUNC config_close(parser_t *parser)
57{
58 // for now just free config data
59 free(parser->data);
60}
61
62char* FAST_FUNC config_read(parser_t *parser, char **tokens, int ntokens, int mintokens, const char *delims, char comment)
63{
64 char *ret, *line;
65 int noreduce = (ntokens<0); // do not treat subsequent delimiters as one delimiter
66 if (ntokens < 0)
67 ntokens = -ntokens;
68 ret = line = parser->line;
69 // nullify tokens
70 memset(tokens, 0, sizeof(void *) * ntokens);
71 // now split to lines
72 while (*line) {
73 int token_num = 0;
74 // limit the line
75 char *ptr = strchrnul(line, '\n');
76 *ptr++ = '\0';
77 // line number
78 parser->lineno++;
79 // comments mean EOLs
80 if (comment)
81 *strchrnul(line, comment) = '\0';
82 // skip leading delimiters
83 while (*line && strchr(delims, *line))
84 line++;
85 // skip empty lines
86 if (*line) {
87 char *s;
88 // now split line to tokens
89 s = line;
90 while (s) {
91 char *p;
92 // get next token
93 if (token_num+1 >= ntokens)
94 break;
95 p = s;
96 while (*p && !strchr(delims, *p))
97 p++;
98 if (!*p)
99 break;
100 *p++ = '\0';
101 // pin token
102 if (noreduce || *s) {
103 tokens[token_num++] = s;
104//bb_error_msg("L[%d] T[%s]", token_num, s);
105 }
106 s = p;
107 }
108 // non-empty remainder is also a token. So if ntokens == 0, we just return the whole line
109 if (s && (noreduce || *s))
110 tokens[token_num++] = s;
111 // sanity check: have we got all required tokens?
112 if (token_num < mintokens)
113 bb_error_msg_and_die("bad line %u, %d tokens found, %d needed", parser->lineno, token_num, mintokens);
114 // advance data for the next call
115 line = ptr;
116 break;
117 }
118 // line didn't contain any token -> try next line
119 ret = line = ptr;
120 }
121 parser->line = line;
122
123 // return current line. caller must check *ret to determine whether to continue
124 return ret;
125}
126
127#else // stdio-based
128
129FILE* FAST_FUNC config_open(parser_t *parser, const char *filename)
130{
131 // empty file configures nothing!
132 parser->fp = fopen_or_warn(filename, "r");
133 if (!parser->fp)
134 return parser->fp;
135
136 // init parser
137 parser->line = NULL;
138 parser->lineno = 0;
139
140 return parser->fp;
141}
142
143void FAST_FUNC config_close(parser_t *parser)
144{
145 fclose(parser->fp);
146}
147
148char* FAST_FUNC config_read(parser_t *parser, char **tokens, int ntokens, int mintokens, const char *delims, char comment)
149{
150 char *line, *q;
151 int token_num, len;
152 int noreduce = (ntokens < 0); // do not treat subsequent delimiters as one delimiter
153
154 if (ntokens < 0)
155 ntokens = -ntokens;
156
157 // nullify tokens
158 memset(tokens, 0, sizeof(void *) * ntokens);
159
160 // free used line
161 free(parser->line);
162 parser->line = NULL;
163
164 while (1) {
165 int n;
166
167 // get fresh line
168//TODO: speed up xmalloc_fgetline by internally using fgets, not fgetc
169 line = xmalloc_fgetline(parser->fp);
170 if (!line)
171 return line;
172
173 parser->lineno++;
174 // handle continuations. Tito's code stolen :)
175 while (1) {
176 len = strlen(line);
177 if (!len)
178 goto free_and_cont;
179 if (line[len - 1] != '\\')
180 break;
181 // multi-line object
182 line[--len] = '\0';
183//TODO: add xmalloc_fgetline-like iface but with appending to existing str
184 q = xmalloc_fgetline(parser->fp);
185 if (q) {
186 parser->lineno++;
187 line = xasprintf("%s%s", line, q);
188 free(q);
189 }
190 }
191 // comments mean EOLs
192 if (comment) {
193 q = strchrnul(line, comment);
194 *q = '\0';
195 len = q - line;
196 }
197 // skip leading delimiters
198 n = strspn(line, delims);
199 if (n) {
200 len -= n;
201 strcpy(line, line + n);
202 }
203 if (len)
204 break;
205 // skip empty lines
206 free_and_cont:
207 free(line);
208 }
209
210 // non-empty line found, parse and return
211
212 // store line
213 parser->line = line = xrealloc(line, len + 1);
214
215 // now split line to tokens
216//TODO: discard consecutive delimiters?
217 token_num = 0;
218 ntokens--; // now it's max allowed token no
219 while (1) {
220 // get next token
221 if (token_num == ntokens)
222 break;
223 q = line + strcspn(line, delims);
224 if (!*q)
225 break;
226 // pin token
227 *q++ = '\0';
228 if (noreduce || *line) {
229 tokens[token_num++] = line;
230//bb_error_msg("L[%d] T[%s]", token_num, line);
231 }
232 line = q;
233 }
234
235 // non-empty remainder is also a token,
236 // so if ntokens <= 1, we just return the whole line
237 if (noreduce || *line)
238 tokens[token_num++] = line;
239
240 if (token_num < mintokens)
241 bb_error_msg_and_die("bad line %u: %d tokens found, %d needed",
242 parser->lineno, token_num, mintokens);
243
244 return parser->line; // maybe token_num instead?
245}
246
247#endif
diff --git a/util-linux/mdev.c b/util-linux/mdev.c
index dd9522999..c61521cbb 100644
--- a/util-linux/mdev.c
+++ b/util-linux/mdev.c
@@ -25,16 +25,6 @@ struct globals {
25/* We use additional 64+ bytes in make_device() */ 25/* We use additional 64+ bytes in make_device() */
26#define SCRATCH_SIZE 80 26#define SCRATCH_SIZE 80
27 27
28static char *next_field(char *s)
29{
30 char *end = skip_non_whitespace(s);
31 s = skip_whitespace(end);
32 *end = '\0';
33 if (*s == '\0')
34 s = NULL;
35 return s;
36}
37
38/* Builds an alias path. 28/* Builds an alias path.
39 * This function potentionally reallocates the alias parameter. 29 * This function potentionally reallocates the alias parameter.
40 */ 30 */
@@ -104,33 +94,26 @@ static void make_device(char *path, int delete)
104 type = S_IFBLK; 94 type = S_IFBLK;
105 95
106 if (ENABLE_FEATURE_MDEV_CONF) { 96 if (ENABLE_FEATURE_MDEV_CONF) {
107 FILE *fp; 97 parser_t parser;
108 char *line, *val, *next; 98 char *tokens[5];
109 unsigned lineno = 0;
110 99
111 /* If we have config file, look up user settings */ 100 /* If we have config file, look up user settings */
112 fp = fopen_or_warn("/etc/mdev.conf", "r"); 101 if (!config_open(&parser, "/etc/mdev.conf"))
113 if (!fp)
114 goto end_parse; 102 goto end_parse;
115 103
116 while ((line = xmalloc_fgetline(fp)) != NULL) { 104 while (config_read(&parser, tokens, 4, 3, " \t", '#')) {
117 regmatch_t off[1+9*ENABLE_FEATURE_MDEV_RENAME_REGEXP]; 105 regmatch_t off[1+9*ENABLE_FEATURE_MDEV_RENAME_REGEXP];
118 106 char *val;
119 ++lineno;
120 trim(line);
121 if (!line[0])
122 goto next_line;
123 107
124 /* Fields: regex uid:gid mode [alias] [cmd] */ 108 /* Fields: regex uid:gid mode [alias] [cmd] */
125 109
126 /* 1st field: regex to match this device */ 110 /* 1st field: regex to match this device */
127 next = next_field(line);
128 { 111 {
129 regex_t match; 112 regex_t match;
130 int result; 113 int result;
131 114
132 /* Is this it? */ 115 /* Is this it? */
133 xregcomp(&match, line, REG_EXTENDED); 116 xregcomp(&match, tokens[0], REG_EXTENDED);
134 result = regexec(&match, device_name, ARRAY_SIZE(off), off, 0); 117 result = regexec(&match, device_name, ARRAY_SIZE(off), off, 0);
135 regfree(&match); 118 regfree(&match);
136 119
@@ -147,7 +130,7 @@ static void make_device(char *path, int delete)
147 if (result || off[0].rm_so 130 if (result || off[0].rm_so
148 || ((int)off[0].rm_eo != (int)strlen(device_name)) 131 || ((int)off[0].rm_eo != (int)strlen(device_name))
149 ) { 132 ) {
150 goto next_line; 133 continue;
151 } 134 }
152 } 135 }
153 136
@@ -155,15 +138,11 @@ static void make_device(char *path, int delete)
155 * after parsing the rest of fields */ 138 * after parsing the rest of fields */
156 139
157 /* 2nd field: uid:gid - device ownership */ 140 /* 2nd field: uid:gid - device ownership */
158 if (!next) /* field must exist */
159 bb_error_msg_and_die("bad line %u", lineno);
160 val = next;
161 next = next_field(val);
162 { 141 {
163 struct passwd *pass; 142 struct passwd *pass;
164 struct group *grp; 143 struct group *grp;
165 char *str_uid = val; 144 char *str_uid = tokens[1];
166 char *str_gid = strchrnul(val, ':'); 145 char *str_gid = strchrnul(str_uid, ':');
167 146
168 if (*str_gid) 147 if (*str_gid)
169 *str_gid++ = '\0'; 148 *str_gid++ = '\0';
@@ -182,33 +161,30 @@ static void make_device(char *path, int delete)
182 } 161 }
183 162
184 /* 3rd field: mode - device permissions */ 163 /* 3rd field: mode - device permissions */
185 if (!next) /* field must exist */ 164 mode = strtoul(tokens[2], NULL, 8);
186 bb_error_msg_and_die("bad line %u", lineno);
187 val = next;
188 next = next_field(val);
189 mode = strtoul(val, NULL, 8);
190 165
166 val = tokens[3];
191 /* 4th field (opt): >alias */ 167 /* 4th field (opt): >alias */
192#if ENABLE_FEATURE_MDEV_RENAME 168#if ENABLE_FEATURE_MDEV_RENAME
193 if (!next) 169 if (!val)
194 break; 170 break;
195 if (*next == '>' || *next == '=') { 171 aliaslink = *val;
196#if ENABLE_FEATURE_MDEV_RENAME_REGEXP 172 if (aliaslink == '>' || aliaslink == '=') {
197 char *s, *p; 173 char *s, *p;
198 unsigned i, n; 174 unsigned i, n;
199 175 char *a = val;
200 aliaslink = *next; 176 s = strchr(val, ' ');
201 val = next; 177 val = (s && s[1]) ? s+1 : NULL;
202 next = next_field(val); 178#if ENABLE_FEATURE_MDEV_RENAME_REGEXP
203 /* substitute %1..9 with off[1..9], if any */ 179 /* substitute %1..9 with off[1..9], if any */
204 n = 0; 180 n = 0;
205 s = val; 181 s = a;
206 while (*s) 182 while (*s)
207 if (*s++ == '%') 183 if (*s++ == '%')
208 n++; 184 n++;
209 185
210 p = alias = xzalloc(strlen(val) + n * strlen(device_name)); 186 p = alias = xzalloc(strlen(a) + n * strlen(device_name));
211 s = val + 1; 187 s = a + 1;
212 while (*s) { 188 while (*s) {
213 *p = *s; 189 *p = *s;
214 if ('%' == *s) { 190 if ('%' == *s) {
@@ -224,24 +200,20 @@ static void make_device(char *path, int delete)
224 s++; 200 s++;
225 } 201 }
226#else 202#else
227 aliaslink = *next; 203 alias = xstrdup(a + 1);
228 val = next;
229 next = next_field(val);
230 alias = xstrdup(val + 1);
231#endif 204#endif
232 } 205 }
233#endif /* ENABLE_FEATURE_MDEV_RENAME */ 206#endif /* ENABLE_FEATURE_MDEV_RENAME */
234 207
235 /* The rest (opt): command to run */ 208 /* The rest (opt): command to run */
236 if (!next) 209 if (!val)
237 break; 210 break;
238 val = next;
239 if (ENABLE_FEATURE_MDEV_EXEC) { 211 if (ENABLE_FEATURE_MDEV_EXEC) {
240 const char *s = "@$*"; 212 const char *s = "@$*";
241 const char *s2 = strchr(s, *val); 213 const char *s2 = strchr(s, *val);
242 214
243 if (!s2) 215 if (!s2)
244 bb_error_msg_and_die("bad line %u", lineno); 216 bb_error_msg_and_die("bad line %u", parser.lineno);
245 217
246 /* Correlate the position in the "@$*" with the delete 218 /* Correlate the position in the "@$*" with the delete
247 * step so that we get the proper behavior: 219 * step so that we get the proper behavior:
@@ -255,12 +227,9 @@ static void make_device(char *path, int delete)
255 } 227 }
256 /* end of field parsing */ 228 /* end of field parsing */
257 break; /* we found matching line, stop */ 229 break; /* we found matching line, stop */
258 next_line:
259 free(line);
260 } /* end of "while line is read from /etc/mdev.conf" */ 230 } /* end of "while line is read from /etc/mdev.conf" */
261 231
262 free(line); /* in case we used "break" to get here */ 232 config_close(&parser);
263 fclose(fp);
264 } 233 }
265 end_parse: 234 end_parse:
266 235