Advertisement
SkyCrafter0

Untitled

Sep 30th, 2020
214
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.68 KB | None | 0 0
  1. --- The shell API provides access to CraftOS's command line interface.
  2. --
  3. -- It allows you to @{run|start programs}, @{setCompletionFunction|add
  4. -- completion for a program}, and much more.
  5. --
  6. -- @{shell} is not a "true" API. Instead, it is a standard program, which its
  7. -- API into the programs that it launches. This allows for multiple shells to
  8. -- run at the same time, but means that the API is not available in the global
  9. -- environment, and so is unavailable to other @{os.loadAPI|APIs}.
  10. --
  11. -- @module[module] shell
  12.  
  13. term.clear()
  14. term.setCursorPos(1,1)
  15. file = require("libraries.file")
  16. file.loadGrpLines("graphics/bootSplash.skgrp")
  17. gpswrapper = require("libraries.gpswrapper")
  18. if fs.exists("osBeta.lua") then
  19. sleep(1)
  20. end
  21. --Do server side things BEFORE term.clear()
  22. local function gpsGet()
  23. local x, y, z = gpswrapper.gpslocate(5)
  24. gpsCoords = vector.new(math.floor(x+0.5), math.floor(y+0.5), math.floor(z+0.5))
  25. gpsTable = file.split(gpsCoords,",")
  26. return gpsTable
  27. end
  28. term.setBackgroundColour(colours.black)
  29. term.clear()
  30. --Load DE
  31. --parallel.waitForAll(function() shell.run("customPrograms/timeManager.lua") end, function() shell.run("customPrograms/applications.lua") end)
  32. local function drawTime(x,y,backColour,textColour)
  33. local time = textutils.formatTime(os.time(), true)
  34. term.setCursorPos(x,y)
  35. term.setBackgroundColour(backColour)
  36. term.setTextColour(textColour)
  37. term.write(time)
  38. end
  39. local function drawDesktop()
  40. desktopImg = "graphics/background/default.skgrp"
  41. taskbarImg = "graphics/taskbar.skgrp"
  42. file.loadGrpLines(desktopImg)
  43. file.loadGrpLines(taskbarImg)
  44. end
  45. drawDesktop()
  46. function main()
  47. while true do
  48. term.setCursorPos(22,20)
  49. term.write(" ")
  50. drawTime(22,20,128,256)
  51. sleep()
  52. end
  53. end
  54. parallel.waitForAny(main(), function()
  55. while true do
  56. local _, char = os.pullEvent "char"
  57. if char == "e" then return end
  58. end
  59. end)
  60. local expect = dofile("rom/modules/main/cc/expect.lua").expect
  61. local make_package = dofile("rom/modules/main/cc/require.lua").make
  62.  
  63. local multishell = multishell
  64. local parentShell = shell
  65. local parentTerm = term.current()
  66.  
  67. if multishell then
  68. multishell.setTitle(multishell.getCurrent(), "shell")
  69. end
  70.  
  71. local bExit = false
  72. local sDir = parentShell and parentShell.dir() or ""
  73. local sPath = parentShell and parentShell.path() or ".:/rom/programs"
  74. local tAliases = parentShell and parentShell.aliases() or {}
  75. local tCompletionInfo = parentShell and parentShell.getCompletionInfo() or {}
  76. local tProgramStack = {}
  77.  
  78. local shell = {} --- @export
  79. local function createShellEnv(dir)
  80. local env = { shell = shell, multishell = multishell }
  81. env.require, env.package = make_package(env, dir)
  82. return env
  83. end
  84.  
  85. -- Colours
  86. local promptColour, textColour, bgColour
  87. if term.isColour() then
  88. promptColour = colours.yellow
  89. textColour = colours.white
  90. bgColour = colours.black
  91. else
  92. promptColour = colours.white
  93. textColour = colours.white
  94. bgColour = colours.black
  95. end
  96.  
  97. --- Run a program with the supplied arguments.
  98. --
  99. -- Unlike @{shell.run}, each argument is passed to the program verbatim. While
  100. -- `shell.run("echo", "b c")` runs `echo` with `b` and `c`,
  101. -- `shell.execute("echo", "b c")` runs `echo` with a single argument `b c`.
  102. --
  103. -- @tparam string command The program to execute.
  104. -- @tparam string ... Arguments to this program.
  105. -- @treturn boolean Whether the program exited successfully.
  106. -- @usage Run `paint my-image` from within your program:
  107. --
  108. -- shell.execute("paint", "my-image")
  109. function shell.execute(command, ...)
  110. expect(1, command, "string")
  111. for i = 1, select('#', ...) do
  112. expect(i + 1, select(i, ...), "string")
  113. end
  114.  
  115. local sPath = shell.resolveProgram(command)
  116. if sPath ~= nil then
  117. tProgramStack[#tProgramStack + 1] = sPath
  118. if multishell then
  119. local sTitle = fs.getName(sPath)
  120. if sTitle:sub(-4) == ".lua" then
  121. sTitle = sTitle:sub(1, -5)
  122. end
  123. multishell.setTitle(multishell.getCurrent(), sTitle)
  124. end
  125.  
  126. local sDir = fs.getDir(sPath)
  127. local env = createShellEnv(sDir)
  128. env.arg = { [0] = command, ... }
  129. local result = os.run(env, sPath, ...)
  130.  
  131. tProgramStack[#tProgramStack] = nil
  132. if multishell then
  133. if #tProgramStack > 0 then
  134. local sTitle = fs.getName(tProgramStack[#tProgramStack])
  135. if sTitle:sub(-4) == ".lua" then
  136. sTitle = sTitle:sub(1, -5)
  137. end
  138. multishell.setTitle(multishell.getCurrent(), sTitle)
  139. else
  140. multishell.setTitle(multishell.getCurrent(), "shell")
  141. end
  142. end
  143. return result
  144. else
  145. printError("No such program")
  146. return false
  147. end
  148. end
  149.  
  150. local function tokenise(...)
  151. local sLine = table.concat({ ... }, " ")
  152. local tWords = {}
  153. local bQuoted = false
  154. for match in string.gmatch(sLine .. "\"", "(.-)\"") do
  155. if bQuoted then
  156. table.insert(tWords, match)
  157. else
  158. for m in string.gmatch(match, "[^ \t]+") do
  159. table.insert(tWords, m)
  160. end
  161. end
  162. bQuoted = not bQuoted
  163. end
  164. return tWords
  165. end
  166.  
  167. -- Install shell API
  168.  
  169. --- Run a program with the supplied arguments.
  170. --
  171. -- All arguments are concatenated together and then parsed as a command line. As
  172. -- a result, `shell.run("program a b")` is the same as `shell.run("program",
  173. -- "a", "b")`.
  174. --
  175. -- @tparam string ... The program to run and its arguments.
  176. -- @treturn boolean Whether the program exited successfully.
  177. -- @usage Run `paint my-image` from within your program:
  178. --
  179. -- shell.run("paint", "my-image")
  180. -- @see shell.execute Run a program directly without parsing the arguments.
  181. function shell.run(...)
  182. local tWords = tokenise(...)
  183. local sCommand = tWords[1]
  184. if sCommand then
  185. return shell.execute(sCommand, table.unpack(tWords, 2))
  186. end
  187. return false
  188. end
  189.  
  190. --- Exit the current shell.
  191. --
  192. -- This does _not_ terminate your program, it simply makes the shell terminate
  193. -- after your program has finished. If this is the toplevel shell, then the
  194. -- computer will be shutdown.
  195. function shell.exit()
  196. bExit = true
  197. end
  198.  
  199. --- Return the current working directory. This is what is displayed before the
  200. -- `> ` of the shell prompt, and is used by @{shell.resolve} to handle relative
  201. -- paths.
  202. --
  203. -- @treturn string The current working directory.
  204. -- @see setDir To change the working directory.
  205. function shell.dir()
  206. return sDir
  207. end
  208.  
  209. --- Set the current working directory.
  210. --
  211. -- @tparam string dir The new working directory.
  212. -- @throws If the path does not exist or is not a directory.
  213. -- @usage Set the working directory to "rom"
  214. --
  215. -- shell.setDir("rom")
  216. function shell.setDir(dir)
  217. expect(1, dir, "string")
  218. if not fs.isDir(dir) then
  219. error("Not a directory", 2)
  220. end
  221. sDir = fs.combine(dir, "")
  222. end
  223.  
  224. --- Set the path where programs are located.
  225. --
  226. -- The path is composed of a list of directory names in a string, each separated
  227. -- by a colon (`:`). On normal turtles will look in the current directory (`.`),
  228. -- `/rom/programs` and `/rom/programs/turtle` folder, making the path
  229. -- `.:/rom/programs:/rom/programs/turtle`.
  230. --
  231. -- @treturn string The current shell's path.
  232. -- @see setPath To change the current path.
  233. function shell.path()
  234. return sPath
  235. end
  236.  
  237. --- Set the @{path|current program path}.
  238. --
  239. -- Be careful to prefix directories with a `/`. Otherwise they will be searched
  240. -- for from the @{shell.dir|current directory}, rather than the computer's root.
  241. --
  242. -- @tparam string path The new program path.
  243. function shell.setPath(path)
  244. expect(1, path, "string")
  245. sPath = path
  246. end
  247.  
  248. --- Resolve a relative path to an absolute path.
  249. --
  250. -- The @{fs} and @{io} APIs work using absolute paths, and so we must convert
  251. -- any paths relative to the @{dir|current directory} to absolute ones. This
  252. -- does nothing when the path starts with `/`.
  253. --
  254. -- @tparam string path The path to resolve.
  255. -- @usage Resolve `startup.lua` when in the `rom` folder.
  256. --
  257. -- shell.setDir("rom")
  258. -- print(shell.resolve("startup.lua"))
  259. -- -- => rom/startup.lua
  260. function shell.resolve(path)
  261. expect(1, path, "string")
  262. local sStartChar = string.sub(path, 1, 1)
  263. if sStartChar == "/" or sStartChar == "\\" then
  264. return fs.combine("", path)
  265. else
  266. return fs.combine(sDir, path)
  267. end
  268. end
  269.  
  270. local function pathWithExtension(_sPath, _sExt)
  271. local nLen = #sPath
  272. local sEndChar = string.sub(_sPath, nLen, nLen)
  273. -- Remove any trailing slashes so we can add an extension to the path safely
  274. if sEndChar == "/" or sEndChar == "\\" then
  275. _sPath = string.sub(_sPath, 1, nLen - 1)
  276. end
  277. return _sPath .. "." .. _sExt
  278. end
  279.  
  280. --- Resolve a program, using the @{path|program path} and list of @{aliases|aliases}.
  281. --
  282. -- @tparam string command The name of the program
  283. -- @treturn string|nil The absolute path to the program, or @{nil} if it could
  284. -- not be found.
  285. -- @usage Locate the `hello` program.
  286. --
  287. -- shell.resolveProgram("hello")
  288. -- -- => rom/programs/fun/hello.lua
  289. function shell.resolveProgram(command)
  290. expect(1, command, "string")
  291. -- Substitute aliases firsts
  292. if tAliases[command] ~= nil then
  293. command = tAliases[command]
  294. end
  295.  
  296. -- If the path is a global path, use it directly
  297. if command:find("/") or command:find("\\") then
  298. local sPath = shell.resolve(command)
  299. if fs.exists(sPath) and not fs.isDir(sPath) then
  300. return sPath
  301. else
  302. local sPathLua = pathWithExtension(sPath, "lua")
  303. if fs.exists(sPathLua) and not fs.isDir(sPathLua) then
  304. return sPathLua
  305. end
  306. end
  307. return nil
  308. end
  309.  
  310. -- Otherwise, look on the path variable
  311. for sPath in string.gmatch(sPath, "[^:]+") do
  312. sPath = fs.combine(shell.resolve(sPath), command)
  313. if fs.exists(sPath) and not fs.isDir(sPath) then
  314. return sPath
  315. else
  316. local sPathLua = pathWithExtension(sPath, "lua")
  317. if fs.exists(sPathLua) and not fs.isDir(sPathLua) then
  318. return sPathLua
  319. end
  320. end
  321. end
  322.  
  323. -- Not found
  324. return nil
  325. end
  326.  
  327. --- Return a list of all programs on the @{shell.path|path}.
  328. --
  329. -- @tparam[opt] boolean include_hidden Include hidden files. Namely, any which
  330. -- start with `.`.
  331. -- @treturn { string } A list of available programs.
  332. -- @usage textutils.tabulate(shell.programs())
  333. function shell.programs(include_hidden)
  334. expect(1, include_hidden, "boolean", "nil")
  335.  
  336. local tItems = {}
  337.  
  338. -- Add programs from the path
  339. for sPath in string.gmatch(sPath, "[^:]+") do
  340. sPath = shell.resolve(sPath)
  341. if fs.isDir(sPath) then
  342. local tList = fs.list(sPath)
  343. for n = 1, #tList do
  344. local sFile = tList[n]
  345. if not fs.isDir(fs.combine(sPath, sFile)) and
  346. (include_hidden or string.sub(sFile, 1, 1) ~= ".") then
  347. if #sFile > 4 and sFile:sub(-4) == ".lua" then
  348. sFile = sFile:sub(1, -5)
  349. end
  350. tItems[sFile] = true
  351. end
  352. end
  353. end
  354. end
  355.  
  356. -- Sort and return
  357. local tItemList = {}
  358. for sItem in pairs(tItems) do
  359. table.insert(tItemList, sItem)
  360. end
  361. table.sort(tItemList)
  362. return tItemList
  363. end
  364.  
  365. local function completeProgram(sLine)
  366. if #sLine > 0 and (sLine:find("/") or sLine:find("\\")) then
  367. -- Add programs from the root
  368. return fs.complete(sLine, sDir, true, false)
  369.  
  370. else
  371. local tResults = {}
  372. local tSeen = {}
  373.  
  374. -- Add aliases
  375. for sAlias in pairs(tAliases) do
  376. if #sAlias > #sLine and string.sub(sAlias, 1, #sLine) == sLine then
  377. local sResult = string.sub(sAlias, #sLine + 1)
  378. if not tSeen[sResult] then
  379. table.insert(tResults, sResult)
  380. tSeen[sResult] = true
  381. end
  382. end
  383. end
  384.  
  385. -- Add all subdirectories. We don't include files as they will be added in the block below
  386. local tDirs = fs.complete(sLine, sDir, false, false)
  387. for i = 1, #tDirs do
  388. local sResult = tDirs[i]
  389. if not tSeen[sResult] then
  390. table.insert (tResults, sResult)
  391. tSeen [sResult] = true
  392. end
  393. end
  394.  
  395. -- Add programs from the path
  396. local tPrograms = shell.programs()
  397. for n = 1, #tPrograms do
  398. local sProgram = tPrograms[n]
  399. if #sProgram > #sLine and string.sub(sProgram, 1, #sLine) == sLine then
  400. local sResult = string.sub(sProgram, #sLine + 1)
  401. if not tSeen[sResult] then
  402. table.insert(tResults, sResult)
  403. tSeen[sResult] = true
  404. end
  405. end
  406. end
  407.  
  408. -- Sort and return
  409. table.sort(tResults)
  410. return tResults
  411. end
  412. end
  413.  
  414. local function completeProgramArgument(sProgram, nArgument, sPart, tPreviousParts)
  415. local tInfo = tCompletionInfo[sProgram]
  416. if tInfo then
  417. return tInfo.fnComplete(shell, nArgument, sPart, tPreviousParts)
  418. end
  419. return nil
  420. end
  421.  
  422. --- Complete a shell command line.
  423. --
  424. -- This accepts an incomplete command, and completes the program name or
  425. -- arguments. For instance, `l` will be completed to `ls`, and `ls ro` will be
  426. -- completed to `ls rom/`.
  427. --
  428. -- Completion handlers for your program may be registered with
  429. -- @{shell.setCompletionFunction}.
  430. --
  431. -- @tparam string sLine The input to complete.
  432. -- @treturn { string }|nil The list of possible completions.
  433. -- @see read For more information about completion.
  434. -- @see shell.completeProgram
  435. -- @see shell.setCompletionFunction
  436. -- @see shell.getCompletionInfo
  437. function shell.complete(sLine)
  438. expect(1, sLine, "string")
  439. if #sLine > 0 then
  440. local tWords = tokenise(sLine)
  441. local nIndex = #tWords
  442. if string.sub(sLine, #sLine, #sLine) == " " then
  443. nIndex = nIndex + 1
  444. end
  445. if nIndex == 1 then
  446. local sBit = tWords[1] or ""
  447. local sPath = shell.resolveProgram(sBit)
  448. if tCompletionInfo[sPath] then
  449. return { " " }
  450. else
  451. local tResults = completeProgram(sBit)
  452. for n = 1, #tResults do
  453. local sResult = tResults[n]
  454. local sPath = shell.resolveProgram(sBit .. sResult)
  455. if tCompletionInfo[sPath] then
  456. tResults[n] = sResult .. " "
  457. end
  458. end
  459. return tResults
  460. end
  461.  
  462. elseif nIndex > 1 then
  463. local sPath = shell.resolveProgram(tWords[1])
  464. local sPart = tWords[nIndex] or ""
  465. local tPreviousParts = tWords
  466. tPreviousParts[nIndex] = nil
  467. return completeProgramArgument(sPath , nIndex - 1, sPart, tPreviousParts)
  468.  
  469. end
  470. end
  471. return nil
  472. end
  473.  
  474. --- Complete the name of a program.
  475. --
  476. -- @tparam string program The name of a program to complete.
  477. -- @treturn { string } A list of possible completions.
  478. -- @see cc.shell.completion.program
  479. function shell.completeProgram(program)
  480. expect(1, program, "string")
  481. return completeProgram(program)
  482. end
  483.  
  484. --- Set the completion function for a program. When the program is entered on
  485. -- the command line, this program will be called to provide auto-complete
  486. -- information.
  487. --
  488. -- The completion function accepts four arguments:
  489. --
  490. -- 1. The current shell. As completion functions are inherited, this is not
  491. -- guaranteed to be the shell you registered this function in.
  492. -- 2. The index of the argument currently being completed.
  493. -- 3. The current argument. This may be the empty string.
  494. -- 4. A list of the previous arguments.
  495. --
  496. -- For instance, when completing `pastebin put rom/st` our pastebin completion
  497. -- function will receive the shell API, an index of 2, `rom/st` as the current
  498. -- argument, and a "previous" table of `{ "put" }`. This function may then wish
  499. -- to return a table containing `artup.lua`, indicating the entire command
  500. -- should be completed to `pastebin put rom/startup.lua`.
  501. --
  502. -- You completion entries may also be followed by a space, if you wish to
  503. -- indicate another argument is expected.
  504. --
  505. -- @tparam string program The path to the program. This should be an absolute path
  506. -- _without_ the leading `/`.
  507. -- @tparam function(shell: table, index: number, argument: string, previous: { string }):({ string }|nil) complete
  508. -- The completion function.
  509. -- @see cc.shell.completion Various utilities to help with writing completion functions.
  510. -- @see shell.complete
  511. -- @see read For more information about completion.
  512. function shell.setCompletionFunction(program, complete)
  513. expect(1, program, "string")
  514. expect(2, complete, "function")
  515. tCompletionInfo[program] = {
  516. fnComplete = complete,
  517. }
  518. end
  519.  
  520. --- Get a table containing all completion functions.
  521. --
  522. -- This should only be needed when building custom shells. Use
  523. -- @{setCompletionFunction} to add a completion function.
  524. --
  525. -- @treturn { [string] = { fnComplete = function } } A table mapping the
  526. -- absolute path of programs, to their completion functions.
  527. function shell.getCompletionInfo()
  528. return tCompletionInfo
  529. end
  530.  
  531. --- Returns the path to the currently running program.
  532. --
  533. -- @treturn string The absolute path to the running program.
  534. function shell.getRunningProgram()
  535. if #tProgramStack > 0 then
  536. return tProgramStack[#tProgramStack]
  537. end
  538. return nil
  539. end
  540.  
  541. --- Add an alias for a program.
  542. --
  543. -- @tparam string command The name of the alias to add.
  544. -- @tparam string program The name or path to the program.
  545. -- @usage Alias `vim` to the `edit` program
  546. --
  547. -- shell.setAlias("vim", "edit")
  548. function shell.setAlias(command, program)
  549. expect(1, command, "string")
  550. expect(2, program, "string")
  551. tAliases[command] = program
  552. end
  553.  
  554. --- Remove an alias.
  555. --
  556. -- @tparam string command The alias name to remove.
  557. function shell.clearAlias(command)
  558. expect(1, command, "string")
  559. tAliases[command] = nil
  560. end
  561.  
  562. --- Get the current aliases for this shell.
  563. --
  564. -- Aliases are used to allow multiple commands to refer to a single program. For
  565. -- instance, the `list` program is aliased `dir` or `ls`. Running `ls`, `dir` or
  566. -- `list` in the shell will all run the `list` program.
  567. --
  568. -- @treturn { [string] = string } A table, where the keys are the names of
  569. -- aliases, and the values are the path to the program.
  570. -- @see shell.setAlias
  571. -- @see shell.resolveProgram This uses aliases when resolving a program name to
  572. -- an absolute path.
  573. function shell.aliases()
  574. -- Copy aliases
  575. local tCopy = {}
  576. for sAlias, sCommand in pairs(tAliases) do
  577. tCopy[sAlias] = sCommand
  578. end
  579. return tCopy
  580. end
  581.  
  582. if multishell then
  583. --- Open a new @{multishell} tab running a command.
  584. --
  585. -- This behaves similarly to @{shell.run}, but instead returns the process
  586. -- index.
  587. --
  588. -- This function is only available if the @{multishell} API is.
  589. --
  590. -- @tparam string ... The command line to run.
  591. -- @see shell.run
  592. -- @see multishell.launch
  593. -- @usage Launch the Lua interpreter and switch to it.
  594. --
  595. -- local id = shell.openTab("lua")
  596. -- shell.switchTab(id)
  597. function shell.openTab(...)
  598. local tWords = tokenise(...)
  599. local sCommand = tWords[1]
  600. if sCommand then
  601. local sPath = shell.resolveProgram(sCommand)
  602. if sPath == "rom/programs/shell.lua" then
  603. return multishell.launch(createShellEnv("rom/programs"), sPath, table.unpack(tWords, 2))
  604. elseif sPath ~= nil then
  605. return multishell.launch(createShellEnv("rom/programs"), "rom/programs/shell.lua", sCommand, table.unpack(tWords, 2))
  606. else
  607. printError("No such program")
  608. end
  609. end
  610. end
  611.  
  612. --- Switch to the @{multishell} tab with the given index.
  613. --
  614. -- @tparam number id The tab to switch to.
  615. -- @see multishell.setFocus
  616. function shell.switchTab(id)
  617. expect(1, id, "number")
  618. multishell.setFocus(id)
  619. end
  620. end
  621.  
  622. local tArgs = { ... }
  623. if #tArgs > 0 then
  624. -- "shell x y z"
  625. -- Run the program specified on the commandline
  626. shell.run(...)
  627.  
  628. else
  629. -- "shell"
  630. -- Print the header
  631. term.setBackgroundColor(bgColour)
  632. term.setTextColour(promptColour)
  633. print(os.version())
  634. term.setTextColour(textColour)
  635.  
  636. -- Run the startup program
  637. if parentShell == nil then
  638. shell.run("/rom/startup.lua")
  639. end
  640.  
  641. -- Read commands and execute them
  642. local tCommandHistory = {}
  643. while not bExit do
  644. term.redirect(parentTerm)
  645. term.setBackgroundColor(bgColour)
  646. term.setTextColour(promptColour)
  647. write(shell.dir() .. ":S> ")
  648. term.setTextColour(textColour)
  649.  
  650.  
  651. local sLine
  652. if settings.get("shell.autocomplete") then
  653. sLine = read(nil, tCommandHistory, shell.complete)
  654. else
  655. sLine = read(nil, tCommandHistory)
  656. end
  657. if sLine:match("%S") and tCommandHistory[#tCommandHistory] ~= sLine then
  658. table.insert(tCommandHistory, sLine)
  659. end
  660. shell.run(sLine)
  661. end
  662. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement