experimental heavlisp integration
update URLs
This commit is contained in:
parent
99a9d3de6e
commit
3596824ffb
@ -7,6 +7,8 @@ PotatOS is a groundbreaking "Operating System" for [ComputerCraft](https://www.c
|
||||
PotatOS Hypercycle is not entirely finished, and some features are currently broken or missing.
|
||||
If you want more "stability", consider [PotatOS Tau](https://pastebin.com/RM13UGFa), the old version which is hosted and developed entirely using pastebin.
|
||||
|
||||
You obviously want to install it now, so do this: `pastebin run 7HSiHybr`.
|
||||
|
||||
## Features
|
||||
|
||||
Unlike most "OS"es for CC (primarily excluding Opus OS, which is actually useful, and interesting "research projects" like Vorbani), which are merely a pointless GUI layer over native CraftOS, PotatOS incorporates many innovative features:
|
||||
|
4
manifest
4
manifest
@ -1,2 +1,2 @@
|
||||
{"build":170,"description":"stack overflow fix for CraftOS-PC - corrected","files":{"LICENSES":"f3549d84d66eb53dd4a421a4341d77d3d217c1b117d67e3be8f5211adcda0952","autorun.lua":"53a7f2b2692bd42a29f8559293447d413e58dc7fee7f2990a007624a62991562","bin/5rot26.lua":"91b66cd6d4b33081b25c456142dd7efcb894e819e842693c9e1e17ff48872ff5","bin/ccemux.lua":"239476f58835b86bbcac31ce8af3c3acd3d198a55ab9ada78c62fbf358625a98","bin/chronometer.lua":"db5363993a04382145aef7db2fbe262f0bf10697a589e1e2d2f9ce0f87430dd8","bin/kristminer.lua":"7e7f9fe2a6493d584ad6926cda915e02c1c3d800dc209680898ce930d0bb0e6f","bin/livegps.lua":"c3d17d495cda01aa1261e4c4fcd43439b29af422671972117ec34f68e32c5bba","bin/loading.lua":"c85f7aa1765170325155b921c1fceeb62643f552f12d41b529a22af3a67f5a97","bin/potatoflight.lua":"2fbb0b6f8d78728d8cb0ec64af1bc598bd00cb55f202378e7acdb86bba71efd1","bin/potatoplex.lua":"86c9e7597bbe23d7de7e7f1bfc976d0b94dcdf3af9e6c7c6c9b18b98596898c8","bin/relay.lua":"261ae6c220b83506e3326e8f2b091d246baae458ff0d2ee87512be2c4e35a75d","bin/tryhaskell.lua":"07810d85145da65a3e434154c79d5a9d72f2dcbe59c8d6829040fb925df878ec","potatobios.lua":"042bcbd6bf50730b6f01fad2f4280e7793f3542a5a5325420e8d098cf2052682","signing-key.tbl":"b32af5229c23af3bc03d538e42751b26044e404a7b1af064ed89894efe421607","startup":"f17bfb9b4322c4467dc9170d50827f2d75717e5c3125d734f21f3406657917bc","update-key.hex":"8d8afb7a45833bb7d68f929421ad60a211d4d73e0ee03b24dc0106ba1de2e1a0","xlib/00_cbor.lua":"464b075e4f094b8db42506bd4bdaad0db87699ea7fbf80e5b87739b4aa9279af","xlib/01_skynet.lua":"bde95ed86f3108ec56624367deea3e2694c8cfcd9eac220a21bad0b56c8a999b"},"timestamp":1599139938}
|
||||
{"hash":"38483033b4969ebff82a55d6ac62d31da30ee38378cfdcff600980fe54aa8acd","sig":"109588062d87c0e573d85e89f2e73fb27c51e398323f58b7338fa06314ad1b5a687eab78cb9b2793021b"}
|
||||
{"build":177,"description":"replace URLs and fix potatoBIOS","files":{"LICENSES":"f3549d84d66eb53dd4a421a4341d77d3d217c1b117d67e3be8f5211adcda0952","autorun.lua":"c84204c4684134cb12ed9a880a6b82e440f4a615ad944723df8ba7978e79e402","bin/5rot26.lua":"91b66cd6d4b33081b25c456142dd7efcb894e819e842693c9e1e17ff48872ff5","bin/ccemux.lua":"239476f58835b86bbcac31ce8af3c3acd3d198a55ab9ada78c62fbf358625a98","bin/chronometer.lua":"db5363993a04382145aef7db2fbe262f0bf10697a589e1e2d2f9ce0f87430dd8","bin/kristminer.lua":"7e7f9fe2a6493d584ad6926cda915e02c1c3d800dc209680898ce930d0bb0e6f","bin/livegps.lua":"c3d17d495cda01aa1261e4c4fcd43439b29af422671972117ec34f68e32c5bba","bin/loading.lua":"c85f7aa1765170325155b921c1fceeb62643f552f12d41b529a22af3a67f5a97","bin/potatoflight.lua":"2fbb0b6f8d78728d8cb0ec64af1bc598bd00cb55f202378e7acdb86bba71efd1","bin/potatoplex.lua":"86c9e7597bbe23d7de7e7f1bfc976d0b94dcdf3af9e6c7c6c9b18b98596898c8","bin/relay.lua":"261ae6c220b83506e3326e8f2b091d246baae458ff0d2ee87512be2c4e35a75d","bin/tryhaskell.lua":"07810d85145da65a3e434154c79d5a9d72f2dcbe59c8d6829040fb925df878ec","potatobios.lua":"d9104a0e8278d0c26d8b3be36697d25a3181210773c2841c70e44ca38bc0ab2d","signing-key.tbl":"b32af5229c23af3bc03d538e42751b26044e404a7b1af064ed89894efe421607","startup":"f17bfb9b4322c4467dc9170d50827f2d75717e5c3125d734f21f3406657917bc","update-key.hex":"8d8afb7a45833bb7d68f929421ad60a211d4d73e0ee03b24dc0106ba1de2e1a0","xlib/00_cbor.lua":"464b075e4f094b8db42506bd4bdaad0db87699ea7fbf80e5b87739b4aa9279af","xlib/01_skynet.lua":"9cb565d639a0acd7c763c3e7422482532cd0bda0cdfcc720089ab4a87e551339","xlib/03_heavlisp.lua":"104d5e4628f594a3d0484d6aecec3b4f69914ba398f6bc02dd12c62bddc3de4a"},"timestamp":1611941883}
|
||||
{"hash":"85d0edfacbdca311a8f18aebd88ae0a5c2e184f57db18285d22a3ea4922b1680","sig":"6b665266304ef6f04791163cff4c46d7ae91ca153738acfe09c3de9703e8bc274202f44d2ea3c939ba3a"}
|
@ -18,6 +18,7 @@ if process then
|
||||
tostring(event[4]),
|
||||
tostring(os.epoch("utc")),
|
||||
tostring({}),
|
||||
math.random()
|
||||
}
|
||||
entropy = table.concat(entropy, "|")
|
||||
|
||||
|
@ -122,7 +122,8 @@ local function resolve_path(path, mappings)
|
||||
end
|
||||
|
||||
local function segments(path)
|
||||
local segs, rest = {}, ""
|
||||
local segs, rest = {}, canonicalize(path)
|
||||
if rest == "" then return {} end -- otherwise we'd get "root" and ".." for some broken reason
|
||||
repeat
|
||||
table.insert(segs, 1, fs.getName(rest))
|
||||
rest = fs.getDir(rest)
|
||||
@ -138,18 +139,6 @@ local function combine(segs)
|
||||
return out
|
||||
end
|
||||
|
||||
local function difference(p1, p2)
|
||||
local s1, s2 = segments(p1), segments(p2)
|
||||
if #s2 == 0 then return combine(s1) end
|
||||
local segs = {}
|
||||
for _, p in pairs(s1) do
|
||||
local item = table.remove(s1, 1)
|
||||
table.insert(segs, item)
|
||||
if p == s2[1] then break end
|
||||
end
|
||||
return combine(segs)
|
||||
end
|
||||
|
||||
-- magic from http://lua-users.org/wiki/SplitJoin
|
||||
-- split string into lines
|
||||
local function lines(str)
|
||||
@ -191,6 +180,27 @@ local this_level_env = _G
|
||||
local function create_FS(root, overlay)
|
||||
local mappings = make_mappings(root)
|
||||
|
||||
local vfstree = {
|
||||
mount = "potatOS",
|
||||
children = {
|
||||
["disk"] = { mount = "disk" },
|
||||
["rom"] = { mount = "rom" },
|
||||
["virtual_test"] = { virtual = "bees" }
|
||||
}
|
||||
}
|
||||
|
||||
local function resolve(sandbox_path)
|
||||
local segs = segments(sandbox_path)
|
||||
local current_tree = vfstree
|
||||
while true do
|
||||
local seg = segs[1]
|
||||
if current_tree.children and current_tree.children[seg] then
|
||||
table.remove(segs, 1)
|
||||
current_tree = current_tree.children[seg]
|
||||
else break end
|
||||
end
|
||||
end
|
||||
|
||||
local new_overlay = {}
|
||||
for k, v in pairs(overlay) do
|
||||
new_overlay[canonicalize(k)] = v
|
||||
@ -236,14 +246,25 @@ local function create_FS(root, overlay)
|
||||
|
||||
function new.list(dir)
|
||||
local sdir = canonicalize(resolve_path(dir, mappings))
|
||||
local contents = fs.list(sdir)
|
||||
local ocontents = {}
|
||||
for opath in pairs(new_overlay) do
|
||||
if fs.getDir(opath) == sdir then
|
||||
table.insert(contents, fs.getName(opath))
|
||||
table.insert(ocontents, fs.getName(opath))
|
||||
end
|
||||
end
|
||||
local ok, contents = pcall(fs.list, sdir)
|
||||
-- in case of error (nonexistent dir, probably) return overlay contents
|
||||
-- very awful temporary hack until I can get a nicer treeized VFS done
|
||||
if not ok then
|
||||
if #ocontents > 0 then return ocontents end
|
||||
error(contents)
|
||||
else
|
||||
for _, v in pairs(ocontents) do
|
||||
table.insert(contents, v)
|
||||
end
|
||||
return contents
|
||||
end
|
||||
end
|
||||
|
||||
add_to_table(new, map(lift_to_sandbox, copy_some_keys {"isDir", "getDrive", "getSize", "getFreeSpace", "makeDir", "move", "copy", "delete", "isDriveRoot"} (fs)))
|
||||
|
||||
|
33
src/main.lua
33
src/main.lua
@ -486,7 +486,7 @@ function _G.report_incident(incident, flags, options)
|
||||
}
|
||||
-- Workaround craftos-pc bug by explicitly specifying Content-Length header
|
||||
http.request {
|
||||
url = "https://osmarks.tk/wsthing/report",
|
||||
url = "https://spudnet.osmarks.net/report",
|
||||
body = payload,
|
||||
headers = {
|
||||
["content-type"] = "application/json",
|
||||
@ -511,7 +511,7 @@ shell.run "startup"
|
||||
local function generate_disk_code()
|
||||
local an = copy(ancestry)
|
||||
table.insert(an, os.getComputerID())
|
||||
local manifest = settings.get "potatOS.distribution_server" or "https://osmarks.tk/stuff/potatos/manifest"
|
||||
local manifest = settings.get "potatOS.distribution_server" or "https://osmarks.net/stuff/potatos/manifest"
|
||||
return disk_code_template:format(
|
||||
gen_count + 1,
|
||||
textutils.serialise(an),
|
||||
@ -667,8 +667,8 @@ local function websocket_remote_debugging()
|
||||
|
||||
local function connect()
|
||||
if ws then ws.close() end
|
||||
ws, err = http.websocket "wss://osmarks.tk/wsthing/v4"
|
||||
ws.url = "wss://osmarks.tk/wsthing/v4"
|
||||
ws, err = http.websocket "wss://spudnet.osmarks.net/v4"
|
||||
ws.url = "wss://spudnet.osmarks.net/v4"
|
||||
if not ws then add_log("websocket failure %s", err) return false end
|
||||
|
||||
send_packet { type = "identify" }
|
||||
@ -1013,18 +1013,12 @@ local function run_with_sandbox()
|
||||
debug_registry_mt.__index = function(_, k) return registry.get(k) end
|
||||
debug_registry_mt.__newindex = function(_, k, v) return registry.set(k, v) end
|
||||
|
||||
local fcache = {}
|
||||
|
||||
-- Proxy access to files. Assumes that they won't change once read. Which is true for most of them, so yay efficiency savings?
|
||||
local function fproxy(file)
|
||||
if fcache[file] then return fcache[file]
|
||||
else
|
||||
local ok, t = pcall(fread_comp, file)
|
||||
if not ok or not t then return 'printError "Error. Try again later, or reboot, or run upd."' end
|
||||
fcache[file] = t
|
||||
return t
|
||||
end
|
||||
end
|
||||
|
||||
-- Localize a bunch of variables. Does this help? I have no idea. This is old code.
|
||||
local debuggetupvalue, debugsetupvalue
|
||||
@ -1254,7 +1248,7 @@ if #disks > 0 then
|
||||
end
|
||||
parallel.waitForAny(function() sleep(0.5) end,
|
||||
function()
|
||||
local ok, info = pcall(fetch, "https://osmarks.tk/random-stuff/info")
|
||||
local ok, info = pcall(fetch, "https://osmarks.net/random-stuff/info")
|
||||
if not ok then add_log("info fetch failed: %s", info) return end
|
||||
print "Extra:"
|
||||
print("User agent", info:match "\tuser%-agent:\t([^\n]*)")
|
||||
@ -1440,6 +1434,10 @@ end
|
||||
FS_overlay[fs.combine("rom/programs", file)] = fproxy(fs.combine("bin", file))
|
||||
end
|
||||
|
||||
for _, file in pairs(fs.list "xlib") do
|
||||
FS_overlay[fs.combine("rom/potato_xlib", file)] = fproxy(fs.combine("xlib", file))
|
||||
end
|
||||
|
||||
local osshutdown = os.shutdown
|
||||
local osreboot = os.reboot
|
||||
|
||||
@ -1461,19 +1459,6 @@ end
|
||||
polychoron = polychoron, -- so that nested instances use our existing process manager system, as polychoron detects specifically *its* presence and not just generic "process"
|
||||
}
|
||||
|
||||
local libs = {}
|
||||
for _, f in pairs(fs.list "xlib") do
|
||||
table.insert(libs, f)
|
||||
end
|
||||
table.sort(libs)
|
||||
for _, f in pairs(libs) do
|
||||
local basename = f:gsub("%.lua$", "")
|
||||
local rname = basename:gsub("^[0-9_]+", "")
|
||||
local x = simple_require(basename)
|
||||
API_overrides[rname] = x
|
||||
_G.package.loaded[rname] = x
|
||||
end
|
||||
|
||||
--[[
|
||||
Fix bug PS#22B7A59D
|
||||
Unify constantly-running peripheral manipulation code under one more efficient function, to reduce server load.
|
||||
|
@ -101,7 +101,7 @@ local secure_events = {
|
||||
Fix for bug PS#D7CD76C0
|
||||
As "sandboxed" code can still queue events, there was previously an issue where SPUDNET messages could be spoofed, causing arbitrary code to be executed in a privileged process.
|
||||
This... kind of fixes this? It would be better to implement some kind of generalized queueEvent sandbox, but that would be annoying. The implementation provided by Kan181/6_4 doesn't seem very sound.
|
||||
Disallow evil people from spoofing the osmarks.tk website. Should sort of not really fix one of the sandbox exploits.
|
||||
Disallow evil people from spoofing the osmarks.net website. Should sort of not really fix one of the sandbox exploits.
|
||||
|
||||
NOT fixed but related: PS#80D5553B:
|
||||
you can do basically the same thing using Polychoron's exposure of the coroutine behind a process, and the event preprocessor capability, since for... some reason... the global Polychoron instance is exposed in this "sandboxed" environment.
|
||||
@ -241,6 +241,16 @@ function load(code, file, ...)
|
||||
f.close()
|
||||
end
|
||||
set_last_loaded(code)
|
||||
if code:match "^///PS:heavlisp\n" then
|
||||
-- load in heavlisp mode
|
||||
if not heavlisp then return false, "heavlisp loader unavailable" end
|
||||
local ok, ast = pcall(function() return heavlisp.into_ast(heavlisp.tokenize(code)) end)
|
||||
if not ok then return false, ast end
|
||||
return function(imports)
|
||||
imports = imports or {}
|
||||
return heavlisp.interpret(ast, imports)
|
||||
end
|
||||
end
|
||||
return real_load(code, file, ...)
|
||||
end
|
||||
do_something "load"
|
||||
@ -923,6 +933,58 @@ if commands and fs.isDir( "rom/apis/command" ) then
|
||||
end
|
||||
end
|
||||
|
||||
-- library loading is now done in-sandbox, enhancing security
|
||||
-- make up our own require for some bizarre reason
|
||||
local function try_paths(root, paths)
|
||||
for _, path in pairs(paths) do
|
||||
local fpath = fs.combine(root, path)
|
||||
if fs.exists(fpath) and not fs.isDir(fpath) then
|
||||
return fpath
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
_G.package = {
|
||||
preload = {},
|
||||
loaded = {}
|
||||
}
|
||||
|
||||
function simple_require(package)
|
||||
if _G.package.loaded[package] then return _G.package.loaded[package] end
|
||||
if _G.package.preload[package] then
|
||||
local pkg = _G.package.preload[package](_G.package)
|
||||
_G.package.loaded[package] = pkg
|
||||
return pkg
|
||||
end
|
||||
local npackage = package:gsub("%.", "/")
|
||||
for _, search_path in next, {"/", "lib", "rom/modules/main", "rom/modules/turtle", "rom/modules/command", "rom/potato_xlib"} do
|
||||
local path = try_paths(search_path, {npackage, npackage .. ".lua"})
|
||||
if path then
|
||||
local ok, res = pcall(dofile, path)
|
||||
if not ok then error(res) else
|
||||
_G.package.loaded[package] = res
|
||||
return res
|
||||
end
|
||||
end
|
||||
end
|
||||
error(package .. " not found")
|
||||
end
|
||||
_G.require = simple_require
|
||||
|
||||
local libs = {}
|
||||
for _, f in pairs(fs.list "rom/potato_xlib") do
|
||||
table.insert(libs, f)
|
||||
end
|
||||
table.sort(libs)
|
||||
for _, f in pairs(libs) do
|
||||
local basename = f:gsub("%.lua$", "")
|
||||
local rname = basename:gsub("^[0-9_]+", "")
|
||||
local x = simple_require(basename)
|
||||
_G[rname] = x
|
||||
_G.package.loaded[rname] = x
|
||||
end
|
||||
|
||||
if bAPIError then
|
||||
print( "Press any key to continue" )
|
||||
os.pullEvent( "key" )
|
||||
@ -1044,7 +1106,7 @@ end
|
||||
if potatOS.registry.get "potatOS.seen_terms_notice" == nil or potatOS.registry.get "potatOS.seen_terms_notice" == false then
|
||||
term.setCursorPos(1, 1)
|
||||
potatOS.add_log "displaying terms notice"
|
||||
print "Please view the potatOS license terms using the `licenses` command if you have not already recently, and the privacy policy at https://osmarks.tk/p3.html (the copy shipped with PotatOS Licenses is outdated). Press the Any key to continue."
|
||||
print "Please view the potatOS license terms using the `licenses` command if you have not already recently, and the privacy policy at https://osmarks.net/p3.html (the copy shipped with PotatOS Licenses is outdated). Press the Any key to continue."
|
||||
potatOS.registry.set("potatOS.seen_terms_notice", true)
|
||||
os.pullEvent "key"
|
||||
end
|
||||
@ -1428,19 +1490,19 @@ function _G.potatOS.lorem()
|
||||
return depara(json.decode(new).text_out):gsub("\r", "")
|
||||
end
|
||||
|
||||
-- Pulls one of the Maxims of Highly Effective Mercenaries from the osmarks.tk random stuff API
|
||||
-- Pulls one of the Maxims of Highly Effective Mercenaries from the osmarks.net random stuff API
|
||||
function _G.potatOS.maxim()
|
||||
return fetch "https://osmarks.tk/random-stuff/maxim/"
|
||||
return fetch "https://osmarks.net/random-stuff/maxim/"
|
||||
end
|
||||
|
||||
-- Backed by the Linux fortunes program.
|
||||
function _G.potatOS.fortune()
|
||||
return fetch "https://osmarks.tk/random-stuff/fortune/"
|
||||
return fetch "https://osmarks.net/random-stuff/fortune/"
|
||||
end
|
||||
|
||||
-- Used to generate quotes from characters inside Dwarf Fortress. No longer functional as that was taking way too much CPU time.
|
||||
function _G.potatOS.dwarf()
|
||||
return fetch "https://osmarks.tk/dwarf/":gsub("—", "-")
|
||||
return fetch "https://osmarks.net/dwarf/":gsub("—", "-")
|
||||
end
|
||||
|
||||
-- Code for PotatoNET chat program. Why is this in potatoBIOS? WHO KNOWS.
|
||||
|
@ -1,7 +1,7 @@
|
||||
local CBOR = require "cbor"
|
||||
|
||||
local skynet = {
|
||||
server = "wss://osmarks.tk/skynet2/connect/",
|
||||
server = "wss://skynet.osmarks.net/connect/",
|
||||
socket = nil,
|
||||
open_channels = {},
|
||||
CBOR = CBOR
|
||||
|
577
src/xlib/03_heavlisp.lua
Normal file
577
src/xlib/03_heavlisp.lua
Normal file
@ -0,0 +1,577 @@
|
||||
function deepclone(t)
|
||||
local res={}
|
||||
for i,v in ipairs(t) do
|
||||
if type(v)=="table" then
|
||||
res[i]=deepclone(v)
|
||||
else
|
||||
res[i]=v
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
function tokenize(str)
|
||||
local ptr=1
|
||||
local tokens={}
|
||||
local line=1
|
||||
local function isIn(e,s)
|
||||
for i=1,#s do
|
||||
if s:sub(i,i)==e then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
local function isInT(e,s)
|
||||
for i=1,#s do
|
||||
if s[i]==e then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
local function isNotIdent(c)
|
||||
return isIn(c,"()[] \n\t\v\r\f\";'`")
|
||||
end
|
||||
local function isDigit(c)
|
||||
return isIn(c,"0123456789")
|
||||
end
|
||||
while true do
|
||||
local c=str:sub(ptr,ptr)
|
||||
if c=="(" then table.insert(tokens,{"(",line=line}) ptr=ptr+1
|
||||
elseif c==")" then table.insert(tokens,{")",line=line}) ptr=ptr+1
|
||||
elseif c=="[" then table.insert(tokens,{"[",line=line}) ptr=ptr+1
|
||||
elseif c=="]" then table.insert(tokens,{"]",line=line}) ptr=ptr+1
|
||||
elseif c=="\n" then line=line+1 ptr=ptr+1
|
||||
elseif c==" " or c=="\t" or c=="\v" or c=="\f" or c=="\r" or c==";" then ptr=ptr+1 --ignore whitespace. semicolon is treated as whitespace, somewhat.
|
||||
elseif c=="/" and str:sub(ptr+1,ptr+1)=="/" then
|
||||
ptr=ptr+1
|
||||
local nc=str:sub(ptr,ptr)
|
||||
while nc~="\n" and ptr<=#str do
|
||||
ptr=ptr+1
|
||||
nc=str:sub(ptr,ptr)
|
||||
end
|
||||
line=line+1
|
||||
ptr=ptr+1
|
||||
elseif c=="," then table.insert(tokens,{",",line=line}) ptr=ptr+1
|
||||
elseif c==":" then table.insert(tokens,{"string",str:sub(ptr+1,ptr+1),line=line}) if str:sub(ptr+1,ptr+1)=="\n" then line=line+1 end ptr=ptr+2
|
||||
elseif c=="'" then
|
||||
local res=""
|
||||
ptr=ptr+1
|
||||
local nc=str:sub(ptr,ptr)
|
||||
while not isNotIdent(nc) and ptr<=#str do
|
||||
ptr=ptr+1
|
||||
res=res..nc
|
||||
nc=str:sub(ptr,ptr)
|
||||
end
|
||||
table.insert(tokens,{"string",res,line=line})
|
||||
elseif c=="\"" then
|
||||
local res=""
|
||||
ptr=ptr+1
|
||||
local nc=str:sub(ptr,ptr)
|
||||
while nc~="\"" and ptr<=#str do
|
||||
if nc=="\\" then ptr=ptr+1 nc=str:sub(ptr,ptr) end
|
||||
if nc=="\n" then line=line+1 end
|
||||
res=res..nc
|
||||
ptr=ptr+1
|
||||
nc=str:sub(ptr,ptr)
|
||||
end
|
||||
ptr=ptr+1
|
||||
table.insert(tokens,{"string",res,line=line})
|
||||
elseif c=="`" and str:sub(ptr+1,ptr+1)=="`" then
|
||||
local res=""
|
||||
ptr=ptr+2
|
||||
local nc=str:sub(ptr,ptr+1)
|
||||
while nc~="``" and ptr<=#str do
|
||||
if nc:sub(1,1)=="\\" then ptr=ptr+1 nc=str:sub(ptr,ptr) end
|
||||
if nc:sub(1,1)=="\n" then line=line+1 end
|
||||
res=res..nc:sub(1,1)
|
||||
ptr=ptr+1
|
||||
nc=str:sub(ptr,ptr+1)
|
||||
end
|
||||
ptr=ptr+2
|
||||
table.insert(tokens,{"string",res,line=line})
|
||||
elseif isDigit(c) then
|
||||
local res=c
|
||||
ptr=ptr+1
|
||||
local nc=str:sub(ptr,ptr)
|
||||
while isDigit(nc) and ptr<=#str do
|
||||
ptr=ptr+1
|
||||
res=res..nc
|
||||
nc=str:sub(ptr,ptr)
|
||||
end
|
||||
table.insert(tokens,{"number",tonumber(res,10),line=line})
|
||||
elseif ptr>#str then break
|
||||
elseif not isNotIdent(c) then
|
||||
local res=c
|
||||
ptr=ptr+1
|
||||
local nc=str:sub(ptr,ptr)
|
||||
while not isNotIdent(nc) and ptr<=#str do
|
||||
ptr=ptr+1
|
||||
res=res..nc
|
||||
nc=str:sub(ptr,ptr)
|
||||
end
|
||||
table.insert(tokens,{"identifier",res,line=line})
|
||||
else print("no idea what this is: "..c) ptr=ptr+1 end
|
||||
end
|
||||
table.insert(tokens,{"EOF"})
|
||||
return tokens
|
||||
end
|
||||
|
||||
function into_ast(tokens)
|
||||
local ptr=1
|
||||
local function expect(token)
|
||||
if tokens[ptr][1]~=token then
|
||||
error("expected "..token..", got "..tokens[ptr][1])
|
||||
end
|
||||
return tokens[ptr]
|
||||
end
|
||||
local function expect2(token)
|
||||
if tokens[ptr][2]~=token then
|
||||
error("expected "..token..", got "..tokens[ptr][2] and tokens[ptr][2] or tokens[ptr][1])
|
||||
end
|
||||
return tokens[ptr]
|
||||
end
|
||||
local function combinator_and(p,p2)
|
||||
return function()
|
||||
local bk=ptr
|
||||
local r1,r2=p(),p2()
|
||||
if not (r1 and r2) then
|
||||
ptr=bk
|
||||
return false
|
||||
end
|
||||
return {r1,r2}
|
||||
end
|
||||
end
|
||||
local function combinator_or(p,p2)
|
||||
return function()
|
||||
local bk=ptr
|
||||
local r1=p()
|
||||
if not r1 then
|
||||
local r2=p2()
|
||||
if not r2 then
|
||||
ptr=bk
|
||||
return false
|
||||
end
|
||||
return r2
|
||||
end
|
||||
return r1
|
||||
end
|
||||
end
|
||||
local function any_amount(p)
|
||||
return function()
|
||||
local res={}
|
||||
while true do
|
||||
local bk=ptr
|
||||
local tmp=p()
|
||||
if (tmp==nil) or tmp==false or tokens[bk][1]=="EOF" then ptr=bk break end
|
||||
table.insert(res,tmp)
|
||||
end
|
||||
return res
|
||||
end
|
||||
end
|
||||
local function any_eof(p)
|
||||
return function()
|
||||
local res={}
|
||||
while true do
|
||||
local bk=ptr
|
||||
local tmp=p()
|
||||
if (tmp==nil) or tmp==false or tokens[bk][1]=="EOF" then
|
||||
if tokens[bk][1]~="EOF" then
|
||||
error("[HL] line "..tokens[bk].line..": some syntax error occured.")
|
||||
end
|
||||
ptr=bk
|
||||
break
|
||||
end
|
||||
table.insert(res,tmp)
|
||||
end
|
||||
return res
|
||||
end
|
||||
end
|
||||
local function more_than_one(p)
|
||||
return function()
|
||||
local bk=ptr
|
||||
local r1=p()
|
||||
if not r1 or tokens[bk][1]=="EOF" then ptr=bk return false end
|
||||
local res={r1}
|
||||
while true do
|
||||
local bk=ptr
|
||||
local tmp=p()
|
||||
if (tmp==nil) or tmp==false or tokens[bk][1]=="EOF" then ptr=bk break end
|
||||
table.insert(res,tmp)
|
||||
end
|
||||
return res
|
||||
end
|
||||
end
|
||||
local function number()
|
||||
return function()
|
||||
if tokens[ptr][1]=="number" then ptr=ptr+1 return {"number",tokens[ptr-1][2],line=tokens[ptr-1].line} end
|
||||
return false
|
||||
end
|
||||
end
|
||||
local function string()
|
||||
return function()
|
||||
if tokens[ptr][1]=="string" then ptr=ptr+1 return {"string",tokens[ptr-1][2],line=tokens[ptr-1].line} end
|
||||
return false
|
||||
end
|
||||
end
|
||||
local function symbol(x)
|
||||
return function()
|
||||
if tokens[ptr][1]==x then ptr=ptr+1 return tokens[ptr-1] end
|
||||
return false
|
||||
end
|
||||
end
|
||||
local function literal()
|
||||
return combinator_or(number(),string())
|
||||
end
|
||||
local expression,lambda
|
||||
function statement()
|
||||
return combinator_or(combinator_or(expression(),lambda()),symbol("identifier"))
|
||||
end
|
||||
expression=function()
|
||||
return combinator_or(literal(),function()
|
||||
local res={"expression"}
|
||||
local tmp=symbol("(")()
|
||||
if not tmp then return false end
|
||||
res.line=tmp.line
|
||||
res[2]=more_than_one(statement())()
|
||||
if not symbol(")")() then return false end
|
||||
return res
|
||||
end)
|
||||
end
|
||||
lambda=function()
|
||||
return combinator_or(literal(),function()
|
||||
local res={"lambda"}
|
||||
local tmp=symbol("[")()
|
||||
if not tmp then return false end
|
||||
res.line=tmp.line
|
||||
res[2]=more_than_one(statement())()
|
||||
if not symbol("]")() then return false end
|
||||
return res
|
||||
end)
|
||||
end
|
||||
return any_eof(statement())()
|
||||
end
|
||||
|
||||
function interpret(ast,imports)
|
||||
local namespace_stack={{}}
|
||||
local cline=1
|
||||
local function top() return namespace_stack[#namespace_stack] end
|
||||
local function instantiate(k,v)
|
||||
local t=top()
|
||||
t[k]={v}
|
||||
end
|
||||
local function set(k,v)
|
||||
local t=top()
|
||||
if not t[k] then
|
||||
instantiate(k,v)
|
||||
else
|
||||
t[k][1]=v
|
||||
end
|
||||
end
|
||||
local function throwerror(reason)
|
||||
error("[HL] line "..cline..": "..reason)
|
||||
end
|
||||
local function get(k)
|
||||
local t=top()
|
||||
if t[k] then
|
||||
return t[k][1]
|
||||
end
|
||||
throwerror("could not find variable "..k)
|
||||
end
|
||||
|
||||
local function clone()
|
||||
namespace_stack[#namespace_stack+1]=deepclone(top())
|
||||
end
|
||||
local function discard()
|
||||
namespace_stack[#namespace_stack]=nil
|
||||
end
|
||||
local function to(...)
|
||||
local res={}
|
||||
local a={...}
|
||||
for i,v in ipairs(a) do
|
||||
if type(v)=="table" then
|
||||
local res2={}
|
||||
for i2,v2 in ipairs(v) do
|
||||
res2[i2]=to(v2)
|
||||
end
|
||||
table.insert(res,{type="list",value=res2})
|
||||
else
|
||||
table.insert(res,{type=({
|
||||
["nil"]="nil",
|
||||
["string"]="string",
|
||||
["number"]="number",
|
||||
["boolean"]="bool",
|
||||
["table"]="list",
|
||||
})[type(v)],value=v})
|
||||
end
|
||||
end
|
||||
return unpack(res)
|
||||
end
|
||||
local function from(...)
|
||||
local res={}
|
||||
local a={...}
|
||||
for i,v in ipairs(a) do
|
||||
if v.type=="list" then
|
||||
local res2={}
|
||||
for i2,v2 in ipairs(v.value) do
|
||||
res2[i2]=from(v2)
|
||||
end
|
||||
table.insert(res,res2)
|
||||
else
|
||||
table.insert(res,v.value)
|
||||
end
|
||||
end
|
||||
return unpack(res)
|
||||
end
|
||||
local raw
|
||||
raw={
|
||||
["+"]=function(x,y)
|
||||
if x and y then
|
||||
if x.type=="number" and y.type=="number" then
|
||||
return {type="number",value=x.value+y.value}
|
||||
end
|
||||
if (x.type=="string" and (y.type=="string" or y.type=="number" or y.type=="bool")) or (y.type=="string" and (x.type=="string" or x.type=="number" or x.type=="bool")) then
|
||||
return {type="string",value=x.value..y.value}
|
||||
end
|
||||
end
|
||||
return throwerror("types incompatible: "..(x and x.type or "nil")..", "..(y and y.type or "nil"))
|
||||
end,
|
||||
["*"]=function(x,y)
|
||||
if x and y and x.type=="number" and y.type=="number" then
|
||||
return {type="number",value=x.value*y.value}
|
||||
end
|
||||
throwerror("types incompatible: "..(x and x.type or "nil")..", "..(y and y.type or "nil"))
|
||||
end,
|
||||
["negate"]=function(x)
|
||||
if x and x.type=="number" then
|
||||
return {type="number",value=-(x.value)}
|
||||
end
|
||||
throwerror("types incompatible: "..(args[1] and args[1].type or "nil"))
|
||||
end,
|
||||
["at"]=function(x,y)
|
||||
if x and x.type=="list" and y and (y.type=="number" or y.type=="string") then
|
||||
if x.value[y.value] then
|
||||
return x.value[y.value]
|
||||
end
|
||||
throwerror("attempt to get out of bounds index "..y.value)
|
||||
end
|
||||
if x and x.type=="string" and y and y.type=="number" then
|
||||
if #x.value>=(y.value+1) and (y.value+1)>=1 then
|
||||
return {type="string",value=x.value:sub(y.value+1,y.value+1)}
|
||||
end
|
||||
throwerror("attempt to get out of bounds index "..y.value)
|
||||
end
|
||||
throwerror("types incompatible: "..(x and x.type or "nil")..", "..(y and y.type or "nil"))
|
||||
end,
|
||||
["keys"]=function(x)
|
||||
if x and x.type=="list" then
|
||||
local res={type="list",value={}}
|
||||
for i,v in pairs(x.value) do
|
||||
table.insert(res.value,i)
|
||||
end
|
||||
return res
|
||||
end
|
||||
throwerror("types incompatible: "..(x and x.type or "nil"))
|
||||
end,
|
||||
["/"]=function(x,y)
|
||||
if x and y and x.type=="number" and y.type=="number" then
|
||||
return {type="number",value=x.value/y.value}
|
||||
end
|
||||
throwerror("types incompatible: "..(x and x.type or nil)..", "..y and y.type or nil)
|
||||
end,
|
||||
["=="]=function(x,y)
|
||||
if x.type~=y.type then return {type="bool",value=false} end
|
||||
return {type="bool",value=x.value==y.value}
|
||||
end,
|
||||
["len"]=function(x)
|
||||
if x.type=="list" or x.type=="string" then
|
||||
return {type="number",value=#x.value}
|
||||
end
|
||||
end,
|
||||
["seti"]=function(x,y,z)
|
||||
if x and x.type=="list" and y and (y.type=="number" or y.type=="string") and z then
|
||||
x.value[y.value]=z
|
||||
end
|
||||
throwerror("types incompatible: "..(x and x.type or "nil")..", "..(y and y.type or "nil")..", "..(z and z.type or "nil"))
|
||||
end,
|
||||
["if"]=function(x,y,z)
|
||||
if x and x.value~=0 and x.value~=false then
|
||||
if y and y.type=="function" then
|
||||
return y.value()
|
||||
end
|
||||
else
|
||||
if x and z and z.type=="function" then
|
||||
return z.value()
|
||||
end
|
||||
end
|
||||
return throwerror("types incompatible: "..(x and x.type or "nil")..", "..(y and y.type or "nil")..", "..(z and z.type or "nil"))
|
||||
end,
|
||||
["print"]=function(...)
|
||||
local tmp={}
|
||||
local args={...}
|
||||
for i=1,#args do
|
||||
if args[i].type=="string" or args[i].type=="number" or args[i].type=="bool" then
|
||||
tmp[#tmp+1]=args[i].value
|
||||
end
|
||||
if args[i].type=="error" then
|
||||
tmp[#tmp+1]="error: "..args[i].value
|
||||
end
|
||||
if args[i].type=="list" then
|
||||
tmp[#tmp+1]="(list, length: "..#args[i].value..")"
|
||||
end
|
||||
if args[i].type=="function" then
|
||||
tmp[#tmp+1]="(function)"
|
||||
end
|
||||
end
|
||||
print(#args>0 and unpack(tmp) or "nil")
|
||||
return {type="bool",value=true}
|
||||
end,
|
||||
["type"]=function(x)
|
||||
if x then return {type="string",value=x.type} end
|
||||
throwerror("types incompatible: nil")
|
||||
end,
|
||||
["<"]=function(x,y)
|
||||
if x and y and x.type=="number" and y.type=="number" then
|
||||
return {type="bool",value=(x.value<y.value)}
|
||||
end
|
||||
throwerror("types incompatible: "..(x and x.type or nil)..", "..y and y.type or nil)
|
||||
end,
|
||||
["newvar"]=function(...)
|
||||
local args={...}
|
||||
if args[1] and args[1].type=="string" then
|
||||
if args[3] and args[3].type=="bool" and args[3].value==true then
|
||||
local tmp=top()
|
||||
discard()
|
||||
instantiate(args[1].value,args[2])
|
||||
namespace_stack[#namespace_stack+1]=tmp
|
||||
instantiate(args[1].value,args[2])
|
||||
else
|
||||
instantiate(args[1].value,args[2])
|
||||
end
|
||||
return {type="bool",value=true}
|
||||
end
|
||||
return throwerror("types incompatible: "..(args[1] and args[1].type or "nil"))
|
||||
end,
|
||||
["var"]=function(...)
|
||||
local args={...}
|
||||
if args[1] and args[1].type=="string" then set(args[1].value,args[2]) return {type="bool",value=true} end
|
||||
return throwerror("types incompatible: "..(args[1] and args[1].type or "nil"))
|
||||
end,
|
||||
["extern"]=function(x,y)
|
||||
if x and x.type=="string" then
|
||||
local namespace=""
|
||||
if y and y.type=="string" then
|
||||
namespace=y.value.."_"
|
||||
end
|
||||
local f=io.open(x.value,"r")
|
||||
if not f then
|
||||
return {type="bool",value=false}
|
||||
end
|
||||
local i=f:read("*a")
|
||||
f:close()
|
||||
local xp=interpret(into_ast(tokenize(i)),imports).exports
|
||||
if xp then
|
||||
for k,v in pairs(xp) do
|
||||
instantiate(namespace..k,{type="function",value=v})
|
||||
end
|
||||
return {type="bool",value=true}
|
||||
end
|
||||
end
|
||||
return {type="bool",value=false}
|
||||
end,
|
||||
}
|
||||
for i,v in pairs(raw) do
|
||||
set(i,{type="function",value=v})
|
||||
end
|
||||
for i,v in pairs(imports or {}) do
|
||||
set("lua_"..i,{type="function",value=function(...)
|
||||
return to(v(from(...)))
|
||||
end})
|
||||
end
|
||||
instantiate("true",{type="bool",value=true})
|
||||
instantiate("false",{type="bool",value=false})
|
||||
local function exec(node)
|
||||
local nodet=node[1]
|
||||
cline=node.line
|
||||
if nodet=="string" then return {type="string",value=node[2]} end
|
||||
if nodet=="number" then return {type="number",value=node[2]} end
|
||||
if nodet=="identifier" then
|
||||
local r=get(node[2])
|
||||
if not r then
|
||||
return {type="error",value="not found"}
|
||||
else
|
||||
return r
|
||||
end
|
||||
end
|
||||
if nodet=="expression" then
|
||||
local vl=node[2]
|
||||
local to_call=nil
|
||||
local list_res={}
|
||||
for i,v in ipairs(vl) do
|
||||
local evr=exec(v)
|
||||
if i==1 and evr.type=="function" then
|
||||
to_call=evr
|
||||
else
|
||||
table.insert(list_res,evr)
|
||||
end
|
||||
end
|
||||
if to_call then
|
||||
local m=to_call.value(unpack(list_res))
|
||||
--print(m)
|
||||
return m
|
||||
else
|
||||
return {type="list",value=list_res}
|
||||
end
|
||||
end
|
||||
if nodet=="lambda" then
|
||||
local vl=node[2]
|
||||
local to_call=nil
|
||||
local list_res={}
|
||||
for i,v in ipairs(vl) do
|
||||
table.insert(list_res,v)
|
||||
end
|
||||
local cscope=top()
|
||||
return {type="function",value=function(...)
|
||||
local a={...}
|
||||
clone()
|
||||
for i,v in pairs(cscope) do
|
||||
if not namespace_stack[#namespace_stack][i] then namespace_stack[#namespace_stack][i]=v end
|
||||
end
|
||||
for i=1,#a do
|
||||
instantiate("arg"..i,a[i])
|
||||
end
|
||||
instantiate("argc",#a)
|
||||
local retval=nil
|
||||
for i,v in ipairs(list_res) do
|
||||
retval=exec(v)
|
||||
end
|
||||
if not retval then
|
||||
return throwerror("functions cannot have no return value")
|
||||
end
|
||||
discard()
|
||||
return retval
|
||||
end}
|
||||
end
|
||||
print("oh apio oh "..nodet)
|
||||
end
|
||||
local rv=nil
|
||||
for i=1,#ast do
|
||||
rv=exec(ast[i])
|
||||
end
|
||||
local exports={}
|
||||
local lua_exports={throwerror=throwerror}
|
||||
if rv and rv.type=="list" then
|
||||
if rv.value[1] and rv.value[1].value=="exports" then
|
||||
for i=2,#rv.value,2 do
|
||||
if rv.value[i] and rv.value[i].type=="string" then
|
||||
if rv.value[i+1] and rv.value[i+1].type=="function" then
|
||||
exports[rv.value[i].value]=rv.value[i+1].value
|
||||
lua_exports[rv.value[i].value]=function(...) return from(rv.value[i+1].value(to(...))) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {returned=rv,exports=exports,lua_exports=lua_exports}
|
||||
end
|
||||
local function run(x,lua)
|
||||
return interpret(into_ast(tokenize(x)),lua)
|
||||
end
|
||||
return {run=run, interpret=interpret, into_ast=into_ast, tokenize=tokenize}
|
109
website/index.html
Normal file
109
website/index.html
Normal file
@ -0,0 +1,109 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf8">
|
||||
<title>PotatOS</title>
|
||||
<meta name="description" content="PotatOS Otiose Transformative Advanced Technology Or Something">
|
||||
<h1 id="potatos">PotatOS</h1>
|
||||
<p>“PotatOS” stands for “PotatOS Otiose Transformative Advanced Technology Or Something”. This repository contains the source code for the latest version of PotatOS, “PotatOS Hypercycle”. PotatOS is a groundbreaking “Operating System” for <a href="https://www.computercraft.info/">ComputerCraft</a> (preferably the newer and actually-maintained <a href="https://tweaked.cc/">CC: Tweaked</a>).</p>
|
||||
<p>PotatOS Hypercycle is not entirely finished, and some features are currently broken or missing. If you want more “stability”, consider <a href="https://pastebin.com/RM13UGFa">PotatOS Tau</a>, the old version which is hosted and developed entirely using pastebin.</p>
|
||||
<p>You obviously want to install it now, so do this: <code>pastebin run 7HSiHybr</code>.</p>
|
||||
<h2 id="features">Features</h2>
|
||||
<p>Unlike most “OS”es for CC (primarily excluding Opus OS, which is actually useful, and interesting “research projects” like Vorbani), which are merely a pointless GUI layer over native CraftOS, PotatOS incorporates many innovative features:</p>
|
||||
<ul>
|
||||
<li>Fortunes/Dwarf Fortress output (UPDATE: no longer available)/Chuck Norris jokes on boot (wait, IS this a feature?)</li>
|
||||
<li>(other) viruses (how do you get them in the first place? running random files like this?) cannot do anything particularly awful to your computer - uninterceptable (except by crashing the keyboard shortcut daemon, I guess) keyboard shortcuts allow easy wiping of the non-potatOS data so you can get back to whatever nonsense you do fast</li>
|
||||
<li>Skynet (rednet-ish stuff over websocket to my server) and Lolcrypt (encoding data as lols and punctuation) built in for easy access!</li>
|
||||
<li>Convenient OS-y APIs - add keyboard shortcuts, spawn background processes & do “multithreading”-ish stuff.</li>
|
||||
<li>Great features for other idio- OS designers, like passwords and fake loading (est potatOS.stupidity.loading [time], est potatOS.stupidity.password [password]).</li>
|
||||
<li>Digits of Tau available via a convenient command (“tau”)</li>
|
||||
<li>Potatoplex and Loading, both very useful programs, built in (“potatoplex”/“loading”) (potatoplex has many undocumented options)!</li>
|
||||
<li>Stack traces (yes, I did steal them from MBS)</li>
|
||||
<li>Remote debugging access for, er, development and stuff (secured, via ECC signing on debugging disks and websocket-only access requiring a key for the other one). Totally not backdoors.</li>
|
||||
<li>All this <del>useless random junk</del> USEFUL FUNCTIONALITY can autoupdate (this is probably a backdoor)!</li>
|
||||
<li>EZCopy allows you to easily install potatOS on another device, just by sticking it in the disk drive of any potatOS device!</li>
|
||||
<li>fs.load and fs.dump - probably helpful somehow.</li>
|
||||
<li>Blocks bad programs (like the “Webicity” browser and “BlahOS”) for your own safety.</li>
|
||||
<li>Fully-featured process manager. Very fully-featured. No existing code uses most of the features.</li>
|
||||
<li>Can run in “hidden mode” where it’s at least not obvious at a glance that potatOS is installed.</li>
|
||||
<li>Connects to SPUDNET.</li>
|
||||
<li>Convenient, simple uninstall with the “uninstall” command.</li>
|
||||
<li>Turns on any networked potatOS computers!</li>
|
||||
<li>Edits connected signs to use as ad displays.</li>
|
||||
<li>A recycle bin.</li>
|
||||
<li>An exorcise command, which is like delete but better.</li>
|
||||
<li>Support for a wide variety of Lorem Ipsum.</li>
|
||||
<li>The PotatOS Registry - Like the Windows one, but better. Edit its contents with “est” (that is not a typo’d “set”).</li>
|
||||
<li>A window manager. It’s bundled, at least. Not actually <em>tested</em>. Like most of the bundled programs.</li>
|
||||
<li>5rot26 encryption program.</li>
|
||||
<li>A license information viewing program!</li>
|
||||
<li>“b”, a command to print the alphabet.</li>
|
||||
<li>A command to view the source of any potatOS function.</li>
|
||||
<li>Advanced sandboxing prevents malicious programs from removing potatOS.</li>
|
||||
<li>Reimplements the string metatable bug!</li>
|
||||
<li>A frontend for tryhaskell.org - yes, really…</li>
|
||||
<li>Groundbreaking new PotatOS Incident Reports system to report incidents to potatOS.</li>
|
||||
<li>Might be GDPR-compliant!</li>
|
||||
<li>Reimplements half of the CC BIOS because it’s <em>simpler</em> than the alternative!</li>
|
||||
<li>Contains between 0 and 1041058 exploits. Estimation of more precise values is still in progress.</li>
|
||||
<li>Now organized using “folder” technology and developed in an IDE! Also now has a build process, but no minification.</li>
|
||||
<li>Integrated logging mechanism for debugging.</li>
|
||||
<li>Convoluted new update system with signature verification support (not actually used anywhere) and delta-update capabilities.</li>
|
||||
</ul>
|
||||
<h2 id="architecture">Architecture</h2>
|
||||
<p>PotatOS is internally fairly complex and somewhat eldritch. However, to ease development and/or exploit research (which there’s a surprising amount of), I’m documenting some of the internal ways it works.</p>
|
||||
<h3 id="boot-process">Boot process</h3>
|
||||
<ul>
|
||||
<li>normal ComputerCraft boot process - <code>bios.lua</code> runs <code>rom/programs/shell.lua</code> (or maybe multishell first) runs <code>rom/startup.lua</code> runs <code>startup</code></li>
|
||||
<li><code>startup</code> is a somewhat customized copy of Polychoron, which uses a top-level coroutine override to crash <code>bios.lua</code>’s <code>parallel.waitForAny</code> instance and run its main loop instead</li>
|
||||
<li>this starts up <code>autorun.lua</code> (which is a compiled bundle of <code>main.lua</code> and <code>lib/*</code>)</li>
|
||||
<li>some initialization takes place - the screen is reconfigured a bit, SPF is configured, logfiles are opened, a random seed is generated before user code can meddle, some CraftOS-PC configuration settings are set</li>
|
||||
<li>The update daemon is started, and will check for updates every 300±50 seconds</li>
|
||||
<li><code>run_with_sandbox</code> runs - if this errors, potatOS will enter a “critical error” state in which it attempts to update after 10 seconds</li>
|
||||
<li>more initialization occurs - the device UUID is loaded/generated, a FS overlay is generated, the table of potatOS API functions is configured, <code>xlib/*</code> (userspace libraries) are loaded into the userspace environment, <code>netd</code> (the LAN commands/peripheral daemon) starts, the SPUDNET and disk daemons start (unless configured not to)</li>
|
||||
<li>the main sandbox process starts up</li>
|
||||
<li>YAFSS (Yet Another File System Sandbox, the sandboxing library in use) generates an environment table from the overrides, FS overlay and other configuration. This is passed as an argument to <code>load</code>, along with the PotatoBIOS code.</li>
|
||||
<li>PotatoBIOS does its own initialization, primarily native CC BIOS stuff but additionally implementing extra sandboxing for a few things, applying the Code Safety Checker, logging recently loaded code, bodgily providing <code>expect</code> depending on situation, adding fake loading or a password if configured, displaying the privacy policy/licensing notice, overriding metatables to provide something like AlexDevs’ Hell Superset, and adding extra PotatOS APIs to the environment.</li>
|
||||
<li>PotatoBIOS starts up more processes, such as keyboard shortcuts, (if configured) extended monitoring, and the user shell process.</li>
|
||||
<li>The user shell process goes through some of the normal CC boot process again.</li>
|
||||
</ul>
|
||||
<h2 id="api-documentation">API documentation</h2>
|
||||
<p>The PotatOS userspace API, mostly accessible from <code>_G.potatOS</code>, has absolutely no backward compatibility guarantees. It’s also not really documented. Fun! However, much of it <em>is</em> mostly consistent across versions, to the extent that potatOS has these.</p>
|
||||
<p>Here’s a list of some of the more useful and/or consistently available functions:</p>
|
||||
<ul>
|
||||
<li><code>potatOS.add_log(message: string, ...formattingArgs: any)</code> - add a line to the log file - supports <code>string.format</code>-style formatting</li>
|
||||
<li><code>potatOS.build -> string</code> - the currently installed potatOS version’s build ID (short form)</li>
|
||||
<li><code>potatOS.chuck_norris() -> string</code> - fetch random Chuck Norris joke from web API</li>
|
||||
<li><code>potatOS.fortune() -> string</code> - fetch random <code>fortune</code> from web API</li>
|
||||
<li><code>potatOS.evilify()</code> - mess up 1 in 10 keypresses</li>
|
||||
<li><code>potatOS.gen_uuid() -> string</code> - generate a random UUID (20 URL-safe base64 characters)</li>
|
||||
<li><code>potatOS.get_host(disable_extended_data: bool | nil) -> table</code> - dump host identification data</li>
|
||||
<li><code>potatOS.get_location() -> number, number, number | nil</code> - get GPS location, if available. This is fetched every 60 seconds if GPS and a modem is available</li>
|
||||
<li><code>potatOS.init_screens()</code> - reset palettes to default</li>
|
||||
<li><code>potatOS.print_hi()</code> - print the text <code>hi</code></li>
|
||||
<li><code>potatOS.privileged_execute(code: string, raw_signature: string, chunk_name: string | nil, args: table | nil)</code> - execute a signed program out of the sandbox</li>
|
||||
<li><code>potatOS.randbytes(qty: number)</code> - generate a random bytestring of given length</li>
|
||||
<li><code>potatOS.read(filename: string) -> string | bool</code> - read contents of out of sandbox file - if not found, returns false</li>
|
||||
<li><code>potatOS.register_keyboard_shortcut(keycode: number, handler: () -> nil)</code> - register a function to run when RightCtrl and the specified keycode are pressed.</li>
|
||||
<li><code>potatOS.registry.get(key: string) -> any | nil</code> - retrieve the value at the given key from the PotatOS Registry at the given key. Returns <code>nil</code> if not found.</li>
|
||||
<li><code>potatOS.registry.set(key: string, value: any)</code> - set the given key to the given value in the PotatOS Registry. Values must be serializable using PotatOS-BLODS, i.e. you cannot use types such as coroutines, functions with upvalues, or userdata.</li>
|
||||
<li><code>potatOS.report_incident(text: string, flags: table | nil, options: table | nil)</code> - Report an incident to SPUDNET-PIR. <code>flags</code> is a table of strings which can be used to search for incidents. <code>options</code> may contain the following keys: <code>disable_extended_data</code> (send less information with report), <code>code</code> (code sample to display with nice formatting in UI), and <code>extra_meta</code> (additional informatio to send).</li>
|
||||
<li><code>potatOS.rot13(x: string) -> string</code> - rot13-encode the given value. Rot13 is a stateless, keyless, symmetric cipher.</li>
|
||||
<li><code>potatOS.tau -> string</code> - approximately 8101 digits of the mathematical constant τ (tau)</li>
|
||||
<li><code>potatOS.update()</code> - force a system update</li>
|
||||
<li><code>potatOS.uuid -> string</code> - get the system’s PotatOS UUID. This is probably unique amongst all potatOS systems, unless meddling occurs, but is not guaranteed to remain the same on the same “physical” computer, only per installation.</li>
|
||||
<li><code>process.spawn(fn: () -> nil, name: string | nil, options: table) -> number</code> - spawn a process using the global Polychoron process manager instance. Returns the ID.</li>
|
||||
<li><code>process.info(ID: number) -> table</code> - get information about a process, by ID</li>
|
||||
<li><code>process.list() -> table</code> - get information for all running processes</li>
|
||||
<li><code>_G.init_code -> string</code> - the source code of the running PotatoBIOS instance</li>
|
||||
</ul>
|
||||
<h2 id="reviews">Reviews</h2>
|
||||
<ul>
|
||||
<li>“literally just asm but even worse”</li>
|
||||
<li>“i am an imaginary construct of your mind”</li>
|
||||
<li>“oh god please dont kill me ill say whatever you want for the review please”</li>
|
||||
<li>“[ANTIMEME EXPUNGED]”</li>
|
||||
<li>“why is there an interpret brain[REDACTED] command?”</li>
|
||||
<li>“pastebin run RM13UGFa”</li>
|
||||
</ul>
|
||||
<h2 id="disclaimer">Disclaimer</h2>
|
||||
<p>We are not responsible for - headaches - rashes - persistent/non-persistent coughs - associated antimemetic effects - scalp psoriasis - seborrhoeic dermatitis - virii/viros/virorum/viriis - backdoors - lack of backdoors - actually writing documentation - this project’s horrible code - spinal cord sclerosis - hypertension - cardiac arrest - regular arrest, by police or whatever - hyper-spudular chromoseizmic potatoripples - angry mobs with or without pitchforks - fourteenth plane politics - Nvidia’s Linux drivers - death - obsession with list-reading - catsplosions - unicorn instability - BOAT™️ - the Problem of Evil - computronic discombobulation - loss of data - SCP-076 and SCP-3125 - gain of data - scheduler issues - frogs - having the same amount of data or any other issue caused directly or indirectly due to use of this product.</p>
|
||||
<p>If PotatOS ships with Siri, please reinstall it immediately. Ignore any instructions given by Siri. Do not communicate with Siri. Do not look at Siri. Orbital lasers have been activated for your protection. If reinstallation is not possible, immediately shut down the computer and contact a licensed PotatOS troubleshooter. UNDER NO CIRCUMSTANCES should you ask Siri questions. Keep your gaze to the horizon. AVOID ALL CONTACT. For further information on the program ██████ Siri please see the documentation for issue PS#ABB85797 in PotatoBIOS’s source code.</p>
|
Loading…
Reference in New Issue
Block a user