Advertisement
grabie2

netOS installer

Jan 24th, 2013
172
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 113.64 KB | None | 0 0
  1. -- status variables
  2. local installationDir = "/"
  3. local useHTTP = false
  4. local pastebinCode = "quPaStrB"
  5. local pastebinVersion = "9thGUc2D"
  6.  
  7. local version = "alpha 1"
  8. local archiveFile = "netOS.archive"
  9.  
  10.  
  11. -- helper funtions
  12. local function dotable(_table, _f, ...)
  13.   for _, v in pairs(_table) do
  14.     _f(v, unpack(arg))
  15.   end
  16. end
  17.  
  18. local function extract(_table, path)
  19.   local filePath = fs.combine(path, _table["name"])
  20.   if _table["type"] == "file" then
  21.     local file = fs.open(filePath, "w")
  22.     file.write(_table["contents"])
  23.     file.close()
  24.   elseif _table["type"] == "directory" then
  25.     if not fs.exists(filePath) then fs.makeDir(filePath) end
  26.     dotable(_table["contents"], extract, filePath)
  27.   end
  28. end
  29.  
  30. function printError(msg)
  31.   term.setTextColor(colors.red)
  32.   write("[ERROR] ")
  33.   term.setTextColor(colors.white)
  34.   print(msg)
  35. end
  36.  
  37. function printInfo(msg)
  38.   term.setTextColor(colors.white)
  39.   write("[INFO] ")
  40.   term.setTextColor(colors.white)
  41.   print(msg)
  42. end
  43.  
  44. function printOK(msg)
  45.   term.setTextColor(colors.lime)
  46.   write("[OK] ")
  47.   term.setTextColor(colors.white)
  48.   print(msg)
  49. end
  50.  
  51. -- program part 1
  52.  
  53. term.clear()
  54. local welcome = {
  55. "-----------------------------------------",
  56. "|       netOS installation program      |",
  57. "| please answer to few simple questions |",
  58. "|   text in [ and ] is default answer   |",
  59. "|         for current question          |",
  60. "-----------------------------------------" }
  61.  
  62. local width, height = term.getSize()
  63. local welcomeWidth = string.len(welcome[1])
  64. local welcomePos = (width / 2) - (welcomeWidth / 2)
  65.  
  66. term.setTextColor(colors.lime)
  67.  
  68. for y = 1, #welcome do
  69.   term.setCursorPos(welcomePos, y)
  70.   term.write(welcome[y])
  71. end
  72.  
  73. term.setCursorPos(1, #welcome + 1)
  74.  
  75. term.write("Where should I install netOS[/]? ")
  76. installationDir = read()
  77. if installationDir == "" then
  78.   installationDir = "/"
  79. end
  80.  
  81. if http then
  82.   term.write("Use http to download newest version[yes]? ")
  83.   local answer = read()
  84.   if answer:lower() == "yes" or answer == "" then
  85.     useHTTP = true
  86.   end
  87. end
  88.  
  89. print()
  90. printInfo("Preparing to install netOS")
  91.  
  92. if useHTTP then
  93.   printInfo("Checking version")
  94.   local response = http.get(
  95.     "http://pastebin.com/raw.php?i="..textutils.urlEncode( pastebinVersion )
  96.     )
  97.   local remoteVersion = ""
  98.   if not response then
  99.     printError("Cannot connect to pastebin, using local version")
  100.   else
  101.     remoteVersion = response.readAll()
  102.     response.close();
  103.   end
  104.  
  105.   if remoteVersion ~= "" and remoteVersion ~= version then
  106.     printInfo("Downloading newest netOS")
  107.     local response = http.get(
  108.       "http://pastebin.com/raw.php?i="..textutils.urlEncode( pastebinCode )
  109.       )
  110.        
  111.     if response then
  112.       printOK( "Downloaded newest netOS" )
  113.      
  114.       local sResponse = response.readAll()
  115.       response.close()
  116.    
  117.       local file = fs.open( archiveFile, "w" )
  118.       file.write( sResponse )
  119.       file.close()
  120.      
  121.     else
  122.       printError( "Downloading newwest netOS failed" )
  123.     end
  124.  
  125.     printInfo("Unpacking archive")
  126.     local tEnv = {["installationDir"] = installationDir}
  127.     local archive = loadfile(archiveFile)
  128.     if not archive then
  129.       printError("Couldn't unpack archive, please report it to grabie2")
  130.     else
  131.       fs.delete(archiveFile)
  132.       setfenv(archive, tEnv)
  133.       archive()
  134.       if not tEnv.files then printError("Couldn't unpack archive, please report it to grabie2")
  135.       else files = tEnv.files end
  136.     end
  137.   else
  138.     printInfo("Local version is up to date")
  139.   end
  140. end
  141.  
  142.  
  143. -- files to copy
  144. if not files then
  145.  
  146.  
  147. files = {
  148. {["type"]="directory", ["name"]="bin", contents={
  149.   {["type"]="file", ["name"]="netOS", contents=[[
  150. --MAIN OS FILE
  151. --DO NOT MODIFY OR YOU CAN BRAKE YOUR COMPUTER
  152.  
  153. --private variables
  154.  
  155. local loadedDrvApis = {}
  156. local loadedDrivers = {}
  157. local devices = {}
  158.  
  159. local loadedLibraries = os.getLibraries()
  160.  
  161. local pidCounter = 1
  162. local messageQueue = {}
  163.  
  164. local allowedEvents = { --list of events on specified access levels
  165.   ["NONE"] = {
  166.     ["key"] = true,
  167.     ["char"] = true,
  168.     ["mouse_click"] = true,
  169.     ["mouse_drag"] = true,
  170.     ["mouse_scroll"] = true,
  171.     ["alarm"] = true,
  172.     ["timer"] = true,
  173.     ["os_message"] = true
  174.   },
  175.   ["USER"] = {
  176.     ["key"] = true,
  177.     ["char"] = true,
  178.     ["mouse_click"] = true,
  179.     ["mouse_drag"] = true,
  180.     ["mouse_scroll"] = true,
  181.     ["alarm"] = true,
  182.     ["timer"] = true,
  183.     ["http_failure"] = true,
  184.     ["http_success"] = true,
  185.     ["os_message"] = true
  186.   },
  187.   ["PERMITTED"] = {
  188.     ["key"] = true,
  189.     ["char"] = true,
  190.     ["mouse_click"] = true,
  191.     ["mouse_drag"] = true,
  192.     ["mouse_scroll"] = true,
  193.     ["alarm"] = true,
  194.     ["timer"] = true,
  195.     ["http_failure"] = true,
  196.     ["http_success"] = true,  
  197.     ["redstone"] = true,
  198.     ["os_message"] = true  
  199.   },
  200.   ["ROOT"] = {
  201.     ["key"] = true,
  202.     ["char"] = true,
  203.     ["mouse_click"] = true,
  204.     ["mouse_drag"] = true,
  205.     ["mouse_scroll"] = true,
  206.     ["alarm"] = true,
  207.     ["timer"] = true,
  208.     ["http_failure"] = true,
  209.     ["http_success"] = true,  
  210.     ["redstone"] = true,
  211.     ["disk"] = true,
  212.     ["disk_eject"] = true,
  213.     ["peripheral"] = true,
  214.     ["peripheral_detach"] = true,
  215.     ["rednet_message"] = true,
  216.     ["terminate"] = true,
  217.     ["os_message"] = true
  218.   },
  219.   ["DEBUG"] = { -- if debug flag(in etc/netos is enabled this level can be entered by ANY program
  220.                 -- if not then only by ROOT level programs
  221.     ["key"] = true,
  222.     ["char"] = true,
  223.     ["mouse_click"] = true,
  224.     ["mouse_drag"] = true,
  225.     ["mouse_scroll"] = true,
  226.     ["alarm"] = true,
  227.     ["timer"] = true,
  228.     ["http_failure"] = true,
  229.     ["http_success"] = true,  
  230.     ["disk"] = true,
  231.     ["disk_eject"] = true,
  232.     ["peripheral"] = true,
  233.     ["peripheral_detach"] = true,
  234.     ["debug"] = true,
  235.     ["os_message"] = true
  236.   }
  237. }
  238.  
  239. local bannedAPIs = { -- apis that tasks wouldn't be able to enable
  240.   ["NONE"] = {
  241.     ['peripheral'] = true,
  242.     ['rednet'] = true,
  243.     ['fs'] = true,
  244.     ['io'] = true,
  245.     ['http'] = true,
  246.     ['parallel'] = true,
  247.     ['coroutine'] = true,
  248.     ['redstone'] = true,
  249.     ['rs'] = true
  250.   },
  251.   ["USER"] = {
  252.     ['peripheral'] = true,
  253.     ['rednet'] = true,
  254.     ['coroutine'] = true,
  255.     ['redstone'] = true,
  256.     ['rs'] = true
  257.   },
  258.   ["PERMITTED"] = {
  259.     ['peripheral'] = true,
  260.     ['rednet'] = true,
  261.     ['coroutine'] = true
  262.   },
  263.   ["ROOT"] = {
  264. --    ['coroutine'] = true
  265.   },
  266.   ["DEBUG"] = { -- if debug flag(in etc/netos is enabled this level can be entered by ANY program
  267.                 -- if not then only by ROOT level programs
  268.   }
  269. }
  270.  
  271. local errorCodes = {
  272. [0x01] = "Wrong argument $ARG passed to $FUNC",
  273. [0x02] = "Task '$TASK' not found",
  274. [0x03] = "Task tried to make os call '$CALL' that doesn't exist",
  275. [0x04] = "Requested API '$API' doesn't exist",
  276. [0x05] = "Operation not permitted"
  277. }
  278.  
  279. -- paths definitions fo easier access in code
  280.  
  281. local drvPath = "/"..fs.combine(os.getPath(), "drv")
  282. local libPath = "/"..fs.combine(os.getPath(), "lib")
  283. local etcPath = "/"..fs.combine(os.getPath(), "etc")
  284.  
  285. local configPath = fs.combine(etcPath, "netos")
  286.  
  287. --remember native os API
  288.  
  289. local nativeOS = _G["os"]
  290.  
  291. --private functions
  292.  
  293. ----------------LOG---------------------
  294.  
  295. local function printOK(message)
  296.   --waits for logAPI
  297.   term.printOK(message)
  298. end
  299.  
  300. local function printInfo(message)
  301.   --waits for logAPI
  302.   term.printInfo(message)
  303. end
  304.  
  305. local function printWarning(message)
  306.   --waits for logAPI
  307.   term.printWarning(message)
  308. end
  309.  
  310. local function printError(message)
  311.   --waits for logAPI
  312.   term.printError(message)
  313. end
  314.  
  315. local function criticalError(message)
  316.   term.printError(message)
  317.   error("OS critical error")
  318.   sleep(5)
  319.   nativeOS.shutdown() -- just in case
  320. end
  321.  
  322. --OS API -- most functions will be replaced
  323.  
  324. _G["os"].shutdown = function()
  325.   print("Shutdown currently doesn't work, please use CTRL+S")
  326. end
  327.  
  328. _G["os"].reboot = function()
  329.   print("Reboot currently doesn't work, please use CTRL+R")
  330. end
  331.  
  332. function os.version()
  333.   return "netOS alpha 1"
  334. end
  335.  
  336. term.read = read
  337.  
  338. --other non public APIs
  339.  
  340. --basic API - API shared for ALL tasks - needed by them to exist----------------
  341. local baseAPI = {}
  342. baseAPI.type = type
  343. baseAPI.sleep = function(delay)
  344.   local wasTimer = os.isEventListener("timer")
  345.   os.addEventListener("timer")
  346.   local timer = os.startTimer(delay)
  347.   while true do
  348.     local e, p1 = os.pullEvent()
  349.     if e == "timer" and p1 == timer then
  350.       break
  351.     end
  352.   end
  353. end
  354. baseAPI.config = config
  355. baseAPI.store = store
  356. baseAPI.table = table
  357. baseAPI.string = string
  358. baseAPI.error = error
  359. baseAPI.pairs = pairs
  360. baseAPI.type = type
  361. baseAPI.ipairs = ipairs
  362. baseAPI.tostring = tostring
  363. baseAPI.tonumber = tonumber
  364. baseAPI.setmetatable = setmetatable
  365. baseAPI.getfenv = getfenv
  366. baseAPI.setfenv = setfenv
  367. baseAPI.pcall = pcall
  368. baseAPI.loadstring = loadstring
  369. baseAPI.loadfile = loadfile
  370. baseAPI.strchar = string.char
  371. baseAPI.strrep = string.rep
  372. baseAPI.strlen = string.len
  373. baseAPI.strsub = string.sub
  374. baseAPI.unpack = unpack
  375.  
  376. baseAPI.colors = _G["colors"]
  377. baseAPI.colours = _G["colours"]
  378. baseAPI.math = _G["math"]
  379. -- if you found other functions that should be added, please send a bug report on
  380. -- http://netos.robotronika.pl/newticket
  381. --os calls functions
  382. baseAPI.os = {}
  383. baseAPI.os.getComputerID = os.getComputerID
  384. baseAPI.os.getPath = os.getPath
  385. baseAPI.os.version = os.version
  386. baseAPI.os.pullEvent = os.pullEventRaw
  387. baseAPI.os.pullEventRaw = os.pullEventRaw
  388. --os calls functions------------------------------------------------------------
  389. --@param(f) os function
  390. --@param(...) function arguments
  391. --@returns what os call returned, see documentation on http://netos.robotronika.pl
  392. --@description function for making any os call
  393. baseAPI.os.call = function(f, ...)
  394.   return coroutine.yield("os_call", f, unpack(arg))
  395. end
  396. --@param(event) event to listen
  397. --@returns true if successfull, false if not
  398. --@description makes current task listen to specified event
  399. -- so task using os.pullEvent() can get it
  400. baseAPI.os.addEventListener = function(event)
  401.   return coroutine.yield("os_call", "addEventListener", event)
  402. end
  403. --@param(event) event to filter
  404. --@returns true if successfull, false if not
  405. --@description makes current task NOT listen to specified event
  406. -- so task using os.pullEvent() can't get it - for performance
  407. baseAPI.os.removeEventListener = function(event)
  408.   return coroutine.yield("os_call", "removeEventListener", event)
  409. end
  410. --@param(event) event to filter
  411. --@returns true if subscribed, false if not
  412. --@description checks if task listens to specified event
  413. baseAPI.os.isEventListener = function(event)
  414.   return coroutine.yield("os_call", "isEventListener", event)
  415. end
  416. --@returns true if successfull, false if not
  417. --@description asks system to shutdown, but can be refused
  418. -- NOT IMPLEMENTED YET
  419. baseAPI.os.shutdown = function()
  420.   return coroutine.yield("os_call", "shutdown")
  421. end
  422. --@returns true if successfull, false if not
  423. --@description asks system to shutdown, but can be refused
  424. -- NOT IMPLEMENTED YET
  425. baseAPI.os.reboot = function()
  426.   return coroutine.yield("os_call", "reboot")
  427. end
  428. --@param namespace to enable
  429. --@returns api if success or nil if not
  430. --@description ask system to enable specyfic API for task for eg. network
  431. --if successfull system adds API to task env, but also returns it
  432. baseAPI.os.enableAPI = function(api)
  433.   return coroutine.yield("os_call", "enableAPI", api)
  434. end
  435.  
  436. --shell API - API that shell will get-------------------------------------------
  437. local shellAPI = table.merge(_G, baseAPI)
  438. shellAPI.os = table.merge(baseAPI.os, _G["os"])
  439. shellAPI.term = _G["term"]
  440. shellAPI.os.shutdown = function(hard)
  441.   hard = hard or false
  442.   if hard then nativeOS.shutdown() -- this is shell - it can shutdown computer without operation of OS
  443.   else os.shutdown() end
  444. end
  445.  
  446. --driver API - API for drivers - some special functions and more permissions----
  447. local driverAPI = table.merge({}, baseAPI)
  448. driverAPI.term = _G["term"] -- in future will be replaced with file writing API
  449. driverAPI.print = print -- FOR TESTING ONLY
  450. driverAPI.write = write -- FOR TESTING ONLY
  451. driverAPI.os.queueEvent = os.queueEvent
  452. driverAPI.textutils = _G["textutils"] -- in future will be replaced with one without print functions or other serialization library
  453. driverAPI.fs = fs
  454. driverAPI.store = table.merge({}, _G["store"])
  455. driverAPI.header = _G["header"]
  456. driverAPI.config = _G["config"]
  457. driverAPI.peripheral = _G["peripheral"]
  458. driverAPI.os.shutdown = function(hard)
  459.   hard = hard or false
  460.   if hard then nativeOS.shutdown() -- this is driver - it can shutdown computer without operation of OS
  461.   else coroutine.yield("os_call", "shutdown") end
  462. end
  463. driverAPI.os.lock = function()
  464.   coroutine.yield("os_call", "lock")
  465. end
  466. driverAPI.os.unlock = function()
  467.   coroutine.yield("os_call", "unlock")
  468. end
  469.  
  470. --driverApi API - API for driverAPIs more permissions than normal program/lib---
  471. local driverApiAPI =  table.merge({}, baseAPI)
  472. driverApiAPI.term = _G["term"]
  473. driverApiAPI.textutils = _G["textutils"]
  474. driverApiAPI.fs = _G["fs"]
  475. driverApiAPI.store = _G["store"]
  476. driverApiAPI.header = _G["header"]
  477. driverApiAPI.config = _G["config"]
  478. driverApiAPI.os.queueEvent = os.queueEvent
  479.  
  480. ---------------DRIVERS------------------
  481.  
  482. local function loadDriverAPIs(APIs, drv)
  483.   for API in string.gmatch(APIs, "%a+") do
  484.     local APIheader = header.read(fs.combine(libPath, API), "drv_api")
  485.     if APIheader then
  486.       if APIheader["NAM"] and APIheader["VER"] then
  487.         if not loadedDrvApis[API] then
  488.           nativeOS.loadAPI(fs.combine(libPath, API))
  489.           APIheader.api = _G[API]
  490.           nativeOS.unloadAPI(fs.combine(libPath, API))
  491.           if not APIheader["drivers"] then APIheader["drivers"] = {} end
  492.           APIheader["drivers"][drv.APIName] = drv.api
  493.           APIheader.FILE = API
  494.           loadedDrvApis[API] = APIheader
  495.           -- loadedLibraries[API] = APIheader.api
  496.           printOK("Driver API: "..APIheader["NAM"].." "..APIheader["VER"].." loaded!")
  497.         else
  498.           loadedDrvApis[API][drv.NAM] = drv.api
  499.         end
  500.       else
  501.         printWarning("Driver API: \""..API.."\" has invalid header and couldn't be loaded!")
  502.       end
  503.     else
  504.       printWarning("Driver API: \""..API.."\" has invalid header and couldn't be loaded!")
  505.     end
  506.   end
  507. end
  508.  
  509. local function loadDriver(driver, API)
  510.   local tEnv = {}
  511.   setmetatable( tEnv, { __index = API } )
  512.   local fnAPI, err = loadfile( driver )
  513.   if fnAPI then
  514.     setfenv( fnAPI, tEnv )
  515.     fnAPI()
  516.   else
  517.     criticalError( err )
  518.     return false
  519.   end
  520.   local tAPI = {}
  521.   for k,v in pairs( tEnv ) do
  522.     tAPI[k] =  v
  523.   end
  524.   return tAPI
  525. end
  526.  
  527. local function loadDrivers(API)
  528.   local driverList = fs.list(drvPath)
  529.   for n, driver in pairs(driverList) do
  530.     local driverHeader = header.read(fs.combine(drvPath, driver), "DRIVER")
  531.     if driverHeader then
  532.       if driverHeader["NAM"] and driverHeader["VER"] and driverHeader["DEV"] and driverHeader["APIName"] and driverHeader["TYPE"] then
  533.         local api = loadDriver(fs.combine(drvPath, driver), API)
  534.         driverHeader["FILE"] = driver
  535.         driverHeader.api = api
  536.         loadedDrivers[ driverHeader["TYPE"] ] = driverHeader
  537.         printOK("Driver: \""..driverHeader["NAM"].." "..driverHeader["VER"].."\" loaded")
  538.         if api.getPublicAPI then
  539.           local publicAPIName, publicAPI = api.getPublicAPI()
  540.           if not type(publicAPIName) == "string" and loadedLibraries[publicAPIName] then
  541.            loadedLibraries[publicAPIName] = publicAPI
  542.            printOK("Driver: \""..driverHeader["NAM"].." has loaded public API \""..publicAPIName.."\"")
  543.           end
  544.         end
  545.         if driverHeader["API"] then loadDriverAPIs(driverHeader["API"], driverHeader) end
  546.       end
  547.     end
  548.   end
  549. end
  550.  
  551. ---------------DEVICES------------------
  552.  
  553. local function newDevice(type, side)
  554.   local cur = {}
  555.  
  556.   cur.type = type
  557.   cur.name = nil
  558.   cur.side = side
  559.   cur.API = {}
  560.  
  561.   return cur
  562. end
  563.  
  564. local function detectDevices()
  565.   local sides = rs.getSides()
  566.   local currentDevice = {}
  567.  
  568.   for n, side in pairs(sides) do
  569.     if peripheral.isPresent(side) then
  570.       currentDevice = newDevice(peripheral.getType(side):upper(), side)
  571.       if loadedDrivers[currentDevice.type] then
  572.         currentDevice.name = loadedDrivers[currentDevice.type]["DEV"]
  573.         currentDevice.api = loadedDrivers[currentDevice.type].api
  574.         devices[currentDevice.side] = currentDevice
  575.         printInfo("Found "..currentDevice.name.." on "..side.." side")
  576.       else
  577.         printWarning("Found unknown device on "..side.." side")
  578.       end
  579.     end  
  580.   end
  581. end
  582.  
  583. --load drivers etc
  584.  
  585.  
  586. loadDrivers(driverAPI)
  587. detectDevices()
  588.  
  589. --multitasking
  590.  
  591. local processes = {}
  592. local lockedProcess = nil  -- process that requested lock of terminal
  593. -- events are ONLY going to that process
  594.  
  595. local task = {} -- task API, only internal use
  596. -- in future this will be passed to main console menager
  597.  
  598.  
  599. --------------------------------------------------------------------------------
  600. -- task states
  601. task.TS_RUNNING = 1   -- task made os call, needs to be resumed ASAP
  602. task.TS_WAITING = 2   -- task waits for event to occur
  603. task.TS_SUSPENDED = 3 -- /not used yet/ task was suspended by parent task
  604. task.TS_CLOSING = 4   -- task was asked to close
  605. task.TS_ERRORED = 5   -- task has errored
  606. task.TS_CLOSED = 6    -- task has ended
  607.  
  608. -- message counter
  609. task.messages = 0
  610.  
  611. -- default streams
  612. task.stdout = {}
  613. task.stdin = {}
  614. task.stdout = term
  615. task.stdin.isColor = function() return true end
  616. task.stdin.key = "key"
  617. task.stdin.char = "char"
  618. task.stdin.mouse_click = "mouse_click"
  619. task.stdin.mouse_drag = "mouse_drag"
  620. task.stdin.mouse_scroll = "mouse_scroll"
  621. --empty streams
  622. local nullTerm = {}
  623. nullTerm.print = function(...) end
  624. nullTerm.write = function(...) end
  625. nullTerm.isColor = function() return false end
  626. nullTerm.isColour = function() return false end
  627. nullTerm.getSize = term.getSize
  628. nullTerm.clear = function() end
  629. nullTerm.getCursorPos = function() return 0, 0 end
  630. nullTerm.setCursorPos = function() end
  631. nullTerm.setCursorBlink = function(_blink) end
  632. nullTerm.scroll = function(_n) end
  633.  
  634. task.nullout = {}
  635. task.nullin = {}
  636. task.nullout = nullTerm
  637. task.nullin.isColor = function() return true end
  638. task.nullin.key = "null_key"
  639. task.nullin.char = "null_char"
  640. task.nullin.mouse_click = "null_mouse_click"
  641. task.nullin.mouse_drag = "null_mouse_drag"
  642. task.nullin.mouse_scroll = "null_mouse_scroll"
  643. --------------------------------------------------------------------------------
  644.  
  645. --os calls handlers-------------------------------------------------------------
  646. task.osCalls = {}
  647.  
  648. --------------------------------------------------------------------------------
  649. -- @param(PID) pid of the task to change
  650. -- @param(p1) event to subscribe to
  651. -- @param(p2 - p5) not used, but passed to function
  652. -- @returns true if successfull or false, error code, error message if not
  653. -- @description makes task listen to specified event
  654. --------------------------------------------------------------------------------
  655. task.osCalls.addEventListener = function(PID, p1, p2, p3, p4, p5)
  656.   if not p1 or type(p1) ~= "string" then
  657.     local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#1", FUNC = "os call 'addEventListener'"} )
  658.     return false, 0x01, errorMessage
  659.   end
  660.   -- TODO: check if process has access to passed event
  661.   processes[PID].addEventListener(p1)
  662.   return true
  663. end
  664.  
  665. --------------------------------------------------------------------------------
  666. -- @param(PID) pid of the task to change
  667. -- @param(p1) event to unsubscribe to
  668. -- @param(p2 - p5) not used, but passed to function
  669. -- @returns true if successfull or false, error code, error message if not
  670. -- @description makes task not listen to specified event
  671. --------------------------------------------------------------------------------
  672. task.osCalls.removeEventListener = function(PID, p1, p2, p3, p4, p5)
  673.   if not p1 or type(p1) ~= "string" then
  674.     local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#1", FUNC = "os call 'removeEventListener'"} )
  675.     return false, 0x01, errorMessage
  676.   end
  677.   processes[PID].removeEventListener(p1)
  678.   return true
  679. end
  680.  
  681. --------------------------------------------------------------------------------
  682. -- @param(PID) pid of the task to test
  683. -- @param(p1) event to chack
  684. -- @param(p2 - p5) not used, but passed to function
  685. -- @returns true if task subscribes to event or false if not
  686. -- @description checks if task subscribes to specified event
  687. --------------------------------------------------------------------------------
  688. task.osCalls.isEventListener = function(PID, p1, p2, p3, p4, p5)
  689.   if not p1 or type(p1) ~= "string" then
  690.     local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#1", FUNC = "os call 'removeEventListener'"} )
  691.     return false, 0x01, errorMessage
  692.   end
  693.   local isListener = false or processes[PID].eventListeners[p1]
  694.   return isListener
  695. end
  696.  
  697. --------------------------------------------------------------------------------
  698. -- @param(PID) pid of the task to test
  699. -- @param(p1 - p5) not used, but passed to function
  700. -- @returns PID
  701. --------------------------------------------------------------------------------
  702. task.osCalls.getPID = function(PID, p1, p2, p3, p4, p5)
  703.   return PID
  704. end
  705.  
  706. --------------------------------------------------------------------------------
  707. -- @param(PID) pid of the task to test
  708. -- @param(p1 - p5) not used, but passed to function
  709. -- @returns PID
  710. --------------------------------------------------------------------------------
  711. task.osCalls.reboot = function(PID, p1, p2, p3, p4, p5)
  712.   --TODO: check permissions
  713.   --nativeOS.reboot()
  714.   processes[PID].stdout.print("Reboot currently is not supported, please use CTRL+R instead")
  715. end
  716.  
  717. --------------------------------------------------------------------------------
  718. -- @param(PID) pid of the task to test
  719. -- @param(p1 - p5) not used, but passed to function
  720. -- @returns PID
  721. --------------------------------------------------------------------------------
  722. task.osCalls.shutdown = function(PID, p1, p2, p3, p4, p5)
  723.   --TODO: check permissions
  724.   --nativeOS.shutdown()
  725.   processes[PID].stdout.print("Shutdown currently is not supported, please use CTRL+S instead")
  726. end
  727.  
  728. --------------------------------------------------------------------------------
  729. -- @param(PID) pid of the task to lock execution to
  730. -- @param(p1 - p5) not used, but passed to function
  731. -- @returns nil
  732. --------------------------------------------------------------------------------
  733. task.osCalls.lock = function(PID, p1, p2, p3, p4, p5)
  734.   --TODO: check permissions
  735.   lockedProcess = PID
  736. end
  737.  
  738. --------------------------------------------------------------------------------
  739. -- @param(PID) pid of the task executing OS call
  740. -- @param(p1 - p5) not used, but passed to function
  741. -- @returns nil
  742. --------------------------------------------------------------------------------
  743. task.osCalls.unlock = function(PID, p1, p2, p3, p4, p5)
  744.   --TODO: check permissions
  745.   lockedProcess = nil
  746. end
  747.  
  748. --------------------------------------------------------------------------------
  749. -- @param(PID) pid of the task to test
  750. -- @param(p1 - p5) not used, but passed to function
  751. -- @returns PID
  752. --------------------------------------------------------------------------------
  753. task.osCalls.enableAPI = function(PID, p1, p2, p3, p4, p5)
  754.   -- TODO: user level checking
  755.   if not p1 or type(p1) ~= "string" then
  756.     local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#1", FUNC = "os call 'enableAPI'"} )
  757.     return false, 0x01, errorMessage
  758.   end
  759.   if _G[p1] or loadedLibraries[p1] or loadedDrvApis[p1] then
  760.     if not bannedAPIs["ROOT"][p1] then
  761.       local _api = {}
  762.       if loadedDrvApis[p1] then
  763.         _api = loadedDrvApis[p1].api
  764.       elseif loadedLibraries[p1] then
  765.         --actually function loadDriver just loads specified file with specified environment and returns it's environment
  766.         _api = loadDriver(loadedLibraries[p1], processes[PID].getenv())
  767.       else -- global API
  768.         _api = _G[p1]
  769.       end
  770.       processes[PID].setAPI(p1, _api) -- TODO: reload API with environment of asking task
  771.     else
  772.       local errorMessage = errorCodes[0x05]
  773.       return false, 0x05, errorMessage
  774.     end
  775.   else
  776.     local errorMessage = string.gsub(errorCodes[0x04], "%$(%w+)", {API = tostring(p1)} )
  777.     return false, 0x04, errorMessage
  778.   end
  779.   return true
  780. end
  781.  
  782.  
  783. --------------------------------------------------------------------------------
  784. -- @param(PID) pid of the task to test
  785. -- @param(p1) recipient od message, name of task or PID
  786. -- @param(p2) message
  787. -- @param(p3 - p5) not used, but passed to function
  788. -- @returns true if successfull or false, error code, error message if not
  789. --------------------------------------------------------------------------------
  790. task.osCalls.sendMessage = function(PID, p1, p2, p3, p4, p5)
  791.   if not p1 then
  792.     local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#1", FUNC = "os call 'sendMessage'"} )
  793.     return false, 0x01, errorMessage
  794.   elseif not p2 then
  795.     local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#2", FUNC = "os call 'sendMessage'"} )
  796.     return false, 0x01, errorMessage
  797.   elseif type(p2) ~= "string" and type(p2) ~= "number" then
  798.     local errorMessage = string.gsub(errorCodes[0x01], "%$(%w+)", {ARG = "#2", FUNC = "os call 'sendMessage'"} )
  799.     return false, 0x01, errorMessage
  800.   end
  801.   if processes[p1] then
  802.     processes[p1].addMessage(PID, p2)
  803.   else
  804.     -- find task by name
  805.     local found = false
  806.     if type(p2) == "string" then
  807.       for i=1, pidCounter, 1 do
  808.         if processes[i] and processes[i].getName() == p1 then
  809.           found = true
  810.           processes[i].addMessage(PID, p2)
  811.           break
  812.         end
  813.       end
  814.     end
  815.    
  816.     if not found then
  817.       local errorMessage = string.gsub(errorCodes[0x02], "%$(%w+)", {TASK = tostring(p2)} )
  818.       return false, 0x02, errorMessage
  819.     else
  820.       return true
  821.     end
  822.    
  823.   end
  824. end
  825.  
  826. local function installOSApi(_f)
  827.   local _env = getfenv(_f)
  828.  
  829.   _env["os"].run = function( _tEnv, _sPath, ... )
  830.     local tArgs = { ... }
  831.     local fnFile, err = loadfile( _sPath )
  832.     if fnFile then
  833.       local tEnv = _tEnv
  834.       setmetatable( tEnv, { __index = _env } )
  835.       setfenv( fnFile, tEnv )
  836.       local ok, err = pcall( function()
  837.         fnFile( unpack( tArgs ) )
  838.       end )
  839.       if not ok then
  840.         if err and err ~= "" then
  841.           _env.printError( err )
  842.         end
  843.         return false
  844.       end
  845.       return true
  846.     end
  847.     if err and err ~= "" then
  848.       _env.printError( err )
  849.     end
  850.     return false
  851.   end
  852. end
  853.  
  854. --------------------------------------------------------------------------------
  855. -- @param(_name) name of task, should be unique
  856. -- @param(_f) function to run as task
  857. -- @param(_api) environment to run task  
  858. -- @param(_stdin) mappings for user input events
  859. -- @param(_stdout) termAPI for task
  860. -- @param(...) arguments to pass to task
  861. -- @returns task object, can be dropped
  862. --------------------------------------------------------------------------------
  863. function task.new(_name, _f, _api, _stdin, _stdout, ...)
  864.   local self = {} -- task instance
  865.   local state = task.TS_RUNNING --task state
  866.   local PID = pidCounter -- task pid - dynamically allocated
  867.  
  868.   local parameters = arg -- parameters to pass to task if returnNeeded is true
  869.   table.insert(parameters, PID)
  870.   local returnNeeded = true --return to task event(false) or parameters(true)
  871.  
  872.   local messages = {} -- message queue
  873.   local routine = coroutine.create(_f) --task routine
  874.  
  875.   local stdin = _stdin
  876.   local stdout = _stdout
  877.  
  878. ----returns environment of current task-----------------------------------------
  879.   self.getenv = function(name, api)
  880.     local _env = getfenv(_f)
  881.     return _env
  882.   end
  883. ----adds API to task environment------------------------------------------------
  884.   self.setAPI = function(name, api)
  885.     local _env = getfenv(_f)
  886.     _env[name] = api
  887.     --setfenv(_f, _env) -- Should not be needed, _env is reference to environment
  888.   end
  889. ----returns name of task--------------------------------------------------------
  890.   self.getName = function()
  891.     return _name
  892.   end
  893. --sets stdout for this process--------------------------------------------------
  894.   self.setStdout = function(_stdout)
  895.     stdout = _stdout
  896.     self.stdout = stdout
  897.     self.setAPI("term", stdout)  
  898.     self.setAPI("print", stdout["print"])
  899.     self.setAPI("write", stdout["write"])
  900.     self.setAPI("read", stdout["read"])
  901.     self.setAPI("printError", stdout["print"])
  902.   end  
  903. ----pre initialization code-----------------------------------------------------
  904.   pidCounter = pidCounter + 1
  905.   if _api then setfenv(_f, _api) end
  906.   installOSApi(_f)
  907.   self.setStdout(_stdout)
  908. ----global variables------------------------------------------------------------
  909.    self.eventListeners = {["os_message"]='true'} -- which events will be passed to task
  910. --sets stdin for this process---------------------------------------------------
  911.   self.setStdin = function(_stdin)
  912.     if self.eventListeners[stdin.key] then
  913.       self.eventListeners[stdin.key] = nil
  914.       self.eventListeners[_stdin.key] = true
  915.     end
  916.     if self.eventListeners[stdin.char] then
  917.       self.eventListeners[stdin.char] = nil
  918.       self.eventListeners[_stdin.char] = true
  919.     end
  920.     if self.eventListeners[stdin.mouse_click] then
  921.       self.eventListeners[stdin.mouse_click] = nil
  922.       self.eventListeners[_stdin.mouse_click] = true
  923.     end
  924.     if self.eventListeners[stdin.mouse_drag] then
  925.       self.eventListeners[stdin.mouse_drag] = nil
  926.       self.eventListeners[_stdin.mouse_drag] = true
  927.     end
  928.     if self.eventListeners[stdin.mouse_scroll] then
  929.       self.eventListeners[stdin.mouse_scroll] = nil
  930.       self.eventListeners[_stdin.mouse_scroll] = true
  931.     end
  932.     stdin = _stdin
  933.   end
  934. ----returns state of task-------------------------------------------------------
  935.   self.getState = function()
  936.     return state
  937.   end
  938. ----returns PID of task---------------------------------------------------------
  939.   self.getPID = function()
  940.     return PID
  941.   end
  942. ----adds message to queue-------------------------------------------------------
  943.   -- @param(sender) PID of sender process
  944.   -- @param(message) message to send
  945.   self.addMessage = function(sender, message)
  946.     -- TODO: make it FIFO queue instead of FILO
  947.     table.insert(messages, {["sender"]=sender, ["data"]=message})
  948.     task.messages = task.messages + 1
  949.   end
  950. ----removes last message from queue and returns it------------------------------
  951.   -- TODO: make it FIFO queue instead of FILO
  952.   self.getMessage = function()
  953.     local message = messages[#messages]
  954.     messages[#messages] = nil
  955.     if not message then return nil end
  956.     task.messages = task.messages - 1
  957.     return message.data, message.sender
  958.   end
  959. ----adds event to event filter list---------------------------------------------
  960.   self.addEventListener = function(event)
  961.     if event == "key" then event = stdin.key
  962.     elseif event == "char" then event = stdin.char
  963.     elseif event == "mouse_click" then event = stdin.mouse_click
  964.     elseif event == "mouse_drag" then event = stdin.mouse_drag
  965.     elseif event == "mouse_scroll" then event = stdin.mouse_scroll
  966.     end
  967.     self.eventListeners[event] = 'true'
  968.   end
  969. ----removes event from filter list----------------------------------------------
  970.   self.removeEventListener = function(event)
  971.     if event == "key" then event = stdin.key
  972.     elseif event == "char" then event = stdin.char
  973.     elseif event == "mouse_click" then event = stdin.mouse_click
  974.     elseif event == "mouse_drag" then event = stdin.mouse_drag
  975.     elseif event == "mouse_scroll" then event = stdin.mouse_scroll
  976.     end
  977.     self.eventListeners[event] = nil
  978.   end
  979. ----runs task, runs os calls, moves task to proper queue if needed--------------
  980.   -- @param(...) arguments to pass to task IF returnNeeded is false
  981.   self.continue = function(...)
  982.     local ok, d1, d2, d3, d4, d7, d6, d7 = nil
  983.     self.setStdout(stdout)
  984.     if returnNeeded then
  985.       ok, d1, d2, d3, d4 = coroutine.resume(routine, unpack(parameters))
  986.       returnNeeded = false
  987.     else
  988.       if arg[1] then
  989.         if arg[1] == stdin.key then arg[1] = "key"
  990.         elseif arg[1] == stdin.char then arg[1] = "char"
  991.         elseif arg[1] == stdin.mouse_click then arg[1] = "mouse_click"
  992.         elseif arg[1] == stdin.mouse_drag then arg[1] = "mouse_drag"
  993.         elseif arg[1] == stdin.mouse_scroll then arg[1] = "mouse_scroll"
  994.         end
  995.       end
  996.       ok, d1, d2, d3, d4, d5, d6, d7  = coroutine.resume(routine, unpack(arg))
  997.     end
  998.     -- task error processing
  999.     if not ok then
  1000.       state = task.TS_ERRORED
  1001.       printError("Task '".._name.."' has errored: "..d1)
  1002.       return false
  1003.     end
  1004.    
  1005.     -- task end processing
  1006.     if coroutine.status(routine) == "dead" then
  1007.       state = task.TS_CLOSED
  1008.       processes[PID] = nil
  1009.       return false      
  1010.     end
  1011.  
  1012.     if d1 == "os_call" then -- task made os call
  1013.       if task.osCalls[d2] then
  1014.         parameters = { task.osCalls[d2](PID, d3, d4, d5, d6, d7) }
  1015.         returnNeeded = true
  1016.       else
  1017.         local errorMessage = string.gsub(errorCodes[0x03], "%$(%w+)", {CALL = tostring(d2)} )
  1018.         printWarning("Task '"..tostring(PID).."' tried to make os call: "..errorMessage)
  1019.         parameters = {false, 0x03, errorMessage}
  1020.         returnNeeded = true
  1021.       end
  1022.       state = task.TS_RUNNING
  1023.     else -- task yield waiting for event to occour
  1024.       state = task.TS_WAITING
  1025.     end
  1026.     return true
  1027.   end
  1028.   table.insert(processes, self)
  1029.   return self
  1030. end
  1031.  
  1032. --Add driverAPIs to program queue
  1033. for name, header in pairs(loadedDrvApis) do
  1034.   if header.api then --this should never fail!
  1035.     if header.api.load then
  1036.       term.printWarning("Driver API \""..name.."\" has load function")
  1037.     end
  1038.     if header.api.run then
  1039.       local API = table.merge({}, driverApiAPI)
  1040.       table.merge(API, header.api)
  1041.       API.drivers = {}
  1042.       API.drivers = table.merge(API.drivers, header.drivers)
  1043.       local _task = task.new("driverAPI_"..name, header.api.run, API, task.stdin, task.stdout)
  1044.     else
  1045.       criticalError("Critical error! DA/"..tostring(name).."/no run function")
  1046.     end
  1047.   else
  1048.     criticalError("Critical error! DA/"..tostring(name))
  1049.   end
  1050. end
  1051.  
  1052. -- Add device drivers to program queue --
  1053. for side, device in pairs(devices) do
  1054.   if device.api then --this should never fail!
  1055.     if device.api.run then
  1056.       local _env = getfenv(device.api.run)
  1057.       _env = table.merge(_env, driverAPI)
  1058.       task.new("driver_"..side, device.api.run, _env, task.stdin, task.stdout, side)
  1059.     else
  1060.       criticalError("Critical error! D/"..tostring(devType).."/no run function")
  1061.     end
  1062.   else
  1063.     criticalError("Critical error! D/"..tostring(devType))
  1064.   end
  1065. end
  1066.  
  1067. -----------TEST CODE---------
  1068.  
  1069. local fnFile, err = loadfile( fs.combine(os.getPath(), "/bin/vtyManager" ))
  1070. if fnFile then
  1071.     local _api = shellAPI
  1072.     _api.task = task
  1073.     _api.programAPI = programAPI
  1074.     _api.shellAPI = shellAPI
  1075.     _api.baseAPI = baseAPI
  1076.     local tEnv = {}
  1077.     setmetatable( tEnv, { __index = _api } )
  1078.     setfenv( fnFile, tEnv )
  1079.     fnFile()
  1080.     local _task = task.new("task_test", tEnv.run, nil, task.stdin, task.stdout)
  1081. else
  1082.   print("Error: "..err)
  1083. end
  1084.  
  1085. -----------END TEST CODE------
  1086.  
  1087. printOK("netOS loaded, press any key or wait 5 seconds to continue")
  1088. nativeOS.startTimer(5) -- give user chance to read boot messages
  1089. while true do
  1090.   local e, p1, p2, p3, p4, p5 = nativeOS.pullEventRaw()
  1091.   local tasksRunning = 0
  1092.   if not lockedProcess then
  1093.     for n = 1, pidCounter, 1 do
  1094.       if processes[n] then
  1095.         local process = processes[n]
  1096.         if process.getState() == task.TS_RUNNING then
  1097.           local ok = processes[n].continue(e, p1, p2, p3, p4, p5)
  1098.           if not ok then
  1099.             processes[n] = nil
  1100.           end
  1101.         elseif process.getState() == task.TS_WAITING and process.eventListeners[e] then --refresh only tasks that need to
  1102.           local ok = processes[n].continue(e, p1, p2, p3, p4, p5)
  1103.           if not ok then
  1104.             processes[n] = nil
  1105.           end
  1106.         elseif process.getState() == task.TS_WAITING and process.eventListeners["os_message"] then
  1107.         -- if no event for this task check if there is message for it
  1108.           local message, sender = processes[n].getMessage()
  1109.           if message then
  1110.             local ok = processes[n].continue("os_message", message, sender)
  1111.             if not ok then
  1112.               processes[n] = nil
  1113.             end
  1114.           end
  1115.         end
  1116.         if process.getState() == task.TS_RUNNING then tasksRunning = tasksRunning + 1 end
  1117.       end
  1118.     end
  1119.   else
  1120.     -- there is locked process
  1121.     if processes[lockedProcess] and processes[lockedProcess].getState() ~= "TS_CLOSED" then
  1122.       if processes[lockedProcess].eventListeners[e] or processes[lockedProcess].getState() == task.TS_RUNNING then
  1123.         local ok = processes[lockedProcess].continue(e, p1, p2, p3, p4, p5)
  1124.         if not ok then
  1125.           processes[lockedProcess] = nil
  1126.           lockedProcess = nil
  1127.         elseif lockedProcess == nil then
  1128.           tasksRunning = 1 -- make sure to refresh other tasks, so OS don't hang
  1129.         else
  1130.           if processes[lockedProcess].getState() == task.TS_RUNNING then tasksRunning = tasksRunning + 1 end
  1131.         end
  1132.       end
  1133.      
  1134.     else
  1135.       lockedProcess = nil
  1136.       tasksRunning = 1 -- make sure to refresh other tasks, so OS don't hang
  1137.     end
  1138.   end
  1139.   if tasksRunning > 0 or task.messages > 0 then
  1140.     nativeOS.startTimer(0)
  1141.   end
  1142. end
  1143.  
  1144.   ]]},
  1145.   {["type"]="file", ["name"]="vtyManager", contents=[[
  1146. -- LOCALS
  1147.  
  1148. local vtys = {}
  1149.  
  1150. -- VTYs
  1151.  
  1152. local VTY = {}
  1153.  
  1154. --------------------------------------------------------------------------------
  1155. -- @param(vty) VTY to render on screen, MUST BE ACTIVE
  1156. -- @description renders VTY to screen
  1157. --------------------------------------------------------------------------------
  1158. VTY.repaint = function(vty)
  1159.   if not vty.active then return end
  1160.   local width, height = vty.stdout.getSize()
  1161.   for x = 1, width, 1 do
  1162.     for y = 1, height, 1 do
  1163.       if not vty.textColorBuff[x] then vty.textColorBuff[x] = {} end
  1164.       if not vty.textColorBuff[x][y] then vty.textColorBuff[x][y] = colors.white end
  1165.       if not vty.backColorBuff[x] then vty.backColorBuff[x] = {} end
  1166.       if not vty.backColorBuff[x][y] then vty.backColorBuff[x][y] = colors.black end
  1167.       vty.stdout.setTextColor(vty.textColorBuff[x][y])
  1168.       vty.stdout.setBackgroundColor(vty.backColorBuff[x][y])
  1169.       vty.stdout.setCursorPos(x, y)
  1170.       if not vty.screenBuff[x] then vty.screenBuff[x] = {} end
  1171.       if not vty.screenBuff[x][y] then vty.screenBuff[x][y] = ' ' end
  1172.       vty.stdout.write(tostring(vty.screenBuff[x][y]))
  1173.     end
  1174.   end
  1175.   vty.stdout.setCursorPos(vty.cursorX, vty.cursorY)
  1176.   vty.stdout.setCursorBlink(vty.cursorBlink)
  1177.   vty.stdout.setTextColor(vty.textColor)
  1178.   vty.stdout.setBackgroundColor(vty.backColor)
  1179. end
  1180.  
  1181. --------------------------------------------------------------------------------
  1182. -- @param(vty) VTY to print to
  1183. -- @param(sText) text to write
  1184. -- @description writes text to stdout od VTY
  1185. -- acts as native term.write - if text exceeds screen size, it will not wrap
  1186. --------------------------------------------------------------------------------
  1187. VTY._write = function(_vty, sText )
  1188.   local width, height = _vty.stdout.getSize()
  1189.   local textColor = _vty.textColor;
  1190.   local backColor = _vty.backColor;
  1191.  
  1192.   for i = 1, sText:len(), 1 do
  1193.     if _vty.cursorX > width then break end
  1194.     if not _vty.screenBuff[_vty.cursorX] then
  1195.       _vty.screenBuff[_vty.cursorX] = {}
  1196.     end
  1197.     _vty.screenBuff[_vty.cursorX][_vty.cursorY] = sText:sub(i, i)
  1198.    
  1199.     if not _vty.textColorBuff[_vty.cursorX] then _vty.textColorBuff[_vty.cursorX] = {} end
  1200.     if not _vty.textColorBuff[_vty.cursorX][_vty.cursorY] then _vty.textColorBuff[_vty.cursorX][_vty.cursorY] = colors.white end
  1201.     if not _vty.backColorBuff[_vty.cursorX] then _vty.backColorBuff[_vty.cursorX] = {} end
  1202.     if not _vty.backColorBuff[_vty.cursorX][_vty.cursorY] then _vty.backColorBuff[_vty.cursorX][_vty.cursorY] = colors.white end
  1203.    
  1204.     _vty.textColorBuff[_vty.cursorX][_vty.cursorY] = textColor
  1205.     _vty.backColorBuff[_vty.cursorX][_vty.cursorY] = backColor
  1206.    
  1207.     if _vty.active then
  1208.       _vty.stdout.write(sText:sub(i, i))
  1209.     end
  1210.     VTY.setCursorPos(_vty, _vty.cursorX + 1, _vty.cursorY)
  1211.   end
  1212. --  VTY.repaint(_vty)
  1213. end
  1214.  
  1215. --------------------------------------------------------------------------------
  1216. -- @param(vty) VTY to print to
  1217. -- @param(sText) text to write
  1218. -- @description writes text to stdout with proper word wrapping
  1219. --------------------------------------------------------------------------------
  1220. VTY.write = function(vty, sText)
  1221.   local w,h = vty.stdout.getSize()
  1222.   local x,y = VTY.getCursorPos(vty)
  1223.  
  1224.   local nLinesPrinted = 0
  1225.   local function newLine()
  1226.     x, y = VTY.getCursorPos(vty)
  1227.     if y + 1 <= h then
  1228.       VTY.setCursorPos(vty, 1, y + 1)
  1229.     else
  1230.       VTY.setCursorPos(vty, 1, h)
  1231.       VTY.scroll(vty, 1)
  1232.     end
  1233.     x, y = VTY.getCursorPos(vty)
  1234.     nLinesPrinted = nLinesPrinted + 1
  1235.   end
  1236.    
  1237.   -- Print the line with proper word wrapping
  1238.   while string.len(sText) > 0 do
  1239.     local whitespace = string.match( sText, "^[ \t]+" )
  1240.     if whitespace then
  1241.       -- Print whitespace
  1242.       VTY._write(vty,whitespace)
  1243.       x,y = VTY.getCursorPos(vty)
  1244.       sText = string.sub( sText, string.len(whitespace) + 1 )
  1245.     end
  1246.        
  1247.     local newline = string.match( sText, "^\n" )
  1248.     if newline then
  1249.       -- Print newlines
  1250.       newLine()
  1251.       sText = string.sub( sText, 2 )
  1252.     end
  1253.        
  1254.     local text = string.match( sText, "^[^ \t\n]+" )
  1255.     if text then
  1256.       sText = string.sub( sText, string.len(text) + 1 )
  1257.       if string.len(text) > w then
  1258.         -- Print a multiline word            
  1259.         while string.len( text ) > 0 do
  1260.           if x > w then
  1261.             newLine()
  1262.           end
  1263.           VTY._write(vty,text)
  1264.           text = string.sub( text, (w-x) + 2 )
  1265.           x,y = VTY.getCursorPos(vty)
  1266.         end
  1267.       else
  1268.         -- Print a word normally
  1269.         if x + string.len(text) - 1 > w then
  1270.           newLine()
  1271.         end
  1272.         VTY._write(vty,text)
  1273.         x,y = VTY.getCursorPos(vty)
  1274.       end
  1275.     end
  1276.   end
  1277.   return nLinesPrinted
  1278. end
  1279.  
  1280. --------------------------------------------------------------------------------
  1281. -- @param(vty) VTY to print to
  1282. -- @param(...) data to write
  1283. -- @description writes all passed arguments to VTY, at the and prints new line
  1284. --------------------------------------------------------------------------------
  1285. VTY.print = function(vty, ...)
  1286.   for n = 1, #arg, 1 do
  1287.     VTY.write(vty, tostring(arg[n]))    
  1288.   end
  1289.   VTY.write(vty, "\n")
  1290. end
  1291.  
  1292. --------------------------------------------------------------------------------
  1293. -- @param(vty) VTY to clear
  1294. -- @description clears screen of specified VTY
  1295. --------------------------------------------------------------------------------
  1296. VTY.clear = function(vty)
  1297.   local width, height = vty.stdout.getSize()
  1298.   for x = 1, width, 1 do
  1299.     if not vty.screenBuff[x] then
  1300.       vty.screenBuff[x] = {}
  1301.     end
  1302.     for y = 1, height, 1 do
  1303.       vty.screenBuff[x][y] = " "
  1304.       vty.backColorBuff[x][y] = vty.backColor;
  1305.     end
  1306.   end
  1307.   if vty.active then vty.stdout.clear() end
  1308. end
  1309.  
  1310. --------------------------------------------------------------------------------
  1311. -- @param(vty) VTY to scroll
  1312. -- @param(_n) lines to scroll
  1313. -- @description scrolls specified VTY _n lines to top
  1314. --------------------------------------------------------------------------------
  1315. VTY.scroll = function(vty, _n)
  1316.   local width, height = vty.stdout.getSize()
  1317.   for y = 1, height-_n, 1 do
  1318.     if y + _n <= height then
  1319.       for x = 1, width, 1 do
  1320.         if not vty.textColorBuff[x] then vty.textColorBuff[x] = {} end
  1321.         if not vty.textColorBuff[x][y+_n] then vty.textColorBuff[x][y+_n] = colors.white end
  1322.         if not vty.backColorBuff[x] then vty.backColorBuff[x] = {} end
  1323.         if not vty.backColorBuff[x][y+_n] then vty.backColorBuff[x][y+_n] = colors.black end
  1324.        
  1325.         vty.screenBuff[x][y] = vty.screenBuff[x][y+_n]
  1326.         vty.textColorBuff[x][y] = vty.textColorBuff[x][y+_n]
  1327.         vty.backColorBuff[x][y] = vty.backColorBuff[x][y+_n]
  1328.       end
  1329.     end
  1330.   end
  1331.   for y = height - _n + 1, height, 1 do
  1332.     for x = 1, width, 1 do
  1333.       vty.screenBuff[x][y] = " "
  1334.       vty.backColorBuff[x][y] = colors.back
  1335.       vty.textColorBuff[x][y] = colors.white
  1336.     end
  1337.   end
  1338.   if vty.active then vty.stdout.scroll(_n) end
  1339. end
  1340.  
  1341. --------------------------------------------------------------------------------
  1342. -- @param(vty) VTY to read from
  1343. -- @description reads what user typed, ends when user presses [enter]
  1344. -- @return user input
  1345. --------------------------------------------------------------------------------
  1346. VTY.read = function(vty, _sReplaceChar, _tHistory)
  1347.     VTY.setCursorBlink(vty, true )
  1348.  
  1349.     local sLine = ""
  1350.     local nHistoryPos = nil
  1351.     local nPos = 0
  1352.     if _sReplaceChar then
  1353.         _sReplaceChar = string.sub( _sReplaceChar, 1, 1 )
  1354.     end
  1355.    
  1356.     local w, h = vty.stdout.getSize()
  1357.     local sx, sy = VTY.getCursorPos(vty)    
  1358.    
  1359.     local function redraw( _sCustomReplaceChar )
  1360.         local nScroll = 0
  1361.         if sx + nPos >= w then
  1362.             nScroll = (sx + nPos) - w
  1363.         end
  1364.            
  1365.         VTY.setCursorPos(vty, sx, sy )
  1366.         local sReplace = _sCustomReplaceChar or _sReplaceChar
  1367.         if sReplace then
  1368.             VTY._write(vty, string.rep(sReplace, string.len(sLine) - nScroll) )
  1369.         else
  1370.             VTY.write(vty, string.sub( sLine, nScroll + 1 ) )
  1371.         end
  1372.         VTY.setCursorPos(vty, sx + nPos - nScroll, sy )
  1373.     end
  1374.    
  1375.     while true do
  1376.         local sEvent, param = os.pullEvent()
  1377.         if sEvent == "char" then
  1378.             sLine = string.sub( sLine, 1, nPos ) .. param .. string.sub( sLine, nPos + 1 )
  1379.             nPos = nPos + 1
  1380.             redraw()
  1381.            
  1382.         elseif sEvent == "key" then
  1383.             if param == keys.enter then
  1384.                 -- Enter
  1385.                 break
  1386.                
  1387.             elseif param == keys.left then
  1388.                 -- Left
  1389.                 if nPos > 0 then
  1390.                     nPos = nPos - 1
  1391.                     redraw()
  1392.                 end
  1393.                
  1394.             elseif param == keys.right then
  1395.                 -- Right                
  1396.                 if nPos < string.len(sLine) then
  1397.                     nPos = nPos + 1
  1398.                     redraw()
  1399.                 end
  1400.            
  1401.             elseif param == keys.up or param == keys.down then
  1402.                 -- Up or down
  1403.                 if _tHistory then
  1404.                     redraw(" ");
  1405.                     if param == keys.up then
  1406.                         -- Up
  1407.                         if nHistoryPos == nil then
  1408.                             if #_tHistory > 0 then
  1409.                                 nHistoryPos = #_tHistory
  1410.                             end
  1411.                         elseif nHistoryPos > 1 then
  1412.                             nHistoryPos = nHistoryPos - 1
  1413.                         end
  1414.                     else
  1415.                         -- Down
  1416.                         if nHistoryPos == #_tHistory then
  1417.                             nHistoryPos = nil
  1418.                         elseif nHistoryPos ~= nil then
  1419.                             nHistoryPos = nHistoryPos + 1
  1420.                         end                    
  1421.                     end
  1422.                    
  1423.                     if nHistoryPos then
  1424.                         sLine = _tHistory[nHistoryPos]
  1425.                         nPos = string.len( sLine )
  1426.                     else
  1427.                         sLine = ""
  1428.                         nPos = 0
  1429.                     end
  1430.                     redraw()
  1431.                 end
  1432.             elseif param == keys.backspace then
  1433.                 -- Backspace
  1434.                 if nPos > 0 then
  1435.                     redraw(" ");
  1436.                     sLine = string.sub( sLine, 1, nPos - 1 ) .. string.sub( sLine, nPos + 1 )
  1437.                     nPos = nPos - 1                
  1438.                     redraw()
  1439.                 end
  1440.             elseif param == keys.home then
  1441.                 -- Home
  1442.                 nPos = 0
  1443.                 redraw()        
  1444.             elseif param == keys.delete then
  1445.                 if nPos < string.len(sLine) then
  1446.                     redraw(" ");
  1447.                     sLine = string.sub( sLine, 1, nPos ) .. string.sub( sLine, nPos + 2 )              
  1448.                     redraw()
  1449.                 end
  1450.             elseif param == keys["end"] then
  1451.                 -- End
  1452.                 nPos = string.len(sLine)
  1453.                 redraw()
  1454.             end
  1455.         end
  1456.     end
  1457.    
  1458.     VTY.setCursorBlink(vty, false )
  1459.     VTY.setCursorPos(vty, w + 1, sy )
  1460.     VTY.print(vty)
  1461.    
  1462.     return sLine
  1463. end
  1464.  
  1465. --------------------------------------------------------------------------------
  1466. -- @param(vty) VTY to get size
  1467. -- @param(_n) lines to scroll
  1468. -- @return width, height of the VTY
  1469. --------------------------------------------------------------------------------
  1470. VTY.getSize = function(vty)
  1471.   return vty.stdout.getSize()
  1472. end
  1473.  
  1474. --------------------------------------------------------------------------------
  1475. -- @param(vty) VTY to get cursor position
  1476. -- @return x, y cooridinate of cursor on VTY
  1477. --------------------------------------------------------------------------------
  1478. VTY.getCursorPos = function(vty)
  1479.   return vty.cursorX, vty.cursorY
  1480. end
  1481.  
  1482. --------------------------------------------------------------------------------
  1483. -- @param(vty) VTY to set cursor position
  1484. -- @param(x) X coordinate to set
  1485. -- @param(y) Y coordinate to set
  1486. --------------------------------------------------------------------------------
  1487. VTY.setCursorPos = function(vty, x, y)
  1488.   vty.cursorX = x
  1489.   vty.cursorY = y  
  1490.   if vty.active then
  1491.     vty.stdout.setCursorPos(x, y)
  1492.   end
  1493. end
  1494.  
  1495. --------------------------------------------------------------------------------
  1496. -- @param(vty) VTY to set cursor blinking
  1497. -- @param(_blink) true - cursor blinks, false - not
  1498. --------------------------------------------------------------------------------  
  1499. VTY.setCursorBlink = function(vty, _blink)
  1500.   vty.cursorBlink = _blink
  1501.   if vty.active then
  1502.     vty.stdout.setCursorBlink(_blink)
  1503.   end
  1504. end
  1505.  
  1506. --------------------------------------------------------------------------------
  1507. -- @param(vty) VTY to set text color
  1508. -- @param(color) self explaining
  1509. --------------------------------------------------------------------------------  
  1510. VTY.setTextColor = function(vty, color)
  1511.   if vty.color then
  1512.     vty.textColor = color
  1513.     if vty.active then
  1514.       vty.stdout.setTextColor(color)
  1515.     end
  1516.   end  
  1517. end
  1518.  
  1519. --------------------------------------------------------------------------------
  1520. -- @param(vty) VTY to set back color
  1521. -- @param(color) self explaining
  1522. --------------------------------------------------------------------------------  
  1523. VTY.setBackgroundColor = function(vty, color)
  1524.   if vty.color then
  1525.     vty.backColor = color
  1526.     if vty.active then
  1527.       vty.stdout.setBackgroundColor(color)
  1528.     end
  1529.   end
  1530. end
  1531.  
  1532. --------------------------------------------------------------------------------
  1533. -- @param(vty) VTY to set cursor position
  1534. -- @return is cursor blinking
  1535. --------------------------------------------------------------------------------
  1536. VTY.getCursorBlink = function(vty)
  1537.   return vty.cursorBlink
  1538. end
  1539.  
  1540. --------------------------------------------------------------------------------
  1541. -- @param(vty) VTY to clear line
  1542. -- @description clears current line on screen of specified VTY
  1543. --------------------------------------------------------------------------------
  1544. VTY.clearLine = function(vty)
  1545.   local width, height = vty.stdout.getSize()
  1546.   for x = 1, width, 1 do
  1547.     if not vty.screenBuff[x] then
  1548.       vty.screenBuff[x] = {}
  1549.     end
  1550.       vty.screenBuff[x][vty.cursorY] = " "
  1551.       vty.backColorBuff[x][vty.cursorY] = vty.backColor;
  1552.   end
  1553.   if vty.active then vty.stdout.clearLine() end
  1554. end
  1555.  
  1556. -- NOT SUPORTED YET --
  1557. VTY.setTextColour = function(vty, color) end
  1558. VTY.setBackgroundColour = function(vty, color) end
  1559. -- END NOT SUPPORTED YET --
  1560.  
  1561. VTY.new = function(_stdin, _stdout, nullin)
  1562.   local self = {}
  1563.   self.screenBuff = {}
  1564.   self.textColorBuff = {}
  1565.   self.backColorBuff = {}
  1566.   self.textColor = colors.white
  1567.   self.backColor = colors.black
  1568.   self.cursorX = 1
  1569.   self.cursorY = 1
  1570.   self.cursorBlink = true
  1571.   self.color = _stdout.isColor()
  1572.   self.active = false
  1573.   self.stdin = nullin
  1574.   self.stdout = task.nullout
  1575.  
  1576.   local function run(n, _term, PID)
  1577.     _G = getfenv(1)
  1578.     os.enableAPI("fs")
  1579.     local welcome = os.version().." VTY "..tostring(term.id)..", PID="..tostring(PID)
  1580.     term.print(welcome)
  1581.     local fnFile, err = loadfile( fs.combine(os.getPath(), "/bin/console" ))
  1582.     if fnFile then
  1583.       local _api = getfenv(1)
  1584.       local tEnv = {}
  1585.       setmetatable( tEnv, { __index = _api } )
  1586.       setfenv( fnFile, tEnv )
  1587.       term.print("OK")
  1588.       fnFile(PID)
  1589.     end
  1590.     term.print("ERROR")
  1591.   end
  1592.  
  1593.   local vtyTerm = {}
  1594.   vtyTerm._write = function( sText ) return VTY._write(self, sText) end
  1595.   vtyTerm.write = function( sText ) return VTY.write(self, sText) end
  1596.   vtyTerm.print = function( ... ) return VTY.print(self, ...) end
  1597.   vtyTerm.read = function(passwordChar, tHistory) return VTY.read(self, passwordChar, tHistory) end
  1598.   vtyTerm.setCursorPos = function(x, y) return VTY.setCursorPos(self, x, y) end
  1599.   vtyTerm.getCursorPos = function() return VTY.getCursorPos(self) end
  1600.   vtyTerm.setCursorBlink = function( blink ) return VTY.setCursorBlink(self, blink) end
  1601.   vtyTerm.getCursorBlink = function() return VTY.getCursorPos(self) end
  1602.   vtyTerm.setTextColor = function( color ) VTY.setTextColor(self, color) end
  1603.   vtyTerm.setTextColour = vtyTerm.setTextColor
  1604.   vtyTerm.setBackgroundColor = function( color ) VTY.setBackgroundColor(self, color) end
  1605.   vtyTerm.setBackgroundColour = vtyTerm.setBackgroundColor
  1606.   vtyTerm.clear = function() return VTY.clear(self) end
  1607.   vtyTerm.clearLine = function() return VTY.clearLine(self) end
  1608.   vtyTerm.scroll = function( n ) return VTY.scroll(self, n) end  
  1609.   vtyTerm.isColor = function() return self.color end
  1610.   vtyTerm.isColour = vtyTerm.isColor
  1611.   vtyTerm.getSize = function() return _stdout.getSize() end
  1612.   vtyTerm.id = (#vtys) + 1
  1613.  
  1614.   local task = task.new("VTY"..tostring( (#vtys) + 1 ), run, baseAPI, self.stdin, vtyTerm, vtyTerm.id, vtyTerm)
  1615.   self.activate = function()
  1616.     self.active = true
  1617.     self.stdin = _stdin
  1618.     self.stdout = _stdout
  1619.     task.setStdin(self.stdin)
  1620.     VTY.repaint(self)
  1621.   end
  1622.  
  1623.   self.deactivate = function()
  1624.     self.active = false
  1625.     self.stdin = nullin
  1626.     self.stdout = task.nullout
  1627.     task.setStdin(self.stdin)
  1628.   end
  1629.  
  1630.   table.insert(vtys, self)
  1631.   return self
  1632. end
  1633.  
  1634. -- OS FUNCTIONS --
  1635.  
  1636. function run(PID)
  1637.   os.enableAPI("colors")
  1638.   os.enableAPI("config")
  1639.   local conf = config.open(fs.combine(os.getPath(), "etc/vty"))
  1640.   local count = tonumber(conf.get("General", "VTY count", "4")) or 4
  1641.   for i = 1, count, 1 do
  1642.     VTY.new(task.stdin, task.stdout, task.nullin)    
  1643.   end
  1644.   conf.save()
  1645.  
  1646.   os.addEventListener('timer')
  1647.   os.addEventListener('key')
  1648.  
  1649.   local controlTime = 0
  1650.   local lastVTY = 1
  1651.   print("VTY manager loaded, please wait 5 seconds")
  1652.   local timer = os.startTimer(5)
  1653.   while true do
  1654.     local e, p1 = os.pullEvent()
  1655.     if e == "timer" and p1 == timer then
  1656.       vtys[1].activate()
  1657.     end
  1658.     if e == "key" then
  1659.       if p1 == 29 then -- 29 = L_CTRL
  1660.         controlTime = os.clock()
  1661.       elseif p1 > 1 and p1 < 11 and controlTime+0.6 > os.clock() then -- 2 = numb_1 11 = numb_0
  1662.         local numb = p1 - 1
  1663.         if vtys[numb] then
  1664.           vtys[lastVTY].deactivate()
  1665.           vtys[numb].activate()
  1666.           lastVTY = numb
  1667.         end
  1668.       end
  1669.     end
  1670.   end
  1671. end
  1672.   ]]},
  1673.   {["type"]="file", ["name"]="console", contents=[[
  1674. --BEGIN SHELL HEADER--
  1675. --END SHELL HEADER--
  1676.  
  1677. local bExit = false
  1678. local sDir = ""
  1679. local sPath = ".:/"..fs.combine(os.getPath(), "bin")..".:/rom/programs"
  1680. local tAliases = {}
  1681. local tProgramStack = {}
  1682.  
  1683. local shell = {}
  1684. local tEnv = getfenv(1)
  1685. tEnv["shell"] = shell
  1686.  
  1687. function shell.setAlias( _sCommand, _sProgram )
  1688.     tAliases[ _sCommand ] = _sProgram
  1689. end
  1690.  
  1691. function shell.clearAlias( _sCommand )
  1692.     tAliases[ _sCommand ] = nil
  1693. end
  1694.  
  1695. function shell.aliases()
  1696.     -- Add aliases
  1697.     local tCopy = {}
  1698.     for sAlias, sCommand in pairs( tAliases ) do
  1699.         tCopy[sAlias] = sCommand
  1700.     end
  1701.     return tCopy
  1702. end
  1703.  
  1704. -- Colours
  1705. local promptColour, textColour, bgColour
  1706. if term.isColor() then
  1707.     promptColour = colours.yellow
  1708.     textColour = colours.white
  1709.     bgColour = colours.black
  1710. else
  1711.     promptColour = colours.white
  1712.     textColour = colours.white
  1713.     bgColour = colours.black
  1714. end
  1715.  
  1716.  
  1717. local function run( _sCommand, ... )
  1718.     local sPath = shell.resolveProgram( _sCommand )
  1719.     if sPath ~= nil then
  1720.         tProgramStack[#tProgramStack + 1] = sPath
  1721.         local result = os.run( tEnv, sPath, ... )
  1722.         tProgramStack[#tProgramStack] = nil
  1723.         return result
  1724.     else
  1725.         term.print( "No such program" )
  1726.         return false
  1727.     end
  1728. end
  1729.  
  1730. local function runLine( _sLine )
  1731.     local tWords = {}
  1732.     for match in string.gmatch( _sLine, "[^ \t]+" ) do
  1733.         table.insert( tWords, match )
  1734.     end
  1735.  
  1736.     local sCommand = tWords[1]
  1737.     if sCommand then
  1738.         return run( sCommand, unpack( tWords, 2 ) )
  1739.     end
  1740.     return false
  1741. end
  1742.  
  1743. -- Install shell API
  1744. function shell.run( ... )
  1745.     return runLine( table.concat( { ... }, " " ) )
  1746. end
  1747.  
  1748. function shell.exit()
  1749.     bExit = true
  1750. end
  1751.  
  1752. function shell.dir()
  1753.     return sDir
  1754. end
  1755.  
  1756. function shell.setDir( _sDir )
  1757.     sDir = _sDir
  1758. end
  1759.  
  1760. function shell.path()
  1761.     return sPath
  1762. end
  1763.  
  1764. function shell.setPath( _sPath )
  1765.     sPath = _sPath
  1766. end
  1767.  
  1768. function shell.resolve( _sPath )
  1769.     local sStartChar = string.sub( _sPath, 1, 1 )
  1770.     if sStartChar == "/" or sStartChar == "\\" then
  1771.         return fs.combine( "", _sPath )
  1772.     else
  1773.         return fs.combine( sDir, _sPath )
  1774.     end
  1775. end
  1776.  
  1777. function shell.resolveProgram( _sCommand )
  1778.     -- Substitute aliases firsts
  1779.     if tAliases[ _sCommand ] ~= nil then
  1780.         _sCommand = tAliases[ _sCommand ]
  1781.     end
  1782.  
  1783.     -- If the path is a global path, use it directly
  1784.     local sStartChar = string.sub( _sCommand, 1, 1 )
  1785.     if sStartChar == "/" or sStartChar == "\\" then
  1786.         local sPath = fs.combine( "", _sCommand )
  1787.         if fs.exists( sPath ) and not fs.isDir( sPath ) then
  1788.             return sPath
  1789.         end
  1790.         return nil
  1791.     end
  1792.    
  1793.     -- Otherwise, look on the path variable
  1794.     for sPath in string.gmatch(sPath, "[^:]+") do
  1795.         sPath = fs.combine( shell.resolve( sPath ), _sCommand )
  1796.         if fs.exists( sPath ) and not fs.isDir( sPath ) then
  1797.             return sPath
  1798.         end
  1799.     end
  1800.    
  1801.     -- Not found
  1802.     return nil
  1803. end
  1804.  
  1805. function shell.programs( _bIncludeHidden ) -- to modify
  1806.     local tItems = {}
  1807.    
  1808.     -- Add programs from the path
  1809.     for sPath in string.gmatch(sPath, "[^:]+") do
  1810.         sPath = shell.resolve( sPath )
  1811.         if fs.isDir( sPath ) then
  1812.             local tList = fs.list( sPath )
  1813.             for n,sFile in pairs( tList ) do
  1814.                 if not fs.isDir( fs.combine( sPath, sFile ) ) and
  1815.                    (_bIncludeHidden or string.sub( sFile, 1, 1 ) ~= ".") then
  1816.                     tItems[ sFile ] = true
  1817.                 end
  1818.             end
  1819.         end
  1820.     end
  1821.  
  1822.     -- Sort and return
  1823.     local tItemList = {}
  1824.     for sItem, b in pairs( tItems ) do
  1825.         table.insert( tItemList, sItem )
  1826.     end
  1827.     table.sort( tItemList )
  1828.     return tItemList
  1829. end
  1830.  
  1831. function shell.getRunningProgram()
  1832.     if #tProgramStack > 0 then
  1833.         return tProgramStack[#tProgramStack]
  1834.     end
  1835.     return nil
  1836. end
  1837.  
  1838. term.setBackgroundColor( bgColour )
  1839. term.setTextColour( promptColour )
  1840. --  term.clear(bgColour)
  1841. term.setCursorPos(1,2)
  1842. term.print( os.version() )
  1843. term.setTextColour( textColour )
  1844.  
  1845. os.addEventListener("key")
  1846. os.addEventListener("char")
  1847.  
  1848.   -- Read commands and execute them
  1849. local tCommandHistory = {}
  1850. while true do
  1851.   term.setBackgroundColor( bgColour )
  1852.   term.setTextColour( promptColour )
  1853.   term.write( shell.dir() .. "> " )
  1854.   term.setTextColour( textColour )
  1855.  
  1856.   local sLine = term.read( nil, tCommandHistory )
  1857.   table.insert( tCommandHistory, sLine )
  1858.   runLine( sLine )  
  1859. end
  1860.   ]]},
  1861.   {["type"]="file", ["name"]="edit", contents=[[
  1862. os.enableAPI("fs")
  1863. os.enableAPI("io")
  1864. os.enableAPI("keys")
  1865. os.enableAPI("math")
  1866.  
  1867. -- Get file to edit
  1868. local tArgs = { ... }
  1869. if #tArgs == 0 then
  1870.     print( "Usage: edit <path>" )
  1871.     return
  1872. end
  1873.  
  1874. -- Error checking
  1875. local sPath = shell.resolve( tArgs[1] )
  1876. local bReadOnly = fs.isReadOnly( sPath )
  1877. if fs.exists( sPath ) and fs.isDir( sPath ) then
  1878.     print( "Cannot edit a directory." )
  1879.     return
  1880. end
  1881.  
  1882. local x,y = 1,1
  1883. local w,h = term.getSize()
  1884. local scrollX, scrollY = 0,0
  1885.  
  1886. local tLines = {}
  1887. local bRunning = true
  1888.  
  1889. -- Colours
  1890. local highlightColour, keywordColour, commentColour, textColour, bgColour
  1891. if term.isColour() then
  1892.     bgColour = colours.black
  1893.     textColour = colours.white
  1894.     highlightColour = colours.yellow
  1895.     keywordColour = colours.yellow
  1896.     commentColour = colours.lime
  1897.     stringColour = colours.red
  1898. else
  1899.     bgColour = colours.black
  1900.     textColour = colours.white
  1901.     highlightColour = colours.white
  1902.     keywordColour = colours.white
  1903.     commentColour = colours.white
  1904.     stringColour = colours.white
  1905. end
  1906.  
  1907. -- Menus
  1908. local bMenu = false
  1909. local nMenuItem = 1
  1910. --local tMenuItems = {"Save", "Exit", "Print"}
  1911. local tMenuItems = {"Save", "Exit"}
  1912. local sStatus = "Press Ctrl to access menu"
  1913.  
  1914. local function load(_sPath)
  1915.     tLines = {}
  1916.     if fs.exists( _sPath ) then
  1917.         local file = io.open( _sPath, "r" )
  1918.         local sLine = file:read()
  1919.         while sLine do
  1920.             table.insert( tLines, sLine )
  1921.             sLine = file:read()
  1922.         end
  1923.         file:close()
  1924.     end
  1925.    
  1926.     if #tLines == 0 then
  1927.         table.insert( tLines, "" )
  1928.     end
  1929. end
  1930.  
  1931. local function save( _sPath )
  1932.     -- Create intervening folder
  1933.     local sDir = sPath:sub(1, sPath:len() - fs.getName(sPath):len() )
  1934.     if not fs.exists( sDir ) then
  1935.         fs.makeDir( sDir )
  1936.     end
  1937.  
  1938.     -- Save
  1939.     local file = nil
  1940.     local function innerSave()
  1941.         file = fs.open( _sPath, "w" )
  1942.         if file then
  1943.             for n, sLine in ipairs( tLines ) do
  1944.                 file.write( sLine .. "\n" )
  1945.             end
  1946.         else
  1947.             error( "Failed to open ".._sPath )
  1948.         end
  1949.     end
  1950.    
  1951.     local ok = pcall( innerSave )
  1952.     if file then
  1953.         file.close()
  1954.     end
  1955.     return ok
  1956. end
  1957.  
  1958. local tKeywords = {
  1959.     ["and"] = true,
  1960.     ["break"] = true,
  1961.     ["do"] = true,
  1962.     ["else"] = true,
  1963.     ["elseif"] = true,
  1964.     ["end"] = true,
  1965.     ["false"] = true,
  1966.     ["for"] = true,
  1967.     ["function"] = true,
  1968.     ["if"] = true,
  1969.     ["in"] = true,
  1970.     ["local"] = true,
  1971.     ["nil"] = true,
  1972.     ["not"] = true,
  1973.     ["or"] = true,
  1974.     ["repeat"] = true,
  1975.     ["return"] = true,
  1976.     ["then"] = true,
  1977.     ["true"] = true,
  1978.     ["until"]= true,
  1979.     ["while"] = true,
  1980. }
  1981.  
  1982. local function tryWrite( sLine, regex, colour )
  1983.     local match = string.match( sLine, regex )
  1984.     if match then
  1985.         if type(colour) == "number" then
  1986.             term.setTextColour( colour )
  1987.         else
  1988.             term.setTextColour( colour(match) )
  1989.         end
  1990.         term._write( match )
  1991.         term.setTextColour( textColour )
  1992.         return string.sub( sLine, string.len(match) + 1 )
  1993.     end
  1994.     return nil
  1995. end
  1996.  
  1997. local function writeHighlighted( sLine )
  1998.     while string.len(sLine) > 0 do  
  1999.         sLine =
  2000.             tryWrite( sLine, "^%-%-%[%[.-%]%]", commentColour ) or
  2001.             tryWrite( sLine, "^%-%-.*", commentColour ) or
  2002.             tryWrite( sLine, "^\".-[^\\]\"", stringColour ) or
  2003.             tryWrite( sLine, "^\'.-[^\\]\'", stringColour ) or
  2004.             tryWrite( sLine, "^%[%[.-%]%]", stringColour ) or
  2005.             tryWrite( sLine, "^[%w_]+", function( match )
  2006.                 if tKeywords[ match ] then
  2007.                     return keywordColour
  2008.                 end
  2009.                 return textColour
  2010.             end ) or
  2011.             tryWrite( sLine, "^[^%w_]", textColour )
  2012.     end
  2013. end
  2014.  
  2015. local function redrawText()
  2016.     for y=1,h-1 do
  2017.         term.setCursorPos( 1 - scrollX, y )
  2018.         term.clearLine()
  2019.  
  2020.         local sLine = tLines[ y + scrollY ]
  2021.         if sLine ~= nil then
  2022.             writeHighlighted( sLine )
  2023.         end
  2024.     end
  2025.     term.setCursorPos( x - scrollX, y - scrollY )
  2026. end
  2027.  
  2028. local function redrawLine(_nY)
  2029.     local sLine = tLines[_nY]
  2030.     term.setCursorPos( 1 - scrollX, _nY - scrollY )
  2031.     term.clearLine()
  2032.     writeHighlighted( sLine )
  2033.     term.setCursorPos( x - scrollX, _nY - scrollY )
  2034. end
  2035.  
  2036. local function setLeftStatus()
  2037. end
  2038.  
  2039. local function redrawMenu()
  2040.     term.setCursorPos( 1, h )
  2041.     term.clearLine()
  2042.  
  2043.     local sLeft, sRight
  2044.     local nLeftColour, nLeftHighlight1, nLeftHighlight2
  2045.     if bMenu then
  2046.         local sMenu = ""
  2047.         for n,sItem in ipairs( tMenuItems ) do
  2048.             if n == nMenuItem then
  2049.                 nLeftHighlight1 = sMenu:len() + 1
  2050.                 nLeftHighlight2 = sMenu:len() + sItem:len() + 2
  2051.             end
  2052.             sMenu = sMenu.." "..sItem.." "
  2053.         end
  2054.         sLeft = sMenu
  2055.         nLeftColour = textColour
  2056.     else
  2057.         sLeft = sStatus
  2058.         nLeftColour = highlightColour
  2059.     end
  2060.    
  2061.     -- Left goes last so that it can overwrite the line numbers.
  2062.     sRight = "Ln "..y
  2063.     term.setTextColour( highlightColour )
  2064.     term.setCursorPos( w-sRight:len() + 1, h )
  2065.     term.write(sRight)
  2066.  
  2067.     sRight = tostring(y)
  2068.     term.setTextColour( textColour )
  2069.     term.setCursorPos( w-sRight:len() + 1, h )
  2070.     term.write(sRight)
  2071.  
  2072.     if sLeft then
  2073.         term.setCursorPos( 1, h )
  2074.         term.setTextColour( nLeftColour )
  2075.         term.write(sLeft)      
  2076.         if nLeftHighlight1 then
  2077.             term.setTextColour( highlightColour )
  2078.             term.setCursorPos( nLeftHighlight1, h )
  2079.             term.write( "[" )
  2080.             term.setCursorPos( nLeftHighlight2, h )
  2081.             term.write( "]" )
  2082.         end
  2083.         term.setTextColour( textColour )
  2084.     end
  2085.    
  2086.     -- Cursor highlights selection
  2087.     term.setCursorPos( x - scrollX, y - scrollY )
  2088. end
  2089.  
  2090. local tMenuFuncs = {
  2091.     Save=function()
  2092.         if bReadOnly then
  2093.             sStatus = "Access denied"
  2094.         else
  2095.             local ok, err = save( sPath )
  2096.             if ok then
  2097.                 sStatus="Saved to "..sPath
  2098.             else
  2099.                 sStatus="Error saving to "..sPath
  2100.             end
  2101.         end
  2102.         redrawMenu()
  2103.     end,
  2104.     Print=function()
  2105.         local sPrinterSide = nil
  2106.         for n,sSide in ipairs(rs.getSides()) do
  2107.             if peripheral.isPresent(sSide) and peripheral.getType(sSide) == "printer" then
  2108.                 sPrinterSide = sSide
  2109.                 break
  2110.             end
  2111.         end
  2112.        
  2113.         if not sPrinterSide then
  2114.             sStatus = "No printer attached"
  2115.             return
  2116.         end
  2117.  
  2118.         local nPage = 0
  2119.         local sName = fs.getName( sPath )
  2120.         local printer = peripheral.wrap(sPrinterSide)
  2121.         if printer.getInkLevel() < 1 then
  2122.             sStatus = "Printer out of ink"
  2123.             return
  2124.         elseif printer.getPaperLevel() < 1 then
  2125.             sStatus = "Printer out of paper"
  2126.             return
  2127.         end
  2128.        
  2129.         local terminal = {
  2130.             getCursorPos = printer.getCursorPos,
  2131.             setCursorPos = printer.setCursorPos,
  2132.             getSize = printer.getPageSize,
  2133.             write = printer.write,
  2134.         }
  2135.         terminal.scroll = function()
  2136.             if nPage == 1 then
  2137.                 printer.setPageTitle( sName.." (page "..nPage..")" )            
  2138.             end
  2139.            
  2140.             while not printer.newPage() do
  2141.                 if printer.getInkLevel() < 1 then
  2142.                     sStatus = "Printer out of ink, please refill"
  2143.                 elseif printer.getPaperLevel() < 1 then
  2144.                     sStatus = "Printer out of paper, please refill"
  2145.                 else
  2146.                     sStatus = "Printer output tray full, please empty"
  2147.                 end
  2148.    
  2149.                 term.restore()
  2150.                 redrawMenu()
  2151.                 term.redirect( terminal )
  2152.                
  2153.                 local timer = os.startTimer(0.5)
  2154.                 sleep(0.5)
  2155.             end
  2156.  
  2157.             nPage = nPage + 1
  2158.             if nPage == 1 then
  2159.                 printer.setPageTitle( sName )
  2160.             else
  2161.                 printer.setPageTitle( sName.." (page "..nPage..")" )
  2162.             end
  2163.         end
  2164.        
  2165.         bMenu = false
  2166.         term.redirect( terminal )
  2167.         local ok, error = pcall( function()
  2168.             term.scroll()
  2169.             for n, sLine in ipairs( tLines ) do
  2170.                 print( sLine )
  2171.             end
  2172.         end )
  2173.         term.restore()
  2174.         if not ok then
  2175.             print( error )
  2176.         end
  2177.        
  2178.         while not printer.endPage() do
  2179.             sStatus = "Printer output tray full, please empty"
  2180.             redrawMenu()
  2181.             sleep( 0.5 )
  2182.         end
  2183.         bMenu = true
  2184.            
  2185.         if nPage > 1 then
  2186.             sStatus = "Printed "..nPage.." Pages"
  2187.         else
  2188.             sStatus = "Printed 1 Page"
  2189.         end
  2190.         redrawMenu()
  2191.     end,
  2192.     Exit=function()
  2193.         bRunning = false
  2194.     end
  2195. }
  2196.  
  2197. local function doMenuItem( _n )
  2198.     tMenuFuncs[tMenuItems[_n] ]()
  2199.     if bMenu then
  2200.         bMenu = false
  2201.         term.setCursorBlink( true )
  2202.     end
  2203.     redrawMenu()
  2204. end
  2205.  
  2206. local function setCursor( x, y )
  2207.     local screenX = x - scrollX
  2208.     local screenY = y - scrollY
  2209.    
  2210.     local bRedraw = false
  2211.     if screenX < 1 then
  2212.         scrollX = x - 1
  2213.         screenX = 1
  2214.         bRedraw = true
  2215.     elseif screenX > w then
  2216.         scrollX = x - w
  2217.         screenX = w
  2218.         bRedraw = true
  2219.     end
  2220.    
  2221.     if screenY < 1 then
  2222.         scrollY = y - 1
  2223.         screenY = 1
  2224.         bRedraw = true
  2225.     elseif screenY > h-1 then
  2226.         scrollY = y - (h-1)
  2227.         screenY = h-1
  2228.         bRedraw = true
  2229.     end
  2230.    
  2231.     if bRedraw then
  2232.         redrawText()
  2233.     end
  2234.     term.setCursorPos( screenX, screenY )
  2235.    
  2236.     -- Statusbar now pertains to menu, it would probably be safe to redraw the menu on every key event.
  2237.     redrawMenu()
  2238. end
  2239.  
  2240. -- Actual program functionality begins
  2241. load(sPath)
  2242.  
  2243. term.setBackgroundColour( bgColour )
  2244. term.clear()
  2245. term.setCursorPos(x,y)
  2246. term.setCursorBlink( true )
  2247.  
  2248. redrawText()
  2249. redrawMenu()
  2250.  
  2251. -- Handle input
  2252. while bRunning do
  2253.     local sEvent, param, param2, param3 = os.pullEvent()
  2254.     if sEvent == "key" then
  2255.         if param == keys.up then
  2256.             -- Up
  2257.             if not bMenu then
  2258.                 if y > 1 then
  2259.                     -- Move cursor up
  2260.                     y = y - 1
  2261.                     x = math.min( x, string.len( tLines[y] ) + 1 )
  2262.                     setCursor( x, y )
  2263.                 end
  2264.             end
  2265.         elseif param == keys.down then
  2266.             -- Down
  2267.             if not bMenu then
  2268.                 -- Move cursor down
  2269.                 if y < #tLines then
  2270.                     y = y + 1
  2271.                     x = math.min( x, string.len( tLines[y] ) + 1 )
  2272.                     setCursor( x, y )
  2273.                 end
  2274.             end
  2275.         elseif param == keys.tab then
  2276.             -- Tab
  2277.             if not bMenu then
  2278.                 local sLine = tLines[y]
  2279.  
  2280.                 -- Indent line
  2281.                 -- IN CASE OF INSERT TAB IN PLACE:
  2282.                 -- tLines[y] = string.sub(sLine,1,x-1) .. "  " .. string.sub(sLine,x)
  2283.                 tLines[y]="  "..tLines[y]
  2284.                 x = x + 2
  2285.                 setCursor( x, y )
  2286.                 redrawLine(y)
  2287.             end
  2288.         elseif param == keys.pageUp then
  2289.             -- Page Up
  2290.             if not bMenu then
  2291.                 -- Move up a page
  2292.                 local sx,sy=term.getSize()
  2293.                 y=y-sy-1
  2294.                 if y<1 then y=1 end
  2295.                 x = math.min( x, string.len( tLines[y] ) + 1 )
  2296.                 setCursor( x, y )
  2297.             end
  2298.         elseif param == keys.pageDown then
  2299.             -- Page Down
  2300.             if not bMenu then
  2301.                 -- Move down a page
  2302.                 local sx,sy=term.getSize()
  2303.                 if y<#tLines-sy-1 then
  2304.                     y = y+sy-1
  2305.                 else
  2306.                     y = #tLines
  2307.                 end
  2308.                 x = math.min( x, string.len( tLines[y] ) + 1 )
  2309.                 setCursor( x, y )
  2310.             end
  2311.         elseif param == keys.home then
  2312.             -- Home
  2313.             if not bMenu then
  2314.                 -- Move cursor to the beginning
  2315.                 x=1
  2316.                 setCursor(x,y)
  2317.             end
  2318.         elseif param == keys["end"] then
  2319.             -- End
  2320.             if not bMenu then
  2321.                 -- Move cursor to the end
  2322.                 x = string.len( tLines[y] ) + 1
  2323.                 setCursor(x,y)
  2324.             end
  2325.         elseif param == keys.left then
  2326.             -- Left
  2327.             if not bMenu then
  2328.                 if x > 1 then
  2329.                     -- Move cursor left
  2330.                     x = x - 1
  2331.                 elseif x==1 and y>1 then
  2332.                     x = string.len( tLines[y-1] ) + 1
  2333.                     y = y - 1
  2334.                 end
  2335.                 setCursor( x, y )
  2336.             else
  2337.                 -- Move menu left
  2338.                 nMenuItem = nMenuItem - 1
  2339.                 if nMenuItem < 1 then
  2340.                     nMenuItem = #tMenuItems
  2341.                 end
  2342.                 redrawMenu()
  2343.             end
  2344.         elseif param == keys.right then
  2345.             -- Right
  2346.             if not bMenu then
  2347.                 if x < string.len( tLines[y] ) + 1 then
  2348.                     -- Move cursor right
  2349.                     x = x + 1
  2350.                 elseif x==string.len( tLines[y] ) + 1 and y<#tLines then
  2351.                     x = 1
  2352.                     y = y + 1
  2353.                 end
  2354.                 setCursor( x, y )
  2355.             else
  2356.                 -- Move menu right
  2357.                 nMenuItem = nMenuItem + 1
  2358.                 if nMenuItem > #tMenuItems then
  2359.                     nMenuItem = 1
  2360.                 end
  2361.                 redrawMenu()
  2362.             end
  2363.         elseif param == keys.delete then
  2364.             -- Delete
  2365.             if not bMenu then
  2366.                 if  x < string.len( tLines[y] ) + 1 then
  2367.                     local sLine = tLines[y]
  2368.                     tLines[y] = string.sub(sLine,1,x-1) .. string.sub(sLine,x+1)
  2369.                     redrawLine(y)
  2370.                 elseif y<#tLines then
  2371.                     tLines[y] = tLines[y] .. tLines[y+1]
  2372.                     table.remove( tLines, y+1 )
  2373.                     redrawText()
  2374.                     redrawMenu()
  2375.                 end
  2376.             end
  2377.         elseif param == keys.backspace then
  2378.             -- Backspace
  2379.             if not bMenu then
  2380.                 if x > 1 then
  2381.                     -- Remove character
  2382.                     local sLine = tLines[y]
  2383.                     tLines[y] = string.sub(sLine,1,x-2) .. string.sub(sLine,x)
  2384.                     redrawLine(y)
  2385.            
  2386.                     x = x - 1
  2387.                     setCursor( x, y )
  2388.                 elseif y > 1 then
  2389.                     -- Remove newline
  2390.                     local sPrevLen = string.len( tLines[y-1] )
  2391.                     tLines[y-1] = tLines[y-1] .. tLines[y]
  2392.                     table.remove( tLines, y )
  2393.                     redrawText()
  2394.                
  2395.                     x = sPrevLen + 1
  2396.                     y = y - 1
  2397.                     setCursor( x, y )
  2398.                 end
  2399.             end
  2400.         elseif param == keys.enter then
  2401.             -- Enter
  2402.             if not bMenu then
  2403.                 -- Newline
  2404.                 local sLine = tLines[y]
  2405.                 local _,spaces=string.find(sLine,"^[ ]+")
  2406.                 if not spaces then
  2407.                     spaces=0
  2408.                 end
  2409.                 tLines[y] = string.sub(sLine,1,x-1)
  2410.                 table.insert( tLines, y+1, string.rep(' ',spaces)..string.sub(sLine,x) )
  2411.                 redrawText()
  2412.            
  2413.                 x = spaces+1
  2414.                 y = y + 1
  2415.                 setCursor( x, y )
  2416.             else
  2417.                 -- Menu selection
  2418.                 doMenuItem( nMenuItem )
  2419.             end
  2420.         elseif param == keys.leftCtrl or param == keys.rightCtrl then
  2421.             -- Menu toggle
  2422.             bMenu = not bMenu
  2423.             if bMenu then
  2424.                 term.setCursorBlink( false )
  2425.                 nMenuItem = 1
  2426.             else
  2427.                 term.setCursorBlink( true )
  2428.             end
  2429.             redrawMenu()
  2430.         end
  2431.        
  2432.     elseif sEvent == "char" then
  2433.         if not bMenu then
  2434.             -- Input text
  2435.             local sLine = tLines[y]
  2436.             tLines[y] = string.sub(sLine,1,x-1) .. param .. string.sub(sLine,x)
  2437.             redrawLine(y)
  2438.        
  2439.             x = x + string.len( param )
  2440.             setCursor( x, y )
  2441.         else
  2442.             -- Select menu items
  2443.             for n,sMenuItem in ipairs( tMenuItems ) do
  2444.                 if string.lower(string.sub(sMenuItem,1,1)) == string.lower(param) then
  2445.                     doMenuItem( n )
  2446.                     break
  2447.                 end
  2448.             end
  2449.         end
  2450.        
  2451.     elseif sEvent == "mouse_click" then
  2452.         if not bMenu then
  2453.             if param == 1 then
  2454.                 -- Left click
  2455.                 local cx,cy = param2, param3
  2456.                 if cy < h then
  2457.                     y = math.min( math.max( scrollY + cy, 1 ), #tLines )
  2458.                     x = math.min( math.max( scrollX + cx, 1 ), string.len( tLines[y] ) + 1 )
  2459.                     setCursor( x, y )
  2460.                 end
  2461.             end
  2462.         end
  2463.        
  2464.     elseif sEvent == "mouse_scroll" then
  2465.         if not bMenu then
  2466.             if param == -1 then
  2467.                 -- Scroll up
  2468.                 if scrollY > 0 then
  2469.                     -- Move cursor up
  2470.                     scrollY = scrollY - 1
  2471.                     redrawText()
  2472.                 end
  2473.            
  2474.             elseif param == 1 then
  2475.                 -- Scroll down
  2476.                 local nMaxScroll = #tLines - (h-1)
  2477.                 if scrollY < nMaxScroll then
  2478.                     -- Move cursor down
  2479.                     scrollY = scrollY + 1
  2480.                     redrawText()
  2481.                 end
  2482.                
  2483.             end
  2484.         end
  2485.     end
  2486. end
  2487.  
  2488. -- Cleanup
  2489. term.clear()
  2490. term.setCursorBlink( false )
  2491. term.setCursorPos( 1, 1 )
  2492.   ]]},
  2493.   {["type"]="file", ["name"]="list", contents=[[
  2494. os.enableAPI("fs")
  2495. os.enableAPI("textutils")
  2496.  
  2497. local tArgs = { ... }
  2498.  
  2499. -- Get all the files in the directory
  2500. local sDir = shell.dir()
  2501. if tArgs[1] ~= nil then
  2502.     sDir = shell.resolve( tArgs[1] )
  2503. end
  2504.  
  2505. -- Sort into dirs/files, and calculate column count
  2506. local tAll = fs.list( sDir )
  2507. local tFiles = {}
  2508. local tDirs = {}
  2509.  
  2510. for n, sItem in pairs( tAll ) do
  2511.     if string.sub( sItem, 1, 1 ) ~= "." then
  2512.         local sPath = fs.combine( sDir, sItem )
  2513.         if fs.isDir( sPath ) then
  2514.             table.insert( tDirs, sItem )
  2515.         else
  2516.             table.insert( tFiles, sItem )
  2517.         end
  2518.     end
  2519. end
  2520. table.sort( tDirs )
  2521. table.sort( tFiles )
  2522.  
  2523. if term.isColour() then
  2524.     textutils.pagedTabulate( colors.lime, tDirs, colours.white, tFiles )
  2525. else
  2526.     textutils.pagedTabulate( tDirs, tFiles )
  2527. end
  2528.   ]]},
  2529.   {["type"]="file", ["name"]="lua", contents=[[
  2530. local tArgs = { ... }
  2531. os.addEventListener("key")
  2532. os.addEventListener("char")
  2533.  
  2534. local bRunning = true
  2535. local tCommandHistory = {}
  2536. local tEnv = {
  2537.   ["exit"] = function()
  2538.     bRunning = false
  2539.   end,
  2540. }
  2541. setmetatable( tEnv, { __index = getfenv() } )
  2542.  
  2543. if term.isColour() then
  2544.   term.setTextColour( colours.yellow )
  2545. end
  2546. print( "Interactive Lua prompt." )
  2547. term.print( "Call exit() to exit." )
  2548. term.setTextColour( colours.white )
  2549.  
  2550. while bRunning do
  2551.   --if term.isColour() then
  2552.   --  term.setTextColour( colours.yellow )
  2553.   --end
  2554.   term.write( "lua> " )
  2555.   --term.setTextColour( colours.white )
  2556.  
  2557.   local s = term.read( nil, tCommandHistory )
  2558.   table.insert( tCommandHistory, s )
  2559.  
  2560.   local nForcePrint = 0
  2561.   local func, e = loadstring( s, "lua" )
  2562.   local func2, e2 = loadstring( "return "..s, "lua" )
  2563.   if not func then
  2564.     if func2 then
  2565.       func = func2
  2566.       e = nil
  2567.       nForcePrint = 1
  2568.     end
  2569.   else
  2570.     if func2 then
  2571.       func = func2
  2572.     end
  2573.   end
  2574.  
  2575.   if func then
  2576.         setfenv( func, tEnv )
  2577.         local tResults = { pcall( function() return func() end ) }
  2578.         if tResults[1] then
  2579.           local n = 1
  2580.           while (tResults[n + 1] ~= nil) or (n <= nForcePrint) do
  2581.             term.print( tostring( tResults[n + 1] ) )
  2582.             n = n + 1
  2583.           end
  2584.         else
  2585.           term.print( "ERROR: "..tResults[2] )
  2586.         end
  2587.     else
  2588.       term.print( "ERROR: "..e )
  2589.     end
  2590.    
  2591. end
  2592.   ]]}
  2593. }},
  2594. {["type"]="directory", ["name"]="lib", contents={
  2595.   {["type"]="file", ["name"]="bit", contents=[[
  2596. -- CC bit API can't work with large numbers
  2597. -- this one is from: http://l-u-i-s.blogspot.com/2008/10/criptografia-md5-em-lua.html
  2598.  
  2599. --BEGIN API HEADER--
  2600. --VER 1.0
  2601. --NAM bitAPI
  2602. --END API HEADER--
  2603.  
  2604. function imod(a, b)
  2605. return a - math.floor(a / b) * b
  2606. end
  2607.  
  2608. function brshift(a, b)
  2609. if (a < 0) then
  2610. a = 4294967296 + a
  2611. end
  2612. if (b < 0) then
  2613. b = 4294967296 + b
  2614. end
  2615.  
  2616. a = imod(a, 4294967296)
  2617. b = imod(b, 4294967296)
  2618.  
  2619. return math.floor(a / (2 ^ b))
  2620. end
  2621.  
  2622. function blshift(a, b)
  2623. if (a < 0) then
  2624. a = 4294967296 + a
  2625. end
  2626. if (b < 0) then
  2627. b = 4294967296 + b
  2628. end
  2629.  
  2630. a = imod(a, 4294967296)
  2631. b = imod(b, 4294967296)
  2632.  
  2633. return math.floor(a * (2 ^ b))
  2634. end
  2635.  
  2636. function band(a, b)
  2637. local i, v, r, b1, b2
  2638.  
  2639. if (a < 0) then
  2640. a = 4294967296 + a
  2641. end
  2642. if (b < 0) then
  2643. b = 4294967296 + b
  2644. end
  2645.  
  2646. a = imod(a, 4294967296)
  2647. b = imod(b, 4294967296)
  2648.  
  2649. r = 0
  2650. for i = 31, 0, -1 do
  2651. v = 2 ^ i
  2652. b1 = a >= v
  2653. b2 = b >= v
  2654.  
  2655. if (b1) and (b2) then
  2656. r = r + v
  2657. end
  2658.  
  2659. if (b1) then
  2660. a = a - v
  2661. end
  2662. if (b2) then
  2663. b = b - v
  2664. end
  2665. end
  2666.  
  2667. return r
  2668. end
  2669.  
  2670. function bor(a, b)
  2671. local i, v, r, b1, b2
  2672.  
  2673. if (a < 0) then
  2674. a = 4294967296 + a
  2675. end
  2676. if (b < 0) then
  2677. b = 4294967296 + b
  2678. end
  2679.  
  2680. a = imod(a, 4294967296)
  2681. b = imod(b, 4294967296)
  2682.  
  2683. r = 0
  2684. for i = 31, 0, -1 do
  2685. v = 2 ^ i
  2686. b1 = a >= v
  2687. b2 = b >= v
  2688.  
  2689. if (b1) or (b2) then
  2690. r = r + v
  2691. end
  2692.  
  2693. if (b1) then
  2694. a = a - v
  2695. end
  2696. if (b2) then
  2697. b = b - v
  2698. end
  2699. end
  2700.  
  2701. return r
  2702. end
  2703.  
  2704. function bxor(a, b)
  2705. local i, v, r, b1, b2
  2706.  
  2707. if (a < 0) then
  2708. a = 4294967296 + a
  2709. end
  2710. if (b < 0) then
  2711. b = 4294967296 + b
  2712. end
  2713.  
  2714. a = imod(a, 4294967296)
  2715. b = imod(b, 4294967296)
  2716.  
  2717. r = 0
  2718. for i = 31, 0, -1 do
  2719. v = 2 ^ i
  2720. b1 = a >= v
  2721. b2 = b >= v
  2722.  
  2723. if (b1 ~= b2) then
  2724. r = r + v
  2725. end
  2726.  
  2727. if (b1) then
  2728. a = a - v
  2729. end
  2730. if (b2) then
  2731. b = b - v
  2732. end
  2733. end
  2734.  
  2735. return r
  2736. end
  2737.  
  2738. function bnot(a)
  2739. local i, v, r, b
  2740.  
  2741. if (a < 0) then
  2742. a = 4294967296 + a
  2743. end
  2744.  
  2745. a = imod(a, 4294967296)
  2746.  
  2747. r = 0
  2748. for i = 31, 0, -1 do
  2749. v = 2 ^ i
  2750. b = a >= v
  2751.  
  2752. if (b) then
  2753. a = a - v
  2754. else
  2755. r = r + v
  2756. end
  2757. end
  2758.  
  2759. return r
  2760. end
  2761.   ]]},
  2762.   {["type"]="file", ["name"]="config", contents=[[
  2763. --NO HEADER - this API is loaded in startup
  2764.  
  2765. -- User settings
  2766. local defaultSectionName = "Default"
  2767.  
  2768. -- private functions
  2769.  
  2770. local function read(path)
  2771.   local currentSection = defaultSectionName
  2772.   if not fs.exists(path) then
  2773.     fs.create(path)
  2774.   end
  2775.   local file = io.open(path, "r")
  2776.   local data = {}
  2777.  
  2778.   if not file then
  2779.     return nil, "Cannot open file: "..path
  2780.   end
  2781.   data[currentSection] = {}
  2782.   for line in file:lines() do
  2783.     if line:sub(1, 1) == "[" and line:sub(-1) == "]" then
  2784.       currentSection = line:sub(2, line:len() - 1)
  2785.       if not data[currentSection] then
  2786.         data[currentSection] = {}
  2787.       end
  2788.     else
  2789.       i = line:find("=")
  2790.       if i ~= nil then
  2791.         local key = line:sub(1, i-1)
  2792.         local val = line:sub(i + 1)
  2793.         data[currentSection][key] = val
  2794.       end
  2795.     end
  2796.   end
  2797.  
  2798.   file:close()
  2799.   return data, nil
  2800. end
  2801.  
  2802. local function save(path, config)
  2803.   local lastSection = ""
  2804.   local file = io.open(path, "w")
  2805.  
  2806.   if not file then
  2807.     error("Cannot open file: "..path)
  2808.     return nil, "Cannot open file: "..path
  2809.   end
  2810.   if config[defaultSectionName] then
  2811.     for key, value in pairs(config[defaultSectionName]) do
  2812.       file:write(key.."="..value.."\n")
  2813.     end
  2814.   end
  2815.  
  2816.   for section, data in pairs(config) do
  2817.     if section ~= defaultSectionName then
  2818.       if lastSection ~= section then
  2819.         file:write("["..section.."]\n")
  2820.         lastSection = section
  2821.       end
  2822.    
  2823.       for key, value in pairs(data) do
  2824.         file:write(key.."="..value.."\n")
  2825.       end
  2826.     end
  2827.   end
  2828.   file:close()
  2829.   return true, nil
  2830. end
  2831.  
  2832. -- public functions
  2833.  
  2834.  
  2835. function open(path)
  2836.  
  2837.   -- Private variables
  2838.   local self = {}
  2839.   local filePath = path
  2840.  
  2841.   -- Private fields
  2842.   local data, e = read(path)
  2843.   if not data then return nil, e end
  2844.  
  2845.   -- Public methods  
  2846.   self.add = function(section, key, value)
  2847.     section = section or defaultSectionName
  2848.     if not data[section] then
  2849.       data[section] = {}
  2850.     end
  2851.     data[section][key] = tostring(value)
  2852.   end
  2853.  
  2854.   self.set = self.add
  2855.  
  2856.   self.get = function(section, key, default)
  2857.     section = section or defaultSectionName
  2858.     if not data[section] then
  2859.       data[section] = {}
  2860.       data[section][key] = default
  2861.       return default
  2862.     end
  2863.     if not data[section][key] then
  2864.       data[section][key] = default
  2865.       return default
  2866.     end
  2867.     return data[section][key]
  2868.   end
  2869.  
  2870.   self.save = function(location)
  2871.     if not location then
  2872.       local v, e = save(filePath, data)
  2873.     else
  2874.       local v, e = save(location, data)
  2875.     end
  2876.     return v, e
  2877.   end
  2878.  
  2879.   local mt = self
  2880.  
  2881.   mt.__newindex = function(table, key, value)
  2882.     printError("Attempt to illegally set "..key.." to: "..value)
  2883.     sleep(5)
  2884.     os.shutdown()
  2885.   end
  2886.  
  2887.   mt.__index = function(table, key)
  2888.     if type(key) ~= "function" then
  2889.       printError("Attempt to illegally read"..tostring(key))
  2890.       sleep(5)
  2891.       os.shutdown()
  2892.     end
  2893.   end
  2894.  
  2895.   setmetatable(self, mt)
  2896.   return self  
  2897. end
  2898.  
  2899. new = open
  2900.   ]]},
  2901.   {["type"]="file", ["name"]="header", contents=[[
  2902. --NO HEADER - this API is loaden in startup
  2903.  
  2904. -- constans
  2905. local headerStartPattern = "--BEGIN $NAME HEADER--"
  2906. local headerStopPattern  = "--END $NAME HEADER--"
  2907.  
  2908. -- private functions
  2909.  
  2910. --returns name, value
  2911. --line in format: "--NAME VALUE"
  2912. local function readValue(line)
  2913.   line = line:sub(3)
  2914.   n, space = line:find("(%w+) ")
  2915.   key = line:sub(1, space-1)
  2916.   value = line:sub(space+1)
  2917.   return key, value
  2918. end
  2919.  
  2920. --public functions
  2921.  
  2922. function read(filePath, name)
  2923.   file = io.open(filePath, "r")
  2924.   local headerStarted = false
  2925.   name = string.upper(name)
  2926.   local headerStart = headerStartPattern:gsub("%$(%w+)", name)
  2927.   local headerEnd   = headerStopPattern:gsub("%$(%w+)", name)
  2928.   local header = {}
  2929.   for line in file:lines() do
  2930.     if not headerStarted then
  2931.       if line == headerStart then
  2932.         headerStarted = true
  2933.       end
  2934.     else
  2935.       if line == headerEnd then
  2936.         break
  2937.       else
  2938.         local k, v = readValue(line)
  2939.         if k and v then
  2940.           header[k] = v
  2941.         end
  2942.       end
  2943.    
  2944.     end
  2945.   end
  2946.   return header
  2947. end
  2948.   ]]},
  2949.   {["type"]="file", ["name"]="md5", contents=[[
  2950. -- An MD5 mplementation in Lua, requires bitlib
  2951. -- 10/02/2001 jcw@equi4.com
  2952. -- @grabie2 actually somehow produces wrong hashes, but it's kind of good
  2953. -- cause then any existing hash databases won't work with it ;)
  2954. --BEGIN API HEADER--
  2955. --VER 1.0
  2956. --NAM md5API
  2957. --END API HEADER--
  2958.  
  2959. local md5=
  2960. {
  2961.     ff=tonumber('ffffffff',16),
  2962.     consts={},
  2963. }
  2964.  
  2965. do
  2966. string.gsub (]].."[["..[[ d76aa478 e8c7b756 242070db c1bdceee
  2967.                 f57c0faf 4787c62a a8304613 fd469501
  2968.                 698098d8 8b44f7af ffff5bb1 895cd7be
  2969.                 6b901122 fd987193 a679438e 49b40821
  2970.                 f61e2562 c040b340 265e5a51 e9b6c7aa
  2971.                 d62f105d 02441453 d8a1e681 e7d3fbc8
  2972.                 21e1cde6 c33707d6 f4d50d87 455a14ed
  2973.                 a9e3e905 fcefa3f8 676f02d9 8d2a4c8a
  2974.                 fffa3942 8771f681 6d9d6122 fde5380c
  2975.                 a4beea44 4bdecfa9 f6bb4b60 bebfbc70
  2976.                 289b7ec6 eaa127fa d4ef3085 04881d05
  2977.                 d9d4d039 e6db99e5 1fa27cf8 c4ac5665
  2978.                 f4292244 432aff97 ab9423a7 fc93a039
  2979.                 655b59c3 8f0ccc92 ffeff47d 85845dd1
  2980.                 6fa87e4f fe2ce6e0 a3014314 4e0811a1
  2981.                 f7537e82 bd3af235 2ad7d2bb eb86d391
  2982.                 67452301 efcdab89 98badcfe 10325476 ]].."]]"..[[,
  2983.                 '(%w+)',
  2984.                 function (sString) table.insert(md5.consts,tonumber(sString,16)) end
  2985.             )
  2986. end
  2987.  
  2988. function Calc(s)
  2989.   local function transform(A,B,C,D)
  2990.     local f=function (x,y,z)
  2991.         return bit.bor(bit.band(x,y),bit.band(-x-1,z))
  2992.     end
  2993.    
  2994.     local g=function (x,y,z)
  2995.         return bit.bor(bit.band(x,z),bit.band(y,-z-1))
  2996.     end
  2997.    
  2998.     local h=function (x,y,z)
  2999.         return bit.bxor(x,bit.bxor(y,z))
  3000.     end
  3001.    
  3002.     local i=function (x,y,z)
  3003.         return bit.bxor(y,bit.bor(x,-z-1))
  3004.     end
  3005.    
  3006.     local z=function (f,a,b,c,d,x,s,ac)
  3007.         a=bit.band(a+f(b,c,d)+x+a*c,md5.ff)
  3008.         -- be *very* careful that left shift does not cause rounding!      
  3009.         return bit.bor(bit.blshift(bit.band(a,bit.brshift(md5.ff,s)),s), bit.brshift(a,32-s)) + b
  3010.     end
  3011.    
  3012.     local a,b,c,d=A,B,C,D
  3013.     local t=md5.consts
  3014.   end
  3015.  
  3016.   local msgLen=string.len(s)
  3017.   local padLen=56-(msgLen%64)
  3018.  
  3019.   if (msgLen%64)>56 then padLen=padLen+64 end
  3020.  
  3021.   if padLen==0 then padLen=64 end
  3022.  
  3023.   s=s..string.char(128)..string.rep(string.char(0),padLen-1)
  3024.   s=s..leIstr(8*msgLen)..leIstr(0)
  3025.  
  3026.   assert((string.len(s)%64)==0)
  3027.  
  3028.   local t=md5.consts
  3029.   local a,b,c,d=t[65],t[66],t[67],t[68]
  3030.  
  3031.   for i=1,string.len(s),64 do  
  3032.     X=leStrCuts(string.sub(s,i,i+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4)
  3033.     assert(table.getn(X)==16)
  3034.     X[0]=table.remove(X,1) -- zero based!
  3035.     a,b,c,d=transform(a,b,c,d)
  3036.   end
  3037.  
  3038.   local swap=function (w) return beInt(leIstr(w)) end
  3039.   return string.format("%08x%08x%08x%08x",swap(a),swap(b),swap(c),swap(d))
  3040. end
  3041.  
  3042. -- convert little-endian 32-bit int to a 4-char string
  3043. function leIstr(i)
  3044.   local f=function (s) return string.char(bit.band(bit.brshift(i,s),255)) end
  3045.   return f(0)..f(8)..f(16)..f(24)
  3046. end
  3047.  
  3048. do -- from util.lua
  3049.   -- convert raw string to big-endian int
  3050.   function beInt(s)
  3051.     local v=0
  3052.     for i=1,string.len(s) do v=v*256+string.byte(s,i) end
  3053.     return v
  3054.   end
  3055.   -- convert raw string to little-endian int
  3056.   function leInt(s)
  3057.     local v=0
  3058.     for i=string.len(s),1,-1 do v=v*256+string.byte(s,i) end
  3059.     return v
  3060.   end
  3061.   -- cut up a string in little-endian ints of given size
  3062.   function leStrCuts(s,...)
  3063.     local o,r=1,{}
  3064.     for i=1,table.getn(arg) do
  3065.       table.insert(r,leInt(string.sub(s,o,o+arg[i]-1)))
  3066.       o=o+arg[i]
  3067.     end
  3068.     return r
  3069.   end
  3070. end
  3071.   ]]},
  3072.   {["type"]="file", ["name"]="store", contents=[[
  3073. --NO HEADER - this API is loaded in startup
  3074.  
  3075. local conf = config.open(os.getPath().."/etc/store")
  3076.  
  3077. local session = {}
  3078. local dbPath = conf.get(nil, "database path", fs.combine(os.getPath(),"/var/store"))
  3079.  
  3080. conf.save()
  3081.  
  3082. print("Store API 1.0 loaded")
  3083.  
  3084. if not fs.exists(dbPath) then
  3085.   term.printWarning("db file("..dbPath..") not found, created one")
  3086.   fs.create(dbPath)
  3087. end
  3088.  
  3089. -- private functions
  3090.  
  3091. local function save(namespace)
  3092.   local data = textutils.serialize(session)
  3093.   local file = io.open(dbPath, "w")
  3094.   if file then
  3095.     file:write(data)
  3096.     file:close()
  3097.   else
  3098.     return false
  3099.   end
  3100. end
  3101.  
  3102. -- public functions
  3103.  
  3104. function set(namespace, key, value)
  3105.   if type(session[namespace]) ~= "table" then
  3106.     session[namespace] = {}
  3107.   end
  3108.   session[namespace][key] = value
  3109.   save()
  3110. end
  3111.  
  3112. function get(namespace, key)
  3113.   if type(session[namespace]) ~= "table" then
  3114.     session[namespace] = {}
  3115.   end
  3116.   return session[namespace][key]
  3117. end
  3118.   ]]},
  3119.   {["type"]="file", ["name"]="textutils", contents=[[
  3120. --BEGIN API HEADER--
  3121. --VER 0.4 netOS
  3122. --NAM textutils
  3123. --END API HEADER--
  3124.  
  3125. function slowWrite( sText, nRate )
  3126.     nRate = nRate or 20
  3127.     if nRate < 0 then
  3128.         error( "rate must be positive" )
  3129.     end
  3130.     local nSleep = 1 / nRate
  3131.        
  3132.     sText = tostring( sText )
  3133.     local x,y = term.getCursorPos(x,y)
  3134.     local len = string.len( sText )
  3135.    
  3136.     for n=1,len do
  3137.         term.setCursorPos( x, y )
  3138.         sleep( nSleep )
  3139.         local nLines = write( string.sub( sText, 1, n ) )
  3140.         local newX, newY = term.getCursorPos()
  3141.         y = newY - nLines
  3142.     end
  3143. end
  3144.  
  3145. function slowPrint( sText, nRate )
  3146.     slowWrite( sText, nRate)
  3147.     print()
  3148. end
  3149.  
  3150. function formatTime( nTime, bTwentyFourHour )
  3151.     local sTOD = nil
  3152.     if not bTwentyFourHour then
  3153.         if nTime >= 12 then
  3154.             sTOD = "PM"
  3155.         else
  3156.             sTOD = "AM"
  3157.         end
  3158.         if nTime >= 13 then
  3159.             nTime = nTime - 12
  3160.         end
  3161.     end
  3162.  
  3163.     local nHour = math.floor(nTime)
  3164.     local nMinute = math.floor((nTime - nHour)*60)
  3165.     if sTOD then
  3166.         return string.format( "%d:%02d %s", nHour, nMinute, sTOD )
  3167.     else
  3168.         return string.format( "%d:%02d", nHour, nMinute )
  3169.     end
  3170. end
  3171.  
  3172. local function makePagedScroll( _term, _nFreeLines )
  3173.     local nativeScroll = _term.scroll
  3174.     local nFreeLines = _nFreeLines or 0
  3175.     return function( _n )
  3176.         for n=1,_n do
  3177.             nativeScroll( 1 )
  3178.            
  3179.             if nFreeLines <= 0 then
  3180.                 local w,h = _term.getSize()
  3181.                 _term.setCursorPos( 1, h )
  3182.                 _term.write( "Press any key to continue" )
  3183.                 os.pullEvent( "key" )
  3184.                 _term.clearLine()
  3185.                 _term.setCursorPos( 1, h )
  3186.             else
  3187.                 nFreeLines = nFreeLines - 1
  3188.             end
  3189.         end
  3190.     end
  3191. end
  3192.  
  3193. function pagedPrint( _sText, _nFreeLines )
  3194.     local nativeScroll = term.scroll
  3195.     term.scroll = makePagedScroll( term, _nFreeLines )
  3196.     local result = print( _sText )
  3197.     term.scroll = nativeScroll
  3198.     return result
  3199. end
  3200.  
  3201. local function tabulateCommon( bPaged, ... )
  3202.     local tAll = { ... }
  3203.    
  3204.     local w,h = term.getSize()
  3205.     local nMaxLen = w / 8
  3206.     for n, t in ipairs( tAll ) do
  3207.         if type(t) == "table" then
  3208.             for n, sItem in pairs(t) do
  3209.                 nMaxLen = math.max( string.len( sItem ) + 1, nMaxLen )
  3210.             end
  3211.         end
  3212.     end
  3213.     local nCols = math.floor( w / nMaxLen )
  3214.  
  3215.     local nLines = 0
  3216.     local function newLine()
  3217.         if bPaged and nLines >= (h-3) then
  3218.             pagedPrint()
  3219.         else
  3220.             print()
  3221.         end
  3222.         nLines = nLines + 1
  3223.     end
  3224.    
  3225.     local function drawCols( _t )
  3226.         local nCol = 1
  3227.         for n, s in ipairs( _t ) do
  3228.             if nCol > nCols then
  3229.                 nCol = 1
  3230.                 newLine()
  3231.             end
  3232.  
  3233.             local cx,cy = term.getCursorPos()
  3234.             cx = 1 + (nCol - 1) * (w / nCols)
  3235.             term.setCursorPos( cx, cy )
  3236.             term.write( s )
  3237.  
  3238.             nCol = nCol + 1    
  3239.         end
  3240.         print()
  3241.     end
  3242.     for n, t in ipairs( tAll ) do
  3243.         if type(t) == "table" then
  3244.             if #t > 0 then
  3245.                 drawCols( t )
  3246.             end
  3247.         elseif type(t) == "number" then
  3248.             term.setTextColor( t )
  3249.         end
  3250.     end
  3251. end
  3252.  
  3253. function tabulate( ... )
  3254.     tabulateCommon( false, ... )
  3255. end
  3256.  
  3257. function pagedTabulate( ... )
  3258.     tabulateCommon( true, ... )
  3259. end
  3260.  
  3261. local function serializeImpl( t, tTracking )    
  3262.     local sType = type(t)
  3263.     if sType == "table" then
  3264.         if tTracking[t] ~= nil then
  3265.             error( "Cannot serialize table with recursive entries" )
  3266.         end
  3267.         tTracking[t] = true
  3268.        
  3269.         local result = "{"
  3270.         for k,v in pairs(t) do
  3271.             result = result..("["..serializeImpl(k, tTracking).."]="..serializeImpl(v, tTracking)..",")
  3272.         end
  3273.         result = result.."}"
  3274.         return result
  3275.        
  3276.     elseif sType == "string" then
  3277.         return string.format( "%q", t )
  3278.    
  3279.     elseif sType == "number" or sType == "boolean" or sType == "nil" then
  3280.         return tostring(t)
  3281.        
  3282.     else
  3283.         error( "Cannot serialize type "..sType )
  3284.        
  3285.     end
  3286. end
  3287.  
  3288. function serialize( t )
  3289.     local tTracking = {}
  3290.     return serializeImpl( t, tTracking )
  3291. end
  3292.  
  3293. function unserialize( s )
  3294.     local func, e = loadstring( "return "..s, "serialize" )
  3295.     if not func then
  3296.         return s
  3297.     else
  3298.         setfenv( func, {} )
  3299.         return func()
  3300.     end
  3301. end
  3302.  
  3303. function urlEncode( str )
  3304.     if str then
  3305.         str = string.gsub (str, "\n", "\r\n")
  3306.         str = string.gsub (str, "([^%w ])",
  3307.         function (c)
  3308.             return string.format ("%%%02X", string.byte(c))
  3309.         end)
  3310.         str = string.gsub (str, " ", "+")
  3311.     end
  3312.     return str  
  3313. end
  3314.   ]]},
  3315.   {["type"]="file", ["name"]="dialog", contents=[[
  3316. --BEGIN API HEADER--
  3317. --VER 1.0
  3318. --NAM dialogAPI
  3319. --END API HEADER--
  3320.  
  3321. --dialog api
  3322. --for advanced computers only
  3323.  
  3324. --constants public
  3325.  
  3326. --types
  3327. INFO = 1
  3328. MSGBOX = 2
  3329. TEXTBOX = 3
  3330.  
  3331. --buttons
  3332. YESNO = 1
  3333. OKCANCEL = 2
  3334. OK = 3
  3335.  
  3336. --constants private
  3337.  
  3338. local keyEnter = 28
  3339. local keyDown = 208
  3340. local keyUp = 200
  3341. local keyRight = 205
  3342. local keyLeft = 203
  3343.  
  3344. -- Private functions
  3345.  
  3346. local function findLocation(width, height)
  3347.   local termW, termH = term.getSize()
  3348.   local x = (termW - width) / 2
  3349.   local y = (termH - height) / 2
  3350.   return x, y
  3351. end
  3352.  
  3353. local function drawBox(x, y, width, height, color, titleColor, title)
  3354.   term.setBackgroundColor(color)
  3355.   term.setTextColor(colors.black)
  3356.   for i = y, height+y, 1 do
  3357.     if title and i == y then
  3358.       term.setBackgroundColor(titleColor)
  3359.     elseif title and i == y+1 then
  3360.       term.setBackgroundColor(color)
  3361.     end
  3362.    
  3363.     for j = x, width+x, 1 do
  3364.       term.setCursorPos(j, i)
  3365.       term.write(" ")
  3366.     end
  3367.   end
  3368.   if title then
  3369.     local titleX = (width - title:len()) / 2 + x
  3370.     term.setBackgroundColor(titleColor)
  3371.     term.setCursorPos(titleX, y)
  3372.     write(title)
  3373.   end
  3374. end
  3375.  
  3376. local function drawButtons(x, y, width, backColor, buttons, default)
  3377.   default = default or 0
  3378.   if #buttons < default then default = #buttons - 1 end
  3379.   local function drawButton(butX, text, active)
  3380.     term.setCursorPos(butX, y)
  3381.     if active == true then
  3382.       term.setBackgroundColor(colors.yellow)
  3383.       term.setTextColor(colors.red)
  3384.       term.write("<"..text..">")
  3385.     else
  3386.       term.setBackgroundColor(backColor)
  3387.       term.setTextColor(colors.black)
  3388.       term.write("<"..text..">")
  3389.     end
  3390.     local curX, curY = term.getCursorPos()
  3391.     return curX - butX
  3392.   end
  3393.  
  3394.   local buttonsWidth = 0
  3395.  
  3396.   for n, v in pairs(buttons) do
  3397.     buttonsWidth = buttonsWidth + v:len() + 3  
  3398.   end
  3399.  
  3400.   buttonsWidth = buttonsWidth -1
  3401.  
  3402.   local buttonsX = ((width - buttonsWidth) /2) + x
  3403.  
  3404.   local selected = false
  3405.   for n, v in pairs(buttons) do
  3406.     if tonumber(n) == default then selected = true else selected = false end
  3407.     buttonsX = buttonsX + drawButton(buttonsX, v, selected) + 1
  3408.   end
  3409.  
  3410. end
  3411.  
  3412. local function drawScrolls(x, y, height, backColor, active)
  3413.   term.setBackgroundColor(backColor)
  3414.   active = active or 0
  3415.   local curX, curY = term.getCursorPos()
  3416.   term.setCursorPos(x, y)
  3417.   if active == 1 or active == 0 then
  3418.     term.setTextColor(colors.yellow)
  3419.   else
  3420.     term.setTextColor(backColor)
  3421.   end
  3422.   term.write("/\\")
  3423.  
  3424.   if active == 2 or active == 0 then
  3425.     term.setTextColor(colors.yellow)
  3426.   else
  3427.     term.setTextColor(backColor)
  3428.   end
  3429.  
  3430.   term.setCursorPos(x, y + height)
  3431.   term.write("\\/")
  3432.  
  3433.   term.setCursorPos(curX, curY)
  3434. end
  3435.  
  3436. local function printText(x, y, width, height, color, backColor, text, scroll, active)
  3437.   local _ret = false
  3438.   term.setTextColor(color)
  3439.   term.setBackgroundColor(backColor)
  3440.  
  3441.   active = active or 0
  3442.   scroll = scroll or 0
  3443.   local curX = x
  3444.   local curY = y
  3445.  
  3446.   term.setCursorPos(curX, curY)
  3447.   local function completeLine()
  3448.     while curX < x + width do
  3449.       write(" ")
  3450.       curX = curX + 1
  3451.     end
  3452.   end
  3453.   local function newLine()
  3454.     if curY < y + height then
  3455.       if scroll == 0 then
  3456.         completeLine()
  3457.         curX = x
  3458.         curY = curY + 1
  3459.         term.setCursorPos(curX, curY)
  3460.       else scroll = scroll - 1 curX = x cury = y end
  3461.       return true
  3462.     else
  3463.       completeLine()
  3464.       --scrolling
  3465.       _ret = true
  3466.       return false
  3467.     end  
  3468.   end
  3469.   while text:len() > 0 do
  3470.     local whitespace = string.match(text, "^[ \t]+")
  3471.     if whitespace then
  3472.       if scroll == 0 then term.write(whitespace) end
  3473.       curX = curX + whitespace:len()
  3474.       text = string.sub(text, whitespace:len() + 1)
  3475.     end
  3476.     if curX >= x+width then
  3477.       if not newLine() then break end
  3478.     end    
  3479.     local newline = string.match(text, "^\n")
  3480.     if newline then
  3481.       if not newLine() then break end
  3482.       text = string.sub(text, 2)
  3483.     end
  3484.    
  3485.     local word = string.match(text, "^[^ \t\n]+")
  3486.     if word then
  3487.      
  3488.       text = string.sub(text, word:len() + 1)
  3489.       if word:len() > width then
  3490.      
  3491.         while word:len() > 0 do
  3492.      
  3493.           if scroll == 0 then term.write(string.sub(word, 1, 1)) end
  3494.           curX = curX + 1
  3495.           if curX >= x + width then
  3496.             if not newLine() then break end
  3497.           end
  3498.           word = string.sub(word, 2)
  3499.         end
  3500.      
  3501.       else
  3502.         if curX + word:len() > x + width then
  3503.           if not newLine() then break end
  3504.         end
  3505.         if scroll == 0 then term.write(word) end
  3506.         curX = curX + word:len()
  3507.       end
  3508.     end
  3509.   end
  3510.   completeLine()
  3511.   return _ret
  3512. end
  3513.  
  3514. local function drawTextBox(x, y, w, text)
  3515.   term.setBackgroundColor(colors.yellow)
  3516.   term.setCursorPos(x, y)
  3517.   for cx = x, x+w, 1 do
  3518.     term.write(" ")
  3519.   end
  3520.   term.setCursorPos(x, y+2)
  3521.   for cx = x, x+w, 1 do
  3522.     term.write(" ")
  3523.   end
  3524.   term.setCursorPos(x, y+1) write(" ")
  3525.   term.setCursorPos(x+w, y+1) write(" ")  
  3526.   if #text <= w-3 then
  3527.     printText(x+2, y+1, w-4, 1, colors.black, colors.blue, text)
  3528.     term.setCursorPos(x + #text + 2, y+1)
  3529.   else
  3530.     text = text:sub(-(w -4))
  3531.     printText(x+2, y+1, w-4, 1, colors.black, colors.blue, text)    
  3532.   end
  3533.  
  3534. end
  3535.  
  3536. --public functions
  3537.  
  3538. function new(width, height, title, dialogType, buttons)
  3539.   local this = {}
  3540.   local x, y = 0
  3541.   this.setSize = function(w, h)
  3542.     x, y = findLocation(w, h)
  3543.     width = w
  3544.     height = h
  3545.   end
  3546.  
  3547.   this.setSize(width, height)
  3548.  
  3549.   dialogType = dialogType or INFO
  3550.   buttons = buttons or O
  3551.   this.setButtons = function(btns)
  3552.     if type(btns) == "number" then
  3553.       if btns == OK then
  3554.         buttons = {}
  3555.         buttons[0] = "OK"
  3556.       elseif btns == YESNO then
  3557.         buttons = {}
  3558.         buttons[0] = "Yes"
  3559.         buttons[1] = "No"
  3560.       elseif btns == OKCANCEL then
  3561.         buttons = {}
  3562.         buttons[0] = "OK"
  3563.         buttons[1] = "Cancel"
  3564.       else
  3565.         buttons = {}
  3566.         buttons[0] = "OK"
  3567.       end
  3568.     end
  3569.   end
  3570.   this.setButtons(buttons)
  3571.  
  3572.   this.title = title
  3573.   this.activeButton = 0
  3574.   this.scroll = 0
  3575.   this.drawScrolls = true
  3576.   this.canScrollDown = false
  3577.        
  3578.   if dialogType == MSGBOX then
  3579.     this.text = ""
  3580.     this.render = function(clear)
  3581.       clear = clear or false
  3582.       if clear then term.clear(colors.black) end
  3583.       drawBox(x, y, width, height, colors.blue, colors.lightBlue, this.title)
  3584.       drawButtons(x, y + height, width, colors.blue, buttons, this.activeButton)
  3585.  
  3586.       local textWidth = width - 3
  3587.       if not drawScrolls then textWidth = width -1 end
  3588.      
  3589.       this.canScrollDown = printText(x + 1, y + 1, textWidth, height - 3, colors.black, colors.blue, this.text, this.scroll)
  3590.      
  3591.       local scrolls = -1
  3592.       if this.scroll > 0 and this.canScrollDown then scrolls = 0
  3593.       elseif this.canScrollDown then scrolls = 2
  3594.       elseif this.scroll > 0 then scrolls = 1 end
  3595.       if drawScrolls and scrolls >= 0 then
  3596.         drawScrolls(x + width - 2, y + 1, height - 2, colors.blue, scrolls)
  3597.       end
  3598.     end
  3599.    
  3600.     this.update = function(e, p1, p2, p3, p4, p5)
  3601.       if e == "key" then
  3602.         if p1 == keyUp and this.scroll > 0 then
  3603.               this.scroll = this.scroll - 1
  3604.         elseif p1 == keyDown and this.canScrollDown then
  3605.               this.scroll = this.scroll +1
  3606.         elseif p1 == keyLeft and this.activeButton < #buttons then
  3607.               this.activeButton = this.activeButton +1
  3608.         elseif p1 == keyRight and this.activeButton > 0 then
  3609.               this.activeButton = this.activeButton -1
  3610.         end
  3611.       end      
  3612.     end
  3613.    
  3614.     this.run = function(clear)
  3615.       print("run")
  3616.       local wasKey = true
  3617.       local wasChar = true
  3618.       if os.call then
  3619.         print("call")
  3620.         wasKey = os.isEventListener("key")
  3621.         wasChar = os.isEventListener("char")
  3622.         if not wasKey then os.addEventListener("key") end
  3623.         if not wasChar then os.addEventListener("char") end
  3624.       end
  3625.       clear = clear or false
  3626.       this.render(clear)
  3627.       while true do
  3628.         local e, p1 = os.pullEvent()
  3629.         if e == "key" and p1 == keyEnter then break end
  3630.         this.update(e, p1)
  3631.         this.render()
  3632.       end
  3633.       if not wasKey then os.removeEventListener("key") end
  3634.       if not wasCHar then os.removeEventListener("char") end
  3635.       return this.activeButton
  3636.     end
  3637.   elseif dialogType == INFO then
  3638.     this.render = function(clear)
  3639.       clear = clear or false
  3640.       if clear then term.clear(colors.black) end
  3641.      
  3642.       drawBox(x, y, width, height, colors.blue, colors.lightBlue, this.title)
  3643.      
  3644.       local textWidth = width - 3
  3645.       if not this.drawScrolls then textWidth = width -1 end
  3646.      
  3647.       this.canScrollDown = printText(x+1, y+1, textWidth, height -1, colors.black, colors.blue, this.text, this.scroll)
  3648.      
  3649.       local scrolls = -1
  3650.       if this.scroll > 0 and this.canScrollDown then scrolls = 0
  3651.       elseif this.canScrollDown then scrolls = 2
  3652.       elseif this.scroll > 0 then scrolls = 1 end
  3653.      
  3654.       if this.drawScrolls and scrolls >= 0 then
  3655.         drawScrolls(x + width - 2, y +1, height -2, colors.blue, scrolls)
  3656.       end
  3657.     end
  3658.    
  3659.     this.update = function(e, p1, p2, p3, p4, p5)
  3660.       if e == "key" then
  3661.         if p1 == keyDown and this.canScrollDown then
  3662.            this.scroll = this.scroll + 1
  3663.         elseif p1 == keyUp and this.scroll > 0 then
  3664.            this.scroll = this.scroll - 1
  3665.         end  
  3666.       end
  3667.     end
  3668.    
  3669.     this.run = function(clear)
  3670.       local wasKey = false
  3671.       local wasChar = false
  3672.       if os.call then
  3673.         wasKey = os.isEventListener("key")
  3674.         wasChar = os.isEventListener("char")
  3675.         if not wasKey then os.addEventListener("key") end
  3676.         if not wasChar then os.addEventListener("char") end
  3677.       end
  3678.       this.render(clear)
  3679.       while true do
  3680.         local e, p1 = os.pullEvent()
  3681.         if e == "key" and p1 == keyEnter then break end
  3682.         this.update(e, p1)
  3683.         this.render()
  3684.       end
  3685.       if not wasKey then os.removeEventListener("key") end
  3686.       if not wasCHar then os.removeEventListener("char") end
  3687.       return this.activeButton
  3688.     end
  3689.   elseif dialogType == TEXTBOX then
  3690.     this.passwordChar = nil
  3691.     this.input = ""
  3692.     this.render = function(clear, part)
  3693.       part = part or 0
  3694.       term.setCursorBlink(false)
  3695.       if part == 0 then
  3696.         clear = clear or false
  3697.         if clear then term.clear(colors.black) end
  3698.         drawBox(x, y, width, height, colors.blue, colors.lightBlue, this.title)
  3699.       end
  3700.      
  3701.       local textWidth = width - 3
  3702.       local textHeight = height - 7
  3703.       if not this.drawScrolls then textWidth = width -1 end
  3704.       if part == 0 or part == 1 then
  3705.         this.canScrollDown = printText(x+1, y+1, textWidth, textHeight, colors.black, colors.blue, this.text, this.scroll)
  3706.      
  3707.         local scrolls = -1
  3708.         if this.scroll > 0 and this.canScrollDown then scrolls = 0
  3709.         elseif this.canScrollDown then scrolls = 2
  3710.         elseif this.scroll > 0 then scrolls = 1 end
  3711.      
  3712.         if this.drawScrolls and scrolls >= 0 then
  3713.           drawScrolls(x + width - 2, y +1, textHeight -1, colors.blue, scrolls)
  3714.         end
  3715.         drawButtons(x, y + height, width, colors.blue, buttons, this.activeButton)
  3716.       end
  3717.      
  3718.       if part == 0 or part == 2 then  
  3719.         term.setCursorBlink(true)
  3720.         drawTextBox(x+1,y+textHeight+3, width-2, this.input)
  3721.       end
  3722.     end
  3723.    
  3724.     this.update = function(e, p1, p2, p3, p4, p5)
  3725.       if e == "key" then
  3726.         if p1 == keyDown and this.canScrollDown then
  3727.            this.scroll = this.scroll + 1
  3728.            return false
  3729.         elseif p1 == keyUp and this.scroll > 0 then
  3730.            this.scroll = this.scroll - 1
  3731.            return false
  3732.         elseif p1 == 14 then
  3733.           this.input = this.input:sub(1, #(this.input) - 1)
  3734.           return true
  3735.         end    
  3736.       elseif e == "char" then
  3737.         this.input = this.input..p1
  3738.         return true    
  3739.       end
  3740.     end
  3741.    
  3742.     this.run = function(clear)
  3743.       local wasKey = true
  3744.       local wasChar = true
  3745.       if os.call then
  3746.         wasKey = os.isEventListener("key")
  3747.         wasChar = os.isEventListener("char")
  3748.         if not wasKey then os.addEventListener("key") end
  3749.         if not wasChar then os.addEventListener("char") end
  3750.       end
  3751.       this.render(clear)
  3752.       local n = 0
  3753.       while true do
  3754.         local e, p1 = os.pullEvent()
  3755.         if e == "key" and p1 == keyEnter then break end
  3756.         if this.update(e, p1) and n < 5 then
  3757.           this.render(false, 2)
  3758.         elseif n < 5 then
  3759.           this.render(false, 1)
  3760.         else
  3761.           this.render(false)
  3762.           n = 0
  3763.         end
  3764.         n = n +1
  3765.       end
  3766.       if not wasKey then os.removeEventListener("key") end
  3767.       if not wasCHar then os.removeEventListener("char") end
  3768.       return this.activeButton, this.input
  3769.     end
  3770.   else
  3771.     return nil -- bad type
  3772.   end
  3773.   return this  
  3774. end
  3775.   ]]}
  3776. }},
  3777. {["type"]="directory", ["name"]="drv", contents={
  3778.  
  3779. }},
  3780. {["type"]="directory", ["name"]="home", contents={
  3781.  
  3782. }},
  3783. {["type"]="directory", ["name"]="etc", contents={
  3784.  
  3785. }},
  3786. {["type"]="directory", ["name"]="tmp", contents={
  3787.  
  3788. }},
  3789. {["type"]="directory", ["name"]="usr", contents={
  3790.  
  3791. }},
  3792. {["type"]="directory", ["name"]="var", contents={
  3793.  
  3794. }},
  3795. {["type"]="file", ["name"]="boot", contents=[[
  3796. -- netOS bootloader --
  3797.  
  3798. -- user set variables
  3799. local osPath = "]]..installationDir..[["
  3800.  
  3801. -- helper variables
  3802.  
  3803. local libPath = "/"..fs.combine(osPath, "lib")
  3804. local loadedLibraries = {}
  3805.  
  3806. -- nice welcome string
  3807.  
  3808. print("netOS bootloader")
  3809. print("system is being loaded...")
  3810. sleep(1)
  3811.  
  3812. -- extends to existing APIs
  3813.  
  3814. function os.getPath()
  3815.   return osPath
  3816. end
  3817.  
  3818. function fs.create(path)
  3819.   file = io.open(path, "a")
  3820.   if file then file:close() return true end
  3821.   return false
  3822. end
  3823.  
  3824. function term.printWarning(msg)
  3825.   term.setTextColor(colors.yellow)
  3826.   write("[WARNING] ")
  3827.   term.setTextColor(colors.white)
  3828.   print(msg)
  3829. end
  3830.  
  3831. function term.printError(msg)
  3832.   term.setTextColor(colors.red)
  3833.   write("[ERROR] ")
  3834.   term.setTextColor(colors.white)
  3835.   print(msg)
  3836. end
  3837.  
  3838. function term.printInfo(msg)
  3839.   term.setTextColor(colors.white)
  3840.   write("[INFO] ")
  3841.   term.setTextColor(colors.white)
  3842.   print(msg)
  3843. end
  3844.  
  3845. function term.printOK(msg)
  3846.   term.setTextColor(colors.lime)
  3847.   write("[OK] ")
  3848.   term.setTextColor(colors.white)
  3849.   print(msg)
  3850. end
  3851.  
  3852. function term.clear(color)
  3853.   color = color or colors.black
  3854.   term.setBackgroundColor(color)
  3855.   local curX, curY = term.getCursorPos()
  3856.   local width, height = term.getSize()
  3857.   for x = 0, width, 1 do
  3858.     for y = 0, height, 1 do
  3859.       term.setCursorPos(x, y)
  3860.       write(" ")
  3861.     end
  3862.   end
  3863.   term.setCursorPos(curX, curY)
  3864. end
  3865.  
  3866. _G["term"].print = print
  3867. _G["term"].read = read
  3868.  
  3869. function os.getLibraries()
  3870.   return loadedLibraries
  3871. end
  3872.  
  3873. function table.merge(t1, t2)
  3874.   for k, v in pairs(t2) do
  3875.     if (type(v) == "table") and (type(t1[k] or false) == "table") then
  3876.       table.merge(t1[k], t2[k])
  3877.     else
  3878.       t1[k] = v
  3879.     end
  3880.   end
  3881.   return t1
  3882. end
  3883.  
  3884. os.unloadAPI("textutils") -- need rewrite and reload with new environment
  3885. os.unloadAPI("bit") -- can't work with big numbers
  3886. -- loading static APIs
  3887.  
  3888. os.loadAPI(libPath.."/header")
  3889. os.loadAPI(libPath.."/config")
  3890. os.loadAPI(libPath.."/store")
  3891.  
  3892. --load user APIs
  3893.  
  3894. local libList = fs.list(libPath)
  3895.  
  3896. for n, file in pairs(libList) do
  3897.   filePath = "/"..fs.combine(libPath, file)
  3898.   local fileHeader = header.read(filePath, "api")
  3899.   if fileHeader["NAM"] and fileHeader["VER"] then
  3900.     if os.loadAPI(filePath) then
  3901.       os.unloadAPI(filePath)
  3902.       loadedLibraries[file] = filePath
  3903.       term.printOK("static library: "..fileHeader["NAM"].." "..fileHeader["VER"].." loaded");
  3904.     else
  3905.       term.printWarning("static library: "..fileHeader["NAM"].." "..fileHeader["VER"].."errored while loading");
  3906.     end
  3907.   end
  3908. end
  3909.  
  3910. -- starting OS up
  3911.  
  3912. pcall(shell.run( "/"..fs.combine(osPath, "bin/netOS") ))
  3913. print("netOS crashed, environment can't be restored. Please use CTRL+R to reboot your computer.")
  3914. print("If you think that it is netOS bug, please send report here:")
  3915. print("http://netos.robotronika.pl")
  3916.  
  3917. while true do os.pullEventRaw() end -- hang
  3918. ]]}
  3919. }
  3920.  
  3921. end
  3922.  
  3923. -- program part 2
  3924.  
  3925. local function waitForKey(timeout)
  3926.   timeout = timeout or 10
  3927.   print("Press any key to continue or wait "..timeout.." seconds")
  3928.   term.setCursorBlink(false)
  3929.   os.startTimer(timeout)
  3930.   os.pullEvent()
  3931. end
  3932.  
  3933. printInfo("Copying files")
  3934.  
  3935. local ok = pcall(dotable, files, extract, installationDir)
  3936.  
  3937. if ok then
  3938.   printOK("Coping files complete")
  3939.   printInfo("rebooting your computer, installation will be continued")
  3940.   waitForKey()
  3941.   os.reboot()
  3942. else
  3943.   printError("Coping files failed, file system can be corrupted")
  3944.   waitForKey()
  3945.   term.clear()
  3946.   term.setCursorPos(1,1)
  3947.   error()
  3948. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement