1/*
2** Copyright (C) 1991, 1997 Free Software Foundation, Inc.
3**
4** This file is part of TACK.
5**
6** TACK is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2, or (at your option)
9** any later version.
10**
11** TACK is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with TACK; see the file COPYING.  If not, write to
18** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19** Boston, MA 02110-1301, USA
20*/
21
22#include <tack.h>
23
24MODULE_ID("$Id: menu.c,v 1.3 2005/09/17 19:49:16 tom Exp $")
25
26/*
27   Menu control
28 */
29
30static void test_byname(struct test_menu *, int *, int *);
31
32struct test_list *augment_test;
33char prompt_string[80];	/* menu prompt storage */
34
35/*
36**	menu_prompt()
37**
38**	Print the menu prompt string.
39*/
40void
41menu_prompt(void)
42{
43	ptext(&prompt_string[1]);
44}
45
46/*
47**	menu_test_loop(test-structure, state, control-character)
48**
49**	This function implements the repeat test function.
50*/
51static void
52menu_test_loop(
53	struct test_list *test,
54	int *state,
55	int *ch)
56{
57	int nch, p;
58
59	if ((test->flags & MENU_REP_MASK) && (augment_test != test)) {
60		/* set the augment variable (first time only) */
61		p = (test->flags >> 8) & 15;
62		if ((test->flags & MENU_REP_MASK) == MENU_LM1) {
63			augment = lines - 1;
64		} else
65		if ((test->flags & MENU_ONE_MASK) == MENU_ONE) {
66			augment = 1;
67		} else
68		if ((test->flags & MENU_LC_MASK) == MENU_lines) {
69			augment = lines * p / 10;
70		} else
71		if ((test->flags & MENU_LC_MASK) == MENU_columns) {
72			augment = columns * p / 10;
73		} else {
74			augment = 1;
75		}
76		augment_test = test;
77		set_augment_txt();
78	}
79	do {
80		if ((test->flags | *state) & MENU_CLEAR) {
81			put_clear();
82		} else
83		if (line_count + test->lines_needed >= lines) {
84			put_clear();
85		}
86		nch = 0;
87		if (test->test_procedure) {
88			/* The procedure takes precedence so I can pass
89			   the menu entry as an argument.
90			*/
91			can_test(test->caps_done, FLAG_TESTED);
92			can_test(test->caps_tested, FLAG_TESTED);
93			test->test_procedure(test, state, &nch);
94		} else
95		if (test->sub_menu) {
96			/* nested menu's */
97			menu_display(test->sub_menu, &nch);
98			*state = 0;
99			if (nch == 'q' || nch == 's') {
100				/* Quit and skip are killed here */
101				nch = '?';
102			}
103		} else {
104			break;	/* cya */
105		}
106		if (nch == '\r' || nch == '\n' || nch == 'n') {
107			nch = 0;
108			break;
109		}
110	} while (nch == 'r');
111	*ch = nch;
112}
113
114/*
115**	menu_display(menu-structure, flags)
116**
117**	This function implements menu control.
118*/
119void
120menu_display(
121	struct test_menu *menu,
122	int *last_ch)
123{
124	int test_state = 0, run_standard_tests;
125	int hot_topic, ch = 0, nch = 0;
126	struct test_list *mt;
127	struct test_list *repeat_tests = 0;
128	int repeat_state = 0;
129	int prompt_length;
130
131	prompt_length = strlen(prompt_string);
132	if (menu->ident) {
133		sprintf(&prompt_string[prompt_length], "/%s", menu->ident);
134	}
135	hot_topic = menu->default_action;
136	run_standard_tests = menu->standard_tests ?
137		menu->standard_tests[0] : -1;
138	if (!last_ch) {
139		last_ch = &ch;
140	}
141	while (1) {
142		if (ch == 0) {
143			/* Display the menu */
144			put_crlf();
145			if (menu->menu_function) {
146				/*
147				   this function may be used to restrict menu
148				   entries.  If used it must print the title.
149				*/
150				menu->menu_function(menu);
151			} else
152			if (menu->menu_title) {
153				ptextln(menu->menu_title);
154			}
155			for (mt = menu->tests; (mt->flags & MENU_LAST) == 0; mt++) {
156				if (mt->menu_entry) {
157					ptext(" ");
158					ptextln(mt->menu_entry);
159				}
160			}
161			if (menu->standard_tests) {
162				ptext(" ");
163				ptextln(menu->standard_tests);
164				ptextln(" r) repeat test");
165				ptextln(" s) skip to next test");
166			}
167			ptextln(" q) quit");
168			ptextln(" ?) help");
169		}
170		if (ch == 0 || ch == REQUEST_PROMPT) {
171			put_crlf();
172			ptext(&prompt_string[1]);
173			if (hot_topic) {
174				ptext(" [");
175				putchp(hot_topic);
176				ptext("]");
177			}
178			ptext(" > ");
179			/* read a character */
180			ch = wait_here();
181		}
182		if (ch == '\r' || ch == '\n') {
183			ch = hot_topic;
184		}
185		if (ch == 'q') {
186			break;
187		}
188		if (ch == '?') {
189			ch = 0;
190			continue;
191		}
192		nch = ch;
193		ch = 0;
194		/* Run one of the standard tests (by request) */
195		for (mt = menu->tests; (mt->flags & MENU_LAST) == 0; mt++) {
196			if (mt->menu_entry && (nch == mt->menu_entry[0])) {
197				if (mt->flags & MENU_MENU) {
198					test_byname(menu, &test_state, &nch);
199				} else {
200					menu_test_loop(mt, &test_state, &nch);
201				}
202				ch = nch;
203				if ((mt->flags & MENU_COMPLETE) && ch == 0) {
204					/* top level */
205					hot_topic = 'q';
206					ch = '?';
207				}
208			}
209		}
210		if (menu->standard_tests && nch == 'r') {
211			menu->resume_tests = repeat_tests;
212			test_state = repeat_state;
213			nch = run_standard_tests;
214		}
215		if (nch == run_standard_tests) {
216			if (!(mt = menu->resume_tests)) {
217				mt = menu->tests;
218			}
219			if (mt->flags & MENU_LAST) {
220				mt = menu->tests;
221			}
222			/* Run the standard test suite */
223			for ( ; (mt->flags & MENU_LAST) == 0; ) {
224				if ((mt->flags & MENU_NEXT) == MENU_NEXT) {
225					repeat_tests = mt;
226					repeat_state = test_state;
227					nch = run_standard_tests;
228					menu_test_loop(mt, &test_state, &nch);
229					if (nch != 0 && nch != 'n') {
230						ch = nch;
231						break;
232					}
233					if (test_state & MENU_STOP) {
234						break;
235					}
236				}
237				mt++;
238			}
239			if (ch == 0) {
240				ch = hot_topic;
241			}
242			menu->resume_tests = mt;
243			menu->resume_state = test_state;
244			menu->resume_char = ch;
245
246			if (ch == run_standard_tests) {
247				/* pop up a level */
248				break;
249			}
250		}
251	}
252	*last_ch = ch;
253	prompt_string[prompt_length] = '\0';
254}
255
256/*
257**	generic_done_message(test_list)
258**
259**	Print the Done message and request input.
260*/
261void
262generic_done_message(
263	struct test_list *test,
264	int *state,
265	int *ch)
266{
267	char done_message[128];
268
269	if (test->caps_done) {
270		sprintf(done_message, "(%s) Done ", test->caps_done);
271		ptext(done_message);
272	} else {
273		ptext("Done ");
274	}
275	*ch = wait_here();
276	if (*ch == '\r' || *ch == '\n' || *ch == 'n') {
277		*ch = 0;
278	}
279	if (*ch == 's') {
280		*state |= MENU_STOP;
281		*ch = 0;
282	}
283}
284
285/*
286**	menu_clear_screen(test, state, ch)
287**
288**	Just clear the screen.
289*/
290void
291menu_clear_screen(
292	struct test_list *test GCC_UNUSED,
293	int *state GCC_UNUSED,
294	int *ch GCC_UNUSED)
295{
296	put_clear();
297}
298
299/*
300**	menu_reset_init(test, state, ch)
301**
302**	Send the reset and init strings.
303*/
304void
305menu_reset_init(
306	struct test_list *test GCC_UNUSED,
307	int *state GCC_UNUSED,
308	int *ch GCC_UNUSED)
309{
310	reset_init();
311	put_crlf();
312}
313
314/*
315**	subtest_menu(test, state, ch)
316**
317**	Scan the menu looking for something to execute
318**	Return TRUE if we found anything.
319*/
320int
321subtest_menu(
322	struct test_list *test,
323	int *state,
324	int *ch)
325{
326	struct test_list *mt;
327
328	if (*ch) {
329		for (mt = test; (mt->flags & MENU_LAST) == 0; mt++) {
330			if (mt->menu_entry && (*ch == mt->menu_entry[0])) {
331				*ch = 0;
332				menu_test_loop(mt, state, ch);
333				return TRUE;
334			}
335		}
336	}
337	return FALSE;
338}
339
340/*
341**	menu_can_scan(menu-structure)
342**
343**	Recursively scan the menu tree and find which cap names can be tested.
344*/
345void
346menu_can_scan(
347	const struct test_menu *menu)
348{
349	struct test_list *mt;
350
351	for (mt = menu->tests; (mt->flags & MENU_LAST) == 0; mt++) {
352		can_test(mt->caps_done, FLAG_CAN_TEST);
353		can_test(mt->caps_tested, FLAG_CAN_TEST);
354		if (!(mt->test_procedure)) {
355			if (mt->sub_menu) {
356				menu_can_scan(mt->sub_menu);
357			}
358		}
359	}
360}
361
362/*
363**	menu_search(menu-structure, cap)
364**
365**	Recursively search the menu tree and execute any tests that use cap.
366*/
367static void
368menu_search(
369	struct test_menu *menu,
370	int *state,
371	int *ch,
372	char *cap)
373{
374	struct test_list *mt;
375	int nch;
376
377	for (mt = menu->tests; (mt->flags & MENU_LAST) == 0; mt++) {
378		nch = 0;
379		if (cap_match(mt->caps_done, cap)
380			|| cap_match(mt->caps_tested, cap)) {
381			menu_test_loop(mt, state, &nch);
382		}
383		if (!(mt->test_procedure)) {
384			if (mt->sub_menu) {
385				menu_search(mt->sub_menu, state, &nch, cap);
386			}
387		}
388		if (*state & MENU_STOP) {
389			break;
390		}
391		if (nch != 0 && nch != 'n') {
392			*ch = nch;
393			break;
394		}
395	}
396}
397
398/*
399**	test_byname(menu, state, ch)
400**
401**	Get a cap name then run all tests that use that cap.
402*/
403static void
404test_byname(
405	struct test_menu *menu,
406	int *state GCC_UNUSED,
407	int *ch)
408{
409	int test_state = 0;
410	char cap[32];
411
412	if (tty_can_sync == SYNC_NOT_TESTED) {
413		verify_time();
414	}
415	ptext("enter name: ");
416	read_string(cap, sizeof(cap));
417	if (cap[0]) {
418		menu_search(menu, &test_state, ch, cap);
419	}
420	*ch = '?';
421}
422