"SfR Fresh" - the SfR Freeware/Shareware Archive 
As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers.
Alternatively you can here view or download the uninterpreted source code file.
That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
1 /*
2 * Brutus source file implementing a configuration file class.
3 * Copyright (C) 2004 OMC Denmark ApS.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include "config.h"
21 #include <fcntl.h>
22 #include <io.h>
23 #include <sys/stat.h>
24 #include <ctype.h>
25 #include <stdio.h>
26
27 #ifdef COMMENT_CHAR
28 #undef COMMENT_CHAR
29 #endif
30 #define COMMENT_CHAR '#'
31
32 #ifdef MAX_CONFIG_FILE_SIZE
33 #undef MAX_CONFIG_FILE_SIZE
34 #endif
35 #define MAX_CONFIG_FILE_SIZE (20480)
36
37 /* Will load the configuration file "file". Returns
38 * zero on success or a positive number which indicates
39 * a faulty configuration line or a negative number
40 * which indicates that the configuration file is to
41 * big or can not be read. */
42 int Config::load(const char *file)
43 {
44 if (!file)
45 return -1;
46
47 // initialize
48 if (_items) {
49 free(_items);
50 _items = NULL;
51 }
52 if (_fbuf) {
53 free(_fbuf);
54 _fbuf = NULL;
55 }
56 _lnum = 1;
57
58 // read file into buffer
59 if (read_conf(file, &_fsize, (void**)&_fbuf))
60 return -1;
61
62 return itemize();
63 }
64
65 int Config::itemize(void)
66 {
67 size_t count = 0;
68 char *p = _fbuf;
69
70 // count maximum number of effective configuration lines
71 for (size_t n = 0; n < _fsize; n++, p++) {
72 // newline?
73 if (*p == '\0')
74 count++;
75 }
76 _items = (struct item*)malloc(sizeof(struct item) * (count + 1));
77
78 // get token/value pairs
79 p = _fbuf;
80 count = 0;
81 if ((wspace(*p)) || (*p == COMMENT_CHAR))
82 if (get_next_line(&p))
83 return _lnum;
84 while (1) {
85 if (!p)
86 break;
87
88 _items[count].token = p;
89
90 // p will be somewhere within the value string when the function returns
91 if (point_to_value(&p, &_items[count++].value))
92 return _lnum;
93
94 if (get_next_line(&p))
95 return _lnum;
96 }
97 _items[count].token = NULL;
98 _items[count].value = NULL;
99
100 return 0;
101 }
102
103 /* Upon return val points to the first character in the null-
104 * or whitespace-terminated value string.
105 *
106 * *str is a pointer to the first character in a null-terminated
107 * configuration line.
108 *
109 * *str is modified by this function to point to the first
110 * character in the value string.
111 *
112 * Will return (-1) on error or zero on success. */
113 int Config::point_to_value(char **str, char **val)
114 {
115 // not possible
116 if (wspace(**str))
117 return -1;
118
119 // skip past token
120 while ((!wspace(**str)) && (**str != '\0')) {
121 (*str)++;
122 }
123 if (**str == '\0')
124 return -1;
125
126 // skip past whitespace to value
127 while (wspace(**str)) {
128 **str = '\0';
129 (*str)++;
130 }
131 if (**str == '\0')
132 return -1;
133
134 *val = *str;
135
136 // remove trailing whitespace
137 char *tmp = *val;
138 while (*tmp != '\0')
139 tmp++;
140 tmp--;
141 while (wspace(*tmp)) {
142 *tmp = '\0';
143 tmp--;
144 }
145
146 return 0;
147 }
148
149 /* *str is a pointer to the first character
150 * of the next configuration line skipping comments and
151 * empty lines.
152 *
153 * *str is NULL on end-of-buffer.
154 *
155 * Returns (-1) on error or zero on success. */
156 int Config::get_next_line(char **str)
157 {
158 int retv = 0;
159 size_t offset = *str - _fbuf;
160
161 do {
162 // skip printable characters
163 while (**str != '\0') {
164 (*str)++;
165
166 offset++;
167 if (offset == _fsize) {
168 *str = NULL;
169 goto out;
170 }
171 }
172
173 // skip '\0's
174 while (**str == '\0') {
175 _lnum++;
176 (*str)++;
177
178 offset++;
179 if (offset == _fsize) {
180 *str = NULL;
181 goto out;
182 }
183 }
184
185 // quick check for floating text (<WHITESPACE>TEXT)
186 if (wspace(**str)) {
187 while (wspace(**str)) {
188 (*str)++;
189 offset++;
190 if (offset == _fsize) {
191 *str = NULL;
192 goto out;
193 }
194 }
195
196 // floating text
197 if (**str != '\0') {
198 retv = -1;
199 goto out;
200 }
201 continue;
202 }
203
204 // configuration line?
205 if (**str != COMMENT_CHAR)
206 break;
207 } while (1);
208
209 out:
210 return retv;
211 }
212
213 /* Will return the corresponding value string or NULL
214 * if non-existent. The token will be cleared if strip
215 * is true. This makes multible like-named options
216 * possible so as to sequentially read them all.
217 *
218 * The return value must be freed by callee. */
219 char *Config::get_value(const char *token)
220 {
221 size_t n = 0;
222
223 if (_items) {
224 while (_items[n].value) {
225 if ((_items[n].token) && !strcmp(token, _items[n].token)) {
226 if (_stack && !strcmp(token, _stack))
227 _items[n].token = NULL;
228 return _strdup(_items[n].value);
229 }
230 n++;
231 }
232 }
233
234 return NULL;
235 }
236
237 /* Reads all of file_name into *buf, as a set of adjoined
238 * strings, correcting for CRLF.
239 *
240 * *size is the size of the file in bytes, not the size
241 * of the buffer *buf. Callee must free() *buf if zero
242 * is returned, otherwise not.
243 *
244 * I am reading one byte of the file at a time. This is to
245 * avoid a second copy/allocation. Not that this method is
246 * particular fast, but it makes the purpose so much clearer.
247 * This function is not a bottleneck, therefore clarity before
248 * speed.
249 *
250 * Returns -1 on error. */
251 int Config::read_conf(const char *file_name, size_t *size, void **buf)
252 {
253 FILE *file;
254 int retv = 0;
255 size_t len;
256 char *tmp;
257 char *first;
258 struct stat fstatus;
259
260
261 retv = stat(file_name, &fstatus);
262 if (retv)
263 return -1;
264
265 if (fstatus.st_size > MAX_CONFIG_FILE_SIZE)
266 return -1;
267
268 tmp = (char*)malloc(fstatus.st_size);
269 if (!tmp)
270 return -1;
271 memset((void*)tmp, '\0', fstatus.st_size);
272 first = tmp;
273
274 if (fopen_s(&file, file_name, "r")) {
275 free(tmp);
276 return -1;
277 }
278
279 char c;
280 off_t pos = 0;
281 while (1) {
282 // For clarity, not speed
283 len = fread((void*)&c, 1, 1, file);
284 if (!len) {
285 if (ferror(file))
286 retv = -1;
287 break;
288 }
289
290 // create null-terminated string
291 if (c == '\n') {
292 c = '\0';
293 }
294
295 // skip CR
296 if (c != '\r') {
297 *tmp = c;
298 pos++;
299 if (pos < fstatus.st_size)
300 tmp++;
301 }
302 }
303 if (file)
304 fclose(file);
305
306 if (retv)
307 free(tmp);
308 else {
309 *buf = first;
310 *size = pos;
311 }
312
313 return retv;
314 }
315
316 bool Config::get_token_value_pair(const char *token,
317 const char *value)
318 {
319 static size_t n = 0;
320
321 if (!_items[n].token)
322 return false;
323
324 token = _items[n].token;
325 value = _items[n].value;
326 n++;
327
328 return true;
329 }