mirror of
https://github.com/kepler155c/opus
synced 2025-01-16 10:25:42 +00:00
spaces->tabs + cleanup + pathing fixes
This commit is contained in:
parent
82ec4db50f
commit
3c22a872b0
@ -5,29 +5,29 @@ local parallel = _G.parallel
|
|||||||
local BulkGet = { }
|
local BulkGet = { }
|
||||||
|
|
||||||
function BulkGet.download(list, callback)
|
function BulkGet.download(list, callback)
|
||||||
local t = { }
|
local t = { }
|
||||||
local failed = false
|
local failed = false
|
||||||
|
|
||||||
for _ = 1, 5 do
|
for _ = 1, 5 do
|
||||||
table.insert(t, function()
|
table.insert(t, function()
|
||||||
while true do
|
while true do
|
||||||
local entry = table.remove(list)
|
local entry = table.remove(list)
|
||||||
if not entry then
|
if not entry then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
local s, m = Util.download(entry.url, entry.path)
|
local s, m = Util.download(entry.url, entry.path)
|
||||||
if not s then
|
if not s then
|
||||||
failed = true
|
failed = true
|
||||||
end
|
end
|
||||||
callback(entry, s, m)
|
callback(entry, s, m)
|
||||||
if failed then
|
if failed then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
parallel.waitForAll(table.unpack(t))
|
parallel.waitForAll(table.unpack(t))
|
||||||
end
|
end
|
||||||
|
|
||||||
return BulkGet
|
return BulkGet
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
--[[
|
|
||||||
Mount a readonly file system from another computer across rednet. The
|
|
||||||
target computer must be running OpusOS or redserver. Dissimlar to samba
|
|
||||||
in that a snapshot of the target is taken upon mounting - making this
|
|
||||||
faster.
|
|
||||||
|
|
||||||
Useful for mounting a non-changing directory tree.
|
|
||||||
|
|
||||||
Syntax:
|
|
||||||
rttp://<id>/directory/subdir
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
rttp://12/usr/etc
|
|
||||||
rttp://8/usr
|
|
||||||
]]--
|
|
||||||
|
|
||||||
local rttp = require('rttp')
|
|
||||||
|
|
||||||
local fs = _G.fs
|
|
||||||
|
|
||||||
local redfs = { }
|
|
||||||
|
|
||||||
local function getListing(uri)
|
|
||||||
local success, response = rttp.get(uri .. '?recursive=true')
|
|
||||||
|
|
||||||
if not success then
|
|
||||||
error(response)
|
|
||||||
end
|
|
||||||
|
|
||||||
if response.statusCode ~= 200 then
|
|
||||||
error('Received response ' .. response.statusCode)
|
|
||||||
end
|
|
||||||
|
|
||||||
local list = { }
|
|
||||||
for _,v in pairs(response.data) do
|
|
||||||
if not v.isDir then
|
|
||||||
list[v.path] = {
|
|
||||||
url = uri .. '/' .. v.path,
|
|
||||||
size = v.size,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return list
|
|
||||||
end
|
|
||||||
|
|
||||||
function redfs.mount(dir, uri)
|
|
||||||
if not uri then
|
|
||||||
error('redfs syntax: uri')
|
|
||||||
end
|
|
||||||
|
|
||||||
local list = getListing(uri)
|
|
||||||
for path, entry in pairs(list) do
|
|
||||||
if not fs.exists(fs.combine(dir, path)) then
|
|
||||||
local node = fs.mount(fs.combine(dir, path), 'urlfs', entry.url)
|
|
||||||
node.size = entry.size
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return redfs
|
|
@ -1,4 +1,4 @@
|
|||||||
local rttp = require('rttp')
|
--local rttp = require('rttp')
|
||||||
local Util = require('util')
|
local Util = require('util')
|
||||||
|
|
||||||
local fs = _G.fs
|
local fs = _G.fs
|
||||||
@ -39,7 +39,6 @@ function urlfs.getDrive()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function urlfs.open(node, fn, fl)
|
function urlfs.open(node, fn, fl)
|
||||||
|
|
||||||
if fl == 'w' or fl == 'wb' then
|
if fl == 'w' or fl == 'wb' then
|
||||||
fs.delete(fn)
|
fs.delete(fn)
|
||||||
return fs.open(fn, fl)
|
return fs.open(fn, fl)
|
||||||
@ -51,12 +50,15 @@ function urlfs.open(node, fn, fl)
|
|||||||
|
|
||||||
local c = node.cache
|
local c = node.cache
|
||||||
if not c then
|
if not c then
|
||||||
|
--[[
|
||||||
if node.url:match("^(rttps?:)") then
|
if node.url:match("^(rttps?:)") then
|
||||||
local s, response = rttp.get(node.url)
|
local s, response = rttp.get(node.url)
|
||||||
c = s and response.statusCode == 200 and response.data
|
c = s and response.statusCode == 200 and response.data
|
||||||
else
|
else
|
||||||
c = Util.httpGet(node.url)
|
c = Util.httpGet(node.url)
|
||||||
end
|
end
|
||||||
|
]]--
|
||||||
|
c = Util.httpGet(node.url)
|
||||||
if c then
|
if c then
|
||||||
node.cache = c
|
node.cache = c
|
||||||
node.size = #c
|
node.size = #c
|
||||||
|
@ -1,215 +1,584 @@
|
|||||||
-- credit ElvishJerricco
|
-- Module options:
|
||||||
-- http://pastebin.com/raw.php?i=4nRg9CHU
|
local register_global_module_table = false
|
||||||
|
local global_module_name = 'json'
|
||||||
|
|
||||||
local json = { }
|
--[==[
|
||||||
|
NOTE: Modified to reduce file size.
|
||||||
|
See https://github.com/LuaDist/dkjson/blob/master/dkjson.lua
|
||||||
|
for full version.
|
||||||
|
|
||||||
------------------------------------------------------------------ utils
|
David Kolf's JSON module for Lua 5.1/5.2
|
||||||
local controls = {["\n"]="\\n", ["\r"]="\\r", ["\t"]="\\t", ["\b"]="\\b", ["\f"]="\\f", ["\""]="\\\"", ["\\"]="\\\\"}
|
Version 2.5
|
||||||
|
|
||||||
local function isArray(t)
|
For the documentation see the corresponding readme.txt or visit
|
||||||
local max = 0
|
<http://dkolf.de/src/dkjson-lua.fsl/>.
|
||||||
for k,v in pairs(t) do
|
|
||||||
if type(k) ~= "number" then
|
You can contact the author by sending an e-mail to 'david' at the
|
||||||
return false
|
domain 'dkolf.de'.
|
||||||
elseif k > max then
|
|
||||||
max = k
|
Copyright (C) 2010-2014 David Heiko Kolf
|
||||||
end
|
|
||||||
end
|
Refer to license located at https://github.com/LuaDist/dkjson/blob/master/dkjson.lua
|
||||||
return max == #t
|
|
||||||
|
--]==]
|
||||||
|
|
||||||
|
-- global dependencies:
|
||||||
|
local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset =
|
||||||
|
pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset
|
||||||
|
local error, require, pcall, select = error, require, pcall, select
|
||||||
|
local floor, huge = math.floor, math.huge
|
||||||
|
local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat =
|
||||||
|
string.rep, string.gsub, string.sub, string.byte, string.char,
|
||||||
|
string.find, string.len, string.format
|
||||||
|
local strmatch = string.match
|
||||||
|
local concat = table.concat
|
||||||
|
|
||||||
|
local json = { version = "dkjson 2.5" }
|
||||||
|
|
||||||
|
if register_global_module_table then
|
||||||
|
_G[global_module_name] = json
|
||||||
end
|
end
|
||||||
|
|
||||||
local whites = {['\n']=true; ['\r']=true; ['\t']=true; [' ']=true; [',']=true; [':']=true}
|
local _ENV = nil -- blocking globals in Lua 5.2
|
||||||
local function removeWhite(str)
|
|
||||||
while whites[str:sub(1, 1)] do
|
|
||||||
str = str:sub(2)
|
|
||||||
end
|
|
||||||
return str
|
|
||||||
end
|
|
||||||
|
|
||||||
------------------------------------------------------------------ encoding
|
pcall (function()
|
||||||
|
-- Enable access to blocked metatables.
|
||||||
|
-- Don't worry, this module doesn't change anything in them.
|
||||||
|
local debmeta = require "debug".getmetatable
|
||||||
|
if debmeta then getmetatable = debmeta end
|
||||||
|
end)
|
||||||
|
|
||||||
local function encodeCommon(val, pretty, tabLevel, tTracking)
|
json.null = setmetatable ({}, {
|
||||||
local str = ""
|
__tojson = function () return "null" end
|
||||||
|
})
|
||||||
|
|
||||||
-- Tabbing util
|
local function isarray (tbl)
|
||||||
local function tab(s)
|
local max, n, arraylen = 0, 0, 0
|
||||||
str = str .. ("\t"):rep(tabLevel) .. s
|
for k,v in pairs (tbl) do
|
||||||
end
|
if k == 'n' and type(v) == 'number' then
|
||||||
|
arraylen = v
|
||||||
local function arrEncoding(val, bracket, closeBracket, iterator, loopFunc)
|
if v > max then
|
||||||
str = str .. bracket
|
max = v
|
||||||
if pretty then
|
end
|
||||||
str = str .. "\n"
|
|
||||||
tabLevel = tabLevel + 1
|
|
||||||
end
|
|
||||||
for k,v in iterator(val) do
|
|
||||||
tab("")
|
|
||||||
loopFunc(k,v)
|
|
||||||
str = str .. ","
|
|
||||||
if pretty then str = str .. "\n" end
|
|
||||||
end
|
|
||||||
if pretty then
|
|
||||||
tabLevel = tabLevel - 1
|
|
||||||
end
|
|
||||||
if str:sub(-2) == ",\n" then
|
|
||||||
str = str:sub(1, -3) .. "\n"
|
|
||||||
elseif str:sub(-1) == "," then
|
|
||||||
str = str:sub(1, -2)
|
|
||||||
end
|
|
||||||
tab(closeBracket)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Table encoding
|
|
||||||
if type(val) == "table" then
|
|
||||||
assert(not tTracking[val], "Cannot encode a table holding itself recursively")
|
|
||||||
tTracking[val] = true
|
|
||||||
if isArray(val) then
|
|
||||||
arrEncoding(val, "[", "]", ipairs, function(k,v)
|
|
||||||
str = str .. encodeCommon(v, pretty, tabLevel, tTracking)
|
|
||||||
end)
|
|
||||||
else
|
else
|
||||||
arrEncoding(val, "{", "}", pairs, function(k,v)
|
if type(k) ~= 'number' or k < 1 or floor(k) ~= k then
|
||||||
assert(type(k) == "string", "JSON object keys must be strings", 2)
|
return false
|
||||||
str = str .. encodeCommon(k, pretty, tabLevel, tTracking)
|
end
|
||||||
str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking)
|
if k > max then
|
||||||
end)
|
max = k
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
end
|
end
|
||||||
-- String encoding
|
end
|
||||||
elseif type(val) == "string" then
|
if max > 10 and max > arraylen and max > n * 2 then
|
||||||
str = '"' .. val:gsub("[%c\"\\]", controls) .. '"'
|
return false -- don't create an array with too many holes
|
||||||
-- Number encoding
|
end
|
||||||
elseif type(val) == "number" or type(val) == "boolean" then
|
return true, max
|
||||||
str = tostring(val)
|
end
|
||||||
|
|
||||||
|
local escapecodes = {
|
||||||
|
["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f",
|
||||||
|
["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"
|
||||||
|
}
|
||||||
|
|
||||||
|
local function escapeutf8 (uchar)
|
||||||
|
local value = escapecodes[uchar]
|
||||||
|
if value then
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
local a, b, c, d = strbyte (uchar, 1, 4)
|
||||||
|
a, b, c, d = a or 0, b or 0, c or 0, d or 0
|
||||||
|
if a <= 0x7f then
|
||||||
|
value = a
|
||||||
|
elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then
|
||||||
|
value = (a - 0xc0) * 0x40 + b - 0x80
|
||||||
|
elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then
|
||||||
|
value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80
|
||||||
|
elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then
|
||||||
|
value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80
|
||||||
else
|
else
|
||||||
error("JSON only supports arrays, objects, numbers, booleans, and strings", 2)
|
return ""
|
||||||
end
|
end
|
||||||
return str
|
if value <= 0xffff then
|
||||||
end
|
return strformat ("\\u%.4x", value)
|
||||||
|
elseif value <= 0x10ffff then
|
||||||
function json.encode(val)
|
-- encode as UTF-16 surrogate pair
|
||||||
return encodeCommon(val, false, 0, {})
|
value = value - 0x10000
|
||||||
end
|
local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400)
|
||||||
|
return strformat ("\\u%.4x\\u%.4x", highsur, lowsur)
|
||||||
function json.encodePretty(val)
|
|
||||||
return encodeCommon(val, true, 0, {})
|
|
||||||
end
|
|
||||||
|
|
||||||
function json.encodeToFile(path, val)
|
|
||||||
local file = io.open(path, "w")
|
|
||||||
assert(file, "Unable to open file")
|
|
||||||
file:write(json.encodePretty(val))
|
|
||||||
file:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
------------------------------------------------------------------ decoding
|
|
||||||
|
|
||||||
local decodeControls = {}
|
|
||||||
for k,v in pairs(controls) do
|
|
||||||
decodeControls[v] = k
|
|
||||||
end
|
|
||||||
|
|
||||||
local function parseBoolean(str)
|
|
||||||
if str:sub(1, 4) == "true" then
|
|
||||||
return true, removeWhite(str:sub(5))
|
|
||||||
else
|
else
|
||||||
return false, removeWhite(str:sub(6))
|
return ""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parseNull(str)
|
local function fsub (str, pattern, repl)
|
||||||
return nil, removeWhite(str:sub(5))
|
-- gsub always builds a new string in a buffer, even when no match
|
||||||
end
|
-- exists. First using find should be more efficient when most strings
|
||||||
|
-- don't contain the pattern.
|
||||||
local numChars = {['e']=true; ['E']=true; ['+']=true; ['-']=true; ['.']=true}
|
if strfind (str, pattern) then
|
||||||
local function parseNumber(str)
|
return gsub (str, pattern, repl)
|
||||||
local i = 1
|
else
|
||||||
while numChars[str:sub(i, i)] or tonumber(str:sub(i, i)) do
|
return str
|
||||||
i = i + 1
|
|
||||||
end
|
end
|
||||||
local val = tonumber(str:sub(1, i - 1))
|
|
||||||
str = removeWhite(str:sub(i))
|
|
||||||
return val, str
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parseString(str)
|
local function quotestring (value)
|
||||||
str = str:sub(2)
|
-- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js
|
||||||
local s = ""
|
value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8)
|
||||||
while str:sub(1,1) ~= "\"" do
|
if strfind (value, "[\194\216\220\225\226\239]") then
|
||||||
local next = str:sub(1,1)
|
value = fsub (value, "\194[\128-\159\173]", escapeutf8)
|
||||||
str = str:sub(2)
|
value = fsub (value, "\216[\128-\132]", escapeutf8)
|
||||||
assert(next ~= "\n", "Unclosed string")
|
value = fsub (value, "\220\143", escapeutf8)
|
||||||
|
value = fsub (value, "\225\158[\180\181]", escapeutf8)
|
||||||
|
value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8)
|
||||||
|
value = fsub (value, "\226\129[\160-\175]", escapeutf8)
|
||||||
|
value = fsub (value, "\239\187\191", escapeutf8)
|
||||||
|
value = fsub (value, "\239\191[\176-\191]", escapeutf8)
|
||||||
|
end
|
||||||
|
return "\"" .. value .. "\""
|
||||||
|
end
|
||||||
|
json.quotestring = quotestring
|
||||||
|
|
||||||
if next == "\\" then
|
local function replace(str, o, n)
|
||||||
local escape = str:sub(1,1)
|
local i, j = strfind (str, o, 1, true)
|
||||||
str = str:sub(2)
|
if i then
|
||||||
|
return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1)
|
||||||
|
else
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
next = assert(decodeControls[next..escape], "Invalid escape character")
|
-- locale independent num2str and str2num functions
|
||||||
|
local decpoint, numfilter
|
||||||
|
|
||||||
|
local function updatedecpoint ()
|
||||||
|
decpoint = strmatch(tostring(0.5), "([^05+])")
|
||||||
|
-- build a filter that can be used to remove group separators
|
||||||
|
numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+"
|
||||||
|
end
|
||||||
|
|
||||||
|
updatedecpoint()
|
||||||
|
|
||||||
|
local function num2str (num)
|
||||||
|
return replace(fsub(tostring(num), numfilter, ""), decpoint, ".")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function str2num (str)
|
||||||
|
local num = tonumber(replace(str, ".", decpoint))
|
||||||
|
if not num then
|
||||||
|
updatedecpoint()
|
||||||
|
num = tonumber(replace(str, ".", decpoint))
|
||||||
|
end
|
||||||
|
return num
|
||||||
|
end
|
||||||
|
|
||||||
|
local function addnewline2 (level, buffer, buflen)
|
||||||
|
buffer[buflen+1] = "\n"
|
||||||
|
buffer[buflen+2] = strrep (" ", level)
|
||||||
|
buflen = buflen + 2
|
||||||
|
return buflen
|
||||||
|
end
|
||||||
|
|
||||||
|
function json.addnewline (state)
|
||||||
|
if state.indent then
|
||||||
|
state.bufferlen = addnewline2 (state.level or 0,
|
||||||
|
state.buffer, state.bufferlen or #(state.buffer))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local encode2 -- forward declaration
|
||||||
|
|
||||||
|
local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state)
|
||||||
|
local kt = type (key)
|
||||||
|
if kt ~= 'string' and kt ~= 'number' then
|
||||||
|
return nil, "type '" .. kt .. "' is not supported as a key by JSON."
|
||||||
|
end
|
||||||
|
if prev then
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = ","
|
||||||
|
end
|
||||||
|
if indent then
|
||||||
|
buflen = addnewline2 (level, buffer, buflen)
|
||||||
|
end
|
||||||
|
buffer[buflen+1] = quotestring (key)
|
||||||
|
buffer[buflen+2] = ":"
|
||||||
|
return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function appendcustom(res, buffer, state)
|
||||||
|
local buflen = state.bufferlen
|
||||||
|
if type (res) == 'string' then
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = res
|
||||||
|
end
|
||||||
|
return buflen
|
||||||
|
end
|
||||||
|
|
||||||
|
local function exception(reason, value, state, buffer, buflen, defaultmessage)
|
||||||
|
defaultmessage = defaultmessage or reason
|
||||||
|
local handler = state.exception
|
||||||
|
if not handler then
|
||||||
|
return nil, defaultmessage
|
||||||
|
else
|
||||||
|
state.bufferlen = buflen
|
||||||
|
local ret, msg = handler (reason, value, state, defaultmessage)
|
||||||
|
if not ret then return nil, msg or defaultmessage end
|
||||||
|
return appendcustom(ret, buffer, state)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function json.encodeexception(reason, value, state, defaultmessage)
|
||||||
|
return quotestring("<" .. defaultmessage .. ">")
|
||||||
|
end
|
||||||
|
|
||||||
|
encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state)
|
||||||
|
local valtype = type (value)
|
||||||
|
local valmeta = getmetatable (value)
|
||||||
|
valmeta = type (valmeta) == 'table' and valmeta -- only tables
|
||||||
|
local valtojson = valmeta and valmeta.__tojson
|
||||||
|
if valtojson then
|
||||||
|
if tables[value] then
|
||||||
|
return exception('reference cycle', value, state, buffer, buflen)
|
||||||
end
|
end
|
||||||
|
tables[value] = true
|
||||||
s = s .. next
|
state.bufferlen = buflen
|
||||||
|
local ret, msg = valtojson (value, state)
|
||||||
|
if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end
|
||||||
|
tables[value] = nil
|
||||||
|
buflen = appendcustom(ret, buffer, state)
|
||||||
|
elseif value == nil then
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = "null"
|
||||||
|
elseif valtype == 'number' then
|
||||||
|
local s
|
||||||
|
if value ~= value or value >= huge or -value >= huge then
|
||||||
|
-- This is the behaviour of the original JSON implementation.
|
||||||
|
s = "null"
|
||||||
|
else
|
||||||
|
s = num2str (value)
|
||||||
|
end
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = s
|
||||||
|
elseif valtype == 'boolean' then
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = value and "true" or "false"
|
||||||
|
elseif valtype == 'string' then
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = quotestring (value)
|
||||||
|
elseif valtype == 'table' then
|
||||||
|
if tables[value] then
|
||||||
|
return exception('reference cycle', value, state, buffer, buflen)
|
||||||
|
end
|
||||||
|
tables[value] = true
|
||||||
|
level = level + 1
|
||||||
|
local isa, n = isarray (value)
|
||||||
|
if n == 0 and valmeta and valmeta.__jsontype == 'object' then
|
||||||
|
isa = false
|
||||||
|
end
|
||||||
|
local msg
|
||||||
|
if isa then -- JSON array
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = "["
|
||||||
|
for i = 1, n do
|
||||||
|
buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state)
|
||||||
|
if not buflen then return nil, msg end
|
||||||
|
if i < n then
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = ","
|
||||||
|
end
|
||||||
|
end
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = "]"
|
||||||
|
else -- JSON object
|
||||||
|
local prev = false
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = "{"
|
||||||
|
local order = valmeta and valmeta.__jsonorder or globalorder
|
||||||
|
if order then
|
||||||
|
local used = {}
|
||||||
|
n = #order
|
||||||
|
for i = 1, n do
|
||||||
|
local k = order[i]
|
||||||
|
local v = value[k]
|
||||||
|
if v then
|
||||||
|
used[k] = true
|
||||||
|
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
|
||||||
|
prev = true -- add a seperator before the next element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for k,v in pairs (value) do
|
||||||
|
if not used[k] then
|
||||||
|
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
|
||||||
|
if not buflen then return nil, msg end
|
||||||
|
prev = true -- add a seperator before the next element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else -- unordered
|
||||||
|
for k,v in pairs (value) do
|
||||||
|
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state)
|
||||||
|
if not buflen then return nil, msg end
|
||||||
|
prev = true -- add a seperator before the next element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if indent then
|
||||||
|
buflen = addnewline2 (level - 1, buffer, buflen)
|
||||||
|
end
|
||||||
|
buflen = buflen + 1
|
||||||
|
buffer[buflen] = "}"
|
||||||
|
end
|
||||||
|
tables[value] = nil
|
||||||
|
else
|
||||||
|
return exception ('unsupported type', value, state, buffer, buflen,
|
||||||
|
"type '" .. valtype .. "' is not supported by JSON.")
|
||||||
end
|
end
|
||||||
return s, removeWhite(str:sub(2))
|
return buflen
|
||||||
end
|
end
|
||||||
|
|
||||||
function json.parseArray(str)
|
function json.encode (value, state)
|
||||||
str = removeWhite(str:sub(2))
|
state = state or {}
|
||||||
|
local oldbuffer = state.buffer
|
||||||
local val = {}
|
local buffer = oldbuffer or {}
|
||||||
local i = 1
|
state.buffer = buffer
|
||||||
while str:sub(1, 1) ~= "]" do
|
updatedecpoint()
|
||||||
local v
|
local ret, msg = encode2 (value, state.indent, state.level or 0,
|
||||||
v, str = json.parseValue(str)
|
buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state)
|
||||||
val[i] = v
|
if not ret then
|
||||||
i = i + 1
|
error (msg, 2)
|
||||||
str = removeWhite(str)
|
elseif oldbuffer == buffer then
|
||||||
end
|
state.bufferlen = ret
|
||||||
str = removeWhite(str:sub(2))
|
return true
|
||||||
return val, str
|
else
|
||||||
end
|
state.bufferlen = nil
|
||||||
|
state.buffer = nil
|
||||||
function json.parseValue(str)
|
return concat (buffer)
|
||||||
local fchar = str:sub(1, 1)
|
|
||||||
if fchar == "{" then
|
|
||||||
return json.parseObject(str)
|
|
||||||
elseif fchar == "[" then
|
|
||||||
return json.parseArray(str)
|
|
||||||
elseif tonumber(fchar) ~= nil or numChars[fchar] then
|
|
||||||
return parseNumber(str)
|
|
||||||
elseif str:sub(1, 4) == "true" or str:sub(1, 5) == "false" then
|
|
||||||
return parseBoolean(str)
|
|
||||||
elseif fchar == "\"" then
|
|
||||||
return parseString(str)
|
|
||||||
elseif str:sub(1, 4) == "null" then
|
|
||||||
return parseNull(str)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function json.parseMember(str)
|
local function loc (str, where)
|
||||||
local k, val
|
local line, pos, linepos = 1, 1, 0
|
||||||
k, str = json.parseValue(str)
|
while true do
|
||||||
val, str = json.parseValue(str)
|
pos = strfind (str, "\n", pos, true)
|
||||||
return k, val, str
|
if pos and pos < where then
|
||||||
end
|
line = line + 1
|
||||||
|
linepos = pos
|
||||||
function json.parseObject(str)
|
pos = pos + 1
|
||||||
str = removeWhite(str:sub(2))
|
else
|
||||||
|
break
|
||||||
local val = {}
|
end
|
||||||
while str:sub(1, 1) ~= "}" do
|
|
||||||
local k, v
|
|
||||||
k, v, str = json.parseMember(str)
|
|
||||||
val[k] = v
|
|
||||||
str = removeWhite(str)
|
|
||||||
end
|
end
|
||||||
str = removeWhite(str:sub(2))
|
return "line " .. line .. ", column " .. (where - linepos)
|
||||||
return val, str
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function json.decode(str)
|
local function unterminated (str, what, where)
|
||||||
str = removeWhite(str)
|
return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where)
|
||||||
return json.parseValue(str)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function scanwhite (str, pos)
|
||||||
|
while true do
|
||||||
|
pos = strfind (str, "%S", pos)
|
||||||
|
if not pos then return nil end
|
||||||
|
local sub2 = strsub (str, pos, pos + 1)
|
||||||
|
if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then
|
||||||
|
-- UTF-8 Byte Order Mark
|
||||||
|
pos = pos + 3
|
||||||
|
elseif sub2 == "//" then
|
||||||
|
pos = strfind (str, "[\n\r]", pos + 2)
|
||||||
|
if not pos then return nil end
|
||||||
|
elseif sub2 == "/*" then
|
||||||
|
pos = strfind (str, "*/", pos + 2)
|
||||||
|
if not pos then return nil end
|
||||||
|
pos = pos + 2
|
||||||
|
else
|
||||||
|
return pos
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local escapechars = {
|
||||||
|
["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f",
|
||||||
|
["n"] = "\n", ["r"] = "\r", ["t"] = "\t"
|
||||||
|
}
|
||||||
|
|
||||||
|
local function unichar (value)
|
||||||
|
if value < 0 then
|
||||||
|
return nil
|
||||||
|
elseif value <= 0x007f then
|
||||||
|
return strchar (value)
|
||||||
|
elseif value <= 0x07ff then
|
||||||
|
return strchar (0xc0 + floor(value/0x40),
|
||||||
|
0x80 + (floor(value) % 0x40))
|
||||||
|
elseif value <= 0xffff then
|
||||||
|
return strchar (0xe0 + floor(value/0x1000),
|
||||||
|
0x80 + (floor(value/0x40) % 0x40),
|
||||||
|
0x80 + (floor(value) % 0x40))
|
||||||
|
elseif value <= 0x10ffff then
|
||||||
|
return strchar (0xf0 + floor(value/0x40000),
|
||||||
|
0x80 + (floor(value/0x1000) % 0x40),
|
||||||
|
0x80 + (floor(value/0x40) % 0x40),
|
||||||
|
0x80 + (floor(value) % 0x40))
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function scanstring (str, pos)
|
||||||
|
local lastpos = pos + 1
|
||||||
|
local buffer, n = {}, 0
|
||||||
|
while true do
|
||||||
|
local nextpos = strfind (str, "[\"\\]", lastpos)
|
||||||
|
if not nextpos then
|
||||||
|
return unterminated (str, "string", pos)
|
||||||
|
end
|
||||||
|
if nextpos > lastpos then
|
||||||
|
n = n + 1
|
||||||
|
buffer[n] = strsub (str, lastpos, nextpos - 1)
|
||||||
|
end
|
||||||
|
if strsub (str, nextpos, nextpos) == "\"" then
|
||||||
|
lastpos = nextpos + 1
|
||||||
|
break
|
||||||
|
else
|
||||||
|
local escchar = strsub (str, nextpos + 1, nextpos + 1)
|
||||||
|
local value
|
||||||
|
if escchar == "u" then
|
||||||
|
value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16)
|
||||||
|
if value then
|
||||||
|
local value2
|
||||||
|
if 0xD800 <= value and value <= 0xDBff then
|
||||||
|
-- we have the high surrogate of UTF-16. Check if there is a
|
||||||
|
-- low surrogate escaped nearby to combine them.
|
||||||
|
if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then
|
||||||
|
value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16)
|
||||||
|
if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then
|
||||||
|
value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000
|
||||||
|
else
|
||||||
|
value2 = nil -- in case it was out of range for a low surrogate
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
value = value and unichar (value)
|
||||||
|
if value then
|
||||||
|
if value2 then
|
||||||
|
lastpos = nextpos + 12
|
||||||
|
else
|
||||||
|
lastpos = nextpos + 6
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not value then
|
||||||
|
value = escapechars[escchar] or escchar
|
||||||
|
lastpos = nextpos + 2
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
buffer[n] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if n == 1 then
|
||||||
|
return buffer[1], lastpos
|
||||||
|
elseif n > 1 then
|
||||||
|
return concat (buffer), lastpos
|
||||||
|
else
|
||||||
|
return "", lastpos
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local scanvalue -- forward declaration
|
||||||
|
|
||||||
|
local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta)
|
||||||
|
local len = strlen (str)
|
||||||
|
local tbl, n = {}, 0
|
||||||
|
local pos = startpos + 1
|
||||||
|
if what == 'object' then
|
||||||
|
setmetatable (tbl, objectmeta)
|
||||||
|
else
|
||||||
|
setmetatable (tbl, arraymeta)
|
||||||
|
end
|
||||||
|
while true do
|
||||||
|
pos = scanwhite (str, pos)
|
||||||
|
if not pos then return unterminated (str, what, startpos) end
|
||||||
|
local char = strsub (str, pos, pos)
|
||||||
|
if char == closechar then
|
||||||
|
return tbl, pos + 1
|
||||||
|
end
|
||||||
|
local val1, err
|
||||||
|
val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
|
||||||
|
if err then return nil, pos, err end
|
||||||
|
pos = scanwhite (str, pos)
|
||||||
|
if not pos then return unterminated (str, what, startpos) end
|
||||||
|
char = strsub (str, pos, pos)
|
||||||
|
if char == ":" then
|
||||||
|
if val1 == nil then
|
||||||
|
return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")"
|
||||||
|
end
|
||||||
|
pos = scanwhite (str, pos + 1)
|
||||||
|
if not pos then return unterminated (str, what, startpos) end
|
||||||
|
local val2
|
||||||
|
val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
|
||||||
|
if err then return nil, pos, err end
|
||||||
|
tbl[val1] = val2
|
||||||
|
pos = scanwhite (str, pos)
|
||||||
|
if not pos then return unterminated (str, what, startpos) end
|
||||||
|
char = strsub (str, pos, pos)
|
||||||
|
else
|
||||||
|
n = n + 1
|
||||||
|
tbl[n] = val1
|
||||||
|
end
|
||||||
|
if char == "," then
|
||||||
|
pos = pos + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
scanvalue = function (str, pos, nullval, objectmeta, arraymeta)
|
||||||
|
pos = pos or 1
|
||||||
|
pos = scanwhite (str, pos)
|
||||||
|
if not pos then
|
||||||
|
return nil, strlen (str) + 1, "no valid JSON value (reached the end)"
|
||||||
|
end
|
||||||
|
local char = strsub (str, pos, pos)
|
||||||
|
if char == "{" then
|
||||||
|
return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta)
|
||||||
|
elseif char == "[" then
|
||||||
|
return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta)
|
||||||
|
elseif char == "\"" then
|
||||||
|
return scanstring (str, pos)
|
||||||
|
else
|
||||||
|
local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos)
|
||||||
|
if pstart then
|
||||||
|
local number = str2num (strsub (str, pstart, pend))
|
||||||
|
if number then
|
||||||
|
return number, pend + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
pstart, pend = strfind (str, "^%a%w*", pos)
|
||||||
|
if pstart then
|
||||||
|
local name = strsub (str, pstart, pend)
|
||||||
|
if name == "true" then
|
||||||
|
return true, pend + 1
|
||||||
|
elseif name == "false" then
|
||||||
|
return false, pend + 1
|
||||||
|
elseif name == "null" then
|
||||||
|
return nullval, pend + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil, pos, "no valid JSON value at " .. loc (str, pos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function optionalmetatables(...)
|
||||||
|
if select("#", ...) > 0 then
|
||||||
|
return ...
|
||||||
|
else
|
||||||
|
return {__jsontype = 'object'}, {__jsontype = 'array'}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function json.decode (str, pos, nullval, ...)
|
||||||
|
local objectmeta, arraymeta = optionalmetatables(...)
|
||||||
|
return scanvalue (str, pos, nullval, objectmeta, arraymeta)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- NOTE: added method - not in original source
|
||||||
function json.decodeFromFile(path)
|
function json.decodeFromFile(path)
|
||||||
local file = assert(fs.open(path, "r"))
|
local file = assert(fs.open(path, "r"))
|
||||||
local decoded = json.decode(file.readAll())
|
local decoded = json.decode(file.readAll())
|
||||||
|
@ -19,9 +19,9 @@ function Map.removeMatches(t, values)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
for k,v in pairs(t) do
|
for _, key in pairs(Util.keys(t)) do
|
||||||
if matchAll(v) then
|
if matchAll(t[key]) then
|
||||||
t[k] = nil
|
t[key] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -106,6 +106,17 @@ local function selectDestination(pts, box, grid)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function updateCanvas(path)
|
||||||
|
local t = { }
|
||||||
|
for node in path:nodes() do
|
||||||
|
table.insert(t, { x = node.x, y = node.y, z = node.z })
|
||||||
|
end
|
||||||
|
os.queueEvent('canvas', {
|
||||||
|
type = 'canvas_path',
|
||||||
|
data = t,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
local function pathTo(dest, options)
|
local function pathTo(dest, options)
|
||||||
local blocks = options.blocks or turtle.getState().blocks or { }
|
local blocks = options.blocks or turtle.getState().blocks or { }
|
||||||
local dests = options.dest or { dest } -- support alternative destinations
|
local dests = options.dest or { dest } -- support alternative destinations
|
||||||
@ -156,6 +167,8 @@ local function pathTo(dest, options)
|
|||||||
if not path then
|
if not path then
|
||||||
Util.removeByValue(dests, dest)
|
Util.removeByValue(dests, dest)
|
||||||
else
|
else
|
||||||
|
updateCanvas(path)
|
||||||
|
|
||||||
path:filter()
|
path:filter()
|
||||||
|
|
||||||
for node in path:nodes() do
|
for node in path:nodes() do
|
||||||
@ -173,11 +186,19 @@ local function pathTo(dest, options)
|
|||||||
|
|
||||||
-- use single turn method so the turtle doesn't turn around
|
-- use single turn method so the turtle doesn't turn around
|
||||||
-- when encountering obstacles
|
-- when encountering obstacles
|
||||||
if not turtle.gotoSingleTurn(pt.x, pt.y, pt.z, pt.heading) then
|
--if not turtle.gotoSingleTurn(pt.x, pt.y, pt.z, pt.heading) then
|
||||||
--if not turtle.goto(pt) then
|
pt.heading = nil
|
||||||
|
if not turtle.go(pt) then
|
||||||
local bpt = Point.nearestTo(turtle.point, pt)
|
local bpt = Point.nearestTo(turtle.point, pt)
|
||||||
|
|
||||||
|
if turtle.getFuelLevel() == 0 then
|
||||||
|
return false, 'Out of fuel'
|
||||||
|
end
|
||||||
table.insert(blocks, bpt)
|
table.insert(blocks, bpt)
|
||||||
|
os.queueEvent('canvas', {
|
||||||
|
type = 'canvas_barrier',
|
||||||
|
data = { bpt },
|
||||||
|
})
|
||||||
-- really need to check if the block we ran into was a turtle.
|
-- really need to check if the block we ran into was a turtle.
|
||||||
-- if so, this block should be temporary (1-2 secs)
|
-- if so, this block should be temporary (1-2 secs)
|
||||||
|
|
||||||
|
@ -131,8 +131,11 @@ end
|
|||||||
function Point.calculateMoves(pta, ptb, distance)
|
function Point.calculateMoves(pta, ptb, distance)
|
||||||
local heading = pta.heading
|
local heading = pta.heading
|
||||||
local moves = distance or Point.turtleDistance(pta, ptb)
|
local moves = distance or Point.turtleDistance(pta, ptb)
|
||||||
|
local weighted = moves
|
||||||
|
|
||||||
if (pta.heading % 2) == 0 and pta.z ~= ptb.z then
|
if (pta.heading % 2) == 0 and pta.z ~= ptb.z then
|
||||||
moves = moves + 1
|
moves = moves + 1
|
||||||
|
weighted = weighted + .9
|
||||||
if ptb.heading and (ptb.heading % 2 == 1) then
|
if ptb.heading and (ptb.heading % 2 == 1) then
|
||||||
heading = ptb.heading
|
heading = ptb.heading
|
||||||
elseif ptb.z > pta.z then
|
elseif ptb.z > pta.z then
|
||||||
@ -142,6 +145,7 @@ function Point.calculateMoves(pta, ptb, distance)
|
|||||||
end
|
end
|
||||||
elseif (pta.heading % 2) == 1 and pta.x ~= ptb.x then
|
elseif (pta.heading % 2) == 1 and pta.x ~= ptb.x then
|
||||||
moves = moves + 1
|
moves = moves + 1
|
||||||
|
weighted = weighted + .9
|
||||||
if ptb.heading and (ptb.heading % 2 == 0) then
|
if ptb.heading and (ptb.heading % 2 == 0) then
|
||||||
heading = ptb.heading
|
heading = ptb.heading
|
||||||
elseif ptb.x > pta.x then
|
elseif ptb.x > pta.x then
|
||||||
@ -152,15 +156,18 @@ function Point.calculateMoves(pta, ptb, distance)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not ptb.heading then
|
if not ptb.heading then
|
||||||
return moves, heading, moves
|
return moves, heading, weighted
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- need to know if we are in digging mode
|
||||||
|
-- if so, we need to face blocks -- need a no-backwards flag
|
||||||
|
|
||||||
-- calc turns as slightly less than moves
|
-- calc turns as slightly less than moves
|
||||||
local weighted = moves
|
-- local weighted = moves
|
||||||
if heading ~= ptb.heading then
|
if heading ~= ptb.heading then
|
||||||
local turns = Point.calculateTurns(heading, ptb.heading)
|
local turns = Point.calculateTurns(heading, ptb.heading)
|
||||||
moves = moves + turns
|
moves = moves + turns
|
||||||
local wturns = { [0] = 0, [1] = .9, [2] = 1.9 }
|
local wturns = { [0] = 0, [1] = .9, [2] = 1.8 }
|
||||||
weighted = weighted + wturns[turns]
|
weighted = weighted + wturns[turns]
|
||||||
heading = ptb.heading
|
heading = ptb.heading
|
||||||
end
|
end
|
||||||
@ -233,7 +240,7 @@ end
|
|||||||
function Point.nearestTo(pta, ptb)
|
function Point.nearestTo(pta, ptb)
|
||||||
local heading
|
local heading
|
||||||
|
|
||||||
if pta.x < ptb.x then
|
if pta.x < ptb.x then
|
||||||
heading = 0
|
heading = 0
|
||||||
elseif pta.z < ptb.z then
|
elseif pta.z < ptb.z then
|
||||||
heading = 1
|
heading = 1
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
local Socket = require('socket')
|
|
||||||
|
|
||||||
local Proxy = { }
|
|
||||||
|
|
||||||
function Proxy.create(remoteId, uri)
|
|
||||||
local socket, msg = Socket.connect(remoteId, 188)
|
|
||||||
|
|
||||||
if not socket then
|
|
||||||
error(msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
socket.co = coroutine.running()
|
|
||||||
|
|
||||||
socket:write(uri)
|
|
||||||
local methods = socket:read(2) or error('Timed out')
|
|
||||||
|
|
||||||
local hijack = { }
|
|
||||||
for _,method in pairs(methods) do
|
|
||||||
hijack[method] = function(...)
|
|
||||||
socket:write({ method, ... })
|
|
||||||
local resp = socket:read()
|
|
||||||
if not resp then
|
|
||||||
error('timed out: ' .. method)
|
|
||||||
end
|
|
||||||
return table.unpack(resp)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return hijack, socket
|
|
||||||
end
|
|
||||||
|
|
||||||
return Proxy
|
|
@ -1,95 +0,0 @@
|
|||||||
local device = _G.device
|
|
||||||
local os = _G.os
|
|
||||||
|
|
||||||
local rttp = { }
|
|
||||||
local computerId = os.getComputerID()
|
|
||||||
|
|
||||||
local function parse(url, default)
|
|
||||||
-- initialize default parameters
|
|
||||||
local parsed = {}
|
|
||||||
local authority
|
|
||||||
|
|
||||||
for i,v in pairs(default or parsed) do parsed[i] = v end
|
|
||||||
-- remove whitespace
|
|
||||||
-- url = string.gsub(url, "%s", "")
|
|
||||||
-- Decode unreserved characters
|
|
||||||
url = string.gsub(url, "%%(%x%x)", function(hex)
|
|
||||||
local char = string.char(tonumber(hex, 16))
|
|
||||||
if string.match(char, "[a-zA-Z0-9._~-]") then
|
|
||||||
return char
|
|
||||||
end
|
|
||||||
-- Hex encodings that are not unreserved must be preserved.
|
|
||||||
return nil
|
|
||||||
end)
|
|
||||||
-- get fragment
|
|
||||||
url = string.gsub(url, "#(.*)$", function(f)
|
|
||||||
parsed.fragment = f
|
|
||||||
return ""
|
|
||||||
end)
|
|
||||||
-- get scheme. Lower-case according to RFC 3986 section 3.1.
|
|
||||||
url = string.gsub(url, "^(%w[%w.+-]*):",
|
|
||||||
function(s) parsed.scheme = string.lower(s); return "" end)
|
|
||||||
-- get authority
|
|
||||||
url = string.gsub(url, "^//([^/]*)", function(n)
|
|
||||||
authority = n
|
|
||||||
return ""
|
|
||||||
end)
|
|
||||||
-- get query stringing
|
|
||||||
url = string.gsub(url, "%?(.*)", function(q)
|
|
||||||
parsed.query = q
|
|
||||||
return ""
|
|
||||||
end)
|
|
||||||
-- get params
|
|
||||||
url = string.gsub(url, "%;(.*)", function(p)
|
|
||||||
parsed.params = p
|
|
||||||
return ""
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- path is whatever was left
|
|
||||||
parsed.path = url
|
|
||||||
|
|
||||||
-- Represents host:port, port = nil if not used.
|
|
||||||
if authority then
|
|
||||||
authority = string.gsub(authority, ":(%d+)$",
|
|
||||||
function(p) parsed.port = tonumber(p); return "" end)
|
|
||||||
if authority ~= "" then
|
|
||||||
parsed.host = authority
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return parsed
|
|
||||||
end
|
|
||||||
|
|
||||||
function rttp.get(url)
|
|
||||||
local modem = device.wireless_modem or error('Modem not found')
|
|
||||||
local parsed = parse(url, { port = 80 })
|
|
||||||
|
|
||||||
parsed.host = tonumber(parsed.host) or error('Invalid url')
|
|
||||||
|
|
||||||
for i = 16384, 32767 do
|
|
||||||
if not modem.isOpen(i) then
|
|
||||||
modem.open(i)
|
|
||||||
local path = parsed.query and parsed.path .. '?' .. parsed.query or parsed.path
|
|
||||||
|
|
||||||
modem.transmit(parsed.port, parsed.host, {
|
|
||||||
method = 'GET',
|
|
||||||
replyAddress = computerId,
|
|
||||||
replyPort = i,
|
|
||||||
path = path,
|
|
||||||
})
|
|
||||||
local timerId = os.startTimer(3)
|
|
||||||
repeat
|
|
||||||
local event, id, dport, dhost, response = os.pullEvent()
|
|
||||||
if event == 'modem_message' and
|
|
||||||
dport == i and
|
|
||||||
dhost == computerId and
|
|
||||||
type(response) == 'table' then
|
|
||||||
modem.close(i)
|
|
||||||
return true, response
|
|
||||||
end
|
|
||||||
until event == 'timer' and id == timerId
|
|
||||||
return false, 'timeout'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return rttp
|
|
200
sys/apis/sha2.lua
Normal file
200
sys/apis/sha2.lua
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
-- SHA-256, HMAC and PBKDF2 functions in ComputerCraft
|
||||||
|
-- By Anavrins
|
||||||
|
|
||||||
|
local bit = _G.bit
|
||||||
|
|
||||||
|
local mod32 = 2^32
|
||||||
|
local band = bit32 and bit32.band or bit.band
|
||||||
|
local bnot = bit32 and bit32.bnot or bit.bnot
|
||||||
|
local bxor = bit32 and bit32.bxor or bit.bxor
|
||||||
|
local blshift = bit32 and bit32.lshift or bit.blshift
|
||||||
|
local upack = unpack
|
||||||
|
|
||||||
|
local function rrotate(n, b)
|
||||||
|
local s = n/(2^b)
|
||||||
|
local f = s%1
|
||||||
|
return (s-f) + f*mod32
|
||||||
|
end
|
||||||
|
local function brshift(int, by)
|
||||||
|
local s = int / (2^by)
|
||||||
|
return s - s%1
|
||||||
|
end
|
||||||
|
|
||||||
|
local H = {
|
||||||
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||||
|
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||||
|
}
|
||||||
|
|
||||||
|
local K = {
|
||||||
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||||
|
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||||
|
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||||
|
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||||
|
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||||
|
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||||
|
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function counter(incr)
|
||||||
|
local t1, t2 = 0, 0
|
||||||
|
if 0xFFFFFFFF - t1 < incr then
|
||||||
|
t2 = t2 + 1
|
||||||
|
t1 = incr - (0xFFFFFFFF - t1) - 1
|
||||||
|
else t1 = t1 + incr
|
||||||
|
end
|
||||||
|
return t2, t1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function BE_toInt(bs, i)
|
||||||
|
return blshift((bs[i] or 0), 24) + blshift((bs[i+1] or 0), 16) + blshift((bs[i+2] or 0), 8) + (bs[i+3] or 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function preprocess(data)
|
||||||
|
local len = #data
|
||||||
|
local proc = {}
|
||||||
|
data[#data+1] = 0x80
|
||||||
|
while #data%64~=56 do data[#data+1] = 0 end
|
||||||
|
local blocks = math.ceil(#data/64)
|
||||||
|
for i = 1, blocks do
|
||||||
|
proc[i] = {}
|
||||||
|
for j = 1, 16 do
|
||||||
|
proc[i][j] = BE_toInt(data, 1+((i-1)*64)+((j-1)*4))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
proc[blocks][15], proc[blocks][16] = counter(len*8)
|
||||||
|
return proc
|
||||||
|
end
|
||||||
|
|
||||||
|
local function digestblock(w, C)
|
||||||
|
for j = 17, 64 do
|
||||||
|
--local v = w[j-15]
|
||||||
|
local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3))
|
||||||
|
local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10))
|
||||||
|
w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32
|
||||||
|
end
|
||||||
|
local a, b, c, d, e, f, g, h = upack(C)
|
||||||
|
for j = 1, 64 do
|
||||||
|
local S1 = bxor(bxor(rrotate(e, 6), rrotate(e, 11)), rrotate(e, 25))
|
||||||
|
local ch = bxor(band(e, f), band(bnot(e), g))
|
||||||
|
local temp1 = (h + S1 + ch + K[j] + w[j])%mod32
|
||||||
|
local S0 = bxor(bxor(rrotate(a, 2), rrotate(a, 13)), rrotate(a, 22))
|
||||||
|
local maj = bxor(bxor(band(a, b), band(a, c)), band(b, c))
|
||||||
|
local temp2 = (S0 + maj)%mod32
|
||||||
|
h, g, f, e, d, c, b, a = g, f, e, (d+temp1)%mod32, c, b, a, (temp1+temp2)%mod32
|
||||||
|
end
|
||||||
|
C[1] = (C[1] + a)%mod32
|
||||||
|
C[2] = (C[2] + b)%mod32
|
||||||
|
C[3] = (C[3] + c)%mod32
|
||||||
|
C[4] = (C[4] + d)%mod32
|
||||||
|
C[5] = (C[5] + e)%mod32
|
||||||
|
C[6] = (C[6] + f)%mod32
|
||||||
|
C[7] = (C[7] + g)%mod32
|
||||||
|
C[8] = (C[8] + h)%mod32
|
||||||
|
return C
|
||||||
|
end
|
||||||
|
|
||||||
|
local mt = {
|
||||||
|
__tostring = function(a) return string.char(unpack(a)) end,
|
||||||
|
__index = {
|
||||||
|
toHex = function(self) return ("%02x"):rep(#self):format(unpack(self)) end,
|
||||||
|
isEqual = function(self, t)
|
||||||
|
if type(t) ~= "table" then return false end
|
||||||
|
if #self ~= #t then return false end
|
||||||
|
local ret = 0
|
||||||
|
for i = 1, #self do
|
||||||
|
ret = bit32.bor(ret, bxor(self[i], t[i]))
|
||||||
|
end
|
||||||
|
return ret == 0
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local function toBytes(t, n)
|
||||||
|
local b = {}
|
||||||
|
for i = 1, n do
|
||||||
|
b[(i-1)*4+1] = band(brshift(t[i], 24), 0xFF)
|
||||||
|
b[(i-1)*4+2] = band(brshift(t[i], 16), 0xFF)
|
||||||
|
b[(i-1)*4+3] = band(brshift(t[i], 8), 0xFF)
|
||||||
|
b[(i-1)*4+4] = band(t[i], 0xFF)
|
||||||
|
end
|
||||||
|
return setmetatable(b, mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function digest(data)
|
||||||
|
data = data or ""
|
||||||
|
data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)}
|
||||||
|
|
||||||
|
data = preprocess(data)
|
||||||
|
local C = {upack(H)}
|
||||||
|
for i = 1, #data do C = digestblock(data[i], C) end
|
||||||
|
return toBytes(C, 8)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hmac(data, key)
|
||||||
|
data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)}
|
||||||
|
key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)}
|
||||||
|
|
||||||
|
local blocksize = 64
|
||||||
|
|
||||||
|
key = #key > blocksize and digest(key) or key
|
||||||
|
|
||||||
|
local ipad = {}
|
||||||
|
local opad = {}
|
||||||
|
local padded_key = {}
|
||||||
|
|
||||||
|
for i = 1, blocksize do
|
||||||
|
ipad[i] = bxor(0x36, key[i] or 0)
|
||||||
|
opad[i] = bxor(0x5C, key[i] or 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #data do
|
||||||
|
ipad[blocksize+i] = data[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
ipad = digest(ipad)
|
||||||
|
|
||||||
|
for i = 1, blocksize do
|
||||||
|
padded_key[i] = opad[i]
|
||||||
|
padded_key[blocksize+i] = ipad[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
return digest(padded_key)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function pbkdf2(pass, salt, iter, dklen)
|
||||||
|
local hashlen = 32
|
||||||
|
local block = 1
|
||||||
|
local out = {}
|
||||||
|
|
||||||
|
dklen = dklen or 32
|
||||||
|
salt = type(salt) == "table" and salt or {tostring(salt):byte(1,-1)}
|
||||||
|
|
||||||
|
while dklen > 0 do
|
||||||
|
local ikey = {}
|
||||||
|
local isalt = {upack(salt)}
|
||||||
|
local clen = dklen > hashlen and hashlen or dklen
|
||||||
|
|
||||||
|
isalt[#isalt+1] = band(brshift(block, 24), 0xFF)
|
||||||
|
isalt[#isalt+1] = band(brshift(block, 16), 0xFF)
|
||||||
|
isalt[#isalt+1] = band(brshift(block, 8), 0xFF)
|
||||||
|
isalt[#isalt+1] = band(block, 0xFF)
|
||||||
|
|
||||||
|
for j = 1, iter do
|
||||||
|
isalt = hmac(isalt, pass)
|
||||||
|
for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end
|
||||||
|
if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end
|
||||||
|
end
|
||||||
|
dklen = dklen - clen
|
||||||
|
block = block+1
|
||||||
|
for k = 1, clen do out[#out+1] = ikey[k] end
|
||||||
|
end
|
||||||
|
|
||||||
|
return setmetatable(out, mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
digest = digest,
|
||||||
|
hmac = hmac,
|
||||||
|
pbkdf2 = pbkdf2,
|
||||||
|
}
|
@ -1,61 +1,61 @@
|
|||||||
local Sync = {
|
local Sync = {
|
||||||
syncLocks = { }
|
syncLocks = { }
|
||||||
}
|
}
|
||||||
|
|
||||||
local os = _G.os
|
local os = _G.os
|
||||||
|
|
||||||
function Sync.sync(obj, fn)
|
function Sync.sync(obj, fn)
|
||||||
local key = tostring(obj)
|
local key = tostring(obj)
|
||||||
if Sync.syncLocks[key] then
|
if Sync.syncLocks[key] then
|
||||||
local cos = tostring(coroutine.running())
|
local cos = tostring(coroutine.running())
|
||||||
table.insert(Sync.syncLocks[key], cos)
|
table.insert(Sync.syncLocks[key], cos)
|
||||||
repeat
|
repeat
|
||||||
local _, co = os.pullEvent('sync_lock')
|
local _, co = os.pullEvent('sync_lock')
|
||||||
until co == cos
|
until co == cos
|
||||||
else
|
else
|
||||||
Sync.syncLocks[key] = { }
|
Sync.syncLocks[key] = { }
|
||||||
end
|
end
|
||||||
local s, m = pcall(fn)
|
local s, m = pcall(fn)
|
||||||
local co = table.remove(Sync.syncLocks[key], 1)
|
local co = table.remove(Sync.syncLocks[key], 1)
|
||||||
if co then
|
if co then
|
||||||
os.queueEvent('sync_lock', co)
|
os.queueEvent('sync_lock', co)
|
||||||
else
|
else
|
||||||
Sync.syncLocks[key] = nil
|
Sync.syncLocks[key] = nil
|
||||||
end
|
end
|
||||||
if not s then
|
if not s then
|
||||||
error(m)
|
error(m)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Sync.lock(obj)
|
function Sync.lock(obj)
|
||||||
local key = tostring(obj)
|
local key = tostring(obj)
|
||||||
if Sync.syncLocks[key] then
|
if Sync.syncLocks[key] then
|
||||||
local cos = tostring(coroutine.running())
|
local cos = tostring(coroutine.running())
|
||||||
table.insert(Sync.syncLocks[key], cos)
|
table.insert(Sync.syncLocks[key], cos)
|
||||||
repeat
|
repeat
|
||||||
local _, co = os.pullEvent('sync_lock')
|
local _, co = os.pullEvent('sync_lock')
|
||||||
until co == cos
|
until co == cos
|
||||||
else
|
else
|
||||||
Sync.syncLocks[key] = { }
|
Sync.syncLocks[key] = { }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Sync.release(obj)
|
function Sync.release(obj)
|
||||||
local key = tostring(obj)
|
local key = tostring(obj)
|
||||||
if not Sync.syncLocks[key] then
|
if not Sync.syncLocks[key] then
|
||||||
error('Sync.release: Lock was not obtained', 2)
|
error('Sync.release: Lock was not obtained', 2)
|
||||||
end
|
end
|
||||||
local co = table.remove(Sync.syncLocks[key], 1)
|
local co = table.remove(Sync.syncLocks[key], 1)
|
||||||
if co then
|
if co then
|
||||||
os.queueEvent('sync_lock', co)
|
os.queueEvent('sync_lock', co)
|
||||||
else
|
else
|
||||||
Sync.syncLocks[key] = nil
|
Sync.syncLocks[key] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Sync.isLocked(obj)
|
function Sync.isLocked(obj)
|
||||||
local key = tostring(obj)
|
local key = tostring(obj)
|
||||||
return not not Sync.syncLocks[key]
|
return not not Sync.syncLocks[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
return Sync
|
return Sync
|
||||||
|
@ -5,106 +5,106 @@ local type = type
|
|||||||
local debug_traceback = type(debug) == "table" and type(debug.traceback) == "function" and debug.traceback
|
local debug_traceback = type(debug) == "table" and type(debug.traceback) == "function" and debug.traceback
|
||||||
|
|
||||||
local function traceback(x)
|
local function traceback(x)
|
||||||
-- Attempt to detect error() and error("xyz", 0).
|
-- Attempt to detect error() and error("xyz", 0).
|
||||||
-- This probably means they're erroring the program intentionally and so we
|
-- This probably means they're erroring the program intentionally and so we
|
||||||
-- shouldn't display anything.
|
-- shouldn't display anything.
|
||||||
if x == nil or (type(x) == "string" and not x:find(":%d+:")) then
|
if x == nil or (type(x) == "string" and not x:find(":%d+:")) then
|
||||||
return x
|
return x
|
||||||
end
|
end
|
||||||
|
|
||||||
if debug_traceback then
|
if debug_traceback then
|
||||||
-- The parens are important, as they prevent a tail call occuring, meaning
|
-- The parens are important, as they prevent a tail call occuring, meaning
|
||||||
-- the stack level is preserved. This ensures the code behaves identically
|
-- the stack level is preserved. This ensures the code behaves identically
|
||||||
-- on LuaJ and PUC Lua.
|
-- on LuaJ and PUC Lua.
|
||||||
return (debug_traceback(tostring(x), 2))
|
return (debug_traceback(tostring(x), 2))
|
||||||
else
|
else
|
||||||
local level = 3
|
local level = 3
|
||||||
local out = { tostring(x), "stack traceback:" }
|
local out = { tostring(x), "stack traceback:" }
|
||||||
while true do
|
while true do
|
||||||
local _, msg = pcall(error, "", level)
|
local _, msg = pcall(error, "", level)
|
||||||
if msg == "" then break end
|
if msg == "" then break end
|
||||||
|
|
||||||
out[#out + 1] = " " .. msg
|
out[#out + 1] = " " .. msg
|
||||||
level = level + 1
|
level = level + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
return table.concat(out, "\n")
|
return table.concat(out, "\n")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function trim_traceback(target, marker)
|
local function trim_traceback(target, marker)
|
||||||
local ttarget, tmarker = {}, {}
|
local ttarget, tmarker = {}, {}
|
||||||
for line in target:gmatch("([^\n]*)\n?") do ttarget[#ttarget + 1] = line end
|
for line in target:gmatch("([^\n]*)\n?") do ttarget[#ttarget + 1] = line end
|
||||||
for line in marker:gmatch("([^\n]*)\n?") do tmarker[#tmarker + 1] = line end
|
for line in marker:gmatch("([^\n]*)\n?") do tmarker[#tmarker + 1] = line end
|
||||||
|
|
||||||
-- Trim identical suffixes
|
-- Trim identical suffixes
|
||||||
local t_len, m_len = #ttarget, #tmarker
|
local t_len, m_len = #ttarget, #tmarker
|
||||||
while t_len >= 3 and ttarget[t_len] == tmarker[m_len] do
|
while t_len >= 3 and ttarget[t_len] == tmarker[m_len] do
|
||||||
table.remove(ttarget, t_len)
|
table.remove(ttarget, t_len)
|
||||||
t_len, m_len = t_len - 1, m_len - 1
|
t_len, m_len = t_len - 1, m_len - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Trim elements from this file and xpcall invocations
|
-- Trim elements from this file and xpcall invocations
|
||||||
while t_len >= 1 and ttarget[t_len]:find("^\tstack_trace%.lua:%d+:") or
|
while t_len >= 1 and ttarget[t_len]:find("^\tstack_trace%.lua:%d+:") or
|
||||||
ttarget[t_len] == "\t[C]: in function 'xpcall'" or ttarget[t_len] == " xpcall: " do
|
ttarget[t_len] == "\t[C]: in function 'xpcall'" or ttarget[t_len] == " xpcall: " do
|
||||||
table.remove(ttarget, t_len)
|
table.remove(ttarget, t_len)
|
||||||
t_len = t_len - 1
|
t_len = t_len - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
ttarget[#ttarget] = nil -- remove 2 calls added by the added xpcall
|
ttarget[#ttarget] = nil -- remove 2 calls added by the added xpcall
|
||||||
ttarget[#ttarget] = nil
|
ttarget[#ttarget] = nil
|
||||||
|
|
||||||
return ttarget
|
return ttarget
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Run a function with
|
--- Run a function with
|
||||||
return function (fn, ...)
|
return function (fn, ...)
|
||||||
-- So this is rather grim: we need to get the full traceback and current one and remove
|
-- So this is rather grim: we need to get the full traceback and current one and remove
|
||||||
-- the common prefix
|
-- the common prefix
|
||||||
local trace
|
local trace
|
||||||
local args = { ... }
|
local args = { ... }
|
||||||
|
|
||||||
-- xpcall in Lua 5.1 does not accept parameters
|
-- xpcall in Lua 5.1 does not accept parameters
|
||||||
-- which is not ideal
|
-- which is not ideal
|
||||||
local res = table.pack(xpcall(function()
|
local res = table.pack(xpcall(function()
|
||||||
return fn(table.unpack(args))
|
return fn(table.unpack(args))
|
||||||
end, traceback))
|
end, traceback))
|
||||||
|
|
||||||
if not res[1] then
|
if not res[1] then
|
||||||
trace = traceback("trace.lua:1:")
|
trace = traceback("trace.lua:1:")
|
||||||
end
|
end
|
||||||
local ok, err = res[1], res[2]
|
local ok, err = res[1], res[2]
|
||||||
|
|
||||||
if not ok and err ~= nil then
|
if not ok and err ~= nil then
|
||||||
trace = trim_traceback(err, trace)
|
trace = trim_traceback(err, trace)
|
||||||
|
|
||||||
-- Find the position where the stack traceback actually starts
|
-- Find the position where the stack traceback actually starts
|
||||||
local trace_starts
|
local trace_starts
|
||||||
for i = #trace, 1, -1 do
|
for i = #trace, 1, -1 do
|
||||||
if trace[i] == "stack traceback:" then trace_starts = i; break end
|
if trace[i] == "stack traceback:" then trace_starts = i; break end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, line in pairs(trace) do
|
for _, line in pairs(trace) do
|
||||||
_G._syslog(line)
|
_G._syslog(line)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If this traceback is more than 15 elements long, keep the first 9, last 5
|
-- If this traceback is more than 15 elements long, keep the first 9, last 5
|
||||||
-- and put an ellipsis between the rest
|
-- and put an ellipsis between the rest
|
||||||
local max = 10
|
local max = 10
|
||||||
if trace_starts and #trace - trace_starts > max then
|
if trace_starts and #trace - trace_starts > max then
|
||||||
local keep_starts = trace_starts + 7
|
local keep_starts = trace_starts + 7
|
||||||
for i = #trace - trace_starts - max, 0, -1 do
|
for i = #trace - trace_starts - max, 0, -1 do
|
||||||
table.remove(trace, keep_starts + i)
|
table.remove(trace, keep_starts + i)
|
||||||
end
|
end
|
||||||
table.insert(trace, keep_starts, " ...")
|
table.insert(trace, keep_starts, " ...")
|
||||||
end
|
end
|
||||||
|
|
||||||
for k, line in pairs(trace) do
|
for k, line in pairs(trace) do
|
||||||
trace[k] = line:gsub("in function", " in")
|
trace[k] = line:gsub("in function", " in")
|
||||||
end
|
end
|
||||||
|
|
||||||
return false, table.remove(trace, 1), table.concat(trace, "\n")
|
return false, table.remove(trace, 1), table.concat(trace, "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
return table.unpack(res, 1, res.n)
|
return table.unpack(res, 1, res.n)
|
||||||
end
|
end
|
||||||
|
@ -329,23 +329,13 @@ function UI.Grid:drawRows()
|
|||||||
local rawRow = self.values[key]
|
local rawRow = self.values[key]
|
||||||
local row = self:getDisplayValues(rawRow, key)
|
local row = self:getDisplayValues(rawRow, key)
|
||||||
|
|
||||||
local ind = ' '
|
|
||||||
if self.focused and index == self.index and not self.inactive then
|
|
||||||
ind = self.focusIndicator
|
|
||||||
end
|
|
||||||
|
|
||||||
local selected = index == self.index and not self.inactive
|
local selected = index == self.index and not self.inactive
|
||||||
local bg = self:getRowBackgroundColor(rawRow, selected)
|
local bg = self:getRowBackgroundColor(rawRow, selected)
|
||||||
local fg = self:getRowTextColor(rawRow, selected)
|
local fg = self:getRowTextColor(rawRow, selected)
|
||||||
|
local focused = self.focused and selected
|
||||||
|
|
||||||
|
self:drawRow(sb, row, focused, bg, fg)
|
||||||
|
|
||||||
for _,col in pairs(self.columns) do
|
|
||||||
sb:write(ind .. safeValue(row[col.key] or ''),
|
|
||||||
col.cw + 1,
|
|
||||||
col.align,
|
|
||||||
bg,
|
|
||||||
fg)
|
|
||||||
ind = ' '
|
|
||||||
end
|
|
||||||
sb:finish(bg)
|
sb:finish(bg)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -354,6 +344,19 @@ function UI.Grid:drawRows()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function UI.Grid:drawRow(sb, row, focused, bg, fg)
|
||||||
|
local ind = focused and self.focusIndicator or ' '
|
||||||
|
|
||||||
|
for _,col in pairs(self.columns) do
|
||||||
|
sb:write(ind .. safeValue(row[col.key] or ''),
|
||||||
|
col.cw + 1,
|
||||||
|
col.align,
|
||||||
|
bg,
|
||||||
|
fg)
|
||||||
|
ind = ' '
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function UI.Grid:getRowTextColor(row, selected)
|
function UI.Grid:getRowTextColor(row, selected)
|
||||||
if selected then
|
if selected then
|
||||||
if self.focused then
|
if self.focused then
|
||||||
|
@ -560,7 +560,7 @@ function Util.insertString(str, istr, pos)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Util.split(str, pattern)
|
function Util.split(str, pattern)
|
||||||
if not str then error('Util.split: Invalid parameters', 2) end
|
if not str or type(str) ~= 'string' then error('Util.split: Invalid parameters', 2) end
|
||||||
pattern = pattern or "(.-)\n"
|
pattern = pattern or "(.-)\n"
|
||||||
local t = {}
|
local t = {}
|
||||||
local function helper(line) table.insert(t, line) return "" end
|
local function helper(line) table.insert(t, line) return "" end
|
||||||
|
@ -15,11 +15,11 @@ local FILE = 1
|
|||||||
UI:configure('Files', ...)
|
UI:configure('Files', ...)
|
||||||
|
|
||||||
local config = Config.load('Files', {
|
local config = Config.load('Files', {
|
||||||
showHidden = false,
|
showHidden = false,
|
||||||
showDirSizes = false,
|
showDirSizes = false,
|
||||||
})
|
})
|
||||||
config.associations = config.associations or {
|
config.associations = config.associations or {
|
||||||
nft = 'pain',
|
nft = 'pain',
|
||||||
}
|
}
|
||||||
|
|
||||||
local copied = { }
|
local copied = { }
|
||||||
@ -28,517 +28,517 @@ local directories = { }
|
|||||||
local cutMode = false
|
local cutMode = false
|
||||||
|
|
||||||
local function formatSize(size)
|
local function formatSize(size)
|
||||||
if size >= 1000000 then
|
if size >= 1000000 then
|
||||||
return string.format('%dM', math.floor(size/1000000, 2))
|
return string.format('%dM', math.floor(size/1000000, 2))
|
||||||
elseif size >= 1000 then
|
elseif size >= 1000 then
|
||||||
return string.format('%dK', math.floor(size/1000, 2))
|
return string.format('%dK', math.floor(size/1000, 2))
|
||||||
end
|
end
|
||||||
return size
|
return size
|
||||||
end
|
end
|
||||||
|
|
||||||
local Browser = UI.Page {
|
local Browser = UI.Page {
|
||||||
menuBar = UI.MenuBar {
|
menuBar = UI.MenuBar {
|
||||||
buttons = {
|
buttons = {
|
||||||
{ text = '^-', event = 'updir' },
|
{ text = '^-', event = 'updir' },
|
||||||
{ text = 'File', dropdown = {
|
{ text = 'File', dropdown = {
|
||||||
{ text = 'Run', event = 'run', flags = FILE },
|
{ text = 'Run', event = 'run', flags = FILE },
|
||||||
{ text = 'Edit e', event = 'edit', flags = FILE },
|
{ text = 'Edit e', event = 'edit', flags = FILE },
|
||||||
{ text = 'Cloud edit c', event = 'cedit', flags = FILE },
|
{ text = 'Cloud edit c', event = 'cedit', flags = FILE },
|
||||||
{ text = 'Pastebin put p', event = 'pastebin', flags = FILE },
|
{ text = 'Pastebin put p', event = 'pastebin', flags = FILE },
|
||||||
{ text = 'Shell s', event = 'shell' },
|
{ text = 'Shell s', event = 'shell' },
|
||||||
{ spacer = true },
|
{ spacer = true },
|
||||||
{ text = 'Quit q', event = 'quit' },
|
{ text = 'Quit q', event = 'quit' },
|
||||||
} },
|
} },
|
||||||
{ text = 'Edit', dropdown = {
|
{ text = 'Edit', dropdown = {
|
||||||
{ text = 'Cut ^x', event = 'cut' },
|
{ text = 'Cut ^x', event = 'cut' },
|
||||||
{ text = 'Copy ^c', event = 'copy' },
|
{ text = 'Copy ^c', event = 'copy' },
|
||||||
{ text = 'Copy path ', event = 'copy_path' },
|
{ text = 'Copy path ', event = 'copy_path' },
|
||||||
{ text = 'Paste ^v', event = 'paste' },
|
{ text = 'Paste ^v', event = 'paste' },
|
||||||
{ spacer = true },
|
{ spacer = true },
|
||||||
{ text = 'Mark m', event = 'mark' },
|
{ text = 'Mark m', event = 'mark' },
|
||||||
{ text = 'Unmark all u', event = 'unmark' },
|
{ text = 'Unmark all u', event = 'unmark' },
|
||||||
{ spacer = true },
|
{ spacer = true },
|
||||||
{ text = 'Delete del', event = 'delete' },
|
{ text = 'Delete del', event = 'delete' },
|
||||||
} },
|
} },
|
||||||
{ text = 'View', dropdown = {
|
{ text = 'View', dropdown = {
|
||||||
{ text = 'Refresh r', event = 'refresh' },
|
{ text = 'Refresh r', event = 'refresh' },
|
||||||
{ text = 'Hidden ^h', event = 'toggle_hidden' },
|
{ text = 'Hidden ^h', event = 'toggle_hidden' },
|
||||||
{ text = 'Dir Size ^s', event = 'toggle_dirSize' },
|
{ text = 'Dir Size ^s', event = 'toggle_dirSize' },
|
||||||
} },
|
} },
|
||||||
{ text = '\187',
|
{ text = '\187',
|
||||||
x = -3,
|
x = -3,
|
||||||
dropdown = {
|
dropdown = {
|
||||||
{ text = 'Associations', event = 'associate' },
|
{ text = 'Associations', event = 'associate' },
|
||||||
} },
|
} },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Name', key = 'name' },
|
{ heading = 'Name', key = 'name' },
|
||||||
{ key = 'flags', width = 2 },
|
{ key = 'flags', width = 2 },
|
||||||
{ heading = 'Size', key = 'fsize', width = 5 },
|
{ heading = 'Size', key = 'fsize', width = 5 },
|
||||||
},
|
},
|
||||||
sortColumn = 'name',
|
sortColumn = 'name',
|
||||||
y = 2, ey = -2,
|
y = 2, ey = -2,
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar {
|
statusBar = UI.StatusBar {
|
||||||
columns = {
|
columns = {
|
||||||
{ key = 'status' },
|
{ key = 'status' },
|
||||||
{ key = 'totalSize', width = 6 },
|
{ key = 'totalSize', width = 6 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
notification = UI.Notification { },
|
notification = UI.Notification { },
|
||||||
associations = UI.SlideOut {
|
associations = UI.SlideOut {
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
menuBar = UI.MenuBar {
|
menuBar = UI.MenuBar {
|
||||||
buttons = {
|
buttons = {
|
||||||
{ text = 'Save', event = 'save' },
|
{ text = 'Save', event = 'save' },
|
||||||
{ text = 'Cancel', event = 'cancel' },
|
{ text = 'Cancel', event = 'cancel' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
grid = UI.ScrollingGrid {
|
grid = UI.ScrollingGrid {
|
||||||
x = 2, ex = -6, y = 3, ey = -5,
|
x = 2, ex = -6, y = 3, ey = -5,
|
||||||
columns = {
|
columns = {
|
||||||
{ heading = 'Extension', key = 'name' },
|
{ heading = 'Extension', key = 'name' },
|
||||||
{ heading = 'Program', key = 'value' },
|
{ heading = 'Program', key = 'value' },
|
||||||
},
|
},
|
||||||
autospace = true,
|
autospace = true,
|
||||||
sortColumn = 'name',
|
sortColumn = 'name',
|
||||||
accelerators = {
|
accelerators = {
|
||||||
delete = 'remove_entry',
|
delete = 'remove_entry',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
remove = UI.Button {
|
remove = UI.Button {
|
||||||
x = -4, y = 6,
|
x = -4, y = 6,
|
||||||
text = '-', event = 'remove_entry', help = 'Remove',
|
text = '-', event = 'remove_entry', help = 'Remove',
|
||||||
},
|
},
|
||||||
form = UI.Form {
|
form = UI.Form {
|
||||||
x = 3, y = -3, ey = -2,
|
x = 3, y = -3, ey = -2,
|
||||||
margin = 1,
|
margin = 1,
|
||||||
manualControls = true,
|
manualControls = true,
|
||||||
[1] = UI.TextEntry {
|
[1] = UI.TextEntry {
|
||||||
width = 20,
|
width = 20,
|
||||||
formLabel = 'Extension', formKey = 'name',
|
formLabel = 'Extension', formKey = 'name',
|
||||||
shadowText = 'extension',
|
shadowText = 'extension',
|
||||||
required = true,
|
required = true,
|
||||||
limit = 64,
|
limit = 64,
|
||||||
},
|
},
|
||||||
[2] = UI.TextEntry {
|
[2] = UI.TextEntry {
|
||||||
width = 20,
|
width = 20,
|
||||||
formLabel = 'Program', formKey = 'value',
|
formLabel = 'Program', formKey = 'value',
|
||||||
shadowText = 'program',
|
shadowText = 'program',
|
||||||
required = true,
|
required = true,
|
||||||
limit = 128,
|
limit = 128,
|
||||||
},
|
},
|
||||||
add = UI.Button {
|
add = UI.Button {
|
||||||
x = -11, y = 1,
|
x = -11, y = 1,
|
||||||
text = 'Add', event = 'add_association',
|
text = 'Add', event = 'add_association',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar {
|
statusBar = UI.StatusBar {
|
||||||
backgroundColor = colors.cyan,
|
backgroundColor = colors.cyan,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
accelerators = {
|
accelerators = {
|
||||||
q = 'quit',
|
q = 'quit',
|
||||||
c = 'cedit',
|
c = 'cedit',
|
||||||
e = 'edit',
|
e = 'edit',
|
||||||
s = 'shell',
|
s = 'shell',
|
||||||
p = 'pastebin',
|
p = 'pastebin',
|
||||||
r = 'refresh',
|
r = 'refresh',
|
||||||
[ ' ' ] = 'mark',
|
[ ' ' ] = 'mark',
|
||||||
m = 'mark',
|
m = 'mark',
|
||||||
backspace = 'updir',
|
backspace = 'updir',
|
||||||
u = 'unmark',
|
u = 'unmark',
|
||||||
d = 'delete',
|
d = 'delete',
|
||||||
delete = 'delete',
|
delete = 'delete',
|
||||||
[ 'control-h' ] = 'toggle_hidden',
|
[ 'control-h' ] = 'toggle_hidden',
|
||||||
[ 'control-s' ] = 'toggle_dirSize',
|
[ 'control-s' ] = 'toggle_dirSize',
|
||||||
[ 'control-x' ] = 'cut',
|
[ 'control-x' ] = 'cut',
|
||||||
[ 'control-c' ] = 'copy',
|
[ 'control-c' ] = 'copy',
|
||||||
paste = 'paste',
|
paste = 'paste',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function Browser:enable()
|
function Browser:enable()
|
||||||
UI.Page.enable(self)
|
UI.Page.enable(self)
|
||||||
self:setFocus(self.grid)
|
self:setFocus(self.grid)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser.menuBar:getActive(menuItem)
|
function Browser.menuBar:getActive(menuItem)
|
||||||
local file = Browser.grid:getSelected()
|
local file = Browser.grid:getSelected()
|
||||||
if menuItem.flags == FILE then
|
if menuItem.flags == FILE then
|
||||||
return file and not file.isDir
|
return file and not file.isDir
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser.grid:sortCompare(a, b)
|
function Browser.grid:sortCompare(a, b)
|
||||||
if self.sortColumn == 'fsize' then
|
if self.sortColumn == 'fsize' then
|
||||||
return a.size < b.size
|
return a.size < b.size
|
||||||
elseif self.sortColumn == 'flags' then
|
elseif self.sortColumn == 'flags' then
|
||||||
return a.flags < b.flags
|
return a.flags < b.flags
|
||||||
end
|
end
|
||||||
if a.isDir == b.isDir then
|
if a.isDir == b.isDir then
|
||||||
return a.name:lower() < b.name:lower()
|
return a.name:lower() < b.name:lower()
|
||||||
end
|
end
|
||||||
return a.isDir
|
return a.isDir
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser.grid:getRowTextColor(file)
|
function Browser.grid:getRowTextColor(file)
|
||||||
if file.marked then
|
if file.marked then
|
||||||
return colors.green
|
return colors.green
|
||||||
end
|
end
|
||||||
if file.isDir then
|
if file.isDir then
|
||||||
return colors.cyan
|
return colors.cyan
|
||||||
end
|
end
|
||||||
if file.isReadOnly then
|
if file.isReadOnly then
|
||||||
return colors.pink
|
return colors.pink
|
||||||
end
|
end
|
||||||
return colors.white
|
return colors.white
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser.grid:eventHandler(event)
|
function Browser.grid:eventHandler(event)
|
||||||
if event.type == 'copy' then -- let copy be handled by parent
|
if event.type == 'copy' then -- let copy be handled by parent
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return UI.ScrollingGrid.eventHandler(self, event)
|
return UI.ScrollingGrid.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser.statusBar:draw()
|
function Browser.statusBar:draw()
|
||||||
if self.parent.dir then
|
if self.parent.dir then
|
||||||
local info = '#:' .. Util.size(self.parent.dir.files)
|
local info = '#:' .. Util.size(self.parent.dir.files)
|
||||||
local numMarked = Util.size(marked)
|
local numMarked = Util.size(marked)
|
||||||
if numMarked > 0 then
|
if numMarked > 0 then
|
||||||
info = info .. ' M:' .. numMarked
|
info = info .. ' M:' .. numMarked
|
||||||
end
|
end
|
||||||
self:setValue('info', info)
|
self:setValue('info', info)
|
||||||
self:setValue('totalSize', formatSize(self.parent.dir.totalSize))
|
self:setValue('totalSize', formatSize(self.parent.dir.totalSize))
|
||||||
UI.StatusBar.draw(self)
|
UI.StatusBar.draw(self)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser:setStatus(status, ...)
|
function Browser:setStatus(status, ...)
|
||||||
self.notification:info(string.format(status, ...))
|
self.notification:info(string.format(status, ...))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser:unmarkAll()
|
function Browser:unmarkAll()
|
||||||
for _,m in pairs(marked) do
|
for _,m in pairs(marked) do
|
||||||
m.marked = false
|
m.marked = false
|
||||||
end
|
end
|
||||||
Util.clear(marked)
|
Util.clear(marked)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser:getDirectory(directory)
|
function Browser:getDirectory(directory)
|
||||||
local s, dir = pcall(function()
|
local s, dir = pcall(function()
|
||||||
|
|
||||||
local dir = directories[directory]
|
local dir = directories[directory]
|
||||||
if not dir then
|
if not dir then
|
||||||
dir = {
|
dir = {
|
||||||
name = directory,
|
name = directory,
|
||||||
size = 0,
|
size = 0,
|
||||||
files = { },
|
files = { },
|
||||||
totalSize = 0,
|
totalSize = 0,
|
||||||
index = 1
|
index = 1
|
||||||
}
|
}
|
||||||
directories[directory] = dir
|
directories[directory] = dir
|
||||||
end
|
end
|
||||||
|
|
||||||
self:updateDirectory(dir)
|
self:updateDirectory(dir)
|
||||||
|
|
||||||
return dir
|
return dir
|
||||||
end)
|
end)
|
||||||
|
|
||||||
return s, dir
|
return s, dir
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser:updateDirectory(dir)
|
function Browser:updateDirectory(dir)
|
||||||
|
|
||||||
dir.size = 0
|
dir.size = 0
|
||||||
dir.totalSize = 0
|
dir.totalSize = 0
|
||||||
Util.clear(dir.files)
|
Util.clear(dir.files)
|
||||||
|
|
||||||
local files = fs.listEx(dir.name)
|
local files = fs.listEx(dir.name)
|
||||||
if files then
|
if files then
|
||||||
dir.size = #files
|
dir.size = #files
|
||||||
for _, file in pairs(files) do
|
for _, file in pairs(files) do
|
||||||
file.fullName = fs.combine(dir.name, file.name)
|
file.fullName = fs.combine(dir.name, file.name)
|
||||||
file.flags = ''
|
file.flags = ''
|
||||||
if not file.isDir then
|
if not file.isDir then
|
||||||
dir.totalSize = dir.totalSize + file.size
|
dir.totalSize = dir.totalSize + file.size
|
||||||
file.fsize = formatSize(file.size)
|
file.fsize = formatSize(file.size)
|
||||||
else
|
else
|
||||||
if config.showDirSizes then
|
if config.showDirSizes then
|
||||||
file.size = fs.getSize(file.fullName, true)
|
file.size = fs.getSize(file.fullName, true)
|
||||||
|
|
||||||
dir.totalSize = dir.totalSize + file.size
|
dir.totalSize = dir.totalSize + file.size
|
||||||
file.fsize = formatSize(file.size)
|
file.fsize = formatSize(file.size)
|
||||||
end
|
end
|
||||||
file.flags = 'D'
|
file.flags = 'D'
|
||||||
end
|
end
|
||||||
if file.isReadOnly then
|
if file.isReadOnly then
|
||||||
file.flags = file.flags .. 'R'
|
file.flags = file.flags .. 'R'
|
||||||
end
|
end
|
||||||
if config.showHidden or file.name:sub(1, 1) ~= '.' then
|
if config.showHidden or file.name:sub(1, 1) ~= '.' then
|
||||||
dir.files[file.fullName] = file
|
dir.files[file.fullName] = file
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- self.grid:update()
|
-- self.grid:update()
|
||||||
-- self.grid:setIndex(dir.index)
|
-- self.grid:setIndex(dir.index)
|
||||||
self.grid:setValues(dir.files)
|
self.grid:setValues(dir.files)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser:setDir(dirName, noStatus)
|
function Browser:setDir(dirName, noStatus)
|
||||||
self:unmarkAll()
|
self:unmarkAll()
|
||||||
|
|
||||||
if self.dir then
|
if self.dir then
|
||||||
self.dir.index = self.grid:getIndex()
|
self.dir.index = self.grid:getIndex()
|
||||||
end
|
end
|
||||||
local DIR = fs.combine('', dirName)
|
local DIR = fs.combine('', dirName)
|
||||||
shell.setDir(DIR)
|
shell.setDir(DIR)
|
||||||
local s, dir = self:getDirectory(DIR)
|
local s, dir = self:getDirectory(DIR)
|
||||||
if s then
|
if s then
|
||||||
self.dir = dir
|
self.dir = dir
|
||||||
elseif noStatus then
|
elseif noStatus then
|
||||||
error(dir)
|
error(dir)
|
||||||
else
|
else
|
||||||
self:setStatus(dir)
|
self:setStatus(dir)
|
||||||
self:setDir('', true)
|
self:setDir('', true)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if not noStatus then
|
if not noStatus then
|
||||||
self.statusBar:setValue('status', '/' .. self.dir.name)
|
self.statusBar:setValue('status', '/' .. self.dir.name)
|
||||||
self.statusBar:draw()
|
self.statusBar:draw()
|
||||||
end
|
end
|
||||||
self.grid:setIndex(self.dir.index)
|
self.grid:setIndex(self.dir.index)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser:run(...)
|
function Browser:run(...)
|
||||||
if multishell then
|
if multishell then
|
||||||
local tabId = shell.openTab(...)
|
local tabId = shell.openTab(...)
|
||||||
multishell.setFocus(tabId)
|
multishell.setFocus(tabId)
|
||||||
else
|
else
|
||||||
shell.run(...)
|
shell.run(...)
|
||||||
Event.terminate = false
|
Event.terminate = false
|
||||||
self:draw()
|
self:draw()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser:hasMarked()
|
function Browser:hasMarked()
|
||||||
if Util.size(marked) == 0 then
|
if Util.size(marked) == 0 then
|
||||||
local file = self.grid:getSelected()
|
local file = self.grid:getSelected()
|
||||||
if file then
|
if file then
|
||||||
file.marked = true
|
file.marked = true
|
||||||
marked[file.fullName] = file
|
marked[file.fullName] = file
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return Util.size(marked) > 0
|
return Util.size(marked) > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser:eventHandler(event)
|
function Browser:eventHandler(event)
|
||||||
local file = self.grid:getSelected()
|
local file = self.grid:getSelected()
|
||||||
|
|
||||||
if event.type == 'quit' then
|
if event.type == 'quit' then
|
||||||
Event.exitPullEvents()
|
Event.exitPullEvents()
|
||||||
|
|
||||||
elseif event.type == 'edit' and file then
|
elseif event.type == 'edit' and file then
|
||||||
self:run('edit', file.name)
|
self:run('edit', file.name)
|
||||||
|
|
||||||
elseif event.type == 'cedit' and file then
|
elseif event.type == 'cedit' and file then
|
||||||
self:run('cedit', file.name)
|
self:run('cedit', file.name)
|
||||||
self:setStatus('Started cloud edit')
|
self:setStatus('Started cloud edit')
|
||||||
|
|
||||||
elseif event.type == 'shell' then
|
elseif event.type == 'shell' then
|
||||||
self:run('sys/apps/shell.lua')
|
self:run('sys/apps/shell.lua')
|
||||||
|
|
||||||
elseif event.type == 'refresh' then
|
elseif event.type == 'refresh' then
|
||||||
self:updateDirectory(self.dir)
|
self:updateDirectory(self.dir)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:setStatus('Refreshed')
|
self:setStatus('Refreshed')
|
||||||
|
|
||||||
elseif event.type == 'associate' then
|
elseif event.type == 'associate' then
|
||||||
self.associations:show()
|
self.associations:show()
|
||||||
|
|
||||||
elseif event.type == 'pastebin' then
|
elseif event.type == 'pastebin' then
|
||||||
if file and not file.isDir then
|
if file and not file.isDir then
|
||||||
local s, m = pastebin.put(file.fullName)
|
local s, m = pastebin.put(file.fullName)
|
||||||
if s then
|
if s then
|
||||||
os.queueEvent('clipboard_copy', s)
|
os.queueEvent('clipboard_copy', s)
|
||||||
self.notification:success(string.format('Uploaded as %s', s), 0)
|
self.notification:success(string.format('Uploaded as %s', s), 0)
|
||||||
else
|
else
|
||||||
self.notification:error(m)
|
self.notification:error(m)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'toggle_hidden' then
|
elseif event.type == 'toggle_hidden' then
|
||||||
config.showHidden = not config.showHidden
|
config.showHidden = not config.showHidden
|
||||||
Config.update('Files', config)
|
Config.update('Files', config)
|
||||||
|
|
||||||
self:updateDirectory(self.dir)
|
self:updateDirectory(self.dir)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
if not config.showHidden then
|
if not config.showHidden then
|
||||||
self:setStatus('Hiding hidden')
|
self:setStatus('Hiding hidden')
|
||||||
else
|
else
|
||||||
self:setStatus('Displaying hidden')
|
self:setStatus('Displaying hidden')
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'toggle_dirSize' then
|
elseif event.type == 'toggle_dirSize' then
|
||||||
config.showDirSizes = not config.showDirSizes
|
config.showDirSizes = not config.showDirSizes
|
||||||
Config.update('Files', config)
|
Config.update('Files', config)
|
||||||
|
|
||||||
self:updateDirectory(self.dir)
|
self:updateDirectory(self.dir)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
if config.showDirSizes then
|
if config.showDirSizes then
|
||||||
self:setStatus('Displaying dir sizes')
|
self:setStatus('Displaying dir sizes')
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'mark' and file then
|
elseif event.type == 'mark' and file then
|
||||||
file.marked = not file.marked
|
file.marked = not file.marked
|
||||||
if file.marked then
|
if file.marked then
|
||||||
marked[file.fullName] = file
|
marked[file.fullName] = file
|
||||||
else
|
else
|
||||||
marked[file.fullName] = nil
|
marked[file.fullName] = nil
|
||||||
end
|
end
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self.statusBar:draw()
|
self.statusBar:draw()
|
||||||
|
|
||||||
elseif event.type == 'unmark' then
|
elseif event.type == 'unmark' then
|
||||||
self:unmarkAll()
|
self:unmarkAll()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:setStatus('Marked files cleared')
|
self:setStatus('Marked files cleared')
|
||||||
|
|
||||||
elseif event.type == 'grid_select' or event.type == 'run' then
|
elseif event.type == 'grid_select' or event.type == 'run' then
|
||||||
if file then
|
if file then
|
||||||
if file.isDir then
|
if file.isDir then
|
||||||
self:setDir(file.fullName)
|
self:setDir(file.fullName)
|
||||||
else
|
else
|
||||||
local ext = file.name:match('%.(%w+)$')
|
local ext = file.name:match('%.(%w+)$')
|
||||||
if ext and config.associations[ext] then
|
if ext and config.associations[ext] then
|
||||||
self:run(config.associations[ext], '/' .. file.fullName)
|
self:run(config.associations[ext], '/' .. file.fullName)
|
||||||
else
|
else
|
||||||
self:run(file.name)
|
self:run(file.name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'updir' then
|
elseif event.type == 'updir' then
|
||||||
local dir = (self.dir.name:match("(.*/)"))
|
local dir = (self.dir.name:match("(.*/)"))
|
||||||
self:setDir(dir or '/')
|
self:setDir(dir or '/')
|
||||||
|
|
||||||
elseif event.type == 'delete' then
|
elseif event.type == 'delete' then
|
||||||
if self:hasMarked() then
|
if self:hasMarked() then
|
||||||
local width = self.statusBar:getColumnWidth('status')
|
local width = self.statusBar:getColumnWidth('status')
|
||||||
self.statusBar:setColumnWidth('status', UI.term.width)
|
self.statusBar:setColumnWidth('status', UI.term.width)
|
||||||
self.statusBar:setValue('status', 'Delete marked? (y/n)')
|
self.statusBar:setValue('status', 'Delete marked? (y/n)')
|
||||||
self.statusBar:draw()
|
self.statusBar:draw()
|
||||||
self.statusBar:sync()
|
self.statusBar:sync()
|
||||||
local _, ch = os.pullEvent('char')
|
local _, ch = os.pullEvent('char')
|
||||||
if ch == 'y' or ch == 'Y' then
|
if ch == 'y' or ch == 'Y' then
|
||||||
for _,m in pairs(marked) do
|
for _,m in pairs(marked) do
|
||||||
pcall(function()
|
pcall(function()
|
||||||
fs.delete(m.fullName)
|
fs.delete(m.fullName)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
marked = { }
|
marked = { }
|
||||||
self.statusBar:setColumnWidth('status', width)
|
self.statusBar:setColumnWidth('status', width)
|
||||||
self.statusBar:setValue('status', '/' .. self.dir.name)
|
self.statusBar:setValue('status', '/' .. self.dir.name)
|
||||||
self:updateDirectory(self.dir)
|
self:updateDirectory(self.dir)
|
||||||
|
|
||||||
self.statusBar:draw()
|
self.statusBar:draw()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:setFocus(self.grid)
|
self:setFocus(self.grid)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'copy' or event.type == 'cut' then
|
elseif event.type == 'copy' or event.type == 'cut' then
|
||||||
if self:hasMarked() then
|
if self:hasMarked() then
|
||||||
cutMode = event.type == 'cut'
|
cutMode = event.type == 'cut'
|
||||||
Util.clear(copied)
|
Util.clear(copied)
|
||||||
Util.merge(copied, marked)
|
Util.merge(copied, marked)
|
||||||
--self:unmarkAll()
|
--self:unmarkAll()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:setStatus('Copied %d file(s)', Util.size(copied))
|
self:setStatus('Copied %d file(s)', Util.size(copied))
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'copy_path' then
|
elseif event.type == 'copy_path' then
|
||||||
if file then
|
if file then
|
||||||
os.queueEvent('clipboard_copy', file.fullName)
|
os.queueEvent('clipboard_copy', file.fullName)
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'paste' then
|
elseif event.type == 'paste' then
|
||||||
for _,m in pairs(copied) do
|
for _,m in pairs(copied) do
|
||||||
local s, m = pcall(function()
|
local s, m = pcall(function()
|
||||||
if cutMode then
|
if cutMode then
|
||||||
fs.move(m.fullName, fs.combine(self.dir.name, m.name))
|
fs.move(m.fullName, fs.combine(self.dir.name, m.name))
|
||||||
else
|
else
|
||||||
fs.copy(m.fullName, fs.combine(self.dir.name, m.name))
|
fs.copy(m.fullName, fs.combine(self.dir.name, m.name))
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
self:updateDirectory(self.dir)
|
self:updateDirectory(self.dir)
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
self:setStatus('Pasted ' .. Util.size(copied) .. ' file(s)')
|
self:setStatus('Pasted ' .. Util.size(copied) .. ' file(s)')
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
self:setFocus(self.grid)
|
self:setFocus(self.grid)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[ Associations slide out ]] --
|
--[[ Associations slide out ]] --
|
||||||
function Browser.associations:show()
|
function Browser.associations:show()
|
||||||
self.grid.values = { }
|
self.grid.values = { }
|
||||||
for k, v in pairs(config.associations) do
|
for k, v in pairs(config.associations) do
|
||||||
table.insert(self.grid.values, {
|
table.insert(self.grid.values, {
|
||||||
name = k,
|
name = k,
|
||||||
value = v,
|
value = v,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
UI.SlideOut.show(self)
|
UI.SlideOut.show(self)
|
||||||
self:setFocus(self.form[1])
|
self:setFocus(self.form[1])
|
||||||
end
|
end
|
||||||
|
|
||||||
function Browser.associations:eventHandler(event)
|
function Browser.associations:eventHandler(event)
|
||||||
if event.type == 'remove_entry' then
|
if event.type == 'remove_entry' then
|
||||||
local row = self.grid:getSelected()
|
local row = self.grid:getSelected()
|
||||||
if row then
|
if row then
|
||||||
Util.removeByValue(self.grid.values, row)
|
Util.removeByValue(self.grid.values, row)
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'add_association' then
|
elseif event.type == 'add_association' then
|
||||||
if self.form:save() then
|
if self.form:save() then
|
||||||
local entry = Util.find(self.grid.values, 'name', self.form[1].value) or { }
|
local entry = Util.find(self.grid.values, 'name', self.form[1].value) or { }
|
||||||
entry.name = self.form[1].value
|
entry.name = self.form[1].value
|
||||||
entry.value = self.form[2].value
|
entry.value = self.form[2].value
|
||||||
table.insert(self.grid.values, entry)
|
table.insert(self.grid.values, entry)
|
||||||
self.form[1]:reset()
|
self.form[1]:reset()
|
||||||
self.form[2]:reset()
|
self.form[2]:reset()
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'cancel' then
|
elseif event.type == 'cancel' then
|
||||||
self:hide()
|
self:hide()
|
||||||
|
|
||||||
elseif event.type == 'save' then
|
elseif event.type == 'save' then
|
||||||
config.associations = { }
|
config.associations = { }
|
||||||
for _, v in pairs(self.grid.values) do
|
for _, v in pairs(self.grid.values) do
|
||||||
config.associations[v.name] = v.value
|
config.associations[v.name] = v.value
|
||||||
end
|
end
|
||||||
Config.update('Files', config)
|
Config.update('Files', config)
|
||||||
self:hide()
|
self:hide()
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.SlideOut.eventHandler(self, event)
|
return UI.SlideOut.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[-- Startup logic --]]--
|
--[[-- Startup logic --]]--
|
||||||
|
@ -294,7 +294,6 @@ end
|
|||||||
function page:rawExecute(s)
|
function page:rawExecute(s)
|
||||||
local fn, m
|
local fn, m
|
||||||
local wrapped
|
local wrapped
|
||||||
local t = os.clock()
|
|
||||||
|
|
||||||
fn = load('return (' ..s.. ')', 'lua', nil, sandboxEnv)
|
fn = load('return (' ..s.. ')', 'lua', nil, sandboxEnv)
|
||||||
|
|
||||||
@ -303,6 +302,7 @@ function page:rawExecute(s)
|
|||||||
wrapped = true
|
wrapped = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local t = os.clock()
|
||||||
if fn then
|
if fn then
|
||||||
fn, m = pcall(fn)
|
fn, m = pcall(fn)
|
||||||
if #m <= 1 and wrapped then
|
if #m <= 1 and wrapped then
|
||||||
@ -311,19 +311,24 @@ function page:rawExecute(s)
|
|||||||
else
|
else
|
||||||
fn, m = load(s, 'lua', nil, sandboxEnv)
|
fn, m = load(s, 'lua', nil, sandboxEnv)
|
||||||
if fn then
|
if fn then
|
||||||
|
t = os.clock()
|
||||||
fn, m = pcall(fn)
|
fn, m = pcall(fn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if fn then
|
if fn then
|
||||||
|
t = os.clock() - t
|
||||||
|
|
||||||
|
local bg, fg = term.getBackgroundColor(), term.getTextColor()
|
||||||
|
term.setTextColor(colors.cyan)
|
||||||
|
term.setBackgroundColor(colors.black)
|
||||||
|
term.write(string.format('out [%.2f]: ', t))
|
||||||
|
term.setBackgroundColor(bg)
|
||||||
|
term.setTextColor(fg)
|
||||||
if m or wrapped then
|
if m or wrapped then
|
||||||
local bg, fg = term.getBackgroundColor(), term.getTextColor()
|
|
||||||
term.setTextColor(colors.cyan)
|
|
||||||
term.setBackgroundColor(colors.black)
|
|
||||||
term.write(string.format('out [%.2f]: ', os.clock() - t))
|
|
||||||
term.setBackgroundColor(bg)
|
|
||||||
term.setTextColor(fg)
|
|
||||||
Util.print(m or 'nil')
|
Util.print(m or 'nil')
|
||||||
|
else
|
||||||
|
print()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
_G.printError(m)
|
_G.printError(m)
|
||||||
|
@ -127,12 +127,14 @@ local function sendCommand(host, command)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
function page.ports:eventHandler(event)
|
function page.ports:eventHandler(event)
|
||||||
if event.type == 'grid_select' then
|
if event.type == 'grid_select' then
|
||||||
shell.openForegroundTab('sniff ' .. event.selected.port)
|
shell.openForegroundTab('sniff ' .. event.selected.port)
|
||||||
end
|
end
|
||||||
return UI.SlideOut.eventHandler(self, event)
|
return UI.SlideOut.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
]]
|
||||||
|
|
||||||
function page.ports.grid:update()
|
function page.ports.grid:update()
|
||||||
local function findConnection(port)
|
local function findConnection(port)
|
||||||
|
@ -5,23 +5,23 @@ local shell = _ENV.shell
|
|||||||
local launcherTab = kernel.getCurrent()
|
local launcherTab = kernel.getCurrent()
|
||||||
|
|
||||||
kernel.hook('kernel_focus', function(_, eventData)
|
kernel.hook('kernel_focus', function(_, eventData)
|
||||||
local focusTab = eventData and eventData[1]
|
local focusTab = eventData and eventData[1]
|
||||||
if focusTab == launcherTab.uid then
|
if focusTab == launcherTab.uid then
|
||||||
local previousTab = eventData[2]
|
local previousTab = eventData[2]
|
||||||
local nextTab = launcherTab
|
local nextTab = launcherTab
|
||||||
if not previousTab then
|
if not previousTab then
|
||||||
for _, v in pairs(kernel.routines) do
|
for _, v in pairs(kernel.routines) do
|
||||||
if not v.hidden and v.uid > nextTab.uid then
|
if not v.hidden and v.uid > nextTab.uid then
|
||||||
nextTab = v
|
nextTab = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if nextTab == launcherTab then
|
if nextTab == launcherTab then
|
||||||
shell.switchTab(shell.openTab('sys/apps/shell.lua'))
|
shell.switchTab(shell.openTab('sys/apps/shell.lua'))
|
||||||
else
|
else
|
||||||
shell.switchTab(nextTab.uid)
|
shell.switchTab(nextTab.uid)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
while os.pullEventRaw() do end
|
os.pullEventRaw('kernel_halt')
|
@ -23,115 +23,115 @@ local packagesIntro = [[Setup Complete
|
|||||||
|
|
||||||
local page = UI.Page {
|
local page = UI.Page {
|
||||||
wizard = UI.Wizard {
|
wizard = UI.Wizard {
|
||||||
ey = -2,
|
ey = -2,
|
||||||
pages = {
|
pages = {
|
||||||
splash = UI.WizardPage {
|
splash = UI.WizardPage {
|
||||||
index = 1,
|
index = 1,
|
||||||
intro = UI.TextArea {
|
intro = UI.TextArea {
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
inactive = true,
|
inactive = true,
|
||||||
x = 3, ex = -3, y = 2, ey = -2,
|
x = 3, ex = -3, y = 2, ey = -2,
|
||||||
value = string.format(splashIntro, Ansi.white),
|
value = string.format(splashIntro, Ansi.white),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
label = UI.WizardPage {
|
label = UI.WizardPage {
|
||||||
index = 2,
|
index = 2,
|
||||||
labelText = UI.Text {
|
labelText = UI.Text {
|
||||||
x = 3, y = 2,
|
x = 3, y = 2,
|
||||||
value = 'Label'
|
value = 'Label'
|
||||||
},
|
},
|
||||||
label = UI.TextEntry {
|
label = UI.TextEntry {
|
||||||
x = 9, y = 2, ex = -3,
|
x = 9, y = 2, ex = -3,
|
||||||
limit = 32,
|
limit = 32,
|
||||||
value = os.getComputerLabel(),
|
value = os.getComputerLabel(),
|
||||||
},
|
},
|
||||||
intro = UI.TextArea {
|
intro = UI.TextArea {
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
inactive = true,
|
inactive = true,
|
||||||
x = 3, ex = -3, y = 4, ey = -3,
|
x = 3, ex = -3, y = 4, ey = -3,
|
||||||
value = string.format(labelIntro, Ansi.white),
|
value = string.format(labelIntro, Ansi.white),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
password = UI.WizardPage {
|
password = UI.WizardPage {
|
||||||
index = 3,
|
index = 3,
|
||||||
passwordLabel = UI.Text {
|
passwordLabel = UI.Text {
|
||||||
x = 3, y = 2,
|
x = 3, y = 2,
|
||||||
value = 'Password'
|
value = 'Password'
|
||||||
},
|
},
|
||||||
newPass = UI.TextEntry {
|
newPass = UI.TextEntry {
|
||||||
x = 12, ex = -3, y = 2,
|
x = 12, ex = -3, y = 2,
|
||||||
limit = 32,
|
limit = 32,
|
||||||
mask = true,
|
mask = true,
|
||||||
shadowText = 'password',
|
shadowText = 'password',
|
||||||
},
|
},
|
||||||
--[[
|
--[[
|
||||||
groupLabel = UI.Text {
|
groupLabel = UI.Text {
|
||||||
x = 3, y = 3,
|
x = 3, y = 3,
|
||||||
value = 'Group'
|
value = 'Group'
|
||||||
},
|
},
|
||||||
group = UI.TextEntry {
|
group = UI.TextEntry {
|
||||||
x = 12, ex = -3, y = 3,
|
x = 12, ex = -3, y = 3,
|
||||||
limit = 32,
|
limit = 32,
|
||||||
shadowText = 'network group',
|
shadowText = 'network group',
|
||||||
},
|
},
|
||||||
]]
|
]]
|
||||||
intro = UI.TextArea {
|
intro = UI.TextArea {
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
inactive = true,
|
inactive = true,
|
||||||
x = 3, ex = -3, y = 5, ey = -3,
|
x = 3, ex = -3, y = 5, ey = -3,
|
||||||
value = string.format(passwordIntro, Ansi.white),
|
value = string.format(passwordIntro, Ansi.white),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
packages = UI.WizardPage {
|
packages = UI.WizardPage {
|
||||||
index = 4,
|
index = 4,
|
||||||
button = UI.Button {
|
button = UI.Button {
|
||||||
x = 3, y = -3,
|
x = 3, y = -3,
|
||||||
text = 'Open Package Manager',
|
text = 'Open Package Manager',
|
||||||
event = 'packages',
|
event = 'packages',
|
||||||
},
|
},
|
||||||
intro = UI.TextArea {
|
intro = UI.TextArea {
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
inactive = true,
|
inactive = true,
|
||||||
x = 3, ex = -3, y = 2, ey = -4,
|
x = 3, ex = -3, y = 2, ey = -4,
|
||||||
value = string.format(packagesIntro, Ansi.white),
|
value = string.format(packagesIntro, Ansi.white),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
notification = UI.Notification { },
|
notification = UI.Notification { },
|
||||||
}
|
}
|
||||||
|
|
||||||
function page.wizard.pages.label:validate()
|
function page.wizard.pages.label:validate()
|
||||||
os.setComputerLabel(self.label.value)
|
os.setComputerLabel(self.label.value)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function page.wizard.pages.password:validate()
|
function page.wizard.pages.password:validate()
|
||||||
if #self.newPass.value > 0 then
|
if #self.newPass.value > 0 then
|
||||||
Security.updatePassword(SHA1.sha1(self.newPass.value))
|
Security.updatePassword(SHA1.sha1(self.newPass.value))
|
||||||
end
|
end
|
||||||
--[[
|
--[[
|
||||||
if #self.group.value > 0 then
|
if #self.group.value > 0 then
|
||||||
local config = Config.load('os')
|
local config = Config.load('os')
|
||||||
config.group = self.group.value
|
config.group = self.group.value
|
||||||
Config.update('os', config)
|
Config.update('os', config)
|
||||||
end
|
end
|
||||||
]]
|
]]
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function page:eventHandler(event)
|
function page:eventHandler(event)
|
||||||
if event.type == 'skip' then
|
if event.type == 'skip' then
|
||||||
self.wizard:emit({ type = 'nextView' })
|
self.wizard:emit({ type = 'nextView' })
|
||||||
|
|
||||||
elseif event.type == 'view_enabled' then
|
elseif event.type == 'view_enabled' then
|
||||||
event.view:focusFirst()
|
event.view:focusFirst()
|
||||||
|
|
||||||
elseif event.type == 'packages' then
|
elseif event.type == 'packages' then
|
||||||
shell.openForegroundTab('PackageManager')
|
shell.openForegroundTab('PackageManager')
|
||||||
|
|
||||||
elseif event.type == 'wizard_complete' or event.type == 'cancel' then
|
elseif event.type == 'wizard_complete' or event.type == 'cancel' then
|
||||||
UI.exitPullEvents()
|
UI.exitPullEvents()
|
||||||
|
|
||||||
else
|
else
|
||||||
return UI.Page.eventHandler(self, event)
|
return UI.Page.eventHandler(self, event)
|
||||||
|
@ -11,60 +11,60 @@ local term = _G.term
|
|||||||
local success = true
|
local success = true
|
||||||
|
|
||||||
local function runDir(directory)
|
local function runDir(directory)
|
||||||
if not fs.exists(directory) then
|
if not fs.exists(directory) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local files = fs.list(directory)
|
local files = fs.list(directory)
|
||||||
table.sort(files)
|
table.sort(files)
|
||||||
|
|
||||||
for _,file in ipairs(files) do
|
for _,file in ipairs(files) do
|
||||||
os.sleep(0)
|
os.sleep(0)
|
||||||
local result, err = shell.run(directory .. '/' .. file)
|
local result, err = shell.run(directory .. '/' .. file)
|
||||||
|
|
||||||
if result then
|
if result then
|
||||||
if term.isColor() then
|
if term.isColor() then
|
||||||
term.setTextColor(colors.green)
|
term.setTextColor(colors.green)
|
||||||
end
|
end
|
||||||
term.write('[PASS] ')
|
term.write('[PASS] ')
|
||||||
term.setTextColor(colors.white)
|
term.setTextColor(colors.white)
|
||||||
term.write(fs.combine(directory, file))
|
term.write(fs.combine(directory, file))
|
||||||
print()
|
print()
|
||||||
else
|
else
|
||||||
if term.isColor() then
|
if term.isColor() then
|
||||||
term.setTextColor(colors.red)
|
term.setTextColor(colors.red)
|
||||||
end
|
end
|
||||||
term.write('[FAIL] ')
|
term.write('[FAIL] ')
|
||||||
term.setTextColor(colors.white)
|
term.setTextColor(colors.white)
|
||||||
term.write(fs.combine(directory, file))
|
term.write(fs.combine(directory, file))
|
||||||
if err then
|
if err then
|
||||||
_G.printError('\n' .. err)
|
_G.printError('\n' .. err)
|
||||||
end
|
end
|
||||||
print()
|
print()
|
||||||
success = false
|
success = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
runDir('sys/autorun')
|
runDir('sys/autorun')
|
||||||
for name in pairs(Packages:installed()) do
|
for name in pairs(Packages:installed()) do
|
||||||
local packageDir = 'packages/' .. name .. '/autorun'
|
local packageDir = 'packages/' .. name .. '/autorun'
|
||||||
runDir(packageDir)
|
runDir(packageDir)
|
||||||
end
|
end
|
||||||
runDir('usr/autorun')
|
runDir('usr/autorun')
|
||||||
|
|
||||||
if not success then
|
if not success then
|
||||||
if multishell then
|
if multishell then
|
||||||
multishell.setFocus(multishell.getCurrent())
|
multishell.setFocus(multishell.getCurrent())
|
||||||
end
|
end
|
||||||
_G.printError('A startup program has errored')
|
_G.printError('A startup program has errored')
|
||||||
print('Press enter to continue')
|
print('Press enter to continue')
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local e, code = os.pullEventRaw('key')
|
local e, code = os.pullEventRaw('key')
|
||||||
if e == 'terminate' or e == 'key' and code == keys.enter then
|
if e == 'terminate' or e == 'key' and code == keys.enter then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -7,32 +7,32 @@ local shell = _ENV.shell
|
|||||||
|
|
||||||
local args = { ... }
|
local args = { ... }
|
||||||
if not args[1] then
|
if not args[1] then
|
||||||
error('Syntax: cedit <filename>')
|
error('Syntax: cedit <filename>')
|
||||||
end
|
end
|
||||||
|
|
||||||
if not _G.http.websocket then
|
if not _G.http.websocket then
|
||||||
error('Requires CC: Tweaked')
|
error('Requires CC: Tweaked')
|
||||||
end
|
end
|
||||||
|
|
||||||
if not _G.cloud_catcher then
|
if not _G.cloud_catcher then
|
||||||
local key = Config.load('cloud').key
|
local key = Config.load('cloud').key
|
||||||
|
|
||||||
if not key then
|
if not key then
|
||||||
print('Visit https://cloud-catcher.squiddev.cc')
|
print('Visit https://cloud-catcher.squiddev.cc')
|
||||||
print('Paste key: ')
|
print('Paste key: ')
|
||||||
key = read()
|
key = read()
|
||||||
if #key == 0 then
|
if #key == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- open an unfocused tab
|
-- open an unfocused tab
|
||||||
local id = shell.openTab('cloud ' .. key)
|
local id = shell.openTab('cloud ' .. key)
|
||||||
print('Connecting...')
|
print('Connecting...')
|
||||||
while not _G.cloud_catcher do
|
while not _G.cloud_catcher do
|
||||||
os.sleep(.2)
|
os.sleep(.2)
|
||||||
end
|
end
|
||||||
multishell.setTitle(id, 'Cloud')
|
multishell.setTitle(id, 'Cloud')
|
||||||
end
|
end
|
||||||
|
|
||||||
shell.run('cloud edit ' .. table.unpack({ ... }))
|
shell.run('cloud edit ' .. table.unpack({ ... }))
|
||||||
|
@ -4,20 +4,20 @@ local read = _G.read
|
|||||||
local shell = _ENV.shell
|
local shell = _ENV.shell
|
||||||
|
|
||||||
if not _G.http.websocket then
|
if not _G.http.websocket then
|
||||||
error('Requires CC: Tweaked')
|
error('Requires CC: Tweaked')
|
||||||
end
|
end
|
||||||
|
|
||||||
if not _G.cloud_catcher then
|
if not _G.cloud_catcher then
|
||||||
local key = Config.load('cloud').key
|
local key = Config.load('cloud').key
|
||||||
|
|
||||||
if not key then
|
if not key then
|
||||||
print('Visit https://cloud-catcher.squiddev.cc')
|
print('Visit https://cloud-catcher.squiddev.cc')
|
||||||
print('Paste key: ')
|
print('Paste key: ')
|
||||||
key = read()
|
key = read()
|
||||||
if #key == 0 then
|
if #key == 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
print('Connecting...')
|
print('Connecting...')
|
||||||
shell.run('cloud ' .. key)
|
shell.run('cloud ' .. key)
|
||||||
end
|
end
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
local Event = require('event')
|
|
||||||
local Util = require('util')
|
|
||||||
|
|
||||||
local fs = _G.fs
|
|
||||||
local modem = _G.device.wireless_modem
|
|
||||||
local os = _G.os
|
|
||||||
|
|
||||||
local computerId = os.getComputerID()
|
|
||||||
|
|
||||||
--modem.open(80)
|
|
||||||
|
|
||||||
-- https://github.com/golgote/neturl/blob/master/lib/net/url.lua
|
|
||||||
local function parseQuery(str)
|
|
||||||
local sep = '&'
|
|
||||||
|
|
||||||
local values = {}
|
|
||||||
for key,val in str:gmatch(string.format('([^%q=]+)(=*[^%q=]*)', sep, sep)) do
|
|
||||||
--local key = decode(key)
|
|
||||||
local keys = {}
|
|
||||||
key = key:gsub('%[([^%]]*)%]', function(v)
|
|
||||||
-- extract keys between balanced brackets
|
|
||||||
if string.find(v, "^-?%d+$") then
|
|
||||||
v = tonumber(v)
|
|
||||||
--else
|
|
||||||
--v = decode(v)
|
|
||||||
end
|
|
||||||
table.insert(keys, v)
|
|
||||||
return "="
|
|
||||||
end)
|
|
||||||
key = key:gsub('=+.*$', "")
|
|
||||||
key = key:gsub('%s', "_") -- remove spaces in parameter name
|
|
||||||
val = val:gsub('^=+', "")
|
|
||||||
|
|
||||||
if not values[key] then
|
|
||||||
values[key] = {}
|
|
||||||
end
|
|
||||||
if #keys > 0 and type(values[key]) ~= 'table' then
|
|
||||||
values[key] = {}
|
|
||||||
elseif #keys == 0 and type(values[key]) == 'table' then
|
|
||||||
values[key] = val --decode(val)
|
|
||||||
end
|
|
||||||
|
|
||||||
local t = values[key]
|
|
||||||
for i,k in ipairs(keys) do
|
|
||||||
if type(t) ~= 'table' then
|
|
||||||
t = {}
|
|
||||||
end
|
|
||||||
if k == "" then
|
|
||||||
k = #t+1
|
|
||||||
end
|
|
||||||
if not t[k] then
|
|
||||||
t[k] = {}
|
|
||||||
end
|
|
||||||
if i == #keys then
|
|
||||||
t[k] = val --decode(val)
|
|
||||||
end
|
|
||||||
t = t[k]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return values
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getListing(path, recursive)
|
|
||||||
local list = { }
|
|
||||||
local function listing(p)
|
|
||||||
for _, f in pairs(fs.listEx(p)) do
|
|
||||||
local abs = fs.combine(p, f.name)
|
|
||||||
table.insert(list, {
|
|
||||||
isDir = f.isDir,
|
|
||||||
path = string.sub(abs, #path + 1),
|
|
||||||
size = f.size,
|
|
||||||
})
|
|
||||||
if recursive and f.isDir then
|
|
||||||
listing(abs)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
listing(path)
|
|
||||||
return list
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
Event.on('modem_message', function(_, _, dport, dhost, request)
|
|
||||||
if dport == 80 and dhost == computerId and type(request) == 'table' then
|
|
||||||
if request.method == 'GET' then
|
|
||||||
local query
|
|
||||||
if not request.path or type(request.path) ~= 'string' then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local path = request.path:gsub('%?(.*)', function(v)
|
|
||||||
query = parseQuery(v)
|
|
||||||
return ''
|
|
||||||
end)
|
|
||||||
if fs.isDir(path) then
|
|
||||||
-- TODO: more validation
|
|
||||||
modem.transmit(request.replyPort, request.replyAddress, {
|
|
||||||
statusCode = 200,
|
|
||||||
contentType = 'table/directory',
|
|
||||||
data = getListing(path, query and query.recursive == 'true'),
|
|
||||||
})
|
|
||||||
elseif fs.exists(path) then
|
|
||||||
modem.transmit(request.replyPort, request.replyAddress, {
|
|
||||||
statusCode = 200,
|
|
||||||
contentType = 'table/file',
|
|
||||||
data = Util.readFile(path),
|
|
||||||
})
|
|
||||||
else
|
|
||||||
modem.transmit(request.replyPort, request.replyAddress, {
|
|
||||||
statusCode = 404,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
]]
|
|
@ -156,7 +156,7 @@ local function sendInfo()
|
|||||||
info.label = os.getComputerLabel()
|
info.label = os.getComputerLabel()
|
||||||
info.uptime = math.floor(os.clock())
|
info.uptime = math.floor(os.clock())
|
||||||
info.group = network.getGroup()
|
info.group = network.getGroup()
|
||||||
if turtle then
|
if turtle and turtle.getStatus then
|
||||||
info.fuel = turtle.getFuelLevel()
|
info.fuel = turtle.getFuelLevel()
|
||||||
info.status = turtle.getStatus()
|
info.status = turtle.getStatus()
|
||||||
info.point = turtle.point
|
info.point = turtle.point
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
local Event = require('event')
|
|
||||||
local Terminal = require('terminal')
|
|
||||||
local Util = require('util')
|
|
||||||
|
|
||||||
local colors = _G.colors
|
|
||||||
local modem = _G.device.wireless_modem
|
|
||||||
local term = _G.term
|
|
||||||
local textutils = _G.textutils
|
|
||||||
|
|
||||||
local terminal = Terminal.window(term.current())
|
|
||||||
terminal.setMaxScroll(300)
|
|
||||||
local oldTerm = term.redirect(terminal)
|
|
||||||
|
|
||||||
local function syntax()
|
|
||||||
error('Syntax: sniff [port]')
|
|
||||||
end
|
|
||||||
|
|
||||||
local port = ({ ... })[1] or syntax()
|
|
||||||
port = tonumber(port) or syntax()
|
|
||||||
|
|
||||||
Event.on('modem_message',
|
|
||||||
function(_, _, dport, _, data, _)
|
|
||||||
if dport == port then
|
|
||||||
terminal.scrollBottom()
|
|
||||||
terminal.setTextColor(colors.white)
|
|
||||||
print(textutils.serialize(data))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
Event.on('mouse_scroll', function(_, direction)
|
|
||||||
if direction == -1 then
|
|
||||||
terminal.scrollUp()
|
|
||||||
else
|
|
||||||
terminal.scrollDown()
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
local function sniffer(_, _, data)
|
|
||||||
terminal.scrollBottom()
|
|
||||||
terminal.setTextColor(colors.yellow)
|
|
||||||
local ot = term.redirect(terminal)
|
|
||||||
print(textutils.serialize(data))
|
|
||||||
term.redirect(ot)
|
|
||||||
end
|
|
||||||
|
|
||||||
local socket = _G.transport.sockets[port]
|
|
||||||
if socket then
|
|
||||||
if not socket.sniffers then
|
|
||||||
socket.sniffers = { modem.transmit }
|
|
||||||
socket.transmit = function(...)
|
|
||||||
for _,v in pairs(socket.sniffers) do
|
|
||||||
v(...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(socket.sniffers, sniffer)
|
|
||||||
end
|
|
||||||
|
|
||||||
local s, m = pcall(Event.pullEvents)
|
|
||||||
|
|
||||||
if socket then
|
|
||||||
Util.removeByValue(socket.sniffers, sniffer)
|
|
||||||
end
|
|
||||||
|
|
||||||
term.redirect(oldTerm)
|
|
||||||
|
|
||||||
if not s and m then
|
|
||||||
error(m)
|
|
||||||
end
|
|
@ -7,51 +7,51 @@ local colors = _G.colors
|
|||||||
-- -t80x30
|
-- -t80x30
|
||||||
|
|
||||||
if _G.http.websocket then
|
if _G.http.websocket then
|
||||||
local config = Config.load('cloud')
|
local config = Config.load('cloud')
|
||||||
|
|
||||||
local tab = UI.Tab {
|
local tab = UI.Tab {
|
||||||
tabTitle = 'Cloud',
|
tabTitle = 'Cloud',
|
||||||
description = 'Cloud catcher options',
|
description = 'Cloud catcher options',
|
||||||
key = UI.TextEntry {
|
key = UI.TextEntry {
|
||||||
x = 3, ex = -3, y = 2,
|
x = 3, ex = -3, y = 2,
|
||||||
limit = 32,
|
limit = 32,
|
||||||
value = config.key,
|
value = config.key,
|
||||||
shadowText = 'Cloud key',
|
shadowText = 'Cloud key',
|
||||||
accelerators = {
|
accelerators = {
|
||||||
enter = 'update_key',
|
enter = 'update_key',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
button = UI.Button {
|
button = UI.Button {
|
||||||
x = 3, y = 4,
|
x = 3, y = 4,
|
||||||
text = 'Update',
|
text = 'Update',
|
||||||
event = 'update_key',
|
event = 'update_key',
|
||||||
},
|
},
|
||||||
labelText = UI.TextArea {
|
labelText = UI.TextArea {
|
||||||
x = 3, ex = -3, y = 6,
|
x = 3, ex = -3, y = 6,
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
marginLeft = 0, marginRight = 0,
|
marginLeft = 0, marginRight = 0,
|
||||||
value = string.format(
|
value = string.format(
|
||||||
[[Use a non-changing cloud key. Note that only a single computer can use this session at one time.
|
[[Use a non-changing cloud key. Note that only a single computer can use this session at one time.
|
||||||
To obtain a key, visit:
|
To obtain a key, visit:
|
||||||
%shttps://cloud-catcher.squiddev.cc%s then bookmark:
|
%shttps://cloud-catcher.squiddev.cc%s then bookmark:
|
||||||
%shttps://cloud-catcher.squiddev.cc/?id=KEY
|
%shttps://cloud-catcher.squiddev.cc/?id=KEY
|
||||||
]],
|
]],
|
||||||
Ansi.white, Ansi.reset, Ansi.white),
|
Ansi.white, Ansi.reset, Ansi.white),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function tab:eventHandler(event)
|
function tab:eventHandler(event)
|
||||||
if event.type == 'update_key' then
|
if event.type == 'update_key' then
|
||||||
if #self.key.value > 0 then
|
if #self.key.value > 0 then
|
||||||
config.key = self.key.value
|
config.key = self.key.value
|
||||||
else
|
else
|
||||||
config.key = nil
|
config.key = nil
|
||||||
end
|
end
|
||||||
Config.update('cloud', config)
|
Config.update('cloud', config)
|
||||||
self:emit({ type = 'success_message', message = 'Updated' })
|
self:emit({ type = 'success_message', message = 'Updated' })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return tab
|
return tab
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -14,13 +14,13 @@ local tab = UI.Tab {
|
|||||||
formLabel = 'Monitor', formKey = 'monitor',
|
formLabel = 'Monitor', formKey = 'monitor',
|
||||||
},
|
},
|
||||||
textScale = UI.Chooser {
|
textScale = UI.Chooser {
|
||||||
formLabel = 'Font Size', formKey = 'textScale',
|
formLabel = 'Font Size', formKey = 'textScale',
|
||||||
nochoice = 'Small',
|
nochoice = 'Small',
|
||||||
choices = {
|
choices = {
|
||||||
{ name = 'Small', value = '.5' },
|
{ name = 'Small', value = '.5' },
|
||||||
{ name = 'Large', value = '1' },
|
{ name = 'Large', value = '1' },
|
||||||
},
|
},
|
||||||
help = 'Adjust text scaling',
|
help = 'Adjust text scaling',
|
||||||
},
|
},
|
||||||
labelText = UI.TextArea {
|
labelText = UI.TextArea {
|
||||||
x = 2, ex = -2, y = 5,
|
x = 2, ex = -2, y = 5,
|
||||||
|
@ -9,76 +9,76 @@ local config = Config.load('multishell')
|
|||||||
local tab = UI.Tab {
|
local tab = UI.Tab {
|
||||||
tabTitle = 'Launcher',
|
tabTitle = 'Launcher',
|
||||||
description = 'Set the application launcher',
|
description = 'Set the application launcher',
|
||||||
launcherLabel = UI.Text {
|
launcherLabel = UI.Text {
|
||||||
x = 3, y = 2,
|
x = 3, y = 2,
|
||||||
value = 'Launcher',
|
value = 'Launcher',
|
||||||
},
|
},
|
||||||
launcher = UI.Chooser {
|
launcher = UI.Chooser {
|
||||||
x = 13, y = 2, width = 12,
|
x = 13, y = 2, width = 12,
|
||||||
choices = {
|
choices = {
|
||||||
{ name = 'Overview', value = 'sys/apps/Overview.lua' },
|
{ name = 'Overview', value = 'sys/apps/Overview.lua' },
|
||||||
{ name = 'Shell', value = 'sys/apps/ShellLauncher.lua' },
|
{ name = 'Shell', value = 'sys/apps/ShellLauncher.lua' },
|
||||||
{ name = 'Custom', value = 'custom' },
|
{ name = 'Custom', value = 'custom' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
custom = UI.TextEntry {
|
custom = UI.TextEntry {
|
||||||
x = 13, ex = -3, y = 3,
|
x = 13, ex = -3, y = 3,
|
||||||
limit = 128,
|
limit = 128,
|
||||||
shadowText = 'File name',
|
shadowText = 'File name',
|
||||||
},
|
},
|
||||||
button = UI.Button {
|
button = UI.Button {
|
||||||
x = 3, y = 5,
|
x = 3, y = 5,
|
||||||
text = 'Update',
|
text = 'Update',
|
||||||
event = 'update',
|
event = 'update',
|
||||||
},
|
},
|
||||||
labelText = UI.TextArea {
|
labelText = UI.TextArea {
|
||||||
x = 3, ex = -3, y = 7,
|
x = 3, ex = -3, y = 7,
|
||||||
textColor = colors.yellow,
|
textColor = colors.yellow,
|
||||||
value = 'Choose an application launcher',
|
value = 'Choose an application launcher',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function tab:enable()
|
function tab:enable()
|
||||||
local launcher = config.launcher and 'custom' or 'sys/apps/Overview.lua'
|
local launcher = config.launcher and 'custom' or 'sys/apps/Overview.lua'
|
||||||
|
|
||||||
for _, v in pairs(self.launcher.choices) do
|
for _, v in pairs(self.launcher.choices) do
|
||||||
if v.value == config.launcher then
|
if v.value == config.launcher then
|
||||||
launcher = v.value
|
launcher = v.value
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
UI.Tab.enable(self)
|
UI.Tab.enable(self)
|
||||||
|
|
||||||
self.launcher.value = launcher
|
self.launcher.value = launcher
|
||||||
self.custom.enabled = launcher == 'custom'
|
self.custom.enabled = launcher == 'custom'
|
||||||
end
|
end
|
||||||
|
|
||||||
function tab:eventHandler(event)
|
function tab:eventHandler(event)
|
||||||
if event.type == 'choice_change' then
|
if event.type == 'choice_change' then
|
||||||
self.custom.enabled = event.value == 'custom'
|
self.custom.enabled = event.value == 'custom'
|
||||||
if self.custom.enabled then
|
if self.custom.enabled then
|
||||||
self.custom.value = config.launcher
|
self.custom.value = config.launcher
|
||||||
end
|
end
|
||||||
self:draw()
|
self:draw()
|
||||||
|
|
||||||
elseif event.type == 'update' then
|
elseif event.type == 'update' then
|
||||||
local launcher
|
local launcher
|
||||||
|
|
||||||
if self.launcher.value ~= 'custom' then
|
if self.launcher.value ~= 'custom' then
|
||||||
launcher = self.launcher.value
|
launcher = self.launcher.value
|
||||||
elseif fs.exists(self.custom.value) and not fs.isDir(self.custom.value) then
|
elseif fs.exists(self.custom.value) and not fs.isDir(self.custom.value) then
|
||||||
launcher = self.custom.value
|
launcher = self.custom.value
|
||||||
end
|
end
|
||||||
|
|
||||||
if launcher then
|
if launcher then
|
||||||
config.launcher = launcher
|
config.launcher = launcher
|
||||||
Config.update('multishell', config)
|
Config.update('multishell', config)
|
||||||
self:emit({ type = 'success_message', message = 'Updated' })
|
self:emit({ type = 'success_message', message = 'Updated' })
|
||||||
else
|
else
|
||||||
self:emit({ type = 'error_message', message = 'Invalid file' })
|
self:emit({ type = 'error_message', message = 'Invalid file' })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return tab
|
return tab
|
||||||
|
@ -13,91 +13,91 @@ local tab = UI.Tab {
|
|||||||
accelerators = {
|
accelerators = {
|
||||||
enter = 'update_path',
|
enter = 'update_path',
|
||||||
},
|
},
|
||||||
help = 'add a new path',
|
help = 'add a new path',
|
||||||
},
|
},
|
||||||
grid = UI.Grid {
|
grid = UI.Grid {
|
||||||
y = 4, ey = -3,
|
y = 4, ey = -3,
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
columns = { { key = 'value' } },
|
columns = { { key = 'value' } },
|
||||||
autospace = true,
|
autospace = true,
|
||||||
sortColumn = 'index',
|
sortColumn = 'index',
|
||||||
help = 'double-click to remove, shift-arrow to move',
|
help = 'double-click to remove, shift-arrow to move',
|
||||||
accelerators = {
|
accelerators = {
|
||||||
delete = 'remove',
|
delete = 'remove',
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
statusBar = UI.StatusBar { },
|
||||||
|
accelerators = {
|
||||||
|
[ 'shift-up' ] = 'move_up',
|
||||||
|
[ 'shift-down' ] = 'move_down',
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar { },
|
|
||||||
accelerators = {
|
|
||||||
[ 'shift-up' ] = 'move_up',
|
|
||||||
[ 'shift-down' ] = 'move_down',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function tab:updateList(path)
|
function tab:updateList(path)
|
||||||
self.grid.values = { }
|
self.grid.values = { }
|
||||||
for k,v in ipairs(Util.split(path, '(.-):')) do
|
for k,v in ipairs(Util.split(path, '(.-):')) do
|
||||||
table.insert(self.grid.values, { index = k, value = v })
|
table.insert(self.grid.values, { index = k, value = v })
|
||||||
end
|
end
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
end
|
end
|
||||||
|
|
||||||
function tab:enable()
|
function tab:enable()
|
||||||
local env = Config.load('shell')
|
local env = Config.load('shell')
|
||||||
self:updateList(env.path)
|
self:updateList(env.path)
|
||||||
UI.Tab.enable(self)
|
UI.Tab.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function tab:save()
|
function tab:save()
|
||||||
local t = { }
|
local t = { }
|
||||||
for _, v in ipairs(self.grid.values) do
|
for _, v in ipairs(self.grid.values) do
|
||||||
table.insert(t, v.value)
|
table.insert(t, v.value)
|
||||||
end
|
end
|
||||||
local env = Config.load('shell')
|
local env = Config.load('shell')
|
||||||
env.path = table.concat(t, ':')
|
env.path = table.concat(t, ':')
|
||||||
self:updateList(env.path)
|
self:updateList(env.path)
|
||||||
Config.update('shell', env)
|
Config.update('shell', env)
|
||||||
end
|
end
|
||||||
|
|
||||||
function tab:eventHandler(event)
|
function tab:eventHandler(event)
|
||||||
if event.type == 'update_path' then
|
if event.type == 'update_path' then
|
||||||
table.insert(self.grid.values, {
|
table.insert(self.grid.values, {
|
||||||
value = self.entry.value,
|
value = self.entry.value,
|
||||||
})
|
})
|
||||||
self:save()
|
self:save()
|
||||||
self.entry:reset()
|
self.entry:reset()
|
||||||
self.entry:draw()
|
self.entry:draw()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
elseif event.type == 'grid_select' or event.type == 'remove' then
|
elseif event.type == 'grid_select' or event.type == 'remove' then
|
||||||
local selected = self.grid:getSelected()
|
local selected = self.grid:getSelected()
|
||||||
if selected then
|
if selected then
|
||||||
table.remove(self.grid.values, selected.index)
|
table.remove(self.grid.values, selected.index)
|
||||||
self:save()
|
self:save()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'focus_change' then
|
elseif event.type == 'focus_change' then
|
||||||
self.statusBar:setStatus(event.focused.help)
|
self.statusBar:setStatus(event.focused.help)
|
||||||
|
|
||||||
elseif event.type == 'move_up' then
|
elseif event.type == 'move_up' then
|
||||||
local entry = self.grid:getSelected()
|
local entry = self.grid:getSelected()
|
||||||
if entry.index > 1 then
|
if entry.index > 1 then
|
||||||
table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index))
|
table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index))
|
||||||
self.grid:setIndex(entry.index - 1)
|
self.grid:setIndex(entry.index - 1)
|
||||||
self:save()
|
self:save()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'move_down' then
|
elseif event.type == 'move_down' then
|
||||||
local entry = self.grid:getSelected()
|
local entry = self.grid:getSelected()
|
||||||
if entry.index < #self.grid.values then
|
if entry.index < #self.grid.values then
|
||||||
table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index))
|
table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index))
|
||||||
self.grid:setIndex(entry.index + 1)
|
self.grid:setIndex(entry.index + 1)
|
||||||
self:save()
|
self:save()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return tab
|
return tab
|
||||||
|
@ -13,91 +13,91 @@ local tab = UI.Tab {
|
|||||||
accelerators = {
|
accelerators = {
|
||||||
enter = 'update_path',
|
enter = 'update_path',
|
||||||
},
|
},
|
||||||
help = 'add a new path (reboot required)',
|
help = 'add a new path (reboot required)',
|
||||||
},
|
},
|
||||||
grid = UI.Grid {
|
grid = UI.Grid {
|
||||||
y = 4, ey = -3,
|
y = 4, ey = -3,
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
columns = { { key = 'value' } },
|
columns = { { key = 'value' } },
|
||||||
autospace = true,
|
autospace = true,
|
||||||
sortColumn = 'index',
|
sortColumn = 'index',
|
||||||
help = 'double-click to remove, shift-arrow to move',
|
help = 'double-click to remove, shift-arrow to move',
|
||||||
accelerators = {
|
accelerators = {
|
||||||
delete = 'remove',
|
delete = 'remove',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
statusBar = UI.StatusBar { },
|
statusBar = UI.StatusBar { },
|
||||||
accelerators = {
|
accelerators = {
|
||||||
[ 'shift-up' ] = 'move_up',
|
[ 'shift-up' ] = 'move_up',
|
||||||
[ 'shift-down' ] = 'move_down',
|
[ 'shift-down' ] = 'move_down',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
function tab:updateList(lua_path)
|
function tab:updateList(lua_path)
|
||||||
self.grid.values = { }
|
self.grid.values = { }
|
||||||
for k,v in ipairs(Util.split(lua_path, '(.-);')) do
|
for k,v in ipairs(Util.split(lua_path, '(.-);')) do
|
||||||
table.insert(self.grid.values, { index = k, value = v })
|
table.insert(self.grid.values, { index = k, value = v })
|
||||||
end
|
end
|
||||||
self.grid:update()
|
self.grid:update()
|
||||||
end
|
end
|
||||||
|
|
||||||
function tab:enable()
|
function tab:enable()
|
||||||
local env = Config.load('shell')
|
local env = Config.load('shell')
|
||||||
self:updateList(env.lua_path)
|
self:updateList(env.lua_path)
|
||||||
UI.Tab.enable(self)
|
UI.Tab.enable(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function tab:save()
|
function tab:save()
|
||||||
local t = { }
|
local t = { }
|
||||||
for _, v in ipairs(self.grid.values) do
|
for _, v in ipairs(self.grid.values) do
|
||||||
table.insert(t, v.value)
|
table.insert(t, v.value)
|
||||||
end
|
end
|
||||||
local env = Config.load('shell')
|
local env = Config.load('shell')
|
||||||
env.lua_path = table.concat(t, ';')
|
env.lua_path = table.concat(t, ';')
|
||||||
self:updateList(env.lua_path)
|
self:updateList(env.lua_path)
|
||||||
Config.update('shell', env)
|
Config.update('shell', env)
|
||||||
end
|
end
|
||||||
|
|
||||||
function tab:eventHandler(event)
|
function tab:eventHandler(event)
|
||||||
if event.type == 'update_path' then
|
if event.type == 'update_path' then
|
||||||
table.insert(self.grid.values, {
|
table.insert(self.grid.values, {
|
||||||
value = self.entry.value,
|
value = self.entry.value,
|
||||||
})
|
})
|
||||||
self:save()
|
self:save()
|
||||||
self.entry:reset()
|
self.entry:reset()
|
||||||
self.entry:draw()
|
self.entry:draw()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
elseif event.type == 'grid_select' or event.type == 'remove' then
|
elseif event.type == 'grid_select' or event.type == 'remove' then
|
||||||
local selected = self.grid:getSelected()
|
local selected = self.grid:getSelected()
|
||||||
if selected then
|
if selected then
|
||||||
table.remove(self.grid.values, selected.index)
|
table.remove(self.grid.values, selected.index)
|
||||||
self:save()
|
self:save()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'focus_change' then
|
elseif event.type == 'focus_change' then
|
||||||
self.statusBar:setStatus(event.focused.help)
|
self.statusBar:setStatus(event.focused.help)
|
||||||
|
|
||||||
elseif event.type == 'move_up' then
|
elseif event.type == 'move_up' then
|
||||||
local entry = self.grid:getSelected()
|
local entry = self.grid:getSelected()
|
||||||
if entry.index > 1 then
|
if entry.index > 1 then
|
||||||
table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index))
|
table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index))
|
||||||
self.grid:setIndex(entry.index - 1)
|
self.grid:setIndex(entry.index - 1)
|
||||||
self:save()
|
self:save()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif event.type == 'move_down' then
|
elseif event.type == 'move_down' then
|
||||||
local entry = self.grid:getSelected()
|
local entry = self.grid:getSelected()
|
||||||
if entry.index < #self.grid.values then
|
if entry.index < #self.grid.values then
|
||||||
table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index))
|
table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index))
|
||||||
self.grid:setIndex(entry.index + 1)
|
self.grid:setIndex(entry.index + 1)
|
||||||
self:save()
|
self:save()
|
||||||
self.grid:draw()
|
self.grid:draw()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return tab
|
return tab
|
||||||
|
@ -9,134 +9,134 @@ local config = Config.load('shellprompt')
|
|||||||
|
|
||||||
local allColors = { }
|
local allColors = { }
|
||||||
for k,v in pairs(colors) do
|
for k,v in pairs(colors) do
|
||||||
if type(v) == 'number' then
|
if type(v) == 'number' then
|
||||||
table.insert(allColors, { name = k, value = v })
|
table.insert(allColors, { name = k, value = v })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local defaults = {
|
local defaults = {
|
||||||
textColor = colors.white,
|
textColor = colors.white,
|
||||||
commandTextColor = colors.yellow,
|
commandTextColor = colors.yellow,
|
||||||
directoryTextColor = colors.orange,
|
directoryTextColor = colors.orange,
|
||||||
directoryBackgroundColor = colors.black,
|
directoryBackgroundColor = colors.black,
|
||||||
promptTextColor = colors.blue,
|
promptTextColor = colors.blue,
|
||||||
promptBackgroundColor = colors.black,
|
promptBackgroundColor = colors.black,
|
||||||
directoryColor = colors.green,
|
directoryColor = colors.green,
|
||||||
fileColor = colors.white,
|
fileColor = colors.white,
|
||||||
backgroundColor = colors.black,
|
backgroundColor = colors.black,
|
||||||
}
|
}
|
||||||
local _colors = config.color or Util.shallowCopy(defaults)
|
local _colors = config.color or Util.shallowCopy(defaults)
|
||||||
|
|
||||||
local allSettings = { }
|
local allSettings = { }
|
||||||
for k, v in pairs(defaults) do
|
for k, v in pairs(defaults) do
|
||||||
table.insert(allSettings, { name = k })
|
table.insert(allSettings, { name = k })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- temp
|
-- temp
|
||||||
if not _colors.backgroundColor then
|
if not _colors.backgroundColor then
|
||||||
_colors.backgroundColor = colors.black
|
_colors.backgroundColor = colors.black
|
||||||
_colors.fileColor = colors.white
|
_colors.fileColor = colors.white
|
||||||
end
|
end
|
||||||
|
|
||||||
local tab = UI.Tab {
|
local tab = UI.Tab {
|
||||||
tabTitle = 'Shell',
|
tabTitle = 'Shell',
|
||||||
description = 'Shell options',
|
description = 'Shell options',
|
||||||
grid1 = UI.ScrollingGrid {
|
grid1 = UI.ScrollingGrid {
|
||||||
y = 2, ey = -10, x = 3, ex = -16,
|
y = 2, ey = -10, x = 3, ex = -16,
|
||||||
disableHeader = true,
|
|
||||||
columns = { { key = 'name' } },
|
|
||||||
values = allSettings,
|
|
||||||
sortColumn = 'name',
|
|
||||||
},
|
|
||||||
grid2 = UI.ScrollingGrid {
|
|
||||||
y = 2, ey = -10, x = -14, ex = -3,
|
|
||||||
disableHeader = true,
|
disableHeader = true,
|
||||||
columns = { { key = 'name' } },
|
columns = { { key = 'name' } },
|
||||||
values = allColors,
|
values = allSettings,
|
||||||
sortColumn = 'name',
|
sortColumn = 'name',
|
||||||
|
},
|
||||||
|
grid2 = UI.ScrollingGrid {
|
||||||
|
y = 2, ey = -10, x = -14, ex = -3,
|
||||||
|
disableHeader = true,
|
||||||
|
columns = { { key = 'name' } },
|
||||||
|
values = allColors,
|
||||||
|
sortColumn = 'name',
|
||||||
|
},
|
||||||
|
directoryLabel = UI.Text {
|
||||||
|
x = 2, y = -2,
|
||||||
|
value = 'Display directory',
|
||||||
|
},
|
||||||
|
directory = UI.Checkbox {
|
||||||
|
x = 20, y = -2,
|
||||||
|
value = config.displayDirectory
|
||||||
|
},
|
||||||
|
reset = UI.Button {
|
||||||
|
x = -18, y = -2,
|
||||||
|
text = 'Reset',
|
||||||
|
event = 'reset',
|
||||||
|
},
|
||||||
|
button = UI.Button {
|
||||||
|
x = -9, y = -2,
|
||||||
|
text = 'Update',
|
||||||
|
event = 'update',
|
||||||
|
},
|
||||||
|
display = UI.Window {
|
||||||
|
x = 3, ex = -3, y = -8, height = 5,
|
||||||
},
|
},
|
||||||
directoryLabel = UI.Text {
|
|
||||||
x = 2, y = -2,
|
|
||||||
value = 'Display directory',
|
|
||||||
},
|
|
||||||
directory = UI.Checkbox {
|
|
||||||
x = 20, y = -2,
|
|
||||||
value = config.displayDirectory
|
|
||||||
},
|
|
||||||
reset = UI.Button {
|
|
||||||
x = -18, y = -2,
|
|
||||||
text = 'Reset',
|
|
||||||
event = 'reset',
|
|
||||||
},
|
|
||||||
button = UI.Button {
|
|
||||||
x = -9, y = -2,
|
|
||||||
text = 'Update',
|
|
||||||
event = 'update',
|
|
||||||
},
|
|
||||||
display = UI.Window {
|
|
||||||
x = 3, ex = -3, y = -8, height = 5,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function tab.grid2:getRowTextColor(row)
|
function tab.grid2:getRowTextColor(row)
|
||||||
local selected = tab.grid1:getSelected()
|
local selected = tab.grid1:getSelected()
|
||||||
if _colors[selected.name] == row.value then
|
if _colors[selected.name] == row.value then
|
||||||
return colors.yellow
|
return colors.yellow
|
||||||
end
|
end
|
||||||
return UI.Grid.getRowTextColor(self, row)
|
return UI.Grid.getRowTextColor(self, row)
|
||||||
end
|
end
|
||||||
|
|
||||||
function tab.display:draw()
|
function tab.display:draw()
|
||||||
self:clear(_colors.backgroundColor)
|
self:clear(_colors.backgroundColor)
|
||||||
local offset = 0
|
local offset = 0
|
||||||
if config.displayDirectory then
|
if config.displayDirectory then
|
||||||
self:write(1, 1,
|
self:write(1, 1,
|
||||||
'==' .. os.getComputerLabel() .. ':/dir/etc',
|
'==' .. os.getComputerLabel() .. ':/dir/etc',
|
||||||
_colors.directoryBackgroundColor, _colors.directoryTextColor)
|
_colors.directoryBackgroundColor, _colors.directoryTextColor)
|
||||||
offset = 1
|
offset = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
self:write(1, 1 + offset, '$ ',
|
self:write(1, 1 + offset, '$ ',
|
||||||
_colors.promptBackgroundColor, _colors.promptTextColor)
|
_colors.promptBackgroundColor, _colors.promptTextColor)
|
||||||
|
|
||||||
self:write(3, 1 + offset, 'ls /',
|
self:write(3, 1 + offset, 'ls /',
|
||||||
_colors.backgroundColor, _colors.commandTextColor)
|
_colors.backgroundColor, _colors.commandTextColor)
|
||||||
|
|
||||||
self:write(1, 2 + offset, 'sys usr',
|
self:write(1, 2 + offset, 'sys usr',
|
||||||
_colors.backgroundColor, _colors.directoryColor)
|
_colors.backgroundColor, _colors.directoryColor)
|
||||||
|
|
||||||
self:write(1, 3 + offset, 'startup',
|
self:write(1, 3 + offset, 'startup',
|
||||||
_colors.backgroundColor, _colors.fileColor)
|
_colors.backgroundColor, _colors.fileColor)
|
||||||
end
|
end
|
||||||
|
|
||||||
function tab:eventHandler(event)
|
function tab:eventHandler(event)
|
||||||
if event.type =='checkbox_change' then
|
if event.type =='checkbox_change' then
|
||||||
config.displayDirectory = not not event.checked
|
config.displayDirectory = not not event.checked
|
||||||
self.display:draw()
|
self.display:draw()
|
||||||
|
|
||||||
elseif event.type == 'grid_focus_row' and event.element == self.grid1 then
|
elseif event.type == 'grid_focus_row' and event.element == self.grid1 then
|
||||||
self.grid2:draw()
|
self.grid2:draw()
|
||||||
|
|
||||||
elseif event.type == 'grid_select' and event.element == self.grid2 then
|
elseif event.type == 'grid_select' and event.element == self.grid2 then
|
||||||
_colors[tab.grid1:getSelected().name] = event.selected.value
|
_colors[tab.grid1:getSelected().name] = event.selected.value
|
||||||
self.display:draw()
|
self.display:draw()
|
||||||
self.grid2:draw()
|
self.grid2:draw()
|
||||||
|
|
||||||
elseif event.type == 'reset' then
|
elseif event.type == 'reset' then
|
||||||
config.color = defaults
|
config.color = defaults
|
||||||
config.displayDirectory = true
|
config.displayDirectory = true
|
||||||
self.directory.value = true
|
self.directory.value = true
|
||||||
_colors = Util.shallowCopy(defaults)
|
_colors = Util.shallowCopy(defaults)
|
||||||
|
|
||||||
Config.update('shellprompt', config)
|
Config.update('shellprompt', config)
|
||||||
self:draw()
|
self:draw()
|
||||||
|
|
||||||
elseif event.type == 'update' then
|
elseif event.type == 'update' then
|
||||||
config.color = _colors
|
config.color = _colors
|
||||||
Config.update('shellprompt', config)
|
Config.update('shellprompt', config)
|
||||||
|
|
||||||
end
|
end
|
||||||
return UI.Tab.eventHandler(self, event)
|
return UI.Tab.eventHandler(self, event)
|
||||||
end
|
end
|
||||||
|
|
||||||
return tab
|
return tab
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
local function completeMultipleChoice(sText, tOptions, bAddSpaces)
|
local function completeMultipleChoice(sText, tOptions, bAddSpaces)
|
||||||
local tResults = { }
|
local tResults = { }
|
||||||
for n = 1,#tOptions do
|
for n = 1,#tOptions do
|
||||||
local sOption = tOptions[n]
|
local sOption = tOptions[n]
|
||||||
if #sOption + (bAddSpaces and 1 or 0) > #sText and string.sub(sOption, 1, #sText) == sText then
|
if #sOption + (bAddSpaces and 1 or 0) > #sText and string.sub(sOption, 1, #sText) == sText then
|
||||||
local sResult = string.sub(sOption, #sText + 1)
|
local sResult = string.sub(sOption, #sText + 1)
|
||||||
if bAddSpaces then
|
if bAddSpaces then
|
||||||
table.insert(tResults, sResult .. " ")
|
table.insert(tResults, sResult .. " ")
|
||||||
else
|
else
|
||||||
table.insert(tResults, sResult)
|
table.insert(tResults, sResult)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return tResults
|
return tResults
|
||||||
end
|
end
|
||||||
|
|
||||||
_ENV.shell.setCompletionFunction("sys/apps/package.lua",
|
_ENV.shell.setCompletionFunction("sys/apps/package.lua",
|
||||||
function(_, index, text)
|
function(_, index, text)
|
||||||
if index == 1 then
|
if index == 1 then
|
||||||
return completeMultipleChoice(text, { "install ", "update ", "uninstall " })
|
return completeMultipleChoice(text, { "install ", "update ", "uninstall " })
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
@ -4,8 +4,8 @@ local shell = _ENV.shell
|
|||||||
|
|
||||||
local config = Config.load('os')
|
local config = Config.load('os')
|
||||||
if not config.welcomed and shell.openForegroundTab then
|
if not config.welcomed and shell.openForegroundTab then
|
||||||
config.welcomed = true
|
config.welcomed = true
|
||||||
Config.update('os', config)
|
Config.update('os', config)
|
||||||
|
|
||||||
shell.openForegroundTab('Welcome')
|
shell.openForegroundTab('Welcome')
|
||||||
end
|
end
|
||||||
|
@ -6,47 +6,47 @@ local os = _G.os
|
|||||||
local peripheral = _G.peripheral
|
local peripheral = _G.peripheral
|
||||||
|
|
||||||
local containers = {
|
local containers = {
|
||||||
manipulator = true,
|
manipulator = true,
|
||||||
neuralInterface = true,
|
neuralInterface = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
local function getModules(dev, side)
|
local function getModules(dev, side)
|
||||||
local list = { }
|
local list = { }
|
||||||
|
|
||||||
if dev then
|
if dev then
|
||||||
for _, module in pairs(dev.listModules()) do
|
for _, module in pairs(dev.listModules()) do
|
||||||
list[module] = Util.shallowCopy(dev)
|
list[module] = Util.shallowCopy(dev)
|
||||||
list[module].name = module
|
list[module].name = module
|
||||||
list[module].type = module
|
list[module].type = module
|
||||||
list[module].side = side
|
list[module].side = side
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return list
|
return list
|
||||||
end
|
end
|
||||||
|
|
||||||
for _,v in pairs(device) do
|
for _,v in pairs(device) do
|
||||||
if containers[v.type] then
|
if containers[v.type] then
|
||||||
local list = getModules(v, v.side)
|
local list = getModules(v, v.side)
|
||||||
for k, dev in pairs(list) do
|
for k, dev in pairs(list) do
|
||||||
-- neural and attached modules have precedence over manipulator modules
|
-- neural and attached modules have precedence over manipulator modules
|
||||||
if not device[k] or v.type ~= 'manipulator' then
|
if not device[k] or v.type ~= 'manipulator' then
|
||||||
device[k] = dev
|
device[k] = dev
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- register modules as peripherals
|
-- register modules as peripherals
|
||||||
kernel.hook('device_attach', function(_, eventData)
|
kernel.hook('device_attach', function(_, eventData)
|
||||||
local dev = eventData[2]
|
local dev = eventData[2]
|
||||||
|
|
||||||
if dev and containers[dev.type] then
|
if dev and containers[dev.type] then
|
||||||
local list = getModules(peripheral.wrap(dev.side), dev.side)
|
local list = getModules(peripheral.wrap(dev.side), dev.side)
|
||||||
for k,v in pairs(list) do
|
for k,v in pairs(list) do
|
||||||
if not device[k] or dev.type ~= 'manipulator' then
|
if not device[k] or dev.type ~= 'manipulator' then
|
||||||
device[k] = v
|
device[k] = v
|
||||||
os.queueEvent('device_attach', k, v)
|
os.queueEvent('device_attach', k, v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
@ -2,26 +2,26 @@ local device = _G.device
|
|||||||
local kernel = _G.kernel
|
local kernel = _G.kernel
|
||||||
|
|
||||||
local function register(v)
|
local function register(v)
|
||||||
if v and v.isWireless and v.isAccessPoint and v.getNamesRemote then
|
if v and v.isWireless and v.isAccessPoint and v.getNamesRemote then
|
||||||
v._children = { }
|
v._children = { }
|
||||||
for _, name in pairs(v.getNamesRemote()) do
|
for _, name in pairs(v.getNamesRemote()) do
|
||||||
local dev = v.getMethodsRemote(name)
|
local dev = v.getMethodsRemote(name)
|
||||||
if dev then
|
if dev then
|
||||||
dev.name = name
|
dev.name = name
|
||||||
dev.side = name
|
dev.side = name
|
||||||
dev.type = v.getTypeRemote(name)
|
dev.type = v.getTypeRemote(name)
|
||||||
device[name] = dev
|
device[name] = dev
|
||||||
table.insert(v._children, dev)
|
table.insert(v._children, dev)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _,v in pairs(device) do
|
for _,v in pairs(device) do
|
||||||
register(v)
|
register(v)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- register oc devices as peripherals
|
-- register oc devices as peripherals
|
||||||
kernel.hook('device_attach', function(_, eventData)
|
kernel.hook('device_attach', function(_, eventData)
|
||||||
register(device[eventData[2]])
|
register(device[eventData[2]])
|
||||||
end)
|
end)
|
||||||
|
@ -6,7 +6,9 @@ local help = _G.help
|
|||||||
local shell = _ENV.shell
|
local shell = _ENV.shell
|
||||||
|
|
||||||
if not fs.exists('usr/config/packages') then
|
if not fs.exists('usr/config/packages') then
|
||||||
Packages:downloadList()
|
pcall(function()
|
||||||
|
Packages:downloadList()
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local appPaths = Util.split(shell.path(), '(.-):')
|
local appPaths = Util.split(shell.path(), '(.-):')
|
||||||
|
@ -52,6 +52,7 @@ function turtle.resetState()
|
|||||||
state.movePolicy = _defaultMove
|
state.movePolicy = _defaultMove
|
||||||
state.moveCallback = noop
|
state.moveCallback = noop
|
||||||
state.blacklist = nil
|
state.blacklist = nil
|
||||||
|
state.reference = nil -- gps reference when converting to relative coords
|
||||||
Pathing.reset()
|
Pathing.reset()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -77,8 +78,6 @@ local function _dig(name, inspect, dig)
|
|||||||
return dig()
|
return dig()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- override dig
|
|
||||||
-- optionally check that the block is a certain type
|
|
||||||
function turtle.dig(s)
|
function turtle.dig(s)
|
||||||
return _dig(s, turtle.inspect, turtle.native.dig)
|
return _dig(s, turtle.inspect, turtle.native.dig)
|
||||||
end
|
end
|
||||||
@ -359,7 +358,7 @@ function turtle.set(args)
|
|||||||
turtle.setDigPolicy(turtle.getPolicy(v))
|
turtle.setDigPolicy(turtle.getPolicy(v))
|
||||||
|
|
||||||
elseif k == 'movePolicy' then
|
elseif k == 'movePolicy' then
|
||||||
turtle.setMovePolicy(turtle.getPolicy(v))
|
state.movePolicy = turtle.getPolicy(v)
|
||||||
|
|
||||||
elseif k == 'movementStrategy' then
|
elseif k == 'movementStrategy' then
|
||||||
turtle.setMovementStrategy(v)
|
turtle.setMovementStrategy(v)
|
||||||
@ -379,6 +378,9 @@ function turtle.set(args)
|
|||||||
elseif k == 'blacklist' then
|
elseif k == 'blacklist' then
|
||||||
state.blacklist = v
|
state.blacklist = v
|
||||||
|
|
||||||
|
elseif k == 'reference' then
|
||||||
|
state.reference = v
|
||||||
|
|
||||||
else
|
else
|
||||||
error('Invalid turle.set: ' .. tostring(k))
|
error('Invalid turle.set: ' .. tostring(k))
|
||||||
end
|
end
|
||||||
@ -426,6 +428,10 @@ function turtle.setHeading(heading)
|
|||||||
return false, 'Invalid heading'
|
return false, 'Invalid heading'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if heading == turtle.point.heading then
|
||||||
|
return turtle.point
|
||||||
|
end
|
||||||
|
|
||||||
local fi = Point.facings[heading]
|
local fi = Point.facings[heading]
|
||||||
if not fi then
|
if not fi then
|
||||||
return false, 'Invalid heading'
|
return false, 'Invalid heading'
|
||||||
|
Loading…
Reference in New Issue
Block a user