Home | History | Annotate | Line # | Download | only in menuc
menu_sys.def revision 1.27
      1 /*	$NetBSD: menu_sys.def,v 1.27 2001/12/06 16:38:30 christos Exp $	*/
      2 
      3 /*
      4  * Copyright 1997 Piermont Information Systems Inc.
      5  * All rights reserved.
      6  *
      7  * Written by Philip A. Nelson for Piermont Information Systems Inc.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  * 3. All advertising materials mentioning features or use of this software
     18  *    must display the following acknowledgement:
     19  *      This product includes software develooped for the NetBSD Project by
     20  *      Piermont Information Systems Inc.
     21  * 4. The name of Piermont Information Systems Inc. may not be used to endorse
     22  *    or promote products derived from this software without specific prior
     23  *    written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
     26  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
     29  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     35  * THE POSSIBILITY OF SUCH DAMAGE.
     36  *
     37  */
     38 
     39 /* menu_sys.defs -- Menu system standard routines. */
     40 
     41 #include <string.h>
     42 #include <ctype.h>
     43 
     44 #define REQ_EXECUTE    1000
     45 #define REQ_NEXT_ITEM  1001
     46 #define REQ_PREV_ITEM  1002
     47 #define REQ_REDISPLAY  1003
     48 #define REQ_SCROLLDOWN 1004
     49 #define REQ_SCROLLUP   1005
     50 #define REQ_HELP       1006
     51 
     52 /* Multiple key support */
     53 #define KEYSEQ_FIRST       256
     54 #define KEYSEQ_DOWN_ARROW  256
     55 #define KEYSEQ_UP_ARROW    257
     56 #define KEYSEQ_LEFT_ARROW  258
     57 #define KEYSEQ_RIGHT_ARROW 259
     58 #define KEYSEQ_PAGE_DOWN   260
     59 #define KEYSEQ_PAGE_UP     261
     60 
     61 struct keyseq {
     62 	char *termcap_name;
     63 	char *chars;
     64 	int  numchars;
     65 	int  keyseq_val;
     66 	struct keyseq *next;
     67 };
     68 
     69 /* keypad and other definitions */
     70 struct keyseq _mc_key_seq[] = {
     71 	/* Cludge for xterm ... */
     72 	{  NULL, "\033[B", 0, KEYSEQ_DOWN_ARROW, NULL },
     73 	{  NULL, "\033[D", 0, KEYSEQ_LEFT_ARROW, NULL },
     74 	{  NULL, "\033[C", 0, KEYSEQ_RIGHT_ARROW, NULL },
     75 	{  NULL, "\033[A", 0, KEYSEQ_UP_ARROW, NULL },
     76 	/* Termcap defined */
     77 	{ "kd", NULL, 0, KEYSEQ_DOWN_ARROW, NULL },
     78 	{ "kl", NULL, 0, KEYSEQ_LEFT_ARROW, NULL },
     79 	{ "kr", NULL, 0, KEYSEQ_RIGHT_ARROW, NULL },
     80 	{ "ku", NULL, 0, KEYSEQ_UP_ARROW, NULL },
     81 	{ "kf", NULL, 0, KEYSEQ_PAGE_DOWN, NULL },  /* scroll forward */
     82 	{ "kN", NULL, 0, KEYSEQ_PAGE_DOWN, NULL },  /* next page */
     83 	{ "kP", NULL, 0, KEYSEQ_PAGE_UP, NULL },    /* scroll backward */
     84 	{ "kR", NULL, 0, KEYSEQ_PAGE_UP, NULL },    /* prev page */
     85 	/* other definitions */
     86 	{ NULL, "\033v", 0, KEYSEQ_PAGE_UP, NULL },   /* ESC-v */
     87 	{ NULL, "\026", 0, KEYSEQ_PAGE_DOWN, NULL },  /* CTL-v */
     88 };
     89 
     90 int _mc_num_key_seq = sizeof(_mc_key_seq) / sizeof(struct keyseq);
     91 struct keyseq *pad_list = NULL;
     92 static char str_area [512];
     93 static int str_size = sizeof(str_area);
     94 static char *str_ptr = str_area;
     95 
     96 /* Macros */
     97 #define MAX(x,y) ((x)>(y)?(x):(y))
     98 #define MIN(x,y) ((x)<(y)?(x):(y))
     99 
    100 /* Initialization state. */
    101 static int __menu_init = 0;
    102 int __m_endwin = 0;
    103 static int max_lines = 0, max_cols = 0;
    104 static char *scrolltext = " <: page up, >: page down";
    105 
    106 static menudesc *menus = menu_def;
    107 
    108 #ifdef DYNAMIC_MENUS
    109 static int num_menus  = 0;
    110 static int num_avail  = 0;
    111 #define DYN_INIT_NUM 32
    112 #endif
    113 
    114 /* prototypes for in here! */
    115 static void ins_keyseq (struct keyseq **seq, struct keyseq *ins);
    116 static void init_keyseq (void);
    117 static void init_menu (struct menudesc *m);
    118 static char opt_ch (int op_no);
    119 static void post_menu (struct menudesc *m);
    120 static void process_help (struct menudesc *m, int num);
    121 static void process_req (struct menudesc *m, int num, int req);
    122 static int menucmd (WINDOW *w);
    123 
    124 #ifndef NULL
    125 #define NULL (void *)0
    126 #endif
    127 
    128 /* menu system processing routines */
    129 #define mbeep() (void)fputc('\a', stderr)
    130 
    131 static void
    132 ins_keyseq (struct keyseq **seq, struct keyseq *ins)
    133 {
    134 	if (*seq == NULL) {
    135 		ins->next = NULL;
    136 		*seq = ins;
    137 	} else if (ins->numchars <= (*seq)->numchars) {
    138 		ins->next = *seq;
    139 		*seq = ins;
    140 	} else
    141 		ins_keyseq (&(*seq)->next, ins);
    142 }
    143 
    144 static void
    145 init_keyseq(void)
    146 {
    147 	/*
    148 	 * XXX XXX XXX THIS SHOULD BE NUKED FROM ORBIT!  DO THIS
    149 	 * XXX XXX XXX WITH NORMAL CURSES FACILITIES!
    150 	 */
    151 	struct tinfo *ti;
    152 	char *term = getenv("TERM");
    153 	int i;
    154 
    155 	if (term == NULL || term[0] == '\0')
    156 		return;
    157 
    158 	if (t_getent(&ti, term) < 0)
    159 		return;
    160 
    161 	for (i = 0; i < _mc_num_key_seq; i++) {
    162 		if (_mc_key_seq[i].termcap_name == NULL)
    163 			continue;
    164 
    165 		_mc_key_seq[i].chars = t_getstr(ti, _mc_key_seq[i].termcap_name,
    166 		     &str_ptr, &str_size);
    167 
    168 		if (_mc_key_seq[i].chars != NULL &&
    169 		    (_mc_key_seq[i].numchars = strlen(_mc_key_seq[i].chars))
    170 		    > 0)
    171 			ins_keyseq(&pad_list, &_mc_key_seq[i]);
    172 	}
    173 	t_freent(ti);
    174 }
    175 
    176 static int
    177 mgetch(WINDOW *w)
    178 {
    179 	static char buf[20];
    180 	static int  num = 0;
    181 	struct keyseq *list = pad_list;
    182 	int i, ret;
    183 
    184 	/* key pad processing */
    185 	while (list) {
    186 		for (i=0; i< list->numchars; i++) {
    187 			if (i >= num)
    188 				buf[num++] = wgetch(w);
    189 			if (buf[i] != list->chars[i])
    190 				break;
    191 		}
    192 		if (i == list->numchars) {
    193 			num = 0;
    194 			return list->keyseq_val;
    195 		}
    196 		list = list->next;
    197 	}
    198 
    199 	ret = buf[0];
    200 	for (i = 0; i < strlen(buf); i++)
    201 		buf[i] = buf[i+1];
    202 	num--;
    203 	return ret;
    204 }
    205 
    206 static int
    207 menucmd (WINDOW *w)
    208 {
    209 	int ch;
    210 
    211 	while (TRUE) {
    212 		ch = mgetch(w);
    213 
    214 		switch (ch) {
    215 		case '\n':
    216 			return REQ_EXECUTE;
    217 		case '\016':  /* Contnrol-P */
    218 		case KEYSEQ_DOWN_ARROW:
    219 			return REQ_NEXT_ITEM;
    220 		case '\020':  /* Control-N */
    221 		case KEYSEQ_UP_ARROW:
    222 			return REQ_PREV_ITEM;
    223 		case '\014':  /* Control-L */
    224 		        return REQ_REDISPLAY;
    225 		case '<':
    226 		case '\010':  /* Control-H (backspace) */
    227 		case KEYSEQ_PAGE_UP:
    228 			return REQ_SCROLLUP;
    229 		case '>':
    230 		case ' ':
    231 		case KEYSEQ_PAGE_DOWN:
    232 			return REQ_SCROLLDOWN;
    233 		case '?':
    234 			return REQ_HELP;
    235 		}
    236 
    237 		if (isalpha(ch))
    238 			return (ch);
    239 
    240 		mbeep();
    241 		wrefresh(w);
    242 	}
    243 }
    244 
    245 static void
    246 init_menu (struct menudesc *m)
    247 {
    248 	int wmax;
    249 	int hadd, wadd, exithadd;
    250 	int i;
    251 
    252 	hadd = ((m->mopt & MC_NOBOX) ? 0 : 2);
    253 	wadd = ((m->mopt & MC_NOBOX) ? 2 : 4);
    254 
    255 	hadd += strlen(m->title) != 0 ? 2 : 0;
    256 	exithadd = ((m->mopt & MC_NOEXITOPT) ? 0 : 1);
    257 
    258 	wmax = strlen(m->title);
    259 
    260 	/* Calculate h? h == number of visible options. */
    261 	if (m->h == 0) {
    262 		m->h = m->numopts + exithadd;
    263 		if (m->h + m->y + hadd >= max_lines && (m->mopt & MC_SCROLL))
    264 			m->h = max_lines - m->y - hadd ;
    265 	}
    266 
    267 	/* Check window heights and set scrolling */
    268 	if (m->h < m->numopts + exithadd) {
    269 		if (!(m->mopt & MC_SCROLL) || m->h < 3) {
    270 			endwin();
    271 			(void) fprintf (stderr,
    272 				"Window too short for menu \"%s\"\n",
    273 				m->title);
    274 			exit(1);
    275 		}
    276 	} else
    277 		m->mopt &= ~MC_SCROLL;
    278 
    279 	/* check for screen fit */
    280 	if (m->y + m->h + hadd > max_lines) {
    281 		endwin();
    282 		(void) fprintf (stderr,
    283 			"Screen too short for menu \"%s\"\n", m->title);
    284 		exit(1);
    285 
    286 	}
    287 
    288 	/* Calculate w? */
    289 	if (m->w == 0) {
    290 		if (m->mopt & MC_SCROLL)
    291 			wmax = MAX(wmax,strlen(scrolltext));
    292 		for (i=0; i < m->numopts; i++ )
    293 			wmax = MAX(wmax,strlen(m->opts[i].opt_name)+3);
    294 		m->w = wmax;
    295 	}
    296 
    297 	/* check and adjust for screen fit */
    298 	if (m->w + wadd > max_cols) {
    299 		endwin();
    300 		(void) fprintf (stderr,
    301 			"Screen too narrow for menu \"%s\"\n", m->title);
    302 		exit(1);
    303 
    304 	}
    305 	if (m->x == -1)
    306 		m->x = (max_cols - (m->w + wadd)) / 2;	/* center */
    307 	else if (m->x + m->w + wadd > max_cols)
    308 		m->x = max_cols - (m->w + wadd);
    309 
    310 	/* Get the windows. */
    311 	m->mw = newwin(m->h+hadd, m->w+wadd, m->y, m->x);
    312 
    313 	if (m->mw == NULL) {
    314 		endwin();
    315 		(void) fprintf (stderr,
    316 			"Could not create window for menu \"%s\"\n", m->title);
    317 		exit(1);
    318 	}
    319 
    320 	/* XXX is it even worth doing this right? */
    321 	if (has_colors()) {
    322 		wbkgd(m->mw, COLOR_PAIR(1));
    323 		wattrset(m->mw, COLOR_PAIR(1));
    324 	}
    325 }
    326 
    327 static char
    328 opt_ch (int op_no)
    329 {
    330 	char c;
    331 	if (op_no < 25) {
    332 		c = 'a' + op_no;
    333 		if (c >= 'x') c++;
    334 	} else
    335 		c = 'A' + op_no - 25;
    336 	return (char) c;
    337 }
    338 
    339 static void
    340 post_menu (struct menudesc *m)
    341 {
    342 	int i;
    343 	int hasbox, cury, maxy, selrow, lastopt;
    344 	int tadd;
    345 	char optstr[5];
    346 
    347 	if (m->mopt & MC_NOBOX) {
    348 		cury = 0;
    349 		maxy = m->h;
    350 		hasbox = 0;
    351 	} else {
    352 		cury = 1;
    353 		maxy = m->h+1;
    354 		hasbox = 1;
    355 	}
    356 
    357 	/* Clear the window */
    358 	wclear (m->mw);
    359 
    360 	tadd = strlen(m->title) ? 2 : 0;
    361 
    362 	if (tadd) {
    363 		mvwaddstr(m->mw, cury, cury, " ");
    364 		mvwaddstr(m->mw, cury, cury + 1, m->title);
    365 		cury += 2;
    366 		maxy += 2;
    367 	}
    368 
    369 	/* Set defaults, calculate lastopt. */
    370 	selrow = -1;
    371 	if (m->mopt & MC_SCROLL) {
    372 		lastopt = MIN(m->numopts, m->topline+m->h-1);
    373 		maxy -= 1;
    374 	} else
    375 		lastopt = m->numopts;
    376 
    377 	for (i=m->topline; i<lastopt; i++, cury++) {
    378 		if (m->cursel == i) {
    379 			mvwaddstr (m->mw, cury, hasbox, ">");
    380 			wstandout(m->mw);
    381 			selrow = cury;
    382 		} else
    383 			mvwaddstr (m->mw, cury, hasbox, " ");
    384 		if (!(m->mopt & MC_NOSHORTCUT)) {
    385 			(void) sprintf (optstr, "%c: ", opt_ch(i));
    386 		    	waddstr (m->mw, optstr);
    387 		}
    388 		waddstr (m->mw, m->opts[i].opt_name);
    389 		if (m->cursel == i)
    390 			wstandend(m->mw);
    391 	}
    392 
    393 	/* Add the exit option. */
    394 	if (!(m->mopt & MC_NOEXITOPT) && cury < maxy) {
    395 		if (m->cursel >= m->numopts) {
    396 			mvwaddstr (m->mw, cury, hasbox, ">");
    397 			wstandout(m->mw);
    398 			selrow = cury;
    399 		} else
    400 			mvwaddstr (m->mw, cury, hasbox, " ");
    401 		if (!(m->mopt & MC_NOSHORTCUT))
    402 			waddstr (m->mw, "x: ");
    403 		waddstr (m->mw, m->exitstr);
    404 		if (m->cursel >= m->numopts)
    405 			wstandend(m->mw);
    406 		cury++;
    407 	}
    408 
    409 	/* Add the scroll line */
    410 	if (m->mopt & MC_SCROLL) {
    411 		mvwaddstr (m->mw, cury, hasbox, scrolltext);
    412 		if (selrow < 0)
    413 			selrow = cury;
    414 	}
    415 
    416 	/* Add the box. */
    417 	if (!(m->mopt & MC_NOBOX))
    418 		box(m->mw, 0, 0);
    419 
    420 	wmove(m->mw, selrow, hasbox);
    421 }
    422 
    423 static void
    424 process_help (struct menudesc *m, int num)
    425 {
    426 	char *help = m->helpstr;
    427 	int lineoff = 0;
    428 	int curoff = 0;
    429 	int again;
    430 	int winin;
    431 
    432 	/* Is there help? */
    433 	if (!help) {
    434 		mbeep();
    435 		return;
    436 	}
    437 
    438 	/* Display the help information. */
    439 	do {
    440 		if (lineoff < curoff) {
    441 			help = m->helpstr;
    442 			curoff = 0;
    443 		}
    444 		while (*help && curoff < lineoff) {
    445 			if (*help == '\n')
    446 				curoff++;
    447 			help++;
    448 		}
    449 
    450 		wclear(stdscr);
    451 		mvwaddstr (stdscr, 0, 0,
    452 			"Help: exit: x,  page up: u <, page down: d >");
    453 		mvwaddstr (stdscr, 2, 0, help);
    454 		wmove (stdscr, 1, 0);
    455 	  	wrefresh(stdscr);
    456 
    457 		do {
    458 			winin = mgetch(stdscr);
    459 			if (winin < KEYSEQ_FIRST)
    460 				winin = tolower(winin);
    461 			again = 0;
    462 			switch (winin) {
    463 				case '<':
    464 				case 'u':
    465 				case KEYSEQ_UP_ARROW:
    466 				case KEYSEQ_LEFT_ARROW:
    467 				case KEYSEQ_PAGE_UP:
    468 					if (lineoff)
    469 						lineoff -= max_lines - 2;
    470 					else
    471 						again = 1;
    472 					break;
    473 				case '>':
    474 				case 'd':
    475 				case KEYSEQ_DOWN_ARROW:
    476 				case KEYSEQ_RIGHT_ARROW:
    477 				case KEYSEQ_PAGE_DOWN:
    478 					if (*help)
    479 						lineoff += max_lines - 2;
    480 					else
    481 						again = 1;
    482 					break;
    483 				case 'q':
    484 					break;
    485 				case 'x':
    486 					winin = 'q';
    487 					break;
    488 				default:
    489 					again = 1;
    490 			}
    491 			if (again)
    492 				mbeep();
    493 		} while (again);
    494 	} while (winin != 'q');
    495 
    496 	/* Restore current menu */
    497 	wclear(stdscr);
    498 	wrefresh(stdscr);
    499 	if (m->post_act)
    500 		(*m->post_act)();
    501 }
    502 
    503 static void
    504 process_req (struct menudesc *m, int num, int req)
    505 {
    506 	int ch;
    507 	int hasexit = (m->mopt & MC_NOEXITOPT ? 0 : 1 );
    508 	int refresh = 0;
    509 	int scroll_sel = 0;
    510 
    511 	if (req == REQ_EXECUTE)
    512 		return;
    513 
    514 	else if (req == REQ_NEXT_ITEM) {
    515 		if (m->cursel < m->numopts + hasexit - 1) {
    516 			m->cursel++;
    517 			scroll_sel = 1;
    518 			refresh = 1;
    519 			if (m->mopt & MC_SCROLL &&
    520 			    m->cursel >= m->topline + m->h -1 )
    521 				m->topline += 1;
    522 		} else
    523 			mbeep();
    524 
    525 	} else if (req == REQ_PREV_ITEM) {
    526 		if (m->cursel > 0) {
    527 			m->cursel--;
    528 			scroll_sel = 1;
    529 			refresh = 1;
    530 			if (m->cursel < m->topline )
    531 				m->topline -= 1;
    532 		} else
    533 			mbeep();
    534 
    535 	} else if (req == REQ_REDISPLAY) {
    536 		wclear(stdscr);
    537 		wrefresh(stdscr);
    538 		if (m->post_act)
    539 			(*m->post_act)();
    540 		refresh = 1;
    541 
    542 	} else if (req == REQ_HELP) {
    543 		process_help (m, num);
    544 		refresh = 1;
    545 
    546 	} else if (req == REQ_SCROLLUP) {
    547 		if (!(m->mopt & MC_SCROLL))
    548 			mbeep();
    549 		else if (m->topline == 0)
    550 			mbeep();
    551 		else {
    552 			m->topline = MAX(0,m->topline-m->h+1);
    553 			m->cursel = MAX(0, m->cursel-m->h+1);
    554 			wclear (m->mw);
    555 			refresh = 1;
    556 		}
    557 
    558 	} else if (req == REQ_SCROLLDOWN) {
    559 		if (!(m->mopt & MC_SCROLL))
    560 			mbeep();
    561 		else if (m->topline + m->h - 1 >= m->numopts + hasexit)
    562 			mbeep();
    563 		else {
    564 			m->topline = MIN(m->topline+m->h-1,
    565 					 m->numopts+hasexit-m->h+1);
    566 			m->cursel = MIN(m->numopts-1, m->cursel+m->h-1);
    567 			wclear (m->mw);
    568 			refresh = 1;
    569 		}
    570 
    571 	} else {
    572 		ch = req;
    573 		if (ch == 'x' && hasexit) {
    574 			m->cursel = m->numopts;
    575 			scroll_sel = 1;
    576 			refresh = 1;
    577 		} else
    578 			if (!(m->mopt & MC_NOSHORTCUT)) {
    579 				if (ch > 'z')
    580 					ch = 255;
    581 				if (ch >= 'a') {
    582 					if (ch > 'x') ch--;
    583 				   		ch = ch - 'a';
    584 				} else
    585 					ch = 25 + ch - 'A';
    586 				if (ch < 0 || ch >= m->numopts)
    587 					mbeep();
    588 				else {
    589 					m->cursel = ch;
    590 					scroll_sel = 1;
    591 					refresh = 1;
    592 				}
    593 			} else
    594 				mbeep();
    595 	}
    596 
    597 	if (m->mopt & MC_SCROLL && scroll_sel) {
    598 		while (m->cursel >= m->topline + m->h -1 )
    599 			m->topline = MIN(m->topline+m->h-1,
    600 					 m->numopts+hasexit-m->h+1);
    601 		while (m->cursel < m->topline)
    602 			m->topline = MAX(0,m->topline-m->h+1);
    603 	}
    604 
    605 	if (refresh) {
    606 		post_menu (m);
    607 		wrefresh (m->mw);
    608 	}
    609 }
    610 
    611 int
    612 menu_init (void)
    613 {
    614 
    615 	if (__menu_init)
    616 		return 0;
    617 
    618 	if (initscr() == NULL)
    619 		return 1;
    620 
    621 	cbreak();
    622 	noecho();
    623 
    624 	/* XXX Should be configurable but it almost isn't worth it. */
    625 	if (has_colors()) {
    626 		start_color();
    627 		init_pair(1, COLOR_WHITE, COLOR_BLUE);
    628 		bkgd(COLOR_PAIR(1));
    629 		attrset(COLOR_PAIR(1));
    630 	}
    631 
    632 	max_lines = getmaxy(stdscr);
    633 	max_cols = getmaxx(stdscr);
    634 	init_keyseq();
    635 #ifdef DYNAMIC_MENUS
    636 	num_menus = DYN_INIT_NUM;
    637 	while (num_menus < DYN_MENU_START)
    638 		num_menus *= 2;
    639 	menus = (menudesc *) malloc(sizeof(menudesc)*num_menus);
    640 	if (menus == NULL)
    641 		return 2;
    642 	(void) memset ((void *)menus, 0, sizeof(menudesc)*num_menus);
    643 	(void) memcpy ((void *)menus, (void *)menu_def,
    644 		sizeof(menudesc)*DYN_MENU_START);
    645 	 num_avail = num_menus - DYN_MENU_START;
    646 #endif
    647 
    648 	__menu_init = 1;
    649 	return (0);
    650 }
    651 
    652 void
    653 process_menu (int num)
    654 {
    655 	int sel = 0;
    656 	int req, done;
    657 	int last_num;
    658 
    659 	struct menudesc *m;
    660 
    661 	m = &menus[num];
    662 
    663 	done = FALSE;
    664 
    665 	/* Initialize? */
    666 	if (menu_init()) {
    667 		__menu_initerror();
    668 		return;
    669 	}
    670 
    671 	if (__m_endwin) {
    672      		wclear(stdscr);
    673 		wrefresh(stdscr);
    674 		__m_endwin = 0;
    675 	}
    676 	if (m->mw == NULL)
    677 		init_menu (m);
    678 
    679 	/* Always preselect option 0 and display from 0! */
    680 	m->cursel = 0;
    681 	m->topline = 0;
    682 
    683 	while (!done) {
    684 		last_num = num;
    685 		if (__m_endwin) {
    686 			wclear(stdscr);
    687 			wrefresh(stdscr);
    688 			__m_endwin = 0;
    689 		}
    690 		/* Process the display action */
    691 		if (m->post_act)
    692 			(*m->post_act)();
    693 		post_menu (m);
    694 		wrefresh (m->mw);
    695 
    696 		while ((req = menucmd (m->mw)) != REQ_EXECUTE)
    697 			process_req (m, num, req);
    698 
    699 		sel = m->cursel;
    700 		wclear (m->mw);
    701 		wrefresh (m->mw);
    702 
    703 		/* Process the items */
    704 		if (sel < m->numopts) {
    705 			if (m->opts[sel].opt_flags & OPT_ENDWIN) {
    706 				endwin();
    707 				__m_endwin = 1;
    708 			}
    709 			if (m->opts[sel].opt_action)
    710 				done = (*m->opts[sel].opt_action)(m);
    711 			if (m->opts[sel].opt_menu != -1) {
    712 				if (m->opts[sel].opt_flags & OPT_SUB)
    713 					process_menu (m->opts[sel].opt_menu);
    714 				else
    715 					num = m->opts[sel].opt_menu;
    716 			}
    717 
    718                         if (m->opts[sel].opt_flags & OPT_EXIT)
    719                                 done = TRUE;
    720 
    721 		} else
    722 			done = TRUE;
    723 
    724 		/* Reselect m just in case */
    725 		if (num != last_num) {
    726 			m = &menus[num];
    727 
    728 			/* Initialize? */
    729 			if (m->mw == NULL)
    730 				init_menu (m);
    731 			if (m->post_act)
    732 				(*m->post_act)();
    733 		}
    734 	}
    735 
    736 	/* Process the exit action */
    737 	if (m->exit_act)
    738 		(*m->exit_act)();
    739 }
    740 
    741 /* Control L is end of standard routines, remaining only for dynamic. */
    743 
    744 /* Beginning of routines for dynamic menus. */
    745 
    746 /* local prototypes */
    747 static int double_menus (void);
    748 
    749 static int
    750 double_menus (void)
    751 {
    752 	menudesc *temp;
    753 
    754 	temp  = (menudesc *) malloc(sizeof(menudesc)*num_menus*2);
    755 	if (temp == NULL)
    756 		return 0;
    757 	(void) memset ((void *)temp, 0,
    758 		sizeof(menudesc)*num_menus*2);
    759 	(void) memcpy ((void *)temp, (void *)menus,
    760 		sizeof(menudesc)*num_menus);
    761 	free (menus);
    762 	menus = temp;
    763 	num_avail = num_menus;
    764 	num_menus *= 2;
    765 
    766 	return 1;
    767 }
    768 
    769 int
    770 new_menu (char * title, menu_ent * opts, int numopts,
    771 	int x, int y, int h, int w, int mopt,
    772 	void (*post_act)(void), void (*exit_act)(void), char * help)
    773 {
    774 	int ix;
    775 
    776 	/* Check for free menu entry. */
    777 	if (num_avail == 0)
    778 		if (!double_menus ())
    779 			return -1;
    780 
    781 	/* Find free menu entry. */
    782 	for (ix = DYN_MENU_START; ix < num_menus && menus[ix].mopt & MC_VALID;
    783 		ix++) /* do  nothing */;
    784 
    785 	/* if ix == num_menus ... panic */
    786 
    787 	/* Set Entries */
    788 	menus[ix].title = title ? title : "";
    789 	menus[ix].opts = opts;
    790 	menus[ix].numopts = numopts;
    791 	menus[ix].x = x;
    792 	menus[ix].y = y;
    793 	menus[ix].h = h;
    794 	menus[ix].w = w;
    795 	menus[ix].mopt = mopt | MC_VALID;
    796 	menus[ix].post_act = post_act;
    797 	menus[ix].exit_act = exit_act;
    798 	menus[ix].helpstr  = help;
    799 	menus[ix].exitstr  = "Exit";
    800 
    801 	init_menu (&menus[ix]);
    802 
    803 	return ix;
    804 }
    805 
    806 void
    807 free_menu (int menu_no)
    808 {
    809 	if (menu_no < num_menus) {
    810 		menus[menu_no].mopt &= ~MC_VALID;
    811 		if (menus[menu_no].mw != NULL)
    812 			delwin (menus[menu_no].mw);
    813 	}
    814 }
    815