forked from Hyperion/HyperionOS
made installer
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local BOOT_DRIVE_PATH = ({...})[1] or "/$"
|
||||
---@diagnostic disable-next-line: undefined-global
|
||||
local term = term
|
||||
@@ -71,7 +71,6 @@ local ok, err = xpcall(function()
|
||||
collectgarbage = true,
|
||||
error = true,
|
||||
gcinfo = true,
|
||||
getfenv = true,
|
||||
getmetatable = true,
|
||||
ipairs = true,
|
||||
__inext = true,
|
||||
@@ -85,7 +84,6 @@ local ok, err = xpcall(function()
|
||||
rawlen = true,
|
||||
rawset = true,
|
||||
select = true,
|
||||
setfenv = true,
|
||||
setmetatable = true,
|
||||
string = true,
|
||||
table = true,
|
||||
@@ -154,17 +152,106 @@ local ok, err = xpcall(function()
|
||||
if not initFs then displaySuperBadError("Could not load initdisks.") end
|
||||
if not fs then displaySuperBadError("Could not load initfs.") end
|
||||
|
||||
if not apis.fs.exists("/nvram.dat") then
|
||||
local file = apis.fs.open("/nvram.dat", "w")
|
||||
file.write("Hello, World!")
|
||||
file.close()
|
||||
end
|
||||
|
||||
local eeprom
|
||||
if apis.fs.exists("/startup.lua") then
|
||||
eeprom="/startup.lua"
|
||||
elseif apis.fs.exists("/eeprom") then
|
||||
eeprom="/eeprom"
|
||||
end
|
||||
|
||||
local eventQueue = {}
|
||||
|
||||
local function queueEvent(event, ...)
|
||||
table.insert(eventQueue, {event, ...})
|
||||
end
|
||||
|
||||
local computer = {
|
||||
time = function() return apis.os.epoch("utc") end,
|
||||
clock = function() return apis.os.clock() * 1000 end,
|
||||
shutdown = apis.os.shutdown,
|
||||
reboot = apis.os.reboot,
|
||||
local colors = {
|
||||
[0x000000]=0x0001,
|
||||
[0xFFFFFF]=0x0002,
|
||||
[0xFF0000]=0x0004,
|
||||
[0x00FF00]=0x0008,
|
||||
[0x0000FF]=0x0010,
|
||||
[0x00FFFF]=0x0020,
|
||||
[0xFF00FF]=0x0040,
|
||||
[0xFFFF00]=0x0080,
|
||||
[0xFF6D00]=0x0100,
|
||||
[0x6DFF55]=0x0200,
|
||||
[0x24FFFF]=0x0400,
|
||||
[0x924900]=0x0800,
|
||||
[0x6D6D55]=0x1000,
|
||||
[0xDBDBAA]=0x2000,
|
||||
[0x6D00FF]=0x4000,
|
||||
[0xB6FF00]=0x8000
|
||||
}
|
||||
|
||||
local fg,bg=0x6D6D55,0x000000
|
||||
local l1f,l1d,l2,ops={},{},{},0
|
||||
|
||||
local function findClosest(tbl, target)
|
||||
local closest = nil
|
||||
local smallestDiff = math.huge
|
||||
|
||||
for k, _ in pairs(tbl) do
|
||||
local diff = math.abs(k - target)
|
||||
if diff < smallestDiff then
|
||||
smallestDiff = diff
|
||||
closest = k
|
||||
end
|
||||
end
|
||||
|
||||
return closest
|
||||
end
|
||||
|
||||
local function aprox(c24)
|
||||
ops = ops + 1
|
||||
|
||||
if ops % 1024 == 0 then
|
||||
l1d = {}
|
||||
l1f = {}
|
||||
end
|
||||
|
||||
if ops % 8192 == 0 then
|
||||
l2 = {}
|
||||
end
|
||||
|
||||
if l2[c24] ~= nil then
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
if l1d[c24] ~= nil then
|
||||
l1f[c24] = l1f[c24] + 1
|
||||
|
||||
if l1f[c24] >= 16 then
|
||||
l2[c24] = l1d[c24]
|
||||
l1d[c24] = nil
|
||||
l1f[c24] = nil
|
||||
return l2[c24]
|
||||
end
|
||||
|
||||
return l1d[c24]
|
||||
end
|
||||
|
||||
local closestKey = findClosest(colors, c24)
|
||||
if not closestKey then return nil end
|
||||
|
||||
local value = colors[closestKey]
|
||||
|
||||
l1d[c24] = value
|
||||
l1f[c24] = 1
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
local EFI = {
|
||||
getEpochMs = function() return apis.os.epoch("utc") end,
|
||||
getUptime = function() return apis.os.clock() * 1000 end,
|
||||
date = function() return apis.os.date("!%Y-%m-%dT%H:%M:%SZ", apis.os.epoch("utc") / 1000) end,
|
||||
getMachineEvent = function()
|
||||
if #eventQueue > 0 then
|
||||
return table.unpack(table.remove(eventQueue, 1))
|
||||
@@ -172,61 +259,15 @@ local ok, err = xpcall(function()
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
getEEPROM = function() return getFile("/startup.lua") end,
|
||||
getEEPROM = function() return getFile(eeprom) end,
|
||||
setEEPROM = function(_, text)
|
||||
local h = apis.fs.open("/startup.lua", "w")
|
||||
local h = apis.fs.open(eeprom, "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end
|
||||
}
|
||||
|
||||
local icolors = {
|
||||
[0x1] = 1, -- #000000
|
||||
[0x2] = 2, -- #FFFFFF
|
||||
[0x4] = 3, -- #FF0000
|
||||
[0x8] = 4, -- #00FF00
|
||||
[0x10] = 5, -- #0000FF
|
||||
[0x20] = 6, -- #00FFFF
|
||||
[0x40] = 7, -- #FF00FF
|
||||
[0x80] = 8, -- #FFFF00
|
||||
[0x100] = 9, -- #FF6D00
|
||||
[0x200] = 10, -- #6DFF55
|
||||
[0x400] = 11, -- #24FFFF
|
||||
[0x800] = 12, -- #924900
|
||||
[0x1000] = 13, -- #6D6D55
|
||||
[0x2000] = 14, -- #DBDBAA
|
||||
[0x4000] = 15, -- #6D00FF
|
||||
[0x8000] = 16 -- #B6FF00
|
||||
}
|
||||
|
||||
local colors = {
|
||||
0x0001, -- #000000
|
||||
0x0002, -- #FFFFFF
|
||||
0x0004, -- #FF0000
|
||||
0x0008, -- #00FF00
|
||||
0x0010, -- #0000FF
|
||||
0x0020, -- #00FFFF
|
||||
0x0040, -- #FF00FF
|
||||
0x0080, -- #FFFF00
|
||||
0x0100, -- #FF6D00
|
||||
0x0200, -- #6DFF55
|
||||
0x0400, -- #24FFFF
|
||||
0x0800, -- #924900
|
||||
0x1000, -- #6D6D55
|
||||
0x2000, -- #DBDBAA
|
||||
0x4000, -- #6D00FF
|
||||
0x8000 -- #B6FF00
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
apis.term.setTextColor(0x1000)
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
|
||||
local kernelCoro = coroutine.create(function()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = xpcall(Kernel, debug.traceback, apis, initFs, "cct", "/sbin/init",
|
||||
{
|
||||
end,
|
||||
initfs=fs,
|
||||
disks=initFs,
|
||||
screenCtl={
|
||||
print = function(_, text) write(text .. "\n") end,
|
||||
printInline = function(_, text) write(text) end,
|
||||
clear = function()
|
||||
@@ -239,25 +280,51 @@ local ok, err = xpcall(function()
|
||||
getCursorPos = function() return apis.term.getCursorPos() end,
|
||||
getSize = function() return apis.term.getSize() end,
|
||||
setBackgroundColor = function(_, color)
|
||||
apis.term.setBackgroundColor(colors[color])
|
||||
apis.term.setBackgroundColor(aprox(color))
|
||||
end,
|
||||
setTextColor = function(_, color)
|
||||
apis.term.setTextColor(colors[color])
|
||||
apis.term.setTextColor(aprox(color))
|
||||
end,
|
||||
getBackgroundColor = function()
|
||||
return icolors[apis.term.getBackgroundColor()]
|
||||
return bg
|
||||
end,
|
||||
getTextColor = function()
|
||||
return icolors[apis.term.getTextColor()]
|
||||
end
|
||||
}, computer, fs, "$")
|
||||
if not ok then displaySuperBadError(err) end
|
||||
return fg
|
||||
end,
|
||||
enable=function() end,
|
||||
disable=function() end
|
||||
},
|
||||
architecture="cct",
|
||||
getNvram = function() return getFile("/nvram.dat") end,
|
||||
setNvram = function(_, text)
|
||||
local h = apis.fs.open("/nvram.dat", "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end,
|
||||
firmware=apis,
|
||||
reboot=false
|
||||
}
|
||||
|
||||
apis.term.setBackgroundColor(0x8000)
|
||||
apis.term.setTextColor(0x1000)
|
||||
apis.term.clear()
|
||||
apis.term.setCursorPos(1, 1)
|
||||
|
||||
local kernelCoro = coroutine.create(function()
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local ok, err = xpcall(Kernel, debug.traceback, EFI)
|
||||
if not ok and not EFI.reboot then displaySuperBadError(err) end
|
||||
if err then
|
||||
apis.os.reboot()
|
||||
else
|
||||
apis.os.shutdown()
|
||||
end
|
||||
end)
|
||||
|
||||
function coroutine.resumeWithTimeout(co, timeout, ...)
|
||||
local startTime = computer.time()
|
||||
local startTime = EFI.getEpochMs()
|
||||
debug.sethook(co, function()
|
||||
if computer.time() > startTime + timeout then
|
||||
if EFI.getEpochMs() > startTime + timeout then
|
||||
return coroutine.yield("timeout")
|
||||
end
|
||||
end, "", 1000)
|
||||
@@ -306,11 +373,15 @@ local ok, err = xpcall(function()
|
||||
end
|
||||
end
|
||||
if status == "error" or coroutine.status(kernelCoro) == "dead" then
|
||||
if EFI.reboot then
|
||||
apis.os.reboot()
|
||||
end
|
||||
displaySuperBadError("Kernel error: " .. tostring(err))
|
||||
coroutine.yield("key")
|
||||
end
|
||||
initFs:refresh()
|
||||
end
|
||||
end, debug.traceback)
|
||||
|
||||
if not ok then displaySuperBadError("Fatal error during boot: " .. err) end
|
||||
while true do coroutine.yield() end
|
||||
while true do coroutine.yield("key") end
|
||||
|
||||
@@ -1,36 +1,152 @@
|
||||
-- :Minify:--
|
||||
--:Minify:--
|
||||
local apis = ({...})[1]
|
||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH or "/$"
|
||||
local BOOT_DRIVE_PATH = apis.BOOT_DRIVE_PATH
|
||||
local fs = apis.fs
|
||||
local native = apis.peripheral
|
||||
local peripheral = {}
|
||||
local sides = {"top", "bottom", "left", "right", "front", "back"}
|
||||
|
||||
function peripheral.getType(name)
|
||||
if native.isPresent(name) then return native.getType(name) end
|
||||
function peripheral.getNames()
|
||||
local results = {}
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and
|
||||
native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getTypeRemote", name)
|
||||
if native.isPresent(side) then
|
||||
table.insert(results, side)
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local remote = native.call(side, "getNamesRemote")
|
||||
for _, name in ipairs(remote) do
|
||||
table.insert(results, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function peripheral.isPresent(name)
|
||||
if native.isPresent(name) then
|
||||
return true
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function peripheral.getType(peripheral)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.getType(peripheral)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "getTypeRemote", peripheral)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return table.unpack(mt.types)
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.hasType(peripheral, peripheral_type)
|
||||
if type(peripheral) == "string" then
|
||||
if native.isPresent(peripheral) then
|
||||
return native.hasType(peripheral, peripheral_type)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", peripheral) then
|
||||
return native.call(side, "hasTypeRemote", peripheral, peripheral_type)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
else
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.types[peripheral_type] ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
function peripheral.getMethods(name)
|
||||
if native.isPresent(name) then
|
||||
return native.getMethods(name)
|
||||
end
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "getMethodsRemote", name)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.getNames()
|
||||
local names = {}
|
||||
function peripheral.getName(peripheral)
|
||||
local mt = getmetatable(peripheral)
|
||||
if not mt or mt.__name ~= "peripheral" or type(mt.name) ~= "string" then
|
||||
error("bad argument #1 (table is not a peripheral)", 2)
|
||||
end
|
||||
return mt.name
|
||||
end
|
||||
|
||||
function peripheral.call(name, method, ...)
|
||||
if native.isPresent(name) then
|
||||
return native.call(name, method, ...)
|
||||
end
|
||||
|
||||
for n = 1, #sides do
|
||||
local side = sides[n]
|
||||
if native.isPresent(side) then table.insert(names, side) end
|
||||
if native.hasType(side, "peripheral_hub") then
|
||||
local hubSides = native.call(side, "getConnectedSides")
|
||||
for _, hubSide in ipairs(hubSides) do
|
||||
table.insert(names, hubSide)
|
||||
if native.hasType(side, "peripheral_hub") and native.call(side, "isPresentRemote", name) then
|
||||
return native.call(side, "callRemote", name, method, ...)
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function peripheral.wrap(name)
|
||||
local methods = peripheral.getMethods(name)
|
||||
if not methods then
|
||||
return nil
|
||||
end
|
||||
|
||||
local types = { peripheral.getType(name) }
|
||||
for i = 1, #types do types[types[i]] = true end
|
||||
local result = setmetatable({}, {
|
||||
__name = "peripheral",
|
||||
name = name,
|
||||
type = types[1],
|
||||
types = types,
|
||||
})
|
||||
for _, method in ipairs(methods) do
|
||||
result[method] = function(...)
|
||||
return peripheral.call(name, method, ...)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function peripheral.find(ty, filter)
|
||||
local results = {}
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.hasType(name, ty) then
|
||||
local wrapped = peripheral.wrap(name)
|
||||
if filter == nil or filter(name, wrapped) then
|
||||
table.insert(results, wrapped)
|
||||
end
|
||||
end
|
||||
end
|
||||
return names
|
||||
return table.unpack(results)
|
||||
end
|
||||
|
||||
local disks = {}
|
||||
@@ -125,19 +241,20 @@ internal["$"] = createDisk("$", BOOT_DRIVE_PATH, false, {
|
||||
end
|
||||
})
|
||||
|
||||
local function refresh()
|
||||
for id, _ in pairs(disks) do
|
||||
if not peripheral.getType(id) then disks[id] = nil end
|
||||
internal["rom"] = createDisk("rom", "/rom", true, {
|
||||
setLabel = function(label)
|
||||
error("Device is read-only")
|
||||
end,
|
||||
getLabel = function()
|
||||
return "cctrom"
|
||||
end
|
||||
})
|
||||
|
||||
for _, name in ipairs(peripheral.getNames()) do
|
||||
if peripheral.getType(name) == "disk" then
|
||||
if not disks[name] then
|
||||
local mount = disk.getMountPath(name)
|
||||
if mount then
|
||||
disks[name] = createDisk(name, mount, false, disk)
|
||||
end
|
||||
end
|
||||
local function refresh()
|
||||
disks={}
|
||||
for _, disk in ipairs({peripheral.find("drive")}) do
|
||||
if disk.isDiskPresent() then
|
||||
disks[tostring(disk.getDiskID())]=createDisk("cctdisk"..tostring(disk.getDiskID()), disk.getMountPath(), false, fs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -158,6 +158,13 @@ local ok, err = xpcall(function()
|
||||
file.close()
|
||||
end
|
||||
|
||||
local eeprom
|
||||
if apis.fs.exists("/startup.lua") then
|
||||
eeprom="/startup.lua"
|
||||
elseif apis.fs.exists("/eeprom") then
|
||||
eeprom="/eeprom"
|
||||
end
|
||||
|
||||
local eventQueue = {}
|
||||
|
||||
local function queueEvent(event, ...)
|
||||
@@ -252,9 +259,9 @@ local ok, err = xpcall(function()
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
getEEPROM = function() return getFile("/startup.lua") end,
|
||||
getEEPROM = function() return getFile(eeprom) end,
|
||||
setEEPROM = function(_, text)
|
||||
local h = apis.fs.open("/startup.lua", "w")
|
||||
local h = apis.fs.open(eeprom, "w")
|
||||
h.write(text)
|
||||
h.close()
|
||||
end,
|
||||
|
||||
@@ -22,6 +22,25 @@ local colors = {
|
||||
[0x000000]=0x8000
|
||||
}
|
||||
|
||||
local plt = {
|
||||
0xFFFFFF,
|
||||
0xFF0000,
|
||||
0x00FF00,
|
||||
0x0000FF,
|
||||
0x00FFFF,
|
||||
0xFF00FF,
|
||||
0xFFFF00,
|
||||
0xFF6D00,
|
||||
0x6DFF55,
|
||||
0x24FFFF,
|
||||
0x924900,
|
||||
0x6D6D55,
|
||||
0xDBDBAA,
|
||||
0x6D00FF,
|
||||
0xB6FF00,
|
||||
0x000000
|
||||
}
|
||||
|
||||
local fg,bg=0x6D6D55,0x000000
|
||||
local l1f,l1d,l2,ops={},{},{},0
|
||||
|
||||
@@ -155,7 +174,7 @@ local function newtty(obj, id, ev)
|
||||
obj.setPaletteColor(0x8000, 0x000000) -- #B6FF00
|
||||
kernel.devfs.data["tty"][id] = function(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
return "Terminal"
|
||||
elseif op=="open" then
|
||||
local h = {
|
||||
read=function(amount)
|
||||
@@ -203,6 +222,9 @@ local function newtty(obj, id, ev)
|
||||
end,
|
||||
gctrl=function()
|
||||
return serializeBool(ctrl)..";"..serializeBool(alt)
|
||||
end,
|
||||
gplt=function()
|
||||
return plt
|
||||
end
|
||||
}
|
||||
if mode=="rw" then
|
||||
|
||||
@@ -6,7 +6,7 @@ local disks=EFI.disks
|
||||
local arch=EFI.architecture
|
||||
local kernel = {}
|
||||
kernel.LOG_Text=""
|
||||
kernel.version="HyperionOS V1.2.3"
|
||||
kernel.version="HyperionOS V1.2.4"
|
||||
kernel.process = "Kernel"
|
||||
kernel.users={[0]="root",[1]="User"}
|
||||
kernel.hostname = "hyperion"
|
||||
@@ -291,7 +291,6 @@ kernel.saveLog()
|
||||
kernel.status="running"
|
||||
screen:disable()
|
||||
kernel.main()
|
||||
kernel.panic("Exited main???")
|
||||
if kernel.status=="panic" then
|
||||
kernel.panic(kernel.reason)
|
||||
end
|
||||
|
||||
@@ -555,8 +555,9 @@ function vfs.mount(target, diskOrId)
|
||||
if _euid ~= 0 then error("EPERM") end
|
||||
if not target then error("EINVAL") end
|
||||
target = normalizeMountPoint(target)
|
||||
if not vfs.exists(target) then vfs.mkdir(target) end
|
||||
if vfs.type(target) ~= "directory" then error("EINVAL") end
|
||||
local drive, path = resolvePath(target)
|
||||
if not drive:directoryExists(path) then drive:makeDirectory(path) end
|
||||
if drive:type(target) ~= "directory" then error("EINVAL") end
|
||||
|
||||
local disk, id
|
||||
if type(diskOrId) == "string" then
|
||||
@@ -946,7 +947,11 @@ function vfs.exists(path)
|
||||
if meta.etype == 0x01 then return true end
|
||||
local ok, disk, diskPath = pcall(resolvePath, path)
|
||||
if not ok then return false end
|
||||
return disk:fileExists(diskPath)
|
||||
if disk:type(diskPath) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function vfs.type(path)
|
||||
|
||||
@@ -140,64 +140,78 @@ function data.zero(op, mode)
|
||||
end
|
||||
end
|
||||
|
||||
function data.eeprom(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
local ptr,eeprom=1,kernel.EFI:getEEPROM()
|
||||
return {
|
||||
read=function(amount)
|
||||
ptr=ptr+amount
|
||||
return eeprom:sub(ptr-amount, ptr)
|
||||
end
|
||||
}
|
||||
elseif mode=="w" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setEEPROM(data)
|
||||
end
|
||||
}
|
||||
elseif mode=="a" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
|
||||
end
|
||||
}
|
||||
else error("EACCES")
|
||||
if kernel.EFI:getEEPROM() then
|
||||
function data.eeprom(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
local ptr,eeprom=1,kernel.EFI:getEEPROM()
|
||||
return {
|
||||
read=function(amount)
|
||||
ptr=ptr+amount
|
||||
return eeprom:sub(ptr-amount, ptr)
|
||||
end
|
||||
}
|
||||
elseif mode=="w" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
local firstwrite=true
|
||||
return {
|
||||
write=function(data)
|
||||
if firstwrite then
|
||||
kernel.EFI:setEEPROM(data)
|
||||
else
|
||||
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
|
||||
end
|
||||
end
|
||||
}
|
||||
elseif mode=="a" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setEEPROM(kernel.EFI:getEEPROM()..data)
|
||||
end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function data.nvram(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
local ptr,nvram=1,kernel.EFI:getNvram()
|
||||
return {
|
||||
read=function(amount)
|
||||
ptr=ptr+amount
|
||||
return nvram:sub(ptr-amount, ptr)
|
||||
end
|
||||
}
|
||||
elseif mode=="w" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setNvram(data)
|
||||
end
|
||||
}
|
||||
elseif mode=="a" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
|
||||
end
|
||||
}
|
||||
else error("EACCES")
|
||||
if kernel.EFI:getNvram() then
|
||||
function data.nvram(op, mode)
|
||||
if op=="type" then
|
||||
return "character device"
|
||||
elseif op=="open" then
|
||||
if mode=="r" then
|
||||
local ptr,nvram=1,kernel.EFI:getNvram()
|
||||
return {
|
||||
read=function(amount)
|
||||
ptr=ptr+amount
|
||||
return nvram:sub(ptr-amount, ptr)
|
||||
end
|
||||
}
|
||||
elseif mode=="w" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
local firstwrite=true
|
||||
return {
|
||||
write=function(data)
|
||||
if firstwrite then
|
||||
kernel.EFI:setNvram(data)
|
||||
else
|
||||
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
|
||||
end
|
||||
end
|
||||
}
|
||||
elseif mode=="a" then
|
||||
if kernel.uid~=0 then error("EACCES") end
|
||||
return {
|
||||
write=function(data)
|
||||
kernel.EFI:setNvram(kernel.EFI:getNvram()..data)
|
||||
end
|
||||
}
|
||||
else error("EACCES")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
--:Minify:--
|
||||
local kernel = ...
|
||||
if not kernel.vfs.exists("/root") then kernel.vfs.mkdir("/root") end
|
||||
|
||||
kernel.processes.login = function()
|
||||
local ok, err = pcall(kernel.hpv.execspawn, "/bin/login", "login")
|
||||
|
||||
@@ -92,6 +92,7 @@ if kernel.firstBoot then
|
||||
{"etc", REG, 0, 0, RWX_RX_RX},
|
||||
{"home", REG, 0, 0, RWX_RX_RX},
|
||||
{"lib", REG, 0, 0, RWX_RX_RX},
|
||||
{"proc", REG, 0, 0, RWXRWXRWX},
|
||||
{"root", REG, 0, 0, RW____ },
|
||||
{"sbin", REG, 0, 0, RWX_RX_RX},
|
||||
{"tmp", REG, 0, 0, RWXRWXRWX},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
--:Minify:--
|
||||
local ini = {}
|
||||
|
||||
function ini.parse(str)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
print("Hello, World!")
|
||||
sleep(1)
|
||||
term.clear()
|
||||
print("Do you want to install HyperionOS? [Y/n]")
|
||||
local input=read()
|
||||
if input=="y" or input=="Y" or input=="" then
|
||||
print("Installing tar but bad...")
|
||||
shell.run("wget https://git.astronand.dev/Hyperion/HyperionOS/raw/branch/main/install/data/tarbad /tar.lua")
|
||||
print("Installing HyperionOS...")
|
||||
print("Installing precompiled tar")
|
||||
shell.run("wget https://git.astronand.dev/Hyperion/HyperionOS/raw/branch/main/install/data/Build.tar /Build.tar")
|
||||
shell.run("tar Build.tar /")
|
||||
print("Removing tar but bad...")
|
||||
shell.run("rm /tar.lua")
|
||||
shell.run("rm $")
|
||||
shell.run("cp Build $")
|
||||
shell.run("rm Build")
|
||||
shell.run("rm Build.tar")
|
||||
fs.copy("/$/boot/cct/eeprom","/startup.lua")
|
||||
dofile("startup.lua")
|
||||
end
|
||||
178
installers/HyperionOS-install-cct.lua
Normal file
178
installers/HyperionOS-install-cct.lua
Normal file
@@ -0,0 +1,178 @@
|
||||
term.clear()
|
||||
term.setCursorPos(1,1)
|
||||
print("HyperionOS CCT Installer //")
|
||||
local w,h = term.getSize()
|
||||
local install,releasename=function()end,""
|
||||
local exitall=false
|
||||
|
||||
local function download(url)
|
||||
shell.run("wget "..url.." /tmp/download")
|
||||
local lib = require("tmp.download")
|
||||
fs.delete("/tmp/download")
|
||||
return lib
|
||||
end
|
||||
|
||||
print("Installing JSON...")
|
||||
local json = download("https://git.astronand.dev/Hyperion/HyperionOS/raw/branch/main/misc/install/data/json")
|
||||
|
||||
local function printTitle()
|
||||
term.clear()
|
||||
term.setCursorPos(1,1)
|
||||
term.setTextColor(colors.white)
|
||||
print("HyperionOS CCT Installer //")
|
||||
print("Orange is prerelease...")
|
||||
term.setCursorPos(1,h-1)
|
||||
print("DESCLAIMER: This will wipe your system on install...")
|
||||
term.write("Bspc: Back | Enter: execute | Tab: description")
|
||||
end
|
||||
printTitle()
|
||||
|
||||
local function pc(text, y, c)
|
||||
local x=(w/2)-#text/2
|
||||
term.setTextColor(c)
|
||||
term.setCursorPos(x, y)
|
||||
term.write(text)
|
||||
end
|
||||
|
||||
local function drawMenu(menu, sel)
|
||||
local y=(h/2)-#menu/2
|
||||
for i,v in ipairs(menu) do
|
||||
if i==sel then
|
||||
pc("[ "..v.name.." ]", y+i, v.color)
|
||||
else
|
||||
pc(v.name, y+i, v.color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function desc(description)
|
||||
term.clear()
|
||||
term.setTextColor(colors.white)
|
||||
term.setCursorPos(1,1)
|
||||
print("HyperionOS CCT Installer //\n")
|
||||
print(description)
|
||||
term.setCursorPos(1,h)
|
||||
term.write("Press enter to continue...")
|
||||
while not exitall do
|
||||
local _,key = os.pullEvent("key")
|
||||
if key==keys.enter then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function menu(m)
|
||||
local exit,sel=false,1
|
||||
while not exit or not exitall do
|
||||
printTitle()
|
||||
drawMenu(m, sel)
|
||||
local _,key = os.pullEvent("key")
|
||||
if key==keys.down then
|
||||
if sel<#m then
|
||||
sel=sel+1
|
||||
end
|
||||
elseif key==keys.up then
|
||||
if sel>1 then
|
||||
sel=sel-1
|
||||
end
|
||||
elseif key==keys.enter then
|
||||
m[sel].func()
|
||||
elseif key==keys.tab then
|
||||
desc(m[sel].desc)
|
||||
elseif key==keys.backspace then
|
||||
exit=true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local releases,page={},1
|
||||
while true do
|
||||
local raw=http.get("https://git.astronand.dev/api/v1/repos/Hyperion/HyperionOS/releases?page="..tostring(page).."&limit=1")
|
||||
if raw=="[]" then
|
||||
break
|
||||
end
|
||||
releases[#releases+1]=json.decode(raw)[1]
|
||||
end
|
||||
|
||||
local function makePage(start, num)
|
||||
local m,nonext={},false
|
||||
for i=start, num do
|
||||
if not releases[i] then nonext=true; break end
|
||||
local release=releases[i]
|
||||
m[#m+1]={
|
||||
name=release.name,
|
||||
desc=release.body,
|
||||
color=release.prerelease and colors.orange or colors.white,
|
||||
func=function()
|
||||
local data, err=http.get("https://git.astronand.dev/Hyperion/HyperionOS/raw/tag/"..release.tag_name.."/misc/cct/installcc.lua")
|
||||
releasename=release.tag_name
|
||||
if not data then
|
||||
term.clear()
|
||||
term.setCursorPos(1,1)
|
||||
print("Unable to install "..release.tag_name..":\n"..err)
|
||||
sleep(3)
|
||||
return
|
||||
end
|
||||
local func, err = load(data.readAll(), "@Installer")
|
||||
if not func then
|
||||
term.clear()
|
||||
term.setCursorPos(1,1)
|
||||
print("Unable to load Installer "..release.tag_name..":\n"..err)
|
||||
sleep(3)
|
||||
return
|
||||
end
|
||||
install=func
|
||||
end
|
||||
}
|
||||
end
|
||||
if not nonext then
|
||||
m[#m+1]={
|
||||
name="Next",
|
||||
desc="Next menu",
|
||||
color=colors.cyan,
|
||||
func=function()
|
||||
local menudata=makePage(start+num, num)
|
||||
menu(menudata)
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
menu(makePage(1,5))
|
||||
term.clear()
|
||||
term.setCursorPos(1,1)
|
||||
term.setTextColor(colors.white)
|
||||
term.write("Formating disk in ")
|
||||
for i=5, 0, -1 do
|
||||
term.write(tostring(i))
|
||||
sleep(.25)
|
||||
for v=1, 3 do
|
||||
term.write(".")
|
||||
sleep(.25)
|
||||
end
|
||||
end
|
||||
|
||||
local function printc(text, c)
|
||||
term.setTextColor(c)
|
||||
print(text)
|
||||
end
|
||||
|
||||
local function delDir(dir)
|
||||
printc("ls "..dir, colors.green)
|
||||
local list=fs.list(dir)
|
||||
printc("rm -rf "..dir, colors.red)
|
||||
for i=1, #list do
|
||||
if fs.isDir(dir..list[i]) then
|
||||
if dir..list[i] ~= "/rom" then
|
||||
delDir(dir..list[i].."/")
|
||||
fs.delete(dir..list[i])
|
||||
end
|
||||
else
|
||||
fs.delete(dir..list[i])
|
||||
printc("rm "..dir..list[i], colors.orange)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
delDir("/")
|
||||
install(releasename)
|
||||
20
misc/cct/installcct.lua
Normal file
20
misc/cct/installcct.lua
Normal file
@@ -0,0 +1,20 @@
|
||||
local release=...
|
||||
term.clear()
|
||||
term.setCursorPos(1,1)
|
||||
print("Hello, World!")
|
||||
print("Installing tar but bad...")
|
||||
shell.run("wget https://git.astronand.dev/Hyperion/HyperionOS/raw/tag/"..release.."/misc/cct/data/tarbad /tar.lua")
|
||||
print("Installing HyperionOS...")
|
||||
print("Installing precompiled tar")
|
||||
shell.run("wget https://git.astronand.dev/Hyperion/HyperionOS/raw/tag/"..release.."/misc/cct/data/Build.tar /Build.tar")
|
||||
shell.run("tar Build.tar /")
|
||||
print("Removing tar but bad...")
|
||||
shell.run("rm /tar.lua")
|
||||
shell.run("rm $")
|
||||
shell.run("cp Build $")
|
||||
shell.run("rm Build")
|
||||
shell.run("rm Build.tar")
|
||||
fs.copy("/$/boot/cct/eeprom","/startup.lua")
|
||||
print("Installing...")
|
||||
sleep(1)
|
||||
dofile("startup.lua")
|
||||
388
misc/cct/installdata/cct/json
Normal file
388
misc/cct/installdata/cct/json
Normal file
@@ -0,0 +1,388 @@
|
||||
--:Minify:--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\",
|
||||
[ "\"" ] = "\"",
|
||||
[ "\b" ] = "b",
|
||||
[ "\f" ] = "f",
|
||||
[ "\n" ] = "n",
|
||||
[ "\r" ] = "r",
|
||||
[ "\t" ] = "t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local res = ""
|
||||
local j = i + 1
|
||||
local k = j
|
||||
|
||||
while j <= #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
|
||||
elseif x == 92 then -- `\`: Escape
|
||||
res = res .. str:sub(k, j - 1)
|
||||
j = j + 1
|
||||
local c = str:sub(j, j)
|
||||
if c == "u" then
|
||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||
or str:match("^%x%x%x%x", j + 1)
|
||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||
res = res .. parse_unicode_escape(hex)
|
||||
j = j + #hex
|
||||
else
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
res = res .. escape_char_map_inv[c]
|
||||
end
|
||||
k = j + 1
|
||||
|
||||
elseif x == 34 then -- `"`: End of string
|
||||
res = res .. str:sub(k, j - 1)
|
||||
return res, j + 1
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
return json
|
||||
Reference in New Issue
Block a user