From e9e68d9f284da651965499b7c37535cc9fb151ff Mon Sep 17 00:00:00 2001 From: JeremyStarTM Date: Mon, 18 Jul 2022 19:33:40 +0200 Subject: [PATCH] Initial commit --- README.md | 5 + bin/alias | 26 + bin/apis | 14 + bin/archiver | 131 ++++ bin/attach | 16 + bin/cat | 18 + bin/cd | 14 + bin/clear | 44 ++ bin/convert | 23 + bin/cp | 32 + bin/credits | 1 + bin/detach | 2 + bin/df | 12 + bin/drive | 21 + bin/eject | 18 + bin/env | 40 + bin/exit | 5 + bin/firststeps | 1 + bin/gclone | 994 ++++++++++++++++++++++++ bin/gist | 109 +++ bin/gps | 94 +++ bin/hostname | 18 + bin/list | 37 + bin/lsh | 102 +++ bin/man | 7 + bin/manedit | 13 + bin/manpages | 1 + bin/mkdir | 18 + bin/monitor | 95 +++ bin/mount | 23 + bin/mv | 47 ++ bin/nano | 772 ++++++++++++++++++ bin/netutil1 | 4 + bin/nmap | 50 ++ bin/passwd | 50 ++ bin/pastebin | 160 ++++ bin/peripherals | 10 + bin/redstone | 118 +++ bin/rm | 30 + bin/screenfetch | 375 +++++++++ bin/script | 7 + bin/set | 55 ++ bin/sh | 256 ++++++ bin/time | 3 + bin/tpm-install | 65 ++ bin/tpm-list | 30 + bin/tpm-recache | 13 + bin/tpm-remove | 68 ++ bin/tpm-repair | 14 + bin/tty | 2 + bin/type | 17 + bin/uname | 62 ++ bin/unmount | 4 + bin/useradd | 25 + bin/userdel | 27 + bin/wget | 92 +++ boot/freax/boot.conf | 6 + boot/freax/freax-small.img | 7 + boot/freax/freax.img | 7 + boot/freax/initrd.img | 169 ++++ boot/freax/kernel.boot | 376 +++++++++ boot/freax/kerneldraw.boot | 286 +++++++ boot/ofbl | 83 ++ boot/ofbl.conf | 7 + etc/autorun | 8 + etc/errorLog | 2 + etc/lib/config | 22 + etc/lib/exception | 26 + etc/lib/log | 8 + etc/lib/luaex | 31 + etc/lib/man | 12 + etc/lib/net | 70 ++ etc/lib/search | 53 ++ etc/lib/security | 42 + etc/lib/tpm | 25 + etc/link.dat | 1 + etc/manuals/api-config | 10 + etc/manuals/api-custom | 4 + etc/manuals/api-gui | 17 + etc/manuals/api-kernel | 25 + etc/manuals/api-log | 12 + etc/manuals/api-luaex | 8 + etc/manuals/api-search | 9 + etc/manuals/api-security | 9 + etc/manuals/api-tpm | 8 + etc/manuals/archiver | 17 + etc/manuals/auth | 11 + etc/manuals/boot-sequence | 18 + etc/manuals/categories | 7 + etc/manuals/changelog | 15 + etc/manuals/config | 7 + etc/manuals/convert | 6 + etc/manuals/coreutils | 15 + etc/manuals/credits | 16 + etc/manuals/custom | 8 + etc/manuals/custom-autorun | 11 + etc/manuals/custom-boot | 16 + etc/manuals/custom-bootloaders | 11 + etc/manuals/custom-build | 17 + etc/manuals/custom-script | 14 + etc/manuals/custom-tpm | 17 + etc/manuals/demos | 15 + etc/manuals/df | 12 + etc/manuals/dmesg | 5 + etc/manuals/extensions | 11 + etc/manuals/firststeps | 13 + etc/manuals/man | 11 + etc/manuals/manedit | 7 + etc/manuals/nmap | 10 + etc/manuals/script | 9 + etc/manuals/shell | 11 + etc/manuals/shutdown | 12 + etc/manuals/superuser | 13 + etc/manuals/tpm | 17 + etc/manuals/uname | 17 + etc/manuals/usrutils | 10 + etc/manuals/versioning | 13 + etc/manuals/versions | 2 + etc/passwd/.shadow/lsh.usr | 7 + etc/passwd/.shadow/root.usr | 7 + etc/passwd/lsh.dat | 6 + etc/passwd/root.dat | 6 + etc/passwd/user.dat | 6 + etc/script_util/@ | 1 + etc/script_util/error | 12 + etc/script_util/scomment | 1 + etc/script_util/sdelay | 7 + etc/script_util/secho | 9 + etc/script_util/slog_auth | 16 + etc/script_util/slog_boot | 16 + etc/script_util/slog_message | 16 + etc/script_util/slog_security | 16 + etc/script_util/slog_shutdown | 16 + etc/script_util/slog_syslog | 16 + etc/scripts/tpm-recache.tsf | 5 + etc/system | 3 + etc/tpm/package.dat | 28 + etc/tpm/packageInstalled.dat | 1 + reload.sh | 56 ++ replace.sh | 96 +++ sbin/asm-reload | 44 ++ sbin/dmesg | 35 + sbin/errpt | 11 + sbin/halt | 1 + sbin/login | 44 ++ sbin/panic | 1 + sbin/poweroff | 1 + sbin/reboot | 1 + sbin/shutdown | 39 + startup | 8 + usr/demos/hips-latest | 26 + usr/demos/module | 5 + usr/demos/nmap | 50 ++ usr/demos/script.tsf | 8 + usr/demos/winsetup | 36 + usr/local/bin/adventure | 1339 ++++++++++++++++++++++++++++++++ usr/local/bin/gfxpaint | 25 + usr/local/bin/hello | 5 + usr/local/bin/levels/0.dat | 8 + usr/local/bin/levels/1.dat | 7 + usr/local/bin/levels/10.dat | 11 + usr/local/bin/levels/11.dat | 10 + usr/local/bin/levels/12.dat | 12 + usr/local/bin/levels/2.dat | 10 + usr/local/bin/levels/3.dat | 10 + usr/local/bin/levels/4.dat | 10 + usr/local/bin/levels/5.dat | 10 + usr/local/bin/levels/6.dat | 11 + usr/local/bin/levels/7.dat | 10 + usr/local/bin/levels/8.dat | 10 + usr/local/bin/levels/9.dat | 12 + usr/local/bin/paint | 452 +++++++++++ usr/local/bin/pngview | 716 +++++++++++++++++ usr/local/bin/redirection | 703 +++++++++++++++++ usr/local/bin/worm | 282 +++++++ 175 files changed, 10337 insertions(+) create mode 100644 README.md create mode 100644 bin/alias create mode 100644 bin/apis create mode 100644 bin/archiver create mode 100644 bin/attach create mode 100644 bin/cat create mode 100644 bin/cd create mode 100644 bin/clear create mode 100644 bin/convert create mode 100644 bin/cp create mode 100644 bin/credits create mode 100644 bin/detach create mode 100644 bin/df create mode 100644 bin/drive create mode 100644 bin/eject create mode 100644 bin/env create mode 100644 bin/exit create mode 100644 bin/firststeps create mode 100644 bin/gclone create mode 100644 bin/gist create mode 100644 bin/gps create mode 100644 bin/hostname create mode 100644 bin/list create mode 100644 bin/lsh create mode 100644 bin/man create mode 100644 bin/manedit create mode 100644 bin/manpages create mode 100644 bin/mkdir create mode 100644 bin/monitor create mode 100644 bin/mount create mode 100644 bin/mv create mode 100644 bin/nano create mode 100644 bin/netutil1 create mode 100644 bin/nmap create mode 100644 bin/passwd create mode 100644 bin/pastebin create mode 100644 bin/peripherals create mode 100644 bin/redstone create mode 100644 bin/rm create mode 100644 bin/screenfetch create mode 100644 bin/script create mode 100644 bin/set create mode 100644 bin/sh create mode 100644 bin/time create mode 100644 bin/tpm-install create mode 100644 bin/tpm-list create mode 100644 bin/tpm-recache create mode 100644 bin/tpm-remove create mode 100644 bin/tpm-repair create mode 100644 bin/tty create mode 100644 bin/type create mode 100644 bin/uname create mode 100644 bin/unmount create mode 100644 bin/useradd create mode 100644 bin/userdel create mode 100644 bin/wget create mode 100644 boot/freax/boot.conf create mode 100644 boot/freax/freax-small.img create mode 100644 boot/freax/freax.img create mode 100644 boot/freax/initrd.img create mode 100644 boot/freax/kernel.boot create mode 100644 boot/freax/kerneldraw.boot create mode 100644 boot/ofbl create mode 100644 boot/ofbl.conf create mode 100644 etc/autorun create mode 100644 etc/errorLog create mode 100644 etc/lib/config create mode 100644 etc/lib/exception create mode 100644 etc/lib/log create mode 100644 etc/lib/luaex create mode 100644 etc/lib/man create mode 100644 etc/lib/net create mode 100644 etc/lib/search create mode 100644 etc/lib/security create mode 100644 etc/lib/tpm create mode 100644 etc/link.dat create mode 100644 etc/manuals/api-config create mode 100644 etc/manuals/api-custom create mode 100644 etc/manuals/api-gui create mode 100644 etc/manuals/api-kernel create mode 100644 etc/manuals/api-log create mode 100644 etc/manuals/api-luaex create mode 100644 etc/manuals/api-search create mode 100644 etc/manuals/api-security create mode 100644 etc/manuals/api-tpm create mode 100644 etc/manuals/archiver create mode 100644 etc/manuals/auth create mode 100644 etc/manuals/boot-sequence create mode 100644 etc/manuals/categories create mode 100644 etc/manuals/changelog create mode 100644 etc/manuals/config create mode 100644 etc/manuals/convert create mode 100644 etc/manuals/coreutils create mode 100644 etc/manuals/credits create mode 100644 etc/manuals/custom create mode 100644 etc/manuals/custom-autorun create mode 100644 etc/manuals/custom-boot create mode 100644 etc/manuals/custom-bootloaders create mode 100644 etc/manuals/custom-build create mode 100644 etc/manuals/custom-script create mode 100644 etc/manuals/custom-tpm create mode 100644 etc/manuals/demos create mode 100644 etc/manuals/df create mode 100644 etc/manuals/dmesg create mode 100644 etc/manuals/extensions create mode 100644 etc/manuals/firststeps create mode 100644 etc/manuals/man create mode 100644 etc/manuals/manedit create mode 100644 etc/manuals/nmap create mode 100644 etc/manuals/script create mode 100644 etc/manuals/shell create mode 100644 etc/manuals/shutdown create mode 100644 etc/manuals/superuser create mode 100644 etc/manuals/tpm create mode 100644 etc/manuals/uname create mode 100644 etc/manuals/usrutils create mode 100644 etc/manuals/versioning create mode 100644 etc/manuals/versions create mode 100644 etc/passwd/.shadow/lsh.usr create mode 100644 etc/passwd/.shadow/root.usr create mode 100644 etc/passwd/lsh.dat create mode 100644 etc/passwd/root.dat create mode 100644 etc/passwd/user.dat create mode 100644 etc/script_util/@ create mode 100644 etc/script_util/error create mode 100644 etc/script_util/scomment create mode 100644 etc/script_util/sdelay create mode 100644 etc/script_util/secho create mode 100644 etc/script_util/slog_auth create mode 100644 etc/script_util/slog_boot create mode 100644 etc/script_util/slog_message create mode 100644 etc/script_util/slog_security create mode 100644 etc/script_util/slog_shutdown create mode 100644 etc/script_util/slog_syslog create mode 100644 etc/scripts/tpm-recache.tsf create mode 100644 etc/system create mode 100644 etc/tpm/package.dat create mode 100644 etc/tpm/packageInstalled.dat create mode 100755 reload.sh create mode 100755 replace.sh create mode 100644 sbin/asm-reload create mode 100644 sbin/dmesg create mode 100644 sbin/errpt create mode 100644 sbin/halt create mode 100644 sbin/login create mode 100644 sbin/panic create mode 100644 sbin/poweroff create mode 100644 sbin/reboot create mode 100644 sbin/shutdown create mode 100644 startup create mode 100644 usr/demos/hips-latest create mode 100644 usr/demos/module create mode 100644 usr/demos/nmap create mode 100644 usr/demos/script.tsf create mode 100644 usr/demos/winsetup create mode 100644 usr/local/bin/adventure create mode 100644 usr/local/bin/gfxpaint create mode 100644 usr/local/bin/hello create mode 100644 usr/local/bin/levels/0.dat create mode 100644 usr/local/bin/levels/1.dat create mode 100644 usr/local/bin/levels/10.dat create mode 100644 usr/local/bin/levels/11.dat create mode 100644 usr/local/bin/levels/12.dat create mode 100644 usr/local/bin/levels/2.dat create mode 100644 usr/local/bin/levels/3.dat create mode 100644 usr/local/bin/levels/4.dat create mode 100644 usr/local/bin/levels/5.dat create mode 100644 usr/local/bin/levels/6.dat create mode 100644 usr/local/bin/levels/7.dat create mode 100644 usr/local/bin/levels/8.dat create mode 100644 usr/local/bin/levels/9.dat create mode 100644 usr/local/bin/paint create mode 100644 usr/local/bin/pngview create mode 100644 usr/local/bin/redirection create mode 100644 usr/local/bin/worm diff --git a/README.md b/README.md new file mode 100644 index 0000000..72dfb64 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# **FREAX** +**FREAX** is a operating system written in Lua and can only be ran in ComputerCraft, a Minecraft modification. It is designed to be as Linux-y as possible. + +# **Install** +Just clone the repository and move everything into your computer's data folder. A advanced installation documentation will be added soon. diff --git a/bin/alias b/bin/alias new file mode 100644 index 0000000..e540a81 --- /dev/null +++ b/bin/alias @@ -0,0 +1,26 @@ +local tArgs = { ... } +if #tArgs > 2 then + local programName = "alias" + print("Usage: " .. programName .. " ") + return +end + +local sAlias = tArgs[1] +local sProgram = tArgs[2] + +if sAlias and sProgram then + -- Set alias + shell.setAlias(sAlias, sProgram) +elseif sAlias then + -- Clear alias + shell.clearAlias(sAlias) +else + -- List aliases + local tAliases = shell.aliases() + local tList = {} + for sAlias, sCommand in pairs(tAliases) do + table.insert(tList, sAlias .. ":" .. sCommand) + end + table.sort(tList) + textutils.pagedTabulate(tList) +end diff --git a/bin/apis b/bin/apis new file mode 100644 index 0000000..3fd56c8 --- /dev/null +++ b/bin/apis @@ -0,0 +1,14 @@ +local tApis = {} +for k, v in pairs(_G) do + if type(k) == "string" and type(v) == "table" and k ~= "_G" then + table.insert(tApis, k) + end +end +table.insert(tApis, "shell") +table.insert(tApis, "package") +if multishell then + table.insert(tApis, "multishell") +end +table.sort(tApis) + +textutils.pagedTabulate(tApis) diff --git a/bin/archiver b/bin/archiver new file mode 100644 index 0000000..1f5c503 --- /dev/null +++ b/bin/archiver @@ -0,0 +1,131 @@ +print("Debian CC Archiver v0.1 by 1Ridav") +tArgs = {...} +local function getPath(input) +if string.sub(input, 1, 1)=="/" then path=input else +path=("/" .. shell.dir() .. "/" .. input) end +return path +end +if tArgs[2] then +tArgs[2]=getPath(tArgs[2]) +end + +local FFormat = ".arch" +local nFile, nDir, size = 0, 0 + +if #tArgs < 3 then + print("Usage:" + .."\narchiver zip [DIRtoSkip] [DIRtoSkip]" + .."\narchiver unzip ") +end + +local function fopen(path, mode) + local f = fs.open(path, mode) + if not f then + print("ERROR: Could not open "..path.." with mode \""..mode.."\"") + exit() + end + return f +end + +local function skip(df) + for i = 3, #tArgs do + if tArgs[i] == fs.getName(df) then + return true + end + end + for i = 4, #tArgs do + if tArgs[i] == fs.getName(df) then + return true + end + end + return false +end + +local function zip(file) + print("zipping: ".. file) + local f = fopen(file, "r") + local z = textutils.serialize(f.readAll()) + f.close() + return z +end + +local function ZIP(path) + local list = fs.list(path) + local array = {} + local t, name, d = 0, "", 0 + + for i = 2, #list * 2, 2 do + t = i/2 + local tpath = path.."/"..list[t] + if fs.isDir(tpath) then + if not skip(tpath) then + name = "D:"..list[t] + array[i] = ZIP(tpath) + nDir = nDir + 1 + end + else + name = "F:"..list[t] + array[i] = zip(tpath) + nFile = nFile + 1 + end + array[i - 1] = name + end + + return textutils.serialize(array) +end + +local function unzip(text, path) + print("unzipping: "..path) + local f = fopen(path, "w") + f.write(textutils.unserialize(text)) + f.close() +end + +local function UNZIP(text, path) + local array = textutils.unserialize(text) + local unz, dp + local d = 0 + for i = 2, #array, 2 do + if string.sub(array[i-1], 1, 1) == "D" then + dp = string.sub(array[i-1], 3, #array[i-1]) + fs.makeDir(path.."/"..dp) + UNZIP(array[i], path.."/"..dp) + nDir = nDir + 1 + elseif string.sub(array[i-1], 1, 1) == "F" then + local p = string.sub(array[i-1], 3, #array[i-1]) + unzip(array[i], path.."/"..p) + nFile = nFile + 1 + end + end +end + +local function result() + print("\nDone" + ,"\n size: " + ,size, " B " + ,math.floor(size/1024), " KB" + ,"\n Files: ", nFile + ,"\n Folders: ", nDir + ) +end + +if tArgs[1] == "zip" then + if fs.exists(tArgs[2]) and fs.isDir(tArgs[2]) then + local zipped = ZIP(shell.resolve(tArgs[2])) + local f = fs.open(tArgs[3]..FFormat, "w") + f.write(zipped) + f.close() + zipped = nil + size = fs.getSize(tArgs[3]..FFormat) + result() + end + +elseif tArgs[1] == "unzip" then + local f = fopen(tArgs[2], "r") + if not fs.exists(tArgs[3]) then + fs.makeDir(tArgs[3]) + end + UNZIP(f.readAll(), tArgs[3]) + size = fs.getSize(tArgs[2]) + result() +end \ No newline at end of file diff --git a/bin/attach b/bin/attach new file mode 100644 index 0000000..c6871b5 --- /dev/null +++ b/bin/attach @@ -0,0 +1,16 @@ +if periphemu == nil then error("Attaching peripherals is not supported in vanilla mode.") end +local args = { ... } +if args[1] == "list" then + print("Available peripheral types:") + for _,p in ipairs(periphemu.names()) do print(p) end +elseif type(args[1]) ~= "string" or type(args[2]) ~= "string" then + print("Usage: attach [options...]\n attach list") +else + if peripheral.isPresent(args[1]) and peripheral.getType(args[1]) == args[2] then + print("Peripheral already attached") + return + end + if tonumber(args[3]) ~= nil then args[3] = tonumber(args[3]) end + local ok, err = periphemu.create(args[1], args[2], args[3]) + if not ok then printError("Could not attach peripheral" .. (err and ": " .. err or "")) end +end \ No newline at end of file diff --git a/bin/cat b/bin/cat new file mode 100644 index 0000000..55a5256 --- /dev/null +++ b/bin/cat @@ -0,0 +1,18 @@ +local tArgs = {...} + +if #tArgs >= 1 then + path = shell.resolve(tArgs[1]) + if fs.isReadOnly(path) and not security.getSU() then + -- prevent users from seeing unauthorized code + -- they should only be able to see their own files and ones in /var/log + exception.throw("SecurityException", path) + return + end + if not fs.exists(path) or fs.isDir(path) then + kerneldraw.printAppInfo("cat", "File or directory not found") + else + textutils.pagedPrint(kernel.printFile(path)) + end +else + kerneldraw.printAppInfo("cat", "No file specified") +end diff --git a/bin/cd b/bin/cd new file mode 100644 index 0000000..f127ea8 --- /dev/null +++ b/bin/cd @@ -0,0 +1,14 @@ +local tArgs = { ... } +if #tArgs < 1 then + local programName = arg[0] or fs.getName(shell.getRunningProgram()) + print("Usage: " .. programName .. " ") + return +end + +local sNewDir = shell.resolve(tArgs[1]) +if fs.isDir(sNewDir) then + shell.setDir(sNewDir) +else + print("Not a directory") + return +end diff --git a/bin/clear b/bin/clear new file mode 100644 index 0000000..4a1e4c3 --- /dev/null +++ b/bin/clear @@ -0,0 +1,44 @@ +local tArgs = { ... } + +local function printUsage() + local programName = "clear" + print("Usages:") + print(programName) + print(programName .. " screen") + print(programName .. " palette") + print(programName .. " all") +end + +local function clear() + term.clear() + term.setCursorPos(1, 1) +end + +local function clearPixels() + if term.getGraphicsMode then + term.setGraphicsMode(1) + term.clear() + term.setGraphicsMode(0) + end +end + +local function resetPalette() + for i = 0, 15 do + term.setPaletteColour(2^i, term.nativePaletteColour(2^i)) + end +end + +local sCommand = tArgs[1] or "screen" +if sCommand == "screen" then + clear() +elseif sCommand == "palette" then + resetPalette() +elseif sCommand == "graphics" then + clearPixels() +elseif sCommand == "all" then + clear() + clearPixels() + resetPalette() +else + printUsage() +end diff --git a/bin/convert b/bin/convert new file mode 100644 index 0000000..ff35658 --- /dev/null +++ b/bin/convert @@ -0,0 +1,23 @@ +local args = {...} + +local function checksys(txt) + if txt == "kernel" or txt == "gui" or txt == "app" or txt == "log" or txt == "config" or txt == "manual" or txt == "search" or txt == "tpm" or txt == "exception" or string.find(txt, "boot/") or string.find(txt, "system/") or string.find(txt, ".freax/startup") then + kerneldraw.printAppWarning("kernel", "attempt to perform restricted operations") + --return true + end +end + +if #args < 1 or not fs.exists(shell.resolveProgram(args[1])) or fs.isDir(shell.resolve(args[1])) then + kerneldraw.printAppInfo("config", "No file specified") + return +end + +if not checksys(shell.resolveProgram(args[1])) then + if os.loadAPI(shell.resolveProgram(args[1])) then + config.save(_G[args[1]], textutils.serialize(args[1])) + os.unloadAPI(args[1]) + kerneldraw.printAppSuccess("config", "Done") + end +else + exception.throw("RestrictedOpsException") +end \ No newline at end of file diff --git a/bin/cp b/bin/cp new file mode 100644 index 0000000..930b501 --- /dev/null +++ b/bin/cp @@ -0,0 +1,32 @@ +local tArgs = { ... } +if #tArgs < 2 then + local programName = "cp" + print("Usage: " .. programName .. " ") + return +end + +local sSource = shell.resolve(tArgs[1]) +local sDest = shell.resolve(tArgs[2]) +local tFiles = fs.find(sSource) +if #tFiles > 0 then + for _, sFile in ipairs(tFiles) do + if fs.isDir(sDest) then + fs.copy(sFile, fs.combine(sDest, fs.getName(sFile))) + elseif #tFiles == 1 then + if fs.exists(sDest) then + printError("Destination exists") + elseif fs.isReadOnly(sDest) then + printError("Destination is read-only") + elseif fs.getFreeSpace(sDest) < fs.getSize(sFile) then + printError("Not enough space") + else + fs.copy(sFile, sDest) + end + else + printError("Cannot overwrite file multiple times") + return + end + end +else + printError("No matching files") +end diff --git a/bin/credits b/bin/credits new file mode 100644 index 0000000..f9b46af --- /dev/null +++ b/bin/credits @@ -0,0 +1 @@ +shell.run("/bin/man credits") \ No newline at end of file diff --git a/bin/detach b/bin/detach new file mode 100644 index 0000000..dce2ff6 --- /dev/null +++ b/bin/detach @@ -0,0 +1,2 @@ +if periphemu == nil then error("Attaching peripherals is not supported in vanilla mode.") end +if type(({...})[1]) ~= "string" then print("Usage: detach ") else if not periphemu.remove(({...})[1]) then printError("Could not detach peripheral") end end \ No newline at end of file diff --git a/bin/df b/bin/df new file mode 100644 index 0000000..65971d2 --- /dev/null +++ b/bin/df @@ -0,0 +1,12 @@ +local tArgs = {...} +local sz = fs.getSize(shell.dir()) +local fspc = fs.getFreeSpace(shell.dir()) + +kerneldraw.printAppInfo("type", fs.getDrive(shell.dir())) +if search.findValue(tArgs, "-sB") then + kerneldraw.printAppInfo("size", tostring(sz).." B") + kerneldraw.printAppInfo("free space", tostring(fspc).." B") +elseif search.findValue(tArgs, "-sKB") then + kerneldraw.printAppInfo("size", tostring(sz/1024).." KB") + kerneldraw.printAppInfo("free space", tostring(fspc/1024).." KB") +end diff --git a/bin/drive b/bin/drive new file mode 100644 index 0000000..799b65e --- /dev/null +++ b/bin/drive @@ -0,0 +1,21 @@ +local tArgs = { ... } + +-- Get where a directory is mounted +local sPath = shell.dir() +if tArgs[1] ~= nil then + sPath = shell.resolve(tArgs[1]) +end + +if fs.exists(sPath) then + write(fs.getDrive(sPath) .. " (") + local nSpace = fs.getFreeSpace(sPath) + if nSpace >= 1000 * 1000 then + print(math.floor(nSpace / (100 * 1000)) / 10 .. "MB remaining)") + elseif nSpace >= 1000 then + print(math.floor(nSpace / 100) / 10 .. "KB remaining)") + else + print(nSpace .. "B remaining)") + end +else + print("No such path") +end diff --git a/bin/eject b/bin/eject new file mode 100644 index 0000000..f56b76d --- /dev/null +++ b/bin/eject @@ -0,0 +1,18 @@ +-- Get arguments +local tArgs = { ... } +if #tArgs == 0 then + local programName = "eject" + print("Usage: " .. programName .. " ") + return +end + +local sDrive = tArgs[1] + +-- Check the disk exists +local bPresent = disk.isPresent(sDrive) +if not bPresent then + print("Nothing in " .. sDrive .. " drive") + return +end + +disk.eject(sDrive) diff --git a/bin/env b/bin/env new file mode 100644 index 0000000..eb5221f --- /dev/null +++ b/bin/env @@ -0,0 +1,40 @@ +if shell.environment == nil then error("Requires the cash shell.") end +local args = {...} + +local env = setmetatable({}, {__index = _ENV}) +local path = shell.path() +local unset = {} +local cmd = {} +local cmdargs = false +local nextarg + +for k,v in ipairs(args) do + if nextarg then + if nextarg == 1 then path = v + elseif nextarg == 2 then table.insert(unset, v) end + nextarg = nil + elseif cmdargs then + table.insert(cmd, v) + else + if v == "-i" then setmetatable(env, {__index = _G}) + elseif v == "-P" then nextarg = 1 + elseif v == "-u" then nextarg = 2 + elseif string.find(v, "=") then env[string.sub(v, 1, string.find(v, "=") - 1)] = string.sub(v, string.find(v, "=") + 1) + else table.insert(cmd, v); cmdargs = true end + end +end + +if #unset > 0 then + local oldidx = getmetatable(env).__index + local u = {} + for k,v in ipairs(unset) do u[v] = true end + setmetatable(env, {__index = function(self, name) if u[name] then return nil else return oldidx[name] end end}) +end + +local oldPath = shell.path() +local oldEnv = shell.environment() +shell.setPath(path) +shell.setEnvironment(env) +shell.run(table.unpack(cmd)) +shell.setPath(oldPath) +shell.setEnvironment(oldEnv) \ No newline at end of file diff --git a/bin/exit b/bin/exit new file mode 100644 index 0000000..87fbd07 --- /dev/null +++ b/bin/exit @@ -0,0 +1,5 @@ +if security.getSU() then + shell.exit() +else + kernel.reboot(true) +end diff --git a/bin/firststeps b/bin/firststeps new file mode 100644 index 0000000..6748762 --- /dev/null +++ b/bin/firststeps @@ -0,0 +1 @@ +shell.run("/bin/man firststeps") \ No newline at end of file diff --git a/bin/gclone b/bin/gclone new file mode 100644 index 0000000..da1fd0e --- /dev/null +++ b/bin/gclone @@ -0,0 +1,994 @@ +local replaceLine = true -- Setting to false may result in corrupted output +local preload = type(package) == "table" and type(package.preload) == "table" and package.preload or {} + +local require = require +if type(require) ~= "function" then + local loading = {} + local loaded = {} + require = function(name) + local result = loaded[name] + + if result ~= nil then + if result == loading then + error("loop or previous error loading module '" .. name .. "'", 2) + end + + return result + end + + loaded[name] = loading + local contents = preload[name] + if contents then + result = contents(name) + else + error("cannot load '" .. name .. "'", 2) + end + + if result == nil then result = true end + loaded[name] = result + return result + end +end +preload["objects"] = function(...) +local inflate_zlib = require "deflate".inflate_zlib +local sha = require "metis.crypto.sha1" + +local band, bor, lshift, rshift = bit32.band, bit32.bor, bit32.lshift, bit32.rshift +local byte, format, sub = string.byte, string.format, string.sub + +local types = { [0] = "none", "commit", "tree", "blob", "tag", nil, "ofs_delta", "ref_delta", "any", "max" } + +--- Get the type of a specific object +-- @tparam Object x The object to get the type of +-- @treturn string The object's type. +local function get_type(x) return types[x.ty] or "?" end + +local event = ("luagit-%08x"):format(math.random(0, 2^24)) +local function check_in() + os.queueEvent(event) + os.pullEvent(event) +end + +local sha_format = ("%02x"):rep(20) + +local function reader(str) + local expected_checksum = format(sha_format, byte(str, -20, -1)) + local actual_checksum = sha(str:sub(1, -21)); + if expected_checksum ~= actual_checksum then + error(("checksum mismatch: expected %s, got %s"):format(expected_checksum, actual_checksum)) + end + + str = str:sub(1, -20) + + local pos = 1 + + local function consume_read(len) + if len <= 0 then error("len < 0", 2) end + if pos > #str then error("end of stream") end + + local cur_pos = pos + pos = pos + len + local res = sub(str, cur_pos, pos - 1) + if #res ~= len then error("expected " .. len .. " bytes, got" .. #res) end + return res + end + + local function read8() + if pos > #str then error("end of stream") end + local cur_pos = pos + pos = pos + 1 + return byte(str, cur_pos) + end + + return { + offset = function() return pos - 1 end, + read8 = read8, + read16 = function() return (read8() * (2^8)) + read8() end, + read32 = function() return (read8() * (2^24)) + (read8() * (2^16)) + (read8() * (2^8)) + read8() end, + read = consume_read, + + close = function() + if pos ~= #str then error(("%d of %d bytes remaining"):format(#str - pos + 1, #str)) end + end, + } +end + +--- Consume a string from the given input buffer +-- +-- @tparam Reader handle The handle to read from +-- @tparam number size The number of decompressed bytes to read +-- @treturn string The decompressed data +local function get_data(handle, size) + local tbl, n = {}, 1 + + inflate_zlib { + input = handle.read8, + output = function(x) tbl[n], n = string.char(x), n + 1 end + } + + local res = table.concat(tbl) + if #res ~= size then error(("expected %d decompressed bytes, got %d"):format(size, #res)) end + return res +end + +--- Decode a binary delta file, applying it to the original +-- +-- The format is described in more detail in [the Git documentation][git_pack] +-- +-- [git_pack]: https://git-scm.com/docs/pack-format#_deltified_representation +-- +-- @tparam string original The original string +-- @tparam string delta The binary delta +-- @treturn string The patched string +local function apply_delta(original, delta) + local delta_offset = 1 + local function read_size() + local c = byte(delta, delta_offset) + delta_offset = delta_offset + 1 + + local size = band(c, 0x7f) + local shift = 7 + while band(c, 0x80) ~= 0 do + c, delta_offset = byte(delta, delta_offset), delta_offset + 1 + size, shift = size + lshift(band(c, 0x7f), shift), shift + 7 + end + + return size + end + + local original_length = read_size() + local patched_length = read_size() + if original_length ~= #original then + error(("expected original of size %d, got size %d"):format(original_length, #original)) + end + + local parts, n = {}, 1 + while delta_offset <= #delta do + local b = byte(delta, delta_offset) + delta_offset = delta_offset + 1 + + if band(b, 0x80) ~= 0 then + -- Copy from the original file. Each bit represents which optional length/offset + -- bits are used. + local offset, length = 0, 0 + + if band(b, 0x01) ~= 0 then + offset, delta_offset = bor(offset, byte(delta, delta_offset)), delta_offset + 1 + end + if band(b, 0x02) ~= 0 then + offset, delta_offset = bor(offset, lshift(byte(delta, delta_offset), 8)), delta_offset + 1 + end + if band(b, 0x04) ~= 0 then + offset, delta_offset = bor(offset, lshift(byte(delta, delta_offset), 16)), delta_offset + 1 + end + if band(b, 0x08) ~= 0 then + offset, delta_offset = bor(offset, lshift(byte(delta, delta_offset), 24)), delta_offset + 1 + end + + if band(b, 0x10) ~= 0 then + length, delta_offset = bor(length, byte(delta, delta_offset)), delta_offset + 1 + end + if band(b, 0x20) ~= 0 then + length, delta_offset = bor(length, lshift(byte(delta, delta_offset), 8)), delta_offset + 1 + end + if band(b, 0x40) ~= 0 then + length, delta_offset = bor(length, lshift(byte(delta, delta_offset), 16)), delta_offset + 1 + end + if length == 0 then length = 0x10000 end + + parts[n], n = sub(original, offset + 1, offset + length), n + 1 + elseif b > 0 then + -- Copy from the delta. The opcode encodes the length + parts[n], n = sub(delta, delta_offset, delta_offset + b - 1), n + 1 + delta_offset = delta_offset + b + else + error(("unknown opcode '%02x'"):format(b)) + end + end + + local patched = table.concat(parts) + if patched_length ~= #patched then + error(("expected patched of size %d, got size %d"):format(patched_length, #patched)) + end + + return patched +end + +--- Unpack a single object, populating the output table +-- +-- @tparam Reader handle The handle to read from +-- @tparam { [string] = Object } out The populated data +local function unpack_object(handle, out) + local c = handle.read8() + local ty = band(rshift(c, 4), 7) + local size = band(c, 15) + local shift = 4 + while band(c, 0x80) ~= 0 do + c = handle.read8() + size = size + lshift(band(c, 0x7f), shift) + shift = shift + 7 + end + + local data + if ty >= 1 and ty <= 4 then + -- commit/tree/blob/tag + data = get_data(handle, size) + elseif ty == 6 then + -- ofs_delta + data = get_data(handle, size) + error("ofs_delta not yet implemented") + + elseif ty == 7 then + -- ref_delta + local base_hash = sha_format:format(handle.read(20):byte(1, 20)) + local delta = get_data(handle, size) + + local original = out[base_hash] + if not original then error(("cannot find object %d to apply diff"):format(base_hash)) return end + ty = original.ty + data = apply_delta(original.data, delta) + else + error(("unknown object of type '%d'"):format(ty)) + end + + -- We've got to do these separately. Format doesn't like null bytes + local whole = ("%s %d\0"):format(types[ty], #data) .. data + local sha = sha(whole) + out[sha] = { ty = ty, data = data, sha = sha } +end + +local function unpack(handle, progress) + local header = handle.read(4) + if header ~= "PACK" then error("expected PACK, got " .. header, 0) end + + local version = handle.read32() + local entries = handle.read32() + + local out = {} + for i = 1, entries do + if progress then progress(i, entries) end + check_in() + + unpack_object(handle, out) + end + + return out +end + +local function build_tree(objects, object, prefix, out) + if not prefix then prefix = "" end + if not out then out = {} end + + local idx = 1 + + while idx <= #object do + -- dddddd NAME\0 + local _, endidx, mode, name = object:find("^(%x+) ([^%z]+)%z", idx) + if not endidx then break end + name = prefix .. name + + local sha = object:sub(endidx + 1, endidx + 20):gsub(".", function(x) return ("%02x"):format(string.byte(x)) end) + + local entry = objects[sha] + if not entry then error(("cannot find %s %s (%s)"):format(mode, name, sha)) end + + if entry.ty == 3 then + out[name] = entry.data + elseif entry.ty == 2 then + build_tree(objects, entry.data, name .. "/", out) + else + error("unknown type for " .. name .. " (" .. sha .. "): " .. get_type(entry)) + end + + idx = endidx + 21 + end + + return out +end + +local function build_commit(objects, sha) + local commit = objects[sha] + if not commit then error("cannot find commit " .. sha) end + if commit.ty ~= 1 then error("Expected commit, got " .. types[commit.ty]) end + + local tree_sha = commit.data:match("tree (%x+)\n") + if not tree_sha then error("Cannot find tree from commit") end + + local tree = objects[tree_sha] + if not tree then error("cannot find tree " .. tree_sha) end + if tree.ty ~= 2 then error("Expected tree, got " .. tree[tree.ty]) end + + return build_tree(objects, tree.data) +end + +return { + reader = reader, + unpack = unpack, + build_tree = build_tree, + build_commit = build_commit, + type = get_type, +} +end +preload["network"] = function(...) +local function pkt_line(msg) + return ("%04x%s\n"):format(5 + #msg, msg) +end + +local function pkt_linef(fmt, ...) + return pkt_line(fmt:format(...)) +end + +local flush_line = "0000" + +local function read_pkt_line(handle) + local data = handle.read(4) + if data == nil or data == "" then return nil end + + local len = tonumber(data, 16) + if len == nil then + error(("read_pkt_line: cannot convert %q to a number"):format(data)) + elseif len == 0 then + return false, data + else + return handle.read(len - 4), data + end +end + +local function fetch(url, lines, content_type) + if type(lines) == "table" then lines = table.concat(lines) end + + local ok, err = http.request(url, lines, { + ['User-Agent'] = 'CCGit/1.0', + ['Content-Type'] = content_type, + }, true) + + if ok then + while true do + local event, event_url, param1, param2 = os.pullEvent() + if event == "http_success" and event_url == url then + return true, param1 + elseif event == "http_failure" and event_url == url then + printError("Cannot fetch " .. url .. ": " .. param1) + return false, param2 + end + end + else + printError("Cannot fetch " .. url .. ": " .. err) + return false, nil + end +end + +local function force_fetch(...) + local ok, handle, err_handle = fetch(...) + if not ok then + if err_handle then + print(err_handle.getStatusCode()) + print(textutils.serialize(err_handle.getResponseHeaders())) + print(err_handle.readAll()) + end + error("Cannot fetch", 0) + end + + return handle +end + +local function receive(handle) + local out = {} + while true do + local line = read_pkt_line(handle) + if line == nil then break end + out[#out + 1] = line + end + + handle.close() + return out +end + +return { + read_pkt_line = read_pkt_line, + force_fetch = force_fetch, + receive = receive, + + pkt_linef = pkt_linef, + flush_line = flush_line, +} +end +preload["deflate"] = function(...) +--[[ + (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). + + 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. + (end license) +--]] + +local assert, error, ipairs, pairs, tostring, type, setmetatable, io, math + = assert, error, ipairs, pairs, tostring, type, setmetatable, io, math +local table_sort, math_max, string_char = table.sort, math.max, string.char +local band, lshift, rshift = bit32.band, bit32.lshift, bit32.rshift + +local function make_outstate(outbs) + local outstate = {} + outstate.outbs = outbs + outstate.len = 0 + outstate.window = {} + outstate.window_pos = 1 + return outstate +end + + +local function output(outstate, byte) + local window_pos = outstate.window_pos + outstate.outbs(byte) + outstate.len = outstate.len + 1 + outstate.window[window_pos] = byte + outstate.window_pos = window_pos % 32768 + 1 -- 32K +end + + +local function noeof(val) + return assert(val, 'unexpected end of file') +end + +local function memoize(f) + return setmetatable({}, { + __index = function(self, k) + local v = f(k) + self[k] = v + return v + end + }) +end + +-- small optimization (lookup table for powers of 2) +local pow2 = memoize(function(n) return 2^n end) + +local function bitstream_from_bytestream(bys) + local buf_byte = 0 + local buf_nbit = 0 + local o = { type = "bitstream" } + + function o:nbits_left_in_byte() + return buf_nbit + end + + function o:read(nbits) + nbits = nbits or 1 + while buf_nbit < nbits do + local byte = bys() + if not byte then return end -- note: more calls also return nil + buf_byte = buf_byte + lshift(byte, buf_nbit) + buf_nbit = buf_nbit + 8 + end + local bits + if nbits == 0 then + bits = 0 + elseif nbits == 32 then + bits = buf_byte + buf_byte = 0 + else + bits = band(buf_byte, rshift(0xffffffff, 32 - nbits)) + buf_byte = rshift(buf_byte, nbits) + end + buf_nbit = buf_nbit - nbits + return bits + end + + return o +end + +local function get_bitstream(o) + if type(o) == "table" and o.type == "bitstream" then + return o + elseif io.type(o) == 'file' then + return bitstream_from_bytestream(function() local sb = o:read(1) if sb then return sb:byte() end end) + elseif type(o) == "function" then + return bitstream_from_bytestream(o) + else + error 'unrecognized type' + end +end + + +local function get_obytestream(o) + local bs + if io.type(o) == 'file' then + bs = function(sbyte) o:write(string_char(sbyte)) end + elseif type(o) == 'function' then + bs = o + else + error('unrecognized type: ' .. tostring(o)) + end + return bs +end + + +local function HuffmanTable(init, is_full) + local t = {} + if is_full then + for val,nbits in pairs(init) do + if nbits ~= 0 then + t[#t+1] = {val=val, nbits=nbits} + end + end + else + for i=1,#init-2,2 do + local firstval, nbits, nextval = init[i], init[i+1], init[i+2] + if nbits ~= 0 then + for val=firstval,nextval-1 do + t[#t+1] = {val=val, nbits=nbits} + end + end + end + end + table_sort(t, function(a,b) + return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits + end) + + -- assign codes + local code = 1 -- leading 1 marker + local nbits = 0 + for _, s in ipairs(t) do + if s.nbits ~= nbits then + code = code * pow2[s.nbits - nbits] + nbits = s.nbits + end + s.code = code + code = code + 1 + end + + local minbits = math.huge + local look = {} + for _, s in ipairs(t) do + minbits = math.min(minbits, s.nbits) + look[s.code] = s.val + end + + local msb = function(bits, nbits) + local res = 0 + for _ = 1, nbits do + res = lshift(res, 1) + band(bits, 1) + bits = rshift(bits, 1) + end + return res + end + + local tfirstcode = memoize( + function(bits) return pow2[minbits] + msb(bits, minbits) end) + + function t:read(bs) + local code = 1 -- leading 1 marker + local nbits = 0 + while 1 do + if nbits == 0 then -- small optimization (optional) + code = tfirstcode[noeof(bs:read(minbits))] + nbits = nbits + minbits + else + local b = noeof(bs:read()) + nbits = nbits + 1 + code = code * 2 + b -- MSB first + end + local val = look[code] + if val then + return val + end + end + end + + return t +end + +local function parse_zlib_header(bs) + local cm = bs:read(4) -- Compression Method + local cinfo = bs:read(4) -- Compression info + local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG) + local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary) + local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level) + local cmf = cinfo * 16 + cm -- CMF (Compresion Method and flags) + local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs + + if cm ~= 8 then -- not "deflate" + error("unrecognized zlib compression method: " .. cm) + end + if cinfo > 7 then + error("invalid zlib window size: cinfo=" .. cinfo) + end + local window_size = 2^(cinfo + 8) + + if (cmf*256 + flg) % 31 ~= 0 then + error("invalid zlib header (bad fcheck sum)") + end + + if fdict == 1 then + error("FIX:TODO - FDICT not currently implemented") + local dictid_ = bs:read(32) + end + + return window_size +end + +local function parse_huffmantables(bs) + local hlit = bs:read(5) -- # of literal/length codes - 257 + local hdist = bs:read(5) -- # of distance codes - 1 + local hclen = noeof(bs:read(4)) -- # of code length codes - 4 + + local ncodelen_codes = hclen + 4 + local codelen_init = {} + local codelen_vals = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} + for i=1,ncodelen_codes do + local nbits = bs:read(3) + local val = codelen_vals[i] + codelen_init[val] = nbits + end + local codelentable = HuffmanTable(codelen_init, true) + + local function decode(ncodes) + local init = {} + local nbits + local val = 0 + while val < ncodes do + local codelen = codelentable:read(bs) + --FIX:check nil? + local nrepeat + if codelen <= 15 then + nrepeat = 1 + nbits = codelen + elseif codelen == 16 then + nrepeat = 3 + noeof(bs:read(2)) + -- nbits unchanged + elseif codelen == 17 then + nrepeat = 3 + noeof(bs:read(3)) + nbits = 0 + elseif codelen == 18 then + nrepeat = 11 + noeof(bs:read(7)) + nbits = 0 + else + error 'ASSERT' + end + for _ = 1, nrepeat do + init[val] = nbits + val = val + 1 + end + end + local huffmantable = HuffmanTable(init, true) + return huffmantable + end + + local nlit_codes = hlit + 257 + local ndist_codes = hdist + 1 + + local littable = decode(nlit_codes) + local disttable = decode(ndist_codes) + + return littable, disttable +end + + +local tdecode_len_base +local tdecode_len_nextrabits +local tdecode_dist_base +local tdecode_dist_nextrabits +local function parse_compressed_item(bs, outstate, littable, disttable) + local val = littable:read(bs) + if val < 256 then -- literal + output(outstate, val) + elseif val == 256 then -- end of block + return true + else + if not tdecode_len_base then + local t = {[257]=3} + local skip = 1 + for i=258,285,4 do + for j=i,i+3 do t[j] = t[j-1] + skip end + if i ~= 258 then skip = skip * 2 end + end + t[285] = 258 + tdecode_len_base = t + end + if not tdecode_len_nextrabits then + local t = {} + for i=257,285 do + local j = math_max(i - 261, 0) + t[i] = rshift(j, 2) + end + t[285] = 0 + tdecode_len_nextrabits = t + end + local len_base = tdecode_len_base[val] + local nextrabits = tdecode_len_nextrabits[val] + local extrabits = bs:read(nextrabits) + local len = len_base + extrabits + + if not tdecode_dist_base then + local t = {[0]=1} + local skip = 1 + for i=1,29,2 do + for j=i,i+1 do t[j] = t[j-1] + skip end + if i ~= 1 then skip = skip * 2 end + end + tdecode_dist_base = t + end + if not tdecode_dist_nextrabits then + local t = {} + for i=0,29 do + local j = math_max(i - 2, 0) + t[i] = rshift(j, 1) + end + tdecode_dist_nextrabits = t + end + local dist_val = disttable:read(bs) + local dist_base = tdecode_dist_base[dist_val] + local dist_nextrabits = tdecode_dist_nextrabits[dist_val] + local dist_extrabits = bs:read(dist_nextrabits) + local dist = dist_base + dist_extrabits + + for _ = 1,len do + local pos = (outstate.window_pos - 1 - dist) % 32768 + 1 -- 32K + output(outstate, assert(outstate.window[pos], 'invalid distance')) + end + end + return false +end + + +local function parse_block(bs, outstate) + local bfinal = bs:read(1) + local btype = bs:read(2) + + local BTYPE_NO_COMPRESSION = 0 + local BTYPE_FIXED_HUFFMAN = 1 + local BTYPE_DYNAMIC_HUFFMAN = 2 + local _BTYPE_RESERVED = 3 + + if btype == BTYPE_NO_COMPRESSION then + bs:read(bs:nbits_left_in_byte()) + local len = bs:read(16) + local _nlen = noeof(bs:read(16)) + + for i=1,len do + local by = noeof(bs:read(8)) + output(outstate, by) + end + elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then + local littable, disttable + if btype == BTYPE_DYNAMIC_HUFFMAN then + littable, disttable = parse_huffmantables(bs) + else + littable = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil} + disttable = HuffmanTable {0,5, 32,nil} + end + + repeat + local is_done = parse_compressed_item( + bs, outstate, littable, disttable) + until is_done + else + error('unrecognized compression type '..btype) + end + + return bfinal ~= 0 +end + + +local function inflate(t) + local bs = get_bitstream(t.input) + local outbs = get_obytestream(t.output) + local outstate = make_outstate(outbs) + + repeat + local is_final = parse_block(bs, outstate) + until is_final +end + +local function adler32(byte, crc) + local s1 = crc % 65536 + local s2 = (crc - s1) / 65536 + s1 = (s1 + byte) % 65521 + s2 = (s2 + s1) % 65521 + return s2*65536 + s1 +end -- 65521 is the largest prime smaller than 2^16 + +local function inflate_zlib(t) + local bs = get_bitstream(t.input) + local outbs = get_obytestream(t.output) + local disable_crc = t.disable_crc + if disable_crc == nil then disable_crc = false end + + local _window_size = parse_zlib_header(bs) + + local data_adler32 = 1 + + inflate { + input=bs, + output = disable_crc and outbs or function(byte) + data_adler32 = adler32(byte, data_adler32) + outbs(byte) + end, + len = t.len, + } + + bs:read(bs:nbits_left_in_byte()) + + local b3 = bs:read(8) + local b2 = bs:read(8) + local b1 = bs:read(8) + local b0 = bs:read(8) + local expected_adler32 = ((b3*256 + b2)*256 + b1)*256 + b0 + + if not disable_crc then + if data_adler32 ~= expected_adler32 then + error('invalid compressed data--crc error') + end + end +end + +return { + inflate = inflate, + inflate_zlib = inflate_zlib, +} +end +preload["clone"] = function(...) +--- Git clone in Lua, from the bottom up +-- +-- http://stefan.saasen.me/articles/git-clone-in-haskell-from-the-bottom-up/#the_clone_process +-- https://github.com/creationix/lua-git + +do -- metis loader + local modules = { + ["metis.argparse"] = "src/metis/argparse.lua", + ["metis.crypto.sha1"] = "src/metis/crypto/sha1.lua", + ["metis.timer"] = "src/metis/timer.lua", + } + package.loaders[#package.loaders + 1] = function(name) + local path = modules[name] + if not path then return nil, "not a metis module" end + + local local_path = "/.cache/metis/ae11085f261e5b506654162c80d21954c0d54e5e/" .. path + if not fs.exists(local_path) then + local url = "https://raw.githubusercontent.com/SquidDev-CC/metis/ae11085f261e5b506654162c80d21954c0d54e5e/" .. path + local request, err = http.get(url) + if not request then return nil, "Cannot download " .. url .. ": " .. err end + + local out = fs.open(local_path, "w") + out.write(request.readAll()) + out.close() + + request.close() + end + + + local fn, err = loadfile(local_path, nil, _ENV) + if fn then return fn, local_path else return nil, err end + end +end + +local network = require "network" +local objects = require "objects" + +local url, name = ... +if not url or url == "-h" or url == "--help" then error("clone.lua URL [name]", 0) end + +if url:sub(-1) == "/" then url = url:sub(1, -2) end +name = name or fs.getName(url):gsub("%.git$", "") + +local destination = shell.resolve(name) +if fs.exists(destination) then + error(("%q already exists"):format(name), 0) +end + +local function report(msg) + local last = "" + for line in msg:gmatch("[^\n]+") do last = line end + if replaceLine then + term.setCursorPos(1, select(2, term.getCursorPos())) + term.clearLine() + term.write(last) + else + print(last) + end +end + +local head +do -- Request a list of all refs + report("Cloning from " .. url) + + local handle = network.force_fetch(url .. "/info/refs?service=git-upload-pack") + local res = network.receive(handle) + + local sha_ptrn = ("%x"):rep(40) + + local caps = {} + local refs = {} + for i = 1, #res do + local line = res[i] + if line ~= false and line:sub(1, 1) ~= "#" then + local sha, name = line:match("(" .. sha_ptrn .. ") ([^%z\n]+)") + if sha and name then + refs[name] = sha + + local capData = line:match("%z([^\n]+)\n") + if capData then + for cap in (capData .. " "):gmatch("%S+") do + local eq = cap:find("=") + if eq then + caps[cap:sub(1, eq - 1)] = cap:sub(eq + 1) + else + caps[cap] = true + end + end + end + else + printError("Unexpected line: " .. line) + end + end + end + head = refs['HEAD'] or refs['refs/heads/master'] or error("Cannot find master", 0) + + if not caps['shallow'] then error("Server does not support shallow fetching", 0) end + + -- TODO: Handle both. We don't even need the side-band really? + if not caps['side-band-64k'] then error("Server does not support side band", 0) end +end + +do -- Now actually perform the clone + local handle = network.force_fetch(url .. "/git-upload-pack", { + network.pkt_linef("want %s side-band-64k shallow", head), + network.pkt_linef("deepen 1"), + network.flush_line, + network.pkt_linef("done"), + }, "application/x-git-upload-pack-request") + + local pack, head = {}, nil + while true do + local line = network.read_pkt_line(handle) + if line == nil then break end + + if line == false or line == "NAK\n" then + -- Skip + elseif line:byte(1) == 1 then + table.insert(pack, line:sub(2)) + elseif line:byte(1) == 2 or line:byte(1) == 3 then + report(line:sub(2):gsub("\r", "\n")) + elseif line:find("^shallow ") then + head = line:sub(#("shallow ") + 1) + else + printError("Unknown line: " .. tostring(line)) + end + end + handle.close() + + local stream = objects.reader(table.concat(pack)) + local objs = objects.unpack(stream, function(x, n) + report(("Extracting %d/%d (%.2f%%)"):format(x, n, x/n*100)) + end) + stream.close() + + if not head then error("Cannot find HEAD commit", 0) end + + for k, v in pairs(objects.build_commit(objs, head)) do + local out = fs.open(fs.combine(destination, fs.combine(k, "")), "wb") + out.write(v) + out.close() + end +end + +report(("Cloned to %q"):format(name)) +print() +end +return preload["clone"](...) \ No newline at end of file diff --git a/bin/gist b/bin/gist new file mode 100644 index 0000000..7574b65 --- /dev/null +++ b/bin/gist @@ -0,0 +1,109 @@ +-- gist.lua - Gist client for ComputerCraft +-- Made by JackMacWindows for CraftOS-PC and CC: Tweaked + +if not http then + printError("Gist requires http API") + if _G.config ~= nil then printError("Set http_enable to true in the CraftOS-PC configuration") + else printError("Set http_enable to true in ComputerCraft's configuration") end + return 2 +end + +local gist = require "cc.http.gist" + +local args = { ... } + +local function readFile(filename, files, isEditing) + if fs.isDir(shell.resolve(filename)) then + for _, v in ipairs(fs.list(shell.resolve(filename))) do if readFile(fs.combine(filename, v), files, isEditing) then return true end end + else + if files[fs.getName(filename)] then print("Cannot upload files with duplicate names.") return true end + local file = fs.open(shell.resolve(filename), "rb") + if file == nil then + if not isEditing then print("Could not read " .. filename .. ".") return true + else files[fs.getName(filename)] = textutils.json_null end + else + files[fs.getName(filename)] = file.readAll() + file.close() + end + end +end + +local function getFiles(isEditing) + local files = {} + local i = isEditing and 3 or 2 + while args[i] ~= nil and args[i] ~= "--" do + if readFile(args[i], files, isEditing) then return nil end + i = i + 1 + end + if args[i] == "--" then return files, table.concat({ table.unpack(args, i + 1) }, " ") end + return files +end + +local function setTextColor(c) if term.isColor() then term.setTextColor(c) elseif c == colors.white or c == colors.yellow then term.setTextColor(colors.white) else term.setTextColor(colors.lightGray) end end + +local helpstr = "Usages:\ngist put [-- description...]\ngist edit [-- description]\ngist delete \ngist get \ngist run [arguments...]\ngist info " + +if #args < 2 then + print(helpstr) + return 1 +end + +if args[1] == "get" then + if #args < 3 then print(helpstr) return 1 end + if args[3]:sub(#args[3]) == "/" or fs.isDir(shell.resolve(args[3])) then + fs.makeDir(shell.resolve(args[3])) + local files, err = gist.getAll(args[2], write) + if files == nil then printError(err) return 3 end + for k, v in pairs(files) do + local file = fs.open(shell.resolve(fs.combine(args[3], k)), "wb") + file.write(v) + file.close() + end + print("Downloaded all files to " .. shell.resolve(args[3])) + else + local data, err = gist.get(args[2], write) + if data == nil then printError(err) return 3 end + local file = fs.open(shell.resolve(args[3]), "wb") + file.write(data) + file.close() + print("Downloaded as " .. shell.resolve(args[3])) + end +elseif args[1] == "run" then + return gist.run(args[2], write, table.unpack(args, 3)) +elseif args[1] == "put" then + local files, description = getFiles(false) + if files == nil then return end + local id, html_url = gist.put(files, description, nil, true) + if id ~= nil then print("Uploaded as " .. html_url .. "\nRun 'gist get " .. id .. "' to download anywhere") + else printError(html_url) return 3 end +elseif args[1] == "info" then + local tab, err = gist.info(args[2], write) + if tab == nil then printError(err) return 3 end + setTextColor(colors.yellow) + write("Description: ") + setTextColor(colors.white) + print(tab.description) + setTextColor(colors.yellow) + write("Author: ") + setTextColor(colors.white) + print(tab.author) + setTextColor(colors.yellow) + write("Revisions: ") + setTextColor(colors.white) + print(tab.revisionCount) + setTextColor(colors.yellow) + print("Files in this Gist:") + setTextColor(colors.white) + textutils.tabulate(tab.files) +elseif args[1] == "edit" then + if #args < 3 then print(helpstr) return 1 end + local files, description = getFiles(true) + if files == nil then return 2 end + if not description then description = gist.info(args[2], write).description end + local id, html_url = gist.put(files, description, args[2], true) + if id then print("Uploaded as " .. html_url .. "\nRun 'gist get " .. args[2] .. "' to download anywhere") + else printError(html_url) return 3 end +elseif args[1] == "delete" then + local ok, err = gist.delete(args[2], true) + if ok then print("The requested Gist has been deleted.") else printError(err) return 3 end +else print(helpstr) return 1 end diff --git a/bin/gps b/bin/gps new file mode 100644 index 0000000..3f1974c --- /dev/null +++ b/bin/gps @@ -0,0 +1,94 @@ +local function printUsage() + local programName = "gps" + print("Usages:") + print(programName .. " host") + print(programName .. " host ") + print(programName .. " locate") +end + +local tArgs = { ... } +if #tArgs < 1 then + printUsage() + return +end + + local sCommand = tArgs[1] +if sCommand == "locate" then + -- "gps locate" + -- Just locate this computer (this will print the results) + gps.locate(2, true) + +elseif sCommand == "host" then + -- "gps host" + -- Act as a GPS host + if pocket then + print("GPS Hosts must be stationary") + return + end + + -- Find a modem + local sModemSide = nil + for _, sSide in ipairs(rs.getSides()) do + if peripheral.getType(sSide) == "modem" and peripheral.call(sSide, "isWireless") then + sModemSide = sSide + break + end + end + + if sModemSide == nil then + print("No wireless modems found. 1 required.") + return + end + + -- Determine position + local x, y, z + if #tArgs >= 4 then + -- Position is manually specified + x = tonumber(tArgs[2]) + y = tonumber(tArgs[3]) + z = tonumber(tArgs[4]) + if x == nil or y == nil or z == nil then + printUsage() + return + end + print("Position is " .. x .. "," .. y .. "," .. z) + else + -- Position is to be determined using locate + x, y, z = gps.locate(2, true) + if x == nil then + print("Run \"gps host \" to set position manually") + return + end + end + + -- Open a channel + local modem = peripheral.wrap(sModemSide) + print("Opening channel on modem " .. sModemSide) + modem.open(gps.CHANNEL_GPS) + + -- Serve requests indefinately + local nServed = 0 + while true do + local e, p1, p2, p3, p4, p5 = os.pullEvent("modem_message") + if e == "modem_message" then + -- We received a message from a modem + local sSide, sChannel, sReplyChannel, sMessage, nDistance = p1, p2, p3, p4, p5 + if sSide == sModemSide and sChannel == gps.CHANNEL_GPS and sMessage == "PING" and nDistance then + -- We received a ping message on the GPS channel, send a response + modem.transmit(sReplyChannel, gps.CHANNEL_GPS, { x, y, z }) + + -- Print the number of requests handled + nServed = nServed + 1 + if nServed > 1 then + local _, y = term.getCursorPos() + term.setCursorPos(1, y - 1) + end + print(nServed .. " GPS requests served") + end + end + end +else + -- "gps somethingelse" + -- Error + printUsage() +end diff --git a/bin/hostname b/bin/hostname new file mode 100644 index 0000000..5586323 --- /dev/null +++ b/bin/hostname @@ -0,0 +1,18 @@ +local tArgs = { ... } +if #tArgs < 1 then + kerneldraw.printAppInfo("net", kernel.getHostname()) +else + if tArgs[1] == "--set" then + if #tArgs >= 2 then + local hostname = tArgs[2] + for i,v in ipairs(tArgs) do + if not (i == 1 or i == 2) then + hostname = hostname .. tArgs[i] + end + end + kernel.setHostname(hostname) + end + else + kerneldraw.printAppInfo("hostname","Arguments not specified") + end +end \ No newline at end of file diff --git a/bin/list b/bin/list new file mode 100644 index 0000000..4906620 --- /dev/null +++ b/bin/list @@ -0,0 +1,37 @@ +local tArgs = { ... } + +-- Get all the files in the directory +local sDir = shell.dir() +if tArgs[1] ~= nil then + sDir = shell.resolve(tArgs[1]) +end + +if not fs.isDir(sDir) then + printError("Not a directory") + return +end + +-- Sort into dirs/files, and calculate column count +local tAll = fs.list(sDir) +local tFiles = {} +local tDirs = {} + +local bShowHidden = settings.get("list.show_hidden") +for _, sItem in pairs(tAll) do + if bShowHidden or string.sub(sItem, 1, 1) ~= "." then + local sPath = fs.combine(sDir, sItem) + if fs.isDir(sPath) then + table.insert(tDirs, sItem) + else + table.insert(tFiles, sItem) + end + end +end +table.sort(tDirs) +table.sort(tFiles) + +if term.isColour() then + textutils.pagedTabulate(colors.green, tDirs, colors.white, tFiles) +else + textutils.pagedTabulate(colors.lightGray, tDirs, colors.white, tFiles) +end diff --git a/bin/lsh b/bin/lsh new file mode 100644 index 0000000..10a3b6c --- /dev/null +++ b/bin/lsh @@ -0,0 +1,102 @@ +local bRunning = true +local tCommandHistory = {} +local tEnv = { + ["exit"] = function() + bRunning = false + end, + ["_echo"] = function( ... ) + return ... + end, +} + +if not security.getSU() then + exception.throw("RestrictedOpsException") + return +end + +setmetatable( tEnv, { __index = _ENV } ) + +local label = os.getComputerLabel() or os.getComputerID() + +print( "lsh - The Lua interpreter shell" ) +print("Call exit() to quit.") +term.setTextColour( colours.white ) + +while bRunning do + if security.getActiveUserStatus() then + term.setTextColour( colours.lime ) + else + term.setTextColour( colours.orange ) + end + write( _activeUser ) + term.setTextColour( colours.lightGrey ) + write( "@" ) + term.setTextColour( colours.lightBlue ) + write( label ) + term.setTextColour( colours.lightGrey ) + write( "/" ) + term.setTextColour( colours.lightBlue ) + write( shell.dir() ) + if security.getActiveUserStatus() then + term.setTextColour( colours.lime ) + else + term.setTextColour( colours.orange ) + end + write( " % " ) + + term.setTextColour( colours.white ) + + local s = read( nil, tCommandHistory, function( sLine ) + local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.]+$" ) + if nStartPos then + sLine = string.sub( sLine, nStartPos ) + end + if #sLine > 0 then + return textutils.complete( sLine, tEnv ) + end + return nil + end ) + term.setTextColour( colours.lightGrey ) + table.insert( tCommandHistory, s ) + + local nForcePrint = 0 + local func, e = load( s, "lua", "t", tEnv ) + local func2, e2 = load( "return _echo("..s..");", "lua", "t", tEnv ) + if not func then + if func2 then + func = func2 + e = nil + nForcePrint = 1 + end + else + if func2 then + func = func2 + end + end + + if func then + local tResults = { pcall( func ) } + if tResults[1] then + local n = 1 + while (tResults[n + 1] ~= nil) or (n <= nForcePrint) do + local value = tResults[ n + 1 ] + if type( value ) == "table" then + local ok, serialised = pcall( textutils.serialise, value ) + if ok then + print( serialised ) + else + print( tostring( value ) ) + end + else + print( tostring( value ) ) + end + n = n + 1 + end + else + printError( tResults[2] ) + end + else + printError( e ) + end + +end \ No newline at end of file diff --git a/bin/man b/bin/man new file mode 100644 index 0000000..765ff69 --- /dev/null +++ b/bin/man @@ -0,0 +1,7 @@ +local tArgs = {...} + +if #tArgs < 1 then + kerneldraw.printAppInfo("man", "type 'ls system/man-pages' or 'manpages'") +else + kerneldraw.printAppInfo("man", man.findManual(tArgs[1])) +end \ No newline at end of file diff --git a/bin/manedit b/bin/manedit new file mode 100644 index 0000000..1af7cd8 --- /dev/null +++ b/bin/manedit @@ -0,0 +1,13 @@ +local tArgs = {...} + +if not security.getSU() then + exception.throw("RestrictedOpsException") + return +end + +if #tArgs < 1 then + kerneldraw.printAppInfo("manedit", "filename not specified") + return +end + +shell.run("/bin/nano /etc/manuals/"..tArgs[1]) \ No newline at end of file diff --git a/bin/manpages b/bin/manpages new file mode 100644 index 0000000..0a286e4 --- /dev/null +++ b/bin/manpages @@ -0,0 +1 @@ +shell.run("/bin/nano /etc/manuals") \ No newline at end of file diff --git a/bin/mkdir b/bin/mkdir new file mode 100644 index 0000000..dc4de2c --- /dev/null +++ b/bin/mkdir @@ -0,0 +1,18 @@ +local tArgs = { ... } + +if #tArgs < 1 then + local programName = "mkdir" + print("Usage: " .. programName .. " ") + return +end + +for _, v in ipairs(tArgs) do + local sNewDir = shell.resolve(v) + if fs.exists(sNewDir) and not fs.isDir(sNewDir) then + printError(v .. ": Destination exists") + elseif fs.isReadOnly(sNewDir) then + printError(v .. ": Access denied") + else + fs.makeDir(sNewDir) + end +end diff --git a/bin/monitor b/bin/monitor new file mode 100644 index 0000000..f6f431b --- /dev/null +++ b/bin/monitor @@ -0,0 +1,95 @@ +local function printUsage() + local programName = "monitor" + print("Usage:") + print(" " .. programName .. " ") + print(" " .. programName .. " scale ") + return +end + +local tArgs = { ... } +if #tArgs < 2 or tArgs[1] == "scale" and #tArgs < 3 then + printUsage() + return +end + +if tArgs[1] == "scale" then + local sName = tArgs[2] + if peripheral.getType(sName) ~= "monitor" then + print("No monitor named " .. sName) + return + end + + local nRes = tonumber(tArgs[3]) + if nRes == nil or nRes < 0.5 or nRes > 5 then + print("Invalid scale: " .. tArgs[3]) + return + end + + peripheral.call(sName, "setTextScale", nRes) + return +end + +local sName = tArgs[1] +if peripheral.getType(sName) ~= "monitor" then + print("No monitor named " .. sName) + return +end + +local sProgram = tArgs[2] + +local sPath = shell.resolveProgram(sProgram) +if sPath == nil then + print("No such program: " .. sProgram) + return +end + +print("Running " .. sProgram .. " on monitor " .. sName) + +local monitor = peripheral.wrap(sName) +local previousTerm = term.redirect(monitor) + +local co = coroutine.create(function() + (shell.execute or shell.run)(sProgram, table.unpack(tArgs, 3)) +end) + +local function resume(...) + local ok, param = coroutine.resume(co, ...) + if not ok then + printError(param) + end + return param +end + +local timers = {} + +local ok, param = pcall(function() + local sFilter = resume() + while coroutine.status(co) ~= "dead" do + local tEvent = table.pack(os.pullEventRaw()) + if sFilter == nil or tEvent[1] == sFilter or tEvent[1] == "terminate" then + sFilter = resume(table.unpack(tEvent, 1, tEvent.n)) + end + if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_click") then + if tEvent[1] == "monitor_touch" and tEvent[2] == sName then + timers[os.startTimer(0.1)] = { tEvent[3], tEvent[4] } + sFilter = resume("mouse_click", 1, table.unpack(tEvent, 3, tEvent.n)) + end + end + if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "term_resize") then + if tEvent[1] == "monitor_resize" and tEvent[2] == sName then + sFilter = resume("term_resize") + end + end + if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_up") then + if tEvent[1] == "timer" and timers[tEvent[2]] then + sFilter = resume("mouse_up", 1, table.unpack(timers[tEvent[2]], 1, 2)) + timers[tEvent[2]] = nil + end + end + end +end) + +term.redirect(previousTerm) +if not ok then + printError(param) +end diff --git a/bin/mount b/bin/mount new file mode 100644 index 0000000..d565040 --- /dev/null +++ b/bin/mount @@ -0,0 +1,23 @@ +if mounter == nil then error("Mounting directories is not supported in vanilla mode.") end +local args = { ... } + +if args[2] ~= nil then + local ro = nil + if args[3] == "readOnly" or args[3] == "true" then ro = true + elseif args[3] == "false" then ro = false end + if config.get("showMountPrompt") then print("A prompt will appear asking to confirm mounting. Press Allow to continue mounting.") end + if not mounter.mount(args[1], args[2], ro) then printError("Could not mount") end +elseif args[1] == "--help" then + term.setTextColor(colors.red) + print("Usage: mount [readOnly]") + print(" mount --help") + print(" mount") + term.setTextColor(colors.white) +else + local mounts = mounter.list() + print("/ on computer/" .. os.getComputerID()) + for k,v in pairs(mounts) do + write("/" .. k .. " on " .. (#v == 1 and v[1] or "(\n " .. table.concat(v, ",\n ") .. "\n)")) + if mounter.isReadOnly(k) then print(" (read-only)") else print() end + end +end \ No newline at end of file diff --git a/bin/mv b/bin/mv new file mode 100644 index 0000000..97f445b --- /dev/null +++ b/bin/mv @@ -0,0 +1,47 @@ +local tArgs = { ... } +if #tArgs < 2 then + local programName = "mv" + print("Usage: " .. programName .. " ") + return +end + +local sSource = shell.resolve(tArgs[1]) +local sDest = shell.resolve(tArgs[2]) +local tFiles = fs.find(sSource) + +local function sanity_checks(source, dest) + if fs.exists(dest) then + printError("Destination exists") + return false + elseif fs.isReadOnly(dest) then + printError("Destination is read-only") + return false + elseif fs.isDriveRoot(source) then + printError("Cannot move mount /" .. source) + return false + elseif fs.isReadOnly(source) then + printError("Cannot move read-only file /" .. source) + return false + end + return true +end + +if #tFiles > 0 then + for _, sFile in ipairs(tFiles) do + if fs.isDir(sDest) then + local dest = fs.combine(sDest, fs.getName(sFile)) + if sanity_checks(sFile, dest) then + fs.move(sFile, dest) + end + elseif #tFiles == 1 then + if sanity_checks(sFile, sDest) then + fs.move(sFile, sDest) + end + else + printError("Cannot overwrite file multiple times") + return + end + end +else + printError("No matching files") +end diff --git a/bin/nano b/bin/nano new file mode 100644 index 0000000..00777f8 --- /dev/null +++ b/bin/nano @@ -0,0 +1,772 @@ +-- Get file to edit +local tArgs = { ... } +if #tArgs == 0 then + print( "Usage: nano " ) + return +end + +-- Error checking +local sPath = shell.resolve( tArgs[1] ) +local bReadOnly = fs.isReadOnly( sPath ) +if fs.exists( sPath ) and fs.isDir( sPath ) then + print( "Cannot edit a directory." ) + return +end + +if bReadOnly and not security.getSU() then + -- prevent users from seeing unauthorized code + -- they should only be able to see their own files and ones in /var/log + exception.throw("SecurityException", sPath) + return +end + +local x,y = 1,1 +local w,h = term.getSize() +local scrollX, scrollY = 0,0 + +local tLines = {} +local bRunning = true + +-- Colours +local highlightColour, keywordColour, commentColour, textColour, bgColour +if term.isColour() then + bgColour = colours.black + textColour = colours.white + highlightColour = colours.yellow + keywordColour = colours.yellow + commentColour = colours.green + stringColour = colours.red +else + bgColour = colours.black + textColour = colours.white + highlightColour = colours.white + keywordColour = colours.white + commentColour = colours.white + stringColour = colours.white +end + +-- Menus +local bMenu = false +local nMenuItem = 1 +local tMenuItems = {} +if not bReadOnly then + table.insert( tMenuItems, "Save" ) +end +if shell.openTab then + table.insert( tMenuItems, "Run" ) +end +if peripheral.find( "printer" ) then + table.insert( tMenuItems, "Print" ) +end +table.insert( tMenuItems, "Exit" ) + +local sStatus = "Press Ctrl to access menu" +if string.len( sStatus ) > w - 5 then + sStatus = "Press Ctrl for menu" +end + +local function load( _sPath ) + tLines = {} + if fs.exists( _sPath ) then + local file = io.open( _sPath, "r" ) + local sLine = file:read() + while sLine do + table.insert( tLines, sLine ) + sLine = file:read() + end + file:close() + end + + if #tLines == 0 then + table.insert( tLines, "" ) + end +end + +local function save( _sPath ) + -- Create intervening folder + local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() ) + if not fs.exists( sDir ) then + fs.makeDir( sDir ) + end + + -- Save + local file = nil + local function innerSave() + file = fs.open( _sPath, "w" ) + if file then + for n, sLine in ipairs( tLines ) do + file.write( sLine .. "\n" ) + end + else + error( "Failed to open ".._sPath ) + end + end + + local ok, err = pcall( innerSave ) + if file then + file.close() + end + return ok, err +end + +local tKeywords = { + ["and"] = true, + ["break"] = true, + ["do"] = true, + ["else"] = true, + ["elseif"] = true, + ["end"] = true, + ["false"] = true, + ["for"] = true, + ["function"] = true, + ["if"] = true, + ["in"] = true, + ["local"] = true, + ["nil"] = true, + ["not"] = true, + ["or"] = true, + ["repeat"] = true, + ["return"] = true, + ["then"] = true, + ["true"] = true, + ["until"]= true, + ["while"] = true, +} + +local function tryWrite( sLine, regex, colour ) + local match = string.match( sLine, regex ) + if match then + if type(colour) == "number" then + term.setTextColour( colour ) + else + term.setTextColour( colour(match) ) + end + term.write( match ) + term.setTextColour( textColour ) + return string.sub( sLine, string.len(match) + 1 ) + end + return nil +end + +local function writeHighlighted( sLine ) + while string.len(sLine) > 0 do + sLine = + tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColour ) or + tryWrite( sLine, "^%-%-.*", commentColour ) or + tryWrite( sLine, "^\".-[^\\]\"", stringColour ) or + tryWrite( sLine, "^\'.-[^\\]\'", stringColour ) or + tryWrite( sLine, "^%[%[.-%]%]", stringColour ) or + tryWrite( sLine, "^[%w_]+", function( match ) + if tKeywords[ match ] then + return keywordColour + end + return textColour + end ) or + tryWrite( sLine, "^[^%w_]", textColour ) + end +end + +local tCompletions +local nCompletion + +local tCompleteEnv = _ENV +local function complete( sLine ) + local nStartPos = string.find( sLine, "[a-zA-Z0-9_%.]+$" ) + if nStartPos then + sLine = string.sub( sLine, nStartPos ) + end + if #sLine > 0 then + return textutils.complete( sLine, tCompleteEnv ) + end + return nil +end + +local function recomplete() + local sLine = tLines[y] + if not bMenu and not bReadOnly and x == string.len(sLine) + 1 then + tCompletions = complete( sLine ) + if tCompletions and #tCompletions > 0 then + nCompletion = 1 + else + nCompletion = nil + end + else + tCompletions = nil + nCompletion = nil + end +end + +local function writeCompletion( sLine ) + if nCompletion then + local sCompletion = tCompletions[ nCompletion ] + term.setTextColor( colours.white ) + term.setBackgroundColor( colours.grey ) + term.write( sCompletion ) + term.setTextColor( textColour ) + term.setBackgroundColor( bgColour ) + end +end + +local function redrawText() + local cursorX, cursorY = x, y + for y=1,h-1 do + term.setCursorPos( 1 - scrollX, y ) + term.clearLine() + + local sLine = tLines[ y + scrollY ] + if sLine ~= nil then + writeHighlighted( sLine ) + if cursorY == y and cursorX == #sLine + 1 then + writeCompletion() + end + end + end + term.setCursorPos( x - scrollX, y - scrollY ) +end + +local function redrawLine(_nY) + local sLine = tLines[_nY] + if sLine then + term.setCursorPos( 1 - scrollX, _nY - scrollY ) + term.clearLine() + writeHighlighted( sLine ) + if _nY == y and x == #sLine + 1 then + writeCompletion() + end + term.setCursorPos( x - scrollX, _nY - scrollY ) + end +end + +local function redrawMenu() + -- Clear line + term.setCursorPos( 1, h ) + term.clearLine() + + -- Draw line numbers + term.setCursorPos( w - string.len( "Ln "..y ) + 1, h ) + term.setTextColour( highlightColour ) + term.write( "Ln " ) + term.setTextColour( textColour ) + term.write( y ) + + term.setCursorPos( 1, h ) + if bMenu then + -- Draw menu + term.setTextColour( textColour ) + for nItem,sItem in pairs( tMenuItems ) do + if nItem == nMenuItem then + term.setTextColour( highlightColour ) + term.write( "[" ) + term.setTextColour( textColour ) + term.write( sItem ) + term.setTextColour( highlightColour ) + term.write( "]" ) + term.setTextColour( textColour ) + else + term.write( " "..sItem.." " ) + end + end + else + -- Draw status + term.setTextColour( highlightColour ) + term.write( sStatus ) + term.setTextColour( textColour ) + end + + -- Reset cursor + term.setCursorPos( x - scrollX, y - scrollY ) +end + +local tMenuFuncs = { + Save = function() + if bReadOnly then + sStatus = "Access denied" + else + local ok, err = save( sPath ) + if ok then + sStatus="Saved to "..sPath + else + sStatus="Error saving to "..sPath + end + end + redrawMenu() + end, + Print = function() + local printer = peripheral.find( "printer" ) + if not printer then + sStatus = "No printer attached" + return + end + + local nPage = 0 + local sName = fs.getName( sPath ) + if printer.getInkLevel() < 1 then + sStatus = "Printer out of ink" + return + elseif printer.getPaperLevel() < 1 then + sStatus = "Printer out of paper" + return + end + + local screenTerminal = term.current() + local printerTerminal = { + getCursorPos = printer.getCursorPos, + setCursorPos = printer.setCursorPos, + getSize = printer.getPageSize, + write = printer.write, + } + printerTerminal.scroll = function() + if nPage == 1 then + printer.setPageTitle( sName.." (page "..nPage..")" ) + end + + while not printer.newPage() do + if printer.getInkLevel() < 1 then + sStatus = "Printer out of ink, please refill" + elseif printer.getPaperLevel() < 1 then + sStatus = "Printer out of paper, please refill" + else + sStatus = "Printer output tray full, please empty" + end + + term.redirect( screenTerminal ) + redrawMenu() + term.redirect( printerTerminal ) + + local timer = os.startTimer(0.5) + sleep(0.5) + end + + nPage = nPage + 1 + if nPage == 1 then + printer.setPageTitle( sName ) + else + printer.setPageTitle( sName.." (page "..nPage..")" ) + end + end + + bMenu = false + term.redirect( printerTerminal ) + local ok, error = pcall( function() + term.scroll() + for n, sLine in ipairs( tLines ) do + print( sLine ) + end + end ) + term.redirect( screenTerminal ) + if not ok then + print( error ) + end + + while not printer.endPage() do + sStatus = "Printer output tray full, please empty" + redrawMenu() + sleep( 0.5 ) + end + bMenu = true + + if nPage > 1 then + sStatus = "Printed "..nPage.." Pages" + else + sStatus = "Printed 1 Page" + end + redrawMenu() + end, + Exit = function() + bRunning = false + end, + Run = function() + local sTempPath = "/.temp" + local ok, err = save( sTempPath ) + if ok then + local nTask = shell.openTab( sTempPath ) + if nTask then + shell.switchTab( nTask ) + else + sStatus="Error starting Task" + end + fs.delete( sTempPath ) + else + sStatus="Error saving to "..sTempPath + end + redrawMenu() + end +} + +local function doMenuItem( _n ) + tMenuFuncs[tMenuItems[_n]]() + if bMenu then + bMenu = false + term.setCursorBlink( true ) + end + redrawMenu() +end + +local function setCursor( newX, newY ) + local oldX, oldY = x, y + x, y = newX, newY + local screenX = x - scrollX + local screenY = y - scrollY + + local bRedraw = false + if screenX < 1 then + scrollX = x - 1 + screenX = 1 + bRedraw = true + elseif screenX > w then + scrollX = x - w + screenX = w + bRedraw = true + end + + if screenY < 1 then + scrollY = y - 1 + screenY = 1 + bRedraw = true + elseif screenY > h-1 then + scrollY = y - (h-1) + screenY = h-1 + bRedraw = true + end + + recomplete() + if bRedraw then + redrawText() + elseif y ~= oldY then + redrawLine( oldY ) + redrawLine( y ) + else + redrawLine( y ) + end + term.setCursorPos( screenX, screenY ) + + redrawMenu() +end + +-- Actual program functionality begins +load(sPath) + +term.setBackgroundColour( bgColour ) +term.clear() +term.setCursorPos(x,y) +term.setCursorBlink( true ) + +recomplete() +redrawText() +redrawMenu() + +local function acceptCompletion() + if nCompletion then + -- Find the common prefix of all the other suggestions which start with the same letter as the current one + local sCompletion = tCompletions[ nCompletion ] + local sFirstLetter = string.sub( sCompletion, 1, 1 ) + local sCommonPrefix = sCompletion + for n=1,#tCompletions do + local sResult = tCompletions[n] + if n ~= nCompletion and string.find( sResult, sFirstLetter, 1, true ) == 1 then + while #sCommonPrefix > 1 do + if string.find( sResult, sCommonPrefix, 1, true ) == 1 then + break + else + sCommonPrefix = string.sub( sCommonPrefix, 1, #sCommonPrefix - 1 ) + end + end + end + end + + -- Append this string + tLines[y] = tLines[y] .. sCommonPrefix + setCursor( x + string.len( sCommonPrefix ), y ) + end +end + +-- Handle input +while bRunning do + local sEvent, param, param2, param3 = os.pullEvent() + if sEvent == "key" then + local oldX, oldY = x, y + if param == keys.up then + -- Up + if not bMenu then + if nCompletion then + -- Cycle completions + nCompletion = nCompletion - 1 + if nCompletion < 1 then + nCompletion = #tCompletions + end + redrawLine(y) + + elseif y > 1 then + -- Move cursor up + setCursor( + math.min( x, string.len( tLines[y - 1] ) + 1 ), + y - 1 + ) + end + end + + elseif param == keys.down then + -- Down + if not bMenu then + -- Move cursor down + if nCompletion then + -- Cycle completions + nCompletion = nCompletion + 1 + if nCompletion > #tCompletions then + nCompletion = 1 + end + redrawLine(y) + + elseif y < #tLines then + -- Move cursor down + setCursor( + math.min( x, string.len( tLines[y + 1] ) + 1 ), + y + 1 + ) + end + end + + elseif param == keys.tab then + -- Tab + if not bMenu and not bReadOnly then + if nCompletion and x == string.len(tLines[y]) + 1 then + -- Accept autocomplete + acceptCompletion() + else + -- Indent line + local sLine = tLines[y] + tLines[y] = string.sub(sLine,1,x-1) .. " " .. string.sub(sLine,x) + setCursor( x + 2, y ) + end + end + + elseif param == keys.pageUp then + -- Page Up + if not bMenu then + -- Move up a page + local newY + if y - (h - 1) >= 1 then + newY = y - (h - 1) + else + newY = 1 + end + setCursor( + math.min( x, string.len( tLines[newY] ) + 1 ), + newY + ) + end + + elseif param == keys.pageDown then + -- Page Down + if not bMenu then + -- Move down a page + local newY + if y + (h - 1) <= #tLines then + newY = y + (h - 1) + else + newY = #tLines + end + local newX = math.min( x, string.len( tLines[newY] ) + 1 ) + setCursor( newX, newY ) + end + + elseif param == keys.home then + -- Home + if not bMenu then + -- Move cursor to the beginning + if x > 1 then + setCursor(1,y) + end + end + + elseif param == keys["end"] then + -- End + if not bMenu then + -- Move cursor to the end + local nLimit = string.len( tLines[y] ) + 1 + if x < nLimit then + setCursor( nLimit, y ) + end + end + + elseif param == keys.left then + -- Left + if not bMenu then + if x > 1 then + -- Move cursor left + setCursor( x - 1, y ) + elseif x==1 and y>1 then + setCursor( string.len( tLines[y-1] ) + 1, y - 1 ) + end + else + -- Move menu left + nMenuItem = nMenuItem - 1 + if nMenuItem < 1 then + nMenuItem = #tMenuItems + end + redrawMenu() + end + + elseif param == keys.right then + -- Right + if not bMenu then + local nLimit = string.len( tLines[y] ) + 1 + if x < nLimit then + -- Move cursor right + setCursor( x + 1, y ) + elseif nCompletion and x == string.len(tLines[y]) + 1 then + -- Accept autocomplete + acceptCompletion() + elseif x==nLimit and y<#tLines then + -- Go to next line + setCursor( 1, y + 1 ) + end + else + -- Move menu right + nMenuItem = nMenuItem + 1 + if nMenuItem > #tMenuItems then + nMenuItem = 1 + end + redrawMenu() + end + + elseif param == keys.delete then + -- Delete + if not bMenu and not bReadOnly then + local nLimit = string.len( tLines[y] ) + 1 + if x < nLimit then + local sLine = tLines[y] + tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1) + recomplete() + redrawLine(y) + elseif y<#tLines then + tLines[y] = tLines[y] .. tLines[y+1] + table.remove( tLines, y+1 ) + recomplete() + redrawText() + end + end + + elseif param == keys.backspace then + -- Backspace + if not bMenu and not bReadOnly then + if x > 1 then + -- Remove character + local sLine = tLines[y] + tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x) + setCursor( x - 1, y ) + elseif y > 1 then + -- Remove newline + local sPrevLen = string.len( tLines[y-1] ) + tLines[y-1] = tLines[y-1] .. tLines[y] + table.remove( tLines, y ) + setCursor( sPrevLen + 1, y - 1 ) + redrawText() + end + end + + elseif param == keys.enter then + -- Enter + if not bMenu and not bReadOnly then + -- Newline + local sLine = tLines[y] + local _,spaces=string.find(sLine,"^[ ]+") + if not spaces then + spaces=0 + end + tLines[y] = string.sub(sLine,1,x-1) + table.insert( tLines, y+1, string.rep(' ',spaces)..string.sub(sLine,x) ) + setCursor( spaces + 1, y + 1 ) + redrawText() + + elseif bMenu then + -- Menu selection + doMenuItem( nMenuItem ) + + end + + elseif param == keys.leftCtrl or param == keys.rightCtrl or param == keys.rightAlt then + -- Menu toggle + bMenu = not bMenu + if bMenu then + term.setCursorBlink( false ) + else + term.setCursorBlink( true ) + end + redrawMenu() + + end + + elseif sEvent == "char" then + if not bMenu and not bReadOnly then + -- Input text + local sLine = tLines[y] + tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x) + setCursor( x + 1, y ) + + elseif bMenu then + -- Select menu items + for n,sMenuItem in ipairs( tMenuItems ) do + if string.lower(string.sub(sMenuItem,1,1)) == string.lower(param) then + doMenuItem( n ) + break + end + end + end + + elseif sEvent == "paste" then + if not bMenu and not bReadOnly then + -- Input text + local sLine = tLines[y] + tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x) + setCursor( x + string.len( param ), y ) + end + + elseif sEvent == "mouse_click" then + if not bMenu then + if param == 1 then + -- Left click + local cx,cy = param2, param3 + if cy < h then + local newY = math.min( math.max( scrollY + cy, 1 ), #tLines ) + local newX = math.min( math.max( scrollX + cx, 1 ), string.len( tLines[newY] ) + 1 ) + setCursor( newX, newY ) + end + end + end + + elseif sEvent == "mouse_scroll" then + if not bMenu then + if param == -1 then + -- Scroll up + if scrollY > 0 then + -- Move cursor up + scrollY = scrollY - 1 + redrawText() + end + + elseif param == 1 then + -- Scroll down + local nMaxScroll = #tLines - (h-1) + if scrollY < nMaxScroll then + -- Move cursor down + scrollY = scrollY + 1 + redrawText() + end + + end + end + + elseif sEvent == "term_resize" then + w,h = term.getSize() + setCursor( x, y ) + redrawMenu() + redrawText() + + end +end + +-- Cleanup +term.clear() +term.setCursorBlink( false ) +term.setCursorPos( 1, 1 ) \ No newline at end of file diff --git a/bin/netutil1 b/bin/netutil1 new file mode 100644 index 0000000..80041ef --- /dev/null +++ b/bin/netutil1 @@ -0,0 +1,4 @@ +-- testing +net.openModems() +net.setWorkstationStatus(true) +print(tostring(net.getWorkstationStatus())) \ No newline at end of file diff --git a/bin/nmap b/bin/nmap new file mode 100644 index 0000000..d9289a7 --- /dev/null +++ b/bin/nmap @@ -0,0 +1,50 @@ +--Initializing IP address generator + +local tArgs = {...} +if #tArgs < 4 then + kerneldraw.printAppInfo("nmap", "No IP address specified") +else + kerneldraw.printAppSuccess("nmap", "Scanning for specified IP") +end + +if #tArgs == 5 then + kerneldraw.printAppSuccess("nmap", "Specified DNS record") +end + +local ip, node + +if #tArgs < 4 then + local x = tostring(math.random(0, 255)) + local y = tostring(math.random(0, 255)) + local z = tostring(math.random(0, 255)) + local r = tostring(math.random(0, 255)) + ip = x.."."..y.."."..z.."."..r +elseif #tArgs >= 4 then + ip = tArgs[1].."."..tArgs[2].."."..tArgs[3].."."..tArgs[4] +end + +if not fs.exists("/network") or not fs.isDir("/network") then + fs.makeDir("/network") +end + +--node = "/network/"..ip + +kerneldraw.printAppInfo("nmap", "Found "..ip) + +if #tArgs == 5 then + kerneldraw.printAppSuccess("nmap", "Assigned DNS record "..tArgs[5]) + node = "/network/"..tArgs[5] +else + kerneldraw.printAppWarning("nmap", "No DNS record found, defaulting to IP") + node = "/network/"..ip +end + +if fs.exists(node) and fs.isDir(node) then + kerneldraw.printAppInfo("nmap", ip.." already known") +else + fs.makeDir(node) + fs.makeDir(node.."/sys") + fs.makeDir(node.."/home") + fs.makeDir(node.."/bin") + kerneldraw.printAppSuccess("nmap", ip.." added to network") +end \ No newline at end of file diff --git a/bin/passwd b/bin/passwd new file mode 100644 index 0000000..d6f3dc0 --- /dev/null +++ b/bin/passwd @@ -0,0 +1,50 @@ +local tArgs = {...} + +if #tArgs < 1 then + kerneldraw.printAppInfo("passwd", "Specify a user") + return +end + +if not security.getSU() then + exception.throw("RestrictedOpsException") + return +end + +if fs.exists("/etc/passwd/"..tArgs[1]..".dat") and not fs.isDir("/etc/passwd/"..tArgs[1]..".dat") then + local passwd = nil + local passwd_reenter = nil + passwd = kernel.secureInputReturn("New Password: ") + os.pullEvent = function() + local eventData = { os.pullEventRaw( _sFilter ) } + if eventData[1] == "terminate" then + error( "Terminated", 0 ) + end + return unpack( eventData ) + end + if passwd == "" or passwd == nil then + kerneldraw.printAppInfo("passwd","Invalid password") + return + else + passwd_reenter = kernel.secureInput("Reenter: ", passwd) + os.pullEvent = function() + local eventData = { os.pullEventRaw( _sFilter ) } + if eventData[1] == "terminate" then + error( "Terminated", 0 ) + end + return unpack( eventData ) + end + if not passwd == passwd_reenter then + kerneldraw.printAppInfo("passwd","Passwords don't match") + return + end + end + if security.passbyte(tArgs[1], passwd) then + kerneldraw.printAppSuccess("passwd", "Password of user "..tArgs[1].." changed") + log.writeSecurity("Password of user "..tArgs[1].." changed") + else + kerneldraw.printAppInfo("passwd", "Failed to change password") + log.writeSecurity("Failed to change password") + end +else + kerneldraw.printAppInfo("security", "User " .. tArgs[1] .. " does not exist") +end \ No newline at end of file diff --git a/bin/pastebin b/bin/pastebin new file mode 100644 index 0000000..612b541 --- /dev/null +++ b/bin/pastebin @@ -0,0 +1,160 @@ +local function printUsage() + local programName = "pastebin" + print("Usages:") + print(programName .. " put ") + print(programName .. " get ") + print(programName .. " run ") +end + +local tArgs = { ... } +if #tArgs < 2 then + printUsage() + return +end + +if not http then + printError("Pastebin requires the http API") + printError("Set http.enabled to true in CC: Tweaked's config") + return +end + +--- Attempts to guess the pastebin ID from the given code or URL +local function extractId(paste) + local patterns = { + "^([%a%d]+)$", + "^https?://pastebin.com/([%a%d]+)$", + "^pastebin.com/([%a%d]+)$", + "^https?://pastebin.com/raw/([%a%d]+)$", + "^pastebin.com/raw/([%a%d]+)$", + } + + for i = 1, #patterns do + local code = paste:match(patterns[i]) + if code then return code end + end + + return nil +end + +local function get(url) + local paste = extractId(url) + if not paste then + io.stderr:write("Invalid pastebin code.\n") + io.write("The code is the ID at the end of the pastebin.com URL.\n") + return + end + + write("Connecting to pastebin.com... ") + -- Add a cache buster so that spam protection is re-checked + local cacheBuster = ("%x"):format(math.random(0, 2 ^ 30)) + local response, err = http.get( + "https://pastebin.com/raw/" .. textutils.urlEncode(paste) .. "?cb=" .. cacheBuster + ) + + if response then + -- If spam protection is activated, we get redirected to /paste with Content-Type: text/html + local headers = response.getResponseHeaders() + if not headers["Content-Type"] or not headers["Content-Type"]:find("^text/plain") then + io.stderr:write("Failed.\n") + print("Pastebin blocked the download due to spam protection. Please complete the captcha in a web browser: https://pastebin.com/" .. textutils.urlEncode(paste)) + return + end + + print("Success.") + + local sResponse = response.readAll() + response.close() + return sResponse + else + io.stderr:write("Failed.\n") + print(err) + end +end + +local sCommand = tArgs[1] +if sCommand == "put" then + -- Upload a file to pastebin.com + -- Determine file to upload + local sFile = tArgs[2] + local sPath = shell.resolve(sFile) + if not fs.exists(sPath) or fs.isDir(sPath) then + print("No such file") + return + end + + -- Read in the file + local sName = fs.getName(sPath) + local file = fs.open(sPath, "r") + local sText = file.readAll() + file.close() + + -- POST the contents to pastebin + write("Connecting to pastebin.com... ") + local key = "0ec2eb25b6166c0c27a394ae118ad829" + local response = http.post( + "https://pastebin.com/api/api_post.php", + "api_option=paste&" .. + "api_dev_key=" .. key .. "&" .. + "api_paste_format=lua&" .. + "api_paste_name=" .. textutils.urlEncode(sName) .. "&" .. + "api_paste_code=" .. textutils.urlEncode(sText) + ) + + if response then + print("Success.") + + local sResponse = response.readAll() + response.close() + + local sCode = string.match(sResponse, "[^/]+$") + print("Uploaded as " .. sResponse) + print("Run \"pastebin get " .. sCode .. "\" to download anywhere") + + else + print("Failed.") + end + +elseif sCommand == "get" then + -- Download a file from pastebin.com + if #tArgs < 3 then + printUsage() + return + end + + -- Determine file to download + local sCode = tArgs[2] + local sFile = tArgs[3] + local sPath = shell.resolve(sFile) + if fs.exists(sPath) then + print("File already exists") + return + end + + -- GET the contents from pastebin + local res = get(sCode) + if res then + local file = fs.open(sPath, "w") + file.write(res) + file.close() + + print("Downloaded as " .. sFile) + end +elseif sCommand == "run" then + local sCode = tArgs[2] + + local res = get(sCode) + if res then + local func, err = load(res, sCode, "t", _ENV) + if not func then + printError(err) + return + end + local success, msg = pcall(func, select(3, ...)) + if not success then + printError(msg) + end + end +else + printUsage() + return +end diff --git a/bin/peripherals b/bin/peripherals new file mode 100644 index 0000000..17d4877 --- /dev/null +++ b/bin/peripherals @@ -0,0 +1,10 @@ +local tPeripherals = peripheral.getNames() +print("Attached Peripherals:") +if #tPeripherals > 0 then + for n = 1, #tPeripherals do + local sPeripheral = tPeripherals[n] + print(sPeripheral .. " (" .. table.concat({ peripheral.getType(sPeripheral) }, ", ") .. ")") + end +else + print("None") +end diff --git a/bin/redstone b/bin/redstone new file mode 100644 index 0000000..2be18c7 --- /dev/null +++ b/bin/redstone @@ -0,0 +1,118 @@ +local tArgs = { ... } + +local function printUsage() + local programName = "redstone" + print("Usages:") + print(programName .. " probe") + print(programName .. " set ") + print(programName .. " set ") + print(programName .. " pulse ") +end + +local sCommand = tArgs[1] +if sCommand == "probe" then + -- "redstone probe" + -- Regular input + print("Redstone inputs: ") + + local count = 0 + local bundledCount = 0 + for _, sSide in ipairs(redstone.getSides()) do + if redstone.getBundledInput(sSide) > 0 then + bundledCount = bundledCount + 1 + end + if redstone.getInput(sSide) then + if count > 0 then + io.write(", ") + end + io.write(sSide) + count = count + 1 + end + end + if count > 0 then + print(".") + else + print("None.") + end + + -- Bundled input + if bundledCount > 0 then + print() + print("Bundled inputs:") + for _, sSide in ipairs(redstone.getSides()) do + local nInput = redstone.getBundledInput(sSide) + if nInput ~= 0 then + write(sSide .. ": ") + local count = 0 + for sColour, nColour in pairs(colors) do + if type(nColour) == "number" and colors.test(nInput, nColour) then + if count > 0 then + write(", ") + end + if term.isColour() then + term.setTextColour(nColour) + end + write(sColour) + if term.isColour() then + term.setTextColour(colours.white) + end + count = count + 1 + end + end + print(".") + end + end + end + +elseif sCommand == "pulse" then + -- "redstone pulse" + local sSide = tArgs[2] + local nCount = tonumber(tArgs[3]) or 1 + local nPeriod = tonumber(tArgs[4]) or 0.5 + for _ = 1, nCount do + redstone.setOutput(sSide, true) + sleep(nPeriod / 2) + redstone.setOutput(sSide, false) + sleep(nPeriod / 2) + end + +elseif sCommand == "set" then + -- "redstone set" + local sSide = tArgs[2] + if #tArgs > 3 then + -- Bundled cable output + local sColour = tArgs[3] + local nColour = colors[sColour] or colours[sColour] + if type(nColour) ~= "number" then + printError("No such color") + return + end + + local sValue = tArgs[4] + if sValue == "true" then + rs.setBundledOutput(sSide, colors.combine(rs.getBundledOutput(sSide), nColour)) + elseif sValue == "false" then + rs.setBundledOutput(sSide, colors.subtract(rs.getBundledOutput(sSide), nColour)) + else + print("Value must be boolean") + end + else + -- Regular output + local sValue = tArgs[3] + local nValue = tonumber(sValue) + if sValue == "true" then + rs.setOutput(sSide, true) + elseif sValue == "false" then + rs.setOutput(sSide, false) + elseif nValue and nValue >= 0 and nValue <= 15 then + rs.setAnalogOutput(sSide, nValue) + else + print("Value must be boolean or 0-15") + end + end + +else + -- Something else + printUsage() + +end diff --git a/bin/rm b/bin/rm new file mode 100644 index 0000000..d17b4d7 --- /dev/null +++ b/bin/rm @@ -0,0 +1,30 @@ +local args = table.pack(...) + +if args.n < 1 then + local programName = "rm" + print("Usage: " .. programName .. " ") + return +end + +for i = 1, args.n do + local files = fs.find(shell.resolve(args[i])) + if #files > 0 then + for _, file in ipairs(files) do + if fs.isReadOnly(file) then + printError("Cannot delete read-only file /" .. file) + elseif fs.isDriveRoot(file) then + printError("Cannot delete mount /" .. file) + if fs.isDir(file) then + print("To delete its contents run rm /" .. fs.combine(file, "*")) + end + else + local ok, err = pcall(fs.delete, file) + if not ok then + printError((err:gsub("^pcall: ", ""))) + end + end + end + else + printError(args[i] .. ": No matching files") + end +end diff --git a/bin/screenfetch b/bin/screenfetch new file mode 100644 index 0000000..f17edd8 --- /dev/null +++ b/bin/screenfetch @@ -0,0 +1,375 @@ +if os.pullEvent ~= nil then + + local ccart, ccart_fg, ccart_bg, ccart_adv_fg, ccart_adv_bg, ccart_width + + if ... == "--small" then + + ccart = ([[\159\143\143\143\143\143\143\143\144 +\150\136\144 \150 +\150\130 \131 \150 +\150 \150 +\150 \150 +\150 \140\150 + ]]):gsub("\\130", "\130"):gsub("\\131", "\131"):gsub("\\136", "\136"):gsub("\\140", "\140"):gsub("\\143", "\143"):gsub("\\144", "\144"):gsub("\\150", "\149"):gsub("\\159", "\159") + + ccart_fg = [[ffffffff7 +f00fffff7 +f0f0ffff7 +ffffffff8 +ffffffff8 +f888888f8 +fffffffff]] + + ccart_bg = [[77777777f +7ffffffff +7ffffffff +8ffffffff +8ffffffff +88888888f +fffffffff]] + + ccart_adv_fg = [[ffffffff4 +f00fffff4 +f0f0ffff4 +ffffffff4 +ffffffff4 +f444444d4 +fffffffff]] + + ccart_adv_bg = [[44444444f +4ffffffff +4ffffffff +4ffffffff +4ffffffff +44444444f +fffffffff]] + + ccart_width = 10 + + else + ccart = [[------------------------ +| | +| -------------------- | +| | \ | | +| | / __ | | +| | | | +| | | | +| | | | +| | | | +| | | | +| | | | +| -------------------- | +| | +| [=] | +| | +------------------------]] + + ccart_fg = [[ffffffffffffffffffffffff +f7777777777777777777777f +f7ffffffffffffffffffff7f +f7ff0fffffffffffffffff7f +f7ff0f00ffffffffffffff7f +f7ffffffffffffffffffff7f +f7ffffffffffffffffffff7f +f7ffffffffffffffffffff7f +f8ffffffffffffffffffff8f +f8ffffffffffffffffffff8f +f8ffffffffffffffffffff8f +f8ffffffffffffffffffff8f +f8888888888888888888888f +f888888888888888888fff8f +f8888888888888888888888f +ffffffffffffffffffffffff]] + + ccart_bg = [[ffffffffffffffffffffffff +f7777777777777777777777f +f7ffffffffffffffffffff7f +f7ffffffffffffffffffff7f +f7ffffffffffffffffffff7f +f7ffffffffffffffffffff7f +f7ffffffffffffffffffff7f +f7ffffffffffffffffffff7f +f8ffffffffffffffffffff8f +f8ffffffffffffffffffff8f +f8ffffffffffffffffffff8f +f8ffffffffffffffffffff8f +f8888888888888888888888f +f888888888888888888fff8f +f8888888888888888888888f +ffffffffffffffffffffffff]] + + ccart_adv_fg = [[ffffffffffffffffffffffff +f4444444444444444444444f +f4ffffffffffffffffffff4f +f4ff0fffffffffffffffff4f +f4ff0f00ffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4444444444444444444444f +f444444444444444444ddd4f +f4444444444444444444444f +ffffffffffffffffffffffff]] + + ccart_adv_bg = [[ffffffffffffffffffffffff +f4444444444444444444444f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4444444444444444444444f +f444444444444444444ddd4f +f4444444444444444444444f +ffffffffffffffffffffffff]] + + ccart_width = 25 + + end + + local function fg(l) if term.isColor() then return string.rep("4", l) else return string.rep("8", l) end end + local function text(title, str) return {title .. str, fg(string.len(title)) .. string.rep("0", string.len(str)), string.rep("f", string.len(title .. str))} end + + local function time(n) + local h = math.floor(n / 3600) + local m = math.floor(n / 60) % 60 + local s = n % 60 + local retval = s .. "s" + if m > 0 or h > 0 then retval = m .. "m " .. retval end + if h > 0 then retval = h .. "h " .. retval end + return retval + end + + local function ext(retval) + if debug ~= nil then table.insert(retval, text(" ", "Debug enabled")) end + if http ~= nil then table.insert(retval, text(" ", "HTTP enabled")) + if http.websocket ~= nil then table.insert(retval, text(" ", "CC: Tweaked")) end end + if mounter ~= nil then table.insert(retval, text(" ", "CraftOS-PC")) end + if term.setGraphicsMode ~= nil then table.insert(retval, text(" ", "CraftOS-PC GFX")) end + if term.screenshot ~= nil then table.insert(retval, text(" ", "CraftOS-PC 2")) end + if ccemux ~= nil then table.insert(retval, text(" ", "CCEmuX")) end + if fs.exists(".mbs") or fs.exists("rom/.mbs") then table.insert(retval, text(" ", "MBS")) end + if type(kernel) == "table" then table.insert(retval, text(" ", "CCKernel2")) end + return retval + end + + local function getRuntime() + if os.about ~= nil then return string.sub(os.about(), 1, string.find(os.about(), "\n")) + elseif ccemux ~= nil then return ccemux.getVersion() + elseif _MC_VERSION ~= nil then return _MC_VERSION + else return "Unknown" end + end + + local sysinfo = { + text(os.getComputerLabel() or "Untitled Computer", ""), + text("Type: ", commands ~= nil and "Command Computer" or term.isColor() and "Advanced Computer" or "Standard Computer"), + text("OS: ", kernel.getOS()), + text("Kernel: ", kernel.getRelease()), + text("Runtime: ", getRuntime()), + text("Lua: ", _VERSION), + text("Host: ", _HOST), + text("Uptime: ", time(os.clock())), + text("Extensions: ", "") + } + ext(sysinfo) + local lines, sw, sh = 2, term.getSize() + term.clear() + term.setCursorPos(1, 2) + for i = 1, string.len(ccart), ccart_width do + term.blit(string.sub(ccart, i, i+ccart_width-2), string.sub(term.isColor() and ccart_adv_fg or ccart_fg, i, i+ccart_width-2), string.sub(term.isColor() and ccart_adv_bg or ccart_bg, i, i+ccart_width-2)) + write(" ") + if sysinfo[((i-1)/ccart_width)+1] ~= nil then term.blit(table.unpack(sysinfo[((i-1)/ccart_width)+1])) end + print("") + lines = lines + 1 + end + for i = lines - 1, #sysinfo do + write(string.rep(" ", ccart_width + 1)) + term.blit(table.unpack(sysinfo[i])) + print("") + end + print("") + if term.screenshot ~= nil then term.screenshot() end + sleep(0.25) + +elseif require("filesystem") ~= nil then + + -- TODO: Find better art + local ccart = [[------------------------ +| | +| -------------------- | +| | \ | | +| | / __ | | +| | | | +| | | | +| | | | +| | | | +| | | | +| | | | +| -------------------- | +| | +| [=] | +| | +------------------------]] + + local ccart_fg = [[ffffffffffffffffffffffff +f0000000000000000000000f +f0ffffffffffffffffffff0f +f0ff0fffffffffffffffff0f +f0ff0f00ffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0000000000000000000000f +f000000000000000000fff0f +f0000000000000000000000f +ffffffffffffffffffffffff]] + + local ccart_bg = [[ffffffffffffffffffffffff +f0000000000000000000000f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0ffffffffffffffffffff0f +f0000000000000000000000f +f000000000000000000fff0f +f0000000000000000000000f +ffffffffffffffffffffffff]] + + local ccart_adv_fg = [[ffffffffffffffffffffffff +f4444444444444444444444f +f4ffffffffffffffffffff4f +f4ff0fffffffffffffffff4f +f4ff0f00ffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4444444444444444444444f +f444444444444444444ddd4f +f4444444444444444444444f +ffffffffffffffffffffffff]] + + local ccart_adv_bg = [[ffffffffffffffffffffffff +f4444444444444444444444f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4ffffffffffffffffffff4f +f4444444444444444444444f +f444444444444444444ddd4f +f4444444444444444444444f +ffffffffffffffffffffffff]] + + local component = require("component") + local computer = require("computer") + local term = require("term") + local gpu = term.gpu() + + local function fg(l) if gpu.getDepth() > 1 then return string.rep("4", l) else return string.rep("0", l) end end + local function text(title, str) return {title .. str, fg(string.len(title)) .. string.rep("0", string.len(str)), string.rep("f", string.len(title .. str))} end + + local function time(n) + local h = math.floor(n / 3600) + local m = math.floor(n / 60) % 60 + local s = n % 60 + local retval = s .. "s" + if m > 0 or h > 0 then retval = m .. "m " .. retval end + if h > 0 then retval = h .. "h " .. retval end + return retval + end + + local function mem(bytes) + if bytes > 1048576 then return string.gsub(string.sub((math.floor((bytes / 1048576) * 1000) / 1000) .. "", 1, 4), "%.$", "") .. " MB" + elseif bytes > 1024 then return string.gsub(string.sub((math.floor((bytes / 1024) * 1000) / 1000) .. "", 1, 4), "%.$", "") .. " kB" + else return bytes .. " B" end + end + + local function ext(retval) + for address, type in component.list() do table.insert(retval, text(" " .. type .. " ", address)) end + return retval + end + + local tier = "Unknown" + for k,v in pairs(component.computer.getDeviceInfo()) do if v.class == "processor" then tier = string.match(v.product, "FlexiArch (%d+) Processor") end end + + local sysinfo = { + text("OpenComputers", ""), + text("Tier: ", tier), + text("OS: ", _OSVERSION), + text("Lua: ", _VERSION), + text("RAM: ", mem(computer.totalMemory() - computer.freeMemory()) .. " / " .. mem(computer.totalMemory())), + text("Uptime: ", time(computer.uptime())), + text("Components: ", "") + } + ext(sysinfo) + + local ccColors = { + ["0"] = 0xFFFFFF, + ["4"] = 0xFFFF00, + ["7"] = 0x404040, + ["8"] = 0x7F7F7F, + d = 0x00FF00, + f = 0x000000 + } + + local function blit(text, fg, bg) + if text == "" then return end + if #text ~= #fg or #fg ~= #bg then error("Unbalanced string lengths in blit", 2) end + if #text > term.getViewport() - term.getCursor() then + text = string.sub(text, 1, term.getViewport() - term.getCursor(), nil) + fg = string.sub(fg, 1, term.getViewport() - term.getCursor(), nil) + bg = string.sub(bg, 1, term.getViewport() - term.getCursor(), nil) + end + for i = 1, #text do + if ccColors[string.sub(fg, i, i)] == nil then error("Unknown color " .. string.sub(fg, i, i)) end + if ccColors[string.sub(bg, i, i)] == nil then error("Unknown color " .. string.sub(bg, i, i)) end + gpu.setForeground(ccColors[string.sub(fg, i, i)]) + gpu.setBackground(ccColors[string.sub(bg, i, i)]) + term.write(string.sub(text, i, i)) + end + end + + print("") + for i = 1, string.len(ccart), 25 do + blit(string.sub(ccart, i, i+23), string.sub(gpu.getDepth() > 1 and ccart_adv_fg or ccart_fg, i, i+23), string.sub(gpu.getDepth() > 1 and ccart_adv_bg or ccart_bg, i, i+23)) + term.write(" ") + if sysinfo[((i-1)/25)+1] ~= nil then blit(table.unpack(sysinfo[((i-1)/25)+1])) end + print("") + end + if #sysinfo > 16 then for i = 16, #sysinfo do + term.write(string.rep(" ", 26)) + blit(table.unpack(sysinfo[i])) + print("") + end end + print("") + os.sleep(0.25) + +else print("Unknown computer type") end \ No newline at end of file diff --git a/bin/script b/bin/script new file mode 100644 index 0000000..3a7a264 --- /dev/null +++ b/bin/script @@ -0,0 +1,7 @@ +local tArgs = {...} + +if #tArgs >= 1 then + shell.executeScript(shell.resolve(tArgs[1])) +else + kerneldraw.printAppInfo("script", "No file specified") +end \ No newline at end of file diff --git a/bin/set b/bin/set new file mode 100644 index 0000000..01d4b24 --- /dev/null +++ b/bin/set @@ -0,0 +1,55 @@ +local pp = require "cc.pretty" + +local tArgs = { ... } +if #tArgs == 0 then + -- "set" + local _, y = term.getCursorPos() + local tSettings = {} + for n, sName in ipairs(settings.getNames()) do + tSettings[n] = textutils.serialize(sName) .. " is " .. textutils.serialize(settings.get(sName)) + end + textutils.pagedPrint(table.concat(tSettings, "\n"), y - 3) + +elseif #tArgs == 1 then + -- "set foo" + local sName = tArgs[1] + local deets = settings.getDetails(sName) + local msg = pp.text(sName, colors.cyan) .. " is " .. pp.pretty(deets.value) + if deets.default ~= nil and deets.value ~= deets.default then + msg = msg .. " (default is " .. pp.pretty(deets.default) .. ")" + end + pp.print(msg) + if deets.description then print(deets.description) end + +else + -- "set foo bar" + local sName = tArgs[1] + local sValue = tArgs[2] + local value + if sValue == "true" then + value = true + elseif sValue == "false" then + value = false + elseif sValue == "nil" then + value = nil + elseif tonumber(sValue) then + value = tonumber(sValue) + else + value = sValue + end + + local option = settings.getDetails(sName) + if value == nil then + settings.unset(sName) + print(textutils.serialize(sName) .. " unset") + elseif option.type and option.type ~= type(value) then + printError(("%s is not a valid %s."):format(textutils.serialize(sValue), option.type)) + else + settings.set(sName, value) + print(textutils.serialize(sName) .. " set to " .. textutils.serialize(value)) + end + + if value ~= option.value then + settings.save() + end +end diff --git a/bin/sh b/bin/sh new file mode 100644 index 0000000..2c8e559 --- /dev/null +++ b/bin/sh @@ -0,0 +1,256 @@ +local tArgs = {...} + +local parentShell = shell + +local bExit = false +local sDir = (parentShell and parentShell.dir()) or "" +local sPath = (parentShell and parentShell.path()) or ".:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin" +local tAliases = (parentShell and parentShell.aliases()) or {} +local tProgramStack = {} + +local shell = {} +local tEnv = { + ["shell"] = shell, +} + +local function findArg(arg) + for _,v in ipairs(tArgs) do + if v == arg then + return true + end + end + return false +end + +-- Colours +local promptColour, textColour, bgColour +if term.isColour() then + promptColour = colours.yellow + textColour = colours.white + bgColour = colours.black +else + promptColour = colours.white + textColour = colours.white + bgColour = colours.black +end + + +local function run( _sCommand, ... ) + local sPath = shell.resolveProgram( _sCommand ) + if sPath ~= nil then + tProgramStack[#tProgramStack + 1] = sPath + local result = os.run( tEnv, sPath, ... ) + tProgramStack[#tProgramStack] = nil + return result + else + printError( "No such program" ) + printError( _sCommand .. ": Command not found" ) + return false + end +end + +local function runLine( _sLine ) + local tWords = {} + for match in string.gmatch( _sLine, "[^ \t]+" ) do + table.insert( tWords, match ) + end + + local sCommand = tWords[1] + if sCommand then + return run( sCommand, unpack( tWords, 2 ) ) + end + return false +end + +-- Install shell API +function shell.run( ... ) + return runLine( table.concat( { ... }, " " ) ) +end + +function shell.exit() + bExit = true +end + +function shell.dir() + return sDir +end + +function shell.setDir( _sDir ) + sDir = _sDir +end + +function shell.path() + return sPath +end + +function shell.setPath( _sPath ) + sPath = _sPath +end + +function shell.resolve( _sPath ) + local sStartChar = string.sub( _sPath, 1, 1 ) + if sStartChar == "/" or sStartChar == "\\" then + return fs.combine( "", _sPath ) + else + return fs.combine( sDir, _sPath ) + end +end + +function shell.resolveProgram( _sCommand ) + -- Substitute aliases firsts + if tAliases[ _sCommand ] ~= nil then + _sCommand = tAliases[ _sCommand ] + end + + -- If the path is a global path, use it directly + local sStartChar = string.sub( _sCommand, 1, 1 ) + if sStartChar == "/" or sStartChar == "\\" then + local sPath = fs.combine( "", _sCommand ) + if fs.exists( sPath ) and not fs.isDir( sPath ) then + return sPath + end + return nil + end + + -- Otherwise, look on the path variable + for sPath in string.gmatch(sPath, "[^:]+") do + sPath = fs.combine( shell.resolve( sPath ), _sCommand ) + if fs.exists( sPath ) and not fs.isDir( sPath ) then + return sPath + end + end + + -- Not found + return nil +end + +function shell.programs( _bIncludeHidden ) + local tItems = {} + + -- Add programs from the path + for sPath in string.gmatch(sPath, "[^:]+") do + sPath = shell.resolve( sPath ) + if fs.isDir( sPath ) then + local tList = fs.list( sPath ) + for n,sFile in pairs( tList ) do + if not fs.isDir( fs.combine( sPath, sFile ) ) and + (_bIncludeHidden or string.sub( sFile, 1, 1 ) ~= ".") then + tItems[ sFile ] = true + end + end + end + end + + -- Sort and return + local tItemList = {} + for sItem, b in pairs( tItems ) do + table.insert( tItemList, sItem ) + end + table.sort( tItemList ) + return tItemList +end + +function shell.getRunningProgram() + if #tProgramStack > 0 then + return tProgramStack[#tProgramStack] + end + return nil +end + +function shell.setAlias( _sCommand, _sProgram ) + tAliases[ _sCommand ] = _sProgram +end + +function shell.clearAlias( _sCommand ) + tAliases[ _sCommand ] = nil +end + +function shell.aliases() + -- Add aliases + local tCopy = {} + for sAlias, sCommand in pairs( tAliases ) do + tCopy[sAlias] = sCommand + end + return tCopy +end + +-- Custom shell API functions + +function shell.executeScript(path) + if not fs.exists(path) or fs.isDir(path) then + return "File or directory not found" + else + local t = luaex.iterateFileLines(path) + if string.find(t[1], "@ @ !! FREAX SCRIPT HEADER") or t[1] == "@ @ !! FREAX SCRIPT HEADER" then + for _,cmd in pairs(t) do + shell.run(cmd) + end + else + return "Couldn't find script header" + end + end +end + +function shell.getActiveUserShell() + if fs.exists("/etc/passwd/.shadow/".._G["_activeUser"]..".usr") then + local ud = dofile("/etc/passwd/.shadow/".._G["_activeUser"]..".usr") + return ud.shell or "/bin/sh" + else + return "/bin/sh" + end +end + +-- Run custom shells +if findArg("--force") or findArg("-f") then + kernel.writeMessage("Ignoring shell settings") +elseif shell.getActiveUserShell() ~= "/bin/sh" then + shell.run(shell.getActiveUserShell()) + kernel.shutdown(false) +end + +term.setBackgroundColor( bgColour ) +term.setTextColour( textColour ) + +local label = os.getComputerLabel() or os.getComputerID() + +-- Read commands and execute them +local tCommandHistory = {} +while not bExit do + term.setBackgroundColor( bgColour ) + if security.getSU() then + if security.getActiveUserStatus() then + term.setTextColour( colours.lime ) + else + term.setTextColour( colours.orange ) + end + else + term.setTextColour( colours.cyan ) + end + write( _activeUser ) + term.setTextColour( colours.lightGrey ) + write( "@" ) + term.setTextColour( colours.lightBlue ) + write( label ) + term.setTextColour( colours.lightGrey ) + write( "/" ) + term.setTextColour( colours.lightBlue ) + write( shell.dir() ) + if security.getSU() then + if security.getActiveUserStatus() then + term.setTextColour( colours.lime ) + else + term.setTextColour( colours.orange ) + end + write( " # " ) + else + term.setTextColour( colours.cyan ) + write( " $ " ) + end + term.setTextColour( textColour ) + + local sLine = read( nil, tCommandHistory ) + table.insert( tCommandHistory, sLine ) + runLine( sLine ) +end + +-- Custom shutdown code goes here \ No newline at end of file diff --git a/bin/time b/bin/time new file mode 100644 index 0000000..9888460 --- /dev/null +++ b/bin/time @@ -0,0 +1,3 @@ +local nTime = os.time() +local nDay = os.day() +print("The time is " .. textutils.formatTime(nTime, false) .. " on Day " .. nDay) diff --git a/bin/tpm-install b/bin/tpm-install new file mode 100644 index 0000000..ccf0a8d --- /dev/null +++ b/bin/tpm-install @@ -0,0 +1,65 @@ +local tArgs = {...} +local packages = config.load("/etc/tpm/package.dat") +local temp = config.load("/etc/tpm/packageInstalled.dat") +local found, value + +if not security.getSU() then + exception.throw("RestrictedOpsException") + return +end + +if #tArgs < 1 then + kerneldraw.printAppInfo("tpm", "Package name not specified") + return +elseif not packages then + kerneldraw.printAppInfo("tpm", "package.dat is missing or corrupt") + log.writeMessage("package.dat unable to be traversed") + return +elseif not temp then + shell.executeScript("/etc/scripts/tpm-recache.tsf") +end + +found, value = search.traverseKey(temp, tArgs[1]) +if found and value then + kerneldraw.printAppInfo("tpm", "Package already installed") + return +end + +kerneldraw.printAppInfo("tpm", "Traversing package.dat") +local ok, pack = search.traverseValue(packages, tArgs[1]) +if ok and pack then + kerneldraw.printAppInfo("tpm", "Found corresponding package") + kerneldraw.printAppInfo("tpm", "Downloading API files [1/2]") + local numapi = 1 + local numbin = 1 + local APIInstallFlag = false + for k,v in pairs(pack.apis) do + if tpm.downloadAPI(k, v) then + APIInstallFlag = true + kerneldraw.printAppInfo("tpm", "Downloaded API "..k.." ["..tostring(numapi).."]") + else + kerneldraw.printAppWarning("tpm", "Failed to download API "..k) + end + numapi = numapi + 1 + end + kerneldraw.printAppInfo("tpm", "Downloading binaries [2/2]") + for k,v in pairs(pack.bins) do + if tpm.downloadBinary(k, v) then + kerneldraw.printAppInfo("tpm", "Downloaded binary "..k.." ["..tostring(numbin).."]") + else + kerneldraw.printAppWarning("tpm", "Failed to download binary "..k) + end + numbin = numbin + 1 + end + table.insert(temp, pack.name) + log.writeMessage("New package downloaded and installed") + config.save(temp, "/etc/tpm/packageInstalled.dat") + kerneldraw.printAppSuccess("tpm", "package installed") + if APIInstallFlag then + if kerneldraw.request("Assemblies were installed, restart") then + kernel.reboot(false) + end + end +else + kerneldraw.printAppInfo("tpm", "Unable to find id for "..tArgs[1]) +end \ No newline at end of file diff --git a/bin/tpm-list b/bin/tpm-list new file mode 100644 index 0000000..511a515 --- /dev/null +++ b/bin/tpm-list @@ -0,0 +1,30 @@ +local tArgs = {...} +local packages = config.load("/etc/tpm/package.dat") +local packageInstalled = config.load("/etc/tpm/packageInstalled.dat") + +if not security.getSU() then + exception.throw("RestrictedOpsException") + return +end + +if not packages then + kerneldraw.printAppInfo("tpm", "package.dat is missing or corrupt") + log.writeMessage("package.dat unable to be traversed") + return +elseif not packageInstalled then + shell.executeScript("/etc/scripts/tpm-recache.tsf") +end + +if search.findValue(tArgs, "available") then + kerneldraw.printAppInfo("tpm", "Traversing package.dat") + for k, v in pairs(packages) do + kerneldraw.printAppInfo("tpm", "Found package identifier "..v.name) + end +elseif search.findValue(tArgs, "installed") then + kerneldraw.printAppInfo("tpm", "Traversing packageInstalled.dat") + for k, v in pairs(packageInstalled) do + kerneldraw.printAppInfo("tpm", "Found package identifier "..v) + end +else + kerneldraw.printAppInfo("tpm", "Specify listing type") +end \ No newline at end of file diff --git a/bin/tpm-recache b/bin/tpm-recache new file mode 100644 index 0000000..9180e90 --- /dev/null +++ b/bin/tpm-recache @@ -0,0 +1,13 @@ +if not security.getSU() then + exception.throw("RestrictedOpsException") + return +end + +kerneldraw.printAppInfo("tpm", "Redownloading package.dat") +if tpm.getPaste("/etc/tpm/package.dat", "0YScZtUc", true) then + kerneldraw.printAppSuccess("tpm", "Package cache updated") + log.writeMessage("Package cache updated") +else + kerneldraw.printAppInfo("tpm", "Failed to update package cache") + log.writeMessage("Failed to update package cache") +end \ No newline at end of file diff --git a/bin/tpm-remove b/bin/tpm-remove new file mode 100644 index 0000000..52ff66a --- /dev/null +++ b/bin/tpm-remove @@ -0,0 +1,68 @@ +local tArgs = {...} +local packages = config.load("/etc/tpm/package.dat") +local temp = config.load("/etc/tpm/packageInstalled.dat") +local found, value + +if not security.getSU() then + exception.throw("RestrictedOpsException") + return +end + +if #tArgs < 1 then + kerneldraw.printAppInfo("tpm", "Package name not specified") + return +elseif not packages then + kerneldraw.printAppInfo("tpm", "package.dat is missing or corrupt") + log.writeMessage("package.dat unable to be traversed") + return +elseif not temp then + shell.executeScript("/etc/scripts/tpm-recache.tsf") +end + + +kerneldraw.printAppInfo("tpm", "Traversing package.dat") +local ok, pack = search.traverseValue(packages, tArgs[1]) +if ok and pack then + found, value = search.traverseKey(temp, pack.name) + if found and value then + temp[value] = nil + else + kerneldraw.printAppInfo("tpm", "Package missing") + return + end + kerneldraw.printAppInfo("tpm", "Found corresponding package") + kerneldraw.printAppInfo("tpm", "Removing API files [1/2]") + local numapi = 1 + local numbin = 1 + local APIRemovalFlag = false + for k,v in pairs(pack.apis) do + if fs.exists(tpm.toPackageAPI(k)) and not fs.isDir(tpm.toPackageAPI(k)) then + fs.delete(tpm.toPackageAPI(k)) + APIRemovalFlag = true + kerneldraw.printAppInfo("tpm", "Removed API "..k.." ["..tostring(numapi).."]") + else + kerneldraw.printAppWarning("tpm", "Failed to remove API "..k) + end + numapi = numapi + 1 + end + kerneldraw.printAppInfo("tpm", "Removing binaries [2/2]") + for k,v in pairs(pack.bins) do + if fs.exists(tpm.toPackageBinary(k)) and not fs.isDir(tpm.toPackageBinary(k)) then + fs.delete(tpm.toPackageBinary(k)) + kerneldraw.printAppInfo("tpm", "Removed binary "..k.." ["..tostring(numbin).."]") + else + kerneldraw.printAppWarning("tpm", "Failed to remove binary "..k) + end + numbin = numbin + 1 + end + if APIRemovalFlag then + if kerneldraw.request("Assemblies were removed, restart") then + kernel.reboot(false) + end + end + config.save(temp, "/etc/tpm/packageInstalled.dat") + log.writeMessage("Removed package "..tArgs[1]) + kerneldraw.printAppSuccess("tpm", "Package removed") +else + kerneldraw.printAppInfo("tpm", "Unable to find id for "..tArgs[1]) +end \ No newline at end of file diff --git a/bin/tpm-repair b/bin/tpm-repair new file mode 100644 index 0000000..9e0f9c5 --- /dev/null +++ b/bin/tpm-repair @@ -0,0 +1,14 @@ +if not security.getSU() then + exception.throw("RestrictedOpsException") + return +end + +if not config.load("/etc/tpm/packageInstalled.dat") then + kerneldraw.printAppInfo("tpm", "packageInstalled.dat is missing or corrupt") + if kerneldraw.request("Repair TPM data files") then + config.save({}, "/etc/tpm/packageInstalled.dat") + kerneldraw.printAppInfo("tpm", "Package registry reconstructed") + end +else + kerneldraw.printAppInfo("tpm", "packageInstalled.dat is correctly readable") +end \ No newline at end of file diff --git a/bin/tty b/bin/tty new file mode 100644 index 0000000..1890e32 --- /dev/null +++ b/bin/tty @@ -0,0 +1,2 @@ +local x, y = term.getSize() +kerneldraw.printAppInfo("tty", "Terminal object size is "..tostring(x).."x"..tostring(y)) diff --git a/bin/type b/bin/type new file mode 100644 index 0000000..431540d --- /dev/null +++ b/bin/type @@ -0,0 +1,17 @@ +local tArgs = { ... } +if #tArgs < 1 then + local programName = "type" + print("Usage: " .. programName .. " ") + return +end + +local sPath = shell.resolve(tArgs[1]) +if fs.exists(sPath) then + if fs.isDir(sPath) then + print("directory") + else + print("file") + end +else + print("No such path") +end diff --git a/bin/uname b/bin/uname new file mode 100644 index 0000000..cafce18 --- /dev/null +++ b/bin/uname @@ -0,0 +1,62 @@ +local tArgs = {...} +local err = true +local function findArg(arg) + for _,v in ipairs(tArgs) do + if v == arg then + return true + end + end + return false +end + +if findArg("--help") or findArg("-h") then + err = false + shell.execute("/bin/man uname") +end +if findArg("--kernel-name") or findArg("-s") then + err = false + kerneldraw.printAppInfo("uname", kernel.getName()) +end +if findArg("--nodename") or findArg("-n") then + err = false + kerneldraw.printAppInfo("uname", kernel.getHostname()) +end +if findArg("--kernel-release") or findArg("-r") then + err = false + kerneldraw.printAppInfo("uname", kernel.getRelease()) +end +if findArg("--kernel-version") or findArg("-v") then + err = false + kerneldraw.printAppInfo("uname", kernel.getVersion()) +end +if findArg("--machine") or findArg("-m") then + err = false + kerneldraw.printAppInfo("uname", kernel.getMachine()) +end +if findArg("--processor") or findArg("-p") then + err = false + kerneldraw.printAppInfo("uname", kernel.getProcessorArchitecture()) +end +if findArg("--hardware-platform") or findArg("-i") then + err = false + kerneldraw.printAppInfo("uname", kernel.getHardwarePlatform()) +end +if findArg("--operating-system") or findArg("-o") then + err = false + kerneldraw.printAppInfo("uname", kernel.getOS()) +end +if findArg("--path") or findArg("-b") then + err = false + kerneldraw.printAppInfo("uname", shell.path()) +end +if findArg("--id") or findArg("-c") then + err = false + kerneldraw.printAppInfo("uname", os.getComputerID()) +end +if findArg("--user") or findArg("-u") then + err = false + kerneldraw.printAppInfo("uname", _G[_activeUser]) +end +if #tArgs < 1 or err then + kerneldraw.printAppInfo("uname", "Arguments not specified") +end \ No newline at end of file diff --git a/bin/unmount b/bin/unmount new file mode 100644 index 0000000..eeacdc9 --- /dev/null +++ b/bin/unmount @@ -0,0 +1,4 @@ +if mounter == nil then error("Mounting directories is not supported in vanilla mode.") end +local args = { ... } +if args[1] ~= nil then if not mounter.unmount(args[1]) then printError("Could not unmount") end +else print("Usage: unmount ") end diff --git a/bin/useradd b/bin/useradd new file mode 100644 index 0000000..3fc2958 --- /dev/null +++ b/bin/useradd @@ -0,0 +1,25 @@ +local tArgs = {...} + +if #tArgs < 2 then + kerneldraw.printAppInfo("useradd", "Specify a user") + return +end + +if fs.exists("/etc/passwd/"..tArgs[1]..".dat") and not fs.isDir("/etc/passwd/"..tArgs[1]..".dat") then + kerneldraw.printAppInfo("useradd", "User already exists") + return +end + +if not security.getSU() then + exception.throw("RestrictedOpsException") + return +end + +if security.passbyte(tArgs[1], tArgs[2]) then + kerneldraw.printAppSuccess("useradd", "User "..tArgs[1].." added") + log.writeSecurity("User "..tArgs[1].." added") + fs.makeDir("/home/"..tArgs[1]) +else + kerneldraw.printAppInfo("useradd", "Failed to add user " .. tArgs[1]) + log.writeSecurity("Failed to add user " .. tArgs[1]) +end \ No newline at end of file diff --git a/bin/userdel b/bin/userdel new file mode 100644 index 0000000..50cfcab --- /dev/null +++ b/bin/userdel @@ -0,0 +1,27 @@ +local tArgs = { ... } + +if #tArgs < 1 then + kerneldraw.printAppInfo( "userdel", "Specify a user" ) + return +end + +if not security.getSU() or tArgs[1] == "root" then + exception.throw("RestrictedOpsException") + return +end + +local pathA = "/etc/passwd/"..tArgs[1]..".dat" +local pathB = "/etc/passwd/.shadow/"..tArgs[1]..".usr" +local pathC = "/home/"..tArgs[1] + +if fs.exists(pathA) then + if kerneldraw.request("remove user record") then + fs.delete( pathA ) + if fs.exists(pathB) then + fs.delete( pathB ) + end + fs.delete( pathC ) + end +else + kerneldraw.printAppInfo("userdel", "User " .. tArgs[1] .. " does not exist") +end \ No newline at end of file diff --git a/bin/wget b/bin/wget new file mode 100644 index 0000000..5bbc060 --- /dev/null +++ b/bin/wget @@ -0,0 +1,92 @@ +local function printUsage() + local programName = "wget" + print("Usage:") + print(programName .. " [filename]") + print(programName .. " run ") +end + +local tArgs = { ... } + +local run = false +if tArgs[1] == "run" then + table.remove(tArgs, 1) + run = true +end + +if #tArgs < 1 then + printUsage() + return +end + +local url = table.remove(tArgs, 1) + +if not http then + printError("wget requires the http API") + printError("Set http.enabled to true in CC: Tweaked's config") + return +end + +local function getFilename(sUrl) + sUrl = sUrl:gsub("[#?].*" , ""):gsub("/+$" , "") + return sUrl:match("/([^/]+)$") +end + +local function get(sUrl) + -- Check if the URL is valid + local ok, err = http.checkURL(url) + if not ok then + printError(err or "Invalid URL.") + return + end + + write("Connecting to " .. sUrl .. "... ") + + local response = http.get(sUrl , nil , true) + if not response then + print("Failed.") + return nil + end + + print("Success.") + + local sResponse = response.readAll() + response.close() + return sResponse or "" +end + +if run then + local res = get(url) + if not res then return end + + local func, err = load(res, getFilename(url), "t", _ENV) + if not func then + printError(err) + return + end + + local ok, err = pcall(func, table.unpack(tArgs)) + if not ok then + printError(err) + end +else + local sFile = tArgs[1] or getFilename(url) or url + local sPath = shell.resolve(sFile) + if fs.exists(sPath) then + print("File already exists") + return + end + + local res = get(url) + if not res then return end + + local file, err = fs.open(sPath, "wb") + if not file then + printError("Cannot save file: " .. err) + return + end + + file.write(res) + file.close() + + print("Downloaded as " .. sFile) +end diff --git a/boot/freax/boot.conf b/boot/freax/boot.conf new file mode 100644 index 0000000..88afebb --- /dev/null +++ b/boot/freax/boot.conf @@ -0,0 +1,6 @@ +{ + [ "enableBootScreen" ] = "true", + [ "doSystemSetup" ] = "true", + [ "logo" ] = "/boot/freax/freax.img", + [ "logo_small" ] = "/boot/freax/freax-small.img", +} diff --git a/boot/freax/freax-small.img b/boot/freax/freax-small.img new file mode 100644 index 0000000..7016886 --- /dev/null +++ b/boot/freax/freax-small.img @@ -0,0 +1,7 @@ +aaaaaaaaaaaaaaaaaaa +aaa000aaaeeaaeeaaaa +aaa0aaaaaaa11aaaaaa +aaa000aaaaa44aaaaaa +aaa0aaaaaaa55aaaaaa +aaa0aaaaa33aa33aaaa +aaaaaaaaaaaaaaaaaaa diff --git a/boot/freax/freax.img b/boot/freax/freax.img new file mode 100644 index 0000000..ac7d3bd --- /dev/null +++ b/boot/freax/freax.img @@ -0,0 +1,7 @@ +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaa000aaa0000aaa000aaa0000aaaeeaaeeaa +aaa0aaaaa0aa0aaa0aaaaa0aa0aaaaa11aaaa +aaa000aaa0000aaa000aaa0000aaaaa44aaaa +aaa0aaaaa00aaaaa0aaaaa0aa0aaaaa55aaaa +aaa0aaaaa0a00aaa000aaa0aa0aaa33aa33aa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/boot/freax/initrd.img b/boot/freax/initrd.img new file mode 100644 index 0000000..2bddd8f --- /dev/null +++ b/boot/freax/initrd.img @@ -0,0 +1,169 @@ +kernel.isBooting = true +settings.load("/boot/freax/boot.conf") +if not fs.exists(settings.get("logo")) then + print("logo is not a valid image") + sleep(3) + os.reboot() +end +if not fs.exists(settings.get("logo_small")) then + print("logo_small is not a valid image") + sleep(3) + os.reboot() +end +local logo = paintutils.loadImage(settings.get("logo")) +local slogo = paintutils.loadImage(settings.get("logo_small")) +local enableBootScreen = settings.get("enableBootScreen") +local doSystemSetup = settings.get("doSystemSetup") +if enableBootScreen == "false" then + enableBootScreen = false +elseif enableBootScreen == "true" then + enableBootScreen = true +else + print("enableBootScreen is a boolean.") + sleep(3) + kernel.reboot(true) +end +if doSystemSetup == "false" then + doSystemSetup = false +elseif doSystemSetup == "true" then + doSystemSetup = true +else + print("doSystemSetup is a boolean.") + sleep(3) + kernel.reboot(true) +end +kernel.printLog = not enableBootScreen +kernel.writeBootLog("Initializing " .. kernel.getName()) +kerneldraw.bootScreen(enableBootScreen,0,logo,slogo,"Initializing " .. kernel.getName()) +kerneldraw.bootDelay(1) +kerneldraw.bootScreen(enableBootScreen,0,logo,slogo,"Loading system libraries") +kernel.loadSysLibs() +local oldReadOnly = _G["fs"]["isReadOnly"] +local oldMove = _G["fs"]["move"] +local oldDelete = _G["fs"]["delete"] +local oldHttpGet = _G["http"]["get"] +local oldError = _G["error"] +kerneldraw.bootDelay(1) +kerneldraw.bootScreen(enableBootScreen,10,logo,slogo,"Initializing logs") +kerneldraw.bootDelay(0.5) +kernel.writeBootLog("Initialized system boot log") +kernel.writeSyslog("Initialized primary system log") +kernel.writeMessage("Initialized secondary system log") +kerneldraw.bootScreen(enableBootScreen,13,logo,slogo,"Initializing logs") +kernel.writeSecurity("Initialized security/access log") +kernel.writeShutdown("Initialized shutdown log") +kernel.writeAuth("Initialized authorization log") +kerneldraw.bootDelay(0.25) +kerneldraw.bootScreen(enableBootScreen,15,logo,slogo,"Initializing HTTP") +if http then + kernel.writeBootLog("HTTP detection succeeded") +else + kernel.writeBootLog("HTTP detection failed") +end +kerneldraw.bootDelay(2.1) +kerneldraw.bootScreen(enableBootScreen,27,logo,slogo,"Executing autorun") +shell.run("/etc/autorun") +kerneldraw.bootScreen(enableBootScreen,36,logo,slogo,"Loading user libraries") +kernel.loadUsrLibs() +kerneldraw.bootDelay(0.25) +kerneldraw.bootScreen(enableBootScreen,44,logo,slogo,"Initializing security") +kernel.writeBootLog("Initializing security") +_G["fs"]["isReadOnly"] = function(path) + local find1 = shell.resolve(path) + if string.find(find1, "boot") or string.find(find1, "etc") or string.find(find1, "bin") or string.find(find1, "sbin") then + kernel.writeSecurity("Attempt to access system files!") + if security.getSU() or _IDENTIFIER then + kernel.writeSecurity("Superuser access verified.") + return false + else + kernel.writeSecurity("Attempt blocked.") + return true + end + elseif find1 == "initrd" or find1 == "/boot/kernel" then + kernel.writeSecurity("Attempt to access bootloader!") + kernel.writeSecurity("Attempt blocked.") + return true + end + return oldReadOnly(path) +end +_G["fs"]["move"] = function(path1, path2) + local find2 = shell.resolve(path1) + if string.find(find2, "boot") or string.find(find2, "etc") or string.find(find2, "bin") or string.find(find2, "sbin") then + kernel.writeSecurity("Attempt to access system files!") + if security.getSU() or _IDENTIFIER then + kernel.writeSecurity("Superuser access verified.") + else + kernel.writeSecurity("Attempt blocked.") + exception.throw("RestrictedOpsException") + return nil + end + elseif find2 == "initrd" or find2 == "/boot/kernel" then + kernel.writeSecurity("Attempt to access bootloader!") + kernel.writeSecurity("Attempt blocked.") + exception.throw("RestrictedOpsException") + return nil + end + return oldMove(path1, path2) +end +_G["fs"]["delete"] = function(path) + local find3 = shell.resolve(path) + if string.find(find3, "boot") or string.find(find3, "etc") or string.find(find3, "bin") or string.find(find3, "sbin") then + kernel.writeSecurity("Attempt to access system files!") + if security.getSU() or _IDENTIFIER then + kernel.writeSecurity("Superuser access verified.") + else + kernel.writeSecurity("Attempt blocked.") + exception.throw("RestrictedOpsException") + return nil + end + elseif find3 == "initrd" or find3 == "/boot/kernel" then + kernel.writeSecurity("Attempt to access bootloader!") + kernel.writeSecurity("Attempt blocked.") + exception.throw("RestrictedOpsException") + return nil + end + return oldDelete(path) +end +_G["http"]["get"] = function(url, headers) + kernel.writeSecurity("Downloaded contents of "..url) + return oldHttpGet(url, headers) +end +_G["error"] = function(text, lvl) + local x = lvl or 2 + local logger = fs.open("/etc/errorLog", "a") + logger.writeLine(tostring(os.day()).."d:"..textutils.formatTime(os.time(), true).." | "..text) + logger.close() + oldError(text, x or 1) +end +rawset(fs, "_native", {}) +rawset(fs, "_native.check", oldReadOnly) +rawset(fs, "_native.move", oldMove) +rawset(fs, "_native.delete", oldDelete) +rawset(http, "_synchronise", oldHttpGet) +rawset(os, "_exception", oldError) +kerneldraw.bootDelay(3.6) +kernel.writeBootLog("Boot sequence stage 2 complete") +kerneldraw.bootScreen(enableBootScreen,86,logo,slogo,"Finishing") +kerneldraw.bootDelay(3) +if doSystemSetup then + if enableBootScreen then + kerneldraw.clearScreen(colors.black) + end + local isLogsEnabled = kernel.printLog + kernel.printLog = true + if kernel.setupSystem() then + settings.set("doSystemSetup","false") + settings.save("/boot/freax/boot.conf") + else + kernel.printLog = isLogsEnabled + kernel.poweroff(true) + end + kernel.printLog = isLogsEnabled +end +kerneldraw.bootScreen(enableBootScreen,100,logo,slogo,"Starting login") +kerneldraw.bootDelay(0.5) +kernel.printLog = true +if enableBootScreen then + kerneldraw.clearScreen(colors.black) +end +shell.run("/sbin/login") diff --git a/boot/freax/kernel.boot b/boot/freax/kernel.boot new file mode 100644 index 0000000..f2b3d05 --- /dev/null +++ b/boot/freax/kernel.boot @@ -0,0 +1,376 @@ +print("freax: loading") + +local printLog = false +local isBooting = false + +function setupSystem() + kerneldraw.printKernelMessage("Starting system setup") + kerneldraw.printKernelMessage("Please enter a new root password") + kerneldraw.printKernelAsking("Password:") + local passwd = secureInputReturn("") + if security.passbyte("root", passwd) then + kerneldraw.printKernelMessage("Changed password of the root user") + while true do + kerneldraw.printKernelMessage("Do you want to add another user?") + kerneldraw.printKernelAsking("[y/n]: ") + local add_user = "n" + add_user = read() + if add_user == "y" or add_user == "Y" then + local username = nil + local password = nil + kerneldraw.printKernelAsking("Username: ") + username = read() + if username == nil or username == "" then + kerneldraw.printKernelMessage("Invalid username") + break + end + kerneldraw.printKernelAsking("Password:") + password = secureInputReturn("") + if password == nil or password == "" then + kerneldraw.printKernelMessage("Invalid password") + end + if security.passbyte(username,password) then + log.writeSecurity("User ".. username .." added") + fs.makeDir("/home/" .. username) + else + kerneldraw.printKernelMessage("Failed to add user " .. username) + break + end + else + break + end + end + kerneldraw.printKernelMessage("System setup completed") + return true + else + kerneldraw.printKernelMessage("Failed to change the password. Shutting down in 3 seconds") + sleep(3) + reboot(true) + return(false) + end +end + +local function initLogger(file) + if not fs.isDir("/var") and not fs.exists("/var/log") and not fs.isDir("/var/log") then + fs.makeDir("/var") + fs.makeDir("/var/log") + end + return "/var/log/" .. file +end + +function halt() + writeKernelCall("halt()") + sleep(1) + writeShutdown("halting") + while true do + coroutine.create(function() while true do coroutine.yield() end end) + coroutine.yield() + end +end + +function poweroff(varDelete) + if varDelete then + writeKernelCall("poweroff(true)") + else + writeKernelCall("poweroff(false)") + end + writeShutdown("Reaching system poweroff") + if varDelete then + writeShutdown("Removing temporary files") + if fs.exists("/var") and fs.isDir("/var") then + fs.delete("/var") + end + sleep(1) + end + writeShutdown("Reached system poweroff") + sleep(1) + os.shutdown() + halt() +end + +function reboot(varDelete) + if varDelete then + writeKernelCall("reboot(true)") + else + writeKernelCall("reboot(false)") + end + writeShutdown("Reaching system reboot") + if varDelete then + sleep(0.5) + writeShutdown("Removing temporary files") + if fs.exists("/var") and fs.isDir("/var") then + fs.delete("/var") + end + end + writeShutdown("Reached system reboot") + sleep(1.5) + os.reboot() + halt() +end + +function panic(text) + writeKernelCall("panic(" .. text .. ")") + local logger = fs.open("/etc/errorLog", "a") + logger.writeLine(tostring(os.day()).. "d:" ..textutils.formatTime(os.time(), true).. " | panic! " ..text) + logger.close() + printError("freax: panic! " .. text) + halt() +end + +function import(file) + writeKernelCall("import(" .. file .. ")") + if fs.exists(file) and not fs.isDir(file) then + return os.loadAPI(file) + else + writeSyslog("Failed to load API " .. file) + return false + end +end + +function require(file) -- A primitive require + writeKernelCall("require(" .. file .. ")") + if fs.exists(file) and not fs.isDir(file) then + return dofile(file) + else + writeSyslog("Failed to load module " .. file) + return nil + end +end + +function APIHook(t) + writeKernelCall("APIHook(" .. t .. ")") + if t then + return true + else + -- Not loaded + if import("/system/api/" .. t) then + return true + end + end + return false +end + +function writeKernelCall(calledFunc) + --kerneldraw.printKernelMessage("Function called: " .. calledFunc) + local logger = fs.open(initLogger("calls"), "a") + logger.writeLine(tostring(os.day()) .. "d:" .. textutils.formatTime(os.time(),true) .. " | Function called: " .. calledFunc) + logger.close() +end + +function writeSyslog(text) + writeKernelCall("writeSyslog(" .. text .. ")") + kerneldraw.printKernelMessage(text) + local logger = fs.open(initLogger("syslog"), "a") + logger.writeLine(tostring(os.day()).. "d:" ..textutils.formatTime(os.time(), true).. " | " ..text) + logger.close() +end + +function writeMessage(text) + writeKernelCall("writeMessage(" .. text .. ")") + kerneldraw.printKernelMessage(text) + local logger = fs.open(initLogger("messages"), "a") + logger.writeLine(tostring(os.day()).. "d:" ..textutils.formatTime(os.time(), true).. " | " ..text) + logger.close() +end + +function writeSecurity(text) + writeKernelCall("writeSecurity(" .. text .. ")") + kerneldraw.printKernelMessage(text) + local logger = fs.open(initLogger("security"), "a") + logger.writeLine(tostring(os.day()).. "d:" ..textutils.formatTime(os.time(), true).. " | " ..text) + logger.close() +end + +function writeShutdown(text) + writeKernelCall("writeShutdown(" .. text .. ")") + kerneldraw.printKernelMessage(text) + local logger = fs.open(initLogger("shutdown"), "a") + logger.writeLine(tostring(os.day()).. "d:" ..textutils.formatTime(os.time(), true).. " | " ..text) + logger.close() +end + +function writeAuth(text) + writeKernelCall("writeAuth(" .. text .. ")") + kerneldraw.printKernelMessage(text) + local logger = fs.open(initLogger("auth"), "a") + logger.writeLine(tostring(os.day()).. "d:" ..textutils.formatTime(os.time(), true).. " | " ..text) + logger.close() +end + +function writeBootLog(text) + writeKernelCall("writeBootLog(" .. text .. ")") + kerneldraw.printKernelMessage(text) + local logger = fs.open(initLogger("boot"), "a") + logger.writeLine(tostring(os.day()).. "d:" ..textutils.formatTime(os.time(), true).. " | " ..text) + logger.close() +end + +function loadSysLibs() + writeKernelCall("loadSysLibs()") + writeBootLog("Loading system libraries") + local flist = fs.list("/etc/lib") + for _, file in ipairs(flist) do + sleep(0.25) + local returnValue = import("/etc/lib/" ..file) + if returnValue then + writeSyslog("Library " .. file .. " loaded") + else + writeSysLog("Library " .. file .. " failed to load") + end + end +end + +function loadUsrLibs() + writeKernelCall("loadUsrLibs()") + writeBootLog("Loading user libraries") + local flist = fs.list("/usr/local/lib") + for _, file in ipairs(flist) do + sleep(0.25) + local returnValue = import("/usr/local/lib/" ..file) + if returnValue then + writeSyslog("Library " .. file .. " loaded") + else + writeSysLog("Library " .. file .. " failed to load") + end + end +end + +function printFile(file) + writeKernelCall("printFile(" .. file .. ")") + if fs.exists(file) and not fs.isDir(file) then + local handle = fs.open(file, "r") + local contents = handle.readAll() + handle.close() + return contents + end +end + +function getURLContents(url) + writeKernelCall("getURLContents(" .. url .. ")") + local handlex = http.get(url) + local contents = handlex.readAll() + handlex.close() + return contents +end + +function getFile(file, url, overwrite) + writeKernelCall("getFile(" .. file .. "," .. url .. "," .. overwrite .. ")") + if not fs.exists(file) then + local handle = fs.open(file, "w") + handle.writeLine(getURLContents(url)) + handle.close() + return true + elseif overwrite and not fs.isDir(file) then + local handle = fs.open(file, "w") + handle.writeLine(getURLContents(url)) + handle.close() + return true + end + return false +end + +function secureInput(invite, definition) + writeKernelCall("secureInput(" .. invite .. "," .. definition .. ")") + write(invite.. " ") + local input = read("*") + if input == definition then + return true + else + return false + end +end + +function secureInputReturn(invite) + writeKernelCall("secureInput(" .. invite .. ")") + write(invite.. " ") + local input = read("*") + return input +end + +function getName() + writeKernelCall("getName()") + return "FREAX Kernel" +end + +function getVersion() + writeKernelCall("getVersion()") + return "d0.0.1.0" +end + +function getRelease() + writeKernelCall("getRelease()") + return getName() .. " " .. getVersion() +end + +function getMachine() + writeKernelCall("getMachine()") + if turtle then + if commands then + return "Command Turtle" + elseif term.isColor() then + return "Advanced Turtle" + else + return "Turtle" + end + elseif pocket then + if commands then + return "Command Pocket Computer" + elseif term.isColor() then + return "Advanced Pocket Computer" + else + return "Pocket Computer" + end + else + if commands then + return "Command Computer" + elseif term.isColor() then + return "Advanced Computer" + else + return "Computer" + end + end +end + +function getProcessorArchitecture() + writeKernelCall("getProcessorArchitecture()") + return "lua" +end + +function getHardwarePlatform() + writeKernelCall("getHardwarePlatform()") + return "ComputerCraft" +end + +function getOS() + writeKernelCall("getOS()") + return "FREAX" +end + +function getHostname() + writeKernelCall("getHostname()") + if os.getComputerLabel() == nil then + return getOS() + else + return os.getComputerLabel() + end +end + +function setHostname(hostname) + writeKernelCall("setHostname()") + local returncode os.setComputerLabel(hostname) + kerneldraw.printAppInfo("net","Changed hostname to " .. hostname) + kernel.writeMessage("Changed hostname to " .. hostname) + return returncode +end + +function numRound(num) + return num>=0 and math.floor(num+0.5) or math.ceil(num-0.5) +end + +function string2table(string_convert) + local table = {} + string_convert:sub(".",function(c) table.insert(table,c) end) + return table +end + +print("freax: loaded") diff --git a/boot/freax/kerneldraw.boot b/boot/freax/kerneldraw.boot new file mode 100644 index 0000000..a8d5937 --- /dev/null +++ b/boot/freax/kerneldraw.boot @@ -0,0 +1,286 @@ +termw,termh = term.getSize() + +function clearScreen(color) + term.setBackgroundColor(color or colors.black) + term.setCursorPos(1,1) + term.clear() +end + +function button(text, x1, x2, y) + term.setCursorPos(x1, y) + print(text) + local a, b, xx, yy = os.pullEvent("mouse_click") + if (xx >= x1 and xx <= x2 and yy == y) then + return true + else + return false + end +end + +function counter(text, y) + for i=1,100 do + sleep(0.1) + term.setCursorPos(1, y) + print(text.." ["..tostring(i).."%]") + end +end + +function drawProgress(txt, y) + term.setCursorPos(4, 4) + print(txt) + term.setCursorPos(1, y) + print("[ ]") + term.setCursorPos(2, y) + textutils.slowPrint("==============================]", 10) +end + +function drawProgressAlt(color) + term.setCursorPos(4, 10) + print("#-------------------------------------------#") + term.setCursorPos(4, 11) + print("| |") + term.setCursorPos(4, 12) + print("#-------------------------------------------#") + term.setBackgroundColor(color or colors.yellow) + term.setCursorPos(5, 11) + textutils.slowWrite(" ", 10) + term.setBackgroundColor(colors.black) +end + +function printColoredTextLine(y, txt, centered,bg, fg) + term.setCursorPos(1, y) + term.setBackgroundColor(bg or colors.lightGray) + term.write(" ") + if centered then + term.setCursorPos(termw/2-(string.len(txt)/2-1),y) + else + term.setCursorPos(1, y) + end + term.setTextColor(fg or colors.black) + print(txt) + term.setBackgroundColor(colors.black) + term.setTextColor(colors.white) +end + +function printColoredProgressTextLine(y, txt, progress, bgn, fgn, bgp, fgp) + if progress >= 101 then + kernel.panic("progress can't be over 100 (printColoredProgressTextLine in kerneldraw)") + return + end + local progress_max = termw + local progress_cur + if progress >= 2 then + if progress_max == 51 then + progress_cur = kernel.numRound(progress/1.96078431357) + else + kernel.panic("monitor has a invalid width that hasn't been programmed in (printColoredProgressTextLine in kerneldraw)") + return + end + else + progress_cur = progress + end + local txt_progress = nil + local txt_normal = nil + local progresstxt_tmp = nil + if progress_cur >= 1 then + if progress_cur >= string.len(txt)+1 then + txt_progress = txt + progresstxttmp = progress_cur-string.len(txt) + repeat + txt_progress = txt_progress .. " " + until progresstxttmp <= 0 + else + txt_progress = string.sub(txt,1,progress_cur) + txt_normal = string.sub(txt,progress_cur,string.len(txt)) + end + else + txt_progress = "" + txt_normal = txt + end + term.setCursorPos(1,1) + term.setBackgroundColor(colors.gray) + term.setTextColor(colors.white) + print("progress:" .. progress) + print("progress_max:" .. progress_max) + print("progress_cur:" .. progress_cur) + print("txt_progress:" .. txt_progress) + print("txt_normal:" .. txt_normal) + term.setCursorPos(1, y) + term.setBackgroundColor(bgn or colors.purple) + term.write(" ") + term.setCursorPos(1, y) + term.setTextColor(fgp or colors.black) + term.setBackgroundColor(bgp or colors.white) + print(txt_progress) + if not txt_progress == "" then + term.setCursorPos(string.len(txt_progress)+1,y) + end + term.setTextColor(fgn or colors.white) + term.setBackgroundColor(bgn or colors.purple) + print(txt_normal) + term.setBackgroundColor(colors.black) + term.setTextColor(colors.white) +end + +function printBootInfo(i) + term.setBackgroundColor(colors.black) + term.setTextColor(colors.lightGray) + write("") + term.setTextColor(colors.cyan) + write("*") + term.setTextColor(colors.lightGray) + write(" ") + print(" "..i) + term.setTextColor(colors.white) + if log then + log.writeBootLog(i) + end +end + +function printBootWarning(w) + term.setBackgroundColor(colors.black) + term.setTextColor(colors.lightGray) + write("") + term.setTextColor(colors.orange) + write("!") + term.setTextColor(colors.lightGray) + write(" ") + print(" "..w) + term.setTextColor(colors.white) + if log then + log.writeBootLog(w) + end +end + +function printBootSuccess(wa) + term.setBackgroundColor(colors.black) + term.setTextColor(colors.lightGray) + write("") + term.setTextColor(colors.lime) + write("v") + term.setTextColor(colors.lightGray) + write(" ") + print(" "..wa) + term.setTextColor(colors.white) + if log then + log.writeBootLog(wa) + end +end + +function printAppInfo(header, msg) + term.setTextColor(colors.cyan) + write(header..": ") + term.setTextColor(colors.lightGray) + print(msg) + term.setTextColor(colors.white) +end + +function printAppWarning(header, msg) + term.setTextColor(colors.red) + write(header..": ") + term.setTextColor(colors.orange) + print(msg) + term.setTextColor(colors.white) +end + +function printAppSuccess(header, msg) + term.setTextColor(colors.lime) + write(header..": ") + term.setTextColor(colors.green) + print(msg) + term.setTextColor(colors.white) +end + +function printKernelMessage(msg) + if kernel.printLog then + term.setTextColor(colors.blue) + write("freax: ") + term.setTextColor(colors.lightBlue) + print(msg) + term.setTextColor(colors.white) + end +end + +function printKernelAsking(msg) + if kernel.printLog then + term.setTextColor(colors.blue) + write("freax: ") + term.setTextColor(colors.lightBlue) + write(msg) + term.setTextColor(colors.white) + end +end + +function request(req) + while true do + term.setTextColor(colors.cyan) + write(req .. "? ") + term.setTextColor(colors.lightGray) + write("(") + term.setTextColor(colors.lime) + write("Y") + term.setTextColor(colors.lightGray) + write("/") + term.setTextColor(colors.red) + write("N") + term.setTextColor(colors.lightGray) + write(") ") + local presel=read() + if presel=="Y" or presel=="y" then + return true + elseif presel=="N" or presel=="n" then + return false + end + end +end + +function drawImg(path, x, y) + if fs.exists(path) and not fs.isDir(path) then + local img = paintutils.loadImage(path) + paintutils.drawImage(img, x, y) + else + printAppWarning("gui", "Image not found") + end +end + +function drawImgArg(img, x, y) + paintutils.drawImage(img, x, y) +end + +function printColored(text, color) + local tmp = term.getTextColor() + term.setTextColor(color) + print(text) + term.setTextColor(tmp) +end + +function bootScreen(enable,progress,logo,logo_small,txt) + if enable then + clearScreen(colors.black) + local check_size = termw + local logo_size = 38 + local logo_small_size = 20 + if check_size == logo_size then + drawImgArg(logo, 1,5) + elseif check_size >= logo_size then + drawImgArg(logo, (termw/2)-(logo_size/2)+1, 5) + elseif check_size == logo_small_size then + drawImgArg(logo_small, 1,5) + elseif check_size >= logo_small_size then + drawImgArg(logo_small, (termw/2)-(logo_small_size/2)+1, 5) + end + printColoredTextLine(termh-1, txt, true, colors.black, colors.white) + --printColoredProgressTextLine(termh-1, txt, progress, colors.black, colors.white, colors.purple, colors.white) + --printColoredProgressTextLine(termh-1, txt, progress) + end +end + +function bootDelay(delay) + local enableSleeps = true -- Disabled sleeps + local enableDisabledSleep = false -- Disables 0.1 sleep when disabled + if enableSleeps then + sleep(delay) + elseif enableDisabledSleep then + sleep(0.1) + end +end diff --git a/boot/ofbl b/boot/ofbl new file mode 100644 index 0000000..02f8be4 --- /dev/null +++ b/boot/ofbl @@ -0,0 +1,83 @@ +--rawset(_G, "nativepullevent", os.pullEvent) +os.pullEvent = os.pullEventRaw + +enableSleeps = true +function string2table(string_convert) + local table = {} + string_convert:sub(".",function(c) table.insert(table,c) end) + return table +end + +if not term.isColour() then + printError("ofbl: OFBL and FREAX only supports") + printError("ofbl: advanced (pocket) computers") + sleep(3) + os.shutdown() +end +print("ofbl: conf: Loading") +if enableSleeps then sleep(0.3) else sleep(0.1) end +local config = {} +local name = nil +local initrd = nil +local kernel = nil +local kerneldraw = nil +if fs.exists("/boot/ofbl.conf") then + settings.load("/boot/ofbl.conf") + --config.oslist = string2table(settings.get("oslist")) + name = settings.get("os0.name") + initrd = settings.get("os0.initrd") + kernel = settings.get("os0.kernel") + kerneldraw = settings.get("os0.kerneldraw") + if name == nil then + print("ofbl: conf: name is null") + return + end + if initrd == nil then + print("ofbl: conf: initrd is null") + return + end + if kernel == nil then + print("ofbl: conf: kernel is null") + return + end + if kerneldraw == nil then + print("ofbl: conf: kerneldraw is null") + return + end + else + print("ofbl: conf: Not found") + return +end +print("ofbl: Copying kernel and initrd.img into RAM") +if enableSleeps then sleep(0.1) else sleep(0.1) end +fs.delete("/var/boot") +if not fs.isDir("/var") and not fs.exists("/var/boot") and not fs.isDir("/var/boot") then + fs.makeDir("/var") + fs.makeDir("/var/boot") +end +if fs.exists(kernel) then + if fs.exists(kerneldraw) then + if fs.exists(initrd) then + fs.copy(initrd,"/var/boot/initrd") + fs.copy(kernel,"/var/boot/kernel") + fs.copy(kerneldraw,"/var/boot/kerneldraw") + else + print("ofbl: " .. initrd .. " not found") + return + end + else + print("ofbl: " .. kerneldraw .. " not found") + return + end +else + print("ofbl: " .. kernel .. " not found") + return +end +if enableSleeps then sleep(1.95) else sleep(0.1) end +print("ofbl: Loading kernel") +if enableSleeps then sleep(3.25) else sleep(0.1) end +os.loadAPI("/var/boot/kernel") +os.loadAPI("/var/boot/kerneldraw") +print("ofbl: Loading initrd.img") +if enableSleeps then sleep(2.65) else sleep(0.1) end +shell.execute(initrd) diff --git a/boot/ofbl.conf b/boot/ofbl.conf new file mode 100644 index 0000000..a581bf0 --- /dev/null +++ b/boot/ofbl.conf @@ -0,0 +1,7 @@ +{ + [ "oslist"] = "0", + [ "os0.name" ] = "FREAX", + [ "os0.initrd" ] = "/boot/freax/initrd.img", + [ "os0.kernel" ] = "/boot/freax/kernel.boot", + [ "os0.kerneldraw" ] = "/boot/freax/kerneldraw.boot", +} diff --git a/etc/autorun b/etc/autorun new file mode 100644 index 0000000..c7e78e9 --- /dev/null +++ b/etc/autorun @@ -0,0 +1,8 @@ +local oldPath = shell.path() +shell.setPath(":/bin:/sbin:/etc/script_util:/usr/bin:/usr/local/bin") + +local link = config.load("/etc/link.dat") + +for k,v in pairs(link) do + shell.setAlias(k, v) +end diff --git a/etc/errorLog b/etc/errorLog new file mode 100644 index 0000000..cb5b08f --- /dev/null +++ b/etc/errorLog @@ -0,0 +1,2 @@ +207d:19:39 | Cannot serialize type function +208d:6:38 | 1 diff --git a/etc/lib/config b/etc/lib/config new file mode 100644 index 0000000..ccd2a68 --- /dev/null +++ b/etc/lib/config @@ -0,0 +1,22 @@ +function load(path) + if fs.exists(path) and not fs.isDir(path) then + local lf = fs.open(path, "r") + local contents = lf.readAll() + lf.close() + return textutils.unserialize(contents) + else + return nil + end +end + +function save(cTable, path) + if fs.exists(path) then + if fs.isDir(path) then + return false + end + end + local sf = fs.open(path, "w") + sf.write(textutils.serialize(cTable)) + sf.close() + return true +end diff --git a/etc/lib/exception b/etc/lib/exception new file mode 100644 index 0000000..b7800a9 --- /dev/null +++ b/etc/lib/exception @@ -0,0 +1,26 @@ +local instances = {} + +instances.GenericException = function(text) + kerneldraw.printAppWarning(fs.getName(shell.getRunningProgram()), text) + log.writeSecurity(text) +end + +instances.RestrictedOpsException = function(a) + b = a or " " + kerneldraw.printAppWarning("kernel", "Attempt to perform restricted operations") + log.writeSecurity("Attempt to perform restricted operations") + log.writeSecurity("Attempt blocked.") +end + +instances.SecurityException = function(path) + kerneldraw.printAppWarning("kernel", "Attempt to perform restricted operations") + log.writeSecurity(_activeUser.." Attempted to open restricted file at "..path) +end + +function throw(exception, arg) + if instances[exception] then + instances[exception](arg) + else + instances.GenericException("Exception not found") + end +end diff --git a/etc/lib/log b/etc/lib/log new file mode 100644 index 0000000..c21edda --- /dev/null +++ b/etc/lib/log @@ -0,0 +1,8 @@ +-- this is a compatibility file for supporting kernel-mode logging + +writeSyslog = kernel.writeSyslog +writeMessage = kernel.writeMessage +writeSecurity = kernel.writeSecurity +writeShutdown = kernel.writeShutdown +writeAuth = kernel.writeAuth +writeBootLog = kernel.writeBootLog diff --git a/etc/lib/luaex b/etc/lib/luaex new file mode 100644 index 0000000..d85e99e --- /dev/null +++ b/etc/lib/luaex @@ -0,0 +1,31 @@ +function stringToByteArray(str) + return string.byte(str, 1, str:len()) +end + +function argsToTable(...) + return { ... } +end + +function stringFromByteArray(...) + return string.char(...) +end + +function iterateFileLines(path) + local handle = fs.open(path, "r") + local commands = {} + local line = handle.readLine() + while line do + commands[#commands + 1] = line + line = handle.readLine() + end + handle.close() + return commands +end + +local function string_separate(str) + local t = {} + for i in string.gmatch(str, "[A-z]") do + table.insert(t, i) + end + return t +end diff --git a/etc/lib/man b/etc/lib/man new file mode 100644 index 0000000..7dc1fed --- /dev/null +++ b/etc/lib/man @@ -0,0 +1,12 @@ +function findManual(name) + local flist = fs.list("/etc/manuals") + for _, file in ipairs(flist) do + if not fs.isDir("/etc/manuals/"..file) and file == name then + local handle = fs.open("/etc/manuals/"..file, "r") + local contents = handle.readAll() + handle.close() + return contents + end + end + return "No manual available" +end diff --git a/etc/lib/net b/etc/lib/net new file mode 100644 index 0000000..97586d4 --- /dev/null +++ b/etc/lib/net @@ -0,0 +1,70 @@ +local isOnline = false + +function getHTTP(url) + local handlex = http.get(url) + local contents = handlex.readAll() + handlex.close() + return contents +end + +function openModems() + for n,sModem in ipairs( peripheral.getNames() ) do + if peripheral.getType( sModem ) == "modem" then + if not rednet.isOpen( sModem ) then + rednet.open( sModem ) + end + end + end +end + +function closeModems() + for n,sModem in ipairs( peripheral.getNames() ) do + if peripheral.getType( sModem ) == "modem" then + if rednet.isOpen( sModem ) then + rednet.close( sModem ) + end + end + end +end + +function getModemStatus(side) + kerneldraw.printAppInfo("net", side .. " modem status: " .. tostring(rednet.isOpen(side))) + return rednet.isOpen(side) +end + +-- DNS self-hosting functions + +function setWorkstationStatus(state) + if state == true and isOnline == false then + local label = os.getComputerLabel() or kernel.getOS() + rednet.host("freax_dns", tostring(os.getComputerID()).."_"..label) + isOnline = true + log.writeSyslog("Now susceptible for DNS lookup") + log.writeMessage("Assigned DNS label "..tostring(os.getComputerID()).."_"..label) + elseif state == false and isOnline == true then + local label = os.getComputerLabel or kernel.getOS() + rednet.host("freax_dns", tostring(os.getComputerID()).."_"..label) + isOnline = false + log.writeSyslog("No longer susceptible for DNS lookup") + log.writeMessage("De-assigned DNS label "..tostring(os.getComputerID()).."_"..label) + elseif state == true and isOnline == true then + kerneldraw.printAppWarning("net", "workstation already online") + elseif state == false and isOnline == false then + kerneldraw.printAppWarning("net", "workstation already offline") + else + log.writeSyslog("RedNet interface error") + return nil, "Error in RNI, please consult developer" + end +end + +function getWorkstationStatus() + return isOnline +end + +function findWorkstation(query) + if rednet.lookup("freax_dns", query) ~= nil then + return true, rednet.lookup("freax_dns", query) + else + return false, nil + end +end diff --git a/etc/lib/search b/etc/lib/search new file mode 100644 index 0000000..e21444d --- /dev/null +++ b/etc/lib/search @@ -0,0 +1,53 @@ +function traverseKey(array, text) + for k,v in pairs(array) do + if v == text then + return true, k + end + end + return false, nil +end + +function traverseValue(array, text) + for k,v in pairs(array) do + if k == text then + return true, v + end + end + return false, nil +end + +function findKey(array, text) + for k,v in pairs(array) do + if k == text then + return true, k + end + end + return false, nil +end + +function findValue(array, text) + for k,v in pairs(array) do + if v == text then + return true, v + end + end + return false, nil +end + +function queryForKey(array, text) + for k,v in pairs(array) do + if string.find(k, text) then + return true, k + end + end + return false, nil +end + +function queryForValue(array, text) + for k,v in pairs(array) do + if string.find(v, text) then + return true, v + end + end + return false, nil +end diff --git a/etc/lib/security b/etc/lib/security new file mode 100644 index 0000000..b8204af --- /dev/null +++ b/etc/lib/security @@ -0,0 +1,42 @@ +local _IDENTIFIER = "ac30527de6170b7d545bdc152f76237276537f2cf8eb7fe5bdb2a7627f48cec1" + +function getSU() + if fs.exists("/etc/passwd/.shadow/".._G["_activeUser"]..".usr") then + local ud = dofile("/etc/passwd/.shadow/".._G["_activeUser"]..".usr") + return ud.SU + else + return false + end +end + +function getID() + return _IDENTIFIER +end + +function getActiveUserStatus() + if fs.exists("/etc/passwd/.shadow/".._G["_activeUser"]..".usr") then + local ud = dofile("/etc/passwd/.shadow/".._G["_activeUser"]..".usr") + if ud.ID == getID() then + return true + end + return false + else + return false + end +end + +function passbyte(username, str) + passTable = luaex.argsToTable(luaex.stringToByteArray(str)) + if config.save(passTable, "/etc/passwd/"..username..".dat") then + return true + else + return false + end +end + +function unpassbyte(username) + passTable = config.load("/etc/passwd/"..username..".dat") + if passTable ~= nil then + return luaex.stringFromByteArray(unpack(passTable)) + end +end diff --git a/etc/lib/tpm b/etc/lib/tpm new file mode 100644 index 0000000..27a9185 --- /dev/null +++ b/etc/lib/tpm @@ -0,0 +1,25 @@ +function getPaste(file, id, overwrite) + if overwrite and fs.exists(file) and string.find(file, "system/") then + if fs.exists("/etc/.backups/"..file) then + fs.delete("/etc/.backups/"..file) + end + fs.copy(file, "/etc/.backups/"..file) + end + return kernel.getFile(file, "http://pastebin.com/raw/"..id, overwrite) +end + +function downloadAPI(filename, id) + return getPaste("/usr/local/lib/"..filename, id, true) +end + +function downloadBinary(filename, id) + return getPaste("/usr/local/bin/"..filename, id, true) +end + +function toPackageAPI(filename) + return "/usr/local/lib/"..filename +end + +function toPackageBinary(filename) + return "/usr/local/bin/"..filename +end diff --git a/etc/link.dat b/etc/link.dat new file mode 100644 index 0000000..b078446 --- /dev/null +++ b/etc/link.dat @@ -0,0 +1 @@ +{["lua"]="/bin/lsh",["username"]="/bin/uname --user",["whoami"]="/bin/uname --user",["help"]="/bin/man",["shell"]="/bin/sh",["sh"]="/bin/sh",["bash"]="/bin/sh",["cash"]="/bin/sh",["shutdown"]="/sbin/shutdown",["reboot"]="/sbin/reboot",["mv"]="/bin/move",["cp"]="/bin/cp",} diff --git a/etc/manuals/api-config b/etc/manuals/api-config new file mode 100644 index 0000000..91390af --- /dev/null +++ b/etc/manuals/api-config @@ -0,0 +1,10 @@ + +The Configuration API deals with configuration +files: it loads and saves tables into files. + +It has two functions: +config.load(path) +config.save(tableToSave, path) + +Configuration files are usually marked by the +'.conf' extension. diff --git a/etc/manuals/api-custom b/etc/manuals/api-custom new file mode 100644 index 0000000..8cb92d4 --- /dev/null +++ b/etc/manuals/api-custom @@ -0,0 +1,4 @@ + +The 'Custom API' is used to add functions and +values to the runtime environment without much +hard work ('man custom-api'). diff --git a/etc/manuals/api-gui b/etc/manuals/api-gui new file mode 100644 index 0000000..8b0fec1 --- /dev/null +++ b/etc/manuals/api-gui @@ -0,0 +1,17 @@ + +The Graphical User Interface API is responsible +for all the colorful text and interface elements +of FREAX and has the following functions: +button(txt, x1, x2, y) +counter(txt, y) +drawProgress(txt, y) / drawProgressAlt() +printBootInfo(i) +printBootWarning(w) +printBootSuccess(s) +printAppInfo(header, msg) +printAppWarning(header, msg) +printAppSuccess(header, msg) +request(text) +drawImg(path, x. y) +printColoredTextLine(y, txt, bg, fg) +clearScreen(bgColor) diff --git a/etc/manuals/api-kernel b/etc/manuals/api-kernel new file mode 100644 index 0000000..8a88a64 --- /dev/null +++ b/etc/manuals/api-kernel @@ -0,0 +1,25 @@ + +The Kernel API is the one that backs the whole +Operating System. It provides wrappers for some +in-built functions, and is responsible for +error report generation. + +Functions in the Kernel API: +credits() +getName() +getRelease() +getVersion() +getMachine() +getProcessorArchitecture() +getHardwarePlatform() +getOS() +clear() +panic(text) +import(file) (A safe os.loadAPI) +require(file) (A primitive require function) +APIHook(api) +printFile(file) +secureInput(invite, definition) +shutdown(removeVar) / reboot(removeVar) +findManual(name) (Find a manual file path) +The Logging API ('man api-log') functions \ No newline at end of file diff --git a/etc/manuals/api-log b/etc/manuals/api-log new file mode 100644 index 0000000..d50466b --- /dev/null +++ b/etc/manuals/api-log @@ -0,0 +1,12 @@ + +The Logging API serves as a log file +handler. +Logs are viewed through dmesg ('man dmesg') + +Functions with corresponding files: +writeAuth(msg) -> /var/log/auth +writeBootLog(msg) -> /var/log/boot +writeMessage(msg) -> /var/log/messages +writeSyslog(msg) -> /var/log/syslog +writeSecurity(msg) -> /var/log/security +writeShutdown(msg) -> /var/log/shutdown diff --git a/etc/manuals/api-luaex b/etc/manuals/api-luaex new file mode 100644 index 0000000..99f8ad2 --- /dev/null +++ b/etc/manuals/api-luaex @@ -0,0 +1,8 @@ + +The LuaEx API is used to add new miscellaneous +features to the runtime environment. + +Functions: +stringToByteArray(str) +argsToTable(...) +stringFromByteArray(byteArrayTable) diff --git a/etc/manuals/api-search b/etc/manuals/api-search new file mode 100644 index 0000000..b2f1770 --- /dev/null +++ b/etc/manuals/api-search @@ -0,0 +1,9 @@ + +The Search API simplifies searching in key-value +arrays. + +Functions: +traverseKey(table, text) +traverseValue(table, text) +findKey(table, text) +findValue(table, text) \ No newline at end of file diff --git a/etc/manuals/api-security b/etc/manuals/api-security new file mode 100644 index 0000000..ce25e89 --- /dev/null +++ b/etc/manuals/api-security @@ -0,0 +1,9 @@ + +The Security API serves as a credential handler, +and is responsible for user creation and +identification. + +Functions: +passbyte(username, password) +unpassbyte(username) +getSU() diff --git a/etc/manuals/api-tpm b/etc/manuals/api-tpm new file mode 100644 index 0000000..93ac4ab --- /dev/null +++ b/etc/manuals/api-tpm @@ -0,0 +1,8 @@ + +The FREAX Package Management API is used to +simplify Pastebin handling and file downloads. + +Functions: +getPaste(file, id, overwrite) +downloadAPI(file, id) / downloadBinary(file, id) +toPackageAPI(filename) / toPackageBinary(filename) diff --git a/etc/manuals/archiver b/etc/manuals/archiver new file mode 100644 index 0000000..0047aae --- /dev/null +++ b/etc/manuals/archiver @@ -0,0 +1,17 @@ + +archiver <...> [...] + +Required: mode (zip/unzip) + directory to zip/archive to unzip + archive file name/path to unzip +Modes: zip (creates an archive) + unzip (unpacks an archive) + +If using the zip mode you can specify two +directories the archiver will skip after +the required parameters. + +(c) 1Ridav. +This is the only third-party program that +FREAX is using. It is, however, +not a required part of the system. diff --git a/etc/manuals/auth b/etc/manuals/auth new file mode 100644 index 0000000..c3555dc --- /dev/null +++ b/etc/manuals/auth @@ -0,0 +1,11 @@ + +The authorization system is one of the FREAX +security measures. + +Credentials are handled with optimal security. + +Each user has a home directory (/home/) +and a credentials identifier used to determine +the user's privileges. + +See 'man usrutils' for account management programs. diff --git a/etc/manuals/boot-sequence b/etc/manuals/boot-sequence new file mode 100644 index 0000000..b50509f --- /dev/null +++ b/etc/manuals/boot-sequence @@ -0,0 +1,18 @@ + +The FREAX Boot Sequence consists of 4 steps: +(OFBL stands for Open FREAX Boot Loader) + + 1 - The boot sequence is initiated. + OFBL is launched and APIs + are loaded into the global environment. + + 2 - The boot loader handles control to the + preferred bootloader record that executes + high-level routines (More: 'man custom-boot'). + +Assuming that the user prefers the default record: + + 3 - Autorun files are handled, and all the code + inside of them is executed. + + 4 - The FREAX shell is launched. diff --git a/etc/manuals/categories b/etc/manuals/categories new file mode 100644 index 0000000..9018810 --- /dev/null +++ b/etc/manuals/categories @@ -0,0 +1,7 @@ + +Some manuals belong to categories. + +Here they are: + + api - FREAX APIs + custom - customization diff --git a/etc/manuals/changelog b/etc/manuals/changelog new file mode 100644 index 0000000..e6eb79d --- /dev/null +++ b/etc/manuals/changelog @@ -0,0 +1,15 @@ + +Build d0.0.1.0 | DEVELOP changelog: + +! Fixed bugs ! ++ Added new kernel calls +* Renamed references from Tesseract + to FREAX +* Combined sysinfo and uname +* Updated uname to include new + kernel calls +* Modified boot logo +* Updated passwd +* Updated many man pages +* Updated messages +- Removed old uname and sysinfo diff --git a/etc/manuals/config b/etc/manuals/config new file mode 100644 index 0000000..b6e81ed --- /dev/null +++ b/etc/manuals/config @@ -0,0 +1,7 @@ + +FREAX Configuration Files are handled by the +Configuration API (man api-config). They are used +to write easily accessible data to disk and access +it when necessary. + +Actually, they are just serialized Lua tables. diff --git a/etc/manuals/convert b/etc/manuals/convert new file mode 100644 index 0000000..d562f70 --- /dev/null +++ b/etc/manuals/convert @@ -0,0 +1,6 @@ + +convert + +Converts an API-like configuration file to a +serializable .conf/.dat file. Does not assign +extension automatically. diff --git a/etc/manuals/coreutils b/etc/manuals/coreutils new file mode 100644 index 0000000..c07cb5b --- /dev/null +++ b/etc/manuals/coreutils @@ -0,0 +1,15 @@ + +As the GNU/Linux systems, FREAX also has its +own coreutils package. + + cat - prints out a file + credits - prints out credits + dmesg (...) - displays logs ('man dmesg') + df - displays filesystem stats ('man df') + man - displays help ('man man') + scp - copies a file to /home + shell - FREAX shell + shutdown - performs shutdown + uname (...) - shows stats ('man uname') + tty - displays terminal size + \ No newline at end of file diff --git a/etc/manuals/credits b/etc/manuals/credits new file mode 100644 index 0000000..813575b --- /dev/null +++ b/etc/manuals/credits @@ -0,0 +1,16 @@ +FREAX Operating System +Made by + - JeremyStarTM (Maintainer) + +Licensed under GNU GPLv3 + + +Thanks to the Tesseract devs! +FREAX is a fork of Tesseract. +Tesseract developers: + - minebuild02 (Head Developer) + - MasatoBaito (Assistant Developer) + +Special thanks to: + - 1Ridav (Developer of the DCC Archiver) + - Luca_S (Developer of the script parser base) diff --git a/etc/manuals/custom b/etc/manuals/custom new file mode 100644 index 0000000..d4fca18 --- /dev/null +++ b/etc/manuals/custom @@ -0,0 +1,8 @@ + +'custom-boot': Customizing the boot sequence + +'custom-build': Building your own Tesseract OS + +'custom-tpm': Creating your own package.dat + +'custom-script': Automating tasks diff --git a/etc/manuals/custom-autorun b/etc/manuals/custom-autorun new file mode 100644 index 0000000..d725b11 --- /dev/null +++ b/etc/manuals/custom-autorun @@ -0,0 +1,11 @@ + +User autorun files can be used to perform various +boot actions while not altering any boot sequence +mechanisms. + +This should not be confused with the system autorun +file ('/system/autorun'), that is used to perform +post-boot initialization actions. + +User autorun files are located in +/home//autorun diff --git a/etc/manuals/custom-boot b/etc/manuals/custom-boot new file mode 100644 index 0000000..88970c4 --- /dev/null +++ b/etc/manuals/custom-boot @@ -0,0 +1,16 @@ + +You can customize the boot sequence of FREAX +in two different ways: + + 1. Writing a user autorun file (/home/autorun) + + User autorun files are good if you're not + too much of a professional. You can load + extra APIs or perform other actions. + More about user autorun files can be found + at 'man custom-autorun'. + + 2. Writing a custom bootloader + + Information about custom bootloaders can + be found at 'man custom-bootloaders' diff --git a/etc/manuals/custom-bootloaders b/etc/manuals/custom-bootloaders new file mode 100644 index 0000000..0701ef8 --- /dev/null +++ b/etc/manuals/custom-bootloaders @@ -0,0 +1,11 @@ + +In FREAX, bootloader records are files that +regulate the boot order of the operating system, +responsible for the low-level system routines. + +Writing a bootloader record requires a lot of +experience in Lua programming. + +FREAX's standard bootloader record is located +in '/boot/loaders/ofbl-splash'. It is strongly +recommended not to touch it. diff --git a/etc/manuals/custom-build b/etc/manuals/custom-build new file mode 100644 index 0000000..d7e43d5 --- /dev/null +++ b/etc/manuals/custom-build @@ -0,0 +1,17 @@ + +You can build your own FREAX distribution, +assuming that you have the system requirements: + - 1Ridav's Archiver must be present on your + system and the receiver's system + +Run these commands in the shell: +archiver zip / rom +mv .arch + +To install, run this command: + unzip .arch / + +Alternatively, you can download the +'freax-buildtools' package and type 'buildtool' +in the console - it will automatically build a +system image. diff --git a/etc/manuals/custom-script b/etc/manuals/custom-script new file mode 100644 index 0000000..c7542fa --- /dev/null +++ b/etc/manuals/custom-script @@ -0,0 +1,14 @@ + +A FREAX script file is actually a bunch of +strings representing commands and executables. + +An example script is provided at the path +'/system/demos/script.tsf'. Note that the +header is VERY necessary, as without it the +script parser will not be able to recognize +the code. + +Useful commands: +@/scomment - comment out strings +sdelay - delay execution for set time +secho [text] - output text/newline to console diff --git a/etc/manuals/custom-tpm b/etc/manuals/custom-tpm new file mode 100644 index 0000000..e3a7a69 --- /dev/null +++ b/etc/manuals/custom-tpm @@ -0,0 +1,17 @@ + +Before Warp 5, building your own package.dat file +wasn't that hard, but with the new update bringing +in dependency handling, the format has changed. +Basically, the package.dat file is a complex table: +{ + package_name = { -- The root structure ID + apis = { -- Files put in /usr/api + apione = "paste123", + apitwo = "pastebin", + }, + bins = { -- Files put in /usr/bin + binone = "pasted00", + }, + name = "simplepackage", -- Package ID (equal to + }, package_name) +} (or ...) diff --git a/etc/manuals/demos b/etc/manuals/demos new file mode 100644 index 0000000..9a2a846 --- /dev/null +++ b/etc/manuals/demos @@ -0,0 +1,15 @@ + +Demonstration, or fake programs are used to +demonstrate various functions of Lua, CC APIs +and FREAX itself. Most of the Demonstration +programs are part of Project Hacknet. + +hips-latest: Counting down with timers +nmap: Fake network filesystem; + App message display +winsetup: GUI testing + +nmap is a very special program because it was +designed to be the core of Project Hacknet, +the program that creates a fake network. It has +its own manual page - see 'man nmap' diff --git a/etc/manuals/df b/etc/manuals/df new file mode 100644 index 0000000..b9be13d --- /dev/null +++ b/etc/manuals/df @@ -0,0 +1,12 @@ + +df + +Displays the current filesystem information. + +Without the size specified, the program only +displays the filesystem type. + +Size can be specified in bytes (-sB) or kilobytes +(-sKB). With the size specified, the program also +displays the current directory's size and free +space. diff --git a/etc/manuals/dmesg b/etc/manuals/dmesg new file mode 100644 index 0000000..c302d84 --- /dev/null +++ b/etc/manuals/dmesg @@ -0,0 +1,5 @@ + +dmesg + +Displays log files. -l/--list displays a list of +them. diff --git a/etc/manuals/extensions b/etc/manuals/extensions new file mode 100644 index 0000000..88a5c08 --- /dev/null +++ b/etc/manuals/extensions @@ -0,0 +1,11 @@ + +Here is the extension list: + +.arch - DCC Archive ('man archiver') +.conf - FREAX Configuration File ('man config') +.dat - Data file, contains unserializable data +.tsf - FREAX Script File ('man script') +.usr - FREAX User Data + +Some of them may be used elsewhere for other +purposes, please be aware of that. diff --git a/etc/manuals/firststeps b/etc/manuals/firststeps new file mode 100644 index 0000000..158b99c --- /dev/null +++ b/etc/manuals/firststeps @@ -0,0 +1,13 @@ + +Welcome to FREAX, the open operating system +based on Tesseract and inspired by Linux. + +The FREAX system is complicated to a non-UNIX +user, but can be understood quite easily by +learning or by being a UNIX user! To get help +when you're stuck, just execute 'man ' + +To view information about FREAX, execute +'uname -a' or 'about'. + +- JeremyStarTM, Founder/Maintainer of FREAX diff --git a/etc/manuals/man b/etc/manuals/man new file mode 100644 index 0000000..0867900 --- /dev/null +++ b/etc/manuals/man @@ -0,0 +1,11 @@ + +man | manpages + +The Manual Browser (man) is the FREAX's help +program. It is modeled after Debian's man, +the documentation browser. + +Typing manpages in the console displays a list +of the available manual pages. + +Also, you are viewing a man about man inside a man. diff --git a/etc/manuals/manedit b/etc/manuals/manedit new file mode 100644 index 0000000..ab85261 --- /dev/null +++ b/etc/manuals/manedit @@ -0,0 +1,7 @@ + +manedit + +manedit is an alias for the built-in editor +program that is used for editing manual pages. + +It is actually the SAME program as 'edit'. diff --git a/etc/manuals/nmap b/etc/manuals/nmap new file mode 100644 index 0000000..f6993bc --- /dev/null +++ b/etc/manuals/nmap @@ -0,0 +1,10 @@ + +nmap [digits 1,2,3,4] [DNS record] + +Scans the network for terminals with open ports. +If digits 1-4 are specified, scans for the +corresponding IP address. +If a DNS record is specified, uses it to register +the terminal. + +Be advised - it is a demonstration program. diff --git a/etc/manuals/script b/etc/manuals/script new file mode 100644 index 0000000..d6fe7bf --- /dev/null +++ b/etc/manuals/script @@ -0,0 +1,9 @@ + +script + +Launches a FREAX script file. + +See 'man custom-script' for the specification. +Scripts respect local security, and will launch +specified applications that may error when not +provided with superuser privileges. diff --git a/etc/manuals/shell b/etc/manuals/shell new file mode 100644 index 0000000..769b6c0 --- /dev/null +++ b/etc/manuals/shell @@ -0,0 +1,11 @@ + +shell [program] + +The FREAX shell doesn't differ from the +built-in one much. What it offers is a color +scheme and username display. Filesystem type +is now displayed by the df utility ('man df'). + +Also, a new function was added to the Shell API - +shell.executeScript(path), which executes a script +on the provided path ('man script') diff --git a/etc/manuals/shutdown b/etc/manuals/shutdown new file mode 100644 index 0000000..82351d5 --- /dev/null +++ b/etc/manuals/shutdown @@ -0,0 +1,12 @@ + +shutdown [params] + +Executes shutdown procedure. + +Parameters: +-r/--reboot: Restart instead of shutting down +-i/--immediate: No software callbacks, halt NOW +-d/--delete-temp: Remove temporary files + +By default, keeps temporary files and executes +shutdown with software callbacks. diff --git a/etc/manuals/superuser b/etc/manuals/superuser new file mode 100644 index 0000000..3467abe --- /dev/null +++ b/etc/manuals/superuser @@ -0,0 +1,13 @@ + + Warp 4 added Linux-like superuser capabilities. + + Superuser privileges are by default granted to + the 'root' user. + + Superuser privileges are REQUIRED to: + + Add/change/remove user records; + Install TPM packages/complex applications; + Edit manual pages; + Edit boot records; + Edit system files diff --git a/etc/manuals/tpm b/etc/manuals/tpm new file mode 100644 index 0000000..6593643 --- /dev/null +++ b/etc/manuals/tpm @@ -0,0 +1,17 @@ + +The Tesseract Package Manager is similar to +Linux-based operating systems' package managers. + +The repository data is stored in the +'/system/data/package.dat' file. + +Programs that compose the TPM are: + +tpm-install : Install a package +tpm-list : List available packages +tpm-recache: Redownload package.dat +tpm-remove : Remove a package + +The tpm-list utility accepts a listing type as a +parameter. You can list available packages +('available') or installed ones ('installed'). diff --git a/etc/manuals/uname b/etc/manuals/uname new file mode 100644 index 0000000..2ceb91e --- /dev/null +++ b/etc/manuals/uname @@ -0,0 +1,17 @@ + +uname + +Displays system information. + +Possible arguments: +--kernel-name |-s - Kernel name +--nodename |-n - Hostname +--kernel-release |-r - Kernel release +--kernel-version |-v - Kernel version +--machine |-m - Machinetype +--processor |-p - Processor architecture +--hardware-platform|-i - Hardware platform +--operating-system |-o - Operating system +--path |-b - PATH variable +--id |-c - Machine ID +--user |-u - Active user diff --git a/etc/manuals/usrutils b/etc/manuals/usrutils new file mode 100644 index 0000000..26ddc68 --- /dev/null +++ b/etc/manuals/usrutils @@ -0,0 +1,10 @@ + + useradd + Adds a new user to the system. + + passwd + Changes a user's password. + + userdel + Remove a user from the system. + \ No newline at end of file diff --git a/etc/manuals/versioning b/etc/manuals/versioning new file mode 100644 index 0000000..53214fe --- /dev/null +++ b/etc/manuals/versioning @@ -0,0 +1,13 @@ + +Meanings: + => Majorversion + => Minorversion +

=> Patchlevel + => Developmentbuild + +Development build candidate + + d..

. + +Release build candidate + r..

diff --git a/etc/manuals/versions b/etc/manuals/versions new file mode 100644 index 0000000..0ded38b --- /dev/null +++ b/etc/manuals/versions @@ -0,0 +1,2 @@ + +d0.0.1.1 diff --git a/etc/passwd/.shadow/lsh.usr b/etc/passwd/.shadow/lsh.usr new file mode 100644 index 0000000..d7e3432 --- /dev/null +++ b/etc/passwd/.shadow/lsh.usr @@ -0,0 +1,7 @@ +local userdata = {} + +userdata.SU = false +userdata.ID = security.getID() +userdata.shell = "/bin/lsh" + +return userdata diff --git a/etc/passwd/.shadow/root.usr b/etc/passwd/.shadow/root.usr new file mode 100644 index 0000000..0b004c0 --- /dev/null +++ b/etc/passwd/.shadow/root.usr @@ -0,0 +1,7 @@ +local userdata = {} + +userdata.SU = true +userdata.ID = "0" +userdata.shell = "/bin/sh" + +return userdata diff --git a/etc/passwd/lsh.dat b/etc/passwd/lsh.dat new file mode 100644 index 0000000..41c7884 --- /dev/null +++ b/etc/passwd/lsh.dat @@ -0,0 +1,6 @@ +{ + 116, + 111, + 111, + 114, +} diff --git a/etc/passwd/root.dat b/etc/passwd/root.dat new file mode 100644 index 0000000..dc47935 --- /dev/null +++ b/etc/passwd/root.dat @@ -0,0 +1,6 @@ +{ + 114, + 111, + 111, + 116, +} diff --git a/etc/passwd/user.dat b/etc/passwd/user.dat new file mode 100644 index 0000000..257b629 --- /dev/null +++ b/etc/passwd/user.dat @@ -0,0 +1,6 @@ +{ + 117, + 115, + 101, + 114, +} diff --git a/etc/script_util/@ b/etc/script_util/@ new file mode 100644 index 0000000..a7355a7 --- /dev/null +++ b/etc/script_util/@ @@ -0,0 +1 @@ +-- script comment exec diff --git a/etc/script_util/error b/etc/script_util/error new file mode 100644 index 0000000..17b85ab --- /dev/null +++ b/etc/script_util/error @@ -0,0 +1,12 @@ +local logger = fs.open("/etc/errorLog", "a") +local tArgs = {...} +local logstr = tostring(os.day()).."d:"..textutils.formatTime(os.time(), true).." | " +for k,v in ipairs(tArgs) do + if k==1 then + logstr = logstr..v + else + logstr = logstr.." "..v + end +end +logger.writeLine(logstr) +logger.close() diff --git a/etc/script_util/scomment b/etc/script_util/scomment new file mode 100644 index 0000000..a7355a7 --- /dev/null +++ b/etc/script_util/scomment @@ -0,0 +1 @@ +-- script comment exec diff --git a/etc/script_util/sdelay b/etc/script_util/sdelay new file mode 100644 index 0000000..f100ad6 --- /dev/null +++ b/etc/script_util/sdelay @@ -0,0 +1,7 @@ +local tArgs = {...} + +if #tArgs >= 1 then + sleep(tonumber(tArgs[1])) +else + sleep(1) +end diff --git a/etc/script_util/secho b/etc/script_util/secho new file mode 100644 index 0000000..07a4c01 --- /dev/null +++ b/etc/script_util/secho @@ -0,0 +1,9 @@ +local tArgs = {...} +for k,v in ipairs(tArgs) do + if k==1 then + write(tostring(v)) + else + write(" "..tostring(v)) + end +end +write("\n") diff --git a/etc/script_util/slog_auth b/etc/script_util/slog_auth new file mode 100644 index 0000000..78bbf7d --- /dev/null +++ b/etc/script_util/slog_auth @@ -0,0 +1,16 @@ +if not fs.isDir("/var") and not fs.exists("/var/log") and not fs.isDir("/var/log") then + fs.makeDir("/var") + fs.makeDir("/var/log") +end +local logger = fs.open("/var/log/auth", "a") +local tArgs = {...} +local logstr = tostring(os.day()).."d:"..textutils.formatTime(os.time(), true).." | " +for k,v in ipairs(tArgs) do + if k==1 then + logstr = logstr..v + else + logstr = logstr.." "..v + end +end +logger.writeLine(logstr) +logger.close() diff --git a/etc/script_util/slog_boot b/etc/script_util/slog_boot new file mode 100644 index 0000000..1ae935d --- /dev/null +++ b/etc/script_util/slog_boot @@ -0,0 +1,16 @@ +if not fs.isDir("/var") and not fs.exists("/var/log") and not fs.isDir("/var/log") then + fs.makeDir("/var") + fs.makeDir("/var/log") +end +local logger = fs.open("/var/log/boot", "a") +local tArgs = {...} +local logstr = tostring(os.day()).."d:"..textutils.formatTime(os.time(), true).." | " +for k,v in ipairs(tArgs) do + if k==1 then + logstr = logstr..v + else + logstr = logstr.." "..v + end +end +logger.writeLine(logstr) +logger.close() diff --git a/etc/script_util/slog_message b/etc/script_util/slog_message new file mode 100644 index 0000000..1abcbb3 --- /dev/null +++ b/etc/script_util/slog_message @@ -0,0 +1,16 @@ +if not fs.isDir("/var") and not fs.exists("/var/log") and not fs.isDir("/var/log") then + fs.makeDir("/var") + fs.makeDir("/var/log") +end +local logger = fs.open("/var/log/messages", "a") +local tArgs = {...} +local logstr = tostring(os.day()).."d:"..textutils.formatTime(os.time(), true).." | " +for k,v in ipairs(tArgs) do + if k==1 then + logstr = logstr..v + else + logstr = logstr.." "..v + end +end +logger.writeLine(logstr) +logger.close() diff --git a/etc/script_util/slog_security b/etc/script_util/slog_security new file mode 100644 index 0000000..568aa09 --- /dev/null +++ b/etc/script_util/slog_security @@ -0,0 +1,16 @@ +if not fs.isDir("/var") and not fs.exists("/var/log") and not fs.isDir("/var/log") then + fs.makeDir("/var") + fs.makeDir("/var/log") +end +local logger = fs.open("/var/log/security", "a") +local tArgs = {...} +local logstr = tostring(os.day()).."d:"..textutils.formatTime(os.time(), true).." | " +for k,v in ipairs(tArgs) do + if k==1 then + logstr = logstr..v + else + logstr = logstr.." "..v + end +end +logger.writeLine(logstr) +logger.close() diff --git a/etc/script_util/slog_shutdown b/etc/script_util/slog_shutdown new file mode 100644 index 0000000..e6b7a6a --- /dev/null +++ b/etc/script_util/slog_shutdown @@ -0,0 +1,16 @@ +if not fs.isDir("/var") and not fs.exists("/var/log") and not fs.isDir("/var/log") then + fs.makeDir("/var") + fs.makeDir("/var/log") +end +local logger = fs.open("/var/log/shutdown", "a") +local tArgs = {...} +local logstr = tostring(os.day()).."d:"..textutils.formatTime(os.time(), true).." | " +for k,v in ipairs(tArgs) do + if k==1 then + logstr = logstr..v + else + logstr = logstr.." "..v + end +end +logger.writeLine(logstr) +logger.close() \ No newline at end of file diff --git a/etc/script_util/slog_syslog b/etc/script_util/slog_syslog new file mode 100644 index 0000000..c7174e2 --- /dev/null +++ b/etc/script_util/slog_syslog @@ -0,0 +1,16 @@ +if not fs.isDir("/var") and not fs.exists("/var/log") and not fs.isDir("/var/log") then + fs.makeDir("/var") + fs.makeDir("/var/log") +end +local logger = fs.open("/var/log/syslog", "a") +local tArgs = {...} +local logstr = tostring(os.day()).."d:"..textutils.formatTime(os.time(), true).." | " +for k,v in ipairs(tArgs) do + if k==1 then + logstr = logstr..v + else + logstr = logstr.." "..v + end +end +logger.writeLine(logstr) +logger.close() diff --git a/etc/scripts/tpm-recache.tsf b/etc/scripts/tpm-recache.tsf new file mode 100644 index 0000000..bbad7ab --- /dev/null +++ b/etc/scripts/tpm-recache.tsf @@ -0,0 +1,5 @@ +@ @ !! FREAX SCRIPT HEADER +@ This script performs recaching of TPM repo data +error local TPM repository data corrupt +slog_syslog reinitializing local TPM data +tpm-repair diff --git a/etc/system b/etc/system new file mode 100644 index 0000000..cc69201 --- /dev/null +++ b/etc/system @@ -0,0 +1,3 @@ +enableComplexApps = true +enablePackageManagement = true +enableHTTPAccess = true diff --git a/etc/tpm/package.dat b/etc/tpm/package.dat new file mode 100644 index 0000000..6d884d7 --- /dev/null +++ b/etc/tpm/package.dat @@ -0,0 +1,28 @@ +{ + ["freax-buildtools"] = { + name = "freax-buildtools", + bins = { + buildtool = "wfvn98h7", + }, + apis = {}, + }, + sketch = { + name = "sketch", + bins = { + sketch = "Mm5hd97E", + }, + apis = {}, + }, + nsh = { + bins = { + nshput = "zeS6uFY7", + nshget = "KJ9Tu2Y9", + nsh = "X5Fysdi4", + vncd = "MC4RFZEK", + }, + apis = { + framebuffer = "Aaza6h5v", + }, + name = "nsh", + }, +} diff --git a/etc/tpm/packageInstalled.dat b/etc/tpm/packageInstalled.dat new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/etc/tpm/packageInstalled.dat @@ -0,0 +1 @@ +{} diff --git a/reload.sh b/reload.sh new file mode 100755 index 0000000..1f2e044 --- /dev/null +++ b/reload.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# Enviroment variables +## RELOADSH_OUTPUT +### Configures the output folder +### Example: RELOADSH_OUTPUT=~/.local/share/ccemux/computer +#RELOADSH_OUTPUT=~/.local/share/multimc/instances/MultiStar/.minecraft/saves/Weihnachten/computercraft/computer +RELOADSH_OUTPUT=~/.local/share/ccemux/computer +## RELOADSH_OUTPUT_LASTDIR +### Configures the output folder's last directory +### Example: RELOADSH_OUTPUT_LASTDIR=0/ +RELOADSH_OUTPUT_LASTDIR=0/ +## RELOADSH_INPUT +### Configures the input folder +### Should not be touched +### Example: RELOADSH_INPUT=$(pwd) +RELOADSH_INPUT=$(pwd) +## RELOADSH_NOLOOP +### Configures if a loop is +### being used or not. +### Example: RELOADSH_NOLOOP=true +#RELOADSH_NOLOOP=false + +# Code +if [ ! -d "$RELOADSH_INPUT" ]; then + echo "$RELOADSH_INPUT (\$RELOADSH_INPUT) is not a directory or does not exist." + exit 1 +fi +if [ ! -d "$RELOADSH_OUTPUT" ]; then + echo "$RELOADSH_OUTPUT (\$RELOADSH_OUTPUT) is not a directory or does not exist." + exit 1 +fi +function reloadOS() { + echo -n "Reloading FREAX... " + if [ ! -d "$RELOADSH_INPUT" ]; then + echo -e "fail.\n$RELOADSH_INPUT (\$RELOADSH_INPUT) is not a directory or does not exist." + exit 1 + fi + if [ ! -d "$RELOADSH_OUTPUT" ]; then + echo -e "fail.\n$RELOADSH_OUTPUT (\$RELOADSH_OUTPUT) is not a directory or does not exist." + exit 1 + fi + /bin/rm -rf "$RELOADSH_OUTPUT/$RELOADSH_OUTPUT_LASTDIR" + /bin/cp -r "$RELOADSH_INPUT" "$RELOADSH_OUTPUT/$RELOADSH_OUTPUT_LASTDIR" + echo "done." +} +if [ "$RELOADSH_NOLOOP" == "true" ]; then + reloadOS +elif [ "$RELOADSH_NOLOOP" == "false" ]; then + while true; do + echo -en "\nPress [enter] to reload" + read -rs + echo "" + reloadOS + done +fi diff --git a/replace.sh b/replace.sh new file mode 100755 index 0000000..13a92c2 --- /dev/null +++ b/replace.sh @@ -0,0 +1,96 @@ +echo "» replace.sh" +FIND_REGEX=$1 +FIND_REPLACEMENT=$2 +REPLACE_PROCESSED_FILES= +REPLACE_PROCESSED_DIRECTORIES= +REPLACE_SKIPPED_FILES= +REPLACE_SKIPPED_DIRECTORIES= +REPLACE_SKIPPED_UNKNOWN= +function convert() { + if [ "$REPLACE_VERBOSE" == "true" ]; then + echo "convert($1 $2 $3)" + fi + sed -i "s/$2/$3/g" "$1" +} +function runProcess() { + cd "$file" + echo "$2» Processing directory $1" + REPLACE_PROCESSED_DIRECTORIES=$REPLACE_PROCESSED_DIRECTORIES+1 + echo "$2»» Processing files" + for file in $(ls -1); do + if [ -f "$file" ]; then + if [ "$file" == "replace.sh" ] || [ "$file" == "README.md" ] || [ "$file" == "LICENSE.md" ]; then + REPLACE_SKIPPED_FILES=$REPLACE_SKIPPED_FILES+1 + if [ "$REPLACE_SHOWERR" == "true" ]; then + echo "$2»»» Skipping file $file (blacklisted)" + else + echo -n "" + fi + else + if [ "${file: -4}" == ".lua" ] || [ "${file: -3}" == ".db" ] || [ "$file" == "fstab" ]; then + REPLACE_PROCESSED_FILES=$REPLACE_PROCESSED_FILES+1 + echo "$2»»» Processing file $file" + convert "$file" "$FIND_REGEX" "$FIND_REPLACEMENT" + else + REPLACE_SKIPPED_FILES=$REPLACE_SKIPPED_FILES+1 + if [ "$REPLACE_SHOWERR" == "true" ]; then + echo "$2»»» Skipping file $file (unknown file type)" + else + echo -n "" + fi + fi + fi + else + if [ -d "$file" ]; then + runProcess "$1/$file" "$2»" + else + REPLACE_SKIPPED_UNKNOWN=$REPLACE_SKIPPED_UNKNOWN+1 + if [ "$REPLACE_SHOWERR" == "true" ]; then + echo "$2»»» Skipping unknown file $file (not a file or directory)" + else + echo -n "" + fi + fi + fi + done + if [ ! "$1" == "." ]; then + cd .. + fi +} +runProcess "$(pwd)" "»" +echo "»» Generating statistics" +if [ "$REPLACE_PROCESSED_FILES" == "" ]; then + REPLACE_PROCESSED_FILES=" 0" +else + echo "»»» Calculating PROCESSED_FILES" + REPLACE_PROCESSED_FILES=$(calc $REPLACE_PROCESSED_FILES) +fi +if [ "$REPLACE_PROCESSED_DIRECTORIES" == "" ]; then + REPLACE_PROCESSED_DIRECTORIES=" 0" +else + echo "»»» Calculating PROCESSED_DIRECTORIES" + REPLACE_PROCESSED_DIRECTORIES=$(calc $REPLACE_PROCESSED_DIRECTORIES) +fi +if [ "$REPLACE_SKIPPED_FILES" == "" ]; then + REPLACE_SKIPPED_FILES=" 0" +else + echo "»»» Calculating SKIPPED_FILES" + REPLACE_SKIPPED_FILES=$(calc $REPLACE_SKIPPED_FILES) +fi +if [ "$REPLACE_SKIPPED_DIRECTORIES" == "" ]; then + REPLACE_SKIPPED_DIRECTORIES=" 0" +else + echo "»»» Calculating SKIPPED_DIRECTORIES" + REPLACE_SKIPPED_DIRECTORIES=$(calc $REPLACE_SKIPPED_DIRECTORIES) +fi +if [ "$REPLACE_SKIPPED_UNKNOWN" == "" ]; then + REPLACE_SKIPPED_UNKNOWN=" 0" +else + echo "»»» Calculating SKIPPED_UNKNOWN" + REPLACE_SKIPPED_UNKNOWN=$(calc $REPLACE_SKIPPED_UNKNOWN) +fi +echo "Processed files: $REPLACE_PROCESSED_FILES" +echo "Processed directories: $REPLACE_PROCESSED_DIRECTORIES" +echo "Skipped files: $REPLACE_SKIPPED_FILES" +echo "Skipped directories: $REPLACE_SKIPPED_DIRECTORIES" +echo "Skipped unknown: $REPLACE_SKIPPED_UNKNOWN" diff --git a/sbin/asm-reload b/sbin/asm-reload new file mode 100644 index 0000000..97cce7e --- /dev/null +++ b/sbin/asm-reload @@ -0,0 +1,44 @@ +local tArgs = {...} +local err = true +local sil = false +local function findArg(arg) + for _,v in ipairs(tArgs) do + if v == arg then + return true + end + end + return false +end + +if findArg("-s") or findArg("--silent") then + sil = true +end +if findArg("-c:SYSTEM_CACHE") or findArg("--cache:SYSTEM_CACHE") then + err = false + if not sil then + kerneldraw.printBootInfo("Reloading system library cache") + kerneldraw.printBootWarning("Loading system libraries") + else + kernel.writeSyslog("Reloading system library cache") + kernel.writeSyslog("Loading system libraries") + end + kernel.loadSysLibs() +end +if findArg("-c:USER_CACHE") or findArg("--cache:USER_CACHE") then + err = false + if not sil then + kerneldraw.printBootInfo("Reloading user library cache") + kerneldraw.printBootWarning("Loading user libraries") + else + kernel.writeSyslog("Reloading user library cache") + kernel.writeSyslog("Loading user libraries") + end + kernel.loadUsrLibs() +end +if #tArgs < 1 then + kerneldraw.printAppInfo("asm-reload", "Arguments not specified") + return +elseif err then + kerneldraw.printAppInfo("asm-reload", "Specify cache to reload") + return +end \ No newline at end of file diff --git a/sbin/dmesg b/sbin/dmesg new file mode 100644 index 0000000..a1e118f --- /dev/null +++ b/sbin/dmesg @@ -0,0 +1,35 @@ +local tArgs = {...} +local logs = {} +local err = true +local function findArg(arg) + for _,v in ipairs(tArgs) do + if v == arg then + return true + end + end + return false +end + +if fs.isDir("/var/log") then + logs = fs.list("/var/log") +else + kerneldraw.printAppWarning("log", "Log files non-existent") + return +end + +if findArg("-l") or findArg("--list") then + shell.run("/bin/ls /var/log") + err = false +end + +for _,v in ipairs(logs) do + if findArg(v) then + local logfile = kernel.printFile("/var/log/"..v) + textutils.pagedPrint(logfile) + err = false + end +end + +if #tArgs < 1 or err then + kerneldraw.printAppInfo("dmesg", "Arguments not specified") +end diff --git a/sbin/errpt b/sbin/errpt new file mode 100644 index 0000000..a6b1e41 --- /dev/null +++ b/sbin/errpt @@ -0,0 +1,11 @@ +local err = true + +if fs.isDir("/etc") then + logs = fs.list("/etc") +else + kerneldraw.printAppWarning("errpt", "/etc directory is non-existent") + return +end + +local logfile = kernel.printFile("/etc/errorLog") +textutils.pagedPrint(logfile) diff --git a/sbin/halt b/sbin/halt new file mode 100644 index 0000000..6c85e0b --- /dev/null +++ b/sbin/halt @@ -0,0 +1 @@ +shell.run("/sbin/shutdown --halt") \ No newline at end of file diff --git a/sbin/login b/sbin/login new file mode 100644 index 0000000..e86732b --- /dev/null +++ b/sbin/login @@ -0,0 +1,44 @@ +if kernel.isBooting then + kernel.isBooting = false + while true do + write("Login: ") + local input = read() + if fs.exists("/etc/passwd/"..input..".dat") and not fs.isDir("/etc/passwd/"..input..".dat") then + local pass = security.unpassbyte(input) + while true do + if kernel.secureInput("Password: ", pass) then + os.pullEvent = function() + local eventData = { os.pullEventRaw( _sFilter ) } + if eventData[1] == "terminate" then + error( "Terminated", 0 ) + end + return unpack( eventData ) + end + rawset(_G, "_activeUser", input) + if fs.exists("/home/".._G["_activeUser"].."/autorun") then + shell.run("/home/".._G["_activeUser"].."/autorun") + end + log.writeAuth("User "..input.." logged in") + if input == "root" then + log.writeSecurity("root logged in") + end + term.setTextColour( colors.yellow ) + print( kernel.getRelease() ) + term.setTextColor( colors.white ) + shell.run("/bin/sh") + if security.getSU() then + kerneldraw.printAppWarning("kernel", "Exiting into top-level shell") + log.writeMessage("Exiting into top-level shell") + break + end + kernel.shutdown(false) + end + end + end + if input == "root" and security.getSU() then + break + end + end +else + kernel.panic("/sbin/login can only be ran at startup") +end \ No newline at end of file diff --git a/sbin/panic b/sbin/panic new file mode 100644 index 0000000..9132572 --- /dev/null +++ b/sbin/panic @@ -0,0 +1 @@ +kernel.panic("Manually initiated crash") \ No newline at end of file diff --git a/sbin/poweroff b/sbin/poweroff new file mode 100644 index 0000000..2b934d9 --- /dev/null +++ b/sbin/poweroff @@ -0,0 +1 @@ +shell.run("/sbin/shutdown --poweroff") \ No newline at end of file diff --git a/sbin/reboot b/sbin/reboot new file mode 100644 index 0000000..157a5b2 --- /dev/null +++ b/sbin/reboot @@ -0,0 +1 @@ +shell.run("/sbin/shutdown --reboot") \ No newline at end of file diff --git a/sbin/shutdown b/sbin/shutdown new file mode 100644 index 0000000..e6d36ed --- /dev/null +++ b/sbin/shutdown @@ -0,0 +1,39 @@ +local tArgs = {...} +local err = true +local reboot = false +local poweroff = false +local halt = false +local remTemp = true +local function findArg(arg) + for _,v in ipairs(tArgs) do + if v == arg then + return true + end + end + return false +end + +if findArg("-r") or findArg("--reboot") then + err = false + reboot = true +end +if findArg("-p") or findArg("--poweroff") then + err = false + poweroff = true +end +if findArg("-h") or findArg("--halt") then + err = false + halt = true +end +if #tArgs < 1 or err then + kerneldraw.printAppInfo("shutdown", "Arguments not specified") + return +end + +if reboot then + kernel.reboot(remTemp) +elseif poweroff then + kernel.poweroff(remTemp) +elseif halt then + kernel.halt() +end \ No newline at end of file diff --git a/startup b/startup new file mode 100644 index 0000000..cfc28ce --- /dev/null +++ b/startup @@ -0,0 +1,8 @@ +term.clear() +term.setCursorPos(1,1) +if fs.exists("/boot/ofbl") then + shell.execute("/boot/ofbl") +else + print("Can't boot FREAX: /boot/ofbl not found") + return +end diff --git a/usr/demos/hips-latest b/usr/demos/hips-latest new file mode 100644 index 0000000..4afb9c6 --- /dev/null +++ b/usr/demos/hips-latest @@ -0,0 +1,26 @@ +local tTimer = os.startTimer(0.5) + +for time=1,30 do + local myEvent = { os.pullEvent("timer") } + if myEvent[2] == tTimer then + local eta = 30 - time + term.setCursorPos(1,18) + if eta < 10 then + printError("TRACE: 0"..tostring(eta)) + else + printError("TRACE: "..tostring(eta)) + end + sleep(0.1) + tTimer = os.startTimer(0.5) + end +end +term.clear() +term.setCursorPos(4,9) +printError(">>> TRACEROUTE COMPLETE") +sleep(0.5) +term.setCursorPos(4,10) +printError(">>> IP FOUND") +sleep(0.5) +term.setCursorPos(4,11) +printError(">>> INITIATING ACTIVE INTRUSION PREVENTION") +sleep(0.5) \ No newline at end of file diff --git a/usr/demos/module b/usr/demos/module new file mode 100644 index 0000000..2852911 --- /dev/null +++ b/usr/demos/module @@ -0,0 +1,5 @@ +local test = {} + +test.address = tostring(_G) + +return test \ No newline at end of file diff --git a/usr/demos/nmap b/usr/demos/nmap new file mode 100644 index 0000000..d9289a7 --- /dev/null +++ b/usr/demos/nmap @@ -0,0 +1,50 @@ +--Initializing IP address generator + +local tArgs = {...} +if #tArgs < 4 then + kerneldraw.printAppInfo("nmap", "No IP address specified") +else + kerneldraw.printAppSuccess("nmap", "Scanning for specified IP") +end + +if #tArgs == 5 then + kerneldraw.printAppSuccess("nmap", "Specified DNS record") +end + +local ip, node + +if #tArgs < 4 then + local x = tostring(math.random(0, 255)) + local y = tostring(math.random(0, 255)) + local z = tostring(math.random(0, 255)) + local r = tostring(math.random(0, 255)) + ip = x.."."..y.."."..z.."."..r +elseif #tArgs >= 4 then + ip = tArgs[1].."."..tArgs[2].."."..tArgs[3].."."..tArgs[4] +end + +if not fs.exists("/network") or not fs.isDir("/network") then + fs.makeDir("/network") +end + +--node = "/network/"..ip + +kerneldraw.printAppInfo("nmap", "Found "..ip) + +if #tArgs == 5 then + kerneldraw.printAppSuccess("nmap", "Assigned DNS record "..tArgs[5]) + node = "/network/"..tArgs[5] +else + kerneldraw.printAppWarning("nmap", "No DNS record found, defaulting to IP") + node = "/network/"..ip +end + +if fs.exists(node) and fs.isDir(node) then + kerneldraw.printAppInfo("nmap", ip.." already known") +else + fs.makeDir(node) + fs.makeDir(node.."/sys") + fs.makeDir(node.."/home") + fs.makeDir(node.."/bin") + kerneldraw.printAppSuccess("nmap", ip.." added to network") +end \ No newline at end of file diff --git a/usr/demos/script.tsf b/usr/demos/script.tsf new file mode 100644 index 0000000..ea2c7b5 --- /dev/null +++ b/usr/demos/script.tsf @@ -0,0 +1,8 @@ +@ @ !! FREAX SCRIPT HEADER +@ type out text +secho This is an example string. +secho The script will now redownload TPM cache. +@ sleep +sdelay 1 +@ recache +tpm-recache \ No newline at end of file diff --git a/usr/demos/winsetup b/usr/demos/winsetup new file mode 100644 index 0000000..f6a1e44 --- /dev/null +++ b/usr/demos/winsetup @@ -0,0 +1,36 @@ +local function annotateLoading(txt, array, delay) + for _, v in ipairs(array) do + kerneldraw.printColoredTextLine(18, txt.." "..v) + sleep(delay or 1.5) + end +end +local setupLines = {} +setupLines.stageOne = {"", "", "", "", ""} +kerneldraw.clearScreen(colors.blue) +term.setCursorPos(1, 2) +print("Windows Setup") +print("==================") +kerneldraw.printColoredTextLine(18, "Windows Setup is checking hardware requirements...") +sleep(3) +annotateLoading("Loading files", setupLines.stageOne, 1.25) +kerneldraw.clearScreen(colors.blue) +term.setCursorPos(1, 2) +print("Windows Setup") +print("==================") +term.setCursorPos(2, 5) +print("Setup is formatting drive C:...") +term.setCursorPos(4, 7) +print("File system: NTFS") +term.setCursorPos(4, 8) +print("Free space: "..fs.getFreeSpace("/").." bytes") +kerneldraw.drawProgressAlt() +sleep(2.5) +kerneldraw.clearScreen(colors.blue) +term.setCursorPos(1, 2) +print("Windows Setup") +print("==================") +term.setCursorPos(2, 5) +print("Setup is copying files...") +kerneldraw.drawProgressAlt() +sleep(5) +kerneldraw.clearScreen(colors.black) \ No newline at end of file diff --git a/usr/local/bin/adventure b/usr/local/bin/adventure new file mode 100644 index 0000000..d13f2cf --- /dev/null +++ b/usr/local/bin/adventure @@ -0,0 +1,1339 @@ +local tBiomes = { + "in a forest", + "in a pine forest", + "knee deep in a swamp", + "in a mountain range", + "in a desert", + "in a grassy plain", + "in frozen tundra", +} + +local function hasTrees(_nBiome) + return _nBiome <= 3 +end + +local function hasStone(_nBiome) + return _nBiome == 4 +end + +local function hasRivers(_nBiome) + return _nBiome ~= 3 and _nBiome ~= 5 +end + +local items = { + ["no tea"] = { + droppable = false, + desc = "Pull yourself together man.", + }, + ["a pig"] = { + heavy = true, + creature = true, + drops = { "some pork" }, + aliases = { "pig" }, + desc = "The pig has a square nose.", + }, + ["a cow"] = { + heavy = true, + creature = true, + aliases = { "cow" }, + desc = "The cow stares at you blankly.", + }, + ["a sheep"] = { + heavy = true, + creature = true, + hitDrops = { "some wool" }, + aliases = { "sheep" }, + desc = "The sheep is fluffy.", + }, + ["a chicken"] = { + heavy = true, + creature = true, + drops = { "some chicken" }, + aliases = { "chicken" }, + desc = "The chicken looks delicious.", + }, + ["a creeper"] = { + heavy = true, + creature = true, + monster = true, + aliases = { "creeper" }, + desc = "The creeper needs a hug.", + }, + ["a skeleton"] = { + heavy = true, + creature = true, + monster = true, + aliases = { "skeleton" }, + nocturnal = true, + desc = "The head bone's connected to the neck bone, the neck bone's connected to the chest bone, the chest bone's connected to the arm bone, the arm bone's connected to the bow, and the bow is pointed at you.", + }, + ["a zombie"] = { + heavy = true, + creature = true, + monster = true, + aliases = { "zombie" }, + nocturnal = true, + desc = "All he wants to do is eat your brains.", + }, + ["a spider"] = { + heavy = true, + creature = true, + monster = true, + aliases = { "spider" }, + desc = "Dozens of eyes stare back at you.", + }, + ["a cave entrance"] = { + heavy = true, + aliases = { "cave entance", "cave", "entrance" }, + desc = "The entrance to the cave is dark, but it looks like you can climb down.", + }, + ["an exit to the surface"] = { + heavy = true, + aliases = { "exit to the surface", "exit", "opening" }, + desc = "You can just see the sky through the opening.", + }, + ["a river"] = { + heavy = true, + aliases = { "river" }, + desc = "The river flows majestically towards the horizon. It doesn't do anything else.", + }, + ["some wood"] = { + aliases = { "wood" }, + material = true, + desc = "You could easilly craft this wood into planks.", + }, + ["some planks"] = { + aliases = { "planks", "wooden planks", "wood planks" }, + desc = "You could easilly craft these planks into sticks.", + }, + ["some sticks"] = { + aliases = { "sticks", "wooden sticks", "wood sticks" }, + desc = "A perfect handle for torches or a pickaxe.", + }, + ["a crafting table"] = { + aliases = { "crafting table", "craft table", "work bench", "workbench", "crafting bench", "table" }, + desc = "It's a crafting table. I shouldn't tell you this, but these don't actually do anything in this game, you can craft tools whenever you like.", + }, + ["a furnace"] = { + aliases = { "furnace" }, + desc = "It's a furnace. Between you and me, these don't actually do anything in this game.", + }, + ["a wooden pickaxe"] = { + aliases = { "pickaxe", "pick", "wooden pick", "wooden pickaxe", "wood pick", "wood pickaxe" }, + tool = true, + toolLevel = 1, + toolType = "pick", + desc = "The pickaxe looks good for breaking stone and coal.", + }, + ["a stone pickaxe"] = { + aliases = { "pickaxe", "pick", "stone pick", "stone pickaxe" }, + tool = true, + toolLevel = 2, + toolType = "pick", + desc = "The pickaxe looks good for breaking iron.", + }, + ["an iron pickaxe"] = { + aliases = { "pickaxe", "pick", "iron pick", "iron pickaxe" }, + tool = true, + toolLevel = 3, + toolType = "pick", + desc = "The pickaxe looks strong enough to break diamond.", + }, + ["a diamond pickaxe"] = { + aliases = { "pickaxe", "pick", "diamond pick", "diamond pickaxe" }, + tool = true, + toolLevel = 4, + toolType = "pick", + desc = "Best. Pickaxe. Ever.", + }, + ["a wooden sword"] = { + aliases = { "sword", "wooden sword", "wood sword" }, + tool = true, + toolLevel = 1, + toolType = "sword", + desc = "Flimsy, but better than nothing.", + }, + ["a stone sword"] = { + aliases = { "sword", "stone sword" }, + tool = true, + toolLevel = 2, + toolType = "sword", + desc = "A pretty good sword.", + }, + ["an iron sword"] = { + aliases = { "sword", "iron sword" }, + tool = true, + toolLevel = 3, + toolType = "sword", + desc = "This sword can slay any enemy.", + }, + ["a diamond sword"] = { + aliases = { "sword", "diamond sword" }, + tool = true, + toolLevel = 4, + toolType = "sword", + desc = "Best. Sword. Ever.", + }, + ["a wooden shovel"] = { + aliases = { "shovel", "wooden shovel", "wood shovel" }, + tool = true, + toolLevel = 1, + toolType = "shovel", + desc = "Good for digging holes.", + }, + ["a stone shovel"] = { + aliases = { "shovel", "stone shovel" }, + tool = true, + toolLevel = 2, + toolType = "shovel", + desc = "Good for digging holes.", + }, + ["an iron shovel"] = { + aliases = { "shovel", "iron shovel" }, + tool = true, + toolLevel = 3, + toolType = "shovel", + desc = "Good for digging holes.", + }, + ["a diamond shovel"] = { + aliases = { "shovel", "diamond shovel" }, + tool = true, + toolLevel = 4, + toolType = "shovel", + desc = "Good for digging holes.", + }, + ["some coal"] = { + aliases = { "coal" }, + ore = true, + toolLevel = 1, + toolType = "pick", + desc = "That coal looks useful for building torches, if only you had a pickaxe to mine it.", + }, + ["some dirt"] = { + aliases = { "dirt" }, + material = true, + desc = "Why not build a mud hut?", + }, + ["some stone"] = { + aliases = { "stone", "cobblestone" }, + material = true, + ore = true, + infinite = true, + toolLevel = 1, + toolType = "pick", + desc = "Stone is useful for building things, and making stone pickaxes.", + }, + ["some iron"] = { + aliases = { "iron" }, + material = true, + ore = true, + toolLevel = 2, + toolType = "pick", + desc = "That iron looks mighty strong, you'll need a stone pickaxe to mine it.", + }, + ["some diamond"] = { + aliases = { "diamond", "diamonds" }, + material = true, + ore = true, + toolLevel = 3, + toolType = "pick", + desc = "Sparkly, rare, and impossible to mine without an iron pickaxe.", + }, + ["some torches"] = { + aliases = { "torches", "torch" }, + desc = "These won't run out for a while.", + }, + ["a torch"] = { + aliases = { "torch" }, + desc = "Fire, fire, burn so bright, won't you light my cave tonight?", + }, + ["some wool"] = { + aliases = { "wool" }, + material = true, + desc = "Soft and good for building.", + }, + ["some pork"] = { + aliases = { "pork", "porkchops" }, + food = true, + desc = "Delicious and nutricious.", + }, + ["some chicken"] = { + aliases = { "chicken" }, + food = true, + desc = "Finger licking good.", + }, +} + +local tAnimals = { + "a pig", "a cow", "a sheep", "a chicken", +} + +local tMonsters = { + "a creeper", "a skeleton", "a zombie", "a spider", +} + +local tRecipes = { + ["some planks"] = { "some wood" }, + ["some sticks"] = { "some planks" }, + ["a crafting table"] = { "some planks" }, + ["a furnace"] = { "some stone" }, + ["some torches"] = { "some sticks", "some coal" }, + + ["a wooden pickaxe"] = { "some planks", "some sticks" }, + ["a stone pickaxe"] = { "some stone", "some sticks" }, + ["an iron pickaxe"] = { "some iron", "some sticks" }, + ["a diamond pickaxe"] = { "some diamond", "some sticks" }, + + ["a wooden sword"] = { "some planks", "some sticks" }, + ["a stone sword"] = { "some stone", "some sticks" }, + ["an iron sword"] = { "some iron", "some sticks" }, + ["a diamond sword"] = { "some diamond", "some sticks" }, + + ["a wooden shovel"] = { "some planks", "some sticks" }, + ["a stone shovel"] = { "some stone", "some sticks" }, + ["an iron shovel"] = { "some iron", "some sticks" }, + ["a diamond shovel"] = { "some diamond", "some sticks" }, +} + +local tGoWest = { + "(life is peaceful there)", + "(lots of open air)", + "(to begin life anew)", + "(this is what we'll do)", + "(sun in winter time)", + "(we will do just fine)", + "(where the skies are blue)", + "(this and more we'll do)", +} +local nGoWest = 0 + +local bRunning = true +local tMap = { { {} } } +local x, y, z = 0, 0, 0 +local inventory = { + ["no tea"] = items["no tea"], +} + +local nTurn = 0 +local nTimeInRoom = 0 +local bInjured = false + +local tDayCycle = { + "It is daytime.", + "It is daytime.", + "It is daytime.", + "It is daytime.", + "It is daytime.", + "It is daytime.", + "It is daytime.", + "It is daytime.", + "The sun is setting.", + "It is night.", + "It is night.", + "It is night.", + "It is night.", + "It is night.", + "The sun is rising.", +} + +local function getTimeOfDay() + return math.fmod(math.floor(nTurn / 3), #tDayCycle) + 1 +end + +local function isSunny() + return getTimeOfDay() < 10 +end + +local function getRoom(x, y, z, dontCreate) + tMap[x] = tMap[x] or {} + tMap[x][y] = tMap[x][y] or {} + if not tMap[x][y][z] and dontCreate ~= true then + local room = { + items = {}, + exits = {}, + nMonsters = 0, + } + tMap[x][y][z] = room + + if y == 0 then + -- Room is above ground + + -- Pick biome + room.nBiome = math.random(1, #tBiomes) + room.trees = hasTrees(room.nBiome) + + -- Add animals + if math.random(1, 3) == 1 then + for _ = 1, math.random(1, 2) do + local sAnimal = tAnimals[math.random(1, #tAnimals)] + room.items[sAnimal] = items[sAnimal] + end + end + + -- Add surface ore + if math.random(1, 5) == 1 or hasStone(room.nBiome) then + room.items["some stone"] = items["some stone"] + end + if math.random(1, 8) == 1 then + room.items["some coal"] = items["some coal"] + end + if math.random(1, 8) == 1 and hasRivers(room.nBiome) then + room.items["a river"] = items["a river"] + end + + -- Add exits + room.exits = { + ["north"] = true, + ["south"] = true, + ["east"] = true, + ["west"] = true, + } + if math.random(1, 8) == 1 then + room.exits.down = true + room.items["a cave entrance"] = items["a cave entrance"] + end + + else + -- Room is underground + -- Add exits + local function tryExit(sDir, sOpp, x, y, z) + local adj = getRoom(x, y, z, true) + if adj then + if adj.exits[sOpp] then + room.exits[sDir] = true + end + else + if math.random(1, 3) == 1 then + room.exits[sDir] = true + end + end + end + + if y == -1 then + local above = getRoom(x, y + 1, z) + if above.exits.down then + room.exits.up = true + room.items["an exit to the surface"] = items["an exit to the surface"] + end + else + tryExit("up", "down", x, y + 1, z) + end + + if y > -3 then + tryExit("down", "up", x, y - 1, z) + end + + tryExit("east", "west", x - 1, y, z) + tryExit("west", "east", x + 1, y, z) + tryExit("north", "south", x, y, z + 1) + tryExit("south", "north", x, y, z - 1) + + -- Add ores + room.items["some stone"] = items["some stone"] + if math.random(1, 3) == 1 then + room.items["some coal"] = items["some coal"] + end + if math.random(1, 8) == 1 then + room.items["some iron"] = items["some iron"] + end + if y == -3 and math.random(1, 15) == 1 then + room.items["some diamond"] = items["some diamond"] + end + + -- Turn out the lights + room.dark = true + end + end + return tMap[x][y][z] +end + +local function itemize(t) + local item = next(t) + if item == nil then + return "nothing" + end + + local text = "" + while item do + text = text .. item + + local nextItem = next(t, item) + if nextItem ~= nil then + local nextNextItem = next(t, nextItem) + if nextNextItem == nil then + text = text .. " and " + else + text = text .. ", " + end + end + item = nextItem + end + return text +end + +local function findItem(_tList, _sQuery) + for sItem, tItem in pairs(_tList) do + if sItem == _sQuery then + return sItem + end + if tItem.aliases ~= nil then + for _, sAlias in pairs(tItem.aliases) do + if sAlias == _sQuery then + return sItem + end + end + end + end + return nil +end + +local tMatches = { + ["wait"] = { + "wait", + }, + ["look"] = { + "look at the ([%a ]+)", + "look at ([%a ]+)", + "look", + "inspect ([%a ]+)", + "inspect the ([%a ]+)", + "inspect", + }, + ["inventory"] = { + "check self", + "check inventory", + "inventory", + "i", + }, + ["go"] = { + "go (%a+)", + "travel (%a+)", + "walk (%a+)", + "run (%a+)", + "go", + }, + ["dig"] = { + "dig (%a+) using ([%a ]+)", + "dig (%a+) with ([%a ]+)", + "dig (%a+)", + "dig", + }, + ["take"] = { + "pick up the ([%a ]+)", + "pick up ([%a ]+)", + "pickup ([%a ]+)", + "take the ([%a ]+)", + "take ([%a ]+)", + "take", + }, + ["drop"] = { + "put down the ([%a ]+)", + "put down ([%a ]+)", + "drop the ([%a ]+)", + "drop ([%a ]+)", + "drop", + }, + ["place"] = { + "place the ([%a ]+)", + "place ([%a ]+)", + "place", + }, + ["cbreak"] = { + "punch the ([%a ]+)", + "punch ([%a ]+)", + "punch", + "break the ([%a ]+) with the ([%a ]+)", + "break ([%a ]+) with ([%a ]+) ", + "break the ([%a ]+)", + "break ([%a ]+)", + "break", + }, + ["mine"] = { + "mine the ([%a ]+) with the ([%a ]+)", + "mine ([%a ]+) with ([%a ]+)", + "mine ([%a ]+)", + "mine", + }, + ["attack"] = { + "attack the ([%a ]+) with the ([%a ]+)", + "attack ([%a ]+) with ([%a ]+)", + "attack ([%a ]+)", + "attack", + "kill the ([%a ]+) with the ([%a ]+)", + "kill ([%a ]+) with ([%a ]+)", + "kill ([%a ]+)", + "kill", + "hit the ([%a ]+) with the ([%a ]+)", + "hit ([%a ]+) with ([%a ]+)", + "hit ([%a ]+)", + "hit", + }, + ["craft"] = { + "craft a ([%a ]+)", + "craft some ([%a ]+)", + "craft ([%a ]+)", + "craft", + "make a ([%a ]+)", + "make some ([%a ]+)", + "make ([%a ]+)", + "make", + }, + ["build"] = { + "build ([%a ]+) out of ([%a ]+)", + "build ([%a ]+) from ([%a ]+)", + "build ([%a ]+)", + "build", + }, + ["eat"] = { + "eat a ([%a ]+)", + "eat the ([%a ]+)", + "eat ([%a ]+)", + "eat", + }, + ["help"] = { + "help me", + "help", + }, + ["exit"] = { + "exit", + "quit", + "goodbye", + "good bye", + "bye", + "farewell", + }, +} + +local commands = {} +local function doCommand(text) + if text == "" then + commands.noinput() + return + end + + for sCommand, t in pairs(tMatches) do + for _, sMatch in pairs(t) do + local tCaptures = { string.match(text, "^" .. sMatch .. "$") } + if #tCaptures ~= 0 then + local fnCommand = commands[sCommand] + if #tCaptures == 1 and tCaptures[1] == sMatch then + fnCommand() + else + fnCommand(table.unpack(tCaptures)) + end + return + end + end + end + commands.badinput() +end + +function commands.wait() + print("Time passes...") +end + +function commands.look(_sTarget) + local room = getRoom(x, y, z) + if room.dark then + print("It is pitch dark.") + return + end + + if _sTarget == nil then + -- Look at the world + if y == 0 then + io.write("You are standing " .. tBiomes[room.nBiome] .. ". ") + print(tDayCycle[getTimeOfDay()]) + else + io.write("You are underground. ") + if next(room.exits) ~= nil then + print("You can travel " .. itemize(room.exits) .. ".") + else + print() + end + end + if next(room.items) ~= nil then + print("There is " .. itemize(room.items) .. " here.") + end + if room.trees then + print("There are trees here.") + end + + else + -- Look at stuff + if room.trees and (_sTarget == "tree" or _sTarget == "trees") then + print("The trees look easy to break.") + elseif _sTarget == "self" or _sTarget == "myself" then + print("Very handsome.") + else + local tItem = nil + local sItem = findItem(room.items, _sTarget) + if sItem then + tItem = room.items[sItem] + else + sItem = findItem(inventory, _sTarget) + if sItem then + tItem = inventory[sItem] + end + end + + if tItem then + print(tItem.desc or "You see nothing special about " .. sItem .. ".") + else + print("You don't see any " .. _sTarget .. " here.") + end + end + end +end + +function commands.go(_sDir) + local room = getRoom(x, y, z) + if _sDir == nil then + print("Go where?") + return + end + + if nGoWest ~= nil then + if _sDir == "west" then + nGoWest = nGoWest + 1 + if nGoWest > #tGoWest then + nGoWest = 1 + end + print(tGoWest[nGoWest]) + else + if nGoWest > 0 or nTurn > 6 then + nGoWest = nil + end + end + end + + if room.exits[_sDir] == nil then + print("You can't go that way.") + return + end + + if _sDir == "north" then + z = z + 1 + elseif _sDir == "south" then + z = z - 1 + elseif _sDir == "east" then + x = x - 1 + elseif _sDir == "west" then + x = x + 1 + elseif _sDir == "up" then + y = y + 1 + elseif _sDir == "down" then + y = y - 1 + else + print("I don't understand that direction.") + return + end + + nTimeInRoom = 0 + doCommand("look") +end + +function commands.dig(_sDir, _sTool) + local room = getRoom(x, y, z) + if _sDir == nil then + print("Dig where?") + return + end + + local sTool = nil + local tTool = nil + if _sTool ~= nil then + sTool = findItem(inventory, _sTool) + if not sTool then + print("You're not carrying a " .. _sTool .. ".") + return + end + tTool = inventory[sTool] + end + + local bActuallyDigging = room.exits[_sDir] ~= true + if bActuallyDigging then + if sTool == nil or tTool.toolType ~= "pick" then + print("You need to use a pickaxe to dig through stone.") + return + end + end + + if _sDir == "north" then + room.exits.north = true + z = z + 1 + getRoom(x, y, z).exits.south = true + + elseif _sDir == "south" then + room.exits.south = true + z = z - 1 + getRoom(x, y, z).exits.north = true + + elseif _sDir == "east" then + room.exits.east = true + x = x - 1 + getRoom(x, y, z).exits.west = true + + elseif _sDir == "west" then + room.exits.west = true + x = x + 1 + getRoom(x, y, z).exits.east = true + + elseif _sDir == "up" then + if y == 0 then + print("You can't dig that way.") + return + end + + room.exits.up = true + if y == -1 then + room.items["an exit to the surface"] = items["an exit to the surface"] + end + y = y + 1 + + room = getRoom(x, y, z) + room.exits.down = true + if y == 0 then + room.items["a cave entrance"] = items["a cave entrance"] + end + + elseif _sDir == "down" then + if y <= -3 then + print("You hit bedrock.") + return + end + + room.exits.down = true + if y == 0 then + room.items["a cave entrance"] = items["a cave entrance"] + end + y = y - 1 + + room = getRoom(x, y, z) + room.exits.up = true + if y == -1 then + room.items["an exit to the surface"] = items["an exit to the surface"] + end + + else + print("I don't understand that direction.") + return + end + + -- + if bActuallyDigging then + if _sDir == "down" and y == -1 or + _sDir == "up" and y == 0 then + inventory["some dirt"] = items["some dirt"] + inventory["some stone"] = items["some stone"] + print("You dig " .. _sDir .. " using " .. sTool .. " and collect some dirt and stone.") + else + inventory["some stone"] = items["some stone"] + print("You dig " .. _sDir .. " using " .. sTool .. " and collect some stone.") + end + end + + nTimeInRoom = 0 + doCommand("look") +end + +function commands.inventory() + print("You are carrying " .. itemize(inventory) .. ".") +end + +function commands.drop(_sItem) + if _sItem == nil then + print("Drop what?") + return + end + + local room = getRoom(x, y, z) + local sItem = findItem(inventory, _sItem) + if sItem then + local tItem = inventory[sItem] + if tItem.droppable == false then + print("You can't drop that.") + else + room.items[sItem] = tItem + inventory[sItem] = nil + print("Dropped.") + end + else + print("You don't have a " .. _sItem .. ".") + end +end + +function commands.place(_sItem) + if _sItem == nil then + print("Place what?") + return + end + + if _sItem == "torch" or _sItem == "a torch" then + local room = getRoom(x, y, z) + if inventory["some torches"] or inventory["a torch"] then + inventory["a torch"] = nil + room.items["a torch"] = items["a torch"] + if room.dark then + print("The cave lights up under the torchflame.") + room.dark = false + elseif y == 0 and not isSunny() then + print("The night gets a little brighter.") + else + print("Placed.") + end + else + print("You don't have torches.") + end + return + end + + commands.drop(_sItem) +end + +function commands.take(_sItem) + if _sItem == nil then + print("Take what?") + return + end + + local room = getRoom(x, y, z) + local sItem = findItem(room.items, _sItem) + if sItem then + local tItem = room.items[sItem] + if tItem.heavy == true then + print("You can't carry " .. sItem .. ".") + elseif tItem.ore == true then + print("You need to mine this ore.") + else + if tItem.infinite ~= true then + room.items[sItem] = nil + end + inventory[sItem] = tItem + + if inventory["some torches"] and inventory["a torch"] then + inventory["a torch"] = nil + end + if sItem == "a torch" and y < 0 then + room.dark = true + print("The cave plunges into darkness.") + else + print("Taken.") + end + end + else + print("You don't see a " .. _sItem .. " here.") + end +end + +function commands.mine(_sItem, _sTool) + if _sItem == nil then + print("Mine what?") + return + end + if _sTool == nil then + print("Mine " .. _sItem .. " with what?") + return + end + commands.cbreak(_sItem, _sTool) +end + +function commands.attack(_sItem, _sTool) + if _sItem == nil then + print("Attack what?") + return + end + commands.cbreak(_sItem, _sTool) +end + +function commands.cbreak(_sItem, _sTool) + if _sItem == nil then + print("Break what?") + return + end + + local sTool = nil + if _sTool ~= nil then + sTool = findItem(inventory, _sTool) + if sTool == nil then + print("You're not carrying a " .. _sTool .. ".") + return + end + end + + local room = getRoom(x, y, z) + if _sItem == "tree" or _sItem == "trees" or _sItem == "a tree" then + print("The tree breaks into blocks of wood, which you pick up.") + inventory["some wood"] = items["some wood"] + return + elseif _sItem == "self" or _sItem == "myself" then + if term.isColour() then + term.setTextColour(colours.red) + end + print("You have died.") + print("Score: &e0") + term.setTextColour(colours.white) + bRunning = false + return + end + + local sItem = findItem(room.items, _sItem) + if sItem then + local tItem = room.items[sItem] + if tItem.ore == true then + -- Breaking ore + if not sTool then + print("You need a tool to break this ore.") + return + end + local tTool = inventory[sTool] + if tTool.tool then + if tTool.toolLevel < tItem.toolLevel then + print(sTool .. " is not strong enough to break this ore.") + elseif tTool.toolType ~= tItem.toolType then + print("You need a different kind of tool to break this ore.") + else + print("The ore breaks, dropping " .. sItem .. ", which you pick up.") + inventory[sItem] = items[sItem] + if tItem.infinite ~= true then + room.items[sItem] = nil + end + end + else + print("You can't break " .. sItem .. " with " .. sTool .. ".") + end + + elseif tItem.creature == true then + -- Fighting monsters (or pigs) + local toolLevel = 0 + local tTool = nil + if sTool then + tTool = inventory[sTool] + if tTool.toolType == "sword" then + toolLevel = tTool.toolLevel + end + end + + local tChances = { 0.2, 0.4, 0.55, 0.8, 1 } + if math.random() <= tChances[toolLevel + 1] then + room.items[sItem] = nil + print("The " .. tItem.aliases[1] .. " dies.") + + if tItem.drops then + for _, sDrop in pairs(tItem.drops) do + if not room.items[sDrop] then + print("The " .. tItem.aliases[1] .. " dropped " .. sDrop .. ".") + room.items[sDrop] = items[sDrop] + end + end + end + + if tItem.monster then + room.nMonsters = room.nMonsters - 1 + end + else + print("The " .. tItem.aliases[1] .. " is injured by your blow.") + end + + if tItem.hitDrops then + for _, sDrop in pairs(tItem.hitDrops) do + if not room.items[sDrop] then + print("The " .. tItem.aliases[1] .. " dropped " .. sDrop .. ".") + room.items[sDrop] = items[sDrop] + end + end + end + + else + print("You can't break " .. sItem .. ".") + end + else + print("You don't see a " .. _sItem .. " here.") + end +end + +function commands.craft(_sItem) + if _sItem == nil then + print("Craft what?") + return + end + + if _sItem == "computer" or _sItem == "a computer" then + print("By creating a computer in a computer in a computer, you tear a hole in the spacetime continuum from which no mortal being can escape.") + if term.isColour() then + term.setTextColour(colours.red) + end + print("You have died.") + print("Score: &e0") + term.setTextColour(colours.white) + bRunning = false + return + end + + local sItem = findItem(items, _sItem) + local tRecipe = sItem and tRecipes[sItem] or nil + if tRecipe then + for _, sReq in ipairs(tRecipe) do + if inventory[sReq] == nil then + print("You don't have the items you need to craft " .. sItem .. ".") + return + end + end + + for _, sReq in ipairs(tRecipe) do + inventory[sReq] = nil + end + inventory[sItem] = items[sItem] + if inventory["some torches"] and inventory["a torch"] then + inventory["a torch"] = nil + end + print("Crafted.") + else + print("You don't know how to make " .. (sItem or _sItem) .. ".") + end +end + +function commands.build(_sThing, _sMaterial) + if _sThing == nil then + print("Build what?") + return + end + + local sMaterial = nil + if _sMaterial == nil then + for sItem, tItem in pairs(inventory) do + if tItem.material then + sMaterial = sItem + break + end + end + if sMaterial == nil then + print("You don't have any building materials.") + return + end + else + sMaterial = findItem(inventory, _sMaterial) + if not sMaterial then + print("You don't have any " .. _sMaterial) + return + end + + if inventory[sMaterial].material ~= true then + print(sMaterial .. " is not a good building material.") + return + end + end + + local alias = nil + if string.sub(_sThing, 1, 1) == "a" then + alias = string.match(_sThing, "a ([%a ]+)") + end + + local room = getRoom(x, y, z) + inventory[sMaterial] = nil + room.items[_sThing] = { + heavy = true, + aliases = { alias }, + desc = "As you look at your creation (made from " .. sMaterial .. "), you feel a swelling sense of pride.", + } + + print("Your construction is complete.") +end + +function commands.help() + local sText = + "Welcome to adventure, the greatest text adventure game on CraftOS. " .. + "To get around the world, type actions, and the adventure will " .. + "be read back to you. The actions availiable to you are go, look, inspect, inventory, " .. + "take, drop, place, punch, attack, mine, dig, craft, build, eat and exit." + print(sText) +end + +function commands.eat(_sItem) + if _sItem == nil then + print("Eat what?") + return + end + + local sItem = findItem(inventory, _sItem) + if not sItem then + print("You don't have any " .. _sItem .. ".") + return + end + + local tItem = inventory[sItem] + if tItem.food then + print("That was delicious!") + inventory[sItem] = nil + + if bInjured then + print("You are no longer injured.") + bInjured = false + end + else + print("You can't eat " .. sItem .. ".") + end +end + +function commands.exit() + bRunning = false +end + +function commands.badinput() + local tResponses = { + "I don't understand.", + "I don't understand you.", + "You can't do that.", + "Nope.", + "Huh?", + "Say again?", + "That's crazy talk.", + "Speak clearly.", + "I'll think about it.", + "Let me get back to you on that one.", + "That doesn't make any sense.", + "What?", + } + print(tResponses[math.random(1, #tResponses)]) +end + +function commands.noinput() + local tResponses = { + "Speak up.", + "Enunciate.", + "Project your voice.", + "Don't be shy.", + "Use your words.", + } + print(tResponses[math.random(1, #tResponses)]) +end + +local function simulate() + local bNewMonstersThisRoom = false + + -- Spawn monsters in nearby rooms + for sx = -2, 2 do + for sy = -1, 1 do + for sz = -2, 2 do + local h = y + sy + if h >= -3 and h <= 0 then + local room = getRoom(x + sx, h, z + sz) + + -- Spawn monsters + if room.nMonsters < 2 and + (h == 0 and not isSunny() and not room.items["a torch"] or room.dark) and + math.random(1, 6) == 1 then + + local sMonster = tMonsters[math.random(1, #tMonsters)] + if room.items[sMonster] == nil then + room.items[sMonster] = items[sMonster] + room.nMonsters = room.nMonsters + 1 + + if sx == 0 and sy == 0 and sz == 0 and not room.dark then + print("From the shadows, " .. sMonster .. " appears.") + bNewMonstersThisRoom = true + end + end + end + + -- Burn monsters + if h == 0 and isSunny() then + for _, sMonster in ipairs(tMonsters) do + if room.items[sMonster] and items[sMonster].nocturnal then + room.items[sMonster] = nil + if sx == 0 and sy == 0 and sz == 0 and not room.dark then + print("With the sun high in the sky, the " .. items[sMonster].aliases[1] .. " bursts into flame and dies.") + end + room.nMonsters = room.nMonsters - 1 + end + end + end + end + end + end + end + + -- Make monsters attack + local room = getRoom(x, y, z) + if nTimeInRoom >= 2 and not bNewMonstersThisRoom then + for _, sMonster in ipairs(tMonsters) do + if room.items[sMonster] then + if math.random(1, 4) == 1 and + not (y == 0 and isSunny() and sMonster == "a spider") then + if sMonster == "a creeper" then + if room.dark then + print("A creeper explodes.") + else + print("The creeper explodes.") + end + room.items[sMonster] = nil + room.nMonsters = room.nMonsters - 1 + else + if room.dark then + print("A " .. items[sMonster].aliases[1] .. " attacks you.") + else + print("The " .. items[sMonster].aliases[1] .. " attacks you.") + end + end + + if bInjured then + if term.isColour() then + term.setTextColour(colours.red) + end + print("You have died.") + print("Score: &e0") + term.setTextColour(colours.white) + bRunning = false + return + else + bInjured = true + end + + break + end + end + end + end + + -- Always print this + if bInjured then + if term.isColour() then + term.setTextColour(colours.red) + end + print("You are injured.") + term.setTextColour(colours.white) + end + + -- Advance time + nTurn = nTurn + 1 + nTimeInRoom = nTimeInRoom + 1 +end + +doCommand("look") +simulate() + +local tCommandHistory = {} +while bRunning do + if term.isColour() then + term.setTextColour(colours.yellow) + end + write("? ") + term.setTextColour(colours.white) + + local sRawLine = read(nil, tCommandHistory) + table.insert(tCommandHistory, sRawLine) + + local sLine = nil + for match in string.gmatch(sRawLine, "%a+") do + if sLine then + sLine = sLine .. " " .. string.lower(match) + else + sLine = string.lower(match) + end + end + + doCommand(sLine or "") + if bRunning then + simulate() + end +end diff --git a/usr/local/bin/gfxpaint b/usr/local/bin/gfxpaint new file mode 100644 index 0000000..2bef10d --- /dev/null +++ b/usr/local/bin/gfxpaint @@ -0,0 +1,25 @@ +if term.getGraphicsMode == nil then error("This requires CraftOS-PC v1.2 or later.") end + +term.setGraphicsMode(true) +for i = 0, 15 do + paintutils.drawFilledBox(i*4, 0, i*4+3, 3, bit.blshift(1, i)) +end +local c = colors.white +paintutils.drawFilledBox(302, 0, 305, 3, c) +while true do + local ev, ch, x, y = os.pullEvent() + if ev == "mouse_click" or ev == "mouse_drag" then + if y < 4 then + if x < 64 then c = bit.blshift(1, math.floor(x / 4)) end + paintutils.drawFilledBox(302, 0, 305, 3, c) + else + term.setPixel(x, y, c) + end + elseif ev == "char" and ch == "q" then break end +end +term.clear() +term.setGraphicsMode(false) +term.setBackgroundColor(colors.black) +term.setTextColor(colors.white) +term.clear() +term.setCursorPos(1, 1) \ No newline at end of file diff --git a/usr/local/bin/hello b/usr/local/bin/hello new file mode 100644 index 0000000..0afdf9e --- /dev/null +++ b/usr/local/bin/hello @@ -0,0 +1,5 @@ +if term.isColour() then + term.setTextColour(2 ^ math.random(0, 15)) +end +textutils.slowPrint("Hello World!") +term.setTextColour(colours.white) diff --git a/usr/local/bin/levels/0.dat b/usr/local/bin/levels/0.dat new file mode 100644 index 0000000..7231b84 --- /dev/null +++ b/usr/local/bin/levels/0.dat @@ -0,0 +1,8 @@ +0 +77 77 +718888887 + 8 8 + 8 8 + 8 8 +788888897 +77 77 diff --git a/usr/local/bin/levels/1.dat b/usr/local/bin/levels/1.dat new file mode 100644 index 0000000..e51a790 --- /dev/null +++ b/usr/local/bin/levels/1.dat @@ -0,0 +1,7 @@ +1 + 777 + 7b7 + 787 +7777778777 +7188888887 +7777777777 diff --git a/usr/local/bin/levels/10.dat b/usr/local/bin/levels/10.dat new file mode 100644 index 0000000..bf1d7cb --- /dev/null +++ b/usr/local/bin/levels/10.dat @@ -0,0 +1,11 @@ +5 + 777 77777 + 727777778837 + 788888878787 + 787777888887 +77877778777777 +7e8b7888b888e7 +7787787b777877 + 777887887887 + 7487807487 + 7777777777 diff --git a/usr/local/bin/levels/11.dat b/usr/local/bin/levels/11.dat new file mode 100644 index 0000000..304f580 --- /dev/null +++ b/usr/local/bin/levels/11.dat @@ -0,0 +1,10 @@ +4 + 777777777 + 727872787 + 787878787 +777787878787777 +7be888888888be7 +777787878787777 + 787878787 + 787478747 + 777777777 diff --git a/usr/local/bin/levels/12.dat b/usr/local/bin/levels/12.dat new file mode 100644 index 0000000..cad16af --- /dev/null +++ b/usr/local/bin/levels/12.dat @@ -0,0 +1,12 @@ +6 +77 777 77 +72888888897 + 8 8 8 + 8 8b888 8 +78 e8888 87 +78888788887 +78 8888e 87 + 8 888b8 8 + 8 8 8 +75888888807 +77 777 77 diff --git a/usr/local/bin/levels/2.dat b/usr/local/bin/levels/2.dat new file mode 100644 index 0000000..0bf3034 --- /dev/null +++ b/usr/local/bin/levels/2.dat @@ -0,0 +1,10 @@ +1 +777777777 +7888888b7 +787778887 +787 78777 +7877787 +7888887 +7777787 + 707 + 777 diff --git a/usr/local/bin/levels/3.dat b/usr/local/bin/levels/3.dat new file mode 100644 index 0000000..40c2c25 --- /dev/null +++ b/usr/local/bin/levels/3.dat @@ -0,0 +1,10 @@ +2 + 77777777 +777888188777 +7b78777787b7 +78787 78787 +78787 78787 +78887 78887 +777877778777 + 78838887 + 77777777 diff --git a/usr/local/bin/levels/4.dat b/usr/local/bin/levels/4.dat new file mode 100644 index 0000000..c77c49a --- /dev/null +++ b/usr/local/bin/levels/4.dat @@ -0,0 +1,10 @@ +2 + 77777777 +777778888887 +788888777787 +7b77787 787 +787 787 787 +7b77787 787 +7888887 787 +7777707 707 + 777 777 diff --git a/usr/local/bin/levels/5.dat b/usr/local/bin/levels/5.dat new file mode 100644 index 0000000..5d084d9 --- /dev/null +++ b/usr/local/bin/levels/5.dat @@ -0,0 +1,10 @@ +3 +777777777 +788888887 +787787787 +787787787 +788888887 +787787787 +787787787 +78e748887 +777777777 diff --git a/usr/local/bin/levels/6.dat b/usr/local/bin/levels/6.dat new file mode 100644 index 0000000..38f0f42 --- /dev/null +++ b/usr/local/bin/levels/6.dat @@ -0,0 +1,11 @@ +4 +7777777777 +7288888837 +78 87 +788888b 87 +788888b 87 +788888b 87 +788888b 87 +78 87 +7188888807 +7777777777 diff --git a/usr/local/bin/levels/7.dat b/usr/local/bin/levels/7.dat new file mode 100644 index 0000000..1456442 --- /dev/null +++ b/usr/local/bin/levels/7.dat @@ -0,0 +1,10 @@ +3 +728777778b7 +78888888887 +78777877787 +787 787 787 +787 7877788 +787 7888889 +88777877777 +e888887 +7777887 diff --git a/usr/local/bin/levels/8.dat b/usr/local/bin/levels/8.dat new file mode 100644 index 0000000..0eb44de --- /dev/null +++ b/usr/local/bin/levels/8.dat @@ -0,0 +1,10 @@ +4 +777777 7777 +7287b7 7867 +788787 7887 +77878777877 + 7888eb8887 + 77877787877 + 7887 787887 + 7487 7e7807 + 7777 777777 diff --git a/usr/local/bin/levels/9.dat b/usr/local/bin/levels/9.dat new file mode 100644 index 0000000..9965aa1 --- /dev/null +++ b/usr/local/bin/levels/9.dat @@ -0,0 +1,12 @@ +2 + 777 777 + 777877778777 + 788838888887 +7778bbbbbbbb8777 +7888b888888b8897 +7878be8888eb8787 +7588b888888b8887 +7778bbbbbbbb8777 + 788888818887 + 777877778777 + 777 777 diff --git a/usr/local/bin/paint b/usr/local/bin/paint new file mode 100644 index 0000000..044b121 --- /dev/null +++ b/usr/local/bin/paint @@ -0,0 +1,452 @@ +-- Paint created by nitrogenfingers (edited by dan200) +-- http://www.youtube.com/user/NitrogenFingers + +------------ +-- Fields -- +------------ + +-- The width and height of the terminal +local w, h = term.getSize() + +-- The selected colours on the left and right mouse button, and the colour of the canvas +local leftColour, rightColour = colours.white, nil +local canvasColour = colours.black + +-- The values stored in the canvas +local canvas = {} + +-- The menu options +local mChoices = { "Save", "Exit" } + +-- The message displayed in the footer bar +local fMessage = "Press Ctrl or click here to access menu" + +------------------------- +-- Initialisation -- +------------------------- + +-- Determine if we can even run this +if not term.isColour() then + print("Requires an Advanced Computer") + return +end + +-- Determines if the file exists, and can be edited on this computer +local tArgs = { ... } +if #tArgs == 0 then + local programName = arg[0] or fs.getName(shell.getRunningProgram()) + print("Usage: " .. programName .. " ") + return +end +local sPath = shell.resolve(tArgs[1]) +local bReadOnly = fs.isReadOnly(sPath) +if fs.exists(sPath) and fs.isDir(sPath) then + print("Cannot edit a directory.") + return +end + +-- Create .nfp files by default +if not fs.exists(sPath) and not string.find(sPath, "%.") then + local sExtension = settings.get("paint.default_extension") + if sExtension ~= "" and type(sExtension) == "string" then + sPath = sPath .. "." .. sExtension + end +end + + +--------------- +-- Functions -- +--------------- + +local function getCanvasPixel(x, y) + if canvas[y] then + return canvas[y][x] + end + return nil +end + +--[[ + Converts a colour value to a text character + params: colour = the number to convert to a hex value + returns: a string representing the chosen colour +]] +local function getCharOf(colour) + -- Incorrect values always convert to nil + if type(colour) == "number" then + local value = math.floor(math.log(colour) / math.log(2)) + 1 + if value >= 1 and value <= 16 then + return string.sub("0123456789abcdef", value, value) + end + end + return " " +end + +--[[ + Converts a text character to colour value + params: char = the char (from string.byte) to convert to number + returns: the colour number of the hex value +]] +local tColourLookup = {} +for n = 1, 16 do + tColourLookup[string.byte("0123456789abcdef", n, n)] = 2 ^ (n - 1) +end +local function getColourOf(char) + -- Values not in the hex table are transparent (canvas coloured) + return tColourLookup[char] +end + +--[[ + Loads the file into the canvas + params: path = the path of the file to open + returns: nil +]] +local function load(path) + -- Load the file + if fs.exists(path) then + local file = fs.open(sPath, "r") + local sLine = file.readLine() + while sLine do + local line = {} + for x = 1, w - 2 do + line[x] = getColourOf(string.byte(sLine, x, x)) + end + table.insert(canvas, line) + sLine = file.readLine() + end + file.close() + end +end + +--[[ + Saves the current canvas to file + params: path = the path of the file to save + returns: true if save was successful, false otherwise +]] +local function save(path) + -- Open file + local sDir = string.sub(sPath, 1, #sPath - #fs.getName(sPath)) + if not fs.exists(sDir) then + fs.makeDir(sDir) + end + + local file, err = fs.open(path, "w") + if not file then + return false, err + end + + -- Encode (and trim) + local tLines = {} + local nLastLine = 0 + for y = 1, h - 1 do + local sLine = "" + local nLastChar = 0 + for x = 1, w - 2 do + local c = getCharOf(getCanvasPixel(x, y)) + sLine = sLine .. c + if c ~= " " then + nLastChar = x + end + end + sLine = string.sub(sLine, 1, nLastChar) + tLines[y] = sLine + if #sLine > 0 then + nLastLine = y + end + end + + -- Save out + for n = 1, nLastLine do + file.writeLine(tLines[n]) + end + file.close() + return true +end + +--[[ + Draws colour picker sidebar, the pallette and the footer + returns: nil +]] +local function drawInterface() + -- Footer + term.setCursorPos(1, h) + term.setBackgroundColour(colours.black) + term.setTextColour(colours.yellow) + term.clearLine() + term.write(fMessage) + + -- Colour Picker + for i = 1, 16 do + term.setCursorPos(w - 1, i) + term.setBackgroundColour(2 ^ (i - 1)) + term.write(" ") + end + + term.setCursorPos(w - 1, 17) + term.setBackgroundColour(canvasColour) + term.setTextColour(colours.grey) + term.write("\127\127") + + -- Left and Right Selected Colours + do + term.setCursorPos(w - 1, 18) + if leftColour ~= nil then + term.setBackgroundColour(leftColour) + term.write(" ") + else + term.setBackgroundColour(canvasColour) + term.setTextColour(colours.grey) + term.write("\127") + end + if rightColour ~= nil then + term.setBackgroundColour(rightColour) + term.write(" ") + else + term.setBackgroundColour(canvasColour) + term.setTextColour(colours.grey) + term.write("\127") + end + end + + -- Padding + term.setBackgroundColour(canvasColour) + for i = 20, h - 1 do + term.setCursorPos(w - 1, i) + term.write(" ") + end +end + +--[[ + Converts a single pixel of a single line of the canvas and draws it + returns: nil +]] +local function drawCanvasPixel(x, y) + local pixel = getCanvasPixel(x, y) + if pixel then + term.setBackgroundColour(pixel or canvasColour) + term.setCursorPos(x, y) + term.write(" ") + else + term.setBackgroundColour(canvasColour) + term.setTextColour(colours.grey) + term.setCursorPos(x, y) + term.write("\127") + end +end + +local color_hex_lookup = {} +for i = 0, 15 do + color_hex_lookup[2 ^ i] = string.format("%x", i) +end + +--[[ + Converts each colour in a single line of the canvas and draws it + returns: nil +]] +local function drawCanvasLine(y) + local text, fg, bg = "", "", "" + for x = 1, w - 2 do + local pixel = getCanvasPixel(x, y) + if pixel then + text = text .. " " + fg = fg .. "0" + bg = bg .. color_hex_lookup[pixel or canvasColour] + else + text = text .. "\127" + fg = fg .. color_hex_lookup[colours.grey] + bg = bg .. color_hex_lookup[canvasColour] + end + end + + term.setCursorPos(1, y) + term.blit(text, fg, bg) +end + +--[[ + Converts each colour in the canvas and draws it + returns: nil +]] +local function drawCanvas() + for y = 1, h - 1 do + drawCanvasLine(y) + end +end + +local menu_choices = { + Save = function() + if bReadOnly then + fMessage = "Access denied" + return false + end + local success, err = save(sPath) + if success then + fMessage = "Saved to " .. sPath + else + if err then + fMessage = "Error saving to " .. err + else + fMessage = "Error saving to " .. sPath + end + end + return false + end, + Exit = function() + sleep(0) -- Super janky, but consumes stray "char" events from pressing Ctrl then E separately. + return true + end, +} + +--[[ + Draws menu options and handles input from within the menu. + returns: true if the program is to be exited; false otherwise +]] +local function accessMenu() + -- Selected menu option + local selection = 1 + + term.setBackgroundColour(colours.black) + + while true do + -- Draw the menu + term.setCursorPos(1, h) + term.clearLine() + term.setTextColour(colours.white) + for k, v in pairs(mChoices) do + if selection == k then + term.setTextColour(colours.yellow) + term.write("[") + term.setTextColour(colours.white) + term.write(v) + term.setTextColour(colours.yellow) + term.write("]") + term.setTextColour(colours.white) + else + term.write(" " .. v .. " ") + end + end + + -- Handle input in the menu + local id, param1, param2, param3 = os.pullEvent() + if id == "key" then + local key = param1 + + -- Handle menu shortcuts. + for _, menu_item in ipairs(mChoices) do + local k = keys[menu_item:sub(1, 1):lower()] + if k and k == key then + return menu_choices[menu_item]() + end + end + + if key == keys.right then + -- Move right + selection = selection + 1 + if selection > #mChoices then + selection = 1 + end + + elseif key == keys.left and selection > 1 then + -- Move left + selection = selection - 1 + if selection < 1 then + selection = #mChoices + end + + elseif key == keys.enter or key == keys.numPadEnter then + -- Select an option + return menu_choices[mChoices[selection]]() + elseif key == keys.leftCtrl or keys == keys.rightCtrl then + -- Cancel the menu + return false + end + elseif id == "mouse_click" then + local cx, cy = param2, param3 + if cy ~= h then return false end -- Exit the menu + + local nMenuPosEnd = 1 + local nMenuPosStart = 1 + for _, sMenuItem in ipairs(mChoices) do + nMenuPosEnd = nMenuPosEnd + #sMenuItem + 1 + if cx > nMenuPosStart and cx < nMenuPosEnd then + return menu_choices[sMenuItem]() + end + nMenuPosEnd = nMenuPosEnd + 1 + nMenuPosStart = nMenuPosEnd + end + end + end +end + +--[[ + Runs the main thread of execution. Draws the canvas and interface, and handles + mouse and key events. + returns: nil +]] +local function handleEvents() + local programActive = true + while programActive do + local id, p1, p2, p3 = os.pullEvent() + if id == "mouse_click" or id == "mouse_drag" then + if p2 >= w - 1 and p3 >= 1 and p3 <= 17 then + if id ~= "mouse_drag" then + -- Selecting an items in the colour picker + if p3 <= 16 then + if p1 == 1 then + leftColour = 2 ^ (p3 - 1) + else + rightColour = 2 ^ (p3 - 1) + end + else + if p1 == 1 then + leftColour = nil + else + rightColour = nil + end + end + --drawCanvas() + drawInterface() + end + elseif p2 < w - 1 and p3 <= h - 1 then + -- Clicking on the canvas + local paintColour = nil + if p1 == 1 then + paintColour = leftColour + elseif p1 == 2 then + paintColour = rightColour + end + if not canvas[p3] then + canvas[p3] = {} + end + canvas[p3][p2] = paintColour + + drawCanvasPixel(p2, p3) + elseif p3 == h and id == "mouse_click" then + -- Open menu + programActive = not accessMenu() + drawInterface() + end + elseif id == "key" then + if p1 == keys.leftCtrl or p1 == keys.rightCtrl then + programActive = not accessMenu() + drawInterface() + end + elseif id == "term_resize" then + w, h = term.getSize() + drawCanvas() + drawInterface() + end + end +end + +-- Init +load(sPath) +drawCanvas() +drawInterface() + +-- Main loop +handleEvents() + +-- Shutdown +term.setBackgroundColour(colours.black) +term.setTextColour(colours.white) +term.clear() +term.setCursorPos(1, 1) diff --git a/usr/local/bin/pngview b/usr/local/bin/pngview new file mode 100644 index 0000000..0ff5bd8 --- /dev/null +++ b/usr/local/bin/pngview @@ -0,0 +1,716 @@ +local png = {} +do -- png.lua + --[[ + BSD 2-Clause License + + Copyright (c) 2018, Kartik Singh + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + --]] + local floor = math.floor + local ceil = math.ceil + local min = math.min + local max = math.max + local abs = math.abs + + -- utility functions + + function memoize(f) + local cache = {} + return function(...) + local key = table.concat({...}, "-") + if not cache[key] then + cache[key] = f(...) + end + return cache[key] + end + end + + function int(bytes) + local n = 0 + for i = 1, #bytes do + n = 256*n + bytes:sub(i, i):byte() + end + return n + end + int = memoize(int) + + function bint(bits) + return tonumber(bits, 2) or 0 + end + bint = memoize(bint) + + function bits(b, width) + local s = "" + if type(b) == "number" then + for i = 1, width do + s = b%2 .. s + b = floor(b/2) + end + else + for i = 1, #b do + s = s .. bits(b:sub(i, i):byte(), 8):reverse() + end + end + return s + end + bits = memoize(bits) + + function fill(bytes, len) + return bytes:rep(floor(len / #bytes)) .. bytes:sub(1, len % #bytes) + end + + function zip(t1, t2) + local zipped = {} + for i = 1, max(#t1, #t2) do + zipped[#zipped + 1] = {t1[i], t2[i]} + end + return zipped + end + + function unzip(zipped) + local t1, t2 = {}, {} + for i = 1, #zipped do + t1[#t1 + 1] = zipped[i][1] + t2[#t2 + 1] = zipped[i][2] + end + return t1, t2 + end + + function map(f, t) + local mapped = {} + for i = 1, #t do + mapped[#mapped + 1] = f(t[i], i) + end + return mapped + end + + function filter(pred, t) + local filtered = {} + for i = 1, #t do + if pred(t[i], i) then + filtered[#filtered + 1] = t[i] + end + end + return filtered + end + + function find(key, t) + if type(key) == "function" then + for i = 1, #t do + if key(t[i]) then + return i + end + end + return nil + else + return find(function(x) return x == key end, t) + end + end + + function slice(t, i, j, step) + local sliced = {} + for k = i < 1 and 1 or i, i < 1 and #t + i or j or #t, step or 1 do + sliced[#sliced + 1] = t[k] + end + return sliced + end + + function range(i, j) + local r = {} + for k = j and i or 0, j or i - 1 do + r[#r + 1] = k + end + return r + end + + -- streams + + function byte_stream(raw) + local stream = {} + local curr = 0 + + function stream:read(n) + local b = raw:sub(curr + 1, curr + n) + curr = curr + n + return b + end + + function stream:seek(n, whence) + if n == "beg" then + curr = 0 + elseif n == "end" then + curr = #raw + elseif whence == "beg" then + curr = n + else + curr = curr + n + end + return self + end + + function stream:is_empty() + return curr >= #raw + end + + function stream:pos() + return curr + end + + function stream:raw() + return raw + end + + return stream + end + + function bit_stream(raw, offset) + local stream = {} + local curr = 0 + offset = offset or 0 + + function stream:read(n, reverse) + local start = floor(curr/8) + offset + 1 + local b = bits(raw:sub(start, start + ceil(n/8))):sub(curr%8 + 1, curr%8 + n) + curr = curr + n + return reverse and b or b:reverse() + end + + function stream:seek(n) + if n == "beg" then + curr = 0 + elseif n == "end" then + curr = #raw + else + curr = curr + n + end + return self + end + + function stream:is_empty() + return curr >= 8*#raw + end + + function stream:pos() + return curr + end + + return stream + end + + function output_stream() + local stream, buffer = {}, {} + local curr = 0 + + function stream:write(bytes) + for i = 1, #bytes do + buffer[#buffer + 1] = bytes:sub(i, i) + end + curr = curr + #bytes + end + + function stream:back_read(offset, n) + local read = {} + for i = curr - offset + 1, curr - offset + n do + read[#read + 1] = buffer[i] + end + return table.concat(read) + end + + function stream:back_copy(dist, len) + local start, copied = curr - dist + 1, {} + for i = start, min(start + len, curr) do + copied[#copied + 1] = buffer[i] + end + self:write(fill(table.concat(copied), len)) + end + + function stream:pos() + return curr + end + + function stream:raw() + return table.concat(buffer) + end + + return stream + end + + -- inflate + + local CL_LENS_ORDER = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15} + local MAX_BITS = 15 + local PT_WIDTH = 8 + + function cl_code_lens(stream, hclen) + local code_lens = {} + for i = 1, hclen do + code_lens[#code_lens + 1] = bint(stream:read(3)) + end + return code_lens + end + + function code_tree(lens, alphabet) + alphabet = alphabet or range(#lens) + local using = filter(function(x, i) return lens[i] and lens[i] > 0 end, alphabet) + lens = filter(function(x) return x > 0 end, lens) + local tree = zip(lens, using) + table.sort(tree, function(a, b) + if a[1] == b[1] then + return a[2] < b[2] + else + return a[1] < b[1] + end + end) + return unzip(tree) + end + + function codes(lens) + local codes = {} + local code = 0 + for i = 1, #lens do + codes[#codes + 1] = bits(code, lens[i]) + if i < #lens then + code = (code + 1)*2^(lens[i + 1] - lens[i]) + end + end + return codes + end + + function handle_long_codes(codes, alphabet, pt) + local i = find(function(x) return #x > PT_WIDTH end, codes) + local long = slice(zip(codes, alphabet), i) + i = 0 + repeat + local prefix = long[i + 1][1]:sub(1, PT_WIDTH) + local same = filter(function(x) return x[1]:sub(1, PT_WIDTH) == prefix end, long) + same = map(function(x) return {x[1]:sub(PT_WIDTH + 1), x[2]} end, same) + pt[prefix] = {rest = prefix_table(unzip(same)), unused = 0} + i = i + #same + until i == #long + end + + function prefix_table(codes, alphabet) + local pt = {} + if #codes[#codes] > PT_WIDTH then + handle_long_codes(codes, alphabet, pt) + end + for i = 1, #codes do + local code = codes[i] + if #code > PT_WIDTH then + break + end + local entry = {value = alphabet[i], unused = PT_WIDTH - #code} + if entry.unused == 0 then + pt[code] = entry + else + for i = 0, 2^entry.unused - 1 do + pt[code .. bits(i, entry.unused)] = entry + end + end + end + return pt + end + + function huffman_decoder(lens, alphabet) + local base_codes = prefix_table(codes(lens), alphabet) + return function(stream) + local codes = base_codes + local entry + repeat + entry = codes[stream:read(PT_WIDTH, true)] + stream:seek(-entry.unused) + codes = entry.rest + until not codes + return entry.value + end + end + + function code_lens(stream, decode, n) + local lens = {} + repeat + local value = decode(stream) + if value < 16 then + lens[#lens + 1] = value + elseif value == 16 then + for i = 1, bint(stream:read(2)) + 3 do + lens[#lens + 1] = lens[#lens] + end + elseif value == 17 then + for i = 1, bint(stream:read(3)) + 3 do + lens[#lens + 1] = 0 + end + elseif value == 18 then + for i = 1, bint(stream:read(7)) + 11 do + lens[#lens + 1] = 0 + end + end + until #lens == n + return lens + end + + function code_trees(stream) + local hlit = bint(stream:read(5)) + 257 + local hdist = bint(stream:read(5)) + 1 + local hclen = bint(stream:read(4)) + 4 + local cl_decode = huffman_decoder(code_tree(cl_code_lens(stream, hclen), CL_LENS_ORDER)) + local ll_decode = huffman_decoder(code_tree(code_lens(stream, cl_decode, hlit))) + local d_decode = huffman_decoder(code_tree(code_lens(stream, cl_decode, hdist))) + return ll_decode, d_decode + end + + function extra_bits(value) + if value >= 4 and value <= 29 then + return floor(value/2) - 1 + elseif value >= 265 and value <= 284 then + return ceil(value/4) - 66 + else + return 0 + end + end + extra_bits = memoize(extra_bits) + + function decode_len(value, bits) + assert(value >= 257 and value <= 285, "value out of range") + assert(#bits == extra_bits(value), "wrong number of extra bits") + if value <= 264 then + return value - 254 + elseif value == 285 then + return 258 + end + local len = 11 + for i = 1, #bits - 1 do + len = len + 2^(i+2) + end + return floor(bint(bits) + len + ((value - 1) % 4)*2^#bits) + end + decode_len = memoize(decode_len) + + function a(n) + if n <= 3 then + return n + 2 + else + return a(n-1) + 2*a(n-2) - 2*a(n-3) + end + end + a = memoize(a) + + function decode_dist(value, bits) + assert(value >= 0 and value <= 29, "value out of range") + assert(#bits == extra_bits(value), "wrong number of extra bits") + return bint(bits) + a(value - 1) + end + decode_dist = memoize(decode_dist) + + function inflate(stream) + local ostream = output_stream() + repeat + local bfinal, btype = bint(stream:read(1)), bint(stream:read(2)) + assert(btype == 2, "compression method not supported") + local ll_decode, d_decode = code_trees(stream) + while true do + local value = ll_decode(stream) + if value < 256 then + ostream:write(string.char(value)) + elseif value == 256 then + break + else + local len = decode_len(value, stream:read(extra_bits(value))) + value = d_decode(stream) + local dist = decode_dist(value, stream:read(extra_bits(value))) + ostream:back_copy(dist, len) + end + end + os.sleep(0) + --write(".") + until bfinal == 1 + return ostream:raw() + end + + -- chunk processing + + local CHANNELS = {} + CHANNELS[0] = 1 + CHANNELS[2] = 3 + CHANNELS[3] = 1 + CHANNELS[4] = 2 + CHANNELS[6] = 4 + + function process_header(stream, image) + stream:seek(8) + image.width = int(stream:read(4)) + image.height = int(stream:read(4)) + image.bit_depth = int(stream:read(1)) + image.color_type= int(stream:read(1)) + image.channels = CHANNELS[image.color_type] + image.compression_method = int(stream:read(1)) + image.filter_method = int(stream:read(1)) + image.interlace_method = int(stream:read(1)) + assert(image.interlace_method == 0, "interlacing not supported") + stream:seek(4) + end + + function process_data(stream, image) + local chunk_len = int(stream:read(4)) + stream:seek(4) + assert(int(stream:read(2)) % 31 == 0, "invalid zlib header") + stream:seek(-2) + local dstream = output_stream() + repeat + dstream:write(stream:read(chunk_len)) + stream:seek(4) + chunk_len = int(stream:read(4)) + until stream:read(4) ~= "IDAT" + stream:seek(-8) + local bstream = bit_stream(dstream:raw(), 2) + image.data = inflate(bstream) + end + + function process_palette(stream, image) + local chunk_len = int(stream:read(4)) + stream:seek(4) + assert(chunk_len % 3 == 0, "invalid palette") + image.palette = {} + for i = 0, chunk_len - 1, 3 do image.palette[i/3] = { + r = int(stream:read(1)), + g = int(stream:read(1)), + b = int(stream:read(1)) + } end + stream:seek(4) + end + + function process_chunk(stream, image) + local chunk_len = int(stream:read(4)) + local chunk_type = stream:read(4) + stream:seek(-8) + if chunk_type == "IHDR" then + process_header(stream, image) + elseif chunk_type == "IDAT" then + process_data(stream, image) + elseif chunk_type == "IEND" then + stream:seek("end") + elseif chunk_type == "PLTE" then + process_palette(stream, image) + else + stream:seek(chunk_len + 12) + end + end + + -- reconstruction + + function paeth(a, b, c) + local p = a + b - c + local pa, pb, pc = abs(p - a), abs(p - b), abs(p - c) + if pa <= pb and pa <= pc then + return a + elseif pb <= pc then + return b + else + return c + end + end + + function scanlines(image) + assert(image.bit_depth % 8 == 0, "bit depth not supported") + local stream = byte_stream(image.data) + local pixel_width = image.channels * image.bit_depth/8 + local scanline_width = image.width * pixel_width + local ostream = output_stream() + return function() + local lstream = output_stream() + if not stream:is_empty() then + local filter_method = int(stream:read(1)) + for i = 1, scanline_width do + local x = int(stream:read(1)) + local a = int(ostream:back_read(pixel_width, 1)) + local b = int(ostream:back_read(scanline_width, 1)) + local c = int(ostream:back_read(scanline_width + pixel_width, 1)) + if i <= pixel_width then + a, c = 0, 0 + end + local byte + if filter_method == 0 then + byte = string.char(x) + elseif filter_method == 1 then + byte = string.char((x + a) % 256) + elseif filter_method == 2 then + byte = string.char((x + b) % 256) + elseif filter_method == 3 then + byte = string.char((x + floor((a + b)/2)) % 256) + elseif filter_method == 4 then + byte = string.char((x + paeth(a, b, c)) % 256) + end + lstream:write(byte) + ostream:write(byte) + end + end + return lstream:raw() + end + end + + function pixel(stream, color_type, bit_depth) + assert(bit_depth % 8 == 0, "bit depth not supported") + local channels = CHANNELS[color_type] + local function read_value() + return int(stream:read(bit_depth/8)) + end + if color_type == 0 then + return { + v = read_value() + } + elseif color_type == 2 then + return { + r = read_value(), + g = read_value(), + b = read_value() + } + elseif color_type == 3 then + return { + v = int(stream:read(bit_depth/8)) + } + elseif color_type == 4 then + return { + v = read_value(), + a = read_value() + } + elseif color_type == 6 then + return { + r = read_value(), + g = read_value(), + b = read_value(), + a = read_value() + } + end + end + + function png.pixels(image) + local i = 0 + local next_scanline = scanlines(image) + local scanline = byte_stream(next_scanline()) + return function() + if scanline:is_empty() then + return + end + local p = pixel(scanline, image.color_type, image.bit_depth) + local x = i % image.width + local y = floor(i / image.width) + i = i + 1 + if scanline:is_empty() then + scanline = byte_stream(next_scanline()) + end + return p, x, y + end + end + + -- exports + + local PNG_HEADER = string.char(0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a) + + function png.load_from_file(filename) + local file = io.open(filename, "rb") + local data = file:read("*all") + file:close() + return png.load(data) + end + + function png.load(data) + local stream = byte_stream(data) + assert(stream:read(8) == PNG_HEADER, "PNG header not found") + local image = {} + repeat + process_chunk(stream, image) + until stream:is_empty() + return image + end +end -- png.lua + +if not term.getGraphicsMode or not term.drawPixels then error("This requires CraftOS-PC v2.1 or later.") end + +local args = {...} +if #args < 1 then error("Usage: pngview ") end + +local image = png.load_from_file(shell.resolve(args[1])) +if image.data == nil then error("data is nil") end +local w, h = term.getSize(2) +if image.width > w or image.height > h then error("Image is too big") end + +os.queueEvent("nosleep") +os.pullEvent() +term.setGraphicsMode(2) +term.clear() + +if image.color_type == 0 or image.color_type == 4 then for i = 0, 2^image.bit_depth-1 do term.setPaletteColor(i, i/(2^image.bit_depth-1), i/(2^image.bit_depth-1), i/(2^image.bit_depth-1)) end +elseif image.color_type == 3 then for i = 0, #image.palette do term.setPaletteColor(i, image.palette[i].r/255, image.palette[i].g/255, image.palette[i].b/255) end +elseif image.color_type == 2 or image.color_type == 6 then + local palette = {} + --local bpp = 3 + (bit.band(image.color_type, 4) / 4) --? + local data = {} + for p, x, y in png.pixels(image) do + local idx + for i,v in ipairs(palette) do if v.r == p.r and v.g == p.g and v.b == p.b then idx = i; break end end + if idx == nil then + if #palette >= 256 then + term.setGraphicsMode(false) + error("Image has too many colors") + end + idx = #palette + 1 + palette[idx] = p + end + if data[y] == nil then data[y] = {} end + data[y][x] = idx-1 + --if x == 0 then os.sleep(0) end + end + for i,v in ipairs(palette) do term.setPaletteColor(i-1, v.r / 255, v.g / 255, v.b / 255) end + term.drawPixels(0, 0, data) + read() + term.setGraphicsMode(false) + for i = 0, 15 do term.setPaletteColor(2^i, term.nativePaletteColor(2^i)) end + return +else error("Image not supported") end + +term.clear() +--local start = os.epoch("utc") +local pixels = {} +for y = 0, image.height - 1 do + if image.color_type == 4 then + local str = string.sub(image.data, y * (image.width * 2 + 1) + 1, (y + 1) * (image.width * 2 + 1)) + pixels[y] = str + for i = 1, #str, 2 do pixels[y] = pixels[y] .. string.sub(str, i, i) end + else + if image.bit_depth == 8 then pixels[y] = string.sub(image.data, y * (image.width + 2) + 1, (y + 1) * (image.width + 2)) + else + pixels[y] = "" + for x = 1, image.width / (8 / image.bit_depth) do + for i = 1, 8 / image.bit_depth do + pixels[y] = pixels[y] .. string.char(bit32.band(bit32.rshift(string.byte(image.data, y * math.floor(image.width / (8 / image.bit_depth) + 1) + x), ((8/image.bit_depth)-i) * image.bit_depth), 2^image.bit_depth - 1)) + end + end + end + end +end +term.drawPixels(0, 0, pixels) +--print("Render took " .. os.epoch("utc") - start .. " ms") +read() + +term.setGraphicsMode(0) +for i = 0, 15 do term.setPaletteColor(2^i, term.nativePaletteColor(2^i)) end diff --git a/usr/local/bin/redirection b/usr/local/bin/redirection new file mode 100644 index 0000000..7cc1233 --- /dev/null +++ b/usr/local/bin/redirection @@ -0,0 +1,703 @@ +--CCRedirection by : RamiLego4Game and Dan200-- +--Based on Redirection by Dan200: http://www.redirectiongame.com-- +--Clearing Screen-- + +--Vars-- +local TermW, TermH = term.getSize() + +local sLevelTitle +local tScreen +local oScreen +local SizeW, SizeH +local aExits +local fExit +local nSpeed +local Speed +local fSpeed +local fSpeedS +local bPaused +local Tick +local Blocks +local XOrgin, YOrgin +local fLevel + +local function reset() + sLevelTitle = "" + tScreen = {} + oScreen = {} + SizeW, SizeH = TermW, TermH + aExits = 0 + fExit = "nop" + nSpeed = 0.6 + Speed = nSpeed + fSpeed = 0.2 + fSpeedS = false + bPaused = false + Tick = os.startTimer(Speed) + Blocks = 0 + XOrgin, YOrgin = 1, 1 + + term.setBackgroundColor(colors.black) + term.setTextColor(colors.white) + term.clear() +end + +local InterFace = {} +InterFace.cExit = colors.red +InterFace.cSpeedD = colors.white +InterFace.cSpeedA = colors.red +InterFace.cTitle = colors.red + +local cG = colors.lightGray +local cW = colors.gray +local cS = colors.black +local cR1 = colors.blue +local cR2 = colors.red +local cR3 = colors.green +local cR4 = colors.yellow + +local tArgs = { ... } + +--Functions-- +local function printCentred(yc, stg) + local xc = math.floor((TermW - #stg) / 2) + 1 + term.setCursorPos(xc, yc) + term.write(stg) +end + +local function centerOrgin() + XOrgin = math.floor(TermW / 2 - SizeW / 2) + YOrgin = math.floor(TermH / 2 - SizeH / 2) +end + +local function reMap() + tScreen = nil + tScreen = {} + for x = 1, SizeW do + tScreen[x] = {} + for y = 1, SizeH do + tScreen[x][y] = { space = true, wall = false, ground = false, robot = "zz", start = "zz", exit = "zz" } + end + end +end + +local function tablecopy(t) + local t2 = {} + for k, v in pairs(t) do + t2[k] = v + end + return t2 +end + +local function buMap() + oScreen = nil + oScreen = {} + for x = 1, SizeW do + oScreen[x] = {} + for y = 1, SizeH do + oScreen[x][y] = tablecopy(tScreen[x][y]) + end + end +end + +local function addRobot(x, y, side, color) + local obj = tScreen[x][y] + local data = side .. color + if obj.wall == nil and obj.robot == nil then + tScreen[x][y].robot = data + else + obj.wall = nil + obj.robot = "zz" + tScreen[x][y].robot = data + end +end + +local function addStart(x, y, side, color) + local obj = tScreen[x][y] + local data = side .. color + if obj.wall == nil and obj.space == nil then + tScreen[x][y].start = data + else + obj.wall = nil + obj.space = nil + tScreen[x][y].start = data + end + aExits = aExits + 1 +end + +local function addGround(x, y) + local obj = tScreen[x][y] + if obj.space == nil and obj.exit == nil and obj.wall == nil and obj.robot == nil and obj.start == nil then + tScreen[x][y].ground = true + else + obj.space = nil + obj.exit = "zz" + obj.wall = nil + obj.robot = "zz" + obj.start = "zz" + tScreen[x][y].ground = true + end +end + +local function addExit(x, y, cl) + local obj = tScreen[x][y] + if obj.space == nil and obj.ground == nil and obj.wall == nil and obj.robot == nil and obj.start == nil then + tScreen[x][y].exit = cl + else + obj.space = nil + obj.ground = nil + obj.wall = nil + obj.robot = "zz" + obj.start = "zz" + tScreen[x][y].exit = cl + end +end + +local function addWall(x, y) + local obj = tScreen[x][y] + if obj == nil then + return error("Here X" .. x .. " Y" .. y) + end + if obj.space == nil and obj.exit == nil and obj.ground == nil and obj.robot == nil and obj.start == nil then + tScreen[x][y].wall = true + else + obj.space = nil + obj.exit = nil + obj.ground = nil + obj.robot = nil + obj.start = nil + tScreen[x][y].wall = true + end +end + +local function loadLevel(nNum) + sLevelTitle = "Level " .. nNum + if nNum == nil then return error("nNum == nil") end + local sDir = fs.getDir(shell.getRunningProgram()) + local sLevelD = sDir .. "/levels/" .. tostring(nNum) .. ".dat" + if not (fs.exists(sLevelD) or fs.isDir(sLevelD)) then return error("Level Not Exists : " .. sLevelD) end + fLevel = fs.open(sLevelD, "r") + local wl = true + Blocks = tonumber(string.sub(fLevel.readLine(), 1, 1)) + local xSize = #fLevel.readLine() + 2 + local Lines = 3 + while wl do + local wLine = fLevel.readLine() + if wLine == nil then + fLevel.close() + wl = false + else + xSize = math.max(#wLine + 2, xSize) + Lines = Lines + 1 + end + end + SizeW, SizeH = xSize, Lines + reMap() + fLevel = fs.open(sLevelD, "r") + fLevel.readLine() + for Line = 2, Lines - 1 do + local sLine = fLevel.readLine() + local chars = #sLine + for char = 1, chars do + local el = string.sub(sLine, char, char) + if el == "8" then + addGround(char + 1, Line) + elseif el == "0" then + addStart(char + 1, Line, "a", "a") + elseif el == "1" then + addStart(char + 1, Line, "b", "a") + elseif el == "2" then + addStart(char + 1, Line, "c", "a") + elseif el == "3" then + addStart(char + 1, Line, "d", "a") + elseif el == "4" then + addStart(char + 1, Line, "a", "b") + elseif el == "5" then + addStart(char + 1, Line, "b", "b") + elseif el == "6" then + addStart(char + 1, Line, "c", "b") + elseif el == "9" then + addStart(char + 1, Line, "d", "b") + elseif el == "b" then + addExit(char + 1, Line, "a") + elseif el == "e" then + addExit(char + 1, Line, "b") + elseif el == "7" then + addWall(char + 1, Line) + end + end + end + fLevel.close() +end + +local function drawStars() + --CCR Background By : RamiLego-- + local cStar, cStarG, crStar, crStarB = colors.lightGray, colors.gray, ".", "*" + local DStar, BStar, nStar, gStar = 14, 10, 16, 3 + local TermW, TermH = term.getSize() + + term.clear() + term.setCursorPos(1, 1) + for x = 1, TermW do + for y = 1, TermH do + local StarT = math.random(1, 30) + if StarT == DStar then + term.setCursorPos(x, y) + term.setTextColor(cStar) + write(crStar) + elseif StarT == BStar then + term.setCursorPos(x, y) + term.setTextColor(cStar) + write(crStarB) + elseif StarT == nStar then + term.setCursorPos(x, y) + term.setTextColor(cStarG) + write(crStar) + elseif StarT == gStar then + term.setCursorPos(x, y) + term.setTextColor(cStarG) + write(crStarB) + end + end + end +end + +local function drawMap() + for x = 1, SizeW do + for y = 1, SizeH do + + local obj = tScreen[x][y] + if obj.ground == true then + paintutils.drawPixel(XOrgin + x, YOrgin + y + 1, cG) + end + if obj.wall == true then + paintutils.drawPixel(XOrgin + x, YOrgin + y + 1, cW) + end + + local ex = tostring(tScreen[x][y].exit) + if not(ex == "zz" or ex == "nil") then + if ex == "a" then + ex = cR1 + elseif ex == "b" then + ex = cR2 + elseif ex == "c" then + ex = cR3 + elseif ex == "d" then + ex = cR4 + else + return error("Exit Color Out") + end + term.setBackgroundColor(cG) + term.setTextColor(ex) + term.setCursorPos(XOrgin + x, YOrgin + y + 1) + print("X") + end + + local st = tostring(tScreen[x][y].start) + if not(st == "zz" or st == "nil") then + local Cr = string.sub(st, 2, 2) + if Cr == "a" then + Cr = cR1 + elseif Cr == "b" then + Cr = cR2 + elseif Cr == "c" then + Cr = cR3 + elseif Cr == "d" then + Cr = cR4 + else + return error("Start Color Out") + end + + term.setTextColor(Cr) + term.setBackgroundColor(cG) + term.setCursorPos(XOrgin + x, YOrgin + y + 1) + + local sSide = string.sub(st, 1, 1) + if sSide == "a" then + print("^") + elseif sSide == "b" then + print(">") + elseif sSide == "c" then + print("v") + elseif sSide == "d" then + print("<") + else + print("@") + end + end + + if obj.space == true then + paintutils.drawPixel(XOrgin + x, YOrgin + y + 1, cS) + end + + local rb = tostring(tScreen[x][y].robot) + if not(rb == "zz" or rb == "nil") then + local Cr = string.sub(rb, 2, 2) + if Cr == "a" then + Cr = cR1 + elseif Cr == "b" then + Cr = cR2 + elseif Cr == "c" then + Cr = cR3 + elseif Cr == "d" then + Cr = cR4 + else + Cr = colors.white + end + term.setBackgroundColor(Cr) + term.setTextColor(colors.white) + term.setCursorPos(XOrgin + x, YOrgin + y + 1) + local sSide = string.sub(rb, 1, 1) + if sSide == "a" then + print("^") + elseif sSide == "b" then + print(">") + elseif sSide == "c" then + print("v") + elseif sSide == "d" then + print("<") + else + print("@") + end + end + end + end +end + +local function isBrick(x, y) + local brb = tostring(tScreen[x][y].robot) + local bobj = oScreen[x][y] + if (brb == "zz" or brb == "nil") and not bobj.wall == true then + return false + else + return true + end +end + +local function gRender(sContext) + if sContext == "start" then + for x = 1, SizeW do + for y = 1, SizeH do + local st = tostring(tScreen[x][y].start) + if not(st == "zz" or st == "nil") then + local Cr = string.sub(st, 2, 2) + local sSide = string.sub(st, 1, 1) + addRobot(x, y, sSide, Cr) + end + end + end + elseif sContext == "tick" then + buMap() + for x = 1, SizeW do + for y = 1, SizeH do + local rb = tostring(oScreen[x][y].robot) + if not(rb == "zz" or rb == "nil") then + local Cr = string.sub(rb, 2, 2) + local sSide = string.sub(rb, 1, 1) + local sobj = oScreen[x][y] + if sobj.space == true then + tScreen[x][y].robot = "zz" + if not sSide == "g" then + addRobot(x, y, "g", Cr) + end + elseif sobj.exit == Cr then + if sSide == "a" or sSide == "b" or sSide == "c" or sSide == "d" then + tScreen[x][y].robot = "zz" + addRobot(x, y, "g", Cr) + aExits = aExits - 1 + end + elseif sSide == "a" then + local obj = isBrick(x, y - 1) + tScreen[x][y].robot = "zz" + if not obj == true then + addRobot(x, y - 1, sSide, Cr) + else + local obj2 = isBrick(x - 1, y) + local obj3 = isBrick(x + 1, y) + if not obj2 == true and not obj3 == true then + if Cr == "a" then + addRobot(x, y, "d", Cr) + elseif Cr == "b" then + addRobot(x, y, "b", Cr) + end + elseif obj == true and obj2 == true and obj3 == true then + addRobot(x, y, "c", Cr) + else + if obj3 == true then + addRobot(x, y, "d", Cr) + elseif obj2 == true then + addRobot(x, y, "b", Cr) + end + end + end + elseif sSide == "b" then + local obj = isBrick(x + 1, y) + tScreen[x][y].robot = "zz" + if not obj == true then + addRobot(x + 1, y, sSide, Cr) + else + local obj2 = isBrick(x, y - 1) + local obj3 = isBrick(x, y + 1) + if not obj2 == true and not obj3 == true then + if Cr == "a" then + addRobot(x, y, "a", Cr) + elseif Cr == "b" then + addRobot(x, y, "c", Cr) + end + elseif obj == true and obj2 == true and obj3 == true then + addRobot(x, y, "d", Cr) + else + if obj3 == true then + addRobot(x, y, "a", Cr) + elseif obj2 == true then + addRobot(x, y, "c", Cr) + end + end + end + elseif sSide == "c" then + local obj = isBrick(x, y + 1) + tScreen[x][y].robot = "zz" + if not obj == true then + addRobot(x, y + 1, sSide, Cr) + else + local obj2 = isBrick(x - 1, y) + local obj3 = isBrick(x + 1, y) + if not obj2 == true and not obj3 == true then + if Cr == "a" then + addRobot(x, y, "b", Cr) + elseif Cr == "b" then + addRobot(x, y, "d", Cr) + end + elseif obj == true and obj2 == true and obj3 == true then + addRobot(x, y, "a", Cr) + else + if obj3 == true then + addRobot(x, y, "d", Cr) + elseif obj2 == true then + addRobot(x, y, "b", Cr) + end + end + end + elseif sSide == "d" then + local obj = isBrick(x - 1, y) + tScreen[x][y].robot = "zz" + if not obj == true then + addRobot(x - 1, y, sSide, Cr) + else + local obj2 = isBrick(x, y - 1) + local obj3 = isBrick(x, y + 1) + if not obj2 == true and not obj3 == true then + if Cr == "a" then + addRobot(x, y, "c", Cr) + elseif Cr == "b" then + addRobot(x, y, "a", Cr) + end + elseif obj == true and obj2 == true and obj3 == true then + addRobot(x, y, "b", Cr) + else + if obj3 == true then + addRobot(x, y, "a", Cr) + elseif obj2 == true then + addRobot(x, y, "c", Cr) + end + end + end + else + addRobot(x, y, sSide, "g") + end + end + end + end + end +end + +function InterFace.drawBar() + term.setBackgroundColor(colors.black) + term.setTextColor(InterFace.cTitle) + printCentred(1, " " .. sLevelTitle .. " ") + + term.setCursorPos(1, 1) + term.setBackgroundColor(cW) + write(" ") + term.setBackgroundColor(colors.black) + write(" x " .. tostring(Blocks) .. " ") + + term.setCursorPos(TermW - 8, TermH) + term.setBackgroundColor(colors.black) + term.setTextColour(InterFace.cSpeedD) + write(" <<") + if bPaused then + term.setTextColour(InterFace.cSpeedA) + else + term.setTextColour(InterFace.cSpeedD) + end + write(" ||") + if fSpeedS then + term.setTextColour(InterFace.cSpeedA) + else + term.setTextColour(InterFace.cSpeedD) + end + write(" >>") + + term.setCursorPos(TermW - 1, 1) + term.setBackgroundColor(colors.black) + term.setTextColour(InterFace.cExit) + write(" X") + term.setBackgroundColor(colors.black) +end + +function InterFace.render() + local id, p1, p2, p3 = os.pullEvent() + if id == "mouse_click" then + if p3 == 1 and p2 == TermW then + return "end" + elseif p3 == TermH and p2 >= TermW - 7 and p2 <= TermW - 6 then + return "retry" + elseif p3 == TermH and p2 >= TermW - 4 and p2 <= TermW - 3 then + bPaused = not bPaused + fSpeedS = false + Speed = bPaused and 0 or nSpeed + if Speed > 0 then + Tick = os.startTimer(Speed) + else + Tick = nil + end + InterFace.drawBar() + elseif p3 == TermH and p2 >= TermW - 1 then + bPaused = false + fSpeedS = not fSpeedS + Speed = fSpeedS and fSpeed or nSpeed + Tick = os.startTimer(Speed) + InterFace.drawBar() + elseif p3 - 1 < YOrgin + SizeH + 1 and p3 - 1 > YOrgin and + p2 < XOrgin + SizeW + 1 and p2 > XOrgin then + local eobj = tScreen[p2 - XOrgin][p3 - YOrgin - 1] + local erobj = tostring(tScreen[p2 - XOrgin][p3 - YOrgin - 1].robot) + if (erobj == "zz" or erobj == "nil") and not eobj.wall == true and not eobj.space == true and Blocks > 0 then + addWall(p2 - XOrgin, p3 - YOrgin - 1) + Blocks = Blocks - 1 + InterFace.drawBar() + drawMap() + end + end + elseif id == "timer" and p1 == Tick then + gRender("tick") + drawMap() + if Speed > 0 then + Tick = os.startTimer(Speed) + else + Tick = nil + end + end +end + +local function startG(LevelN) + drawStars() + loadLevel(LevelN) + centerOrgin() + drawMap() + InterFace.drawBar() + gRender("start") + drawMap() + + local NExit = true + if aExits == 0 then + NExit = false + end + + while true do + local isExit = InterFace.render() + if isExit == "end" then + return nil + elseif isExit == "retry" then + return LevelN + elseif fExit == "yes" then + if fs.exists(fs.getDir(shell.getRunningProgram()) .. "/levels/" .. tostring(LevelN + 1) .. ".dat") then + return LevelN + 1 + else + return nil + end + end + if aExits == 0 and NExit == true then + fExit = "yes" + end + end +end + +local ok, err = true, nil + +--Menu-- +local sStartLevel = tArgs[1] +if ok and not sStartLevel then + ok, err = pcall(function() + term.setTextColor(colors.white) + term.setBackgroundColor(colors.black) + term.clear() + drawStars() + term.setTextColor(colors.red) + printCentred(TermH / 2 - 1, " REDIRECTION ") + printCentred(TermH / 2 - 0, " ComputerCraft Edition ") + term.setTextColor(colors.yellow) + printCentred(TermH / 2 + 2, " Click to Begin ") + os.pullEvent("mouse_click") + end) +end + +--Game-- +if ok then + ok, err = pcall(function() + local nLevel + if sStartLevel then + nLevel = tonumber(sStartLevel) + else + nLevel = 1 + end + while nLevel do + reset() + nLevel = startG(nLevel) + end + end) +end + +--Upsell screen-- +if ok then + ok, err = pcall(function() + term.setTextColor(colors.white) + term.setBackgroundColor(colors.black) + term.clear() + drawStars() + term.setTextColor(colors.red) + if TermW >= 40 then + printCentred(TermH / 2 - 1, " Thank you for playing Redirection ") + printCentred(TermH / 2 - 0, " ComputerCraft Edition ") + printCentred(TermH / 2 + 2, " Check out the full game: ") + term.setTextColor(colors.yellow) + printCentred(TermH / 2 + 3, " http://www.redirectiongame.com ") + else + printCentred(TermH / 2 - 2, " Thank you for ") + printCentred(TermH / 2 - 1, " playing Redirection ") + printCentred(TermH / 2 - 0, " ComputerCraft Edition ") + printCentred(TermH / 2 + 2, " Check out the full game: ") + term.setTextColor(colors.yellow) + printCentred(TermH / 2 + 3, " www.redirectiongame.com ") + end + parallel.waitForAll( + function() sleep(2) end, + function() os.pullEvent("mouse_click") end + ) + end) +end + +--Clear and exit-- +term.setCursorPos(1, 1) +term.setTextColor(colors.white) +term.setBackgroundColor(colors.black) +term.clear() +if not ok then + if err == "Terminated" then + print("Check out the full version of Redirection:") + print("http://www.redirectiongame.com") + else + printError(err) + end +end diff --git a/usr/local/bin/worm b/usr/local/bin/worm new file mode 100644 index 0000000..e1cc3ec --- /dev/null +++ b/usr/local/bin/worm @@ -0,0 +1,282 @@ +sleep(0.1) +-- Display the start screen +local w, h = term.getSize() + +local headingColour, textColour, wormColour, fruitColour +if term.isColour() then + headingColour = colours.yellow + textColour = colours.white + wormColour = colours.green + fruitColour = colours.red +else + headingColour = colours.white + textColour = colours.white + wormColour = colours.white + fruitColour = colours.white +end + +local function printCentred(y, s) + local x = math.floor((w - #s) / 2) + term.setCursorPos(x, y) + --term.clearLine() + term.write(s) +end + +local xVel, yVel = 1, 0 +local xPos, yPos = math.floor(w / 2), math.floor(h / 2) +local pxVel, pyVel = nil, nil +local nExtraLength = 6 +local bRunning = true + +local tailX, tailY = xPos, yPos +local nScore = 0 +local nDifficulty = 2 +local nSpeed, nInterval + +-- Setup the screen +local screen = {} +for x = 1, w do + screen[x] = {} + for y = 1, h do + screen[x][y] = {} + end +end +screen[xPos][yPos] = { snake = true } + +local nFruit = 1 +local tFruits = { + "A", "B", "C", "D", "E", "F", "G", "H", + "I", "J", "K", "L", "M", "N", "O", "P", + "Q", "R", "S", "T", "U", "V", "W", "X", + "Y", "Z", + "a", "b", "c", "d", "e", "f", "g", "h", + "i", "j", "k", "l", "m", "n", "o", "p", + "q", "r", "s", "t", "u", "v", "w", "x", + "y", "z", + "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", + "@", "$", "%", "#", "&", "!", "?", "+", "*", "~", +} + +local function addFruit() + while true do + local x = math.random(1, w) + local y = math.random(2, h) + local fruit = screen[x][y] + if fruit.snake == nil and fruit.wall == nil and fruit.fruit == nil then + screen[x][y] = { fruit = true } + term.setCursorPos(x, y) + term.setBackgroundColour(fruitColour) + term.write(" ") + term.setBackgroundColour(colours.black) + break + end + end + + nFruit = nFruit + 1 + if nFruit > #tFruits then + nFruit = 1 + end +end + +local function drawMenu() + term.setTextColour(headingColour) + term.setCursorPos(1, 1) + term.write("SCORE ") + + term.setTextColour(textColour) + term.setCursorPos(7, 1) + term.write(tostring(nScore)) + + term.setTextColour(headingColour) + term.setCursorPos(w - 11, 1) + term.write("DIFFICULTY ") + + term.setTextColour(textColour) + term.setCursorPos(w, 1) + term.write(tostring(nDifficulty or "?")) + + term.setTextColour(colours.white) +end + +local function update( ) + if pxVel and pyVel then + xVel, yVel = pxVel, pyVel + pxVel, pyVel = nil, nil + end + + -- Remove the tail + if nExtraLength == 0 then + local tail = screen[tailX][tailY] + screen[tailX][tailY] = {} + term.setCursorPos(tailX, tailY) + term.write(" ") + tailX = tail.nextX + tailY = tail.nextY + else + nExtraLength = nExtraLength - 1 + end + + -- Update the head + local head = screen[xPos][yPos] + local newXPos = xPos + xVel + local newYPos = yPos + yVel + if newXPos < 1 then + newXPos = w + elseif newXPos > w then + newXPos = 1 + end + if newYPos < 2 then + newYPos = h + elseif newYPos > h then + newYPos = 2 + end + + local newHead = screen[newXPos][newYPos] + if newHead.snake == true or newHead.wall == true then + bRunning = false + + else + if newHead.fruit == true then + nScore = nScore + 10 + nExtraLength = nExtraLength + 1 + addFruit() + end + xPos = newXPos + yPos = newYPos + head.nextX = newXPos + head.nextY = newYPos + screen[newXPos][newYPos] = { snake = true } + + end + + term.setCursorPos(xPos, yPos) + term.setBackgroundColour(wormColour) + term.write(" ") + term.setBackgroundColour(colours.black) + + drawMenu() +end + +-- Display the frontend +term.clear() +local function drawFrontend() + --term.setTextColour( titleColour ) + --printCentred( math.floor(h/2) - 4, " W O R M " ) + + term.setTextColour(headingColour) + printCentred(math.floor(h / 2) - 3, "") + printCentred(math.floor(h / 2) - 2, " SELECT DIFFICULTY ") + printCentred(math.floor(h / 2) - 1, "") + + printCentred(math.floor(h / 2) + 0, " ") + printCentred(math.floor(h / 2) + 1, " ") + printCentred(math.floor(h / 2) + 2, " ") + printCentred(math.floor(h / 2) - 1 + nDifficulty, " [ ] ") + + term.setTextColour(textColour) + printCentred(math.floor(h / 2) + 0, "EASY") + printCentred(math.floor(h / 2) + 1, "MEDIUM") + printCentred(math.floor(h / 2) + 2, "HARD") + printCentred(math.floor(h / 2) + 3, "") + + term.setTextColour(colours.white) +end + +drawMenu() +drawFrontend() +while true do + local _, key = os.pullEvent("key") + if key == keys.up or key == keys.w then + -- Up + if nDifficulty > 1 then + nDifficulty = nDifficulty - 1 + drawMenu() + drawFrontend() + end + elseif key == keys.down or key == keys.s then + -- Down + if nDifficulty < 3 then + nDifficulty = nDifficulty + 1 + drawMenu() + drawFrontend() + end + elseif key == keys.enter or key == keys.numPadEnter then + -- Enter/Numpad Enter + break + end +end + +local tSpeeds = { 5, 10, 25 } +nSpeed = tSpeeds[nDifficulty] +nInterval = 1 / nSpeed + +-- Grow the snake to its intended size +term.clear() +drawMenu() +screen[tailX][tailY].snake = true +while nExtraLength > 0 do + update() +end +addFruit() +addFruit() + +-- Play the game +local timer = os.startTimer(0) +while bRunning do + local event, p1 = os.pullEvent() + if event == "timer" and p1 == timer then + timer = os.startTimer(nInterval) + update(false) + + elseif event == "key" then + local key = p1 + if key == keys.up or key == keys.w then + -- Up + if yVel == 0 then + pxVel, pyVel = 0, -1 + end + elseif key == keys.down or key == keys.s then + -- Down + if yVel == 0 then + pxVel, pyVel = 0, 1 + end + elseif key == keys.left or key == keys.a then + -- Left + if xVel == 0 then + pxVel, pyVel = -1, 0 + end + + elseif key == keys.right or key == keys.d then + -- Right + if xVel == 0 then + pxVel, pyVel = 1, 0 + end + + end + end +end + +-- Display the gameover screen +term.setTextColour(headingColour) +printCentred(math.floor(h / 2) - 2, " ") +printCentred(math.floor(h / 2) - 1, " G A M E O V E R ") + +term.setTextColour(textColour) +printCentred(math.floor(h / 2) + 0, " ") +printCentred(math.floor(h / 2) + 1, " FINAL SCORE " .. nScore .. " ") +printCentred(math.floor(h / 2) + 2, " ") +term.setTextColour(colours.white) + +local timer = os.startTimer(2.5) +repeat + local e, p = os.pullEvent() + if e == "timer" and p == timer then + term.setTextColour(textColour) + printCentred(math.floor(h / 2) + 2, " PRESS ANY KEY ") + printCentred(math.floor(h / 2) + 3, " ") + term.setTextColour(colours.white) + end +until e == "char" + +term.clear() +term.setCursorPos(1, 1)