initial revision
[ucgine.git] / tools / cfzy / libconfizery / cfzy_dotconfig.c
1 /*
2  * Copyright (c) 2013, Olivier MATZ <zer0@droids-corp.org>
3  * All rights reserved.
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include <errno.h>
33
34 #include "cfzy_log.h"
35 #include "cfzy_htable.h"
36 #include "cfzy_string.h"
37 #include "cfzy_file.h"
38 #include "cfzy_conftree.h"
39 #include "cfzy_confnode.h"
40
41 #define LOG(level, fmt, args...)                                \
42         CFZY_LOG("dotconfig", level, fmt, ##args)
43
44 /* Allocate and return string containing the value of the
45  * option. Buffer must point to the option (after the '=' in the
46  * .config), and it can be quoted. The string must be freed by the
47  * user. */
48 static char *dotconfig_get_optval(const char *buf)
49 {
50         const char *s;
51         unsigned i, len;
52         char *retbuf;
53
54         /* skip spaces */
55         s = buf;
56         while (*s != '\0' && isspace(*s))
57                 s++;
58         if (*s == '\0' || *s == '#')
59                 return 0;
60
61         /* if it's a quote, unquote the string */
62         if (*s == '"' || *s == '\'') {
63                 retbuf = cfzy_string_unquote(s, &len);
64         }
65         else {
66                 retbuf = strdup(s);
67                 if (retbuf == NULL)
68                         return NULL;
69
70                 for (i = 0; s[i] != '\0' && s[i] != '\n' && s[i] != '#'; i ++)
71                         ;
72
73                 retbuf[i] = '\0';
74         }
75         return retbuf;
76 }
77
78 /* parse a line of .config: fill the uservalue of nodes in the
79  * conftree structure */
80 static int dotconfig_parse_line(struct cfzy_conftree *conftree, const char *line)
81 {
82         struct cfzy_confnode *n;
83         char buf[BUFSIZ];
84         char *nodename, *s;
85         char *optval;
86
87         /* copy line in a writable buffer */
88         buf[sizeof(buf) - 1] = '\0';
89         strncpy(buf, line, sizeof(buf) - 1);
90         s = buf;
91
92         /* skip spaces */
93         while (*s != '\0' && isspace(*s))
94                 s++;
95
96         /* nothing to do, empty line */
97         if (*s == '\0')
98                 return 0;
99
100         /* skip the first "#", expecting "is not set" */
101         if (*s == '#') {
102                 s++;
103                 while (*s != '\0' && isspace(*s))
104                         s++;
105
106                 /* nothing to do, it's a comment */
107                 if (*s == '\0')
108                         return 0;
109
110                 /* get node name, ignore invalid names */
111                 if (strncmp("CONFIG_", s, strlen("CONFIG_")))
112                         return 0;
113                 nodename = s + strlen("CONFIG_");
114                 s = nodename;
115
116                 /* skip spaces */
117                 while (*s != '\0' && !isspace(*s))
118                         s++;
119
120                 /* nothing to do, it's a comment */
121                 if (*s == '\0')
122                         return 0;
123
124                 *s = '\0';
125                 s ++;
126
127                 /* should end with "is not set", else it's a comment */
128                 if (strcmp(s, "is not set\n"))
129                         return 0;
130
131                 optval = strdup("n");
132                 if (optval == NULL)
133                         return -1;
134         }
135         else {
136                 /* get node name, ignore invalid names */
137                 if (strncmp("CONFIG_", s, strlen("CONFIG_")))
138                         return -1;
139                 nodename = s + strlen("CONFIG_");
140
141                 while (*s != '\0' && *s != '=')
142                         s++;
143                 /* invalid line */
144                 if (*s != '=')
145                         return -1;
146
147                 *s = '\0';
148                 optval = dotconfig_get_optval(s + 1);
149                 if (optval == NULL)
150                         return -1;
151         }
152
153         /* find the node in the conf htable */
154         n = cfzy_htable_lookup(conftree->nodes_htable, nodename);
155         if (n == NULL) {
156                 LOG(NOTICE, "ignore unknown symbol <%s>\n", nodename);
157                 free(optval);
158                 return 0;
159         }
160
161         /* check that value is valid */
162         if (cfzy_confnode_str2bool(n, optval) < 0) {
163                 LOG(ERR, "invalid option value %s=<%s>\n", nodename, optval);
164                 free(optval);
165                 return -1;
166         }
167
168         /* set value in node */
169         LOG(DEBUG, "%s=<%s>\n", nodename, optval);
170         if (cfzy_confnode_set_uservalue(n, optval) < 0) {
171                 LOG(ERR, "cannot set value %s=<%s>\n", nodename, optval);
172                 free(optval);
173                 return -1;
174         }
175
176         free(optval);
177         return 0;
178 }
179
180 /* parse .config */
181 int cfzy_dotconfig_read(struct cfzy_conftree *conftree, const char *filename)
182 {
183         FILE *f;
184         char buf[BUFSIZ];
185         int err = 0;
186
187         LOG(INFO, "dotconfig read <%s>\n", filename);
188
189         f = fopen(filename, "r");
190         if (f == NULL) {
191                 LOG(ERR, "cannot find file <%s>\n", filename);
192                 return -1;
193         }
194         while (fgets(buf, sizeof(buf), f) != NULL) {
195
196                 if (dotconfig_parse_line(conftree, buf) < 0) {
197                         err = -1;
198                         break;
199                 }
200         }
201         fclose(f);
202
203         return err;
204 }
205
206 /* write .config */
207 int cfzy_dotconfig_write(const struct cfzy_conftree *conftree,
208                          const char *filename)
209 {
210         FILE *f, *tmp_f;
211         const struct cfzy_confnode *c;
212         int ret = 0;
213
214         LOG(INFO, "open old dotconfig <%s>\n", filename);
215
216         /* open old file in read mode */
217
218         f = fopen(filename, "r");
219         if (f == NULL && errno != ENOENT) {
220                 printf("cannot open file <%s>: %s\n",
221                        filename, strerror(errno));
222                 return -1;
223         }
224
225         /* old file exists */
226         if (f != NULL) {
227
228                 /* write C header in temp file */
229                 tmp_f = tmpfile();
230                 if (tmp_f == NULL) {
231                         printf("cannot open temp file\n");
232                         return -1;
233                 }
234
235                 /* dump all the children of root node */
236                 TAILQ_FOREACH(c, &conftree->root->children, child_next) {
237                         ret = cfzy_confnode_dotconfig_write(c, tmp_f);
238                         if (ret < 0)
239                                 break;
240                 }
241
242                 /* files are the same, nothing to do */
243                 if (cfzy_file_compare(f, tmp_f) == 0) {
244                         LOG(INFO, "already up to date: <%s>\n", filename);
245                         fclose(tmp_f);
246                         fclose(f);
247                         return 0;
248                 }
249
250                 fclose(tmp_f);
251                 fclose(f);
252         }
253
254         /* reopen the file in write mode */
255
256         LOG(INFO, "write new C header <%s>\n", filename);
257
258         f = fopen(filename, "w");
259         if (f == NULL) {
260                 LOG(ERR, "cannot open file <%s>\n", filename);
261                 return -1;
262         }
263
264         /* dump all the children of root node */
265         TAILQ_FOREACH(c, &conftree->root->children, child_next) {
266                 ret = cfzy_confnode_dotconfig_write(c, f);
267                 if (ret < 0)
268                         break;
269         }
270
271         fclose(f);
272
273         return ret;
274 }