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