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
This commit is contained in:
Denis Vlasenko
2008-07-26 23:08:31 +00:00
parent 8895c2073e
commit 084266ed52
18 changed files with 160 additions and 142 deletions

View File

@@ -14,8 +14,9 @@ int parse_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
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 @@ Typical usage:
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 @@ static void config_free_data(parser_t *const parser)
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 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const
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 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const
// skip empty line
free(line);
}
// non-empty line found, parse and return the number of tokens
// store line
@@ -190,14 +204,15 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const
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 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const
*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 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const
}
*line = '\0';
}
#endif
}
line = q;
//bb_info_msg("A[%s]", line);
@@ -234,6 +251,7 @@ int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const
parser->lineno, ii, mintokens);
if (flags & PARSE_MIN_DIE)
xfunc_die();
ntokens++;
goto again;
}