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