initial revision
[ucgine.git] / tools / cfzy / libconfizery / cfzy_c_hdr.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 #include <libgen.h>
34 #include <unistd.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
38
39 #include "cfzy_log.h"
40 #include "cfzy_file.h"
41 #include "cfzy_conftree.h"
42 #include "cfzy_confnode.h"
43
44 #define LOG(level, fmt, args...)                                \
45         CFZY_LOG("c_hdr", level, fmt, ##args)
46
47 /* does not clean directories on error, if path exist, assume it's a
48  * directory and ignore error */
49 int cfzy_recursive_mkdir(const char *path)
50 {
51         char tmp[PATH_MAX];
52         char *p = NULL;
53         int n, ret;
54
55         n = snprintf(tmp, sizeof(tmp),"%s", path);
56         if (n <= 0 || n >= PATH_MAX)
57                 return -1;
58
59         /* ignore all trailing '/' */
60         while (n > 0 && tmp[--n] == '/')
61                 tmp[n] = '\0';
62
63         /* create all intermediate directories */
64         for (p = tmp + 1; *p; p++) {
65                 if (*p != '/')
66                         continue;
67
68                 *p = '\0';
69                 if (mkdir(tmp, S_IRWXU) < 0 && errno != EEXIST)
70                         return -1;
71
72                 *p = '/';
73         }
74
75         /* and last one */
76         ret = mkdir(tmp, S_IRWXU);
77         if (ret < 0 && errno == EEXIST)
78                 return 0;
79
80         return ret;
81 }
82
83 /*
84  * Get the file name from a config name. Replace first underscore
85  * only:
86  *   CONFIG_A_B -> config/a/b.h
87  *   CONFIG_A__B___C -> config/a/_b/__c.h
88  */
89 static char *configname_to_filename(const char *name)
90 {
91         char *filename, *s;
92         int len;
93
94         /* allocate room for dst buffer */
95         len = strlen(name);
96         filename = malloc(len + 3);
97         if (filename == NULL)
98                 return NULL;
99         strncpy(filename, name, len + 1);
100
101         for (s = filename; *s != '\0'; s++) {
102                 /* '/' is not allowed in variable name */
103                 if (*s == '/') {
104                         free(filename);
105                         return NULL;
106                 }
107
108                 /* convert the first '_' in '/' */
109                 if (*s == '_') {
110                         *s = '/';
111                         while (s[1] == '/')
112                                 s++;
113                         continue;
114                 }
115
116                 if (isalpha(*s))
117                         *s = tolower(*s);
118         }
119         *s++ = '.';
120         *s++ = 'h';
121         *s = '\0';
122
123         return filename;
124 }
125
126 static int __cfzy_multi_c_hdr_write(const struct cfzy_confnode *n)
127 {
128         const struct cfzy_confnode *c;
129         FILE *f = NULL;
130         char *filename = NULL;
131         char *dname_buf = NULL, *dname_ptr = NULL;
132         char oldval[512];
133         char newval[512];
134         int ret, val;
135
136         /* only dump the node if it has a name and a value */
137         if (n->name != NULL && n->effective_value != NULL) {
138                 /* get the name of the file (full path) */
139                 filename = configname_to_filename(n->name);
140                 if (filename == NULL)
141                         goto fail;
142
143                 /* get directory name from file name */
144                 dname_buf = strdup(filename);
145                 if (dname_buf == NULL)
146                         goto fail;
147                 dname_ptr = dirname(dname_buf);
148
149                 /* create dirs */
150                 if (cfzy_recursive_mkdir(dname_ptr) < 0)
151                         goto fail;
152
153                 /* get old value */
154                 memset(oldval, 0, sizeof(oldval));
155                 f = fopen(filename, "r");
156                 if (f != NULL) {
157                         ret = fread(oldval, 1, sizeof(oldval) - 1, f);
158                         if (ret < 0) {
159                                 LOG(ERR, "cannot read <%s>\n", filename);
160                                 goto fail;
161                         }
162                         fclose(f);
163                         f = NULL;
164                 }
165
166                 /* if config node is a bool, we have NULL and n will
167                  * output the same val (is not st) */
168                 if (n->flags & CFZY_F_VAL_IS_BOOL) {
169                         val = cfzy_confnode_str2bool(n, n->effective_value);
170                         if (val < 0)
171                                 return -1;
172
173                         /* bool is false -> not set */
174                         if (val == 0) {
175                                 if (snprintf(newval, sizeof(newval),
176                                              "/* %s is not set */",
177                                              cfzy_confnode_name(n)) < 0)
178                                         goto fail;
179                         }
180                         else {
181                                 /* bool is true */
182                                 if (snprintf(newval, sizeof(newval),
183                                              "/* %s = %s */", cfzy_confnode_name(n),
184                                              n->effective_value) < 0)
185                                         goto fail;
186                         }
187                 }
188                 else {
189                         if (snprintf(newval, sizeof(newval),
190                                      "/* %s = %s */", cfzy_confnode_name(n),
191                                      n->effective_value) < 0)
192                                 goto fail;
193                 }
194
195                 /* if the value is different, write the new value */
196                 if (strncmp(newval, oldval, sizeof(newval)) != 0) {
197                         f = fopen(filename, "w");
198                         if (f == NULL) {
199                                 LOG(ERR, "cannot open file <%s>\n", filename);
200                                 goto fail;
201                         }
202
203                         if (fprintf(f, "%s", newval) < 0) {
204                                 LOG(ERR, "cannot write in <%s>\n",
205                                     filename);
206                                 goto fail;
207                         }
208                         fclose(f);
209                         f = NULL;
210                 }
211
212                 free(dname_buf);
213                 free(filename);
214         }
215
216         /* dump all the children of root node */
217         TAILQ_FOREACH(c, &n->children, child_next) {
218                 if (__cfzy_multi_c_hdr_write(c) < 0)
219                         break;
220         }
221
222         return 0;
223
224  fail:
225         if (f != NULL)
226                 fclose(f);
227         if (dname_buf != NULL)
228                 free(dname_buf);
229         if (filename != NULL)
230                 free(filename);
231
232         return -1;
233 }
234
235 /* write config/foo/bar.h */
236 int cfzy_multi_c_hdr_write(const struct cfzy_conftree *conftree,
237                            const char *basedir)
238 {
239         char old_cwd_buf[PATH_MAX];
240         char *old_cwd = NULL;
241         const struct cfzy_confnode *c;
242         int ret = 0;
243
244         LOG(INFO, "multi C headers write <%s>\n", basedir);
245
246         if (cfzy_recursive_mkdir(basedir) < 0)
247                 return -1;
248
249         old_cwd = getcwd(old_cwd_buf, sizeof(old_cwd_buf));
250         if (old_cwd == NULL) {
251                 LOG(ERR, "getcwd failed: %s\n", strerror(errno));
252                 return -1;
253         }
254
255         if (chdir(basedir) < 0) {
256                 LOG(ERR, "chdir failed: %s\n", strerror(errno));
257                 return -1;
258         }
259
260         /* dump all the children of root node */
261         TAILQ_FOREACH(c, &conftree->root->children, child_next) {
262                 ret = __cfzy_multi_c_hdr_write(c);
263                 if (ret < 0)
264                         break;
265         }
266
267         if (chdir(old_cwd) < 0)
268                 LOG(ERR, "chdir back to <%s> failed: %s\n",
269                     old_cwd, strerror(errno));
270
271         return ret;
272 }
273
274 /* write config/autoconf.h */
275 int cfzy_c_hdr_write(const struct cfzy_conftree *conftree,
276                      const char *filename)
277 {
278         FILE *tmp_f, *f;
279         const struct cfzy_confnode *c;
280         int ret = 0;
281
282         LOG(INFO, "open old C header <%s>\n", filename);
283
284         /* open old file in read mode */
285
286         f = fopen(filename, "r");
287         if (f == NULL && errno != ENOENT) {
288                 printf("cannot open file <%s>: %s\n",
289                        filename, strerror(errno));
290                 return -1;
291         }
292
293         /* old file exists */
294         if (f != NULL) {
295
296                 /* write C header in temp file */
297                 tmp_f = tmpfile();
298                 if (tmp_f == NULL) {
299                         printf("cannot open temp file\n");
300                         return -1;
301                 }
302
303                 /* dump all the children of root node */
304                 TAILQ_FOREACH(c, &conftree->root->children, child_next) {
305                         ret = cfzy_confnode_c_hdr_write(c, tmp_f);
306                         if (ret < 0)
307                                 break;
308                 }
309
310                 /* files are the same, nothing to do */
311                 if (cfzy_file_compare(f, tmp_f) == 0) {
312                         LOG(INFO, "already up to date: <%s>\n", filename);
313                         fclose(tmp_f);
314                         fclose(f);
315                         return 0;
316                 }
317
318                 fclose(tmp_f);
319                 fclose(f);
320         }
321
322         /* reopen the file in write mode */
323
324         LOG(INFO, "write new C header <%s>\n", filename);
325
326         f = fopen(filename, "w");
327         if (f == NULL) {
328                 printf("cannot open file <%s>\n", filename);
329                 return -1;
330         }
331
332         /* dump all the children of root node */
333         TAILQ_FOREACH(c, &conftree->root->children, child_next) {
334                 ret = cfzy_confnode_c_hdr_write(c, f);
335                 if (ret < 0)
336                         break;
337         }
338
339         fclose(f);
340
341         return ret;
342 }