1/* Simple Config file API
2 * Dave Eckhardt
3 * Rob Siemborski
4 * Tim Martin (originally in Cyrus distribution)
5 * $Id: cfile.c,v 1.4 2006/01/24 00:16:03 snsimon 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, (int)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	  return 0;
120	}
121	*p++ = '\0';
122
123	while (*p && isspace((int) *p)) p++;
124
125	if (!*p) {
126	  if (complaint)
127	    snprintf(complaint, complaint_len, "%s: line %d: keyword %s: no value", filename, lineno, key);
128	  cfile_free(cf);
129	  return 0;
130	}
131
132	if (cf->n_kv == alloced) {
133	    alloced += CONFIGLISTGROWSIZE;
134	    cf->kvlist=realloc((char *)cf->kvlist,
135				    alloced * sizeof(struct cf_keyval));
136	    if (cf->kvlist==NULL) {
137	      if (complaint)
138	        snprintf(complaint, complaint_len, "cfile_read: no memory");
139	      cfile_free(cf);
140	      return 0;
141	    }
142	}
143
144	if (!(cf->kvlist[cf->n_kv].key = strdup(key)) ||
145	    !(cf->kvlist[cf->n_kv].value = strdup(p))) {
146	      if (complaint)
147	        snprintf(complaint, complaint_len, "cfile_read: no memory");
148	      cf->n_kv++; /* maybe one strdup() worked */
149	      cfile_free(cf);
150	      return 0;
151	}
152
153	cf->n_kv++;
154    }
155    fclose(infile);
156
157    return cf;
158}
159
160const char *cfile_getstring(cfile cf,const char *key,const char *def)
161{
162    int opt;
163
164    for (opt = 0; opt < cf->n_kv; opt++) {
165	if (*key == cf->kvlist[opt].key[0] &&
166	    !strcmp(key, cf->kvlist[opt].key))
167	  return cf->kvlist[opt].value;
168    }
169    return def;
170}
171
172int cfile_getint(cfile cf,const char *key,int def)
173{
174    const char *val = cfile_getstring(cf, key, (char *)0);
175
176    if (!val) return def;
177    if (!isdigit((int) *val) && (*val != '-' || !isdigit((int) val[1]))) return def;
178    return atoi(val);
179}
180
181int cfile_getswitch(cfile cf,const char *key,int def)
182{
183    const char *val = cfile_getstring(cf, key, (char *)0);
184
185    if (!val) return def;
186
187    if (*val == '0' || *val == 'n' ||
188	(*val == 'o' && val[1] == 'f') || *val == 'f') {
189	return 0;
190    }
191    else if (*val == '1' || *val == 'y' ||
192	     (*val == 'o' && val[1] == 'n') || *val == 't') {
193	return 1;
194    }
195    return def;
196}
197
198void cfile_free(cfile cf)
199{
200    int opt;
201
202    if (cf->kvlist) {
203	for (opt = 0; opt < cf->n_kv; opt++) {
204	    if (cf->kvlist[opt].key)
205	      free(cf->kvlist[opt].key);
206	    if (cf->kvlist[opt].value)
207	      free(cf->kvlist[opt].value);
208	}
209	free(cf->kvlist);
210    }
211    free(cf);
212}
213