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.

516 lines
13KB

  1. -- shamelessly stolen from plan9k
  2. buffer = {}
  3. function buffer.new(mode, stream) -- string table -- table -- create a new buffer in mode *mode* backed by stream object *stream*
  4. local result = {
  5. mode = {},
  6. stream = stream,
  7. bufferRead = "",
  8. bufferWrite = "",
  9. bufferSize = math.max(512, math.min(8 * 1024, computer.freeMemory() / 8)),
  10. bufferMode = "full",
  11. readTimeout = math.huge
  12. }
  13. mode = mode or "r"
  14. for i = 1, unicode.len(mode) do
  15. result.mode[unicode.sub(mode, i, i)] = true
  16. end
  17. local metatable = {
  18. __index = buffer,
  19. __metatable = "file"
  20. }
  21. return setmetatable(result, metatable)
  22. end
  23. local function badFileDescriptor()
  24. return nil, "bad file descriptor"
  25. end
  26. function buffer:close()
  27. if self.mode.w or self.mode.a then
  28. self:flush()
  29. end
  30. self.closed = true
  31. return self.stream:close()
  32. end
  33. function buffer:flush()
  34. local result, reason = self.stream:write(self.bufferWrite)
  35. if result then
  36. self.bufferWrite = ""
  37. else
  38. if reason then
  39. return nil, reason
  40. else
  41. return badFileDescriptor()
  42. end
  43. end
  44. return self
  45. end
  46. function buffer:lines(...)
  47. local args = table.pack(...)
  48. return function()
  49. local result = table.pack(self:read(table.unpack(args, 1, args.n)))
  50. if not result[1] and result[2] then
  51. error(result[2])
  52. end
  53. return table.unpack(result, 1, result.n)
  54. end
  55. end
  56. function buffer:read(...)
  57. local timeout = computer.uptime() + self.readTimeout
  58. local function readChunk()
  59. if computer.uptime() > timeout then
  60. error("timeout")
  61. end
  62. local result, reason = self.stream:read(self.bufferSize)
  63. if result then
  64. self.bufferRead = self.bufferRead .. result
  65. return self
  66. else -- error or eof
  67. return nil, reason
  68. end
  69. end
  70. local function readBytesOrChars(n)
  71. n = math.max(n, 0)
  72. local len, sub
  73. if self.mode.b then
  74. len = rawlen
  75. sub = string.sub
  76. else
  77. len = unicode.len
  78. sub = unicode.sub
  79. end
  80. local buffer = ""
  81. repeat
  82. if len(self.bufferRead) == 0 then
  83. local result, reason = readChunk()
  84. if not result then
  85. if reason then
  86. return nil, reason
  87. else -- eof
  88. return #buffer > 0 and buffer or nil
  89. end
  90. end
  91. end
  92. local left = n - len(buffer)
  93. buffer = buffer .. sub(self.bufferRead, 1, left)
  94. self.bufferRead = sub(self.bufferRead, left + 1)
  95. until len(buffer) == n
  96. return buffer
  97. end
  98. local function readNumber()
  99. local len, sub
  100. if self.mode.b then
  101. len = rawlen
  102. sub = string.sub
  103. else
  104. len = unicode.len
  105. sub = unicode.sub
  106. end
  107. local buffer = ""
  108. local first = true
  109. local decimal = false
  110. local last = false
  111. local hex = false
  112. local pat = "^[0-9]+"
  113. local minbuf = 3 -- "+0x" (sign + hexadecimal tag)
  114. -- this function is used to read trailing numbers (1e2, 0x1p2, etc)
  115. local function readnum(checksign)
  116. local _buffer = ""
  117. local sign = ""
  118. while true do
  119. if len(self.bufferRead) == 0 then
  120. local result, reason = readChunk()
  121. if not result then
  122. if reason then
  123. return nil, reason
  124. else -- eof
  125. return #_buffer > 0 and (sign .. _buffer) or nil
  126. end
  127. end
  128. end
  129. if checksign then
  130. local _sign = sub(self.bufferRead, 1, 1)
  131. if _sign == "+" or _sign == "-" then
  132. -- "eat" the sign (Rio Lua behaviour)
  133. sign = sub(self.bufferRead, 1, 1)
  134. self.bufferRead = sub(self.bufferRead, 2)
  135. end
  136. checksign = false
  137. else
  138. local x,y = string.find(self.bufferRead, pat)
  139. if not x then
  140. break
  141. else
  142. _buffer = _buffer .. sub(self.bufferRead, 1, y)
  143. self.bufferRead = sub(self.bufferRead, y + 1)
  144. end
  145. end
  146. end
  147. return #_buffer > 0 and (sign .. _buffer) or nil
  148. end
  149. while true do
  150. if len(self.bufferRead) == 0 or len(self.bufferRead) < minbuf then
  151. local result, reason = readChunk()
  152. if not result then
  153. if reason then
  154. return nil, reason
  155. else -- eof
  156. return #buffer > 0 and tonumber(buffer) or nil
  157. end
  158. end
  159. end
  160. -- these ifs are here so we run the buffer check above
  161. if first then
  162. local sign = sub(self.bufferRead, 1, 1)
  163. if sign == "+" or sign == "-" then
  164. -- "eat" the sign (Rio Lua behaviour)
  165. buffer = buffer .. sub(self.bufferRead, 1, 1)
  166. self.bufferRead = sub(self.bufferRead, 2)
  167. end
  168. local hextag = sub(self.bufferRead, 1, 2)
  169. if hextag == "0x" or hextag == "0X" then
  170. pat = "^[0-9A-Fa-f]+"
  171. -- "eat" the 0x, see https://gist.github.com/SoniEx2/570a363d81b743353151
  172. buffer = buffer .. sub(self.bufferRead, 1, 2)
  173. self.bufferRead = sub(self.bufferRead, 3)
  174. hex = true
  175. end
  176. minbuf = 0
  177. first = false
  178. elseif decimal then
  179. local sep = sub(self.bufferRead, 1, 1)
  180. if sep == "." then
  181. buffer = buffer .. sep
  182. self.bufferRead = sub(self.bufferRead, 2)
  183. local temp = readnum(false) -- no sign
  184. if temp then
  185. buffer = buffer .. temp
  186. end
  187. end
  188. if not tonumber(buffer) then break end
  189. decimal = false
  190. last = true
  191. minbuf = 1
  192. elseif last then
  193. local tag = sub(self.bufferRead, 1, 1)
  194. if hex and (tag == "p" or tag == "P") then
  195. local temp = sub(self.bufferRead, 1, 1)
  196. self.bufferRead = sub(self.bufferRead, 2)
  197. local temp2 = readnum(true) -- this eats the next sign if any
  198. if temp2 then
  199. buffer = buffer .. temp .. temp2
  200. end
  201. elseif tag == "e" or tag == "E" then
  202. local temp = sub(self.bufferRead, 1, 1)
  203. self.bufferRead = sub(self.bufferRead, 2)
  204. local temp2 = readnum(true) -- this eats the next sign if any
  205. if temp2 then
  206. buffer = buffer .. temp .. temp2
  207. end
  208. end
  209. break
  210. else
  211. local x,y = string.find(self.bufferRead, pat)
  212. if not x then
  213. minbuf = 1
  214. decimal = true
  215. else
  216. buffer = buffer .. sub(self.bufferRead, 1, y)
  217. self.bufferRead = sub(self.bufferRead, y + 1)
  218. end
  219. end
  220. end
  221. return tonumber(buffer)
  222. end
  223. local function readLine(chop)
  224. if not self.mode.t then
  225. local start = 1
  226. while true do
  227. local l = self.bufferRead:find("\n", start, true)
  228. if l then
  229. local result = self.bufferRead:sub(1, l + (chop and -1 or 0))
  230. self.bufferRead = self.bufferRead:sub(l + 1)
  231. return result
  232. else
  233. start = #self.bufferRead
  234. local result, reason = readChunk()
  235. if not result then
  236. if reason then
  237. return nil, reason
  238. else -- eof
  239. local result = #self.bufferRead > 0 and self.bufferRead or nil
  240. self.bufferRead = ""
  241. return result
  242. end
  243. end
  244. end
  245. end
  246. else
  247. -- this whole thing is a house of cards. good luck.
  248. io.write("\27[s\27[8m\27[6n")
  249. if not (self.mx or self.my) then
  250. io.write("\27[9999;9999H\27[6n\27[u")
  251. end
  252. local pos, buffer, hIndex, sx, sy = 1, "", 0
  253. self.history = self.history or {}
  254. local function redraw()
  255. -- scroll until the buffer will fit on the screen
  256. while sx and sy and self.mx and self.my and #buffer > (self.mx * ((self.my - sy) + 1)) - sx do
  257. sy = sy - 1
  258. io.write("\27[9999;9999H ")
  259. io.write(string.format("\27[2K\27[%i;%iH\27[s", sx, sy))
  260. end
  261. io.write(string.format("\27[u%s \27[u\27[%iC",buffer,(#buffer-pos)+1))
  262. end
  263. while true do
  264. char = readBytesOrChars(1)
  265. if char == "\27" then
  266. if readBytesOrChars(1) == "[" then
  267. local args = {""}
  268. repeat
  269. char = readBytesOrChars(1)
  270. if char:match("%d") then
  271. args[#args] = args[#args]..char
  272. else
  273. args[#args] = tonumber(args[#args])
  274. args[#args+1] = ""
  275. end
  276. until not char:match("[%d;]")
  277. if char == "C" then -- right
  278. if pos > 1 then
  279. pos = pos - 1
  280. end
  281. elseif char == "D" then -- left
  282. if pos <= #buffer then
  283. pos = pos + 1
  284. end
  285. elseif char == "A" then -- up
  286. hIndex = hIndex + 1
  287. io.write("\27[u"..(" "):rep(buffer:len()+1))
  288. buffer = self.history[1+#self.history-hIndex] or buffer
  289. pos = 1
  290. elseif char == "B" then -- down
  291. hIndex = hIndex - 1
  292. io.write("\27[u"..(" "):rep(buffer:len()+1))
  293. if hIndex == 0 then
  294. hIndex = hIndex - 1
  295. buffer = ""
  296. end
  297. buffer = self.history[1+#self.history-hIndex] or buffer
  298. pos = 1
  299. elseif char == "R" then -- cursor position report
  300. self.mx, self.my = sx and math.max(self.mx or 0, args[1]) or self.mx, sy and math.max(self.my or 0, args[2]) or self.my
  301. sx, sy = sx or args[1], sy or args[2]
  302. end
  303. hIndex = math.max(math.min(hIndex,#self.history),0)
  304. end
  305. elseif char == "\8" then -- backspace
  306. if #buffer > 0 and pos <= #buffer then
  307. buffer = buffer:sub(1, (#buffer - pos)) .. buffer:sub((#buffer - pos) + 2)
  308. end
  309. elseif char == "\3" then -- ^C, error
  310. error("terminated")
  311. elseif char == "\1" then -- ^A, go to start of line
  312. pos = buffer:len()+1
  313. elseif char == "\5" then -- ^E, go to end of line
  314. pos = 1
  315. elseif char == "\2" then -- ^B, back one word
  316. local nc = buffer:reverse():find(" ",pos+1)
  317. pos = nc or #buffer+1
  318. elseif char == "\6" then -- ^F, forward one word
  319. local nc = buffer:find(" ",math.max(#buffer-pos+3,0))
  320. pos = (nc and #buffer-nc+2) or 1
  321. elseif char == "\13" or char == "\10" or char == "\n" then -- return / newline
  322. io.write("\n")
  323. self.history[#self.history+1] = buffer ~= "" and buffer ~= self.history[#self.history] and buffer or nil
  324. if #self.history > (self.maxhistory or 16) then table.remove(self.history,1) end
  325. if chop then buffer = buffer .. "\n" end
  326. return buffer
  327. else
  328. buffer = buffer:sub(1, (#buffer - pos) + 1) .. char .. buffer:sub((#buffer - pos) + 2)
  329. end
  330. redraw()
  331. end
  332. end
  333. end
  334. local function readAll()
  335. repeat
  336. local result, reason = readChunk()
  337. if not result and reason then
  338. return nil, reason
  339. end
  340. until not result -- eof
  341. local result = self.bufferRead
  342. self.bufferRead = ""
  343. return result
  344. end
  345. local function read(n, format)
  346. if type(format) == "number" then
  347. return readBytesOrChars(format)
  348. else
  349. if type(format) ~= "string" or unicode.sub(format, 1, 1) ~= "*" then
  350. error("bad argument #" .. n .. " (invalid option)")
  351. end
  352. format = unicode.sub(format, 2, 2)
  353. if format == "n" then
  354. return readNumber()
  355. elseif format == "l" then
  356. return readLine(true)
  357. elseif format == "L" then
  358. return readLine(false)
  359. elseif format == "a" then
  360. return readAll()
  361. else
  362. error("bad argument #" .. n .. " (invalid format)")
  363. end
  364. end
  365. end
  366. if self.mode.w or self.mode.a then
  367. self:flush()
  368. end
  369. local results = {}
  370. local formats = table.pack(...)
  371. if formats.n == 0 then
  372. return readLine(true)
  373. end
  374. for i = 1, formats.n do
  375. local result, reason = read(i, formats[i])
  376. if result then
  377. results[i] = result
  378. elseif reason then
  379. return nil, reason
  380. end
  381. end
  382. return table.unpack(results, 1, formats.n)
  383. end
  384. function buffer:seek(whence, offset)
  385. whence = tostring(whence or "cur")
  386. assert(whence == "set" or whence == "cur" or whence == "end",
  387. "bad argument #1 (set, cur or end expected, got " .. whence .. ")")
  388. offset = offset or 0
  389. checkArg(2, offset, "number")
  390. assert(math.floor(offset) == offset, "bad argument #2 (not an integer)")
  391. if self.mode.w or self.mode.a then
  392. self:flush()
  393. elseif whence == "cur" then
  394. offset = offset - #self.bufferRead
  395. end
  396. local result, reason = self.stream:seek(whence, offset)
  397. if result then
  398. self.bufferRead = ""
  399. return result
  400. else
  401. return nil, reason
  402. end
  403. end
  404. function buffer:setvbuf(mode, size)
  405. mode = mode or self.bufferMode
  406. size = size or self.bufferSize
  407. assert(mode == "no" or mode == "full" or mode == "line",
  408. "bad argument #1 (no, full or line expected, got " .. tostring(mode) .. ")")
  409. assert(mode == "no" or type(size) == "number",
  410. "bad argument #2 (number expected, got " .. type(size) .. ")")
  411. self.bufferMode = mode
  412. self.bufferSize = size
  413. return self.bufferMode, self.bufferSize
  414. end
  415. function buffer:getTimeout()
  416. return self.readTimeout
  417. end
  418. function buffer:setTimeout(value)
  419. self.readTimeout = tonumber(value)
  420. end
  421. function buffer:write(...)
  422. if self.closed then
  423. return badFileDescriptor()
  424. end
  425. local args = table.pack(...)
  426. for i = 1, args.n do
  427. if type(args[i]) == "number" then
  428. args[i] = tostring(args[i])
  429. end
  430. checkArg(i, args[i], "string")
  431. end
  432. for i = 1, args.n do
  433. local arg = args[i]
  434. local result, reason
  435. if self.bufferMode == "full" then
  436. if self.bufferSize - #self.bufferWrite < #arg then
  437. result, reason = self:flush()
  438. if not result then
  439. return nil, reason
  440. end
  441. end
  442. if #arg > self.bufferSize then
  443. result, reason = self.stream:write(arg)
  444. else
  445. self.bufferWrite = self.bufferWrite .. arg
  446. result = self
  447. end
  448. elseif self.bufferMode == "line" then
  449. local l
  450. repeat
  451. local idx = arg:find("\n", (l or 0) + 1, true)
  452. if idx then
  453. l = idx
  454. end
  455. until not idx
  456. if l or #arg > self.bufferSize then
  457. result, reason = self:flush()
  458. if not result then
  459. return nil, reason
  460. end
  461. end
  462. if l then
  463. result, reason = self.stream:write(arg:sub(1, l))
  464. if not result then
  465. return nil, reason
  466. end
  467. arg = arg:sub(l + 1)
  468. end
  469. if #arg > self.bufferSize then
  470. result, reason = self.stream:write(arg)
  471. else
  472. self.bufferWrite = self.bufferWrite .. arg
  473. result = self
  474. end
  475. else -- self.bufferMode == "no"
  476. result, reason = self.stream:write(arg)
  477. end
  478. if not result then
  479. return nil, reason
  480. end
  481. end
  482. return self
  483. end