Home | History | Annotate | Line # | Download | only in lib
      1 /* $NetBSD: conffile.c,v 1.3 2012/03/15 04:06:54 joerg 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 __printflike(3, 4) 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