Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- status variables
- local installationDir = "/"
- local useHTTP = false
- local pastebinCode = "quPaStrB"
- local pastebinVersion = "9thGUc2D"
- local version = "alpha 1"
- local archiveFile = "netOS.archive"
- -- helper funtions
- local function dotable(_table, _f, ...)
- for _, v in pairs(_table) do
- _f(v, unpack(arg))
- end
- end
- local function extract(_table, path)
- local filePath = fs.combine(path, _table["name"])
- if _table["type"] == "file" then
- local file = fs.open(filePath, "w")
- file.write(_table["contents"])
- file.close()
- elseif _table["type"] == "directory" then
- if not fs.exists(filePath) then fs.makeDir(filePath) end
- dotable(_table["contents"], extract, filePath)
- end
- end
- function printError(msg)
- term.setTextColor(colors.red)
- write("[ERROR] ")
- term.setTextColor(colors.white)
- print(msg)
- end
- function printInfo(msg)
- term.setTextColor(colors.white)
- write("[INFO] ")
- term.setTextColor(colors.white)
- print(msg)
- end
- function printOK(msg)
- term.setTextColor(colors.lime)
- write("[OK] ")
- term.setTextColor(colors.white)
- print(msg)
- end
- -- program part 1
- term.clear()
- local welcome = {
- "-----------------------------------------",
- "| netOS installation program |",
- "| please answer to few simple questions |",
- "| text in [ and ] is default answer |",
- "| for current question |",
- "-----------------------------------------" }
- local width, height = term.getSize()
- local welcomeWidth = string.len(welcome[1])
- local welcomePos = (width / 2) - (welcomeWidth / 2)
- term.setTextColor(colors.lime)
- for y = 1, #welcome do
- term.setCursorPos(welcomePos, y)
- term.write(welcome[y])
- end
- term.setCursorPos(1, #welcome + 1)
- term.write("Where should I install netOS[/]? ")
- installationDir = read()
- if installationDir == "" then
- installationDir = "/"
- end
- if http then
- term.write("Use http to download newest version[yes]? ")
- local answer = read()
- if answer:lower() == "yes" or answer == "" then
- useHTTP = true
- end
- end
- print()
- printInfo("Preparing to install netOS")
- if useHTTP then
- printInfo("Checking version")
- local response = http.get(
- "http://pastebin.com/raw.php?i="..textutils.urlEncode( pastebinVersion )
- )
- local remoteVersion = ""
- if not response then
- printError("Cannot connect to pastebin, using local version")
- else
- remoteVersion = response.readAll()
- response.close();
- end
- if remoteVersion ~= "" and remoteVersion ~= version then
- printInfo("Downloading newest netOS")
- local response = http.get(
- "http://pastebin.com/raw.php?i="..textutils.urlEncode( pastebinCode )
- )
- if response then
- printOK( "Downloaded newest netOS" )
- local sResponse = response.readAll()
- response.close()
- local file = fs.open( archiveFile, "w" )
- file.write( sResponse )
- file.close()
- else
- printError( "Downloading newwest netOS failed" )
- end
- printInfo("Unpacking archive")
- local tEnv = {["installationDir"] = installationDir}
- local archive = loadfile(archiveFile)
- if not archive then
- printError("Couldn't unpack archive, please report it to grabie2")
- else
- fs.delete(archiveFile)
- setfenv(archive, tEnv)
- archive()
- if not tEnv.files then printError("Couldn't unpack archive, please report it to grabie2")
- else files = tEnv.files end
- end
- else
- printInfo("Local version is up to date")
- end
- end
- -- files to copy
- if not files then
- files = {
- {["type"]="directory", ["name"]="bin", contents={
- {["type"]="file", ["name"]="netOS", contents=[[
- --MAIN OS FILE
- --DO NOT MODIFY OR YOU CAN BRAKE YOUR COMPUTER
- --private variables
- local loadedDrvApis = {}
- local loadedDrivers = {}
- local devices = {}
- local loadedLibraries = os.getLibraries()
- local pidCounter = 1
- local messageQueue = {}
- local allowedEvents = { --list of events on specified access levels
- ["NONE"] = {
- ["key"] = true,
- ["char"] = true,
- ["mouse_click"] = true,
- ["mouse_drag"] = true,
- ["mouse_scroll"] = true,
- ["alarm"] = true,
- ["timer"] = true,
- ["os_message"] = true
- },
- ["USER"] = {
- ["key"] = true,
- ["char"] = true,
- ["mouse_click"] = true,
- ["mouse_drag"] = true,
- ["mouse_scroll"] = true,
- ["alarm"] = true,
- ["timer"] = true,
- ["http_failure"] = true,
- ["http_success"] = true,
- ["os_message"] = true
- },
- ["PERMITTED"] = {
- ["key"] = true,
- ["char"] = true,
- ["mouse_click"] = true,
- ["mouse_drag"] = true,
- ["mouse_scroll"] = true,
- ["alarm"] = true,
- ["timer"] = true,
- ["http_failure"] = true,
- ["http_success"] = true,
- ["redstone"] = true,
- ["os_message"] = true
- },
- ["ROOT"] = {
- ["key"] = true,
- ["char"] = true,
- ["mouse_click"] = true,
- ["mouse_drag"] = true,
- ["mouse_scroll"] = true,
- ["alarm"] = true,
- ["timer"] = true,
- ["http_failure"] = true,
- ["http_success"] = true,
- ["redstone"] = true,
- ["disk"] = true,
- ["disk_eject"] = true,
- ["peripheral"] = true,
- ["peripheral_detach"] = true,
- ["rednet_message"] = true,
- ["terminate"] = true,
- ["os_message"] = true
- },
- ["DEBUG"] = { -- if debug flag(in etc/netos is enabled this level can be entered by ANY program
- -- if not then only by ROOT level programs
- ["key"] = true,
- ["char"] = true,
- ["mouse_click"] = true,
- ["mouse_drag"] = true,
- ["mouse_scroll"] = true,
- ["alarm"] = true,
- ["timer"] = true,
- ["http_failure"] = true,
- ["http_success"] = true,
- ["disk"] = true,
- ["disk_eject"] = true,
- ["peripheral"] = true,
- ["peripheral_detach"] = true,
- ["debug"] = true,
- ["os_message"] = true
- }
- }
- local bannedAPIs = { -- apis that tasks wouldn't be able to enable
- ["NONE"] = {
- ['peripheral'] = true,
- ['rednet'] = true,
- ['fs'] = true,
- ['io'] = true,
- ['http'] = true,
- ['parallel'] = true,
- ['coroutine'] = true,
- ['redstone'] = true,
- ['rs'] = true
- },
- ["USER"] = {
- ['peripheral'] = true,
- ['rednet'] = true,
- ['coroutine'] = true,
- ['redstone'] = true,
- ['rs'] = true
- },
- ["PERMITTED"] = {
- ['peripheral'] = true,
- ['rednet'] = true,
- ['coroutine'] = true
- },
- ["ROOT"] = {
- -- ['coroutine'] = true
- },
- ["DEBUG"] = { -- if debug flag(in etc/netos is enabled this level can be entered by ANY program
- -- if not then only by ROOT level programs
- }
- }
- local errorCodes = {
- [0x01] = "Wrong argument $ARG passed to $FUNC",
- [0x02] = "Task '$TASK' not found",
- [0x03] = "Task tried to make os call '$CALL' that doesn't exist",
- [0x04] = "Requested API '$API' doesn't exist",
- [0x05] = "Operation not permitted"
- }
- -- paths definitions fo easier access in code
- local drvPath = "/"..fs.combine(os.getPath(), "drv")
- local libPath = "/"..fs.combine(os.getPath(), "lib")
- local etcPath = "/"..fs.combine(os.getPath(), "etc")
- local configPath = fs.combine(etcPath, "netos")
- --remember native os API
- local nativeOS = _G["os"]
- --private functions
- ----------------LOG---------------------
- local function printOK(message)
- --waits for logAPI
- term.printOK(message)
- end
- local function printInfo(message)
- --waits for logAPI
- term.printInfo(message)
- end
- local function printWarning(message)
- --waits for logAPI
- term.printWarning(message)
- end
- local function printError(message)
- --waits for logAPI
- term.printError(message)
- end
- local function criticalError(message)
- term.printError(message)
- error("OS critical error")
- sleep(5)
- nativeOS.shutdown() -- just in case
- end
- --OS API -- most functions will be replaced
- _G["os"].shutdown = function()
- print("Shutdown currently doesn't work, please use CTRL+S")
- end
- _G["os"].reboot = function()
- print("Reboot currently doesn't work, please use CTRL+R")
- end
- function os.version()
- return "netOS alpha 1"
- end
- term.read = read
- --other non public APIs
- --basic API - API shared for ALL tasks - needed by them to exist----------------
- local baseAPI = {}
- baseAPI.type = type
- baseAPI.sleep = function(delay)
- local wasTimer = os.isEventListener("timer")
- os.addEventListener("timer")
- local timer = os.startTimer(delay)
- while true do
- local e, p1 = os.pullEvent()
- if e == "timer" and p1 == timer then
- break
- end
- end
- end
- baseAPI.config = config
- baseAPI.store = store
- baseAPI.table = table
- baseAPI.string = string
- baseAPI.error = error
- baseAPI.pairs = pairs
- baseAPI.type = type
- baseAPI.ipairs = ipairs
- baseAPI.tostring = tostring
- baseAPI.tonumber = tonumber
- baseAPI.setmetatable = setmetatable
- baseAPI.getfenv = getfenv
- baseAPI.setfenv = setfenv
- baseAPI.pcall = pcall
- baseAPI.loadstring = loadstring
- baseAPI.loadfile = loadfile
- baseAPI.strchar = string.char
- baseAPI.strrep = string.rep
- baseAPI.strlen = string.len
- baseAPI.strsub = string.sub
- baseAPI.unpack = unpack
- baseAPI.colors = _G["colors"]
- baseAPI.colours = _G["colours"]
- baseAPI.math = _G["math"]
- -- if you found other functions that should be added, please send a bug report on
- -- http://netos.robotronika.pl/newticket
- --os calls functions
- baseAPI.os = {}
- baseAPI.os.getComputerID = os.getComputerID
- baseAPI.os.getPath = os.getPath
- baseAPI.os.version = os.version
- baseAPI.os.pullEvent = os.pullEventRaw
- baseAPI.os.pullEventRaw = os.pullEventRaw
- --os calls functions------------------------------------------------------------
- --@param(f) os function
- --@param(...) function arguments
- --@returns what os call returned, see documentation on http://netos.robotronika.pl
- --@description function for making any os call
- baseAPI.os.call = function(f, ...)
- return coroutine.yield("os_call", f, unpack(arg))
- end
- --@param(event) event to listen
- --@returns true if successfull, false if not
- --@description makes current task listen to specified event
- -- so task using os.pullEvent() can get it
- baseAPI.os.addEventListener = function(event)
- return coroutine.yield("os_call", "addEventListener", event)
- end
- --@param(event) event to filter
- --@returns true if successfull, false if not
- --@description makes current task NOT listen to specified event
- -- so task using os.pullEvent() can't get it - for performance
- baseAPI.os.removeEventListener = function(event)
- return coroutine.yield("os_call", "removeEventListener", event)
- end
- --@param(event) event to filter
- --@returns true if subscribed, false if not
- --@description checks if task listens to specified event
- baseAPI.os.isEventListener = function(event)
- return coroutine.yield("os_call", "isEventListener", event)
- end
- --@returns true if successfull, false if not
- --@description asks system to shutdown, but can be refused
- -- NOT IMPLEMENTED YET
- baseAPI.os.shutdown = function()
- return coroutine.yield("os_call", "shutdown")
- end
- --@returns true if successfull, false if not
- --@description asks system to shutdown, but can be refused
- -- NOT IMPLEMENTED YET
- baseAPI.os.reboot = function()
- return coroutine.yield("os_call", "reboot")
- end
- --@param namespace to enable
- --@returns api if success or nil if not
- --@description ask system to enable specyfic API for task for eg. network
- --if successfull system adds API to task env, but also returns it
- baseAPI.os.enableAPI = function(api)
- return coroutine.yield("os_call", "enableAPI", api)
- end
- --shell API - API that shell will get-------------------------------------------
- local shellAPI = table.merge(_G, baseAPI)
- shellAPI.os = table.merge(baseAPI.os, _G["os"])
- shellAPI.term = _G["term"]
- shellAPI.os.shutdown = function(hard)
- hard = hard or false
- if hard then nativeOS.shutdown() -- this is shell - it can shutdown computer without operation of OS
- else os.shutdown() end
- end
- --driver API - API for drivers - some special functions and more permissions----
- local driverAPI = table.merge({}, baseAPI)
- driverAPI.term = _G["term"] -- in future will be replaced with file writing API
- driverAPI.print = print -- FOR TESTING ONLY
- driverAPI.write = write -- FOR TESTING ONLY
- driverAPI.os.queueEvent = os.queueEvent
- driverAPI.textutils = _G["textutils"] -- in future will be replaced with one without print functions or other serialization library
- driverAPI.fs = fs
- driverAPI.store = table.merge({}, _G["store"])
- driverAPI.header = _G["header"]
- driverAPI.config = _G["config"]
- driverAPI.peripheral = _G["peripheral"]
- driverAPI.os.shutdown = function(hard)
- hard = hard or false
- if hard then nativeOS.shutdown() -- this is driver - it can shutdown computer without operation of OS
- else coroutine.yield("os_call", "shutdown") end
- end
- driverAPI.os.lock = function()
- coroutine.yield("os_call", "lock")
- end
- driverAPI.os.unlock = function()
- coroutine.yield("os_call", "unlock")
- end
- --driverApi API - API for driverAPIs more permissions than normal program/lib---
- local driverApiAPI = table.merge({}, baseAPI)
- driverApiAPI.term = _G["term"]
- driverApiAPI.textutils = _G["textutils"]
- driverApiAPI.fs = _G["fs"]
- driverApiAPI.store = _G["store"]
- driverApiAPI.header = _G["header"]
- driverApiAPI.config = _G["config"]
- driverApiAPI.os.queueEvent = os.queueEvent
- ---------------DRIVERS------------------
- local function loadDriverAPIs(APIs, drv)
- for API in string.gmatch(APIs, "%a+") do
- local APIheader = header.read(fs.combine(libPath, API), "drv_api")
- if APIheader then
- if APIheader["NAM"] and APIheader["VER"] then
- if not loadedDrvApis[API] then
- nativeOS.loadAPI(fs.combine(libPath, API))
- APIheader.api = _G[API]
- nativeOS.unloadAPI(fs.combine(libPath, API))
- if not APIheader["drivers"] then APIheader["drivers"] = {} end
- APIheader["drivers"][drv.APIName] = drv.api
- APIheader.FILE = API
- loadedDrvApis[API] = APIheader
- -- loadedLibraries[API] = APIheader.api
- printOK("Driver API: "..APIheader["NAM"].." "..APIheader["VER"].." loaded!")
- else
- loadedDrvApis[API][drv.NAM] = drv.api
- end
- else
- printWarning("Driver API: \""..API.."\" has invalid header and couldn't be loaded!")
- end
- else
- printWarning("Driver API: \""..API.."\" has invalid header and couldn't be loaded!")
- end
- end
- end
- local function loadDriver(driver, API)
- local tEnv = {}
- setmetatable( tEnv, { __index = API } )
- local fnAPI, err = loadfile( driver )
- if fnAPI then
- setfenv( fnAPI, tEnv )
- fnAPI()
- else
- criticalError( err )
- return false
- end
- local tAPI = {}
- for k,v in pairs( tEnv ) do
- tAPI[k] = v
- end
- return tAPI
- end
- local function loadDrivers(API)
- local driverList = fs.list(drvPath)
- for n, driver in pairs(driverList) do
- local driverHeader = header.read(fs.combine(drvPath, driver), "DRIVER")
- if driverHeader then
- if driverHeader["NAM"] and driverHeader["VER"] and driverHeader["DEV"] and driverHeader["APIName"] and driverHeader["TYPE"] then
- local api = loadDriver(fs.combine(drvPath, driver), API)
- driverHeader["FILE"] = driver
- driverHeader.api = api
- loadedDrivers[ driverHeader["TYPE"] ] = driverHeader
- printOK("Driver: \""..driverHeader["NAM"].." "..driverHeader["VER"].."\" loaded")
- if api.getPublicAPI then
- local publicAPIName, publicAPI = api.getPublicAPI()
- if not type(publicAPIName) == "string" and loadedLibraries[publicAPIName] then
- loadedLibraries[publicAPIName] = publicAPI
- printOK("Driver: \""..driverHeader["NAM"].." has loaded public API \""..publicAPIName.."\"")
- end
- end
- if driverHeader["API"] then loadDriverAPIs(driverHeader["API"], driverHeader) end
- end
- end
- end
- end
- ---------------DEVICES------------------
- local function newDevice(type, side)
- local cur = {}
- cur.type = type
- cur.name = nil
- cur.side = side
- cur.API = {}
- return cur
- end
- local function detectDevices()
- local sides = rs.getSides()
- local currentDevice = {}
- for n, side in pairs(sides) do
- if peripheral.isPresent(side) then
- currentDevice = newDevice(peripheral.getType(side):upper(), side)
- if loadedDrivers[currentDevice.type] then
- currentDevice.name = loadedDrivers[currentDevice.type]["DEV"]
- currentDevice.api = loadedDrivers[currentDevice.type].api
- devices[currentDevice.side] = currentDevice
- printInfo("Found "..currentDevice.name.." on "..side.." side")
- else
- printWarning("Found unknown device on "..side.." side")
- end
- end
- end
- end
- --load drivers etc
- loadDrivers(driverAPI)
- detectDevices()
- --multitasking
- local processes = {}
- local lockedProcess = nil -- process that requested lock of terminal
- -- events are ONLY going to that process
- local task = {} -- task API, only internal use
- -- in future this will be passed to main console menager
- --------------------------------------------------------------------------------
- -- task states
- task.TS_RUNNING = 1 -- task made os call, needs to be resumed ASAP
- task.TS_WAITING = 2 -- task waits for event to occur
- task.TS_SUSPENDED = 3 -- /not used yet/ task was suspended by parent task
- task.TS_CLOSING = 4 -- task was asked to close
- task.TS_ERRORED = 5 -- task has errored
- task.TS_CLOSED = 6 -- task has ended
- -- message counter
- task.messages = 0
- -- default streams
- task.stdout = {}
- task.stdin = {}
- task.stdout = term
- task.stdin.isColor = function() return true end
- task.stdin.key = "key"
- task.stdin.char = "char"
- task.stdin.mouse_click = "mouse_click"
- task.stdin.mouse_drag = "mouse_drag"
- task.stdin.mouse_scroll = "mouse_scroll"
- --empty streams
- local nullTerm = {}
- nullTerm.print = function(...) end
- nullTerm.write = function(...) end
- nullTerm.isColor = function() return false end
- nullTerm.isColour = function() return false end
- nullTerm.getSize = term.getSize
- nullTerm.clear = function() end
- nullTerm.getCursorPos = function() return 0, 0 end
- nullTerm.setCursorPos = function() end
- nullTerm.setCursorBlink = function(_blink) end
- nullTerm.scroll = function(_n) end
- task.nullout = {}
- task.nullin = {}
- task.nullout = nullTerm
- task.nullin.isColor = function() return true end
- task.nullin.key = "null_key"
- task.nullin.char = "null_char"
- task.nullin.mouse_click = "null_mouse_click"
- task.nullin.mouse_drag = "null_mouse_drag"
- task.nullin.mouse_scroll = "null_mouse_scroll"
- --------------------------------------------------------------------------------
- --os calls handlers-------------------------------------------------------------
- task.osCalls = {}
- --------------------------------------------------------------------------------
- -- @param(PID) pid of the task to change
- -- @param(p1) event to subscribe to
- -- @param(p2 - p5) not used, but passed to function
- -- @returns true if successfull or false, error code, error message if not
- -- @description makes task listen to specified event
- --------------------------------------------------------------------------------
- task.osCalls.addEventListener = function(PID, p1, p2, p3, p4, p5)
- if not p1 or type(p1) ~= "string" then
- local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#1", FUNC = "os call 'addEventListener'"} )
- return false, 0x01, errorMessage
- end
- -- TODO: check if process has access to passed event
- processes[PID].addEventListener(p1)
- return true
- end
- --------------------------------------------------------------------------------
- -- @param(PID) pid of the task to change
- -- @param(p1) event to unsubscribe to
- -- @param(p2 - p5) not used, but passed to function
- -- @returns true if successfull or false, error code, error message if not
- -- @description makes task not listen to specified event
- --------------------------------------------------------------------------------
- task.osCalls.removeEventListener = function(PID, p1, p2, p3, p4, p5)
- if not p1 or type(p1) ~= "string" then
- local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#1", FUNC = "os call 'removeEventListener'"} )
- return false, 0x01, errorMessage
- end
- processes[PID].removeEventListener(p1)
- return true
- end
- --------------------------------------------------------------------------------
- -- @param(PID) pid of the task to test
- -- @param(p1) event to chack
- -- @param(p2 - p5) not used, but passed to function
- -- @returns true if task subscribes to event or false if not
- -- @description checks if task subscribes to specified event
- --------------------------------------------------------------------------------
- task.osCalls.isEventListener = function(PID, p1, p2, p3, p4, p5)
- if not p1 or type(p1) ~= "string" then
- local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#1", FUNC = "os call 'removeEventListener'"} )
- return false, 0x01, errorMessage
- end
- local isListener = false or processes[PID].eventListeners[p1]
- return isListener
- end
- --------------------------------------------------------------------------------
- -- @param(PID) pid of the task to test
- -- @param(p1 - p5) not used, but passed to function
- -- @returns PID
- --------------------------------------------------------------------------------
- task.osCalls.getPID = function(PID, p1, p2, p3, p4, p5)
- return PID
- end
- --------------------------------------------------------------------------------
- -- @param(PID) pid of the task to test
- -- @param(p1 - p5) not used, but passed to function
- -- @returns PID
- --------------------------------------------------------------------------------
- task.osCalls.reboot = function(PID, p1, p2, p3, p4, p5)
- --TODO: check permissions
- --nativeOS.reboot()
- processes[PID].stdout.print("Reboot currently is not supported, please use CTRL+R instead")
- end
- --------------------------------------------------------------------------------
- -- @param(PID) pid of the task to test
- -- @param(p1 - p5) not used, but passed to function
- -- @returns PID
- --------------------------------------------------------------------------------
- task.osCalls.shutdown = function(PID, p1, p2, p3, p4, p5)
- --TODO: check permissions
- --nativeOS.shutdown()
- processes[PID].stdout.print("Shutdown currently is not supported, please use CTRL+S instead")
- end
- --------------------------------------------------------------------------------
- -- @param(PID) pid of the task to lock execution to
- -- @param(p1 - p5) not used, but passed to function
- -- @returns nil
- --------------------------------------------------------------------------------
- task.osCalls.lock = function(PID, p1, p2, p3, p4, p5)
- --TODO: check permissions
- lockedProcess = PID
- end
- --------------------------------------------------------------------------------
- -- @param(PID) pid of the task executing OS call
- -- @param(p1 - p5) not used, but passed to function
- -- @returns nil
- --------------------------------------------------------------------------------
- task.osCalls.unlock = function(PID, p1, p2, p3, p4, p5)
- --TODO: check permissions
- lockedProcess = nil
- end
- --------------------------------------------------------------------------------
- -- @param(PID) pid of the task to test
- -- @param(p1 - p5) not used, but passed to function
- -- @returns PID
- --------------------------------------------------------------------------------
- task.osCalls.enableAPI = function(PID, p1, p2, p3, p4, p5)
- -- TODO: user level checking
- if not p1 or type(p1) ~= "string" then
- local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#1", FUNC = "os call 'enableAPI'"} )
- return false, 0x01, errorMessage
- end
- if _G[p1] or loadedLibraries[p1] or loadedDrvApis[p1] then
- if not bannedAPIs["ROOT"][p1] then
- local _api = {}
- if loadedDrvApis[p1] then
- _api = loadedDrvApis[p1].api
- elseif loadedLibraries[p1] then
- --actually function loadDriver just loads specified file with specified environment and returns it's environment
- _api = loadDriver(loadedLibraries[p1], processes[PID].getenv())
- else -- global API
- _api = _G[p1]
- end
- processes[PID].setAPI(p1, _api) -- TODO: reload API with environment of asking task
- else
- local errorMessage = errorCodes[0x05]
- return false, 0x05, errorMessage
- end
- else
- local errorMessage = string.gsub(errorCodes[0x04], "%$(%w+)", {API = tostring(p1)} )
- return false, 0x04, errorMessage
- end
- return true
- end
- --------------------------------------------------------------------------------
- -- @param(PID) pid of the task to test
- -- @param(p1) recipient od message, name of task or PID
- -- @param(p2) message
- -- @param(p3 - p5) not used, but passed to function
- -- @returns true if successfull or false, error code, error message if not
- --------------------------------------------------------------------------------
- task.osCalls.sendMessage = function(PID, p1, p2, p3, p4, p5)
- if not p1 then
- local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#1", FUNC = "os call 'sendMessage'"} )
- return false, 0x01, errorMessage
- elseif not p2 then
- local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#2", FUNC = "os call 'sendMessage'"} )
- return false, 0x01, errorMessage
- elseif type(p2) ~= "string" and type(p2) ~= "number" then
- local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#2", FUNC = "os call 'sendMessage'"} )
- return false, 0x01, errorMessage
- end
- if processes[p1] then
- processes[p1].addMessage(PID, p2)
- else
- -- find task by name
- local found = false
- if type(p2) == "string" then
- for i=1, pidCounter, 1 do
- if processes[i] and processes[i].getName() == p1 then
- found = true
- processes[i].addMessage(PID, p2)
- break
- end
- end
- end
- if not found then
- local errorMessage = string.gsub(errorCodes[0x02], "%$(%w+)", {TASK = tostring(p2)} )
- return false, 0x02, errorMessage
- else
- return true
- end
- end
- end
- local function installOSApi(_f)
- local _env = getfenv(_f)
- _env["os"].run = function( _tEnv, _sPath, ... )
- local tArgs = { ... }
- local fnFile, err = loadfile( _sPath )
- if fnFile then
- local tEnv = _tEnv
- setmetatable( tEnv, { __index = _env } )
- setfenv( fnFile, tEnv )
- local ok, err = pcall( function()
- fnFile( unpack( tArgs ) )
- end )
- if not ok then
- if err and err ~= "" then
- _env.printError( err )
- end
- return false
- end
- return true
- end
- if err and err ~= "" then
- _env.printError( err )
- end
- return false
- end
- end
- --------------------------------------------------------------------------------
- -- @param(_name) name of task, should be unique
- -- @param(_f) function to run as task
- -- @param(_api) environment to run task
- -- @param(_stdin) mappings for user input events
- -- @param(_stdout) termAPI for task
- -- @param(...) arguments to pass to task
- -- @returns task object, can be dropped
- --------------------------------------------------------------------------------
- function task.new(_name, _f, _api, _stdin, _stdout, ...)
- local self = {} -- task instance
- local state = task.TS_RUNNING --task state
- local PID = pidCounter -- task pid - dynamically allocated
- local parameters = arg -- parameters to pass to task if returnNeeded is true
- table.insert(parameters, PID)
- local returnNeeded = true --return to task event(false) or parameters(true)
- local messages = {} -- message queue
- local routine = coroutine.create(_f) --task routine
- local stdin = _stdin
- local stdout = _stdout
- ----returns environment of current task-----------------------------------------
- self.getenv = function(name, api)
- local _env = getfenv(_f)
- return _env
- end
- ----adds API to task environment------------------------------------------------
- self.setAPI = function(name, api)
- local _env = getfenv(_f)
- _env[name] = api
- --setfenv(_f, _env) -- Should not be needed, _env is reference to environment
- end
- ----returns name of task--------------------------------------------------------
- self.getName = function()
- return _name
- end
- --sets stdout for this process--------------------------------------------------
- self.setStdout = function(_stdout)
- stdout = _stdout
- self.stdout = stdout
- self.setAPI("term", stdout)
- self.setAPI("print", stdout["print"])
- self.setAPI("write", stdout["write"])
- self.setAPI("read", stdout["read"])
- self.setAPI("printError", stdout["print"])
- end
- ----pre initialization code-----------------------------------------------------
- pidCounter = pidCounter + 1
- if _api then setfenv(_f, _api) end
- installOSApi(_f)
- self.setStdout(_stdout)
- ----global variables------------------------------------------------------------
- self.eventListeners = {["os_message"]='true'} -- which events will be passed to task
- --sets stdin for this process---------------------------------------------------
- self.setStdin = function(_stdin)
- if self.eventListeners[stdin.key] then
- self.eventListeners[stdin.key] = nil
- self.eventListeners[_stdin.key] = true
- end
- if self.eventListeners[stdin.char] then
- self.eventListeners[stdin.char] = nil
- self.eventListeners[_stdin.char] = true
- end
- if self.eventListeners[stdin.mouse_click] then
- self.eventListeners[stdin.mouse_click] = nil
- self.eventListeners[_stdin.mouse_click] = true
- end
- if self.eventListeners[stdin.mouse_drag] then
- self.eventListeners[stdin.mouse_drag] = nil
- self.eventListeners[_stdin.mouse_drag] = true
- end
- if self.eventListeners[stdin.mouse_scroll] then
- self.eventListeners[stdin.mouse_scroll] = nil
- self.eventListeners[_stdin.mouse_scroll] = true
- end
- stdin = _stdin
- end
- ----returns state of task-------------------------------------------------------
- self.getState = function()
- return state
- end
- ----returns PID of task---------------------------------------------------------
- self.getPID = function()
- return PID
- end
- ----adds message to queue-------------------------------------------------------
- -- @param(sender) PID of sender process
- -- @param(message) message to send
- self.addMessage = function(sender, message)
- -- TODO: make it FIFO queue instead of FILO
- table.insert(messages, {["sender"]=sender, ["data"]=message})
- task.messages = task.messages + 1
- end
- ----removes last message from queue and returns it------------------------------
- -- TODO: make it FIFO queue instead of FILO
- self.getMessage = function()
- local message = messages[#messages]
- messages[#messages] = nil
- if not message then return nil end
- task.messages = task.messages - 1
- return message.data, message.sender
- end
- ----adds event to event filter list---------------------------------------------
- self.addEventListener = function(event)
- if event == "key" then event = stdin.key
- elseif event == "char" then event = stdin.char
- elseif event == "mouse_click" then event = stdin.mouse_click
- elseif event == "mouse_drag" then event = stdin.mouse_drag
- elseif event == "mouse_scroll" then event = stdin.mouse_scroll
- end
- self.eventListeners[event] = 'true'
- end
- ----removes event from filter list----------------------------------------------
- self.removeEventListener = function(event)
- if event == "key" then event = stdin.key
- elseif event == "char" then event = stdin.char
- elseif event == "mouse_click" then event = stdin.mouse_click
- elseif event == "mouse_drag" then event = stdin.mouse_drag
- elseif event == "mouse_scroll" then event = stdin.mouse_scroll
- end
- self.eventListeners[event] = nil
- end
- ----runs task, runs os calls, moves task to proper queue if needed--------------
- -- @param(...) arguments to pass to task IF returnNeeded is false
- self.continue = function(...)
- local ok, d1, d2, d3, d4, d7, d6, d7 = nil
- self.setStdout(stdout)
- if returnNeeded then
- ok, d1, d2, d3, d4 = coroutine.resume(routine, unpack(parameters))
- returnNeeded = false
- else
- if arg[1] then
- if arg[1] == stdin.key then arg[1] = "key"
- elseif arg[1] == stdin.char then arg[1] = "char"
- elseif arg[1] == stdin.mouse_click then arg[1] = "mouse_click"
- elseif arg[1] == stdin.mouse_drag then arg[1] = "mouse_drag"
- elseif arg[1] == stdin.mouse_scroll then arg[1] = "mouse_scroll"
- end
- end
- ok, d1, d2, d3, d4, d5, d6, d7 = coroutine.resume(routine, unpack(arg))
- end
- -- task error processing
- if not ok then
- state = task.TS_ERRORED
- printError("Task '".._name.."' has errored: "..d1)
- return false
- end
- -- task end processing
- if coroutine.status(routine) == "dead" then
- state = task.TS_CLOSED
- processes[PID] = nil
- return false
- end
- if d1 == "os_call" then -- task made os call
- if task.osCalls[d2] then
- parameters = { task.osCalls[d2](PID, d3, d4, d5, d6, d7) }
- returnNeeded = true
- else
- local errorMessage = string.gsub(errorCodes[0x03], "%$(%w+)", {CALL = tostring(d2)} )
- printWarning("Task '"..tostring(PID).."' tried to make os call: "..errorMessage)
- parameters = {false, 0x03, errorMessage}
- returnNeeded = true
- end
- state = task.TS_RUNNING
- else -- task yield waiting for event to occour
- state = task.TS_WAITING
- end
- return true
- end
- table.insert(processes, self)
- return self
- end
- --Add driverAPIs to program queue
- for name, header in pairs(loadedDrvApis) do
- if header.api then --this should never fail!
- if header.api.load then
- term.printWarning("Driver API \""..name.."\" has load function")
- end
- if header.api.run then
- local API = table.merge({}, driverApiAPI)
- table.merge(API, header.api)
- API.drivers = {}
- API.drivers = table.merge(API.drivers, header.drivers)
- local _task = task.new("driverAPI_"..name, header.api.run, API, task.stdin, task.stdout)
- else
- criticalError("Critical error! DA/"..tostring(name).."/no run function")
- end
- else
- criticalError("Critical error! DA/"..tostring(name))
- end
- end
- -- Add device drivers to program queue --
- for side, device in pairs(devices) do
- if device.api then --this should never fail!
- if device.api.run then
- local _env = getfenv(device.api.run)
- _env = table.merge(_env, driverAPI)
- task.new("driver_"..side, device.api.run, _env, task.stdin, task.stdout, side)
- else
- criticalError("Critical error! D/"..tostring(devType).."/no run function")
- end
- else
- criticalError("Critical error! D/"..tostring(devType))
- end
- end
- -----------TEST CODE---------
- local fnFile, err = loadfile( fs.combine(os.getPath(), "/bin/vtyManager" ))
- if fnFile then
- local _api = shellAPI
- _api.task = task
- _api.programAPI = programAPI
- _api.shellAPI = shellAPI
- _api.baseAPI = baseAPI
- local tEnv = {}
- setmetatable( tEnv, { __index = _api } )
- setfenv( fnFile, tEnv )
- fnFile()
- local _task = task.new("task_test", tEnv.run, nil, task.stdin, task.stdout)
- else
- print("Error: "..err)
- end
- -----------END TEST CODE------
- printOK("netOS loaded, press any key or wait 5 seconds to continue")
- nativeOS.startTimer(5) -- give user chance to read boot messages
- while true do
- local e, p1, p2, p3, p4, p5 = nativeOS.pullEventRaw()
- local tasksRunning = 0
- if not lockedProcess then
- for n = 1, pidCounter, 1 do
- if processes[n] then
- local process = processes[n]
- if process.getState() == task.TS_RUNNING then
- local ok = processes[n].continue(e, p1, p2, p3, p4, p5)
- if not ok then
- processes[n] = nil
- end
- elseif process.getState() == task.TS_WAITING and process.eventListeners[e] then --refresh only tasks that need to
- local ok = processes[n].continue(e, p1, p2, p3, p4, p5)
- if not ok then
- processes[n] = nil
- end
- elseif process.getState() == task.TS_WAITING and process.eventListeners["os_message"] then
- -- if no event for this task check if there is message for it
- local message, sender = processes[n].getMessage()
- if message then
- local ok = processes[n].continue("os_message", message, sender)
- if not ok then
- processes[n] = nil
- end
- end
- end
- if process.getState() == task.TS_RUNNING then tasksRunning = tasksRunning + 1 end
- end
- end
- else
- -- there is locked process
- if processes[lockedProcess] and processes[lockedProcess].getState() ~= "TS_CLOSED" then
- if processes[lockedProcess].eventListeners[e] or processes[lockedProcess].getState() == task.TS_RUNNING then
- local ok = processes[lockedProcess].continue(e, p1, p2, p3, p4, p5)
- if not ok then
- processes[lockedProcess] = nil
- lockedProcess = nil
- elseif lockedProcess == nil then
- tasksRunning = 1 -- make sure to refresh other tasks, so OS don't hang
- else
- if processes[lockedProcess].getState() == task.TS_RUNNING then tasksRunning = tasksRunning + 1 end
- end
- end
- else
- lockedProcess = nil
- tasksRunning = 1 -- make sure to refresh other tasks, so OS don't hang
- end
- end
- if tasksRunning > 0 or task.messages > 0 then
- nativeOS.startTimer(0)
- end
- end
- ]]},
- {["type"]="file", ["name"]="vtyManager", contents=[[
- -- LOCALS
- local vtys = {}
- -- VTYs
- local VTY = {}
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to render on screen, MUST BE ACTIVE
- -- @description renders VTY to screen
- --------------------------------------------------------------------------------
- VTY.repaint = function(vty)
- if not vty.active then return end
- local width, height = vty.stdout.getSize()
- for x = 1, width, 1 do
- for y = 1, height, 1 do
- if not vty.textColorBuff[x] then vty.textColorBuff[x] = {} end
- if not vty.textColorBuff[x][y] then vty.textColorBuff[x][y] = colors.white end
- if not vty.backColorBuff[x] then vty.backColorBuff[x] = {} end
- if not vty.backColorBuff[x][y] then vty.backColorBuff[x][y] = colors.black end
- vty.stdout.setTextColor(vty.textColorBuff[x][y])
- vty.stdout.setBackgroundColor(vty.backColorBuff[x][y])
- vty.stdout.setCursorPos(x, y)
- if not vty.screenBuff[x] then vty.screenBuff[x] = {} end
- if not vty.screenBuff[x][y] then vty.screenBuff[x][y] = ' ' end
- vty.stdout.write(tostring(vty.screenBuff[x][y]))
- end
- end
- vty.stdout.setCursorPos(vty.cursorX, vty.cursorY)
- vty.stdout.setCursorBlink(vty.cursorBlink)
- vty.stdout.setTextColor(vty.textColor)
- vty.stdout.setBackgroundColor(vty.backColor)
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to print to
- -- @param(sText) text to write
- -- @description writes text to stdout od VTY
- -- acts as native term.write - if text exceeds screen size, it will not wrap
- --------------------------------------------------------------------------------
- VTY._write = function(_vty, sText )
- local width, height = _vty.stdout.getSize()
- local textColor = _vty.textColor;
- local backColor = _vty.backColor;
- for i = 1, sText:len(), 1 do
- if _vty.cursorX > width then break end
- if not _vty.screenBuff[_vty.cursorX] then
- _vty.screenBuff[_vty.cursorX] = {}
- end
- _vty.screenBuff[_vty.cursorX][_vty.cursorY] = sText:sub(i, i)
- if not _vty.textColorBuff[_vty.cursorX] then _vty.textColorBuff[_vty.cursorX] = {} end
- if not _vty.textColorBuff[_vty.cursorX][_vty.cursorY] then _vty.textColorBuff[_vty.cursorX][_vty.cursorY] = colors.white end
- if not _vty.backColorBuff[_vty.cursorX] then _vty.backColorBuff[_vty.cursorX] = {} end
- if not _vty.backColorBuff[_vty.cursorX][_vty.cursorY] then _vty.backColorBuff[_vty.cursorX][_vty.cursorY] = colors.white end
- _vty.textColorBuff[_vty.cursorX][_vty.cursorY] = textColor
- _vty.backColorBuff[_vty.cursorX][_vty.cursorY] = backColor
- if _vty.active then
- _vty.stdout.write(sText:sub(i, i))
- end
- VTY.setCursorPos(_vty, _vty.cursorX + 1, _vty.cursorY)
- end
- -- VTY.repaint(_vty)
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to print to
- -- @param(sText) text to write
- -- @description writes text to stdout with proper word wrapping
- --------------------------------------------------------------------------------
- VTY.write = function(vty, sText)
- local w,h = vty.stdout.getSize()
- local x,y = VTY.getCursorPos(vty)
- local nLinesPrinted = 0
- local function newLine()
- x, y = VTY.getCursorPos(vty)
- if y + 1 <= h then
- VTY.setCursorPos(vty, 1, y + 1)
- else
- VTY.setCursorPos(vty, 1, h)
- VTY.scroll(vty, 1)
- end
- x, y = VTY.getCursorPos(vty)
- nLinesPrinted = nLinesPrinted + 1
- end
- -- Print the line with proper word wrapping
- while string.len(sText) > 0 do
- local whitespace = string.match( sText, "^[ \t]+" )
- if whitespace then
- -- Print whitespace
- VTY._write(vty,whitespace)
- x,y = VTY.getCursorPos(vty)
- sText = string.sub( sText, string.len(whitespace) + 1 )
- end
- local newline = string.match( sText, "^\n" )
- if newline then
- -- Print newlines
- newLine()
- sText = string.sub( sText, 2 )
- end
- local text = string.match( sText, "^[^ \t\n]+" )
- if text then
- sText = string.sub( sText, string.len(text) + 1 )
- if string.len(text) > w then
- -- Print a multiline word
- while string.len( text ) > 0 do
- if x > w then
- newLine()
- end
- VTY._write(vty,text)
- text = string.sub( text, (w-x) + 2 )
- x,y = VTY.getCursorPos(vty)
- end
- else
- -- Print a word normally
- if x + string.len(text) - 1 > w then
- newLine()
- end
- VTY._write(vty,text)
- x,y = VTY.getCursorPos(vty)
- end
- end
- end
- return nLinesPrinted
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to print to
- -- @param(...) data to write
- -- @description writes all passed arguments to VTY, at the and prints new line
- --------------------------------------------------------------------------------
- VTY.print = function(vty, ...)
- for n = 1, #arg, 1 do
- VTY.write(vty, tostring(arg[n]))
- end
- VTY.write(vty, "\n")
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to clear
- -- @description clears screen of specified VTY
- --------------------------------------------------------------------------------
- VTY.clear = function(vty)
- local width, height = vty.stdout.getSize()
- for x = 1, width, 1 do
- if not vty.screenBuff[x] then
- vty.screenBuff[x] = {}
- end
- for y = 1, height, 1 do
- vty.screenBuff[x][y] = " "
- vty.backColorBuff[x][y] = vty.backColor;
- end
- end
- if vty.active then vty.stdout.clear() end
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to scroll
- -- @param(_n) lines to scroll
- -- @description scrolls specified VTY _n lines to top
- --------------------------------------------------------------------------------
- VTY.scroll = function(vty, _n)
- local width, height = vty.stdout.getSize()
- for y = 1, height-_n, 1 do
- if y + _n <= height then
- for x = 1, width, 1 do
- if not vty.textColorBuff[x] then vty.textColorBuff[x] = {} end
- if not vty.textColorBuff[x][y+_n] then vty.textColorBuff[x][y+_n] = colors.white end
- if not vty.backColorBuff[x] then vty.backColorBuff[x] = {} end
- if not vty.backColorBuff[x][y+_n] then vty.backColorBuff[x][y+_n] = colors.black end
- vty.screenBuff[x][y] = vty.screenBuff[x][y+_n]
- vty.textColorBuff[x][y] = vty.textColorBuff[x][y+_n]
- vty.backColorBuff[x][y] = vty.backColorBuff[x][y+_n]
- end
- end
- end
- for y = height - _n + 1, height, 1 do
- for x = 1, width, 1 do
- vty.screenBuff[x][y] = " "
- vty.backColorBuff[x][y] = colors.back
- vty.textColorBuff[x][y] = colors.white
- end
- end
- if vty.active then vty.stdout.scroll(_n) end
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to read from
- -- @description reads what user typed, ends when user presses [enter]
- -- @return user input
- --------------------------------------------------------------------------------
- VTY.read = function(vty, _sReplaceChar, _tHistory)
- VTY.setCursorBlink(vty, true )
- local sLine = ""
- local nHistoryPos = nil
- local nPos = 0
- if _sReplaceChar then
- _sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
- end
- local w, h = vty.stdout.getSize()
- local sx, sy = VTY.getCursorPos(vty)
- local function redraw( _sCustomReplaceChar )
- local nScroll = 0
- if sx + nPos >= w then
- nScroll = (sx + nPos) - w
- end
- VTY.setCursorPos(vty, sx, sy )
- local sReplace = _sCustomReplaceChar or _sReplaceChar
- if sReplace then
- VTY._write(vty, string.rep(sReplace, string.len(sLine) - nScroll) )
- else
- VTY.write(vty, string.sub( sLine, nScroll + 1 ) )
- end
- VTY.setCursorPos(vty, sx + nPos - nScroll, sy )
- end
- while true do
- local sEvent, param = os.pullEvent()
- if sEvent == "char" then
- sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
- nPos = nPos + 1
- redraw()
- elseif sEvent == "key" then
- if param == keys.enter then
- -- Enter
- break
- elseif param == keys.left then
- -- Left
- if nPos > 0 then
- nPos = nPos - 1
- redraw()
- end
- elseif param == keys.right then
- -- Right
- if nPos < string.len(sLine) then
- nPos = nPos + 1
- redraw()
- end
- elseif param == keys.up or param == keys.down then
- -- Up or down
- if _tHistory then
- redraw(" ");
- if param == keys.up then
- -- Up
- if nHistoryPos == nil then
- if #_tHistory > 0 then
- nHistoryPos = #_tHistory
- end
- elseif nHistoryPos > 1 then
- nHistoryPos = nHistoryPos - 1
- end
- else
- -- Down
- if nHistoryPos == #_tHistory then
- nHistoryPos = nil
- elseif nHistoryPos ~= nil then
- nHistoryPos = nHistoryPos + 1
- end
- end
- if nHistoryPos then
- sLine = _tHistory[nHistoryPos]
- nPos = string.len( sLine )
- else
- sLine = ""
- nPos = 0
- end
- redraw()
- end
- elseif param == keys.backspace then
- -- Backspace
- if nPos > 0 then
- redraw(" ");
- sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
- nPos = nPos - 1
- redraw()
- end
- elseif param == keys.home then
- -- Home
- nPos = 0
- redraw()
- elseif param == keys.delete then
- if nPos < string.len(sLine) then
- redraw(" ");
- sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 )
- redraw()
- end
- elseif param == keys["end"] then
- -- End
- nPos = string.len(sLine)
- redraw()
- end
- end
- end
- VTY.setCursorBlink(vty, false )
- VTY.setCursorPos(vty, w + 1, sy )
- VTY.print(vty)
- return sLine
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to get size
- -- @param(_n) lines to scroll
- -- @return width, height of the VTY
- --------------------------------------------------------------------------------
- VTY.getSize = function(vty)
- return vty.stdout.getSize()
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to get cursor position
- -- @return x, y cooridinate of cursor on VTY
- --------------------------------------------------------------------------------
- VTY.getCursorPos = function(vty)
- return vty.cursorX, vty.cursorY
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to set cursor position
- -- @param(x) X coordinate to set
- -- @param(y) Y coordinate to set
- --------------------------------------------------------------------------------
- VTY.setCursorPos = function(vty, x, y)
- vty.cursorX = x
- vty.cursorY = y
- if vty.active then
- vty.stdout.setCursorPos(x, y)
- end
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to set cursor blinking
- -- @param(_blink) true - cursor blinks, false - not
- --------------------------------------------------------------------------------
- VTY.setCursorBlink = function(vty, _blink)
- vty.cursorBlink = _blink
- if vty.active then
- vty.stdout.setCursorBlink(_blink)
- end
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to set text color
- -- @param(color) self explaining
- --------------------------------------------------------------------------------
- VTY.setTextColor = function(vty, color)
- if vty.color then
- vty.textColor = color
- if vty.active then
- vty.stdout.setTextColor(color)
- end
- end
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to set back color
- -- @param(color) self explaining
- --------------------------------------------------------------------------------
- VTY.setBackgroundColor = function(vty, color)
- if vty.color then
- vty.backColor = color
- if vty.active then
- vty.stdout.setBackgroundColor(color)
- end
- end
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to set cursor position
- -- @return is cursor blinking
- --------------------------------------------------------------------------------
- VTY.getCursorBlink = function(vty)
- return vty.cursorBlink
- end
- --------------------------------------------------------------------------------
- -- @param(vty) VTY to clear line
- -- @description clears current line on screen of specified VTY
- --------------------------------------------------------------------------------
- VTY.clearLine = function(vty)
- local width, height = vty.stdout.getSize()
- for x = 1, width, 1 do
- if not vty.screenBuff[x] then
- vty.screenBuff[x] = {}
- end
- vty.screenBuff[x][vty.cursorY] = " "
- vty.backColorBuff[x][vty.cursorY] = vty.backColor;
- end
- if vty.active then vty.stdout.clearLine() end
- end
- -- NOT SUPORTED YET --
- VTY.setTextColour = function(vty, color) end
- VTY.setBackgroundColour = function(vty, color) end
- -- END NOT SUPPORTED YET --
- VTY.new = function(_stdin, _stdout, nullin)
- local self = {}
- self.screenBuff = {}
- self.textColorBuff = {}
- self.backColorBuff = {}
- self.textColor = colors.white
- self.backColor = colors.black
- self.cursorX = 1
- self.cursorY = 1
- self.cursorBlink = true
- self.color = _stdout.isColor()
- self.active = false
- self.stdin = nullin
- self.stdout = task.nullout
- local function run(n, _term, PID)
- _G = getfenv(1)
- os.enableAPI("fs")
- local welcome = os.version().." VTY "..tostring(term.id)..", PID="..tostring(PID)
- term.print(welcome)
- local fnFile, err = loadfile( fs.combine(os.getPath(), "/bin/console" ))
- if fnFile then
- local _api = getfenv(1)
- local tEnv = {}
- setmetatable( tEnv, { __index = _api } )
- setfenv( fnFile, tEnv )
- term.print("OK")
- fnFile(PID)
- end
- term.print("ERROR")
- end
- local vtyTerm = {}
- vtyTerm._write = function( sText ) return VTY._write(self, sText) end
- vtyTerm.write = function( sText ) return VTY.write(self, sText) end
- vtyTerm.print = function( ... ) return VTY.print(self, ...) end
- vtyTerm.read = function(passwordChar, tHistory) return VTY.read(self, passwordChar, tHistory) end
- vtyTerm.setCursorPos = function(x, y) return VTY.setCursorPos(self, x, y) end
- vtyTerm.getCursorPos = function() return VTY.getCursorPos(self) end
- vtyTerm.setCursorBlink = function( blink ) return VTY.setCursorBlink(self, blink) end
- vtyTerm.getCursorBlink = function() return VTY.getCursorPos(self) end
- vtyTerm.setTextColor = function( color ) VTY.setTextColor(self, color) end
- vtyTerm.setTextColour = vtyTerm.setTextColor
- vtyTerm.setBackgroundColor = function( color ) VTY.setBackgroundColor(self, color) end
- vtyTerm.setBackgroundColour = vtyTerm.setBackgroundColor
- vtyTerm.clear = function() return VTY.clear(self) end
- vtyTerm.clearLine = function() return VTY.clearLine(self) end
- vtyTerm.scroll = function( n ) return VTY.scroll(self, n) end
- vtyTerm.isColor = function() return self.color end
- vtyTerm.isColour = vtyTerm.isColor
- vtyTerm.getSize = function() return _stdout.getSize() end
- vtyTerm.id = (#vtys) + 1
- local task = task.new("VTY"..tostring( (#vtys) + 1 ), run, baseAPI, self.stdin, vtyTerm, vtyTerm.id, vtyTerm)
- self.activate = function()
- self.active = true
- self.stdin = _stdin
- self.stdout = _stdout
- task.setStdin(self.stdin)
- VTY.repaint(self)
- end
- self.deactivate = function()
- self.active = false
- self.stdin = nullin
- self.stdout = task.nullout
- task.setStdin(self.stdin)
- end
- table.insert(vtys, self)
- return self
- end
- -- OS FUNCTIONS --
- function run(PID)
- os.enableAPI("colors")
- os.enableAPI("config")
- local conf = config.open(fs.combine(os.getPath(), "etc/vty"))
- local count = tonumber(conf.get("General", "VTY count", "4")) or 4
- for i = 1, count, 1 do
- VTY.new(task.stdin, task.stdout, task.nullin)
- end
- conf.save()
- os.addEventListener('timer')
- os.addEventListener('key')
- local controlTime = 0
- local lastVTY = 1
- print("VTY manager loaded, please wait 5 seconds")
- local timer = os.startTimer(5)
- while true do
- local e, p1 = os.pullEvent()
- if e == "timer" and p1 == timer then
- vtys[1].activate()
- end
- if e == "key" then
- if p1 == 29 then -- 29 = L_CTRL
- controlTime = os.clock()
- elseif p1 > 1 and p1 < 11 and controlTime+0.6 > os.clock() then -- 2 = numb_1 11 = numb_0
- local numb = p1 - 1
- if vtys[numb] then
- vtys[lastVTY].deactivate()
- vtys[numb].activate()
- lastVTY = numb
- end
- end
- end
- end
- end
- ]]},
- {["type"]="file", ["name"]="console", contents=[[
- --BEGIN SHELL HEADER--
- --END SHELL HEADER--
- local bExit = false
- local sDir = ""
- local sPath = ".:/"..fs.combine(os.getPath(), "bin")..".:/rom/programs"
- local tAliases = {}
- local tProgramStack = {}
- local shell = {}
- local tEnv = getfenv(1)
- tEnv["shell"] = shell
- 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
- -- Colours
- local promptColour, textColour, bgColour
- if term.isColor() 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
- term.print( "No such program" )
- 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 ) -- to modify
- 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
- term.setBackgroundColor( bgColour )
- term.setTextColour( promptColour )
- -- term.clear(bgColour)
- term.setCursorPos(1,2)
- term.print( os.version() )
- term.setTextColour( textColour )
- os.addEventListener("key")
- os.addEventListener("char")
- -- Read commands and execute them
- local tCommandHistory = {}
- while true do
- term.setBackgroundColor( bgColour )
- term.setTextColour( promptColour )
- term.write( shell.dir() .. "> " )
- term.setTextColour( textColour )
- local sLine = term.read( nil, tCommandHistory )
- table.insert( tCommandHistory, sLine )
- runLine( sLine )
- end
- ]]},
- {["type"]="file", ["name"]="edit", contents=[[
- os.enableAPI("fs")
- os.enableAPI("io")
- os.enableAPI("keys")
- os.enableAPI("math")
- -- Get file to edit
- local tArgs = { ... }
- if #tArgs == 0 then
- print( "Usage: edit <path>" )
- 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
- 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.lime
- 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 = {"Save", "Exit", "Print"}
- local tMenuItems = {"Save", "Exit"}
- local sStatus = "Press Ctrl to access menu"
- 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 = pcall( innerSave )
- if file then
- file.close()
- end
- return ok
- 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 function redrawText()
- for y=1,h-1 do
- term.setCursorPos( 1 - scrollX, y )
- term.clearLine()
- local sLine = tLines[ y + scrollY ]
- if sLine ~= nil then
- writeHighlighted( sLine )
- end
- end
- term.setCursorPos( x - scrollX, y - scrollY )
- end
- local function redrawLine(_nY)
- local sLine = tLines[_nY]
- term.setCursorPos( 1 - scrollX, _nY - scrollY )
- term.clearLine()
- writeHighlighted( sLine )
- term.setCursorPos( x - scrollX, _nY - scrollY )
- end
- local function setLeftStatus()
- end
- local function redrawMenu()
- term.setCursorPos( 1, h )
- term.clearLine()
- local sLeft, sRight
- local nLeftColour, nLeftHighlight1, nLeftHighlight2
- if bMenu then
- local sMenu = ""
- for n,sItem in ipairs( tMenuItems ) do
- if n == nMenuItem then
- nLeftHighlight1 = sMenu:len() + 1
- nLeftHighlight2 = sMenu:len() + sItem:len() + 2
- end
- sMenu = sMenu.." "..sItem.." "
- end
- sLeft = sMenu
- nLeftColour = textColour
- else
- sLeft = sStatus
- nLeftColour = highlightColour
- end
- -- Left goes last so that it can overwrite the line numbers.
- sRight = "Ln "..y
- term.setTextColour( highlightColour )
- term.setCursorPos( w-sRight:len() + 1, h )
- term.write(sRight)
- sRight = tostring(y)
- term.setTextColour( textColour )
- term.setCursorPos( w-sRight:len() + 1, h )
- term.write(sRight)
- if sLeft then
- term.setCursorPos( 1, h )
- term.setTextColour( nLeftColour )
- term.write(sLeft)
- if nLeftHighlight1 then
- term.setTextColour( highlightColour )
- term.setCursorPos( nLeftHighlight1, h )
- term.write( "[" )
- term.setCursorPos( nLeftHighlight2, h )
- term.write( "]" )
- end
- term.setTextColour( textColour )
- end
- -- Cursor highlights selection
- 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 sPrinterSide = nil
- for n,sSide in ipairs(rs.getSides()) do
- if peripheral.isPresent(sSide) and peripheral.getType(sSide) == "printer" then
- sPrinterSide = sSide
- break
- end
- end
- if not sPrinterSide then
- sStatus = "No printer attached"
- return
- end
- local nPage = 0
- local sName = fs.getName( sPath )
- local printer = peripheral.wrap(sPrinterSide)
- if printer.getInkLevel() < 1 then
- sStatus = "Printer out of ink"
- return
- elseif printer.getPaperLevel() < 1 then
- sStatus = "Printer out of paper"
- return
- end
- local terminal = {
- getCursorPos = printer.getCursorPos,
- setCursorPos = printer.setCursorPos,
- getSize = printer.getPageSize,
- write = printer.write,
- }
- terminal.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.restore()
- redrawMenu()
- term.redirect( terminal )
- 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( terminal )
- local ok, error = pcall( function()
- term.scroll()
- for n, sLine in ipairs( tLines ) do
- print( sLine )
- end
- end )
- term.restore()
- 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
- }
- local function doMenuItem( _n )
- tMenuFuncs[tMenuItems[_n] ]()
- if bMenu then
- bMenu = false
- term.setCursorBlink( true )
- end
- redrawMenu()
- end
- local function setCursor( x, y )
- 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
- if bRedraw then
- redrawText()
- end
- term.setCursorPos( screenX, screenY )
- -- Statusbar now pertains to menu, it would probably be safe to redraw the menu on every key event.
- redrawMenu()
- end
- -- Actual program functionality begins
- load(sPath)
- term.setBackgroundColour( bgColour )
- term.clear()
- term.setCursorPos(x,y)
- term.setCursorBlink( true )
- redrawText()
- redrawMenu()
- -- Handle input
- while bRunning do
- local sEvent, param, param2, param3 = os.pullEvent()
- if sEvent == "key" then
- if param == keys.up then
- -- Up
- if not bMenu then
- if y > 1 then
- -- Move cursor up
- y = y - 1
- x = math.min( x, string.len( tLines[y] ) + 1 )
- setCursor( x, y )
- end
- end
- elseif param == keys.down then
- -- Down
- if not bMenu then
- -- Move cursor down
- if y < #tLines then
- y = y + 1
- x = math.min( x, string.len( tLines[y] ) + 1 )
- setCursor( x, y )
- end
- end
- elseif param == keys.tab then
- -- Tab
- if not bMenu then
- local sLine = tLines[y]
- -- Indent line
- -- IN CASE OF INSERT TAB IN PLACE:
- -- tLines[y] = string.sub(sLine,1,x-1) .. " " .. string.sub(sLine,x)
- tLines[y]=" "..tLines[y]
- x = x + 2
- setCursor( x, y )
- redrawLine(y)
- end
- elseif param == keys.pageUp then
- -- Page Up
- if not bMenu then
- -- Move up a page
- local sx,sy=term.getSize()
- y=y-sy-1
- if y<1 then y=1 end
- x = math.min( x, string.len( tLines[y] ) + 1 )
- setCursor( x, y )
- end
- elseif param == keys.pageDown then
- -- Page Down
- if not bMenu then
- -- Move down a page
- local sx,sy=term.getSize()
- if y<#tLines-sy-1 then
- y = y+sy-1
- else
- y = #tLines
- end
- x = math.min( x, string.len( tLines[y] ) + 1 )
- setCursor( x, y )
- end
- elseif param == keys.home then
- -- Home
- if not bMenu then
- -- Move cursor to the beginning
- x=1
- setCursor(x,y)
- end
- elseif param == keys["end"] then
- -- End
- if not bMenu then
- -- Move cursor to the end
- x = string.len( tLines[y] ) + 1
- setCursor(x,y)
- end
- elseif param == keys.left then
- -- Left
- if not bMenu then
- if x > 1 then
- -- Move cursor left
- x = x - 1
- elseif x==1 and y>1 then
- x = string.len( tLines[y-1] ) + 1
- y = y - 1
- end
- setCursor( x, y )
- 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
- if x < string.len( tLines[y] ) + 1 then
- -- Move cursor right
- x = x + 1
- elseif x==string.len( tLines[y] ) + 1 and y<#tLines then
- x = 1
- y = y + 1
- end
- setCursor( x, y )
- 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 then
- if x < string.len( tLines[y] ) + 1 then
- local sLine = tLines[y]
- tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1)
- redrawLine(y)
- elseif y<#tLines then
- tLines[y] = tLines[y] .. tLines[y+1]
- table.remove( tLines, y+1 )
- redrawText()
- redrawMenu()
- end
- end
- elseif param == keys.backspace then
- -- Backspace
- if not bMenu then
- if x > 1 then
- -- Remove character
- local sLine = tLines[y]
- tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x)
- redrawLine(y)
- x = x - 1
- setCursor( x, 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 )
- redrawText()
- x = sPrevLen + 1
- y = y - 1
- setCursor( x, y )
- end
- end
- elseif param == keys.enter then
- -- Enter
- if not bMenu 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) )
- redrawText()
- x = spaces+1
- y = y + 1
- setCursor( x, y )
- else
- -- Menu selection
- doMenuItem( nMenuItem )
- end
- elseif param == keys.leftCtrl or param == keys.rightCtrl then
- -- Menu toggle
- bMenu = not bMenu
- if bMenu then
- term.setCursorBlink( false )
- nMenuItem = 1
- else
- term.setCursorBlink( true )
- end
- redrawMenu()
- end
- elseif sEvent == "char" then
- if not bMenu then
- -- Input text
- local sLine = tLines[y]
- tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
- redrawLine(y)
- x = x + string.len( param )
- setCursor( x, y )
- else
- -- 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 == "mouse_click" then
- if not bMenu then
- if param == 1 then
- -- Left click
- local cx,cy = param2, param3
- if cy < h then
- y = math.min( math.max( scrollY + cy, 1 ), #tLines )
- x = math.min( math.max( scrollX + cx, 1 ), string.len( tLines[y] ) + 1 )
- setCursor( x, y )
- 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
- end
- end
- -- Cleanup
- term.clear()
- term.setCursorBlink( false )
- term.setCursorPos( 1, 1 )
- ]]},
- {["type"]="file", ["name"]="list", contents=[[
- os.enableAPI("fs")
- os.enableAPI("textutils")
- local tArgs = { ... }
- -- Get all the files in the directory
- local sDir = shell.dir()
- if tArgs[1] ~= nil then
- sDir = shell.resolve( tArgs[1] )
- end
- -- Sort into dirs/files, and calculate column count
- local tAll = fs.list( sDir )
- local tFiles = {}
- local tDirs = {}
- for n, sItem in pairs( tAll ) do
- if 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.lime, tDirs, colours.white, tFiles )
- else
- textutils.pagedTabulate( tDirs, tFiles )
- end
- ]]},
- {["type"]="file", ["name"]="lua", contents=[[
- local tArgs = { ... }
- os.addEventListener("key")
- os.addEventListener("char")
- local bRunning = true
- local tCommandHistory = {}
- local tEnv = {
- ["exit"] = function()
- bRunning = false
- end,
- }
- setmetatable( tEnv, { __index = getfenv() } )
- if term.isColour() then
- term.setTextColour( colours.yellow )
- end
- print( "Interactive Lua prompt." )
- term.print( "Call exit() to exit." )
- term.setTextColour( colours.white )
- while bRunning do
- --if term.isColour() then
- -- term.setTextColour( colours.yellow )
- --end
- term.write( "lua> " )
- --term.setTextColour( colours.white )
- local s = term.read( nil, tCommandHistory )
- table.insert( tCommandHistory, s )
- local nForcePrint = 0
- local func, e = loadstring( s, "lua" )
- local func2, e2 = loadstring( "return "..s, "lua" )
- 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
- setfenv( func, tEnv )
- local tResults = { pcall( function() return func() end ) }
- if tResults[1] then
- local n = 1
- while (tResults[n + 1] ~= nil) or (n <= nForcePrint) do
- term.print( tostring( tResults[n + 1] ) )
- n = n + 1
- end
- else
- term.print( "ERROR: "..tResults[2] )
- end
- else
- term.print( "ERROR: "..e )
- end
- end
- ]]}
- }},
- {["type"]="directory", ["name"]="lib", contents={
- {["type"]="file", ["name"]="bit", contents=[[
- -- CC bit API can't work with large numbers
- -- this one is from: http://l-u-i-s.blogspot.com/2008/10/criptografia-md5-em-lua.html
- --BEGIN API HEADER--
- --VER 1.0
- --NAM bitAPI
- --END API HEADER--
- function imod(a, b)
- return a - math.floor(a / b) * b
- end
- function brshift(a, b)
- if (a < 0) then
- a = 4294967296 + a
- end
- if (b < 0) then
- b = 4294967296 + b
- end
- a = imod(a, 4294967296)
- b = imod(b, 4294967296)
- return math.floor(a / (2 ^ b))
- end
- function blshift(a, b)
- if (a < 0) then
- a = 4294967296 + a
- end
- if (b < 0) then
- b = 4294967296 + b
- end
- a = imod(a, 4294967296)
- b = imod(b, 4294967296)
- return math.floor(a * (2 ^ b))
- end
- function band(a, b)
- local i, v, r, b1, b2
- if (a < 0) then
- a = 4294967296 + a
- end
- if (b < 0) then
- b = 4294967296 + b
- end
- a = imod(a, 4294967296)
- b = imod(b, 4294967296)
- r = 0
- for i = 31, 0, -1 do
- v = 2 ^ i
- b1 = a >= v
- b2 = b >= v
- if (b1) and (b2) then
- r = r + v
- end
- if (b1) then
- a = a - v
- end
- if (b2) then
- b = b - v
- end
- end
- return r
- end
- function bor(a, b)
- local i, v, r, b1, b2
- if (a < 0) then
- a = 4294967296 + a
- end
- if (b < 0) then
- b = 4294967296 + b
- end
- a = imod(a, 4294967296)
- b = imod(b, 4294967296)
- r = 0
- for i = 31, 0, -1 do
- v = 2 ^ i
- b1 = a >= v
- b2 = b >= v
- if (b1) or (b2) then
- r = r + v
- end
- if (b1) then
- a = a - v
- end
- if (b2) then
- b = b - v
- end
- end
- return r
- end
- function bxor(a, b)
- local i, v, r, b1, b2
- if (a < 0) then
- a = 4294967296 + a
- end
- if (b < 0) then
- b = 4294967296 + b
- end
- a = imod(a, 4294967296)
- b = imod(b, 4294967296)
- r = 0
- for i = 31, 0, -1 do
- v = 2 ^ i
- b1 = a >= v
- b2 = b >= v
- if (b1 ~= b2) then
- r = r + v
- end
- if (b1) then
- a = a - v
- end
- if (b2) then
- b = b - v
- end
- end
- return r
- end
- function bnot(a)
- local i, v, r, b
- if (a < 0) then
- a = 4294967296 + a
- end
- a = imod(a, 4294967296)
- r = 0
- for i = 31, 0, -1 do
- v = 2 ^ i
- b = a >= v
- if (b) then
- a = a - v
- else
- r = r + v
- end
- end
- return r
- end
- ]]},
- {["type"]="file", ["name"]="config", contents=[[
- --NO HEADER - this API is loaded in startup
- -- User settings
- local defaultSectionName = "Default"
- -- private functions
- local function read(path)
- local currentSection = defaultSectionName
- if not fs.exists(path) then
- fs.create(path)
- end
- local file = io.open(path, "r")
- local data = {}
- if not file then
- return nil, "Cannot open file: "..path
- end
- data[currentSection] = {}
- for line in file:lines() do
- if line:sub(1, 1) == "[" and line:sub(-1) == "]" then
- currentSection = line:sub(2, line:len() - 1)
- if not data[currentSection] then
- data[currentSection] = {}
- end
- else
- i = line:find("=")
- if i ~= nil then
- local key = line:sub(1, i-1)
- local val = line:sub(i + 1)
- data[currentSection][key] = val
- end
- end
- end
- file:close()
- return data, nil
- end
- local function save(path, config)
- local lastSection = ""
- local file = io.open(path, "w")
- if not file then
- error("Cannot open file: "..path)
- return nil, "Cannot open file: "..path
- end
- if config[defaultSectionName] then
- for key, value in pairs(config[defaultSectionName]) do
- file:write(key.."="..value.."\n")
- end
- end
- for section, data in pairs(config) do
- if section ~= defaultSectionName then
- if lastSection ~= section then
- file:write("["..section.."]\n")
- lastSection = section
- end
- for key, value in pairs(data) do
- file:write(key.."="..value.."\n")
- end
- end
- end
- file:close()
- return true, nil
- end
- -- public functions
- function open(path)
- -- Private variables
- local self = {}
- local filePath = path
- -- Private fields
- local data, e = read(path)
- if not data then return nil, e end
- -- Public methods
- self.add = function(section, key, value)
- section = section or defaultSectionName
- if not data[section] then
- data[section] = {}
- end
- data[section][key] = tostring(value)
- end
- self.set = self.add
- self.get = function(section, key, default)
- section = section or defaultSectionName
- if not data[section] then
- data[section] = {}
- data[section][key] = default
- return default
- end
- if not data[section][key] then
- data[section][key] = default
- return default
- end
- return data[section][key]
- end
- self.save = function(location)
- if not location then
- local v, e = save(filePath, data)
- else
- local v, e = save(location, data)
- end
- return v, e
- end
- local mt = self
- mt.__newindex = function(table, key, value)
- printError("Attempt to illegally set "..key.." to: "..value)
- sleep(5)
- os.shutdown()
- end
- mt.__index = function(table, key)
- if type(key) ~= "function" then
- printError("Attempt to illegally read"..tostring(key))
- sleep(5)
- os.shutdown()
- end
- end
- setmetatable(self, mt)
- return self
- end
- new = open
- ]]},
- {["type"]="file", ["name"]="header", contents=[[
- --NO HEADER - this API is loaden in startup
- -- constans
- local headerStartPattern = "--BEGIN $NAME HEADER--"
- local headerStopPattern = "--END $NAME HEADER--"
- -- private functions
- --returns name, value
- --line in format: "--NAME VALUE"
- local function readValue(line)
- line = line:sub(3)
- n, space = line:find("(%w+) ")
- key = line:sub(1, space-1)
- value = line:sub(space+1)
- return key, value
- end
- --public functions
- function read(filePath, name)
- file = io.open(filePath, "r")
- local headerStarted = false
- name = string.upper(name)
- local headerStart = headerStartPattern:gsub("%$(%w+)", name)
- local headerEnd = headerStopPattern:gsub("%$(%w+)", name)
- local header = {}
- for line in file:lines() do
- if not headerStarted then
- if line == headerStart then
- headerStarted = true
- end
- else
- if line == headerEnd then
- break
- else
- local k, v = readValue(line)
- if k and v then
- header[k] = v
- end
- end
- end
- end
- return header
- end
- ]]},
- {["type"]="file", ["name"]="md5", contents=[[
- -- An MD5 mplementation in Lua, requires bitlib
- -- 10/02/2001 jcw@equi4.com
- -- @grabie2 actually somehow produces wrong hashes, but it's kind of good
- -- cause then any existing hash databases won't work with it ;)
- --BEGIN API HEADER--
- --VER 1.0
- --NAM md5API
- --END API HEADER--
- local md5=
- {
- ff=tonumber('ffffffff',16),
- consts={},
- }
- do
- string.gsub (]].."[["..[[ d76aa478 e8c7b756 242070db c1bdceee
- f57c0faf 4787c62a a8304613 fd469501
- 698098d8 8b44f7af ffff5bb1 895cd7be
- 6b901122 fd987193 a679438e 49b40821
- f61e2562 c040b340 265e5a51 e9b6c7aa
- d62f105d 02441453 d8a1e681 e7d3fbc8
- 21e1cde6 c33707d6 f4d50d87 455a14ed
- a9e3e905 fcefa3f8 676f02d9 8d2a4c8a
- fffa3942 8771f681 6d9d6122 fde5380c
- a4beea44 4bdecfa9 f6bb4b60 bebfbc70
- 289b7ec6 eaa127fa d4ef3085 04881d05
- d9d4d039 e6db99e5 1fa27cf8 c4ac5665
- f4292244 432aff97 ab9423a7 fc93a039
- 655b59c3 8f0ccc92 ffeff47d 85845dd1
- 6fa87e4f fe2ce6e0 a3014314 4e0811a1
- f7537e82 bd3af235 2ad7d2bb eb86d391
- 67452301 efcdab89 98badcfe 10325476 ]].."]]"..[[,
- '(%w+)',
- function (sString) table.insert(md5.consts,tonumber(sString,16)) end
- )
- end
- function Calc(s)
- local function transform(A,B,C,D)
- local f=function (x,y,z)
- return bit.bor(bit.band(x,y),bit.band(-x-1,z))
- end
- local g=function (x,y,z)
- return bit.bor(bit.band(x,z),bit.band(y,-z-1))
- end
- local h=function (x,y,z)
- return bit.bxor(x,bit.bxor(y,z))
- end
- local i=function (x,y,z)
- return bit.bxor(y,bit.bor(x,-z-1))
- end
- local z=function (f,a,b,c,d,x,s,ac)
- a=bit.band(a+f(b,c,d)+x+a*c,md5.ff)
- -- be *very* careful that left shift does not cause rounding!
- return bit.bor(bit.blshift(bit.band(a,bit.brshift(md5.ff,s)),s), bit.brshift(a,32-s)) + b
- end
- local a,b,c,d=A,B,C,D
- local t=md5.consts
- end
- local msgLen=string.len(s)
- local padLen=56-(msgLen%64)
- if (msgLen%64)>56 then padLen=padLen+64 end
- if padLen==0 then padLen=64 end
- s=s..string.char(128)..string.rep(string.char(0),padLen-1)
- s=s..leIstr(8*msgLen)..leIstr(0)
- assert((string.len(s)%64)==0)
- local t=md5.consts
- local a,b,c,d=t[65],t[66],t[67],t[68]
- for i=1,string.len(s),64 do
- X=leStrCuts(string.sub(s,i,i+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
- assert(table.getn(X)==16)
- X[0]=table.remove(X,1) -- zero based!
- a,b,c,d=transform(a,b,c,d)
- end
- local swap=function (w) return beInt(leIstr(w)) end
- return string.format("%08x%08x%08x%08x",swap(a),swap(b),swap(c),swap(d))
- end
- -- convert little-endian 32-bit int to a 4-char string
- function leIstr(i)
- local f=function (s) return string.char(bit.band(bit.brshift(i,s),255)) end
- return f(0)..f(8)..f(16)..f(24)
- end
- do -- from util.lua
- -- convert raw string to big-endian int
- function beInt(s)
- local v=0
- for i=1,string.len(s) do v=v*256+string.byte(s,i) end
- return v
- end
- -- convert raw string to little-endian int
- function leInt(s)
- local v=0
- for i=string.len(s),1,-1 do v=v*256+string.byte(s,i) end
- return v
- end
- -- cut up a string in little-endian ints of given size
- function leStrCuts(s,...)
- local o,r=1,{}
- for i=1,table.getn(arg) do
- table.insert(r,leInt(string.sub(s,o,o+arg[i]-1)))
- o=o+arg[i]
- end
- return r
- end
- end
- ]]},
- {["type"]="file", ["name"]="store", contents=[[
- --NO HEADER - this API is loaded in startup
- local conf = config.open(os.getPath().."/etc/store")
- local session = {}
- local dbPath = conf.get(nil, "database path", fs.combine(os.getPath(),"/var/store"))
- conf.save()
- print("Store API 1.0 loaded")
- if not fs.exists(dbPath) then
- term.printWarning("db file("..dbPath..") not found, created one")
- fs.create(dbPath)
- end
- -- private functions
- local function save(namespace)
- local data = textutils.serialize(session)
- local file = io.open(dbPath, "w")
- if file then
- file:write(data)
- file:close()
- else
- return false
- end
- end
- -- public functions
- function set(namespace, key, value)
- if type(session[namespace]) ~= "table" then
- session[namespace] = {}
- end
- session[namespace][key] = value
- save()
- end
- function get(namespace, key)
- if type(session[namespace]) ~= "table" then
- session[namespace] = {}
- end
- return session[namespace][key]
- end
- ]]},
- {["type"]="file", ["name"]="textutils", contents=[[
- --BEGIN API HEADER--
- --VER 0.4 netOS
- --NAM textutils
- --END API HEADER--
- function slowWrite( sText, nRate )
- nRate = nRate or 20
- if nRate < 0 then
- error( "rate must be positive" )
- end
- local nSleep = 1 / nRate
- sText = tostring( sText )
- local x,y = term.getCursorPos(x,y)
- local len = string.len( sText )
- for n=1,len do
- term.setCursorPos( x, y )
- sleep( nSleep )
- local nLines = write( string.sub( sText, 1, n ) )
- local newX, newY = term.getCursorPos()
- y = newY - nLines
- end
- end
- function slowPrint( sText, nRate )
- slowWrite( sText, nRate)
- print()
- end
- function formatTime( nTime, bTwentyFourHour )
- local sTOD = nil
- if not bTwentyFourHour then
- if nTime >= 12 then
- sTOD = "PM"
- else
- sTOD = "AM"
- end
- if nTime >= 13 then
- nTime = nTime - 12
- end
- end
- local nHour = math.floor(nTime)
- local nMinute = math.floor((nTime - nHour)*60)
- if sTOD then
- return string.format( "%d:%02d %s", nHour, nMinute, sTOD )
- else
- return string.format( "%d:%02d", nHour, nMinute )
- end
- end
- local function makePagedScroll( _term, _nFreeLines )
- local nativeScroll = _term.scroll
- local nFreeLines = _nFreeLines or 0
- return function( _n )
- for n=1,_n do
- nativeScroll( 1 )
- if nFreeLines <= 0 then
- local w,h = _term.getSize()
- _term.setCursorPos( 1, h )
- _term.write( "Press any key to continue" )
- os.pullEvent( "key" )
- _term.clearLine()
- _term.setCursorPos( 1, h )
- else
- nFreeLines = nFreeLines - 1
- end
- end
- end
- end
- function pagedPrint( _sText, _nFreeLines )
- local nativeScroll = term.scroll
- term.scroll = makePagedScroll( term, _nFreeLines )
- local result = print( _sText )
- term.scroll = nativeScroll
- return result
- end
- local function tabulateCommon( bPaged, ... )
- local tAll = { ... }
- local w,h = term.getSize()
- local nMaxLen = w / 8
- for n, t in ipairs( tAll ) do
- if type(t) == "table" then
- for n, sItem in pairs(t) do
- nMaxLen = math.max( string.len( sItem ) + 1, nMaxLen )
- end
- end
- end
- local nCols = math.floor( w / nMaxLen )
- local nLines = 0
- local function newLine()
- if bPaged and nLines >= (h-3) then
- pagedPrint()
- else
- print()
- end
- nLines = nLines + 1
- end
- local function drawCols( _t )
- local nCol = 1
- for n, s in ipairs( _t ) do
- if nCol > nCols then
- nCol = 1
- newLine()
- end
- local cx,cy = term.getCursorPos()
- cx = 1 + (nCol - 1) * (w / nCols)
- term.setCursorPos( cx, cy )
- term.write( s )
- nCol = nCol + 1
- end
- print()
- end
- for n, t in ipairs( tAll ) do
- if type(t) == "table" then
- if #t > 0 then
- drawCols( t )
- end
- elseif type(t) == "number" then
- term.setTextColor( t )
- end
- end
- end
- function tabulate( ... )
- tabulateCommon( false, ... )
- end
- function pagedTabulate( ... )
- tabulateCommon( true, ... )
- end
- local function serializeImpl( t, tTracking )
- local sType = type(t)
- if sType == "table" then
- if tTracking[t] ~= nil then
- error( "Cannot serialize table with recursive entries" )
- end
- tTracking[t] = true
- local result = "{"
- for k,v in pairs(t) do
- result = result..("["..serializeImpl(k, tTracking).."]="..serializeImpl(v, tTracking)..",")
- end
- result = result.."}"
- return result
- elseif sType == "string" then
- return string.format( "%q", t )
- elseif sType == "number" or sType == "boolean" or sType == "nil" then
- return tostring(t)
- else
- error( "Cannot serialize type "..sType )
- end
- end
- function serialize( t )
- local tTracking = {}
- return serializeImpl( t, tTracking )
- end
- function unserialize( s )
- local func, e = loadstring( "return "..s, "serialize" )
- if not func then
- return s
- else
- setfenv( func, {} )
- return func()
- end
- end
- function urlEncode( str )
- if str then
- str = string.gsub (str, "\n", "\r\n")
- str = string.gsub (str, "([^%w ])",
- function (c)
- return string.format ("%%%02X", string.byte(c))
- end)
- str = string.gsub (str, " ", "+")
- end
- return str
- end
- ]]},
- {["type"]="file", ["name"]="dialog", contents=[[
- --BEGIN API HEADER--
- --VER 1.0
- --NAM dialogAPI
- --END API HEADER--
- --dialog api
- --for advanced computers only
- --constants public
- --types
- INFO = 1
- MSGBOX = 2
- TEXTBOX = 3
- --buttons
- YESNO = 1
- OKCANCEL = 2
- OK = 3
- --constants private
- local keyEnter = 28
- local keyDown = 208
- local keyUp = 200
- local keyRight = 205
- local keyLeft = 203
- -- Private functions
- local function findLocation(width, height)
- local termW, termH = term.getSize()
- local x = (termW - width) / 2
- local y = (termH - height) / 2
- return x, y
- end
- local function drawBox(x, y, width, height, color, titleColor, title)
- term.setBackgroundColor(color)
- term.setTextColor(colors.black)
- for i = y, height+y, 1 do
- if title and i == y then
- term.setBackgroundColor(titleColor)
- elseif title and i == y+1 then
- term.setBackgroundColor(color)
- end
- for j = x, width+x, 1 do
- term.setCursorPos(j, i)
- term.write(" ")
- end
- end
- if title then
- local titleX = (width - title:len()) / 2 + x
- term.setBackgroundColor(titleColor)
- term.setCursorPos(titleX, y)
- write(title)
- end
- end
- local function drawButtons(x, y, width, backColor, buttons, default)
- default = default or 0
- if #buttons < default then default = #buttons - 1 end
- local function drawButton(butX, text, active)
- term.setCursorPos(butX, y)
- if active == true then
- term.setBackgroundColor(colors.yellow)
- term.setTextColor(colors.red)
- term.write("<"..text..">")
- else
- term.setBackgroundColor(backColor)
- term.setTextColor(colors.black)
- term.write("<"..text..">")
- end
- local curX, curY = term.getCursorPos()
- return curX - butX
- end
- local buttonsWidth = 0
- for n, v in pairs(buttons) do
- buttonsWidth = buttonsWidth + v:len() + 3
- end
- buttonsWidth = buttonsWidth -1
- local buttonsX = ((width - buttonsWidth) /2) + x
- local selected = false
- for n, v in pairs(buttons) do
- if tonumber(n) == default then selected = true else selected = false end
- buttonsX = buttonsX + drawButton(buttonsX, v, selected) + 1
- end
- end
- local function drawScrolls(x, y, height, backColor, active)
- term.setBackgroundColor(backColor)
- active = active or 0
- local curX, curY = term.getCursorPos()
- term.setCursorPos(x, y)
- if active == 1 or active == 0 then
- term.setTextColor(colors.yellow)
- else
- term.setTextColor(backColor)
- end
- term.write("/\\")
- if active == 2 or active == 0 then
- term.setTextColor(colors.yellow)
- else
- term.setTextColor(backColor)
- end
- term.setCursorPos(x, y + height)
- term.write("\\/")
- term.setCursorPos(curX, curY)
- end
- local function printText(x, y, width, height, color, backColor, text, scroll, active)
- local _ret = false
- term.setTextColor(color)
- term.setBackgroundColor(backColor)
- active = active or 0
- scroll = scroll or 0
- local curX = x
- local curY = y
- term.setCursorPos(curX, curY)
- local function completeLine()
- while curX < x + width do
- write(" ")
- curX = curX + 1
- end
- end
- local function newLine()
- if curY < y + height then
- if scroll == 0 then
- completeLine()
- curX = x
- curY = curY + 1
- term.setCursorPos(curX, curY)
- else scroll = scroll - 1 curX = x cury = y end
- return true
- else
- completeLine()
- --scrolling
- _ret = true
- return false
- end
- end
- while text:len() > 0 do
- local whitespace = string.match(text, "^[ \t]+")
- if whitespace then
- if scroll == 0 then term.write(whitespace) end
- curX = curX + whitespace:len()
- text = string.sub(text, whitespace:len() + 1)
- end
- if curX >= x+width then
- if not newLine() then break end
- end
- local newline = string.match(text, "^\n")
- if newline then
- if not newLine() then break end
- text = string.sub(text, 2)
- end
- local word = string.match(text, "^[^ \t\n]+")
- if word then
- text = string.sub(text, word:len() + 1)
- if word:len() > width then
- while word:len() > 0 do
- if scroll == 0 then term.write(string.sub(word, 1, 1)) end
- curX = curX + 1
- if curX >= x + width then
- if not newLine() then break end
- end
- word = string.sub(word, 2)
- end
- else
- if curX + word:len() > x + width then
- if not newLine() then break end
- end
- if scroll == 0 then term.write(word) end
- curX = curX + word:len()
- end
- end
- end
- completeLine()
- return _ret
- end
- local function drawTextBox(x, y, w, text)
- term.setBackgroundColor(colors.yellow)
- term.setCursorPos(x, y)
- for cx = x, x+w, 1 do
- term.write(" ")
- end
- term.setCursorPos(x, y+2)
- for cx = x, x+w, 1 do
- term.write(" ")
- end
- term.setCursorPos(x, y+1) write(" ")
- term.setCursorPos(x+w, y+1) write(" ")
- if #text <= w-3 then
- printText(x+2, y+1, w-4, 1, colors.black, colors.blue, text)
- term.setCursorPos(x + #text + 2, y+1)
- else
- text = text:sub(-(w -4))
- printText(x+2, y+1, w-4, 1, colors.black, colors.blue, text)
- end
- end
- --public functions
- function new(width, height, title, dialogType, buttons)
- local this = {}
- local x, y = 0
- this.setSize = function(w, h)
- x, y = findLocation(w, h)
- width = w
- height = h
- end
- this.setSize(width, height)
- dialogType = dialogType or INFO
- buttons = buttons or O
- this.setButtons = function(btns)
- if type(btns) == "number" then
- if btns == OK then
- buttons = {}
- buttons[0] = "OK"
- elseif btns == YESNO then
- buttons = {}
- buttons[0] = "Yes"
- buttons[1] = "No"
- elseif btns == OKCANCEL then
- buttons = {}
- buttons[0] = "OK"
- buttons[1] = "Cancel"
- else
- buttons = {}
- buttons[0] = "OK"
- end
- end
- end
- this.setButtons(buttons)
- this.title = title
- this.activeButton = 0
- this.scroll = 0
- this.drawScrolls = true
- this.canScrollDown = false
- if dialogType == MSGBOX then
- this.text = ""
- this.render = function(clear)
- clear = clear or false
- if clear then term.clear(colors.black) end
- drawBox(x, y, width, height, colors.blue, colors.lightBlue, this.title)
- drawButtons(x, y + height, width, colors.blue, buttons, this.activeButton)
- local textWidth = width - 3
- if not drawScrolls then textWidth = width -1 end
- this.canScrollDown = printText(x + 1, y + 1, textWidth, height - 3, colors.black, colors.blue, this.text, this.scroll)
- local scrolls = -1
- if this.scroll > 0 and this.canScrollDown then scrolls = 0
- elseif this.canScrollDown then scrolls = 2
- elseif this.scroll > 0 then scrolls = 1 end
- if drawScrolls and scrolls >= 0 then
- drawScrolls(x + width - 2, y + 1, height - 2, colors.blue, scrolls)
- end
- end
- this.update = function(e, p1, p2, p3, p4, p5)
- if e == "key" then
- if p1 == keyUp and this.scroll > 0 then
- this.scroll = this.scroll - 1
- elseif p1 == keyDown and this.canScrollDown then
- this.scroll = this.scroll +1
- elseif p1 == keyLeft and this.activeButton < #buttons then
- this.activeButton = this.activeButton +1
- elseif p1 == keyRight and this.activeButton > 0 then
- this.activeButton = this.activeButton -1
- end
- end
- end
- this.run = function(clear)
- print("run")
- local wasKey = true
- local wasChar = true
- if os.call then
- print("call")
- wasKey = os.isEventListener("key")
- wasChar = os.isEventListener("char")
- if not wasKey then os.addEventListener("key") end
- if not wasChar then os.addEventListener("char") end
- end
- clear = clear or false
- this.render(clear)
- while true do
- local e, p1 = os.pullEvent()
- if e == "key" and p1 == keyEnter then break end
- this.update(e, p1)
- this.render()
- end
- if not wasKey then os.removeEventListener("key") end
- if not wasCHar then os.removeEventListener("char") end
- return this.activeButton
- end
- elseif dialogType == INFO then
- this.render = function(clear)
- clear = clear or false
- if clear then term.clear(colors.black) end
- drawBox(x, y, width, height, colors.blue, colors.lightBlue, this.title)
- local textWidth = width - 3
- if not this.drawScrolls then textWidth = width -1 end
- this.canScrollDown = printText(x+1, y+1, textWidth, height -1, colors.black, colors.blue, this.text, this.scroll)
- local scrolls = -1
- if this.scroll > 0 and this.canScrollDown then scrolls = 0
- elseif this.canScrollDown then scrolls = 2
- elseif this.scroll > 0 then scrolls = 1 end
- if this.drawScrolls and scrolls >= 0 then
- drawScrolls(x + width - 2, y +1, height -2, colors.blue, scrolls)
- end
- end
- this.update = function(e, p1, p2, p3, p4, p5)
- if e == "key" then
- if p1 == keyDown and this.canScrollDown then
- this.scroll = this.scroll + 1
- elseif p1 == keyUp and this.scroll > 0 then
- this.scroll = this.scroll - 1
- end
- end
- end
- this.run = function(clear)
- local wasKey = false
- local wasChar = false
- if os.call then
- wasKey = os.isEventListener("key")
- wasChar = os.isEventListener("char")
- if not wasKey then os.addEventListener("key") end
- if not wasChar then os.addEventListener("char") end
- end
- this.render(clear)
- while true do
- local e, p1 = os.pullEvent()
- if e == "key" and p1 == keyEnter then break end
- this.update(e, p1)
- this.render()
- end
- if not wasKey then os.removeEventListener("key") end
- if not wasCHar then os.removeEventListener("char") end
- return this.activeButton
- end
- elseif dialogType == TEXTBOX then
- this.passwordChar = nil
- this.input = ""
- this.render = function(clear, part)
- part = part or 0
- term.setCursorBlink(false)
- if part == 0 then
- clear = clear or false
- if clear then term.clear(colors.black) end
- drawBox(x, y, width, height, colors.blue, colors.lightBlue, this.title)
- end
- local textWidth = width - 3
- local textHeight = height - 7
- if not this.drawScrolls then textWidth = width -1 end
- if part == 0 or part == 1 then
- this.canScrollDown = printText(x+1, y+1, textWidth, textHeight, colors.black, colors.blue, this.text, this.scroll)
- local scrolls = -1
- if this.scroll > 0 and this.canScrollDown then scrolls = 0
- elseif this.canScrollDown then scrolls = 2
- elseif this.scroll > 0 then scrolls = 1 end
- if this.drawScrolls and scrolls >= 0 then
- drawScrolls(x + width - 2, y +1, textHeight -1, colors.blue, scrolls)
- end
- drawButtons(x, y + height, width, colors.blue, buttons, this.activeButton)
- end
- if part == 0 or part == 2 then
- term.setCursorBlink(true)
- drawTextBox(x+1,y+textHeight+3, width-2, this.input)
- end
- end
- this.update = function(e, p1, p2, p3, p4, p5)
- if e == "key" then
- if p1 == keyDown and this.canScrollDown then
- this.scroll = this.scroll + 1
- return false
- elseif p1 == keyUp and this.scroll > 0 then
- this.scroll = this.scroll - 1
- return false
- elseif p1 == 14 then
- this.input = this.input:sub(1, #(this.input) - 1)
- return true
- end
- elseif e == "char" then
- this.input = this.input..p1
- return true
- end
- end
- this.run = function(clear)
- local wasKey = true
- local wasChar = true
- if os.call then
- wasKey = os.isEventListener("key")
- wasChar = os.isEventListener("char")
- if not wasKey then os.addEventListener("key") end
- if not wasChar then os.addEventListener("char") end
- end
- this.render(clear)
- local n = 0
- while true do
- local e, p1 = os.pullEvent()
- if e == "key" and p1 == keyEnter then break end
- if this.update(e, p1) and n < 5 then
- this.render(false, 2)
- elseif n < 5 then
- this.render(false, 1)
- else
- this.render(false)
- n = 0
- end
- n = n +1
- end
- if not wasKey then os.removeEventListener("key") end
- if not wasCHar then os.removeEventListener("char") end
- return this.activeButton, this.input
- end
- else
- return nil -- bad type
- end
- return this
- end
- ]]}
- }},
- {["type"]="directory", ["name"]="drv", contents={
- }},
- {["type"]="directory", ["name"]="home", contents={
- }},
- {["type"]="directory", ["name"]="etc", contents={
- }},
- {["type"]="directory", ["name"]="tmp", contents={
- }},
- {["type"]="directory", ["name"]="usr", contents={
- }},
- {["type"]="directory", ["name"]="var", contents={
- }},
- {["type"]="file", ["name"]="boot", contents=[[
- -- netOS bootloader --
- -- user set variables
- local osPath = "]]..installationDir..[["
- -- helper variables
- local libPath = "/"..fs.combine(osPath, "lib")
- local loadedLibraries = {}
- -- nice welcome string
- print("netOS bootloader")
- print("system is being loaded...")
- sleep(1)
- -- extends to existing APIs
- function os.getPath()
- return osPath
- end
- function fs.create(path)
- file = io.open(path, "a")
- if file then file:close() return true end
- return false
- end
- function term.printWarning(msg)
- term.setTextColor(colors.yellow)
- write("[WARNING] ")
- term.setTextColor(colors.white)
- print(msg)
- end
- function term.printError(msg)
- term.setTextColor(colors.red)
- write("[ERROR] ")
- term.setTextColor(colors.white)
- print(msg)
- end
- function term.printInfo(msg)
- term.setTextColor(colors.white)
- write("[INFO] ")
- term.setTextColor(colors.white)
- print(msg)
- end
- function term.printOK(msg)
- term.setTextColor(colors.lime)
- write("[OK] ")
- term.setTextColor(colors.white)
- print(msg)
- end
- function term.clear(color)
- color = color or colors.black
- term.setBackgroundColor(color)
- local curX, curY = term.getCursorPos()
- local width, height = term.getSize()
- for x = 0, width, 1 do
- for y = 0, height, 1 do
- term.setCursorPos(x, y)
- write(" ")
- end
- end
- term.setCursorPos(curX, curY)
- end
- _G["term"].print = print
- _G["term"].read = read
- function os.getLibraries()
- return loadedLibraries
- end
- function table.merge(t1, t2)
- for k, v in pairs(t2) do
- if (type(v) == "table") and (type(t1[k] or false) == "table") then
- table.merge(t1[k], t2[k])
- else
- t1[k] = v
- end
- end
- return t1
- end
- os.unloadAPI("textutils") -- need rewrite and reload with new environment
- os.unloadAPI("bit") -- can't work with big numbers
- -- loading static APIs
- os.loadAPI(libPath.."/header")
- os.loadAPI(libPath.."/config")
- os.loadAPI(libPath.."/store")
- --load user APIs
- local libList = fs.list(libPath)
- for n, file in pairs(libList) do
- filePath = "/"..fs.combine(libPath, file)
- local fileHeader = header.read(filePath, "api")
- if fileHeader["NAM"] and fileHeader["VER"] then
- if os.loadAPI(filePath) then
- os.unloadAPI(filePath)
- loadedLibraries[file] = filePath
- term.printOK("static library: "..fileHeader["NAM"].." "..fileHeader["VER"].." loaded");
- else
- term.printWarning("static library: "..fileHeader["NAM"].." "..fileHeader["VER"].."errored while loading");
- end
- end
- end
- -- starting OS up
- pcall(shell.run( "/"..fs.combine(osPath, "bin/netOS") ))
- print("netOS crashed, environment can't be restored. Please use CTRL+R to reboot your computer.")
- print("If you think that it is netOS bug, please send report here:")
- print("http://netos.robotronika.pl")
- while true do os.pullEventRaw() end -- hang
- ]]}
- }
- end
- -- program part 2
- local function waitForKey(timeout)
- timeout = timeout or 10
- print("Press any key to continue or wait "..timeout.." seconds")
- term.setCursorBlink(false)
- os.startTimer(timeout)
- os.pullEvent()
- end
- printInfo("Copying files")
- local ok = pcall(dotable, files, extract, installationDir)
- if ok then
- printOK("Coping files complete")
- printInfo("rebooting your computer, installation will be continued")
- waitForKey()
- os.reboot()
- else
- printError("Coping files failed, file system can be corrupted")
- waitForKey()
- term.clear()
- term.setCursorPos(1,1)
- error()
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement