xref: /freebsd/stand/lua/menu.lua (revision d2187b39cf671004ce1b7dc8f152b8b1cce6885b)
1088b4f5fSWarner Losh--
272e39d71SKyle Evans-- SPDX-License-Identifier: BSD-2-Clause-FreeBSD
372e39d71SKyle Evans--
4088b4f5fSWarner Losh-- Copyright (c) 2015 Pedro Souza <pedrosouza@freebsd.org>
5e12ff891SKyle Evans-- Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
6088b4f5fSWarner Losh-- All rights reserved.
7088b4f5fSWarner Losh--
8088b4f5fSWarner Losh-- Redistribution and use in source and binary forms, with or without
9088b4f5fSWarner Losh-- modification, are permitted provided that the following conditions
10088b4f5fSWarner Losh-- are met:
11088b4f5fSWarner Losh-- 1. Redistributions of source code must retain the above copyright
12088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer.
13088b4f5fSWarner Losh-- 2. Redistributions in binary form must reproduce the above copyright
14088b4f5fSWarner Losh--    notice, this list of conditions and the following disclaimer in the
15088b4f5fSWarner Losh--    documentation and/or other materials provided with the distribution.
16088b4f5fSWarner Losh--
17088b4f5fSWarner Losh-- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18088b4f5fSWarner Losh-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19088b4f5fSWarner Losh-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20088b4f5fSWarner Losh-- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21088b4f5fSWarner Losh-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22088b4f5fSWarner Losh-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23088b4f5fSWarner Losh-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24088b4f5fSWarner Losh-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25088b4f5fSWarner Losh-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26088b4f5fSWarner Losh-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27088b4f5fSWarner Losh-- SUCH DAMAGE.
28088b4f5fSWarner Losh--
29088b4f5fSWarner Losh-- $FreeBSD$
30088b4f5fSWarner Losh--
31088b4f5fSWarner Losh
3276c75816SKyle Evanslocal cli = require("cli")
33aedd6be5SKyle Evanslocal core = require("core")
34aedd6be5SKyle Evanslocal color = require("color")
35aedd6be5SKyle Evanslocal config = require("config")
36aedd6be5SKyle Evanslocal screen = require("screen")
37aedd6be5SKyle Evanslocal drawer = require("drawer")
38088b4f5fSWarner Losh
39aedd6be5SKyle Evanslocal menu = {}
40c8518398SKyle Evans
41beafe961SKyle Evanslocal drawn_menu
4243f7d9d1SKyle Evanslocal return_menu_entry = {
4343f7d9d1SKyle Evans	entry_type = core.MENU_RETURN,
4443f7d9d1SKyle Evans	name = "Back to main menu" .. color.highlight(" [Backspace]"),
4543f7d9d1SKyle Evans}
46ca16d83fSKyle Evans
4704af4229SKyle Evanslocal function OnOff(str, value)
4804af4229SKyle Evans	if value then
498ce1744fSKyle Evans		return str .. color.escapefg(color.GREEN) .. "On" ..
506dd078dfSToomas Soome		    color.resetfg()
51e15abd1fSKyle Evans	else
528ce1744fSKyle Evans		return str .. color.escapefg(color.RED) .. "off" ..
536dd078dfSToomas Soome		    color.resetfg()
54e15abd1fSKyle Evans	end
55e15abd1fSKyle Evansend
56e15abd1fSKyle Evans
579ed6f9efSKyle Evanslocal function bootenvSet(env)
587efc058fSKyle Evans	loader.setenv("vfs.root.mountfrom", env)
597efc058fSKyle Evans	loader.setenv("currdev", env .. ":")
607efc058fSKyle Evans	config.reload()
617efc058fSKyle Evansend
627efc058fSKyle Evans
63b5746545SKyle Evans-- Module exports
648d415029SKyle Evansmenu.handlers = {
658d415029SKyle Evans	-- Menu handlers take the current menu and selected entry as parameters,
668d415029SKyle Evans	-- and should return a boolean indicating whether execution should
678d415029SKyle Evans	-- continue or not. The return value may be omitted if this entry should
688d415029SKyle Evans	-- have no bearing on whether we continue or not, indicating that we
698d415029SKyle Evans	-- should just continue after execution.
70e2df27e3SKyle Evans	[core.MENU_ENTRY] = function(_, entry)
718d415029SKyle Evans		-- run function
72aedd6be5SKyle Evans		entry.func()
738d415029SKyle Evans	end,
74e2df27e3SKyle Evans	[core.MENU_CAROUSEL_ENTRY] = function(_, entry)
758d415029SKyle Evans		-- carousel (rotating) functionality
76aedd6be5SKyle Evans		local carid = entry.carousel_id
77aedd6be5SKyle Evans		local caridx = config.getCarouselIndex(carid)
784f437f9eSKyle Evans		local choices = entry.items
794f437f9eSKyle Evans		if type(choices) == "function" then
804f437f9eSKyle Evans			choices = choices()
814f437f9eSKyle Evans		end
829f71d421SKyle Evans		if #choices > 0 then
83aedd6be5SKyle Evans			caridx = (caridx % #choices) + 1
84aedd6be5SKyle Evans			config.setCarouselIndex(carid, caridx)
85aedd6be5SKyle Evans			entry.func(caridx, choices[caridx], choices)
868d415029SKyle Evans		end
878d415029SKyle Evans	end,
88e2df27e3SKyle Evans	[core.MENU_SUBMENU] = function(_, entry)
89da9ab827SKyle Evans		menu.process(entry.submenu)
908d415029SKyle Evans	end,
91e2df27e3SKyle Evans	[core.MENU_RETURN] = function(_, entry)
928d415029SKyle Evans		-- allow entry to have a function/side effect
939f71d421SKyle Evans		if entry.func ~= nil then
94aedd6be5SKyle Evans			entry.func()
958d415029SKyle Evans		end
96aedd6be5SKyle Evans		return false
978d415029SKyle Evans	end,
98aedd6be5SKyle Evans}
99280e990bSKyle Evans-- loader menu tree is rooted at menu.welcome
100088b4f5fSWarner Losh
1017efc058fSKyle Evansmenu.boot_environments = {
1027efc058fSKyle Evans	entries = {
1037efc058fSKyle Evans		-- return to welcome menu
10443f7d9d1SKyle Evans		return_menu_entry,
1057efc058fSKyle Evans		{
1067efc058fSKyle Evans			entry_type = core.MENU_CAROUSEL_ENTRY,
1077efc058fSKyle Evans			carousel_id = "be_active",
1087efc058fSKyle Evans			items = core.bootenvList,
1097efc058fSKyle Evans			name = function(idx, choice, all_choices)
1107efc058fSKyle Evans				if #all_choices == 0 then
1117efc058fSKyle Evans					return "Active: "
1127efc058fSKyle Evans				end
1137efc058fSKyle Evans
1147efc058fSKyle Evans				local is_default = (idx == 1)
1157efc058fSKyle Evans				local bootenv_name = ""
1167efc058fSKyle Evans				local name_color
1177efc058fSKyle Evans				if is_default then
1188ce1744fSKyle Evans					name_color = color.escapefg(color.GREEN)
1197efc058fSKyle Evans				else
1208ce1744fSKyle Evans					name_color = color.escapefg(color.BLUE)
1217efc058fSKyle Evans				end
1227efc058fSKyle Evans				bootenv_name = bootenv_name .. name_color ..
1238ce1744fSKyle Evans				    choice .. color.resetfg()
1247efc058fSKyle Evans				return color.highlight("A").."ctive: " ..
1257efc058fSKyle Evans				    bootenv_name .. " (" .. idx .. " of " ..
1267efc058fSKyle Evans				    #all_choices .. ")"
1277efc058fSKyle Evans			end,
128e2df27e3SKyle Evans			func = function(_, choice, _)
1297efc058fSKyle Evans				bootenvSet(choice)
1307efc058fSKyle Evans			end,
1317efc058fSKyle Evans			alias = {"a", "A"},
1327efc058fSKyle Evans		},
1337efc058fSKyle Evans		{
1347efc058fSKyle Evans			entry_type = core.MENU_ENTRY,
1357efc058fSKyle Evans			name = function()
1367efc058fSKyle Evans				return color.highlight("b") .. "ootfs: " ..
1377efc058fSKyle Evans				    core.bootenvDefault()
1387efc058fSKyle Evans			end,
1397efc058fSKyle Evans			func = function()
1407efc058fSKyle Evans				-- Reset active boot environment to the default
1417efc058fSKyle Evans				config.setCarouselIndex("be_active", 1)
1427efc058fSKyle Evans				bootenvSet(core.bootenvDefault())
1437efc058fSKyle Evans			end,
1447efc058fSKyle Evans			alias = {"b", "B"},
1457efc058fSKyle Evans		},
1467efc058fSKyle Evans	},
1477efc058fSKyle Evans}
1487efc058fSKyle Evans
149088b4f5fSWarner Loshmenu.boot_options = {
150d8757746SKyle Evans	entries = {
151088b4f5fSWarner Losh		-- return to welcome menu
15243f7d9d1SKyle Evans		return_menu_entry,
153088b4f5fSWarner Losh		-- load defaults
154088b4f5fSWarner Losh		{
155a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
156a51f9f0cSKyle Evans			name = "Load System " .. color.highlight("D") ..
157a51f9f0cSKyle Evans			    "efaults",
158a51f9f0cSKyle Evans			func = core.setDefaults,
1593cd5547bSKyle Evans			alias = {"d", "D"},
160088b4f5fSWarner Losh		},
161088b4f5fSWarner Losh		{
162a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
163088b4f5fSWarner Losh		},
164088b4f5fSWarner Losh		{
165a7cf0562SKyle Evans			entry_type = core.MENU_SEPARATOR,
166a51f9f0cSKyle Evans			name = "Boot Options:",
167088b4f5fSWarner Losh		},
168088b4f5fSWarner Losh		-- acpi
169088b4f5fSWarner Losh		{
170a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
171c1ab36f5SKyle Evans			visible = core.isSystem386,
172088b4f5fSWarner Losh			name = function()
173fd2b19b3SKyle Evans				return OnOff(color.highlight("A") ..
174aedd6be5SKyle Evans				    "CPI       :", core.acpi)
175088b4f5fSWarner Losh			end,
176a51f9f0cSKyle Evans			func = core.setACPI,
1773cd5547bSKyle Evans			alias = {"a", "A"},
178088b4f5fSWarner Losh		},
179088b4f5fSWarner Losh		-- safe mode
180088b4f5fSWarner Losh		{
181a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
182088b4f5fSWarner Losh			name = function()
18357099121SKyle Evans				return OnOff("Safe " .. color.highlight("M") ..
184aedd6be5SKyle Evans				    "ode  :", core.sm)
185088b4f5fSWarner Losh			end,
186a51f9f0cSKyle Evans			func = core.setSafeMode,
1873cd5547bSKyle Evans			alias = {"m", "M"},
188088b4f5fSWarner Losh		},
189088b4f5fSWarner Losh		-- single user
190088b4f5fSWarner Losh		{
191a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
192088b4f5fSWarner Losh			name = function()
193fd2b19b3SKyle Evans				return OnOff(color.highlight("S") ..
194aedd6be5SKyle Evans				    "ingle user:", core.su)
195088b4f5fSWarner Losh			end,
196a51f9f0cSKyle Evans			func = core.setSingleUser,
1973cd5547bSKyle Evans			alias = {"s", "S"},
198088b4f5fSWarner Losh		},
199088b4f5fSWarner Losh		-- verbose boot
200088b4f5fSWarner Losh		{
201a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
202088b4f5fSWarner Losh			name = function()
203fd2b19b3SKyle Evans				return OnOff(color.highlight("V") ..
204aedd6be5SKyle Evans				    "erbose    :", core.verbose)
205088b4f5fSWarner Losh			end,
206a51f9f0cSKyle Evans			func = core.setVerbose,
2073cd5547bSKyle Evans			alias = {"v", "V"},
208088b4f5fSWarner Losh		},
209d8757746SKyle Evans	},
210aedd6be5SKyle Evans}
211088b4f5fSWarner Losh
212088b4f5fSWarner Loshmenu.welcome = {
213303253e5SKyle Evans	entries = function()
214aedd6be5SKyle Evans		local menu_entries = menu.welcome.all_entries
215*d2187b39SRyan Moeller		local multi_user = menu_entries.multi_user
216*d2187b39SRyan Moeller		local single_user = menu_entries.single_user
217*d2187b39SRyan Moeller		local boot_entry_1, boot_entry_2
2189f71d421SKyle Evans		if core.isSingleUserBoot() then
219*d2187b39SRyan Moeller			-- Swap the first two menu items on single user boot.
220*d2187b39SRyan Moeller			-- We'll cache the alternate entries for performance.
221*d2187b39SRyan Moeller			local alts = menu_entries.alts
222*d2187b39SRyan Moeller			if alts == nil then
223*d2187b39SRyan Moeller				single_user = core.deepCopyTable(single_user)
224*d2187b39SRyan Moeller				multi_user = core.deepCopyTable(multi_user)
225*d2187b39SRyan Moeller				single_user.name = single_user.alternate_name
226*d2187b39SRyan Moeller				multi_user.name = multi_user.alternate_name
227*d2187b39SRyan Moeller				menu_entries.alts = {
228*d2187b39SRyan Moeller					single_user = single_user,
229*d2187b39SRyan Moeller					multi_user = multi_user,
230*d2187b39SRyan Moeller				}
231*d2187b39SRyan Moeller			else
232*d2187b39SRyan Moeller				single_user = alts.single_user
233*d2187b39SRyan Moeller				multi_user = alts.multi_user
2349a0904b0SKyle Evans			end
235*d2187b39SRyan Moeller			boot_entry_1, boot_entry_2 = single_user, multi_user
236*d2187b39SRyan Moeller		else
237*d2187b39SRyan Moeller			boot_entry_1, boot_entry_2 = multi_user, single_user
238303253e5SKyle Evans		end
239*d2187b39SRyan Moeller		return {
240*d2187b39SRyan Moeller			boot_entry_1,
241*d2187b39SRyan Moeller			boot_entry_2,
242*d2187b39SRyan Moeller			menu_entries.prompt,
243*d2187b39SRyan Moeller			menu_entries.reboot,
244*d2187b39SRyan Moeller			{
245*d2187b39SRyan Moeller				entry_type = core.MENU_SEPARATOR,
246*d2187b39SRyan Moeller			},
247*d2187b39SRyan Moeller			{
248*d2187b39SRyan Moeller				entry_type = core.MENU_SEPARATOR,
249*d2187b39SRyan Moeller				name = "Options:",
250*d2187b39SRyan Moeller			},
251*d2187b39SRyan Moeller			menu_entries.kernel_options,
252*d2187b39SRyan Moeller			menu_entries.boot_options,
253*d2187b39SRyan Moeller			menu_entries.boot_envs,
254*d2187b39SRyan Moeller			menu_entries.chainload,
255*d2187b39SRyan Moeller		}
256303253e5SKyle Evans	end,
257303253e5SKyle Evans	all_entries = {
258*d2187b39SRyan Moeller		multi_user = {
259a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
260a51f9f0cSKyle Evans			name = color.highlight("B") .. "oot Multi user " ..
261a51f9f0cSKyle Evans			    color.highlight("[Enter]"),
2625c1b5165SKyle Evans			-- Not a standard menu entry function!
263a51f9f0cSKyle Evans			alternate_name = color.highlight("B") ..
264a51f9f0cSKyle Evans			    "oot Multi user",
265088b4f5fSWarner Losh			func = function()
266aedd6be5SKyle Evans				core.setSingleUser(false)
267aedd6be5SKyle Evans				core.boot()
268088b4f5fSWarner Losh			end,
2693cd5547bSKyle Evans			alias = {"b", "B"},
270088b4f5fSWarner Losh		},
271*d2187b39SRyan Moeller		single_user = {
272a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
273a51f9f0cSKyle Evans			name = "Boot " .. color.highlight("S") .. "ingle user",
2745c1b5165SKyle Evans			-- Not a standard menu entry function!
275a51f9f0cSKyle Evans			alternate_name = "Boot " .. color.highlight("S") ..
276a51f9f0cSKyle Evans			    "ingle user " .. color.highlight("[Enter]"),
277088b4f5fSWarner Losh			func = function()
278aedd6be5SKyle Evans				core.setSingleUser(true)
279aedd6be5SKyle Evans				core.boot()
280088b4f5fSWarner Losh			end,
2813cd5547bSKyle Evans			alias = {"s", "S"},
282088b4f5fSWarner Losh		},
283*d2187b39SRyan Moeller		prompt = {
284a7cf0562SKyle Evans			entry_type = core.MENU_RETURN,
285a51f9f0cSKyle Evans			name = color.highlight("Esc") .. "ape to loader prompt",
286ef625845SKyle Evans			func = function()
287aedd6be5SKyle Evans				loader.setenv("autoboot_delay", "NO")
288ef625845SKyle Evans			end,
2893cd5547bSKyle Evans			alias = {core.KEYSTR_ESCAPE},
290088b4f5fSWarner Losh		},
291*d2187b39SRyan Moeller		reboot = {
292a7cf0562SKyle Evans			entry_type = core.MENU_ENTRY,
293a51f9f0cSKyle Evans			name = color.highlight("R") .. "eboot",
294088b4f5fSWarner Losh			func = function()
295aedd6be5SKyle Evans				loader.perform("reboot")
296088b4f5fSWarner Losh			end,
2973cd5547bSKyle Evans			alias = {"r", "R"},
298088b4f5fSWarner Losh		},
299*d2187b39SRyan Moeller		kernel_options = {
300a7cf0562SKyle Evans			entry_type = core.MENU_CAROUSEL_ENTRY,
301ada26c4aSKyle Evans			carousel_id = "kernel",
302ada26c4aSKyle Evans			items = core.kernelList,
303ada26c4aSKyle Evans			name = function(idx, choice, all_choices)
3049f71d421SKyle Evans				if #all_choices == 0 then
305aedd6be5SKyle Evans					return "Kernel: "
306088b4f5fSWarner Losh				end
307b1b1f2b8SKyle Evans
308aedd6be5SKyle Evans				local is_default = (idx == 1)
309aedd6be5SKyle Evans				local kernel_name = ""
310aedd6be5SKyle Evans				local name_color
3119f71d421SKyle Evans				if is_default then
3128ce1744fSKyle Evans					name_color = color.escapefg(color.GREEN)
313aedd6be5SKyle Evans					kernel_name = "default/"
314bcf48a15SKyle Evans				else
3158ce1744fSKyle Evans					name_color = color.escapefg(color.BLUE)
316b1b1f2b8SKyle Evans				end
317fd2b19b3SKyle Evans				kernel_name = kernel_name .. name_color ..
3188ce1744fSKyle Evans				    choice .. color.resetfg()
319fd2b19b3SKyle Evans				return color.highlight("K") .. "ernel: " ..
320fd2b19b3SKyle Evans				    kernel_name .. " (" .. idx .. " of " ..
321aedd6be5SKyle Evans				    #all_choices .. ")"
322088b4f5fSWarner Losh			end,
323e2df27e3SKyle Evans			func = function(_, choice, _)
324e414851fSKyle Evans				if loader.getenv("kernelname") ~= nil then
325e414851fSKyle Evans					loader.perform("unload")
326e414851fSKyle Evans				end
327322a2dddSKyle Evans				config.selectKernel(choice)
328088b4f5fSWarner Losh			end,
3293cd5547bSKyle Evans			alias = {"k", "K"},
330088b4f5fSWarner Losh		},
331*d2187b39SRyan Moeller		boot_options = {
332a7cf0562SKyle Evans			entry_type = core.MENU_SUBMENU,
333a51f9f0cSKyle Evans			name = "Boot " .. color.highlight("O") .. "ptions",
3349a28f948SKyle Evans			submenu = menu.boot_options,
3353cd5547bSKyle Evans			alias = {"o", "O"},
336d8757746SKyle Evans		},
337*d2187b39SRyan Moeller		boot_envs = {
3387efc058fSKyle Evans			entry_type = core.MENU_SUBMENU,
3397efc058fSKyle Evans			visible = function()
3407efc058fSKyle Evans				return core.isZFSBoot() and
3417efc058fSKyle Evans				    #core.bootenvList() > 1
3427efc058fSKyle Evans			end,
3437efc058fSKyle Evans			name = "Boot " .. color.highlight("E") .. "nvironments",
3447efc058fSKyle Evans			submenu = menu.boot_environments,
3457efc058fSKyle Evans			alias = {"e", "E"},
3467efc058fSKyle Evans		},
347*d2187b39SRyan Moeller		chainload = {
348bdf12807SKyle Evans			entry_type = core.MENU_ENTRY,
349bdf12807SKyle Evans			name = function()
350bdf12807SKyle Evans				return 'Chain' .. color.highlight("L") ..
351bdf12807SKyle Evans				    "oad " .. loader.getenv('chain_disk')
352bdf12807SKyle Evans			end,
353bdf12807SKyle Evans			func = function()
354bdf12807SKyle Evans				loader.perform("chain " ..
355bdf12807SKyle Evans				    loader.getenv('chain_disk'))
356bdf12807SKyle Evans			end,
357bdf12807SKyle Evans			visible = function()
358bdf12807SKyle Evans				return loader.getenv('chain_disk') ~= nil
359bdf12807SKyle Evans			end,
360bdf12807SKyle Evans			alias = {"l", "L"},
361bdf12807SKyle Evans		},
362d8757746SKyle Evans	},
363aedd6be5SKyle Evans}
364088b4f5fSWarner Losh
36520a81676SKyle Evansmenu.default = menu.welcome
36628384160SKyle Evans-- current_alias_table will be used to keep our alias table consistent across
36728384160SKyle Evans-- screen redraws, instead of relying on whatever triggered the redraw to update
36828384160SKyle Evans-- the local alias_table in menu.process.
36928384160SKyle Evansmenu.current_alias_table = {}
37020a81676SKyle Evans
3712a11b810SKyle Evansfunction menu.draw(menudef)
372beafe961SKyle Evans	-- Clear the screen, reset the cursor, then draw
373aedd6be5SKyle Evans	screen.clear()
3742a11b810SKyle Evans	menu.current_alias_table = drawer.drawscreen(menudef)
3752a11b810SKyle Evans	drawn_menu = menudef
376decacd91SKyle Evans	screen.defcursor()
37728384160SKyle Evansend
37828384160SKyle Evans
379ca16d83fSKyle Evans-- 'keypress' allows the caller to indicate that a key has been pressed that we
380ca16d83fSKyle Evans-- should process as our initial input.
3812a11b810SKyle Evansfunction menu.process(menudef, keypress)
3822a11b810SKyle Evans	assert(menudef ~= nil)
38328384160SKyle Evans
3842a11b810SKyle Evans	if drawn_menu ~= menudef then
3852a11b810SKyle Evans		menu.draw(menudef)
3867dcffa90SKyle Evans	end
387ca16d83fSKyle Evans
388da9ab827SKyle Evans	while true do
389ca16d83fSKyle Evans		local key = keypress or io.getchar()
390ca16d83fSKyle Evans		keypress = nil
391088b4f5fSWarner Losh
392b458bf0dSKyle Evans		-- Special key behaviors
3939f71d421SKyle Evans		if (key == core.KEY_BACKSPACE or key == core.KEY_DELETE) and
3942a11b810SKyle Evans		    menudef ~= menu.default then
395aedd6be5SKyle Evans			break
3969f71d421SKyle Evans		elseif key == core.KEY_ENTER then
397aedd6be5SKyle Evans			core.boot()
398041929aaSKyle Evans			-- Should not return.  If it does, escape menu handling
399041929aaSKyle Evans			-- and drop to loader prompt.
400041929aaSKyle Evans			return false
401abc4f7e7SKyle Evans		end
402abc4f7e7SKyle Evans
403abc4f7e7SKyle Evans		key = string.char(key)
404088b4f5fSWarner Losh		-- check to see if key is an alias
405aedd6be5SKyle Evans		local sel_entry = nil
40628384160SKyle Evans		for k, v in pairs(menu.current_alias_table) do
4079f71d421SKyle Evans			if key == k then
408aedd6be5SKyle Evans				sel_entry = v
40928384160SKyle Evans				break
410088b4f5fSWarner Losh			end
411088b4f5fSWarner Losh		end
412088b4f5fSWarner Losh
413088b4f5fSWarner Losh		-- if we have an alias do the assigned action:
4149f71d421SKyle Evans		if sel_entry ~= nil then
415aedd6be5SKyle Evans			local handler = menu.handlers[sel_entry.entry_type]
4162a11b810SKyle Evans			assert(handler ~= nil)
417da9ab827SKyle Evans			-- The handler's return value indicates if we
418da9ab827SKyle Evans			-- need to exit this menu.  An omitted or true
419da9ab827SKyle Evans			-- return value means to continue.
4202a11b810SKyle Evans			if handler(menudef, sel_entry) == false then
421da9ab827SKyle Evans				return
422aefcaa7eSKyle Evans			end
42328384160SKyle Evans			-- If we got an alias key the screen is out of date...
42428384160SKyle Evans			-- redraw it.
4252a11b810SKyle Evans			menu.draw(menudef)
426088b4f5fSWarner Losh		end
427088b4f5fSWarner Losh	end
428088b4f5fSWarner Loshend
429088b4f5fSWarner Losh
430da9ab827SKyle Evansfunction menu.run()
431041929aaSKyle Evans	local autoboot_key
432c84dbc53SKyle Evans	local delay = loader.getenv("autoboot_delay")
433c84dbc53SKyle Evans
434c84dbc53SKyle Evans	if delay ~= nil and delay:lower() == "no" then
435c84dbc53SKyle Evans		delay = nil
436c84dbc53SKyle Evans	else
437c84dbc53SKyle Evans		delay = tonumber(delay) or 10
438c84dbc53SKyle Evans	end
439c84dbc53SKyle Evans
440c84dbc53SKyle Evans	if delay == -1 then
441c84dbc53SKyle Evans		core.boot()
442c84dbc53SKyle Evans		return
443c84dbc53SKyle Evans	end
444c84dbc53SKyle Evans
445beafe961SKyle Evans	menu.draw(menu.default)
446c84dbc53SKyle Evans
447041929aaSKyle Evans	if delay ~= nil then
448041929aaSKyle Evans		autoboot_key = menu.autoboot(delay)
449041929aaSKyle Evans
450041929aaSKyle Evans		-- autoboot_key should return the key pressed.  It will only
451041929aaSKyle Evans		-- return nil if we hit the timeout and executed the timeout
452041929aaSKyle Evans		-- command.  Bail out.
453041929aaSKyle Evans		if autoboot_key == nil then
454041929aaSKyle Evans			return
455041929aaSKyle Evans		end
456041929aaSKyle Evans	end
457ca16d83fSKyle Evans
458ca16d83fSKyle Evans	menu.process(menu.default, autoboot_key)
459beafe961SKyle Evans	drawn_menu = nil
460da9ab827SKyle Evans
461da9ab827SKyle Evans	screen.defcursor()
462da9ab827SKyle Evans	print("Exiting menu!")
463088b4f5fSWarner Loshend
464088b4f5fSWarner Losh
465c84dbc53SKyle Evansfunction menu.autoboot(delay)
4661495c98fSKyle Evans	local x = loader.getenv("loader_menu_timeout_x") or 4
4671495c98fSKyle Evans	local y = loader.getenv("loader_menu_timeout_y") or 23
468c84dbc53SKyle Evans	local endtime = loader.time() + delay
469aedd6be5SKyle Evans	local time
4703244729fSKyle Evans	local last
471088b4f5fSWarner Losh	repeat
472aedd6be5SKyle Evans		time = endtime - loader.time()
4733244729fSKyle Evans		if last == nil or last ~= time then
4743244729fSKyle Evans			last = time
475aedd6be5SKyle Evans			screen.setcursor(x, y)
47657099121SKyle Evans			print("Autoboot in " .. time ..
47757099121SKyle Evans			    " seconds, hit [Enter] to boot" ..
478aedd6be5SKyle Evans			    " or any other key to stop     ")
479aedd6be5SKyle Evans			screen.defcursor()
4803244729fSKyle Evans		end
4819f71d421SKyle Evans		if io.ischar() then
482aedd6be5SKyle Evans			local ch = io.getchar()
4839f71d421SKyle Evans			if ch == core.KEY_ENTER then
484aedd6be5SKyle Evans				break
485088b4f5fSWarner Losh			else
486088b4f5fSWarner Losh				-- erase autoboot msg
487aedd6be5SKyle Evans				screen.setcursor(0, y)
48866964bbcSKyle Evans				print(string.rep(" ", 80))
489aedd6be5SKyle Evans				screen.defcursor()
49012b95c84SKyle Evans				return ch
491088b4f5fSWarner Losh			end
492088b4f5fSWarner Losh		end
493088b4f5fSWarner Losh
494aedd6be5SKyle Evans		loader.delay(50000)
495aedd6be5SKyle Evans	until time <= 0
496088b4f5fSWarner Losh
497a76f8a5bSKyle Evans	local cmd = loader.getenv("menu_timeout_command") or "boot"
498e9c3ceb1SKyle Evans	cli_execute_unparsed(cmd)
499041929aaSKyle Evans	return nil
500088b4f5fSWarner Loshend
501088b4f5fSWarner Losh
50276c75816SKyle Evans-- CLI commands
5038f7f3d08SKyle Evansfunction cli.menu()
50476c75816SKyle Evans	menu.run()
50576c75816SKyle Evansend
50676c75816SKyle Evans
507aedd6be5SKyle Evansreturn menu
508