· 5 years ago · Sep 28, 2020, 02:34 PM
1term.clear()
2term.setCursorPos(1,1)
3file = require("libraries.file")
4file.loadGrpLines("graphics/bootSplash.skgrp")
5gpswrapper = require("libraries.gpswrapper")
6--Do server side things BEFORE term.clear()
7term.setBackgroundColour(colours.black)
8term.clear()
9--Load DE
10file.loadGrpLines("graphics/background/default.skgrp")
11file.loadGrpLines("graphics/taskbar.skgrp")
12--parallel.waitForAll(function() shell.run("customPrograms/timeManager.lua") end, function() shell.run("customPrograms/applications.lua") end)
13while true do
14 local time = textutils.formatTime(os.time(), true)
15 term.setCursorPos(22,20)
16 term.setBackgroundColour(colours.grey)
17 term.write(" ")
18 term.setCursorPos(22,20)
19 term.write(time)
20 term.setBackgroundColour(colours.blue)
21 x, y, z = gpswrapper.gpslocate(5)
22 term.setCursorPos(1,6)
23 term.write(math.floor(x+0.5))
24 term.setCursorPos(1,7)
25 term.write(math.floor(y+0.5))
26 term.setCursorPos(1,8)
27 term.write(math.floor(z+0.5))
28 sleep()
29end
30
31local expect = dofile("rom/modules/main/cc/expect.lua").expect
32local make_package = dofile("rom/modules/main/cc/require.lua").make
33
34function read2(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
35 expect(1, _sReplaceChar, "string", "nil")
36 expect(2, _tHistory, "table", "nil")
37 expect(3, _fnComplete, "function", "nil")
38 expect(4, _sDefault, "string", "nil")
39
40 term.setCursorBlink(false)
41
42 local sLine
43 if type(_sDefault) == "string" then
44 sLine = _sDefault
45 else
46 sLine = ""
47 end
48 local nHistoryPos
49 local nPos, nScroll = #sLine, 0
50 if _sReplaceChar then
51 _sReplaceChar = string.sub(_sReplaceChar, 1, 1)
52 end
53
54 local tCompletions
55 local nCompletion
56 local function recomplete()
57 if _fnComplete and nPos == #sLine then
58 tCompletions = _fnComplete(sLine)
59 if tCompletions and #tCompletions > 0 then
60 nCompletion = 1
61 else
62 nCompletion = nil
63 end
64 else
65 tCompletions = nil
66 nCompletion = nil
67 end
68 end
69
70 local function uncomplete()
71 tCompletions = nil
72 nCompletion = nil
73 end
74
75 local w = term.getSize()
76 local sx = term.getCursorPos()
77
78 local function redraw(_bClear)
79 local cursor_pos = nPos - nScroll
80 if sx + cursor_pos >= w then
81 -- We've moved beyond the RHS, ensure we're on the edge.
82 nScroll = sx + nPos - w
83 elseif cursor_pos < 0 then
84 -- We've moved beyond the LHS, ensure we're on the edge.
85 nScroll = nPos
86 end
87
88 local _, cy = term.getCursorPos()
89 term.setCursorPos(sx, cy)
90 local sReplace = _bClear and " " or _sReplaceChar
91 if sReplace then
92 term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
93 else
94 term.write(string.sub(sLine, nScroll + 1))
95 end
96
97 if nCompletion then
98 local sCompletion = tCompletions[nCompletion]
99 local oldText, oldBg
100 if not _bClear then
101 oldText = term.getTextColor()
102 oldBg = term.getBackgroundColor()
103 term.setTextColor(colors.white)
104 term.setBackgroundColor(colors.gray)
105 end
106 if sReplace then
107 term.write(string.rep(sReplace, #sCompletion))
108 else
109 term.write(sCompletion)
110 end
111 if not _bClear then
112 term.setTextColor(oldText)
113 term.setBackgroundColor(oldBg)
114 end
115 end
116
117 term.setCursorPos(sx + nPos - nScroll, cy)
118 end
119
120 local function clear()
121 redraw(true)
122 end
123
124 recomplete()
125 redraw()
126
127 local function acceptCompletion()
128 if nCompletion then
129 -- Clear
130 clear()
131
132 -- Find the common prefix of all the other suggestions which start with the same letter as the current one
133 local sCompletion = tCompletions[nCompletion]
134 sLine = sLine .. sCompletion
135 nPos = #sLine
136
137 -- Redraw
138 recomplete()
139 redraw()
140 end
141 end
142 while true do
143 local sEvent, param, param1, param2 = os.pullEvent()
144 if sEvent == "char" then
145 -- Typed key
146 clear()
147 sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
148 nPos = nPos + 1
149 recomplete()
150 redraw()
151
152 elseif sEvent == "paste" then
153 -- Pasted text
154 clear()
155 sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
156 nPos = nPos + #param
157 recomplete()
158 redraw()
159
160 elseif sEvent == "key" then
161 if param == keys.enter then
162 -- Enter
163 if nCompletion then
164 clear()
165 uncomplete()
166 redraw()
167 end
168 break
169
170 elseif param == keys.left then
171 -- Left
172 if nPos > 0 then
173 clear()
174 nPos = nPos - 1
175 recomplete()
176 redraw()
177 end
178
179 elseif param == keys.right then
180 -- Right
181 if nPos < #sLine then
182 -- Move right
183 clear()
184 nPos = nPos + 1
185 recomplete()
186 redraw()
187 else
188 -- Accept autocomplete
189 acceptCompletion()
190 end
191
192 elseif param == keys.up or param == keys.down then
193 -- Up or down
194 if nCompletion then
195 -- Cycle completions
196 clear()
197 if param == keys.up then
198 nCompletion = nCompletion - 1
199 if nCompletion < 1 then
200 nCompletion = #tCompletions
201 end
202 elseif param == keys.down then
203 nCompletion = nCompletion + 1
204 if nCompletion > #tCompletions then
205 nCompletion = 1
206 end
207 end
208 redraw()
209
210 elseif _tHistory then
211 -- Cycle history
212 clear()
213 if param == keys.up then
214 -- Up
215 if nHistoryPos == nil then
216 if #_tHistory > 0 then
217 nHistoryPos = #_tHistory
218 end
219 elseif nHistoryPos > 1 then
220 nHistoryPos = nHistoryPos - 1
221 end
222 else
223 -- Down
224 if nHistoryPos == #_tHistory then
225 nHistoryPos = nil
226 elseif nHistoryPos ~= nil then
227 nHistoryPos = nHistoryPos + 1
228 end
229 end
230 if nHistoryPos then
231 sLine = _tHistory[nHistoryPos]
232 nPos, nScroll = #sLine, 0
233 else
234 sLine = ""
235 nPos, nScroll = 0, 0
236 end
237 uncomplete()
238 redraw()
239
240 end
241
242 elseif param == keys.backspace then
243 -- Backspace
244 if nPos > 0 then
245 clear()
246 sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
247 nPos = nPos - 1
248 if nScroll > 0 then nScroll = nScroll - 1 end
249 recomplete()
250 redraw()
251 end
252
253 elseif param == keys.home then
254 -- Home
255 if nPos > 0 then
256 clear()
257 nPos = 0
258 recomplete()
259 redraw()
260 end
261
262 elseif param == keys.delete then
263 -- Delete
264 if nPos < #sLine then
265 clear()
266 sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
267 recomplete()
268 redraw()
269 end
270
271 elseif param == keys["end"] then
272 -- End
273 if nPos < #sLine then
274 clear()
275 nPos = #sLine
276 recomplete()
277 redraw()
278 end
279
280 elseif param == keys.tab then
281 -- Tab (accept autocomplete)
282 acceptCompletion()
283
284 end
285
286 elseif sEvent == "mouse_click" or sEvent == "mouse_drag" and param == 1 then
287 local _, cy = term.getCursorPos()
288 if param1 >= sx and param1 <= w and param2 == cy then
289 -- Ensure we don't scroll beyond the current line
290 nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine)
291 redraw()
292 end
293
294 elseif sEvent == "term_resize" then
295 -- Terminal resized
296 w = term.getSize()
297 redraw()
298
299 end
300 end
301
302 local _, cy = term.getCursorPos()
303 term.setCursorBlink(false)
304 term.setCursorPos(w + 1, cy)
305 print()
306
307 return sLine
308end
309
310
311local multishell = multishell
312local parentShell = shell
313local parentTerm = term.current()
314
315if multishell then
316 multishell.setTitle(multishell.getCurrent(), "shell")
317end
318
319local bExit = false
320local sDir = parentShell and parentShell.dir() or ""
321local sPath = parentShell and parentShell.path() or ".:/rom/programs"
322local tAliases = parentShell and parentShell.aliases() or {}
323local tCompletionInfo = parentShell and parentShell.getCompletionInfo() or {}
324local tProgramStack = {}
325
326local shell = {} --- @export
327local function createShellEnv(dir)
328 local env = { shell = shell, multishell = multishell }
329 env.require, env.package = make_package(env, dir)
330 return env
331end
332
333-- Colours
334local promptColour, textColour, bgColour
335if term.isColour() then
336 promptColour = colours.yellow
337 textColour = colours.white
338 bgColour = colours.black
339else
340 promptColour = colours.white
341 textColour = colours.white
342 bgColour = colours.black
343end
344
345--- Run a program with the supplied arguments.
346--
347-- Unlike @{shell.run}, each argument is passed to the program verbatim. While
348-- `shell.run("echo", "b c")` runs `echo` with `b` and `c`,
349-- `shell.execute("echo", "b c")` runs `echo` with a single argument `b c`.
350--
351-- @tparam string command The program to execute.
352-- @tparam string ... Arguments to this program.
353-- @treturn boolean Whether the program exited successfully.
354-- @usage Run `paint my-image` from within your program:
355--
356-- shell.execute("paint", "my-image")
357function shell.execute(command, ...)
358 expect(1, command, "string")
359 for i = 1, select('#', ...) do
360 expect(i + 1, select(i, ...), "string")
361 end
362
363 local sPath = shell.resolveProgram(command)
364 if sPath ~= nil then
365 tProgramStack[#tProgramStack + 1] = sPath
366 if multishell then
367 local sTitle = fs.getName(sPath)
368 if sTitle:sub(-4) == ".lua" then
369 sTitle = sTitle:sub(1, -5)
370 end
371 multishell.setTitle(multishell.getCurrent(), sTitle)
372 end
373
374 local sDir = fs.getDir(sPath)
375 local env = createShellEnv(sDir)
376 env.arg = { [0] = command, ... }
377 local result = os.run(env, sPath, ...)
378
379 tProgramStack[#tProgramStack] = nil
380 if multishell then
381 if #tProgramStack > 0 then
382 local sTitle = fs.getName(tProgramStack[#tProgramStack])
383 if sTitle:sub(-4) == ".lua" then
384 sTitle = sTitle:sub(1, -5)
385 end
386 multishell.setTitle(multishell.getCurrent(), sTitle)
387 else
388 multishell.setTitle(multishell.getCurrent(), "shell")
389 end
390 end
391 return result
392 else
393 printError("No such program")
394 return false
395 end
396end
397
398local function tokenise(...)
399 local sLine = table.concat({ ... }, " ")
400 local tWords = {}
401 local bQuoted = false
402 for match in string.gmatch(sLine .. "\"", "(.-)\"") do
403 if bQuoted then
404 table.insert(tWords, match)
405 else
406 for m in string.gmatch(match, "[^ \t]+") do
407 table.insert(tWords, m)
408 end
409 end
410 bQuoted = not bQuoted
411 end
412 return tWords
413end
414
415-- Install shell API
416
417--- Run a program with the supplied arguments.
418--
419-- All arguments are concatenated together and then parsed as a command line. As
420-- a result, `shell.run("program a b")` is the same as `shell.run("program",
421-- "a", "b")`.
422--
423-- @tparam string ... The program to run and its arguments.
424-- @treturn boolean Whether the program exited successfully.
425-- @usage Run `paint my-image` from within your program:
426--
427-- shell.run("paint", "my-image")
428-- @see shell.execute Run a program directly without parsing the arguments.
429function shell.run(...)
430 local tWords = tokenise(...)
431 local sCommand = tWords[1]
432 if sCommand then
433 return shell.execute(sCommand, table.unpack(tWords, 2))
434 end
435 return false
436end
437
438--- Exit the current shell.
439--
440-- This does _not_ terminate your program, it simply makes the shell terminate
441-- after your program has finished. If this is the toplevel shell, then the
442-- computer will be shutdown.
443function shell.exit()
444 bExit = true
445end
446
447--- Return the current working directory. This is what is displayed before the
448-- `> ` of the shell prompt, and is used by @{shell.resolve} to handle relative
449-- paths.
450--
451-- @treturn string The current working directory.
452-- @see setDir To change the working directory.
453function shell.dir()
454 return sDir
455end
456
457--- Set the current working directory.
458--
459-- @tparam string dir The new working directory.
460-- @throws If the path does not exist or is not a directory.
461-- @usage Set the working directory to "rom"
462--
463-- shell.setDir("rom")
464function shell.setDir(dir)
465 expect(1, dir, "string")
466 if not fs.isDir(dir) then
467 error("Not a directory", 2)
468 end
469 sDir = fs.combine(dir, "")
470end
471
472--- Set the path where programs are located.
473--
474-- The path is composed of a list of directory names in a string, each separated
475-- by a colon (`:`). On normal turtles will look in the current directory (`.`),
476-- `/rom/programs` and `/rom/programs/turtle` folder, making the path
477-- `.:/rom/programs:/rom/programs/turtle`.
478--
479-- @treturn string The current shell's path.
480-- @see setPath To change the current path.
481function shell.path()
482 return sPath
483end
484
485--- Set the @{path|current program path}.
486--
487-- Be careful to prefix directories with a `/`. Otherwise they will be searched
488-- for from the @{shell.dir|current directory}, rather than the computer's root.
489--
490-- @tparam string path The new program path.
491function shell.setPath(path)
492 expect(1, path, "string")
493 sPath = path
494end
495
496--- Resolve a relative path to an absolute path.
497--
498-- The @{fs} and @{io} APIs work using absolute paths, and so we must convert
499-- any paths relative to the @{dir|current directory} to absolute ones. This
500-- does nothing when the path starts with `/`.
501--
502-- @tparam string path The path to resolve.
503-- @usage Resolve `startup.lua` when in the `rom` folder.
504--
505-- shell.setDir("rom")
506-- print(shell.resolve("startup.lua"))
507-- -- => rom/startup.lua
508function shell.resolve(path)
509 expect(1, path, "string")
510 local sStartChar = string.sub(path, 1, 1)
511 if sStartChar == "/" or sStartChar == "\\" then
512 return fs.combine("", path)
513 else
514 return fs.combine(sDir, path)
515 end
516end
517
518local function pathWithExtension(_sPath, _sExt)
519 local nLen = #sPath
520 local sEndChar = string.sub(_sPath, nLen, nLen)
521 -- Remove any trailing slashes so we can add an extension to the path safely
522 if sEndChar == "/" or sEndChar == "\\" then
523 _sPath = string.sub(_sPath, 1, nLen - 1)
524 end
525 return _sPath .. "." .. _sExt
526end
527
528--- Resolve a program, using the @{path|program path} and list of @{aliases|aliases}.
529--
530-- @tparam string command The name of the program
531-- @treturn string|nil The absolute path to the program, or @{nil} if it could
532-- not be found.
533-- @usage Locate the `hello` program.
534--
535-- shell.resolveProgram("hello")
536-- -- => rom/programs/fun/hello.lua
537function shell.resolveProgram(command)
538 expect(1, command, "string")
539 -- Substitute aliases firsts
540 if tAliases[command] ~= nil then
541 command = tAliases[command]
542 end
543
544 -- If the path is a global path, use it directly
545 if command:find("/") or command:find("\\") then
546 local sPath = shell.resolve(command)
547 if fs.exists(sPath) and not fs.isDir(sPath) then
548 return sPath
549 else
550 local sPathLua = pathWithExtension(sPath, "lua")
551 if fs.exists(sPathLua) and not fs.isDir(sPathLua) then
552 return sPathLua
553 end
554 end
555 return nil
556 end
557
558 -- Otherwise, look on the path variable
559 for sPath in string.gmatch(sPath, "[^:]+") do
560 sPath = fs.combine(shell.resolve(sPath), command)
561 if fs.exists(sPath) and not fs.isDir(sPath) then
562 return sPath
563 else
564 local sPathLua = pathWithExtension(sPath, "lua")
565 if fs.exists(sPathLua) and not fs.isDir(sPathLua) then
566 return sPathLua
567 end
568 end
569 end
570
571 -- Not found
572 return nil
573end
574
575--- Return a list of all programs on the @{shell.path|path}.
576--
577-- @tparam[opt] boolean include_hidden Include hidden files. Namely, any which
578-- start with `.`.
579-- @treturn { string } A list of available programs.
580-- @usage textutils.tabulate(shell.programs())
581function shell.programs(include_hidden)
582 expect(1, include_hidden, "boolean", "nil")
583
584 local tItems = {}
585
586 -- Add programs from the path
587 for sPath in string.gmatch(sPath, "[^:]+") do
588 sPath = shell.resolve(sPath)
589 if fs.isDir(sPath) then
590 local tList = fs.list(sPath)
591 for n = 1, #tList do
592 local sFile = tList[n]
593 if not fs.isDir(fs.combine(sPath, sFile)) and
594 (include_hidden or string.sub(sFile, 1, 1) ~= ".") then
595 if #sFile > 4 and sFile:sub(-4) == ".lua" then
596 sFile = sFile:sub(1, -5)
597 end
598 tItems[sFile] = true
599 end
600 end
601 end
602 end
603
604 -- Sort and return
605 local tItemList = {}
606 for sItem in pairs(tItems) do
607 table.insert(tItemList, sItem)
608 end
609 table.sort(tItemList)
610 return tItemList
611end
612
613local function completeProgram(sLine)
614 if #sLine > 0 and (sLine:find("/") or sLine:find("\\")) then
615 -- Add programs from the root
616 return fs.complete(sLine, sDir, true, false)
617
618 else
619 local tResults = {}
620 local tSeen = {}
621
622 -- Add aliases
623 for sAlias in pairs(tAliases) do
624 if #sAlias > #sLine and string.sub(sAlias, 1, #sLine) == sLine then
625 local sResult = string.sub(sAlias, #sLine + 1)
626 if not tSeen[sResult] then
627 table.insert(tResults, sResult)
628 tSeen[sResult] = true
629 end
630 end
631 end
632
633 -- Add all subdirectories. We don't include files as they will be added in the block below
634 local tDirs = fs.complete(sLine, sDir, false, false)
635 for i = 1, #tDirs do
636 local sResult = tDirs[i]
637 if not tSeen[sResult] then
638 table.insert (tResults, sResult)
639 tSeen [sResult] = true
640 end
641 end
642
643 -- Add programs from the path
644 local tPrograms = shell.programs()
645 for n = 1, #tPrograms do
646 local sProgram = tPrograms[n]
647 if #sProgram > #sLine and string.sub(sProgram, 1, #sLine) == sLine then
648 local sResult = string.sub(sProgram, #sLine + 1)
649 if not tSeen[sResult] then
650 table.insert(tResults, sResult)
651 tSeen[sResult] = true
652 end
653 end
654 end
655
656 -- Sort and return
657 table.sort(tResults)
658 return tResults
659 end
660end
661
662local function completeProgramArgument(sProgram, nArgument, sPart, tPreviousParts)
663 local tInfo = tCompletionInfo[sProgram]
664 if tInfo then
665 return tInfo.fnComplete(shell, nArgument, sPart, tPreviousParts)
666 end
667 return nil
668end
669
670--- Complete a shell command line.
671--
672-- This accepts an incomplete command, and completes the program name or
673-- arguments. For instance, `l` will be completed to `ls`, and `ls ro` will be
674-- completed to `ls rom/`.
675--
676-- Completion handlers for your program may be registered with
677-- @{shell.setCompletionFunction}.
678--
679-- @tparam string sLine The input to complete.
680-- @treturn { string }|nil The list of possible completions.
681-- @see read For more information about completion.
682-- @see shell.completeProgram
683-- @see shell.setCompletionFunction
684-- @see shell.getCompletionInfo
685function shell.complete(sLine)
686 expect(1, sLine, "string")
687 if #sLine > 0 then
688 local tWords = tokenise(sLine)
689 local nIndex = #tWords
690 if string.sub(sLine, #sLine, #sLine) == " " then
691 nIndex = nIndex + 1
692 end
693 if nIndex == 1 then
694 local sBit = tWords[1] or ""
695 local sPath = shell.resolveProgram(sBit)
696 if tCompletionInfo[sPath] then
697 return { " " }
698 else
699 local tResults = completeProgram(sBit)
700 for n = 1, #tResults do
701 local sResult = tResults[n]
702 local sPath = shell.resolveProgram(sBit .. sResult)
703 if tCompletionInfo[sPath] then
704 tResults[n] = sResult .. " "
705 end
706 end
707 return tResults
708 end
709
710 elseif nIndex > 1 then
711 local sPath = shell.resolveProgram(tWords[1])
712 local sPart = tWords[nIndex] or ""
713 local tPreviousParts = tWords
714 tPreviousParts[nIndex] = nil
715 return completeProgramArgument(sPath , nIndex - 1, sPart, tPreviousParts)
716
717 end
718 end
719 return nil
720end
721
722--- Complete the name of a program.
723--
724-- @tparam string program The name of a program to complete.
725-- @treturn { string } A list of possible completions.
726-- @see cc.shell.completion.program
727function shell.completeProgram(program)
728 expect(1, program, "string")
729 return completeProgram(program)
730end
731
732--- Set the completion function for a program. When the program is entered on
733-- the command line, this program will be called to provide auto-complete
734-- information.
735--
736-- The completion function accepts four arguments:
737--
738-- 1. The current shell. As completion functions are inherited, this is not
739-- guaranteed to be the shell you registered this function in.
740-- 2. The index of the argument currently being completed.
741-- 3. The current argument. This may be the empty string.
742-- 4. A list of the previous arguments.
743--
744-- For instance, when completing `pastebin put rom/st` our pastebin completion
745-- function will receive the shell API, an index of 2, `rom/st` as the current
746-- argument, and a "previous" table of `{ "put" }`. This function may then wish
747-- to return a table containing `artup.lua`, indicating the entire command
748-- should be completed to `pastebin put rom/startup.lua`.
749--
750-- You completion entries may also be followed by a space, if you wish to
751-- indicate another argument is expected.
752--
753-- @tparam string program The path to the program. This should be an absolute path
754-- _without_ the leading `/`.
755-- @tparam function(shell: table, index: number, argument: string, previous: { string }):({ string }|nil) complete
756-- The completion function.
757-- @see cc.shell.completion Various utilities to help with writing completion functions.
758-- @see shell.complete
759-- @see read For more information about completion.
760function shell.setCompletionFunction(program, complete)
761 expect(1, program, "string")
762 expect(2, complete, "function")
763 tCompletionInfo[program] = {
764 fnComplete = complete,
765 }
766end
767
768--- Get a table containing all completion functions.
769--
770-- This should only be needed when building custom shells. Use
771-- @{setCompletionFunction} to add a completion function.
772--
773-- @treturn { [string] = { fnComplete = function } } A table mapping the
774-- absolute path of programs, to their completion functions.
775function shell.getCompletionInfo()
776 return tCompletionInfo
777end
778
779--- Returns the path to the currently running program.
780--
781-- @treturn string The absolute path to the running program.
782function shell.getRunningProgram()
783 if #tProgramStack > 0 then
784 return tProgramStack[#tProgramStack]
785 end
786 return nil
787end
788
789--- Add an alias for a program.
790--
791-- @tparam string command The name of the alias to add.
792-- @tparam string program The name or path to the program.
793-- @usage Alias `vim` to the `edit` program
794--
795-- shell.setAlias("vim", "edit")
796function shell.setAlias(command, program)
797 expect(1, command, "string")
798 expect(2, program, "string")
799 tAliases[command] = program
800end
801
802--- Remove an alias.
803--
804-- @tparam string command The alias name to remove.
805function shell.clearAlias(command)
806 expect(1, command, "string")
807 tAliases[command] = nil
808end
809
810--- Get the current aliases for this shell.
811--
812-- Aliases are used to allow multiple commands to refer to a single program. For
813-- instance, the `list` program is aliased `dir` or `ls`. Running `ls`, `dir` or
814-- `list` in the shell will all run the `list` program.
815--
816-- @treturn { [string] = string } A table, where the keys are the names of
817-- aliases, and the values are the path to the program.
818-- @see shell.setAlias
819-- @see shell.resolveProgram This uses aliases when resolving a program name to
820-- an absolute path.
821function shell.aliases()
822 -- Copy aliases
823 local tCopy = {}
824 for sAlias, sCommand in pairs(tAliases) do
825 tCopy[sAlias] = sCommand
826 end
827 return tCopy
828end
829
830if multishell then
831 --- Open a new @{multishell} tab running a command.
832 --
833 -- This behaves similarly to @{shell.run}, but instead returns the process
834 -- index.
835 --
836 -- This function is only available if the @{multishell} API is.
837 --
838 -- @tparam string ... The command line to run.
839 -- @see shell.run
840 -- @see multishell.launch
841 -- @usage Launch the Lua interpreter and switch to it.
842 --
843 -- local id = shell.openTab("lua")
844 -- shell.switchTab(id)
845 function shell.openTab(...)
846 local tWords = tokenise(...)
847 local sCommand = tWords[1]
848 if sCommand then
849 local sPath = shell.resolveProgram(sCommand)
850 if sPath == "rom/programs/shell.lua" then
851 return multishell.launch(createShellEnv("rom/programs"), sPath, table.unpack(tWords, 2))
852 elseif sPath ~= nil then
853 return multishell.launch(createShellEnv("rom/programs"), "rom/programs/shell.lua", sCommand, table.unpack(tWords, 2))
854 else
855 printError("No such program")
856 end
857 end
858 end
859
860 --- Switch to the @{multishell} tab with the given index.
861 --
862 -- @tparam number id The tab to switch to.
863 -- @see multishell.setFocus
864 function shell.switchTab(id)
865 expect(1, id, "number")
866 multishell.setFocus(id)
867 end
868end
869
870local tArgs = { ... }
871if #tArgs > 0 then
872 -- "shell x y z"
873 -- Run the program specified on the commandline
874 shell.run(...)
875
876else
877 -- "shell"
878 -- Print the header
879 term.setBackgroundColor(bgColour)
880 term.setTextColour(promptColour)
881 term.setTextColour(textColour)
882
883 -- Run the startup program
884 if parentShell == nil then
885 shell.run("/rom/startup.lua")
886 end
887
888 -- Read commands and execute them
889 local tCommandHistory = {}
890 while not bExit do
891 term.redirect(parentTerm)
892 term.setBackgroundColor(bgColour)
893 term.setTextColour(promptColour)
894 term.write("SkyShell>")
895 term.setTextColour(textColour)
896
897
898 local sLine
899 if settings.get("shell.autocomplete") then
900 sLine = read2(nil, tCommandHistory, shell.complete)
901 else
902 sLine = read2(nil, tCommandHistory)
903 end
904 if sLine:match("%S") and tCommandHistory[#tCommandHistory] ~= sLine then
905 table.insert(tCommandHistory, sLine)
906 end
907 shell.run(sLine)
908 end
909end