"SfR Fresh" - the SfR Freeware/Shareware Archive 
Member "petidomo-4.0b6/libconfigfile/config.c" of archive petidomo-4.0b6.tar.gz:
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 $Source: /e/ossp/cvs/ossp-pkg/petidomo/libconfigfile/config.c,v $
3 $Revision: 1.4 $
4
5 Copyright (C) 2000 by CyberSolutions GmbH, Germany.
6
7 This file is part of OpenPetidomo.
8
9 OpenPetidomo is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
13
14 OpenPetidomo is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with OpenPetidomo; see the file COPYING. If not, write to
21 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA.
23 */
24
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <syslog.h>
29
30 #include "../liblists/lists.h"
31 #include "../libtext/text.h"
32 #include "configfile.h"
33
34 char * loadfile(const char *);
35
36 static List Files = NULL;
37
38 /* Remove a trailing carrige return from a string */
39
40 static void
41 ChopCR(char * p)
42 {
43 while (*p != '\0' && *p != '\n')
44 p++;
45
46 *p = '\0';
47 }
48
49 /* Turn a string into the number of an array element that matches this
50 string. */
51
52 static int
53 FindCFEntry(struct ConfigFile cf[], const char * keyword)
54 {
55 int i;
56
57 for (i = 0; (&(cf[i]))->keyword != NULL; i++) {
58 if ((strcasecmp((&(cf[i]))->keyword, keyword)) == 0)
59 return i;
60 }
61
62 return -1;
63 }
64
65 /* ReadConfig() will read and parse a given config file and fill the
66 parsed tags into the ConfigFile structure given on the command
67 line. The 'ConfigFile' structure consists of three fields: The name
68 of the config tag to look for, a description of how to interpret
69 the parameter specified with it and a pointer to a 'void
70 pointer'-type, which will contain the result when ReadConfig()
71 returns.
72
73 Valid tag types are CF_STRING (result buffer will contain a string
74 pointer to the text), CF_INTEGER (parameter will be turned into an
75 integer and stored into the result buffer) and CF_YES_NO
76 (parameter is expected to be a 'yes', 'no', 'true' or 'false' and
77 the appropriate binary setting will be stored).
78
79 RETURNS: A return code of zero (0) indicates success, -1 indicates
80 failure. In case of a failure, ReadConfig() will log an error
81 description using the syslog(3) mechanism.
82
83 */
84
85 int
86 ReadConfig(const char * filename, /* path to the config file to parse */
87 struct ConfigFile cf[] /* structure describing the config tags */
88 )
89 {
90 Node node;
91 char * file_buf;
92 char * currLine;
93 char * nextLine;
94 char * keyword;
95 char * data;
96 int rc;
97 int i;
98 unsigned int numLine;
99
100 /* Initialize list of loaded config files. */
101
102 if (Files == NULL) {
103 Files = InitList(NULL);
104 if (Files == NULL) {
105 syslog(LOG_ERR, "ReadConfig: Failed to initialize my internal config file list.");
106 return -1;
107 }
108 }
109 else {
110 /* Check whether we already parsed that file. */
111
112 node = FindNodeByKey(Files, filename);
113 if (node != NULL)
114 return 0;
115 }
116
117 /* Load the file into memory. */
118
119 file_buf = loadfile(filename);
120 if (file_buf == NULL) {
121 syslog(LOG_WARNING, "ReadConfig: Failed to load config file \"%s\": %s", filename, strerror(errno));
122 return -1;
123 }
124
125 /* Store the buffer details in the linked list. */
126
127 filename = strdup(filename);
128 if (filename == NULL) {
129 syslog(LOG_ERR, "ReadConfig: Failed to load config file \"%s\": %s", filename, strerror(errno));
130 return -1;
131 }
132 node = AppendNode(Files, filename, file_buf);
133 if (node == NULL) {
134 syslog(LOG_ERR, "ReadConfig: Internal Error: Couldn't add config file data to my list.");
135 return -1;
136 }
137
138 /* Parse the file line by line. */
139
140 for (numLine = 1, nextLine = currLine = file_buf; *currLine != '\0'; currLine = nextLine, numLine++) {
141 nextLine = text_find_next_line(currLine);
142 ChopCR(currLine);
143
144 /* Ignore comments or empty lines. */
145
146 if ((text_easy_pattern_match(currLine, "^[\t ]*#|^[\t ]*$")) == TRUE)
147 continue; /* ignore it */
148
149 /* Line is supposed to contain a config statement, so we
150 should do a consistency check first. If the line passes,
151 there're no surprises when we actually parse it. */
152
153 if ((text_easy_pattern_match(currLine, "^[[:alnum:]_-]+[[:space:]]+[^[:space:]]+.*[^[:space:]]+[[:space:]]*$")) == FALSE) {
154 syslog(LOG_WARNING, "ReadConfig: Line \"%s\" is syntactically incorrect.",
155 currLine);
156 continue; /* ignore it */
157 }
158
159 /* Remove all unnecessary whitespace. */
160
161 rc = text_transform_text(currLine, currLine, "^([[:alnum:]_-]+)[[:space:]]+([^[:space:]]+.*[^[:space:]]+)[[:space:]]*$", "\\1 \\2");
162 if (rc != 0) {
163 syslog(LOG_WARNING, "ReadConfig: Internal error while parsing line: %d.", rc);
164 continue; /* ignore it */
165 }
166
167 /* Locate the keyword and data part. */
168
169 for (keyword = currLine; *currLine != ' '; currLine++)
170 ;
171 *currLine++ = '\0';
172 data = currLine;
173
174 /* Find appropriate entry in the ConfigFile structure and
175 check whether we know the keyword. */
176
177 i = FindCFEntry(cf, keyword);
178 if (i == -1) {
179 syslog(LOG_WARNING, "ReadConfig: Unrecognized keyword \"%s\" in file \"%s\", line %d.",
180 keyword, filename, numLine);
181 continue; /* ignore it */
182 }
183
184 /* Determine type of data and store it appropriately. */
185
186 switch ((&(cf[i]))->type) {
187 case CF_STRING:
188 /* Handle strings included in quotes. */
189
190 rc = text_transform_text(data, data, "^\"([^\"]*)\"$", "\\1");
191 if (rc != 0 && rc != TEXT_REGEX_TRANSFORM_DIDNT_MATCH) {
192 syslog(LOG_WARNING, "ReadConfig: Internal error while parsing file \"%s\", line: %d.",
193 filename, numLine);
194 continue;
195 }
196
197 /* Store the string. */
198
199 if ((&(cf[i]))->data != NULL)
200 *((char **)(&(cf[i]))->data) = data;
201 break;
202
203 case CF_INTEGER:
204 /* Integers will have an additional consistency check to
205 warn the user about typing errors. */
206
207 if ((text_easy_pattern_match(data, "^[-+][[:digit:]]+$|^[[:digit:]]+$")) == FALSE) {
208 syslog(LOG_WARNING, "ReadConfig: Specified parameter \"%s\" in file \"%s\", line %d, is not a number.", data, filename, numLine);
209 continue; /* ignore it */
210 }
211 if ((&(cf[i]))->data != NULL)
212 *((int *)(&(cf[i]))->data) = atoi(data);
213 break;
214 case CF_YES_NO:
215 if ((text_easy_pattern_match(data, "^yes$|^true$|^y$")) == TRUE) {
216 if ((&(cf[i]))->data != NULL)
217 *((bool *)(&(cf[i]))->data) = TRUE;
218 } else if ((text_easy_pattern_match(data, "^no$|^false$|^n$")) == TRUE) {
219 if ((&(cf[i]))->data != NULL)
220 *((bool *)(&(cf[i]))->data) = FALSE;
221 } else {
222 syslog(LOG_WARNING, "ReadConfig: Specified parameter \"%s\" in file \"%s\", line %d, is not a yes or a no.", data, filename, numLine);
223 continue; /* ignore it */
224 }
225 break;
226 case CF_MULTI:
227 syslog(LOG_WARNING, "ReadConfig: Method not supported at the moment.");
228 break;
229 default:
230 syslog(LOG_ERR, "ReadConfig internal error: ConfigFile structure element %d has unknown type %d.", i, (&(cf[i]))->type);
231 }
232
233 }
234
235 return 0;
236 }
237
238 /* This routine must be used to return all resources to the system,
239 that have been allocaged by a specific ReadConfig(3) call. After
240 that, all result buffers provided by ReadConfig() will be invalid. */
241
242 void
243 FreeConfig(const char * filename)
244 {
245 Node node;
246
247 node = FindNodeByKey(Files, filename);
248 if (node == NULL)
249 return;
250
251 free((void *) getNodeData(node));
252 free((void *) getNodeKey(node));
253 RemoveNode(node);
254 FreeNode(node);
255 }
256
257 /* This function call will free all resources for all previous
258 ReadConfig(3) calls.
259
260 BUGS: Not implemented at the moment.
261 */
262
263 void
264 FreeAllConfigs(void)
265 {
266
267 }
268
269 static char *
270 LocateKeywordInBuffer(char * buffer, char * keyword)
271 {
272 char * p;
273
274 for (p = buffer; p != NULL; ) {
275 p = text_find_string(p, keyword);
276 if (p != NULL) {
277 if (p == buffer || p[-1] == '\n' || p[-1] == '\r')
278 break;
279 else
280 p++; /* Not a keyword, look again. */
281 }
282 }
283
284 return p;
285 }
286
287 /* Load a config file and parse it for the required keyword. Then
288 return the parameter. The buffer containing the parameter should be
289 free()'d by the caller. #
290
291 RETURNS: If the keyword is not found, NULL is returned and errno is
292 set to 0. If an error occurs, NULL is returned also, but errno is
293 set accordingly.
294 */
295
296 char *
297 GetConfig(const char * filename, char * keyword)
298 {
299 char * file_buf;
300 char * result_buf;
301 char * p;
302 int rc;
303
304 /* Load the config file. */
305
306 file_buf = loadfile(filename);
307 if (file_buf == NULL) {
308 syslog(LOG_ERR, "GetConfig: Failed to load config file \"%s\": %s", filename, strerror(errno));
309 return NULL;
310 }
311
312 /* Find the keyword. */
313
314 p = LocateKeywordInBuffer(file_buf, keyword);
315
316 if (p == NULL) {
317 /* No luck, keyword doesn't exist. */
318 errno = 0;
319 return NULL;
320 }
321
322 /* Okay, keyword found. Now extract the parameter. */
323
324 ChopCR(p);
325 rc = text_transform_text(p, p, "^[^\t ]+[\t ]+([^\t ].*)[\t ]*$", "\\1");
326 if (rc != 0) {
327 syslog(LOG_WARNING, "GetConfig: Config file entry \"%s\" is corrupt.", keyword);
328 errno = -1;
329 return NULL;
330 }
331 rc = text_transform_text(p, p, "^\"([^\"]*)\"$", "\\1");
332 if (rc != 0 && rc != TEXT_REGEX_TRANSFORM_DIDNT_MATCH) {
333 syslog(LOG_WARNING, "GetConfig: Config file entry \"%s\" is corrupt.", keyword);
334 errno = -1;
335 return NULL;
336 }
337
338
339 /* Copy the parameter into a new buffer. */
340
341 result_buf = strdup(p);
342 if (result_buf == NULL) {
343 syslog(LOG_ERR, "GetConfig: Failed to allocate %d byte of memory.", strlen(p));
344 errno = ENOMEM;
345 return NULL;
346 }
347
348 /* Free the used memory and return the result. */
349
350 free(file_buf);
351 return result_buf;
352 }
353
354 /* This routine will set a given config entry in a config file. When
355 the keyword is already there, the parameter will be changed.
356 Otherwise the string "keyword<tab>data" is appended at the end of
357 the file.
358
359 RETURNS: If something goes wrong, the routine returns -1 and sets
360 errno. Otherwise we return 0.
361
362 BUGS: This routine may DESTRUCT THE FILE if some I/O error occurs.
363 It is the caller's responsibility to backup the file before calling
364 us.
365 */
366
367 int
368 SetConfig(const char * filename,
369 char * keyword, /* string pointer to the tag name */
370 const char * data /* string pointer to the tag data */
371 )
372 {
373 struct flock lock;
374 char * file_buf;
375 int file_len;
376 char * p;
377 char * nextLine;
378 int fd;
379 int rc;
380
381 /* Load the config file. */
382
383 file_buf = loadfile(filename);
384 if (file_buf == NULL) {
385 syslog(LOG_ERR, "SetConfig: Failed to load config file \"%s\": %s", filename, strerror(errno));
386 return -1;
387 }
388 file_len = errno;
389
390 /* Find the keyword. */
391
392 p = LocateKeywordInBuffer(file_buf, keyword);
393
394 if (p == NULL) {
395 /* No luck, keyword doesn't exist. Append the data. */
396
397 fd = open(filename, O_WRONLY | O_APPEND, 0666);
398 if (fd == -1)
399 goto io_error;
400
401 lock.l_start = 0;
402 lock.l_len = 0;
403 lock.l_type = F_WRLCK;
404 lock.l_whence = SEEK_SET;
405 fcntl(fd, F_SETLKW, &lock);
406
407 /* Does the file end in a '\n' or must we append one? */
408
409 if (file_buf[file_len - 1] != '\n') {
410 rc = write(fd, "\n", 1);
411 if (rc == -1)
412 goto io_error;
413 }
414
415 /* Write the actual keyword and data. */
416
417 if ((write(fd, keyword, strlen(keyword)) == -1)
418 || (write(fd, "\t", 1) == -1)
419 || (write(fd, data, strlen(data)) == -1)
420 || (write(fd, "\n", 1) == -1)) {
421 goto io_error;
422 }
423 }
424 else {
425 fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
426 if (fd == -1)
427 goto io_error;
428
429 lock.l_start = 0;
430 lock.l_len = 0;
431 lock.l_type = F_WRLCK;
432 lock.l_whence = SEEK_SET;
433 fcntl(fd, F_SETLKW, &lock);
434
435 nextLine = text_find_next_line(p);
436 *p = '\0';
437
438 /* Write buffer back until the to-be-replaced keyword. */
439
440 rc = write(fd, file_buf, strlen(file_buf));
441 if (fd == -1)
442 goto io_error;
443
444 /* Write the actual keyword and data. */
445
446 if ((write(fd, keyword, strlen(keyword)) == -1)
447 || (write(fd, "\t", 1) == -1)
448 || (write(fd, data, strlen(data)) == -1)
449 || (write(fd, "\n", 1) == -1)) {
450 goto io_error;
451 }
452
453 /* Write the rest of the buffer. */
454
455 rc = write(fd, nextLine, strlen(nextLine));
456 if (fd == -1)
457 goto io_error;
458 }
459
460 close(fd);
461 free(file_buf);
462 return 0;
463
464 io_error:
465 syslog(LOG_ERR, "SetConfig: Failed to write to file \"%s\": %s", filename, strerror(errno));
466 free(file_buf);
467 return -1;
468 }