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