From e559e0a75738044403e9a43f54ccb0ac3091cd9a Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Tue, 15 Jul 2008 21:09:30 +0000 Subject: 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 --- libbb/parse_config.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 libbb/parse_config.c (limited to 'libbb/parse_config.c') 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 @@ +/* vi: set sw=4 ts=4: */ +/* + * config file parser helper + * + * Copyright (C) 2008 by Vladimir Dronnikov + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" + +/* + +Typical usage: + +----- CUT ----- + char *t[3]; // tokens placeholder + parser_t p; // parser structure + // open file + if (config_open(filename, &p)) { + // parse line-by-line + while (*config_read(&p, t, 3, 0, delimiters, comment_char)) { // 0..3 tokens + // use tokens + bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]); + } + ... + // free parser + config_close(&p); + } +----- CUT ----- + +*/ + +#if !PARSER_STDIO_BASED + +char* FAST_FUNC config_open(parser_t *parser, const char *filename) +{ + // empty file configures nothing! + char *data = xmalloc_open_read_close(filename, NULL); + if (!data) + return data; + + // convert 0x5c 0x0a (backslashes at the very end of line) to 0x20 0x20 (spaces) + for (char *s = data; (s = strchr(s, '\\')) != NULL; ++s) + if ('\n' == s[1]) { + s[0] = s[1] = ' '; + } + + // init parser + parser->line = parser->data = data; + parser->lineno = 0; + + return data; +} + +void FAST_FUNC config_close(parser_t *parser) +{ + // for now just free config data + free(parser->data); +} + +char* FAST_FUNC config_read(parser_t *parser, char **tokens, int ntokens, int mintokens, const char *delims, char comment) +{ + char *ret, *line; + int noreduce = (ntokens<0); // do not treat subsequent delimiters as one delimiter + if (ntokens < 0) + ntokens = -ntokens; + ret = line = parser->line; + // nullify tokens + memset(tokens, 0, sizeof(void *) * ntokens); + // now split to lines + while (*line) { + int token_num = 0; + // limit the line + char *ptr = strchrnul(line, '\n'); + *ptr++ = '\0'; + // line number + parser->lineno++; + // comments mean EOLs + if (comment) + *strchrnul(line, comment) = '\0'; + // skip leading delimiters + while (*line && strchr(delims, *line)) + line++; + // skip empty lines + if (*line) { + char *s; + // now split line to tokens + s = line; + while (s) { + char *p; + // get next token + if (token_num+1 >= ntokens) + break; + p = s; + while (*p && !strchr(delims, *p)) + p++; + if (!*p) + break; + *p++ = '\0'; + // pin token + if (noreduce || *s) { + tokens[token_num++] = s; +//bb_error_msg("L[%d] T[%s]", token_num, s); + } + s = p; + } + // non-empty remainder is also a token. So if ntokens == 0, we just return the whole line + if (s && (noreduce || *s)) + tokens[token_num++] = s; + // sanity check: have we got all required tokens? + if (token_num < mintokens) + bb_error_msg_and_die("bad line %u, %d tokens found, %d needed", parser->lineno, token_num, mintokens); + // advance data for the next call + line = ptr; + break; + } + // line didn't contain any token -> try next line + ret = line = ptr; + } + parser->line = line; + + // return current line. caller must check *ret to determine whether to continue + return ret; +} + +#else // stdio-based + +FILE* FAST_FUNC config_open(parser_t *parser, const char *filename) +{ + // empty file configures nothing! + parser->fp = fopen_or_warn(filename, "r"); + if (!parser->fp) + return parser->fp; + + // init parser + parser->line = NULL; + parser->lineno = 0; + + return parser->fp; +} + +void FAST_FUNC config_close(parser_t *parser) +{ + fclose(parser->fp); +} + +char* FAST_FUNC config_read(parser_t *parser, char **tokens, int ntokens, int mintokens, const char *delims, char comment) +{ + char *line, *q; + int token_num, len; + int noreduce = (ntokens < 0); // do not treat subsequent delimiters as one delimiter + + if (ntokens < 0) + ntokens = -ntokens; + + // nullify tokens + memset(tokens, 0, sizeof(void *) * ntokens); + + // free used line + free(parser->line); + parser->line = NULL; + + while (1) { + int n; + + // get fresh line +//TODO: speed up xmalloc_fgetline by internally using fgets, not fgetc + line = xmalloc_fgetline(parser->fp); + if (!line) + return line; + + parser->lineno++; + // handle continuations. Tito's code stolen :) + while (1) { + len = strlen(line); + if (!len) + goto free_and_cont; + if (line[len - 1] != '\\') + break; + // multi-line object + line[--len] = '\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); + } + } + // comments mean EOLs + if (comment) { + q = strchrnul(line, comment); + *q = '\0'; + len = q - line; + } + // skip leading delimiters + n = strspn(line, delims); + if (n) { + len -= n; + strcpy(line, line + n); + } + if (len) + break; + // skip empty lines + free_and_cont: + free(line); + } + + // non-empty line found, parse and return + + // store line + parser->line = line = xrealloc(line, len + 1); + + // now split line to tokens +//TODO: discard consecutive delimiters? + token_num = 0; + ntokens--; // now it's max allowed token no + while (1) { + // get next token + if (token_num == ntokens) + break; + q = line + strcspn(line, delims); + if (!*q) + break; + // pin token + *q++ = '\0'; + if (noreduce || *line) { + tokens[token_num++] = line; +//bb_error_msg("L[%d] T[%s]", token_num, line); + } + line = q; + } + + // non-empty remainder is also a token, + // so if ntokens <= 1, we just return the whole line + if (noreduce || *line) + tokens[token_num++] = line; + + if (token_num < mintokens) + bb_error_msg_and_die("bad line %u: %d tokens found, %d needed", + parser->lineno, token_num, mintokens); + + return parser->line; // maybe token_num instead? +} + +#endif -- cgit v1.2.3