]>
Commit | Line | Data |
---|---|---|
2fa9ba98 TT |
1 | /* |
2 | * profile.c -- A simple configuration file parsing "library in a file" | |
efc6f628 | 3 | * |
2fa9ba98 TT |
4 | * The profile library was originally written by Theodore Ts'o in 1995 |
5 | * for use in the MIT Kerberos v5 library. It has been | |
6 | * modified/enhanced/bug-fixed over time by other members of the MIT | |
7 | * Kerberos team. This version was originally taken from the Kerberos | |
8 | * v5 distribution, version 1.4.2, and radically simplified for use in | |
9 | * e2fsprogs. (Support for locking for multi-threaded operations, | |
10 | * being able to modify and update the configuration file | |
11 | * programmatically, and Mac/Windows portability have been removed. | |
12 | * It has been folded into a single C source file to make it easier to | |
13 | * fold into an application program.) | |
14 | * | |
d45544ca | 15 | * Copyright (C) 2005, 2006 by Theodore Ts'o. |
2fa9ba98 TT |
16 | * |
17 | * %Begin-Header% | |
18 | * This file may be redistributed under the terms of the GNU Public | |
19 | * License. | |
20 | * %End-Header% | |
efc6f628 | 21 | * |
2fa9ba98 | 22 | * Copyright (C) 1985-2005 by the Massachusetts Institute of Technology. |
efc6f628 | 23 | * |
2fa9ba98 | 24 | * All rights reserved. |
efc6f628 | 25 | * |
2fa9ba98 TT |
26 | * Export of this software from the United States of America may require |
27 | * a specific license from the United States Government. It is the | |
28 | * responsibility of any person or organization contemplating export to | |
29 | * obtain such a license before exporting. | |
efc6f628 | 30 | * |
2fa9ba98 TT |
31 | * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and |
32 | * distribute this software and its documentation for any purpose and | |
33 | * without fee is hereby granted, provided that the above copyright | |
34 | * notice appear in all copies and that both that copyright notice and | |
35 | * this permission notice appear in supporting documentation, and that | |
36 | * the name of M.I.T. not be used in advertising or publicity pertaining | |
37 | * to distribution of the software without specific, written prior | |
38 | * permission. Furthermore if you modify this software you must label | |
39 | * your software as modified software and not distribute it in such a | |
40 | * fashion that it might be confused with the original MIT software. | |
41 | * M.I.T. makes no representations about the suitability of this software | |
42 | * for any purpose. It is provided "as is" without express or implied | |
43 | * warranty. | |
efc6f628 | 44 | * |
2fa9ba98 TT |
45 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
46 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
47 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
efc6f628 | 48 | * |
2fa9ba98 TT |
49 | */ |
50 | ||
d1154eb4 | 51 | #include "config.h" |
2fa9ba98 TT |
52 | #ifdef HAVE_UNISTD_H |
53 | #include <unistd.h> | |
54 | #endif | |
55 | #include <stdio.h> | |
56 | #ifdef HAVE_STDLIB_H | |
57 | #include <stdlib.h> | |
58 | #endif | |
59 | #include <time.h> | |
60 | #include <string.h> | |
61 | #include <errno.h> | |
62 | #include <ctype.h> | |
63 | #include <limits.h> | |
64 | #include <stddef.h> | |
65 | #include <sys/types.h> | |
66 | #include <sys/stat.h> | |
7d922f8b | 67 | #include <dirent.h> |
2fa9ba98 TT |
68 | #ifdef HAVE_PWD_H |
69 | #include <pwd.h> | |
70 | #endif | |
71 | ||
cec7103d | 72 | #include <et/com_err.h> |
2fa9ba98 | 73 | #include "profile.h" |
fd7ac1fc TT |
74 | #include "prof_err.h" |
75 | ||
5582275f TT |
76 | #undef STAT_ONCE_PER_SECOND |
77 | #undef HAVE_STAT | |
2fa9ba98 | 78 | |
2fa9ba98 TT |
79 | /* |
80 | * prof_int.h | |
81 | */ | |
82 | ||
83 | typedef long prf_magic_t; | |
84 | ||
85 | /* | |
86 | * This is the structure which stores the profile information for a | |
87 | * particular configuration file. | |
88 | */ | |
d45544ca | 89 | struct _prf_file_t { |
2fa9ba98 | 90 | prf_magic_t magic; |
d45544ca | 91 | char *filespec; |
2fa9ba98 TT |
92 | #ifdef STAT_ONCE_PER_SECOND |
93 | time_t last_stat; | |
94 | #endif | |
95 | time_t timestamp; /* time tree was last updated from file */ | |
96 | int flags; /* r/w, dirty */ | |
97 | int upd_serial; /* incremented when data changes */ | |
d45544ca | 98 | struct profile_node *root; |
2fa9ba98 TT |
99 | struct _prf_file_t *next; |
100 | }; | |
101 | ||
102 | typedef struct _prf_file_t *prf_file_t; | |
103 | ||
104 | /* | |
105 | * The profile flags | |
106 | */ | |
107 | #define PROFILE_FILE_RW 0x0001 | |
108 | #define PROFILE_FILE_DIRTY 0x0002 | |
6bde5c28 | 109 | #define PROFILE_FILE_NO_RELOAD 0x0004 |
2fa9ba98 TT |
110 | |
111 | /* | |
112 | * This structure defines the high-level, user visible profile_t | |
113 | * object, which is used as a handle by users who need to query some | |
114 | * configuration file(s) | |
115 | */ | |
116 | struct _profile_t { | |
117 | prf_magic_t magic; | |
118 | prf_file_t first_file; | |
119 | }; | |
120 | ||
121 | /* | |
122 | * Used by the profile iterator in prof_get.c | |
123 | */ | |
124 | #define PROFILE_ITER_LIST_SECTION 0x0001 | |
125 | #define PROFILE_ITER_SECTIONS_ONLY 0x0002 | |
126 | #define PROFILE_ITER_RELATIONS_ONLY 0x0004 | |
127 | ||
128 | #define PROFILE_ITER_FINAL_SEEN 0x0100 | |
129 | ||
130 | /* | |
131 | * Check if a filespec is last in a list (NULL on UNIX, invalid FSSpec on MacOS | |
132 | */ | |
133 | ||
134 | #define PROFILE_LAST_FILESPEC(x) (((x) == NULL) || ((x)[0] == '\0')) | |
135 | ||
9a4c209c TT |
136 | struct profile_node { |
137 | errcode_t magic; | |
138 | char *name; | |
139 | char *value; | |
140 | int group_level; | |
642935c0 TT |
141 | unsigned int final:1; /* Indicate don't search next file */ |
142 | unsigned int deleted:1; | |
9a4c209c TT |
143 | struct profile_node *first_child; |
144 | struct profile_node *parent; | |
145 | struct profile_node *next, *prev; | |
146 | }; | |
147 | ||
148 | #define CHECK_MAGIC(node) \ | |
149 | if ((node)->magic != PROF_MAGIC_NODE) \ | |
150 | return PROF_MAGIC_NODE; | |
151 | ||
204ae371 TT |
152 | /* profile parser declarations */ |
153 | struct parse_state { | |
154 | int state; | |
155 | int group_level; | |
156 | int line_num; | |
157 | struct profile_node *root_section; | |
158 | struct profile_node *current_section; | |
159 | }; | |
160 | ||
6f890e51 TT |
161 | static const char *default_filename = "<default>"; |
162 | ||
204ae371 TT |
163 | static profile_syntax_err_cb_t syntax_err_cb; |
164 | ||
165 | static errcode_t parse_line(char *line, struct parse_state *state); | |
2fa9ba98 TT |
166 | |
167 | #ifdef DEBUG_PROGRAM | |
168 | static errcode_t profile_write_tree_file | |
169 | (struct profile_node *root, FILE *dstfile); | |
170 | ||
171 | static errcode_t profile_write_tree_to_buffer | |
172 | (struct profile_node *root, char **buf); | |
173 | #endif | |
174 | ||
175 | ||
2fa9ba98 TT |
176 | static void profile_free_node |
177 | (struct profile_node *relation); | |
178 | ||
179 | static errcode_t profile_create_node | |
180 | (const char *name, const char *value, | |
181 | struct profile_node **ret_node); | |
182 | ||
183 | #ifdef DEBUG_PROGRAM | |
184 | static errcode_t profile_verify_node | |
185 | (struct profile_node *node); | |
186 | #endif | |
187 | ||
188 | static errcode_t profile_add_node | |
189 | (struct profile_node *section, | |
190 | const char *name, const char *value, | |
191 | struct profile_node **ret_node); | |
192 | ||
2fa9ba98 TT |
193 | static errcode_t profile_find_node |
194 | (struct profile_node *section, | |
195 | const char *name, const char *value, | |
196 | int section_flag, void **state, | |
197 | struct profile_node **node); | |
198 | ||
2fa9ba98 TT |
199 | static errcode_t profile_node_iterator |
200 | (void **iter_p, struct profile_node **ret_node, | |
201 | char **ret_name, char **ret_value); | |
202 | ||
2fa9ba98 TT |
203 | static errcode_t profile_open_file |
204 | (const char * file, prf_file_t *ret_prof); | |
205 | ||
d45544ca TT |
206 | static errcode_t profile_update_file |
207 | (prf_file_t prf); | |
2fa9ba98 TT |
208 | |
209 | static void profile_free_file | |
210 | (prf_file_t profile); | |
211 | ||
9a4c209c TT |
212 | static errcode_t profile_get_value(profile_t profile, const char *name, |
213 | const char *subname, const char *subsubname, | |
214 | const char **ret_value); | |
215 | ||
2fa9ba98 | 216 | |
2fa9ba98 TT |
217 | /* |
218 | * prof_init.c --- routines that manipulate the user-visible profile_t | |
219 | * object. | |
220 | */ | |
221 | ||
efc6f628 | 222 | static int compstr(const void *m1, const void *m2) |
7d922f8b | 223 | { |
642935c0 TT |
224 | const char *s1 = *((const char * const *) m1); |
225 | const char *s2 = *((const char * const *) m2); | |
7d922f8b | 226 | |
efc6f628 | 227 | return strcmp(s1, s2); |
7d922f8b TT |
228 | } |
229 | ||
230 | static void free_list(char **list) | |
231 | { | |
232 | char **cp; | |
233 | ||
234 | if (list == 0) | |
235 | return; | |
efc6f628 | 236 | |
7d922f8b TT |
237 | for (cp = list; *cp; cp++) |
238 | free(*cp); | |
239 | free(list); | |
240 | } | |
241 | ||
242 | static errcode_t get_dirlist(const char *dirname, char***ret_array) | |
243 | { | |
244 | DIR *dir; | |
245 | struct dirent *de; | |
246 | struct stat st; | |
247 | errcode_t retval; | |
248 | char *fn, *cp; | |
249 | char **array = 0, **new_array; | |
250 | int max = 0, num = 0; | |
251 | ||
252 | dir = opendir(dirname); | |
253 | if (!dir) | |
254 | return errno; | |
255 | ||
256 | while ((de = readdir(dir)) != NULL) { | |
257 | for (cp = de->d_name; *cp; cp++) { | |
258 | if (!isalnum(*cp) && | |
259 | (*cp != '-') && | |
260 | (*cp != '_')) | |
261 | break; | |
262 | } | |
263 | if (*cp) | |
264 | continue; | |
265 | fn = malloc(strlen(dirname) + strlen(de->d_name) + 2); | |
266 | if (!fn) { | |
267 | retval = ENOMEM; | |
268 | goto errout; | |
269 | } | |
270 | sprintf(fn, "%s/%s", dirname, de->d_name); | |
271 | if ((stat(fn, &st) < 0) || !S_ISREG(st.st_mode)) { | |
272 | free(fn); | |
273 | continue; | |
274 | } | |
275 | if (num >= max) { | |
276 | max += 10; | |
277 | new_array = realloc(array, sizeof(char *) * (max+1)); | |
278 | if (!new_array) { | |
279 | retval = ENOMEM; | |
6d4ced21 | 280 | free(fn); |
7d922f8b TT |
281 | goto errout; |
282 | } | |
283 | array = new_array; | |
284 | } | |
285 | array[num++] = fn; | |
286 | } | |
12f91959 BB |
287 | if (array) { |
288 | qsort(array, num, sizeof(char *), compstr); | |
289 | array[num++] = 0; | |
290 | } | |
7d922f8b TT |
291 | *ret_array = array; |
292 | closedir(dir); | |
293 | return 0; | |
294 | errout: | |
6d4ced21 ES |
295 | if (array) |
296 | array[num] = 0; | |
7d922f8b TT |
297 | closedir(dir); |
298 | free_list(array); | |
299 | return retval; | |
300 | } | |
301 | ||
efc6f628 | 302 | errcode_t |
2fa9ba98 TT |
303 | profile_init(const char **files, profile_t *ret_profile) |
304 | { | |
305 | const char **fs; | |
306 | profile_t profile; | |
7d922f8b | 307 | prf_file_t new_file, *last; |
2fa9ba98 | 308 | errcode_t retval = 0; |
7d922f8b | 309 | char **cpp, *cp, **array = 0; |
2fa9ba98 TT |
310 | |
311 | profile = malloc(sizeof(struct _profile_t)); | |
312 | if (!profile) | |
313 | return ENOMEM; | |
314 | memset(profile, 0, sizeof(struct _profile_t)); | |
315 | profile->magic = PROF_MAGIC_PROFILE; | |
7d922f8b | 316 | last = &profile->first_file; |
2fa9ba98 TT |
317 | |
318 | /* if the filenames list is not specified return an empty profile */ | |
319 | if ( files ) { | |
320 | for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) { | |
f0131bdc DW |
321 | if (array) |
322 | free_list(array); | |
603fc2cb | 323 | array = NULL; |
7d922f8b TT |
324 | retval = get_dirlist(*fs, &array); |
325 | if (retval == 0) { | |
12f91959 BB |
326 | if (!array) |
327 | continue; | |
7d922f8b TT |
328 | for (cpp = array; (cp = *cpp); cpp++) { |
329 | retval = profile_open_file(cp, &new_file); | |
330 | if (retval == EACCES) | |
331 | continue; | |
332 | if (retval) | |
333 | goto errout; | |
334 | *last = new_file; | |
335 | last = &new_file->next; | |
336 | } | |
efc6f628 | 337 | } else if ((retval != ENOTDIR) && |
6f890e51 | 338 | strcmp(*fs, default_filename)) |
7d922f8b TT |
339 | goto errout; |
340 | ||
2fa9ba98 TT |
341 | retval = profile_open_file(*fs, &new_file); |
342 | /* if this file is missing, skip to the next */ | |
343 | if (retval == ENOENT || retval == EACCES) { | |
344 | continue; | |
345 | } | |
7d922f8b TT |
346 | if (retval) |
347 | goto errout; | |
348 | *last = new_file; | |
349 | last = &new_file->next; | |
2fa9ba98 TT |
350 | } |
351 | /* | |
7d922f8b | 352 | * If all the files were not found, return the appropriate error. |
2fa9ba98 | 353 | */ |
7d922f8b | 354 | if (!profile->first_file) { |
6d4ced21 ES |
355 | retval = ENOENT; |
356 | goto errout; | |
2fa9ba98 TT |
357 | } |
358 | } | |
359 | ||
7d922f8b | 360 | free_list(array); |
2fa9ba98 TT |
361 | *ret_profile = profile; |
362 | return 0; | |
7d922f8b TT |
363 | errout: |
364 | free_list(array); | |
365 | profile_release(profile); | |
366 | return retval; | |
2fa9ba98 TT |
367 | } |
368 | ||
efc6f628 | 369 | void |
2fa9ba98 TT |
370 | profile_release(profile_t profile) |
371 | { | |
372 | prf_file_t p, next; | |
373 | ||
374 | if (!profile || profile->magic != PROF_MAGIC_PROFILE) | |
375 | return; | |
376 | ||
377 | for (p = profile->first_file; p; p = next) { | |
378 | next = p->next; | |
379 | profile_free_file(p); | |
380 | } | |
381 | profile->magic = 0; | |
382 | free(profile); | |
383 | } | |
384 | ||
6f890e51 TT |
385 | /* |
386 | * This function sets the value of the pseudo file "<default>". If | |
387 | * the file "<default>" had previously been passed to profile_init(), | |
388 | * then def_string parameter will be parsed and used as the profile | |
389 | * information for the "<default>" file. | |
390 | */ | |
391 | errcode_t profile_set_default(profile_t profile, const char *def_string) | |
392 | { | |
393 | struct parse_state state; | |
394 | prf_file_t prf; | |
395 | errcode_t retval; | |
396 | const char *in; | |
397 | char *line, *p, *end; | |
398 | int line_size, len; | |
399 | ||
400 | if (!def_string || !profile || profile->magic != PROF_MAGIC_PROFILE) | |
401 | return PROF_MAGIC_PROFILE; | |
402 | ||
403 | for (prf = profile->first_file; prf; prf = prf->next) { | |
404 | if (strcmp(prf->filespec, default_filename) == 0) | |
405 | break; | |
406 | } | |
407 | if (!prf) | |
408 | return 0; | |
409 | ||
410 | if (prf->root) { | |
411 | profile_free_node(prf->root); | |
412 | prf->root = 0; | |
413 | } | |
414 | ||
415 | memset(&state, 0, sizeof(struct parse_state)); | |
416 | retval = profile_create_node("(root)", 0, &state.root_section); | |
417 | if (retval) | |
418 | return retval; | |
419 | ||
420 | line = 0; | |
421 | line_size = 0; | |
422 | in = def_string; | |
423 | while (*in) { | |
424 | end = strchr(in, '\n'); | |
425 | len = end ? (end - in) : (int) strlen(in); | |
426 | if (len >= line_size) { | |
427 | line_size = len+1; | |
428 | p = realloc(line, line_size); | |
429 | if (!p) { | |
430 | retval = ENOMEM; | |
431 | goto errout; | |
432 | } | |
433 | line = p; | |
434 | } | |
435 | memcpy(line, in, len); | |
436 | line[len] = 0; | |
437 | retval = parse_line(line, &state); | |
438 | if (retval) { | |
439 | errout: | |
440 | if (syntax_err_cb) | |
efc6f628 | 441 | (syntax_err_cb)(prf->filespec, retval, |
6f890e51 TT |
442 | state.line_num); |
443 | free(line); | |
444 | if (prf->root) | |
445 | profile_free_node(prf->root); | |
446 | return retval; | |
447 | } | |
448 | if (!end) | |
449 | break; | |
450 | in = end+1; | |
451 | } | |
452 | prf->root = state.root_section; | |
453 | free(line); | |
454 | ||
455 | return 0; | |
456 | } | |
2fa9ba98 | 457 | |
2fa9ba98 TT |
458 | /* |
459 | * prof_file.c ---- routines that manipulate an individual profile file. | |
460 | */ | |
461 | ||
2fa9ba98 TT |
462 | errcode_t profile_open_file(const char * filespec, |
463 | prf_file_t *ret_prof) | |
464 | { | |
465 | prf_file_t prf; | |
466 | errcode_t retval; | |
467 | char *home_env = 0; | |
468 | unsigned int len; | |
2fa9ba98 TT |
469 | char *expanded_filename; |
470 | ||
471 | prf = malloc(sizeof(struct _prf_file_t)); | |
472 | if (!prf) | |
473 | return ENOMEM; | |
474 | memset(prf, 0, sizeof(struct _prf_file_t)); | |
475 | prf->magic = PROF_MAGIC_FILE; | |
476 | ||
477 | len = strlen(filespec)+1; | |
478 | if (filespec[0] == '~' && filespec[1] == '/') { | |
479 | home_env = getenv("HOME"); | |
480 | #ifdef HAVE_PWD_H | |
481 | if (home_env == NULL) { | |
95a8d1de | 482 | #ifdef HAVE_GETWUID_R |
2fa9ba98 | 483 | struct passwd *pw, pwx; |
95a8d1de | 484 | uid_t uid; |
2fa9ba98 TT |
485 | char pwbuf[BUFSIZ]; |
486 | ||
487 | uid = getuid(); | |
95a8d1de | 488 | if (!getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw) |
2fa9ba98 TT |
489 | && pw != NULL && pw->pw_dir[0] != 0) |
490 | home_env = pw->pw_dir; | |
95a8d1de TT |
491 | #else |
492 | struct passwd *pw; | |
493 | ||
494 | pw = getpwuid(getuid()); | |
495 | home_env = pw->pw_dir; | |
496 | #endif | |
2fa9ba98 TT |
497 | } |
498 | #endif | |
499 | if (home_env) | |
500 | len += strlen(home_env); | |
501 | } | |
502 | expanded_filename = malloc(len); | |
93503267 | 503 | if (expanded_filename == 0) { |
efc6f628 | 504 | profile_free_file(prf); |
2fa9ba98 | 505 | return errno; |
93503267 | 506 | } |
2fa9ba98 TT |
507 | if (home_env) { |
508 | strcpy(expanded_filename, home_env); | |
509 | strcat(expanded_filename, filespec+1); | |
510 | } else | |
511 | memcpy(expanded_filename, filespec, len); | |
512 | ||
d45544ca | 513 | prf->filespec = expanded_filename; |
2fa9ba98 | 514 | |
6f890e51 TT |
515 | if (strcmp(prf->filespec, default_filename) != 0) { |
516 | retval = profile_update_file(prf); | |
517 | if (retval) { | |
518 | profile_free_file(prf); | |
519 | return retval; | |
520 | } | |
2fa9ba98 TT |
521 | } |
522 | ||
523 | *ret_prof = prf; | |
524 | return 0; | |
525 | } | |
526 | ||
d45544ca | 527 | errcode_t profile_update_file(prf_file_t prf) |
2fa9ba98 TT |
528 | { |
529 | errcode_t retval; | |
530 | #ifdef HAVE_STAT | |
531 | struct stat st; | |
532 | #ifdef STAT_ONCE_PER_SECOND | |
533 | time_t now; | |
534 | #endif | |
535 | #endif | |
536 | FILE *f; | |
204ae371 TT |
537 | char buf[2048]; |
538 | struct parse_state state; | |
2fa9ba98 | 539 | |
6bde5c28 TT |
540 | if (prf->flags & PROFILE_FILE_NO_RELOAD) |
541 | return 0; | |
542 | ||
2fa9ba98 TT |
543 | #ifdef HAVE_STAT |
544 | #ifdef STAT_ONCE_PER_SECOND | |
545 | now = time(0); | |
d45544ca | 546 | if (now == prf->last_stat && prf->root != NULL) { |
2fa9ba98 TT |
547 | return 0; |
548 | } | |
549 | #endif | |
d45544ca | 550 | if (stat(prf->filespec, &st)) { |
2fa9ba98 TT |
551 | retval = errno; |
552 | return retval; | |
553 | } | |
554 | #ifdef STAT_ONCE_PER_SECOND | |
d45544ca | 555 | prf->last_stat = now; |
2fa9ba98 | 556 | #endif |
d45544ca | 557 | if (st.st_mtime == prf->timestamp && prf->root != NULL) { |
2fa9ba98 TT |
558 | return 0; |
559 | } | |
d45544ca TT |
560 | if (prf->root) { |
561 | profile_free_node(prf->root); | |
562 | prf->root = 0; | |
2fa9ba98 TT |
563 | } |
564 | #else | |
565 | /* | |
566 | * If we don't have the stat() call, assume that our in-core | |
567 | * memory image is correct. That is, we won't reread the | |
568 | * profile file if it changes. | |
569 | */ | |
d45544ca | 570 | if (prf->root) { |
2fa9ba98 TT |
571 | return 0; |
572 | } | |
573 | #endif | |
204ae371 TT |
574 | memset(&state, 0, sizeof(struct parse_state)); |
575 | retval = profile_create_node("(root)", 0, &state.root_section); | |
576 | if (retval) | |
577 | return retval; | |
2fa9ba98 | 578 | errno = 0; |
d45544ca | 579 | f = fopen(prf->filespec, "r"); |
2fa9ba98 TT |
580 | if (f == NULL) { |
581 | retval = errno; | |
582 | if (retval == 0) | |
583 | retval = ENOENT; | |
584 | return retval; | |
585 | } | |
d45544ca | 586 | prf->upd_serial++; |
204ae371 TT |
587 | while (!feof(f)) { |
588 | if (fgets(buf, sizeof(buf), f) == NULL) | |
589 | break; | |
590 | retval = parse_line(buf, &state); | |
591 | if (retval) { | |
592 | if (syntax_err_cb) | |
efc6f628 | 593 | (syntax_err_cb)(prf->filespec, retval, |
204ae371 TT |
594 | state.line_num); |
595 | fclose(f); | |
596 | return retval; | |
597 | } | |
2fa9ba98 | 598 | } |
204ae371 TT |
599 | prf->root = state.root_section; |
600 | ||
601 | fclose(f); | |
602 | ||
2fa9ba98 | 603 | #ifdef HAVE_STAT |
d45544ca | 604 | prf->timestamp = st.st_mtime; |
2fa9ba98 TT |
605 | #endif |
606 | return 0; | |
607 | } | |
608 | ||
609 | void profile_free_file(prf_file_t prf) | |
610 | { | |
d45544ca TT |
611 | if (prf->root) |
612 | profile_free_node(prf->root); | |
45e338f5 | 613 | free(prf->filespec); |
2fa9ba98 TT |
614 | free(prf); |
615 | } | |
616 | ||
d45544ca | 617 | /* Begin the profile parser */ |
2fa9ba98 | 618 | |
f5f14fcf TT |
619 | profile_syntax_err_cb_t profile_set_syntax_err_cb(profile_syntax_err_cb_t hook) |
620 | { | |
621 | profile_syntax_err_cb_t old; | |
622 | ||
623 | old = syntax_err_cb; | |
624 | syntax_err_cb = hook; | |
625 | return(old); | |
626 | } | |
627 | ||
f5f14fcf TT |
628 | #define STATE_INIT_COMMENT 0 |
629 | #define STATE_STD_LINE 1 | |
630 | #define STATE_GET_OBRACE 2 | |
2fa9ba98 | 631 | |
2fa9ba98 TT |
632 | static char *skip_over_blanks(char *cp) |
633 | { | |
634 | while (*cp && isspace((int) (*cp))) | |
635 | cp++; | |
636 | return cp; | |
637 | } | |
638 | ||
95a8d1de TT |
639 | static int end_or_comment(char ch) |
640 | { | |
641 | return (ch == 0 || ch == '#' || ch == ';'); | |
642 | } | |
643 | ||
644 | static char *skip_over_nonblanks(char *cp) | |
645 | { | |
646 | while (!end_or_comment(*cp) && !isspace(*cp)) | |
647 | cp++; | |
648 | return cp; | |
649 | } | |
650 | ||
2fa9ba98 TT |
651 | static void strip_line(char *line) |
652 | { | |
653 | char *p = line + strlen(line); | |
654 | while (p > line && (p[-1] == '\n' || p[-1] == '\r')) | |
655 | *p-- = 0; | |
656 | } | |
657 | ||
658 | static void parse_quoted_string(char *str) | |
659 | { | |
660 | char *to, *from; | |
661 | ||
662 | to = from = str; | |
663 | ||
664 | for (to = from = str; *from && *from != '"'; to++, from++) { | |
665 | if (*from == '\\') { | |
666 | from++; | |
667 | switch (*from) { | |
668 | case 'n': | |
669 | *to = '\n'; | |
670 | break; | |
671 | case 't': | |
672 | *to = '\t'; | |
673 | break; | |
674 | case 'b': | |
675 | *to = '\b'; | |
676 | break; | |
677 | default: | |
678 | *to = *from; | |
679 | } | |
680 | continue; | |
681 | } | |
682 | *to = *from; | |
683 | } | |
684 | *to = '\0'; | |
685 | } | |
686 | ||
204ae371 | 687 | static errcode_t parse_line(char *line, struct parse_state *state) |
2fa9ba98 TT |
688 | { |
689 | char *cp, ch, *tag, *value; | |
690 | char *p; | |
691 | errcode_t retval; | |
692 | struct profile_node *node; | |
693 | int do_subsection = 0; | |
694 | void *iter = 0; | |
efc6f628 | 695 | |
204ae371 TT |
696 | state->line_num++; |
697 | if (state->state == STATE_GET_OBRACE) { | |
698 | cp = skip_over_blanks(line); | |
699 | if (*cp != '{') | |
700 | return PROF_MISSING_OBRACE; | |
701 | state->state = STATE_STD_LINE; | |
702 | return 0; | |
703 | } | |
704 | if (state->state == STATE_INIT_COMMENT) { | |
705 | if (line[0] != '[') | |
706 | return 0; | |
707 | state->state = STATE_STD_LINE; | |
708 | } | |
709 | ||
2fa9ba98 TT |
710 | if (*line == 0) |
711 | return 0; | |
2fa9ba98 TT |
712 | strip_line(line); |
713 | cp = skip_over_blanks(line); | |
714 | ch = *cp; | |
95a8d1de | 715 | if (end_or_comment(ch)) |
2fa9ba98 TT |
716 | return 0; |
717 | if (ch == '[') { | |
718 | if (state->group_level > 0) | |
719 | return PROF_SECTION_NOTOP; | |
720 | cp++; | |
22fe674d | 721 | cp = skip_over_blanks(cp); |
2fa9ba98 TT |
722 | p = strchr(cp, ']'); |
723 | if (p == NULL) | |
724 | return PROF_SECTION_SYNTAX; | |
22fe674d TT |
725 | if (*cp == '"') { |
726 | cp++; | |
727 | parse_quoted_string(cp); | |
728 | } else { | |
729 | *p-- = '\0'; | |
730 | while (isspace(*p) && (p > cp)) | |
731 | *p-- = '\0'; | |
732 | if (*cp == 0) | |
733 | return PROF_SECTION_SYNTAX; | |
734 | } | |
efc6f628 | 735 | retval = profile_find_node(state->root_section, cp, 0, 1, |
9a4c209c | 736 | &iter, &state->current_section); |
2fa9ba98 TT |
737 | if (retval == PROF_NO_SECTION) { |
738 | retval = profile_add_node(state->root_section, | |
739 | cp, 0, | |
740 | &state->current_section); | |
741 | if (retval) | |
742 | return retval; | |
743 | } else if (retval) | |
744 | return retval; | |
745 | ||
746 | /* | |
747 | * Finish off the rest of the line. | |
748 | */ | |
749 | cp = p+1; | |
750 | if (*cp == '*') { | |
9a4c209c | 751 | state->current_section->final = 1; |
2fa9ba98 TT |
752 | cp++; |
753 | } | |
754 | /* | |
efc6f628 | 755 | * Spaces or comments after ']' should not be fatal |
2fa9ba98 TT |
756 | */ |
757 | cp = skip_over_blanks(cp); | |
95a8d1de | 758 | if (!end_or_comment(*cp)) |
2fa9ba98 TT |
759 | return PROF_SECTION_SYNTAX; |
760 | return 0; | |
761 | } | |
762 | if (ch == '}') { | |
763 | if (state->group_level == 0) | |
764 | return PROF_EXTRA_CBRACE; | |
765 | if (*(cp+1) == '*') | |
9a4c209c TT |
766 | state->current_section->final = 1; |
767 | state->current_section = state->current_section->parent; | |
2fa9ba98 TT |
768 | state->group_level--; |
769 | return 0; | |
770 | } | |
771 | /* | |
772 | * Parse the relations | |
773 | */ | |
774 | tag = cp; | |
775 | cp = strchr(cp, '='); | |
776 | if (!cp) | |
777 | return PROF_RELATION_SYNTAX; | |
778 | if (cp == tag) | |
779 | return PROF_RELATION_SYNTAX; | |
780 | *cp = '\0'; | |
22fe674d TT |
781 | if (*tag == '"') { |
782 | tag++; | |
783 | parse_quoted_string(tag); | |
784 | } else { | |
785 | /* Look for whitespace on left-hand side. */ | |
786 | p = skip_over_nonblanks(tag); | |
787 | if (*p) | |
788 | *p++ = 0; | |
789 | p = skip_over_blanks(p); | |
790 | /* If we have more non-whitespace, it's an error. */ | |
791 | if (*p) | |
792 | return PROF_RELATION_SYNTAX; | |
793 | } | |
95a8d1de | 794 | |
2fa9ba98 TT |
795 | cp = skip_over_blanks(cp+1); |
796 | value = cp; | |
95a8d1de TT |
797 | ch = value[0]; |
798 | if (ch == '"') { | |
2fa9ba98 TT |
799 | value++; |
800 | parse_quoted_string(value); | |
95a8d1de | 801 | } else if (end_or_comment(ch)) { |
2fa9ba98 TT |
802 | do_subsection++; |
803 | state->state = STATE_GET_OBRACE; | |
95a8d1de TT |
804 | } else if (value[0] == '{') { |
805 | cp = skip_over_blanks(value+1); | |
806 | ch = *cp; | |
807 | if (end_or_comment(ch)) | |
808 | do_subsection++; | |
809 | else | |
810 | return PROF_RELATION_SYNTAX; | |
811 | } else { | |
812 | cp = skip_over_nonblanks(value); | |
813 | p = skip_over_blanks(cp); | |
814 | ch = *p; | |
815 | *cp = 0; | |
816 | if (!end_or_comment(ch)) | |
817 | return PROF_RELATION_SYNTAX; | |
2fa9ba98 TT |
818 | } |
819 | if (do_subsection) { | |
820 | p = strchr(tag, '*'); | |
821 | if (p) | |
822 | *p = '\0'; | |
823 | retval = profile_add_node(state->current_section, | |
824 | tag, 0, &state->current_section); | |
825 | if (retval) | |
826 | return retval; | |
827 | if (p) | |
9a4c209c | 828 | state->current_section->final = 1; |
2fa9ba98 TT |
829 | state->group_level++; |
830 | return 0; | |
831 | } | |
832 | p = strchr(tag, '*'); | |
833 | if (p) | |
834 | *p = '\0'; | |
835 | profile_add_node(state->current_section, tag, value, &node); | |
836 | if (p) | |
9a4c209c | 837 | node->final = 1; |
2fa9ba98 TT |
838 | return 0; |
839 | } | |
840 | ||
44dc5f88 | 841 | #ifdef DEBUG_PROGRAM |
2fa9ba98 TT |
842 | /* |
843 | * Return TRUE if the string begins or ends with whitespace | |
844 | */ | |
845 | static int need_double_quotes(char *str) | |
846 | { | |
847 | if (!str || !*str) | |
848 | return 0; | |
849 | if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1)))) | |
850 | return 1; | |
95a8d1de TT |
851 | if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b') || |
852 | strchr(str, ' ') || strchr(str, '#') || strchr(str, ';')) | |
2fa9ba98 TT |
853 | return 1; |
854 | return 0; | |
855 | } | |
856 | ||
857 | /* | |
858 | * Output a string with double quotes, doing appropriate backquoting | |
859 | * of characters as necessary. | |
860 | */ | |
861 | static void output_quoted_string(char *str, void (*cb)(const char *,void *), | |
862 | void *data) | |
863 | { | |
864 | char ch; | |
865 | char buf[2]; | |
866 | ||
867 | cb("\"", data); | |
868 | if (!str) { | |
869 | cb("\"", data); | |
870 | return; | |
871 | } | |
872 | buf[1] = 0; | |
873 | while ((ch = *str++)) { | |
874 | switch (ch) { | |
875 | case '\\': | |
876 | cb("\\\\", data); | |
877 | break; | |
878 | case '\n': | |
879 | cb("\\n", data); | |
880 | break; | |
881 | case '\t': | |
882 | cb("\\t", data); | |
883 | break; | |
884 | case '\b': | |
885 | cb("\\b", data); | |
886 | break; | |
887 | default: | |
888 | /* This would be a lot faster if we scanned | |
889 | forward for the next "interesting" | |
890 | character. */ | |
891 | buf[0] = ch; | |
892 | cb(buf, data); | |
893 | break; | |
894 | } | |
895 | } | |
896 | cb("\"", data); | |
897 | } | |
898 | ||
899 | #ifndef EOL | |
900 | #define EOL "\n" | |
901 | #endif | |
902 | ||
903 | /* Errors should be returned, not ignored! */ | |
904 | static void dump_profile(struct profile_node *root, int level, | |
905 | void (*cb)(const char *, void *), void *data) | |
906 | { | |
907 | int i; | |
908 | struct profile_node *p; | |
909 | void *iter; | |
910 | long retval; | |
efc6f628 | 911 | |
2fa9ba98 TT |
912 | iter = 0; |
913 | do { | |
9a4c209c | 914 | retval = profile_find_node(root, 0, 0, 0, &iter, &p); |
2fa9ba98 TT |
915 | if (retval) |
916 | break; | |
917 | for (i=0; i < level; i++) | |
918 | cb("\t", data); | |
22fe674d TT |
919 | if (need_double_quotes(p->name)) |
920 | output_quoted_string(p->name, cb, data); | |
921 | else | |
9a4c209c | 922 | cb(p->name, data); |
22fe674d TT |
923 | cb(" = ", data); |
924 | if (need_double_quotes(p->value)) | |
9a4c209c | 925 | output_quoted_string(p->value, cb, data); |
22fe674d | 926 | else |
9a4c209c | 927 | cb(p->value, data); |
22fe674d | 928 | cb(EOL, data); |
2fa9ba98 TT |
929 | } while (iter != 0); |
930 | ||
931 | iter = 0; | |
932 | do { | |
9a4c209c | 933 | retval = profile_find_node(root, 0, 0, 1, &iter, &p); |
2fa9ba98 TT |
934 | if (retval) |
935 | break; | |
936 | if (level == 0) { /* [xxx] */ | |
937 | cb("[", data); | |
22fe674d TT |
938 | if (need_double_quotes(p->name)) |
939 | output_quoted_string(p->name, cb, data); | |
940 | else | |
941 | cb(p->name, data); | |
2fa9ba98 | 942 | cb("]", data); |
9a4c209c | 943 | cb(p->final ? "*" : "", data); |
2fa9ba98 TT |
944 | cb(EOL, data); |
945 | dump_profile(p, level+1, cb, data); | |
946 | cb(EOL, data); | |
947 | } else { /* xxx = { ... } */ | |
948 | for (i=0; i < level; i++) | |
949 | cb("\t", data); | |
22fe674d TT |
950 | if (need_double_quotes(p->name)) |
951 | output_quoted_string(p->name, cb, data); | |
952 | else | |
953 | cb(p->name, data); | |
2fa9ba98 TT |
954 | cb(" = {", data); |
955 | cb(EOL, data); | |
956 | dump_profile(p, level+1, cb, data); | |
957 | for (i=0; i < level; i++) | |
958 | cb("\t", data); | |
959 | cb("}", data); | |
9a4c209c | 960 | cb(p->final ? "*" : "", data); |
2fa9ba98 TT |
961 | cb(EOL, data); |
962 | } | |
963 | } while (iter != 0); | |
964 | } | |
965 | ||
2fa9ba98 TT |
966 | static void dump_profile_to_file_cb(const char *str, void *data) |
967 | { | |
968 | fputs(str, data); | |
969 | } | |
970 | ||
971 | errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile) | |
972 | { | |
973 | dump_profile(root, 0, dump_profile_to_file_cb, dstfile); | |
974 | return 0; | |
975 | } | |
2fa9ba98 TT |
976 | |
977 | struct prof_buf { | |
978 | char *base; | |
979 | size_t cur, max; | |
980 | int err; | |
981 | }; | |
982 | ||
2fa9ba98 TT |
983 | static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len) |
984 | { | |
985 | if (b->err) | |
986 | return; | |
987 | if (b->max - b->cur < len) { | |
988 | size_t newsize; | |
989 | char *newptr; | |
990 | ||
991 | newsize = b->max + (b->max >> 1) + len + 1024; | |
992 | newptr = realloc(b->base, newsize); | |
993 | if (newptr == NULL) { | |
994 | b->err = 1; | |
995 | return; | |
996 | } | |
997 | b->base = newptr; | |
998 | b->max = newsize; | |
999 | } | |
1000 | memcpy(b->base + b->cur, d, len); | |
1001 | b->cur += len; /* ignore overflow */ | |
1002 | } | |
1003 | ||
1004 | static void dump_profile_to_buffer_cb(const char *str, void *data) | |
1005 | { | |
1006 | add_data_to_buffer((struct prof_buf *)data, str, strlen(str)); | |
1007 | } | |
1008 | ||
1009 | errcode_t profile_write_tree_to_buffer(struct profile_node *root, | |
1010 | char **buf) | |
1011 | { | |
1012 | struct prof_buf prof_buf = { 0, 0, 0, 0 }; | |
1013 | ||
1014 | dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf); | |
1015 | if (prof_buf.err) { | |
1016 | *buf = NULL; | |
1017 | return ENOMEM; | |
1018 | } | |
1019 | add_data_to_buffer(&prof_buf, "", 1); /* append nul */ | |
1020 | if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) { | |
1021 | char *newptr = realloc(prof_buf.base, prof_buf.cur); | |
1022 | if (newptr) | |
1023 | prof_buf.base = newptr; | |
1024 | } | |
1025 | *buf = prof_buf.base; | |
1026 | return 0; | |
1027 | } | |
1028 | #endif | |
1029 | ||
2fa9ba98 TT |
1030 | /* |
1031 | * prof_tree.c --- these routines maintain the parse tree of the | |
1032 | * config file. | |
efc6f628 | 1033 | * |
2fa9ba98 TT |
1034 | * All of the details of how the tree is stored is abstracted away in |
1035 | * this file; all of the other profile routines build, access, and | |
1036 | * modify the tree via the accessor functions found in this file. | |
1037 | * | |
1038 | * Each node may represent either a relation or a section header. | |
efc6f628 | 1039 | * |
2fa9ba98 TT |
1040 | * A section header must have its value field set to 0, and may a one |
1041 | * or more child nodes, pointed to by first_child. | |
efc6f628 | 1042 | * |
2fa9ba98 TT |
1043 | * A relation has as its value a pointer to allocated memory |
1044 | * containing a string. Its first_child pointer must be null. | |
1045 | * | |
1046 | */ | |
1047 | ||
2fa9ba98 TT |
1048 | /* |
1049 | * Free a node, and any children | |
1050 | */ | |
1051 | void profile_free_node(struct profile_node *node) | |
1052 | { | |
1053 | struct profile_node *child, *next; | |
1054 | ||
1055 | if (node->magic != PROF_MAGIC_NODE) | |
1056 | return; | |
efc6f628 | 1057 | |
45e338f5 JM |
1058 | free(node->name); |
1059 | free(node->value); | |
2fa9ba98 TT |
1060 | |
1061 | for (child=node->first_child; child; child = next) { | |
1062 | next = child->next; | |
1063 | profile_free_node(child); | |
1064 | } | |
1065 | node->magic = 0; | |
efc6f628 | 1066 | |
2fa9ba98 TT |
1067 | free(node); |
1068 | } | |
1069 | ||
1070 | #ifndef HAVE_STRDUP | |
1071 | #undef strdup | |
1072 | #define strdup MYstrdup | |
1073 | static char *MYstrdup (const char *s) | |
1074 | { | |
1075 | size_t sz = strlen(s) + 1; | |
1076 | char *p = malloc(sz); | |
1077 | if (p != 0) | |
1078 | memcpy(p, s, sz); | |
1079 | return p; | |
1080 | } | |
1081 | #endif | |
1082 | ||
1083 | /* | |
1084 | * Create a node | |
1085 | */ | |
1086 | errcode_t profile_create_node(const char *name, const char *value, | |
1087 | struct profile_node **ret_node) | |
1088 | { | |
1089 | struct profile_node *new; | |
1090 | ||
1091 | new = malloc(sizeof(struct profile_node)); | |
1092 | if (!new) | |
1093 | return ENOMEM; | |
1094 | memset(new, 0, sizeof(struct profile_node)); | |
1095 | new->name = strdup(name); | |
1096 | if (new->name == 0) { | |
1097 | profile_free_node(new); | |
1098 | return ENOMEM; | |
1099 | } | |
1100 | if (value) { | |
1101 | new->value = strdup(value); | |
1102 | if (new->value == 0) { | |
1103 | profile_free_node(new); | |
1104 | return ENOMEM; | |
1105 | } | |
1106 | } | |
1107 | new->magic = PROF_MAGIC_NODE; | |
1108 | ||
1109 | *ret_node = new; | |
1110 | return 0; | |
1111 | } | |
1112 | ||
1113 | /* | |
1114 | * This function verifies that all of the representation invarients of | |
1115 | * the profile are true. If not, we have a programming bug somewhere, | |
1116 | * probably in this file. | |
1117 | */ | |
1118 | #ifdef DEBUG_PROGRAM | |
1119 | errcode_t profile_verify_node(struct profile_node *node) | |
1120 | { | |
1121 | struct profile_node *p, *last; | |
1122 | errcode_t retval; | |
1123 | ||
1124 | CHECK_MAGIC(node); | |
1125 | ||
1126 | if (node->value && node->first_child) | |
1127 | return PROF_SECTION_WITH_VALUE; | |
1128 | ||
1129 | last = 0; | |
1130 | for (p = node->first_child; p; last = p, p = p->next) { | |
1131 | if (p->prev != last) | |
1132 | return PROF_BAD_LINK_LIST; | |
1133 | if (last && (last->next != p)) | |
1134 | return PROF_BAD_LINK_LIST; | |
1135 | if (node->group_level+1 != p->group_level) | |
1136 | return PROF_BAD_GROUP_LVL; | |
1137 | if (p->parent != node) | |
1138 | return PROF_BAD_PARENT_PTR; | |
1139 | retval = profile_verify_node(p); | |
1140 | if (retval) | |
1141 | return retval; | |
1142 | } | |
1143 | return 0; | |
1144 | } | |
1145 | #endif | |
1146 | ||
1147 | /* | |
1148 | * Add a node to a particular section | |
1149 | */ | |
1150 | errcode_t profile_add_node(struct profile_node *section, const char *name, | |
1151 | const char *value, struct profile_node **ret_node) | |
1152 | { | |
1153 | errcode_t retval; | |
1154 | struct profile_node *p, *last, *new; | |
1155 | ||
1156 | CHECK_MAGIC(section); | |
1157 | ||
1158 | if (section->value) | |
1159 | return PROF_ADD_NOT_SECTION; | |
1160 | ||
1161 | /* | |
1162 | * Find the place to insert the new node. We look for the | |
efc6f628 | 1163 | * place *after* the last match of the node name, since |
2fa9ba98 TT |
1164 | * order matters. |
1165 | */ | |
1166 | for (p=section->first_child, last = 0; p; last = p, p = p->next) { | |
1167 | int cmp; | |
1168 | cmp = strcmp(p->name, name); | |
1169 | if (cmp > 0) | |
1170 | break; | |
1171 | } | |
1172 | retval = profile_create_node(name, value, &new); | |
1173 | if (retval) | |
1174 | return retval; | |
1175 | new->group_level = section->group_level+1; | |
1176 | new->deleted = 0; | |
1177 | new->parent = section; | |
1178 | new->prev = last; | |
1179 | new->next = p; | |
1180 | if (p) | |
1181 | p->prev = new; | |
1182 | if (last) | |
1183 | last->next = new; | |
1184 | else | |
1185 | section->first_child = new; | |
1186 | if (ret_node) | |
1187 | *ret_node = new; | |
1188 | return 0; | |
1189 | } | |
1190 | ||
2fa9ba98 TT |
1191 | /* |
1192 | * Iterate through the section, returning the nodes which match | |
1193 | * the given name. If name is NULL, then interate through all the | |
1194 | * nodes in the section. If section_flag is non-zero, only return the | |
1195 | * section which matches the name; don't return relations. If value | |
1196 | * is non-NULL, then only return relations which match the requested | |
1197 | * value. (The value argument is ignored if section_flag is non-zero.) | |
efc6f628 | 1198 | * |
2fa9ba98 TT |
1199 | * The first time this routine is called, the state pointer must be |
1200 | * null. When this profile_find_node_relation() returns, if the state | |
1201 | * pointer is non-NULL, then this routine should be called again. | |
1202 | * (This won't happen if section_flag is non-zero, obviously.) | |
1203 | * | |
1204 | */ | |
1205 | errcode_t profile_find_node(struct profile_node *section, const char *name, | |
1206 | const char *value, int section_flag, void **state, | |
1207 | struct profile_node **node) | |
1208 | { | |
1209 | struct profile_node *p; | |
1210 | ||
1211 | CHECK_MAGIC(section); | |
1212 | p = *state; | |
1213 | if (p) { | |
1214 | CHECK_MAGIC(p); | |
1215 | } else | |
1216 | p = section->first_child; | |
efc6f628 | 1217 | |
2fa9ba98 TT |
1218 | for (; p; p = p->next) { |
1219 | if (name && (strcmp(p->name, name))) | |
1220 | continue; | |
1221 | if (section_flag) { | |
1222 | if (p->value) | |
1223 | continue; | |
1224 | } else { | |
1225 | if (!p->value) | |
1226 | continue; | |
1227 | if (value && (strcmp(p->value, value))) | |
1228 | continue; | |
1229 | } | |
1230 | if (p->deleted) | |
1231 | continue; | |
1232 | /* A match! */ | |
1233 | if (node) | |
1234 | *node = p; | |
1235 | break; | |
1236 | } | |
1237 | if (p == 0) { | |
1238 | *state = 0; | |
1239 | return section_flag ? PROF_NO_SECTION : PROF_NO_RELATION; | |
1240 | } | |
1241 | /* | |
1242 | * OK, we've found one match; now let's try to find another | |
1243 | * one. This way, if we return a non-zero state pointer, | |
1244 | * there's guaranteed to be another match that's returned. | |
1245 | */ | |
1246 | for (p = p->next; p; p = p->next) { | |
1247 | if (name && (strcmp(p->name, name))) | |
1248 | continue; | |
1249 | if (section_flag) { | |
1250 | if (p->value) | |
1251 | continue; | |
1252 | } else { | |
1253 | if (!p->value) | |
1254 | continue; | |
1255 | if (value && (strcmp(p->value, value))) | |
1256 | continue; | |
1257 | } | |
1258 | /* A match! */ | |
1259 | break; | |
1260 | } | |
1261 | *state = p; | |
1262 | return 0; | |
1263 | } | |
1264 | ||
2fa9ba98 TT |
1265 | /* |
1266 | * This is a general-purpose iterator for returning all nodes that | |
efc6f628 | 1267 | * match the specified name array. |
2fa9ba98 TT |
1268 | */ |
1269 | struct profile_iterator { | |
1270 | prf_magic_t magic; | |
1271 | profile_t profile; | |
1272 | int flags; | |
1273 | const char *const *names; | |
1274 | const char *name; | |
1275 | prf_file_t file; | |
1276 | int file_serial; | |
1277 | int done_idx; | |
1278 | struct profile_node *node; | |
1279 | int num; | |
1280 | }; | |
1281 | ||
efc6f628 | 1282 | errcode_t |
9a4c209c TT |
1283 | profile_iterator_create(profile_t profile, const char *const *names, int flags, |
1284 | void **ret_iter) | |
2fa9ba98 TT |
1285 | { |
1286 | struct profile_iterator *iter; | |
1287 | int done_idx = 0; | |
1288 | ||
1289 | if (profile == 0) | |
1290 | return PROF_NO_PROFILE; | |
1291 | if (profile->magic != PROF_MAGIC_PROFILE) | |
1292 | return PROF_MAGIC_PROFILE; | |
1293 | if (!names) | |
1294 | return PROF_BAD_NAMESET; | |
1295 | if (!(flags & PROFILE_ITER_LIST_SECTION)) { | |
1296 | if (!names[0]) | |
1297 | return PROF_BAD_NAMESET; | |
1298 | done_idx = 1; | |
1299 | } | |
1300 | ||
1301 | if ((iter = malloc(sizeof(struct profile_iterator))) == NULL) | |
1302 | return ENOMEM; | |
1303 | ||
1304 | iter->magic = PROF_MAGIC_ITERATOR; | |
1305 | iter->profile = profile; | |
1306 | iter->names = names; | |
1307 | iter->flags = flags; | |
1308 | iter->file = profile->first_file; | |
1309 | iter->done_idx = done_idx; | |
1310 | iter->node = 0; | |
1311 | iter->num = 0; | |
1312 | *ret_iter = iter; | |
1313 | return 0; | |
1314 | } | |
1315 | ||
9a4c209c | 1316 | void profile_iterator_free(void **iter_p) |
2fa9ba98 TT |
1317 | { |
1318 | struct profile_iterator *iter; | |
1319 | ||
1320 | if (!iter_p) | |
1321 | return; | |
1322 | iter = *iter_p; | |
1323 | if (!iter || iter->magic != PROF_MAGIC_ITERATOR) | |
1324 | return; | |
1325 | free(iter); | |
1326 | *iter_p = 0; | |
1327 | } | |
1328 | ||
1329 | /* | |
1330 | * Note: the returned character strings in ret_name and ret_value | |
1331 | * points to the stored character string in the parse string. Before | |
1332 | * this string value is returned to a calling application | |
1333 | * (profile_node_iterator is not an exported interface), it should be | |
1334 | * strdup()'ed. | |
1335 | */ | |
1336 | errcode_t profile_node_iterator(void **iter_p, struct profile_node **ret_node, | |
1337 | char **ret_name, char **ret_value) | |
1338 | { | |
1339 | struct profile_iterator *iter = *iter_p; | |
1340 | struct profile_node *section, *p; | |
1341 | const char *const *cpp; | |
1342 | errcode_t retval; | |
1343 | int skip_num = 0; | |
1344 | ||
1345 | if (!iter || iter->magic != PROF_MAGIC_ITERATOR) | |
1346 | return PROF_MAGIC_ITERATOR; | |
1347 | if (iter->file && iter->file->magic != PROF_MAGIC_FILE) | |
1348 | return PROF_MAGIC_FILE; | |
2fa9ba98 TT |
1349 | /* |
1350 | * If the file has changed, then the node pointer is invalid, | |
1351 | * so we'll have search the file again looking for it. | |
1352 | */ | |
efc6f628 | 1353 | if (iter->node && (iter->file && |
8f234d96 | 1354 | iter->file->upd_serial != iter->file_serial)) { |
2fa9ba98 TT |
1355 | iter->flags &= ~PROFILE_ITER_FINAL_SEEN; |
1356 | skip_num = iter->num; | |
1357 | iter->node = 0; | |
1358 | } | |
1359 | if (iter->node && iter->node->magic != PROF_MAGIC_NODE) { | |
1360 | return PROF_MAGIC_NODE; | |
1361 | } | |
1362 | get_new_file: | |
1363 | if (iter->node == 0) { | |
1364 | if (iter->file == 0 || | |
1365 | (iter->flags & PROFILE_ITER_FINAL_SEEN)) { | |
9a4c209c | 1366 | profile_iterator_free(iter_p); |
2fa9ba98 TT |
1367 | if (ret_node) |
1368 | *ret_node = 0; | |
1369 | if (ret_name) | |
1370 | *ret_name = 0; | |
1371 | if (ret_value) | |
1372 | *ret_value =0; | |
1373 | return 0; | |
1374 | } | |
1375 | if ((retval = profile_update_file(iter->file))) { | |
1376 | if (retval == ENOENT || retval == EACCES) { | |
1377 | /* XXX memory leak? */ | |
1378 | iter->file = iter->file->next; | |
1379 | skip_num = 0; | |
1380 | retval = 0; | |
1381 | goto get_new_file; | |
1382 | } else { | |
9a4c209c | 1383 | profile_iterator_free(iter_p); |
2fa9ba98 TT |
1384 | return retval; |
1385 | } | |
1386 | } | |
d45544ca | 1387 | iter->file_serial = iter->file->upd_serial; |
2fa9ba98 TT |
1388 | /* |
1389 | * Find the section to list if we are a LIST_SECTION, | |
1390 | * or find the containing section if not. | |
1391 | */ | |
d45544ca | 1392 | section = iter->file->root; |
2fa9ba98 TT |
1393 | for (cpp = iter->names; cpp[iter->done_idx]; cpp++) { |
1394 | for (p=section->first_child; p; p = p->next) { | |
1395 | if (!strcmp(p->name, *cpp) && !p->value) | |
1396 | break; | |
1397 | } | |
1398 | if (!p) { | |
1399 | section = 0; | |
1400 | break; | |
1401 | } | |
1402 | section = p; | |
1403 | if (p->final) | |
1404 | iter->flags |= PROFILE_ITER_FINAL_SEEN; | |
1405 | } | |
1406 | if (!section) { | |
1407 | iter->file = iter->file->next; | |
1408 | skip_num = 0; | |
1409 | goto get_new_file; | |
1410 | } | |
1411 | iter->name = *cpp; | |
1412 | iter->node = section->first_child; | |
1413 | } | |
1414 | /* | |
1415 | * OK, now we know iter->node is set up correctly. Let's do | |
1416 | * the search. | |
1417 | */ | |
1418 | for (p = iter->node; p; p = p->next) { | |
1419 | if (iter->name && strcmp(p->name, iter->name)) | |
1420 | continue; | |
1421 | if ((iter->flags & PROFILE_ITER_SECTIONS_ONLY) && | |
1422 | p->value) | |
1423 | continue; | |
1424 | if ((iter->flags & PROFILE_ITER_RELATIONS_ONLY) && | |
1425 | !p->value) | |
1426 | continue; | |
1427 | if (skip_num > 0) { | |
1428 | skip_num--; | |
1429 | continue; | |
1430 | } | |
1431 | if (p->deleted) | |
1432 | continue; | |
1433 | break; | |
1434 | } | |
1435 | iter->num++; | |
1436 | if (!p) { | |
1437 | iter->file = iter->file->next; | |
2fa9ba98 TT |
1438 | iter->node = 0; |
1439 | skip_num = 0; | |
1440 | goto get_new_file; | |
1441 | } | |
1442 | if ((iter->node = p->next) == NULL) | |
1443 | iter->file = iter->file->next; | |
1444 | if (ret_node) | |
1445 | *ret_node = p; | |
1446 | if (ret_name) | |
1447 | *ret_name = p->name; | |
1448 | if (ret_value) | |
1449 | *ret_value = p->value; | |
1450 | return 0; | |
1451 | } | |
1452 | ||
1453 | ||
2fa9ba98 TT |
1454 | /* |
1455 | * prof_get.c --- routines that expose the public interfaces for | |
1456 | * querying items from the profile. | |
1457 | * | |
1458 | */ | |
1459 | ||
2fa9ba98 TT |
1460 | /* |
1461 | * This function only gets the first value from the file; it is a | |
1462 | * helper function for profile_get_string, profile_get_integer, etc. | |
1463 | */ | |
9a4c209c TT |
1464 | errcode_t profile_get_value(profile_t profile, const char *name, |
1465 | const char *subname, const char *subsubname, | |
2fa9ba98 TT |
1466 | const char **ret_value) |
1467 | { | |
1468 | errcode_t retval; | |
1469 | void *state; | |
1470 | char *value; | |
9a4c209c | 1471 | const char *names[4]; |
2fa9ba98 | 1472 | |
9a4c209c TT |
1473 | names[0] = name; |
1474 | names[1] = subname; | |
1475 | names[2] = subsubname; | |
1476 | names[3] = 0; | |
1477 | ||
1478 | if ((retval = profile_iterator_create(profile, names, | |
1479 | PROFILE_ITER_RELATIONS_ONLY, | |
1480 | &state))) | |
2fa9ba98 TT |
1481 | return retval; |
1482 | ||
1483 | if ((retval = profile_node_iterator(&state, 0, 0, &value))) | |
1484 | goto cleanup; | |
1485 | ||
1486 | if (value) | |
1487 | *ret_value = value; | |
1488 | else | |
1489 | retval = PROF_NO_RELATION; | |
efc6f628 | 1490 | |
2fa9ba98 | 1491 | cleanup: |
9a4c209c | 1492 | profile_iterator_free(&state); |
2fa9ba98 TT |
1493 | return retval; |
1494 | } | |
1495 | ||
efc6f628 | 1496 | errcode_t |
2fa9ba98 TT |
1497 | profile_get_string(profile_t profile, const char *name, const char *subname, |
1498 | const char *subsubname, const char *def_val, | |
1499 | char **ret_string) | |
1500 | { | |
1501 | const char *value; | |
1502 | errcode_t retval; | |
2fa9ba98 TT |
1503 | |
1504 | if (profile) { | |
efc6f628 | 1505 | retval = profile_get_value(profile, name, subname, |
9a4c209c | 1506 | subsubname, &value); |
2fa9ba98 TT |
1507 | if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) |
1508 | value = def_val; | |
1509 | else if (retval) | |
1510 | return retval; | |
1511 | } else | |
1512 | value = def_val; | |
efc6f628 | 1513 | |
2fa9ba98 TT |
1514 | if (value) { |
1515 | *ret_string = malloc(strlen(value)+1); | |
1516 | if (*ret_string == 0) | |
1517 | return ENOMEM; | |
1518 | strcpy(*ret_string, value); | |
1519 | } else | |
1520 | *ret_string = 0; | |
1521 | return 0; | |
1522 | } | |
1523 | ||
efc6f628 | 1524 | errcode_t |
2fa9ba98 TT |
1525 | profile_get_integer(profile_t profile, const char *name, const char *subname, |
1526 | const char *subsubname, int def_val, int *ret_int) | |
1527 | { | |
1528 | const char *value; | |
1529 | errcode_t retval; | |
2fa9ba98 TT |
1530 | char *end_value; |
1531 | long ret_long; | |
1532 | ||
1533 | *ret_int = def_val; | |
1534 | if (profile == 0) | |
1535 | return 0; | |
1536 | ||
9a4c209c | 1537 | retval = profile_get_value(profile, name, subname, subsubname, &value); |
2fa9ba98 TT |
1538 | if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { |
1539 | *ret_int = def_val; | |
1540 | return 0; | |
1541 | } else if (retval) | |
1542 | return retval; | |
1543 | ||
1544 | if (value[0] == 0) | |
1545 | /* Empty string is no good. */ | |
1546 | return PROF_BAD_INTEGER; | |
1547 | errno = 0; | |
a9d7fc69 | 1548 | ret_long = strtol(value, &end_value, 0); |
2fa9ba98 TT |
1549 | |
1550 | /* Overflow or underflow. */ | |
1551 | if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0) | |
1552 | return PROF_BAD_INTEGER; | |
1553 | /* Value outside "int" range. */ | |
1554 | if ((long) (int) ret_long != ret_long) | |
1555 | return PROF_BAD_INTEGER; | |
1556 | /* Garbage in string. */ | |
1557 | if (end_value != value + strlen (value)) | |
1558 | return PROF_BAD_INTEGER; | |
efc6f628 TT |
1559 | |
1560 | ||
2fa9ba98 TT |
1561 | *ret_int = ret_long; |
1562 | return 0; | |
1563 | } | |
1564 | ||
efc6f628 | 1565 | errcode_t |
32460c1e | 1566 | profile_get_uint(profile_t profile, const char *name, const char *subname, |
efc6f628 | 1567 | const char *subsubname, unsigned int def_val, |
32460c1e TT |
1568 | unsigned int *ret_int) |
1569 | { | |
1570 | const char *value; | |
1571 | errcode_t retval; | |
1572 | char *end_value; | |
1573 | unsigned long ret_long; | |
1574 | ||
1575 | *ret_int = def_val; | |
1576 | if (profile == 0) | |
1577 | return 0; | |
1578 | ||
1579 | retval = profile_get_value(profile, name, subname, subsubname, &value); | |
1580 | if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { | |
1581 | *ret_int = def_val; | |
1582 | return 0; | |
1583 | } else if (retval) | |
1584 | return retval; | |
1585 | ||
1586 | if (value[0] == 0) | |
1587 | /* Empty string is no good. */ | |
1588 | return PROF_BAD_INTEGER; | |
1589 | errno = 0; | |
a9d7fc69 | 1590 | ret_long = strtoul(value, &end_value, 0); |
32460c1e TT |
1591 | |
1592 | /* Overflow or underflow. */ | |
1593 | if ((ret_long == ULONG_MAX) && errno != 0) | |
1594 | return PROF_BAD_INTEGER; | |
1595 | /* Value outside "int" range. */ | |
1596 | if ((unsigned long) (unsigned int) ret_long != ret_long) | |
1597 | return PROF_BAD_INTEGER; | |
1598 | /* Garbage in string. */ | |
1599 | if (end_value != value + strlen (value)) | |
1600 | return PROF_BAD_INTEGER; | |
efc6f628 | 1601 | |
32460c1e TT |
1602 | *ret_int = ret_long; |
1603 | return 0; | |
1604 | } | |
1605 | ||
d3859af3 AK |
1606 | errcode_t |
1607 | profile_get_double(profile_t profile, const char *name, const char *subname, | |
1608 | const char *subsubname, double def_val, double *ret_double) | |
1609 | { | |
1610 | const char *value; | |
1611 | errcode_t retval; | |
1612 | char *end_value; | |
1613 | double double_val; | |
1614 | ||
1615 | *ret_double = def_val; | |
1616 | if (profile == 0) | |
1617 | return 0; | |
1618 | ||
1619 | retval = profile_get_value(profile, name, subname, subsubname, &value); | |
1620 | if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { | |
1621 | *ret_double = def_val; | |
1622 | return 0; | |
1623 | } else if (retval) | |
1624 | return retval; | |
1625 | ||
1626 | if (value[0] == 0) | |
1627 | /* Empty string is no good. */ | |
1628 | return PROF_BAD_INTEGER; | |
1629 | errno = 0; | |
1630 | double_val = strtod(value, &end_value); | |
1631 | ||
1632 | /* Overflow or underflow. */ | |
1633 | if (errno != 0) | |
1634 | return PROF_BAD_INTEGER; | |
1635 | /* Garbage in string. */ | |
1636 | if (end_value != value + strlen(value)) | |
1637 | return PROF_BAD_INTEGER; | |
1638 | ||
1639 | *ret_double = double_val; | |
1640 | return 0; | |
1641 | } | |
1642 | ||
2fa9ba98 TT |
1643 | static const char *const conf_yes[] = { |
1644 | "y", "yes", "true", "t", "1", "on", | |
1645 | 0, | |
1646 | }; | |
1647 | ||
1648 | static const char *const conf_no[] = { | |
1649 | "n", "no", "false", "nil", "0", "off", | |
1650 | 0, | |
1651 | }; | |
1652 | ||
1653 | static errcode_t | |
1654 | profile_parse_boolean(const char *s, int *ret_boolean) | |
1655 | { | |
1656 | const char *const *p; | |
efc6f628 | 1657 | |
2fa9ba98 TT |
1658 | if (ret_boolean == NULL) |
1659 | return PROF_EINVAL; | |
1660 | ||
1661 | for(p=conf_yes; *p; p++) { | |
1662 | if (!strcasecmp(*p,s)) { | |
1663 | *ret_boolean = 1; | |
1664 | return 0; | |
1665 | } | |
1666 | } | |
1667 | ||
1668 | for(p=conf_no; *p; p++) { | |
1669 | if (!strcasecmp(*p,s)) { | |
1670 | *ret_boolean = 0; | |
1671 | return 0; | |
1672 | } | |
1673 | } | |
efc6f628 | 1674 | |
2fa9ba98 TT |
1675 | return PROF_BAD_BOOLEAN; |
1676 | } | |
1677 | ||
efc6f628 | 1678 | errcode_t |
2fa9ba98 TT |
1679 | profile_get_boolean(profile_t profile, const char *name, const char *subname, |
1680 | const char *subsubname, int def_val, int *ret_boolean) | |
1681 | { | |
1682 | const char *value; | |
1683 | errcode_t retval; | |
2fa9ba98 TT |
1684 | |
1685 | if (profile == 0) { | |
1686 | *ret_boolean = def_val; | |
1687 | return 0; | |
1688 | } | |
1689 | ||
9a4c209c | 1690 | retval = profile_get_value(profile, name, subname, subsubname, &value); |
2fa9ba98 TT |
1691 | if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { |
1692 | *ret_boolean = def_val; | |
1693 | return 0; | |
1694 | } else if (retval) | |
1695 | return retval; | |
efc6f628 | 1696 | |
2fa9ba98 TT |
1697 | return profile_parse_boolean (value, ret_boolean); |
1698 | } | |
1699 | ||
efc6f628 | 1700 | errcode_t |
2fa9ba98 TT |
1701 | profile_iterator(void **iter_p, char **ret_name, char **ret_value) |
1702 | { | |
1703 | char *name, *value; | |
1704 | errcode_t retval; | |
efc6f628 | 1705 | |
2fa9ba98 TT |
1706 | retval = profile_node_iterator(iter_p, 0, &name, &value); |
1707 | if (retval) | |
1708 | return retval; | |
1709 | ||
1710 | if (ret_name) { | |
1711 | if (name) { | |
1712 | *ret_name = malloc(strlen(name)+1); | |
1713 | if (!*ret_name) | |
1714 | return ENOMEM; | |
1715 | strcpy(*ret_name, name); | |
1716 | } else | |
1717 | *ret_name = 0; | |
1718 | } | |
1719 | if (ret_value) { | |
1720 | if (value) { | |
1721 | *ret_value = malloc(strlen(value)+1); | |
1722 | if (!*ret_value) { | |
1723 | if (ret_name) { | |
1724 | free(*ret_name); | |
1725 | *ret_name = 0; | |
1726 | } | |
1727 | return ENOMEM; | |
1728 | } | |
1729 | strcpy(*ret_value, value); | |
1730 | } else | |
1731 | *ret_value = 0; | |
1732 | } | |
1733 | return 0; | |
1734 | } | |
1735 | ||
2fa9ba98 TT |
1736 | #ifdef DEBUG_PROGRAM |
1737 | ||
1738 | /* | |
1739 | * test_profile.c --- testing program for the profile routine | |
1740 | */ | |
1741 | ||
1742 | #include "argv_parse.h" | |
9a4c209c | 1743 | #include "profile_helpers.h" |
2fa9ba98 TT |
1744 | |
1745 | const char *program_name = "test_profile"; | |
1746 | ||
1747 | #define PRINT_VALUE 1 | |
1748 | #define PRINT_VALUES 2 | |
1749 | ||
44dc5f88 | 1750 | static void do_cmd(profile_t profile, char **argv) |
2fa9ba98 TT |
1751 | { |
1752 | errcode_t retval; | |
2fa9ba98 | 1753 | const char **names, *value; |
44dc5f88 TT |
1754 | char **values, **cpp; |
1755 | char *cmd; | |
2fa9ba98 TT |
1756 | int print_status; |
1757 | ||
44dc5f88 TT |
1758 | cmd = *(argv); |
1759 | names = (const char **) argv + 1; | |
1760 | print_status = 0; | |
1761 | retval = 0; | |
1762 | if (cmd == 0) | |
1763 | return; | |
1764 | if (!strcmp(cmd, "query")) { | |
1765 | retval = profile_get_values(profile, names, &values); | |
1766 | print_status = PRINT_VALUES; | |
1767 | } else if (!strcmp(cmd, "query1")) { | |
9a4c209c TT |
1768 | const char *name = 0; |
1769 | const char *subname = 0; | |
1770 | const char *subsubname = 0; | |
1771 | ||
1772 | name = names[0]; | |
1773 | if (name) | |
1774 | subname = names[1]; | |
1775 | if (subname) | |
1776 | subsubname = names[2]; | |
1777 | if (subsubname && names[3]) { | |
efc6f628 | 1778 | fprintf(stderr, |
9a4c209c TT |
1779 | "Only 3 levels are allowed with query1\n"); |
1780 | retval = EINVAL; | |
1781 | } else | |
efc6f628 | 1782 | retval = profile_get_value(profile, name, subname, |
9a4c209c | 1783 | subsubname, &value); |
44dc5f88 TT |
1784 | print_status = PRINT_VALUE; |
1785 | } else if (!strcmp(cmd, "list_sections")) { | |
efc6f628 | 1786 | retval = profile_get_subsection_names(profile, names, |
44dc5f88 TT |
1787 | &values); |
1788 | print_status = PRINT_VALUES; | |
1789 | } else if (!strcmp(cmd, "list_relations")) { | |
efc6f628 | 1790 | retval = profile_get_relation_names(profile, names, |
44dc5f88 TT |
1791 | &values); |
1792 | print_status = PRINT_VALUES; | |
1793 | } else if (!strcmp(cmd, "dump")) { | |
1794 | retval = profile_write_tree_file | |
d45544ca | 1795 | (profile->first_file->root, stdout); |
44dc5f88 TT |
1796 | #if 0 |
1797 | } else if (!strcmp(cmd, "clear")) { | |
1798 | retval = profile_clear_relation(profile, names); | |
1799 | } else if (!strcmp(cmd, "update")) { | |
1800 | retval = profile_update_relation(profile, names+2, | |
1801 | *names, *(names+1)); | |
1802 | #endif | |
1803 | } else if (!strcmp(cmd, "verify")) { | |
1804 | retval = profile_verify_node | |
d45544ca | 1805 | (profile->first_file->root); |
44dc5f88 TT |
1806 | #if 0 |
1807 | } else if (!strcmp(cmd, "rename_section")) { | |
1808 | retval = profile_rename_section(profile, names+1, *names); | |
1809 | } else if (!strcmp(cmd, "add")) { | |
1810 | value = *names; | |
1811 | if (strcmp(value, "NULL") == 0) | |
1812 | value = NULL; | |
1813 | retval = profile_add_relation(profile, names+1, value); | |
1814 | } else if (!strcmp(cmd, "flush")) { | |
1815 | retval = profile_flush(profile); | |
1816 | #endif | |
1817 | } else { | |
1818 | printf("Invalid command.\n"); | |
1819 | } | |
1820 | if (retval) { | |
1821 | com_err(cmd, retval, ""); | |
1822 | print_status = 0; | |
1823 | } | |
1824 | switch (print_status) { | |
1825 | case PRINT_VALUE: | |
1826 | printf("%s\n", value); | |
1827 | break; | |
1828 | case PRINT_VALUES: | |
1829 | for (cpp = values; *cpp; cpp++) | |
1830 | printf("%s\n", *cpp); | |
1831 | profile_free_list(values); | |
1832 | break; | |
1833 | } | |
1834 | } | |
1835 | ||
1836 | static void do_batchmode(profile_t profile) | |
1837 | { | |
1838 | int argc, ret; | |
1839 | char **argv; | |
1840 | char buf[256]; | |
1841 | ||
2fa9ba98 TT |
1842 | while (!feof(stdin)) { |
1843 | if (fgets(buf, sizeof(buf), stdin) == NULL) | |
1844 | break; | |
1845 | printf(">%s", buf); | |
1846 | ret = argv_parse(buf, &argc, &argv); | |
1847 | if (ret != 0) { | |
1848 | printf("Argv_parse returned %d!\n", ret); | |
1849 | continue; | |
1850 | } | |
44dc5f88 | 1851 | do_cmd(profile, argv); |
2fa9ba98 TT |
1852 | printf("\n"); |
1853 | argv_free(argv); | |
1854 | } | |
1855 | profile_release(profile); | |
1856 | exit(0); | |
efc6f628 | 1857 | |
2fa9ba98 TT |
1858 | } |
1859 | ||
f5f14fcf TT |
1860 | void syntax_err_report(const char *filename, long err, int line_num) |
1861 | { | |
1862 | fprintf(stderr, "Syntax error in %s, line number %d: %s\n", | |
1863 | filename, line_num, error_message(err)); | |
1864 | exit(1); | |
1865 | } | |
2fa9ba98 | 1866 | |
6f890e51 TT |
1867 | const char *default_str = "[foo]\n\tbar=quux\n\tsub = {\n\t\twin = true\n}\n"; |
1868 | ||
44dc5f88 | 1869 | int main(int argc, char **argv) |
2fa9ba98 TT |
1870 | { |
1871 | profile_t profile; | |
1872 | long retval; | |
2fa9ba98 | 1873 | char *cmd; |
efc6f628 | 1874 | |
2fa9ba98 TT |
1875 | if (argc < 2) { |
1876 | fprintf(stderr, "Usage: %s filename [cmd argset]\n", program_name); | |
1877 | exit(1); | |
1878 | } | |
1879 | ||
1880 | initialize_prof_error_table(); | |
f5f14fcf TT |
1881 | |
1882 | profile_set_syntax_err_cb(syntax_err_report); | |
efc6f628 | 1883 | |
2fa9ba98 TT |
1884 | retval = profile_init_path(argv[1], &profile); |
1885 | if (retval) { | |
1886 | com_err(program_name, retval, "while initializing profile"); | |
1887 | exit(1); | |
1888 | } | |
6f890e51 TT |
1889 | retval = profile_set_default(profile, default_str); |
1890 | if (retval) { | |
1891 | com_err(program_name, retval, "while setting default"); | |
1892 | exit(1); | |
1893 | } | |
1894 | ||
2fa9ba98 | 1895 | cmd = *(argv+2); |
2fa9ba98 TT |
1896 | if (!cmd || !strcmp(cmd, "batch")) |
1897 | do_batchmode(profile); | |
44dc5f88 TT |
1898 | else |
1899 | do_cmd(profile, argv+2); | |
2fa9ba98 TT |
1900 | profile_release(profile); |
1901 | ||
1902 | return 0; | |
1903 | } | |
1904 | ||
1905 | #endif |