* Config file parser
*/
enum {
- PARSE_VANILLA = 0x00000000, // trim line, collapse delimiters, warn and continue if less than mintokens
- PARSE_DONT_REDUCE = 0x00010000, // do not treat consecutive delimiters as one
- PARSE_DONT_TRIM = 0x00020000, // do not trim line of leading and trailing delimiters
- PARSE_LAST_IS_GREEDY = 0x00040000, // last token takes whole remainder of the line
-// PARSE_DONT_NULL = 0x00080000, // do not set tokens[] to NULL
- PARSE_MIN_DIE = 0x00100000, // die if less tokens found
+ PARSE_COLLAPSE = 0x00010000, // treat consecutive delimiters as one
+ PARSE_TRIM = 0x00020000, // trim leading and trailing delimiters
+// TODO: COLLAPSE and TRIM seem to always go in pair
+ PARSE_GREEDY = 0x00040000, // last token takes entire remainder of the line
+ PARSE_MIN_DIE = 0x00100000, // die if < min tokens found
// keep a copy of current line
- PARSE_KEEP_COPY = 0x00200000 * ENABLE_DEBUG_CROND_OPTION,
- PARSE_ESCAPE = 0x00400000, // process escape sequences in tokens
+ PARSE_KEEP_COPY = 0x00200000 * ENABLE_DEBUG_CROND_OPTION,
+// PARSE_ESCAPE = 0x00400000, // process escape sequences in tokens
+ // NORMAL is:
+ // * remove leading and trailing delimiters and collapse
+ // multiple delimiters into one
+ // * warn and continue if less than mintokens delimiters found
+ // * grab everything into last token
+ PARSE_NORMAL = PARSE_COLLAPSE | PARSE_TRIM | PARSE_GREEDY,
};
typedef struct parser_t {
FILE *fp;
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
/* sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, "");
-
return;
}
/* optional_tty:ignored_runlevel:action:command
* Delims are not to be collapsed and need exactly 4 tokens
*/
- while (config_read(parser, token, 4, 0, "#:", PARSE_DONT_TRIM|PARSE_DONT_REDUCE|PARSE_LAST_IS_GREEDY)) {
+ while (config_read(parser, token, 4, 0, "#:",
+ PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) {
int action;
char *tty = token[0];
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;
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)
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) {
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) {
// skip empty line
free(line);
}
-
// non-empty line found, parse and return the number of tokens
// store line
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);
*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) {
}
*line = '\0';
}
+#endif
}
line = q;
//bb_info_msg("A[%s]", line);
parser->lineno, ii, mintokens);
if (flags & PARSE_MIN_DIE)
xfunc_die();
+ ntokens++;
goto again;
}
#if ENABLE_FEATURE_SECURETTY && !ENABLE_PAM
static int check_securetty(void)
{
- char *buf;
- int ret = 1;
+ char *buf = (char*)"/etc/securetty"; /* any non-NULL is ok */
parser_t *parser = config_open2("/etc/securetty", fopen_for_read);
- /* N.B. A missing securetty file is not an error. */
- if (parser) {
- while (config_read(parser, &buf, 1, 1, "# \t", 0)) {
- if (strcmp(buf, short_tty) == 0)
- break;
- }
- config_close(parser);
- // buf != NULL here iff config file was empty (OK) or buf equals short_tty (OK)
- ret = buf != NULL;
+ while (config_read(parser, &buf, 1, 1, "# \t", PARSE_NORMAL)) {
+ if (strcmp(buf, short_tty) == 0)
+ break;
+ buf = NULL;
}
- return ret;
+ config_close(parser);
+ /* buf != NULL here if config file was not found, empty
+ * or line was found which equals short_tty */
+ return buf != NULL;
}
#else
static ALWAYS_INLINE int check_securetty(void) { return 1; }
file->cf_User = xstrdup(fileName);
pline = &file->cf_LineBase;
- while (--maxLines
- && (n = config_read(parser, tokens, 6, 1, "# \t", PARSE_LAST_IS_GREEDY|PARSE_KEEP_COPY))
- ) {
+ while (1) {
CronLine *line;
+ if (!--maxLines)
+ break;
+ n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY);
+ if (!n)
+ break;
+
if (DebugOpt)
crondlog(LVL5 "user:%s entry:%s", fileName, parser->data);
/* check if a minimum of tokens is specified */
if (n < 6)
continue;
- *pline = line = xzalloc(sizeof(CronLine));
+ *pline = line = xzalloc(sizeof(*line));
/* parse date ranges */
ParseField(file->cf_User, line->cl_Mins, 60, 0, NULL, tokens[0]);
ParseField(file->cf_User, line->cl_Hrs, 24, 0, NULL, tokens[1]);
"DEBUG\0"
#endif
;
-
char *token[2];
parser_t *parser = config_open2(cfg_filename, xfopen_stdin);
- while (config_read(parser, token, 2, 2, "#=", PARSE_MIN_DIE)) {
+ while (config_read(parser, token, 2, 2, "#=",
+ (PARSE_NORMAL | PARSE_MIN_DIE) & ~(PARSE_TRIM | PARSE_COLLAPSE))) {
unsigned val = xatoi_u(token[1]);
int i = index_in_strings(param_names, token[0]);
if (i < 0)
char *cur_path, *cur_sect;
int count_mp, cur_mp;
int opt, not_found;
+ char *token[2];
opt_complementary = "-1"; /* at least one argument */
opt = getopt32(argv, "+aw");
int ret = 0;
char *s;
parser_t *parser = config_open2("/proc/modules", xfopen_for_read);
- while (config_read(parser, &s, 1, 1, "# \t", 0)) {
+ while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
if (strcmp(s, name) == 0) {
ret = 1;
break;
*/
static void dnsentryinit(void)
{
+ char *token[2];
parser_t *parser;
struct dns_entry *m, *prev;
prev = dnsentry = NULL;
parser = config_open(fileconf);
- if (parser) {
- char *token[2];
- while (config_read(parser, token, 2, 2, "# \t", 0)) {
- unsigned int a,b,c,d;
- /*
- * Assumes all host names are lower case only
- * Hostnames with more than one label are not handled correctly.
- * Presently the dot is copied into name without
- * converting to a length/string substring for that label.
- */
-// if (!token[1] || sscanf(token[1], ".%u.%u.%u.%u"+1, &a, &b, &c, &d) != 4)
- if (sscanf(token[1], ".%u.%u.%u.%u"+1, &a, &b, &c, &d) != 4)
- continue;
-
- m = xzalloc(sizeof(*m));
- /*m->next = NULL;*/
- sprintf(m->ip, ".%u.%u.%u.%u"+1, a, b, c, d);
- sprintf(m->rip, ".%u.%u.%u.%u", d, c, b, a);
- undot((uint8_t*)m->rip);
- convname(m->name, (uint8_t*)token[0]);
-
- if (OPT_verbose)
- fprintf(stderr, "\tname:%s, ip:%s\n", &(m->name[1]), m->ip);
-
- if (prev == NULL)
- dnsentry = m;
- else
- prev->next = m;
- prev = m;
- }
- config_close(parser);
+ while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
+ unsigned a, b, c, d;
+ /*
+ * Assumes all host names are lower case only
+ * Hostnames with more than one label are not handled correctly.
+ * Presently the dot is copied into name without
+ * converting to a length/string substring for that label.
+ */
+// if (!token[1] || sscanf(token[1], ".%u.%u.%u.%u"+1, &a, &b, &c, &d) != 4)
+ if (sscanf(token[1], ".%u.%u.%u.%u"+1, &a, &b, &c, &d) != 4)
+ continue;
+
+ m = xzalloc(sizeof(*m));
+ /*m->next = NULL;*/
+ sprintf(m->ip, ".%u.%u.%u.%u"+1, a, b, c, d);
+ sprintf(m->rip, ".%u.%u.%u.%u", d, c, b, a);
+ undot((uint8_t*)m->rip);
+ convname(m->name, (uint8_t*)token[0]);
+
+ if (OPT_verbose)
+ fprintf(stderr, "\tname:%s, ip:%s\n", &(m->name[1]), m->ip);
+
+ if (prev == NULL)
+ dnsentry = m;
+ else
+ prev->next = m;
+ prev = m;
}
+ config_close(parser);
}
/*
return;
if (isfile) {
parser_t *parser = config_open2(s, xfopen_for_read);
- while (config_read(parser, &s, 1, 1, "# \t", 0)) {
+ while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
do_sethostname(s, 0);
}
if (ENABLE_FEATURE_CLEAN_UP)
{
char *token[2];
parser_t *parser = config_open2(file, fopen_for_read);
- if (!parser)
- return;
- while (config_read(parser, token, 2, 2, "# \t", 0)) {
+ while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
int id = bb_strtou(token[0], NULL, 0);
if (id < 0 || id > size) {
bb_error_msg("database %s is corrupted at line %d",
prepend_new_eth_table(&clist, ifname, *argv++);
}
} else {
+ char *tokens[2];
struct parser_t *parser = config_open(fname);
- if (parser) {
- char *tokens[2];
- while (config_read(parser, tokens, 2, 2, "# \t", 0))
- prepend_new_eth_table(&clist, tokens[0], tokens[1]);
- config_close(parser);
- }
+ while (config_read(parser, tokens, 2, 2, "# \t", PARSE_NORMAL))
+ prepend_new_eth_table(&clist, tokens[0], tokens[1]);
+ config_close(parser);
}
ctl_sk = xsocket(PF_INET, SOCK_DGRAM, 0);
keywords[i].handler(keywords[i].def, keywords[i].var);
parser = config_open(file);
- if (!parser)
- return;
-
- while (config_read(parser, token, 2, 2, "# \t", PARSE_LAST_IS_GREEDY)) {
+ while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
if (!strcasecmp(token[0], k->keyword)) {
if (!k->handler(token[1], k->var)) {
parser_t *parser;
parser = config_open(filename);
- if (!parser)
- return 1;
-
- while (config_read(parser, token, 2, 2, "# \t=", PARSE_LAST_IS_GREEDY)) { // TODO: ';' is comment char too
+ while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) { // TODO: ';' is comment char too
// if (!token[1]) {
// bb_error_msg(WARN_BAD_LINE, filename, parser->lineno);
// } else {
pc[0] = fc[0] = NULL;
parser = config_open("/etc/sestatus.conf");
- if (!parser)
- return;
-
- while (config_read(parser, &buf, 1, 1, "# \t", PARSE_LAST_IS_GREEDY)) {
+ while (config_read(parser, &buf, 1, 1, "# \t", PARSE_NORMAL)) {
if (strcmp(buf, "[process]") == 0) {
section = 1;
} else if (strcmp(buf, "[files]") == 0) {
. testing.sh
-NO_REDUCE=65536
-NO_TRIM=131072
-GREEDY=262144
+COLLAPSE=$(( 0x00010000))
+TRIM=$(( 0x00020000))
+GREEDY=$(( 0x00040000))
+MIN_DIE=$(( 0x00100000))
+KEEP_COPY=$((0x00200000))
+ESCAPE=$(( 0x00400000))
+NORMAL=$(( COLLAPSE | TRIM | GREEDY))
# testing "description" "command" "result" "infile" "stdin"
-testing "mdev.conf" \
- "parse -n 4 -m 3 -f $GREEDY -" \
+testing "parse mdev.conf" \
+ "parse -n 4 -m 3 -f $((NORMAL)) -" \
"[sda][0:0][644][@echo @echo TEST]\n" \
"-" \
" sda 0:0 644 @echo @echo TEST # echo trap\n"
-testing "notrim" \
- "parse -n 4 -m 3 -f $(($GREEDY+$NO_TRIM)) -" \
+testing "parse notrim" \
+ "parse -n 4 -m 3 -f $((NORMAL - TRIM - COLLAPSE)) -" \
"[][sda][0:0][644 @echo @echo TEST ]\n" \
"-" \
" sda 0:0 644 @echo @echo TEST \n"
[/dev/cdrom][/cdrom][iso9660][ro,user,noauto,nohide][0][0]
[/dev/hdb5][/redhat][ext2][rw,root,noauto,nohide][0][0]
[/dev/hdb6][/win2home][ntfs][rw,root,noauto,nohide][0][0]
-[/dev/hdb7][/win2skul][ntfs][rw,root,noauto,nohide][none][0]
+[/dev/hdb7][/win2skul][ntfs][rw,root,noauto,nohide][none][0 0]
[none][/dev/pts][devpts][gid=5,mode=620][0][0]
[none][/proc][proc][defaults][0][0]
EOF
-testing "polluted fstab" \
+testing "parse polluted fstab" \
"parse -n 6 -m 6 $FILE" \
"`cat $FILE.res`\n" \
"" \
[][][shutdown][/sbin/swapoff -a]
EOF
-testing "inittab from examples" \
- "parse -n 4 -m 4 -f $(($GREEDY+$NO_TRIM)) -d'#:' $FILE" \
+testing "parse inittab from examples" \
+ "parse -n 4 -m 4 -f $((NORMAL - TRIM - COLLAPSE)) -d'#:' $FILE" \
"`cat $FILE.res`\n" \
"" \
""
[option][lease][864000]
EOF
-testing "udhcpd.conf from examples" \
+testing "parse udhcpd.conf from examples" \
"parse -n 127 $FILE" \
"`cat $FILE.res`\n" \
"" \
static void bb_dump_addfile(dumper_t *dumper, char *name)
{
- parser_t *parser = config_open2(name, xfopen_for_read);
- while (config_read(parser, &name, 1, 1, "# \t", 0)) {
- bb_dump_add(dumper, name);
+ char *p;
+ FILE *fp;
+ char *buf;
+
+ fp = xfopen_for_read(name);
+ while ((buf = xmalloc_fgetline(fp)) != NULL) {
+ p = skip_whitespace(buf);
+ if (*p && (*p != '#')) {
+ bb_dump_add(dumper, p);
+ }
+ free(buf);
}
- config_close(parser);
+ fclose(fp);
}
static const char *const add_strings[] = {
parser = config_open2("/etc/mdev.conf", fopen_for_read);
/* If we have config file, look up user settings */
- if (!parser)
- goto end_parse;
-
- while (config_read(parser, tokens, 4, 3, "# \t", PARSE_LAST_IS_GREEDY)) {
+ while (config_read(parser, tokens, 4, 3, "# \t", PARSE_NORMAL)) {
regmatch_t off[1 + 9*ENABLE_FEATURE_MDEV_RENAME_REGEXP];
char *val;
} /* end of "while line is read from /etc/mdev.conf" */
config_close(parser);
- end_parse:
#endif /* ENABLE_FEATURE_MDEV_CONF */
if (!delete && sscanf(dev_maj_min, "%u:%u", &major, &minor) == 2) {