5f6dbbde165f19b639b7a10582f3b4091140872f
[oweals/busybox.git] / libbb / parse_config.c
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 tarball for details.
8  */
9
10 #include "libbb.h"
11
12 /*
13
14 Typical usage:
15
16 ----- CUT -----
17         char *t[3];     // tokens placeholder
18         parser_t p;     // parser structure
19         // open file
20         if (config_open(filename, &p)) {
21                 // parse line-by-line
22                 while (*config_read(&p, t, 3, 0, delimiters, comment_char) >= 0) { // 0..3 tokens
23                         // use tokens
24                         bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]);
25                 }
26                 ...
27                 // free parser
28                 config_close(&p);
29         }
30 ----- CUT -----
31
32 */
33
34 parser_t* FAST_FUNC config_open(const char *filename)
35 {
36         parser_t *parser = xzalloc(sizeof(parser_t));
37         /* empty file configures nothing */
38         parser->fp = fopen_or_warn(filename, "r");
39         if (parser->fp)
40                 return parser;
41         config_close (parser);
42         if (ENABLE_FEATURE_CLEAN_UP)
43           free(parser);
44         return NULL;
45 }
46
47 static void config_free_data(parser_t *const parser)
48 {
49         free(parser->line);
50         free(parser->data);
51         parser->line = parser->data = NULL;
52 }
53 void FAST_FUNC config_close(parser_t *parser)
54 {
55         config_free_data(parser);
56         fclose(parser->fp);
57 }
58
59 int FAST_FUNC config_read(parser_t *parser, char **tokens, int ntokens, int mintokens, const char *delims, char comment)
60 {
61         char *line, *q;
62         int ii, seen;
63         /* do not treat consecutive delimiters as one delimiter */
64         bool noreduce = (ntokens < 0);
65         if (noreduce)
66                 ntokens = -ntokens;
67
68         memset(tokens, 0, sizeof(tokens[0]) * ntokens);
69         config_free_data(parser);
70
71         while (1) {
72 //TODO: speed up xmalloc_fgetline by internally using fgets, not fgetc
73                 line = xmalloc_fgetline(parser->fp);
74                 if (!line)
75                         return -1;
76
77                 parser->lineno++;
78                 // handle continuations. Tito's code stolen :)
79                 while (1) {
80                         ii = strlen(line);
81                         if (!ii)
82                                 goto next_line;
83                         if (line[ii - 1] != '\\')
84                                 break;
85                         // multi-line object
86                         line[--ii] = '\0';
87 //TODO: add xmalloc_fgetline-like iface but with appending to existing str
88                         q = xmalloc_fgetline(parser->fp);
89                         if (q) {
90                                 parser->lineno++;
91                                 line = xasprintf("%s%s", line, q);
92                                 free(q);
93                         }
94                 }
95                 // comments mean EOLs
96                 if (comment) {
97                         q = strchrnul(line, comment);
98                         *q = '\0';
99                         ii = q - line;
100                 }
101                 // skip leading delimiters
102                 seen = strspn(line, delims);
103                 if (seen) {
104                         ii -= seen;
105                         strcpy(line, line + seen);
106                 }
107                 if (ii)
108                         break;
109
110  next_line:
111                 /* skip empty line */
112                 free(line);
113         }
114
115         // non-empty line found, parse and return
116
117         // store line
118         parser->line = line = xrealloc(line, ii + 1);
119         parser->data = xstrdup(line);
120
121         /* now split line to tokens */
122         ii = noreduce ? seen : 0;
123         ntokens--; // now it's max allowed token no
124         while (1) {
125                 // get next token
126                 if (ii == ntokens)
127                         break;
128                 q = line + strcspn(line, delims);
129                 if (!*q)
130                         break;
131                 // pin token
132                 *q++ = '\0';
133                 if (noreduce || *line) {
134                         tokens[ii++] = line;
135 //bb_info_msg("L[%d] T[%s]\n", ii, line);
136                 }
137                 line = q;
138         }
139
140         // non-empty remainder is also a token,
141         // so if ntokens <= 1, we just return the whole line
142         if (noreduce || *line)
143                 tokens[ii++] = line;
144
145         if (ii < mintokens)
146                 bb_error_msg_and_die("bad line %u: %d tokens found, %d needed",
147                                 parser->lineno, ii, mintokens);
148
149         return ii;
150 }