diff options
Diffstat (limited to 'macros/luatex/generic/lualibs/lualibs-io.lua')
-rw-r--r-- | macros/luatex/generic/lualibs/lualibs-io.lua | 473 |
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 |