Annotation of /trunk/mkinitrd-magellan/busybox/libbb/parse_config.c
Parent Directory | Revision Log
Revision 1123 -
(hide annotations)
(download)
Wed Aug 18 21:56:57 2010 UTC (14 years, 1 month ago) by niro
File MIME type: text/plain
File size: 5519 byte(s)
Wed Aug 18 21:56:57 2010 UTC (14 years, 1 month ago) by niro
File MIME type: text/plain
File size: 5519 byte(s)
-updated to busybox-1.17.1
1 | niro | 816 | /* 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 | niro | 984 | * Also for use in uClibc (http://uclibc.org/) licensed under LGPLv2.1 or later. |
9 | niro | 816 | */ |
10 | |||
11 | #include "libbb.h" | ||
12 | |||
13 | #if defined ENABLE_PARSE && ENABLE_PARSE | ||
14 | int parse_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
15 | int parse_main(int argc UNUSED_PARAM, char **argv) | ||
16 | { | ||
17 | const char *delims = "# \t"; | ||
18 | unsigned flags = PARSE_NORMAL; | ||
19 | int mintokens = 0, ntokens = 128; | ||
20 | |||
21 | opt_complementary = "-1:n+:m+:f+"; | ||
22 | getopt32(argv, "n:m:d:f:", &ntokens, &mintokens, &delims, &flags); | ||
23 | //argc -= optind; | ||
24 | argv += optind; | ||
25 | while (*argv) { | ||
26 | parser_t *p = config_open(*argv); | ||
27 | if (p) { | ||
28 | int n; | ||
29 | char **t = xmalloc(sizeof(char *) * ntokens); | ||
30 | while ((n = config_read(p, t, ntokens, mintokens, delims, flags)) != 0) { | ||
31 | for (int i = 0; i < n; ++i) | ||
32 | printf("[%s]", t[i]); | ||
33 | puts(""); | ||
34 | } | ||
35 | config_close(p); | ||
36 | } | ||
37 | argv++; | ||
38 | } | ||
39 | return EXIT_SUCCESS; | ||
40 | } | ||
41 | #endif | ||
42 | |||
43 | /* | ||
44 | |||
45 | Typical usage: | ||
46 | |||
47 | ----- CUT ----- | ||
48 | char *t[3]; // tokens placeholder | ||
49 | parser_t *p = config_open(filename); | ||
50 | if (p) { | ||
51 | // parse line-by-line | ||
52 | while (config_read(p, t, 3, 0, delimiters, flags)) { // 1..3 tokens | ||
53 | // use tokens | ||
54 | bb_error_msg("TOKENS: [%s][%s][%s]", t[0], t[1], t[2]); | ||
55 | } | ||
56 | ... | ||
57 | // free parser | ||
58 | config_close(p); | ||
59 | } | ||
60 | ----- CUT ----- | ||
61 | |||
62 | */ | ||
63 | |||
64 | parser_t* FAST_FUNC config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path)) | ||
65 | { | ||
66 | FILE* fp; | ||
67 | parser_t *parser; | ||
68 | |||
69 | fp = fopen_func(filename); | ||
70 | if (!fp) | ||
71 | return NULL; | ||
72 | parser = xzalloc(sizeof(*parser)); | ||
73 | parser->fp = fp; | ||
74 | return parser; | ||
75 | } | ||
76 | |||
77 | parser_t* FAST_FUNC config_open(const char *filename) | ||
78 | { | ||
79 | return config_open2(filename, fopen_or_warn_stdin); | ||
80 | } | ||
81 | |||
82 | niro | 984 | static void config_free_data(parser_t *parser) |
83 | niro | 816 | { |
84 | free(parser->line); | ||
85 | parser->line = NULL; | ||
86 | if (PARSE_KEEP_COPY) { /* compile-time constant */ | ||
87 | free(parser->data); | ||
88 | parser->data = NULL; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | void FAST_FUNC config_close(parser_t *parser) | ||
93 | { | ||
94 | if (parser) { | ||
95 | config_free_data(parser); | ||
96 | fclose(parser->fp); | ||
97 | free(parser); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | 0. If parser is NULL return 0. | ||
103 | 1. Read a line from config file. If nothing to read then return 0. | ||
104 | Handle continuation character. Advance lineno for each physical line. | ||
105 | niro | 984 | Discard everything past comment character. |
106 | niro | 816 | 2. if PARSE_TRIM is set (default), remove leading and trailing delimiters. |
107 | 3. If resulting line is empty goto 1. | ||
108 | 4. Look for first delimiter. If !PARSE_COLLAPSE or !PARSE_TRIM is set then | ||
109 | remember the token as empty. | ||
110 | 5. Else (default) if number of seen tokens is equal to max number of tokens | ||
111 | (token is the last one) and PARSE_GREEDY is set then the remainder | ||
112 | of the line is the last token. | ||
113 | Else (token is not last or PARSE_GREEDY is not set) just replace | ||
114 | first delimiter with '\0' thus delimiting the token. | ||
115 | 6. Advance line pointer past the end of token. If number of seen tokens | ||
116 | is less than required number of tokens then goto 4. | ||
117 | 7. Check the number of seen tokens is not less the min number of tokens. | ||
118 | Complain or die otherwise depending on PARSE_MIN_DIE. | ||
119 | 8. Return the number of seen tokens. | ||
120 | |||
121 | mintokens > 0 make config_read() print error message if less than mintokens | ||
122 | (but more than 0) are found. Empty lines are always skipped (not warned about). | ||
123 | */ | ||
124 | #undef config_read | ||
125 | int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims) | ||
126 | { | ||
127 | char *line; | ||
128 | int ntokens, mintokens; | ||
129 | int t, len; | ||
130 | |||
131 | niro | 1123 | ntokens = (uint8_t)flags; |
132 | mintokens = (uint8_t)(flags >> 8); | ||
133 | niro | 816 | |
134 | if (parser == NULL) | ||
135 | return 0; | ||
136 | |||
137 | again: | ||
138 | memset(tokens, 0, sizeof(tokens[0]) * ntokens); | ||
139 | config_free_data(parser); | ||
140 | |||
141 | /* Read one line (handling continuations with backslash) */ | ||
142 | line = bb_get_chunk_with_continuation(parser->fp, &len, &parser->lineno); | ||
143 | if (line == NULL) | ||
144 | return 0; | ||
145 | parser->line = line; | ||
146 | |||
147 | /* Strip trailing line-feed if any */ | ||
148 | if (len && line[len-1] == '\n') | ||
149 | line[len-1] = '\0'; | ||
150 | |||
151 | /* Skip token in the start of line? */ | ||
152 | if (flags & PARSE_TRIM) | ||
153 | line += strspn(line, delims + 1); | ||
154 | |||
155 | if (line[0] == '\0' || line[0] == delims[0]) | ||
156 | goto again; | ||
157 | |||
158 | if (flags & PARSE_KEEP_COPY) | ||
159 | parser->data = xstrdup(line); | ||
160 | |||
161 | /* Tokenize the line */ | ||
162 | niro | 1123 | t = 0; |
163 | do { | ||
164 | niro | 816 | /* Pin token */ |
165 | tokens[t] = line; | ||
166 | |||
167 | /* Combine remaining arguments? */ | ||
168 | if ((t != (ntokens-1)) || !(flags & PARSE_GREEDY)) { | ||
169 | /* Vanilla token, find next delimiter */ | ||
170 | line += strcspn(line, delims[0] ? delims : delims + 1); | ||
171 | } else { | ||
172 | /* Combining, find comment char if any */ | ||
173 | line = strchrnul(line, delims[0]); | ||
174 | |||
175 | /* Trim any extra delimiters from the end */ | ||
176 | if (flags & PARSE_TRIM) { | ||
177 | while (strchr(delims + 1, line[-1]) != NULL) | ||
178 | line--; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | /* Token not terminated? */ | ||
183 | niro | 1123 | if (*line == delims[0]) |
184 | niro | 816 | *line = '\0'; |
185 | niro | 1123 | else if (*line != '\0') |
186 | *line++ = '\0'; | ||
187 | niro | 816 | |
188 | #if 0 /* unused so far */ | ||
189 | if (flags & PARSE_ESCAPE) { | ||
190 | const char *from; | ||
191 | char *to; | ||
192 | |||
193 | from = to = tokens[t]; | ||
194 | while (*from) { | ||
195 | if (*from == '\\') { | ||
196 | from++; | ||
197 | *to++ = bb_process_escape_sequence(&from); | ||
198 | } else { | ||
199 | *to++ = *from++; | ||
200 | } | ||
201 | } | ||
202 | *to = '\0'; | ||
203 | } | ||
204 | #endif | ||
205 | /* Skip possible delimiters */ | ||
206 | if (flags & PARSE_COLLAPSE) | ||
207 | line += strspn(line, delims + 1); | ||
208 | |||
209 | niro | 1123 | t++; |
210 | } while (*line && *line != delims[0] && t < ntokens); | ||
211 | |||
212 | niro | 816 | if (t < mintokens) { |
213 | bb_error_msg("bad line %u: %d tokens found, %d needed", | ||
214 | parser->lineno, t, mintokens); | ||
215 | if (flags & PARSE_MIN_DIE) | ||
216 | xfunc_die(); | ||
217 | niro | 1123 | if (flags & PARSE_KEEP_COPY) |
218 | free(parser->data); | ||
219 | niro | 816 | goto again; |
220 | } | ||
221 | |||
222 | return t; | ||
223 | } |