ncurses 5.6 - patch 20080209
[ncurses.git] / test / demo_menus.c
1 /****************************************************************************
2  * Copyright (c) 2005-2007,2008 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.22 2008/02/09 18:09:26 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_MENUS char *labels[] =
295     {
296         "Exit",
297         (char *) 0
298     };
299     static ITEM *items[SIZEOF(labels)];
300
301     ITEM **ip = items;
302     CONST_MENUS 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_MENUS 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_MENUS 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_MENUS char **list =
354             (CONST_MENUS char **) calloc(sizeof(*list), size + 1);
355
356             items = (ITEM **) calloc(sizeof(ITEM *), size + 1);
357             if (blob != 0 && list != 0) {
358                 FILE *fp = fopen(filename, "r");
359                 if (fp != 0) {
360                     if (fread(blob, sizeof(char), size, fp) == size) {
361                         bool mark = TRUE;
362                         for (j = k = 0; j < size; ++j) {
363                             if (mark) {
364                                 list[k++] = blob + j;
365                                 mark = FALSE;
366                             }
367                             if (blob[j] == '\n') {
368                                 blob[j] = '\0';
369                                 if (k > 0 && *list[k - 1] == '\0')
370                                     --k;
371                                 mark = TRUE;
372                             } else if (blob[j] == '\t') {
373                                 blob[j] = ' ';  /* menu items are printable */
374                             }
375                         }
376                         list[k] = 0;
377                         count = k;
378                         ap = list;
379                     }
380                     fclose(fp);
381                 }
382             }
383         }
384     }
385     if (ap == 0) {
386         count = SIZEOF(labels) - 1;
387         items = (ITEM **) calloc(count + 1, sizeof(*items));
388         ap = labels;
389     }
390
391     ip = items;
392     while (*ap != 0)
393         *ip++ = new_item(*ap++, "");
394     *ip = 0;
395
396     mpSelect = menu_create(items, (int) count, 1, number);
397 }
398
399 static int
400 perform_select_menu(int cmd)
401 {
402     return menu_driver(mpSelect, cmd);
403 }
404
405 /*****************************************************************************/
406
407 #ifdef TRACE
408 #define T_TBL(name) { #name, name }
409 static struct {
410     const char *name;
411     unsigned mask;
412 } t_tbl[] = {
413
414     T_TBL(TRACE_DISABLE),
415         T_TBL(TRACE_TIMES),
416         T_TBL(TRACE_TPUTS),
417         T_TBL(TRACE_UPDATE),
418         T_TBL(TRACE_MOVE),
419         T_TBL(TRACE_CHARPUT),
420         T_TBL(TRACE_ORDINARY),
421         T_TBL(TRACE_CALLS),
422         T_TBL(TRACE_VIRTPUT),
423         T_TBL(TRACE_IEVENT),
424         T_TBL(TRACE_BITS),
425         T_TBL(TRACE_ICALLS),
426         T_TBL(TRACE_CCALLS),
427         T_TBL(TRACE_DATABASE),
428         T_TBL(TRACE_ATTRS),
429         T_TBL(TRACE_MAXIMUM),
430     {
431         (char *) 0, 0
432     }
433 };
434
435 static void
436 build_trace_menu(MenuNo number)
437 {
438     static ITEM *items[SIZEOF(t_tbl)];
439
440     ITEM **ip = items;
441     int n;
442
443     for (n = 0; t_tbl[n].name != 0; n++)
444         *ip++ = new_item(t_tbl[n].name, "");
445     *ip = (ITEM *) 0;
446
447     mpTrace = menu_create(items, SIZEOF(t_tbl) - 1, 2, number);
448 }
449
450 static char *
451 tracetrace(unsigned tlevel)
452 {
453     static char *buf;
454     int n;
455
456     if (buf == 0) {
457         size_t need = 12;
458         for (n = 0; t_tbl[n].name != 0; n++)
459             need += strlen(t_tbl[n].name) + 2;
460         buf = (char *) malloc(need);
461     }
462     sprintf(buf, "0x%02x = {", tlevel);
463     if (tlevel == 0) {
464         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
465     } else {
466         for (n = 1; t_tbl[n].name != 0; n++)
467             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
468                 strcat(buf, t_tbl[n].name);
469                 strcat(buf, ", ");
470             }
471     }
472     if (buf[strlen(buf) - 2] == ',')
473         buf[strlen(buf) - 2] = '\0';
474     return (strcat(buf, "}"));
475 }
476
477 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
478  * the others
479  */
480 static bool
481 update_trace_menu(MENU * m)
482 {
483     ITEM **items;
484     ITEM *i, **p;
485     bool changed = FALSE;
486
487     items = menu_items(m);
488     i = current_item(m);
489     if (i == items[0]) {
490         if (item_value(i)) {
491             for (p = items + 1; *p != 0; p++)
492                 if (item_value(*p)) {
493                     set_item_value(*p, FALSE);
494                     changed = TRUE;
495                 }
496         }
497     }
498     return changed;
499 }
500
501 static int
502 perform_trace_menu(int cmd)
503 /* interactively set the trace level */
504 {
505     ITEM **ip;
506     unsigned newtrace;
507     int result;
508
509     for (ip = menu_items(mpTrace); *ip; ip++) {
510         unsigned mask = t_tbl[item_index(*ip)].mask;
511         if (mask == 0)
512             set_item_value(*ip, _nc_tracing == 0);
513         else if ((mask & _nc_tracing) == mask)
514             set_item_value(*ip, TRUE);
515     }
516
517     result = menu_driver(mpTrace, cmd);
518
519     if (result == E_OK) {
520         if (update_trace_menu(mpTrace) || cmd == REQ_TOGGLE_ITEM) {
521             newtrace = 0;
522             for (ip = menu_items(mpTrace); *ip; ip++) {
523                 if (item_value(*ip))
524                     newtrace |= t_tbl[item_index(*ip)].mask;
525             }
526             trace(newtrace);
527             _tracef("trace level interactively set to %s", tracetrace(_nc_tracing));
528
529             (void) mvprintw(LINES - 2, 0,
530                             "Trace level is %s\n", tracetrace(_nc_tracing));
531             refresh();
532         }
533     }
534     return result;
535 }
536 #endif /* TRACE */
537
538 /*****************************************************************************/
539
540 static int
541 menu_number(void)
542 {
543     return item_index(current_item(mpBanner)) - (eBanner + 1);
544 }
545
546 static MENU *
547 current_menu(void)
548 {
549     MENU *result;
550
551     switch (menu_number()) {
552     case eFile:
553         result = mpFile;
554         break;
555     case eSelect:
556         result = mpSelect;
557         break;
558 #ifdef TRACE
559     case eTrace:
560         result = mpTrace;
561         break;
562 #endif
563     default:
564         result = 0;
565         break;
566     }
567     return result;
568 }
569
570 static void
571 build_menus(char *filename)
572 {
573     static CONST_MENUS char *labels[] =
574     {
575         "File",
576         "Select",
577 #ifdef TRACE
578         "Trace",
579 #endif
580         (char *) 0
581     };
582     static ITEM *items[SIZEOF(labels)];
583
584     ITEM **ip = items;
585     CONST_MENUS char **ap;
586
587     for (ap = labels; *ap; ap++)
588         *ip++ = new_item(*ap, "");
589     *ip = (ITEM *) 0;
590
591     mpBanner = menu_create(items, SIZEOF(labels) - 1, SIZEOF(labels) - 1, eBanner);
592     set_menu_mark(mpBanner, ">");
593
594     build_file_menu(eFile);
595     build_select_menu(eSelect, filename);
596 #ifdef TRACE
597     build_trace_menu(eTrace);
598 #endif
599 }
600
601 static int
602 move_menu(MENU * menu, MENU * current, int by_y, int by_x)
603 {
604     WINDOW *top_win = menu_win(menu);
605     WINDOW *sub_win = menu_sub(menu);
606     int y0, x0;
607     int y1, x1;
608     int result;
609
610     getbegyx(top_win, y0, x0);
611     y0 += by_y;
612     x0 += by_x;
613
614     getbegyx(sub_win, y1, x1);
615     y1 += by_y;
616     x1 += by_x;
617
618     if ((result = mvwin(top_win, y0, x0)) != ERR) {
619 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH < 20060218)
620         sub_win->_begy = y1;
621         sub_win->_begx = x1;
622 #else
623         mvwin(sub_win, y1, x1);
624 #endif
625         if (menu == current) {
626             touchwin(top_win);
627             wnoutrefresh(top_win);
628         }
629     }
630     return result;
631 }
632
633 /*
634  * Move the menus around on the screen, to test mvwin().
635  */
636 static void
637 move_menus(MENU * current, int by_y, int by_x)
638 {
639     if (move_menu(mpBanner, current, by_y, by_x) != ERR) {
640         erase();
641         wnoutrefresh(stdscr);
642         move_menu(mpFile, current, by_y, by_x);
643         move_menu(mpSelect, current, by_y, by_x);
644 #ifdef TRACE
645         move_menu(mpTrace, current, by_y, by_x);
646 #endif
647         doupdate();
648     }
649 }
650
651 static void
652 show_status(int ch, MENU * menu)
653 {
654     move(LINES - 1, 0);
655     printw("key %s, menu %d, mark %s, match %s",
656            keyname(ch),
657            menu_number(),
658            menu_mark(menu),
659            menu_pattern(menu));
660     clrtoeol();
661     refresh();
662 }
663
664 static void
665 perform_menus(void)
666 {
667     MENU *this_menu;
668     MENU *last_menu = mpFile;
669     int code = E_UNKNOWN_COMMAND;
670     int cmd;
671     int ch = ERR;
672
673 #ifdef NCURSES_MOUSE_VERSION
674     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
675 #endif
676
677     menu_display(last_menu);
678
679     for (;;) {
680
681         if (ch != ERR)
682             show_status(ch, last_menu);
683
684         ch = menu_getc(mpBanner);
685
686         /*
687          * Provide for moving the menu around in the screen using shifted
688          * cursor keys.
689          */
690         switch (ch) {
691         case KEY_SF:
692             move_menus(last_menu, 1, 0);
693             continue;
694         case KEY_SR:
695             move_menus(last_menu, -1, 0);
696             continue;
697         case KEY_SLEFT:
698             move_menus(last_menu, 0, -1);
699             continue;
700         case KEY_SRIGHT:
701             move_menus(last_menu, 0, 1);
702             continue;
703         }
704         cmd = menu_virtualize(ch);
705
706         switch (cmd) {
707             /*
708              * The banner menu acts solely to select one of the other menus.
709              * Move between its items, wrapping at the left/right limits.
710              */
711         case REQ_LEFT_ITEM:
712         case REQ_RIGHT_ITEM:
713             code = menu_driver(mpBanner, cmd);
714             if (code == E_REQUEST_DENIED) {
715                 if (menu_number() > 0)
716                     code = menu_driver(mpBanner, REQ_FIRST_ITEM);
717                 else
718                     code = menu_driver(mpBanner, REQ_LAST_ITEM);
719             }
720             break;
721         default:
722             switch (menu_number()) {
723             case eFile:
724                 code = perform_file_menu(cmd);
725                 break;
726             case eSelect:
727                 code = perform_select_menu(cmd);
728                 break;
729 #ifdef TRACE
730             case eTrace:
731                 code = perform_trace_menu(cmd);
732                 break;
733 #endif
734             }
735
736             if ((code == E_REQUEST_DENIED) && (cmd == KEY_MOUSE)) {
737                 code = menu_driver(mpBanner, cmd);
738             }
739
740             break;
741         }
742
743         if (code == E_OK) {
744             this_menu = current_menu();
745             if (this_menu != last_menu) {
746                 move(1, 0);
747                 clrtobot();
748                 box(menu_win(this_menu), 0, 0);
749                 refresh();
750
751                 /* force the current menu to appear */
752                 menu_display(this_menu);
753
754                 last_menu = this_menu;
755             }
756         }
757         wrefresh(menu_win(last_menu));
758         if (code == E_UNKNOWN_COMMAND
759             || code == E_NOT_POSTED) {
760             if (menu_number() == eFile)
761                 break;
762             beep();
763         }
764         if (code == E_REQUEST_DENIED)
765             beep();
766         continue;
767     }
768
769 #ifdef NCURSES_MOUSE_VERSION
770     mousemask(0, (mmask_t *) 0);
771 #endif
772 }
773
774 static void
775 destroy_menus(void)
776 {
777     menu_destroy(mpFile);
778     menu_destroy(mpSelect);
779 #ifdef TRACE
780     menu_destroy(mpTrace);
781 #endif
782     menu_destroy(mpBanner);
783 }
784
785 #if HAVE_RIPOFFLINE
786 static int
787 rip_footer(WINDOW *win, int cols)
788 {
789     wbkgd(win, A_REVERSE);
790     werase(win);
791     wmove(win, 0, 0);
792     wprintw(win, "footer: %d columns", cols);
793     wnoutrefresh(win);
794     return OK;
795 }
796
797 static int
798 rip_header(WINDOW *win, int cols)
799 {
800     wbkgd(win, A_REVERSE);
801     werase(win);
802     wmove(win, 0, 0);
803     wprintw(win, "header: %d columns", cols);
804     wnoutrefresh(win);
805     return OK;
806 }
807 #endif /* HAVE_RIPOFFLINE */
808
809 static void
810 usage(void)
811 {
812     static const char *const tbl[] =
813     {
814         "Usage: demo_menus [options]"
815         ,""
816         ,"Options:"
817 #if HAVE_RIPOFFLINE
818         ,"  -f       rip-off footer line (can repeat)"
819         ,"  -h       rip-off header line (can repeat)"
820 #endif
821 #ifdef TRACE
822         ,"  -t mask  specify default trace-level (may toggle with ^T)"
823 #endif
824     };
825     size_t n;
826     for (n = 0; n < SIZEOF(tbl); n++)
827         fprintf(stderr, "%s\n", tbl[n]);
828     ExitProgram(EXIT_FAILURE);
829 }
830
831 int
832 main(int argc, char *argv[])
833 {
834     int c;
835
836     setlocale(LC_ALL, "");
837
838     while ((c = getopt(argc, argv, "a:de:fhmp:s:t:")) != -1) {
839         switch (c) {
840 #if HAVE_RIPOFFLINE
841         case 'f':
842             ripoffline(-1, rip_footer);
843             break;
844         case 'h':
845             ripoffline(1, rip_header);
846             break;
847 #endif /* HAVE_RIPOFFLINE */
848 #ifdef TRACE
849         case 't':
850             trace(strtoul(optarg, 0, 0));
851             break;
852 #endif
853         default:
854             usage();
855         }
856     }
857
858     initscr();
859     noraw();
860     cbreak();
861     noecho();
862
863     if (has_colors()) {
864         start_color();
865         init_pair(1, COLOR_RED, COLOR_BLACK);
866         init_pair(2, COLOR_BLUE, COLOR_WHITE);
867     }
868     build_menus(argc > 1 ? argv[1] : 0);
869     perform_menus();
870     destroy_menus();
871
872     endwin();
873     ExitProgram(EXIT_SUCCESS);
874 }
875 #else
876 int
877 main(void)
878 {
879     printf("This program requires the curses menu library\n");
880     ExitProgram(EXIT_FAILURE);
881 }
882 #endif