|
- local buffer = {}
- local metatable = {
- __index = buffer,
- __metatable = "file",
- __close = buffer.close
- }
-
- function buffer.new(mode, stream)
- local result = {
- closed = false,
- tty = false,
- mode = {},
- stream = stream,
- bufferRead = "",
- bufferWrite = "",
- bufferSize = math.max(512, math.min(8 * 1024, computer.freeMemory() / 8)),
- bufferMode = "full",
- readTimeout = math.huge,
- }
- mode = mode or "r"
- for i = 1, mode:len() do
- result.mode[mode:sub(i, i)] = true
- end
- -- when stream closes, result should close first
- -- when result closes, stream should close after
- -- when stream closes, it is removed from the proc
- stream.close = setmetatable({close = stream.close,parent = result},{__call = buffer.close})
- return setmetatable(result, metatable)
- end
-
- function buffer:close()
- -- self is either the buffer, or the stream.close callable
- local meta = getmetatable(self)
- if meta == metatable.__metatable then
- return self.stream:close()
- end
- local parent = self.parent
-
- if parent.mode.w or parent.mode.a then
- parent:flush()
- end
- parent.closed = true
- return self.close(parent.stream)
- end
-
- function buffer:flush()
- if #self.bufferWrite > 0 then
- local tmp = self.bufferWrite
- self.bufferWrite = ""
- local result, reason = self.stream:write(tmp)
- if not result then
- return nil, reason or "bad file descriptor"
- end
- end
-
- return self
- end
-
- function buffer:lines(...)
- local args = table.pack(...)
- return function()
- local result = table.pack(self:read(table.unpack(args, 1, args.n)))
- if not result[1] and result[2] then
- error(result[2])
- end
- return table.unpack(result, 1, result.n)
- end
- end
-
- local function readChunk(self)
- if computer.uptime() > self.timeout then
- error("timeout")
- end
- local result, reason = self.stream:read(math.max(1,self.bufferSize))
- if result then
- self.bufferRead = self.bufferRead .. result
- return self
- else -- error or eof
- return result, reason
- end
- end
-
- function buffer:readLine(chop, timeout)
- self.timeout = timeout or (computer.uptime() + self.readTimeout)
- local start = 1
- while true do
- local buf = self.bufferRead
- local i = buf:find("[\r\n]", start)
- local c = i and buf:sub(i,i)
- local is_cr = c == "\r"
- if i and (not is_cr or i < #buf) then
- local n = buf:sub(i+1,i+1)
- if is_cr and n == "\n" then
- c = c .. n
- end
- local result = buf:sub(1, i - 1) .. (chop and "" or c)
- self.bufferRead = buf:sub(i + #c)
- return result
- else
- start = #self.bufferRead - (is_cr and 1 or 0)
- local result, reason = readChunk(self)
- if not result then
- if reason then
- return result, reason
- else -- eof
- result = #self.bufferRead > 0 and self.bufferRead or nil
- self.bufferRead = ""
- return result
- end
- end
- end
- coroutine.yield()
- end
- end
-
- function buffer:read(...)
- if not self.mode.r then
- return nil, "read mode was not enabled for this stream"
- end
-
- if self.mode.w or self.mode.a then
- self:flush()
- end
-
- if select("#", ...) == 0 then
- return self:readLine(true)
- end
- return self:formatted_read(readChunk, ...)
- end
-
- function buffer:setvbuf(mode, size)
- mode = mode or self.bufferMode
- size = size or self.bufferSize
-
- assert(mode == "no" or mode == "full" or mode == "line",
- "bad argument #1 (no, full or line expected, got " .. tostring(mode) .. ")")
- assert(mode == "no" or type(size) == "number",
- "bad argument #2 (number expected, got " .. type(size) .. ")")
-
- self.bufferMode = mode
- self.bufferSize = size
-
- return self.bufferMode, self.bufferSize
- end
-
- function buffer:write(...)
- if self.closed then
- return nil, "bad file descriptor"
- end
- if not self.mode.w and not self.mode.a then
- return nil, "write mode was not enabled for this stream"
- end
- local args = table.pack(...)
- for i = 1, args.n do
- if type(args[i]) == "number" then
- args[i] = tostring(args[i])
- end
- checkArg(i, args[i], "string")
- end
-
- for i = 1, args.n do
- local arg = args[i]
- local result, reason
- result, reason = self.stream:write(arg)
- if not result then
- return nil, reason
- end
- end
-
- return self
- end
|