blob: f07099285c1e68677b11e6281943f2a4e2e749f3 [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 *
7 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 */
9
10#include "libbb.h"
11
12/*
13
14Typical 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
Denis Vlasenkofb1642f2008-07-16 23:04:49 +000022 while (*config_read(&p, t, 3, 0, delimiters, comment_char) >= 0) { // 0..3 tokens
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000023 // 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
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000034FILE* FAST_FUNC config_open(parser_t *parser, const char *filename)
35{
36 // empty file configures nothing!
37 parser->fp = fopen_or_warn(filename, "r");
38 if (!parser->fp)
39 return parser->fp;
40
41 // init parser
42 parser->line = NULL;
43 parser->lineno = 0;
44
45 return parser->fp;
46}
47
48void FAST_FUNC config_close(parser_t *parser)
49{
Denis Vlasenkoc01340f2008-07-16 22:12:18 +000050 free(parser->line);
51 free(parser->data);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000052 fclose(parser->fp);
53}
54
Denis Vlasenkoc01340f2008-07-16 22:12:18 +000055int FAST_FUNC config_read(parser_t *parser, char **tokens, int ntokens, int mintokens, const char *delims, char comment)
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000056{
57 char *line, *q;
58 int token_num, len;
59 int noreduce = (ntokens < 0); // do not treat subsequent delimiters as one delimiter
60
61 if (ntokens < 0)
62 ntokens = -ntokens;
63
64 // nullify tokens
65 memset(tokens, 0, sizeof(void *) * ntokens);
66
67 // free used line
68 free(parser->line);
69 parser->line = NULL;
Denis Vlasenkoc01340f2008-07-16 22:12:18 +000070 free(parser->data);
71 parser->data = NULL;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000072
73 while (1) {
74 int n;
75
76 // get fresh line
77//TODO: speed up xmalloc_fgetline by internally using fgets, not fgetc
78 line = xmalloc_fgetline(parser->fp);
79 if (!line)
Denis Vlasenkofb1642f2008-07-16 23:04:49 +000080 return -1;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000081
82 parser->lineno++;
83 // handle continuations. Tito's code stolen :)
84 while (1) {
85 len = strlen(line);
86 if (!len)
87 goto free_and_cont;
88 if (line[len - 1] != '\\')
89 break;
90 // multi-line object
91 line[--len] = '\0';
92//TODO: add xmalloc_fgetline-like iface but with appending to existing str
93 q = xmalloc_fgetline(parser->fp);
94 if (q) {
95 parser->lineno++;
96 line = xasprintf("%s%s", line, q);
97 free(q);
98 }
99 }
100 // comments mean EOLs
101 if (comment) {
102 q = strchrnul(line, comment);
103 *q = '\0';
104 len = q - line;
105 }
106 // skip leading delimiters
107 n = strspn(line, delims);
108 if (n) {
109 len -= n;
110 strcpy(line, line + n);
111 }
112 if (len)
113 break;
114 // skip empty lines
115 free_and_cont:
116 free(line);
117 }
118
119 // non-empty line found, parse and return
120
121 // store line
122 parser->line = line = xrealloc(line, len + 1);
Denis Vlasenkoc01340f2008-07-16 22:12:18 +0000123 parser->data = xstrdup(line);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000124
125 // now split line to tokens
126//TODO: discard consecutive delimiters?
127 token_num = 0;
128 ntokens--; // now it's max allowed token no
129 while (1) {
130 // get next token
131 if (token_num == ntokens)
132 break;
133 q = line + strcspn(line, delims);
134 if (!*q)
135 break;
136 // pin token
137 *q++ = '\0';
138 if (noreduce || *line) {
139 tokens[token_num++] = line;
140//bb_error_msg("L[%d] T[%s]", token_num, line);
141 }
142 line = q;
143 }
144
145 // non-empty remainder is also a token,
146 // so if ntokens <= 1, we just return the whole line
147 if (noreduce || *line)
148 tokens[token_num++] = line;
149
150 if (token_num < mintokens)
151 bb_error_msg_and_die("bad line %u: %d tokens found, %d needed",
152 parser->lineno, token_num, mintokens);
153
Denis Vlasenkoc01340f2008-07-16 22:12:18 +0000154 return token_num;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000155}