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