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