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
diff --git a/libbb/parse_config.c b/libbb/parse_config.c
index 5f6dbbd..3945501 100644
--- a/libbb/parse_config.c
+++ b/libbb/parse_config.c
@@ -9,23 +9,51 @@
#include "libbb.h"
+#if ENABLE_PARSE
+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;
+ int mintokens = 0, ntokens = 128;
+ opt_complementary = "-1:n+:m+:f+";
+ getopt32(argv, "n:m:d:f:", &ntokens, &mintokens, &delims, &flags);
+ //argc -= optind;
+ argv += optind;
+ while (*argv) {
+ parser_t *p = config_open(*argv);
+ if (p) {
+ int n;
+ char **t = xmalloc(sizeof(char *) * ntokens);
+ while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) > 0) {
+ for (int i = 0; i < n; ++i)
+ printf("[%s]", t[i]);
+ puts("");
+ }
+ config_close(p);
+ }
+ argv++;
+ }
+ return EXIT_SUCCESS;
+}
+#endif
+
/*
Typical usage:
----- CUT -----
char *t[3]; // tokens placeholder
- parser_t p; // parser structure
- // open file
- if (config_open(filename, &p)) {
+ parser_t *p = config_open(filename);
+ if (p) {
// parse line-by-line
- while (*config_read(&p, t, 3, 0, delimiters, comment_char) >= 0) { // 0..3 tokens
+ while (config_read(p, t, 3, 0, delimiters, flags)) { // 1..3 tokens
// use tokens
bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]);
}
...
// free parser
- config_close(&p);
+ config_close(p);
}
----- CUT -----
@@ -35,44 +63,69 @@
{
parser_t *parser = xzalloc(sizeof(parser_t));
/* empty file configures nothing */
- parser->fp = fopen_or_warn(filename, "r");
+ parser->fp = fopen_or_warn_stdin(filename);
if (parser->fp)
return parser;
- config_close (parser);
if (ENABLE_FEATURE_CLEAN_UP)
- free(parser);
+ free(parser);
return NULL;
}
static void config_free_data(parser_t *const parser)
{
free(parser->line);
- free(parser->data);
- parser->line = parser->data = NULL;
+ parser->line = NULL;
+ USE_FEATURE_PARSE_COPY(
+ free(parser->data);
+ parser->data = NULL;
+ )
}
+
void FAST_FUNC config_close(parser_t *parser)
{
config_free_data(parser);
fclose(parser->fp);
}
-int FAST_FUNC config_read(parser_t *parser, char **tokens, int ntokens, int mintokens, const char *delims, char comment)
+/*
+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.
+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.
+8. Return the number of seen tokens.
+
+mintokens > 0 make config_read() exit with 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;
- int ii, seen;
- /* do not treat consecutive delimiters as one delimiter */
- bool noreduce = (ntokens < 0);
- if (noreduce)
- ntokens = -ntokens;
+ char comment = *delims++;
+ int ii;
+ int ntokens = flags & 0xFF;
+ int mintokens = (flags & 0xFF00) >> 8;
- memset(tokens, 0, sizeof(tokens[0]) * ntokens);
+ /*
+ // 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);
config_free_data(parser);
while (1) {
//TODO: speed up xmalloc_fgetline by internally using fgets, not fgetc
line = xmalloc_fgetline(parser->fp);
if (!line)
- return -1;
+ return 0;
parser->lineno++;
// handle continuations. Tito's code stolen :)
@@ -98,12 +151,22 @@
*q = '\0';
ii = q - line;
}
- // skip leading delimiters
- seen = strspn(line, delims);
- if (seen) {
- ii -= seen;
- strcpy(line, line + seen);
+ // skip leading and trailing delimiters
+ if (!(flags & PARSE_DONT_TRIM)) {
+ // skip leading
+ int n = strspn(line, delims);
+ if (n) {
+ ii -= n;
+ strcpy(line, line + n);
+ }
+ // cut trailing
+ if (ii) {
+ while (strchr(delims, line[--ii]))
+ continue;
+ line[++ii] = '\0';
+ }
}
+ // if something still remains -> return it
if (ii)
break;
@@ -112,36 +175,39 @@
free(line);
}
- // non-empty line found, parse and return
+ // non-empty line found, parse and return the number of tokens
// store line
parser->line = line = xrealloc(line, ii + 1);
- parser->data = xstrdup(line);
+ USE_FEATURE_PARSE_COPY(
+ parser->data = xstrdup(line);
+ )
/* now split line to tokens */
- ii = noreduce ? seen : 0;
ntokens--; // now it's max allowed token no
- while (1) {
+ // 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; ) {
+ //bb_info_msg("L[%s]", line);
// get next token
- if (ii == ntokens)
- break;
- q = line + strcspn(line, delims);
- if (!*q)
- break;
+ // at the last token and need greedy token ->
+ if ((flags & PARSE_LAST_IS_GREEDY) && (ii == ntokens)) {
+ // ... don't cut the line
+ q = line + strlen(line);
+ } else {
+ // vanilla token. cut the line at the first delim
+ q = line + strcspn(line, delims);
+ *q++ = '\0';
+ }
// pin token
- *q++ = '\0';
- if (noreduce || *line) {
+ if ((flags & (PARSE_DONT_REDUCE|PARSE_DONT_TRIM)) || *line) {
+ //bb_info_msg("N[%d] T[%s]", ii, line);
tokens[ii++] = line;
-//bb_info_msg("L[%d] T[%s]\n", ii, line);
}
line = q;
}
- // non-empty remainder is also a token,
- // so if ntokens <= 1, we just return the whole line
- if (noreduce || *line)
- tokens[ii++] = line;
-
if (ii < mintokens)
bb_error_msg_and_die("bad line %u: %d tokens found, %d needed",
parser->lineno, ii, mintokens);