ncurses 5.9 - patch 20121102
[ncurses.git] / test / demo_menus.c
1 /****************************************************************************
2  * Copyright (c) 2005-2011,2012 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.36 2012/11/03 19:27:01 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 static bool loaded_file = FALSE;
111
112 /* Common function to allow ^T to toggle trace-mode in the middle of a test
113  * so that trace-files can be made smaller.
114  */
115 static int
116 wGetchar(WINDOW *win)
117 {
118     int c;
119 #ifdef TRACE
120     while ((c = wgetch(win)) == CTRL('T')) {
121         if (_nc_tracing) {
122             save_trace = _nc_tracing;
123             Trace(("TOGGLE-TRACING OFF"));
124             _nc_tracing = 0;
125         } else {
126             _nc_tracing = save_trace;
127         }
128         trace(_nc_tracing);
129         if (_nc_tracing)
130             Trace(("TOGGLE-TRACING ON"));
131     }
132 #else
133     c = wgetch(win);
134 #endif
135     return c;
136 }
137 #define Getchar() wGetchar(stdscr)
138
139 static int
140 menu_virtualize(int c)
141 {
142     int result;
143
144     if (c == '\n' || c == KEY_EXIT)
145         result = (MAX_COMMAND + 1);
146     else if (c == 'u')
147         result = (REQ_SCR_ULINE);
148     else if (c == 'd')
149         result = (REQ_SCR_DLINE);
150     else if (c == 'b' || c == KEY_NPAGE)
151         result = (REQ_SCR_UPAGE);
152     else if (c == 'f' || c == KEY_PPAGE)
153         result = (REQ_SCR_DPAGE);
154     else if (c == 'l' || c == KEY_LEFT || c == KEY_BTAB)
155         result = (REQ_LEFT_ITEM);
156     else if (c == 'n' || c == KEY_DOWN)
157         result = (REQ_NEXT_ITEM);
158     else if (c == 'p' || c == KEY_UP)
159         result = (REQ_PREV_ITEM);
160     else if (c == 'r' || c == KEY_RIGHT || c == '\t')
161         result = (REQ_RIGHT_ITEM);
162     else if (c == ' ')
163         result = (REQ_TOGGLE_ITEM);
164     else {
165         if (c != KEY_MOUSE)
166             beep();
167         result = (c);
168     }
169     return result;
170 }
171
172 static int
173 menu_getc(MENU * m)
174 {
175     return wGetchar(menu_win(m));
176 }
177
178 static int
179 menu_offset(MenuNo number)
180 {
181     int result = 0;
182
183     if (okMenuNo(number)) {
184         int spc_desc, spc_rows, spc_cols;
185
186 #ifdef NCURSES_VERSION
187         menu_spacing(mpBanner, &spc_desc, &spc_rows, &spc_cols);
188 #else
189         spc_rows = 0;
190 #endif
191
192         /* FIXME: MENU.itemlen seems the only way to get actual width of items */
193         result = (number - (eBanner + 1)) * (menu_itemwidth(mpBanner) + spc_rows);
194     }
195     return result;
196 }
197
198 static MENU *
199 menu_create(ITEM ** items, int count, int ncols, MenuNo number)
200 {
201     MENU *result;
202     WINDOW *menuwin;
203     int mrows, mcols;
204     int y = okMenuNo(number) ? MENU_Y : 0;
205     int x = menu_offset(number);
206     int margin = (y == MENU_Y) ? 1 : 0;
207     int maxcol = (ncols + x) < COLS ? ncols : (COLS - x - 1);
208     int maxrow = (count + 1) / ncols;
209
210     if ((maxrow + y) >= (LINES - 4))
211         maxrow = LINES - 4 - y;
212
213     result = new_menu(items);
214
215     if (has_colors()) {
216         set_menu_fore(result, (chtype) COLOR_PAIR(1));
217         set_menu_back(result, (chtype) COLOR_PAIR(2));
218     }
219
220     set_menu_format(result, maxrow, maxcol);
221     scale_menu(result, &mrows, &mcols);
222
223     if (mcols + (2 * margin + x) >= COLS)
224         mcols = COLS - (2 * margin + x);
225
226 #ifdef TRACE
227     if (number == eTrace)
228         menu_opts_off(result, O_ONEVALUE);
229     else
230         menu_opts_on(result, O_ONEVALUE);
231 #endif
232
233     menuwin = newwin(mrows + (2 * margin), mcols + (2 * margin), y, x);
234     set_menu_win(result, menuwin);
235     keypad(menuwin, TRUE);
236     if (margin)
237         box(menuwin, 0, 0);
238
239     set_menu_sub(result, derwin(menuwin, mrows, mcols, margin, margin));
240
241     post_menu(result);
242
243     return result;
244 }
245
246 static void
247 menu_destroy(MENU * m)
248 {
249     int count;
250
251     Trace(("menu_destroy %p", (void *) m));
252     if (m != 0) {
253         ITEM **items = menu_items(m);
254         const char *blob = 0;
255
256         count = item_count(m);
257         Trace(("menu_destroy %p count %d", (void *) m, count));
258         if ((count > 0) && (m == mpSelect)) {
259             blob = item_name(*items);
260         }
261
262         unpost_menu(m);
263         free_menu(m);
264
265         /* free the extra data allocated in build_select_menu() */
266         if ((count > 0) && (m == mpSelect)) {
267             if (blob && loaded_file) {
268                 Trace(("freeing blob %p", blob));
269                 free((char *) blob);
270             }
271             free(items);
272         }
273 #ifdef TRACE
274         if ((count > 0) && (m == mpTrace)) {
275             ITEM **ip = items;
276             while (*ip)
277                 free(*ip++);
278         }
279 #endif
280     }
281 }
282
283 /* force the given menu to appear */
284 static void
285 menu_display(MENU * m)
286 {
287     touchwin(menu_win(m));
288     wrefresh(menu_win(m));
289 }
290
291 /*****************************************************************************/
292
293 static void
294 build_file_menu(MenuNo number)
295 {
296     static CONST_MENUS char *labels[] =
297     {
298         "Exit",
299         (char *) 0
300     };
301     static ITEM *items[SIZEOF(labels)];
302
303     ITEM **ip = items;
304     CONST_MENUS char **ap;
305
306     for (ap = labels; *ap; ap++)
307         *ip++ = new_item(*ap, "");
308     *ip = (ITEM *) 0;
309
310     mpFile = menu_create(items, SIZEOF(labels) - 1, 1, number);
311 }
312
313 static int
314 perform_file_menu(int cmd)
315 {
316     return menu_driver(mpFile, cmd);
317 }
318
319 /*****************************************************************************/
320
321 static void
322 build_select_menu(MenuNo number, char *filename)
323 {
324     static CONST_MENUS char *labels[] =
325     {
326         "Lions",
327         "Tigers",
328         "Bears",
329         "(Oh my!)",
330         "Newts",
331         "Platypi",
332         "Lemurs",
333         "(Oh really?!)",
334         "Leopards",
335         "Panthers",
336         "Pumas",
337         "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
338         "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
339         (char *) 0
340     };
341     static ITEM **items;
342
343     ITEM **ip;
344     CONST_MENUS char **ap = 0;
345     CONST_MENUS char **myList = 0;
346     unsigned count = 0;
347
348     if (filename != 0) {
349         struct stat sb;
350         if (stat(filename, &sb) == 0
351             && (sb.st_mode & S_IFMT) == S_IFREG
352             && sb.st_size != 0) {
353             size_t size = (size_t) sb.st_size;
354             unsigned j, k;
355             char *blob = typeMalloc(char, size + 1);
356             CONST_MENUS char **list = typeCalloc(CONST_MENUS char *, size + 1);
357
358             items = typeCalloc(ITEM *, size + 1);
359             Trace(("build_select_menu blob=%p, items=%p",
360                    (void *) blob,
361                    (void *) items));
362             if (blob != 0 && list != 0) {
363                 FILE *fp = fopen(filename, "r");
364                 if (fp != 0) {
365                     if (fread(blob, sizeof(char), size, fp) == size) {
366                         bool mark = TRUE;
367                         for (j = k = 0; j < size; ++j) {
368                             if (mark) {
369                                 list[k++] = blob + j;
370                                 mark = FALSE;
371                             }
372                             if (blob[j] == '\n') {
373                                 blob[j] = '\0';
374                                 if (k > 0 && *list[k - 1] == '\0')
375                                     --k;
376                                 mark = TRUE;
377                             } else if (blob[j] == '\t') {
378                                 blob[j] = ' ';  /* menu items are printable */
379                             }
380                         }
381                         list[k] = 0;
382                         count = k;
383                         ap = myList = list;
384                     }
385                     fclose(fp);
386                 }
387                 loaded_file = TRUE;
388             }
389         }
390     }
391     if (ap == 0) {
392         count = SIZEOF(labels) - 1;
393         items = typeCalloc(ITEM *, count + 1);
394         ap = labels;
395     }
396
397     ip = items;
398     while (*ap != 0)
399         *ip++ = new_item(*ap++, "");
400     *ip = 0;
401
402     mpSelect = menu_create(items, (int) count, 1, number);
403     if (myList != 0)
404         free(myList);
405 }
406
407 static int
408 perform_select_menu(int cmd)
409 {
410     return menu_driver(mpSelect, cmd);
411 }
412
413 /*****************************************************************************/
414
415 #ifdef TRACE
416 #define T_TBL(name) { #name, name }
417 static struct {
418     const char *name;
419     unsigned mask;
420 } t_tbl[] = {
421
422     T_TBL(TRACE_DISABLE),
423         T_TBL(TRACE_TIMES),
424         T_TBL(TRACE_TPUTS),
425         T_TBL(TRACE_UPDATE),
426         T_TBL(TRACE_MOVE),
427         T_TBL(TRACE_CHARPUT),
428         T_TBL(TRACE_ORDINARY),
429         T_TBL(TRACE_CALLS),
430         T_TBL(TRACE_VIRTPUT),
431         T_TBL(TRACE_IEVENT),
432         T_TBL(TRACE_BITS),
433         T_TBL(TRACE_ICALLS),
434         T_TBL(TRACE_CCALLS),
435         T_TBL(TRACE_DATABASE),
436         T_TBL(TRACE_ATTRS),
437         T_TBL(TRACE_MAXIMUM),
438     {
439         (char *) 0, 0
440     }
441 };
442
443 static void
444 build_trace_menu(MenuNo number)
445 {
446     static ITEM *items[SIZEOF(t_tbl)];
447
448     ITEM **ip = items;
449     int n;
450
451     for (n = 0; t_tbl[n].name != 0; n++)
452         *ip++ = new_item(t_tbl[n].name, "");
453     *ip = (ITEM *) 0;
454
455     mpTrace = menu_create(items, SIZEOF(t_tbl) - 1, 2, number);
456 }
457
458 static char *
459 tracetrace(unsigned tlevel)
460 {
461     static char *buf;
462     int n;
463
464     if (buf == 0) {
465         size_t need = 12;
466         for (n = 0; t_tbl[n].name != 0; n++)
467             need += strlen(t_tbl[n].name) + 2;
468         buf = typeMalloc(char, need);
469     }
470     sprintf(buf, "0x%02x = {", tlevel);
471     if (tlevel == 0) {
472         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
473     } else {
474         for (n = 1; t_tbl[n].name != 0; n++)
475             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
476                 strcat(buf, t_tbl[n].name);
477                 strcat(buf, ", ");
478             }
479     }
480     if (buf[strlen(buf) - 2] == ',')
481         buf[strlen(buf) - 2] = '\0';
482     return (strcat(buf, "}"));
483 }
484
485 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
486  * the others
487  */
488 static bool
489 update_trace_menu(MENU * m)
490 {
491     ITEM **items;
492     ITEM *i, **p;
493     bool changed = FALSE;
494
495     items = menu_items(m);
496     i = current_item(m);
497     if (i == items[0]) {
498         if (item_value(i)) {
499             for (p = items + 1; *p != 0; p++)
500                 if (item_value(*p)) {
501                     set_item_value(*p, FALSE);
502                     changed = TRUE;
503                 }
504         }
505     }
506     return changed;
507 }
508
509 static int
510 perform_trace_menu(int cmd)
511 /* interactively set the trace level */
512 {
513     ITEM **ip;
514     unsigned newtrace;
515     int result;
516
517     for (ip = menu_items(mpTrace); *ip; ip++) {
518         unsigned mask = t_tbl[item_index(*ip)].mask;
519         if (mask == 0)
520             set_item_value(*ip, _nc_tracing == 0);
521         else if ((mask & _nc_tracing) == mask)
522             set_item_value(*ip, TRUE);
523     }
524
525     result = menu_driver(mpTrace, cmd);
526
527     if (result == E_OK) {
528         if (update_trace_menu(mpTrace) || cmd == REQ_TOGGLE_ITEM) {
529             newtrace = 0;
530             for (ip = menu_items(mpTrace); *ip; ip++) {
531                 if (item_value(*ip))
532                     newtrace |= t_tbl[item_index(*ip)].mask;
533             }
534             trace(newtrace);
535             Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
536
537             MvPrintw(LINES - 2, 0,
538                      "Trace level is %s\n", tracetrace(_nc_tracing));
539             refresh();
540         }
541     }
542     return result;
543 }
544 #endif /* TRACE */
545
546 /*****************************************************************************/
547
548 static int
549 menu_number(void)
550 {
551     return item_index(current_item(mpBanner)) - (eBanner + 1);
552 }
553
554 static MENU *
555 current_menu(void)
556 {
557     MENU *result;
558
559     switch (menu_number()) {
560     case eFile:
561         result = mpFile;
562         break;
563     case eSelect:
564         result = mpSelect;
565         break;
566 #ifdef TRACE
567     case eTrace:
568         result = mpTrace;
569         break;
570 #endif
571     default:
572         result = 0;
573         break;
574     }
575     return result;
576 }
577
578 static void
579 build_menus(char *filename)
580 {
581     static CONST_MENUS char *labels[] =
582     {
583         "File",
584         "Select",
585 #ifdef TRACE
586         "Trace",
587 #endif
588         (char *) 0
589     };
590     static ITEM *items[SIZEOF(labels)];
591
592     ITEM **ip = items;
593     CONST_MENUS char **ap;
594
595     for (ap = labels; *ap; ap++)
596         *ip++ = new_item(*ap, "");
597     *ip = (ITEM *) 0;
598
599     mpBanner = menu_create(items, SIZEOF(labels) - 1, SIZEOF(labels) - 1, eBanner);
600     set_menu_mark(mpBanner, ">");
601
602     build_file_menu(eFile);
603     build_select_menu(eSelect, filename);
604 #ifdef TRACE
605     build_trace_menu(eTrace);
606 #endif
607 }
608
609 static int
610 move_menu(MENU * menu, MENU * current, int by_y, int by_x)
611 {
612     WINDOW *top_win = menu_win(menu);
613     WINDOW *sub_win = menu_sub(menu);
614     int y0, x0;
615     int y1, x1;
616     int result;
617
618     getbegyx(top_win, y0, x0);
619     y0 += by_y;
620     x0 += by_x;
621
622     getbegyx(sub_win, y1, x1);
623     y1 += by_y;
624     x1 += by_x;
625
626     if ((result = mvwin(top_win, y0, x0)) != ERR) {
627 #if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH < 20060218)
628         sub_win->_begy = y1;
629         sub_win->_begx = x1;
630 #else
631         mvwin(sub_win, y1, x1);
632 #endif
633         if (menu == current) {
634             touchwin(top_win);
635             wnoutrefresh(top_win);
636         }
637     }
638     return result;
639 }
640
641 /*
642  * Move the menus around on the screen, to test mvwin().
643  */
644 static void
645 move_menus(MENU * current, int by_y, int by_x)
646 {
647     if (move_menu(mpBanner, current, by_y, by_x) != ERR) {
648         erase();
649         wnoutrefresh(stdscr);
650         move_menu(mpFile, current, by_y, by_x);
651         move_menu(mpSelect, current, by_y, by_x);
652 #ifdef TRACE
653         move_menu(mpTrace, current, by_y, by_x);
654 #endif
655         doupdate();
656     }
657 }
658
659 static void
660 show_status(int ch, MENU * menu)
661 {
662     move(LINES - 1, 0);
663     printw("key %s, menu %d, mark %s, match %s",
664            keyname(ch),
665            menu_number(),
666            menu_mark(menu),
667            menu_pattern(menu));
668     clrtoeol();
669     refresh();
670 }
671
672 static void
673 perform_menus(void)
674 {
675     MENU *this_menu;
676     MENU *last_menu = mpFile;
677     int code = E_UNKNOWN_COMMAND;
678     int cmd;
679     int ch = ERR;
680
681 #ifdef NCURSES_MOUSE_VERSION
682     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
683 #endif
684
685     menu_display(last_menu);
686
687     for (;;) {
688
689         if (ch != ERR)
690             show_status(ch, last_menu);
691
692         ch = menu_getc(mpBanner);
693
694         /*
695          * Provide for moving the menu around in the screen using shifted
696          * cursor keys.
697          */
698         switch (ch) {
699         case KEY_SF:
700             move_menus(last_menu, 1, 0);
701             continue;
702         case KEY_SR:
703             move_menus(last_menu, -1, 0);
704             continue;
705         case KEY_SLEFT:
706             move_menus(last_menu, 0, -1);
707             continue;
708         case KEY_SRIGHT:
709             move_menus(last_menu, 0, 1);
710             continue;
711         }
712         cmd = menu_virtualize(ch);
713
714         switch (cmd) {
715             /*
716              * The banner menu acts solely to select one of the other menus.
717              * Move between its items, wrapping at the left/right limits.
718              */
719         case REQ_LEFT_ITEM:
720         case REQ_RIGHT_ITEM:
721             code = menu_driver(mpBanner, cmd);
722             if (code == E_REQUEST_DENIED) {
723                 if (menu_number() > 0)
724                     code = menu_driver(mpBanner, REQ_FIRST_ITEM);
725                 else
726                     code = menu_driver(mpBanner, REQ_LAST_ITEM);
727             }
728             break;
729         default:
730             switch (menu_number()) {
731             case eFile:
732                 code = perform_file_menu(cmd);
733                 break;
734             case eSelect:
735                 code = perform_select_menu(cmd);
736                 break;
737 #ifdef TRACE
738             case eTrace:
739                 code = perform_trace_menu(cmd);
740                 break;
741 #endif
742             }
743
744             if ((code == E_REQUEST_DENIED) && (cmd == KEY_MOUSE)) {
745                 code = menu_driver(mpBanner, cmd);
746             }
747
748             break;
749         }
750
751         if (code == E_OK) {
752             this_menu = current_menu();
753             if (this_menu != last_menu) {
754                 move(1, 0);
755                 clrtobot();
756                 box(menu_win(this_menu), 0, 0);
757                 refresh();
758
759                 /* force the current menu to appear */
760                 menu_display(this_menu);
761
762                 last_menu = this_menu;
763             }
764         }
765         wrefresh(menu_win(last_menu));
766         if (code == E_UNKNOWN_COMMAND
767             || code == E_NOT_POSTED) {
768             if (menu_number() == eFile)
769                 break;
770             beep();
771         }
772         if (code == E_REQUEST_DENIED)
773             beep();
774         continue;
775     }
776
777 #ifdef NCURSES_MOUSE_VERSION
778     mousemask(0, (mmask_t *) 0);
779 #endif
780 }
781
782 static void
783 destroy_menus(void)
784 {
785     menu_destroy(mpFile);
786     menu_destroy(mpSelect);
787 #ifdef TRACE
788     menu_destroy(mpTrace);
789 #endif
790     menu_destroy(mpBanner);
791 }
792
793 #if HAVE_RIPOFFLINE
794 static int
795 rip_footer(WINDOW *win, int cols)
796 {
797     wbkgd(win, A_REVERSE);
798     werase(win);
799     wmove(win, 0, 0);
800     wprintw(win, "footer: %d columns", cols);
801     wnoutrefresh(win);
802     return OK;
803 }
804
805 static int
806 rip_header(WINDOW *win, int cols)
807 {
808     wbkgd(win, A_REVERSE);
809     werase(win);
810     wmove(win, 0, 0);
811     wprintw(win, "header: %d columns", cols);
812     wnoutrefresh(win);
813     return OK;
814 }
815 #endif /* HAVE_RIPOFFLINE */
816
817 static void
818 usage(void)
819 {
820     static const char *const tbl[] =
821     {
822         "Usage: demo_menus [options] [menu-file]"
823         ,""
824         ,"Options:"
825 #if HAVE_RIPOFFLINE
826         ,"  -f       rip-off footer line (can repeat)"
827         ,"  -h       rip-off header line (can repeat)"
828 #endif
829 #ifdef TRACE
830         ,"  -t mask  specify default trace-level (may toggle with ^T)"
831 #endif
832     };
833     size_t n;
834     for (n = 0; n < SIZEOF(tbl); n++)
835         fprintf(stderr, "%s\n", tbl[n]);
836     ExitProgram(EXIT_FAILURE);
837 }
838
839 int
840 main(int argc, char *argv[])
841 {
842     int c;
843
844     setlocale(LC_ALL, "");
845
846     while ((c = getopt(argc, argv, "a:de:fhmp:s:t:")) != -1) {
847         switch (c) {
848 #if HAVE_RIPOFFLINE
849         case 'f':
850             ripoffline(-1, rip_footer);
851             break;
852         case 'h':
853             ripoffline(1, rip_header);
854             break;
855 #endif /* HAVE_RIPOFFLINE */
856 #ifdef TRACE
857         case 't':
858             trace((unsigned) strtoul(optarg, 0, 0));
859             break;
860 #endif
861         default:
862             usage();
863         }
864     }
865
866     initscr();
867     noraw();
868     cbreak();
869     noecho();
870
871     if (has_colors()) {
872         start_color();
873         init_pair(1, COLOR_RED, COLOR_BLACK);
874         init_pair(2, COLOR_BLUE, COLOR_WHITE);
875     }
876     build_menus(argc > 1 ? argv[1] : 0);
877     perform_menus();
878     destroy_menus();
879
880     endwin();
881     ExitProgram(EXIT_SUCCESS);
882 }
883 #else
884 int
885 main(void)
886 {
887     printf("This program requires the curses menu library\n");
888     ExitProgram(EXIT_FAILURE);
889 }
890 #endif