fix several problems with config parser:
a bug where it underflows the string
a bug where it never frees parser_t struct
make read_config() return 0 if parser is NULL,
make config_close() accept and ignore NULL parser -
eliminates many if() blocks
reverse the sense of parser bit flags - negative flags
are harder to grok.
hexdump: revert the change to use config parser, it is BIGGER
and also requires additional quirks in parser
*: explicitly use PARSER_NORMAL instead of 0
function old new delta
login_main 1575 1596 +21
config_close 18 29 +11
bbunpack 383 391 +8
qgravechar 106 109 +3
rtnl_tab_initialize 121 117 -4
expand 1697 1693 -4
man_main 717 712 -5
nameif_main 674 668 -6
hexdump_main 597 591 -6
read_config 217 209 -8
dnsd_main 1478 1470 -8
sysctl_main 203 189 -14
config_open2 44 25 -19
make_device 1177 1141 -36
config_read 597 549 -48
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 4/11 up/down: 43/-158) Total: -115 bytes
diff --git a/libbb/parse_config.c b/libbb/parse_config.c
index 83dc997..ace6f3a 100644
--- a/libbb/parse_config.c
+++ b/libbb/parse_config.c
@@ -14,8 +14,9 @@
int parse_main(int argc UNUSED_PARAM, char **argv)
{
const char *delims = "# \t";
- unsigned flags = 0;
+ unsigned flags = PARSE_NORMAL;
int mintokens = 0, ntokens = 128;
+
opt_complementary = "-1:n+:m+:f+";
getopt32(argv, "n:m:d:f:", &ntokens, &mintokens, &delims, &flags);
//argc -= optind;
@@ -61,13 +62,15 @@
parser_t* FAST_FUNC config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path))
{
- parser_t *parser = xzalloc(sizeof(parser_t));
- /* empty file configures nothing */
- parser->fp = fopen_func(filename);
- if (parser->fp)
- return parser;
- free(parser);
- return NULL;
+ FILE* fp;
+ parser_t *parser;
+
+ fp = fopen_func(filename);
+ if (!fp)
+ return NULL;
+ parser = xzalloc(sizeof(*parser));
+ parser->fp = fp;
+ return parser;
}
parser_t* FAST_FUNC config_open(const char *filename)
@@ -87,41 +90,53 @@
void FAST_FUNC config_close(parser_t *parser)
{
- config_free_data(parser);
- fclose(parser->fp);
+ if (parser) {
+ config_free_data(parser);
+ fclose(parser->fp);
+ free(parser);
+ }
}
/*
-1. Read a line from config file. If nothing to read then bail out returning 0.
- Handle continuation character. Advance lineno for each physical line. Cut comments.
-2. if PARSE_DONT_TRIM is not set (default) skip leading and cut trailing delimiters, if any.
+0. If parser is NULL return 0.
+1. Read a line from config file. If nothing to read then return 0.
+ Handle continuation character. Advance lineno for each physical line.
+ Discard everything past comment characher.
+2. if PARSE_TRIM is set (default), remove leading and trailing delimiters.
3. If resulting line is empty goto 1.
-4. Look for first delimiter. If PARSE_DONT_REDUCE or PARSE_DONT_TRIM is set then pin empty token.
-5. Else (default) if number of seen tokens is equal to max number of tokens (token is the last one)
- and PARSE_LAST_IS_GREEDY is set then pin the remainder of the line as the last token.
- Else (token is not last or PARSE_LAST_IS_GREEDY is not set) just replace first delimiter with '\0'
- thus delimiting token and pin it.
-6. Advance line pointer past the end of token. If number of seen tokens is less than required number
- of tokens then goto 4.
-7. Control the number of seen tokens is not less the min number of tokens. Die if condition is not met.
+4. Look for first delimiter. If !PARSE_COLLAPSE or !PARSE_TRIM is set then
+ remember the token as empty.
+5. Else (default) if number of seen tokens is equal to max number of tokens
+ (token is the last one) and PARSE_GREEDY is set then the remainder
+ of the line is the last token.
+ Else (token is not last or PARSE_GREEDY is not set) just replace
+ first delimiter with '\0' thus delimiting the token.
+6. Advance line pointer past the end of token. If number of seen tokens
+ is less than required number of tokens then goto 4.
+7. Check the number of seen tokens is not less the min number of tokens.
+ Complain or die otherwise depending on PARSE_MIN_DIE.
8. Return the number of seen tokens.
-mintokens > 0 make config_read() exit with error message if less than mintokens
+mintokens > 0 make config_read() print error message if less than mintokens
(but more than 0) are found. Empty lines are always skipped (not warned about).
*/
#undef config_read
int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims)
{
char *line, *q;
- char comment = *delims++;
+ char comment;
int ii;
- int ntokens = flags & 0xFF;
- int mintokens = (flags & 0xFF00) >> 8;
+ int ntokens;
+ int mintokens;
+
+ comment = *delims++;
+ ntokens = flags & 0xFF;
+ mintokens = (flags & 0xFF00) >> 8;
again:
- // N.B. this could only be used in read-in-one-go version, or when tokens use xstrdup(). TODO
- //if (!parser->lineno || !(flags & PARSE_DONT_NULL))
- memset(tokens, 0, sizeof(tokens[0]) * ntokens);
+ memset(tokens, 0, sizeof(tokens[0]) * ntokens);
+ if (!parser)
+ return 0;
config_free_data(parser);
while (1) {
@@ -142,20 +157,20 @@
line[--ii] = '\0';
//TODO: add xmalloc_fgetline-like iface but with appending to existing str
q = xmalloc_fgetline(parser->fp);
- if (q) {
- parser->lineno++;
- line = xasprintf("%s%s", line, q);
- free(q);
- }
+ if (!q)
+ break;
+ parser->lineno++;
+ line = xasprintf("%s%s", line, q);
+ free(q);
}
- // comments mean EOLs
+ // discard comments
if (comment) {
q = strchrnul(line, comment);
*q = '\0';
ii = q - line;
}
// skip leading and trailing delimiters
- if (!(flags & PARSE_DONT_TRIM)) {
+ if (flags & PARSE_TRIM) {
// skip leading
int n = strspn(line, delims);
if (n) {
@@ -177,7 +192,6 @@
// skip empty line
free(line);
}
-
// non-empty line found, parse and return the number of tokens
// store line
@@ -190,14 +204,15 @@
ntokens--; // now it's max allowed token no
// N.B. non-empty remainder is also a token,
// so if ntokens <= 1, we just return the whole line
- // N.B. if PARSE_LAST_IS_GREEDY is set the remainder of the line is stuck to the last token
- for (ii = 0; *line && ii <= ntokens; ) {
+ // N.B. if PARSE_GREEDY is set the remainder of the line is stuck to the last token
+ ii = 0;
+ while (*line && ii <= ntokens) {
//bb_info_msg("L[%s]", line);
// get next token
- // at the last token and need greedy token ->
- if ((flags & PARSE_LAST_IS_GREEDY) && (ii == ntokens)) {
+ // at last token and need greedy token ->
+ if ((flags & PARSE_GREEDY) && (ii == ntokens)) {
// skip possible delimiters
- if (!(flags & PARSE_DONT_REDUCE))
+ if (flags & PARSE_COLLAPSE)
line += strspn(line, delims);
// don't cut the line
q = line + strlen(line);
@@ -208,10 +223,11 @@
*q++ = '\0';
}
// pin token
- if ((flags & (PARSE_DONT_REDUCE|PARSE_DONT_TRIM)) || *line) {
+ if (!(flags & (PARSE_COLLAPSE | PARSE_TRIM)) || *line) {
//bb_info_msg("N[%d] T[%s]", ii, line);
tokens[ii++] = line;
// process escapes in token
+#if 0 // unused so far
if (flags & PARSE_ESCAPE) {
char *s = line;
while (*s) {
@@ -224,6 +240,7 @@
}
*line = '\0';
}
+#endif
}
line = q;
//bb_info_msg("A[%s]", line);
@@ -234,6 +251,7 @@
parser->lineno, ii, mintokens);
if (flags & PARSE_MIN_DIE)
xfunc_die();
+ ntokens++;
goto again;
}