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