xref: /freebsd/stand/lua/menu.lua (revision 57099121d558d6da23415edc05da07c77884f24b)
1088b4f5fSWarner Losh--
2088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
3088b4f5fSWarner Losh-- All rights reserved.
4088b4f5fSWarner Losh--
5088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without
6088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions
7088b4f5fSWarner Losh-- are met:
8088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright
9088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer.
10088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright
11088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer in the
12088b4f5fSWarner Losh--    documentation and/or other materials provided with the distribution.
13088b4f5fSWarner Losh--
14088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17088b4f5fSWarner Losh-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24088b4f5fSWarner Losh-- SUCH DAMAGE.
25088b4f5fSWarner Losh--
26088b4f5fSWarner Losh-- $FreeBSD$
27088b4f5fSWarner Losh--
28088b4f5fSWarner Losh
29088b4f5fSWarner Losh
30088b4f5fSWarner Loshlocal menu = {};
31088b4f5fSWarner Losh
32088b4f5fSWarner Loshlocal core = require("core");
33088b4f5fSWarner Loshlocal color = require("color");
34088b4f5fSWarner Loshlocal config = require("config");
35088b4f5fSWarner Loshlocal screen = require("screen");
36088b4f5fSWarner Loshlocal drawer = require("drawer");
37088b4f5fSWarner Losh
38088b4f5fSWarner Loshlocal OnOff;
39088b4f5fSWarner Loshlocal skip;
40088b4f5fSWarner Loshlocal run;
41088b4f5fSWarner Loshlocal autoboot;
42ada26c4aSKyle Evanslocal carousel_choices = {};
43088b4f5fSWarner Losh
44088b4f5fSWarner Losh--loader menu tree:
45088b4f5fSWarner Losh--rooted at menu.welcome
46088b4f5fSWarner Losh--submenu declarations:
47088b4f5fSWarner Loshlocal boot_options;
48088b4f5fSWarner Loshlocal welcome;
49088b4f5fSWarner Losh
50088b4f5fSWarner Loshmenu.boot_options = {
51088b4f5fSWarner Losh	-- return to welcome menu
52088b4f5fSWarner Losh	{
53a7cf0562SKyle Evans		entry_type = core.MENU_RETURN,
54088b4f5fSWarner Losh		name = function()
55*57099121SKyle Evans			return "Back to main menu" ..
56*57099121SKyle Evans			    color.highlight(" [Backspace]");
571666dfc0SKyle Evans		end
58088b4f5fSWarner Losh	},
59088b4f5fSWarner Losh
60088b4f5fSWarner Losh	-- load defaults
61088b4f5fSWarner Losh	{
62a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
63088b4f5fSWarner Losh		name = function()
64*57099121SKyle Evans			return "Load System " .. color.highlight("D") ..
65*57099121SKyle Evans			    "efaults";
66088b4f5fSWarner Losh		end,
67088b4f5fSWarner Losh		func = function()
6824a1bd54SKyle Evans			core.setDefaults();
69088b4f5fSWarner Losh		end,
70088b4f5fSWarner Losh		alias = {"d", "D"}
71088b4f5fSWarner Losh	},
72088b4f5fSWarner Losh
73088b4f5fSWarner Losh	{
74a7cf0562SKyle Evans		entry_type = core.MENU_SEPARATOR,
75088b4f5fSWarner Losh		name = function()
76088b4f5fSWarner Losh			return "";
77088b4f5fSWarner Losh		end
78088b4f5fSWarner Losh	},
79088b4f5fSWarner Losh
80088b4f5fSWarner Losh	{
81a7cf0562SKyle Evans		entry_type = core.MENU_SEPARATOR,
82088b4f5fSWarner Losh		name = function()
83088b4f5fSWarner Losh			return "Boot Options:";
84088b4f5fSWarner Losh		end
85088b4f5fSWarner Losh	},
86088b4f5fSWarner Losh
87088b4f5fSWarner Losh	-- acpi
88088b4f5fSWarner Losh	{
89a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
90088b4f5fSWarner Losh		name = function()
91*57099121SKyle Evans			return OnOff(color.highlight("A") .. "CPI       :",
92*57099121SKyle Evans			    core.acpi);
93088b4f5fSWarner Losh		end,
94088b4f5fSWarner Losh		func = function()
95088b4f5fSWarner Losh			core.setACPI();
96088b4f5fSWarner Losh		end,
97088b4f5fSWarner Losh		alias = {"a", "A"}
98088b4f5fSWarner Losh	},
99088b4f5fSWarner Losh	-- safe mode
100088b4f5fSWarner Losh	{
101a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
102088b4f5fSWarner Losh		name = function()
103*57099121SKyle Evans			return OnOff("Safe " .. color.highlight("M") ..
104*57099121SKyle Evans			    "ode  :", core.sm);
105088b4f5fSWarner Losh		end,
106088b4f5fSWarner Losh		func = function()
107088b4f5fSWarner Losh			core.setSafeMode();
108088b4f5fSWarner Losh		end,
109088b4f5fSWarner Losh		alias = {"m", "M"}
110088b4f5fSWarner Losh	},
111088b4f5fSWarner Losh	-- single user
112088b4f5fSWarner Losh	{
113a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
114088b4f5fSWarner Losh		name = function()
115*57099121SKyle Evans			return OnOff(color.highlight("S") .. "ingle user:",
116*57099121SKyle Evans			    core.su);
117088b4f5fSWarner Losh		end,
118088b4f5fSWarner Losh		func = function()
119088b4f5fSWarner Losh			core.setSingleUser();
120088b4f5fSWarner Losh		end,
121088b4f5fSWarner Losh		alias = {"s", "S"}
122088b4f5fSWarner Losh	},
123088b4f5fSWarner Losh	-- verbose boot
124088b4f5fSWarner Losh	{
125a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
126088b4f5fSWarner Losh		name = function()
127*57099121SKyle Evans			return OnOff(color.highlight("V") .. "erbose    :",
128*57099121SKyle Evans			    core.verbose);
129088b4f5fSWarner Losh		end,
130088b4f5fSWarner Losh		func = function()
131088b4f5fSWarner Losh			core.setVerbose();
132088b4f5fSWarner Losh		end,
133088b4f5fSWarner Losh		alias = {"v", "V"}
134088b4f5fSWarner Losh	},
135088b4f5fSWarner Losh};
136088b4f5fSWarner Losh
137088b4f5fSWarner Loshmenu.welcome = {
138088b4f5fSWarner Losh	-- boot multi user
139088b4f5fSWarner Losh	{
140a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
141088b4f5fSWarner Losh		name = function()
142*57099121SKyle Evans			return color.highlight("B") .. "oot Multi user " ..
143*57099121SKyle Evans			    color.highlight("[Enter]");
144088b4f5fSWarner Losh		end,
145088b4f5fSWarner Losh		func = function()
146088b4f5fSWarner Losh			core.setSingleUser(false);
147088b4f5fSWarner Losh			core.boot();
148088b4f5fSWarner Losh		end,
149b458bf0dSKyle Evans		alias = {"b", "B"}
150088b4f5fSWarner Losh	},
151088b4f5fSWarner Losh
152088b4f5fSWarner Losh	-- boot single user
153088b4f5fSWarner Losh	{
154a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
155088b4f5fSWarner Losh		name = function()
156088b4f5fSWarner Losh			return "Boot " .. color.highlight("S") .. "ingle user";
157088b4f5fSWarner Losh		end,
158088b4f5fSWarner Losh		func = function()
159088b4f5fSWarner Losh			core.setSingleUser(true);
160088b4f5fSWarner Losh			core.boot();
161088b4f5fSWarner Losh		end,
162088b4f5fSWarner Losh		alias = {"s", "S"}
163088b4f5fSWarner Losh	},
164088b4f5fSWarner Losh
165088b4f5fSWarner Losh	-- escape to interpreter
166088b4f5fSWarner Losh	{
167a7cf0562SKyle Evans		entry_type = core.MENU_RETURN,
168088b4f5fSWarner Losh		name = function()
169e9084012SKyle Evans			return color.highlight("Esc") .. "ape to loader prompt";
170088b4f5fSWarner Losh		end,
171ef625845SKyle Evans		func = function()
17224a1bd54SKyle Evans			loader.setenv("autoboot_delay", "NO");
173ef625845SKyle Evans		end,
17439006570SKyle Evans		alias = {core.KEYSTR_ESCAPE}
175088b4f5fSWarner Losh	},
176088b4f5fSWarner Losh
177088b4f5fSWarner Losh	-- reboot
178088b4f5fSWarner Losh	{
179a7cf0562SKyle Evans		entry_type = core.MENU_ENTRY,
180088b4f5fSWarner Losh		name = function()
181088b4f5fSWarner Losh			return color.highlight("R") .. "eboot";
182088b4f5fSWarner Losh		end,
183088b4f5fSWarner Losh		func = function()
184088b4f5fSWarner Losh			loader.perform("reboot");
185088b4f5fSWarner Losh		end,
186088b4f5fSWarner Losh		alias = {"r", "R"}
187088b4f5fSWarner Losh	},
188088b4f5fSWarner Losh
189088b4f5fSWarner Losh
190088b4f5fSWarner Losh	{
191a7cf0562SKyle Evans		entry_type = core.MENU_SEPARATOR,
192088b4f5fSWarner Losh		name = function()
193088b4f5fSWarner Losh			return "";
194088b4f5fSWarner Losh		end
195088b4f5fSWarner Losh	},
196088b4f5fSWarner Losh
197088b4f5fSWarner Losh	{
198a7cf0562SKyle Evans		entry_type = core.MENU_SEPARATOR,
199088b4f5fSWarner Losh		name = function()
200088b4f5fSWarner Losh			return "Options:";
201088b4f5fSWarner Losh		end
202088b4f5fSWarner Losh	},
203088b4f5fSWarner Losh
204088b4f5fSWarner Losh	-- kernel options
205088b4f5fSWarner Losh	{
206a7cf0562SKyle Evans		entry_type = core.MENU_CAROUSEL_ENTRY,
207ada26c4aSKyle Evans		carousel_id = "kernel",
208ada26c4aSKyle Evans		items = core.kernelList,
209ada26c4aSKyle Evans		name = function(idx, choice, all_choices)
21024a1bd54SKyle Evans			if (#all_choices == 0) then
211b1b1f2b8SKyle Evans				return "Kernel: ";
212088b4f5fSWarner Losh			end
213b1b1f2b8SKyle Evans
214bcf48a15SKyle Evans			local is_default = (idx == 1);
215bcf48a15SKyle Evans			local kernel_name = "";
216bcf48a15SKyle Evans			local name_color;
21724a1bd54SKyle Evans			if (is_default) then
218bcf48a15SKyle Evans				name_color = color.escapef(color.GREEN);
219bcf48a15SKyle Evans				kernel_name = "default/";
220bcf48a15SKyle Evans			else
221bcf48a15SKyle Evans				name_color = color.escapef(color.BLUE);
222b1b1f2b8SKyle Evans			end
223bcf48a15SKyle Evans			kernel_name = kernel_name .. name_color .. choice ..
224bcf48a15SKyle Evans			    color.default();
225b1b1f2b8SKyle Evans			return color.highlight("K").."ernel: " .. kernel_name ..
226ada26c4aSKyle Evans			    " (" .. idx ..
227ada26c4aSKyle Evans			    " of " .. #all_choices .. ")";
228088b4f5fSWarner Losh		end,
2295d1e2f83SKyle Evans		func = function(idx, choice, all_choices)
230fa4a2394SKyle Evans			config.selectkernel(choice);
231088b4f5fSWarner Losh		end,
232088b4f5fSWarner Losh		alias = {"k", "K"}
233088b4f5fSWarner Losh	},
234088b4f5fSWarner Losh
235088b4f5fSWarner Losh	-- boot options
236088b4f5fSWarner Losh	{
237a7cf0562SKyle Evans		entry_type = core.MENU_SUBMENU,
238088b4f5fSWarner Losh		name = function()
239088b4f5fSWarner Losh			return "Boot " .. color.highlight("O") .. "ptions";
240088b4f5fSWarner Losh		end,
241088b4f5fSWarner Losh		submenu = function()
242088b4f5fSWarner Losh			return menu.boot_options;
243088b4f5fSWarner Losh		end,
244088b4f5fSWarner Losh		alias = {"o", "O"}
245088b4f5fSWarner Losh	}
246088b4f5fSWarner Losh
247088b4f5fSWarner Losh};
248088b4f5fSWarner Losh
249ada26c4aSKyle Evans-- The first item in every carousel is always the default item.
250ada26c4aSKyle Evansfunction menu.getCarouselIndex(id)
251ada26c4aSKyle Evans	local val = carousel_choices[id];
252ada26c4aSKyle Evans	if (val == nil) then
253ada26c4aSKyle Evans		return 1;
254ada26c4aSKyle Evans	end
255ada26c4aSKyle Evans	return val;
256ada26c4aSKyle Evansend
257ada26c4aSKyle Evans
258ada26c4aSKyle Evansfunction menu.setCarouselIndex(id, idx)
259ada26c4aSKyle Evans	carousel_choices[id] = idx;
260ada26c4aSKyle Evansend
261ada26c4aSKyle Evans
262088b4f5fSWarner Loshfunction menu.run(m)
263088b4f5fSWarner Losh
264088b4f5fSWarner Losh	if (menu.skip()) then
265088b4f5fSWarner Losh		core.autoboot();
266088b4f5fSWarner Losh		return false;
267088b4f5fSWarner Losh	end
268088b4f5fSWarner Losh
269088b4f5fSWarner Losh	if (m == nil) then
270088b4f5fSWarner Losh		m = menu.welcome;
271088b4f5fSWarner Losh	end
272088b4f5fSWarner Losh
273088b4f5fSWarner Losh	-- redraw screen
274088b4f5fSWarner Losh	screen.clear();
275088b4f5fSWarner Losh	screen.defcursor();
276088b4f5fSWarner Losh	local alias_table = drawer.drawscreen(m);
277088b4f5fSWarner Losh
2783a0a07d0SKyle Evans	menu.autoboot();
279088b4f5fSWarner Losh
280088b4f5fSWarner Losh	cont = true;
28124a1bd54SKyle Evans	while (cont) do
282abc4f7e7SKyle Evans		local key = io.getchar();
283088b4f5fSWarner Losh
284b458bf0dSKyle Evans		-- Special key behaviors
2851504bce3SKyle Evans		if ((key == core.KEY_BACKSPACE) or (key == core.KEY_DELETE)) and
2861504bce3SKyle Evans		    (m ~= menu.welcome) then
287abc4f7e7SKyle Evans			break
288fe672a15SKyle Evans		elseif (key == core.KEY_ENTER) then
289b458bf0dSKyle Evans			core.boot();
290b458bf0dSKyle Evans			-- Should not return
291abc4f7e7SKyle Evans		end
292abc4f7e7SKyle Evans
293abc4f7e7SKyle Evans		key = string.char(key)
294088b4f5fSWarner Losh		-- check to see if key is an alias
295088b4f5fSWarner Losh		local sel_entry = nil;
296088b4f5fSWarner Losh		for k, v in pairs(alias_table) do
297088b4f5fSWarner Losh			if (key == k) then
298088b4f5fSWarner Losh				sel_entry = v;
299088b4f5fSWarner Losh			end
300088b4f5fSWarner Losh		end
301088b4f5fSWarner Losh
302088b4f5fSWarner Losh		-- if we have an alias do the assigned action:
303088b4f5fSWarner Losh		if (sel_entry ~= nil) then
304a7cf0562SKyle Evans			if (sel_entry.entry_type == core.MENU_ENTRY) then
305088b4f5fSWarner Losh				-- run function
306088b4f5fSWarner Losh				sel_entry.func();
307a7cf0562SKyle Evans			elseif (sel_entry.entry_type == core.MENU_CAROUSEL_ENTRY) then
308ada26c4aSKyle Evans				-- carousel (rotating) functionality
309ada26c4aSKyle Evans				local carid = sel_entry.carousel_id;
310ada26c4aSKyle Evans				local caridx = menu.getCarouselIndex(carid);
311ada26c4aSKyle Evans				local choices = sel_entry.items();
312ada26c4aSKyle Evans
313aefcaa7eSKyle Evans				if (#choices > 0) then
314ada26c4aSKyle Evans					caridx = (caridx % #choices) + 1;
315ada26c4aSKyle Evans					menu.setCarouselIndex(carid, caridx);
3165d1e2f83SKyle Evans					sel_entry.func(caridx, choices[caridx],
31784f82e46SKyle Evans					    choices);
318aefcaa7eSKyle Evans				end
319a7cf0562SKyle Evans			elseif (sel_entry.entry_type == core.MENU_SUBMENU) then
320088b4f5fSWarner Losh				-- recurse
321088b4f5fSWarner Losh				cont = menu.run(sel_entry.submenu());
322a7cf0562SKyle Evans			elseif (sel_entry.entry_type == core.MENU_RETURN) then
323ef625845SKyle Evans				-- allow entry to have a function/side effect
324ef625845SKyle Evans				if (sel_entry.func ~= nil) then
325ef625845SKyle Evans					sel_entry.func();
326ef625845SKyle Evans				end
327088b4f5fSWarner Losh				-- break recurse
328088b4f5fSWarner Losh				cont = false;
329088b4f5fSWarner Losh			end
330088b4f5fSWarner Losh			-- if we got an alias key the screen is out of date:
331088b4f5fSWarner Losh			screen.clear();
332088b4f5fSWarner Losh			screen.defcursor();
333088b4f5fSWarner Losh			alias_table = drawer.drawscreen(m);
334088b4f5fSWarner Losh		end
335088b4f5fSWarner Losh	end
336088b4f5fSWarner Losh
337088b4f5fSWarner Losh	if (m == menu.welcome) then
338088b4f5fSWarner Losh		screen.defcursor();
339088b4f5fSWarner Losh		print("Exiting menu!");
340fa4a2394SKyle Evans		config.loadelf();
341088b4f5fSWarner Losh		return false;
342088b4f5fSWarner Losh	end
343088b4f5fSWarner Losh
344088b4f5fSWarner Losh	return true;
345088b4f5fSWarner Loshend
346088b4f5fSWarner Losh
347088b4f5fSWarner Loshfunction menu.skip()
348b140d14bSKyle Evans	if (core.isSerialBoot()) then
349088b4f5fSWarner Losh		return true;
350088b4f5fSWarner Losh	end
351088b4f5fSWarner Losh	local c = string.lower(loader.getenv("console") or "");
35224a1bd54SKyle Evans	if ((c:match("^efi[ ;]") or c:match("[ ;]efi[ ;]")) ~= nil) then
353088b4f5fSWarner Losh		return true;
354088b4f5fSWarner Losh	end
355088b4f5fSWarner Losh
356088b4f5fSWarner Losh	c = string.lower(loader.getenv("beastie_disable") or "");
357088b4f5fSWarner Losh	print("beastie_disable", c);
358088b4f5fSWarner Losh	return c == "yes";
359088b4f5fSWarner Loshend
360088b4f5fSWarner Losh
361088b4f5fSWarner Loshfunction menu.autoboot()
36224a1bd54SKyle Evans	if (menu.already_autoboot == true) then
363088b4f5fSWarner Losh		return;
364088b4f5fSWarner Losh	end
365088b4f5fSWarner Losh	menu.already_autoboot = true;
366088b4f5fSWarner Losh
367088b4f5fSWarner Losh	local ab = loader.getenv("autoboot_delay");
368702b460dSKyle Evans	if (ab ~= nil) and (ab:lower() == "no") then
369702b460dSKyle Evans		return;
370702b460dSKyle Evans	elseif (tonumber(ab) == -1) then
371088b4f5fSWarner Losh		core.boot();
372088b4f5fSWarner Losh	end
373088b4f5fSWarner Losh	ab = tonumber(ab) or 10;
374088b4f5fSWarner Losh
375088b4f5fSWarner Losh	local x = loader.getenv("loader_menu_timeout_x") or 5;
376088b4f5fSWarner Losh	local y = loader.getenv("loader_menu_timeout_y") or 22;
377088b4f5fSWarner Losh
378088b4f5fSWarner Losh	local endtime = loader.time() + ab;
379088b4f5fSWarner Losh	local time;
380088b4f5fSWarner Losh
381088b4f5fSWarner Losh	repeat
382088b4f5fSWarner Losh		time = endtime - loader.time();
383088b4f5fSWarner Losh		screen.setcursor(x, y);
384*57099121SKyle Evans		print("Autoboot in " .. time ..
385*57099121SKyle Evans		    " seconds, hit [Enter] to boot" ..
386*57099121SKyle Evans		    " or any other key to stop     ");
387088b4f5fSWarner Losh		screen.defcursor();
38824a1bd54SKyle Evans		if (io.ischar()) then
389088b4f5fSWarner Losh			local ch = io.getchar();
39024a1bd54SKyle Evans			if (ch == core.KEY_ENTER) then
391088b4f5fSWarner Losh				break;
392088b4f5fSWarner Losh			else
393088b4f5fSWarner Losh				-- erase autoboot msg
394088b4f5fSWarner Losh				screen.setcursor(0, y);
395088b4f5fSWarner Losh				print("                                        "
396088b4f5fSWarner Losh				    .. "                                        ");
397088b4f5fSWarner Losh				screen.defcursor();
398088b4f5fSWarner Losh				return;
399088b4f5fSWarner Losh			end
400088b4f5fSWarner Losh		end
401088b4f5fSWarner Losh
402088b4f5fSWarner Losh		loader.delay(50000);
403088b4f5fSWarner Losh	until time <= 0
404088b4f5fSWarner Losh	core.boot();
405088b4f5fSWarner Losh
406088b4f5fSWarner Loshend
407088b4f5fSWarner Losh
408088b4f5fSWarner Loshfunction OnOff(str, b)
409088b4f5fSWarner Losh	if (b) then
410*57099121SKyle Evans		return str .. color.escapef(color.GREEN) .. "On" ..
411*57099121SKyle Evans		    color.escapef(color.WHITE);
412088b4f5fSWarner Losh	else
413*57099121SKyle Evans		return str .. color.escapef(color.RED) .. "off" ..
414*57099121SKyle Evans		    color.escapef(color.WHITE);
415088b4f5fSWarner Losh	end
416088b4f5fSWarner Loshend
417088b4f5fSWarner Losh
41824a1bd54SKyle Evansreturn menu;
419