1 /* vi: set sw=4 ts=4: */
3 * config file parser helper
5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
13 int parse_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
14 int parse_main(int argc UNUSED_PARAM, char **argv)
16 const char *delims = "# \t";
18 int mintokens = 0, ntokens = 128;
19 opt_complementary = "-1:n+:m+:f+";
20 getopt32(argv, "n:m:d:f:", &ntokens, &mintokens, &delims, &flags);
24 parser_t *p = config_open(*argv);
27 char **t = xmalloc(sizeof(char *) * ntokens);
28 while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) != 0) {
29 for (int i = 0; i < n; ++i)
46 char *t[3]; // tokens placeholder
47 parser_t *p = config_open(filename);
50 while (config_read(p, t, 3, 0, delimiters, flags)) { // 1..3 tokens
52 bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]);
62 parser_t* FAST_FUNC config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path))
64 parser_t *parser = xzalloc(sizeof(parser_t));
65 /* empty file configures nothing */
66 parser->fp = fopen_func(filename);
73 parser_t* FAST_FUNC config_open(const char *filename)
75 return config_open2(filename, fopen_or_warn_stdin);
78 static void config_free_data(parser_t *const parser)
82 if (PARSE_KEEP_COPY) { /* compile-time constant */
88 void FAST_FUNC config_close(parser_t *parser)
90 config_free_data(parser);
95 1. Read a line from config file. If nothing to read then bail out returning 0.
96 Handle continuation character. Advance lineno for each physical line. Cut comments.
97 2. if PARSE_DONT_TRIM is not set (default) skip leading and cut trailing delimiters, if any.
98 3. If resulting line is empty goto 1.
99 4. Look for first delimiter. If PARSE_DONT_REDUCE or PARSE_DONT_TRIM is set then pin empty token.
100 5. Else (default) if number of seen tokens is equal to max number of tokens (token is the last one)
101 and PARSE_LAST_IS_GREEDY is set then pin the remainder of the line as the last token.
102 Else (token is not last or PARSE_LAST_IS_GREEDY is not set) just replace first delimiter with '\0'
103 thus delimiting token and pin it.
104 6. Advance line pointer past the end of token. If number of seen tokens is less than required number
105 of tokens then goto 4.
106 7. Control the number of seen tokens is not less the min number of tokens. Die if condition is not met.
107 8. Return the number of seen tokens.
109 mintokens > 0 make config_read() exit with error message if less than mintokens
110 (but more than 0) are found. Empty lines are always skipped (not warned about).
113 int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims)
116 char comment = *delims++;
118 int ntokens = flags & 0xFF;
119 int mintokens = (flags & 0xFF00) >> 8;
122 // N.B. this could only be used in read-in-one-go version, or when tokens use xstrdup(). TODO
123 //if (!parser->lineno || !(flags & PARSE_DONT_NULL))
124 memset(tokens, 0, sizeof(tokens[0]) * ntokens);
125 config_free_data(parser);
128 //TODO: speed up xmalloc_fgetline by internally using fgets, not fgetc
129 line = xmalloc_fgetline(parser->fp);
134 // handle continuations. Tito's code stolen :)
139 if (line[ii - 1] != '\\')
143 //TODO: add xmalloc_fgetline-like iface but with appending to existing str
144 q = xmalloc_fgetline(parser->fp);
147 line = xasprintf("%s%s", line, q);
151 // comments mean EOLs
153 q = strchrnul(line, comment);
157 // skip leading and trailing delimiters
158 if (!(flags & PARSE_DONT_TRIM)) {
160 int n = strspn(line, delims);
163 overlapping_strcpy(line, line + n);
167 while (strchr(delims, line[--ii]))
172 // if something still remains -> return it
181 // non-empty line found, parse and return the number of tokens
184 parser->line = line = xrealloc(line, ii + 1);
185 if (flags & PARSE_KEEP_COPY) {
186 parser->data = xstrdup(line);
189 // split line to tokens
190 ntokens--; // now it's max allowed token no
191 // N.B. non-empty remainder is also a token,
192 // so if ntokens <= 1, we just return the whole line
193 // N.B. if PARSE_LAST_IS_GREEDY is set the remainder of the line is stuck to the last token
194 for (ii = 0; *line && ii <= ntokens; ) {
195 //bb_info_msg("L[%s]", line);
197 // at the last token and need greedy token ->
198 if ((flags & PARSE_LAST_IS_GREEDY) && (ii == ntokens)) {
199 // skip possible delimiters
200 if (!(flags & PARSE_DONT_REDUCE))
201 line += strspn(line, delims);
202 // don't cut the line
203 q = line + strlen(line);
205 // vanilla token. cut the line at the first delim
206 q = line + strcspn(line, delims);
207 if (*q) // watch out: do not step past the line end!
211 if ((flags & (PARSE_DONT_REDUCE|PARSE_DONT_TRIM)) || *line) {
212 //bb_info_msg("N[%d] T[%s]", ii, line);
214 // process escapes in token
215 if (flags & PARSE_ESCAPE) {
220 *line++ = bb_process_escape_sequence((const char **)&s);
229 //bb_info_msg("A[%s]", line);
232 if (ii < mintokens) {
233 bb_error_msg("bad line %u: %d tokens found, %d needed",
234 parser->lineno, ii, mintokens);
235 if (flags & PARSE_MIN_DIE)