70f933fe384f03dda9b262c6a976d84cb82b2f69
[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;
63         /* do not treat subsequent delimiters as one delimiter */
64         bool noreduce = (ntokens < 0);
65         if (noreduce)
66                 ntokens = -ntokens;
67
68         memset(tokens, 0, sizeof(void *) * ntokens);
69         config_free_data(parser);
70
71         while (1) {
72                 int n;
73
74                 // get fresh line
75 //TODO: speed up xmalloc_fgetline by internally using fgets, not fgetc
76                 line = xmalloc_fgetline(parser->fp);
77                 if (!line)
78                         return -1;
79
80                 parser->lineno++;
81                 // handle continuations. Tito's code stolen :)
82                 while (1) {
83                         ii = strlen(line);
84                         if (!ii)
85                                 goto next_line;
86                         if (line[ii - 1] != '\\')
87                                 break;
88                         // multi-line object
89                         line[--ii] = '\0';
90 //TODO: add xmalloc_fgetline-like iface but with appending to existing str
91                         q = xmalloc_fgetline(parser->fp);
92                         if (q) {
93                                 parser->lineno++;
94                                 line = xasprintf("%s%s", line, q);
95                                 free(q);
96                         }
97                 }
98                 // comments mean EOLs
99                 if (comment) {
100                         q = strchrnul(line, comment);
101                         *q = '\0';
102                         ii = q - line;
103                 }
104                 // skip leading delimiters
105                 n = strspn(line, delims);
106                 if (n) {
107                         ii -= n;
108                         strcpy(line, line + n);
109                 }
110                 if (ii)
111                         break;
112
113  next_line:
114                 /* skip empty line */
115                 free(line);
116         }
117
118         // non-empty line found, parse and return
119
120         // store line
121         parser->line = line = xrealloc(line, ii + 1);
122         parser->data = xstrdup(line);
123
124         // now split line to tokens
125 //TODO: discard consecutive delimiters?
126         ii = 0;
127         ntokens--; // now it's max allowed token no
128         while (1) {
129                 // get next token
130                 if (ii == ntokens)
131                         break;
132                 q = line + strcspn(line, delims);
133                 if (!*q)
134                         break;
135                 // pin token
136                 *q++ = '\0';
137                 if (noreduce || *line) {
138                         tokens[ii++] = line;
139 //bb_info_msg("L[%d] T[%s]\n", ii, line);
140                 }
141                 line = q;
142         }
143
144         // non-empty remainder is also a token,
145         // so if ntokens <= 1, we just return the whole line
146         if (noreduce || *line)
147                 tokens[ii++] = line;
148
149         if (ii < mintokens)
150                 bb_error_msg_and_die("bad line %u: %d tokens found, %d needed",
151                                 parser->lineno, ii, mintokens);
152
153         return ii;
154 }