1 /* $NetBSD: conffile.c,v 1.2 2009/06/30 02:44:52 agc Exp $ */ 2 3 /* 4 * Copyright � 2006 Alistair Crooks. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote 15 * products derived from this software without specific prior written 16 * permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 24 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 #include <sys/types.h> 31 #include <sys/param.h> 32 #include <sys/stat.h> 33 34 #include <ctype.h> 35 #include <errno.h> 36 #include <stdarg.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #include "conffile.h" 43 44 /* start of split routines */ 45 46 /* open a file */ 47 int 48 conffile_open(conffile_t *sp, const char *f, const char *mode, const char *sep, const char *comment) 49 { 50 (void) memset(sp, 0x0, sizeof(*sp)); 51 if ((sp->fp = fopen(f, mode)) == NULL) { 52 (void) fprintf(stderr, "can't open `%s' `%s' (%s)\n", f, mode, strerror(errno)); 53 return 0; 54 } 55 (void) strlcpy(sp->name, f, sizeof(sp->name)); 56 sp->sep = sep; 57 sp->comment = comment; 58 sp->readonly = (strcmp(mode, "r") == 0); 59 return 1; 60 } 61 62 /* close a file */ 63 void 64 conffile_close(conffile_t *sp) 65 { 66 (void) fclose(sp->fp); 67 } 68 69 /* read the next line from the file */ 70 static char * 71 read_line(conffile_t *sp, ent_t *ep) 72 { 73 char *from; 74 75 if (fgets(ep->buf, sizeof(ep->buf), sp->fp) == NULL) { 76 return NULL; 77 } 78 sp->lineno += 1; 79 for (from = ep->buf ; *from && isspace((unsigned int)*from) ; from++) { 80 } 81 return from; 82 } 83 84 /* return 1 if this line contains no entry */ 85 static int 86 iscomment(conffile_t *sp, char *from) 87 { 88 return (*from == 0x0 || *from == '\n' || strchr(sp->comment, *from) != NULL); 89 } 90 91 /* split the entry up into fields */ 92 int 93 conffile_split(conffile_t *sp, ent_t *ep, char *from) 94 { 95 FILE *fp; 96 const char *seps; 97 char *to; 98 char was; 99 int sepseen; 100 int cc; 101 102 seps = (sp == NULL) ? " \t" : sp->sep; 103 fp = (sp == NULL) ? stdin : sp->fp; 104 for (ep->sv.c = 0 ; *from && *from != '\n' ; ) { 105 for (to = from, sepseen = 0 ; *to && *to != '\n' && strchr(seps, *to) == NULL ; to++) { 106 if (*to == '\\') { 107 if (*(to + 1) == '\n') { 108 cc = (int)(to - ep->buf); 109 if (fgets(&ep->buf[cc], (int)(sizeof(ep->buf) - cc), fp) != NULL) { 110 if (sp != NULL) { 111 sp->lineno += 1; 112 } 113 } 114 } else { 115 sepseen = 1; 116 to++; 117 } 118 } 119 } 120 ALLOC(char *, ep->sv.v, ep->sv.size, ep->sv.c, 14, 14, "conffile_getent", exit(EXIT_FAILURE)); 121 ep->sv.v[ep->sv.c++] = from; 122 was = *to; 123 *to = 0x0; 124 if (sepseen) { 125 char *cp; 126 127 for (cp = from ; *cp ; cp++) { 128 if (strchr(seps, *cp) != NULL) { 129 (void) strcpy(cp - 1, cp); 130 } 131 } 132 } 133 if (was == 0x0 || was == '\n') { 134 break; 135 } 136 for (from = to + 1 ; *from && *from != '\n' && strchr(seps, *from) != NULL ; from++) { 137 } 138 } 139 return 1; 140 } 141 142 /* get the next entry */ 143 int 144 conffile_getent(conffile_t *sp, ent_t *ep) 145 { 146 char *from; 147 148 for (;;) { 149 if ((from = read_line(sp, ep)) == NULL) { 150 return 0; 151 } 152 if (iscomment(sp, from)) { 153 continue; 154 } 155 return conffile_split(sp, ep, from); 156 } 157 } 158 159 /* return the line number */ 160 int 161 conffile_get_lineno(conffile_t *sp) 162 { 163 return sp->lineno; 164 } 165 166 /* return the name */ 167 char * 168 conffile_get_name(conffile_t *sp) 169 { 170 return sp->name; 171 } 172 173 /* return the entry based upon the contents of field `f' */ 174 int 175 conffile_get_by_field(conffile_t *sp, ent_t *ep, int f, char *val) 176 { 177 while (conffile_getent(sp, ep)) { 178 if (ep->sv.c > (uint32_t)f && strcmp(ep->sv.v[f], val) == 0) { 179 return 1; 180 } 181 } 182 return 0; 183 } 184 185 /* check that we wrote `cc' chars of `buf' to `fp' */ 186 static int 187 safe_write(FILE *fp, char *buf, unsigned cc) 188 { 189 return fwrite(buf, sizeof(char), cc, fp) == cc; 190 } 191 192 #if 0 193 /* check that we wrote the entry correctly */ 194 static int 195 safe_write_ent(FILE *fp, conffile_t *sp, ent_t *ep) 196 { 197 char buf[BUFSIZ]; 198 int cc; 199 int i; 200 201 for (cc = i = 0 ; i < ep->sv.c ; i++) { 202 cc += snprintf(&buf[cc], sizeof(buf) - cc, "%s%1.1s", ep->sv.v[i], (i == ep->sv.c - 1) ? "\n" : sp->sep); 203 } 204 return safe_write(fp, buf, cc); 205 } 206 #endif 207 208 /* report an error and clear up */ 209 static int 210 report_error(FILE *fp, char *name, const char *fmt, ...) 211 { 212 va_list vp; 213 214 va_start(vp, fmt); 215 (void) vfprintf(stderr, fmt, vp); 216 va_end(vp); 217 if (fp) 218 (void) fclose(fp); 219 (void) unlink(name); 220 return 0; 221 } 222 223 /* put the new entry (in place of ent[f] == val, if val is non-NULL) */ 224 int 225 conffile_putent(conffile_t *sp, int f, char *val, char *newent) 226 { 227 ent_t e; 228 FILE *fp; 229 char name[MAXPATHLEN]; 230 char *from; 231 int fd; 232 233 (void) strlcpy(name, "/tmp/split.XXXXXX", sizeof(name)); 234 if ((fd = mkstemp(name)) < 0) { 235 (void) fprintf(stderr, "can't mkstemp `%s' (%s)\n", name, strerror(errno)); 236 return 0; 237 } 238 fp = fdopen(fd, "w"); 239 (void) memset(&e, 0x0, sizeof(e)); 240 for (;;) { 241 if ((from = read_line(sp, &e)) == NULL) { 242 break; 243 } 244 if (iscomment(sp, from)) { 245 if (!safe_write(fp, e.buf, strlen(e.buf))) { 246 return report_error(fp, name, "Short write 1 to `%s' (%s)\n", name, strerror(errno)); 247 } 248 } 249 (void) conffile_split(sp, &e, from); 250 if (val != NULL && (uint32_t)f < e.sv.c && strcmp(val, e.sv.v[f]) == 0) { 251 /* replace it */ 252 if (!safe_write(fp, newent, strlen(newent))) { 253 return report_error(fp, name, "Short write 2 to `%s' (%s)\n", name, strerror(errno)); 254 } 255 } else { 256 if (!safe_write(fp, e.buf, strlen(e.buf))) { 257 return report_error(fp, name, "Short write 3 to `%s' (%s)\n", name, strerror(errno)); 258 } 259 } 260 } 261 if (val == NULL && !safe_write(fp, newent, strlen(newent))) { 262 return report_error(fp, name, "Short write 4 to `%s' (%s)\n", name, strerror(errno)); 263 } 264 (void) fclose(fp); 265 if (rename(name, sp->name) < 0) { 266 return report_error(NULL, name, "can't rename %s to %s (%s)\n", name, sp->name, strerror(errno)); 267 } 268 return 1; 269 } 270 271 /* print the entry on stdout */ 272 void 273 conffile_printent(ent_t *ep) 274 { 275 uint32_t i; 276 277 for (i = 0 ; i < ep->sv.c ; i++) { 278 printf("(%d `%s') ", i, ep->sv.v[i]); 279 } 280 printf("\n"); 281 } 282