Conflicts:
	src/main/resources/mcmod.info
This commit is contained in:
Florian Nücke 2014-02-23 01:39:17 +01:00
commit dfe856a954
15 changed files with 607 additions and 501 deletions

View File

@ -18,7 +18,7 @@ buildscript {
apply plugin: 'forge'
apply plugin: 'scala'
version = "2.0.0"
version = "2.0.1"
group= "li.cil.oc" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "OpenComputers"

View File

@ -270,6 +270,17 @@ sandbox._G = sandbox
-- Start of non-standard stuff.
local libcomponent
local callback = {
__call = function(method, ...)
return libcomponent.invoke(method.address, method.name, ...)
end,
__tostring = function(method)
return libcomponent.doc(method.address, method.name) or "function"
end,
__metatable = "callback"
}
libcomponent = {
doc = function(address, method)
checkArg(1, address, "string")
@ -283,7 +294,16 @@ libcomponent = {
invoke = function(address, method, ...)
checkArg(1, address, "string")
checkArg(2, method, "string")
return invoke(false, address, method, ...)
local methods, reason = component.methods(address)
if not methods then
return nil, reason
end
for name, direct in pairs(methods) do
if name == method then
return invoke(direct, address, method, ...)
end
end
error("no such method", 1)
end,
list = function(filter)
checkArg(1, filter, "string", "nil")
@ -307,15 +327,8 @@ libcomponent = {
if not methods then
return nil, reason
end
for method, direct in pairs(methods) do
proxy[method] = setmetatable({}, {
__call = function(_, ...)
return invoke(direct, address, method, ...)
end,
__tostring = function()
return libcomponent.doc(address, method) or "function"
end
})
for method in pairs(methods) do
proxy[method] = setmetatable({address=address,name=method}, callback)
end
return proxy
end,

View File

@ -0,0 +1,351 @@
-- BEtter SHell, a wrapper for the normal shell that adds many POSIX
-- features, such as pipes, redirects and variable expansion.
local component = require("component")
local computer = require("computer")
local event = require("event")
local fs = require("filesystem")
local process = require("process")
local shell = require("shell")
local term = require("term")
local text = require("text")
local unicode = require("unicode")
local function expandVars(token)
local name = nil
local special = false
local ignore = false
local ignoreChar =''
local escaped = false
local lastEnd = 1
local doubleQuote = false
local singleQuote = false
local endToken = {}
for i = 1, unicode.len(token) do
local char = unicode.sub(token, i, i)
if escaped then
if name then
table.insert(name, char)
end
escaped = false
elseif char == '\\' then
escaped = not escaped
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
lastEnd = i+1
elseif char == '"' and not singleQuote then
doubleQuote = not doubleQuote
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
lastEnd = i+1
elseif char == "'" and not doubleQuote then
singleQuote = not singleQuote
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
lastEnd = i+1
elseif char == "$" and not doubleQuote and not singleQuote then
if name then
ignore = true
else
name = {}
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
end
elseif char == '{' and #name == 0 then
if ignore and ignoreChar == '' then
ignoreChar = '}'
else
special = true
end
elseif char == '(' and ignoreChar == '' then
ignoreChar = ')'
elseif char == '`' and special then
ignore = true
ignoreChar = '`'
elseif char == '}' and not ignore and not doubleQuote and not singleQuote then
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
lastEnd = i+1
elseif char == '"' and not singleQuote then
doubleQuote = not doubleQuote
elseif char == "'" and not doubleQuote then
singleQuote = not singleQuote
elseif name and (char:match("[%a%d_]") or special) then
if char:match("%d") and #name == 0 then
error "Identifiers can't start with a digit!"
end
table.insert(name, char)
elseif char == ignoreChar and ignore then
ignore = false
ignoreChar = ''
elseif name then -- We are done with gathering the name
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
lastEnd = i
end
end
if name then
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
else
table.insert(endToken, unicode.sub(token, lastEnd, -1))
end
return table.concat(endToken)
end
local function parseCommand(tokens)
if #tokens == 0 then
return
end
-- Variable expansion for all command parts.
for i = 1, #tokens do
tokens[i] = expandVars(tokens[i])
end
-- Resolve alias for command.
local program, args = shell.resolveAlias(tokens[1], table.pack(select(2, table.unpack(tokens))))
-- Find redirects.
local input, output, mode = nil, nil, "write"
tokens = args
args = {}
local function smt(call) -- state metatable factory
local function index(_, token)
if token == "<" or token == ">" or token == ">>" then
return "parse error near " .. token
end
call(token)
return "args" -- default, return to normal arg parsing
end
return {__index=index}
end
local sm = { -- state machine for redirect parsing
args = setmetatable({["<"]="input", [">"]="output", [">>"]="append"},
smt(function(token)
table.insert(args, token)
end)),
input = setmetatable({}, smt(function(token)
input = token
end)),
output = setmetatable({}, smt(function(token)
output = token
mode = "write"
end)),
append = setmetatable({}, smt(function(token)
output = token
mode = "append"
end))
}
-- Run state machine over tokens.
local state = "args"
for i = 1, #tokens do
local token = tokens[i]
state = sm[state][token]
if not sm[state] then
return nil, state
end
end
return program, args, input, output, mode
end
local function parseCommands(command)
local tokens, reason = text.tokenize(command)
if not tokens then
return nil, reason
end
local commands, command = {}, {}
for i = 1, #tokens do
if tokens[i] == "|" then
if #command == 0 then
return nil, "parse error near '|'"
end
table.insert(commands, command)
command = {}
else
table.insert(command, tokens[i])
end
end
if #command > 0 then
table.insert(commands, command)
end
for i = 1, #commands do
commands[i] = table.pack(parseCommand(commands[i]))
if commands[i][1] == nil then
return nil, commands[i][2]
end
end
return commands
end
-------------------------------------------------------------------------------
local memoryStream = {}
function memoryStream:close()
self.closed = true
end
function memoryStream:seek()
return nil, "bad file descriptor"
end
function memoryStream:read(n)
if self.closed then
if self.buffer == "" and self.redirect.read then
return self.redirect.read:read(n)
end
return nil -- eof
end
if self.buffer == "" then
self.args = table.pack(coroutine.yield(table.unpack(self.result)))
end
local result = string.sub(self.buffer, 1, n)
self.buffer = string.sub(self.buffer, n + 1)
return result
end
function memoryStream:write(value)
local ok
if self.redirect.write then
ok = self.redirect.write:write(value)
end
if not self.closed then
self.buffer = self.buffer .. value
self.result = table.pack(coroutine.resume(self.next, table.unpack(self.args)))
ok = true
end
if ok then
return true
end
return nil, "stream is closed"
end
function memoryStream.new()
local stream = {closed = false, buffer = "",
redirect = {}, result = {}, args = {}}
local metatable = {__index = memoryStream,
__gc = memoryStream.close,
__metatable = "memorystream"}
return setmetatable(stream, metatable)
end
-------------------------------------------------------------------------------
local function execute(command, env, ...)
checkArg(1, command, "string")
local commands, reason = parseCommands(command)
if not commands then
return false, reason
end
if #commands == 0 then
return true
end
-- Piping data between programs works like so:
-- program1 gets its output replaced with our custom stream.
-- program2 gets its input replaced with our custom stream.
-- repeat for all programs
-- custom stream triggers execution of 'next' program after write.
-- custom stream triggers yield before read if buffer is empty.
-- custom stream may have 'redirect' entries for fallback/duplication.
local threads, pipes, inputs, outputs = {}, {}, {}, {}
for i = 1, #commands do
local program, args, input, output, mode = table.unpack(commands[i])
local reason
threads[i], reason = shell.load(program, env, function()
if input then
local file, reason = io.open(shell.resolve(input))
if not file then
error(reason)
end
table.insert(inputs, file)
if pipes[i - 1] then
pipes[i - 1].stream.redirect.read = file
io.input(pipes[i - 1])
else
io.input(file)
end
elseif pipes[i - 1] then
io.input(pipes[i - 1])
end
if output then
local file, reason = io.open(shell.resolve(output), mode == "append" and "a" or "w")
if not file then
error(reason)
end
if mode == "append" then
io.write("\n")
end
table.insert(outputs, file)
if pipes[i] then
pipes[i].stream.redirect.write = file
io.output(pipes[i])
else
io.output(file)
end
elseif pipes[i] then
io.output(pipes[i])
end
end, command)
if not threads[i] then
return false, reason
end
if i < #commands then
pipes[i] = require("buffer").new("rw", memoryStream.new())
pipes[i]:setvbuf("no")
end
if i > 1 then
pipes[i - 1].stream.next = threads[i]
pipes[i - 1].stream.args = args
end
end
local args = select(2, table.unpack(commands[1]))
table.insert(args, 1, true)
for _, arg in ipairs(table.pack(...)) do
table.insert(args, arg)
end
args.n = #args
local result = nil
for i = 1, #threads do
-- Emulate CC behavior by making yields a filtered event.pull()
while args[1] and coroutine.status(threads[i]) ~= "dead" do
result = table.pack(coroutine.resume(threads[i], table.unpack(args, 2, args.n)))
if coroutine.status(threads[i]) ~= "dead" then
if type(result[2]) == "string" then
args = table.pack(pcall(event.pull, table.unpack(result, 2, result.n)))
else
args = {true, n=1}
end
end
end
if pipes[i] then
pipes[i]:close()
end
if i < #threads and not result[1] then
io.write(result[2])
end
end
for _, input in ipairs(inputs) do
input:close()
end
for _, output in ipairs(outputs) do
output:close()
end
if not args[1] then
return false, args[2]
end
if not result[1] and type(result[2]) == "table" and result[2].reason == "terminated" then
if result[2].code then
return true
else
return false, "terminated"
end
end
return table.unpack(result, 1, result.n)
end
local env = setmetatable({os=setmetatable({execute=execute}, {__index=os})}, {__index=_ENV})
shell.execute("/bin/sh", env, ...)

View File

@ -7,6 +7,7 @@ if #dirs == 0 then
table.insert(dirs, ".")
end
io.output():setvbuf("line")
for i = 1, #dirs do
local path = shell.resolve(dirs[i])
if #dirs > 1 then
@ -21,6 +22,7 @@ for i = 1, #dirs do
else
local function setColor(c)
if component.gpu.getForeground() ~= c then
io.stdout:flush()
component.gpu.setForeground(c)
end
end
@ -67,4 +69,5 @@ for i = 1, #dirs do
io.write("\n")
end
end
end
end
io.output():setvbuf("no")

View File

@ -41,7 +41,7 @@ while term.isAvailable() do
code, reason = load(command, "=stdin", "t", env)
end
if code then
local result = table.pack(pcall(code))
local result = table.pack(xpcall(code, debug.traceback))
if not result[1] then
if type(result[2]) == "table" and result[2].reason == "terminated" then
os.exit(result[2].code)

View File

@ -10,9 +10,9 @@ end
local topic = args[1]
for path in string.gmatch(os.getenv("MANPATH"), "[^:]+") do
path = fs.concat(path, topic)
path = shell.resolve(fs.concat(path, topic), "man")
if fs.exists(path) and not fs.isDirectory(path) then
os.execute("more " .. path)
os.execute(os.getenv("PAGER") .. " " .. path)
os.exit()
end
end

View File

@ -2,11 +2,12 @@ local component = require("component")
local keyboard = require("keyboard")
local shell = require("shell")
local term = require("term")
local text = require("text")
local unicode = require("unicode")
local args = shell.parse(...)
if #args == 0 then
io.write("Usage: less <filename1>")
io.write("Usage: more <filename1>")
return
end
@ -29,13 +30,9 @@ while true do
return
end
end
if unicode.len(line) > w then
io.write(unicode.sub(line, 1, w), "\n")
line = unicode.sub(line, w + 1)
else
io.write(line, "\n")
line = nil
end
local wrapped
wrapped, line = text.wrap(text.detab(line), w, w)
io.write(wrapped .. "\n")
i = i + 1
end
term.setCursor(1, h)
@ -53,4 +50,4 @@ while true do
end
end
end
end
end

View File

@ -5,14 +5,19 @@ if #args < 1 then
io.write(k, "='", string.gsub(v, "'", [['"'"']]), "'\n")
end
else
local count = 1
local count = 0
for _, expr in ipairs(args) do
local k, v = string.match(expr, "(.-)=(.*)")
if v then
os.setenv(k, v)
else
os.setenv(tostring(count), k)
if count == 0 then
for i = 1, os.getenv('#') do
os.setenv(i, nil)
end
end
count = count + 1
os.setenv(count, expr)
end
end
end

View File

@ -25,7 +25,7 @@ while true do
end
while term.isAvailable() do
local foreground = component.gpu.setForeground(0xFF0000)
term.write("# ")
term.write(os.getenv("PS1") or "# ")
component.gpu.setForeground(foreground)
local command = term.read(history)
if not command then

View File

@ -7,7 +7,9 @@ local unicode = require("unicode")
local env = {
HOME="/home",
MANPATH="/usr/man",
PAGER="/bin/more",
PATH="/bin:/usr/bin:/home/bin:.",
PS1="# ",
PWD="/",
SHELL="/bin/sh",
TMP="/tmp"
@ -25,7 +27,9 @@ function os.exit(code)
end
function os.getenv(varname)
if varname ~= nil then
if varname == '#' then
return #env
elseif varname ~= nil then
return env[varname]
else
return env
@ -33,9 +37,18 @@ function os.getenv(varname)
end
function os.setenv(varname, value)
checkArg(1, varname, "string")
env[varname] = value
return env[varname]
checkArg(1, varname, "string", "number")
if value == nil then
env[varname] = nil
else
local success, val = pcall(tostring, value)
if success then
env[varname] = val
return env[varname]
else
return nil, val
end
end
end
function os.remove(...)

View File

@ -0,0 +1,119 @@
local serialization = {}
-- Important: pretty formatting will allow presenting non-serializable values
-- but may generate output that cannot be unserialized back.
function serialization.serialize(value, pretty)
local kw = {["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true,
["elseif"]=true, ["end"]=true, ["false"]=true, ["for"]=true,
["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true,
["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true,
["repeat"]=true, ["return"]=true, ["then"]=true, ["true"]=true,
["until"]=true, ["while"]=true}
local id = "^[%a_][%w_]*$"
local ts = {}
local function s(v, l)
local t = type(v)
if t == "nil" then
return "nil"
elseif t == "boolean" then
return v and "true" or "false"
elseif t == "number" then
if v ~= v then
return "0/0"
elseif v == math.huge then
return "math.huge"
elseif v == -math.huge then
return "-math.huge"
else
return tostring(v)
end
elseif t == "string" then
return string.format("%q", v)
elseif t == "table" and pretty and getmetatable(v) and getmetatable(v).__tostring then
return tostring(v)
elseif t == "table" then
if ts[v] then
if pretty then
return "recursion"
else
error("tables with cycles are not supported")
end
end
ts[v] = true
local i, r = 1, nil
local f
if pretty then
local ks = {}
for k in pairs(v) do table.insert(ks, k) end
table.sort(ks)
local n = 0
f = table.pack(function()
n = n + 1
local k = ks[n]
if k ~= nil then
return k, v[k]
else
return nil
end
end)
else
f = table.pack(pairs(v))
end
for k, v in table.unpack(f) do
if r then
r = r .. "," .. (pretty and ("\n" .. string.rep(" ", l)) or "")
else
r = "{"
end
local tk = type(k)
if tk == "number" and k == i then
i = i + 1
r = r .. s(v, l + 1)
else
if tk == "string" and not kw[k] and string.match(k, id) then
r = r .. k
else
r = r .. "[" .. s(k, l + 1) .. "]"
end
r = r .. "=" .. s(v, l + 1)
end
end
ts[v] = nil -- allow writing same table more than once
return (r or "{") .. "}"
else
if pretty then
return tostring(t)
else
error("unsupported type: " .. t)
end
end
end
local result = s(value, 1)
local limit = type(pretty) == "number" and pretty or 10
if pretty then
local truncate = 0
while limit > 0 and truncate do
truncate = string.find(result, "\n", truncate + 1, true)
limit = limit - 1
end
if truncate then
return result:sub(1, truncate) .. "..."
end
end
return result
end
function serialization.unserialize(data)
checkArg(1, data, "string")
local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}})
if not result then
return nil, reason
end
local ok, output = pcall(result)
if not ok then
return nil, output
end
return output
end
return serialization

View File

@ -53,244 +53,6 @@ local function findFile(name, ext)
return false
end
function expandVars(token)
local name = nil
local special = false
local ignore = false
local ignoreChar =''
local escaped = false
local lastEnd = 1
local doubleQuote = false
local singleQuote = false
local endToken = {}
for i = 1, unicode.len(token) do
local char = unicode.sub(token, i, i)
if escaped then
if name then
table.insert(name, char)
end
escaped = false
elseif char == '\\' then
escaped = not escaped
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
lastEnd = i+1
elseif char == '"' and not singleQuote then
doubleQuote = not doubleQuote
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
lastEnd = i+1
elseif char == "'" and not doubleQuote then
singleQuote = not singleQuote
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
lastEnd = i+1
elseif char == "$" and not doubleQuote and not singleQuote then
if name then
ignore = true
else
name = {}
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
end
elseif char == '{' and #name == 0 then
if ignore and ignoreChar == '' then
ignoreChar = '}'
else
special = true
end
elseif char == '(' and ignoreChar == '' then
ignoreChar = ')'
elseif char == '`' and special then
ignore = true
ignoreChar = '`'
elseif char == '}' and not ignore and not doubleQuote and not singleQuote then
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
lastEnd = i+1
elseif char == '"' and not singleQuote then
doubleQuote = not doubleQuote
elseif char == "'" and not doubleQuote then
singleQuote = not singleQuote
elseif name and (char:match("[%a%d_]") or special) then
if char:match("%d") and #name == 0 then
error "Identifiers can't start with a digit!"
end
table.insert(name, char)
elseif char == ignoreChar and ignore then
ignore = false
ignoreChar = ''
elseif name then -- We are done with gathering the name
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
lastEnd = i
end
end
if name then
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
else
table.insert(endToken, unicode.sub(token, lastEnd, -1))
end
return table.concat(endToken)
end
local function resolveAlias(tokens)
local program, lastProgram = tokens[1], nil
table.remove(tokens, 1)
while true do
local alias = text.tokenize(shell.getAlias(program) or program)
program = alias[1]
if program == lastProgram then
break
end
lastProgram = program
table.remove(alias, 1)
for i = 1, #tokens do
table.insert(alias, tokens[i])
end
tokens = alias
end
return program, tokens
end
local function parseCommand(tokens)
if #tokens == 0 then
return
end
-- Variable expansion for all command parts.
for i = 1, #tokens do
tokens[i] = expandVars(tokens[i])
end
-- Resolve alias for command.
local program, args = resolveAlias(tokens)
-- Find redirects.
local input, output, mode = nil, nil, "write"
tokens = args
args = {}
local function smt(call) -- state metatable factory
local function index(_, token)
if token == "<" or token == ">" or token == ">>" then
return "parse error near " .. token
end
call(token)
return "args" -- default, return to normal arg parsing
end
return {__index=index}
end
local sm = { -- state machine for redirect parsing
args = setmetatable({["<"]="input", [">"]="output", [">>"]="append"},
smt(function(token)
table.insert(args, token)
end)),
input = setmetatable({}, smt(function(token)
input = token
end)),
output = setmetatable({}, smt(function(token)
output = token
mode = "write"
end)),
append = setmetatable({}, smt(function(token)
output = token
mode = "append"
end))
}
-- Run state machine over tokens.
local state = "args"
for i = 1, #tokens do
local token = tokens[i]
state = sm[state][token]
if not sm[state] then
return nil, state
end
end
return program, args, input, output, mode
end
local function parseCommands(command)
local tokens, reason = text.tokenize(command)
if not tokens then
return nil, reason
end
local commands, command = {}, {}
for i = 1, #tokens do
if tokens[i] == "|" then
if #command == 0 then
return nil, "parse error near '|'"
end
table.insert(commands, command)
command = {}
else
table.insert(command, tokens[i])
end
end
if #command > 0 then
table.insert(commands, command)
end
for i = 1, #commands do
commands[i] = table.pack(parseCommand(commands[i]))
if commands[i][1] == nil then
return nil, commands[i][2]
end
end
return commands
end
-------------------------------------------------------------------------------
local memoryStream = {}
function memoryStream:close()
self.closed = true
end
function memoryStream:seek()
return nil, "bad file descriptor"
end
function memoryStream:read(n)
if self.closed then
if self.buffer == "" and self.redirect.read then
return self.redirect.read:read(n)
end
return nil -- eof
end
if self.buffer == "" then
self.args = table.pack(coroutine.yield(table.unpack(self.result)))
end
local result = string.sub(self.buffer, 1, n)
self.buffer = string.sub(self.buffer, n + 1)
return result
end
function memoryStream:write(value)
local ok
if self.redirect.write then
ok = self.redirect.write:write(value)
end
if not self.closed then
self.buffer = self.buffer .. value
self.result = table.pack(coroutine.resume(self.next, table.unpack(self.args)))
ok = true
end
if ok then
return true
end
return nil, "stream is closed"
end
function memoryStream.new()
local stream = {closed = false, buffer = "",
redirect = {}, result = {}, args = {}}
local metatable = {__index = memoryStream,
__gc = memoryStream.close,
__metatable = "memorystream"}
return setmetatable(stream, metatable)
end
-------------------------------------------------------------------------------
function shell.getAlias(alias)
@ -307,6 +69,24 @@ function shell.aliases()
return pairs(aliases)
end
function shell.resolveAlias(command, args)
checkArg(1, command, "string")
checkArg(2, args, "table", "nil")
local program, lastProgram = command, nil
while true do
local tokens = text.tokenize(shell.getAlias(program) or program)
program = tokens[1]
if program == lastProgram then
break
end
lastProgram = program
for i = #tokens, 2, -1 do
table.insert(args, 1, tokens[i])
end
end
return program, args
end
function shell.getWorkingDirectory()
return os.getenv("PWD")
end
@ -351,105 +131,34 @@ end
function shell.execute(command, env, ...)
checkArg(1, command, "string")
local commands, reason = parseCommands(command)
if not commands then
local parts, reason = text.tokenize(command)
if not parts then
return false, reason
end
if #commands == 0 then
if #parts == 0 then
return true
end
-- Piping data between programs works like so:
-- program1 gets its output replaced with our custom stream.
-- program2 gets its input replaced with our custom stream.
-- repeat for all programs
-- custom stream triggers execution of 'next' program after write.
-- custom stream triggers yield before read if buffer is empty.
-- custom stream may have 'redirect' entries for fallback/duplication.
local threads, pipes, inputs, outputs = {}, {}, {}, {}
for i = 1, #commands do
local program, args, input, output, mode = table.unpack(commands[i])
local reason
threads[i], reason = shell.load(program, env, function()
if input then
local file, reason = io.open(shell.resolve(input))
if not file then
error(reason)
end
table.insert(inputs, file)
if pipes[i - 1] then
pipes[i - 1].stream.redirect.read = file
io.input(pipes[i - 1])
else
io.input(file)
end
elseif pipes[i - 1] then
io.input(pipes[i - 1])
end
if output then
local file, reason = io.open(shell.resolve(output), mode == "append" and "a" or "w")
if not file then
error(reason)
end
if mode == "append" then
io.write("\n")
end
table.insert(outputs, file)
if pipes[i] then
pipes[i].stream.redirect.write = file
io.output(pipes[i])
else
io.output(file)
end
elseif pipes[i] then
io.output(pipes[i])
end
end, command)
if not threads[i] then
return false, reason
end
if i < #commands then
pipes[i] = require("buffer").new("rw", memoryStream.new())
pipes[i]:setvbuf("no")
end
if i > 1 then
pipes[i - 1].stream.next = threads[i]
pipes[i - 1].stream.args = args
end
end
local args = select(2, table.unpack(commands[1]))
local program, args = shell.resolveAlias(parts[1], table.pack(select(2, table.unpack(parts))))
table.insert(args, 1, true)
for _, arg in ipairs(table.pack(...)) do
table.insert(args, arg)
end
args.n = #args
local thread, reason = shell.load(program, env, nil, command)
if not thread then
return false, reason
end
local result = nil
for i = 1, #threads do
-- Emulate CC behavior by making yields a filtered event.pull()
while args[1] and coroutine.status(threads[i]) ~= "dead" do
result = table.pack(coroutine.resume(threads[i], table.unpack(args, 2, args.n)))
if coroutine.status(threads[i]) ~= "dead" then
if type(result[2]) == "string" then
args = table.pack(pcall(event.pull, table.unpack(result, 2, result.n)))
else
args = {true, n=1}
end
-- Emulate CC behavior by making yields a filtered event.pull()
while args[1] and coroutine.status(thread) ~= "dead" do
result = table.pack(coroutine.resume(thread, table.unpack(args, 2, args.n)))
if coroutine.status(thread) ~= "dead" then
if type(result[2]) == "string" then
args = table.pack(pcall(event.pull, table.unpack(result, 2, result.n)))
else
args = {true, n=1}
end
end
if pipes[i] then
pipes[i]:close()
end
if i < #threads and not result[1] then
io.write(result[2])
end
end
for _, input in ipairs(inputs) do
input:close()
end
for _, output in ipairs(outputs) do
output:close()
end
if not args[1] then
return false, args[2]

View File

@ -102,21 +102,22 @@ function term.read(history)
scrollY = nby - 1
nbx = math.max(1, math.min(unicode.len(history[nby]) + 1, nbx))
local ncx = nbx + offset - scrollX
if ncx > w then
local sx = nbx - (w - offset)
local dx = math.abs(scrollX - sx)
scrollX = sx
component.gpu.copy(1 + offset + dx, cy, w - offset - dx, 1, -dx, 0)
local str = unicode.sub(line(), nbx - (dx - 1), nbx)
local str = unicode.sub(history[nby], nbx - (dx - 1), nbx)
str = text.padRight(str, dx)
component.gpu.set(1 + (w - dx), cy, str)
component.gpu.set(1 + math.max(offset, w - dx), cy, unicode.sub(str, 1 + math.max(0, dx - (w - offset))))
elseif ncx < 1 + offset then
local sx = nbx - 1
local dx = math.abs(scrollX - sx)
scrollX = sx
component.gpu.copy(1 + offset, cy, w - offset - dx, 1, dx, 0)
local str = unicode.sub(line(), nbx, nbx + dx)
local str = unicode.sub(history[nby], nbx, nbx + dx)
--str = text.padRight(str, dx)
component.gpu.set(1 + offset, cy, str)
end
@ -172,7 +173,7 @@ function term.read(history)
local function up()
local cbx, cby = getCursor()
if cby > 1 then
setCursor(cbx, cby - 1)
setCursor(1, cby - 1)
redraw()
ende()
end
@ -181,7 +182,7 @@ function term.read(history)
local function down()
local cbx, cby = getCursor()
if cby < #history then
setCursor(cbx, cby + 1)
setCursor(1, cby + 1)
redraw()
ende()
end
@ -337,8 +338,14 @@ function term.write(value, wrap)
end
local blink = term.getCursorBlink()
term.setCursorBlink(false)
local function checkCursor()
if cursorX > w then
local line, nl = value
repeat
if wrap then
line, value, nl = text.wrap(value, w - (cursorX - 1), w)
end
component.gpu.set(cursorX, cursorY, line)
cursorX = cursorX + unicode.len(line)
if nl or cursorX > w then
cursorX = 1
cursorY = cursorY + 1
end
@ -347,28 +354,7 @@ function term.write(value, wrap)
component.gpu.fill(1, h, w, 1, " ")
cursorY = h
end
end
for line, nl in value:gmatch("([^\r\n]*)([\r\n]?)") do
while wrap and unicode.len(line) > w - (cursorX - 1) do
local partial = unicode.sub(line, 1, w - (cursorX - 1))
local wordWrapped = partial:match("(.*[^a-zA-Z0-9._])")
if wordWrapped or unicode.len(line) > w then
partial = wordWrapped or partial
line = unicode.sub(line, unicode.len(partial) + 1)
component.gpu.set(cursorX, cursorY, partial)
end
cursorX = math.huge
checkCursor()
end
if unicode.len(line) > 0 then
component.gpu.set(cursorX, cursorY, line)
cursorX = cursorX + unicode.len(line)
end
if unicode.len(nl) == 1 then
cursorX = math.huge
checkCursor()
end
end
until not wrap or not value
term.setCursorBlink(blink)
end

View File

@ -5,7 +5,7 @@ local text = {}
function text.detab(value, tabWidth)
checkArg(1, value, "string")
checkArg(2, tabWidth, "number", "nil")
tabWidth = tabWidth or 4
tabWidth = tabWidth or 8
local function rep(match)
local spaces = tabWidth - match:len() % tabWidth
return match .. string.rep(" ", spaces)
@ -40,6 +40,26 @@ function text.trim(value) -- from http://lua-users.org/wiki/StringTrim
return from > #value and "" or string.match(value, ".*%S", from)
end
function text.wrap(value, width, maxWidth)
checkArg(1, value, "string")
checkArg(2, width, "number")
local line, nl = value:match("([^\r\n]*)([\r\n]?)") -- read until newline
if unicode.len(line) > width then -- do we even need to wrap?
local partial = unicode.sub(line, 1, width)
local wrapped = partial:match("(.*[^a-zA-Z0-9._])")
if wrapped or unicode.len(line) > maxWidth then
partial = wrapped or partial
return partial, unicode.sub(value, unicode.len(partial) + 1), true
else
return "", value, true -- write in new line.
end
end
local start = unicode.len(line) + unicode.len(nl) + 1
return line, start <= unicode.len(value) and unicode.sub(value, start) or nil, unicode.len(nl) > 0
end
-------------------------------------------------------------------------------
function text.tokenize(value)
checkArg(1, value, "string")
local tokens, token = {}, ""
@ -77,122 +97,12 @@ function text.tokenize(value)
return tokens
end
-------------------------------------------------------------------------------
-- Important: pretty formatting will allow presenting non-serializable values
-- but may generate output that cannot be unserialized back.
function text.serialize(value, pretty)
local kw = {["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true,
["elseif"]=true, ["end"]=true, ["false"]=true, ["for"]=true,
["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true,
["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true,
["repeat"]=true, ["return"]=true, ["then"]=true, ["true"]=true,
["until"]=true, ["while"]=true}
local id = "^[%a_][%w_]*$"
local ts = {}
local function s(v, l)
local t = type(v)
if t == "nil" then
return "nil"
elseif t == "boolean" then
return v and "true" or "false"
elseif t == "number" then
if v ~= v then
return "0/0"
elseif v == math.huge then
return "math.huge"
elseif v == -math.huge then
return "-math.huge"
else
return tostring(v)
end
elseif t == "string" then
return string.format("%q", v)
elseif t == "table" and pretty and getmetatable(v) and getmetatable(v).__tostring then
return tostring(v)
elseif t == "table" then
if ts[v] then
if pretty then
return "recursion"
else
error("tables with cycles are not supported")
end
end
ts[v] = true
local i, r = 1, nil
local f
if pretty then
local ks = {}
for k in pairs(v) do table.insert(ks, k) end
table.sort(ks)
local n = 0
f = table.pack(function()
n = n + 1
local k = ks[n]
if k ~= nil then
return k, v[k]
else
return nil
end
end)
else
f = table.pack(pairs(v))
end
for k, v in table.unpack(f) do
if r then
r = r .. "," .. (pretty and ("\n" .. string.rep(" ", l)) or "")
else
r = "{"
end
local tk = type(k)
if tk == "number" and k == i then
i = i + 1
r = r .. s(v, l + 1)
else
if tk == "string" and not kw[k] and string.match(k, id) then
r = r .. k
else
r = r .. "[" .. s(k, l + 1) .. "]"
end
r = r .. "=" .. s(v, l + 1)
end
end
ts[v] = nil -- allow writing same table more than once
return (r or "{") .. "}"
else
if pretty then
return tostring(t)
else
error("unsupported type: " .. t)
end
end
end
local result = s(value, 1)
local limit = type(pretty) == "number" and pretty or 10
if pretty then
local truncate = 0
while limit > 0 and truncate do
truncate = string.find(result, "\n", truncate + 1, true)
limit = limit - 1
end
if truncate then
return result:sub(1, truncate) .. "..."
end
end
return result
function text.serialize(value, pretty) -- deprecated, use serialization module
return require("serialization").serialize(value, pretty)
end
function text.unserialize(data)
checkArg(1, data, "string")
local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}})
if not result then
return nil, reason
end
local ok, output = pcall(result)
if not ok then
return nil, output
end
return output
function text.unserialize(data) -- deprecated, use serialization module
return require("serialization").unserialize(data)
end
-------------------------------------------------------------------------------

View File

@ -491,7 +491,7 @@ class Robot(isRemote: Boolean) extends Computer(isRemote) with ISidedInventory w
// ----------------------------------------------------------------------- //
override def installedMemory = 128 * 1024
override def installedMemory = 96 * 1024
override def tier = 0