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
conffile_open(conffile_t * sp,const char * f,const char * mode,const char * sep,const char * comment)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
conffile_close(conffile_t * sp)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 *
read_line(conffile_t * sp,ent_t * ep)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
iscomment(conffile_t * sp,char * from)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
conffile_split(conffile_t * sp,ent_t * ep,char * from)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
conffile_getent(conffile_t * sp,ent_t * ep)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
conffile_get_lineno(conffile_t * sp)161 conffile_get_lineno(conffile_t *sp)
162 {
163           return sp->lineno;
164 }
165 
166 /* return the name */
167 char *
conffile_get_name(conffile_t * sp)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
conffile_get_by_field(conffile_t * sp,ent_t * ep,int f,char * val)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
safe_write(FILE * fp,char * buf,unsigned cc)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
report_error(FILE * fp,char * name,const char * fmt,...)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
conffile_putent(conffile_t * sp,int f,char * val,char * newent)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
conffile_printent(ent_t * ep)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