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.

430 lines
10.0KB

  1. -- shamelessly stolen from plan9k
  2. buffer = {}
  3. function buffer.new(mode, 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 nil, "bad file descriptor"
  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. --kernel.io.println("buffer read: "..tostring(buffer))
  97. return buffer
  98. end
  99. local function readNumber()
  100. local len, sub
  101. if self.mode.b then
  102. len = rawlen
  103. sub = string.sub
  104. else
  105. len = unicode.len
  106. sub = unicode.sub
  107. end
  108. local buffer = ""
  109. local first = true
  110. local decimal = false
  111. local last = false
  112. local hex = false
  113. local pat = "^[0-9]+"
  114. local minbuf = 3 -- "+0x" (sign + hexadecimal tag)
  115. -- this function is used to read trailing numbers (1e2, 0x1p2, etc)
  116. local function readnum(checksign)
  117. local _buffer = ""
  118. local sign = ""
  119. while true do
  120. if len(self.bufferRead) == 0 then
  121. local result, reason = readChunk()
  122. if not result then
  123. if reason then
  124. return nil, reason
  125. else -- eof
  126. return #_buffer > 0 and (sign .. _buffer) or nil
  127. end
  128. end
  129. end
  130. if checksign then
  131. local _sign = sub(self.bufferRead, 1, 1)
  132. if _sign == "+" or _sign == "-" then
  133. -- "eat" the sign (Rio Lua behaviour)
  134. sign = sub(self.bufferRead, 1, 1)
  135. self.bufferRead = sub(self.bufferRead, 2)
  136. end
  137. checksign = false
  138. else
  139. local x,y = string.find(self.bufferRead, pat)
  140. if not x then
  141. break
  142. else
  143. _buffer = _buffer .. sub(self.bufferRead, 1, y)
  144. self.bufferRead = sub(self.bufferRead, y + 1)
  145. end
  146. end
  147. end
  148. return #_buffer > 0 and (sign .. _buffer) or nil
  149. end
  150. while true do
  151. if len(self.bufferRead) == 0 or len(self.bufferRead) < minbuf then
  152. local result, reason = readChunk()
  153. if not result then
  154. if reason then
  155. return nil, reason
  156. else -- eof
  157. return #buffer > 0 and tonumber(buffer) or nil
  158. end
  159. end
  160. end
  161. -- these ifs are here so we run the buffer check above
  162. if first then
  163. local sign = sub(self.bufferRead, 1, 1)
  164. if sign == "+" or sign == "-" then
  165. -- "eat" the sign (Rio Lua behaviour)
  166. buffer = buffer .. sub(self.bufferRead, 1, 1)
  167. self.bufferRead = sub(self.bufferRead, 2)
  168. end
  169. local hextag = sub(self.bufferRead, 1, 2)
  170. if hextag == "0x" or hextag == "0X" then
  171. pat = "^[0-9A-Fa-f]+"
  172. -- "eat" the 0x, see https://gist.github.com/SoniEx2/570a363d81b743353151
  173. buffer = buffer .. sub(self.bufferRead, 1, 2)
  174. self.bufferRead = sub(self.bufferRead, 3)
  175. hex = true
  176. end
  177. minbuf = 0
  178. first = false
  179. elseif decimal then
  180. local sep = sub(self.bufferRead, 1, 1)
  181. if sep == "." then
  182. buffer = buffer .. sep
  183. self.bufferRead = sub(self.bufferRead, 2)
  184. local temp = readnum(false) -- no sign
  185. if temp then
  186. buffer = buffer .. temp
  187. end
  188. end
  189. if not tonumber(buffer) then break end
  190. decimal = false
  191. last = true
  192. minbuf = 1
  193. elseif last then
  194. local tag = sub(self.bufferRead, 1, 1)
  195. if hex and (tag == "p" or tag == "P") then
  196. local temp = sub(self.bufferRead, 1, 1)
  197. self.bufferRead = sub(self.bufferRead, 2)
  198. local temp2 = readnum(true) -- this eats the next sign if any
  199. if temp2 then
  200. buffer = buffer .. temp .. temp2
  201. end
  202. elseif tag == "e" or tag == "E" then
  203. local temp = sub(self.bufferRead, 1, 1)
  204. self.bufferRead = sub(self.bufferRead, 2)
  205. local temp2 = readnum(true) -- this eats the next sign if any
  206. if temp2 then
  207. buffer = buffer .. temp .. temp2
  208. end
  209. end
  210. break
  211. else
  212. local x,y = string.find(self.bufferRead, pat)
  213. if not x then
  214. minbuf = 1
  215. decimal = true
  216. else
  217. buffer = buffer .. sub(self.bufferRead, 1, y)
  218. self.bufferRead = sub(self.bufferRead, y + 1)
  219. end
  220. end
  221. end
  222. return tonumber(buffer)
  223. end
  224. local function readLine(chop)
  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. end
  247. local function readAll()
  248. repeat
  249. local result, reason = readChunk()
  250. if not result and reason then
  251. return nil, reason
  252. end
  253. until not result -- eof
  254. local result = self.bufferRead
  255. self.bufferRead = ""
  256. return result
  257. end
  258. local function read(n, format)
  259. if type(format) == "number" then
  260. return readBytesOrChars(format)
  261. else
  262. if type(format) ~= "string" or unicode.sub(format, 1, 1) ~= "*" then
  263. error("bad argument #" .. n .. " (invalid option)")
  264. end
  265. format = unicode.sub(format, 2, 2)
  266. if format == "n" then
  267. return readNumber()
  268. elseif format == "l" then
  269. return readLine(true)
  270. elseif format == "L" then
  271. return readLine(false)
  272. elseif format == "a" then
  273. return readAll()
  274. else
  275. error("bad argument #" .. n .. " (invalid format)")
  276. end
  277. end
  278. end
  279. if self.mode.w or self.mode.a then
  280. self:flush()
  281. end
  282. local results = {}
  283. local formats = table.pack(...)
  284. if formats.n == 0 then
  285. return readLine(true)
  286. end
  287. for i = 1, formats.n do
  288. local result, reason = read(i, formats[i])
  289. if result then
  290. results[i] = result
  291. elseif reason then
  292. return nil, reason
  293. end
  294. end
  295. return table.unpack(results, 1, formats.n)
  296. end
  297. function buffer:seek(whence, offset)
  298. whence = tostring(whence or "cur")
  299. assert(whence == "set" or whence == "cur" or whence == "end",
  300. "bad argument #1 (set, cur or end expected, got " .. whence .. ")")
  301. offset = offset or 0
  302. checkArg(2, offset, "number")
  303. assert(math.floor(offset) == offset, "bad argument #2 (not an integer)")
  304. if self.mode.w or self.mode.a then
  305. self:flush()
  306. elseif whence == "cur" then
  307. offset = offset - #self.bufferRead
  308. end
  309. local result, reason = self.stream:seek(whence, offset)
  310. if result then
  311. self.bufferRead = ""
  312. return result
  313. else
  314. return nil, reason
  315. end
  316. end
  317. function buffer:setvbuf(mode, size)
  318. mode = mode or self.bufferMode
  319. size = size or self.bufferSize
  320. assert(mode == "no" or mode == "full" or mode == "line",
  321. "bad argument #1 (no, full or line expected, got " .. tostring(mode) .. ")")
  322. assert(mode == "no" or type(size) == "number",
  323. "bad argument #2 (number expected, got " .. type(size) .. ")")
  324. self.bufferMode = mode
  325. self.bufferSize = size
  326. return self.bufferMode, self.bufferSize
  327. end
  328. function buffer:getTimeout()
  329. return self.readTimeout
  330. end
  331. function buffer:setTimeout(value)
  332. self.readTimeout = tonumber(value)
  333. end
  334. function buffer:write(...)
  335. if self.closed then
  336. return nil, "bad file descriptor"
  337. end
  338. local args = table.pack(...)
  339. for i = 1, args.n do
  340. if type(args[i]) == "number" then
  341. args[i] = tostring(args[i])
  342. end
  343. checkArg(i, args[i], "string")
  344. end
  345. for i = 1, args.n do
  346. local arg = args[i]
  347. local result, reason
  348. if self.bufferMode == "full" then
  349. if self.bufferSize - #self.bufferWrite < #arg then
  350. result, reason = self:flush()
  351. if not result then
  352. return nil, reason
  353. end
  354. end
  355. if #arg > self.bufferSize then
  356. result, reason = self.stream:write(arg)
  357. else
  358. self.bufferWrite = self.bufferWrite .. arg
  359. result = self
  360. end
  361. elseif self.bufferMode == "line" then
  362. local l
  363. repeat
  364. local idx = arg:find("\n", (l or 0) + 1, true)
  365. if idx then
  366. l = idx
  367. end
  368. until not idx
  369. if l or #arg > self.bufferSize then
  370. result, reason = self:flush()
  371. if not result then
  372. return nil, reason
  373. end
  374. end
  375. if l then
  376. result, reason = self.stream:write(arg:sub(1, l))
  377. if not result then
  378. return nil, reason
  379. end
  380. arg = arg:sub(l + 1)
  381. end
  382. if #arg > self.bufferSize then
  383. result, reason = self.stream:write(arg)
  384. else
  385. self.bufferWrite = self.bufferWrite .. arg
  386. result = self
  387. end
  388. else -- self.bufferMode == "no"
  389. result, reason = self.stream:write(arg)
  390. end
  391. if not result then
  392. return nil, reason
  393. end
  394. end
  395. return self
  396. end