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/*
35item_description		-
36item_init			-
37item_opts			-
38item_opts_off			-
39item_opts_on			-
40item_term			-
41item_userptr			-
42item_visible			-
43menu_back			-
44menu_fore			-
45menu_format			-
46menu_grey			-
47menu_init			-
48menu_opts			-
49menu_pad			-
50menu_request_by_name		-
51menu_request_name		-
52menu_sub			-
53menu_term			-
54menu_userptr			-
55set_current_item		-
56set_item_init			-
57set_item_opts			-
58set_item_term			-
59set_item_userptr		-
60set_menu_grey			-
61set_menu_init			-
62set_menu_items			-
63set_menu_opts			-
64set_menu_pad			-
65set_menu_pattern		-
66set_menu_spacing		-
67set_menu_term			-
68set_menu_userptr		-
69set_top_row			-
70top_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
84static unsigned save_trace = TRACE_ORDINARY | TRACE_CALLS;
85extern unsigned _nc_tracing;
86static MENU *mpTrace;
87#endif
88#else
89#undef TRACE
90#endif
91
92typedef 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
106static MENU *mpBanner;
107static MENU *mpFile;
108static MENU *mpSelect;
109
110static bool loaded_file = FALSE;
111
112#if !HAVE_STRDUP
113#define strdup my_strdup
114static char *
115strdup(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 */
127static int
128wGetchar(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
151static int
152menu_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
184static int
185menu_getc(MENU * m)
186{
187    return wGetchar(menu_win(m));
188}
189
190static int
191menu_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
210static MENU *
211menu_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
258static void
259menu_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 */
296static void
297menu_display(MENU * m)
298{
299    touchwin(menu_win(m));
300    wrefresh(menu_win(m));
301}
302
303/*****************************************************************************/
304
305static void
306build_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
325static int
326perform_file_menu(int cmd)
327{
328    return menu_driver(mpFile, cmd);
329}
330
331/*****************************************************************************/
332
333static void
334build_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
417static int
418perform_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 }
427static 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
453static void
454build_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
468static char *
469tracetrace(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 */
498static bool
499update_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
519static int
520perform_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
558static int
559menu_number(void)
560{
561    return item_index(current_item(mpBanner)) - (eBanner + 1);
562}
563
564static MENU *
565current_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
588static void
589build_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
619static int
620move_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 */
654static void
655move_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
669static void
670show_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
682static void
683perform_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
792static void
793destroy_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
804static int
805rip_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
815static int
816rip_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
827static void
828usage(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
849int
850main(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
894int
895main(void)
896{
897    printf("This program requires the curses menu library\n");
898    ExitProgram(EXIT_FAILURE);
899}
900#endif
901