summaryrefslogtreecommitdiff
path: root/macros/luatex/generic/lualibs/lualibs-io.lua
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/luatex/generic/lualibs/lualibs-io.lua
Initial commit
Diffstat (limited to 'macros/luatex/generic/lualibs/lualibs-io.lua')
-rw-r--r--macros/luatex/generic/lualibs/lualibs-io.lua473
1 files changed, 473 insertions, 0 deletions
diff --git a/macros/luatex/generic/lualibs/lualibs-io.lua b/macros/luatex/generic/lualibs/lualibs-io.lua
new file mode 100644
index 0000000000..fa3ad3c4cd
--- /dev/null
+++ b/macros/luatex/generic/lualibs/lualibs-io.lua
@@ -0,0 +1,473 @@
+if not modules then modules = { } end modules ['l-io'] = {
+ version = 1.001,
+ comment = "companion to luat-lib.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+local io = io
+local open, flush, write, read = io.open, io.flush, io.write, io.read
+local byte, find, gsub, format = string.byte, string.find, string.gsub, string.format
+local concat = table.concat
+----- floor = math.floor
+local type = type
+
+if string.find(os.getenv("PATH"),";",1,true) then
+ io.fileseparator, io.pathseparator = "\\", ";"
+else
+ io.fileseparator, io.pathseparator = "/" , ":"
+end
+
+-- local function readall(f)
+-- return f:read("*all")
+-- end
+
+-- The next one is upto 50% faster on large files and less memory consumption due
+-- to less intermediate large allocations. This phenomena was discussed on the
+-- luatex dev list.
+
+local large = 0x01000000 -- 2^24 16.777.216
+local medium = 0x00100000 -- 2^20 1.048.576
+local small = 0x00020000 -- 2^17 131.072
+
+-- local function readall(f)
+-- local size = f:seek("end")
+-- if size == 0 then
+-- return ""
+-- end
+-- f:seek("set",0)
+-- if size < medium then
+-- return f:read('*all')
+-- else
+-- local step = (size > large) and large or (floor(size/(medium)) * small)
+-- local data = { }
+-- while true do
+-- local r = f:read(step)
+-- if not r then
+-- return concat(data)
+-- else
+-- data[#data+1] = r
+-- end
+-- end
+-- end
+-- end
+
+local function readall(f)
+ -- return f:read("*all")
+ local size = f:seek("end")
+ if size > 0 then
+ f:seek("set",0)
+ return f:read(size)
+ else
+ return ""
+ end
+end
+
+io.readall = readall
+
+function io.loaddata(filename,textmode) -- return nil if empty
+ local f = open(filename,(textmode and 'r') or 'rb')
+ if f then
+ local size = f:seek("end")
+ local data = nil
+ if size > 0 then
+ -- data = f:read("*all")
+ f:seek("set",0)
+ data = f:read(size)
+ end
+ f:close()
+ return data
+ end
+end
+
+-- function io.copydata(source,target,action)
+-- local f = open(source,"rb")
+-- if f then
+-- local g = open(target,"wb")
+-- if g then
+-- local size = f:seek("end")
+-- if size == 0 then
+-- -- empty
+-- else
+-- f:seek("set",0)
+-- if size < medium then
+-- local data = f:read('*all')
+-- if action then
+-- data = action(data)
+-- end
+-- if data then
+-- g:write(data)
+-- end
+-- else
+-- local step = (size > large) and large or (floor(size/(medium)) * small)
+-- while true do
+-- local data = f:read(step)
+-- if data then
+-- if action then
+-- data = action(data)
+-- end
+-- if data then
+-- g:write(data)
+-- end
+-- else
+-- break
+-- end
+-- end
+-- end
+-- end
+-- g:close()
+-- end
+-- f:close()
+-- flush()
+-- end
+-- end
+
+function io.copydata(source,target,action)
+ local f = open(source,"rb")
+ if f then
+ local g = open(target,"wb")
+ if g then
+ local size = f:seek("end")
+ if size > 0 then
+ -- local data = f:read('*all')
+ f:seek("set",0)
+ local data = f:read(size)
+ if action then
+ data = action(data)
+ end
+ if data then
+ g:write(data)
+ end
+ end
+ g:close()
+ end
+ f:close()
+ flush()
+ end
+end
+
+function io.savedata(filename,data,joiner)
+ local f = open(filename,"wb")
+ if f then
+ if type(data) == "table" then
+ f:write(concat(data,joiner or ""))
+ elseif type(data) == "function" then
+ data(f)
+ else
+ f:write(data or "")
+ end
+ f:close()
+ flush()
+ return true
+ else
+ return false
+ end
+end
+
+-- we can also chunk this one if needed: io.lines(filename,chunksize,"*l")
+
+-- ffi.readline
+
+if fio and fio.readline then
+
+ local readline = fio.readline
+
+ function io.loadlines(filename,n) -- return nil if empty
+ local f = open(filename,'r')
+ if not f then
+ -- no file
+ elseif n then
+ local lines = { }
+ for i=1,n do
+ local line = readline(f)
+ if line then
+ lines[i] = line
+ else
+ break
+ end
+ end
+ f:close()
+ lines = concat(lines,"\n")
+ if #lines > 0 then
+ return lines
+ end
+ else
+ local line = readline(f)
+ f:close()
+ if line and #line > 0 then
+ return line
+ end
+ end
+ end
+
+else
+
+ function io.loadlines(filename,n) -- return nil if empty
+ local f = open(filename,'r')
+ if not f then
+ -- no file
+ elseif n then
+ local lines = { }
+ for i=1,n do
+ local line = f:read("*lines")
+ if line then
+ lines[i] = line
+ else
+ break
+ end
+ end
+ f:close()
+ lines = concat(lines,"\n")
+ if #lines > 0 then
+ return lines
+ end
+ else
+ local line = f:read("*line") or ""
+ f:close()
+ if #line > 0 then
+ return line
+ end
+ end
+ end
+
+end
+
+function io.loadchunk(filename,n)
+ local f = open(filename,'rb')
+ if f then
+ local data = f:read(n or 1024)
+ f:close()
+ if #data > 0 then
+ return data
+ end
+ end
+end
+
+function io.exists(filename)
+ local f = open(filename)
+ if f == nil then
+ return false
+ else
+ f:close()
+ return true
+ end
+end
+
+function io.size(filename)
+ local f = open(filename)
+ if f == nil then
+ return 0
+ else
+ local s = f:seek("end")
+ f:close()
+ return s
+ end
+end
+
+local function noflines(f)
+ if type(f) == "string" then
+ local f = open(filename)
+ if f then
+ local n = f and noflines(f) or 0
+ f:close()
+ return n
+ else
+ return 0
+ end
+ else
+ -- todo: load and lpeg
+ local n = 0
+ for _ in f:lines() do
+ n = n + 1
+ end
+ f:seek('set',0)
+ return n
+ end
+end
+
+io.noflines = noflines
+
+-- inlined is faster ... beware, better use util-fil
+
+local nextchar = {
+ [ 4] = function(f)
+ return f:read(1,1,1,1)
+ end,
+ [ 2] = function(f)
+ return f:read(1,1)
+ end,
+ [ 1] = function(f)
+ return f:read(1)
+ end,
+ [-2] = function(f)
+ local a, b = f:read(1,1)
+ return b, a
+ end,
+ [-4] = function(f)
+ local a, b, c, d = f:read(1,1,1,1)
+ return d, c, b, a
+ end
+}
+
+function io.characters(f,n)
+ if f then
+ return nextchar[n or 1], f
+ end
+end
+
+local nextbyte = {
+ [4] = function(f)
+ local a, b, c, d = f:read(1,1,1,1)
+ if d then
+ return byte(a), byte(b), byte(c), byte(d)
+ end
+ end,
+ [3] = function(f)
+ local a, b, c = f:read(1,1,1)
+ if b then
+ return byte(a), byte(b), byte(c)
+ end
+ end,
+ [2] = function(f)
+ local a, b = f:read(1,1)
+ if b then
+ return byte(a), byte(b)
+ end
+ end,
+ [1] = function (f)
+ local a = f:read(1)
+ if a then
+ return byte(a)
+ end
+ end,
+ [-2] = function (f)
+ local a, b = f:read(1,1)
+ if b then
+ return byte(b), byte(a)
+ end
+ end,
+ [-3] = function(f)
+ local a, b, c = f:read(1,1,1)
+ if b then
+ return byte(c), byte(b), byte(a)
+ end
+ end,
+ [-4] = function(f)
+ local a, b, c, d = f:read(1,1,1,1)
+ if d then
+ return byte(d), byte(c), byte(b), byte(a)
+ end
+ end
+}
+
+function io.bytes(f,n)
+ if f then
+ return nextbyte[n or 1], f
+ else
+ return nil, nil
+ end
+end
+
+function io.ask(question,default,options)
+ while true do
+ write(question)
+ if options then
+ write(format(" [%s]",concat(options,"|")))
+ end
+ if default then
+ write(format(" [%s]",default))
+ end
+ write(format(" "))
+ flush()
+ local answer = read()
+ answer = gsub(answer,"^%s*(.*)%s*$","%1")
+ if answer == "" and default then
+ return default
+ elseif not options then
+ return answer
+ else
+ for k=1,#options do
+ if options[k] == answer then
+ return answer
+ end
+ end
+ local pattern = "^" .. answer
+ for k=1,#options do
+ local v = options[k]
+ if find(v,pattern) then
+ return v
+ end
+ end
+ end
+ end
+end
+
+local function readnumber(f,n,m) -- to be replaced
+ if m then
+ f:seek("set",n)
+ n = m
+ end
+ if n == 1 then
+ return byte(f:read(1))
+ elseif n == 2 then
+ local a, b = byte(f:read(2),1,2)
+ return 0x100 * a + b
+ elseif n == 3 then
+ local a, b, c = byte(f:read(3),1,3)
+ return 0x10000 * a + 0x100 * b + c
+ elseif n == 4 then
+ local a, b, c, d = byte(f:read(4),1,4)
+ return 0x1000000 * a + 0x10000 * b + 0x100 * c + d
+ elseif n == 8 then
+ local a, b = readnumber(f,4), readnumber(f,4)
+ return 0x100 * a + b
+ elseif n == 12 then
+ local a, b, c = readnumber(f,4), readnumber(f,4), readnumber(f,4)
+ return 0x10000 * a + 0x100 * b + c
+ elseif n == -2 then
+ local b, a = byte(f:read(2),1,2)
+ return 0x100 * a + b
+ elseif n == -3 then
+ local c, b, a = byte(f:read(3),1,3)
+ return 0x10000 * a + 0x100 * b + c
+ elseif n == -4 then
+ local d, c, b, a = byte(f:read(4),1,4)
+ return 0x1000000 * a + 0x10000 * b + 0x100*c + d
+ elseif n == -8 then
+ local h, g, f, e, d, c, b, a = byte(f:read(8),1,8)
+ return 0x100000000000000 * a + 0x1000000000000 * b + 0x10000000000 * c + 0x100000000 * d +
+ 0x1000000 * e + 0x10000 * f + 0x100 * g + h
+ else
+ return 0
+ end
+end
+
+io.readnumber = readnumber
+
+function io.readstring(f,n,m)
+ if m then
+ f:seek("set",n)
+ n = m
+ end
+ local str = gsub(f:read(n),"\000","")
+ return str
+end
+
+-- This works quite ok:
+--
+-- function io.piped(command,writer)
+-- local pipe = io.popen(command)
+-- -- for line in pipe:lines() do
+-- -- print(line)
+-- -- end
+-- while true do
+-- local line = pipe:read(1)
+-- if not line then
+-- break
+-- elseif line ~= "\n" then
+-- writer(line)
+-- end
+-- end
+-- return pipe:close() -- ok, status, (error)code
+-- end