source: branches/3.2/mindi-busybox/libbb/parse_config.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
  • Property svn:eol-style set to native
File size: 6.2 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * config file parser helper
4 *
5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 * Also for use in uClibc (http://uclibc.org/) licensed under LGPLv2.1 or later.
9 */
10
11/* Uncomment to enable test applet */
12////config:config PARSE
13////config: bool "Uniform config file parser debugging applet: parse"
14////config: default n
15////config: help
16////config:   Typical usage of parse API:
17////config:     char *t[3];
18////config:     parser_t *p = config_open(filename);
19////config:     while (config_read(p, t, 3, 0, delimiters, flags)) { // 1..3 tokens
20////config:         bb_error_msg("TOKENS: '%s''%s''%s'", t[0], t[1], t[2]);
21////config:     }
22////config:     config_close(p);
23
24////applet:IF_PARSE(APPLET(parse, BB_DIR_USR_BIN, BB_SUID_DROP))
25
26//kbuild:lib-y += parse_config.o
27
28//usage:#define parse_trivial_usage
29//usage:       "[-x] [-n MAXTOKENS] [-m MINTOKENS] [-d DELIMS] [-f FLAGS] FILE..."
30//usage:#define parse_full_usage "\n\n"
31//usage:       "    -x  Suppress output (for benchmarking)"
32
33#include "libbb.h"
34
35#if defined ENABLE_PARSE && ENABLE_PARSE
36int parse_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
37int parse_main(int argc UNUSED_PARAM, char **argv)
38{
39    const char *delims = "# \t";
40    char **t;
41    unsigned flags = PARSE_NORMAL;
42    int mintokens = 0, ntokens = 128;
43    unsigned noout;
44
45    opt_complementary = "-1:n+:m+:f+";
46    noout = 1 & getopt32(argv, "xn:m:d:f:", &ntokens, &mintokens, &delims, &flags);
47    //argc -= optind;
48    argv += optind;
49
50    t = xmalloc(sizeof(t[0]) * ntokens);
51    while (*argv) {
52        int n;
53        parser_t *p = config_open(*argv);
54        while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) != 0) {
55            if (!noout) {
56                for (int i = 0; i < n; ++i)
57                    printf("[%s]", t[i]);
58                puts("");
59            }
60        }
61        config_close(p);
62        argv++;
63    }
64    return EXIT_SUCCESS;
65}
66#endif
67
68parser_t* FAST_FUNC config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path))
69{
70    FILE* fp;
71    parser_t *parser;
72
73    fp = fopen_func(filename);
74    if (!fp)
75        return NULL;
76    parser = xzalloc(sizeof(*parser));
77    parser->fp = fp;
78    return parser;
79}
80
81parser_t* FAST_FUNC config_open(const char *filename)
82{
83    return config_open2(filename, fopen_or_warn_stdin);
84}
85
86void FAST_FUNC config_close(parser_t *parser)
87{
88    if (parser) {
89        if (PARSE_KEEP_COPY) /* compile-time constant */
90            free(parser->data);
91        fclose(parser->fp);
92        free(parser->line);
93        free(parser->nline);
94        free(parser);
95    }
96}
97
98/* This function reads an entire line from a text file,
99 * up to a newline, exclusive.
100 * Trailing '\' is recognized as line continuation.
101 * Returns -1 if EOF/error.
102 */
103static int get_line_with_continuation(parser_t *parser)
104{
105    ssize_t len, nlen;
106    char *line;
107
108    len = getline(&parser->line, &parser->line_alloc, parser->fp);
109    if (len <= 0)
110        return len;
111
112    line = parser->line;
113    for (;;) {
114        parser->lineno++;
115        if (line[len - 1] == '\n')
116            len--;
117        if (len == 0 || line[len - 1] != '\\')
118            break;
119        len--;
120
121        nlen = getline(&parser->nline, &parser->nline_alloc, parser->fp);
122        if (nlen <= 0)
123            break;
124
125        if (parser->line_alloc < len + nlen + 1) {
126            parser->line_alloc = len + nlen + 1;
127            line = parser->line = xrealloc(line, parser->line_alloc);
128        }
129        memcpy(&line[len], parser->nline, nlen);
130        len += nlen;
131    }
132
133    line[len] = '\0';
134    return len;
135}
136
137
138/*
1390. If parser is NULL return 0.
1401. Read a line from config file. If nothing to read then return 0.
141   Handle continuation character. Advance lineno for each physical line.
142   Discard everything past comment character.
1432. if PARSE_TRIM is set (default), remove leading and trailing delimiters.
1443. If resulting line is empty goto 1.
1454. Look for first delimiter. If !PARSE_COLLAPSE or !PARSE_TRIM is set then
146   remember the token as empty.
1475. Else (default) if number of seen tokens is equal to max number of tokens
148   (token is the last one) and PARSE_GREEDY is set then the remainder
149   of the line is the last token.
150   Else (token is not last or PARSE_GREEDY is not set) just replace
151   first delimiter with '\0' thus delimiting the token.
1526. Advance line pointer past the end of token. If number of seen tokens
153   is less than required number of tokens then goto 4.
1547. Check the number of seen tokens is not less the min number of tokens.
155   Complain or die otherwise depending on PARSE_MIN_DIE.
1568. Return the number of seen tokens.
157
158mintokens > 0 make config_read() print error message if less than mintokens
159(but more than 0) are found. Empty lines are always skipped (not warned about).
160*/
161#undef config_read
162int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims)
163{
164    char *line;
165    int ntokens, mintokens;
166    int t;
167
168    if (!parser)
169        return 0;
170
171    ntokens = (uint8_t)flags;
172    mintokens = (uint8_t)(flags >> 8);
173
174 again:
175    memset(tokens, 0, sizeof(tokens[0]) * ntokens);
176
177    /* Read one line (handling continuations with backslash) */
178    if (get_line_with_continuation(parser) < 0)
179        return 0;
180
181    line = parser->line;
182
183    /* Skip token in the start of line? */
184    if (flags & PARSE_TRIM)
185        line += strspn(line, delims + 1);
186
187    if (line[0] == '\0' || line[0] == delims[0])
188        goto again;
189
190    if (flags & PARSE_KEEP_COPY) {
191        free(parser->data);
192        parser->data = xstrdup(line);
193    }
194
195    /* Tokenize the line */
196    t = 0;
197    do {
198        /* Pin token */
199        tokens[t] = line;
200
201        /* Combine remaining arguments? */
202        if ((t != (ntokens-1)) || !(flags & PARSE_GREEDY)) {
203            /* Vanilla token, find next delimiter */
204            line += strcspn(line, delims[0] ? delims : delims + 1);
205        } else {
206            /* Combining, find comment char if any */
207            line = strchrnul(line, PARSE_EOL_COMMENTS ? delims[0] : '\0');
208
209            /* Trim any extra delimiters from the end */
210            if (flags & PARSE_TRIM) {
211                while (strchr(delims + 1, line[-1]) != NULL)
212                    line--;
213            }
214        }
215
216        /* Token not terminated? */
217        if (*line == delims[0])
218            *line = '\0';
219        else if (*line != '\0')
220            *line++ = '\0';
221
222#if 0 /* unused so far */
223        if (flags & PARSE_ESCAPE) {
224            strcpy_and_process_escape_sequences(tokens[t], tokens[t]);
225        }
226#endif
227        /* Skip possible delimiters */
228        if (flags & PARSE_COLLAPSE)
229            line += strspn(line, delims + 1);
230
231        t++;
232    } while (*line && *line != delims[0] && t < ntokens);
233
234    if (t < mintokens) {
235        bb_error_msg("bad line %u: %d tokens found, %d needed",
236                parser->lineno, t, mintokens);
237        if (flags & PARSE_MIN_DIE)
238            xfunc_die();
239        goto again;
240    }
241
242    return t;
243}
Note: See TracBrowser for help on using the repository browser.