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