1/* Simple Config file API
2 * Dave Eckhardt
3 * Rob Siemborski
4 * Tim Martin (originally in Cyrus distribution)
5 * $Id: cfile.c,v 1.1 2005/01/19 00:11:41 shadow Exp $
6 */
7/*
8 * Copyright (c) 2001 Carnegie Mellon University.  All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in
19 *    the documentation and/or other materials provided with the
20 *    distribution.
21 *
22 * 3. The name "Carnegie Mellon University" must not be used to
23 *    endorse or promote products derived from this software without
24 *    prior written permission. For permission or any other legal
25 *    details, please contact
26 *      Office of Technology Transfer
27 *      Carnegie Mellon University
28 *      5000 Forbes Avenue
29 *      Pittsburgh, PA  15213-3890
30 *      (412) 268-4387, fax: (412) 268-7395
31 *      tech-transfer@andrew.cmu.edu
32 *
33 * 4. Redistributions of any form whatsoever must retain the following
34 *    acknowledgment:
35 *    "This product includes software developed by Computing Services
36 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
37 *
38 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
39 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
40 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
41 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
42 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
43 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
44 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
45 */
46
47/* cfile_read() has a clumsy error reporting path
48 * so that it doesn't depend on any particular package's
49 * return code space.
50 */
51
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include <ctype.h>
56
57#include "cfile.h"
58
59struct cf_keyval {
60    char *key;
61    char *value;
62};
63
64struct cfile {
65    struct cf_keyval *kvlist;
66    int n_kv;
67};
68
69#define CONFIGLISTGROWSIZE 100
70#define BIG_ENOUGH 4096
71
72cfile cfile_read(const char *filename, char *complaint, int complaint_len)
73{
74    FILE *infile;
75    int lineno = 0;
76    int alloced = 0;
77    char buf[BIG_ENOUGH];
78    char *p, *key;
79    int result;
80    struct cfile *cf;
81
82	if (complaint)
83      complaint[0] = '\0';
84
85    if (!(cf = malloc(sizeof (*cf)))) {
86      /* then strdup() will probably fail, sigh */
87      if (complaint)
88        snprintf(complaint, complaint_len, "cfile_read: no memory");
89      return 0;
90    }
91
92    cf->n_kv = 0;
93    cf->kvlist = 0;
94
95    infile = fopen(filename, "r");
96    if (!infile) {
97      if (complaint)
98        snprintf(complaint, complaint_len, "cfile_read: cannot open %s", filename);
99      cfile_free(cf);
100      return 0;
101    }
102
103    while (fgets(buf, sizeof(buf), infile)) {
104	lineno++;
105
106	if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0';
107	for (p = buf; *p && isspace((int) *p); p++);
108	if (!*p || *p == '#') continue;
109
110	key = p;
111	while (*p && (isalnum((int) *p) || *p == '-' || *p == '_')) {
112	    if (isupper((int) *p)) *p = tolower(*p);
113	    p++;
114	}
115	if (*p != ':') {
116	  if (complaint)
117	    snprintf(complaint, complaint_len, "%s: line %d: no colon separator", filename, lineno);
118	  cfile_free(cf);
119	  fclose(infile);
120	  return 0;
121	}
122	*p++ = '\0';
123
124	while (*p && isspace((int) *p)) p++;
125
126	if (!*p) {
127	  if (complaint)
128	    snprintf(complaint, complaint_len, "%s: line %d: keyword %s: no value", filename, lineno, key);
129	  cfile_free(cf);
130	  fclose(infile);
131	  return 0;
132	}
133
134	if (cf->n_kv == alloced) {
135	    alloced += CONFIGLISTGROWSIZE;
136	    cf->kvlist=realloc((char *)cf->kvlist,
137				    alloced * sizeof(struct cf_keyval));
138	    if (cf->kvlist==NULL) {
139	      if (complaint)
140	        snprintf(complaint, complaint_len, "cfile_read: no memory");
141	      cfile_free(cf);
142	      fclose(infile);
143	      return 0;
144	    }
145	}
146
147	if (!(cf->kvlist[cf->n_kv].key = strdup(key)) ||
148	    !(cf->kvlist[cf->n_kv].value = strdup(p))) {
149	      if (complaint)
150	        snprintf(complaint, complaint_len, "cfile_read: no memory");
151	      cf->n_kv++; /* maybe one strdup() worked */
152	      cfile_free(cf);
153	      fclose(infile);
154	      return 0;
155	}
156
157	cf->n_kv++;
158    }
159    fclose(infile);
160
161    return cf;
162}
163
164const char *cfile_getstring(cfile cf,const char *key,const char *def)
165{
166    int opt;
167
168    for (opt = 0; opt < cf->n_kv; opt++) {
169	if (*key == cf->kvlist[opt].key[0] &&
170	    !strcmp(key, cf->kvlist[opt].key))
171	  return cf->kvlist[opt].value;
172    }
173    return def;
174}
175
176int cfile_getint(cfile cf,const char *key,int def)
177{
178    const char *val = cfile_getstring(cf, key, (char *)0);
179
180    if (!val) return def;
181    if (!isdigit((int) *val) && (*val != '-' || !isdigit((int) val[1]))) return def;
182    return atoi(val);
183}
184
185int cfile_getswitch(cfile cf,const char *key,int def)
186{
187    const char *val = cfile_getstring(cf, key, (char *)0);
188
189    if (!val) return def;
190
191    if (*val == '0' || *val == 'n' ||
192	(*val == 'o' && val[1] == 'f') || *val == 'f') {
193	return 0;
194    }
195    else if (*val == '1' || *val == 'y' ||
196	     (*val == 'o' && val[1] == 'n') || *val == 't') {
197	return 1;
198    }
199    return def;
200}
201
202void cfile_free(cfile cf)
203{
204    int opt;
205
206    if (cf->kvlist) {
207	for (opt = 0; opt < cf->n_kv; opt++) {
208	    if (cf->kvlist[opt].key)
209	      free(cf->kvlist[opt].key);
210	    if (cf->kvlist[opt].value)
211	      free(cf->kvlist[opt].value);
212	}
213	free(cf->kvlist);
214    }
215    free(cf);
216}
217