Operating system for OpenComputers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

433 lines
9.8KB

  1. tTasks,nPid,nTimeout,cPid = {},1,1,0
  2. function os.spawn(f,n)
  3. tTasks[nPid] = {["c"]=coroutine.create(f),["n"]=n,["p"]=nPid}
  4. for k,v in pairs(tTasks[cPid] or {}) do
  5. tTasks[nPid][k] = tTasks[nPid][k] or v
  6. end
  7. nPid = nPid + 1
  8. return nPid - 1
  9. end
  10. function sched()
  11. while #tTasks > 0 do
  12. local tEv = {computer.pullSignal(nTimeout)}
  13. for k,v in pairs(tTasks) do
  14. if coroutine.status(v.c) ~= "dead" then
  15. cPid = k
  16. coroutine.resume(v.c,table.unpack(tEv))
  17. else
  18. tTasks[k] = nil
  19. end
  20. end
  21. end
  22. end
  23. function vt100emu(gpu)
  24. local mx, my = gpu.maxResolution()
  25. local cx, cy = 1, 1
  26. local pc = " "
  27. local lc = ""
  28. local mode = "n"
  29. local lw = true
  30. local sx, sy = 1,1
  31. local cs = ""
  32. -- setup
  33. gpu.setResolution(mx,my)
  34. gpu.fill(1,1,mx,my," ")
  35. function termwrite(s)
  36. s=s:gsub("\8","\27[D")
  37. pc = gpu.get(cx,cy)
  38. gpu.setForeground(0xFFFFFF)
  39. gpu.setBackground(0)
  40. gpu.set(cx,cy,pc)
  41. for i = 1, s:len() do
  42. local cc = s:sub(i,i)
  43. if mode == "n" then
  44. if cc == "\n" then -- line feed
  45. cx, cy = 1, cy+1
  46. elseif cc == "\r" then -- cursor home
  47. cx = 1
  48. elseif cc == "\27" then -- escape
  49. mode = "e"
  50. elseif string.byte(cc) > 31 and string.byte(cc) < 127 then -- printable, I guess
  51. gpu.set(cx, cy, cc)
  52. cx = cx + 1
  53. end
  54. elseif mode == "e" then
  55. if cc == "[" then
  56. mode = "v"
  57. cs = ""
  58. elseif cc == "D" then -- scroll down
  59. gpu.copy(1,2,mx,my-1,0,-1)
  60. gpu.fill(1,my,mx,1," ")
  61. cy=cy+1
  62. mode = "n"
  63. elseif cc == "M" then -- scroll up
  64. gpu.copy(1,1,mx,my-1,0,1)
  65. gpu.fill(1,1,mx,1," ")
  66. mode = "n"
  67. end
  68. elseif mode == "v" then -- save cursor
  69. local n = cs:sub(cs:len(),cs:len())
  70. if n == "" then n = "\1" end
  71. if cc == "s" then
  72. sx, sy = cx, cy
  73. mode = "n"
  74. elseif cc == "u" then -- restore cursor
  75. cx, cy = sx, sy
  76. mode = "n"
  77. elseif cc == "H" then -- cursor home or to
  78. local tx, ty = cs:match("(.);(.)")
  79. tx, ty = tx or "\1", ty or "\1"
  80. cx, cy = string.byte(tx), string.byte(ty)
  81. mode = "n"
  82. elseif cc == "A" then -- cursor up
  83. cy = cy - string.byte(n)
  84. mode = "n"
  85. elseif cc == "B" then -- cursor down
  86. cy = cy + string.byte(n)
  87. mode = "n"
  88. elseif cc == "C" then -- cursor right
  89. cx = cx + string.byte(n)
  90. mode = "n"
  91. elseif cc == "D" then -- cursor left
  92. cx = cx - string.byte(n)
  93. mode = "n"
  94. elseif cc == "h" and lc == "7" then -- enable line wrap
  95. lw = true
  96. elseif cc == "l" and lc == "7" then -- disable line wrap
  97. lw = false
  98. end
  99. cs = cs .. cc
  100. end
  101. if cx > mx and lw then
  102. cx, cy = 1, cy+1
  103. end
  104. if cy > my then
  105. gpu.copy(1,2,mx,my-1,0,-1)
  106. gpu.fill(1,my,mx,1," ")
  107. cy=my
  108. end
  109. if cy < 1 then cy = 1 end
  110. if cx < 1 then cx = 1 end
  111. lc = cc
  112. end
  113. pc = gpu.get(cx,cy)
  114. gpu.setForeground(0)
  115. gpu.setBackground(0xFFFFFF)
  116. gpu.set(cx,cy,pc)
  117. gpu.setForeground(0xFFFFFF)
  118. gpu.setBackground(0)
  119. end
  120. return termwrite
  121. end
  122. fs = {}
  123. fs.mounts = {}
  124. -- basics
  125. function fs.segments(path)
  126. local segments = {}
  127. for segment in path:gmatch("[^/]+") do
  128. segments[#segments+1] = segment
  129. end
  130. return segments
  131. end
  132. function fs.resolve(path)
  133. local segments, rpath = fs.segments(path), "/"
  134. for i = 2, #segments do
  135. rpath = rpath .. segments[i] .. "/"
  136. end
  137. rpath = rpath:match("(.+)/") or rpath
  138. return segments[1] or "root",rpath
  139. end
  140. -- generate some simple functions
  141. for k,v in pairs({"makeDirectory","exists","isDirectory","list","lastModified","remove","size","spaceUsed","isReadOnly","getLabel"}) do
  142. fs[v] = function(path)
  143. local fsi,path = fs.resolve(path)
  144. return fs.mounts[fsi][v](path)
  145. end
  146. end
  147. local function fread(self,length)
  148. if length == "*a" then
  149. length = math.huge
  150. end
  151. local rstr, lstr = "", ""
  152. repeat
  153. lstr = fs.mounts[self.fs].read(self.fid,math.min(2^16,length-rstr:len())) or ""
  154. rstr = rstr .. lstr
  155. until rstr:len() == length or lstr == ""
  156. return rstr
  157. end
  158. local function fwrite(self,data)
  159. fs.mounts[self.fs].write(self.fid,data)
  160. end
  161. local function fclose(self)
  162. fs.mounts[self.fs].close(self.fid)
  163. end
  164. function fs.open(path,mode)
  165. mode = mode or "rb"
  166. local fsi,path = fs.resolve(path)
  167. if not fs.mounts[fsi] then return false end
  168. local fid = fs.mounts[fsi].open(path,mode)
  169. if fid then
  170. local fobj = {["fs"]=fsi,["fid"]=fid,["close"]=fclose}
  171. if mode:sub(1,1) == "r" then
  172. fobj.read = fread
  173. else
  174. fobj.write = fwrite
  175. end
  176. return fobj
  177. end
  178. return false
  179. end
  180. function fs.copy(from,to)
  181. local of = fs.open(from,"rb")
  182. local df = fs.open(to,"wb")
  183. if not of or not df then
  184. return false
  185. end
  186. df:write(of:read("*a"))
  187. df:close()
  188. of:close()
  189. end
  190. function fs.rename(from,to)
  191. local ofsi, opath = fs.resolve(from)
  192. local dfsi, dpath = fs.resolve(to)
  193. if ofsi == dfsi then
  194. fs.mounts[ofsi].rename(opath,dpath)
  195. return true
  196. end
  197. fs.copy(from,to)
  198. fs.remove(from)
  199. return true
  200. end
  201. fs.mounts.temp = component.proxy(computer.tmpAddress())
  202. if computer.getBootAddress then
  203. fs.mounts.boot = component.proxy(computer.getBootAddress())
  204. end
  205. for addr, _ in component.list("filesystem") do
  206. fs.mounts[addr:sub(1,3)] = component.proxy(addr)
  207. end
  208. local function rf()
  209. return false
  210. end
  211. fs.mounts.root = {}
  212. for k,v in pairs(fs.mounts.temp) do
  213. fs.mounts.root[k] = rf
  214. end
  215. function fs.mounts.root.list()
  216. local t = {}
  217. for k,v in pairs(fs.mounts) do
  218. t[#t+1] = k
  219. end
  220. t.n = #t
  221. return t
  222. end
  223. function fs.mounts.root.isReadOnly()
  224. return true
  225. end
  226. function loadfile(p)
  227. local f = fs.open(p,"rb")
  228. local c = f:read("*a")
  229. f:close()
  230. return load(c,p,"t")
  231. end
  232. function runfile(p,...)
  233. loadfile(p)(...)
  234. end
  235. function spawnfile(p,n)
  236. os.spawn(loadfile(p),n)
  237. end
  238. function vtemu(gpua,scra)
  239. local gpu,scr = component.proxy(gpua),component.proxy(scra)
  240. gpu.bind(scra)
  241. local write = vt100emu(gpu)
  242. local kba = {}
  243. for k,v in ipairs(scr.getKeyboards()) do
  244. kba[v]=true
  245. end
  246. local buf = ""
  247. os.spawn(function()
  248. while true do
  249. local ty,ka,ch = coroutine.yield()
  250. if ty == "key_down" and kba[ka] then
  251. if ch == 13 then ch = 10 end
  252. if ch == 8 and buf:len() > 0 then
  253. write("\8 \8")
  254. buf = buf:sub(1,-2)
  255. elseif ch > 0 then
  256. write(string.char(ch))
  257. buf = buf .. string.char(ch)
  258. end
  259. end
  260. end
  261. end,"keyboard daemon for "..gpua:sub(1,8)..":"..scra:sub(1,8))
  262. local function read(n)
  263. n = n or "\n"
  264. local rdata = ""
  265. if type(n) == "number" then
  266. rdata = buf:sub(1,n)
  267. return rdata
  268. else
  269. if n == "*a" then
  270. rdata = buf
  271. buf = ""
  272. return rdata
  273. end
  274. local pr,po = buf:match("(.-)"..n.."(.*)")
  275. buf = po or buf
  276. return pr
  277. end
  278. end
  279. return read,write
  280. end
  281. _G.fd,_G.io = {},{}
  282. do
  283. function io.write(d)
  284. fd[tTasks[cPid].t or 1].w(d)
  285. end
  286. function io.read(d,b)
  287. local r = ""
  288. repeat
  289. r=fd[tTasks[cPid].t or 1].r(d)
  290. coroutine.yield()
  291. until r or b
  292. return r
  293. end
  294. function print(...)
  295. for k,v in pairs({...}) do
  296. io.write(tostring(v).."\n")
  297. end
  298. end
  299. local ts = {}
  300. for a,_ in component.list("screen") do
  301. ts[#ts+1] = a
  302. end
  303. for a,_ in component.list("gpu") do
  304. local r,w = vtemu(a,table.remove(ts,1))
  305. fd[#fd+1] = {["r"]=r,["w"]=w,["t"]="t"}
  306. end
  307. end
  308. _G.net={}
  309. do
  310. local modems,packetQueue,packetCache,routeCache,C,Y = {},{},{},{},COMPUTER,UNPACK
  311. net.port,net.hostname,net.route,net.hook,U=4096,computer.address():sub(1,8),true,{},UPTIME
  312. for a in component.list("modem") do
  313. modems[a] = component.proxy(a)
  314. modems[a].open(net.port)
  315. end
  316. local function genPacketID()
  317. local packetID = ""
  318. for i = 1, 16 do
  319. packetID = packetID .. string.char(math.random(32,126))
  320. end
  321. return packetID
  322. end
  323. local function rawSendPacket(packetID,packetType,to,from,vport,data)
  324. if routeCache[to] then
  325. modems[routeCache[to][1]].send(routeCache[to][2],net.port,packetID,packetType,to,from,vport,data)
  326. else
  327. for k,v in pairs(modems) do
  328. v.broadcast(net.port,packetID,packetType,to,from,vport,data)
  329. end
  330. end
  331. end
  332. local function sendPacket(packetID,packetType,to,vport,data)
  333. packetCache[packetID] = computer.uptime()
  334. rawSendPacket(packetID,packetType,to,net.hostname,vport,data)
  335. end
  336. function net.send(to,vport,data,packetType,packetID)
  337. packetType,packetID = packetType or 1, packetID or genPacketID()
  338. packetQueue[packetID] = {packetType,to,vport,data,0}
  339. sendPacket(packetID,packetType,to,vport,data)
  340. end
  341. local function checkCache(packetID)
  342. for k,v in pairs(packetCache) do
  343. if k == packetID then
  344. return false
  345. end
  346. end
  347. return true
  348. end
  349. os.spawn(function()
  350. while true do
  351. local eventTab = {coroutine.yield()}
  352. if eventTab[1] == "modem_message" and (eventTab[4] == net.port or eventTab[4] == 0) and checkCache(eventTab[6]) then
  353. for k,v in pairs(packetCache) do
  354. if computer.uptime() > v+30 then
  355. packetCache[k] = nil
  356. end
  357. end
  358. for k,v in pairs(routeCache) do
  359. if computer.uptime() > v[3]+30 then
  360. routeCache[k] = nil
  361. end
  362. end
  363. routeCache[eventTab[9]] = {eventTab[2],eventTab[3],computer.uptime()}
  364. if eventTab[8] == net.hostname then
  365. if eventTab[7] ~= 2 then
  366. computer.pushSignal("net_msg",eventTab[9],eventTab[10],eventTab[11])
  367. if eventTab[7] == 1 then
  368. sendPacket(genPacketID(),2,eventTab[9],eventTab[10],eventTab[6])
  369. end
  370. else
  371. packetQueue[eventTab[11]] = nil
  372. end
  373. elseif net.route and checkCache(eventTab[6]) then
  374. rawSendPacket(eventTab[6],eventTab[7],eventTab[8],eventTab[9],eventTab[10],eventTab[11])
  375. end
  376. packetCache[eventTab[6]] = computer.uptime()
  377. end
  378. for k,v in pairs(packetQueue) do
  379. if computer.uptime() > v[5] then
  380. sendPacket(k,table.unpack(v))
  381. v[5]=computer.uptime()+30
  382. end
  383. end
  384. end
  385. end,"minitel.3")
  386. end
  387. os.spawn(function() print(pcall(function()
  388. print(_OSVERSION,tostring(computer.totalMemory()/1024).."K memory")
  389. local f = fs.open("/boot/init.txt","rb")
  390. local fc = f:read("*a")
  391. f:close()
  392. for line in fc:gmatch("[^\n]+") do
  393. print(line)
  394. end
  395. for k,v in pairs(fd) do
  396. if v.t == "t" then
  397. tTasks[cPid].t = k
  398. print("Spawning a shell for terminal #"..tostring(k))
  399. spawnfile("/boot/exec/shell.lua","shell #"..tostring(k))
  400. end
  401. end
  402. end)) end,"init")
  403. _OSVERSION="PsychOS 2.0a0"
  404. sched()