80ad01cc6e742a1441c80150d6698d94fdea6b0a
[ncurses.git] / test / demo_menus.c
1 /****************************************************************************
2  * Copyright (c) 2005-2006,2007 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /*
29  * $Id: demo_menus.c,v 1.21 2007/07/21 17:45:09 tom Exp $
30  *
31  * Demonstrate a variety of functions from the menu library.
32  * Thomas Dickey - 2005/4/9
33  */
34 /*
35 item_description                -
36 item_init                       -
37 item_opts                       -
38 item_opts_off                   -
39 item_opts_on                    -
40 item_term                       -
41 item_userptr                    -
42 item_visible                    -
43 menu_back                       -
44 menu_fore                       -
45 menu_format                     -
46 menu_grey                       -
47 menu_init                       -
48 menu_opts                       -
49 menu_pad                        -
50 menu_request_by_name            -
51 menu_request_name               -
52 menu_sub                        -
53 menu_term                       -
54 menu_userptr                    -
55 set_current_item                -
56 set_item_init                   -
57 set_item_opts                   -
58 set_item_term                   -
59 set_item_userptr                -
60 set_menu_grey                   -
61 set_menu_init                   -
62 set_menu_items                  -
63 set_menu_opts                   -
64 set_menu_pad                    -
65 set_menu_pattern                -
66 set_menu_spacing                -
67 set_menu_term                   -
68 set_menu_userptr                -
69 set_top_row                     -
70 top_row                         -
71 */
72
73 #include <test.priv.h>
74
75 #if USE_LIBMENU
76
77 #include <menu.h>
78
79 #include <sys/types.h>
80 #include <sys/stat.h>
81
82 #ifdef NCURSES_VERSION
83 #ifdef TRACE
84 static unsigned save_trace = TRACE_ORDINARY | TRACE_CALLS;
85 extern unsigned _nc_tracing;
86 static MENU *mpTrace;
87 #endif
88 #else
89 #undef TRACE
90 #endif
91
92 typedef enum {
93     eBanner = -1
94     ,eFile
95     ,eSelect
96 #ifdef TRACE
97     ,eTrace
98 #endif
99     ,eMAX
100 } MenuNo;
101
102 #define okMenuNo(n) (((n) > eBanner) && ((n) < eMAX))
103
104 #define MENU_Y  1
105
106 static MENU *mpBanner;
107 static MENU *mpFile;
108 static MENU *mpSelect;
109
110 #if !HAVE_STRDUP
111 #define strdup my_strdup
112 static char *
113 strdup(char *s)
114 {
115     char *p = (char *) malloc(strlen(s) + 1);
116     if (p)
117         strcpy(p, s);
118     return (p);
119 }
120 #endif /* not HAVE_STRDUP */
121
122 /* Common function to allow ^T to toggle trace-mode in the middle of a test
123  * so that trace-files can be made smaller.
124  */
125 static int
126 wGetchar(WINDOW *win)
127 {
128     int c;
129 #ifdef TRACE
130     while ((c = wgetch(win)) == CTRL('T')) {
131         if (_nc_tracing) {
132             save_trace = _nc_tracing;
133             _tracef("TOGGLE-TRACING OFF");
134             _nc_tracing = 0;
135         } else {
136             _nc_tracing = save_trace;
137         }
138         trace(_nc_tracing);
139         if (_nc_tracing)
140             _tracef("TOGGLE-TRACING ON");
141     }
142 #else
143     c = wgetch(win);
144 #endif
145     return c;
146 }
147 #define Getchar() wGetchar(stdscr)
148
149 static int
150 menu_virtualize(int c)
151 {
152     int result;
153
154     if (c == '\n' || c == KEY_EXIT)
155         result = (MAX_COMMAND + 1);
156     else if (c == 'u')
157         result = (REQ_SCR_ULINE);
158     else if (c == 'd')
159         result = (REQ_SCR_DLINE);
160     else if (c == 'b' || c == KEY_NPAGE)
161         result = (REQ_SCR_UPAGE);
162     else if (c == 'f' || c == KEY_PPAGE)
163         result = (REQ_SCR_DPAGE);
164     else if (c == 'l' || c == KEY_LEFT || c == KEY_BTAB)
165         result = (REQ_LEFT_ITEM);
166     else if (c == 'n' || c == KEY_DOWN)
167         result = (REQ_NEXT_ITEM);
168     else if (c == 'p' || c == KEY_UP)
169         result = (REQ_PREV_ITEM);
170     else if (c == 'r' || c == KEY_RIGHT || c == '\t')
171         result = (REQ_RIGHT_ITEM);
172     else if (c == ' ')
173         result = (REQ_TOGGLE_ITEM);
174     else {
175         if (c != KEY_MOUSE)
176             beep();
177         result = (c);
178     }
179     return result;
180 }
181
182 static int
183 menu_getc(MENU * m)
184 {
185     return wGetchar(menu_win(m));
186 }
187
188 static int
189 menu_offset(MenuNo number)
190 {
191     int result = 0;
192
193     if (okMenuNo(number)) {
194         int spc_desc, spc_rows, spc_cols;
195
196 #ifdef NCURSES_VERSION
197         menu_spacing(mpBanner, &spc_desc, &spc_rows, &spc_cols);
198 #else
199         spc_rows = 0;
200 #endif
201
202         /* FIXME: MENU.itemlen seems the only way to get actual width of items */
203         result = (number - (eBanner + 1)) * (mpBanner->itemlen + spc_rows);
204     }
205     return result;
206 }
207
208 static MENU *
209 menu_create(ITEM ** items, int count, int ncols, MenuNo number)
210 {
211     MENU *result;
212     WINDOW *menuwin;
213     int mrows, mcols;
214     int y = okMenuNo(number) ? MENU_Y : 0;
215     int x = menu_offset(number);
216     int margin = (y == MENU_Y) ? 1 : 0;
217     int maxcol = (ncols + x) < COLS ? ncols : (COLS - x - 1);
218     int maxrow = (count + 1) / ncols;
219
220     if ((maxrow + y) >= (LINES - 4))
221         maxrow = LINES - 4 - y;
222
223     result = new_menu(items);
224
225     if (has_colors()) {
226         set_menu_fore(result, COLOR_PAIR(1));
227         set_menu_back(result, COLOR_PAIR(2));
228     }
229
230     set_menu_format(result, maxrow, maxcol);
231     scale_menu(result, &mrows, &mcols);
232
233     if (mcols + (2 * margin + x) >= COLS)
234         mcols = COLS - (2 * margin + x);
235
236 #ifdef TRACE
237     if (number == eTrace)
238         menu_opts_off(result, O_ONEVALUE);
239     else
240         menu_opts_on(result, O_ONEVALUE);
241 #endif
242
243     menuwin = newwin(mrows + (2 * margin), mcols + (2 * margin), y, x);
244     set_menu_win(result, menuwin);
245     keypad(menuwin, TRUE);
246     if (margin)
247         box(menuwin, 0, 0);
248
249     set_menu_sub(result, derwin(menuwin, mrows, mcols, margin, margin));
250
251     post_menu(result);
252
253     return result;
254 }
255
256 static void
257 menu_destroy(MENU * m)
258 {
259     ITEM **ip;
260     int count;
261
262     if (m != 0) {
263         delwin(menu_sub(m));
264         delwin(menu_win(m));
265
266         ip = menu_items(m);
267         count = item_count(m);
268
269         free_menu(m);
270 #if 0
271         if (count > 0) {
272             while (*ip) {
273                 _tracef("freeing item %d:%d", ip - menu_items(m), count);
274                 free_item(*ip++);
275             }
276         }
277 #endif
278     }
279 }
280
281 /* force the given menu to appear */
282 static void
283 menu_display(MENU * m)
284 {
285     touchwin(menu_win(m));
286     wrefresh(menu_win(m));
287 }
288
289 /*****************************************************************************/
290
291 static void
292 build_file_menu(MenuNo number)
293 {
294     static const char *labels[] =
295     {
296         "Exit",
297         (char *) 0
298     };
299     static ITEM *items[SIZEOF(labels)];
300
301     ITEM **ip = items;
302     const char **ap;
303
304     for (ap = labels; *ap; ap++)
305         *ip++ = new_item(*ap, "");
306     *ip = (ITEM *) 0;
307
308     mpFile = menu_create(items, SIZEOF(labels) - 1, 1, number);
309 }
310
311 static int
312 perform_file_menu(int cmd)
313 {
314     return menu_driver(mpFile, cmd);
315 }
316
317 /*****************************************************************************/
318
319 static void
320 build_select_menu(MenuNo number, char *filename)
321 {
322     static const char *labels[] =
323     {
324         "Lions",
325         "Tigers",
326         "Bears",
327         "(Oh my!)",
328         "Newts",
329         "Platypi",
330         "Lemurs",
331         "(Oh really?!)",
332         "Leopards",
333         "Panthers",
334         "Pumas",
335         "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
336         "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
337         (char *) 0
338     };
339     static ITEM **items;
340
341     ITEM **ip;
342     const char **ap = 0;
343     unsigned count = 0;
344
345     if (filename != 0) {
346         struct stat sb;
347         if (stat(filename, &sb) == 0
348             && (sb.st_mode & S_IFMT) == S_IFREG
349             && sb.st_size != 0) {
350             unsigned size = sb.st_size;
351             unsigned j, k;
352             char *blob = malloc(size + 1);
353             const char **list = (const char **) calloc(sizeof(*list), size + 1);
354
355             items = (ITEM **) calloc(sizeof(ITEM *), size + 1);
356             if (blob != 0 && list != 0) {
357                 FILE *fp = fopen(filename, "r");
358                 if (fp != 0) {
359                     if (fread(blob, sizeof(char), size, fp) == size) {
360                         bool mark = TRUE;
361                         for (j = k = 0; j < size; ++j) {
362                             if (mark) {
363                                 list[k++] = blob + j;
364                                 mark = FALSE;
365                             }
366                             if (blob[j] == '\n') {
367                                 blob[j] = '\0';
368                                 if (k > 0 && *list[k - 1] == '\0')
369                                     --k;
370                                 mark = TRUE;
371                             } else if (blob[j] == '\t') {
372                                 blob[j] = ' ';  /* menu items are printable */
373                             }
374                         }
375                         list[k] = 0;
376                         count = k;
377                         ap = list;
378                     }
379                     fclose(fp);
380                 }
381             }
382         }
383     }
384     if (ap == 0) {
385         count = SIZEOF(labels) - 1;
386         items = (ITEM **) calloc(count + 1, sizeof(*items));
387         ap = labels;
388     }
389
390     ip = items;
391     while (*ap != 0)
392         *ip++ = new_item(*ap++, "");
393     *ip = 0;
394
395     mpSelect = menu_create(items, (int) count, 1, number);
396 }
397
398 static int
399 perform_select_menu(int cmd)
400 {
401     return menu_driver(mpSelect, cmd);
402 }
403
404 /*****************************************************************************/
405
406 #ifdef TRACE
407 #define T_TBL(name) { #name, name }
408 static struct {
409     const char *name;
410     unsigned mask;
411 } t_tbl[] = {
412
413     T_TBL(TRACE_DISABLE),
414         T_TBL(TRACE_TIMES),
415         T_TBL(TRACE_TPUTS),
416         T_TBL(TRACE_UPDATE),
417         T_TBL(TRACE_MOVE),
418         T_TBL(TRACE_CHARPUT),
419         T_TBL(TRACE_ORDINARY),
420         T_TBL(TRACE_CALLS),
421         T_TBL(TRACE_VIRTPUT),
422         T_TBL(TRACE_IEVENT),
423         T_TBL(TRACE_BITS),
424         T_TBL(TRACE_ICALLS),
425         T_TBL(TRACE_CCALLS),
426         T_TBL(TRACE_DATABASE),
427         T_TBL(TRACE_ATTRS),
428         T_TBL(TRACE_MAXIMUM),
429     {
430         (char *) 0, 0
431     }
432 };
433
434 static void
435 build_trace_menu(MenuNo number)
436 {
437     static ITEM *items[SIZEOF(t_tbl)];
438
439     ITEM **ip = items;
440     int n;
441
442     for (n = 0; t_tbl[n].name != 0; n++)
443         *ip++ = new_item(t_tbl[n].name, "");
444     *ip = (ITEM *) 0;
445
446     mpTrace = menu_create(items, SIZEOF(t_tbl) - 1, 2, number);
447 }
448
449 static char *
450 tracetrace(unsigned tlevel)
451 {
452     static char *buf;
453     int n;
454
455     if (buf == 0) {
456         size_t need = 12;
457         for (n = 0; t_tbl[n].name != 0; n++)
458             need += strlen(t_tbl[n].name) + 2;
459         buf = (char *) malloc(need);
460     }
461     sprintf(buf, "0x%02x = {", tlevel);
462     if (tlevel == 0) {
463         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
464     } else {
465         for (n = 1; t_tbl[n].name != 0; n++)
466             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
467                 strcat(buf, t_tbl[n].name);
468                 strcat(buf, ", ");
469             }
470     }
471     if (buf[strlen(buf) - 2] == ',')
472         buf[strlen(buf) - 2] = '\0';
473     return (strcat(buf, "}"));
474 }
475
476 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
477  * the others
478  */
479 static bool
480 update_trace_menu(MENU * m)
481 {
482     ITEM **items;
483     ITEM *i, **p;
484     bool changed = FALSE;
485
486     items = menu_items(m);
487     i = current_item(m);
488     if (i == items[0]) {
489         if (item_value(i)) {
490             for (p = items + 1; *p != 0; p++)
491                 if (item_value(*p)) {
492                     set_item_value(*p, FALSE);
493                     changed = TRUE;
494                 }
495         }
496     }
497     return changed;
498 }
499
500 static int
501 perform_trace_menu(int cmd)
502 /* interactively set the trace level */
503 {
504     ITEM **ip;
505     unsigned newtrace;
506     int result;
507
508     for (ip = menu_items(mpTrace); *ip; ip++) {
509         unsigned mask = t_tbl[item_index(*ip)].mask;
510         if (mask == 0)
511             set_item_value(*ip, _nc_tracing == 0);
512         else if ((mask & _nc_tracing) == mask)
513             set_item_value(*ip, TRUE);
514     }
515
516     result = menu_driver(mpTrace, cmd);
517
518     if (result == E_OK) {
519         if (update_trace_menu(mpTrace) || cmd == REQ_TOGGLE_ITEM) {
520             newtrace = 0;
521             for (ip = menu_items(mpTrace); *ip; ip++) {
522                 if (item_value(*ip))
523                     newtrace |= t_tbl[item_index(*ip)].mask;
524             }
525             trace(newtrace);
526             _tracef("trace level interactively set to %s", tracetrace(_nc_tracing));
527
528             (void) mvprintw(LINES - 2, 0,
529                             "Trace level is %s\n", tracetrace(_nc_tracing));
530             refresh();
531         }
532     }
533     return result;
534 }
535 #endif /* TRACE */
536
537 /*****************************************************************************/
538
539 static int
540 menu_number(void)
541 {
542     return item_index(current_item(mpBanner)) - (eBanner + 1);
543 }
544
545 static MENU *
546 current_menu(void)
547 {
548     MENU *result;
549
550     switch (menu_number()) {
551     case eFile:
552         result = mpFile;
553         break;
554     case eSelect:
555         result = mpSelect;
556         break;
557 #ifdef TRACE
558     case eTrace:
559         result = mpTrace;
560         break;
561 #endif
562     default:
563         result = 0;
564         break;
565     }
566     return result;
567 }
568
569 static void
570 build_menus(char *filename)
571 {
572     static const char *labels[] =
573     {
574         "File",
575         "Select",
576 #ifdef TRACE
577         "Trace",
578 #endif
579         (char *) 0
580     };
581     static ITEM *items[SIZEOF(labels)];
582
583     ITEM **ip = items;
584     const char **ap;
585
586     for (ap = labels; *ap; ap++)
587         *ip++ = new_item(*ap, "");
588     *ip = (ITEM *) 0;
589
590     mpBanner = menu_create(items, SIZEOF(labels) - 1, SIZEOF(labels) - 1, eBanner);
591     set_menu_mark(mpBanner, ">");
592
593     build_file_menu(eFile);
594     build_select_menu(eSelect, filename);
595 #ifdef TRACE
596     build_trace_menu(eTrace);
597 #endif
598 }
599
600 static int
601 move_menu(MENU * menu, MENU * current, int by_y, int by_x)
602 {
603     WINDOW *top_win = menu_win(menu);
604     WINDOW *sub_win = menu_sub(menu);
605     int y0, x0;
606     int y1, x1;
607     int result;
608
609     getbegyx(top_win, y0, x0);
610     y0 += by_y;
611     x0 += by_x;
612
613     getbegyx(sub_win, y1, x1);
614     y1 += by_y;
615     x1 += by_x;
616
617     if ((result = mvwin(top_win, y0, x0)) != ERR) {
618 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH < 20060218)
619         sub_win->_begy = y1;
620         sub_win->_begx = x1;
621 #else
622         mvwin(sub_win, y1, x1);
623 #endif
624         if (menu == current) {
625             touchwin(top_win);
626             wnoutrefresh(top_win);
627         }
628     }
629     return result;
630 }
631
632 /*
633  * Move the menus around on the screen, to test mvwin().
634  */
635 static void
636 move_menus(MENU * current, int by_y, int by_x)
637 {
638     if (move_menu(mpBanner, current, by_y, by_x) != ERR) {
639         erase();
640         wnoutrefresh(stdscr);
641         move_menu(mpFile, current, by_y, by_x);
642         move_menu(mpSelect, current, by_y, by_x);
643 #ifdef TRACE
644         move_menu(mpTrace, current, by_y, by_x);
645 #endif
646         doupdate();
647     }
648 }
649
650 static void
651 show_status(int ch, MENU * menu)
652 {
653     move(LINES - 1, 0);
654     printw("key %s, menu %d, mark %s, match %s",
655            keyname(ch),
656            menu_number(),
657            menu_mark(menu),
658            menu_pattern(menu));
659     clrtoeol();
660     refresh();
661 }
662
663 static void
664 perform_menus(void)
665 {
666     MENU *this_menu;
667     MENU *last_menu = mpFile;
668     int code = E_UNKNOWN_COMMAND;
669     int cmd;
670     int ch = ERR;
671
672 #ifdef NCURSES_MOUSE_VERSION
673     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
674 #endif
675
676     menu_display(last_menu);
677
678     for (;;) {
679
680         if (ch != ERR)
681             show_status(ch, last_menu);
682
683         ch = menu_getc(mpBanner);
684
685         /*
686          * Provide for moving the menu around in the screen using shifted
687          * cursor keys.
688          */
689         switch (ch) {
690         case KEY_SF:
691             move_menus(last_menu, 1, 0);
692             continue;
693         case KEY_SR:
694             move_menus(last_menu, -1, 0);
695             continue;
696         case KEY_SLEFT:
697             move_menus(last_menu, 0, -1);
698             continue;
699         case KEY_SRIGHT:
700             move_menus(last_menu, 0, 1);
701             continue;
702         }
703         cmd = menu_virtualize(ch);
704
705         switch (cmd) {
706             /*
707              * The banner menu acts solely to select one of the other menus.
708              * Move between its items, wrapping at the left/right limits.
709              */
710         case REQ_LEFT_ITEM:
711         case REQ_RIGHT_ITEM:
712             code = menu_driver(mpBanner, cmd);
713             if (code == E_REQUEST_DENIED) {
714                 if (menu_number() > 0)
715                     code = menu_driver(mpBanner, REQ_FIRST_ITEM);
716                 else
717                     code = menu_driver(mpBanner, REQ_LAST_ITEM);
718             }
719             break;
720         default:
721             switch (menu_number()) {
722             case eFile:
723                 code = perform_file_menu(cmd);
724                 break;
725             case eSelect:
726                 code = perform_select_menu(cmd);
727                 break;
728 #ifdef TRACE
729             case eTrace:
730                 code = perform_trace_menu(cmd);
731                 break;
732 #endif
733             }
734
735             if ((code == E_REQUEST_DENIED) && (cmd == KEY_MOUSE)) {
736                 code = menu_driver(mpBanner, cmd);
737             }
738
739             break;
740         }
741
742         if (code == E_OK) {
743             this_menu = current_menu();
744             if (this_menu != last_menu) {
745                 move(1, 0);
746                 clrtobot();
747                 box(menu_win(this_menu), 0, 0);
748                 refresh();
749
750                 /* force the current menu to appear */
751                 menu_display(this_menu);
752
753                 last_menu = this_menu;
754             }
755         }
756         wrefresh(menu_win(last_menu));
757         if (code == E_UNKNOWN_COMMAND
758             || code == E_NOT_POSTED) {
759             if (menu_number() == eFile)
760                 break;
761             beep();
762         }
763         if (code == E_REQUEST_DENIED)
764             beep();
765         continue;
766     }
767
768 #ifdef NCURSES_MOUSE_VERSION
769     mousemask(0, (mmask_t *) 0);
770 #endif
771 }
772
773 static void
774 destroy_menus(void)
775 {
776     menu_destroy(mpFile);
777     menu_destroy(mpSelect);
778 #ifdef TRACE
779     menu_destroy(mpTrace);
780 #endif
781     menu_destroy(mpBanner);
782 }
783
784 #if HAVE_RIPOFFLINE
785 static int
786 rip_footer(WINDOW *win, int cols)
787 {
788     wbkgd(win, A_REVERSE);
789     werase(win);
790     wmove(win, 0, 0);
791     wprintw(win, "footer: %d columns", cols);
792     wnoutrefresh(win);
793     return OK;
794 }
795
796 static int
797 rip_header(WINDOW *win, int cols)
798 {
799     wbkgd(win, A_REVERSE);
800     werase(win);
801     wmove(win, 0, 0);
802     wprintw(win, "header: %d columns", cols);
803     wnoutrefresh(win);
804     return OK;
805 }
806 #endif /* HAVE_RIPOFFLINE */
807
808 static void
809 usage(void)
810 {
811     static const char *const tbl[] =
812     {
813         "Usage: demo_menus [options]"
814         ,""
815         ,"Options:"
816 #if HAVE_RIPOFFLINE
817         ,"  -f       rip-off footer line (can repeat)"
818         ,"  -h       rip-off header line (can repeat)"
819 #endif
820 #ifdef TRACE
821         ,"  -t mask  specify default trace-level (may toggle with ^T)"
822 #endif
823     };
824     size_t n;
825     for (n = 0; n < SIZEOF(tbl); n++)
826         fprintf(stderr, "%s\n", tbl[n]);
827     ExitProgram(EXIT_FAILURE);
828 }
829
830 int
831 main(int argc, char *argv[])
832 {
833     int c;
834
835     setlocale(LC_ALL, "");
836
837     while ((c = getopt(argc, argv, "a:de:fhmp:s:t:")) != -1) {
838         switch (c) {
839 #if HAVE_RIPOFFLINE
840         case 'f':
841             ripoffline(-1, rip_footer);
842             break;
843         case 'h':
844             ripoffline(1, rip_header);
845             break;
846 #endif /* HAVE_RIPOFFLINE */
847 #ifdef TRACE
848         case 't':
849             trace(strtoul(optarg, 0, 0));
850             break;
851 #endif
852         default:
853             usage();
854         }
855     }
856
857     initscr();
858     noraw();
859     cbreak();
860     noecho();
861
862     if (has_colors()) {
863         start_color();
864         init_pair(1, COLOR_RED, COLOR_BLACK);
865         init_pair(2, COLOR_BLUE, COLOR_WHITE);
866     }
867     build_menus(argc > 1 ? argv[1] : 0);
868     perform_menus();
869     destroy_menus();
870
871     endwin();
872     ExitProgram(EXIT_SUCCESS);
873 }
874 #else
875 int
876 main(void)
877 {
878     printf("This program requires the curses menu library\n");
879     ExitProgram(EXIT_FAILURE);
880 }
881 #endif