1 /* Simple Config file API
2 * Dave Eckhardt
3 * Rob Siemborski
4 * Tim Martin (originally in Cyrus distribution)
5 */
6 /*
7 * Copyright (c) 2001-2016 Carnegie Mellon University. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The name "Carnegie Mellon University" must not be used to
22 * endorse or promote products derived from this software without
23 * prior written permission. For permission or any other legal
24 * details, please contact
25 * Carnegie Mellon University
26 * Center for Technology Transfer and Enterprise Creation
27 * 4615 Forbes Avenue
28 * Suite 302
29 * Pittsburgh, PA 15213
30 * (412) 268-7393, fax: (412) 268-7395
31 * innovation@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
59 struct cf_keyval {
60 char *key;
61 char *value;
62 };
63
64 struct cfile {
65 struct cf_keyval *kvlist;
66 int n_kv;
67 };
68
69 #define CONFIGLISTGROWSIZE 100
70 #define BIG_ENOUGH 4096
71
cfile_read(const char * filename,char * complaint,int complaint_len)72 cfile 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 struct cfile *cf;
80
81 if (complaint)
82 complaint[0] = '\0';
83
84 if (!(cf = malloc(sizeof (*cf)))) {
85 /* then strdup() will probably fail, sigh */
86 if (complaint)
87 snprintf(complaint, complaint_len, "cfile_read: no memory");
88 return 0;
89 }
90
91 cf->n_kv = 0;
92 cf->kvlist = 0;
93
94 infile = fopen(filename, "r");
95 if (!infile) {
96 if (complaint)
97 snprintf(complaint, complaint_len, "cfile_read: cannot open %s", filename);
98 cfile_free(cf);
99 return 0;
100 }
101
102 while (fgets(buf, sizeof(buf), infile)) {
103 lineno++;
104
105 if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0';
106 for (p = buf; *p && isspace((int) *p); p++);
107 if (!*p || *p == '#') continue;
108
109 key = p;
110 while (*p && (isalnum((int) *p) || *p == '-' || *p == '_')) {
111 if (isupper((int) *p)) *p = tolower(*p);
112 p++;
113 }
114 if (*p != ':') {
115 if (complaint)
116 snprintf(complaint, complaint_len, "%s: line %d: no colon separator", filename, lineno);
117 cfile_free(cf);
118 fclose(infile);
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 fclose(infile);
130 return 0;
131 }
132
133 if (cf->n_kv == alloced) {
134 alloced += CONFIGLISTGROWSIZE;
135 cf->kvlist=realloc((char *)cf->kvlist,
136 alloced * sizeof(struct cf_keyval));
137 if (cf->kvlist==NULL) {
138 if (complaint)
139 snprintf(complaint, complaint_len, "cfile_read: no memory");
140 cfile_free(cf);
141 fclose(infile);
142 return 0;
143 }
144 }
145
146 if (!(cf->kvlist[cf->n_kv].key = strdup(key)) ||
147 !(cf->kvlist[cf->n_kv].value = strdup(p))) {
148 if (complaint)
149 snprintf(complaint, complaint_len, "cfile_read: no memory");
150 /* maybe one strdup() worked */
151 if (cf->kvlist[cf->n_kv].key)
152 free(cf->kvlist[cf->n_kv].key);
153 cfile_free(cf);
154 fclose(infile);
155 return 0;
156 }
157
158 cf->n_kv++;
159 }
160 fclose(infile);
161
162 return cf;
163 }
164
cfile_getstring(cfile cf,const char * key,const char * def)165 const char *cfile_getstring(cfile cf,const char *key,const char *def)
166 {
167 int opt;
168
169 for (opt = 0; opt < cf->n_kv; opt++) {
170 if (*key == cf->kvlist[opt].key[0] &&
171 !strcmp(key, cf->kvlist[opt].key))
172 return cf->kvlist[opt].value;
173 }
174 return def;
175 }
176
cfile_getint(cfile cf,const char * key,int def)177 int cfile_getint(cfile cf,const char *key,int def)
178 {
179 const char *val = cfile_getstring(cf, key, (char *)0);
180
181 if (!val) return def;
182 if (!isdigit((int) *val) && (*val != '-' || !isdigit((int) val[1]))) return def;
183 return atoi(val);
184 }
185
cfile_getswitch(cfile cf,const char * key,int def)186 int cfile_getswitch(cfile cf,const char *key,int def)
187 {
188 const char *val = cfile_getstring(cf, key, (char *)0);
189
190 if (!val) return def;
191
192 if (*val == '0' || *val == 'n' ||
193 (*val == 'o' && val[1] == 'f') || *val == 'f') {
194 return 0;
195 }
196 else if (*val == '1' || *val == 'y' ||
197 (*val == 'o' && val[1] == 'n') || *val == 't') {
198 return 1;
199 }
200 return def;
201 }
202
cfile_free(cfile cf)203 void cfile_free(cfile cf)
204 {
205 int opt;
206
207 if (cf->kvlist) {
208 for (opt = 0; opt < cf->n_kv; opt++) {
209 if (cf->kvlist[opt].key)
210 free(cf->kvlist[opt].key);
211 if (cf->kvlist[opt].value)
212 free(cf->kvlist[opt].value);
213 }
214 free(cf->kvlist);
215 }
216 free(cf);
217 }
218