initial revision
[ucgine.git] / tools / cfzy / cfzy-basic / main.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 <string.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <getopt.h>
33
34 #include <cfzy_log.h>
35 #include <cfzy_list.h>
36 #include <cfzy_conftree.h>
37 #include <cfzy_dotconfig.h>
38 #include <cfzy_c_hdr.h>
39
40 #define LOG(level, fmt, args...)                                \
41         CFZY_LOG("cfzy-basic", level, fmt, ##args)
42
43 /* Parsed arguments */
44 struct cfzy_arguments {
45         char *input_conftree;
46         char *input_dotconfig;
47         char *output_dotconfig;
48         char *output_c_hdr;
49         char *output_multi_c_hdr;
50 };
51
52 static void
53 usage(const char *prog, int code)
54 {
55         fprintf(stderr,
56                 "\nUsage : \n");
57         fprintf(stderr,
58                 "  %s [options]\n", prog);
59         fprintf(stderr,
60                 "-h, --help\n"
61                 "            show help\n");
62         fprintf(stderr,
63                 "-d, --debug-level\n"
64                 "            set debug level (0: no log, 4 noisy)\n");
65         fprintf(stderr,
66                 "-c, --input-conftree\n"
67                 "            path to input configuration tree file "
68                 "(mandatory)\n");
69         fprintf(stderr,
70                 "-i, --input-dotconfig\n"
71                 "            path to input dotconfig file\n");
72         fprintf(stderr,
73                 "-o, --output-dotconfig\n"
74                 "            path to output dotconfig file\n");
75         fprintf(stderr,
76                 "-O, --output-c-hdr\n"
77                 "            path to output C header file\n");
78         fprintf(stderr,
79                 "-m, --output-multi-c-hdr\n"
80                 "            path to output multiple C header directory\n");
81         exit(code);
82 }
83
84 static void
85 free_args(struct cfzy_arguments *cfzy_args)
86 {
87         if (cfzy_args->input_conftree != NULL)
88                 free(cfzy_args->input_conftree);
89         if (cfzy_args->input_dotconfig != NULL)
90                 free(cfzy_args->input_dotconfig);
91         if (cfzy_args->output_dotconfig != NULL)
92                 free(cfzy_args->output_dotconfig);
93         if (cfzy_args->output_c_hdr != NULL)
94                 free(cfzy_args->output_c_hdr);
95         if (cfzy_args->output_multi_c_hdr != NULL)
96                 free(cfzy_args->output_multi_c_hdr);
97 }
98
99 static void
100 parse_args(struct cfzy_arguments *cfzy_args, int argc, char **argv)
101 {
102         int ch;
103         const char * prog = argv[0];
104         int option_index;
105         int level;
106         static struct option lgopts[] = {
107                 {"help", 0, 0, 'h'},
108                 {"debug-level", 0, 0, 'd'},
109                 {"input-conftree", 0, 0, 'c'},
110                 {"input-dotconfig", 0, 0, 'i'},
111                 {"output-dotconfig", 0, 0, 'o'},
112                 {"output-c-hdr", 0, 0, 'O'},
113                 {"output-multi-c-hdr", 0, 0, 'm'},
114                 {NULL, 0, 0, 0}
115         };
116
117         while ((ch = getopt_long(argc, argv,
118                                  "h"  /* help */
119                                  "d:"  /* debug-level */
120                                  "c:"  /* input-conftree */
121                                  "i:"  /* input-dotconfig */
122                                  "o:"  /* output-dotconfig */
123                                  "O:"  /* output-c-hdr */
124                                  "m:"  /* output-multi-c-hdr */
125                                  ,
126                             lgopts, &option_index)) != -1) {
127                 switch (ch) {
128                 case 'h': /* help */
129                         free_args(cfzy_args);
130                         usage(prog, 0); /* will exit */
131                         break;
132
133                 case 'd': /* debug-level */
134                         level = atoi(optarg);
135                         if (level < 0 || level > CFZY_LOG_DEBUG) {
136                                 LOG(ERR, "invalid log level\n");
137                                 free_args(cfzy_args);
138                                 cfzy_log_exit();
139                                 exit(1);
140                         }
141                         cfzy_log_set_default_level(level);
142                         break;
143
144                 case 'c': /* input-conftree */
145                         cfzy_args->input_conftree = strdup(optarg);
146                         if (cfzy_args->input_conftree == NULL) {
147                                 LOG(ERR, "Cannot alloc input conftree str\n");
148                                 free_args(cfzy_args);
149                                 cfzy_log_exit();
150                                 exit(1);
151                         }
152                         break;
153
154                 case 'i': /* input-dotconfig */
155                         cfzy_args->input_dotconfig = strdup(optarg);
156                         if (cfzy_args->input_dotconfig == NULL) {
157                                 LOG(ERR, "Cannot alloc input dotconfig str\n");
158                                 free_args(cfzy_args);
159                                 cfzy_log_exit();
160                                 exit(1);
161                         }
162                         break;
163
164                 case 'o': /* output-dotconfig */
165                         cfzy_args->output_dotconfig = strdup(optarg);
166                         if (cfzy_args->output_dotconfig == NULL) {
167                                 LOG(ERR, "Cannot alloc output dotconfig str\n");
168                                 free_args(cfzy_args);
169                                 cfzy_log_exit();
170                                 exit(1);
171                         }
172                         break;
173
174                 case 'O': /* output-c-hdr */
175                         cfzy_args->output_c_hdr = strdup(optarg);
176                         if (cfzy_args->output_c_hdr == NULL) {
177                                 LOG(ERR, "Cannot alloc output c_hdr str\n");
178                                 free_args(cfzy_args);
179                                 cfzy_log_exit();
180                                 exit(1);
181                         }
182                         break;
183
184                 case 'm': /* output-multi-c-hdr */
185                         cfzy_args->output_multi_c_hdr = strdup(optarg);
186                         if (cfzy_args->output_multi_c_hdr == NULL) {
187                                 LOG(ERR, "Cannot alloc output multi_c_hdr str\n");
188                                 free_args(cfzy_args);
189                                 cfzy_log_exit();
190                                 exit(1);
191                         }
192                         break;
193
194                 default:
195                         LOG(ERR, "invalid option\n");
196                         free_args(cfzy_args);
197                         cfzy_log_exit();
198                         usage(prog, 1); /* will exit */
199                 }
200         }
201
202         /* conftree file is mandatory */
203         if (cfzy_args->input_conftree == NULL) {
204                 LOG(ERR, "input conftree is mandatory\n");
205                 free_args(cfzy_args);
206                 cfzy_log_exit();
207                 usage(prog, 1); /* will exit */
208         }
209
210         /* check that at least one output argument is given */
211         if (cfzy_args->output_dotconfig == NULL &&
212             cfzy_args->output_c_hdr == NULL &&
213             cfzy_args->output_multi_c_hdr == NULL) {
214                 LOG(ERR, "at least one output argument is needed\n");
215                 free_args(cfzy_args);
216                 cfzy_log_exit();
217                 usage(prog, 1); /* will exit */
218         }
219
220         argc -= optind;
221         argv += optind;
222         /* XXX parse mode */
223 }
224
225 /*
226  * configuration tree
227  *
228  * -i input .config:
229  *   default is none
230  *
231  * -o output .config
232  *   defaut is none
233  *
234  * ?? output autoconf.h
235  *
236  * output config/xxx.h
237  *
238  * -v verbose
239  *
240  * oldconfig:
241  *   prompt for options that have no user value
242  *
243  * silentoldconfig
244  *   set all options that have no user value to default
245  *
246  * config
247  *    ?
248  *
249  * defconfig
250  *    ?
251  *
252  * randconfig
253  *    set all options to a random value (only for booleans, other
254  *    values are set to default)
255  *
256  * allyesconfig
257  *    set all options to y (booleans only)
258  *
259  * allnoconfig
260  *    set all options to n (booleans only)
261  *
262  * - ouvrir le fichier conftree
263  * - si -i, ouvrir .config
264  * - effectue les operations
265  * - si -o et/ou ... output
266  */
267
268 static int
269 do_configuration(struct cfzy_arguments *cfzy_args)
270 {
271         struct cfzy_confnode *n;
272         struct cfzy_conftree *conftree;
273         char buf[BUFSIZ];
274         char *s;
275         int ret;
276
277         /* parse configuration tree */
278         conftree = cfzy_conftree_new(cfzy_args->input_conftree);
279         if (conftree == NULL) {
280                 LOG(ERR, "Cannot parse configuration tree\n");
281                 return -1;
282         }
283
284         /* parse .config if provided */
285         if (cfzy_args->input_dotconfig != NULL &&
286             cfzy_dotconfig_read(conftree, cfzy_args->input_dotconfig) < 0) {
287                 cfzy_conftree_free(conftree);
288                 LOG(ERR, "Cannot parse dotconfig <%s>\n",
289                     cfzy_args->input_dotconfig);
290                 return -1;
291         }
292
293 #if 0
294         /* evaluate effective values of conftree */
295         if (cfzy_conftree_evaluate(conftree) < 0) {
296                 cfzy_conftree_free(conftree);
297                 LOG(ERR, "Cannot evaluate configuration tree\n");
298                 return -1;
299         }
300 #endif
301
302         memset(buf, 0, sizeof(buf));
303
304         /* browse all nodes following priority list and prompt user */
305         TAILQ_FOREACH(n, &conftree->prio_list, prio_next) {
306
307                 if (n->flags & CFZY_F_INVISIBLE)
308                         continue;
309                 if (n->flags & CFZY_F_NO_NAME)
310                         continue;
311
312                 /* node has a user value or user value cannot be set,
313                  * just display it */
314                 if ((n->user_value != NULL) || (n->flags & CFZY_F_NO_SET_VALUE)) {
315                         /* XXX we should have a difference between
316                          * NO_VALUE (menu for instance) and
317                          * NO_SET_VALUE */
318                         cfzy_confnode_evaluate(n);
319                         printf("%s=%s\n", cfzy_confnode_name(n),
320                                n->effective_value);
321                         continue;
322                 }
323
324                 /* node cannot be enabled due to deps */
325                 ret = cfzy_confnode_check_deps(n);
326                 if (ret == 0) {
327                         cfzy_confnode_evaluate(n);
328                         printf("%s=%s\n", cfzy_confnode_name(n),
329                                n->effective_value);
330                         continue;
331                 }
332                 if (ret < 0) {
333                         LOG(ERR, "Cannot check deps\n");
334                         return -1;
335                 }
336
337                 /* ok, now prompt user */
338
339                 cfzy_confnode_dump(n, stdout, CFZY_DUMP_MODE_STANDARD);
340
341                 while (1) {
342                         printf("Enter user value for %s > ",
343                                cfzy_confnode_name(n));
344                         fflush(stdout);
345
346                         fgets(buf, sizeof(buf) - 1, stdin);
347                         s = buf;
348                         strsep(&s, "\r\n");
349
350                         /* empty buf means default value */
351                         if (strcmp(buf, "") == 0)
352                                 break;
353
354                         /* invalid value, prompt again */
355                         if (cfzy_confnode_set_uservalue(n, buf) < 0) {
356                                 printf("invalid value\n");
357                                 continue;
358                         }
359
360                         break;
361                 }
362
363                 cfzy_confnode_evaluate(n);
364                 printf("%s=%s\n", cfzy_confnode_name(n),
365                        n->effective_value);
366         }
367
368         /* output .config if provided */
369         if (cfzy_args->output_dotconfig != NULL &&
370             cfzy_dotconfig_write(conftree, cfzy_args->output_dotconfig) < 0) {
371                 cfzy_conftree_free(conftree);
372                 LOG(ERR, "Cannot output dotconfig <%s>\n",
373                     cfzy_args->output_dotconfig);
374                 return -1;
375         }
376
377         /* output C header if provided */
378         if (cfzy_args->output_c_hdr != NULL &&
379             cfzy_c_hdr_write(conftree, cfzy_args->output_c_hdr) < 0) {
380                 cfzy_conftree_free(conftree);
381                 LOG(ERR, "Cannot output C header <%s>\n",
382                     cfzy_args->output_c_hdr);
383                 return -1;
384         }
385
386         /* output multiple C headers if provided */
387         if (cfzy_args->output_multi_c_hdr != NULL &&
388             cfzy_multi_c_hdr_write(conftree, cfzy_args->output_multi_c_hdr) < 0) {
389                 cfzy_conftree_free(conftree);
390                 LOG(ERR, "Cannot output multiple C headers <%s>\n",
391                     cfzy_args->output_multi_c_hdr);
392                 return -1;
393         }
394
395         cfzy_conftree_free(conftree);
396         return 0;
397 }
398
399 int main(int argc, char **argv)
400 {
401         int ret = EXIT_SUCCESS;
402         struct cfzy_arguments cfzy_args;
403
404         /* XXX a cfzy_init() would be better */
405         cfzy_log_init();
406
407         memset(&cfzy_args, 0, sizeof(cfzy_args));
408
409         /* argument parsing is always successful (will exit on error) */
410         parse_args(&cfzy_args, argc, argv);
411
412         /* do the configuration */
413         if (do_configuration(&cfzy_args) < 0) {
414                 LOG(ERR, "Configuration failed");
415                 ret = EXIT_FAILURE;
416         }
417
418         free_args(&cfzy_args);
419         cfzy_log_exit();
420
421         return ret;
422 }