Merge "res_pjsip.c: Fix ident_to_str() and refactor ident_handler()."
[asterisk/asterisk.git] / menuselect / menuselect.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005 - 2010, Digium, Inc.
5  *
6  * Russell Bryant <russell@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  *
22  * \author Russell Bryant <russell@digium.com>
23  *
24  * \brief A menu-driven system for Asterisk module selection
25  */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <stdarg.h>
32 #include <getopt.h>
33 #include <ctype.h>
34
35 #include <libxml/parser.h>
36 #include <libxml/tree.h>
37
38 #include "autoconfig.h"
39 #include "linkedlists.h"
40 #include "menuselect.h"
41
42 #define ARRAY_LEN(a) (size_t) (sizeof(a) / sizeof(0[a]))
43
44 #ifdef MENUSELECT_DEBUG
45 static FILE *debug;
46 #endif
47
48 /*! The list of categories */
49 struct categories categories = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
50
51 /*!
52    We have to maintain a pointer to the root of the trees generated from reading
53    the build options XML files so that we can free it when we're done.  We don't
54    copy any of the information over from these trees. Our list is just a
55    convenient mapping to the information contained in these lists with one
56    additional piece of information - whether the build option is enabled or not.
57 */
58 struct tree {
59         /*! the root of the tree */
60         xmlDoc *root;
61         /*! for linking */
62         AST_LIST_ENTRY(tree) list;
63 };
64
65 /*! The list of trees from menuselect-tree files */
66 static AST_LIST_HEAD_NOLOCK_STATIC(trees, tree);
67
68 static const char * const tree_files[] = {
69         "menuselect-tree"
70 };
71
72 static char *output_makeopts = OUTPUT_MAKEOPTS_DEFAULT;
73 static char *output_makedeps = OUTPUT_MAKEDEPS_DEFAULT;
74
75 /*! This is set to 1 if menuselect.makeopts pre-existed the execution of this app */
76 static int existing_config = 0;
77
78 /*! This is set when the --check-deps argument is provided. */
79 static int check_deps = 0;
80
81 /*! These are set when the --list-options or --list-groups arguments are provided. */
82 static int list_options = 0, list_groups = 0;
83
84 /*! This variable is non-zero when any changes are made */
85 int changes_made = 0;
86
87 /*! Menu name */
88 const char *menu_name = "Menuselect";
89
90 enum dep_file_state {
91         DEP_FILE_UNKNOWN = -2,
92         DEP_FILE_DISABLED = -1,
93         DEP_FILE_UNMET = 0,
94         DEP_FILE_MET = 1,
95 };
96
97 /*! Global list of dependencies that are external to the tree */
98 struct dep_file {
99         char name[32];
100         enum dep_file_state met;
101         enum dep_file_state previously_met;
102         AST_LIST_ENTRY(dep_file) list;
103 };
104 AST_LIST_HEAD_NOLOCK_STATIC(deps_file, dep_file);
105
106 /*! \brief return a pointer to the first non-whitespace character */
107 static inline char *skip_blanks(char *str)
108 {
109         if (!str)
110                 return NULL;
111
112         while (*str && *str < 33)
113                 str++;
114
115         return str;
116 }
117
118 static int open_debug(void)
119 {
120 #ifdef MENUSELECT_DEBUG
121         if (!(debug = fopen("menuselect_debug.txt", "w"))) {
122                 fprintf(stderr, "Failed to open menuselect_debug.txt for debug output.\n");
123                 return -1;
124         }
125 #endif
126         return 0;
127 }
128
129 #define print_debug(f, ...) __print_debug(__LINE__, f, ## __VA_ARGS__)
130 static void __attribute__((format(printf, 2, 3))) __print_debug(int line, const char *format, ...)
131 {
132 #ifdef MENUSELECT_DEBUG
133         va_list ap;
134
135         fprintf(debug, "%d -", line);
136
137         va_start(ap, format);
138         vfprintf(debug, format, ap);
139         va_end(ap);
140
141         fflush(debug);
142 #endif
143 }
144
145 static void close_debug(void)
146 {
147 #ifdef MENUSELECT_DEBUG
148         if (debug)
149                 fclose(debug);
150 #endif
151 }
152
153 /*! \brief Finds a category with the given name or creates it if not found */
154 static struct category *category_find_or_create(const char *name)
155 {
156         struct category *c;
157
158         AST_LIST_TRAVERSE(&categories, c, list) {
159                 if (!strcmp(c->name, name)) {
160                         xmlFree((void *) name);
161                         return c;
162                 }
163         }
164
165         if (!(c = calloc(1, sizeof(*c)))) {
166                 return NULL;
167         }
168
169         c->name = name;
170
171         AST_LIST_INSERT_TAIL(&categories, c, list);
172
173         return c;
174 }
175
176 static void free_reference(struct reference *ref)
177 {
178         /* If name and displayname point to the same place, only free one of them */
179         if (ref->name == ref->displayname) {
180                 xmlFree((void *) ref->name);
181         } else {
182                 xmlFree((void *) ref->name);
183                 xmlFree((void *) ref->displayname);
184         }
185
186         free(ref);
187 }
188
189 /*! \brief Free a member structure and all of its members */
190 static void free_member(struct member *mem)
191 {
192         struct reference *ref;
193
194         if (!mem) {
195                 return;
196         }
197
198         while ((ref = AST_LIST_REMOVE_HEAD(&mem->deps, list)))
199                 free_reference(ref);
200         while ((ref = AST_LIST_REMOVE_HEAD(&mem->conflicts, list)))
201                 free_reference(ref);
202         while ((ref = AST_LIST_REMOVE_HEAD(&mem->uses, list)))
203                 free_reference(ref);
204
205         if (!mem->is_separator) {
206                 xmlFree((void *) mem->name);
207                 xmlFree((void *) mem->displayname);
208                 xmlFree((void *) mem->touch_on_change);
209                 xmlFree((void *) mem->remove_on_change);
210                 xmlFree((void *) mem->defaultenabled);
211                 xmlFree((void *) mem->support_level);
212                 xmlFree((void *) mem->replacement);
213         }
214
215         free(mem);
216 }
217
218 /*! \assigns values to support level strings */
219 static enum support_level_values string_to_support_level(const char *support_level)
220 {
221         if (!support_level) {
222                 return SUPPORT_UNSPECIFIED;
223         }
224
225         if (!strcasecmp(support_level, "core")) {
226                 return SUPPORT_CORE;
227         }
228
229         if (!strcasecmp(support_level, "extended")) {
230                 return SUPPORT_EXTENDED;
231         }
232
233         if (!strcasecmp(support_level, "deprecated")) {
234                 return SUPPORT_DEPRECATED;
235         }
236
237         if (!strcasecmp(support_level, "external")) {
238                 return SUPPORT_EXTERNAL;
239         }
240
241         if (!strcasecmp(support_level, "option")) {
242                 return SUPPORT_OPTION;
243         }
244
245         return SUPPORT_UNSPECIFIED;
246 }
247
248 /*! \gets const separator strings from support level values */
249 static const char *support_level_to_string(enum support_level_values support_level)
250 {
251         switch (support_level) {
252         case SUPPORT_CORE:
253                 return "Core";
254         case SUPPORT_EXTENDED:
255                 return "Extended";
256         case SUPPORT_DEPRECATED:
257                 return "Deprecated";
258         case SUPPORT_EXTERNAL:
259                 return "External";
260         case SUPPORT_OPTION:
261                 return "Module Options";
262         default:
263                 return "Unspecified";
264         }
265 }
266
267 static void categories_flatten()
268 {
269         int idx;
270         struct category *c;
271         struct member *m;
272
273         AST_LIST_TRAVERSE(&categories, c, list) {
274                 for (idx = 0; idx < SUPPORT_COUNT; idx++) {
275                         struct support_level_bucket bucket = c->buckets[idx];
276                         while ((m = AST_LIST_REMOVE_HEAD(&bucket, list))) {
277                                 AST_LIST_INSERT_TAIL(&c->members, m, list);
278                         }
279                 }
280         }
281 }
282
283 /*! \sets default values for a given separator */
284 static struct member *create_separator(enum support_level_values level)
285 {
286         struct member *separator = calloc(1, sizeof(*separator));
287         separator->name = support_level_to_string(level);
288         separator->displayname = "";
289         separator->is_separator = 1;
290         return separator;
291 }
292
293 /*! \adds a member to a category and attaches it to the last element of a particular support level used */
294 static int add_member_list_order(struct member *mem, struct category *cat)
295 {
296         enum support_level_values support_level = string_to_support_level(mem->support_level);
297         struct support_level_bucket *bucket = &cat->buckets[support_level];
298
299         if (AST_LIST_EMPTY(bucket)) {
300                 struct member *sep = create_separator(support_level);
301                 AST_LIST_INSERT_TAIL(bucket, sep, list);
302         }
303
304         AST_LIST_INSERT_TAIL(bucket, mem, list);
305
306         return 0;
307 }
308
309 static int process_xml_defaultenabled_node(xmlNode *node, struct member *mem)
310 {
311         const char *tmp = (const char *) xmlNodeGetContent(node);
312
313         if (tmp && !strlen_zero(tmp)) {
314                 mem->defaultenabled = tmp;
315         }
316
317         return 0;
318 }
319
320 static int process_xml_supportlevel_node(xmlNode *node, struct member *mem)
321 {
322         const char *tmp = (const char *) xmlNodeGetContent(node);
323
324         if (tmp && !strlen_zero(tmp)) {
325                 xmlFree((void *) mem->support_level);
326                 mem->support_level = tmp;
327                 print_debug("Set support_level for %s to %s\n", mem->name, mem->support_level);
328         }
329
330         return 0;
331 }
332
333 static int process_xml_replacement_node(xmlNode *node, struct member *mem)
334 {
335         const char *tmp = (const char *) xmlNodeGetContent(node);
336
337         if (tmp && !strlen_zero(tmp)) {
338                 mem->replacement = tmp;
339         }
340
341         return 0;
342 }
343
344 static int process_xml_ref_node(xmlNode *node, struct member *mem, struct reference_list *refs)
345 {
346         struct reference *ref;
347         const char *tmp;
348
349         if (!(ref = calloc(1, sizeof(*ref)))) {
350                 free_member(mem);
351                 return -1;
352         }
353
354         if ((tmp = (const char *) xmlGetProp(node, BAD_CAST "name"))) {
355                 if (!strlen_zero(tmp)) {
356                         ref->name = tmp;
357                 }
358         }
359
360         if ((tmp = (const char *) xmlGetProp(node, BAD_CAST "autoselect"))) {
361                 ref->autoselect = !strcasecmp(tmp, "yes");
362         }
363
364         tmp = (const char *) xmlNodeGetContent(node);
365
366         if (tmp && !strlen_zero(tmp)) {
367                 ref->displayname = tmp;
368                 if (!ref->name) {
369                         ref->name = ref->displayname;
370                 }
371
372                 AST_LIST_INSERT_TAIL(refs, ref, list);
373         } else {
374                 free_reference(ref);
375         }
376
377         return 0;
378 }
379
380 static int process_xml_depend_node(xmlNode *node, struct member *mem)
381 {
382         return process_xml_ref_node(node, mem, &mem->deps);
383 }
384
385 static int process_xml_conflict_node(xmlNode *node, struct member *mem)
386 {
387         return process_xml_ref_node(node, mem, &mem->conflicts);
388 }
389
390 static int process_xml_use_node(xmlNode *node, struct member *mem)
391 {
392         return process_xml_ref_node(node, mem, &mem->uses);
393 }
394
395 static int process_xml_member_data_node(xmlNode *node, struct member *mem)
396 {
397         return 0;
398 }
399
400 static int process_xml_unknown_node(xmlNode *node, struct member *mem)
401 {
402         fprintf(stderr, "Encountered unknown node: %s\n", node->name);
403         return 0;
404 }
405
406 typedef int (*node_handler)(xmlNode *, struct member *);
407
408 static const struct {
409         const char *name;
410         node_handler func;
411 } node_handlers[] = {
412         { "defaultenabled", process_xml_defaultenabled_node },
413         { "support_level",  process_xml_supportlevel_node   },
414         { "replacement",    process_xml_replacement_node    },
415         { "depend",         process_xml_depend_node         },
416         { "conflict",       process_xml_conflict_node       },
417         { "use",            process_xml_use_node            },
418         { "member_data",    process_xml_member_data_node    },
419 };
420
421 static node_handler lookup_node_handler(xmlNode *node)
422 {
423         int i;
424
425         for (i = 0; i < ARRAY_LEN(node_handlers); i++) {
426                 if (!strcmp(node_handlers[i].name, (const char *) node->name)) {
427                         return node_handlers[i].func;
428                 }
429         }
430
431         return process_xml_unknown_node;
432 }
433
434 static int process_process_xml_category_child_node(xmlNode *node, struct member *mem)
435 {
436         node_handler handler = lookup_node_handler(node);
437
438         return handler(node, mem);
439 }
440
441 static int process_xml_member_node(xmlNode *node, struct category *cat)
442 {
443         xmlNode *cur;
444         struct member *mem;
445         xmlChar *tmp;
446
447         if (!(mem = calloc(1, sizeof(*mem)))) {
448                 return -1;
449         }
450
451         mem->name = (const char *) xmlGetProp(node, BAD_CAST "name");
452         mem->displayname = (const char *) xmlGetProp(node, BAD_CAST "displayname");
453         mem->touch_on_change = (const char *) xmlGetProp(node, BAD_CAST "touch_on_change");
454         mem->remove_on_change = (const char *) xmlGetProp(node, BAD_CAST "remove_on_change");
455         mem->support_level = (const char *) xmlCharStrdup("unspecified");
456
457         if ((tmp = xmlGetProp(node, BAD_CAST "explicitly_enabled_only"))) {
458                 mem->explicitly_enabled_only = !xmlStrcasecmp(tmp, BAD_CAST "yes");
459                 xmlFree(tmp);
460         }
461
462         for (cur = node->children; cur; cur = cur->next) {
463                 if (cur->type != XML_ELEMENT_NODE) {
464                         continue;
465                 }
466
467                 process_process_xml_category_child_node(cur, mem);
468         }
469
470         if (!cat->positive_output && strcasecmp(mem->support_level, "option")) {
471                 mem->enabled = 1;
472                 if (!mem->defaultenabled || strcasecmp(mem->defaultenabled, "no")) {
473                         mem->was_enabled = 1;
474                         print_debug("Enabled %s because the category does not have positive output\n", mem->name);
475                 }
476         }
477
478         if (add_member_list_order(mem, cat)) {
479                 free_member(mem);
480         }
481
482         return 0;
483 }
484
485 static void free_category(struct category *cat)
486 {
487         struct member *mem;
488
489         xmlFree((void *) cat->name);
490         xmlFree((void *) cat->displayname);
491         xmlFree((void *) cat->remove_on_change);
492         xmlFree((void *) cat->touch_on_change);
493
494         while ((mem = AST_LIST_REMOVE_HEAD(&cat->members, list))) {
495                 free_member(mem);
496         }
497
498         free(cat);
499 }
500
501 static int process_xml_category_node(xmlNode *node)
502 {
503         struct category *cat;
504         const char *tmp;
505         xmlNode *cur;
506
507         if (!(tmp = (const char *) xmlGetProp(node, BAD_CAST "name"))) {
508                 fprintf(stderr, "Missing 'name' attribute for 'category' element.  Skipping...\n");
509                 /* Return success here so we don't bail on the whole document */
510                 return 0;
511         }
512
513         cat = category_find_or_create(tmp);
514
515         if ((tmp = (const char *) xmlGetProp(node, BAD_CAST "displayname"))) {
516                 xmlFree((void *) cat->displayname);
517                 cat->displayname = tmp;
518         }
519
520         if ((tmp = (const char *) xmlGetProp(node, BAD_CAST "remove_on_change"))) {
521                 xmlFree((void *) cat->remove_on_change);
522                 cat->remove_on_change = tmp;
523         }
524
525         if ((tmp = (const char *) xmlGetProp(node, BAD_CAST "touch_on_change"))) {
526                 xmlFree((void *) cat->touch_on_change);
527                 cat->touch_on_change = tmp;
528         }
529
530         if ((tmp = (const char *) xmlGetProp(node, BAD_CAST "positive_output"))) {
531                 cat->positive_output = !strcasecmp(tmp, "yes");
532                 xmlFree((void *) tmp);
533         }
534
535         if ((tmp = (const char *) xmlGetProp(node, BAD_CAST "exclusive"))) {
536                 cat->exclusive = !strcasecmp(tmp, "yes");
537                 xmlFree((void *) tmp);
538         }
539
540         for (cur = node->children; cur; cur = cur->next) {
541                 if (cur->type != XML_ELEMENT_NODE) {
542                         continue;
543                 }
544
545                 if (xmlStrcmp(cur->name, BAD_CAST "member")) {
546                         fprintf(stderr, "Ignoring unknown element: %s\n", cur->name);
547                         continue;
548                 }
549
550                 process_xml_member_node(cur, cat);
551         }
552
553         return 0;
554 }
555
556 static int process_xml_menu_node(struct tree *tree, xmlNode *node)
557 {
558         xmlNode *cur;
559         xmlChar *tmp;
560
561         if (strcmp((const char *) node->name, "menu")) {
562                 fprintf(stderr, "Invalid document: Expected \"menu\" element.\n");
563                 return -1;
564         }
565
566         AST_LIST_INSERT_HEAD(&trees, tree, list);
567
568         if ((tmp = xmlGetProp(node, BAD_CAST "name"))) {
569                 menu_name = (const char *) tmp;
570         }
571
572         for (cur = node->children; cur; cur = cur->next) {
573                 if (cur->type != XML_ELEMENT_NODE) {
574                         continue;
575                 }
576
577                 if (xmlStrcmp(cur->name, BAD_CAST "category")) {
578                         fprintf(stderr, "Ignoring unknown element: %s\n", cur->name);
579                         continue;
580                 }
581
582                 if (process_xml_category_node(cur)) {
583                         return -1;
584                 }
585         }
586
587         categories_flatten();
588
589         return 0;
590 }
591
592 /*! \brief Parse an input makeopts file */
593 static int parse_tree(const char *tree_file)
594 {
595         struct tree *tree;
596         xmlNode *menu;
597
598         if (!(tree = calloc(1, sizeof(*tree)))) {
599                 return -1;
600         }
601
602         if (!(tree->root = xmlParseFile(tree_file))) {
603                 free(tree);
604                 return -1;
605         }
606
607         if (!(menu = xmlDocGetRootElement(tree->root))) {
608                 fprintf(stderr, "Invalid document: No root element\n");
609                 xmlFreeDoc(tree->root);
610                 free(tree);
611                 return -1;
612         }
613
614         if (process_xml_menu_node(tree, menu)) {
615                 xmlFreeDoc(tree->root);
616                 free(tree);
617                 return -1;
618         }
619
620         return 0;
621 }
622
623 /*!
624  * \arg interactive Set to non-zero if being called while user is making changes
625  */
626 static unsigned int calc_dep_failures(int interactive, int pre_confload)
627 {
628         unsigned int result = 0;
629         struct category *cat;
630         struct member *mem;
631         struct reference *dep;
632         struct dep_file *dep_file;
633         unsigned int changed, old_failure;
634
635         AST_LIST_TRAVERSE(&categories, cat, list) {
636                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
637                         if (mem->is_separator) {
638                                 continue;
639                         }
640                         old_failure = mem->depsfailed;
641                         AST_LIST_TRAVERSE(&mem->deps, dep, list) {
642                                 if (dep->member)
643                                         continue;
644
645                                 mem->depsfailed = HARD_FAILURE;
646                                 AST_LIST_TRAVERSE(&deps_file, dep_file, list) {
647                                         if (!strcasecmp(dep_file->name, dep->name)) {
648                                                 if (dep_file->met == DEP_FILE_MET) {
649                                                         mem->depsfailed = NO_FAILURE;
650                                                 }
651                                                 break;
652                                         }
653                                 }
654                                 if (mem->depsfailed != NO_FAILURE) {
655                                         break; /* This dependency is not met, so we can stop now */
656                                 }
657                         }
658                         if (old_failure == SOFT_FAILURE && mem->depsfailed != HARD_FAILURE)
659                                 mem->depsfailed = SOFT_FAILURE;
660                 }
661         }
662
663         if (pre_confload) {
664                 return 0;
665         }
666
667         do {
668                 changed = 0;
669
670                 AST_LIST_TRAVERSE(&categories, cat, list) {
671                         AST_LIST_TRAVERSE(&cat->members, mem, list) {
672                                 if (mem->is_separator) {
673                                         continue;
674                                 }
675
676                                 old_failure = mem->depsfailed;
677
678                                 if (mem->depsfailed == HARD_FAILURE)
679                                         continue;
680
681                                 mem->depsfailed = NO_FAILURE;
682
683                                 AST_LIST_TRAVERSE(&mem->deps, dep, list) {
684                                         if (!dep->member)
685                                                 continue;
686                                         if (dep->member->depsfailed == HARD_FAILURE) {
687                                                 mem->depsfailed = HARD_FAILURE;
688                                                 break;
689                                         } else if (dep->member->depsfailed == SOFT_FAILURE) {
690                                                 mem->depsfailed = SOFT_FAILURE;
691                                         } else if (!dep->member->enabled) {
692                                                 mem->depsfailed = SOFT_FAILURE;
693                                         }
694                                 }
695
696                                 if (mem->depsfailed != old_failure) {
697                                         if ((mem->depsfailed == NO_FAILURE) && mem->was_defaulted) {
698                                                 mem->enabled = !strcasecmp(mem->defaultenabled, "yes");
699                                                 print_debug("Just set %s enabled to %d\n", mem->name, mem->enabled);
700                                         } else {
701                                                 mem->enabled = interactive ? 0 : mem->was_enabled;
702                                                 print_debug("Just set %s enabled to %d\n", mem->name, mem->enabled);
703                                         }
704                                         changed = 1;
705                                         break; /* This dependency is not met, so we can stop now */
706                                 }
707                         }
708                         if (changed)
709                                 break;
710                 }
711
712                 if (changed)
713                         result = 1;
714
715         } while (changed);
716
717         return result;
718 }
719
720 static unsigned int calc_conflict_failures(int interactive, int pre_confload)
721 {
722         unsigned int result = 0;
723         struct category *cat;
724         struct member *mem;
725         struct reference *cnf;
726         struct dep_file *dep_file;
727         unsigned int changed, old_failure;
728
729         AST_LIST_TRAVERSE(&categories, cat, list) {
730                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
731                         if (mem->is_separator) {
732                                 continue;
733                         }
734
735                         old_failure = mem->conflictsfailed;
736                         AST_LIST_TRAVERSE(&mem->conflicts, cnf, list) {
737                                 if (cnf->member)
738                                         continue;
739
740                                 mem->conflictsfailed = NO_FAILURE;
741                                 AST_LIST_TRAVERSE(&deps_file, dep_file, list) {
742                                         if (!strcasecmp(dep_file->name, cnf->name)) {
743                                                 if (dep_file->met == DEP_FILE_MET) {
744                                                         mem->conflictsfailed = HARD_FAILURE;
745                                                         print_debug("Setting %s conflictsfailed to HARD_FAILURE\n", mem->name);
746                                                 }
747                                                 break;
748                                         }
749                                 }
750
751                                 if (mem->conflictsfailed != NO_FAILURE)
752                                         break; /* This conflict was found, so we can stop now */
753                         }
754                         if (old_failure == SOFT_FAILURE && mem->conflictsfailed != HARD_FAILURE) {
755                                 print_debug("%d - Setting %s conflictsfailed to SOFT_FAILURE\n", __LINE__, mem->name);
756                                 mem->conflictsfailed = SOFT_FAILURE;
757                         }
758                 }
759         }
760
761         if (pre_confload) {
762                 return 0;
763         }
764
765         do {
766                 changed = 0;
767
768                 AST_LIST_TRAVERSE(&categories, cat, list) {
769                         AST_LIST_TRAVERSE(&cat->members, mem, list) {
770                                 if (mem->is_separator) {
771                                         continue;
772                                 }
773
774                                 old_failure = mem->conflictsfailed;
775
776                                 if (mem->conflictsfailed == HARD_FAILURE)
777                                         continue;
778
779                                 mem->conflictsfailed = NO_FAILURE;
780
781                                 AST_LIST_TRAVERSE(&mem->conflicts, cnf, list) {
782                                         if (!cnf->member)
783                                                 continue;
784
785                                         if (cnf->member->enabled) {
786                                                 mem->conflictsfailed = SOFT_FAILURE;
787                                                 print_debug("%d - Setting %s conflictsfailed to SOFT_FAILURE because %s is enabled\n", __LINE__, mem->name, cnf->member->name);
788                                                 break;
789                                         }
790                                 }
791
792                                 if (mem->conflictsfailed != old_failure && mem->conflictsfailed != NO_FAILURE) {
793                                         mem->enabled = 0;
794                                         print_debug("Just set %s enabled to %d because of conflicts\n", mem->name, mem->enabled);
795                                         changed = 1;
796                                         break; /* This conflict has been found, so we can stop now */
797                                 }
798                         }
799                         if (changed)
800                                 break;
801                 }
802
803                 if (changed)
804                         result = 1;
805
806         } while (changed);
807
808         return result;
809 }
810
811 /*! \brief Process dependencies against the input dependencies file */
812 static int process_deps(void)
813 {
814         FILE *f;
815         char buf[80];
816         int res = 0;
817         struct dep_file *dep_file;
818
819         if (!(f = fopen(MENUSELECT_DEPS, "r"))) {
820                 fprintf(stderr, "Unable to open '%s' for reading!  Did you run ./configure ?\n", MENUSELECT_DEPS);
821                 return -1;
822         }
823
824         /* Build a dependency list from the file generated by configure */
825         while (memset(buf, 0, sizeof(buf)), fgets(buf, sizeof(buf), f)) {
826                 char *name, *cur, *prev, *p;
827                 int val;
828
829                 /* Strip trailing CR/NL */
830                 while ((p = strchr(buf, '\r')) || (p = strchr(buf, '\n'))) {
831                         *p = '\0';
832                 }
833
834                 p = buf;
835                 name = strsep(&p, "=");
836
837                 if (!p)
838                         continue;
839
840                 cur = strsep(&p, ":");
841                 prev = strsep(&p, ":");
842
843                 if (!(dep_file = calloc(1, sizeof(*dep_file))))
844                         break;
845
846                 strncpy(dep_file->name, name, sizeof(dep_file->name) - 1);
847                 dep_file->met = DEP_FILE_UNKNOWN;
848                 dep_file->previously_met = DEP_FILE_UNKNOWN;
849
850                 if (sscanf(cur, "%d", &val) != 1) {
851                         fprintf(stderr, "Unknown value '%s' found in %s for %s\n", cur, MENUSELECT_DEPS, name);
852                 } else {
853                         switch (val) {
854                         case DEP_FILE_MET:
855                         case DEP_FILE_UNMET:
856                         case DEP_FILE_DISABLED:
857                                 dep_file->met = val;
858                                 break;
859                         default:
860                                 fprintf(stderr, "Unknown value '%s' found in %s for %s\n", cur, MENUSELECT_DEPS, name);
861                                 break;
862                         }
863                 }
864
865                 if (prev) {
866                         if (sscanf(prev, "%d", &val) != 1) {
867                                 fprintf(stderr, "Unknown value '%s' found in %s for %s\n", prev, MENUSELECT_DEPS, name);
868                         } else {
869                                 switch (val) {
870                                 case DEP_FILE_MET:
871                                 case DEP_FILE_UNMET:
872                                 case DEP_FILE_DISABLED:
873                                         dep_file->previously_met = val;
874                                         break;
875                                 default:
876                                         fprintf(stderr, "Unknown value '%s' found in %s for %s\n", prev, MENUSELECT_DEPS, name);
877                                         break;
878                                 }
879                         }
880                 }
881
882                 AST_LIST_INSERT_TAIL(&deps_file, dep_file, list);
883         }
884
885         fclose(f);
886
887         return res;
888 }
889
890 static void free_deps_file(void)
891 {
892         struct dep_file *dep_file;
893
894         /* Free the dependency list we built from the file */
895         while ((dep_file = AST_LIST_REMOVE_HEAD(&deps_file, list)))
896                 free(dep_file);
897 }
898
899 static int match_member_relations(void)
900 {
901         struct category *cat, *cat2;
902         struct member *mem, *mem2;
903         struct reference *dep;
904         struct reference *cnf;
905         struct reference *use;
906
907         /* Traverse through each module's dependency list and determine whether each is another module */
908         AST_LIST_TRAVERSE(&categories, cat, list) {
909                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
910                         if (mem->is_separator) {
911                                 continue;
912                         }
913
914                         AST_LIST_TRAVERSE(&mem->deps, dep, list) {
915                                 AST_LIST_TRAVERSE(&cat->members, mem2, list) {
916                                         if (mem->is_separator) {
917                                                 continue;
918                                         }
919
920                                         if (strcasecmp(mem2->name, dep->name))
921                                                 continue;
922
923                                         dep->member = mem2;
924                                         break;
925                                 }
926                                 if (dep->member)
927                                         continue;
928
929                                 AST_LIST_TRAVERSE(&categories, cat2, list) {
930                                         AST_LIST_TRAVERSE(&cat2->members, mem2, list) {
931                                                 if (mem->is_separator) {
932                                                         continue;
933                                                 }
934
935                                                 if (strcasecmp(mem2->name, dep->name))
936                                                         continue;
937
938                                                 dep->member = mem2;
939                                                 break;
940                                         }
941                                         if (dep->member)
942                                                 break;
943                                 }
944                         }
945                 }
946         }
947
948         /* Traverse through each module's use list and determine whether each is another module */
949         AST_LIST_TRAVERSE(&categories, cat, list) {
950                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
951                         if (mem->is_separator) {
952                                 continue;
953                         }
954
955                         AST_LIST_TRAVERSE(&mem->uses, use, list) {
956                                 AST_LIST_TRAVERSE(&cat->members, mem2, list) {
957                                         if (mem->is_separator) {
958                                                 continue;
959                                         }
960
961                                         if (strcasecmp(mem2->name, use->name))
962                                                 continue;
963
964                                         use->member = mem2;
965                                         break;
966                                 }
967                                 if (use->member)
968                                         continue;
969
970                                 AST_LIST_TRAVERSE(&categories, cat2, list) {
971                                         AST_LIST_TRAVERSE(&cat2->members, mem2, list) {
972                                                 if (mem->is_separator) {
973                                                         continue;
974                                                 }
975
976                                                 if (strcasecmp(mem2->name, use->name))
977                                                         continue;
978
979                                                 use->member = mem2;
980                                                 break;
981                                         }
982                                         if (use->member)
983                                                 break;
984                                 }
985                         }
986                 }
987         }
988
989 /*
990  * BUGBUG:
991  * This doesn't work, the only way we can fix this is to remove OPTIONAL_API
992  * toggle from menuselect and add a command-line argument to ./configure.
993  */
994 #if 0
995         AST_LIST_TRAVERSE(&categories, cat, list) {
996                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
997                         if (mem->is_separator) {
998                                 continue;
999                         }
1000
1001                         AST_LIST_TRAVERSE_SAFE_BEGIN(&mem->uses, use, list) {
1002                                 if (use->member) {
1003                                         AST_LIST_REMOVE_CURRENT(&mem->uses, list);
1004                                         AST_LIST_INSERT_TAIL(&mem->deps, use, list);
1005                                 }
1006                         }
1007                         AST_LIST_TRAVERSE_SAFE_END;
1008                 }
1009         }
1010 #endif
1011
1012         /* Traverse through each category marked as exclusive and mark every member as conflicting with every other member */
1013         AST_LIST_TRAVERSE(&categories, cat, list) {
1014                 if (!cat->exclusive)
1015                         continue;
1016
1017                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1018                         if (mem->is_separator) {
1019                                 continue;
1020                         }
1021
1022                         AST_LIST_TRAVERSE(&cat->members, mem2, list) {
1023                                 if (mem->is_separator) {
1024                                         continue;
1025                                 }
1026
1027                                 if (mem2 == mem)
1028                                         continue;
1029
1030                                 if (!(cnf = calloc(1, sizeof(*cnf))))
1031                                         return -1;
1032
1033                                 cnf->name = mem2->name;
1034                                 cnf->member = mem2;
1035                                 AST_LIST_INSERT_TAIL(&mem->conflicts, cnf, list);
1036                         }
1037                 }
1038         }
1039
1040         /* Traverse through each category and determine whether named conflicts for each module are other modules */
1041         AST_LIST_TRAVERSE(&categories, cat, list) {
1042                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1043                         if (mem->is_separator) {
1044                                 continue;
1045                         }
1046
1047                         AST_LIST_TRAVERSE(&mem->conflicts, cnf, list) {
1048                                 AST_LIST_TRAVERSE(&cat->members, mem2, list) {
1049                                         if (mem->is_separator) {
1050                                                 continue;
1051                                         }
1052
1053                                         if (strcasecmp(mem2->name, cnf->name))
1054                                                 continue;
1055
1056                                         cnf->member = mem2;
1057                                         break;
1058                                 }
1059                                 if (cnf->member)
1060                                         continue;
1061
1062                                 AST_LIST_TRAVERSE(&categories, cat2, list) {
1063                                         AST_LIST_TRAVERSE(&cat2->members, mem2, list) {
1064                                                 if (mem->is_separator) {
1065                                                         continue;
1066                                                 }
1067
1068                                                 if (strcasecmp(mem2->name, cnf->name))
1069                                                         continue;
1070
1071                                                 cnf->member = mem2;
1072                                                 break;
1073                                         }
1074                                         if (cnf->member)
1075                                                 break;
1076                                 }
1077                         }
1078                 }
1079         }
1080
1081         return 0;
1082 }
1083
1084 /*! \brief Iterate through all of the input tree files and call the parse function on them */
1085 static int build_member_list(void)
1086 {
1087         int i;
1088         int res = -1;
1089
1090         for (i = 0; i < (sizeof(tree_files) / sizeof(tree_files[0])); i++) {
1091                 if ((res = parse_tree(tree_files[i]))) {
1092                         fprintf(stderr, "Error parsing '%s'!\n", tree_files[i]);
1093                         break;
1094                 }
1095         }
1096
1097         if (!res)
1098                 res = match_member_relations();
1099
1100         return res;
1101 }
1102
1103 /*! \brief Given the string representation of a member and category, mark it as present in a given input file */
1104 static void mark_as_present(const char *member, const char *category)
1105 {
1106         struct category *cat;
1107         struct member *mem;
1108         char negate = 0;
1109
1110         if (*member == '-') {
1111                 member++;
1112                 negate = 1;
1113         }
1114
1115         print_debug("Marking %s of %s as present\n", member, category);
1116
1117         AST_LIST_TRAVERSE(&categories, cat, list) {
1118                 if (strcmp(category, cat->name))
1119                         continue;
1120                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1121                         if (mem->is_separator) {
1122                                 continue;
1123                         }
1124
1125                         if (!strcmp(member, mem->name)) {
1126                                 mem->was_enabled = mem->enabled = (negate ? !cat->positive_output : cat->positive_output);
1127                                 print_debug("Just set %s enabled to %d\n", mem->name, mem->enabled);
1128                                 break;
1129                         }
1130                 }
1131                 if (!mem)
1132                         fprintf(stderr, "member '%s' in category '%s' not found, ignoring.\n", member, category);
1133                 break;
1134         }
1135
1136         if (!cat)
1137                 fprintf(stderr, "category '%s' not found! Can't mark '%s' as disabled.\n", category, member);
1138 }
1139
1140 unsigned int enable_member(struct member *mem)
1141 {
1142         struct reference *dep;
1143         unsigned int can_enable = 1;
1144
1145         AST_LIST_TRAVERSE(&mem->deps, dep, list) {
1146                 if (!dep->member)
1147                         continue;
1148
1149                 if (!dep->member->enabled) {
1150                         if (dep->member->conflictsfailed != NO_FAILURE) {
1151                                 can_enable = 0;
1152                                 break;
1153                         }
1154
1155                         if (dep->member->depsfailed == HARD_FAILURE) {
1156                                 can_enable = 0;
1157                                 break;
1158                         }
1159
1160                         if (dep->member->explicitly_enabled_only) {
1161                                 can_enable = 0;
1162                                 break;
1163                         }
1164
1165                         if (!(can_enable = enable_member(dep->member)))
1166                                 break;
1167                 }
1168         }
1169
1170         if ((mem->enabled = can_enable)) {
1171                 struct reference *use;
1172
1173                 print_debug("Just set %s enabled to %d\n", mem->name, mem->enabled);
1174                 while (calc_dep_failures(1, 0) || calc_conflict_failures(1, 0));
1175
1176                 AST_LIST_TRAVERSE(&mem->uses, use, list) {
1177                         if (use->member && use->autoselect && !use->member->enabled) {
1178                                 enable_member(use->member);
1179                         }
1180                 }
1181         }
1182
1183         return can_enable;
1184 }
1185
1186 void toggle_enabled(struct member *mem)
1187 {
1188         if ((mem->depsfailed == HARD_FAILURE) || (mem->conflictsfailed == HARD_FAILURE) || (mem->is_separator))
1189                 return;
1190
1191         if (!mem->enabled)
1192                 enable_member(mem);
1193         else
1194                 mem->enabled = 0;
1195
1196         print_debug("3- changed %s to %d\n", mem->name, mem->enabled);
1197         mem->was_defaulted = 0;
1198         changes_made++;
1199
1200         while (calc_dep_failures(1, 0) || calc_conflict_failures(1, 0));
1201 }
1202
1203 /*! \brief Toggle a member of a category at the specified index to enabled/disabled */
1204 void toggle_enabled_index(struct category *cat, int index)
1205 {
1206         struct member *mem;
1207         int i = 0;
1208
1209         AST_LIST_TRAVERSE(&cat->members, mem, list) {
1210                 if (i++ == index)
1211                         break;
1212         }
1213
1214         if (!mem)
1215                 return;
1216
1217         toggle_enabled(mem);
1218 }
1219
1220 static void set_member_enabled(struct member *mem)
1221 {
1222         if ((mem->depsfailed == HARD_FAILURE) || (mem->conflictsfailed == HARD_FAILURE))
1223                 return;
1224
1225         if ((mem->enabled) || (mem->is_separator))
1226                 return;
1227
1228         enable_member(mem);
1229         mem->was_defaulted = 0;
1230         changes_made++;
1231
1232         while (calc_dep_failures(1, 0) || calc_conflict_failures(1, 0));
1233 }
1234
1235 void set_enabled(struct category *cat, int index)
1236 {
1237         struct member *mem;
1238         int i = 0;
1239
1240         AST_LIST_TRAVERSE(&cat->members, mem, list) {
1241                 if (mem->is_separator) {
1242                         continue;
1243                 }
1244
1245                 if (i++ == index)
1246                         break;
1247         }
1248
1249         if (!mem)
1250                 return;
1251
1252         set_member_enabled(mem);
1253 }
1254
1255 static void clear_member_enabled(struct member *mem)
1256 {
1257         if (!mem->enabled)
1258                 return;
1259
1260         mem->enabled = 0;
1261         mem->was_defaulted = 0;
1262         changes_made++;
1263
1264         while (calc_dep_failures(1, 0) || calc_conflict_failures(1, 0));
1265 }
1266
1267 void clear_enabled(struct category *cat, int index)
1268 {
1269         struct member *mem;
1270         int i = 0;
1271
1272         AST_LIST_TRAVERSE(&cat->members, mem, list) {
1273                 if (mem->is_separator) {
1274                         continue;
1275                 }
1276
1277                 if (i++ == index)
1278                         break;
1279         }
1280
1281         if (!mem)
1282                 return;
1283
1284         clear_member_enabled(mem);
1285 }
1286
1287 /*! \brief Process a previously failed dependency
1288  *
1289  * If a module was previously disabled because of a failed dependency
1290  * or a conflict, and not because the user selected it to be that way,
1291  * then it needs to be re-enabled by default if the problem is no longer present.
1292  */
1293 static void process_prev_failed_deps(char *buf)
1294 {
1295         const char *cat_name, *mem_name;
1296         struct category *cat;
1297         struct member *mem;
1298
1299         cat_name = strsep(&buf, "=");
1300         mem_name = strsep(&buf, "\n");
1301
1302         if (!cat_name || !mem_name)
1303                 return;
1304
1305         AST_LIST_TRAVERSE(&categories, cat, list) {
1306                 if (strcasecmp(cat->name, cat_name))
1307                         continue;
1308                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1309                         if (mem->is_separator) {
1310                                 continue;
1311                         }
1312
1313                         if (strcasecmp(mem->name, mem_name))
1314                                 continue;
1315
1316                         if (!mem->depsfailed && !mem->conflictsfailed) {
1317                                 mem->enabled = 1;
1318                                 print_debug("Just set %s enabled to %d in processing of previously failed deps\n", mem->name, mem->enabled);
1319                                 mem->was_defaulted = 0;
1320                         }
1321
1322                         break;
1323                 }
1324                 break;
1325         }
1326
1327         if (!cat || !mem)
1328                 fprintf(stderr, "Unable to find '%s' in category '%s'\n", mem_name, cat_name);
1329 }
1330
1331 /*! \brief Parse an existing output makeopts file and enable members previously selected */
1332 static int parse_existing_config(const char *infile)
1333 {
1334         FILE *f;
1335 #define PARSE_BUF_SIZE 8192
1336         char *buf = NULL;
1337         char *category, *parse, *member;
1338         int lineno = 0;
1339
1340         if (!(f = fopen(infile, "r"))) {
1341                 /* This isn't really an error, so only print the message in debug mode */
1342                 print_debug("Unable to open '%s' for reading existing config.\n", infile);
1343                 return -1;
1344         }
1345
1346         buf = malloc(PARSE_BUF_SIZE);
1347         if (!buf) {
1348                 fprintf(stderr, "Unable to allocate buffer for reading existing config.\n");
1349                 exit(1);
1350         }
1351
1352         while (fgets(buf, PARSE_BUF_SIZE, f)) {
1353                 lineno++;
1354
1355                 if (strlen_zero(buf))
1356                         continue;
1357
1358                 /* skip lines that are not for this tool */
1359                 if (strncasecmp(buf, "MENUSELECT_", strlen("MENUSELECT_")))
1360                         continue;
1361
1362                 if (!strncasecmp(buf, "MENUSELECT_DEPENDS_", strlen("MENUSELECT_DEPENDS_")))
1363                         continue;
1364
1365                 if (!strncasecmp(buf, "MENUSELECT_BUILD_DEPS", strlen("MENUSELECT_BUILD_DEPS")))
1366                         continue;
1367
1368                 parse = buf;
1369                 parse = skip_blanks(parse);
1370                 if (strlen_zero(parse))
1371                         continue;
1372
1373                 /* Grab the category name */
1374                 category = strsep(&parse, "=");
1375                 if (!parse) {
1376                         fprintf(stderr, "Invalid string in '%s' at line '%d'!\n", output_makeopts, lineno);
1377                         continue;
1378                 }
1379
1380                 parse = skip_blanks(parse);
1381
1382                 if (!strcasecmp(category, "MENUSELECT_DEPSFAILED")) {
1383                         process_prev_failed_deps(parse);
1384                         continue;
1385                 }
1386
1387                 while ((member = strsep(&parse, " \n"))) {
1388                         member = skip_blanks(member);
1389                         if (strlen_zero(member))
1390                                 continue;
1391                         mark_as_present(member, category);
1392                 }
1393         }
1394
1395         free(buf);
1396         fclose(f);
1397
1398         return 0;
1399 }
1400
1401 /*! \brief Create the output dependencies file */
1402 static int generate_makedeps_file(void)
1403 {
1404         FILE *f;
1405         struct category *cat;
1406         struct member *mem;
1407         struct reference *dep;
1408         struct reference *use;
1409         struct dep_file *dep_file;
1410
1411         if (!(f = fopen(output_makedeps, "w"))) {
1412                 fprintf(stderr, "Unable to open dependencies file (%s) for writing!\n", output_makedeps);
1413                 return -1;
1414         }
1415
1416         /* Traverse all categories and members and mark which used packages were found,
1417          * skipping other members
1418          */
1419         AST_LIST_TRAVERSE(&categories, cat, list) {
1420                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1421                         if (mem->is_separator) {
1422                                 continue;
1423                         }
1424
1425                         AST_LIST_TRAVERSE(&mem->uses, use, list) {
1426                                 if (use->member) {
1427                                         use->met = 0;
1428                                         continue;
1429                                 }
1430                                 AST_LIST_TRAVERSE(&deps_file, dep_file, list) {
1431                                         if ((use->met = !strcasecmp(use->name, dep_file->name))) {
1432                                                 break;
1433                                         }
1434                                 }
1435                         }
1436                 }
1437         }
1438
1439         /* Traverse all categories and members and output dependencies for each member */
1440         AST_LIST_TRAVERSE(&categories, cat, list) {
1441                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1442                         if (mem->is_separator) {
1443                                 continue;
1444                         }
1445
1446                         unsigned char header_printed = 0;
1447
1448                         if (AST_LIST_EMPTY(&mem->deps) && AST_LIST_EMPTY(&mem->uses))
1449                                 continue;
1450
1451                         AST_LIST_TRAVERSE(&mem->deps, dep, list) {
1452                                 const char *c;
1453
1454                                 if (dep->member) {
1455                                         continue;
1456                                 }
1457
1458                                 if (!header_printed) {
1459                                         fprintf(f, "MENUSELECT_DEPENDS_%s=", mem->name);
1460                                         header_printed = 1;
1461                                 }
1462
1463                                 for (c = dep->name; *c; c++)
1464                                         fputc(toupper(*c), f);
1465                                 fputc(' ', f);
1466                         }
1467                         AST_LIST_TRAVERSE(&mem->uses, use, list) {
1468                                 const char *c;
1469
1470                                 if (!use->met) {
1471                                         continue;
1472                                 }
1473
1474                                 if (!header_printed) {
1475                                         fprintf(f, "MENUSELECT_DEPENDS_%s=", mem->name);
1476                                         header_printed = 1;
1477                                 }
1478
1479                                 for (c = use->name; *c; c++)
1480                                         fputc(toupper(*c), f);
1481                                 fputc(' ', f);
1482                         }
1483
1484                         if (header_printed) {
1485                                 fprintf(f, "\n");
1486                         }
1487                 }
1488         }
1489
1490         fclose(f);
1491
1492         return 0;
1493 }
1494
1495 /*! \brief Create the output makeopts file that results from the user's selections */
1496 static int generate_makeopts_file(void)
1497 {
1498         FILE *f;
1499         struct category *cat;
1500         struct member *mem;
1501         struct reference *dep;
1502         struct reference *use;
1503
1504         if (!(f = fopen(output_makeopts, "w"))) {
1505                 fprintf(stderr, "Unable to open build configuration file (%s) for writing!\n", output_makeopts);
1506                 return -1;
1507         }
1508
1509         /* Traverse all categories and members and output them as var/val pairs */
1510         AST_LIST_TRAVERSE(&categories, cat, list) {
1511                 fprintf(f, "%s=", cat->name);
1512                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1513                         if (mem->is_separator) {
1514                                 continue;
1515                         }
1516
1517                         if ((!cat->positive_output && (!mem->enabled || mem->depsfailed || mem->conflictsfailed)) ||
1518                             (cat->positive_output && mem->enabled && !mem->depsfailed && !mem->conflictsfailed))
1519                                 fprintf(f, "%s ", mem->name);
1520                 }
1521                 fprintf(f, "\n");
1522         }
1523
1524         /* Traverse all categories and members, and for every member that is not disabled,
1525            if it has internal dependencies (other members), list those members one time only
1526            in a special variable */
1527         fprintf(f, "MENUSELECT_BUILD_DEPS=");
1528         AST_LIST_TRAVERSE(&categories, cat, list) {
1529                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1530                         if (mem->is_separator) {
1531                                 continue;
1532                         }
1533
1534                         if ((!cat->positive_output && (!mem->enabled || mem->depsfailed || mem->conflictsfailed)) ||
1535                             (cat->positive_output && mem->enabled && !mem->depsfailed && !mem->conflictsfailed))
1536                                 continue;
1537
1538                         AST_LIST_TRAVERSE(&mem->deps, dep, list) {
1539                                 /* we only care about dependencies between members (internal, not external) */
1540                                 if (!dep->member)
1541                                         continue;
1542                                 /* if this has already been output, continue */
1543                                 if (dep->member->build_deps_output)
1544                                         continue;
1545                                 fprintf(f, "%s ", dep->member->name);
1546                                 dep->member->build_deps_output = 1;
1547                         }
1548                         AST_LIST_TRAVERSE(&mem->uses, use, list) {
1549                                 /* we only care about dependencies between members (internal, not external) */
1550                                 if (!use->member)
1551                                         continue;
1552                                 /* if the dependency module is not going to be built, don't list it */
1553                                 if (!use->member->enabled)
1554                                         continue;
1555                                 /* if this has already been output, continue */
1556                                 if (use->member->build_deps_output)
1557                                         continue;
1558                                 fprintf(f, "%s ", use->member->name);
1559                                 use->member->build_deps_output = 1;
1560                         }
1561                 }
1562         }
1563         fprintf(f, "\n");
1564
1565         /* Output which members were disabled because of failed dependencies or conflicts */
1566         AST_LIST_TRAVERSE(&categories, cat, list) {
1567                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1568                         if (mem->is_separator) {
1569                                 continue;
1570                         }
1571
1572                         if (mem->depsfailed != HARD_FAILURE && mem->conflictsfailed != HARD_FAILURE)
1573                                 continue;
1574
1575                         if (!mem->defaultenabled || !strcasecmp(mem->defaultenabled, "yes"))
1576                                 fprintf(f, "MENUSELECT_DEPSFAILED=%s=%s\n", cat->name, mem->name);
1577                 }
1578         }
1579
1580         fclose(f);
1581
1582         /* there is no need to process remove_on_change rules if we did not have
1583            configuration information to start from
1584         */
1585         if (!existing_config)
1586                 return 0;
1587
1588         /* Traverse all categories and members and remove any files that are supposed
1589            to be removed when an item has been changed */
1590         AST_LIST_TRAVERSE(&categories, cat, list) {
1591                 unsigned int had_changes = 0;
1592                 char rmcommand[256] = "rm -rf ";
1593                 char touchcommand[256] = "touch -c ";
1594                 char *file, *buf;
1595
1596                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1597                         if (mem->is_separator) {
1598                                 continue;
1599                         }
1600
1601                         if ((mem->enabled == mem->was_enabled) && !mem->was_defaulted)
1602                                 continue;
1603
1604                         had_changes = 1;
1605
1606                         if (mem->touch_on_change) {
1607                                 for (buf = ast_strdupa(mem->touch_on_change), file = strsep(&buf, " ");
1608                                      file;
1609                                      file = strsep(&buf, " ")) {
1610                                         strcpy(&touchcommand[9], file);
1611                                         system(touchcommand);
1612                                 }
1613                         }
1614
1615                         if (mem->remove_on_change) {
1616                                 for (buf = ast_strdupa(mem->remove_on_change), file = strsep(&buf, " ");
1617                                      file;
1618                                      file = strsep(&buf, " ")) {
1619                                         strcpy(&rmcommand[7], file);
1620                                         system(rmcommand);
1621                                 }
1622                         }
1623                 }
1624
1625                 if (cat->touch_on_change && had_changes) {
1626                         for (buf = ast_strdupa(cat->touch_on_change), file = strsep(&buf, " ");
1627                              file;
1628                              file = strsep(&buf, " ")) {
1629                                 strcpy(&touchcommand[9], file);
1630                                 system(touchcommand);
1631                         }
1632                 }
1633
1634                 if (cat->remove_on_change && had_changes) {
1635                         for (buf = ast_strdupa(cat->remove_on_change), file = strsep(&buf, " ");
1636                              file;
1637                              file = strsep(&buf, " ")) {
1638                                 strcpy(&rmcommand[7], file);
1639                                 system(rmcommand);
1640                         }
1641                 }
1642         }
1643
1644         return 0;
1645 }
1646
1647 /*! \brief Print out all of the information contained in our tree */
1648 static void dump_member_list(void)
1649 {
1650 #ifdef MENUSELECT_DEBUG
1651         struct category *cat;
1652         struct member *mem;
1653         struct reference *dep;
1654         struct reference *cnf;
1655
1656         AST_LIST_TRAVERSE(&categories, cat, list) {
1657                 fprintf(stderr, "Category: '%s'\n", cat->name);
1658                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1659                         if (mem->is_separator) {
1660                                 continue;
1661                         }
1662
1663                         fprintf(stderr, "   ==>> Member: '%s'  (%s)", mem->name, mem->enabled ? "Enabled" : "Disabled");
1664                         fprintf(stderr, "        Was %s\n", mem->was_enabled ? "Enabled" : "Disabled");
1665                         if (mem->defaultenabled)
1666                                 fprintf(stderr, "        Defaults to %s\n", !strcasecmp(mem->defaultenabled, "yes") ? "Enabled" : "Disabled");
1667                         AST_LIST_TRAVERSE(&mem->deps, dep, list)
1668                                 fprintf(stderr, "      --> Depends on: '%s'\n", dep->name);
1669                         if (!AST_LIST_EMPTY(&mem->deps))
1670                                 fprintf(stderr, "      --> Dependencies Met: %s\n", mem->depsfailed ? "No" : "Yes");
1671                         AST_LIST_TRAVERSE(&mem->conflicts, cnf, list)
1672                                 fprintf(stderr, "      --> Conflicts with: '%s'\n", cnf->name);
1673                         if (!AST_LIST_EMPTY(&mem->conflicts))
1674                                 fprintf(stderr, "      --> Conflicts Found: %s\n", mem->conflictsfailed ? "Yes" : "No");
1675                 }
1676         }
1677 #endif
1678 }
1679
1680 /*! \brief Free all categories and their members */
1681 static void free_member_list(void)
1682 {
1683         struct category *cat;
1684
1685         while ((cat = AST_LIST_REMOVE_HEAD(&categories, list))) {
1686                 free_category(cat);
1687         }
1688 }
1689
1690 /*! \brief Free all of the XML trees */
1691 static void free_trees(void)
1692 {
1693         struct tree *tree;
1694
1695         while ((tree = AST_LIST_REMOVE_HEAD(&trees, list))) {
1696                 xmlFreeDoc(tree->root);
1697                 free(tree);
1698         }
1699 }
1700
1701 /*! \brief Enable/Disable all members of a category as long as dependencies have been met and no conflicts are found */
1702 void set_all(struct category *cat, int val)
1703 {
1704         struct member *mem;
1705
1706         AST_LIST_TRAVERSE(&cat->members, mem, list) {
1707                 if (mem->enabled == val)
1708                         continue;
1709
1710                 if (mem->is_separator)
1711                         continue;
1712
1713                 if ((mem->depsfailed == HARD_FAILURE) || (mem->conflictsfailed == HARD_FAILURE))
1714                         continue;
1715
1716                 if (val) {
1717                         enable_member(mem);
1718                 } else {
1719                         mem->enabled = 0;
1720                 }
1721
1722                 mem->was_defaulted = 0;
1723                 changes_made++;
1724         }
1725
1726         while (calc_dep_failures(1, 0) || calc_conflict_failures(1, 0));
1727 }
1728
1729 int count_categories(void)
1730 {
1731         struct category *cat;
1732         int count = 0;
1733
1734         AST_LIST_TRAVERSE(&categories, cat, list)
1735                 count++;
1736
1737         return count;
1738 }
1739
1740 int count_members(struct category *cat)
1741 {
1742         struct member *mem;
1743         int count = 0;
1744
1745         AST_LIST_TRAVERSE(&cat->members, mem, list)
1746                 count++;
1747
1748         return count;
1749 }
1750
1751 static void print_sanity_dep_header(struct dep_file *dep_file, unsigned int *flag)
1752 {
1753         fprintf(stderr, "\n"
1754                 "***********************************************************\n"
1755                 "  The '%s' dependency was previously satisfied but         \n"
1756                 "  is now unsatisfied.                                      \n",
1757                 dep_file->name);
1758         *flag = 1;
1759 }
1760
1761 /*! \brief Make sure an existing menuselect.makeopts disabled everything it should have */
1762 static int sanity_check(void)
1763 {
1764         unsigned int insane = 0;
1765         struct category *cat;
1766         struct member *mem;
1767         struct reference *dep;
1768         struct reference *use;
1769         struct dep_file *dep_file;
1770         unsigned int dep_header_printed;
1771         unsigned int group_header_printed;
1772
1773         AST_LIST_TRAVERSE(&deps_file, dep_file, list) {
1774                 if (!((dep_file->previously_met == DEP_FILE_MET) &&
1775                       (dep_file->met == DEP_FILE_UNMET))) {
1776                         continue;
1777                 }
1778
1779                 /* this dependency was previously met, but now is not, so
1780                    warn the user about members that could be affected by it
1781                 */
1782
1783                 dep_header_printed = 0;
1784
1785                 group_header_printed = 0;
1786                 AST_LIST_TRAVERSE(&categories, cat, list) {
1787                         AST_LIST_TRAVERSE(&cat->members, mem, list) {
1788                                 if (mem->is_separator) {
1789                                         continue;
1790                                 }
1791
1792                                 if (!mem->enabled) {
1793                                         continue;
1794                                 }
1795                                 AST_LIST_TRAVERSE(&mem->deps, dep, list) {
1796                                         if (strcasecmp(dep->name, dep_file->name)) {
1797                                                 continue;
1798                                         }
1799                                         if (!group_header_printed) {
1800                                                 if (!dep_header_printed) {
1801                                                         print_sanity_dep_header(dep_file, &dep_header_printed);
1802                                                 }
1803                                                 fprintf(stderr, "\n"
1804                                                         "  The following modules will no longer be available:\n");
1805                                                 group_header_printed = 1;
1806                                         }
1807                                         fprintf(stderr, "          %s\n", mem->name);
1808                                         insane = 1;
1809                                 }
1810                         }
1811                 }
1812
1813                 group_header_printed = 0;
1814                 AST_LIST_TRAVERSE(&categories, cat, list) {
1815                         AST_LIST_TRAVERSE(&cat->members, mem, list) {
1816                                 if (mem->is_separator) {
1817                                         continue;
1818                                 }
1819
1820                                 if (!mem->enabled) {
1821                                         continue;
1822                                 }
1823                                 AST_LIST_TRAVERSE(&mem->uses, use, list) {
1824                                         if (strcasecmp(use->name, dep_file->name)) {
1825                                                 continue;
1826                                         }
1827                                         if (!group_header_printed) {
1828                                                 if (!dep_header_printed) {
1829                                                         print_sanity_dep_header(dep_file, &dep_header_printed);
1830                                                 }
1831                                                 fprintf(stderr, "\n"
1832                                                         "  The functionality of the following modules will\n"
1833                                                         "  be affected:\n");
1834                                                 group_header_printed = 1;
1835                                         }
1836                                         fprintf(stderr, "          %s\n", mem->name);
1837                                         insane = 1;
1838                                 }
1839                         }
1840                 }
1841
1842                 if (dep_header_printed) {
1843                         fprintf(stderr,
1844                                 "***********************************************************\n");
1845                 }
1846         }
1847
1848         AST_LIST_TRAVERSE(&categories, cat, list) {
1849                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1850                         if (mem->is_separator) {
1851                                 continue;
1852                         }
1853
1854                         if ((mem->depsfailed || mem->conflictsfailed) && mem->enabled) {
1855                                 fprintf(stderr, "\n"
1856                                         "***********************************************************\n"
1857                                         "  The existing menuselect.makeopts file did not specify    \n"
1858                                         "  that '%s' should not be included.  However, either some  \n"
1859                                         "  dependencies for this module were not found or a         \n"
1860                                         "  conflict exists.                                         \n"
1861                                         "                                                           \n"
1862                                         "  Either run 'make menuselect' or remove the existing      \n"
1863                                         "  menuselect.makeopts file to resolve this issue.          \n"
1864                                         "***********************************************************\n"
1865                                         "\n", mem->name);
1866                                 insane = 1;
1867                         }
1868                 }
1869         }
1870
1871         return insane ? -1 : 0;
1872 }
1873
1874 /* \brief Set the forced default values if they exist */
1875 static void process_defaults(void)
1876 {
1877         struct category *cat;
1878         struct member *mem;
1879
1880         print_debug("Processing default values since config was not present\n");
1881
1882         AST_LIST_TRAVERSE(&categories, cat, list) {
1883                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1884                         if (mem->is_separator) {
1885                                 continue;
1886                         }
1887
1888                         if (!mem->defaultenabled)
1889                                 continue;
1890
1891                         if (mem->depsfailed == HARD_FAILURE)
1892                                 continue;
1893
1894                         if (mem->conflictsfailed == HARD_FAILURE)
1895                                 continue;
1896
1897                         if (!strcasecmp(mem->defaultenabled, "yes")) {
1898                                 mem->enabled = 1;
1899                                 mem->was_defaulted = 1;
1900                         } else if (!strcasecmp(mem->defaultenabled, "no")) {
1901                                 mem->enabled = 0;
1902                                 mem->was_defaulted = 1;
1903                         } else
1904                                 fprintf(stderr, "Invalid defaultenabled value for '%s' in category '%s'\n", mem->name, cat->name);
1905                 }
1906         }
1907
1908 }
1909
1910 struct member *find_member(const char *name)
1911 {
1912         struct category *cat;
1913         struct member *mem;
1914
1915         AST_LIST_TRAVERSE(&categories, cat, list) {
1916                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
1917                         if (mem->is_separator) {
1918                                 continue;
1919                         }
1920
1921                         if (!strcasecmp(name, mem->name)) {
1922                                 return mem;
1923                         }
1924                 }
1925         }
1926
1927         return NULL;
1928 }
1929
1930 struct category *find_category(const char *name)
1931 {
1932         struct category *cat;
1933
1934         AST_LIST_TRAVERSE(&categories, cat, list) {
1935                 if (!strcasecmp(name, cat->name)) {
1936                         return cat;
1937                 }
1938         }
1939
1940         return NULL;
1941 }
1942
1943 static int usage(const char *argv0)
1944 {
1945         fprintf(stderr, "Usage: %s [--enable <option>] [--disable <option>]\n", argv0);
1946         fprintf(stderr, "   [--enable-category <category>] [--enable-all]\n");
1947         fprintf(stderr, "   [--disable-category <category>] [--disable-all] [...]\n");
1948         fprintf(stderr, "   [<config-file> [...]]\n");
1949         fprintf(stderr, "Usage: %s { --check-deps | --list-options\n", argv0);
1950         fprintf(stderr, "   | --list-category <category> | --category-list | --help }\n");
1951         fprintf(stderr, "   [<config-file> [...]]\n");
1952         return 0;
1953 }
1954
1955 int main(int argc, char *argv[])
1956 {
1957         int res = 0;
1958         const char *list_group = NULL;
1959         unsigned int x;
1960         static struct option long_options[] = {
1961                 /*
1962                  * The --check-deps option is used to ask this application to check to
1963                  * see if that an existing menuselect.makeopts file contains all of the
1964                  * modules that have dependencies that have not been met.  If this
1965                  * is not the case, an informative message will be printed to the
1966                  * user and the build will fail.
1967                  */
1968                 { "check-deps",       no_argument,       &check_deps,   1  },
1969                 { "enable",           required_argument, 0,            'e' },
1970                 { "enable-category",  required_argument, 0,            'E' },
1971                 { "enable-all",       no_argument,       0,            'a' },
1972                 { "disable",          required_argument, 0,            'd' },
1973                 { "disable-category", required_argument, 0,            'D' },
1974                 { "disable-all",      no_argument,       0,            'A' },
1975                 { "list-options",     no_argument,       &list_options, 1  },
1976                 { "list-category",    required_argument, 0,            'L' },
1977                 { "category-list",    no_argument,       &list_groups,  1  },
1978                 { "help",             no_argument,       0,            'h' },
1979
1980                 { 0, 0, 0, 0 },
1981         };
1982         int do_menu = 1, do_settings = 1;
1983         int c, option_index = 0;
1984
1985         if (open_debug()) {
1986                 exit(1);
1987         }
1988
1989         LIBXML_TEST_VERSION;
1990
1991         /* Parse the input XML files to build the list of available options */
1992         if ((res = build_member_list()))
1993                 exit(res);
1994
1995         /* Load module dependencies */
1996         if ((res = process_deps()))
1997                 exit(res);
1998
1999         while (calc_dep_failures(0, 1) || calc_conflict_failures(0, 1));
2000
2001         while ((c = getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
2002                 switch (c) {
2003                 case 'L':
2004                         list_group = optarg;
2005                         do_settings = 0;
2006                         /* Fall-through */
2007                 case 'a':
2008                 case 'A':
2009                 case 'e':
2010                 case 'E':
2011                 case 'd':
2012                 case 'D':
2013                         do_menu = 0;
2014                         break;
2015                 case 'h':
2016                         return usage(argv[0]);
2017                         break;
2018                 default:
2019                         break;
2020                 }
2021         }
2022
2023         if (check_deps || list_options || list_groups) {
2024                 do_menu = 0;
2025                 do_settings = 0;
2026         }
2027
2028         if (optind < argc) {
2029                 for (x = optind; x < argc; x++) {
2030                         res = parse_existing_config(argv[x]);
2031                         if (!res && !strcasecmp(argv[x], OUTPUT_MAKEOPTS_DEFAULT))
2032                                 existing_config = 1;
2033                         res = 0;
2034                 }
2035         }
2036
2037         /* Dump the list produced by parsing the various input files */
2038         dump_member_list();
2039
2040         while (calc_dep_failures(0, 0) || calc_conflict_failures(0, 0));
2041
2042         if (!existing_config)
2043                 process_defaults();
2044         else if (check_deps)
2045                 res = sanity_check();
2046
2047         while (calc_dep_failures(0, 0) || calc_conflict_failures(0, 0));
2048
2049         print_debug("do_menu=%d, do_settings=%d\n", do_menu, do_settings);
2050
2051         if (do_menu && !res) {
2052                 res = run_menu();
2053         } else if (!do_settings) {
2054                 if (list_groups) {
2055                         struct category *cat;
2056                         AST_LIST_TRAVERSE(&categories, cat, list) {
2057                                 fprintf(stdout, "%s\n", cat->name);
2058                         }
2059                 } else if (list_options) {
2060                         struct category *cat;
2061                         struct member *mem;
2062                         AST_LIST_TRAVERSE(&categories, cat, list) {
2063                                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
2064                                         if (mem->is_separator) {
2065                                                 continue;
2066                                         }
2067
2068                                         fprintf(stdout, "%c %-30.30s %s\n", mem->enabled ? '+' : '-', mem->name, cat->name);
2069                                 }
2070                         }
2071                 } else if (!strlen_zero(list_group)) {
2072                         struct category *cat;
2073                         struct member *mem;
2074                         if ((cat = find_category(list_group))) {
2075                                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
2076                                         if (mem->is_separator) {
2077                                                 continue;
2078                                         }
2079
2080                                         fprintf(stdout, "%c %s\n", mem->enabled ? '+' : '-', mem->name);
2081                                 }
2082                         }
2083                 }
2084         } else if (!do_menu && do_settings) {
2085                 struct member *mem;
2086                 struct category *cat;
2087
2088                 print_debug("Doing settings with argc=%d\n", argc);
2089
2090                 /* Reset options processing */
2091                 option_index = 0;
2092                 optind = 1;
2093
2094                 while ((c = getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
2095                         print_debug("Got option %c\n", c);
2096                         switch (c) {
2097                         case 'e':
2098                                 if (!strlen_zero(optarg)) {
2099                                         if ((mem = find_member(optarg))) {
2100                                                 set_member_enabled(mem);
2101                                         } else {
2102                                                 fprintf(stderr, "'%s' not found\n", optarg);
2103                                         }
2104                                 }
2105                                 break;
2106                         case 'E':
2107                                 if (!strlen_zero(optarg)) {
2108                                         if ((cat = find_category(optarg))) {
2109                                                 set_all(cat, 1);
2110                                         } else {
2111                                                 fprintf(stderr, "'%s' not found\n", optarg);
2112                                         }
2113                                 }
2114                                 break;
2115                         case 'a': /* enable-all */
2116                                 AST_LIST_TRAVERSE(&categories, cat, list) {
2117                                         set_all(cat, 1);
2118                                 }
2119                                 break;
2120                         case 'd':
2121                                 if (!strlen_zero(optarg)) {
2122                                         if ((mem = find_member(optarg))) {
2123                                                 clear_member_enabled(mem);
2124                                         } else {
2125                                                 fprintf(stderr, "'%s' not found\n", optarg);
2126                                         }
2127                                 }
2128                                 break;
2129                         case 'D':
2130                                 if (!strlen_zero(optarg)) {
2131                                         if ((cat = find_category(optarg))) {
2132                                                 set_all(cat, 0);
2133                                         } else {
2134                                                 fprintf(stderr, "'%s' not found\n", optarg);
2135                                         }
2136                                 }
2137                                 break;
2138                         case 'A': /* disable-all */
2139                                 AST_LIST_TRAVERSE(&categories, cat, list) {
2140                                         set_all(cat, 0);
2141                                 }
2142                                 break;
2143                         case '?':
2144                                 break;
2145                         default:
2146                                 break;
2147                         }
2148                 }
2149                 res = 0;
2150         }
2151
2152         if (!res) {
2153                 res = generate_makeopts_file();
2154         }
2155
2156         /* Always generate the dependencies file */
2157         if (!res) {
2158                 generate_makedeps_file();
2159         }
2160
2161         /* free everything we allocated */
2162         free_deps_file();
2163         free_trees();
2164         free_member_list();
2165
2166         close_debug();
2167
2168         xmlCleanupParser();
2169
2170         exit(res);
2171 }