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