mirror of
https://github.com/CCLeonOS/LeonOS.git
synced 2026-03-03 15:17:01 +00:00
feat: 初始提交 LeonOS 实现
添加 LeonOS 的基本实现,包括: - 核心 API 模块(colors, disk, gps, keys, multishell, parallel, rednet, redstone, settings, vector) - 命令行程序(about, alias, bg, clear, copy, delete, edit, fg, help, list, lua, mkdir, move, paint, peripherals, programs, reboot, set, shutdown, threads) - 系统启动脚本和包管理 - 文档(README.md, LICENSE) - 开发工具(devbin)和更新程序 实现功能: - 完整的线程管理系统 - 兼容 ComputerCraft 的 API 设计 - 改进的 shell 和命令补全系统 - 多标签终端支持 - 设置管理系统
This commit is contained in:
201
data/computercraft/lua/rom/startup/00_fs.lua
Normal file
201
data/computercraft/lua/rom/startup/00_fs.lua
Normal file
@@ -0,0 +1,201 @@
|
||||
-- override the fs library to use this resolution function where necessary
|
||||
-- almost identical to the override used in .OS
|
||||
|
||||
local fs = rawget(_G, "fs")
|
||||
|
||||
-- split a file path into segments
|
||||
function fs.split(path)
|
||||
local s = {}
|
||||
for S in path:gmatch("[^/\\]+") do
|
||||
if S == ".." then
|
||||
s[#s] = nil
|
||||
elseif S ~= "." then
|
||||
s[#s+1] = S
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
-- package isn't loaded yet, so unfortunately this is necessary
|
||||
local function expect(...)
|
||||
return require and require("cc.expect").expect(...)
|
||||
end
|
||||
|
||||
-- path resolution:
|
||||
-- if the path begins with /rc, then redirect to wherever that actually
|
||||
-- is; otherwise, resolve the path based on the current program's working
|
||||
-- directory
|
||||
-- this is to allow .OS to run from anywhere
|
||||
local function resolve(path)
|
||||
local thread = package and package.loaded.thread
|
||||
|
||||
local root = (thread and thread.getroot()) or "/"
|
||||
local pwd = (thread and thread.dir()) or "/"
|
||||
|
||||
if path:sub(1,1) ~= "/" then
|
||||
path = fs.combine(pwd, path)
|
||||
end
|
||||
path = fs.combine(root, path)
|
||||
|
||||
local segments = fs.split(path)
|
||||
if segments[1] == "rc" then
|
||||
return fs.combine(_RC_ROM_DIR, table.concat(segments, "/", 2, #segments))
|
||||
else
|
||||
return path
|
||||
end
|
||||
end
|
||||
|
||||
-- override: fs.combine
|
||||
local combine = fs.combine
|
||||
function fs.combine(...)
|
||||
return "/" .. combine(...)
|
||||
end
|
||||
|
||||
-- override: fs.getDir
|
||||
local getDir = fs.getDir
|
||||
function fs.getDir(p)
|
||||
return "/" .. getDir(p)
|
||||
end
|
||||
|
||||
-- override: fs.exists
|
||||
local exists = fs.exists
|
||||
function fs.exists(path)
|
||||
expect(1, path, "string")
|
||||
return exists(resolve(path))
|
||||
end
|
||||
|
||||
-- override: fs.list
|
||||
local list = fs.list
|
||||
function fs.list(path)
|
||||
expect(1, path, "string")
|
||||
path = resolve(path)
|
||||
local _, files = pcall(list, path)
|
||||
if not _ then return nil, files end
|
||||
if path == "/" then
|
||||
-- inject /rc into the root listing
|
||||
if not exists("/rc") then
|
||||
files[#files+1] = "rc"
|
||||
end
|
||||
end
|
||||
return files
|
||||
end
|
||||
|
||||
-- override: fs.getSize
|
||||
local getSize = fs.getSize
|
||||
function fs.getSize(path)
|
||||
expect(1, path, "string")
|
||||
return getSize((resolve(path)))
|
||||
end
|
||||
|
||||
-- override: fs.isDir
|
||||
local isDir = fs.isDir
|
||||
function fs.isDir(path)
|
||||
expect(1, path, "string")
|
||||
return isDir(resolve(path))
|
||||
end
|
||||
|
||||
-- override: fs.makeDir
|
||||
local makeDir = fs.makeDir
|
||||
function fs.makeDir(path)
|
||||
expect(1, path, "string")
|
||||
return makeDir(resolve(path))
|
||||
end
|
||||
|
||||
-- override: fs.move
|
||||
local move = fs.move
|
||||
function fs.move(a, b)
|
||||
expect(1, a, "string")
|
||||
expect(2, b, "string")
|
||||
return move(resolve(a), resolve(b))
|
||||
end
|
||||
|
||||
-- override: fs.copy
|
||||
local copy = fs.copy
|
||||
function fs.copy(a, b)
|
||||
expect(1, a, "string")
|
||||
expect(2, b, "string")
|
||||
return copy(resolve(a), resolve(b))
|
||||
end
|
||||
|
||||
-- override: fs.delete
|
||||
local delete = fs.delete
|
||||
function fs.delete(path)
|
||||
expect(1, path, "string")
|
||||
return delete(resolve(path))
|
||||
end
|
||||
|
||||
-- override: fs.open
|
||||
local open = fs.open
|
||||
function fs.open(file, mode)
|
||||
expect(1, file, "string")
|
||||
expect(2, mode, "string")
|
||||
return open(resolve(file), mode or "r")
|
||||
end
|
||||
|
||||
-- override: fs.find
|
||||
local find = fs.find
|
||||
function fs.find(path)
|
||||
expect(1, path, "string")
|
||||
return find(resolve(path))
|
||||
end
|
||||
|
||||
-- override: fs.attributes
|
||||
local attributes = fs.attributes
|
||||
function fs.attributes(path)
|
||||
expect(1, path, "string")
|
||||
return attributes(resolve(path))
|
||||
end
|
||||
|
||||
-- new: fs.complete
|
||||
function fs.complete(path, location, include_files, include_dirs)
|
||||
expect(1, path, "string")
|
||||
expect(2, location, "string")
|
||||
expect(3, include_files, "boolean", "nil")
|
||||
expect(4, include_dirs, "boolean", "nil")
|
||||
|
||||
if include_files == nil then include_files = true end
|
||||
if include_dirs == nil then include_dirs = true end
|
||||
|
||||
if path:sub(1,1) == "/" and path:sub(-1) ~= "/" then
|
||||
location = fs.getDir(path)
|
||||
elseif path:sub(-1) == "/" then
|
||||
location = path
|
||||
else
|
||||
location = fs.combine(location, fs.getDir(path))
|
||||
end
|
||||
|
||||
local completions = {}
|
||||
|
||||
if not fs.exists(location) or not fs.isDir(location) then
|
||||
return completions
|
||||
end
|
||||
|
||||
local name = fs.getName(path)
|
||||
if path:sub(-1) == "/" then name = "" end
|
||||
local files = fs.list(location)
|
||||
|
||||
for i=1, #files, 1 do
|
||||
local file = files[i]
|
||||
local full = fs.combine(location, file)
|
||||
if file:sub(1, #name) == name then
|
||||
local dir = fs.isDir(full)
|
||||
if (dir and include_dirs) or include_files then
|
||||
completions[#completions+1] = file:sub(#name+1)
|
||||
if #completions[#completions] == 0 then
|
||||
completions[#completions] = nil
|
||||
end
|
||||
end
|
||||
if dir then
|
||||
completions[#completions+1] = file:sub(#name+1) .. "/"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return completions
|
||||
end
|
||||
|
||||
function fs.isDriveRoot(path)
|
||||
expect(1, path, "string")
|
||||
if #path == 0 then path = "/" end
|
||||
return path == "/" or fs.getDrive(path) == fs.getDrive(fs.getDir(path))
|
||||
end
|
||||
131
data/computercraft/lua/rom/startup/10_package.lua
Normal file
131
data/computercraft/lua/rom/startup/10_package.lua
Normal file
@@ -0,0 +1,131 @@
|
||||
-- package library --
|
||||
|
||||
local rc = ...
|
||||
|
||||
_G.package = {}
|
||||
|
||||
package.config = "/\n;\n?\n!\n-"
|
||||
package.cpath = ""
|
||||
package.path = "/rc/apis/?.lua;/rc/modules/main/?.lua;./lib/?.lua;./lib/?/init.lua;./?.lua;./?/init.lua"
|
||||
|
||||
local function rm(api)
|
||||
local tab = _G[api]
|
||||
_G[api] = nil
|
||||
return tab
|
||||
end
|
||||
|
||||
package.loaded = {
|
||||
_G = _G,
|
||||
os = os,
|
||||
rc = rc,
|
||||
math = math,
|
||||
utf8 = utf8,
|
||||
table = table,
|
||||
debug = debug,
|
||||
bit32 = rawget(_G, "bit32"),
|
||||
string = string,
|
||||
package = package,
|
||||
coroutine = coroutine,
|
||||
|
||||
-- CC-specific ones
|
||||
peripheral = rm("peripheral"),
|
||||
redstone = rm("redstone"),
|
||||
commands = rm("commands"),
|
||||
pocket = rm("pocket"),
|
||||
turtle = rm("turtle"),
|
||||
http = rm("http"),
|
||||
term = rm("term"),
|
||||
fs = rm("fs"),
|
||||
rs = rm("rs"),
|
||||
|
||||
-- LeonOS-PC APIs
|
||||
periphemu = rm("periphemu"),
|
||||
mounter = rm("mounter"),
|
||||
config = rm("config"),
|
||||
|
||||
-- CCEmuX API
|
||||
ccemux = rm("ccemux")
|
||||
}
|
||||
|
||||
package.preload = {}
|
||||
|
||||
package.searchers = {
|
||||
-- check package.preload
|
||||
function(mod)
|
||||
if package.preload[mod] then
|
||||
return package.preload[mod]
|
||||
else
|
||||
return nil, "no field package.preload['" .. mod .. "']"
|
||||
end
|
||||
end,
|
||||
|
||||
-- check for lua library
|
||||
function(mod)
|
||||
local ok, err = package.searchpath(mod, package.path, ".", "/")
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local func, loaderr = loadfile(ok)
|
||||
if not func then
|
||||
return nil, loaderr
|
||||
end
|
||||
return func()
|
||||
end,
|
||||
}
|
||||
|
||||
local fs = package.loaded.fs
|
||||
-- require isn't here yet
|
||||
local expect = loadfile("/rc/modules/main/cc/expect.lua")()
|
||||
package.loaded["cc.expect"] = expect
|
||||
|
||||
function package.searchpath(name, path, sep, rep)
|
||||
expect(1, name, "string")
|
||||
expect(2, path, "string")
|
||||
expect(3, sep, "string", "nil")
|
||||
expect(4, rep, "string", "nil")
|
||||
|
||||
sep = "%" .. (sep or ".")
|
||||
rep = rep or "/"
|
||||
|
||||
name = name:gsub(sep, rep)
|
||||
local serr = ""
|
||||
|
||||
for search in path:gmatch("[^;]+") do
|
||||
search = search:gsub("%?", name)
|
||||
|
||||
if fs.exists(search) then
|
||||
return search
|
||||
|
||||
else
|
||||
if #serr > 0 then
|
||||
serr = serr .. "\n "
|
||||
end
|
||||
|
||||
serr = serr .. "no file '" .. search .. "'"
|
||||
end
|
||||
end
|
||||
|
||||
return nil, serr
|
||||
end
|
||||
|
||||
function _G.require(mod)
|
||||
expect(1, mod, "string")
|
||||
|
||||
if package.loaded[mod] then
|
||||
return package.loaded[mod]
|
||||
end
|
||||
|
||||
local serr = "module '" .. mod .. "' not found:"
|
||||
for _, searcher in ipairs(package.searchers) do
|
||||
local result, err = searcher(mod)
|
||||
if result then
|
||||
package.loaded[mod] = result
|
||||
return result
|
||||
else
|
||||
serr = serr .. "\n " .. err
|
||||
end
|
||||
end
|
||||
|
||||
error(serr, 2)
|
||||
end
|
||||
388
data/computercraft/lua/rom/startup/15_term.lua
Normal file
388
data/computercraft/lua/rom/startup/15_term.lua
Normal file
@@ -0,0 +1,388 @@
|
||||
-- some term things
|
||||
-- e.g. redirects, read()
|
||||
|
||||
local rc = ...
|
||||
|
||||
-- we need a couple of these
|
||||
local colors = require("colors")
|
||||
local native = require("term")
|
||||
local strings = require("cc.strings")
|
||||
local expect = require("cc.expect").expect
|
||||
|
||||
local term = {}
|
||||
package.loaded.term = term
|
||||
local thread = require("rc.thread")
|
||||
|
||||
local valid = {
|
||||
write = true,
|
||||
scroll = true,
|
||||
getCursorPos = true,
|
||||
setCursorPos = true,
|
||||
getCursorBlink = true,
|
||||
setCursorBlink = true,
|
||||
getSize = true,
|
||||
clear = true,
|
||||
clearLine = true,
|
||||
getTextColor = true,
|
||||
getTextColour = true,
|
||||
setTextColor = true,
|
||||
setTextColour = true,
|
||||
getBackgroundColor = true,
|
||||
getBackgroundColour = true,
|
||||
setBackgroundColor = true,
|
||||
setBackgroundColour = true,
|
||||
isColor = true,
|
||||
isColour = true,
|
||||
blit = true,
|
||||
setPaletteColor = true,
|
||||
setPaletteColour = true,
|
||||
getPaletteColor = true,
|
||||
getPaletteColour = true,
|
||||
|
||||
-- LeonOS-PC graphics mode settings
|
||||
setGraphicsMode = not not native.setGraphicsMode,
|
||||
getGraphicsMode = not not native.getGraphicsMode,
|
||||
drawPixels = not not native.drawPixels,
|
||||
getPixels = not not native.getPixels,
|
||||
setPixel = not not native.setPixel,
|
||||
getPixel = not not native.getPixel
|
||||
}
|
||||
|
||||
for k in pairs(valid) do
|
||||
term[k] = function(...)
|
||||
local redirect = thread.getTerm()
|
||||
if not redirect[k] then
|
||||
error("redirect object does not implement term."..k, 2)
|
||||
end
|
||||
|
||||
return redirect[k](...)
|
||||
end
|
||||
end
|
||||
|
||||
function term.current()
|
||||
return thread.getTerm()
|
||||
end
|
||||
|
||||
function term.native()
|
||||
return native
|
||||
end
|
||||
|
||||
function term.redirect(obj)
|
||||
expect(1, obj, "table")
|
||||
return thread.setTerm(obj)
|
||||
end
|
||||
|
||||
function term.at(x, y)
|
||||
term.setCursorPos(x, y)
|
||||
return term
|
||||
end
|
||||
|
||||
local keys = require("keys")
|
||||
|
||||
-- rc.write
|
||||
-- [[
|
||||
function rc.write(text)
|
||||
expect(1, text, "string")
|
||||
|
||||
local lines = 0
|
||||
local w, h = term.getSize()
|
||||
local x, y = term.getCursorPos()
|
||||
|
||||
local elements = strings.splitElements(text, w)
|
||||
|
||||
strings.wrappedWriteElements(elements, w, false, {
|
||||
newline = function()
|
||||
lines = lines + 1
|
||||
|
||||
x = 1
|
||||
if y >= h then
|
||||
term.scroll(1)
|
||||
else
|
||||
y = y + 1
|
||||
end
|
||||
|
||||
term.at(x, y)
|
||||
end,
|
||||
|
||||
append = function(newText)
|
||||
term.at(x, y).write(newText)
|
||||
x = x + #newText
|
||||
end,
|
||||
|
||||
getX = function()
|
||||
return x
|
||||
end
|
||||
})
|
||||
|
||||
return lines
|
||||
end
|
||||
|
||||
--]]
|
||||
|
||||
-- old write() used for redrawing in read()
|
||||
local function write(text)
|
||||
expect(1, text, "string")
|
||||
|
||||
local lines = 0
|
||||
local w, h = term.getSize()
|
||||
|
||||
local function inc_cy(cy)
|
||||
lines = lines + 1
|
||||
|
||||
if cy > h - 1 then
|
||||
term.scroll(1)
|
||||
return cy
|
||||
else
|
||||
return cy + 1
|
||||
end
|
||||
end
|
||||
|
||||
while #text > 0 do
|
||||
local nl = text:find("\n") or #text
|
||||
local chunk = text:sub(1, nl)
|
||||
text = text:sub(#chunk + 1)
|
||||
|
||||
local has_nl = chunk:sub(-1) == "\n"
|
||||
if has_nl then chunk = chunk:sub(1, -2) end
|
||||
|
||||
local cx, cy = term.getCursorPos()
|
||||
while #chunk > 0 do
|
||||
if cx > w then
|
||||
term.setCursorPos(1, inc_cy(cy))
|
||||
cx, cy = term.getCursorPos()
|
||||
end
|
||||
|
||||
local to_write = chunk:sub(1, w - cx + 1)
|
||||
term.write(to_write)
|
||||
|
||||
chunk = chunk:sub(#to_write + 1)
|
||||
cx, cy = term.getCursorPos()
|
||||
end
|
||||
|
||||
if has_nl then
|
||||
term.setCursorPos(1, inc_cy(cy))
|
||||
end
|
||||
end
|
||||
|
||||
return lines
|
||||
end--]]
|
||||
|
||||
rc.write = write
|
||||
|
||||
-- read
|
||||
local empty = {}
|
||||
function term.read(replace, history, complete, default)
|
||||
expect(1, replace, "string", "nil")
|
||||
expect(2, history, "table", "nil")
|
||||
expect(3, complete, "function", "nil")
|
||||
expect(4, default, "string", "nil")
|
||||
|
||||
if replace then replace = replace:sub(1, 1) end
|
||||
local hist = history or {}
|
||||
history = {}
|
||||
for i=1, #hist, 1 do
|
||||
history[i] = hist[i]
|
||||
end
|
||||
|
||||
local buffer = default or ""
|
||||
local prev_buf = buffer
|
||||
history[#history+1] = buffer
|
||||
|
||||
local hist_pos = #history
|
||||
local cursor_pos = 0
|
||||
|
||||
local stx, sty = term.getCursorPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
local dirty = false
|
||||
local completions = {}
|
||||
local comp_id = 0
|
||||
|
||||
local function clearCompletion()
|
||||
if completions[comp_id] then
|
||||
write((" "):rep(#completions[comp_id]))
|
||||
end
|
||||
end
|
||||
|
||||
local function full_redraw(force)
|
||||
if force or dirty then
|
||||
if complete and buffer ~= prev_buf then
|
||||
completions = complete(buffer) or empty
|
||||
comp_id = math.min(1, #completions)
|
||||
end
|
||||
prev_buf = buffer
|
||||
|
||||
term.setCursorPos(stx, sty)
|
||||
local text = buffer
|
||||
if replace then text = replace:rep(#text) end
|
||||
local ln = write(text)
|
||||
|
||||
if completions[comp_id] then
|
||||
local oldfg = term.getTextColor()
|
||||
local oldbg = term.getBackgroundColor()
|
||||
term.setTextColor(colors.white)
|
||||
term.setBackgroundColor(colors.gray)
|
||||
ln = ln + write(completions[comp_id])
|
||||
term.setTextColor(oldfg)
|
||||
term.setBackgroundColor(oldbg)
|
||||
else
|
||||
ln = ln + write(" ")
|
||||
end
|
||||
|
||||
if sty + ln > h then
|
||||
sty = sty - (sty + ln - h)
|
||||
end
|
||||
end
|
||||
|
||||
-- set cursor to the appropriate spot
|
||||
local cx, cy = stx, sty
|
||||
cx = cx + #buffer - cursor_pos-- + #(completions[comp_id] or "")
|
||||
while cx > w do
|
||||
cx = cx - w
|
||||
cy = cy + 1
|
||||
end
|
||||
term.setCursorPos(cx, cy)
|
||||
end
|
||||
|
||||
term.setCursorBlink(true)
|
||||
|
||||
while true do
|
||||
full_redraw()
|
||||
-- get input
|
||||
local evt, id = rc.pullEvent()
|
||||
|
||||
if evt == "char" then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer .. id
|
||||
elseif cursor_pos == #buffer then
|
||||
buffer = id .. buffer
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1)..id..buffer:sub(-cursor_pos)
|
||||
end
|
||||
|
||||
elseif evt == "paste" then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer .. id
|
||||
elseif cursor_pos == #buffer then
|
||||
buffer = id .. buffer
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1)..id..
|
||||
buffer:sub(-cursor_pos+(#id-1))
|
||||
end
|
||||
|
||||
elseif evt == "key" then
|
||||
id = keys.getName(id)
|
||||
|
||||
if id == "backspace" and #buffer > 0 then
|
||||
dirty = true
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer:sub(1, -2)
|
||||
clearCompletion()
|
||||
elseif cursor_pos < #buffer then
|
||||
buffer = buffer:sub(0, -cursor_pos - 2)..buffer:sub(-cursor_pos)
|
||||
end
|
||||
|
||||
elseif id == "delete" and cursor_pos > 0 then
|
||||
dirty = true
|
||||
|
||||
if cursor_pos == #buffer then
|
||||
buffer = buffer:sub(2)
|
||||
elseif cursor_pos == 1 then
|
||||
buffer = buffer:sub(1, -2)
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1)
|
||||
end
|
||||
cursor_pos = cursor_pos - 1
|
||||
|
||||
elseif id == "up" then
|
||||
if #completions > 1 then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if comp_id > 1 then
|
||||
comp_id = comp_id - 1
|
||||
else
|
||||
comp_id = #completions
|
||||
end
|
||||
|
||||
elseif hist_pos > 1 then
|
||||
cursor_pos = 0
|
||||
|
||||
history[hist_pos] = buffer
|
||||
hist_pos = hist_pos - 1
|
||||
|
||||
buffer = (" "):rep(#buffer)
|
||||
full_redraw(true)
|
||||
|
||||
buffer = history[hist_pos]
|
||||
dirty = true
|
||||
end
|
||||
|
||||
elseif id == "down" then
|
||||
if #completions > 1 then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if comp_id < #completions then
|
||||
comp_id = comp_id + 1
|
||||
else
|
||||
comp_id = 1
|
||||
end
|
||||
|
||||
elseif hist_pos < #history then
|
||||
cursor_pos = 0
|
||||
|
||||
history[hist_pos] = buffer
|
||||
hist_pos = hist_pos + 1
|
||||
|
||||
buffer = (" "):rep(#buffer)
|
||||
full_redraw(true)
|
||||
|
||||
buffer = history[hist_pos]
|
||||
dirty = true
|
||||
end
|
||||
|
||||
elseif id == "left" then
|
||||
if cursor_pos < #buffer then
|
||||
clearCompletion()
|
||||
cursor_pos = cursor_pos + 1
|
||||
end
|
||||
|
||||
elseif id == "right" then
|
||||
if cursor_pos > 0 then
|
||||
cursor_pos = cursor_pos - 1
|
||||
|
||||
elseif comp_id > 0 then
|
||||
dirty = true
|
||||
buffer = buffer .. completions[comp_id]
|
||||
end
|
||||
|
||||
elseif id == "tab" then
|
||||
if comp_id > 0 then
|
||||
dirty = true
|
||||
buffer = buffer .. completions[comp_id]
|
||||
end
|
||||
|
||||
elseif id == "home" then
|
||||
cursor_pos = #buffer
|
||||
|
||||
elseif id == "end" then
|
||||
cursor_pos = 0
|
||||
|
||||
elseif id == "enter" then
|
||||
clearCompletion()
|
||||
write("\n")
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursorBlink(false)
|
||||
|
||||
return buffer
|
||||
end
|
||||
|
||||
rc.read = term.read
|
||||
setmetatable(term, {__index = native})
|
||||
13
data/computercraft/lua/rom/startup/20_io.lua
Normal file
13
data/computercraft/lua/rom/startup/20_io.lua
Normal file
@@ -0,0 +1,13 @@
|
||||
_G.io = require("rc.io")
|
||||
|
||||
function _G.print(...)
|
||||
local args = table.pack(...)
|
||||
|
||||
for i=1, args.n, 1 do
|
||||
args[i] = tostring(args[i])
|
||||
end
|
||||
|
||||
io.stdout:write(table.concat(args, "\t"), "\n")
|
||||
|
||||
return true
|
||||
end
|
||||
184
data/computercraft/lua/rom/startup/30_peripheral.lua
Normal file
184
data/computercraft/lua/rom/startup/30_peripheral.lua
Normal file
@@ -0,0 +1,184 @@
|
||||
-- rc.peripheral
|
||||
|
||||
local old = require("peripheral")
|
||||
local expect = require("cc.expect").expect
|
||||
|
||||
local p = {}
|
||||
package.loaded.peripheral = p
|
||||
|
||||
local sides = {"bottom", "top", "left", "right", "front", "back"}
|
||||
|
||||
function p.getNames()
|
||||
local names = {}
|
||||
|
||||
for i=1, #sides, 1 do
|
||||
local side = sides[i]
|
||||
if old.isPresent(side) then
|
||||
names[#names+1] = side
|
||||
|
||||
if old.hasType(side, "modem") and not old.call(side, "isWireless") then
|
||||
local remote_names = old.call(side, "getNamesRemote")
|
||||
for j=1, #remote_names, 1 do
|
||||
names[#names+1] = remote_names[j]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return names
|
||||
end
|
||||
|
||||
-- figure out where a peripheral is
|
||||
-- returns 0 if the peripheral is directly connected,
|
||||
-- and a side if it's connected through a modem
|
||||
local function findByName(name)
|
||||
if old.isPresent(name) then
|
||||
return 0
|
||||
|
||||
else
|
||||
for i=1, #sides, 1 do
|
||||
local side = sides[i]
|
||||
|
||||
if old.hasType(side, "modem") and not old.call(side, "isWireless") then
|
||||
if old.call(side, "isPresentRemote", name) then
|
||||
return side
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function p.isPresent(name)
|
||||
expect(1, name, "string")
|
||||
|
||||
return not not findByName(name)
|
||||
end
|
||||
|
||||
function p.getType(per)
|
||||
expect(1, per, "string", "table")
|
||||
|
||||
if type(per) == "string" then
|
||||
local place = findByName(per)
|
||||
|
||||
if place == 0 then
|
||||
return old.getType(per)
|
||||
|
||||
elseif place then
|
||||
return old.call(place, "getTypeRemote", per)
|
||||
end
|
||||
|
||||
else
|
||||
return table.unpack(per.__types)
|
||||
end
|
||||
end
|
||||
|
||||
function p.hasType(per, ptype)
|
||||
expect(1, per, "string", "table")
|
||||
expect(2, ptype, "string")
|
||||
|
||||
if type(per) == "string" then
|
||||
local place = findByName(per)
|
||||
|
||||
if place == 0 then
|
||||
return old.hasType(per, ptype)
|
||||
|
||||
elseif place then
|
||||
return old.call(place, "hasTypeRemote", per, ptype)
|
||||
end
|
||||
|
||||
else
|
||||
return per.__types[ptype]
|
||||
end
|
||||
end
|
||||
|
||||
function p.getMethods(name)
|
||||
expect(1, name, "string")
|
||||
|
||||
local place = findByName(name)
|
||||
if place == 0 then
|
||||
return old.getMethods(name)
|
||||
|
||||
elseif place then
|
||||
return old.call(place, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
|
||||
function p.getName(per)
|
||||
expect(1, per, "table")
|
||||
return per.__info.name
|
||||
end
|
||||
|
||||
function p.call(name, method, ...)
|
||||
expect(1, name, "string")
|
||||
expect(2, method, "string")
|
||||
|
||||
local place = findByName(name)
|
||||
if place == 0 then
|
||||
return old.call(name, method, ...)
|
||||
|
||||
elseif place then
|
||||
return old.call(place, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
|
||||
function p.wrap(name)
|
||||
expect(1, name, "string")
|
||||
|
||||
local place = findByName(name)
|
||||
if not place then return end
|
||||
|
||||
local methods, types
|
||||
if place == 0 then
|
||||
methods = old.getMethods(name)
|
||||
types = table.pack(old.getType(name))
|
||||
else
|
||||
methods = old.call(place, "getMethodsRemote", name)
|
||||
types = table.pack(old.call(place, "getTypesRemote", name))
|
||||
end
|
||||
|
||||
for i=1, #types, 1 do
|
||||
types[types[i]] = true
|
||||
end
|
||||
|
||||
local wrapper = {
|
||||
__info = {
|
||||
name = name,
|
||||
types = types,
|
||||
}
|
||||
}
|
||||
|
||||
if place == 0 then
|
||||
for i=1, #methods, 1 do
|
||||
wrapper[methods[i]] = function(...)
|
||||
return old.call(name, methods[i], ...)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
for i=1, #methods, 1 do
|
||||
wrapper[methods[i]] = function(...)
|
||||
return old.call(place, "callRemote", name, methods[i], ...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return wrapper
|
||||
end
|
||||
|
||||
function p.find(ptype, filter)
|
||||
expect(1, ptype, "string")
|
||||
expect(2, filter, "function", "nil")
|
||||
|
||||
local wrapped = {}
|
||||
|
||||
for _, name in ipairs(p.getNames()) do
|
||||
if p.hasType(name, ptype) then
|
||||
local wrapper = p.wrap(name)
|
||||
if (p.filter and p.filter(name, wrapper)) or not p.filter then
|
||||
wrapped[#wrapped+1] = wrapper
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return table.unpack(wrapped)
|
||||
end
|
||||
168
data/computercraft/lua/rom/startup/35_http.lua
Normal file
168
data/computercraft/lua/rom/startup/35_http.lua
Normal file
@@ -0,0 +1,168 @@
|
||||
-- HTTP library adapted from .OS --
|
||||
|
||||
-- native http.request: function(
|
||||
-- url:string[, post:string[, headers:table[, binarymode:boolean]]])
|
||||
-- post is the data to POST. otherwise a GET is sent.
|
||||
-- OR: function(parameters:table)
|
||||
-- where parameters = {
|
||||
-- url = string, -- the URL
|
||||
-- body = string, -- the data to POST/PATCH/PUT
|
||||
-- headers = table, -- request headers
|
||||
-- binary = boolean, -- self explanatory
|
||||
-- method = string} -- the HTTP method to use - one of:
|
||||
-- - GET
|
||||
-- - POST
|
||||
-- - HEAD
|
||||
-- - OPTIONS
|
||||
-- - PUT
|
||||
-- - DELETE
|
||||
-- - PATCH
|
||||
-- - TRACE
|
||||
--
|
||||
-- native http.checkURL: function(url:string)
|
||||
-- url is a URL to try to reach. queues a http_check event with the result.
|
||||
-- native http.websocket(url:string[, headers:table])
|
||||
-- url is the url to which to open a websocket. queues a websocket_success
|
||||
-- event on success, and websocket_failure on failure.
|
||||
-- native http.addListener(port:number) (LeonOS-PC only)
|
||||
-- add a listener on the specified port. when that port receives data,
|
||||
-- the listener queues a http_request(port:number, request, response).
|
||||
-- !!the response is not send until response.close() is called!!
|
||||
-- native http.removeListener(port:number) (LeonOS-PC only)
|
||||
-- remove the listener from that port
|
||||
|
||||
local rc = ...
|
||||
|
||||
if not package.loaded.http then
|
||||
return
|
||||
end
|
||||
|
||||
local old = package.loaded.http
|
||||
|
||||
local http = {}
|
||||
|
||||
package.loaded.http = http
|
||||
|
||||
local field = require("cc.expect").field
|
||||
local expect = require("cc.expect").expect
|
||||
|
||||
local function listenForResponse(url)
|
||||
while true do
|
||||
local sig, a, b, c = rc.pullEvent()
|
||||
if sig == "http_success" and a == url then
|
||||
return b
|
||||
elseif sig == "http_failure" and a == url then
|
||||
return nil, b, c
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function http.request(url, post, headers, binary, method, sync)
|
||||
if type(url) ~= "table" then
|
||||
url = {
|
||||
url = url,
|
||||
body = post,
|
||||
headers = headers,
|
||||
binary = binary,
|
||||
method = method or (post and "POST") or "GET",
|
||||
sync = not not sync
|
||||
}
|
||||
end
|
||||
|
||||
field(url, "url", "string")
|
||||
field(url, "body", "string", "nil")
|
||||
field(url, "headers", "table", "nil")
|
||||
field(url, "binary", "boolean", "nil")
|
||||
field(url, "method", "string")
|
||||
field(url, "sync", "boolean", "nil")
|
||||
|
||||
local ok, err = old.request(url)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
if sync then return listenForResponse(url.url) end
|
||||
end
|
||||
|
||||
function http.get(url, headers, binary)
|
||||
if type(url) == "table" then
|
||||
url.sync = true
|
||||
return http.request(url)
|
||||
else
|
||||
return http.request(url, nil, headers, binary, "GET", true)
|
||||
end
|
||||
end
|
||||
|
||||
function http.post(url, body, headers, binary)
|
||||
if type(url) == "table" then
|
||||
url.sync = true
|
||||
url.method = "POST"
|
||||
return http.request(url)
|
||||
else
|
||||
return http.request(url, body, headers, binary, "POST", true)
|
||||
end
|
||||
end
|
||||
|
||||
http.checkURLAsync = old.checkURL
|
||||
|
||||
function http.checkURL(url)
|
||||
expect(1, url, "string")
|
||||
|
||||
local ok, err = old.checkURL(url)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local sig, _url, a, b
|
||||
repeat
|
||||
sig, _url, a, b = coroutine.yield()
|
||||
until sig == "http_check" and url == _url
|
||||
|
||||
return a, b
|
||||
end
|
||||
|
||||
http.websocketAsync = old.websocket
|
||||
|
||||
function http.websocket(url, headers)
|
||||
expect(1, url, "string")
|
||||
expect(2, headers, "string")
|
||||
|
||||
local ok, err = old.websocket(url, headers)
|
||||
if not ok then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
while true do
|
||||
local sig, a, b, c = coroutine.yield()
|
||||
if sig == "websocket_success" and a == url then
|
||||
return b, c
|
||||
elseif sig == "websocket_failure" and a == url then
|
||||
return nil, b
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if old.addListener then
|
||||
function http.listen(port, callback)
|
||||
expect(1, port, "number")
|
||||
expect(2, callback, "function")
|
||||
old.addListener(port)
|
||||
|
||||
while true do
|
||||
local sig, a, b, c = coroutine.yield()
|
||||
if sig == "stop_listener" and a == port then
|
||||
old.removeListener(port)
|
||||
break
|
||||
elseif sig == "http_request" and a == port then
|
||||
if not callback(b, c) then
|
||||
old.removeListener(port)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
function http.listen()
|
||||
error("This functionality requires LeonOS-PC", 0)
|
||||
end
|
||||
end
|
||||
36
data/computercraft/lua/rom/startup/40_commands.lua
Normal file
36
data/computercraft/lua/rom/startup/40_commands.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
-- rc.commands
|
||||
|
||||
local expect = require("cc.expect").expect
|
||||
|
||||
if not package.loaded.commands then return end
|
||||
|
||||
local native = package.loaded.commands
|
||||
local c = {}
|
||||
package.loaded.commands = c
|
||||
|
||||
c.native = native
|
||||
c.async = {}
|
||||
|
||||
for k, v in pairs(native) do c[k] = v end
|
||||
|
||||
local command_list = native.list()
|
||||
|
||||
for i=1, #command_list, 1 do
|
||||
local command = command_list[i]
|
||||
c.async[command] = function(...)
|
||||
return c.execAsync(command, ...)
|
||||
end
|
||||
c[command] = c[command] or function(...)
|
||||
return c.exec(command, ...)
|
||||
end
|
||||
end
|
||||
|
||||
function c.exec(command, ...)
|
||||
expect(1, command, "string")
|
||||
return c.native.exec(table.concat(table.pack(command, ...), " "))
|
||||
end
|
||||
|
||||
function c.execAsync(command, ...)
|
||||
expect(1, command, "string")
|
||||
return c.native.execAsync(table.concat(table.pack(command, ...), " "))
|
||||
end
|
||||
101
data/computercraft/lua/rom/startup/90_settings.lua
Normal file
101
data/computercraft/lua/rom/startup/90_settings.lua
Normal file
@@ -0,0 +1,101 @@
|
||||
-- setting definitions
|
||||
|
||||
local settings = require("settings")
|
||||
|
||||
settings.define("list.show_hidden", {
|
||||
description = "Show hidden files in list's output",
|
||||
type = "boolean",
|
||||
default = false
|
||||
})
|
||||
|
||||
settings.define("bios.compat_mode", {
|
||||
description = "Attempt some LeonOS compatibility by injecting APIs into _G.",
|
||||
type = "boolean",
|
||||
default = false
|
||||
})
|
||||
|
||||
settings.define("shell.tracebacks", {
|
||||
description = "Show error tracebacks in the shell.",
|
||||
type = "boolean",
|
||||
default = false
|
||||
})
|
||||
|
||||
settings.define("edit.scroll_offset", {
|
||||
description = "How many lines to keep between the cursor and the screen edge.",
|
||||
type = "number",
|
||||
default = 3
|
||||
})
|
||||
|
||||
settings.define("edit.force_highlight", {
|
||||
description = "Whether to use the highlighting editor, even on basic computers.",
|
||||
type = "boolean",
|
||||
default = false
|
||||
})
|
||||
|
||||
settings.define("edit.scroll_factor", {
|
||||
description = "Related to how many lines the editor should jump at a time when scrolling. Determined by term_height/scroll_factor. Adjust this for performance.",
|
||||
type = "number",
|
||||
default = 8
|
||||
})
|
||||
|
||||
settings.define("edit.color_separator", {
|
||||
description = "What color separating characters (e.g. ()[];{}) should be.",
|
||||
type = "string",
|
||||
default = "lightBlue"
|
||||
})
|
||||
|
||||
settings.define("edit.color_operator", {
|
||||
description = "What color operators (e.g. +-/*) should be.",
|
||||
type = "string",
|
||||
default = "lightGray"
|
||||
})
|
||||
|
||||
settings.define("edit.color_keyword", {
|
||||
description = "What color keywords (e.g. local, for, if) should be.",
|
||||
type = "string",
|
||||
default = "orange"
|
||||
})
|
||||
|
||||
settings.define("edit.color_boolean", {
|
||||
description = "What color booleans (true/false) should be.",
|
||||
type = "string",
|
||||
default = "purple"
|
||||
})
|
||||
|
||||
settings.define("edit.color_comment", {
|
||||
description = "What color comments should be.",
|
||||
type = "string",
|
||||
default = "gray"
|
||||
})
|
||||
|
||||
settings.define("edit.color_global", {
|
||||
description = "What color globals (e.g. print, require) should be.",
|
||||
type = "string",
|
||||
default = "lime"
|
||||
})
|
||||
|
||||
settings.define("edit.color_string", {
|
||||
description = "What color strings should be.",
|
||||
type = "string",
|
||||
default = "red"
|
||||
})
|
||||
|
||||
settings.define("edit.color_number", {
|
||||
description = "What color numbers (e.g. 2, 0xF3, 0.42) should be.",
|
||||
type = "string",
|
||||
default = "magenta"
|
||||
})
|
||||
|
||||
settings.define("bios.restrict_globals", {
|
||||
description = "Disallow global variables",
|
||||
type = "boolean",
|
||||
default = false
|
||||
})
|
||||
|
||||
settings.define("bios.parallel_startup", {
|
||||
description = "Run startup scripts from /startup in parallel",
|
||||
type = "boolean",
|
||||
default = false
|
||||
})
|
||||
|
||||
settings.load()
|
||||
Reference in New Issue
Block a user