~mmach/netext73/busybox

« back to all changes in this revision

Viewing changes to libbb/parse_config.c

  • Committer: mmach
  • Date: 2021-04-14 13:54:24 UTC
  • Revision ID: netbit73@gmail.com-20210414135424-8x3fxf716zs4wflb
1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
36
int parse_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 
37
int 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
        noout = 1 & getopt32(argv, "^" "xn:+m:+d:f:+" "\0" "-1",
 
46
                                &ntokens, &mintokens, &delims, &flags
 
47
        );
 
48
        //argc -= optind;
 
49
        argv += optind;
 
50
 
 
51
        t = xmalloc(sizeof(t[0]) * ntokens);
 
52
        while (*argv) {
 
53
                int n;
 
54
                parser_t *p = config_open(*argv);
 
55
                while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) != 0) {
 
56
                        if (!noout) {
 
57
                                for (int i = 0; i < n; ++i)
 
58
                                        printf("[%s]", t[i]);
 
59
                                puts("");
 
60
                        }
 
61
                }
 
62
                config_close(p);
 
63
                argv++;
 
64
        }
 
65
        return EXIT_SUCCESS;
 
66
}
 
67
#endif
 
68
 
 
69
parser_t* FAST_FUNC config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path))
 
70
{
 
71
        FILE* fp;
 
72
        parser_t *parser;
 
73
 
 
74
        fp = fopen_func(filename);
 
75
        if (!fp)
 
76
                return NULL;
 
77
        parser = xzalloc(sizeof(*parser));
 
78
        parser->fp = fp;
 
79
        return parser;
 
80
}
 
81
 
 
82
parser_t* FAST_FUNC config_open(const char *filename)
 
83
{
 
84
        return config_open2(filename, fopen_or_warn_stdin);
 
85
}
 
86
 
 
87
void FAST_FUNC config_close(parser_t *parser)
 
88
{
 
89
        if (parser) {
 
90
                if (PARSE_KEEP_COPY) /* compile-time constant */
 
91
                        free(parser->data);
 
92
                fclose(parser->fp);
 
93
                free(parser->line);
 
94
                free(parser->nline);
 
95
                free(parser);
 
96
        }
 
97
}
 
98
 
 
99
/* This function reads an entire line from a text file,
 
100
 * up to a newline, exclusive.
 
101
 * Trailing '\' is recognized as line continuation.
 
102
 * Returns -1 if EOF/error.
 
103
 */
 
104
static int get_line_with_continuation(parser_t *parser)
 
105
{
 
106
        ssize_t len, nlen;
 
107
        char *line;
 
108
 
 
109
        len = getline(&parser->line, &parser->line_alloc, parser->fp);
 
110
        if (len <= 0)
 
111
                return len;
 
112
 
 
113
        line = parser->line;
 
114
        for (;;) {
 
115
                parser->lineno++;
 
116
                if (line[len - 1] == '\n')
 
117
                        len--;
 
118
                if (len == 0 || line[len - 1] != '\\')
 
119
                        break;
 
120
                len--;
 
121
 
 
122
                nlen = getline(&parser->nline, &parser->nline_alloc, parser->fp);
 
123
                if (nlen <= 0)
 
124
                        break;
 
125
 
 
126
                if (parser->line_alloc < len + nlen + 1) {
 
127
                        parser->line_alloc = len + nlen + 1;
 
128
                        line = parser->line = xrealloc(line, parser->line_alloc);
 
129
                }
 
130
                memcpy(&line[len], parser->nline, nlen);
 
131
                len += nlen;
 
132
        }
 
133
 
 
134
        line[len] = '\0';
 
135
        return len;
 
136
}
 
137
 
 
138
 
 
139
/*
 
140
0. If parser is NULL return 0.
 
141
1. Read a line from config file. If nothing to read then return 0.
 
142
   Handle continuation character. Advance lineno for each physical line.
 
143
   Discard everything past comment character.
 
144
2. if PARSE_TRIM is set (default), remove leading and trailing delimiters.
 
145
3. If resulting line is empty goto 1.
 
146
4. Look for first delimiter. If !PARSE_COLLAPSE or !PARSE_TRIM is set then
 
147
   remember the token as empty.
 
148
5. Else (default) if number of seen tokens is equal to max number of tokens
 
149
   (token is the last one) and PARSE_GREEDY is set then the remainder
 
150
   of the line is the last token.
 
151
   Else (token is not last or PARSE_GREEDY is not set) just replace
 
152
   first delimiter with '\0' thus delimiting the token.
 
153
6. Advance line pointer past the end of token. If number of seen tokens
 
154
   is less than required number of tokens then goto 4.
 
155
7. Check the number of seen tokens is not less the min number of tokens.
 
156
   Complain or die otherwise depending on PARSE_MIN_DIE.
 
157
8. Return the number of seen tokens.
 
158
 
 
159
mintokens > 0 make config_read() print error message if less than mintokens
 
160
(but more than 0) are found. Empty lines are always skipped (not warned about).
 
161
*/
 
162
#undef config_read
 
163
int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims)
 
164
{
 
165
        char *line, *p;
 
166
        int ntokens, mintokens;
 
167
        int t;
 
168
        char alt_comment_ch;
 
169
 
 
170
        if (!parser)
 
171
                return 0;
 
172
 
 
173
        alt_comment_ch = '\0';
 
174
        if (flags & PARSE_ALT_COMMENTS)
 
175
                alt_comment_ch = *delims++;
 
176
 
 
177
        ntokens = (uint8_t)flags;
 
178
        mintokens = (uint8_t)(flags >> 8);
 
179
 
 
180
 again:
 
181
        memset(tokens, 0, sizeof(tokens[0]) * ntokens);
 
182
 
 
183
        /* Read one line (handling continuations with backslash) */
 
184
        if (get_line_with_continuation(parser) < 0)
 
185
                return 0;
 
186
 
 
187
        line = parser->line;
 
188
 
 
189
        /* Skip token in the start of line? */
 
190
        if (flags & PARSE_TRIM)
 
191
                line += strspn(line, delims + 1);
 
192
 
 
193
        p = line;
 
194
        if (flags & PARSE_WS_COMMENTS)
 
195
                p = skip_whitespace(p);
 
196
        if (p[0] == '\0' || p[0] == delims[0] || p[0] == alt_comment_ch)
 
197
                goto again;
 
198
 
 
199
        if (flags & PARSE_KEEP_COPY) {
 
200
                free(parser->data);
 
201
                parser->data = xstrdup(line);
 
202
        }
 
203
 
 
204
        /* Tokenize the line */
 
205
        t = 0;
 
206
        do {
 
207
                /* Pin token */
 
208
                tokens[t] = line;
 
209
 
 
210
                /* Combine remaining arguments? */
 
211
                if ((t != (ntokens-1)) || !(flags & PARSE_GREEDY)) {
 
212
                        /* Vanilla token, find next delimiter */
 
213
                        line += strcspn(line, (delims[0] && (flags & PARSE_EOL_COMMENTS)) ? delims : delims + 1);
 
214
                } else {
 
215
                        /* Combining, find comment char if any */
 
216
                        line = strchrnul(line, (flags & PARSE_EOL_COMMENTS) ? delims[0] : '\0');
 
217
 
 
218
                        /* Trim any extra delimiters from the end */
 
219
                        if (flags & PARSE_TRIM) {
 
220
                                while (strchr(delims + 1, line[-1]) != NULL)
 
221
                                        line--;
 
222
                        }
 
223
                }
 
224
 
 
225
                /* Token not terminated? */
 
226
                if ((flags & PARSE_EOL_COMMENTS) && *line == delims[0])
 
227
                        *line = '\0'; /* ends with comment char: this line is done */
 
228
                else if (*line != '\0')
 
229
                        *line++ = '\0'; /* token is done, continue parsing line */
 
230
 
 
231
#if 0 /* unused so far */
 
232
                if (flags & PARSE_ESCAPE) {
 
233
                        strcpy_and_process_escape_sequences(tokens[t], tokens[t]);
 
234
                }
 
235
#endif
 
236
                /* Skip possible delimiters */
 
237
                if (flags & PARSE_COLLAPSE)
 
238
                        line += strspn(line, delims + 1);
 
239
 
 
240
                t++;
 
241
        } while (*line && *line != delims[0] && t < ntokens);
 
242
 
 
243
        if (t < mintokens) {
 
244
                bb_error_msg("bad line %u: %d tokens found, %d needed",
 
245
                                parser->lineno, t, mintokens);
 
246
                if (flags & PARSE_MIN_DIE)
 
247
                        xfunc_die();
 
248
                goto again;
 
249
        }
 
250
 
 
251
        return t;
 
252
}