Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --- The shell API provides access to CraftOS's command line interface.
- --
- -- It allows you to @{run|start programs}, @{setCompletionFunction|add
- -- completion for a program}, and much more.
- --
- -- @{shell} is not a "true" API. Instead, it is a standard program, which its
- -- API into the programs that it launches. This allows for multiple shells to
- -- run at the same time, but means that the API is not available in the global
- -- environment, and so is unavailable to other @{os.loadAPI|APIs}.
- --
- -- @module[module] shell
- term.clear()
- term.setCursorPos(1,1)
- file = require("libraries.file")
- file.loadGrpLines("graphics/bootSplash.skgrp")
- gpswrapper = require("libraries.gpswrapper")
- if fs.exists("osBeta.lua") then
- sleep(1)
- end
- --Do server side things BEFORE term.clear()
- local function gpsGet()
- local x, y, z = gpswrapper.gpslocate(5)
- gpsCoords = vector.new(math.floor(x+0.5), math.floor(y+0.5), math.floor(z+0.5))
- gpsTable = file.split(gpsCoords,",")
- return gpsTable
- end
- term.setBackgroundColour(colours.black)
- term.clear()
- --Load DE
- --parallel.waitForAll(function() shell.run("customPrograms/timeManager.lua") end, function() shell.run("customPrograms/applications.lua") end)
- local function drawTime(x,y,backColour,textColour)
- local time = textutils.formatTime(os.time(), true)
- term.setCursorPos(x,y)
- term.setBackgroundColour(backColour)
- term.setTextColour(textColour)
- term.write(time)
- end
- local function drawDesktop()
- desktopImg = "graphics/background/default.skgrp"
- taskbarImg = "graphics/taskbar.skgrp"
- file.loadGrpLines(desktopImg)
- file.loadGrpLines(taskbarImg)
- end
- drawDesktop()
- function main()
- while true do
- term.setCursorPos(22,20)
- term.write(" ")
- drawTime(22,20,128,256)
- sleep()
- end
- end
- parallel.waitForAny(main(), function()
- while true do
- local _, char = os.pullEvent "char"
- if char == "e" then return end
- end
- end)
- local expect = dofile("rom/modules/main/cc/expect.lua").expect
- local make_package = dofile("rom/modules/main/cc/require.lua").make
- local multishell = multishell
- local parentShell = shell
- local parentTerm = term.current()
- if multishell then
- multishell.setTitle(multishell.getCurrent(), "shell")
- end
- local bExit = false
- local sDir = parentShell and parentShell.dir() or ""
- local sPath = parentShell and parentShell.path() or ".:/rom/programs"
- local tAliases = parentShell and parentShell.aliases() or {}
- local tCompletionInfo = parentShell and parentShell.getCompletionInfo() or {}
- local tProgramStack = {}
- local shell = {} --- @export
- local function createShellEnv(dir)
- local env = { shell = shell, multishell = multishell }
- env.require, env.package = make_package(env, dir)
- return env
- end
- -- Colours
- local promptColour, textColour, bgColour
- if term.isColour() then
- promptColour = colours.yellow
- textColour = colours.white
- bgColour = colours.black
- else
- promptColour = colours.white
- textColour = colours.white
- bgColour = colours.black
- end
- --- Run a program with the supplied arguments.
- --
- -- Unlike @{shell.run}, each argument is passed to the program verbatim. While
- -- `shell.run("echo", "b c")` runs `echo` with `b` and `c`,
- -- `shell.execute("echo", "b c")` runs `echo` with a single argument `b c`.
- --
- -- @tparam string command The program to execute.
- -- @tparam string ... Arguments to this program.
- -- @treturn boolean Whether the program exited successfully.
- -- @usage Run `paint my-image` from within your program:
- --
- -- shell.execute("paint", "my-image")
- function shell.execute(command, ...)
- expect(1, command, "string")
- for i = 1, select('#', ...) do
- expect(i + 1, select(i, ...), "string")
- end
- local sPath = shell.resolveProgram(command)
- if sPath ~= nil then
- tProgramStack[#tProgramStack + 1] = sPath
- if multishell then
- local sTitle = fs.getName(sPath)
- if sTitle:sub(-4) == ".lua" then
- sTitle = sTitle:sub(1, -5)
- end
- multishell.setTitle(multishell.getCurrent(), sTitle)
- end
- local sDir = fs.getDir(sPath)
- local env = createShellEnv(sDir)
- env.arg = { [0] = command, ... }
- local result = os.run(env, sPath, ...)
- tProgramStack[#tProgramStack] = nil
- if multishell then
- if #tProgramStack > 0 then
- local sTitle = fs.getName(tProgramStack[#tProgramStack])
- if sTitle:sub(-4) == ".lua" then
- sTitle = sTitle:sub(1, -5)
- end
- multishell.setTitle(multishell.getCurrent(), sTitle)
- else
- multishell.setTitle(multishell.getCurrent(), "shell")
- end
- end
- return result
- else
- printError("No such program")
- return false
- end
- end
- local function tokenise(...)
- local sLine = table.concat({ ... }, " ")
- local tWords = {}
- local bQuoted = false
- for match in string.gmatch(sLine .. "\"", "(.-)\"") do
- if bQuoted then
- table.insert(tWords, match)
- else
- for m in string.gmatch(match, "[^ \t]+") do
- table.insert(tWords, m)
- end
- end
- bQuoted = not bQuoted
- end
- return tWords
- end
- -- Install shell API
- --- Run a program with the supplied arguments.
- --
- -- All arguments are concatenated together and then parsed as a command line. As
- -- a result, `shell.run("program a b")` is the same as `shell.run("program",
- -- "a", "b")`.
- --
- -- @tparam string ... The program to run and its arguments.
- -- @treturn boolean Whether the program exited successfully.
- -- @usage Run `paint my-image` from within your program:
- --
- -- shell.run("paint", "my-image")
- -- @see shell.execute Run a program directly without parsing the arguments.
- function shell.run(...)
- local tWords = tokenise(...)
- local sCommand = tWords[1]
- if sCommand then
- return shell.execute(sCommand, table.unpack(tWords, 2))
- end
- return false
- end
- --- Exit the current shell.
- --
- -- This does _not_ terminate your program, it simply makes the shell terminate
- -- after your program has finished. If this is the toplevel shell, then the
- -- computer will be shutdown.
- function shell.exit()
- bExit = true
- end
- --- Return the current working directory. This is what is displayed before the
- -- `> ` of the shell prompt, and is used by @{shell.resolve} to handle relative
- -- paths.
- --
- -- @treturn string The current working directory.
- -- @see setDir To change the working directory.
- function shell.dir()
- return sDir
- end
- --- Set the current working directory.
- --
- -- @tparam string dir The new working directory.
- -- @throws If the path does not exist or is not a directory.
- -- @usage Set the working directory to "rom"
- --
- -- shell.setDir("rom")
- function shell.setDir(dir)
- expect(1, dir, "string")
- if not fs.isDir(dir) then
- error("Not a directory", 2)
- end
- sDir = fs.combine(dir, "")
- end
- --- Set the path where programs are located.
- --
- -- The path is composed of a list of directory names in a string, each separated
- -- by a colon (`:`). On normal turtles will look in the current directory (`.`),
- -- `/rom/programs` and `/rom/programs/turtle` folder, making the path
- -- `.:/rom/programs:/rom/programs/turtle`.
- --
- -- @treturn string The current shell's path.
- -- @see setPath To change the current path.
- function shell.path()
- return sPath
- end
- --- Set the @{path|current program path}.
- --
- -- Be careful to prefix directories with a `/`. Otherwise they will be searched
- -- for from the @{shell.dir|current directory}, rather than the computer's root.
- --
- -- @tparam string path The new program path.
- function shell.setPath(path)
- expect(1, path, "string")
- sPath = path
- end
- --- Resolve a relative path to an absolute path.
- --
- -- The @{fs} and @{io} APIs work using absolute paths, and so we must convert
- -- any paths relative to the @{dir|current directory} to absolute ones. This
- -- does nothing when the path starts with `/`.
- --
- -- @tparam string path The path to resolve.
- -- @usage Resolve `startup.lua` when in the `rom` folder.
- --
- -- shell.setDir("rom")
- -- print(shell.resolve("startup.lua"))
- -- -- => rom/startup.lua
- function shell.resolve(path)
- expect(1, path, "string")
- local sStartChar = string.sub(path, 1, 1)
- if sStartChar == "/" or sStartChar == "\\" then
- return fs.combine("", path)
- else
- return fs.combine(sDir, path)
- end
- end
- local function pathWithExtension(_sPath, _sExt)
- local nLen = #sPath
- local sEndChar = string.sub(_sPath, nLen, nLen)
- -- Remove any trailing slashes so we can add an extension to the path safely
- if sEndChar == "/" or sEndChar == "\\" then
- _sPath = string.sub(_sPath, 1, nLen - 1)
- end
- return _sPath .. "." .. _sExt
- end
- --- Resolve a program, using the @{path|program path} and list of @{aliases|aliases}.
- --
- -- @tparam string command The name of the program
- -- @treturn string|nil The absolute path to the program, or @{nil} if it could
- -- not be found.
- -- @usage Locate the `hello` program.
- --
- -- shell.resolveProgram("hello")
- -- -- => rom/programs/fun/hello.lua
- function shell.resolveProgram(command)
- expect(1, command, "string")
- -- Substitute aliases firsts
- if tAliases[command] ~= nil then
- command = tAliases[command]
- end
- -- If the path is a global path, use it directly
- if command:find("/") or command:find("\\") then
- local sPath = shell.resolve(command)
- if fs.exists(sPath) and not fs.isDir(sPath) then
- return sPath
- else
- local sPathLua = pathWithExtension(sPath, "lua")
- if fs.exists(sPathLua) and not fs.isDir(sPathLua) then
- return sPathLua
- end
- end
- return nil
- end
- -- Otherwise, look on the path variable
- for sPath in string.gmatch(sPath, "[^:]+") do
- sPath = fs.combine(shell.resolve(sPath), command)
- if fs.exists(sPath) and not fs.isDir(sPath) then
- return sPath
- else
- local sPathLua = pathWithExtension(sPath, "lua")
- if fs.exists(sPathLua) and not fs.isDir(sPathLua) then
- return sPathLua
- end
- end
- end
- -- Not found
- return nil
- end
- --- Return a list of all programs on the @{shell.path|path}.
- --
- -- @tparam[opt] boolean include_hidden Include hidden files. Namely, any which
- -- start with `.`.
- -- @treturn { string } A list of available programs.
- -- @usage textutils.tabulate(shell.programs())
- function shell.programs(include_hidden)
- expect(1, include_hidden, "boolean", "nil")
- local tItems = {}
- -- Add programs from the path
- for sPath in string.gmatch(sPath, "[^:]+") do
- sPath = shell.resolve(sPath)
- if fs.isDir(sPath) then
- local tList = fs.list(sPath)
- for n = 1, #tList do
- local sFile = tList[n]
- if not fs.isDir(fs.combine(sPath, sFile)) and
- (include_hidden or string.sub(sFile, 1, 1) ~= ".") then
- if #sFile > 4 and sFile:sub(-4) == ".lua" then
- sFile = sFile:sub(1, -5)
- end
- tItems[sFile] = true
- end
- end
- end
- end
- -- Sort and return
- local tItemList = {}
- for sItem in pairs(tItems) do
- table.insert(tItemList, sItem)
- end
- table.sort(tItemList)
- return tItemList
- end
- local function completeProgram(sLine)
- if #sLine > 0 and (sLine:find("/") or sLine:find("\\")) then
- -- Add programs from the root
- return fs.complete(sLine, sDir, true, false)
- else
- local tResults = {}
- local tSeen = {}
- -- Add aliases
- for sAlias in pairs(tAliases) do
- if #sAlias > #sLine and string.sub(sAlias, 1, #sLine) == sLine then
- local sResult = string.sub(sAlias, #sLine + 1)
- if not tSeen[sResult] then
- table.insert(tResults, sResult)
- tSeen[sResult] = true
- end
- end
- end
- -- Add all subdirectories. We don't include files as they will be added in the block below
- local tDirs = fs.complete(sLine, sDir, false, false)
- for i = 1, #tDirs do
- local sResult = tDirs[i]
- if not tSeen[sResult] then
- table.insert (tResults, sResult)
- tSeen [sResult] = true
- end
- end
- -- Add programs from the path
- local tPrograms = shell.programs()
- for n = 1, #tPrograms do
- local sProgram = tPrograms[n]
- if #sProgram > #sLine and string.sub(sProgram, 1, #sLine) == sLine then
- local sResult = string.sub(sProgram, #sLine + 1)
- if not tSeen[sResult] then
- table.insert(tResults, sResult)
- tSeen[sResult] = true
- end
- end
- end
- -- Sort and return
- table.sort(tResults)
- return tResults
- end
- end
- local function completeProgramArgument(sProgram, nArgument, sPart, tPreviousParts)
- local tInfo = tCompletionInfo[sProgram]
- if tInfo then
- return tInfo.fnComplete(shell, nArgument, sPart, tPreviousParts)
- end
- return nil
- end
- --- Complete a shell command line.
- --
- -- This accepts an incomplete command, and completes the program name or
- -- arguments. For instance, `l` will be completed to `ls`, and `ls ro` will be
- -- completed to `ls rom/`.
- --
- -- Completion handlers for your program may be registered with
- -- @{shell.setCompletionFunction}.
- --
- -- @tparam string sLine The input to complete.
- -- @treturn { string }|nil The list of possible completions.
- -- @see read For more information about completion.
- -- @see shell.completeProgram
- -- @see shell.setCompletionFunction
- -- @see shell.getCompletionInfo
- function shell.complete(sLine)
- expect(1, sLine, "string")
- if #sLine > 0 then
- local tWords = tokenise(sLine)
- local nIndex = #tWords
- if string.sub(sLine, #sLine, #sLine) == " " then
- nIndex = nIndex + 1
- end
- if nIndex == 1 then
- local sBit = tWords[1] or ""
- local sPath = shell.resolveProgram(sBit)
- if tCompletionInfo[sPath] then
- return { " " }
- else
- local tResults = completeProgram(sBit)
- for n = 1, #tResults do
- local sResult = tResults[n]
- local sPath = shell.resolveProgram(sBit .. sResult)
- if tCompletionInfo[sPath] then
- tResults[n] = sResult .. " "
- end
- end
- return tResults
- end
- elseif nIndex > 1 then
- local sPath = shell.resolveProgram(tWords[1])
- local sPart = tWords[nIndex] or ""
- local tPreviousParts = tWords
- tPreviousParts[nIndex] = nil
- return completeProgramArgument(sPath , nIndex - 1, sPart, tPreviousParts)
- end
- end
- return nil
- end
- --- Complete the name of a program.
- --
- -- @tparam string program The name of a program to complete.
- -- @treturn { string } A list of possible completions.
- -- @see cc.shell.completion.program
- function shell.completeProgram(program)
- expect(1, program, "string")
- return completeProgram(program)
- end
- --- Set the completion function for a program. When the program is entered on
- -- the command line, this program will be called to provide auto-complete
- -- information.
- --
- -- The completion function accepts four arguments:
- --
- -- 1. The current shell. As completion functions are inherited, this is not
- -- guaranteed to be the shell you registered this function in.
- -- 2. The index of the argument currently being completed.
- -- 3. The current argument. This may be the empty string.
- -- 4. A list of the previous arguments.
- --
- -- For instance, when completing `pastebin put rom/st` our pastebin completion
- -- function will receive the shell API, an index of 2, `rom/st` as the current
- -- argument, and a "previous" table of `{ "put" }`. This function may then wish
- -- to return a table containing `artup.lua`, indicating the entire command
- -- should be completed to `pastebin put rom/startup.lua`.
- --
- -- You completion entries may also be followed by a space, if you wish to
- -- indicate another argument is expected.
- --
- -- @tparam string program The path to the program. This should be an absolute path
- -- _without_ the leading `/`.
- -- @tparam function(shell: table, index: number, argument: string, previous: { string }):({ string }|nil) complete
- -- The completion function.
- -- @see cc.shell.completion Various utilities to help with writing completion functions.
- -- @see shell.complete
- -- @see read For more information about completion.
- function shell.setCompletionFunction(program, complete)
- expect(1, program, "string")
- expect(2, complete, "function")
- tCompletionInfo[program] = {
- fnComplete = complete,
- }
- end
- --- Get a table containing all completion functions.
- --
- -- This should only be needed when building custom shells. Use
- -- @{setCompletionFunction} to add a completion function.
- --
- -- @treturn { [string] = { fnComplete = function } } A table mapping the
- -- absolute path of programs, to their completion functions.
- function shell.getCompletionInfo()
- return tCompletionInfo
- end
- --- Returns the path to the currently running program.
- --
- -- @treturn string The absolute path to the running program.
- function shell.getRunningProgram()
- if #tProgramStack > 0 then
- return tProgramStack[#tProgramStack]
- end
- return nil
- end
- --- Add an alias for a program.
- --
- -- @tparam string command The name of the alias to add.
- -- @tparam string program The name or path to the program.
- -- @usage Alias `vim` to the `edit` program
- --
- -- shell.setAlias("vim", "edit")
- function shell.setAlias(command, program)
- expect(1, command, "string")
- expect(2, program, "string")
- tAliases[command] = program
- end
- --- Remove an alias.
- --
- -- @tparam string command The alias name to remove.
- function shell.clearAlias(command)
- expect(1, command, "string")
- tAliases[command] = nil
- end
- --- Get the current aliases for this shell.
- --
- -- Aliases are used to allow multiple commands to refer to a single program. For
- -- instance, the `list` program is aliased `dir` or `ls`. Running `ls`, `dir` or
- -- `list` in the shell will all run the `list` program.
- --
- -- @treturn { [string] = string } A table, where the keys are the names of
- -- aliases, and the values are the path to the program.
- -- @see shell.setAlias
- -- @see shell.resolveProgram This uses aliases when resolving a program name to
- -- an absolute path.
- function shell.aliases()
- -- Copy aliases
- local tCopy = {}
- for sAlias, sCommand in pairs(tAliases) do
- tCopy[sAlias] = sCommand
- end
- return tCopy
- end
- if multishell then
- --- Open a new @{multishell} tab running a command.
- --
- -- This behaves similarly to @{shell.run}, but instead returns the process
- -- index.
- --
- -- This function is only available if the @{multishell} API is.
- --
- -- @tparam string ... The command line to run.
- -- @see shell.run
- -- @see multishell.launch
- -- @usage Launch the Lua interpreter and switch to it.
- --
- -- local id = shell.openTab("lua")
- -- shell.switchTab(id)
- function shell.openTab(...)
- local tWords = tokenise(...)
- local sCommand = tWords[1]
- if sCommand then
- local sPath = shell.resolveProgram(sCommand)
- if sPath == "rom/programs/shell.lua" then
- return multishell.launch(createShellEnv("rom/programs"), sPath, table.unpack(tWords, 2))
- elseif sPath ~= nil then
- return multishell.launch(createShellEnv("rom/programs"), "rom/programs/shell.lua", sCommand, table.unpack(tWords, 2))
- else
- printError("No such program")
- end
- end
- end
- --- Switch to the @{multishell} tab with the given index.
- --
- -- @tparam number id The tab to switch to.
- -- @see multishell.setFocus
- function shell.switchTab(id)
- expect(1, id, "number")
- multishell.setFocus(id)
- end
- end
- local tArgs = { ... }
- if #tArgs > 0 then
- -- "shell x y z"
- -- Run the program specified on the commandline
- shell.run(...)
- else
- -- "shell"
- -- Print the header
- term.setBackgroundColor(bgColour)
- term.setTextColour(promptColour)
- print(os.version())
- term.setTextColour(textColour)
- -- Run the startup program
- if parentShell == nil then
- shell.run("/rom/startup.lua")
- end
- -- Read commands and execute them
- local tCommandHistory = {}
- while not bExit do
- term.redirect(parentTerm)
- term.setBackgroundColor(bgColour)
- term.setTextColour(promptColour)
- write(shell.dir() .. ":S> ")
- term.setTextColour(textColour)
- local sLine
- if settings.get("shell.autocomplete") then
- sLine = read(nil, tCommandHistory, shell.complete)
- else
- sLine = read(nil, tCommandHistory)
- end
- if sLine:match("%S") and tCommandHistory[#tCommandHistory] ~= sLine then
- table.insert(tCommandHistory, sLine)
- end
- shell.run(sLine)
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement