ensure menuselect fails if the deps file isn't found
[asterisk/asterisk.git] / build_tools / menuselect.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005 - 2006, Russell Bryant
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 "asterisk.h"
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include "mxml/mxml.h"
35 #include "menuselect.h"
36
37 #include "asterisk/linkedlists.h"
38
39 #undef MENUSELECT_DEBUG
40
41 /*! The list of categories */
42 struct categories categories = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
43
44 /*!
45    We have to maintain a pointer to the root of the trees generated from reading
46    the build options XML files so that we can free it when we're done.  We don't
47    copy any of the information over from these trees. Our list is just a 
48    convenient mapping to the information contained in these lists with one
49    additional piece of information - whether the build option is enabled or not.
50 */
51 struct tree {
52         /*! the root of the tree */
53         mxml_node_t *root;
54         /*! for linking */
55         AST_LIST_ENTRY(tree) list;
56 };
57
58 /*! The list of trees from makeopts.xml files */
59 static AST_LIST_HEAD_NOLOCK_STATIC(trees, tree);
60
61 static const char * const makeopts_files[] = {
62         "makeopts.xml"
63 };
64
65 static char *output_makeopts = OUTPUT_MAKEOPTS_DEFAULT;
66
67 /*! This is set to 1 if menuselect.makeopts pre-existed the execution of this app */
68 static int existing_config = 0;
69
70 /*! This is set when the --check-deps argument is provided. */
71 static int check_deps = 0;
72
73 #if !defined(ast_strdupa) && defined(__GNUC__)
74 #define ast_strdupa(s)                                                    \
75         (__extension__                                                    \
76         ({                                                                \
77                 const char *__old = (s);                                  \
78                 size_t __len = strlen(__old) + 1;                         \
79                 char *__new = __builtin_alloca(__len);                    \
80                 memcpy (__new, __old, __len);                             \
81                 __new;                                                    \
82         }))
83 #endif
84
85 /*! \brief return a pointer to the first non-whitespace character */
86 static inline char *skip_blanks(char *str)
87 {
88         if (!str)
89                 return NULL;
90
91         while (*str && *str < 33)
92                 str++;
93
94         return str;
95 }
96
97 /*! \brief Add a category to the category list, ensuring that there are no duplicates */
98 static int add_category(struct category *cat)
99 {
100         struct category *tmp;
101
102         AST_LIST_TRAVERSE(&categories, tmp, list) {
103                 if (!strcmp(tmp->name, cat->name)) {
104                         fprintf(stderr, "Category '%s' specified more than once!\n", cat->name);
105                         return -1;
106                 }
107         }
108         AST_LIST_INSERT_TAIL(&categories, cat, list);
109
110         return 0;
111 }
112
113 /*! \brief Add a member to the member list of a category, ensuring that there are no duplicates */
114 static int add_member(struct member *mem, struct category *cat)
115 {
116         struct member *tmp;
117
118         AST_LIST_TRAVERSE(&cat->members, tmp, list) {
119                 if (!strcmp(tmp->name, mem->name)) {
120                         fprintf(stderr, "Member '%s' already exists in category '%s', ignoring.\n", mem->name, cat->name);
121                         return -1;
122                 }
123         }
124         AST_LIST_INSERT_TAIL(&cat->members, mem, list);
125
126         return 0;
127 }
128
129 /*! \brief Free a member structure and all of its members */
130 static void free_member(struct member *mem)
131 {
132         struct depend *dep;
133         struct conflict *cnf;
134
135         while ((dep = AST_LIST_REMOVE_HEAD(&mem->deps, list)))
136                 free(dep);
137         while ((cnf = AST_LIST_REMOVE_HEAD(&mem->conflicts, list)))
138                 free(cnf);
139         free(mem);
140 }
141
142 /*! \brief Parse an input makeopts file */
143 static int parse_makeopts_xml(const char *makeopts_xml)
144 {
145         FILE *f;
146         struct category *cat;
147         struct tree *tree;
148         struct member *mem;
149         struct depend *dep;
150         struct conflict *cnf;
151         mxml_node_t *cur;
152         mxml_node_t *cur2;
153         mxml_node_t *cur3;
154         mxml_node_t *menu;
155         const char *tmp;
156
157         if (!(f = fopen(makeopts_xml, "r"))) {
158                 fprintf(stderr, "Unable to open '%s' for reading!\n", makeopts_xml);
159                 return -1;
160         }
161
162         if (!(tree = calloc(1, sizeof(*tree)))) {
163                 fclose(f);
164                 return -1;
165         }
166
167         if (!(tree->root = mxmlLoadFile(NULL, f, MXML_OPAQUE_CALLBACK))) {
168                 fclose(f);
169                 free(tree);
170                 return -1;
171         }
172
173         AST_LIST_INSERT_HEAD(&trees, tree, list);
174
175         menu = mxmlFindElement(tree->root, tree->root, "menu", NULL, NULL, MXML_DESCEND);
176         for (cur = mxmlFindElement(menu, menu, "category", NULL, NULL, MXML_DESCEND);
177              cur;
178              cur = mxmlFindElement(cur, menu, "category", NULL, NULL, MXML_DESCEND))
179         {
180                 if (!(cat = calloc(1, sizeof(*cat))))
181                         return -1;
182
183                 cat->name = mxmlElementGetAttr(cur, "name");
184                 cat->displayname = mxmlElementGetAttr(cur, "displayname");
185                 if ((tmp = mxmlElementGetAttr(cur, "positive_output")))
186                         cat->positive_output = !strcasecmp(tmp, "yes");
187                 cat->remove_on_change = mxmlElementGetAttr(cur, "remove_on_change");
188
189                 if (add_category(cat)) {
190                         free(cat);
191                         continue;
192                 }
193
194                 for (cur2 = mxmlFindElement(cur, cur, "member", NULL, NULL, MXML_DESCEND);
195                      cur2;
196                      cur2 = mxmlFindElement(cur2, cur, "member", NULL, NULL, MXML_DESCEND))
197                 {
198                         if (!(mem = calloc(1, sizeof(*mem))))
199                                 return -1;
200                         
201                         mem->name = mxmlElementGetAttr(cur2, "name");
202                         mem->displayname = mxmlElementGetAttr(cur2, "displayname");
203                 
204                         mem->remove_on_change = mxmlElementGetAttr(cur2, "remove_on_change");
205
206                         if (!cat->positive_output)
207                                 mem->was_enabled = mem->enabled = 1;
208         
209                         cur3 = mxmlFindElement(cur2, cur2, "defaultenabled", NULL, NULL, MXML_DESCEND);
210                         if (cur3 && cur3->child)
211                                 mem->defaultenabled = cur3->child->value.opaque;
212                         
213                         for (cur3 = mxmlFindElement(cur2, cur2, "depend", NULL, NULL, MXML_DESCEND);
214                              cur3 && cur3->child;
215                              cur3 = mxmlFindElement(cur3, cur2, "depend", NULL, NULL, MXML_DESCEND))
216                         {
217                                 if (!(dep = calloc(1, sizeof(*dep)))) {
218                                         free_member(mem);
219                                         return -1;
220                                 }
221                                 if (!strlen_zero(cur3->child->value.opaque)) {
222                                         dep->name = cur3->child->value.opaque;
223                                         AST_LIST_INSERT_HEAD(&mem->deps, dep, list);
224                                 } else
225                                         free(dep);
226                         }
227
228                         for (cur3 = mxmlFindElement(cur2, cur2, "conflict", NULL, NULL, MXML_DESCEND);
229                              cur3 && cur3->child;
230                              cur3 = mxmlFindElement(cur3, cur2, "conflict", NULL, NULL, MXML_DESCEND))
231                         {
232                                 if (!(cnf = calloc(1, sizeof(*cnf)))) {
233                                         free_member(mem);
234                                         return -1;
235                                 }
236                                 if (!strlen_zero(cur3->child->value.opaque)) {
237                                         cnf->name = cur3->child->value.opaque;
238                                         AST_LIST_INSERT_HEAD(&mem->conflicts, cnf, list);
239                                 } else
240                                         free(cnf);
241                         }
242
243                         if (add_member(mem, cat))
244                                 free_member(mem);
245                 }
246         }
247
248         fclose(f);
249
250         return 0;
251 }
252
253 /*! \brief Process dependencies against the input dependencies file */
254 static int process_deps(void)
255 {
256         struct category *cat;
257         struct member *mem;
258         struct depend *dep;
259         struct conflict *cnf;
260         FILE *f;
261         struct dep_file {
262                 char name[32];
263                 int met;
264                 AST_LIST_ENTRY(dep_file) list;
265         } *dep_file;
266         AST_LIST_HEAD_NOLOCK_STATIC(deps_file, dep_file);
267         char buf[80];
268         char *p;
269         int res = 0;
270
271         if (!(f = fopen(MENUSELECT_DEPS, "r"))) {
272                 fprintf(stderr, "Unable to open '%s' for reading!  Did you run ./configure ?\n", MENUSELECT_DEPS);
273                 return -1;
274         }
275
276         /* Build a dependency list from the file generated by configure */      
277         while (memset(buf, 0, sizeof(buf)), fgets(buf, sizeof(buf), f)) {
278                 p = buf;
279                 strsep(&p, "=");
280                 if (!p)
281                         continue;
282                 if (!(dep_file = calloc(1, sizeof(*dep_file))))
283                         break;
284                 strncpy(dep_file->name, buf, sizeof(dep_file->name) - 1);
285                 dep_file->met = atoi(p);
286                 AST_LIST_INSERT_TAIL(&deps_file, dep_file, list);
287         }
288
289         fclose(f);
290
291         /* Process dependencies of all modules */
292         AST_LIST_TRAVERSE(&categories, cat, list) {
293                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
294                         AST_LIST_TRAVERSE(&mem->deps, dep, list) {
295                                 mem->depsfailed = 1;
296                                 AST_LIST_TRAVERSE(&deps_file, dep_file, list) {
297                                         if (!strcasecmp(dep_file->name, dep->name)) {
298                                                 if (dep_file->met)
299                                                         mem->depsfailed = 0;
300                                                 break;
301                                         }
302                                 }
303                                 if (mem->depsfailed)
304                                         break; /* This dependency is not met, so we can stop now */
305                         }
306                 }
307         }
308
309         /* Process conflicts of all modules */
310         AST_LIST_TRAVERSE(&categories, cat, list) {
311                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
312                         AST_LIST_TRAVERSE(&mem->conflicts, cnf, list) {
313                                 mem->conflictsfailed = 0;
314                                 AST_LIST_TRAVERSE(&deps_file, dep_file, list) {
315                                         if (!strcasecmp(dep_file->name, cnf->name)) {
316                                                 if (dep_file->met)
317                                                         mem->conflictsfailed = 1;
318                                                 break;
319                                         }
320                                 }
321                                 if (mem->conflictsfailed)
322                                         break; /* This conflict was found, so we can stop now */
323                         }
324                 }
325         }
326
327         /* Free the dependency list we built from the file */
328         while ((dep_file = AST_LIST_REMOVE_HEAD(&deps_file, list)))
329                 free(dep_file);
330
331         return res;
332 }
333
334 /*! \brief Iterate through all of the input makeopts files and call the parse function on them */
335 static int build_member_list(void)
336 {
337         int i;
338         int res = -1;
339
340         for (i = 0; i < (sizeof(makeopts_files) / sizeof(makeopts_files[0])); i++) {
341                 if ((res = parse_makeopts_xml(makeopts_files[i]))) {
342                         fprintf(stderr, "Error parsing '%s'!\n", makeopts_files[i]);
343                         break;
344                 }
345         }
346
347         return res;
348 }
349
350 /*! \brief Given the string representation of a member and category, mark it as present in a given input file */
351 static void mark_as_present(const char *member, const char *category)
352 {
353         struct category *cat;
354         struct member *mem;
355
356         AST_LIST_TRAVERSE(&categories, cat, list) {
357                 if (strcmp(category, cat->name))
358                         continue;
359                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
360                         if (!strcmp(member, mem->name)) {
361                                 mem->was_enabled = mem->enabled = cat->positive_output;
362                                 break;
363                         }
364                 }
365                 if (!mem)
366                         fprintf(stderr, "member '%s' in category '%s' not found, ignoring.\n", member, category);
367                 break;
368         }
369
370         if (!cat)
371                 fprintf(stderr, "category '%s' not found! Can't mark '%s' as disabled.\n", category, member);
372 }
373
374 /*! \brief Toggle a member of a category at the specified index to enabled/disabled */
375 void toggle_enabled(struct category *cat, int index)
376 {
377         struct member *mem;
378         int i = 0;
379
380         AST_LIST_TRAVERSE(&cat->members, mem, list) {
381                 if (i++ == index)
382                         break;
383         }
384
385         if (mem && !(mem->depsfailed || mem->conflictsfailed)) {
386                 mem->enabled = !mem->enabled;
387         }
388 }
389
390 /*! \brief Process a previously failed dependency
391  *
392  * If a module was previously disabled because of a failed dependency
393  * or a conflict, and not because the user selected it to be that way,
394  * then it needs to be re-enabled by default if the problem is no longer present.
395  */
396 static void process_prev_failed_deps(char *buf)
397 {
398         const char *cat_name, *mem_name;
399         struct category *cat;
400         struct member *mem;
401
402         cat_name = strsep(&buf, "=");
403         mem_name = strsep(&buf, "\n");
404
405         if (!cat_name || !mem_name)
406                 return;
407
408         AST_LIST_TRAVERSE(&categories, cat, list) {
409                 if (strcasecmp(cat->name, cat_name))
410                         continue;
411                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
412                         if (strcasecmp(mem->name, mem_name))
413                                 continue;
414
415                         if (!mem->depsfailed && !mem->conflictsfailed)
416                                 mem->enabled = 1;                       
417         
418                         break;
419                 }
420                 break;  
421         }
422
423         if (!cat || !mem)
424                 fprintf(stderr, "Unable to find '%s' in category '%s'\n", mem_name, cat_name);
425 }
426
427 /*! \brief Parse an existing output makeopts file and enable members previously selected */
428 static int parse_existing_config(const char *infile)
429 {
430         FILE *f;
431         char buf[2048];
432         char *category, *parse, *member;
433         int lineno = 0;
434
435         if (!(f = fopen(infile, "r"))) {
436 #ifdef MENUSELECT_DEBUG
437                 /* This isn't really an error, so only print the message in debug mode */
438                 fprintf(stderr, "Unable to open '%s' for reading existing config.\n", infile);
439 #endif  
440                 return -1;
441         }
442
443         while (fgets(buf, sizeof(buf), f)) {
444                 lineno++;
445
446                 if (strlen_zero(buf))
447                         continue;
448
449                 /* skip lines that are not for this tool */
450                 if (strncasecmp(buf, "MENUSELECT_", strlen("MENUSELECT_")))
451                         continue;
452
453                 parse = buf;
454                 parse = skip_blanks(parse);
455                 if (strlen_zero(parse))
456                         continue;
457
458                 /* Grab the category name */    
459                 category = strsep(&parse, "=");
460                 if (!parse) {
461                         fprintf(stderr, "Invalid string in '%s' at line '%d'!\n", output_makeopts, lineno);
462                         continue;
463                 }
464                 
465                 parse = skip_blanks(parse);
466         
467                 if (!strcasecmp(category, "MENUSELECT_DEPSFAILED")) {
468                         process_prev_failed_deps(parse);
469                         continue;
470                 }
471         
472                 while ((member = strsep(&parse, " \n"))) {
473                         member = skip_blanks(member);
474                         if (strlen_zero(member))
475                                 continue;
476                         mark_as_present(member, category);
477                 }
478         }
479
480         fclose(f);
481
482         return 0;
483 }
484
485 /*! \brief Create the output makeopts file that results from the user's selections */
486 static int generate_makeopts_file(void)
487 {
488         FILE *f;
489         struct category *cat;
490         struct member *mem;
491
492         if (!(f = fopen(output_makeopts, "w"))) {
493                 fprintf(stderr, "Unable to open build configuration file (%s) for writing!\n", output_makeopts);
494                 return -1;
495         }
496
497         /* Traverse all categories and members and output them as var/val pairs */
498         AST_LIST_TRAVERSE(&categories, cat, list) {
499                 fprintf(f, "%s=", cat->name);
500                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
501                         if ((!cat->positive_output && (!mem->enabled || mem->depsfailed || mem->conflictsfailed)) ||
502                             (cat->positive_output && mem->enabled && !mem->depsfailed && !mem->conflictsfailed))
503                                 fprintf(f, "%s ", mem->name);
504                 }
505                 fprintf(f, "\n");
506         }
507
508         /* Output which members were disabled because of failed dependencies or conflicts */
509         AST_LIST_TRAVERSE(&categories, cat, list) {
510                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
511                         if (mem->depsfailed || mem->conflictsfailed)
512                                 fprintf(f, "MENUSELECT_DEPSFAILED=%s=%s\n", cat->name, mem->name);
513                 }
514         }
515
516         fclose(f);
517
518         /* Traverse all categories and members and remove any files that are supposed
519            to be removed when an item has been changed */
520         AST_LIST_TRAVERSE(&categories, cat, list) {
521                 unsigned int had_changes = 0;
522                 char *file, *buf;
523
524                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
525                         if (mem->enabled == mem->was_enabled)
526                                 continue;
527
528                         had_changes = 1;
529
530                         if (mem->remove_on_change) {
531                                 for (buf = ast_strdupa(mem->remove_on_change), file = strsep(&buf, " ");
532                                      file;
533                                      file = strsep(&buf, " "))
534                                         unlink(file);
535                         }
536                 }
537
538                 if (cat->remove_on_change && had_changes) {
539                         for (buf = ast_strdupa(cat->remove_on_change), file = strsep(&buf, " ");
540                              file;
541                              file = strsep(&buf, " "))
542                                 unlink(file);
543                 }
544         }
545
546         return 0;
547 }
548
549 #ifdef MENUSELECT_DEBUG
550 /*! \brief Print out all of the information contained in our tree */
551 static void dump_member_list(void)
552 {
553         struct category *cat;
554         struct member *mem;
555         struct depend *dep;
556         struct conflict *cnf;
557
558         AST_LIST_TRAVERSE(&categories, cat, list) {
559                 fprintf(stderr, "Category: '%s'\n", cat->name);
560                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
561                         fprintf(stderr, "   ==>> Member: '%s'  (%s)", mem->name, mem->enabled ? "Enabled" : "Disabled");
562                         fprintf(stderr, "        Was %s\n", mem->was_enabled ? "Enabled" : "Disabled");
563                         AST_LIST_TRAVERSE(&mem->deps, dep, list)
564                                 fprintf(stderr, "      --> Depends on: '%s'\n", dep->name);
565                         if (!AST_LIST_EMPTY(&mem->deps))
566                                 fprintf(stderr, "      --> Dependencies Met: %s\n", mem->depsfailed ? "No" : "Yes");    
567                         AST_LIST_TRAVERSE(&mem->conflicts, cnf, list)
568                                 fprintf(stderr, "      --> Conflicts with: '%s'\n", cnf->name);
569                         if (!AST_LIST_EMPTY(&mem->conflicts))
570                                 fprintf(stderr, "      --> Conflicts Found: %s\n", mem->conflictsfailed ? "Yes" : "No");
571                 }
572         }
573 }
574 #endif
575
576 /*! \brief Free all categories and their members */
577 static void free_member_list(void)
578 {
579         struct category *cat;
580         struct member *mem;
581         struct depend *dep;
582         struct conflict *cnf;
583
584         while ((cat = AST_LIST_REMOVE_HEAD(&categories, list))) {
585                 while ((mem = AST_LIST_REMOVE_HEAD(&cat->members, list))) {
586                         while ((dep = AST_LIST_REMOVE_HEAD(&mem->deps, list)))
587                                 free(dep);
588                         while ((cnf = AST_LIST_REMOVE_HEAD(&mem->conflicts, list)))
589                                 free(cnf);
590                         free(mem);
591                 }
592                 free(cat);
593         }
594 }
595
596 /*! \brief Free all of the XML trees */
597 static void free_trees(void)
598 {
599         struct tree *tree;
600
601         while ((tree = AST_LIST_REMOVE_HEAD(&trees, list))) {
602                 mxmlDelete(tree->root);
603                 free(tree);
604         }
605 }
606
607 /*! \brief Enable/Disable all members of a category as long as dependencies have been met and no conflicts are found */
608 void set_all(struct category *cat, int val)
609 {
610         struct member *mem;
611
612         AST_LIST_TRAVERSE(&cat->members, mem, list) {
613                 if (!(mem->depsfailed || mem->conflictsfailed))
614                         mem->enabled = val;
615         }
616 }
617
618 int count_categories(void)
619 {
620         struct category *cat;
621         int count = 0;
622
623         AST_LIST_TRAVERSE(&categories, cat, list)
624                 count++;
625
626         return count;           
627 }
628
629 int count_members(struct category *cat)
630 {
631         struct member *mem;
632         int count = 0;
633
634         AST_LIST_TRAVERSE(&cat->members, mem, list)
635                 count++;
636
637         return count;           
638 }
639
640 /*! \brief Make sure an existing menuselect.makeopts disabled everything it should have */
641 static int sanity_check(void)
642 {
643         struct category *cat;
644         struct member *mem;
645
646         AST_LIST_TRAVERSE(&categories, cat, list) {
647                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
648                         if ((mem->depsfailed || mem->conflictsfailed) && mem->enabled) {
649                                 fprintf(stderr, "\n***********************************************************\n"
650                                                 "  The existing menuselect.makeopts file did not specify    \n"
651                                                 "  that '%s' should not be included.  However, either some  \n"
652                                                 "  dependencies for this module were not found or a         \n"
653                                                 "  conflict exists.                                         \n"
654                                                 "                                                           \n"
655                                                 "  Either run 'make menuselect' or remove the existing      \n"
656                                                 "  menuselect.makeopts file to resolve this issue.          \n"
657                                                 "***********************************************************\n\n", mem->name);
658                                 return -1;
659                         }
660                 }
661         }
662         return 0;       /* all good... */
663 }
664
665 /* \brief Set the forced default values if they exist */
666 static void process_defaults(void)
667 {
668         struct category *cat;
669         struct member *mem;
670
671         AST_LIST_TRAVERSE(&categories, cat, list) {
672                 AST_LIST_TRAVERSE(&cat->members, mem, list) {
673                         if (!mem->defaultenabled)
674                                 continue;
675                         
676                         if (!strcasecmp(mem->defaultenabled, "yes"))
677                                 mem->enabled = 1;
678                         else if (!strcasecmp(mem->defaultenabled, "no"))
679                                 mem->enabled = 0;
680                         else
681                                 fprintf(stderr, "Invalid defaultenabled value for '%s' in category '%s'\n", mem->name, cat->name);      
682                 }
683         }
684
685 }
686
687 int main(int argc, char *argv[])
688 {
689         int res = 0;
690         unsigned int x;
691
692         /* Parse the input XML files to build the list of available options */
693         if ((res = build_member_list()))
694                 exit(res);
695         
696         /* Process module dependencies */
697         if ((res = process_deps()))
698                 exit(res);
699         
700         /* The --check-deps option is used to ask this application to check to
701          * see if that an existing menuselect.makeopts file contails all of the
702          * modules that have dependencies that have not been met.  If this
703          * is not the case, an informative message will be printed to the
704          * user and the build will fail. */
705         for (x = 1; x < argc; x++) {
706                 if (!strcmp(argv[x], "--check-deps"))
707                         check_deps = 1;
708                 else {
709                         res = parse_existing_config(argv[x]);
710                         if (!res && !strcasecmp(argv[x], OUTPUT_MAKEOPTS_DEFAULT))
711                                 existing_config = 1;
712                         res = 0;
713                 }
714         }
715
716 #ifdef MENUSELECT_DEBUG
717         /* Dump the list produced by parsing the various input files */
718         dump_member_list();
719 #endif
720
721         if (!existing_config)
722                 process_defaults();
723         else if (check_deps)
724                 res = sanity_check();
725
726         /* Run the menu to let the user enable/disable options */
727         if (!check_deps && !res)
728                 res = run_menu();
729
730         /* Write out the menuselect.makeopts file if
731          * 1) menuselect was not executed with --check-deps
732          * 2) menuselect was executed with --check-deps but menuselect.makeopts
733          *    did not already exist.
734          */
735         if ((!check_deps || !existing_config) && !res)
736                 res = generate_makeopts_file();
737         
738         /* free everything we allocated */
739         free_trees();
740         free_member_list();
741
742         exit(res);
743 }