local shell = require "shell" local mtar = require "libmtar" local fs = require "filesystem" local args, opts = shell.parse(...) local w, lz16 = pcall(require, "liblz16") if not w then lz16 = nil end local function vw(s) if opts.v and s then io.write(tostring(s)) end end local function vp(...) for k,v in ipairs({...}) do vw(tostring(v).."\n") end end local function cleanPath(path) local pt = {} for segment in path:gmatch("[^/]+") do if segment == ".." then pt[#pt] = nil elseif segment ~= "." then pt[#pt+1] = segment end end return table.concat(pt,"/") end function unarchive(stream,dest,verbose,csize) csize = csize or 2048 function vwrite(...) if verbose then io.write(...) end end for fname, read, len in mtar.iter(stream) do local written = 0 fname = cleanPath(fname) vwrite(string.format("%s %i... ",fname,len)) local dir = fname:match("(.+)/.*%.?.+") if dir then if dir:sub(1,1) ~= "/" then dir = os.getenv("PWD").."/"..dir end fs.makeDirectory(dest.."/"..dir) end local f = io.open(dest.."/"..fname,"wb") if f then repeat local rb = read(csize) written = written + rb:len() f:write(rb) vwrite(written) vwrite(" ") until written == len f:close() vwrite("done\n") else vwrite("failed to open "..fname.."\n") end end end function open(fn,mode) local f if opts.z then f = lz16.open(fn, mode) if mode == "w" and tonumber(opts.csize) then f.bufferSize = tonumber(opts.csize) end else f = io.open(fn, mode) end return f end if opts.x then local f = open(args[1],"rb") if not f then error("unable to open file") end local w, r = unarchive(f,args[2] or ".",opts.v) f:close() elseif opts.t then local f = open(args[1],"rb") if not f then error("unable to open file") end for name, _, fsize in mtar.iter(f) do print(name..": "..tostring(fsize)) end f:close() elseif opts.c then local mode = (opts.r and "ab") or "wb" local f = open(args[1],mode) if not f then error("unable to open file") end table.remove(args,1) local fs = require "filesystem" for k,v in pairs(args) do local ap = true if v:sub(1,1) ~= "/" then ap = false v = os.getenv("PWD") .. "/" .. v end v=fs.canonical(v) vw(v.."... ") if fs.isDirectory(v) then vp("directory.") for file in fs.list(v) do if ap then args[#args+1] = v.."/"..file else args[#args+1] = v:sub(os.getenv("PWD"):len()+2).."/"..file end end else local fsize = fs.size(v) local inputf = io.open(v,"rb") if fsize and inputf then local c = 0 if ap then f:write(mtar.genHeader(v,fsize)) else f:write(mtar.genHeader(v:sub(os.getenv("PWD"):len()+2),fsize)) end while true do local ib = inputf:read(4096) if ib then c=c+ib:len() f:write(ib) else break end end vp(c) else vp("failed to open.") end end end f:close() else print([[Usage: mtar -[crtxvz] [otherfile] [otherfile] [--csize=blocksize] Operations: x: extract archive c: create archive t: list contents of archive Flags: v: be verbose z: use LZSS compression (requires liblz16) r: append rather than overwrite Extended options: csize: compression input block size]]) end