blob: 70f933fe384f03dda9b262c6a976d84cb82b2f69 [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
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000034parser_t* FAST_FUNC config_open(const char *filename)
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000035{
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000036 parser_t *parser = xzalloc(sizeof(parser_t));
37 /* empty file configures nothing */
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000038 parser->fp = fopen_or_warn(filename, "r");
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000039 if (parser->fp)
40 return parser;
41 config_close (parser);
42 if (ENABLE_FEATURE_CLEAN_UP)
43 free(parser);
44 return NULL;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000045}
46
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000047static void config_free_data(parser_t *const parser)
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000048{
Denis Vlasenkoc01340f2008-07-16 22:12:18 +000049 free(parser->line);
50 free(parser->data);
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000051 parser->line = parser->data = NULL;
52}
53void FAST_FUNC config_close(parser_t *parser)
54{
55 config_free_data(parser);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000056 fclose(parser->fp);
57}
58
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000059int 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 +000060{
61 char *line, *q;
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000062 int ii;
63 /* do not treat subsequent delimiters as one delimiter */
64 bool noreduce = (ntokens < 0);
65 if (noreduce)
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000066 ntokens = -ntokens;
67
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000068 memset(tokens, 0, sizeof(void *) * ntokens);
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000069 config_free_data(parser);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000070
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)
Denis Vlasenkofb1642f2008-07-16 23:04:49 +000078 return -1;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000079
80 parser->lineno++;
81 // handle continuations. Tito's code stolen :)
82 while (1) {
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000083 ii = strlen(line);
84 if (!ii)
85 goto next_line;
86 if (line[ii - 1] != '\\')
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000087 break;
88 // multi-line object
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +000089 line[--ii] = '\0';
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +000090//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';
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000102 ii = q - line;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000103 }
104 // skip leading delimiters
105 n = strspn(line, delims);
106 if (n) {
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000107 ii -= n;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000108 strcpy(line, line + n);
109 }
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000110 if (ii)
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000111 break;
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000112
113 next_line:
114 /* skip empty line */
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000115 free(line);
116 }
117
118 // non-empty line found, parse and return
119
120 // store line
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000121 parser->line = line = xrealloc(line, ii + 1);
Denis Vlasenkoc01340f2008-07-16 22:12:18 +0000122 parser->data = xstrdup(line);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000123
124 // now split line to tokens
125//TODO: discard consecutive delimiters?
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000126 ii = 0;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000127 ntokens--; // now it's max allowed token no
128 while (1) {
129 // get next token
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000130 if (ii == ntokens)
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000131 break;
132 q = line + strcspn(line, delims);
133 if (!*q)
134 break;
135 // pin token
136 *q++ = '\0';
137 if (noreduce || *line) {
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000138 tokens[ii++] = line;
139//bb_info_msg("L[%d] T[%s]\n", ii, line);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000140 }
141 line = q;
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000142 }
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000143
144 // non-empty remainder is also a token,
145 // so if ntokens <= 1, we just return the whole line
146 if (noreduce || *line)
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000147 tokens[ii++] = line;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000148
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000149 if (ii < mintokens)
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000150 bb_error_msg_and_die("bad line %u: %d tokens found, %d needed",
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000151 parser->lineno, ii, mintokens);
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000152
Bernhard Reutner-Fischer67921282008-07-17 11:59:13 +0000153 return ii;
Denis Vlasenkoe559e0a2008-07-15 21:09:30 +0000154}