blob: 4b0236028828b9c175cc81db3a38bfd8ceb2e5cb [file] [log] [blame]
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +00001/* vi: set sw=4 ts=4: */
2/*
3 * config file parser helper
4 *
5 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
6 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02007 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Bernhard Reutner-Fischerf3b39a22009-02-23 16:21:53 +00008 * Also for use in uClibc (http://uclibc.org/) licensed under LGPLv2.1 or later.
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +00009 */
10
Pere Orga5bc8c002011-04-11 03:29:49 +020011/*
12//usage:#define parse_trivial_usage
13//usage: "[-n MAXTOKENS] [-m MINTOKENS] [-d DELIMS] [-f FLAGS] FILE..."
14//usage:#define parse_full_usage ""
15*/
16
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000017#include "libbb.h"
18
Denis Vlasenko2d5bd802008-10-24 10:49:49 +000019#if defined ENABLE_PARSE && ENABLE_PARSE
Denis Vlasenko2e157dd2008-07-19 09:27:19 +000020int parse_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
21int parse_main(int argc UNUSED_PARAM, char **argv)
22{
23 const char *delims = "# \t";
Denis Vlasenko084266e2008-07-26 23:08:31 +000024 unsigned flags = PARSE_NORMAL;
Denis Vlasenko2e157dd2008-07-19 09:27:19 +000025 int mintokens = 0, ntokens = 128;
Denis Vlasenko084266e2008-07-26 23:08:31 +000026
Denis Vlasenko2e157dd2008-07-19 09:27:19 +000027 opt_complementary = "-1:n+:m+:f+";
28 getopt32(argv, "n:m:d:f:", &ntokens, &mintokens, &delims, &flags);
29 //argc -= optind;
30 argv += optind;
31 while (*argv) {
32 parser_t *p = config_open(*argv);
33 if (p) {
34 int n;
35 char **t = xmalloc(sizeof(char *) * ntokens);
Denis Vlasenko4a717e02008-07-20 13:01:56 +000036 while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) != 0) {
Denis Vlasenko2e157dd2008-07-19 09:27:19 +000037 for (int i = 0; i < n; ++i)
38 printf("[%s]", t[i]);
39 puts("");
40 }
41 config_close(p);
42 }
43 argv++;
44 }
45 return EXIT_SUCCESS;
46}
47#endif
48
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000049/*
50
51Typical usage:
52
53----- CUT -----
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +020054 char *t[3]; // tokens placeholder
Denis Vlasenko2e157dd2008-07-19 09:27:19 +000055 parser_t *p = config_open(filename);
56 if (p) {
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000057 // parse line-by-line
Denis Vlasenko2e157dd2008-07-19 09:27:19 +000058 while (config_read(p, t, 3, 0, delimiters, flags)) { // 1..3 tokens
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000059 // use tokens
60 bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]);
61 }
62 ...
63 // free parser
Denis Vlasenko2e157dd2008-07-19 09:27:19 +000064 config_close(p);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000065 }
66----- CUT -----
67
68*/
69
Denis Vlasenko5415c852008-07-21 23:05:26 +000070parser_t* FAST_FUNC config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path))
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000071{
Denis Vlasenko084266e2008-07-26 23:08:31 +000072 FILE* fp;
73 parser_t *parser;
74
75 fp = fopen_func(filename);
76 if (!fp)
77 return NULL;
78 parser = xzalloc(sizeof(*parser));
79 parser->fp = fp;
80 return parser;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000081}
82
Denis Vlasenko5415c852008-07-21 23:05:26 +000083parser_t* FAST_FUNC config_open(const char *filename)
84{
85 return config_open2(filename, fopen_or_warn_stdin);
86}
87
Denis Vlasenkoc7cc5a92009-04-19 01:27:20 +000088static void config_free_data(parser_t *parser)
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000089{
Denis Vlasenkoc01340f2008-07-16 22:12:18 +000090 free(parser->line);
Denis Vlasenko2e157dd2008-07-19 09:27:19 +000091 parser->line = NULL;
Denis Vlasenkodcb3fcb2008-07-19 22:57:00 +000092 if (PARSE_KEEP_COPY) { /* compile-time constant */
Denis Vlasenko2e157dd2008-07-19 09:27:19 +000093 free(parser->data);
94 parser->data = NULL;
Denis Vlasenkodcb3fcb2008-07-19 22:57:00 +000095 }
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000096}
Denis Vlasenko2e157dd2008-07-19 09:27:19 +000097
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000098void FAST_FUNC config_close(parser_t *parser)
99{
Denis Vlasenko084266e2008-07-26 23:08:31 +0000100 if (parser) {
101 config_free_data(parser);
102 fclose(parser->fp);
103 free(parser);
104 }
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000105}
106
Denis Vlasenko2e157dd2008-07-19 09:27:19 +0000107/*
Denis Vlasenko084266e2008-07-26 23:08:31 +00001080. If parser is NULL return 0.
1091. Read a line from config file. If nothing to read then return 0.
110 Handle continuation character. Advance lineno for each physical line.
Denys Vlasenko5370bfb2009-09-06 02:58:59 +0200111 Discard everything past comment character.
Denis Vlasenko084266e2008-07-26 23:08:31 +00001122. if PARSE_TRIM is set (default), remove leading and trailing delimiters.
Denis Vlasenko2e157dd2008-07-19 09:27:19 +00001133. If resulting line is empty goto 1.
Denis Vlasenko084266e2008-07-26 23:08:31 +00001144. Look for first delimiter. If !PARSE_COLLAPSE or !PARSE_TRIM is set then
115 remember the token as empty.
1165. Else (default) if number of seen tokens is equal to max number of tokens
117 (token is the last one) and PARSE_GREEDY is set then the remainder
118 of the line is the last token.
119 Else (token is not last or PARSE_GREEDY is not set) just replace
120 first delimiter with '\0' thus delimiting the token.
1216. Advance line pointer past the end of token. If number of seen tokens
122 is less than required number of tokens then goto 4.
1237. Check the number of seen tokens is not less the min number of tokens.
124 Complain or die otherwise depending on PARSE_MIN_DIE.
Denis Vlasenko2e157dd2008-07-19 09:27:19 +00001258. Return the number of seen tokens.
126
Denis Vlasenko084266e2008-07-26 23:08:31 +0000127mintokens > 0 make config_read() print error message if less than mintokens
Denis Vlasenko2e157dd2008-07-19 09:27:19 +0000128(but more than 0) are found. Empty lines are always skipped (not warned about).
129*/
130#undef config_read
131int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims)
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000132{
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000133 char *line;
134 int ntokens, mintokens;
135 int t, len;
Denis Vlasenko084266e2008-07-26 23:08:31 +0000136
Denys Vlasenko63144be2010-06-26 04:00:52 +0200137 ntokens = (uint8_t)flags;
138 mintokens = (uint8_t)(flags >> 8);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000139
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000140 if (parser == NULL)
Denis Vlasenko084266e2008-07-26 23:08:31 +0000141 return 0;
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000142
143again:
144 memset(tokens, 0, sizeof(tokens[0]) * ntokens);
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000145 config_free_data(parser);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000146
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000147 /* Read one line (handling continuations with backslash) */
148 line = bb_get_chunk_with_continuation(parser->fp, &len, &parser->lineno);
149 if (line == NULL)
150 return 0;
151 parser->line = line;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000152
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000153 /* Strip trailing line-feed if any */
154 if (len && line[len-1] == '\n')
155 line[len-1] = '\0';
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000156
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000157 /* Skip token in the start of line? */
158 if (flags & PARSE_TRIM)
159 line += strspn(line, delims + 1);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000160
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000161 if (line[0] == '\0' || line[0] == delims[0])
162 goto again;
163
164 if (flags & PARSE_KEEP_COPY)
Denis Vlasenko2e157dd2008-07-19 09:27:19 +0000165 parser->data = xstrdup(line);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000166
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000167 /* Tokenize the line */
Denys Vlasenko63144be2010-06-26 04:00:52 +0200168 t = 0;
169 do {
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000170 /* Pin token */
171 tokens[t] = line;
172
173 /* Combine remaining arguments? */
174 if ((t != (ntokens-1)) || !(flags & PARSE_GREEDY)) {
175 /* Vanilla token, find next delimiter */
176 line += strcspn(line, delims[0] ? delims : delims + 1);
Denis Vlasenko2e157dd2008-07-19 09:27:19 +0000177 } else {
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000178 /* Combining, find comment char if any */
179 line = strchrnul(line, delims[0]);
180
181 /* Trim any extra delimiters from the end */
182 if (flags & PARSE_TRIM) {
183 while (strchr(delims + 1, line[-1]) != NULL)
184 line--;
Denis Vlasenko0f99d492008-07-24 23:38:04 +0000185 }
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000186 }
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000187
188 /* Token not terminated? */
Denys Vlasenko63144be2010-06-26 04:00:52 +0200189 if (*line == delims[0])
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000190 *line = '\0';
Denys Vlasenko63144be2010-06-26 04:00:52 +0200191 else if (*line != '\0')
192 *line++ = '\0';
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000193
194#if 0 /* unused so far */
195 if (flags & PARSE_ESCAPE) {
Denys Vlasenko53600592010-10-23 21:06:06 +0200196 strcpy_and_process_escape_sequences(tokens[t], tokens[t]);
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000197 }
198#endif
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000199 /* Skip possible delimiters */
200 if (flags & PARSE_COLLAPSE)
201 line += strspn(line, delims + 1);
Denys Vlasenko63144be2010-06-26 04:00:52 +0200202
203 t++;
204 } while (*line && *line != delims[0] && t < ntokens);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000205
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000206 if (t < mintokens) {
Denis Vlasenko5415c852008-07-21 23:05:26 +0000207 bb_error_msg("bad line %u: %d tokens found, %d needed",
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000208 parser->lineno, t, mintokens);
Denis Vlasenko5415c852008-07-21 23:05:26 +0000209 if (flags & PARSE_MIN_DIE)
210 xfunc_die();
Denys Vlasenko63144be2010-06-26 04:00:52 +0200211 if (flags & PARSE_KEEP_COPY)
212 free(parser->data);
Denis Vlasenko5415c852008-07-21 23:05:26 +0000213 goto again;
214 }
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000215
Denis Vlasenko69f4f9a2008-08-09 17:16:40 +0000216 return t;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000217}