From 3c22a872b0eb408fcbe28010533b9d06cbf5b925 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 18 Jun 2019 15:19:24 -0400 Subject: [PATCH] spaces->tabs + cleanup + pathing fixes --- sys/apis/bulkget.lua | 42 +- sys/apis/fs/redfs.lua | 61 --- sys/apis/fs/urlfs.lua | 6 +- sys/apis/json.lua | 727 ++++++++++++++++++++------- sys/apis/map.lua | 6 +- sys/apis/pathfind.lua | 25 +- sys/apis/point.lua | 15 +- sys/apis/proxy.lua | 32 -- sys/apis/rttp.lua | 95 ---- sys/apis/sha2.lua | 200 ++++++++ sys/apis/sync.lua | 86 ++-- sys/apis/trace.lua | 158 +++--- sys/apis/ui/components/Grid.lua | 29 +- sys/apis/util.lua | 2 +- sys/apps/Files.lua | 852 ++++++++++++++++---------------- sys/apps/Lua.lua | 19 +- sys/apps/Network.lua | 2 + sys/apps/ShellLauncher.lua | 36 +- sys/apps/Welcome.lua | 170 +++---- sys/apps/autorun.lua | 86 ++-- sys/apps/cedit.lua | 36 +- sys/apps/cshell.lua | 24 +- sys/apps/network/redserver.lua | 115 ----- sys/apps/network/snmp.lua | 2 +- sys/apps/sniff.lua | 69 --- sys/apps/system/cloud.lua | 78 +-- sys/apps/system/kiosk.lua | 14 +- sys/apps/system/launcher.lua | 112 ++--- sys/apps/system/path.lua | 108 ++-- sys/apps/system/requires.lua | 114 ++--- sys/apps/system/shell.lua | 180 +++---- sys/autorun/complete.lua | 36 +- sys/autorun/welcome.lua | 6 +- sys/init/3.modules.lua | 62 +-- sys/init/3.relay.lua | 30 +- sys/init/6.packages.lua | 4 +- sys/init/6.tl3.lua | 12 +- 37 files changed, 1948 insertions(+), 1703 deletions(-) delete mode 100644 sys/apis/fs/redfs.lua delete mode 100644 sys/apis/proxy.lua delete mode 100644 sys/apis/rttp.lua create mode 100644 sys/apis/sha2.lua delete mode 100644 sys/apps/network/redserver.lua delete mode 100644 sys/apps/sniff.lua diff --git a/sys/apis/bulkget.lua b/sys/apis/bulkget.lua index 1135847..a9679ef 100644 --- a/sys/apis/bulkget.lua +++ b/sys/apis/bulkget.lua @@ -5,29 +5,29 @@ local parallel = _G.parallel local BulkGet = { } function BulkGet.download(list, callback) - local t = { } - local failed = false + local t = { } + local failed = false - for _ = 1, 5 do - table.insert(t, function() - while true do - local entry = table.remove(list) - if not entry then - break - end - local s, m = Util.download(entry.url, entry.path) - if not s then - failed = true - end - callback(entry, s, m) - if failed then - break - end - end - end) - end + for _ = 1, 5 do + table.insert(t, function() + while true do + local entry = table.remove(list) + if not entry then + break + end + local s, m = Util.download(entry.url, entry.path) + if not s then + failed = true + end + callback(entry, s, m) + if failed then + break + end + end + end) + end - parallel.waitForAll(table.unpack(t)) + parallel.waitForAll(table.unpack(t)) end return BulkGet diff --git a/sys/apis/fs/redfs.lua b/sys/apis/fs/redfs.lua deleted file mode 100644 index bbd981a..0000000 --- a/sys/apis/fs/redfs.lua +++ /dev/null @@ -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:///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 diff --git a/sys/apis/fs/urlfs.lua b/sys/apis/fs/urlfs.lua index 6feb4f0..fc3b655 100644 --- a/sys/apis/fs/urlfs.lua +++ b/sys/apis/fs/urlfs.lua @@ -1,4 +1,4 @@ -local rttp = require('rttp') +--local rttp = require('rttp') local Util = require('util') local fs = _G.fs @@ -39,7 +39,6 @@ function urlfs.getDrive() end function urlfs.open(node, fn, fl) - if fl == 'w' or fl == 'wb' then fs.delete(fn) return fs.open(fn, fl) @@ -51,12 +50,15 @@ function urlfs.open(node, fn, fl) local c = node.cache if not c then + --[[ if node.url:match("^(rttps?:)") then local s, response = rttp.get(node.url) c = s and response.statusCode == 200 and response.data else c = Util.httpGet(node.url) end + ]]-- + c = Util.httpGet(node.url) if c then node.cache = c node.size = #c diff --git a/sys/apis/json.lua b/sys/apis/json.lua index 64f8825..04b1cd1 100644 --- a/sys/apis/json.lua +++ b/sys/apis/json.lua @@ -1,215 +1,584 @@ --- credit ElvishJerricco --- http://pastebin.com/raw.php?i=4nRg9CHU +-- Module options: +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 -local controls = {["\n"]="\\n", ["\r"]="\\r", ["\t"]="\\t", ["\b"]="\\b", ["\f"]="\\f", ["\""]="\\\"", ["\\"]="\\\\"} +David Kolf's JSON module for Lua 5.1/5.2 +Version 2.5 -local function isArray(t) - local max = 0 - for k,v in pairs(t) do - if type(k) ~= "number" then - return false - elseif k > max then - max = k - end - end - return max == #t +For the documentation see the corresponding readme.txt or visit +. + +You can contact the author by sending an e-mail to 'david' at the +domain 'dkolf.de'. + +Copyright (C) 2010-2014 David Heiko Kolf + +Refer to license located at https://github.com/LuaDist/dkjson/blob/master/dkjson.lua + +--]==] + +-- 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 -local whites = {['\n']=true; ['\r']=true; ['\t']=true; [' ']=true; [',']=true; [':']=true} -local function removeWhite(str) - while whites[str:sub(1, 1)] do - str = str:sub(2) - end - return str -end +local _ENV = nil -- blocking globals in Lua 5.2 ------------------------------------------------------------------- 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) - local str = "" +json.null = setmetatable ({}, { + __tojson = function () return "null" end +}) - -- Tabbing util - local function tab(s) - str = str .. ("\t"):rep(tabLevel) .. s - end - - local function arrEncoding(val, bracket, closeBracket, iterator, loopFunc) - str = str .. bracket - if pretty then - 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) +local function isarray (tbl) + local max, n, arraylen = 0, 0, 0 + for k,v in pairs (tbl) do + if k == 'n' and type(v) == 'number' then + arraylen = v + if v > max then + max = v + end else - arrEncoding(val, "{", "}", pairs, function(k,v) - assert(type(k) == "string", "JSON object keys must be strings", 2) - str = str .. encodeCommon(k, pretty, tabLevel, tTracking) - str = str .. (pretty and ": " or ":") .. encodeCommon(v, pretty, tabLevel, tTracking) - end) + if type(k) ~= 'number' or k < 1 or floor(k) ~= k then + return false + end + if k > max then + max = k + end + n = n + 1 end - -- String encoding - elseif type(val) == "string" then - str = '"' .. val:gsub("[%c\"\\]", controls) .. '"' - -- Number encoding - elseif type(val) == "number" or type(val) == "boolean" then - str = tostring(val) + end + if max > 10 and max > arraylen and max > n * 2 then + return false -- don't create an array with too many holes + end + return true, max +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 - error("JSON only supports arrays, objects, numbers, booleans, and strings", 2) + return "" end - return str -end - -function json.encode(val) - return encodeCommon(val, false, 0, {}) -end - -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)) + if value <= 0xffff then + return strformat ("\\u%.4x", value) + elseif value <= 0x10ffff then + -- encode as UTF-16 surrogate pair + value = value - 0x10000 + local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) + return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) else - return false, removeWhite(str:sub(6)) + return "" end end -local function parseNull(str) - return nil, removeWhite(str:sub(5)) -end - -local numChars = {['e']=true; ['E']=true; ['+']=true; ['-']=true; ['.']=true} -local function parseNumber(str) - local i = 1 - while numChars[str:sub(i, i)] or tonumber(str:sub(i, i)) do - i = i + 1 +local function fsub (str, pattern, repl) + -- gsub always builds a new string in a buffer, even when no match + -- exists. First using find should be more efficient when most strings + -- don't contain the pattern. + if strfind (str, pattern) then + return gsub (str, pattern, repl) + else + return str end - local val = tonumber(str:sub(1, i - 1)) - str = removeWhite(str:sub(i)) - return val, str end -local function parseString(str) - str = str:sub(2) - local s = "" - while str:sub(1,1) ~= "\"" do - local next = str:sub(1,1) - str = str:sub(2) - assert(next ~= "\n", "Unclosed string") +local function quotestring (value) + -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js + value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) + if strfind (value, "[\194\216\220\225\226\239]") then + value = fsub (value, "\194[\128-\159\173]", escapeutf8) + value = fsub (value, "\216[\128-\132]", escapeutf8) + 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 escape = str:sub(1,1) - str = str:sub(2) +local function replace(str, o, n) + local i, j = strfind (str, o, 1, true) + 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 - - s = s .. next + tables[value] = true + 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 - return s, removeWhite(str:sub(2)) + return buflen end -function json.parseArray(str) - str = removeWhite(str:sub(2)) - - local val = {} - local i = 1 - while str:sub(1, 1) ~= "]" do - local v - v, str = json.parseValue(str) - val[i] = v - i = i + 1 - str = removeWhite(str) - end - str = removeWhite(str:sub(2)) - return val, str -end - -function json.parseValue(str) - 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) +function json.encode (value, state) + state = state or {} + local oldbuffer = state.buffer + local buffer = oldbuffer or {} + state.buffer = buffer + updatedecpoint() + local ret, msg = encode2 (value, state.indent, state.level or 0, + buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) + if not ret then + error (msg, 2) + elseif oldbuffer == buffer then + state.bufferlen = ret + return true + else + state.bufferlen = nil + state.buffer = nil + return concat (buffer) end end -function json.parseMember(str) - local k, val - k, str = json.parseValue(str) - val, str = json.parseValue(str) - return k, val, str -end - -function json.parseObject(str) - str = removeWhite(str:sub(2)) - - local val = {} - while str:sub(1, 1) ~= "}" do - local k, v - k, v, str = json.parseMember(str) - val[k] = v - str = removeWhite(str) +local function loc (str, where) + local line, pos, linepos = 1, 1, 0 + while true do + pos = strfind (str, "\n", pos, true) + if pos and pos < where then + line = line + 1 + linepos = pos + pos = pos + 1 + else + break + end end - str = removeWhite(str:sub(2)) - return val, str + return "line " .. line .. ", column " .. (where - linepos) end -function json.decode(str) - str = removeWhite(str) - return json.parseValue(str) +local function unterminated (str, what, where) + return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) 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) local file = assert(fs.open(path, "r")) local decoded = json.decode(file.readAll()) diff --git a/sys/apis/map.lua b/sys/apis/map.lua index a6b033c..8380f38 100644 --- a/sys/apis/map.lua +++ b/sys/apis/map.lua @@ -19,9 +19,9 @@ function Map.removeMatches(t, values) return true end - for k,v in pairs(t) do - if matchAll(v) then - t[k] = nil + for _, key in pairs(Util.keys(t)) do + if matchAll(t[key]) then + t[key] = nil end end end diff --git a/sys/apis/pathfind.lua b/sys/apis/pathfind.lua index ffb5d25..f34c6ac 100644 --- a/sys/apis/pathfind.lua +++ b/sys/apis/pathfind.lua @@ -106,6 +106,17 @@ local function selectDestination(pts, box, grid) 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 blocks = options.blocks or turtle.getState().blocks or { } local dests = options.dest or { dest } -- support alternative destinations @@ -156,6 +167,8 @@ local function pathTo(dest, options) if not path then Util.removeByValue(dests, dest) else + updateCanvas(path) + path:filter() 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 -- when encountering obstacles - if not turtle.gotoSingleTurn(pt.x, pt.y, pt.z, pt.heading) then - --if not turtle.goto(pt) then + --if not turtle.gotoSingleTurn(pt.x, pt.y, pt.z, pt.heading) then + pt.heading = nil + if not turtle.go(pt) then local bpt = Point.nearestTo(turtle.point, pt) + if turtle.getFuelLevel() == 0 then + return false, 'Out of fuel' + end 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. -- if so, this block should be temporary (1-2 secs) diff --git a/sys/apis/point.lua b/sys/apis/point.lua index 690b446..da1d15b 100644 --- a/sys/apis/point.lua +++ b/sys/apis/point.lua @@ -131,8 +131,11 @@ end function Point.calculateMoves(pta, ptb, distance) local heading = pta.heading local moves = distance or Point.turtleDistance(pta, ptb) + local weighted = moves + if (pta.heading % 2) == 0 and pta.z ~= ptb.z then moves = moves + 1 + weighted = weighted + .9 if ptb.heading and (ptb.heading % 2 == 1) then heading = ptb.heading elseif ptb.z > pta.z then @@ -142,6 +145,7 @@ function Point.calculateMoves(pta, ptb, distance) end elseif (pta.heading % 2) == 1 and pta.x ~= ptb.x then moves = moves + 1 + weighted = weighted + .9 if ptb.heading and (ptb.heading % 2 == 0) then heading = ptb.heading elseif ptb.x > pta.x then @@ -152,15 +156,18 @@ function Point.calculateMoves(pta, ptb, distance) end if not ptb.heading then - return moves, heading, moves + return moves, heading, weighted 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 - local weighted = moves + -- local weighted = moves if heading ~= ptb.heading then local turns = Point.calculateTurns(heading, ptb.heading) 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] heading = ptb.heading end @@ -233,7 +240,7 @@ end function Point.nearestTo(pta, ptb) local heading - if pta.x < ptb.x then + if pta.x < ptb.x then heading = 0 elseif pta.z < ptb.z then heading = 1 diff --git a/sys/apis/proxy.lua b/sys/apis/proxy.lua deleted file mode 100644 index eb3d819..0000000 --- a/sys/apis/proxy.lua +++ /dev/null @@ -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 diff --git a/sys/apis/rttp.lua b/sys/apis/rttp.lua deleted file mode 100644 index 8696e9e..0000000 --- a/sys/apis/rttp.lua +++ /dev/null @@ -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 diff --git a/sys/apis/sha2.lua b/sys/apis/sha2.lua new file mode 100644 index 0000000..f7965bc --- /dev/null +++ b/sys/apis/sha2.lua @@ -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, +} \ No newline at end of file diff --git a/sys/apis/sync.lua b/sys/apis/sync.lua index 7987cb4..fd3402c 100644 --- a/sys/apis/sync.lua +++ b/sys/apis/sync.lua @@ -1,61 +1,61 @@ local Sync = { - syncLocks = { } + syncLocks = { } } local os = _G.os function Sync.sync(obj, fn) - local key = tostring(obj) - if Sync.syncLocks[key] then - local cos = tostring(coroutine.running()) - table.insert(Sync.syncLocks[key], cos) - repeat - local _, co = os.pullEvent('sync_lock') - until co == cos - else - Sync.syncLocks[key] = { } - end - local s, m = pcall(fn) - local co = table.remove(Sync.syncLocks[key], 1) - if co then - os.queueEvent('sync_lock', co) - else - Sync.syncLocks[key] = nil - end - if not s then - error(m) - end + local key = tostring(obj) + if Sync.syncLocks[key] then + local cos = tostring(coroutine.running()) + table.insert(Sync.syncLocks[key], cos) + repeat + local _, co = os.pullEvent('sync_lock') + until co == cos + else + Sync.syncLocks[key] = { } + end + local s, m = pcall(fn) + local co = table.remove(Sync.syncLocks[key], 1) + if co then + os.queueEvent('sync_lock', co) + else + Sync.syncLocks[key] = nil + end + if not s then + error(m) + end end function Sync.lock(obj) - local key = tostring(obj) - if Sync.syncLocks[key] then - local cos = tostring(coroutine.running()) - table.insert(Sync.syncLocks[key], cos) - repeat - local _, co = os.pullEvent('sync_lock') - until co == cos - else - Sync.syncLocks[key] = { } - end + local key = tostring(obj) + if Sync.syncLocks[key] then + local cos = tostring(coroutine.running()) + table.insert(Sync.syncLocks[key], cos) + repeat + local _, co = os.pullEvent('sync_lock') + until co == cos + else + Sync.syncLocks[key] = { } + end end function Sync.release(obj) - local key = tostring(obj) - if not Sync.syncLocks[key] then - error('Sync.release: Lock was not obtained', 2) - end - local co = table.remove(Sync.syncLocks[key], 1) - if co then - os.queueEvent('sync_lock', co) - else - Sync.syncLocks[key] = nil - end + local key = tostring(obj) + if not Sync.syncLocks[key] then + error('Sync.release: Lock was not obtained', 2) + end + local co = table.remove(Sync.syncLocks[key], 1) + if co then + os.queueEvent('sync_lock', co) + else + Sync.syncLocks[key] = nil + end end function Sync.isLocked(obj) - local key = tostring(obj) - return not not Sync.syncLocks[key] + local key = tostring(obj) + return not not Sync.syncLocks[key] end return Sync diff --git a/sys/apis/trace.lua b/sys/apis/trace.lua index 4323003..5673b6d 100644 --- a/sys/apis/trace.lua +++ b/sys/apis/trace.lua @@ -5,106 +5,106 @@ local type = type local debug_traceback = type(debug) == "table" and type(debug.traceback) == "function" and debug.traceback local function traceback(x) - -- Attempt to detect error() and error("xyz", 0). - -- This probably means they're erroring the program intentionally and so we - -- shouldn't display anything. - if x == nil or (type(x) == "string" and not x:find(":%d+:")) then - return x - end + -- Attempt to detect error() and error("xyz", 0). + -- This probably means they're erroring the program intentionally and so we + -- shouldn't display anything. + if x == nil or (type(x) == "string" and not x:find(":%d+:")) then + return x + end - if debug_traceback then - -- The parens are important, as they prevent a tail call occuring, meaning - -- the stack level is preserved. This ensures the code behaves identically - -- on LuaJ and PUC Lua. - return (debug_traceback(tostring(x), 2)) - else - local level = 3 - local out = { tostring(x), "stack traceback:" } - while true do - local _, msg = pcall(error, "", level) - if msg == "" then break end + if debug_traceback then + -- The parens are important, as they prevent a tail call occuring, meaning + -- the stack level is preserved. This ensures the code behaves identically + -- on LuaJ and PUC Lua. + return (debug_traceback(tostring(x), 2)) + else + local level = 3 + local out = { tostring(x), "stack traceback:" } + while true do + local _, msg = pcall(error, "", level) + if msg == "" then break end - out[#out + 1] = " " .. msg - level = level + 1 - end + out[#out + 1] = " " .. msg + level = level + 1 + end - return table.concat(out, "\n") - end + return table.concat(out, "\n") + end end local function trim_traceback(target, marker) - local ttarget, tmarker = {}, {} - 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 + local ttarget, tmarker = {}, {} + 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 - -- Trim identical suffixes - local t_len, m_len = #ttarget, #tmarker - while t_len >= 3 and ttarget[t_len] == tmarker[m_len] do - table.remove(ttarget, t_len) - t_len, m_len = t_len - 1, m_len - 1 - end + -- Trim identical suffixes + local t_len, m_len = #ttarget, #tmarker + while t_len >= 3 and ttarget[t_len] == tmarker[m_len] do + table.remove(ttarget, t_len) + t_len, m_len = t_len - 1, m_len - 1 + end - -- Trim elements from this file and xpcall invocations - 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 - table.remove(ttarget, t_len) - t_len = t_len - 1 - end + -- Trim elements from this file and xpcall invocations + 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 + table.remove(ttarget, t_len) + t_len = t_len - 1 + end - ttarget[#ttarget] = nil -- remove 2 calls added by the added xpcall - ttarget[#ttarget] = nil + ttarget[#ttarget] = nil -- remove 2 calls added by the added xpcall + ttarget[#ttarget] = nil - return ttarget + return ttarget end --- Run a function with return function (fn, ...) - -- So this is rather grim: we need to get the full traceback and current one and remove - -- the common prefix - local trace - local args = { ... } + -- So this is rather grim: we need to get the full traceback and current one and remove + -- the common prefix + local trace + local args = { ... } - -- xpcall in Lua 5.1 does not accept parameters - -- which is not ideal - local res = table.pack(xpcall(function() - return fn(table.unpack(args)) - end, traceback)) + -- xpcall in Lua 5.1 does not accept parameters + -- which is not ideal + local res = table.pack(xpcall(function() + return fn(table.unpack(args)) + end, traceback)) - if not res[1] then - trace = traceback("trace.lua:1:") - end - local ok, err = res[1], res[2] + if not res[1] then + trace = traceback("trace.lua:1:") + end + local ok, err = res[1], res[2] - if not ok and err ~= nil then - trace = trim_traceback(err, trace) + if not ok and err ~= nil then + trace = trim_traceback(err, trace) - -- Find the position where the stack traceback actually starts - local trace_starts - for i = #trace, 1, -1 do - if trace[i] == "stack traceback:" then trace_starts = i; break end - end + -- Find the position where the stack traceback actually starts + local trace_starts + for i = #trace, 1, -1 do + if trace[i] == "stack traceback:" then trace_starts = i; break end + end - for _, line in pairs(trace) do - _G._syslog(line) - end + for _, line in pairs(trace) do + _G._syslog(line) + end - -- If this traceback is more than 15 elements long, keep the first 9, last 5 - -- and put an ellipsis between the rest - local max = 10 - if trace_starts and #trace - trace_starts > max then - local keep_starts = trace_starts + 7 - for i = #trace - trace_starts - max, 0, -1 do - table.remove(trace, keep_starts + i) - end - table.insert(trace, keep_starts, " ...") - end + -- If this traceback is more than 15 elements long, keep the first 9, last 5 + -- and put an ellipsis between the rest + local max = 10 + if trace_starts and #trace - trace_starts > max then + local keep_starts = trace_starts + 7 + for i = #trace - trace_starts - max, 0, -1 do + table.remove(trace, keep_starts + i) + end + table.insert(trace, keep_starts, " ...") + end - for k, line in pairs(trace) do - trace[k] = line:gsub("in function", " in") - end + for k, line in pairs(trace) do + trace[k] = line:gsub("in function", " in") + end - return false, table.remove(trace, 1), table.concat(trace, "\n") - end + return false, table.remove(trace, 1), table.concat(trace, "\n") + end - return table.unpack(res, 1, res.n) + return table.unpack(res, 1, res.n) end diff --git a/sys/apis/ui/components/Grid.lua b/sys/apis/ui/components/Grid.lua index 262faee..21069ee 100644 --- a/sys/apis/ui/components/Grid.lua +++ b/sys/apis/ui/components/Grid.lua @@ -329,23 +329,13 @@ function UI.Grid:drawRows() local rawRow = self.values[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 bg = self:getRowBackgroundColor(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) end @@ -354,6 +344,19 @@ function UI.Grid:drawRows() 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) if selected then if self.focused then diff --git a/sys/apis/util.lua b/sys/apis/util.lua index e774ed9..042ba84 100644 --- a/sys/apis/util.lua +++ b/sys/apis/util.lua @@ -560,7 +560,7 @@ function Util.insertString(str, istr, pos) end 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" local t = {} local function helper(line) table.insert(t, line) return "" end diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index f7564ed..ea3126d 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -15,11 +15,11 @@ local FILE = 1 UI:configure('Files', ...) local config = Config.load('Files', { - showHidden = false, - showDirSizes = false, + showHidden = false, + showDirSizes = false, }) config.associations = config.associations or { - nft = 'pain', + nft = 'pain', } local copied = { } @@ -28,517 +28,517 @@ local directories = { } local cutMode = false local function formatSize(size) - if size >= 1000000 then - return string.format('%dM', math.floor(size/1000000, 2)) - elseif size >= 1000 then - return string.format('%dK', math.floor(size/1000, 2)) - end - return size + if size >= 1000000 then + return string.format('%dM', math.floor(size/1000000, 2)) + elseif size >= 1000 then + return string.format('%dK', math.floor(size/1000, 2)) + end + return size end local Browser = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { text = '^-', event = 'updir' }, - { text = 'File', dropdown = { - { text = 'Run', event = 'run', flags = FILE }, - { text = 'Edit e', event = 'edit', flags = FILE }, - { text = 'Cloud edit c', event = 'cedit', flags = FILE }, - { text = 'Pastebin put p', event = 'pastebin', flags = FILE }, - { text = 'Shell s', event = 'shell' }, - { spacer = true }, - { text = 'Quit q', event = 'quit' }, - } }, - { text = 'Edit', dropdown = { - { text = 'Cut ^x', event = 'cut' }, - { text = 'Copy ^c', event = 'copy' }, - { text = 'Copy path ', event = 'copy_path' }, - { text = 'Paste ^v', event = 'paste' }, - { spacer = true }, - { text = 'Mark m', event = 'mark' }, - { text = 'Unmark all u', event = 'unmark' }, - { spacer = true }, - { text = 'Delete del', event = 'delete' }, - } }, - { text = 'View', dropdown = { - { text = 'Refresh r', event = 'refresh' }, - { text = 'Hidden ^h', event = 'toggle_hidden' }, - { text = 'Dir Size ^s', event = 'toggle_dirSize' }, - } }, - { text = '\187', - x = -3, - dropdown = { - { text = 'Associations', event = 'associate' }, - } }, - }, - }, - grid = UI.ScrollingGrid { - columns = { - { heading = 'Name', key = 'name' }, - { key = 'flags', width = 2 }, - { heading = 'Size', key = 'fsize', width = 5 }, - }, - sortColumn = 'name', - y = 2, ey = -2, - }, - statusBar = UI.StatusBar { - columns = { - { key = 'status' }, - { key = 'totalSize', width = 6 }, - }, - }, - notification = UI.Notification { }, - associations = UI.SlideOut { - backgroundColor = colors.cyan, - menuBar = UI.MenuBar { - buttons = { - { text = 'Save', event = 'save' }, - { text = 'Cancel', event = 'cancel' }, - }, - }, - grid = UI.ScrollingGrid { - x = 2, ex = -6, y = 3, ey = -5, - columns = { - { heading = 'Extension', key = 'name' }, - { heading = 'Program', key = 'value' }, - }, - autospace = true, - sortColumn = 'name', - accelerators = { - delete = 'remove_entry', - }, - }, - remove = UI.Button { - x = -4, y = 6, - text = '-', event = 'remove_entry', help = 'Remove', - }, - form = UI.Form { - x = 3, y = -3, ey = -2, - margin = 1, - manualControls = true, - [1] = UI.TextEntry { - width = 20, - formLabel = 'Extension', formKey = 'name', - shadowText = 'extension', - required = true, - limit = 64, - }, - [2] = UI.TextEntry { - width = 20, - formLabel = 'Program', formKey = 'value', - shadowText = 'program', - required = true, - limit = 128, - }, - add = UI.Button { - x = -11, y = 1, - text = 'Add', event = 'add_association', - }, - }, - statusBar = UI.StatusBar { - backgroundColor = colors.cyan, - }, - }, - accelerators = { - q = 'quit', - c = 'cedit', - e = 'edit', - s = 'shell', - p = 'pastebin', - r = 'refresh', - [ ' ' ] = 'mark', - m = 'mark', - backspace = 'updir', - u = 'unmark', - d = 'delete', - delete = 'delete', - [ 'control-h' ] = 'toggle_hidden', - [ 'control-s' ] = 'toggle_dirSize', - [ 'control-x' ] = 'cut', - [ 'control-c' ] = 'copy', - paste = 'paste', - }, + menuBar = UI.MenuBar { + buttons = { + { text = '^-', event = 'updir' }, + { text = 'File', dropdown = { + { text = 'Run', event = 'run', flags = FILE }, + { text = 'Edit e', event = 'edit', flags = FILE }, + { text = 'Cloud edit c', event = 'cedit', flags = FILE }, + { text = 'Pastebin put p', event = 'pastebin', flags = FILE }, + { text = 'Shell s', event = 'shell' }, + { spacer = true }, + { text = 'Quit q', event = 'quit' }, + } }, + { text = 'Edit', dropdown = { + { text = 'Cut ^x', event = 'cut' }, + { text = 'Copy ^c', event = 'copy' }, + { text = 'Copy path ', event = 'copy_path' }, + { text = 'Paste ^v', event = 'paste' }, + { spacer = true }, + { text = 'Mark m', event = 'mark' }, + { text = 'Unmark all u', event = 'unmark' }, + { spacer = true }, + { text = 'Delete del', event = 'delete' }, + } }, + { text = 'View', dropdown = { + { text = 'Refresh r', event = 'refresh' }, + { text = 'Hidden ^h', event = 'toggle_hidden' }, + { text = 'Dir Size ^s', event = 'toggle_dirSize' }, + } }, + { text = '\187', + x = -3, + dropdown = { + { text = 'Associations', event = 'associate' }, + } }, + }, + }, + grid = UI.ScrollingGrid { + columns = { + { heading = 'Name', key = 'name' }, + { key = 'flags', width = 2 }, + { heading = 'Size', key = 'fsize', width = 5 }, + }, + sortColumn = 'name', + y = 2, ey = -2, + }, + statusBar = UI.StatusBar { + columns = { + { key = 'status' }, + { key = 'totalSize', width = 6 }, + }, + }, + notification = UI.Notification { }, + associations = UI.SlideOut { + backgroundColor = colors.cyan, + menuBar = UI.MenuBar { + buttons = { + { text = 'Save', event = 'save' }, + { text = 'Cancel', event = 'cancel' }, + }, + }, + grid = UI.ScrollingGrid { + x = 2, ex = -6, y = 3, ey = -5, + columns = { + { heading = 'Extension', key = 'name' }, + { heading = 'Program', key = 'value' }, + }, + autospace = true, + sortColumn = 'name', + accelerators = { + delete = 'remove_entry', + }, + }, + remove = UI.Button { + x = -4, y = 6, + text = '-', event = 'remove_entry', help = 'Remove', + }, + form = UI.Form { + x = 3, y = -3, ey = -2, + margin = 1, + manualControls = true, + [1] = UI.TextEntry { + width = 20, + formLabel = 'Extension', formKey = 'name', + shadowText = 'extension', + required = true, + limit = 64, + }, + [2] = UI.TextEntry { + width = 20, + formLabel = 'Program', formKey = 'value', + shadowText = 'program', + required = true, + limit = 128, + }, + add = UI.Button { + x = -11, y = 1, + text = 'Add', event = 'add_association', + }, + }, + statusBar = UI.StatusBar { + backgroundColor = colors.cyan, + }, + }, + accelerators = { + q = 'quit', + c = 'cedit', + e = 'edit', + s = 'shell', + p = 'pastebin', + r = 'refresh', + [ ' ' ] = 'mark', + m = 'mark', + backspace = 'updir', + u = 'unmark', + d = 'delete', + delete = 'delete', + [ 'control-h' ] = 'toggle_hidden', + [ 'control-s' ] = 'toggle_dirSize', + [ 'control-x' ] = 'cut', + [ 'control-c' ] = 'copy', + paste = 'paste', + }, } function Browser:enable() - UI.Page.enable(self) - self:setFocus(self.grid) + UI.Page.enable(self) + self:setFocus(self.grid) end function Browser.menuBar:getActive(menuItem) - local file = Browser.grid:getSelected() - if menuItem.flags == FILE then - return file and not file.isDir - end - return true + local file = Browser.grid:getSelected() + if menuItem.flags == FILE then + return file and not file.isDir + end + return true end function Browser.grid:sortCompare(a, b) - if self.sortColumn == 'fsize' then - return a.size < b.size - elseif self.sortColumn == 'flags' then - return a.flags < b.flags - end - if a.isDir == b.isDir then - return a.name:lower() < b.name:lower() - end - return a.isDir + if self.sortColumn == 'fsize' then + return a.size < b.size + elseif self.sortColumn == 'flags' then + return a.flags < b.flags + end + if a.isDir == b.isDir then + return a.name:lower() < b.name:lower() + end + return a.isDir end function Browser.grid:getRowTextColor(file) - if file.marked then - return colors.green - end - if file.isDir then - return colors.cyan - end - if file.isReadOnly then - return colors.pink - end - return colors.white + if file.marked then + return colors.green + end + if file.isDir then + return colors.cyan + end + if file.isReadOnly then + return colors.pink + end + return colors.white end function Browser.grid:eventHandler(event) - if event.type == 'copy' then -- let copy be handled by parent - return false - end - return UI.ScrollingGrid.eventHandler(self, event) + if event.type == 'copy' then -- let copy be handled by parent + return false + end + return UI.ScrollingGrid.eventHandler(self, event) end function Browser.statusBar:draw() - if self.parent.dir then - local info = '#:' .. Util.size(self.parent.dir.files) - local numMarked = Util.size(marked) - if numMarked > 0 then - info = info .. ' M:' .. numMarked - end - self:setValue('info', info) - self:setValue('totalSize', formatSize(self.parent.dir.totalSize)) - UI.StatusBar.draw(self) - end + if self.parent.dir then + local info = '#:' .. Util.size(self.parent.dir.files) + local numMarked = Util.size(marked) + if numMarked > 0 then + info = info .. ' M:' .. numMarked + end + self:setValue('info', info) + self:setValue('totalSize', formatSize(self.parent.dir.totalSize)) + UI.StatusBar.draw(self) + end end function Browser:setStatus(status, ...) - self.notification:info(string.format(status, ...)) + self.notification:info(string.format(status, ...)) end function Browser:unmarkAll() - for _,m in pairs(marked) do - m.marked = false - end - Util.clear(marked) + for _,m in pairs(marked) do + m.marked = false + end + Util.clear(marked) end function Browser:getDirectory(directory) - local s, dir = pcall(function() + local s, dir = pcall(function() - local dir = directories[directory] - if not dir then - dir = { - name = directory, - size = 0, - files = { }, - totalSize = 0, - index = 1 - } - directories[directory] = dir - end + local dir = directories[directory] + if not dir then + dir = { + name = directory, + size = 0, + files = { }, + totalSize = 0, + index = 1 + } + directories[directory] = dir + end - self:updateDirectory(dir) + self:updateDirectory(dir) - return dir - end) + return dir + end) - return s, dir + return s, dir end function Browser:updateDirectory(dir) - dir.size = 0 - dir.totalSize = 0 - Util.clear(dir.files) + dir.size = 0 + dir.totalSize = 0 + Util.clear(dir.files) - local files = fs.listEx(dir.name) - if files then - dir.size = #files - for _, file in pairs(files) do - file.fullName = fs.combine(dir.name, file.name) - file.flags = '' - if not file.isDir then - dir.totalSize = dir.totalSize + file.size - file.fsize = formatSize(file.size) - else - if config.showDirSizes then - file.size = fs.getSize(file.fullName, true) + local files = fs.listEx(dir.name) + if files then + dir.size = #files + for _, file in pairs(files) do + file.fullName = fs.combine(dir.name, file.name) + file.flags = '' + if not file.isDir then + dir.totalSize = dir.totalSize + file.size + file.fsize = formatSize(file.size) + else + if config.showDirSizes then + file.size = fs.getSize(file.fullName, true) - dir.totalSize = dir.totalSize + file.size - file.fsize = formatSize(file.size) - end - file.flags = 'D' - end - if file.isReadOnly then - file.flags = file.flags .. 'R' - end - if config.showHidden or file.name:sub(1, 1) ~= '.' then - dir.files[file.fullName] = file - end - end - end + dir.totalSize = dir.totalSize + file.size + file.fsize = formatSize(file.size) + end + file.flags = 'D' + end + if file.isReadOnly then + file.flags = file.flags .. 'R' + end + if config.showHidden or file.name:sub(1, 1) ~= '.' then + dir.files[file.fullName] = file + end + end + end -- self.grid:update() -- self.grid:setIndex(dir.index) - self.grid:setValues(dir.files) + self.grid:setValues(dir.files) end function Browser:setDir(dirName, noStatus) - self:unmarkAll() + self:unmarkAll() - if self.dir then - self.dir.index = self.grid:getIndex() - end - local DIR = fs.combine('', dirName) - shell.setDir(DIR) - local s, dir = self:getDirectory(DIR) - if s then - self.dir = dir - elseif noStatus then - error(dir) - else - self:setStatus(dir) - self:setDir('', true) - return - end + if self.dir then + self.dir.index = self.grid:getIndex() + end + local DIR = fs.combine('', dirName) + shell.setDir(DIR) + local s, dir = self:getDirectory(DIR) + if s then + self.dir = dir + elseif noStatus then + error(dir) + else + self:setStatus(dir) + self:setDir('', true) + return + end - if not noStatus then - self.statusBar:setValue('status', '/' .. self.dir.name) - self.statusBar:draw() - end - self.grid:setIndex(self.dir.index) + if not noStatus then + self.statusBar:setValue('status', '/' .. self.dir.name) + self.statusBar:draw() + end + self.grid:setIndex(self.dir.index) end function Browser:run(...) - if multishell then - local tabId = shell.openTab(...) - multishell.setFocus(tabId) - else - shell.run(...) - Event.terminate = false - self:draw() - end + if multishell then + local tabId = shell.openTab(...) + multishell.setFocus(tabId) + else + shell.run(...) + Event.terminate = false + self:draw() + end end function Browser:hasMarked() - if Util.size(marked) == 0 then - local file = self.grid:getSelected() - if file then - file.marked = true - marked[file.fullName] = file - self.grid:draw() - end - end - return Util.size(marked) > 0 + if Util.size(marked) == 0 then + local file = self.grid:getSelected() + if file then + file.marked = true + marked[file.fullName] = file + self.grid:draw() + end + end + return Util.size(marked) > 0 end function Browser:eventHandler(event) - local file = self.grid:getSelected() + local file = self.grid:getSelected() - if event.type == 'quit' then - Event.exitPullEvents() + if event.type == 'quit' then + Event.exitPullEvents() - elseif event.type == 'edit' and file then - self:run('edit', file.name) + elseif event.type == 'edit' and file then + self:run('edit', file.name) - elseif event.type == 'cedit' and file then - self:run('cedit', file.name) - self:setStatus('Started cloud edit') + elseif event.type == 'cedit' and file then + self:run('cedit', file.name) + self:setStatus('Started cloud edit') - elseif event.type == 'shell' then - self:run('sys/apps/shell.lua') + elseif event.type == 'shell' then + self:run('sys/apps/shell.lua') - elseif event.type == 'refresh' then - self:updateDirectory(self.dir) - self.grid:draw() - self:setStatus('Refreshed') + elseif event.type == 'refresh' then + self:updateDirectory(self.dir) + self.grid:draw() + self:setStatus('Refreshed') - elseif event.type == 'associate' then - self.associations:show() + elseif event.type == 'associate' then + self.associations:show() - elseif event.type == 'pastebin' then - if file and not file.isDir then - local s, m = pastebin.put(file.fullName) - if s then - os.queueEvent('clipboard_copy', s) - self.notification:success(string.format('Uploaded as %s', s), 0) - else - self.notification:error(m) - end - end + elseif event.type == 'pastebin' then + if file and not file.isDir then + local s, m = pastebin.put(file.fullName) + if s then + os.queueEvent('clipboard_copy', s) + self.notification:success(string.format('Uploaded as %s', s), 0) + else + self.notification:error(m) + end + end - elseif event.type == 'toggle_hidden' then - config.showHidden = not config.showHidden - Config.update('Files', config) + elseif event.type == 'toggle_hidden' then + config.showHidden = not config.showHidden + Config.update('Files', config) - self:updateDirectory(self.dir) - self.grid:draw() - if not config.showHidden then - self:setStatus('Hiding hidden') - else - self:setStatus('Displaying hidden') - end + self:updateDirectory(self.dir) + self.grid:draw() + if not config.showHidden then + self:setStatus('Hiding hidden') + else + self:setStatus('Displaying hidden') + end - elseif event.type == 'toggle_dirSize' then - config.showDirSizes = not config.showDirSizes - Config.update('Files', config) + elseif event.type == 'toggle_dirSize' then + config.showDirSizes = not config.showDirSizes + Config.update('Files', config) - self:updateDirectory(self.dir) - self.grid:draw() - if config.showDirSizes then - self:setStatus('Displaying dir sizes') - end + self:updateDirectory(self.dir) + self.grid:draw() + if config.showDirSizes then + self:setStatus('Displaying dir sizes') + end - elseif event.type == 'mark' and file then - file.marked = not file.marked - if file.marked then - marked[file.fullName] = file - else - marked[file.fullName] = nil - end - self.grid:draw() - self.statusBar:draw() + elseif event.type == 'mark' and file then + file.marked = not file.marked + if file.marked then + marked[file.fullName] = file + else + marked[file.fullName] = nil + end + self.grid:draw() + self.statusBar:draw() - elseif event.type == 'unmark' then - self:unmarkAll() - self.grid:draw() - self:setStatus('Marked files cleared') + elseif event.type == 'unmark' then + self:unmarkAll() + self.grid:draw() + self:setStatus('Marked files cleared') - elseif event.type == 'grid_select' or event.type == 'run' then - if file then - if file.isDir then - self:setDir(file.fullName) - else - local ext = file.name:match('%.(%w+)$') - if ext and config.associations[ext] then - self:run(config.associations[ext], '/' .. file.fullName) - else - self:run(file.name) - end - end - end + elseif event.type == 'grid_select' or event.type == 'run' then + if file then + if file.isDir then + self:setDir(file.fullName) + else + local ext = file.name:match('%.(%w+)$') + if ext and config.associations[ext] then + self:run(config.associations[ext], '/' .. file.fullName) + else + self:run(file.name) + end + end + end - elseif event.type == 'updir' then - local dir = (self.dir.name:match("(.*/)")) - self:setDir(dir or '/') + elseif event.type == 'updir' then + local dir = (self.dir.name:match("(.*/)")) + self:setDir(dir or '/') - elseif event.type == 'delete' then - if self:hasMarked() then - local width = self.statusBar:getColumnWidth('status') - self.statusBar:setColumnWidth('status', UI.term.width) - self.statusBar:setValue('status', 'Delete marked? (y/n)') - self.statusBar:draw() - self.statusBar:sync() - local _, ch = os.pullEvent('char') - if ch == 'y' or ch == 'Y' then - for _,m in pairs(marked) do - pcall(function() - fs.delete(m.fullName) - end) - end - end - marked = { } - self.statusBar:setColumnWidth('status', width) - self.statusBar:setValue('status', '/' .. self.dir.name) - self:updateDirectory(self.dir) + elseif event.type == 'delete' then + if self:hasMarked() then + local width = self.statusBar:getColumnWidth('status') + self.statusBar:setColumnWidth('status', UI.term.width) + self.statusBar:setValue('status', 'Delete marked? (y/n)') + self.statusBar:draw() + self.statusBar:sync() + local _, ch = os.pullEvent('char') + if ch == 'y' or ch == 'Y' then + for _,m in pairs(marked) do + pcall(function() + fs.delete(m.fullName) + end) + end + end + marked = { } + self.statusBar:setColumnWidth('status', width) + self.statusBar:setValue('status', '/' .. self.dir.name) + self:updateDirectory(self.dir) - self.statusBar:draw() - self.grid:draw() - self:setFocus(self.grid) - end + self.statusBar:draw() + self.grid:draw() + self:setFocus(self.grid) + end - elseif event.type == 'copy' or event.type == 'cut' then - if self:hasMarked() then - cutMode = event.type == 'cut' - Util.clear(copied) - Util.merge(copied, marked) - --self:unmarkAll() - self.grid:draw() - self:setStatus('Copied %d file(s)', Util.size(copied)) - end + elseif event.type == 'copy' or event.type == 'cut' then + if self:hasMarked() then + cutMode = event.type == 'cut' + Util.clear(copied) + Util.merge(copied, marked) + --self:unmarkAll() + self.grid:draw() + self:setStatus('Copied %d file(s)', Util.size(copied)) + end - elseif event.type == 'copy_path' then - if file then - os.queueEvent('clipboard_copy', file.fullName) - end + elseif event.type == 'copy_path' then + if file then + os.queueEvent('clipboard_copy', file.fullName) + end - elseif event.type == 'paste' then - for _,m in pairs(copied) do - local s, m = pcall(function() - if cutMode then - fs.move(m.fullName, fs.combine(self.dir.name, m.name)) - else - fs.copy(m.fullName, fs.combine(self.dir.name, m.name)) - end - end) - end - self:updateDirectory(self.dir) - self.grid:draw() - self:setStatus('Pasted ' .. Util.size(copied) .. ' file(s)') + elseif event.type == 'paste' then + for _,m in pairs(copied) do + local s, m = pcall(function() + if cutMode then + fs.move(m.fullName, fs.combine(self.dir.name, m.name)) + else + fs.copy(m.fullName, fs.combine(self.dir.name, m.name)) + end + end) + end + self:updateDirectory(self.dir) + self.grid:draw() + self:setStatus('Pasted ' .. Util.size(copied) .. ' file(s)') - else - return UI.Page.eventHandler(self, event) - end - self:setFocus(self.grid) - return true + else + return UI.Page.eventHandler(self, event) + end + self:setFocus(self.grid) + return true end --[[ Associations slide out ]] -- function Browser.associations:show() - self.grid.values = { } - for k, v in pairs(config.associations) do - table.insert(self.grid.values, { - name = k, - value = v, - }) - end - self.grid:update() - UI.SlideOut.show(self) - self:setFocus(self.form[1]) + self.grid.values = { } + for k, v in pairs(config.associations) do + table.insert(self.grid.values, { + name = k, + value = v, + }) + end + self.grid:update() + UI.SlideOut.show(self) + self:setFocus(self.form[1]) end function Browser.associations:eventHandler(event) - if event.type == 'remove_entry' then - local row = self.grid:getSelected() - if row then - Util.removeByValue(self.grid.values, row) - self.grid:update() - self.grid:draw() - end + if event.type == 'remove_entry' then + local row = self.grid:getSelected() + if row then + Util.removeByValue(self.grid.values, row) + self.grid:update() + self.grid:draw() + end - elseif event.type == 'add_association' then - if self.form:save() then - local entry = Util.find(self.grid.values, 'name', self.form[1].value) or { } - entry.name = self.form[1].value - entry.value = self.form[2].value - table.insert(self.grid.values, entry) - self.form[1]:reset() - self.form[2]:reset() - self.grid:update() - self.grid:draw() - end + elseif event.type == 'add_association' then + if self.form:save() then + local entry = Util.find(self.grid.values, 'name', self.form[1].value) or { } + entry.name = self.form[1].value + entry.value = self.form[2].value + table.insert(self.grid.values, entry) + self.form[1]:reset() + self.form[2]:reset() + self.grid:update() + self.grid:draw() + end - elseif event.type == 'cancel' then - self:hide() + elseif event.type == 'cancel' then + self:hide() - elseif event.type == 'save' then - config.associations = { } - for _, v in pairs(self.grid.values) do - config.associations[v.name] = v.value - end - Config.update('Files', config) - self:hide() + elseif event.type == 'save' then + config.associations = { } + for _, v in pairs(self.grid.values) do + config.associations[v.name] = v.value + end + Config.update('Files', config) + self:hide() - else - return UI.SlideOut.eventHandler(self, event) - end - return true + else + return UI.SlideOut.eventHandler(self, event) + end + return true end --[[-- Startup logic --]]-- diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 4eba8ab..bfc080b 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -294,7 +294,6 @@ end function page:rawExecute(s) local fn, m local wrapped - local t = os.clock() fn = load('return (' ..s.. ')', 'lua', nil, sandboxEnv) @@ -303,6 +302,7 @@ function page:rawExecute(s) wrapped = true end + local t = os.clock() if fn then fn, m = pcall(fn) if #m <= 1 and wrapped then @@ -311,19 +311,24 @@ function page:rawExecute(s) else fn, m = load(s, 'lua', nil, sandboxEnv) if fn then + t = os.clock() fn, m = pcall(fn) end end 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 - 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') + else + print() end else _G.printError(m) diff --git a/sys/apps/Network.lua b/sys/apps/Network.lua index 8b7b7b2..e6341eb 100644 --- a/sys/apps/Network.lua +++ b/sys/apps/Network.lua @@ -127,12 +127,14 @@ local function sendCommand(host, command) end end +--[[ function page.ports:eventHandler(event) if event.type == 'grid_select' then shell.openForegroundTab('sniff ' .. event.selected.port) end return UI.SlideOut.eventHandler(self, event) end +]] function page.ports.grid:update() local function findConnection(port) diff --git a/sys/apps/ShellLauncher.lua b/sys/apps/ShellLauncher.lua index 9407e46..d905653 100644 --- a/sys/apps/ShellLauncher.lua +++ b/sys/apps/ShellLauncher.lua @@ -5,23 +5,23 @@ local shell = _ENV.shell local launcherTab = kernel.getCurrent() kernel.hook('kernel_focus', function(_, eventData) - local focusTab = eventData and eventData[1] - if focusTab == launcherTab.uid then - local previousTab = eventData[2] - local nextTab = launcherTab - if not previousTab then - for _, v in pairs(kernel.routines) do - if not v.hidden and v.uid > nextTab.uid then - nextTab = v - end - end - end - if nextTab == launcherTab then - shell.switchTab(shell.openTab('sys/apps/shell.lua')) - else - shell.switchTab(nextTab.uid) - end - end + local focusTab = eventData and eventData[1] + if focusTab == launcherTab.uid then + local previousTab = eventData[2] + local nextTab = launcherTab + if not previousTab then + for _, v in pairs(kernel.routines) do + if not v.hidden and v.uid > nextTab.uid then + nextTab = v + end + end + end + if nextTab == launcherTab then + shell.switchTab(shell.openTab('sys/apps/shell.lua')) + else + shell.switchTab(nextTab.uid) + end + end end) -while os.pullEventRaw() do end \ No newline at end of file +os.pullEventRaw('kernel_halt') \ No newline at end of file diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index e735840..80d170e 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -23,115 +23,115 @@ local packagesIntro = [[Setup Complete local page = UI.Page { wizard = UI.Wizard { - ey = -2, + ey = -2, pages = { splash = UI.WizardPage { - index = 1, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 2, ey = -2, - value = string.format(splashIntro, Ansi.white), - }, - }, + index = 1, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = string.format(splashIntro, Ansi.white), + }, + }, label = UI.WizardPage { - index = 2, - labelText = UI.Text { - x = 3, y = 2, - value = 'Label' - }, - label = UI.TextEntry { - x = 9, y = 2, ex = -3, - limit = 32, - value = os.getComputerLabel(), - }, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 4, ey = -3, - value = string.format(labelIntro, Ansi.white), - }, - }, + index = 2, + labelText = UI.Text { + x = 3, y = 2, + value = 'Label' + }, + label = UI.TextEntry { + x = 9, y = 2, ex = -3, + limit = 32, + value = os.getComputerLabel(), + }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 4, ey = -3, + value = string.format(labelIntro, Ansi.white), + }, + }, password = UI.WizardPage { index = 3, - passwordLabel = UI.Text { - x = 3, y = 2, - value = 'Password' - }, - newPass = UI.TextEntry { - x = 12, ex = -3, y = 2, - limit = 32, - mask = true, - shadowText = 'password', - }, + passwordLabel = UI.Text { + x = 3, y = 2, + value = 'Password' + }, + newPass = UI.TextEntry { + x = 12, ex = -3, y = 2, + limit = 32, + mask = true, + shadowText = 'password', + }, --[[ - groupLabel = UI.Text { - x = 3, y = 3, - value = 'Group' - }, - group = UI.TextEntry { - x = 12, ex = -3, y = 3, - limit = 32, - shadowText = 'network group', - }, + groupLabel = UI.Text { + x = 3, y = 3, + value = 'Group' + }, + group = UI.TextEntry { + x = 12, ex = -3, y = 3, + limit = 32, + shadowText = 'network group', + }, ]] - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 5, ey = -3, - value = string.format(passwordIntro, Ansi.white), - }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 5, ey = -3, + value = string.format(passwordIntro, Ansi.white), + }, }, packages = UI.WizardPage { index = 4, - button = UI.Button { - x = 3, y = -3, - text = 'Open Package Manager', - event = 'packages', - }, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 2, ey = -4, - value = string.format(packagesIntro, Ansi.white), - }, + button = UI.Button { + x = 3, y = -3, + text = 'Open Package Manager', + event = 'packages', + }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -4, + value = string.format(packagesIntro, Ansi.white), + }, }, }, - }, - notification = UI.Notification { }, + }, + notification = UI.Notification { }, } function page.wizard.pages.label:validate() - os.setComputerLabel(self.label.value) - return true + os.setComputerLabel(self.label.value) + return true end function page.wizard.pages.password:validate() - if #self.newPass.value > 0 then - Security.updatePassword(SHA1.sha1(self.newPass.value)) - end - --[[ - if #self.group.value > 0 then - local config = Config.load('os') - config.group = self.group.value - Config.update('os', config) - end - ]] - return true + if #self.newPass.value > 0 then + Security.updatePassword(SHA1.sha1(self.newPass.value)) + end + --[[ + if #self.group.value > 0 then + local config = Config.load('os') + config.group = self.group.value + Config.update('os', config) + end + ]] + return true end function page:eventHandler(event) - if event.type == 'skip' then - self.wizard:emit({ type = 'nextView' }) + if event.type == 'skip' then + self.wizard:emit({ type = 'nextView' }) - elseif event.type == 'view_enabled' then - event.view:focusFirst() + elseif event.type == 'view_enabled' then + event.view:focusFirst() - elseif event.type == 'packages' then - shell.openForegroundTab('PackageManager') + elseif event.type == 'packages' then + shell.openForegroundTab('PackageManager') - elseif event.type == 'wizard_complete' or event.type == 'cancel' then - UI.exitPullEvents() + elseif event.type == 'wizard_complete' or event.type == 'cancel' then + UI.exitPullEvents() else return UI.Page.eventHandler(self, event) diff --git a/sys/apps/autorun.lua b/sys/apps/autorun.lua index 40e0e74..1d650da 100644 --- a/sys/apps/autorun.lua +++ b/sys/apps/autorun.lua @@ -11,60 +11,60 @@ local term = _G.term local success = true local function runDir(directory) - if not fs.exists(directory) then - return true - end + if not fs.exists(directory) then + return true + end - local files = fs.list(directory) - table.sort(files) + local files = fs.list(directory) + table.sort(files) - for _,file in ipairs(files) do - os.sleep(0) - local result, err = shell.run(directory .. '/' .. file) + for _,file in ipairs(files) do + os.sleep(0) + local result, err = shell.run(directory .. '/' .. file) - if result then - if term.isColor() then - term.setTextColor(colors.green) - end - term.write('[PASS] ') - term.setTextColor(colors.white) - term.write(fs.combine(directory, file)) - print() - else - if term.isColor() then - term.setTextColor(colors.red) - end - term.write('[FAIL] ') - term.setTextColor(colors.white) - term.write(fs.combine(directory, file)) - if err then - _G.printError('\n' .. err) - end - print() - success = false - end - end + if result then + if term.isColor() then + term.setTextColor(colors.green) + end + term.write('[PASS] ') + term.setTextColor(colors.white) + term.write(fs.combine(directory, file)) + print() + else + if term.isColor() then + term.setTextColor(colors.red) + end + term.write('[FAIL] ') + term.setTextColor(colors.white) + term.write(fs.combine(directory, file)) + if err then + _G.printError('\n' .. err) + end + print() + success = false + end + end end runDir('sys/autorun') for name in pairs(Packages:installed()) do - local packageDir = 'packages/' .. name .. '/autorun' - runDir(packageDir) + local packageDir = 'packages/' .. name .. '/autorun' + runDir(packageDir) end runDir('usr/autorun') if not success then - if multishell then - multishell.setFocus(multishell.getCurrent()) - end - _G.printError('A startup program has errored') - print('Press enter to continue') + if multishell then + multishell.setFocus(multishell.getCurrent()) + end + _G.printError('A startup program has errored') + print('Press enter to continue') - while true do - local e, code = os.pullEventRaw('key') - if e == 'terminate' or e == 'key' and code == keys.enter then - break - end - end + while true do + local e, code = os.pullEventRaw('key') + if e == 'terminate' or e == 'key' and code == keys.enter then + break + end + end end diff --git a/sys/apps/cedit.lua b/sys/apps/cedit.lua index f62a063..138f977 100644 --- a/sys/apps/cedit.lua +++ b/sys/apps/cedit.lua @@ -7,32 +7,32 @@ local shell = _ENV.shell local args = { ... } if not args[1] then - error('Syntax: cedit ') + error('Syntax: cedit ') end if not _G.http.websocket then - error('Requires CC: Tweaked') + error('Requires CC: Tweaked') end if not _G.cloud_catcher then - local key = Config.load('cloud').key + local key = Config.load('cloud').key - if not key then - print('Visit https://cloud-catcher.squiddev.cc') - print('Paste key: ') - key = read() - if #key == 0 then - return - end - end + if not key then + print('Visit https://cloud-catcher.squiddev.cc') + print('Paste key: ') + key = read() + if #key == 0 then + return + end + end - -- open an unfocused tab - local id = shell.openTab('cloud ' .. key) - print('Connecting...') - while not _G.cloud_catcher do - os.sleep(.2) - end - multishell.setTitle(id, 'Cloud') + -- open an unfocused tab + local id = shell.openTab('cloud ' .. key) + print('Connecting...') + while not _G.cloud_catcher do + os.sleep(.2) + end + multishell.setTitle(id, 'Cloud') end shell.run('cloud edit ' .. table.unpack({ ... })) diff --git a/sys/apps/cshell.lua b/sys/apps/cshell.lua index 44cee34..34e5c91 100644 --- a/sys/apps/cshell.lua +++ b/sys/apps/cshell.lua @@ -4,20 +4,20 @@ local read = _G.read local shell = _ENV.shell if not _G.http.websocket then - error('Requires CC: Tweaked') + error('Requires CC: Tweaked') end if not _G.cloud_catcher then - local key = Config.load('cloud').key + local key = Config.load('cloud').key - if not key then - print('Visit https://cloud-catcher.squiddev.cc') - print('Paste key: ') - key = read() - if #key == 0 then - return - end - end - print('Connecting...') - shell.run('cloud ' .. key) + if not key then + print('Visit https://cloud-catcher.squiddev.cc') + print('Paste key: ') + key = read() + if #key == 0 then + return + end + end + print('Connecting...') + shell.run('cloud ' .. key) end diff --git a/sys/apps/network/redserver.lua b/sys/apps/network/redserver.lua deleted file mode 100644 index 6c63d21..0000000 --- a/sys/apps/network/redserver.lua +++ /dev/null @@ -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) -]] diff --git a/sys/apps/network/snmp.lua b/sys/apps/network/snmp.lua index 0e69a03..11ed673 100644 --- a/sys/apps/network/snmp.lua +++ b/sys/apps/network/snmp.lua @@ -156,7 +156,7 @@ local function sendInfo() info.label = os.getComputerLabel() info.uptime = math.floor(os.clock()) info.group = network.getGroup() - if turtle then + if turtle and turtle.getStatus then info.fuel = turtle.getFuelLevel() info.status = turtle.getStatus() info.point = turtle.point diff --git a/sys/apps/sniff.lua b/sys/apps/sniff.lua deleted file mode 100644 index ada3d77..0000000 --- a/sys/apps/sniff.lua +++ /dev/null @@ -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 diff --git a/sys/apps/system/cloud.lua b/sys/apps/system/cloud.lua index 434a16c..61d47f9 100644 --- a/sys/apps/system/cloud.lua +++ b/sys/apps/system/cloud.lua @@ -7,51 +7,51 @@ local colors = _G.colors -- -t80x30 if _G.http.websocket then - local config = Config.load('cloud') + local config = Config.load('cloud') - local tab = UI.Tab { - tabTitle = 'Cloud', - description = 'Cloud catcher options', - key = UI.TextEntry { - x = 3, ex = -3, y = 2, - limit = 32, - value = config.key, - shadowText = 'Cloud key', - accelerators = { - enter = 'update_key', - }, - }, - button = UI.Button { - x = 3, y = 4, - text = 'Update', - event = 'update_key', - }, - labelText = UI.TextArea { - x = 3, ex = -3, y = 6, - textColor = colors.yellow, - marginLeft = 0, marginRight = 0, - value = string.format( + local tab = UI.Tab { + tabTitle = 'Cloud', + description = 'Cloud catcher options', + key = UI.TextEntry { + x = 3, ex = -3, y = 2, + limit = 32, + value = config.key, + shadowText = 'Cloud key', + accelerators = { + enter = 'update_key', + }, + }, + button = UI.Button { + x = 3, y = 4, + text = 'Update', + event = 'update_key', + }, + labelText = UI.TextArea { + x = 3, ex = -3, y = 6, + textColor = colors.yellow, + marginLeft = 0, marginRight = 0, + value = string.format( [[Use a non-changing cloud key. Note that only a single computer can use this session at one time. To obtain a key, visit: %shttps://cloud-catcher.squiddev.cc%s then bookmark: %shttps://cloud-catcher.squiddev.cc/?id=KEY - ]], - Ansi.white, Ansi.reset, Ansi.white), - }, - } + ]], + Ansi.white, Ansi.reset, Ansi.white), + }, + } - function tab:eventHandler(event) - if event.type == 'update_key' then - if #self.key.value > 0 then - config.key = self.key.value - else - config.key = nil - end - Config.update('cloud', config) - self:emit({ type = 'success_message', message = 'Updated' }) - end - end + function tab:eventHandler(event) + if event.type == 'update_key' then + if #self.key.value > 0 then + config.key = self.key.value + else + config.key = nil + end + Config.update('cloud', config) + self:emit({ type = 'success_message', message = 'Updated' }) + end + end - return tab + return tab end diff --git a/sys/apps/system/kiosk.lua b/sys/apps/system/kiosk.lua index 89a24e2..1adb110 100644 --- a/sys/apps/system/kiosk.lua +++ b/sys/apps/system/kiosk.lua @@ -14,13 +14,13 @@ local tab = UI.Tab { formLabel = 'Monitor', formKey = 'monitor', }, textScale = UI.Chooser { - formLabel = 'Font Size', formKey = 'textScale', - nochoice = 'Small', - choices = { - { name = 'Small', value = '.5' }, - { name = 'Large', value = '1' }, - }, - help = 'Adjust text scaling', + formLabel = 'Font Size', formKey = 'textScale', + nochoice = 'Small', + choices = { + { name = 'Small', value = '.5' }, + { name = 'Large', value = '1' }, + }, + help = 'Adjust text scaling', }, labelText = UI.TextArea { x = 2, ex = -2, y = 5, diff --git a/sys/apps/system/launcher.lua b/sys/apps/system/launcher.lua index 5108ad0..cebd5b7 100644 --- a/sys/apps/system/launcher.lua +++ b/sys/apps/system/launcher.lua @@ -9,76 +9,76 @@ local config = Config.load('multishell') local tab = UI.Tab { tabTitle = 'Launcher', description = 'Set the application launcher', - launcherLabel = UI.Text { - x = 3, y = 2, - value = 'Launcher', - }, - launcher = UI.Chooser { - x = 13, y = 2, width = 12, - choices = { - { name = 'Overview', value = 'sys/apps/Overview.lua' }, - { name = 'Shell', value = 'sys/apps/ShellLauncher.lua' }, - { name = 'Custom', value = 'custom' }, - }, - }, - custom = UI.TextEntry { - x = 13, ex = -3, y = 3, - limit = 128, - shadowText = 'File name', - }, - button = UI.Button { - x = 3, y = 5, - text = 'Update', - event = 'update', - }, - labelText = UI.TextArea { - x = 3, ex = -3, y = 7, - textColor = colors.yellow, - value = 'Choose an application launcher', - }, + launcherLabel = UI.Text { + x = 3, y = 2, + value = 'Launcher', + }, + launcher = UI.Chooser { + x = 13, y = 2, width = 12, + choices = { + { name = 'Overview', value = 'sys/apps/Overview.lua' }, + { name = 'Shell', value = 'sys/apps/ShellLauncher.lua' }, + { name = 'Custom', value = 'custom' }, + }, + }, + custom = UI.TextEntry { + x = 13, ex = -3, y = 3, + limit = 128, + shadowText = 'File name', + }, + button = UI.Button { + x = 3, y = 5, + text = 'Update', + event = 'update', + }, + labelText = UI.TextArea { + x = 3, ex = -3, y = 7, + textColor = colors.yellow, + value = 'Choose an application launcher', + }, } 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 - if v.value == config.launcher then - launcher = v.value - break - end - end + for _, v in pairs(self.launcher.choices) do + if v.value == config.launcher then + launcher = v.value + break + end + end UI.Tab.enable(self) - self.launcher.value = launcher - self.custom.enabled = launcher == 'custom' + self.launcher.value = launcher + self.custom.enabled = launcher == 'custom' end function tab:eventHandler(event) if event.type == 'choice_change' then - self.custom.enabled = event.value == 'custom' - if self.custom.enabled then - self.custom.value = config.launcher - end - self:draw() + self.custom.enabled = event.value == 'custom' + if self.custom.enabled then + self.custom.value = config.launcher + end + self:draw() - elseif event.type == 'update' then - local launcher + elseif event.type == 'update' then + local launcher - if self.launcher.value ~= 'custom' then - launcher = self.launcher.value - elseif fs.exists(self.custom.value) and not fs.isDir(self.custom.value) then - launcher = self.custom.value - end + if self.launcher.value ~= 'custom' then + launcher = self.launcher.value + elseif fs.exists(self.custom.value) and not fs.isDir(self.custom.value) then + launcher = self.custom.value + end - if launcher then - config.launcher = launcher - Config.update('multishell', config) - self:emit({ type = 'success_message', message = 'Updated' }) - else - self:emit({ type = 'error_message', message = 'Invalid file' }) - end - end + if launcher then + config.launcher = launcher + Config.update('multishell', config) + self:emit({ type = 'success_message', message = 'Updated' }) + else + self:emit({ type = 'error_message', message = 'Invalid file' }) + end + end end return tab diff --git a/sys/apps/system/path.lua b/sys/apps/system/path.lua index 504c7e1..803ed51 100644 --- a/sys/apps/system/path.lua +++ b/sys/apps/system/path.lua @@ -13,91 +13,91 @@ local tab = UI.Tab { accelerators = { enter = 'update_path', }, - help = 'add a new path', + help = 'add a new path', }, grid = UI.Grid { y = 4, ey = -3, disableHeader = true, columns = { { key = 'value' } }, autospace = true, - sortColumn = 'index', - help = 'double-click to remove, shift-arrow to move', - accelerators = { - delete = 'remove', - }, + sortColumn = 'index', + help = 'double-click to remove, shift-arrow to move', + accelerators = { + 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) self.grid.values = { } for k,v in ipairs(Util.split(path, '(.-):')) do table.insert(self.grid.values, { index = k, value = v }) - end - self.grid:update() + end + self.grid:update() end function tab:enable() - local env = Config.load('shell') - self:updateList(env.path) + local env = Config.load('shell') + self:updateList(env.path) UI.Tab.enable(self) end function tab:save() - local t = { } - for _, v in ipairs(self.grid.values) do - table.insert(t, v.value) - end - local env = Config.load('shell') - env.path = table.concat(t, ':') - self:updateList(env.path) - Config.update('shell', env) + local t = { } + for _, v in ipairs(self.grid.values) do + table.insert(t, v.value) + end + local env = Config.load('shell') + env.path = table.concat(t, ':') + self:updateList(env.path) + Config.update('shell', env) end function tab:eventHandler(event) - if event.type == 'update_path' then - table.insert(self.grid.values, { - value = self.entry.value, - }) + if event.type == 'update_path' then + table.insert(self.grid.values, { + value = self.entry.value, + }) self:save() self.entry:reset() - self.entry:draw() - self.grid:draw() + self.entry:draw() + self.grid:draw() return true - elseif event.type == 'grid_select' or event.type == 'remove' then - local selected = self.grid:getSelected() - if selected then - table.remove(self.grid.values, selected.index) - self:save() - self.grid:draw() - end + elseif event.type == 'grid_select' or event.type == 'remove' then + local selected = self.grid:getSelected() + if selected then + table.remove(self.grid.values, selected.index) + self:save() + self.grid:draw() + end - elseif event.type == 'focus_change' then + elseif event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help) - elseif event.type == 'move_up' then - local entry = self.grid:getSelected() - if entry.index > 1 then - table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index)) - self.grid:setIndex(entry.index - 1) - self:save() - self.grid:draw() - end + elseif event.type == 'move_up' then + local entry = self.grid:getSelected() + if entry.index > 1 then + table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index - 1) + self:save() + self.grid:draw() + end - elseif event.type == 'move_down' then - local entry = self.grid:getSelected() - if entry.index < #self.grid.values then - table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index)) - self.grid:setIndex(entry.index + 1) - self:save() - self.grid:draw() - end - end + elseif event.type == 'move_down' then + local entry = self.grid:getSelected() + if entry.index < #self.grid.values then + table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index + 1) + self:save() + self.grid:draw() + end + end end return tab diff --git a/sys/apps/system/requires.lua b/sys/apps/system/requires.lua index 2bd97cf..b98d93b 100644 --- a/sys/apps/system/requires.lua +++ b/sys/apps/system/requires.lua @@ -13,91 +13,91 @@ local tab = UI.Tab { accelerators = { enter = 'update_path', }, - help = 'add a new path (reboot required)', + help = 'add a new path (reboot required)', }, grid = UI.Grid { y = 4, ey = -3, disableHeader = true, columns = { { key = 'value' } }, - autospace = true, - sortColumn = 'index', - help = 'double-click to remove, shift-arrow to move', - accelerators = { - delete = 'remove', - }, - }, - statusBar = UI.StatusBar { }, - accelerators = { - [ 'shift-up' ] = 'move_up', - [ 'shift-down' ] = 'move_down', - }, + autospace = true, + sortColumn = 'index', + help = 'double-click to remove, shift-arrow to move', + accelerators = { + delete = 'remove', + }, + }, + statusBar = UI.StatusBar { }, + accelerators = { + [ 'shift-up' ] = 'move_up', + [ 'shift-down' ] = 'move_down', + }, } function tab:updateList(lua_path) self.grid.values = { } for k,v in ipairs(Util.split(lua_path, '(.-);')) do table.insert(self.grid.values, { index = k, value = v }) - end - self.grid:update() + end + self.grid:update() end function tab:enable() - local env = Config.load('shell') - self:updateList(env.lua_path) + local env = Config.load('shell') + self:updateList(env.lua_path) UI.Tab.enable(self) end function tab:save() - local t = { } - for _, v in ipairs(self.grid.values) do - table.insert(t, v.value) - end - local env = Config.load('shell') - env.lua_path = table.concat(t, ';') - self:updateList(env.lua_path) - Config.update('shell', env) + local t = { } + for _, v in ipairs(self.grid.values) do + table.insert(t, v.value) + end + local env = Config.load('shell') + env.lua_path = table.concat(t, ';') + self:updateList(env.lua_path) + Config.update('shell', env) end function tab:eventHandler(event) - if event.type == 'update_path' then - table.insert(self.grid.values, { - value = self.entry.value, - }) - self:save() + if event.type == 'update_path' then + table.insert(self.grid.values, { + value = self.entry.value, + }) + self:save() self.entry:reset() - self.entry:draw() - self.grid:draw() + self.entry:draw() + self.grid:draw() return true - elseif event.type == 'grid_select' or event.type == 'remove' then - local selected = self.grid:getSelected() - if selected then - table.remove(self.grid.values, selected.index) - self:save() - self.grid:draw() - end + elseif event.type == 'grid_select' or event.type == 'remove' then + local selected = self.grid:getSelected() + if selected then + table.remove(self.grid.values, selected.index) + self:save() + self.grid:draw() + end - elseif event.type == 'focus_change' then + elseif event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help) - elseif event.type == 'move_up' then - local entry = self.grid:getSelected() - if entry.index > 1 then - table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index)) - self.grid:setIndex(entry.index - 1) - self:save() - self.grid:draw() - end + elseif event.type == 'move_up' then + local entry = self.grid:getSelected() + if entry.index > 1 then + table.insert(self.grid.values, entry.index - 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index - 1) + self:save() + self.grid:draw() + end - elseif event.type == 'move_down' then - local entry = self.grid:getSelected() - if entry.index < #self.grid.values then - table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index)) - self.grid:setIndex(entry.index + 1) - self:save() - self.grid:draw() - end - end + elseif event.type == 'move_down' then + local entry = self.grid:getSelected() + if entry.index < #self.grid.values then + table.insert(self.grid.values, entry.index + 1, table.remove(self.grid.values, entry.index)) + self.grid:setIndex(entry.index + 1) + self:save() + self.grid:draw() + end + end end return tab diff --git a/sys/apps/system/shell.lua b/sys/apps/system/shell.lua index ee413f5..238a8d9 100644 --- a/sys/apps/system/shell.lua +++ b/sys/apps/system/shell.lua @@ -9,134 +9,134 @@ local config = Config.load('shellprompt') local allColors = { } for k,v in pairs(colors) do - if type(v) == 'number' then - table.insert(allColors, { name = k, value = v }) - end + if type(v) == 'number' then + table.insert(allColors, { name = k, value = v }) + end end local defaults = { - textColor = colors.white, - commandTextColor = colors.yellow, - directoryTextColor = colors.orange, - directoryBackgroundColor = colors.black, - promptTextColor = colors.blue, - promptBackgroundColor = colors.black, - directoryColor = colors.green, - fileColor = colors.white, - backgroundColor = colors.black, + textColor = colors.white, + commandTextColor = colors.yellow, + directoryTextColor = colors.orange, + directoryBackgroundColor = colors.black, + promptTextColor = colors.blue, + promptBackgroundColor = colors.black, + directoryColor = colors.green, + fileColor = colors.white, + backgroundColor = colors.black, } local _colors = config.color or Util.shallowCopy(defaults) local allSettings = { } for k, v in pairs(defaults) do - table.insert(allSettings, { name = k }) + table.insert(allSettings, { name = k }) end -- temp if not _colors.backgroundColor then - _colors.backgroundColor = colors.black - _colors.fileColor = colors.white + _colors.backgroundColor = colors.black + _colors.fileColor = colors.white end local tab = UI.Tab { tabTitle = 'Shell', - description = 'Shell options', - grid1 = UI.ScrollingGrid { - 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, + description = 'Shell options', + grid1 = UI.ScrollingGrid { + y = 2, ey = -10, x = 3, ex = -16, disableHeader = true, columns = { { key = 'name' } }, - values = allColors, - sortColumn = 'name', + values = allSettings, + 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) - local selected = tab.grid1:getSelected() - if _colors[selected.name] == row.value then - return colors.yellow - end - return UI.Grid.getRowTextColor(self, row) + local selected = tab.grid1:getSelected() + if _colors[selected.name] == row.value then + return colors.yellow + end + return UI.Grid.getRowTextColor(self, row) end function tab.display:draw() - self:clear(_colors.backgroundColor) - local offset = 0 - if config.displayDirectory then - self:write(1, 1, - '==' .. os.getComputerLabel() .. ':/dir/etc', - _colors.directoryBackgroundColor, _colors.directoryTextColor) - offset = 1 - end + self:clear(_colors.backgroundColor) + local offset = 0 + if config.displayDirectory then + self:write(1, 1, + '==' .. os.getComputerLabel() .. ':/dir/etc', + _colors.directoryBackgroundColor, _colors.directoryTextColor) + offset = 1 + end - self:write(1, 1 + offset, '$ ', - _colors.promptBackgroundColor, _colors.promptTextColor) + self:write(1, 1 + offset, '$ ', + _colors.promptBackgroundColor, _colors.promptTextColor) - self:write(3, 1 + offset, 'ls /', - _colors.backgroundColor, _colors.commandTextColor) + self:write(3, 1 + offset, 'ls /', + _colors.backgroundColor, _colors.commandTextColor) - self:write(1, 2 + offset, 'sys usr', - _colors.backgroundColor, _colors.directoryColor) + self:write(1, 2 + offset, 'sys usr', + _colors.backgroundColor, _colors.directoryColor) - self:write(1, 3 + offset, 'startup', - _colors.backgroundColor, _colors.fileColor) + self:write(1, 3 + offset, 'startup', + _colors.backgroundColor, _colors.fileColor) end function tab:eventHandler(event) - if event.type =='checkbox_change' then - config.displayDirectory = not not event.checked - self.display:draw() + if event.type =='checkbox_change' then + config.displayDirectory = not not event.checked + self.display:draw() - elseif event.type == 'grid_focus_row' and event.element == self.grid1 then - self.grid2:draw() + elseif event.type == 'grid_focus_row' and event.element == self.grid1 then + self.grid2:draw() - elseif event.type == 'grid_select' and event.element == self.grid2 then - _colors[tab.grid1:getSelected().name] = event.selected.value - self.display:draw() - self.grid2:draw() + elseif event.type == 'grid_select' and event.element == self.grid2 then + _colors[tab.grid1:getSelected().name] = event.selected.value + self.display:draw() + self.grid2:draw() - elseif event.type == 'reset' then - config.color = defaults - config.displayDirectory = true - self.directory.value = true - _colors = Util.shallowCopy(defaults) + elseif event.type == 'reset' then + config.color = defaults + config.displayDirectory = true + self.directory.value = true + _colors = Util.shallowCopy(defaults) - Config.update('shellprompt', config) - self:draw() + Config.update('shellprompt', config) + self:draw() - elseif event.type == 'update' then - config.color = _colors - Config.update('shellprompt', config) + elseif event.type == 'update' then + config.color = _colors + Config.update('shellprompt', config) - end - return UI.Tab.eventHandler(self, event) + end + return UI.Tab.eventHandler(self, event) end return tab diff --git a/sys/autorun/complete.lua b/sys/autorun/complete.lua index 332c8a3..82b2fe5 100644 --- a/sys/autorun/complete.lua +++ b/sys/autorun/complete.lua @@ -1,22 +1,22 @@ local function completeMultipleChoice(sText, tOptions, bAddSpaces) - local tResults = { } - for n = 1,#tOptions do - local sOption = tOptions[n] - if #sOption + (bAddSpaces and 1 or 0) > #sText and string.sub(sOption, 1, #sText) == sText then - local sResult = string.sub(sOption, #sText + 1) - if bAddSpaces then - table.insert(tResults, sResult .. " ") - else - table.insert(tResults, sResult) - end - end - end - return tResults + local tResults = { } + for n = 1,#tOptions do + local sOption = tOptions[n] + if #sOption + (bAddSpaces and 1 or 0) > #sText and string.sub(sOption, 1, #sText) == sText then + local sResult = string.sub(sOption, #sText + 1) + if bAddSpaces then + table.insert(tResults, sResult .. " ") + else + table.insert(tResults, sResult) + end + end + end + return tResults end _ENV.shell.setCompletionFunction("sys/apps/package.lua", - function(_, index, text) - if index == 1 then - return completeMultipleChoice(text, { "install ", "update ", "uninstall " }) - end - end) + function(_, index, text) + if index == 1 then + return completeMultipleChoice(text, { "install ", "update ", "uninstall " }) + end + end) diff --git a/sys/autorun/welcome.lua b/sys/autorun/welcome.lua index df0b2fd..2ecf6d8 100644 --- a/sys/autorun/welcome.lua +++ b/sys/autorun/welcome.lua @@ -4,8 +4,8 @@ local shell = _ENV.shell local config = Config.load('os') if not config.welcomed and shell.openForegroundTab then - config.welcomed = true - Config.update('os', config) + config.welcomed = true + Config.update('os', config) - shell.openForegroundTab('Welcome') + shell.openForegroundTab('Welcome') end diff --git a/sys/init/3.modules.lua b/sys/init/3.modules.lua index 6eb8c57..91a7159 100644 --- a/sys/init/3.modules.lua +++ b/sys/init/3.modules.lua @@ -6,47 +6,47 @@ local os = _G.os local peripheral = _G.peripheral local containers = { - manipulator = true, - neuralInterface = true, + manipulator = true, + neuralInterface = true, } local function getModules(dev, side) - local list = { } + local list = { } - if dev then - for _, module in pairs(dev.listModules()) do - list[module] = Util.shallowCopy(dev) - list[module].name = module - list[module].type = module - list[module].side = side - end - end - return list + if dev then + for _, module in pairs(dev.listModules()) do + list[module] = Util.shallowCopy(dev) + list[module].name = module + list[module].type = module + list[module].side = side + end + end + return list end for _,v in pairs(device) do - if containers[v.type] then - local list = getModules(v, v.side) - for k, dev in pairs(list) do - -- neural and attached modules have precedence over manipulator modules - if not device[k] or v.type ~= 'manipulator' then - device[k] = dev - end - end - end + if containers[v.type] then + local list = getModules(v, v.side) + for k, dev in pairs(list) do + -- neural and attached modules have precedence over manipulator modules + if not device[k] or v.type ~= 'manipulator' then + device[k] = dev + end + end + end end -- register modules as peripherals kernel.hook('device_attach', function(_, eventData) - local dev = eventData[2] + local dev = eventData[2] - if dev and containers[dev.type] then - local list = getModules(peripheral.wrap(dev.side), dev.side) - for k,v in pairs(list) do - if not device[k] or dev.type ~= 'manipulator' then - device[k] = v - os.queueEvent('device_attach', k, v) - end - end - end + if dev and containers[dev.type] then + local list = getModules(peripheral.wrap(dev.side), dev.side) + for k,v in pairs(list) do + if not device[k] or dev.type ~= 'manipulator' then + device[k] = v + os.queueEvent('device_attach', k, v) + end + end + end end) diff --git a/sys/init/3.relay.lua b/sys/init/3.relay.lua index ca75dc2..5ddc4bc 100644 --- a/sys/init/3.relay.lua +++ b/sys/init/3.relay.lua @@ -2,26 +2,26 @@ local device = _G.device local kernel = _G.kernel local function register(v) - if v and v.isWireless and v.isAccessPoint and v.getNamesRemote then - v._children = { } - for _, name in pairs(v.getNamesRemote()) do - local dev = v.getMethodsRemote(name) - if dev then - dev.name = name - dev.side = name - dev.type = v.getTypeRemote(name) - device[name] = dev - table.insert(v._children, dev) - end - end - end + if v and v.isWireless and v.isAccessPoint and v.getNamesRemote then + v._children = { } + for _, name in pairs(v.getNamesRemote()) do + local dev = v.getMethodsRemote(name) + if dev then + dev.name = name + dev.side = name + dev.type = v.getTypeRemote(name) + device[name] = dev + table.insert(v._children, dev) + end + end + end end for _,v in pairs(device) do - register(v) + register(v) end -- register oc devices as peripherals kernel.hook('device_attach', function(_, eventData) - register(device[eventData[2]]) + register(device[eventData[2]]) end) diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index 9baf271..ba52af1 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -6,7 +6,9 @@ local help = _G.help local shell = _ENV.shell if not fs.exists('usr/config/packages') then - Packages:downloadList() + pcall(function() + Packages:downloadList() + end) end local appPaths = Util.split(shell.path(), '(.-):') diff --git a/sys/init/6.tl3.lua b/sys/init/6.tl3.lua index 53519c4..4e797e2 100644 --- a/sys/init/6.tl3.lua +++ b/sys/init/6.tl3.lua @@ -52,6 +52,7 @@ function turtle.resetState() state.movePolicy = _defaultMove state.moveCallback = noop state.blacklist = nil + state.reference = nil -- gps reference when converting to relative coords Pathing.reset() return true end @@ -77,8 +78,6 @@ local function _dig(name, inspect, dig) return dig() end --- override dig --- optionally check that the block is a certain type function turtle.dig(s) return _dig(s, turtle.inspect, turtle.native.dig) end @@ -359,7 +358,7 @@ function turtle.set(args) turtle.setDigPolicy(turtle.getPolicy(v)) elseif k == 'movePolicy' then - turtle.setMovePolicy(turtle.getPolicy(v)) + state.movePolicy = turtle.getPolicy(v) elseif k == 'movementStrategy' then turtle.setMovementStrategy(v) @@ -379,6 +378,9 @@ function turtle.set(args) elseif k == 'blacklist' then state.blacklist = v + elseif k == 'reference' then + state.reference = v + else error('Invalid turle.set: ' .. tostring(k)) end @@ -426,6 +428,10 @@ function turtle.setHeading(heading) return false, 'Invalid heading' end + if heading == turtle.point.heading then + return turtle.point + end + local fi = Point.facings[heading] if not fi then return false, 'Invalid heading'