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